From 12da995a99d38c25afb7c4df6b21683877d496a1 Mon Sep 17 00:00:00 2001 From: J Carreira Date: Thu, 20 Feb 2014 20:17:37 +0000 Subject: [PATCH] Init repo from r216 of svn repo (before mess things up) --- CHANGES.TXT | 1562 ++++++ COPYRIGHT_ISO_IEC.txt | 18 + COPYRIGHT_ITU.txt | 32 + Changes_detail.txt | 195 + FREXT_changes.txt | 167 + JM.xcodeproj/project.pbxproj | 1661 ++++++ Makefile | 46 + Readme.txt | 195 + bin/HM-like/encoder_JM_Intra_HE.cfg | 660 +++ bin/HM-like/encoder_JM_LP_HE.cfg | 671 +++ bin/HM-like/encoder_JM_RA_B_HE.cfg | 671 +++ bin/HM-like/per-sequence_JM/BQMall.cfg | 9 + bin/HM-like/per-sequence_JM/BQSquare.cfg | 9 + bin/HM-like/per-sequence_JM/BQTerrace.cfg | 9 + .../per-sequence_JM/BasketballDrill.cfg | 9 + .../per-sequence_JM/BasketballDrillText.cfg | 9 + .../per-sequence_JM/BasketballDrive.cfg | 9 + .../per-sequence_JM/BasketballPass.cfg | 9 + .../per-sequence_JM/BlowingBubbles.cfg | 9 + bin/HM-like/per-sequence_JM/Cactus.cfg | 9 + bin/HM-like/per-sequence_JM/ChinaSpeed.cfg | 9 + bin/HM-like/per-sequence_JM/Kimono.cfg | 9 + .../per-sequence_JM/NebutaFestival_10bit.cfg | 9 + bin/HM-like/per-sequence_JM/ParkScene.cfg | 9 + bin/HM-like/per-sequence_JM/PartyScene.cfg | 9 + .../per-sequence_JM/PeopleOnStreet.cfg | 9 + bin/HM-like/per-sequence_JM/RaceHorses.cfg | 9 + bin/HM-like/per-sequence_JM/RaceHorsesC.cfg | 9 + bin/HM-like/per-sequence_JM/SlideEditing.cfg | 9 + bin/HM-like/per-sequence_JM/SlideShow.cfg | 9 + .../SteamLocomotiveTrain_10bit.cfg | 9 + bin/HM-like/per-sequence_JM/Traffic.cfg | 9 + bin/HM-like/per-sequence_JM/Vidyo1.cfg | 9 + bin/HM-like/per-sequence_JM/Vidyo3.cfg | 9 + bin/HM-like/per-sequence_JM/Vidyo4.cfg | 9 + bin/ToneMapping.cfg | 25 + bin/decoder.cfg | 52 + bin/decoder_stereo.cfg | 40 + bin/encoder.cfg | 671 +++ bin/encoder_baseline.cfg | 376 ++ bin/encoder_extended.cfg | 524 ++ bin/encoder_fast.cfg | 648 +++ bin/encoder_main.cfg | 499 ++ bin/encoder_stereo.cfg | 650 +++ bin/encoder_tonemapping.cfg | 570 ++ bin/encoder_view1.cfg | 102 + bin/encoder_yuv422.cfg | 635 +++ bin/explicit_seq.cfg | 136 + bin/foreman_part_qcif.yuv | 1 + bin/foreman_part_qcif_422.yuv | 1 + bin/foreman_part_qcif_444.yuv | 1 + bin/q_matrix.cfg | 118 + bin/q_matrix2.cfg | 61 + bin/q_matrix_def.cfg | 61 + bin/q_offset.cfg | 147 + bin/sg0conf.cfg | 9 + bin/sg2conf.cfg | 11 + bin/sg6conf.cfg | 396 ++ disclaimer.txt | 25 + doc/JM Reference Software Manual.pdf | Bin 0 -> 1195417 bytes doc/coding_style.doc | Bin 0 -> 54272 bytes doc/doxygen.txt | 18 + doc/foot.html | 2 + doc/ldecod.dox | 234 + doc/lencod.dox | 234 + jm_vc10.sln | 59 + jm_vc7.sln | 47 + jm_vc8.sln | 57 + jm_vc9.sln | 61 + lcommon/inc/blk_prediction.h | 25 + lcommon/inc/config_common.h | 35 + lcommon/inc/ctx_tables.h | 994 ++++ lcommon/inc/distortion.h | 31 + lcommon/inc/enc_statistics.h | 70 + lcommon/inc/fast_memory.h | 37 + lcommon/inc/frame.h | 66 + lcommon/inc/ifunctions.h | 448 ++ lcommon/inc/img_io.h | 30 + lcommon/inc/img_process.h | 25 + lcommon/inc/img_process_types.h | 24 + lcommon/inc/input.h | 25 + lcommon/inc/io_image.h | 45 + lcommon/inc/io_raw.h | 22 + lcommon/inc/io_tiff.h | 24 + lcommon/inc/io_video.h | 63 + lcommon/inc/lagrangian.h | 25 + lcommon/inc/mb_access.h | 33 + lcommon/inc/mbuffer_common.h | 225 + lcommon/inc/memalloc.h | 217 + lcommon/inc/mv_prediction.h | 19 + lcommon/inc/nalucommon.h | 91 + lcommon/inc/parsetcommon.h | 274 + lcommon/inc/quant_params.h | 54 + lcommon/inc/report.h | 25 + lcommon/inc/transcode_common.h | 33 + lcommon/inc/transform.h | 32 + lcommon/inc/typedefs.h | 75 + lcommon/inc/types.h | 203 + lcommon/inc/vui_params.h | 74 + lcommon/inc/win32.h | 122 + lcommon/src/blk_prediction.c | 63 + lcommon/src/config_common.c | 417 ++ lcommon/src/img_io.c | 327 ++ lcommon/src/img_process.c | 272 + lcommon/src/input.c | 924 ++++ lcommon/src/io_raw.c | 203 + lcommon/src/io_tiff.c | 1153 ++++ lcommon/src/mbuffer_common.c | 863 +++ lcommon/src/memalloc.c | 2726 +++++++++ lcommon/src/mv_prediction.c | 312 ++ lcommon/src/nalucommon.c | 71 + lcommon/src/parsetcommon.c | 244 + lcommon/src/transcode_common.c | 39 + lcommon/src/transform.c | 548 ++ lcommon/src/win32.c | 78 + ldecod/Makefile | 152 + ldecod/decoder_default_config.cfg | 86 + ldecod/inc/annexb.h | 40 + ldecod/inc/biaridecod.h | 133 + ldecod/inc/block.h | 127 + ldecod/inc/cabac.h | 63 + ldecod/inc/configfile.h | 107 + ldecod/inc/context_ini.h | 23 + ldecod/inc/contributors.h | 232 + ldecod/inc/dec_statistics.h | 33 + ldecod/inc/defines.h | 310 ++ ldecod/inc/elements.h | 112 + ldecod/inc/erc_api.h | 164 + ldecod/inc/erc_do.h | 44 + ldecod/inc/erc_globals.h | 52 + ldecod/inc/errorconcealment.h | 21 + ldecod/inc/ferc/ferc_api.h | 106 + ldecod/inc/ferc/ferc_internals.h | 148 + ldecod/inc/ferc/ferc_opencv_api.h | 68 + ldecod/inc/ferc/ferc_proposed.h | 22 + ldecod/inc/filehandle.h | 23 + ldecod/inc/fmo.h | 30 + ldecod/inc/global.h | 1242 +++++ ldecod/inc/h264decoder.h | 54 + ldecod/inc/header.h | 22 + ldecod/inc/image.h | 47 + ldecod/inc/intra16x16_pred.h | 26 + ldecod/inc/intra4x4_pred.h | 26 + ldecod/inc/intra8x8_pred.h | 26 + ldecod/inc/leaky_bucket.h | 26 + ldecod/inc/loop_filter.h | 68 + ldecod/inc/loopfilter.h | 19 + ldecod/inc/macroblock.h | 152 + ldecod/inc/mb_prediction.h | 35 + ldecod/inc/mbuffer.h | 291 + ldecod/inc/mbuffer_mvc.h | 37 + ldecod/inc/mc_prediction.h | 33 + ldecod/inc/nalu.h | 27 + ldecod/inc/output.h | 26 + ldecod/inc/parset.h | 68 + ldecod/inc/quant.h | 169 + ldecod/inc/report_dec.h | 23 + ldecod/inc/rtp.h | 46 + ldecod/inc/sei.h | 144 + ldecod/inc/stream_parser.h | 37 + ldecod/inc/transcode_dec.h | 25 + ldecod/inc/transform8x8.h | 23 + ldecod/inc/vlc.h | 112 + ldecod/instructions_and_script.bat | 20 + ldecod/ldecod.props | 17 + ldecod/src/annexb.c | 353 ++ ldecod/src/biaridecod.c | 314 ++ ldecod/src/block.c | 907 +++ ldecod/src/cabac.c | 2448 +++++++++ ldecod/src/configfile.c | 312 ++ ldecod/src/context_ini.c | 122 + ldecod/src/dec_statistics.c | 71 + ldecod/src/decoder_test.c | 305 + ldecod/src/erc_api.c | 482 ++ ldecod/src/erc_do_i.c | 544 ++ ldecod/src/erc_do_p.c | 1862 +++++++ ldecod/src/errorconcealment.c | 220 + ldecod/src/ferc/ferc_api.c | 1419 +++++ ldecod/src/ferc/ferc_dfdp.c | 589 ++ ldecod/src/ferc/ferc_internals.c | 946 ++++ ldecod/src/ferc/ferc_ismid.c | 657 +++ ldecod/src/ferc/ferc_motion.c | 262 + ldecod/src/ferc/ferc_motion_estimation.c | 288 + ldecod/src/ferc/ferc_opencv_api.c | 485 ++ ldecod/src/ferc/ferc_proposed.c | 3693 +++++++++++++ ldecod/src/ferc/ferc_used_code.txt | 734 +++ ldecod/src/filehandle.c | 177 + ldecod/src/fmo.c | 555 ++ ldecod/src/header.c | 995 ++++ ldecod/src/image.c | 2785 ++++++++++ ldecod/src/intra16x16_pred.c | 348 ++ ldecod/src/intra16x16_pred_mbaff.c | 320 ++ ldecod/src/intra16x16_pred_normal.c | 350 ++ ldecod/src/intra4x4_pred.c | 833 +++ ldecod/src/intra4x4_pred_mbaff.c | 826 +++ ldecod/src/intra4x4_pred_normal.c | 816 +++ ldecod/src/intra8x8_pred.c | 1546 ++++++ ldecod/src/intra8x8_pred_mbaff.c | 1571 ++++++ ldecod/src/intra8x8_pred_normal.c | 1612 ++++++ ldecod/src/intra_chroma_pred.c | 363 ++ ldecod/src/intra_chroma_pred_mbaff.c | 359 ++ ldecod/src/intra_pred_common.c | 43 + ldecod/src/ldecod.c | 1693 ++++++ ldecod/src/leaky_bucket.c | 139 + ldecod/src/loopFilter.c | 664 +++ ldecod/src/loop_filter_mbaff.c | 1147 ++++ ldecod/src/loop_filter_normal.c | 1576 ++++++ ldecod/src/macroblock.c | 1494 +++++ ldecod/src/mb_access.c | 666 +++ ldecod/src/mb_prediction.c | 1300 +++++ ldecod/src/mb_read.c | 2230 ++++++++ ldecod/src/mbuffer.c | 3512 ++++++++++++ ldecod/src/mbuffer_mvc.c | 859 +++ ldecod/src/mc_direct.c | 709 +++ ldecod/src/mc_prediction.c | 1802 ++++++ ldecod/src/nal.c | 120 + ldecod/src/nalu.c | 187 + ldecod/src/output.c | 959 ++++ ldecod/src/parset.c | 1604 ++++++ ldecod/src/quant.c | 353 ++ ldecod/src/read_comp_cabac.c | 1942 +++++++ ldecod/src/read_comp_cavlc.c | 2156 ++++++++ ldecod/src/report_dec.c | 566 ++ ldecod/src/rtp.c | 388 ++ ldecod/src/sei.c | 2236 ++++++++ ldecod/src/stream_parser.c | 546 ++ ldecod/src/transcode_dec.c | 104 + ldecod/src/transform8x8.c | 115 + ldecod/src/vlc.c | 1289 +++++ ldecod/use_opencv_release.props | 17 + ldecod/use_opencv_x86_release.props | 17 + ldecod_vc10.vcxproj | 565 ++ ldecod_vc10.vcxproj.filters | 496 ++ ldecod_vc10.vcxproj.user | 15 + ldecod_vc7.vcproj | 853 +++ ldecod_vc8.vcproj | 1347 +++++ ldecod_vc9.vcproj | 956 ++++ lencod/Makefile | 132 + lencod/inc/annexb.h | 25 + lencod/inc/annexb_split.h | 34 + lencod/inc/biariencode.h | 69 + lencod/inc/block.h | 103 + lencod/inc/cabac.h | 73 + lencod/inc/cconv_yuv2rgb.h | 28 + lencod/inc/configfile.h | 642 +++ lencod/inc/conformance.h | 32 + lencod/inc/context_ini.h | 27 + lencod/inc/contributors.h | 217 + lencod/inc/defines.h | 372 ++ lencod/inc/elements.h | 89 + lencod/inc/errdo.h | 123 + lencod/inc/errdo_dist_mhyp.h | 21 + lencod/inc/explicit_gop.h | 23 + lencod/inc/explicit_seq.h | 39 + lencod/inc/filehandle.h | 25 + lencod/inc/fmo.h | 35 + lencod/inc/get_block_otf.h | 95 + lencod/inc/global.h | 1635 ++++++ lencod/inc/header.h | 20 + lencod/inc/image.h | 59 + lencod/inc/img_chroma.h | 23 + lencod/inc/img_dist_ms_ssim.h | 25 + lencod/inc/img_dist_snr.h | 24 + lencod/inc/img_dist_ssim.h | 26 + lencod/inc/img_distortion.h | 27 + lencod/inc/img_luma.h | 39 + lencod/inc/intra16x16.h | 30 + lencod/inc/intra4x4.h | 26 + lencod/inc/intra8x8.h | 27 + lencod/inc/intrarefresh.h | 26 + lencod/inc/lambda.h | 31 + lencod/inc/leaky_bucket.h | 29 + lencod/inc/list_reorder.h | 39 + lencod/inc/lln_mc_prediction.h | 35 + lencod/inc/loop_filter.h | 67 + lencod/inc/macroblock.h | 128 + lencod/inc/macroblock_p444.h | 24 + lencod/inc/mbuffer.h | 258 + lencod/inc/mc_prediction.h | 38 + lencod/inc/mc_prediction_otf.h | 43 + lencod/inc/md_common.h | 38 + lencod/inc/md_distortion.h | 31 + lencod/inc/me_distortion.h | 92 + lencod/inc/me_distortion_otf.h | 40 + lencod/inc/me_epzs.h | 53 + lencod/inc/me_epzs_common.h | 155 + lencod/inc/me_epzs_int.h | 29 + lencod/inc/me_fullfast.h | 39 + lencod/inc/me_fullfast_otf.h | 17 + lencod/inc/me_fullsearch.h | 28 + lencod/inc/me_umhex.h | 147 + lencod/inc/me_umhexsmp.h | 61 + lencod/inc/mmco.h | 32 + lencod/inc/mode_decision.h | 64 + lencod/inc/mode_decision_p8x8.h | 31 + lencod/inc/mv_search.h | 140 + lencod/inc/nal.h | 28 + lencod/inc/nalu.h | 26 + lencod/inc/output.h | 26 + lencod/inc/params.h | 453 ++ lencod/inc/parset.h | 60 + lencod/inc/pred_struct.h | 35 + lencod/inc/pred_struct_adapt.h | 23 + lencod/inc/pred_struct_adapt_types.h | 22 + lencod/inc/pred_struct_types.h | 143 + lencod/inc/q_around.h | 26 + lencod/inc/q_matrix.h | 33 + lencod/inc/q_offsets.h | 25 + lencod/inc/quant4x4.h | 53 + lencod/inc/quant8x8.h | 29 + lencod/inc/quantChroma.h | 47 + lencod/inc/ratectl.h | 44 + lencod/inc/rc_quadratic.h | 76 + lencod/inc/rc_types.h | 156 + lencod/inc/rd_intra_jm.h | 27 + lencod/inc/rd_intra_jm444.h | 24 + lencod/inc/rdopt.h | 132 + lencod/inc/rdopt_coding_state.h | 53 + lencod/inc/rdoq.h | 202 + lencod/inc/refbuf.h | 82 + lencod/inc/refbuf_otf.h | 32 + lencod/inc/rtp.h | 56 + lencod/inc/sei.h | 433 ++ lencod/inc/slice.h | 48 + lencod/inc/symbol.h | 27 + lencod/inc/transcode_enc.h | 25 + lencod/inc/transform8x8.h | 37 + lencod/inc/vlc.h | 72 + lencod/inc/wp.h | 56 + lencod/inc/wp_lms.h | 26 + lencod/inc/wp_mciter.h | 29 + lencod/inc/wp_mcprec.h | 52 + lencod/src/annexb.c | 199 + lencod/src/annexb_split.c | 197 + lencod/src/biariencode.c | 572 ++ lencod/src/block.c | 2463 +++++++++ lencod/src/cabac.c | 1879 +++++++ lencod/src/cconv_yuv2rgb.c | 149 + lencod/src/configfile.c | 1529 ++++++ lencod/src/conformance.c | 633 +++ lencod/src/context_ini.c | 372 ++ lencod/src/errdo.c | 732 +++ lencod/src/errdo_dist_mhyp.c | 671 +++ lencod/src/explicit_gop.c | 216 + lencod/src/explicit_seq.c | 329 ++ lencod/src/filehandle.c | 277 + lencod/src/fmo.c | 717 +++ lencod/src/get_block_otf.c | 1083 ++++ lencod/src/header.c | 614 +++ lencod/src/image.c | 3374 ++++++++++++ lencod/src/image_mp.c | 691 +++ lencod/src/img_chroma.c | 437 ++ lencod/src/img_dist_ms_ssim.c | 349 ++ lencod/src/img_dist_snr.c | 76 + lencod/src/img_dist_ssim.c | 115 + lencod/src/img_distortion.c | 211 + lencod/src/img_luma.c | 680 +++ lencod/src/intra16x16.c | 517 ++ lencod/src/intra4x4.c | 561 ++ lencod/src/intra8x8.c | 756 +++ lencod/src/intra_chroma.c | 1215 ++++ lencod/src/intrarefresh.c | 125 + lencod/src/lambda.c | 365 ++ lencod/src/leaky_bucket.c | 314 ++ lencod/src/lencod.c | 2639 +++++++++ lencod/src/list_reorder.c | 1411 +++++ lencod/src/lln_mc_prediction.c | 1316 +++++ lencod/src/loopFilter.c | 294 + lencod/src/loop_filter_mbaff.c | 709 +++ lencod/src/loop_filter_normal.c | 758 +++ lencod/src/macroblock.c | 4882 +++++++++++++++++ lencod/src/macroblock_P444.c | 467 ++ lencod/src/mb_access.c | 575 ++ lencod/src/mbuffer.c | 3407 ++++++++++++ lencod/src/mc_prediction.c | 653 +++ lencod/src/mc_prediction_otf.c | 387 ++ lencod/src/md_common.c | 594 ++ lencod/src/md_distortion.c | 200 + lencod/src/md_high.c | 316 ++ lencod/src/md_highfast.c | 465 ++ lencod/src/md_highloss.c | 318 ++ lencod/src/md_low.c | 688 +++ lencod/src/me_distortion.c | 1565 ++++++ lencod/src/me_distortion_otf.c | 1273 +++++ lencod/src/me_epzs.c | 1002 ++++ lencod/src/me_epzs_common.c | 1862 +++++++ lencod/src/me_epzs_int.c | 1030 ++++ lencod/src/me_epzs_sub.c | 423 ++ lencod/src/me_fullfast.c | 690 +++ lencod/src/me_fullfast_otf.c | 378 ++ lencod/src/me_fullsearch.c | 534 ++ lencod/src/me_umhex.c | 1396 +++++ lencod/src/me_umhexsmp.c | 1025 ++++ lencod/src/mmco.c | 479 ++ lencod/src/mode_decision.c | 819 +++ lencod/src/mode_decision_P8x8.c | 1080 ++++ lencod/src/mv_direct.c | 814 +++ lencod/src/mv_search.c | 1861 +++++++ lencod/src/nal.c | 145 + lencod/src/nalu.c | 164 + lencod/src/output.c | 556 ++ lencod/src/parset.c | 1453 +++++ lencod/src/pred_struct.c | 2602 +++++++++ lencod/src/q_around.c | 295 + lencod/src/q_matrix.c | 901 +++ lencod/src/q_offsets.c | 636 +++ lencod/src/quant4x4.c | 66 + lencod/src/quant4x4_2step.c | 300 + lencod/src/quant4x4_around.c | 279 + lencod/src/quant4x4_normal.c | 259 + lencod/src/quant4x4_trellis.c | 433 ++ lencod/src/quant8x8.c | 51 + lencod/src/quant8x8_around.c | 223 + lencod/src/quant8x8_normal.c | 202 + lencod/src/quant8x8_trellis.c | 247 + lencod/src/quantChroma.c | 74 + lencod/src/quantChroma_2step.c | 173 + lencod/src/quantChroma_around.c | 174 + lencod/src/quantChroma_normal.c | 173 + lencod/src/quantChroma_trellis.c | 223 + lencod/src/ratectl.c | 406 ++ lencod/src/rc_quadratic.c | 2754 ++++++++++ lencod/src/rd_intra_jm.c | 563 ++ lencod/src/rd_intra_jm444.c | 468 ++ lencod/src/rd_intra_jm_low.c | 278 + lencod/src/rdopt.c | 4081 ++++++++++++++ lencod/src/rdopt_coding_state.c | 299 + lencod/src/rdoq.c | 496 ++ lencod/src/rdoq_cabac.c | 888 +++ lencod/src/rdoq_cavlc.c | 721 +++ lencod/src/rdpicdecision.c | 64 + lencod/src/report.c | 1514 +++++ lencod/src/rtp.c | 324 ++ lencod/src/sei.c | 3065 +++++++++++ lencod/src/slice.c | 2451 +++++++++ lencod/src/symbol.c | 18 + lencod/src/transcode_enc.c | 81 + lencod/src/transform8x8.c | 900 +++ lencod/src/transform8x8_H444.c | 424 ++ lencod/src/vlc.c | 1569 ++++++ lencod/src/weighted_prediction.c | 885 +++ lencod/src/wp_lms.c | 886 +++ lencod/src/wp_mciter.c | 874 +++ lencod/src/wp_mcprec.c | 375 ++ lencod_vc10.vcxproj | 525 ++ lencod_vc10.vcxproj.filters | 787 +++ lencod_vc10.vcxproj.user | 27 + lencod_vc7.vcproj | 1170 ++++ lencod_vc8.vcproj | 1980 +++++++ lencod_vc9.vcproj | 1384 +++++ rtp_loss/Makefile | 97 + rtp_loss/ReadMe.txt | 13 + rtp_loss/rtp_loss.cpp | 136 + rtp_loss/rtp_loss.h | 0 rtp_loss/rtp_loss_vc10.vcxproj | 90 + rtp_loss/rtp_loss_vc10.vcxproj.user | 3 + rtp_loss/rtp_loss_vc7.vcproj | 199 + rtp_loss/rtp_loss_vc8.vcproj | 360 ++ rtp_loss/rtp_loss_vc9.vcproj | 349 ++ rtpdump/Makefile | 96 + rtpdump/ReadMe.txt | 34 + rtpdump/rtpdump.cpp | 72 + rtpdump/rtpdump_vc10.vcxproj | 307 ++ rtpdump/rtpdump_vc10.vcxproj.user | 3 + rtpdump/rtpdump_vc7.vcproj | 174 + rtpdump/rtpdump_vc8.vcproj | 450 ++ rtpdump/rtpdump_vc9.vcproj | 344 ++ unixprep.sh | 28 + 468 files changed, 204309 insertions(+) create mode 100644 CHANGES.TXT create mode 100644 COPYRIGHT_ISO_IEC.txt create mode 100644 COPYRIGHT_ITU.txt create mode 100644 Changes_detail.txt create mode 100644 FREXT_changes.txt create mode 100644 JM.xcodeproj/project.pbxproj create mode 100644 Makefile create mode 100644 Readme.txt create mode 100644 bin/HM-like/encoder_JM_Intra_HE.cfg create mode 100644 bin/HM-like/encoder_JM_LP_HE.cfg create mode 100644 bin/HM-like/encoder_JM_RA_B_HE.cfg create mode 100644 bin/HM-like/per-sequence_JM/BQMall.cfg create mode 100644 bin/HM-like/per-sequence_JM/BQSquare.cfg create mode 100644 bin/HM-like/per-sequence_JM/BQTerrace.cfg create mode 100644 bin/HM-like/per-sequence_JM/BasketballDrill.cfg create mode 100644 bin/HM-like/per-sequence_JM/BasketballDrillText.cfg create mode 100644 bin/HM-like/per-sequence_JM/BasketballDrive.cfg create mode 100644 bin/HM-like/per-sequence_JM/BasketballPass.cfg create mode 100644 bin/HM-like/per-sequence_JM/BlowingBubbles.cfg create mode 100644 bin/HM-like/per-sequence_JM/Cactus.cfg create mode 100644 bin/HM-like/per-sequence_JM/ChinaSpeed.cfg create mode 100644 bin/HM-like/per-sequence_JM/Kimono.cfg create mode 100644 bin/HM-like/per-sequence_JM/NebutaFestival_10bit.cfg create mode 100644 bin/HM-like/per-sequence_JM/ParkScene.cfg create mode 100644 bin/HM-like/per-sequence_JM/PartyScene.cfg create mode 100644 bin/HM-like/per-sequence_JM/PeopleOnStreet.cfg create mode 100644 bin/HM-like/per-sequence_JM/RaceHorses.cfg create mode 100644 bin/HM-like/per-sequence_JM/RaceHorsesC.cfg create mode 100644 bin/HM-like/per-sequence_JM/SlideEditing.cfg create mode 100644 bin/HM-like/per-sequence_JM/SlideShow.cfg create mode 100644 bin/HM-like/per-sequence_JM/SteamLocomotiveTrain_10bit.cfg create mode 100644 bin/HM-like/per-sequence_JM/Traffic.cfg create mode 100644 bin/HM-like/per-sequence_JM/Vidyo1.cfg create mode 100644 bin/HM-like/per-sequence_JM/Vidyo3.cfg create mode 100644 bin/HM-like/per-sequence_JM/Vidyo4.cfg create mode 100644 bin/ToneMapping.cfg create mode 100644 bin/decoder.cfg create mode 100644 bin/decoder_stereo.cfg create mode 100644 bin/encoder.cfg create mode 100644 bin/encoder_baseline.cfg create mode 100644 bin/encoder_extended.cfg create mode 100644 bin/encoder_fast.cfg create mode 100644 bin/encoder_main.cfg create mode 100644 bin/encoder_stereo.cfg create mode 100644 bin/encoder_tonemapping.cfg create mode 100644 bin/encoder_view1.cfg create mode 100644 bin/encoder_yuv422.cfg create mode 100644 bin/explicit_seq.cfg create mode 100644 bin/foreman_part_qcif.yuv create mode 100644 bin/foreman_part_qcif_422.yuv create mode 100644 bin/foreman_part_qcif_444.yuv create mode 100644 bin/q_matrix.cfg create mode 100644 bin/q_matrix2.cfg create mode 100644 bin/q_matrix_def.cfg create mode 100644 bin/q_offset.cfg create mode 100644 bin/sg0conf.cfg create mode 100644 bin/sg2conf.cfg create mode 100644 bin/sg6conf.cfg create mode 100644 disclaimer.txt create mode 100644 doc/JM Reference Software Manual.pdf create mode 100644 doc/coding_style.doc create mode 100644 doc/doxygen.txt create mode 100644 doc/foot.html create mode 100644 doc/ldecod.dox create mode 100644 doc/lencod.dox create mode 100644 jm_vc10.sln create mode 100644 jm_vc7.sln create mode 100644 jm_vc8.sln create mode 100644 jm_vc9.sln create mode 100644 lcommon/inc/blk_prediction.h create mode 100644 lcommon/inc/config_common.h create mode 100644 lcommon/inc/ctx_tables.h create mode 100644 lcommon/inc/distortion.h create mode 100644 lcommon/inc/enc_statistics.h create mode 100644 lcommon/inc/fast_memory.h create mode 100644 lcommon/inc/frame.h create mode 100644 lcommon/inc/ifunctions.h create mode 100644 lcommon/inc/img_io.h create mode 100644 lcommon/inc/img_process.h create mode 100644 lcommon/inc/img_process_types.h create mode 100644 lcommon/inc/input.h create mode 100644 lcommon/inc/io_image.h create mode 100644 lcommon/inc/io_raw.h create mode 100644 lcommon/inc/io_tiff.h create mode 100644 lcommon/inc/io_video.h create mode 100644 lcommon/inc/lagrangian.h create mode 100644 lcommon/inc/mb_access.h create mode 100644 lcommon/inc/mbuffer_common.h create mode 100644 lcommon/inc/memalloc.h create mode 100644 lcommon/inc/mv_prediction.h create mode 100644 lcommon/inc/nalucommon.h create mode 100644 lcommon/inc/parsetcommon.h create mode 100644 lcommon/inc/quant_params.h create mode 100644 lcommon/inc/report.h create mode 100644 lcommon/inc/transcode_common.h create mode 100644 lcommon/inc/transform.h create mode 100644 lcommon/inc/typedefs.h create mode 100644 lcommon/inc/types.h create mode 100644 lcommon/inc/vui_params.h create mode 100644 lcommon/inc/win32.h create mode 100644 lcommon/src/blk_prediction.c create mode 100644 lcommon/src/config_common.c create mode 100644 lcommon/src/img_io.c create mode 100644 lcommon/src/img_process.c create mode 100644 lcommon/src/input.c create mode 100644 lcommon/src/io_raw.c create mode 100644 lcommon/src/io_tiff.c create mode 100644 lcommon/src/mbuffer_common.c create mode 100644 lcommon/src/memalloc.c create mode 100644 lcommon/src/mv_prediction.c create mode 100644 lcommon/src/nalucommon.c create mode 100644 lcommon/src/parsetcommon.c create mode 100644 lcommon/src/transcode_common.c create mode 100644 lcommon/src/transform.c create mode 100644 lcommon/src/win32.c create mode 100644 ldecod/Makefile create mode 100644 ldecod/decoder_default_config.cfg create mode 100644 ldecod/inc/annexb.h create mode 100644 ldecod/inc/biaridecod.h create mode 100644 ldecod/inc/block.h create mode 100644 ldecod/inc/cabac.h create mode 100644 ldecod/inc/configfile.h create mode 100644 ldecod/inc/context_ini.h create mode 100644 ldecod/inc/contributors.h create mode 100644 ldecod/inc/dec_statistics.h create mode 100644 ldecod/inc/defines.h create mode 100644 ldecod/inc/elements.h create mode 100644 ldecod/inc/erc_api.h create mode 100644 ldecod/inc/erc_do.h create mode 100644 ldecod/inc/erc_globals.h create mode 100644 ldecod/inc/errorconcealment.h create mode 100644 ldecod/inc/ferc/ferc_api.h create mode 100644 ldecod/inc/ferc/ferc_internals.h create mode 100644 ldecod/inc/ferc/ferc_opencv_api.h create mode 100644 ldecod/inc/ferc/ferc_proposed.h create mode 100644 ldecod/inc/filehandle.h create mode 100644 ldecod/inc/fmo.h create mode 100644 ldecod/inc/global.h create mode 100644 ldecod/inc/h264decoder.h create mode 100644 ldecod/inc/header.h create mode 100644 ldecod/inc/image.h create mode 100644 ldecod/inc/intra16x16_pred.h create mode 100644 ldecod/inc/intra4x4_pred.h create mode 100644 ldecod/inc/intra8x8_pred.h create mode 100644 ldecod/inc/leaky_bucket.h create mode 100644 ldecod/inc/loop_filter.h create mode 100644 ldecod/inc/loopfilter.h create mode 100644 ldecod/inc/macroblock.h create mode 100644 ldecod/inc/mb_prediction.h create mode 100644 ldecod/inc/mbuffer.h create mode 100644 ldecod/inc/mbuffer_mvc.h create mode 100644 ldecod/inc/mc_prediction.h create mode 100644 ldecod/inc/nalu.h create mode 100644 ldecod/inc/output.h create mode 100644 ldecod/inc/parset.h create mode 100644 ldecod/inc/quant.h create mode 100644 ldecod/inc/report_dec.h create mode 100644 ldecod/inc/rtp.h create mode 100644 ldecod/inc/sei.h create mode 100644 ldecod/inc/stream_parser.h create mode 100644 ldecod/inc/transcode_dec.h create mode 100644 ldecod/inc/transform8x8.h create mode 100644 ldecod/inc/vlc.h create mode 100644 ldecod/instructions_and_script.bat create mode 100644 ldecod/ldecod.props create mode 100644 ldecod/src/annexb.c create mode 100644 ldecod/src/biaridecod.c create mode 100644 ldecod/src/block.c create mode 100644 ldecod/src/cabac.c create mode 100644 ldecod/src/configfile.c create mode 100644 ldecod/src/context_ini.c create mode 100644 ldecod/src/dec_statistics.c create mode 100644 ldecod/src/decoder_test.c create mode 100644 ldecod/src/erc_api.c create mode 100644 ldecod/src/erc_do_i.c create mode 100644 ldecod/src/erc_do_p.c create mode 100644 ldecod/src/errorconcealment.c create mode 100644 ldecod/src/ferc/ferc_api.c create mode 100644 ldecod/src/ferc/ferc_dfdp.c create mode 100644 ldecod/src/ferc/ferc_internals.c create mode 100644 ldecod/src/ferc/ferc_ismid.c create mode 100644 ldecod/src/ferc/ferc_motion.c create mode 100644 ldecod/src/ferc/ferc_motion_estimation.c create mode 100644 ldecod/src/ferc/ferc_opencv_api.c create mode 100644 ldecod/src/ferc/ferc_proposed.c create mode 100644 ldecod/src/ferc/ferc_used_code.txt create mode 100644 ldecod/src/filehandle.c create mode 100644 ldecod/src/fmo.c create mode 100644 ldecod/src/header.c create mode 100644 ldecod/src/image.c create mode 100644 ldecod/src/intra16x16_pred.c create mode 100644 ldecod/src/intra16x16_pred_mbaff.c create mode 100644 ldecod/src/intra16x16_pred_normal.c create mode 100644 ldecod/src/intra4x4_pred.c create mode 100644 ldecod/src/intra4x4_pred_mbaff.c create mode 100644 ldecod/src/intra4x4_pred_normal.c create mode 100644 ldecod/src/intra8x8_pred.c create mode 100644 ldecod/src/intra8x8_pred_mbaff.c create mode 100644 ldecod/src/intra8x8_pred_normal.c create mode 100644 ldecod/src/intra_chroma_pred.c create mode 100644 ldecod/src/intra_chroma_pred_mbaff.c create mode 100644 ldecod/src/intra_pred_common.c create mode 100644 ldecod/src/ldecod.c create mode 100644 ldecod/src/leaky_bucket.c create mode 100644 ldecod/src/loopFilter.c create mode 100644 ldecod/src/loop_filter_mbaff.c create mode 100644 ldecod/src/loop_filter_normal.c create mode 100644 ldecod/src/macroblock.c create mode 100644 ldecod/src/mb_access.c create mode 100644 ldecod/src/mb_prediction.c create mode 100644 ldecod/src/mb_read.c create mode 100644 ldecod/src/mbuffer.c create mode 100644 ldecod/src/mbuffer_mvc.c create mode 100644 ldecod/src/mc_direct.c create mode 100644 ldecod/src/mc_prediction.c create mode 100644 ldecod/src/nal.c create mode 100644 ldecod/src/nalu.c create mode 100644 ldecod/src/output.c create mode 100644 ldecod/src/parset.c create mode 100644 ldecod/src/quant.c create mode 100644 ldecod/src/read_comp_cabac.c create mode 100644 ldecod/src/read_comp_cavlc.c create mode 100644 ldecod/src/report_dec.c create mode 100644 ldecod/src/rtp.c create mode 100644 ldecod/src/sei.c create mode 100644 ldecod/src/stream_parser.c create mode 100644 ldecod/src/transcode_dec.c create mode 100644 ldecod/src/transform8x8.c create mode 100644 ldecod/src/vlc.c create mode 100644 ldecod/use_opencv_release.props create mode 100644 ldecod/use_opencv_x86_release.props create mode 100644 ldecod_vc10.vcxproj create mode 100644 ldecod_vc10.vcxproj.filters create mode 100644 ldecod_vc10.vcxproj.user create mode 100644 ldecod_vc7.vcproj create mode 100644 ldecod_vc8.vcproj create mode 100644 ldecod_vc9.vcproj create mode 100644 lencod/Makefile create mode 100644 lencod/inc/annexb.h create mode 100644 lencod/inc/annexb_split.h create mode 100644 lencod/inc/biariencode.h create mode 100644 lencod/inc/block.h create mode 100644 lencod/inc/cabac.h create mode 100644 lencod/inc/cconv_yuv2rgb.h create mode 100644 lencod/inc/configfile.h create mode 100644 lencod/inc/conformance.h create mode 100644 lencod/inc/context_ini.h create mode 100644 lencod/inc/contributors.h create mode 100644 lencod/inc/defines.h create mode 100644 lencod/inc/elements.h create mode 100644 lencod/inc/errdo.h create mode 100644 lencod/inc/errdo_dist_mhyp.h create mode 100644 lencod/inc/explicit_gop.h create mode 100644 lencod/inc/explicit_seq.h create mode 100644 lencod/inc/filehandle.h create mode 100644 lencod/inc/fmo.h create mode 100644 lencod/inc/get_block_otf.h create mode 100644 lencod/inc/global.h create mode 100644 lencod/inc/header.h create mode 100644 lencod/inc/image.h create mode 100644 lencod/inc/img_chroma.h create mode 100644 lencod/inc/img_dist_ms_ssim.h create mode 100644 lencod/inc/img_dist_snr.h create mode 100644 lencod/inc/img_dist_ssim.h create mode 100644 lencod/inc/img_distortion.h create mode 100644 lencod/inc/img_luma.h create mode 100644 lencod/inc/intra16x16.h create mode 100644 lencod/inc/intra4x4.h create mode 100644 lencod/inc/intra8x8.h create mode 100644 lencod/inc/intrarefresh.h create mode 100644 lencod/inc/lambda.h create mode 100644 lencod/inc/leaky_bucket.h create mode 100644 lencod/inc/list_reorder.h create mode 100644 lencod/inc/lln_mc_prediction.h create mode 100644 lencod/inc/loop_filter.h create mode 100644 lencod/inc/macroblock.h create mode 100644 lencod/inc/macroblock_p444.h create mode 100644 lencod/inc/mbuffer.h create mode 100644 lencod/inc/mc_prediction.h create mode 100644 lencod/inc/mc_prediction_otf.h create mode 100644 lencod/inc/md_common.h create mode 100644 lencod/inc/md_distortion.h create mode 100644 lencod/inc/me_distortion.h create mode 100644 lencod/inc/me_distortion_otf.h create mode 100644 lencod/inc/me_epzs.h create mode 100644 lencod/inc/me_epzs_common.h create mode 100644 lencod/inc/me_epzs_int.h create mode 100644 lencod/inc/me_fullfast.h create mode 100644 lencod/inc/me_fullfast_otf.h create mode 100644 lencod/inc/me_fullsearch.h create mode 100644 lencod/inc/me_umhex.h create mode 100644 lencod/inc/me_umhexsmp.h create mode 100644 lencod/inc/mmco.h create mode 100644 lencod/inc/mode_decision.h create mode 100644 lencod/inc/mode_decision_p8x8.h create mode 100644 lencod/inc/mv_search.h create mode 100644 lencod/inc/nal.h create mode 100644 lencod/inc/nalu.h create mode 100644 lencod/inc/output.h create mode 100644 lencod/inc/params.h create mode 100644 lencod/inc/parset.h create mode 100644 lencod/inc/pred_struct.h create mode 100644 lencod/inc/pred_struct_adapt.h create mode 100644 lencod/inc/pred_struct_adapt_types.h create mode 100644 lencod/inc/pred_struct_types.h create mode 100644 lencod/inc/q_around.h create mode 100644 lencod/inc/q_matrix.h create mode 100644 lencod/inc/q_offsets.h create mode 100644 lencod/inc/quant4x4.h create mode 100644 lencod/inc/quant8x8.h create mode 100644 lencod/inc/quantChroma.h create mode 100644 lencod/inc/ratectl.h create mode 100644 lencod/inc/rc_quadratic.h create mode 100644 lencod/inc/rc_types.h create mode 100644 lencod/inc/rd_intra_jm.h create mode 100644 lencod/inc/rd_intra_jm444.h create mode 100644 lencod/inc/rdopt.h create mode 100644 lencod/inc/rdopt_coding_state.h create mode 100644 lencod/inc/rdoq.h create mode 100644 lencod/inc/refbuf.h create mode 100644 lencod/inc/refbuf_otf.h create mode 100644 lencod/inc/rtp.h create mode 100644 lencod/inc/sei.h create mode 100644 lencod/inc/slice.h create mode 100644 lencod/inc/symbol.h create mode 100644 lencod/inc/transcode_enc.h create mode 100644 lencod/inc/transform8x8.h create mode 100644 lencod/inc/vlc.h create mode 100644 lencod/inc/wp.h create mode 100644 lencod/inc/wp_lms.h create mode 100644 lencod/inc/wp_mciter.h create mode 100644 lencod/inc/wp_mcprec.h create mode 100644 lencod/src/annexb.c create mode 100644 lencod/src/annexb_split.c create mode 100644 lencod/src/biariencode.c create mode 100644 lencod/src/block.c create mode 100644 lencod/src/cabac.c create mode 100644 lencod/src/cconv_yuv2rgb.c create mode 100644 lencod/src/configfile.c create mode 100644 lencod/src/conformance.c create mode 100644 lencod/src/context_ini.c create mode 100644 lencod/src/errdo.c create mode 100644 lencod/src/errdo_dist_mhyp.c create mode 100644 lencod/src/explicit_gop.c create mode 100644 lencod/src/explicit_seq.c create mode 100644 lencod/src/filehandle.c create mode 100644 lencod/src/fmo.c create mode 100644 lencod/src/get_block_otf.c create mode 100644 lencod/src/header.c create mode 100644 lencod/src/image.c create mode 100644 lencod/src/image_mp.c create mode 100644 lencod/src/img_chroma.c create mode 100644 lencod/src/img_dist_ms_ssim.c create mode 100644 lencod/src/img_dist_snr.c create mode 100644 lencod/src/img_dist_ssim.c create mode 100644 lencod/src/img_distortion.c create mode 100644 lencod/src/img_luma.c create mode 100644 lencod/src/intra16x16.c create mode 100644 lencod/src/intra4x4.c create mode 100644 lencod/src/intra8x8.c create mode 100644 lencod/src/intra_chroma.c create mode 100644 lencod/src/intrarefresh.c create mode 100644 lencod/src/lambda.c create mode 100644 lencod/src/leaky_bucket.c create mode 100644 lencod/src/lencod.c create mode 100644 lencod/src/list_reorder.c create mode 100644 lencod/src/lln_mc_prediction.c create mode 100644 lencod/src/loopFilter.c create mode 100644 lencod/src/loop_filter_mbaff.c create mode 100644 lencod/src/loop_filter_normal.c create mode 100644 lencod/src/macroblock.c create mode 100644 lencod/src/macroblock_P444.c create mode 100644 lencod/src/mb_access.c create mode 100644 lencod/src/mbuffer.c create mode 100644 lencod/src/mc_prediction.c create mode 100644 lencod/src/mc_prediction_otf.c create mode 100644 lencod/src/md_common.c create mode 100644 lencod/src/md_distortion.c create mode 100644 lencod/src/md_high.c create mode 100644 lencod/src/md_highfast.c create mode 100644 lencod/src/md_highloss.c create mode 100644 lencod/src/md_low.c create mode 100644 lencod/src/me_distortion.c create mode 100644 lencod/src/me_distortion_otf.c create mode 100644 lencod/src/me_epzs.c create mode 100644 lencod/src/me_epzs_common.c create mode 100644 lencod/src/me_epzs_int.c create mode 100644 lencod/src/me_epzs_sub.c create mode 100644 lencod/src/me_fullfast.c create mode 100644 lencod/src/me_fullfast_otf.c create mode 100644 lencod/src/me_fullsearch.c create mode 100644 lencod/src/me_umhex.c create mode 100644 lencod/src/me_umhexsmp.c create mode 100644 lencod/src/mmco.c create mode 100644 lencod/src/mode_decision.c create mode 100644 lencod/src/mode_decision_P8x8.c create mode 100644 lencod/src/mv_direct.c create mode 100644 lencod/src/mv_search.c create mode 100644 lencod/src/nal.c create mode 100644 lencod/src/nalu.c create mode 100644 lencod/src/output.c create mode 100644 lencod/src/parset.c create mode 100644 lencod/src/pred_struct.c create mode 100644 lencod/src/q_around.c create mode 100644 lencod/src/q_matrix.c create mode 100644 lencod/src/q_offsets.c create mode 100644 lencod/src/quant4x4.c create mode 100644 lencod/src/quant4x4_2step.c create mode 100644 lencod/src/quant4x4_around.c create mode 100644 lencod/src/quant4x4_normal.c create mode 100644 lencod/src/quant4x4_trellis.c create mode 100644 lencod/src/quant8x8.c create mode 100644 lencod/src/quant8x8_around.c create mode 100644 lencod/src/quant8x8_normal.c create mode 100644 lencod/src/quant8x8_trellis.c create mode 100644 lencod/src/quantChroma.c create mode 100644 lencod/src/quantChroma_2step.c create mode 100644 lencod/src/quantChroma_around.c create mode 100644 lencod/src/quantChroma_normal.c create mode 100644 lencod/src/quantChroma_trellis.c create mode 100644 lencod/src/ratectl.c create mode 100644 lencod/src/rc_quadratic.c create mode 100644 lencod/src/rd_intra_jm.c create mode 100644 lencod/src/rd_intra_jm444.c create mode 100644 lencod/src/rd_intra_jm_low.c create mode 100644 lencod/src/rdopt.c create mode 100644 lencod/src/rdopt_coding_state.c create mode 100644 lencod/src/rdoq.c create mode 100644 lencod/src/rdoq_cabac.c create mode 100644 lencod/src/rdoq_cavlc.c create mode 100644 lencod/src/rdpicdecision.c create mode 100644 lencod/src/report.c create mode 100644 lencod/src/rtp.c create mode 100644 lencod/src/sei.c create mode 100644 lencod/src/slice.c create mode 100644 lencod/src/symbol.c create mode 100644 lencod/src/transcode_enc.c create mode 100644 lencod/src/transform8x8.c create mode 100644 lencod/src/transform8x8_H444.c create mode 100644 lencod/src/vlc.c create mode 100644 lencod/src/weighted_prediction.c create mode 100644 lencod/src/wp_lms.c create mode 100644 lencod/src/wp_mciter.c create mode 100644 lencod/src/wp_mcprec.c create mode 100644 lencod_vc10.vcxproj create mode 100644 lencod_vc10.vcxproj.filters create mode 100644 lencod_vc10.vcxproj.user create mode 100644 lencod_vc7.vcproj create mode 100644 lencod_vc8.vcproj create mode 100644 lencod_vc9.vcproj create mode 100644 rtp_loss/Makefile create mode 100644 rtp_loss/ReadMe.txt create mode 100644 rtp_loss/rtp_loss.cpp create mode 100644 rtp_loss/rtp_loss.h create mode 100644 rtp_loss/rtp_loss_vc10.vcxproj create mode 100644 rtp_loss/rtp_loss_vc10.vcxproj.user create mode 100644 rtp_loss/rtp_loss_vc7.vcproj create mode 100644 rtp_loss/rtp_loss_vc8.vcproj create mode 100644 rtp_loss/rtp_loss_vc9.vcproj create mode 100644 rtpdump/Makefile create mode 100644 rtpdump/ReadMe.txt create mode 100644 rtpdump/rtpdump.cpp create mode 100644 rtpdump/rtpdump_vc10.vcxproj create mode 100644 rtpdump/rtpdump_vc10.vcxproj.user create mode 100644 rtpdump/rtpdump_vc7.vcproj create mode 100644 rtpdump/rtpdump_vc8.vcproj create mode 100644 rtpdump/rtpdump_vc9.vcproj create mode 100644 unixprep.sh diff --git a/CHANGES.TXT b/CHANGES.TXT new file mode 100644 index 0000000..2e6d934 --- /dev/null +++ b/CHANGES.TXT @@ -0,0 +1,1562 @@ +Changes in Version JM 18.3 (since JM 18.2) +------------------------------------------ +- encoder: allow coding with the same GOP/reference picture settings like HM5 (BiL) +- add HM-like example configurations (BiL) + + +Changes in Version JM 18.2 (since JM 18.1) +------------------------------------------ +- encoder: fix MVC interlace encoder/decoder mismatch (AT/YHe/PeP) +- encoder: reference picture list performance fixes (AT/YHe) +- decoder: fix deblocking filter strength for SP frames (KS/AT) + + +Changes in Version JM 18.1 (since JM 18.0) +------------------------------------------ +- encoder/decoder: Direct Mode updates (YHe/KS) +- encoder/decoder: MVC (Stereo High Profile) Interlace Bug fixes (PeP/YHe) +- encoder: Coding structure updates and bug fixes (AL/AT) +- encoder: On the Fly Interpolation for ME/MC (JlT/AL/AT) +- encoder/decoder: Architecture and various software cleanups and naming conventions (KS/AT) +- encoder/decoder: DPB code updates (AT/PeP) +- decoder: Predictive High 444 bug fixes (KS/AT/JjC/JGG) + +Changes in Version JM 18.0 (since JM 17.2) +------------------------------------------ +- MVC decoded picture buffer separation (YHe/AT) +- move MVC parameters into a separate config file (KS) +- add MS Visual Studio 2010 work spaces (AT/KS) +- bug fixes (see bug tracker) + +Changes in Version JM 17.2 (since JM 17.1) +------------------------------------------ +- encoder: fix memory leak for picture based RD-decision (YY) +- rtpdump: fix build in old workspaces (KS) + +Changes in Version JM 17.1 (since JM 17.0) +------------------------------------------ +- decoder: fix delta_pic_order_cnt[0] in IDR frames (KS, bug #57) +- decoder: Direct mode fixes (YHE) +- encoder/decoder: various cleanups and bug fixes (AT/YHE) +- encoder/decoder: rewrite of deblocking functions (AT) +- decoder : rewrite of intra prediction functions (AT) +- encoder/decoder: MVC bug fixes (PeP/AL) +- encoder/decoder: Frame Packing SEI Message (AL) +- decoder: Minor CABAC updates (AT) +- encoder/decoder: Update work spaces for VS 2008 and XCode with x64 and OPENMP support (KS) +- encoder/decoder: Update Makefiles for OPENMP support (KS) +- encoder: avoid crashes by prohibiting incompatible tool combinations (KS/YHE) + +Changes in Version JM 17.0 (since JM 16.2) +------------------------------------------ +- encoder: TIFF input support (LZL) +- encoder: Reorganization/Generalization of RDPictureDecision (YY) +- encoder: Weighted Prediction bug fixes (YY) +- encoder: Hierarchical Coding structure bug fixes (AL) +- encoder: Architecture changes/bug fixes for error resilient video coding (ZC, PeP) +- encoder/decoder: Codec architecture reorganization/structure renaming (AT, AL, CV, PeP, YHe) +- encoder/decoder: lossless coding bug fixes to align code with the spec, and various encoder/decoder mismatch bug fixes(LZ/YY) +- encoder/decoder: initial MVC extension, i.e. stereo high profile, support (CL/SWi/TN) +- encoder/decoder: various architectural improvements/bug fixes for stereo high profile support (AT/AL/PeP/YHe) +- encoder/decoder: deblocking filter reorganization/rewrite (AT/YHe/CV) +- decoder: Multislice handling support (YHe) +- encoder/decoder: motion information handling reorganization (AT/YHe) +- encoder/decoder: direct mode handling reorganization/rewrite (YHe/AT) +- encoder/decoder: 4:2:2/4:4:4 profile support bug fixes (YHe) +- encoder/decoder: Interlace handling bug fixes (YHe, AT) +- encoder/decoder: intra prediction process rewrite (AT, CV) +- decoder: Motion Compensation operation rewrite (CV/AT/YHe) + +Changes in Version JM 16.2 (since JM 16.1) +------------------------------------------ +- encoder: changes necessary for low delay anchor generation (DHo) + see doc/jm16.2_changes.doc for details +- encoder: update encoder.cfg for high performance based on VCEG common coding condition (KS) + +Changes in Version JM 16.1 (since JM 16.0) +------------------------------------------ +- encoder: check slice group parameter size for slice_group_map_type equal to 2 (KS) +- encoder/decoder: SP/SI bug fixes (YL/AL) +- encoder: Integer distortion cost calculation (YHe/AT) +- encoder: Updated and Improved Sequence/GOP design architecture (AL) +- encoder/decoder: Various cleanups/bugfixes (AT) +- encoder/decoder: Deblocking speedups (AT) +- encoder/decoder: add version information command line switches (KS) +- encoder: NAL unit priority bug fixes (KSw) +- encoder: RDOQ bug fixes (YY) +- encoder: Motion estimation and mode decision bug fixes (YHe/AT) +- decoder: fox cropping for IMGPEL equal to 0 (KS) + +Changes in Version JM 16.0 (since JM 15.1) +------------------------------------------ +- encoder: Early termination in various distortion computation functions (AT) +- encoder/decoder: Various speed optimizations/restructuring/cleanups (AT) +- encoder/decoder: removal of almost all global variables (AT) +- encoder/decoder: Severe restructuring and merging of Various encoder and decoder functions in lcommon (AT) +- decoder: Deblocking bug fix in the presence of errors (TB/AT) +- encoder: Rate Control Improvements/Reentrancy (AL) +- encoder: RDOQ/Mode decision bug fixes (YY) +- encoder: Motion Estimation/Mode decision bug fixes (YHe) +- encoder: Motion Estimation reorganization/cleanups (AT) +- encoder/decoder: doxygen cleanups (YHe/AT) +- encoder/decoder: add workspaces for Visual Studio 2009 (Windows) and Xcode (MacOS) + + +Changes in Version JM 15.1 (since JM 15.0) +------------------------------------------ +- encoder/decoder: Redundant picture bug fixes (KS) +- encoder: fix config limit type for IntraProfile (KS, bug id #153) +- decoder: fix copy and paste bug in pps_is_equal() (KS, bug id #157) +- decoder: cleanups to intra prediction functions (AT) +- decoder: 4:4:4 bug fix (AT) +- encoder/decoder: Various bug fixes/cleanups (AT/KS) + +Changes in Version JM 15.0 (since JM 14.2) +------------------------------------------ +- encoder: Various bug fixes relating to MBAFF and High 444P profile (EM) +- encoder: Adaptive Rounding reorganization and bug fixes(EM) +- encoder: Motion Estimation reorganization (AT) +- encoder: Various bugfixes related to 8x8 transform and submacroblock partitions (AT) +- encoder: Extension to support VLC codes > 32 bits (AL) +- encoder: RC bug fixes (ASe) +- encoder: SEI/AU support & cleanups (AL) +- encoder: Various bug fixes and cleanups, including bipred ME (AT) +- encoder: Weighted Prediction offset computation using Motion Compensation (PC, MK, YY) +- encoder: support of explicit sequence information functionality (AT/KS) +- encoder: support of separate & interleaved raw video files (AT) +- encoder: don't allow GenerateMultiplePPS in baseline profile (KS) +- encoder: encoder support of some ANNEX A level limits (AT/KS) +- encoder/decoder: Lossless encoding bug fixes (AT) +- encoder/decoder: memory alloc/free speedups (AT) +- encoder/decoder: VLC speedups (JaB/AT) +- encoder/decoder: don't access uninitialized memory in cabac.c (PL/KS) +- encoder/decoder: various cleanups and bug fixes (KS/AT) +- decoder: Support for 64 bit bitstream I/O (AT/KS) +- decoder: Severe code reorganization and various bug fixes, some relating to High 444P (AT) + +Changes in Version JM 14.2 (since JM 14.1) +------------------------------------------ +- encoder: fix cabac_zero_word calculation (KS) +- encoder: bipredictive ME bug fixes (EM/AT) +- encoder: MBAFF qp bug fix (AT) +- encoder: Statistics collection rewrite (AT) +- encoder: RDOQ CAVLC support & improvements (PC/YY/MK/LL/AT) +- encoder: fix range of DFDisable* parameters (KS) +- decoder: Fix copy and paste error in sei.c (KS) +- decoder: Fix syntax of full frame freeze SEI message (KS) +- encoder/decoder: convert time measurement system calls to be more consistent and don't use "obsolete" functions (KS) + +Changes in Version JM 14.1 (since JM 14.0) +------------------------------------------ +- encoder: fixes in lossless coding (KHH) +- encoder: Improved MC precision using Reordering & Weighed Prediction (AL, AT) +- encoder: Weighted Prediction Extensions (AT, AL) +- encoder: MS-SSIM distortion computation (PeP) +- encoder: Rewrite of Error Resilient RDO (PeP/AT) +- encoder: SSIM distortion computation (ZHL/WK/AT) +- encoder: RGB color space distortion computation (WK/AT) +- encoder: Distortion/Statistics functionality cleanups (AT/WK) +- encoder: RDOQ and Quantization cleanups (LL/AT) +- encoder: RC cleanups (AT) +- encoder: Adaptive Rounding Support for Low Complexity RDO (EM) +- encoder: Multipass RD coding bug fix for PicAFF (AL) +- decoder: Motion vector limit checking (LlL) +- encoder/decoder: Various code optimizations (AT) +- encoder/decoder: Various cleanups /reorganizations (AT) +- decoder: SP/SI slice decoder fixes (AT) +- encoder: fix 4:0:0 encoder bit depth initialization (KS) +- encoder: use SPS id parameter in GenerateSequenceParameterSet (KS) +- encoder: fix number of bits calculation for pic aff with slices/DP (CS) + + +Changes in Version JM 14.0 (since JM 13.2) +------------------------------------------ +- encoder: Bipredictive ME bug fixes (AT) +- encoder: Intra mode decision reorganization (LL/AT) +- encoder: Quantization/transform process reorganization (AT) +- encoder: Trellis based quantization (VCEG-AH21) (PC/YY/MK/LL/AT) +- encoder: WP Cleanups (AT) +- encoder: Lossless Coding Cleanups / Bug fixes (AT) +- encoder: Profile/Level Conformance code cleanups (AT) +- encoder: fix trace file for data partitioning (KS) +- encoder: fix encoding for picture aff (KS) +- encoder: fix memory allocations with num_ref_frames=0 (KS) +- encoder: fix rate control config error message (AL) +- encoder: fix 4:2:2 chroma transform (KS) +- encoder: fix 4:2:2 chroma scaling matrices (SSe) +- encoder: improve profile restrictions (KS) +- decoder: fix 4:2:2 decoding (SWi) +- decoder: allow intra-only deblocking filter configuration in decoder.cfg (KS) +- decoder: add quantizer initialization for data partitioning (PL) +- encoder/decoder: memory assignment reorganization (AT) +- encoder/decoder: deblocking cleanups (AT) + + +Changes in Version JM 13.2 (since JM 13.1) +------------------------------------------ +- encoder: 4:4:4 chroma mismatch fix (4x4 intra) (KS) +- encoder: 4:2:2 chroma mismatch fix (THi) +- encoder: fixes for negative QP (AT) +- encoder: fix trace file and average qp calculation for slices with fixed byte size (AL) +- encoder/decoder: various 4:4:4 fixes (HY) +- decoder: fix temporal direct mode (KS) +- decoder: implement I_PCM for more than 8bpp (KS) + + +Changes in Version JM 13.1 (since JM 13.0) +------------------------------------------ +- encoder: 4:4:4 performance fix (4x4 intra) (KS) +- encoder: always use ChromaMEEnable for 4:4:4 common mode (KS) +- encoder: 4:2:2 transform fix (AT) +- decoder: fix slice_group_id allocation (KS) +- encoder/decoder: fix inline functions for compilers without c99 support (KS) +- encoder/decoder: makefile updates (LW/KS) +- encoder/decoder: 4:0:0 fixes (KS) +- encoder/decoder: further cleanups (AT/KS) + + +Changes in Version JM 13.0 (since JM 12.4) +------------------------------------------ +Professional Profiles: +- encoder/decoder: 4:4:4 combined color coding mode +- encoder/decoder: lossless coding + +- encoder/decoder: remove MS Visual Studio 6 workspaces (KS) +- encoder/decoder: various optimizations (AT/AL) +- encoder/decoder: fix mbuffer.c copy and paste error (KS) +- decoder: allow loss of single partitions (DA/KS) + Note: some loss pattern will require usage of RTP file. +- decoder: indicate wrong emulation prevention (ZH) +- decoder: make memory allocation for slice_group_id dynamic (KS) + +- add simple tool for pseudo-random RTP packet loss (KS) + + +Changes in Version JM 12.4 (since JM 12.3) +------------------------------------------ +Professional Profiles: +- encoder/decoder: 4:4:4 independent mode quantization matrix bug fix (SSe) +- encoder: 4:4:4 independent config file update (SSe) + + +Changes in Version JM 12.3 (since JM 12.2) +------------------------------------------ +Professional Profiles: +- encoder/decoder: 4:4:4 independent mode (SSe) + +- encoder/decoder: add top level Makefile (LW) +- encoder: initialize lprc->AveWb (AL) +- encoder: restrict pel values in simulated decoder (CS) +- encoder: closed IDR GOP support (AT) +- encoder: delayed IDR GOP support (AT) +- encoder: rate control extension for adaptive rounding (AT) +- encoder: chroma ME bug fix (AT) +- encoder: RC bug fixes (AL) +- encoder: top/bottom field slice type decoder output consideration (AT) +- encoder: adjustments to frame_num consideration (AL/AT) +- encoder: removal of secondGOP concept (AT) + + +Changes in Version JM 12.2 (since JM 12.1) +------------------------------------------ + +Professional Profiles: +- encoder: 420/422 intra only coding (SWi) +- encoder/decoder: post-filter hint SEI (SWi) +- encoder/decoder: tone-mapping SEI (ASe) + +- encoder/decoder: cleanup and speed improvements (AT/AL) +- encoder/decoder: deblocking filter speedup (AT) +- encoder/decoder: CAVLC entropy coder speedup (AT) +- encoder/decoder: CABAC arithmetical coder speedup [JVT-U084] (GM/DM/KS) +- encoder: VUI support (AL) +- encoder: further rate control fixes (AL) +- encoder: add error checks for slice group config files (KS) +- encoder: fix I_PCM encoding with CABAC (KS) +- decoder: Motion Compensation speedup (AT) + + +Changes in Version JM 12.1 (since JM 12.0) +------------------------------------------ +- encoder: fix example configuration files (KS/AT) +- encoder: make direct_8x8_inference_flag=1 default (KS) +- decoder: fix compilation with trace enabled (KS) +- encoder/decoder: fix for shift_right_round functions on 64-bit machines (AT) +- encoder/decoder: predict_nnz functions (YDh) +- encoder/decoder: cleanups (KS/AT/AL) + + +Changes in Version JM 12.0 (since JM 11.0) +------------------------------------------ +- encoder: Context Adaptive Lagrange Multiplier [JVT-T046] (JZ) +- encoder: Severe Rate Control reorganization and debugging. Support for interlace coding modes and hierarchical/multi-pass encoding (AL/AT) +- encoder: Lagrangian Cleanups based on "layered" motion estimation (YD/AT) +- encoder: reorganization/unification of motion estimation and compensation functions (AL/AT) +- encoder: Revised Luma/Chroma interpolation (AL/AT) +- encoder: Chroma motion estimation consideration (AL/AT) +- encoder: Separation of intra chroma mode decision and RDO (AL/AT) +- encoder: split up macroblock mode decision (AT) +- encoder: Bug correction relating to 64bit type statistics output (AT) +- encoder: Simple "text" SEI message generation and basic support for VUI parameters (to be extended) (AT) +- encoder: reorganization of adaptive quantization and support of 4:2:2 and 4:4:4 formats (AT) +- encoder: resolved 8x8 intra prediction + MB-AFF bug (AT) +- encoder: resolved illegal WP value vs Eq.8-294 bug (AT) +- encoder: subpel EPZS bug resolutions and addition of bipredictive subpel EPZS (AT) +- encoder: resolve bug with IDR pictures + RDPictureDecision (AT) +- encoder: fix SP/SI memory leaks (AT) +- encoder: bidirectional search for simplified fast ME (BY) +- encoder: fix start code prefix length (SN) +- decoder: update of decoder input parameters (AT) +- encoder: fix incorrect usage of 8x8 transform with direct mode and non-rd-opt coding (DH) +- encoder: fix wrong pointer dereferences (KS/SH) +- encoder: fix SP/SI file i/o issue (ES) +- encoder: allocate less stream buffer memory for slice modes 1 and 2 (KS) +- encoder: fix bit statistics for slice mode 2 (enricomasala) +- decoder: trace file format cleanup (KS) +- decoder: fix psnr calculation for field with IDR (SN) +- decoder: error concealment - conceal intra blocks from decoded blocks first (KS) +- decoder: decoder cleanups and optimizations (AT/KS) +- encoder/decoder: various small code cleanups (AT) +- encoder/decoder: correction of fix for predict_nnz with constrained intra and data partitioning (CK/KS) +- encoder/decoder: fix memory leaks mostly in error paths (KS) +- encoder/decoder: add MS Visual Studio 2005 project files (KS) + + +Changes in Version JM 11.0 (since JM 10.2) +------------------------------------------ +- encoder: FME updated [JVT-R085 and JVT-Q088] (XX) +- encoder: I_PCM support for CABAC (SL) +- encoder: fix crash with large number of frames (AT) +- encoder: disable writing if no yuv output file is specified (AT) +- encoder: disallow POC reorder with field coding (AT) +- encoder: rename Pyramid->Hierarchy (AT/KS) +- encoder: remove remainings of former redundant slice implementation (KS) +- encoder: fix IPCM encoding with rate control (AT/HZ) +- encoder: Intra 8x8 prediction mode write cleanup (KS) +- encoder: Avoid I_PCM trace data during rdopt (KS) +- encoder: LA-RDO bug fix (CZ) +- encoder: disallow DP+CABAC (KS) +- encoder: SP pictures cannot be IDR (KS) +- encoder: fix overwritten line buffer in fast ME (KS) +- encoder: allow picture frame/field encoding of non-reference pictures when DPB is full of reference pictures (AT) +- encoder: enable EPZSSubPelME (AT) +- decoder: start decoding after recovery point SEI (BL) [JVT-R017] +- decoder: remove size restriction when parsing reserved SEI (KS/MW) +- decoder: fix weighted prediction initializations (AT) +- decoder: fix Motion-constrained slice group set SEI message reading (AT) +- decoder: fix dec_picture->slice_id memory leak (KS) +- decoder: fix SEI MMCO list free (RB) +- decoder: make vlc tables static const (SF) +- decoder: fix for poc_order_cnt_type=2 (JXi) +- decoder: fix field combine/split for different reference picture size per slice (KS/AT) +- encoder/decoder: Redundant picture for error resilience [JVT-R058] (CZ) +- encoder/decoder: don't write empty DP NALUs and handle this at the decoder (KS) +- encoder/decoder: fix predict_nnz for constrained intra and data partitioning (SMy/KS) +- encoder/decoder: fix cabac context initilization (DM) +- encoder/decoder: fix various chroma bitdepth related issues (KS/YC) + + +Changes in Version JM 10.2 (since JM 10.1) +------------------------------------------ +- encoder: fast subpixel motion estimation for EPZS [JVT-Q079] (AT) +- encoder: SI picture implementation (ES) +- encoder: lossless coding fixes (SSu) +- encoder: add warning for bit/mb limit (AT) +- encoder: enable I_PCM (for VLC coding) by default (KS) +- encoder: fix non-rd-optimized mode decision (AT) +- encoder: fix memory allocation in search module (TP) +- encoder: fix bitrate output (PP) +- encoder: fix data partitioning with constrained intra prediction (KS) +- encoder: fix bidirectional prediction with RDOpt=2 (AT) +- encoder: fix MBAFF with constrained intra prediction (KS) +- encoder: fix MBAFF with slice mode 2 and restrict slice mode 1 (KS) +- decoder: SI/SP picture updates (ES) +- decoder: add check for QP range (MT) +- decoder: fix/add SEI parsing: pan/scan, timing, stereo, deblocking filter, film grain (MT) +- decoder: fix dummy croma component output for bit depth > 8 (KS) +- decoder: work around problems in error concealment (KS) +- decoder: fix idr psnr output (SB) +- encoder/decoder: update RTP format (SW) + + +Changes in Version JM 10.1 (since JM 10.0) +------------------------------------------ +- encoder/decoder: apply chroma motion vector adjustment only for 4:2:0 (SZ/KS) +- encoder: write vlc run if last mb is skipped () + + +Changes in Version JM 10.0 (since JM 9.8) +----------------------------------------- +- encoder: simplified UMHexagonS fast motion estimation [JVT-P021] (XY) +- encoder: EPZS fast motion estimation [JVT-P026] (AT) +- encoder: fix reference picture reordering problem (AT) +- encoder: disallow (log2_max_frame_num_minus4 == 0 && input->num_ref_frames == 16) (AT) +- decoder: error concealment for lost pictures [JVT-P072] (SB) +- decoder: fix long-term reference picture reordering problem (KS) +- decoder: allow differing nal_ref_idc (!=0) in same AU (JSa) +- fix example config files (KS) + + +Changes for Version JM 9.8 to JM 9.7 +------------------------------------ +- encoder/decoder: fix monochrome pred_weight_table syntax (MT) +- encoder/decoder: fix cropping_flag usage (KS) +- encoder: fix frame_num for reference B pictures (AT) + + +Changes for Version JM 9.7 to JM 9.6 +------------------------------------ +- encoder: revert zero motion vector change in low complexity mode (YH) +- encoder: fix 4:2:2 encoding (KS) +- encoder: fix mb_qp_delta CABAC context selection (KS) +- encoder: weighted prediction fix +- encoder: fix frame_poc setting (AS) +- decoder: fix direct mode rare case problem (AS) +- decoder: fix pic_struct parsing in timing SEI +- decoder: fix parsing of filler data SEI +- encoder/decoder: fix deblocking filter strength calculation for negative POC (AS/KS) +- encoder/decoder: fix frame_poc setting (AS) +- encoder/decoder: fix SE_TRANSFORM_SIZE usage (KS) +- encoder: updates to explicit hierarchical coding and picture coding structures (AT) +- encoder: update/enhance control of log2_max_pic_order_cnt_lsb_minus4 and log2_max_frame_num_minus4 (AT) +- encoder: addition of computation of Sequence Level PSNR statistics (AT) +- encoder: fix frame_num setting for pyramid coding (AT) +- encoder/decoder: fix invalid line in deblocking filter (PL) + + +Changes for Version JM 9.6 to JM 9.5 +------------------------------------ +- encoder: FME fixes (WF) +- encoder: fixes for I_PCM and encoder output (SN) +- encoder: fix rounding for CABAC stuffing +- encoder: lossless mode fixes (WK) +- encoder: increase maximum slice size (WK/KS) +- add platform specific notes to readme.txt + + +Changes for Version JM 9.5 to JM 9.4 +------------------------------------ +- encoder: open GOP support (AT) +- encoder: I_PCM encoding/decoding for CAVLC (AT) +- encoder: I_PCM decoding for non-4:2:0 and CAVLC >8bit (AT) +- encoder: fix encoder/decoder mismatch for non-rd-opt coding (AT) +- encoder: fix crash with bi-predictive search (AT) +- encoder: further speed optimization (AT) +- encoder: fix mismatch in low complexity mode (YH/AT) +- encoder: fix zero motion vector in low complexity mode (YH) +- encoder: fix 8:9:9 bit support (WK) +- encoder: FME improvements (WF) +- encoder/decoder: fix RCT Cb/Cr order (WK) + + +Changes for Version JM 9.4 to JM 9.3 +------------------------------------ +- encoder: fast mode implementation (BJ) +- encoder: FME update (WF) +- encoder: adaptive quantization rounding offset (GS/SSu/AT) +- encoder: restructure encode_one_macroblock (AT) +- encoder: weighted prediction fix (AT) +- encoder: fix reported bit rate with LastFrameNumber parameter (AT) +- encoder: fix cost initialization for Transform8x8Mode=2 (SeS) +- encoder: restrict motion vector size (AT) +- encoder: fix rate control/non-rd-opt mismatch (AT) +- encoder: cleanups (AT) +- encoder: remove memory leak and clean up PPS usage (KS) +- encoder: fix CABAC zero word calculation (KS) +- decoder: fix POC mode 0 calculation (XI) +- decoder: fix timing SEI parsing (IP) +- decoder: fix dangling field output (KS) +- decoder: fix VLC trace output (MT) +- encoder/decoder: large performance and memory usage improvements (AT/KS) +- encoder/decoder: fix CABAC CBP_block_bit (VG) +- encoder/decoder: clean up and fix usage of skip_flag (KS/AM) +- encoder/decoder: implement lossless coding for non 4:4:4 cases (SSu) + + +Changes for Version JM 9.3 to JM 9.2 +------------------------------------ +- encoder: Bi predictive ME (AT) +- encoder: encoder parameter tuning (AT) +- encoder: fix allocation in error concealment (SM) +- encoder: fix allocation in pyramid code (AT) +- encoder: fix qp_const_422 assignment (SKa) +- encoder: fix padding for automatically added borders (SKa) +- encoder: support multiple PPS (AT) +- encoder: picture RD-decision (AT) +- encoder: rate-control fix (SM) +- encoder: restrict num_ref_frames to MaxDpbSize (ZH/KS) +- encoder: allocate rate control memory depending on frame size (KS) +- decoder: open trace file only once (AT) +- decoder: remove duplicate initialization of error states (MA) +- decoder: fix timing SEI parsing (PW/KS) +- decoder: fix CABAC SI context selection (DM/KS) +- encoder/decoder: fix neighboring of TotalCoeff in CAVLC (KS/WC) +- encoder/decoder: fix PSNR calculation if cropping is enabled (KS) + + +Changes for Version JM 9.2 to JM 9.1 +------------------------------------ +- encoder: fix possible integer overflow in quantization offsets (SN/AT) +- encoder: fix I_PCM trace (SN) +- encoder: restructure code (KS) +- unixprep.sh: also convert config files (KS) + + +Changes for Version JM 9.1 to JM 9.0 +------------------------------------ +- encoder: RCT: Enabling inter mode selection (WK) +- encoder: RCT: Correcting I16 mode selection (WK) +- encoder: fix YUV header seek problem (PL) +- encoder: reduce memory usage (KS) +- encoder: remove assert in RC (KS) +- encoder: fix qpBSoffset usage (AT) +- encoder: fix stored B-pictures (KS) +- encoder: fix qp change (AT) +- encoder: fix direct mode selection (KS) +- encoder: fix wrong memory allocation in FastME code (KS) +- encoder: fix IDR encoding (LP/SKa/AT) +- encoder: fix QMatrix config file reading (KS) +- encoder: automatically add borders for picture sizes that are not multiple of 16 (KS) +- encoder: default config parameter initialization (AT) +- encoder: add checks for minimum and maximun values for each config parameter (AT) +- encoder: support for explicit lagrangian parameters (AT) +- encoder: support for explicit rounding offset parameters (AT) +- decoder: allow 4:2:0 file format for monochrome streams (LW/KS) +- decoder: fix VLC coeff reading (WZ) +- decoder: fix second chroma qp offset (TS/KS) +- decoder: fix VUI syntax (SK) +- decoder: new command line interface (AT) +- encoder/decoder: RCT: Correcting lossless coding/decoding (WK) +- encoder/decoder: fix file reading/writing for non-Intel architectures (KS) +- encoder/decoder: Fix YUV 4:0:0 output (SG) +- encoder/decoder: Fix prediction weight calculations (AT) + + +Changes for Version JM 9.0 to JM 8.6 +------------------------------------ + +New Features: +- import FRExt branch (KS) +- encoder: pyramid encoding structure (AT) +- encoder/decoder: YUV 4:0:0 support (VG) +- encoder: thresholding control (AT) + +Fixes/Upates: +- encoder: clean up includes (KS) +- encoder: increase maximum file name length (KS) +- encoder: fix failed merge of temporal direct fix (LP) +- encoder: fix RCT enc/dec mismatch (WK) +- encoder: fix for CAVLC neighbouring total coeff (for RDopt) (SN) +- encoder: fix for CAVLC level limit for small QP (SN) +- encoder: fix memory leaks (CS/KS) +- encoder: disallow RDOptimazation=2 (KS) +- encoder: improve parameter names(AT) +- encoder: fix rate control diff calculation (SM) +- decoder: fix QP output (AT) +- decoder: fix paired field output for increased bit depth (KS) +- decoder: fix img->quad allocation (KS) +- decoder: fix RGB file output (KS) +- decoder: don't crash on PicTiming SEI when no SPS is active (KS) +- encoder/decoder: fix gcc compiler warnings (KS) +- encoder/decoder: fix inverse DC transform (WZ/KS) +- encoder/decoder: reduce memory usage by making imgpel "unsinged short" (KS) + + +Changes for Version JM 8.6 to JM 8.5 +------------------------------------ +- encoder: fix command line checking order (KS) +- encoder: fix coded_frame flag (KS) +- encoder: fix skip mode mb pair problem (KS) +- decoder: fix macroblock counter (KS) +- decoder: fix output field pairing (KS) +- decoder: fix POC previous frame num on MMCO=5(BH) +- decoder: fix max num ref frames in poc cycle +- decoder: allow Annex B leading_zero_8bits and trailing_zero_8bits (ZH) +- decoder: fix SI decoding (MWa) +- encoder/decoder: fix dpb size (KS) +- encoder/decoder: hopefully work around VS compiler optimization bug (KS) +- encoder/decoder: remove a number of gcc warnings (KS) + + +Changes for Version JM 8.5 to JM 8.4 +------------------------------------ +- encoder: fix wrong direct mode selection for non-rd-optimized encoding (KS) +- decoder: fix RPLR (KS) +- encoder: fix rate-control for non-rd-optimized encoding (SM/KS) +- encoder: fix POC mode 1 for stored B pictures (KS) +- encoder: fix MB AFF encoder/decoder mismatch (KS) +- encoder: fix list 1 reference index selection (SN) +- encoder: fix IDR pictures and wrapping around frame_num (TK/KS) +- encoder: fix log2_max_frame_num_minus4 and log2_max_pic_order_cnt_lsb_minus4 (AT) +- encoder/decoder: align chroma sample interpolation to standard text (no real bug, only cleanup) (ZH/KS) + + +Changes for Version JM 8.4 to JM 8.3 +------------------------------------ +- encoder: fix encoder merge bug (AT) +- encoder: count parameter set bits (AT) +- decoder: spatial direct long-term support (KS) +- decoder: fix colocation problem (KS) +- decoder: fix SPS/PPS changes (KS) +- decoder: fix reordering with multiple slices (KS) +- decoder: allow paired output of unpaired fields (KS) +- decoder: fix POC on MMCO=5 (KS) +- decoder: fix picture unmarking problem (KS) +- decoder: SPS activation on buffering SEI (KS) +- decoder: weighted prediction typo (AT) +- decoder: fix CeilLog2 function (KS) + + +Changes for Version JM 8.3 to JM 8.2 +------------------------------------ +- encoder: fast motion estimation fixes und cleanup (WF) +- decoder: fix for SP decoding (ES) +- encoder/decoder: direct mode cleanup and fixes (AT) +- decoder: POC cleanup and fixes (AT) +- encoder: set log2_max_frame_num_minus4 dynamically (AT) +- decoder: fix dangling fields (KS) +- encoder/decoder: fix mismatch in B-pictures with RDOptimization=0 (KS) +- encoder/decoder: fix redundant_pic_cnt syntax element type (KS) +- encoder: fix num_slice_groups_minus1>0 bug (KS) +- decoder: fix field mmco bugs and (KS) +- decoder: fix initial reference picture list generation with long-term pictures (KS) +- decoder: fix new picture detection (KS) +- decoder: fix weighted prediction (KS/AS) +- encoder/decoder: fix max PPS id (KS) +- decoder: remove redundant variables (KS) +- decoder: re-enable non-normative error concealment for non mb-aff (KS/THi) +- decoder: several smaller fixes (KS) + + +Changes for Version JM 8.2 to JM 8.1a +------------------------------------- +- encoder: merge FMO (DW) +- encoder/decoder: add error/warning if DPB size is zero at specified level (KS) +- fix unixprep.sh script for Solaris /bin/sh (DD) + + +Changes for Version JM 8.1a to JM 8.1 +------------------------------------- +- encoder/decoder: fix bug in constrained intra prediction fix (KS) +- encoder/decoder: fix version number typo (KS) + + +Changes for Version JM 8.1 to JM 8.0 +------------------------------------ +- decoder: fix I_PCM decoding for field macroblocks (AS) +- encoder: rate control fix (SM) +- encoder/decoder: fix max DPB size for level 5.0 (KS) +- encoder/decoder: fix constrained intra prediction left neighbour (KS) +- decoder: fix crash on SPS activation in SEI (KS) +- encoder/decoder: documentation updates / remove encodeiff.h (KS) +- encoder: add basic profile checks (KS) + + +Changes for Version JM 8.0 to JM 7.6 +------------------------------------ +- encoder/decoder: SP filter strength (KS) +- decoder: fix decoder snr calculation for streams that contain idr pictures (AT) +- decoder: increase NAL unit buffer size (KS) +- encoder: further statistics fixes (AW) +- encoder: fix wrong LumaPrediction4x4 call (AW) +- encoder: fix reference index writing for MB AFF with one reference frame (AW) +- decoder: fix bottom field first decoding (KS) +- encoder: add profile and level idc to config file (KS) +- encoder/decoder: determine DPB size depending on level (KS) +- encoder/deocder: fix field pairing (KS) +- encoder/deocder: fix memory allocation for reference picture list (KS) +- encoder: decrease list0 size for b slices (KS) +- encoder: make FME a config file option (TK) +- encoder: add -h help option (AT) +- encoder/decoder: fix writing of unpaired field (ZY) +- decoder: fix currMB calculation in i_pcm reading (ZY) +- decoder: set ref_pic_num for each slice (KS/AS) +- decoder: disable deblocking filter P_SLICE simplifications (KS/AS) +- decoder: "hack fix" for direct mode decoding (AT) + +Changes for Version JM7.6 to JM 7.5c +------------------------------------ +- encoder/decoder: temporal direct loop index fix (RV/AT/KS) +- encoder: fix memory leak (AT) +- decoder: reset the wp_offset for the implicit mode (AT) +- encoder: temporary fix for slice mode 2, Won't work with FMO (PK) +- decoder: don't reset poc of other field (OD) +- decoder: fix bw_ref_idx (AT) +- encoder/decoder: fix long_term initialization (JK) +- encoder/decoder: fix pic_num / frame_num_wrap initialization for intra sclices (JK) +- encoder/decoder: datapartition fixed (ZL) +- encoder: fix stored B pictures (AT) +- encoder: small mbuffer.c fixes (AT) +- encoder/decoder: add Visual Studio .NET workspaces (KS) +- encoder: fix random intra blocks (TSh) +- encoder: fix statistics (KS) +- encoder: fix mb delta qp CABAC context selection (KS/AT) +- encoder: Rate control fixes (SM) +- encoder/decoder: fix initial reference picture list generation for long term pictures (RV) +- encoder/decoder: fix no_output_of_prior_pics_flag handling (KS) +- encoder: FAST_ME fixes (JX) + +Changes for Version JM7.5c to JM 7.5b +------------------------------------- +- encoder: add DirectInference parameter (AT) +- encoder: generate interlaced representation, only if interlaced encoding mode (AT/KS) +- encoder: fix picture aff psnr calculation (AT) +- encoder: weighted prediction fix (AT) +- decoder: small direct mode fix (AT) +- encoder/decoder: reference index fix in generation of field representation (AS) +- encoder/decoder: fix CurrPicNum for field calculation (KS) +- encoder: fix CABAC contexts for cbp and skip flag / mb aff (VG/KS) +- encoder: fix invalid usage of 8x8 temporal direct mode (KS) +- decoder: fix field psnr calculation (KS) +- encoder: don't try to output reconstructed sequence, if no file specified (AT) +- encoder: clean exit if input file reading fails (AT) +- encoder: initialize POC before initializing parameter sets (KS) +- encoder: fix POC mode 1 (KS) +- decoder: rename POC mode 1 variables to match text (KS) +- encoder/decoder: fix bottom field access in is_short_term_reference() + and is_long_term_reference() (ML) + +Changes for Version JM7.5b to JM 7.5a +------------------------------------- +Bug fixes +- encoder: b-frame performance fix (KS) +- encoder: CABAC context selection fix (KS) +- encoder: fix typo in rd-opt labda calculation (AT) +- encoder: weighted prediction updates (AT) +- encoder: fix wrong reference index if NumberReferenceFrames=1 (KS) +- encoder: add IDR picture selection parameter (AT) + +Changes for Version JM7.5a to JM 7.5 +------------------------------------ +Bug fixes +- encoder: fix direct mode coding (KS/AT) +- encoder: reenable old rd-opt labda calculation (KS/AT) + +Changes for Version JM7.5 to JM 7.4 +----------------------------------- +Bug fixes +- decoder: SEI parsing (JC) + +- encoder: merge deblocking filter from decoder (KS) +- encoder: merge dpb fixes from decoder (KS) + +- encoder: rewrite most of MB AFF handling, + support list_0 and list_1 properly, etc. (KS) + +- encoder/decoder: fix first_mb_in_slice for mb aff (KS) +- encoder/decoder: deblocking filter with multiple slices (KS) + + +Changes for Version JM7.4 to JM 7.3 +----------------------------------- +Bug fixes +- decoder: lt.ref. pic handling with sliding window (AF) +- decoder: fix CABAC parsing bug for skip_flag (KS) +- decoder: gaps in frame_num (DT) +- decoder: error message, if direct mode fails (BH) +- decoder: fix long term reference pictures (KS) +- decoder: deblocking filter for picture aff (PN) +- decoder: long term picture handling/MMCO (AF/KS) +- decoder: chroma QP offset (JS) +- decoder: frame cropping support (KS) +- decoder: deblocking filter fix (SY, others) + +New features: +- decoder: I_PCM macroblock mode for VLC (DW) +- decoder: I_PCM macroblock mode for CABAC (DW) +- encoder: fast motion estimation +- encoder: rate control + + +Changes for Version JM7.3 to JM 7.2 +----------------------------------- +Bug fixes +- encoder: DPB for field coding (SM) +- encoder: Spatial direct mode (SM) +- decoder: POC mode 2 with multiple slices (BH) +- decoder: data partitioning (has known problem in CAVLC/CABAC parsing when + constrained intra an dp are used) +- both : reference picture list generation +- both : doxygen documentation generation fixes (KS) + + +Changes for Encoder Version JM7.2 to JM 6.2 +-------------------------------------------- +Bug fixes +- DPB correlated things, such as motion search etc.(KS,SM) +- Direct mode(SM) + +Changes for Decoder Version JM 7.2 to JM 7.1 +-------------------------------------------- +Bug fixes +- deblocking filter reference pictures (LK) +- small POC fixes (KS) + +Cleanup +- remove JVT-F100 syntax support (KS) + +Changes for Decoder Version JM 7.1 to JM 7.0 +-------------------------------------------- +Bug fixes +- loop filter (KS) +- weighted prediction (KS) +- reference list generation (KS) +- 8x8 direct modes (AT) +- constrained intra prediction (KS) + + +Changes for Decoder Version JM 7.0 to JM 6.2 +-------------------------------------------- +Bug fixes +- POC updates (BH) +- CABAC dqant and red_idx_lx contexts (KS) +- small cleanups and other fixes + +Other +- removed common directory, put a copy of these file in each + encoder and decoder + + +Changes for Version JM 6.2 to JM 6.1b +-------------------------------------- +Note: This is a decoder only release. Encoding is broken. + +Bug fixes +- rewrote DPB (KS) +- rewrote most of MB AFF handling (KS) +- direct mode updates (AT) + +- many other cleanups and fixes (various poeple) + + +Changes for Version JM 6.1b to JM 6.1a +-------------------------------------- +Bug fixes +* FMO decoding (KS) +* fix motion vector predictor (HS/KS) +* intra prediction mode selection (KS) +* SPS Syntax update frame_height_in_mb -> pic_height_in_mb (KS) +* fix memory leak (KSa) + +Changes for Version JM 6.1a to JM 6.1 +------------------------------------- +Bug fixes +* VLC 0 sign bit (AJ) +* Correct rounding (AJ) +* Chroma intra pred mode order (AJ/KS) +* Loop filter chroma QP mapping (RaS) +* Fix memory leaks (KS) +* MB AFF fixes (HS) +* CABAC text alignment (HS) +* frame_num modulo (KS) +* intra pred allowed mode fixes (HS/KS) + +Cleanups +* remove strange postfilter arrays from decoder (KS) +* remove duplicte decoder frame types (_1 and _MULT) (KS) +* clean up vlc writing (KS) + + +Changes for Version JM 6.1 to JM 6.0a +------------------------------------- +New features +* SEI decoding reenabled and new messages added (KS) +* Weighted Prediction Updates (JB) +* POC updated (DT) +* CABAC slice initializations (HS) + +Bug fixes +* fix mb aff again (KS) + +Cleanups +* remove many gcc warnings (KS) +* remove unused code (KS) +* clean up VLC reading (KS) +* allocate less memory (YW) + + +Changes for Version JM 6.0a to JM 6.0 +------------------------------------- +New features +* chroma loop filter update (CG) + +Bug fixes +* fix overflowing buffer for large image formats (PW/KS) +* fix large memory leak (KS) +* slice coding (RaS) +* fix bit statistics (KS) +* call EBSPtoRBSP at the decoder (TS/KS) + +Cleanups +* remove some of the gcc warnings and old files (KS) + + +Changes for Version JM 6.0 to JM 5.0h-suehring +---------------------------------------------- +New features +* post-Awaji slice header and parameter sets (except POC) (KS) + +Removed features +* ABT (KS) + +Bug fixes +* AFF (picture and mb) (KS) +* Slice parsing with CAVLC (KS) +* CABAC byte alignment (KS) +* lots of stuff I can't remember in detail (KS) + +Cleanups +* lots of stuff I can't remember in detail (KS) + +Known problems +* slices have still encoder mismatch (looks like motion vector prediction) +* FMO not tested, but probably won't work +* SEI disabled (needs update to NAL units) +* weighted prediction implementation in JM 5.0g unfinished +* Error concealment disabled (needs new triggers) +* DP (???) +* I haven't merged any patches, I received since December + +Changes for Version JM 5.0h-suehring to JM 5.0g +----------------------------------------------- +New features +* Sequence and Picture Parameter Sets +* writes Annex B bytestream and RTP packets + + +Changes for Version JM 5.0g to JM 5.0f +-------------------------------------- +New features +* Changes for Weigthed Prediction (JB) + +Note: This version seems to have severe performance problems + +Changes for Version JM 5.0f to JM 5.0e +-------------------------------------- +New features +* Reducing Pixel Dependencies for Intra-Prediction (JVT-E111) (GrC) + + +Changes for Version JM 5.0e to JM 5.0d +-------------------------------------- +New features +* alternate scan for aff coding + + +Changes for Version JM 5.0d to JM 5.0c +-------------------------------------- +New features +* added POC - for implementation details see changes_detail.txt (BH) + +Bug Fixes +* reorder chroma intra pred modes aligned to draft (KS) + +Cleanups (no functional change) +* cleaner parameter transfer for CABAC stuffing words (FB) + + +Changes for Version JM 5.0c to JM 5.0b +-------------------------------------- +New features +* CABC Cleanup and Complexity reduction (FB) + +Bug Fixes +* Chroma intra pred fix (AH/KS) +* Loop filter Clipping (AH) + +Cleanups (no functional change) +* 4x4 intra prediction mode order (KS) +* remove old qp range support (simplifies loop filter) (KS) +* remove some never called code (KS) + + +Changes for Version JM 5.0b to JM 5.0a +-------------------------------------- +New features +* CABAC cleanup (GB/DM) + + +Changes for Version JM 5.0a to JM 5.0 +------------------------------------- +Removed features +* Intra 8x8 sub macroblocks (HS) + +Bug Fixes +* Choose right data partition in RDCost_for_4x4IntraBlocks() MW + + +Changes for Version JM 5.0 to JM 4.4a +------------------------------------- +New features +* Changed startcode length (KS) +* SEI for (SR) + a) Unregistered (arbitrary) data + b) Registered ITU_T_t35 data + c) Random access + d) Pan Scan Information + +Removed features +* Special Position (KS) +* 1/8 pel motion compensation (KS) + +Bug Fixes +* Temporal Direct mode fix for bottom field (LW) +* fix statistics file + + +Changes for Version JM 4.4a to JM 4.4 +------------------------------------- +New features +* MB AFF and CABAC (GB/HS) +Bug Fixes +* fix chroma QP mapping (AH) + + +Changes for Version JM 4.4 to JM 4.3a +------------------------------------- +New features +* FMO mode 3# [JVT-D095] (YW) +* FMO modes 4#~6# [JVT-D097] (YW) +* Scene information SEI message [JVT-D099] (YW) +* Redundant slices [JVT-D101] (YW) + +Note: FMO is broken. + + +Changes for Version JM 4.3a to JM 4.3 +------------------------------------- +New features +* Spare Pictures [JVT-D100] (DT) +* Signaling of Enhanced GOPs [JVT-D098] (DT) + +Bug Fixes +* state table fix (BH) +* fix chroma intra prediction and MB AFF (DH) +* double decoder bitstream buffer size for HD material (KS) +* SP chroma calcultaion fix (MN) +* fix long term picture remove from buffer (DC) + +Changes for Version JM 4.3 to JM 4.2a +------------------------------------- +New features +* Chroma Intra prediction (DH/GB) + +Bug Fixes +* Data partitioning (TO) +* remove assert from ABT (TH/MW) +* fix for constrained intra prediction (TWa) + +Note: MB AFF is broken in this version + + +Changes for Version JM 4.2a to JM 4.2 +------------------------------------- +New features +* Loop Filter Simplification and Improvement (AJ) + + +Changes for Version JM 4.2 to JM 4.1c +------------------------------------- +New features +* Spatial Direct Prediction for MB AFF (AT) + +Bug Fixes +* align simplified intra prediction to text (AH) +* fix simplified intra prediction for mb aff (AH) +* fix spatial direct (YK) +* fix temporal direct (YK) +* MB AFF fixes (LW,RG) + +Cleanups +* clean up decode_one_slice (KS) +* removed commented out code (KS) + + +Changes for Version JM 4.1c to JM 4.1b +-------------------------------------- +New features +* Implicit bi-predictive weighting (YK) + +Bug Fixes +* MAX_QP define (AJ) + + +Changes for Version JM 4.1b to JM 4.1a +-------------------------------------- +New features +* Simplification of Intra Prediction (AH) + +Cleanups +* also always enable EncapsulatedNALPayload at the decoder (KS) +* reference frame check AFF fix (AT) + + +Changes for Version JM 4.1a to JM 4.1 +------------------------------------- +New features +* Spatial Direct Prediction (AT) + Note: Spatial Direct Currently disabled for MB-AFF + +Bug Fixes +* SP chroma bug fixed (XS) + Removed SP Switch flag from SI encoding (AT) + Corrected readSyntaxElement_fixed (AT) +* close reconstruction file only if opened (LWi) +* MB AFF fixes (AT) + +Cleanups +* remove double scan routines (KS) +* always enable EncapsulatedNALPayload (KS) + + +Changes for Version JM 4.1 to JM 4.0d +------------------------------------- +New features +* macroblock adaptive frame/field (LW) + + +Changes for Version JM 4.0d to JM 4.0c +-------------------------------------- +Bug Fixes +* CAVLC/AFF memory allocation (KL) +* Solaris compiler problem (MW) +* CAVLC and ABT updates + + +Changes for Version JM 4.0c to JM 4.0b +------------------------------- +New features +* New Macroblock modes for SP-frames [JVT-B055] (RK) + Note: some problems related to decoding of SI-frames + are reported and have to be fixed + + + +Changes for Version JM 4.0b to JM 4.0a +------------------------------- +New features +* CAVLC [JVT-C028, D034, D036] (KL, JA) +* Low Complexity Diagonal Mode for Intra Prediction [JVT-D059] (GrC) + +Bug Fixes +* Bitstream in MSB first order for CABAC (DM, GB) + + +Changes for Version JM 4.0a to JM 3.9a +------------------------------- +New features +* Improved CABAC [JVT-C060] (HS, GB)) +* Fast Arithmetic Coding for CABAC [JVT-C061] (DM, GB) +* CABAC cleanup [JVT-D019] (GB, DM) +* CABAC and ABT [JVT-D021] (HS, DM, GH)) +* New end-of-slice signaling for CABAC [JVT-D020] (HS) + + +Changes for Version JM 3.9a to JM 3.9 +------------------------------------- + +Bug Fixes +* fix ABT and VLC (MW) +* fix slice mode 2 (KS) + + +Changes for Version JM 3.9 to JM 3.8 +------------------------------------ + +Bug Fixes +* fix bitstream NAL encapsulation for slice modes 1,2 and interlace (KS) +* merge interlace buffer changes (KP/KS) +* fix encoder/decoder mismatch for SP-frames (MW) +* remove unnecessary calculations (AT) +* fix SP compiling warning (FW) +* disable 8x8 intra blocks in data partitioning mode (TO) + +Cleanups +* remove dead code and variables (KS) +* free all prviously allocated memory (KS) + +Note: +* FMO is currently broken + +Changes for Version JM 3.8 to JM 3.7 +------------------------------------ + +New features +* Adaptive Block-size Transforms (MW) + +Changes for Version JM 3.7 to JM 3.6 +------------------------------------ + +New features +* Loop filter complexity reduction [JVT-C094] (AJ) + + +Changes for Version JM 3.6 to JM 3.5 +------------------------------------ + +New features +* support for FMO, see JVT-C089-C092 (SW) + -> FMO requires RTP file format and ConstrainedIntraPrediction = 1 + +Bug Fixes +* many FMO and out-of-order slices related ones (SW) +* fix old QPs (KS) +* fix chroma coding (AT/KS) +* error concealment fix (YW) + +Cleanups +* really remove old start codes (KS) +* remove some unused decoder functions (KS) + +Note: +* in Version 3.5 and 3.6 is in the bit stream format the Slice mode broken +* RTP still has the ASCII Parameter Set format + +Changes for Version JM 3.4 to JM 3.5 +------------------------------------ + +New Features +* improved SP technique [JVT-c114] (FW) +* add syntax flag for switching (FW) + + +Bug Fixes +* new QP range bug fixes (chroma & SP coding) (AT/FW) +* SKIP mode support and Runlength Coding for SP frames (AT/FW) +* Modified lambda weight in RDO for SP frames (AT/FW) + + +Changes for Version JM 3.4 to JM 3.3 +------------------------------------ + +New Features +* stuffing to prevent emulation of start-code [JVT-C064] (SR) +* New start-code for byte-stream format. (SR) +* New method for slice-level QP signaling [JVT-C136] (SR) +* QP range shifted to [0,51] (SR) + -> use #define _OLDSTYLEQP_ for old range + +Bug Fixes +* fix single reference frame problem (AT) +* fix B-Frame coding (AT) +* fix ADAPT_LAST_GROUP (DT) +* fix bit statistics (KS) +* fix search if _FAST_FULL_ME_ disabled (KS) + +Cleanups +* remove old slice-level QP signaling (KS) +* remove old start codes (KS) +* large QP handling cleanups (KS) + + +Changes for Version JM 3.3 to JM 3.2 +------------------------------------ + +New Features +* Consideration of MVs of Direct Mode Macroblocks in B frame MV prediction [JVT-C127] (AT) + +Bug Fixes +* fix deblocking filter for B frames [JVT-C127] (AT) + + +Changes for Version JM 3.2 to JM 3.1 +------------------------------------ + +New Features +* Skip mode motion compensation as defined in JVT-C027 (JL) + + +Changes for Version JM 3.1 to JM 3.0 +------------------------------------ + +New Features +* Quantization range changed from [-8,39] to [-12,39] [JVT-B039] (AH) +* Simplified rounding for transform and quantization [JVT-C136] (AH) +* Simplified formula for intra 16x16 plane prediction [JVT-C136] (AH) + +Bug Fixes +* fix RTP assert (DT) + +Cleanups +* remove gcc warnings (KS) + +Known Problems +* AFF + 1/8 pel motion compensation broken +* if "LastFrameNumber" is used, "FramesToBeEncoded" must be >= "LastFrameNumber" + + +Changes for Version JM 3.0 to JM 2.1 +------------------------------------ + +New Features +* Signaling of "Clean" Random Access Points [JVT-B042, JVT-C083] (DT) + +Bug Fixes +* error concealment fixes (TO) +* constrained intra prediction (GRC) +* align intra pred to CD text (GRC) +* adaptive frame/field fixes + - lambda calculation (LW) + - average psnr output (LW) + - fix slice modes 1 and 2 (LW) + - fix data partitioning (TO) +* add adaptive frame/field parameter consistency checks (KS/LW) +* encoder frame buffer management changes for non-AFF mode (DT) + +Cleanups +* remove old 6 mode intra prediction (KS) + + +Changes for Version JM 2.1 to JM 2.0b +------------------------------------- + +New Features +* adaptive frame/field coding (LW) + +Bug Fixes +* intra 4x4 prediction block availability (several people) +* DP fixes (TO) + + +Changes for Version JM 2.0b to JM 2.0 +------------------------------------- +Bug Fixes +* fix array bounds overflow in cabac.c (KS/IM) +* fix byte sex issues in IFF (KS) +* add UNIX preparation script (KS) + + +Changes for Version JM 2.0 to JM 1.9 +------------------------------------- +I dropped adaptive frame/field coding, so only bugfixes for this release + +Bug Fixes +* decoder delta qp decoding fix (HS) +* correction of wrong calculation of written bits in CABAC mode + (correction according to the new coding engine) (DM) +* major cleanup of the routines related to the statistics reported + in stat.dat (DM): + - correction of wrong calculation of used bits for header and mode info + - addition of new table entries "motion info" and "stuffing bits" + such that the sum of all entries matches _exactly_ the + plotted number of average bits/frame, which in turn is + now equal to the number of bits reported on the console +* correction of wrong calculation of written bytes for CABAC in RTP mode (DM) +* correction of total bits/frame printed out to the data.txt file for with B-frames (JL) +* fix mprr size for new intra pred modes (GC) +* fix frame_rate calculation for B-frames (GH) +* fix restriction on reference frames for intra frame period (KS) +* first step RPSL fixes (KS) +* fix RTP assert (YW) +* tracefile fix (DC) +* fix spelling of config file parameter "NumberReferenceFrames" (MB) + +Changes for Version JM 1.9 to JM 1.8 +------------------------------------- +New Features +* Loopfilter revised (PL) +* INTRA-Prediction on unfiltered content (PL) + +Bug Fixes +* use for each intra pred mode one CABAC context (DM) +* increase version number (KS) +* Interim file format updates (DT) + +Cleanups +* GCC compatibility, remove warnings/unused variables (KS) +* Add UNIX Makefiles (KS/HS) + + +Changes for Version JM 1.8 to JM 1.7 +------------------------------------ +New Features +* New Intra Prediction Modes [JVT-B080] (GrC) + + +Changes for Version JM 1.7 to JM 1.4 +------------------------------------ +New Features +* New Arithmetic Coding Engine [JVT-B036, Moffat/Neal/Witten ACM98] +* Improved CABAC [JVT-B101] (GB) +* Improved MB Pred Modes [JVT-B054] (HS) + +Bug Fixes +* Fix MB statistics (HS) +* Compile fix ERPS test (KS) +* Restrict qp_factor for CABAC initialization (DM/KS) +* Uses MAX_QP for IFF qp encoding (KS) +* Fix decoder TR calculation (again) (AT) +* Fix wrong concealment for B-frames and TR wrap around (KS) +* Correct error handling if first slice (or first frame) was lost (TO) +* Introduced macroblock map for concealment of lost or damaged macroblocks (TO) + + +Changes for Version JM 1.4 to JM 1.0 +------------------------------------ +New Features +* Interim file format according to Appendix III of WD-1 [JVT-A003] (DT) +* Transform and Quantizer changes [JVT-B008, JVT-B038] (AH) +* VLC update [JVT-B029] (LK) +* Updated Error Rebust Macroblock Mode and Reference Frame Selection [JVT-B102] (DK) + +Bug Fixes +* DP & CABAC (TO) + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +People: +KS Karsten Suehring Karsten.Suehring (at) hhi.fraunhofer.de +AT Alexis Michael Tourapis alexismt@ieee.org + +AF Arild Fuldseth arild.fuldseth@tandberg.no +AH Antti Hallapuro antti.hallapuro@nokia.com +AL Athanasios Leontaris athanasios.leontaris@dolby.com +AJ Anthony Joch anthony@ubvideo.com +AM Alan MacDonald newslists@warpzillion.com +AS Albert Simpson ASimpson@amphion.com +ASe Andrew Segall asegall@sharplabs.com +AW Armand Wemelsfelder wemel@planet.nl +BH Barry Haskell bhaskell@apple.com +BJ Byeungwoo Jeon bjeon@yurim.skku.ac.kr +BL Brandon Lin brandon@ubvideo.com +BiL Bin Li yhlibin@mail.ustc.edu.cn +BY Bill Yi bill.xyi@gmail.com +CG Cristina Gomila GomilaC@tce.com +CK Christopher Kotchey ckotchey@us.ibm.com +CL ChongSoon Lim chongsoon.lim@sg.panasonic.com +CS Christian Schmidt schmidt@ece.ucsb.edu +CV Chris Vogt +CZ Chunbo Zhu cbzhu@mail.ustc.edu.cn +DA D. Agrafiotis d.agrafiotis@bristol.ac.uk +DC Doug Chin dougchin@sandvideo.com +DD Dmitriy Demin Dmitriy.Demin@Sun.COM +DH Dzung Hoang dzung.hoang@conexant.com +DK Dimitris Kontopodis dkonto@eikon.tum.de +DM Detlev Marpe +DT Dong Tian tian@cs.tut.fi +DW Dong Wang Dong.Wang@bristol.ac.uk +EM Ehsan Maani emaan@dolby.com +ES Eric Setton esetton@stanford.edu +FB Frank Bossen bossen@docomolabs-usa.com +GB Gabi Blaetterman +GC Guy Cote guy@videolocus.com +GM Gunnar Marten gunnar_marten@web.de +GrC Greg Conklin gregc@real.com +GS Gary Sullivan garysull@microsoft.com +FW Feng Wu fengwu@microsoft.com +HS Heiko Schwarz +HY Haoping Yu haoping.yu@thomson.net +HZ Huipin Zhang hzhang@mobilygen.com +IL Inge Lille-Langoy Inge.Lille-Langoy@telenor.com +IM Iole Moccagatta imoccaga@lsil.com +IP Isabelle Perroux isabelle.perroux@allegrodvt.com +JA James Au james@ubvideo.com +JaB James Bassett jbas021@aucklanduni.ac.nz +JB Jill Boyce BoyceJ@tce.com +JC John Cox jc@sj.co.uk +JGG Jason Garret-Glaser jason@x264.com +JjC Jianjun Chen cjj730@hotmail.com +JK Jeong Kwon Kim kjk@mcubeworks.com +JL Jani Lainema Jani.Lainema@nokia.com +JlT Julien Le Tanou jletanou@gmail.com +JS Joonho Song jhsong@varovision.com +JSa Jose Sanches jose.sanches@st.com +JX Jian Feng Xu fenax@video.mdc.tsinghua.edu.cn +JXi Jun Xin jxin@merl.com +JZ Jun Zhang j_zhang1999@yahoo.com +KL Karl Lillevold Karll@real.com +KHH Ki-Hun Han khhan@dms.sejong.ac.kr +KP Krit Panusopone KPanusopone@gi.com +KSa Kazushi Sato kazushi@av.crl.sony.co.jp +KSw Kumar Swaminathan kswam@dolby.com +LK Louis Kerofsky lkerofsky@sharplabs.com +LL Limin Liu lliu@dolby.com +LlL Loïc Le Loarer loic_leloarer@sdesigns.eu +LW Limin Wang liwang@gi.com +LWi Lowell Winger lwinger@videolocus.com +LP Luca Pezzoni luca.pezzoni@st.com +LZ lzyn llzzyynjupt@sina.com +LZL Larry Luther lzl@dolby.com +MA Mauricio Alvarez alvarez@ac.upc.edu +MB Marcus Barkowsky Marcus.Barkowsky@nt.e-technik.uni-erlangen.de +MG Michael Gallant mgallant@videolocus.com +MK Marta Karczewicz martak@qualcomm.com +ML Maxim Lobasso lobasso@hotmail.com +MN Mike Nilsson mike.nilsson@bt.com +MT Mario Trentini mario_trentini@realmagic.fr +MW Mathias Wien wien@ient.rwth-aachen.de +MWa Michael Walter eiMichaelWalter@web.de +OD Olivier Deygas olivier.deygas@st.com +PC Peison Cheng peisongc@qualcomm.com +PeP Peshala Pahalawatta Peshala.Pahalawatta@dolby.com +PK Petr Kadlec Kadlec@FTW.at +PL Peter List Peter.List@t-systems.de +PN Pavel Novotny pnovotny@lsil.com +PP Purvin Pandit purvin.pandit@thomson.net +PW Ping Wu pwu@tandbergtv.com +RB Roman Barsky romanb@dsp.co.il +RG Rajeev Gandhi rajeev@motorola.com +RK Ragip Kurceren ragip.kurceren@nokia.com +RS Rickard Sjoberg Rickard.Sjoberg@era.ericsson.se +RaS Raghavan Subramaniyan Raghavan.S@motorola.com +RV Rajesh Kumar S Venkata v-rajesh.kumar@st.com +SB Saurav Bandyopadhyay skb3@buffalo.edu +SF Simon Fenney simon.fenney@powervr.com +SG Steve Gordon sgordon@broadcom.com +SH Sven Heithecker heithecker@ida.ing.tu-bs.de +SK Shafi Ullah Khan shafi@sarayusoftech.com +SL Stephan Lachowsky stephan@mobilygen.com +SKa Shinya Kadono kadono.shinya@jp.panasonic.com +SM Siwei Ma swma@jdl.ac.cn +SM Sebastian Manciulea sebastian@splash.ro +SMy Stefaan Mys stefaan.mys@ugent.be +SN Satoshi Nakagawa nakagawa@okilab.net +SR Shankar Regunathan shanre@microsoft.com +SeS Sergey A. Sablin sergey@elecard.net.ru +SSu Shijun Sun ssun@sharplabs.com +SSe Shun-ichi Sekiguchi Sekiguchi.Shunichi@eb.MitsubishiElectric.co.jp +SW Stephan Wenger stewe@stewe.org +SWi Steffen Wittmann Steffen.Wittmann@eu.panasonic.com +SY Steve Yao steveyao@sandvideo.com +SZ Steve Zeng stevezeng@rogers.com +TB Tomás Brandão tomas.brandao@iscte.pt +TH Till Hallbach halbach@tele.ntnu.no +THi Tobias Hinz +TK Thomas Kunlin thomas.kunlin@st.com +TN Takahiro Nishi nishi.takahiro@jp.panasonic.com +TO Tobias Oelbaum drehvial@gmx.net +TP Tom Pycke tom.pycke@ugent.be +TS Teruhiko Suzuki teruhiko@av.crl.sony.co.jp +TSh Tamar Shoham Tamar.Shoham@comverse.com +TSt Thomas Stockhammer stockhammer@lnt.ei.tum.de +TW Thomas Wiegand +TWe Thomas Wedi wedi@tnt.uni-hannover.de +TWa Toby Walker tobyw@usrl.sony.com +VG Valeri George valeri.george@hhi.fraunhofer.de +VV Viktor Varsa viktor.varsa@nokia.com +WC Woong Il Choi creata@ece.skku.ac.kr +WF Wenfang Fu fwf@video.mdc.tsinghua.edu.cn +WK Woo-Shik Kim wooshik.kim@usc.edu +WZ Weimin Zeng wzeng@wischip.com +XI Xin xin@apple.com +XS Xiaoyan Sun sunxiaoyan@msrchina.research.microsoft.com +XX Xiaozhong Xu xxz@video.mdc.tsinghua.edu.cn +XY Xiaoquan Yi xyi@scu.edu +YC Yi-Jen Chiu yi-jen.chiu@intel.com +YD Yunyang Dai ydai@dolby.com +YDh Yves Dhondt yves.dhondt@ugent.be +YH Yu-Wen Huang YW_Huang@mtk.com.tw +YHe Yuwen He yhe@dolby.com +YK Yoshihiro Kikuchi yoshihiro.kikuchi@toshiba.co.jp +YL Yann Lemaguet yann.lemaguet@philips.com +YW Ye-Kui Wang ye-kui.wang@nokia.com +YL Yiting Liao yliao@dolby.com +YX Yanjun Xu yxu@sarnoff.com +YY Yan Ye yye@dolby.com +ZC Zhifeng Chen Zhifeng@ecel.ufl.edu +ZH Zhou Heng zhou_heng@yahoo.com +ZHL Zhen Li zhen.li@dolby.com +ZL Li Zhang zhangli@jdl.ac.cn +ZX Z. Xue jon_xz@yahoo.com +ZY Zheng Yanzhao yzzheng@singnet.com diff --git a/COPYRIGHT_ISO_IEC.txt b/COPYRIGHT_ISO_IEC.txt new file mode 100644 index 0000000..b7b6b05 --- /dev/null +++ b/COPYRIGHT_ISO_IEC.txt @@ -0,0 +1,18 @@ +/********************************************************************** + * Software Copyright Licensing Disclaimer + * + * This software module was originally developed by contributors to the + * course of the development of ISO/IEC 14496-10 for reference purposes + * and its performance may not have been optimized. This software + * module is an implementation of one or more tools as specified by + * ISO/IEC 14496-10. ISO/IEC gives users free license to this software + * module or modifications thereof. Those intending to use this software + * module in products are advised that its use may infringe existing + * patents. ISO/IEC have no liability for use of this software module + * or modifications thereof. The original contributors retain full + * rights to modify and use the code for their own purposes, and to + * assign or donate the code to third-parties. + * + * This copyright notice must be included in all copies or derivative + * works. Copyright (c) ISO/IEC 2004, 2005, 2006, 2007, 2008. + **********************************************************************/ diff --git a/COPYRIGHT_ITU.txt b/COPYRIGHT_ITU.txt new file mode 100644 index 0000000..27361e5 --- /dev/null +++ b/COPYRIGHT_ITU.txt @@ -0,0 +1,32 @@ +/* +*********************************************************************** +* COPYRIGHT AND WARRANTY INFORMATION +* +* Copyright 2001, International Telecommunications Union, Geneva +* +* DISCLAIMER OF WARRANTY +* +* These software programs are available to the user without any +* license fee or royalty on an "as is" basis. The ITU disclaims +* any and all warranties, whether express, implied, or +* statutory, including any implied warranties of merchantability +* or of fitness for a particular purpose. In no event shall the +* contributor or the ITU be liable for any incidental, punitive, or +* consequential damages of any kind whatsoever arising from the +* use of these programs. +* +* This disclaimer of warranty extends to the user of these programs +* and user's customers, employees, agents, transferees, successors, +* and assigns. +* +* The ITU does not represent or warrant that the programs furnished +* hereunder are free of infringement of any third-party patents. +* Commercial implementations of ITU-T Recommendations, including +* shareware, may be subject to royalty fees to patent holders. +* Information regarding the ITU-T patent policy is available from +* the ITU Web site at http://www.itu.int. +* +* THIS IS NOT A GRANT OF PATENT RIGHTS - SEE THE ITU-T PATENT POLICY. +************************************************************************ +*/ + diff --git a/Changes_detail.txt b/Changes_detail.txt new file mode 100644 index 0000000..c1b5ed7 --- /dev/null +++ b/Changes_detail.txt @@ -0,0 +1,195 @@ +Detailed Changes for JM 13.0 +---------------------------- + +JVT-U043 +-------- + +(1) Highlight of the changes to encoder: + +* Two fields - ToneMappingSEIPresentFlag and ToneMappingFile are added to the bottom of encoder.cfg + ToneMappingFile is the tone mapping configuation file + +* #define PRINT_TONE_MAPPING in sei.c to print the tone mapping sei messages + several tone mapping related functions are implemented in sei.c + +* Writing SEI message into bitstream is done at the end of init_frame () in image.c + + +(2) Highlight of the changes to decoder: + +* Define PRINT_TONE_MAPPING at the top of in sei.c to print out the tone mapping sei messages + Tone mapping SEI messages is interpreted in interpret_tone_mapping() in sei.c + +* Define ENABLE_OUTPUT_TONEMAPPING in defines.h to enable the tone-mappping processing. + The actual tone mapping processing is done in output.c write_out_picture() + A tone_mapping flag and tone-mapping look-up-table is added to the StorablePicture stucture. + + +(3) Note: +* The actual tone-mapping processing is perormed only when tone_map_id ==0. +For other tone_map_id, the tone-mapping sei messages are printed out, but no actual processing is performed. + +* Curretnly only the RGB 4:4:4 tone mapping is supported. For other format, it needs color format +conversion before performing the tone mapping process. + + +(4) Test bitstreams: +source Crew_10.rgb, size 1280x720, bit-depth 10, RGB 4:4:4; 10 frames, encoded with all intra +source Crew_8.rgb, size 1280x720, bit-depth 8, RGB 4:4:4; 10 frames, encoded with all intra + + 1) test1.264 // test processing by different model_id. These models are intentionally very different, so that one can easily tell that different tone mappings are applied. + +at frame 0, send: tone_map_id=0, tone_map_cancel_flag=0, tone_map_repetition_period=1, coded_data_bit_depth=10, sei_bit_depth=8, model_id=0 +at frame=2, send: tone_map_id=0, tone_map_cancel_flag=0, tone_map_repetition_period=1, coded_data_bit_depth=10, sei_bit_depth=8, model_id=1 +at frame=4, send: tone_map_id=0, tone_map_cancel_flag=0, tone_map_repetition_period=1, coded_data_bit_depth=10, sei_bit_depth=8, model_id=2 +at frame=6, send: tone_map_id=0, tone_map_cancel_flag=0, tone_map_repetition_period=1, coded_data_bit_depth=10, sei_bit_depth=8, model_id=3 + + 2) test2.264 // test the tone_map_repetition_period and tone_map cancel flag + +at frame 0, send: tone_map_id=0, tone_map_cancel_flag=0, tone_map_repetition_period=0, coded_data_bit_depth=8, sei_bit_depth=8, model_id=1 +at frame 2, send: tone_map_id=0, tone_map_cancel_flag=0, tone_map_repetition_period=5, coded_data_bit_depth=8, sei_bit_depth=8, model_id=1 +at frame 8, send: tone_map_id=0, tone_map_cancel_flag=1 + + 3) test3.264 // test the case of tone_map_id != 0 +at frame 0, send: tone_map_id=1, tone_map_cancel_flag=0, tone_map_repetition_period=1, coded_data_bit_depth=10, sei_bit_depth=8, model_id=1 + + + +Sharp +2/1/2007 + +Changes for JM 7.5 +------------------ +I updated the reference encoder with proper reference picture list handling +(independent list_0 and list_1 prediction), especially for MB and picture AFF. + +Also picture and MB AFF can now be enabled independently in the config file. + +I merged the updated deblocking filter along with many other fixes that were +already in the decoder dpb code. + +This version is not a complete working encoder, but it's a big step forward +in code correction and cleanup. + +I only worked through the main code paths, which means RD-optimization on, +standard motion estimation (no fme), no stored B pictures, no rate-control, +no prediction weights, no FMO. + +The Non-RD-optimized code should probably work, but hasn't been tested. + +The FME implementation copies large sections of encode_one_macroblock inside +a #define. That code duplication is bad coding style, which is the main +reason why I didn't touch it. I did some modifications in fast_me.c, but +due to some interface changes the file won't compile when enabling the +#define FAST_ME + +The rate control makes many assumptions from the config file AFF options. +I tried to update these, but I'm not sure if this works. In some places the +if/else decisions seemed redundant. There were also lots of flags that +indicated frame or field coding. I tried to reduce these to a minimum. + +I put some suggestions for updating FME/rate control in the section +"implementation hints" below. + +RDopt=2 mode: The simulated decoders of this mode actually don't have +anything to do with current real decoders. I also think, the compile fixes +that went into this section probably broke the mode at all. I would suggest +to remove the mode as long as there is nobody willing to invest much +time in a proper upgrade. So, is somebody interested in working on +this mode? TU Munich? + +The weigted prediction functions make some keen assumptions on the reference +picture list organization (e.g. that there is only one list which contains +exactly one backward reference picture in case of B pictures...) +I vaguely remember Jill Boyce saying something like: WP can't work properly +in the current reference software. We will fix that when there is working +reference picture list support. +Well, we now have proper reference picture list support... + +In case of enabling stored B pictures the bidirectional prediction functions +need also an update. They make also assumptions about the reference picture +list sturcture. + + +known problems: +--------------- +- num_slice_groups_minus1>0 (FMO) is broken + +- slice coding needs to be checked (fixed number of bytes has been reported to fail) + +- weigted prediction doesn't work + +- RDOpt mode 2 doesn't work + +- stored B pictures need to be checkeded (function call is disabled) + +- rate control needs to be checked + +- FME needs update + +- direct mode mismatches with HHI software if direct_8x8_inference_flag=false + (ldecod matches lencod) + +- data partitioning? + +- profile and level are set hard coded + +- CABAC doesn't work for mb aff (context model selection problem at the encoder, + HHI is working on a fix) + + +implementation hints: +--------------------- +- all reference picture indexes and motion vectors are stored in enc_picture + (global arrays are removed) + +- all_mv has additional dimension for LIST_0 / LIST_1 + +- img->MbAffFrameFlag=1 indicates MB AFF coding in current picture + +- img->mb_data[img->current_mb_nr].mb_field=1 indicates field coding for current mb pair + +- (img->current_mb_nr%2)==0 indicates top mb (of mb pair) + +- img->structure= TOP_FIELD/BOTTOM_FIELD/FRAME indiactes picture structure + +- img->pix_x, img->pix_y -> current pixel coordinates in enc_picture + +- img->opix_x, img->opix_y -> current pixel coordinates in original and reference picture + +- img->block_x, img->block_y -> current 4x4 block coordinates in enc_picture + + +(KS) + + +Changes for JM 6.2 +------------------ + +The decoder should be nearly in JVT-G50 (final draft) state. + +Known problems: + - the old Parameter Set syntax is used by default. Enable + #define G50_SPS + in defines.h for the new syntax + - the transform/inverse transform has not been reversed + - the direct modes seem to have some mismatches (compared with the + HHI implementation) for MB AFF (Alexis Tourapis is working on that) + - PCM macroblock mode not supported + - deblocking filter is not aligned to the text + - reference picture reordering / mmco hasn't got much testing + +The encoder in this version is completely broken. I started merging some +code from the decoder, but currently don't have the time to finish that. + +If you're interested in encoder work, please contact me. + +Karsten +Changes for JM 7.1 encoder +-------------------------- +Known problems: + - Decoder sometimes can't decode the bitstream with CABAC on, + multiple reference and B slice coding option. + - constrained intra prediction + +Siwei \ No newline at end of file diff --git a/FREXT_changes.txt b/FREXT_changes.txt new file mode 100644 index 0000000..9ea7f25 --- /dev/null +++ b/FREXT_changes.txt @@ -0,0 +1,167 @@ +Fidelity Range Extensions Development +====================================================================== + +for later FRExt Changes see CHANGES.txt + + +From v.2.1 to v.2.2 +---------------- +- fixed bug on wrong assignment of luma_transform_size_8x8_flag when intra coding is selected in inter pictures for CAVLC +- changed normalization for SAD cost for 4x4 and 8x8 +- allow Intra16x16 to be included in Transform8x8Mode=2 +- changed calculation of coefficient cost for dct_luma8x8 for CAVLC +- fixed bug on difference in debug and release mode for I pictures in encoder +- fixed bug on initialisation error for cost when transform 8x8 mode is 0 during P coding in encoder +- fixed CAVLC decoding of level > 16 +- merge up to JM 8.6 + +From v.2.0a to v.2.1 +---------------- +- Residue Color Transform(RCT) is implemented (can be used for RGB-sequences) +- file I/O part is modified to support 8-9-9 bit file input (luma 8 bit, chroma 9 bit) +- added the maximum pixel value for chroma part (different bitdepth values for luma and chroma) +- encoder: fixed the difference between the FRExt spec and JM code for determining ctxIdxInc for significant_.. (field coded macroblocks). +- encoder: fixed DC_PRED initialisation of intrapred[][]-array for non-4x4/8x8 intra MBs +- encoder: fixed overflowing of diff-variables while distortion calculation (for huge frame sizes) +- encoder: fixed clipping of chroma QP value in dct_chroma() +- encoder: fixed pre-filtering for 8x8 intra +- encoder: the range for delta_qp is extended (depends on YUV-Format) +- encoder: fixed pos2ctx_map and pos2ctx_last context mapping for chroma YUV422/YUV444 +- decoder: fixed conditional statement for qp_per_uv in CHROMA DC YUV444 path +- decoder: 64 bit file access for output and reference file (KS) +- decoder: complete VUI parsing (fixes streams with _real_ VUI data) (KS) +- decoder: known bug - QP report + +From v.2.0 to v.2.0a +---------------- +- in config file: luma/chroma bitdepth info is extended to 12 bit +- CBP intra/inter mapping for 8x8-intra macroblocks is fixed +- CABAC context switching (frame/field) while MBAFF mode is fixed (in write_significance_map()) + +From v.1.9c to v.2.0 +---------------- +- updated to JM reference software ver. 8.4 (all corresponding changes are in CHANGES.TXT) +- merged JM 8.5 POC mode 1 fix +- added new CABAC ctx ini-tables +- CAVLC coding mode support for 8x8 transform +- PicUnitBitDepth is changed to BitDepthLuma and BitDepthChroma (encoder cfg-file changed) +- FREXT chrominance qp offsets CbQPOffset and CrQPOffset are added (encoder cfg-file changed) +- FREXT profile idc numbers are updated (beginning from 100 now) and through #define implemented +- fixes for lossless mode: rgb-sequence reading (cfg file is updated with 'RGBinput' parameter) +- bug fixed for writing/reading of trasform_size_8x8_flag when DIRECT-mode and DirectInferenceFlag is disabled +- encoder: bit setting for cbp_blk-chroma is alligned to decoder method +- decoder: error concealment is aligned to FREXT but not tested +- decoder: fixed bug of encoder-decoder mismatch in MBAFF mode + + +From v.1.9b to v.1.9c +---------------- +- setting of available Q-matrix pic scaling list is changed +- decoder: added new default Q-matrix (in v.1.9a was not complete implemented) +- added lossless_qpprime_y_zero funcionality (encoder config file is also extended) +- decoder: bug fixed in derivation of QPy while mb_qp_delta reading + +From v.1.9a to v.1.9b +---------------- +- encoder: bug fixed for B_Direct_8x8 (while Non-RDOpt Mode) + +From v.1.9 to v.1.9a +---------------- +- added new default Q-matrix +- encoder: bug fixed, restoring of motion vector data for P8x8 MBs with transform size 4x4 +- encoder: bug fixed in YUV422 transform and quantizing process (q_bits422, qp_const422 added) +- encoder: changed the mode choosing for 8x8 and 4x4 transform sizes for P8x8-MBtype (now modes 0(B_DIRECT_8x8) and/or 4 can be taken for 8x8 transform) +- changed transform_size_8x8_flag bitstream output to intra-part and inter-part(with checking for cbp_luma==0) +- changed frame statistic output for 8x8 and 4x4 transform separation (with considering of CBP_luma==0) + + +From v.1.8 to v.1.9 +---------------- +- TRANSFORM SIZE FLAG Context Models for P-Frame are extended (CABAC) +- fixed qp_const assigning for luma coeff reading in decoder +- removed rounding for chroma (YUV 420) while dequantizing (refer to FRExt Spec.) +- fixed QP assigning for Macroblock after Delta-QP reading in decoder +- fixed DIAG_DOWN_RIGHT pred mode for 8x8 intra pred +- removed CBP writing when B-Skip and CABAC while rdopt +- code restructuring: writeMBHeader() is changed to writeMBLayer() +- Changed slice-based rounding offset to MB-based rounding offset in the encoder ( this improves significantly picture quality, can we keep this way? ) +- When Transform8x8Mode==2, Direct_8x8 mode sub-macroblock type is disabled. +- Configuration file encoder_highquality_HD.cfg is added to assist encoding of high-res HD content. + + +From v.1.7 to v.1.8 +---------------- +- inserted syntax for Constraint_set3 flag in encoder and decoder +- fixed syntax error during luma 8x8 coefficient decoding when the total number of coefficient is 64 +- changed syntax for transform_size_8x8_flag to be placed at the correct location for mb_type with sub_mb_pred for encoder and decoder to follow the standards +- changed order of 8x8 transform direction to performing vertical direction first instead of horizontal first. +- added condition to check for intra-coded neighbours when constrained intra prediction flag is on, for intra 8x8 coding for encoder +- added setting of high cost for temporal direct mode when 8x8transform only is chosen and co-located pic is not available in encoder +- changed high cost for direct mode when temporal direct mode is not allowed in encoder. Previous value will cause overflow in total cost +- fixed B_skip mode to stop sending/reading of 8x8 transform flag in encoder and decoder +- changed encoding using 8x8transform to be only equal to 1 when Transform8x8Mode in cfg file is equal to 2 for RD-on for encoder +- skipped selecting of B_skip mode with 8x8transform_flag=1 when RD-on in encoder because it is not valid mode +- fixed setting of NoSubMbPartSizeLessThan8x8Flag to zero when Intra16x16 mode is chosen in encoder to prevent encoder sending of 8x8transform flag + +- fixed sending (in encoder) and receiving (in decoder) of q_matrix values only in zig_zag scan +- fixed the correct mapping of matrix data to quantisation values in encoder and decoder +- fixed using of wrong quantized levels for de-quantisation in encoder and decoder +- switch order of getting transform values and inverse DC chroma420 level in encoder and decoder +- removed Intra8x8_ChromaU, Intra8x8_ChromaV, Inter8x8_ChromaU, Inter8x8_ChromaV matrix in encoder and decoder +- changed total matrix list to 8 according to standards +- changed scalingList routine to follow the style stated in the standards in encoder and decoder +- changed variables' names in encoder and decoder: + Old Names -> New Names + scaling_matrix_present_flag -> seq_scaling_matrix_present_flag + scaling_list_present_flag -> seq_scaling_list_present_flag + scaling_matrix_refinement_present_flag -> pic_scaling_matrix_present_flag + scaling_list_refinement_present_flag -> pic_scaling_list_present_flag + +- changed assigning of default matrix and fall-back rule for q matrix +- added syntax in sps for chroma_format_idc and lossless_qpprime in encoder and decoder +- changed location of pps fidelity range extension syntax to end of pps in encoder and decoder + +From v.1.6 to v.1.7 +---------------- +- changed Chroma Intra DC calculation for YUV422 and YUV444 + +From v.1.3 to v.1.6 +---------------- +- added YUV422, YUV444 Formats + +V.1.3 +---------------- +- added Q-matrix syntax and semantics in encoder and decoder +- Q-matrix parameters are obtained from a file set in encoder.cfg +- Q-matrix flags can be set in the encoder.cfg +- fixed calculating of cost for Intra8x8 using SATD8x8 +- 8x8 transform is now able to be used with rd-off +- New option for Transform8x8Mode, 2 for rd-off 8x8 transform mode only +- changed getting of qp_const in dct to be macroblock-based instead of slice-based +- changed output display to show Intra MB for all coding type +- fixed bug on I8MB, storing of ipredmode in wrong location +- fixed bug on I8MB, include setting of ipredmode to DC if Inter 8x8 is chosen +- fixed ref idx for direct mode +- updated changes from jm81a + + +v.1.2 +---------------- +- new PROFILE_IDCs (for Fidelity Extensions) are added +- Transform8x8Mode can be managed only at encoder site +- LowPassForIntra8x8 is turned off +- '#ifdef MIXED_TRANSFORM_SIZES' removed +- small code resructuring and cleaning + + +v.1.1 +---------------- +- QP scaling added (negative QPs are possible now) +- lambda(rdcost) scaling added +- luma/chroma bit depth variables to SPS added + +v.1.0 +---------------- +- Mixed Transform Sizes and Variable Bit Depth (v.6.0) added + + diff --git a/JM.xcodeproj/project.pbxproj b/JM.xcodeproj/project.pbxproj new file mode 100644 index 0000000..da3431c --- /dev/null +++ b/JM.xcodeproj/project.pbxproj @@ -0,0 +1,1661 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 44; + objects = { + +/* Begin PBXBuildFile section */ + 436FC29E0D85FBC500D8E371 /* rtpdump.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 436FC25A0D85FB3900D8E371 /* rtpdump.cpp */; }; + 436FC2B20D85FC9400D8E371 /* rtp_loss.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 436FC2AD0D85FC4B00D8E371 /* rtp_loss.cpp */; }; + 71927CF71472DF74000AB618 /* annexb.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927C891472DF74000AB618 /* annexb.c */; }; + 71927CF81472DF74000AB618 /* biariencode.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927C8A1472DF74000AB618 /* biariencode.c */; }; + 71927CF91472DF74000AB618 /* block.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927C8B1472DF74000AB618 /* block.c */; }; + 71927CFA1472DF74000AB618 /* cabac.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927C8C1472DF74000AB618 /* cabac.c */; }; + 71927CFB1472DF74000AB618 /* cconv_yuv2rgb.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927C8D1472DF74000AB618 /* cconv_yuv2rgb.c */; }; + 71927CFC1472DF74000AB618 /* configfile.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927C8E1472DF74000AB618 /* configfile.c */; }; + 71927CFD1472DF74000AB618 /* conformance.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927C8F1472DF74000AB618 /* conformance.c */; }; + 71927CFE1472DF74000AB618 /* context_ini.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927C901472DF74000AB618 /* context_ini.c */; }; + 71927CFF1472DF74000AB618 /* errdo_dist_mhyp.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927C911472DF74000AB618 /* errdo_dist_mhyp.c */; }; + 71927D001472DF74000AB618 /* errdo.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927C921472DF74000AB618 /* errdo.c */; }; + 71927D011472DF74000AB618 /* explicit_gop.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927C931472DF74000AB618 /* explicit_gop.c */; }; + 71927D021472DF74000AB618 /* explicit_seq.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927C941472DF74000AB618 /* explicit_seq.c */; }; + 71927D031472DF74000AB618 /* filehandle.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927C951472DF74000AB618 /* filehandle.c */; }; + 71927D041472DF74000AB618 /* fmo.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927C961472DF74000AB618 /* fmo.c */; }; + 71927D051472DF74000AB618 /* get_block_otf.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927C971472DF74000AB618 /* get_block_otf.c */; }; + 71927D061472DF74000AB618 /* header.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927C981472DF74000AB618 /* header.c */; }; + 71927D071472DF74000AB618 /* image_mp.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927C991472DF74000AB618 /* image_mp.c */; }; + 71927D081472DF74000AB618 /* image.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927C9A1472DF74000AB618 /* image.c */; }; + 71927D091472DF74000AB618 /* img_chroma.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927C9B1472DF74000AB618 /* img_chroma.c */; }; + 71927D0A1472DF74000AB618 /* img_dist_ms_ssim.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927C9C1472DF74000AB618 /* img_dist_ms_ssim.c */; }; + 71927D0B1472DF74000AB618 /* img_dist_snr.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927C9D1472DF74000AB618 /* img_dist_snr.c */; }; + 71927D0C1472DF74000AB618 /* img_dist_ssim.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927C9E1472DF74000AB618 /* img_dist_ssim.c */; }; + 71927D0D1472DF74000AB618 /* img_distortion.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927C9F1472DF74000AB618 /* img_distortion.c */; }; + 71927D0E1472DF74000AB618 /* img_luma.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927CA01472DF74000AB618 /* img_luma.c */; }; + 71927D0F1472DF74000AB618 /* intra_chroma.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927CA11472DF74000AB618 /* intra_chroma.c */; }; + 71927D101472DF74000AB618 /* intra4x4.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927CA21472DF74000AB618 /* intra4x4.c */; }; + 71927D111472DF74000AB618 /* intra8x8.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927CA31472DF74000AB618 /* intra8x8.c */; }; + 71927D121472DF74000AB618 /* intra16x16.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927CA41472DF74000AB618 /* intra16x16.c */; }; + 71927D131472DF74000AB618 /* intrarefresh.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927CA51472DF74000AB618 /* intrarefresh.c */; }; + 71927D141472DF74000AB618 /* lambda.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927CA61472DF74000AB618 /* lambda.c */; }; + 71927D151472DF74000AB618 /* leaky_bucket.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927CA71472DF74000AB618 /* leaky_bucket.c */; }; + 71927D161472DF74000AB618 /* lencod.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927CA81472DF74000AB618 /* lencod.c */; }; + 71927D171472DF74000AB618 /* list_reorder.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927CA91472DF74000AB618 /* list_reorder.c */; }; + 71927D181472DF74000AB618 /* lln_mc_prediction.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927CAA1472DF74000AB618 /* lln_mc_prediction.c */; }; + 71927D191472DF74000AB618 /* loop_filter_mbaff.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927CAB1472DF74000AB618 /* loop_filter_mbaff.c */; }; + 71927D1A1472DF74000AB618 /* loop_filter_normal.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927CAC1472DF74000AB618 /* loop_filter_normal.c */; }; + 71927D1B1472DF74000AB618 /* loopFilter.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927CAD1472DF74000AB618 /* loopFilter.c */; }; + 71927D1C1472DF74000AB618 /* macroblock_P444.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927CAE1472DF74000AB618 /* macroblock_P444.c */; }; + 71927D1D1472DF74000AB618 /* macroblock.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927CAF1472DF74000AB618 /* macroblock.c */; }; + 71927D1E1472DF74000AB618 /* mb_access.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927CB01472DF74000AB618 /* mb_access.c */; }; + 71927D1F1472DF74000AB618 /* mbuffer.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927CB11472DF74000AB618 /* mbuffer.c */; }; + 71927D201472DF74000AB618 /* mc_prediction_otf.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927CB21472DF74000AB618 /* mc_prediction_otf.c */; }; + 71927D211472DF74000AB618 /* mc_prediction.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927CB31472DF74000AB618 /* mc_prediction.c */; }; + 71927D221472DF74000AB618 /* md_common.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927CB41472DF74000AB618 /* md_common.c */; }; + 71927D231472DF74000AB618 /* md_distortion.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927CB51472DF74000AB618 /* md_distortion.c */; }; + 71927D241472DF74000AB618 /* md_high.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927CB61472DF74000AB618 /* md_high.c */; }; + 71927D251472DF74000AB618 /* md_highfast.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927CB71472DF74000AB618 /* md_highfast.c */; }; + 71927D261472DF74000AB618 /* md_highloss.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927CB81472DF74000AB618 /* md_highloss.c */; }; + 71927D271472DF74000AB618 /* md_low.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927CB91472DF74000AB618 /* md_low.c */; }; + 71927D281472DF74000AB618 /* me_distortion_otf.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927CBA1472DF74000AB618 /* me_distortion_otf.c */; }; + 71927D291472DF74000AB618 /* me_distortion.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927CBB1472DF74000AB618 /* me_distortion.c */; }; + 71927D2A1472DF74000AB618 /* me_epzs_common.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927CBC1472DF74000AB618 /* me_epzs_common.c */; }; + 71927D2B1472DF74000AB618 /* me_epzs_int.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927CBD1472DF74000AB618 /* me_epzs_int.c */; }; + 71927D2C1472DF74000AB618 /* me_epzs_sub.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927CBE1472DF74000AB618 /* me_epzs_sub.c */; }; + 71927D2D1472DF74000AB618 /* me_epzs.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927CBF1472DF74000AB618 /* me_epzs.c */; }; + 71927D2E1472DF74000AB618 /* me_fullfast_otf.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927CC01472DF74000AB618 /* me_fullfast_otf.c */; }; + 71927D2F1472DF74000AB618 /* me_fullfast.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927CC11472DF74000AB618 /* me_fullfast.c */; }; + 71927D301472DF74000AB618 /* me_fullsearch.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927CC21472DF74000AB618 /* me_fullsearch.c */; }; + 71927D311472DF74000AB618 /* me_umhex.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927CC31472DF74000AB618 /* me_umhex.c */; }; + 71927D321472DF74000AB618 /* me_umhexsmp.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927CC41472DF74000AB618 /* me_umhexsmp.c */; }; + 71927D331472DF74000AB618 /* mmco.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927CC51472DF74000AB618 /* mmco.c */; }; + 71927D341472DF74000AB618 /* mode_decision_P8x8.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927CC61472DF74000AB618 /* mode_decision_P8x8.c */; }; + 71927D351472DF74000AB618 /* mode_decision.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927CC71472DF74000AB618 /* mode_decision.c */; }; + 71927D361472DF74000AB618 /* mv_direct.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927CC81472DF74000AB618 /* mv_direct.c */; }; + 71927D371472DF74000AB618 /* mv_search.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927CC91472DF74000AB618 /* mv_search.c */; }; + 71927D381472DF74000AB618 /* nal.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927CCA1472DF74000AB618 /* nal.c */; }; + 71927D391472DF74000AB618 /* nalu.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927CCB1472DF74000AB618 /* nalu.c */; }; + 71927D3A1472DF74000AB618 /* output.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927CCC1472DF74000AB618 /* output.c */; }; + 71927D3B1472DF74000AB618 /* parset.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927CCD1472DF74000AB618 /* parset.c */; }; + 71927D3C1472DF74000AB618 /* pred_struct.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927CCE1472DF74000AB618 /* pred_struct.c */; }; + 71927D3D1472DF74000AB618 /* q_around.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927CCF1472DF74000AB618 /* q_around.c */; }; + 71927D3E1472DF74000AB618 /* q_matrix.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927CD01472DF74000AB618 /* q_matrix.c */; }; + 71927D3F1472DF74000AB618 /* q_offsets.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927CD11472DF74000AB618 /* q_offsets.c */; }; + 71927D401472DF74000AB618 /* quant4x4_2step.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927CD21472DF74000AB618 /* quant4x4_2step.c */; }; + 71927D411472DF74000AB618 /* quant4x4_around.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927CD31472DF74000AB618 /* quant4x4_around.c */; }; + 71927D421472DF74000AB618 /* quant4x4_normal.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927CD41472DF74000AB618 /* quant4x4_normal.c */; }; + 71927D431472DF74000AB618 /* quant4x4_trellis.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927CD51472DF74000AB618 /* quant4x4_trellis.c */; }; + 71927D441472DF74000AB618 /* quant4x4.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927CD61472DF74000AB618 /* quant4x4.c */; }; + 71927D451472DF74000AB618 /* quant8x8_around.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927CD71472DF74000AB618 /* quant8x8_around.c */; }; + 71927D461472DF74000AB618 /* quant8x8_normal.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927CD81472DF74000AB618 /* quant8x8_normal.c */; }; + 71927D471472DF74000AB618 /* quant8x8_trellis.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927CD91472DF74000AB618 /* quant8x8_trellis.c */; }; + 71927D481472DF74000AB618 /* quant8x8.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927CDA1472DF74000AB618 /* quant8x8.c */; }; + 71927D491472DF74000AB618 /* quantChroma_2step.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927CDB1472DF74000AB618 /* quantChroma_2step.c */; }; + 71927D4A1472DF74000AB618 /* quantChroma_around.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927CDC1472DF74000AB618 /* quantChroma_around.c */; }; + 71927D4B1472DF74000AB618 /* quantChroma_normal.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927CDD1472DF74000AB618 /* quantChroma_normal.c */; }; + 71927D4C1472DF74000AB618 /* quantChroma_trellis.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927CDE1472DF74000AB618 /* quantChroma_trellis.c */; }; + 71927D4D1472DF74000AB618 /* quantChroma.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927CDF1472DF74000AB618 /* quantChroma.c */; }; + 71927D4E1472DF74000AB618 /* ratectl.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927CE01472DF74000AB618 /* ratectl.c */; }; + 71927D4F1472DF74000AB618 /* rc_quadratic.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927CE11472DF74000AB618 /* rc_quadratic.c */; }; + 71927D501472DF74000AB618 /* rd_intra_jm_low.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927CE21472DF74000AB618 /* rd_intra_jm_low.c */; }; + 71927D511472DF74000AB618 /* rd_intra_jm.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927CE31472DF74000AB618 /* rd_intra_jm.c */; }; + 71927D521472DF74000AB618 /* rd_intra_jm444.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927CE41472DF74000AB618 /* rd_intra_jm444.c */; }; + 71927D531472DF74000AB618 /* rdopt_coding_state.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927CE51472DF74000AB618 /* rdopt_coding_state.c */; }; + 71927D541472DF74000AB618 /* rdopt.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927CE61472DF74000AB618 /* rdopt.c */; }; + 71927D551472DF74000AB618 /* rdoq_cabac.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927CE71472DF74000AB618 /* rdoq_cabac.c */; }; + 71927D561472DF74000AB618 /* rdoq_cavlc.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927CE81472DF74000AB618 /* rdoq_cavlc.c */; }; + 71927D571472DF74000AB618 /* rdoq.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927CE91472DF74000AB618 /* rdoq.c */; }; + 71927D581472DF74000AB618 /* rdpicdecision.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927CEA1472DF74000AB618 /* rdpicdecision.c */; }; + 71927D591472DF74000AB618 /* report.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927CEB1472DF74000AB618 /* report.c */; }; + 71927D5A1472DF74000AB618 /* rtp.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927CEC1472DF74000AB618 /* rtp.c */; }; + 71927D5B1472DF74000AB618 /* sei.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927CED1472DF74000AB618 /* sei.c */; }; + 71927D5C1472DF74000AB618 /* slice.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927CEE1472DF74000AB618 /* slice.c */; }; + 71927D5D1472DF74000AB618 /* symbol.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927CEF1472DF74000AB618 /* symbol.c */; }; + 71927D5E1472DF74000AB618 /* transform8x8_H444.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927CF01472DF74000AB618 /* transform8x8_H444.c */; }; + 71927D5F1472DF74000AB618 /* transform8x8.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927CF11472DF74000AB618 /* transform8x8.c */; }; + 71927D601472DF74000AB618 /* vlc.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927CF21472DF74000AB618 /* vlc.c */; }; + 71927D611472DF74000AB618 /* weighted_prediction.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927CF31472DF74000AB618 /* weighted_prediction.c */; }; + 71927D621472DF74000AB618 /* wp_lms.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927CF41472DF74000AB618 /* wp_lms.c */; }; + 71927D631472DF74000AB618 /* wp_mciter.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927CF51472DF74000AB618 /* wp_mciter.c */; }; + 71927D641472DF74000AB618 /* wp_mcprec.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927CF61472DF74000AB618 /* wp_mcprec.c */; }; + 71927D991472DF98000AB618 /* annexb.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927D651472DF98000AB618 /* annexb.c */; }; + 71927D9A1472DF98000AB618 /* biaridecod.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927D661472DF98000AB618 /* biaridecod.c */; }; + 71927D9B1472DF98000AB618 /* block.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927D671472DF98000AB618 /* block.c */; }; + 71927D9C1472DF98000AB618 /* cabac.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927D681472DF98000AB618 /* cabac.c */; }; + 71927D9D1472DF98000AB618 /* configfile.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927D691472DF98000AB618 /* configfile.c */; }; + 71927D9E1472DF98000AB618 /* context_ini.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927D6A1472DF98000AB618 /* context_ini.c */; }; + 71927D9F1472DF98000AB618 /* dec_statistics.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927D6B1472DF98000AB618 /* dec_statistics.c */; }; + 71927DA01472DF98000AB618 /* decoder_test.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927D6C1472DF98000AB618 /* decoder_test.c */; }; + 71927DA11472DF98000AB618 /* erc_api.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927D6D1472DF98000AB618 /* erc_api.c */; }; + 71927DA21472DF98000AB618 /* erc_do_i.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927D6E1472DF98000AB618 /* erc_do_i.c */; }; + 71927DA31472DF98000AB618 /* erc_do_p.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927D6F1472DF98000AB618 /* erc_do_p.c */; }; + 71927DA41472DF98000AB618 /* errorconcealment.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927D701472DF98000AB618 /* errorconcealment.c */; }; + 71927DA51472DF98000AB618 /* filehandle.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927D711472DF98000AB618 /* filehandle.c */; }; + 71927DA61472DF98000AB618 /* fmo.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927D721472DF98000AB618 /* fmo.c */; }; + 71927DA71472DF98000AB618 /* header.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927D731472DF98000AB618 /* header.c */; }; + 71927DA81472DF98000AB618 /* image.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927D741472DF98000AB618 /* image.c */; }; + 71927DA91472DF98000AB618 /* intra_chroma_pred_mbaff.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927D751472DF98000AB618 /* intra_chroma_pred_mbaff.c */; }; + 71927DAA1472DF98000AB618 /* intra_chroma_pred.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927D761472DF98000AB618 /* intra_chroma_pred.c */; }; + 71927DAB1472DF98000AB618 /* intra_pred_common.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927D771472DF98000AB618 /* intra_pred_common.c */; }; + 71927DAC1472DF98000AB618 /* intra4x4_pred_mbaff.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927D781472DF98000AB618 /* intra4x4_pred_mbaff.c */; }; + 71927DAD1472DF98000AB618 /* intra4x4_pred_normal.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927D791472DF98000AB618 /* intra4x4_pred_normal.c */; }; + 71927DAE1472DF98000AB618 /* intra4x4_pred.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927D7A1472DF98000AB618 /* intra4x4_pred.c */; }; + 71927DAF1472DF98000AB618 /* intra8x8_pred_mbaff.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927D7B1472DF98000AB618 /* intra8x8_pred_mbaff.c */; }; + 71927DB01472DF98000AB618 /* intra8x8_pred_normal.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927D7C1472DF98000AB618 /* intra8x8_pred_normal.c */; }; + 71927DB11472DF98000AB618 /* intra8x8_pred.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927D7D1472DF98000AB618 /* intra8x8_pred.c */; }; + 71927DB21472DF98000AB618 /* intra16x16_pred_mbaff.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927D7E1472DF98000AB618 /* intra16x16_pred_mbaff.c */; }; + 71927DB31472DF98000AB618 /* intra16x16_pred_normal.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927D7F1472DF98000AB618 /* intra16x16_pred_normal.c */; }; + 71927DB41472DF98000AB618 /* intra16x16_pred.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927D801472DF98000AB618 /* intra16x16_pred.c */; }; + 71927DB51472DF98000AB618 /* ldecod.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927D811472DF98000AB618 /* ldecod.c */; }; + 71927DB61472DF98000AB618 /* leaky_bucket.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927D821472DF98000AB618 /* leaky_bucket.c */; }; + 71927DB71472DF98000AB618 /* loop_filter_mbaff.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927D831472DF98000AB618 /* loop_filter_mbaff.c */; }; + 71927DB81472DF98000AB618 /* loop_filter_normal.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927D841472DF98000AB618 /* loop_filter_normal.c */; }; + 71927DB91472DF98000AB618 /* loopFilter.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927D851472DF98000AB618 /* loopFilter.c */; }; + 71927DBA1472DF98000AB618 /* macroblock.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927D861472DF98000AB618 /* macroblock.c */; }; + 71927DBB1472DF98000AB618 /* mb_access.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927D871472DF98000AB618 /* mb_access.c */; }; + 71927DBC1472DF98000AB618 /* mb_prediction.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927D881472DF98000AB618 /* mb_prediction.c */; }; + 71927DBD1472DF98000AB618 /* mb_read.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927D891472DF98000AB618 /* mb_read.c */; }; + 71927DBE1472DF98000AB618 /* mbuffer_mvc.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927D8A1472DF98000AB618 /* mbuffer_mvc.c */; }; + 71927DBF1472DF98000AB618 /* mbuffer.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927D8B1472DF98000AB618 /* mbuffer.c */; }; + 71927DC01472DF98000AB618 /* mc_direct.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927D8C1472DF98000AB618 /* mc_direct.c */; }; + 71927DC11472DF98000AB618 /* mc_prediction.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927D8D1472DF98000AB618 /* mc_prediction.c */; }; + 71927DC21472DF98000AB618 /* nal.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927D8E1472DF98000AB618 /* nal.c */; }; + 71927DC31472DF98000AB618 /* nalu.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927D8F1472DF98000AB618 /* nalu.c */; }; + 71927DC41472DF98000AB618 /* output.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927D901472DF98000AB618 /* output.c */; }; + 71927DC51472DF98000AB618 /* parset.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927D911472DF98000AB618 /* parset.c */; }; + 71927DC61472DF98000AB618 /* quant.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927D921472DF98000AB618 /* quant.c */; }; + 71927DC71472DF98000AB618 /* read_comp_cabac.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927D931472DF98000AB618 /* read_comp_cabac.c */; }; + 71927DC81472DF98000AB618 /* read_comp_cavlc.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927D941472DF98000AB618 /* read_comp_cavlc.c */; }; + 71927DC91472DF98000AB618 /* rtp.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927D951472DF98000AB618 /* rtp.c */; }; + 71927DCA1472DF98000AB618 /* sei.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927D961472DF98000AB618 /* sei.c */; }; + 71927DCB1472DF98000AB618 /* transform8x8.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927D971472DF98000AB618 /* transform8x8.c */; }; + 71927DCC1472DF98000AB618 /* vlc.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927D981472DF98000AB618 /* vlc.c */; }; + 71927E201472DFE1000AB618 /* blk_prediction.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927E121472DFE1000AB618 /* blk_prediction.c */; }; + 71927E211472DFE1000AB618 /* config_common.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927E131472DFE1000AB618 /* config_common.c */; }; + 71927E221472DFE1000AB618 /* img_io.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927E141472DFE1000AB618 /* img_io.c */; }; + 71927E231472DFE1000AB618 /* img_process.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927E151472DFE1000AB618 /* img_process.c */; }; + 71927E241472DFE1000AB618 /* input.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927E161472DFE1000AB618 /* input.c */; }; + 71927E251472DFE1000AB618 /* io_raw.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927E171472DFE1000AB618 /* io_raw.c */; }; + 71927E261472DFE1000AB618 /* io_tiff.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927E181472DFE1000AB618 /* io_tiff.c */; }; + 71927E271472DFE1000AB618 /* mbuffer_common.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927E191472DFE1000AB618 /* mbuffer_common.c */; }; + 71927E281472DFE1000AB618 /* memalloc.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927E1A1472DFE1000AB618 /* memalloc.c */; }; + 71927E291472DFE1000AB618 /* mv_prediction.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927E1B1472DFE1000AB618 /* mv_prediction.c */; }; + 71927E2A1472DFE1000AB618 /* nalucommon.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927E1C1472DFE1000AB618 /* nalucommon.c */; }; + 71927E2B1472DFE1000AB618 /* parsetcommon.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927E1D1472DFE1000AB618 /* parsetcommon.c */; }; + 71927E2C1472DFE1000AB618 /* transform.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927E1E1472DFE1000AB618 /* transform.c */; }; + 71927E2D1472DFE1000AB618 /* win32.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927E1F1472DFE1000AB618 /* win32.c */; }; + 71927E2E1472DFE1000AB618 /* blk_prediction.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927E121472DFE1000AB618 /* blk_prediction.c */; }; + 71927E2F1472DFE1000AB618 /* config_common.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927E131472DFE1000AB618 /* config_common.c */; }; + 71927E301472DFE1000AB618 /* img_io.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927E141472DFE1000AB618 /* img_io.c */; }; + 71927E311472DFE1000AB618 /* img_process.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927E151472DFE1000AB618 /* img_process.c */; }; + 71927E321472DFE1000AB618 /* input.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927E161472DFE1000AB618 /* input.c */; }; + 71927E331472DFE1000AB618 /* io_raw.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927E171472DFE1000AB618 /* io_raw.c */; }; + 71927E341472DFE1000AB618 /* io_tiff.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927E181472DFE1000AB618 /* io_tiff.c */; }; + 71927E351472DFE1000AB618 /* mbuffer_common.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927E191472DFE1000AB618 /* mbuffer_common.c */; }; + 71927E361472DFE1000AB618 /* memalloc.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927E1A1472DFE1000AB618 /* memalloc.c */; }; + 71927E371472DFE1000AB618 /* mv_prediction.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927E1B1472DFE1000AB618 /* mv_prediction.c */; }; + 71927E381472DFE1000AB618 /* nalucommon.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927E1C1472DFE1000AB618 /* nalucommon.c */; }; + 71927E391472DFE1000AB618 /* parsetcommon.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927E1D1472DFE1000AB618 /* parsetcommon.c */; }; + 71927E3A1472DFE1000AB618 /* transform.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927E1E1472DFE1000AB618 /* transform.c */; }; + 71927E3B1472DFE1000AB618 /* win32.c in Sources */ = {isa = PBXBuildFile; fileRef = 71927E1F1472DFE1000AB618 /* win32.c */; }; +/* End PBXBuildFile section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 436FC2980D85FB4900D8E371 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 8; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + ); + runOnlyForDeploymentPostprocessing = 1; + }; + 436FC2A60D85FC1C00D8E371 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 8; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + ); + runOnlyForDeploymentPostprocessing = 1; + }; + 43FA4B130D4EECBB001709F1 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 8; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + ); + runOnlyForDeploymentPostprocessing = 1; + }; + 8DD76FAF0486AB0100D96B5E /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 8; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + ); + runOnlyForDeploymentPostprocessing = 1; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 434D2AC20D5C1C3E00610974 /* decoder.cfg */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = decoder.cfg; path = bin/decoder.cfg; sourceTree = SOURCE_ROOT; }; + 434D2AC40D5C1C3E00610974 /* encoder.cfg */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = encoder.cfg; path = bin/encoder.cfg; sourceTree = SOURCE_ROOT; }; + 434D2AC50D5C1C3E00610974 /* encoder_baseline.cfg */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = encoder_baseline.cfg; path = bin/encoder_baseline.cfg; sourceTree = SOURCE_ROOT; }; + 434D2AC60D5C1C3E00610974 /* encoder_extended.cfg */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = encoder_extended.cfg; path = bin/encoder_extended.cfg; sourceTree = SOURCE_ROOT; }; + 434D2AC80D5C1C3E00610974 /* encoder_main.cfg */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = encoder_main.cfg; path = bin/encoder_main.cfg; sourceTree = SOURCE_ROOT; }; + 434D2AC90D5C1C3E00610974 /* encoder_tonemapping.cfg */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = encoder_tonemapping.cfg; path = bin/encoder_tonemapping.cfg; sourceTree = SOURCE_ROOT; }; + 434D2ACA0D5C1C3E00610974 /* encoder_yuv422.cfg */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = encoder_yuv422.cfg; path = bin/encoder_yuv422.cfg; sourceTree = SOURCE_ROOT; }; + 434D2ACD0D5C1C3E00610974 /* leakybucketrate.cfg */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = leakybucketrate.cfg; path = bin/leakybucketrate.cfg; sourceTree = SOURCE_ROOT; }; + 434D2ACE0D5C1C3E00610974 /* q_matrix.cfg */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = q_matrix.cfg; path = bin/q_matrix.cfg; sourceTree = SOURCE_ROOT; }; + 434D2ACF0D5C1C3E00610974 /* q_matrix2.cfg */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = q_matrix2.cfg; path = bin/q_matrix2.cfg; sourceTree = SOURCE_ROOT; }; + 434D2AD00D5C1C3E00610974 /* q_matrix_def.cfg */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = q_matrix_def.cfg; path = bin/q_matrix_def.cfg; sourceTree = SOURCE_ROOT; }; + 434D2AD10D5C1C3E00610974 /* q_offset.cfg */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = q_offset.cfg; path = bin/q_offset.cfg; sourceTree = SOURCE_ROOT; }; + 434D2AD30D5C1C3E00610974 /* sg0conf.cfg */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = sg0conf.cfg; path = bin/sg0conf.cfg; sourceTree = SOURCE_ROOT; }; + 434D2AD40D5C1C3E00610974 /* sg2conf.cfg */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = sg2conf.cfg; path = bin/sg2conf.cfg; sourceTree = SOURCE_ROOT; }; + 434D2AD50D5C1C3E00610974 /* sg6conf.cfg */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = sg6conf.cfg; path = bin/sg6conf.cfg; sourceTree = SOURCE_ROOT; }; + 434D2AD80D5C1C3E00610974 /* ToneMapping.cfg */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = ToneMapping.cfg; path = bin/ToneMapping.cfg; sourceTree = SOURCE_ROOT; }; + 436FC25A0D85FB3900D8E371 /* rtpdump.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = rtpdump.cpp; path = rtpdump/rtpdump.cpp; sourceTree = ""; }; + 436FC29C0D85FB4900D8E371 /* rtpdump */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = rtpdump; sourceTree = BUILT_PRODUCTS_DIR; }; + 436FC2AA0D85FC1C00D8E371 /* rtp_loss */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = rtp_loss; sourceTree = BUILT_PRODUCTS_DIR; }; + 436FC2AC0D85FC4B00D8E371 /* rtp_loss.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = rtp_loss.h; path = rtp_loss/rtp_loss.h; sourceTree = SOURCE_ROOT; }; + 436FC2AD0D85FC4B00D8E371 /* rtp_loss.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = rtp_loss.cpp; path = rtp_loss/rtp_loss.cpp; sourceTree = SOURCE_ROOT; }; + 436FC2AE0D85FC4B00D8E371 /* ReadMe.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = ReadMe.txt; path = rtp_loss/ReadMe.txt; sourceTree = SOURCE_ROOT; }; + 43FA4B170D4EECBB001709F1 /* ldecod */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = ldecod; sourceTree = BUILT_PRODUCTS_DIR; }; + 71927C2D1472DF4A000AB618 /* annexb.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = annexb.h; path = lencod/inc/annexb.h; sourceTree = ""; }; + 71927C2E1472DF4A000AB618 /* biariencode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = biariencode.h; path = lencod/inc/biariencode.h; sourceTree = ""; }; + 71927C2F1472DF4A000AB618 /* block.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = block.h; path = lencod/inc/block.h; sourceTree = ""; }; + 71927C301472DF4A000AB618 /* cabac.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = cabac.h; path = lencod/inc/cabac.h; sourceTree = ""; }; + 71927C311472DF4A000AB618 /* cconv_yuv2rgb.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = cconv_yuv2rgb.h; path = lencod/inc/cconv_yuv2rgb.h; sourceTree = ""; }; + 71927C321472DF4A000AB618 /* configfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = configfile.h; path = lencod/inc/configfile.h; sourceTree = ""; }; + 71927C331472DF4A000AB618 /* conformance.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = conformance.h; path = lencod/inc/conformance.h; sourceTree = ""; }; + 71927C341472DF4A000AB618 /* context_ini.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = context_ini.h; path = lencod/inc/context_ini.h; sourceTree = ""; }; + 71927C351472DF4A000AB618 /* contributors.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = contributors.h; path = lencod/inc/contributors.h; sourceTree = ""; }; + 71927C361472DF4A000AB618 /* defines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = defines.h; path = lencod/inc/defines.h; sourceTree = ""; }; + 71927C371472DF4A000AB618 /* elements.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = elements.h; path = lencod/inc/elements.h; sourceTree = ""; }; + 71927C381472DF4A000AB618 /* errdo_dist_mhyp.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = errdo_dist_mhyp.h; path = lencod/inc/errdo_dist_mhyp.h; sourceTree = ""; }; + 71927C391472DF4A000AB618 /* errdo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = errdo.h; path = lencod/inc/errdo.h; sourceTree = ""; }; + 71927C3A1472DF4A000AB618 /* explicit_gop.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = explicit_gop.h; path = lencod/inc/explicit_gop.h; sourceTree = ""; }; + 71927C3B1472DF4A000AB618 /* explicit_seq.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = explicit_seq.h; path = lencod/inc/explicit_seq.h; sourceTree = ""; }; + 71927C3C1472DF4A000AB618 /* filehandle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = filehandle.h; path = lencod/inc/filehandle.h; sourceTree = ""; }; + 71927C3D1472DF4A000AB618 /* fmo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = fmo.h; path = lencod/inc/fmo.h; sourceTree = ""; }; + 71927C3E1472DF4A000AB618 /* get_block_otf.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = get_block_otf.h; path = lencod/inc/get_block_otf.h; sourceTree = ""; }; + 71927C3F1472DF4A000AB618 /* global.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = global.h; path = lencod/inc/global.h; sourceTree = ""; }; + 71927C401472DF4A000AB618 /* header.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = header.h; path = lencod/inc/header.h; sourceTree = ""; }; + 71927C411472DF4A000AB618 /* image.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = image.h; path = lencod/inc/image.h; sourceTree = ""; }; + 71927C421472DF4A000AB618 /* img_chroma.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = img_chroma.h; path = lencod/inc/img_chroma.h; sourceTree = ""; }; + 71927C431472DF4A000AB618 /* img_dist_ms_ssim.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = img_dist_ms_ssim.h; path = lencod/inc/img_dist_ms_ssim.h; sourceTree = ""; }; + 71927C441472DF4A000AB618 /* img_dist_snr.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = img_dist_snr.h; path = lencod/inc/img_dist_snr.h; sourceTree = ""; }; + 71927C451472DF4A000AB618 /* img_dist_ssim.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = img_dist_ssim.h; path = lencod/inc/img_dist_ssim.h; sourceTree = ""; }; + 71927C461472DF4A000AB618 /* img_distortion.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = img_distortion.h; path = lencod/inc/img_distortion.h; sourceTree = ""; }; + 71927C471472DF4A000AB618 /* img_luma.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = img_luma.h; path = lencod/inc/img_luma.h; sourceTree = ""; }; + 71927C481472DF4A000AB618 /* intra4x4.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = intra4x4.h; path = lencod/inc/intra4x4.h; sourceTree = ""; }; + 71927C491472DF4A000AB618 /* intra8x8.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = intra8x8.h; path = lencod/inc/intra8x8.h; sourceTree = ""; }; + 71927C4A1472DF4A000AB618 /* intra16x16.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = intra16x16.h; path = lencod/inc/intra16x16.h; sourceTree = ""; }; + 71927C4B1472DF4A000AB618 /* intrarefresh.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = intrarefresh.h; path = lencod/inc/intrarefresh.h; sourceTree = ""; }; + 71927C4C1472DF4A000AB618 /* lambda.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = lambda.h; path = lencod/inc/lambda.h; sourceTree = ""; }; + 71927C4D1472DF4A000AB618 /* leaky_bucket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = leaky_bucket.h; path = lencod/inc/leaky_bucket.h; sourceTree = ""; }; + 71927C4E1472DF4A000AB618 /* list_reorder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = list_reorder.h; path = lencod/inc/list_reorder.h; sourceTree = ""; }; + 71927C4F1472DF4A000AB618 /* lln_mc_prediction.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = lln_mc_prediction.h; path = lencod/inc/lln_mc_prediction.h; sourceTree = ""; }; + 71927C501472DF4A000AB618 /* loop_filter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = loop_filter.h; path = lencod/inc/loop_filter.h; sourceTree = ""; }; + 71927C511472DF4A000AB618 /* macroblock_p444.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = macroblock_p444.h; path = lencod/inc/macroblock_p444.h; sourceTree = ""; }; + 71927C521472DF4A000AB618 /* macroblock.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = macroblock.h; path = lencod/inc/macroblock.h; sourceTree = ""; }; + 71927C531472DF4A000AB618 /* mbuffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = mbuffer.h; path = lencod/inc/mbuffer.h; sourceTree = ""; }; + 71927C541472DF4A000AB618 /* mc_prediction_otf.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = mc_prediction_otf.h; path = lencod/inc/mc_prediction_otf.h; sourceTree = ""; }; + 71927C551472DF4A000AB618 /* mc_prediction.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = mc_prediction.h; path = lencod/inc/mc_prediction.h; sourceTree = ""; }; + 71927C561472DF4A000AB618 /* md_common.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = md_common.h; path = lencod/inc/md_common.h; sourceTree = ""; }; + 71927C571472DF4A000AB618 /* md_distortion.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = md_distortion.h; path = lencod/inc/md_distortion.h; sourceTree = ""; }; + 71927C581472DF4A000AB618 /* me_distortion_otf.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = me_distortion_otf.h; path = lencod/inc/me_distortion_otf.h; sourceTree = ""; }; + 71927C591472DF4A000AB618 /* me_distortion.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = me_distortion.h; path = lencod/inc/me_distortion.h; sourceTree = ""; }; + 71927C5A1472DF4A000AB618 /* me_epzs_common.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = me_epzs_common.h; path = lencod/inc/me_epzs_common.h; sourceTree = ""; }; + 71927C5B1472DF4A000AB618 /* me_epzs_int.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = me_epzs_int.h; path = lencod/inc/me_epzs_int.h; sourceTree = ""; }; + 71927C5C1472DF4A000AB618 /* me_epzs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = me_epzs.h; path = lencod/inc/me_epzs.h; sourceTree = ""; }; + 71927C5D1472DF4A000AB618 /* me_fullfast_otf.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = me_fullfast_otf.h; path = lencod/inc/me_fullfast_otf.h; sourceTree = ""; }; + 71927C5E1472DF4A000AB618 /* me_fullfast.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = me_fullfast.h; path = lencod/inc/me_fullfast.h; sourceTree = ""; }; + 71927C5F1472DF4A000AB618 /* me_fullsearch.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = me_fullsearch.h; path = lencod/inc/me_fullsearch.h; sourceTree = ""; }; + 71927C601472DF4A000AB618 /* me_umhex.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = me_umhex.h; path = lencod/inc/me_umhex.h; sourceTree = ""; }; + 71927C611472DF4A000AB618 /* me_umhexsmp.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = me_umhexsmp.h; path = lencod/inc/me_umhexsmp.h; sourceTree = ""; }; + 71927C621472DF4A000AB618 /* mmco.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = mmco.h; path = lencod/inc/mmco.h; sourceTree = ""; }; + 71927C631472DF4A000AB618 /* mode_decision_p8x8.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = mode_decision_p8x8.h; path = lencod/inc/mode_decision_p8x8.h; sourceTree = ""; }; + 71927C641472DF4A000AB618 /* mode_decision.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = mode_decision.h; path = lencod/inc/mode_decision.h; sourceTree = ""; }; + 71927C651472DF4A000AB618 /* mv_search.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = mv_search.h; path = lencod/inc/mv_search.h; sourceTree = ""; }; + 71927C661472DF4A000AB618 /* nal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = nal.h; path = lencod/inc/nal.h; sourceTree = ""; }; + 71927C671472DF4A000AB618 /* nalu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = nalu.h; path = lencod/inc/nalu.h; sourceTree = ""; }; + 71927C681472DF4A000AB618 /* output.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = output.h; path = lencod/inc/output.h; sourceTree = ""; }; + 71927C691472DF4A000AB618 /* params.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = params.h; path = lencod/inc/params.h; sourceTree = ""; }; + 71927C6A1472DF4A000AB618 /* parset.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = parset.h; path = lencod/inc/parset.h; sourceTree = ""; }; + 71927C6B1472DF4A000AB618 /* pred_struct_adapt_types.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = pred_struct_adapt_types.h; path = lencod/inc/pred_struct_adapt_types.h; sourceTree = ""; }; + 71927C6C1472DF4A000AB618 /* pred_struct_adapt.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = pred_struct_adapt.h; path = lencod/inc/pred_struct_adapt.h; sourceTree = ""; }; + 71927C6D1472DF4A000AB618 /* pred_struct_types.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = pred_struct_types.h; path = lencod/inc/pred_struct_types.h; sourceTree = ""; }; + 71927C6E1472DF4A000AB618 /* pred_struct.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = pred_struct.h; path = lencod/inc/pred_struct.h; sourceTree = ""; }; + 71927C6F1472DF4A000AB618 /* q_around.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = q_around.h; path = lencod/inc/q_around.h; sourceTree = ""; }; + 71927C701472DF4A000AB618 /* q_matrix.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = q_matrix.h; path = lencod/inc/q_matrix.h; sourceTree = ""; }; + 71927C711472DF4A000AB618 /* q_offsets.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = q_offsets.h; path = lencod/inc/q_offsets.h; sourceTree = ""; }; + 71927C721472DF4A000AB618 /* quant4x4.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = quant4x4.h; path = lencod/inc/quant4x4.h; sourceTree = ""; }; + 71927C731472DF4A000AB618 /* quant8x8.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = quant8x8.h; path = lencod/inc/quant8x8.h; sourceTree = ""; }; + 71927C741472DF4A000AB618 /* quantChroma.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = quantChroma.h; path = lencod/inc/quantChroma.h; sourceTree = ""; }; + 71927C751472DF4A000AB618 /* ratectl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ratectl.h; path = lencod/inc/ratectl.h; sourceTree = ""; }; + 71927C761472DF4A000AB618 /* rc_quadratic.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = rc_quadratic.h; path = lencod/inc/rc_quadratic.h; sourceTree = ""; }; + 71927C771472DF4A000AB618 /* rc_types.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = rc_types.h; path = lencod/inc/rc_types.h; sourceTree = ""; }; + 71927C781472DF4A000AB618 /* rd_intra_jm.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = rd_intra_jm.h; path = lencod/inc/rd_intra_jm.h; sourceTree = ""; }; + 71927C791472DF4A000AB618 /* rd_intra_jm444.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = rd_intra_jm444.h; path = lencod/inc/rd_intra_jm444.h; sourceTree = ""; }; + 71927C7A1472DF4A000AB618 /* rdopt_coding_state.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = rdopt_coding_state.h; path = lencod/inc/rdopt_coding_state.h; sourceTree = ""; }; + 71927C7B1472DF4A000AB618 /* rdopt.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = rdopt.h; path = lencod/inc/rdopt.h; sourceTree = ""; }; + 71927C7C1472DF4A000AB618 /* rdoq.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = rdoq.h; path = lencod/inc/rdoq.h; sourceTree = ""; }; + 71927C7D1472DF4A000AB618 /* refbuf_otf.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = refbuf_otf.h; path = lencod/inc/refbuf_otf.h; sourceTree = ""; }; + 71927C7E1472DF4A000AB618 /* refbuf.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = refbuf.h; path = lencod/inc/refbuf.h; sourceTree = ""; }; + 71927C7F1472DF4A000AB618 /* rtp.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = rtp.h; path = lencod/inc/rtp.h; sourceTree = ""; }; + 71927C801472DF4A000AB618 /* sei.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = sei.h; path = lencod/inc/sei.h; sourceTree = ""; }; + 71927C811472DF4A000AB618 /* slice.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = slice.h; path = lencod/inc/slice.h; sourceTree = ""; }; + 71927C821472DF4A000AB618 /* symbol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = symbol.h; path = lencod/inc/symbol.h; sourceTree = ""; }; + 71927C831472DF4A000AB618 /* transform8x8.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = transform8x8.h; path = lencod/inc/transform8x8.h; sourceTree = ""; }; + 71927C841472DF4A000AB618 /* vlc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = vlc.h; path = lencod/inc/vlc.h; sourceTree = ""; }; + 71927C851472DF4A000AB618 /* wp_lms.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = wp_lms.h; path = lencod/inc/wp_lms.h; sourceTree = ""; }; + 71927C861472DF4A000AB618 /* wp_mciter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = wp_mciter.h; path = lencod/inc/wp_mciter.h; sourceTree = ""; }; + 71927C871472DF4A000AB618 /* wp_mcprec.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = wp_mcprec.h; path = lencod/inc/wp_mcprec.h; sourceTree = ""; }; + 71927C881472DF4A000AB618 /* wp.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = wp.h; path = lencod/inc/wp.h; sourceTree = ""; }; + 71927C891472DF74000AB618 /* annexb.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = annexb.c; path = lencod/src/annexb.c; sourceTree = ""; }; + 71927C8A1472DF74000AB618 /* biariencode.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = biariencode.c; path = lencod/src/biariencode.c; sourceTree = ""; }; + 71927C8B1472DF74000AB618 /* block.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = block.c; path = lencod/src/block.c; sourceTree = ""; }; + 71927C8C1472DF74000AB618 /* cabac.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = cabac.c; path = lencod/src/cabac.c; sourceTree = ""; }; + 71927C8D1472DF74000AB618 /* cconv_yuv2rgb.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = cconv_yuv2rgb.c; path = lencod/src/cconv_yuv2rgb.c; sourceTree = ""; }; + 71927C8E1472DF74000AB618 /* configfile.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = configfile.c; path = lencod/src/configfile.c; sourceTree = ""; }; + 71927C8F1472DF74000AB618 /* conformance.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = conformance.c; path = lencod/src/conformance.c; sourceTree = ""; }; + 71927C901472DF74000AB618 /* context_ini.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = context_ini.c; path = lencod/src/context_ini.c; sourceTree = ""; }; + 71927C911472DF74000AB618 /* errdo_dist_mhyp.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = errdo_dist_mhyp.c; path = lencod/src/errdo_dist_mhyp.c; sourceTree = ""; }; + 71927C921472DF74000AB618 /* errdo.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = errdo.c; path = lencod/src/errdo.c; sourceTree = ""; }; + 71927C931472DF74000AB618 /* explicit_gop.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = explicit_gop.c; path = lencod/src/explicit_gop.c; sourceTree = ""; }; + 71927C941472DF74000AB618 /* explicit_seq.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = explicit_seq.c; path = lencod/src/explicit_seq.c; sourceTree = ""; }; + 71927C951472DF74000AB618 /* filehandle.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = filehandle.c; path = lencod/src/filehandle.c; sourceTree = ""; }; + 71927C961472DF74000AB618 /* fmo.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = fmo.c; path = lencod/src/fmo.c; sourceTree = ""; }; + 71927C971472DF74000AB618 /* get_block_otf.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = get_block_otf.c; path = lencod/src/get_block_otf.c; sourceTree = ""; }; + 71927C981472DF74000AB618 /* header.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = header.c; path = lencod/src/header.c; sourceTree = ""; }; + 71927C991472DF74000AB618 /* image_mp.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = image_mp.c; path = lencod/src/image_mp.c; sourceTree = ""; }; + 71927C9A1472DF74000AB618 /* image.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = image.c; path = lencod/src/image.c; sourceTree = ""; }; + 71927C9B1472DF74000AB618 /* img_chroma.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = img_chroma.c; path = lencod/src/img_chroma.c; sourceTree = ""; }; + 71927C9C1472DF74000AB618 /* img_dist_ms_ssim.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = img_dist_ms_ssim.c; path = lencod/src/img_dist_ms_ssim.c; sourceTree = ""; }; + 71927C9D1472DF74000AB618 /* img_dist_snr.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = img_dist_snr.c; path = lencod/src/img_dist_snr.c; sourceTree = ""; }; + 71927C9E1472DF74000AB618 /* img_dist_ssim.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = img_dist_ssim.c; path = lencod/src/img_dist_ssim.c; sourceTree = ""; }; + 71927C9F1472DF74000AB618 /* img_distortion.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = img_distortion.c; path = lencod/src/img_distortion.c; sourceTree = ""; }; + 71927CA01472DF74000AB618 /* img_luma.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = img_luma.c; path = lencod/src/img_luma.c; sourceTree = ""; }; + 71927CA11472DF74000AB618 /* intra_chroma.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = intra_chroma.c; path = lencod/src/intra_chroma.c; sourceTree = ""; }; + 71927CA21472DF74000AB618 /* intra4x4.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = intra4x4.c; path = lencod/src/intra4x4.c; sourceTree = ""; }; + 71927CA31472DF74000AB618 /* intra8x8.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = intra8x8.c; path = lencod/src/intra8x8.c; sourceTree = ""; }; + 71927CA41472DF74000AB618 /* intra16x16.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = intra16x16.c; path = lencod/src/intra16x16.c; sourceTree = ""; }; + 71927CA51472DF74000AB618 /* intrarefresh.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = intrarefresh.c; path = lencod/src/intrarefresh.c; sourceTree = ""; }; + 71927CA61472DF74000AB618 /* lambda.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lambda.c; path = lencod/src/lambda.c; sourceTree = ""; }; + 71927CA71472DF74000AB618 /* leaky_bucket.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = leaky_bucket.c; path = lencod/src/leaky_bucket.c; sourceTree = ""; }; + 71927CA81472DF74000AB618 /* lencod.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lencod.c; path = lencod/src/lencod.c; sourceTree = ""; }; + 71927CA91472DF74000AB618 /* list_reorder.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = list_reorder.c; path = lencod/src/list_reorder.c; sourceTree = ""; }; + 71927CAA1472DF74000AB618 /* lln_mc_prediction.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lln_mc_prediction.c; path = lencod/src/lln_mc_prediction.c; sourceTree = ""; }; + 71927CAB1472DF74000AB618 /* loop_filter_mbaff.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = loop_filter_mbaff.c; path = lencod/src/loop_filter_mbaff.c; sourceTree = ""; }; + 71927CAC1472DF74000AB618 /* loop_filter_normal.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = loop_filter_normal.c; path = lencod/src/loop_filter_normal.c; sourceTree = ""; }; + 71927CAD1472DF74000AB618 /* loopFilter.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = loopFilter.c; path = lencod/src/loopFilter.c; sourceTree = ""; }; + 71927CAE1472DF74000AB618 /* macroblock_P444.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = macroblock_P444.c; path = lencod/src/macroblock_P444.c; sourceTree = ""; }; + 71927CAF1472DF74000AB618 /* macroblock.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = macroblock.c; path = lencod/src/macroblock.c; sourceTree = ""; }; + 71927CB01472DF74000AB618 /* mb_access.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = mb_access.c; path = lencod/src/mb_access.c; sourceTree = ""; }; + 71927CB11472DF74000AB618 /* mbuffer.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = mbuffer.c; path = lencod/src/mbuffer.c; sourceTree = ""; }; + 71927CB21472DF74000AB618 /* mc_prediction_otf.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = mc_prediction_otf.c; path = lencod/src/mc_prediction_otf.c; sourceTree = ""; }; + 71927CB31472DF74000AB618 /* mc_prediction.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = mc_prediction.c; path = lencod/src/mc_prediction.c; sourceTree = ""; }; + 71927CB41472DF74000AB618 /* md_common.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = md_common.c; path = lencod/src/md_common.c; sourceTree = ""; }; + 71927CB51472DF74000AB618 /* md_distortion.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = md_distortion.c; path = lencod/src/md_distortion.c; sourceTree = ""; }; + 71927CB61472DF74000AB618 /* md_high.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = md_high.c; path = lencod/src/md_high.c; sourceTree = ""; }; + 71927CB71472DF74000AB618 /* md_highfast.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = md_highfast.c; path = lencod/src/md_highfast.c; sourceTree = ""; }; + 71927CB81472DF74000AB618 /* md_highloss.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = md_highloss.c; path = lencod/src/md_highloss.c; sourceTree = ""; }; + 71927CB91472DF74000AB618 /* md_low.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = md_low.c; path = lencod/src/md_low.c; sourceTree = ""; }; + 71927CBA1472DF74000AB618 /* me_distortion_otf.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = me_distortion_otf.c; path = lencod/src/me_distortion_otf.c; sourceTree = ""; }; + 71927CBB1472DF74000AB618 /* me_distortion.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = me_distortion.c; path = lencod/src/me_distortion.c; sourceTree = ""; }; + 71927CBC1472DF74000AB618 /* me_epzs_common.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = me_epzs_common.c; path = lencod/src/me_epzs_common.c; sourceTree = ""; }; + 71927CBD1472DF74000AB618 /* me_epzs_int.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = me_epzs_int.c; path = lencod/src/me_epzs_int.c; sourceTree = ""; }; + 71927CBE1472DF74000AB618 /* me_epzs_sub.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = me_epzs_sub.c; path = lencod/src/me_epzs_sub.c; sourceTree = ""; }; + 71927CBF1472DF74000AB618 /* me_epzs.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = me_epzs.c; path = lencod/src/me_epzs.c; sourceTree = ""; }; + 71927CC01472DF74000AB618 /* me_fullfast_otf.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = me_fullfast_otf.c; path = lencod/src/me_fullfast_otf.c; sourceTree = ""; }; + 71927CC11472DF74000AB618 /* me_fullfast.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = me_fullfast.c; path = lencod/src/me_fullfast.c; sourceTree = ""; }; + 71927CC21472DF74000AB618 /* me_fullsearch.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = me_fullsearch.c; path = lencod/src/me_fullsearch.c; sourceTree = ""; }; + 71927CC31472DF74000AB618 /* me_umhex.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = me_umhex.c; path = lencod/src/me_umhex.c; sourceTree = ""; }; + 71927CC41472DF74000AB618 /* me_umhexsmp.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = me_umhexsmp.c; path = lencod/src/me_umhexsmp.c; sourceTree = ""; }; + 71927CC51472DF74000AB618 /* mmco.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = mmco.c; path = lencod/src/mmco.c; sourceTree = ""; }; + 71927CC61472DF74000AB618 /* mode_decision_P8x8.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = mode_decision_P8x8.c; path = lencod/src/mode_decision_P8x8.c; sourceTree = ""; }; + 71927CC71472DF74000AB618 /* mode_decision.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = mode_decision.c; path = lencod/src/mode_decision.c; sourceTree = ""; }; + 71927CC81472DF74000AB618 /* mv_direct.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = mv_direct.c; path = lencod/src/mv_direct.c; sourceTree = ""; }; + 71927CC91472DF74000AB618 /* mv_search.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = mv_search.c; path = lencod/src/mv_search.c; sourceTree = ""; }; + 71927CCA1472DF74000AB618 /* nal.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = nal.c; path = lencod/src/nal.c; sourceTree = ""; }; + 71927CCB1472DF74000AB618 /* nalu.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = nalu.c; path = lencod/src/nalu.c; sourceTree = ""; }; + 71927CCC1472DF74000AB618 /* output.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = output.c; path = lencod/src/output.c; sourceTree = ""; }; + 71927CCD1472DF74000AB618 /* parset.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = parset.c; path = lencod/src/parset.c; sourceTree = ""; }; + 71927CCE1472DF74000AB618 /* pred_struct.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = pred_struct.c; path = lencod/src/pred_struct.c; sourceTree = ""; }; + 71927CCF1472DF74000AB618 /* q_around.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = q_around.c; path = lencod/src/q_around.c; sourceTree = ""; }; + 71927CD01472DF74000AB618 /* q_matrix.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = q_matrix.c; path = lencod/src/q_matrix.c; sourceTree = ""; }; + 71927CD11472DF74000AB618 /* q_offsets.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = q_offsets.c; path = lencod/src/q_offsets.c; sourceTree = ""; }; + 71927CD21472DF74000AB618 /* quant4x4_2step.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = quant4x4_2step.c; path = lencod/src/quant4x4_2step.c; sourceTree = ""; }; + 71927CD31472DF74000AB618 /* quant4x4_around.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = quant4x4_around.c; path = lencod/src/quant4x4_around.c; sourceTree = ""; }; + 71927CD41472DF74000AB618 /* quant4x4_normal.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = quant4x4_normal.c; path = lencod/src/quant4x4_normal.c; sourceTree = ""; }; + 71927CD51472DF74000AB618 /* quant4x4_trellis.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = quant4x4_trellis.c; path = lencod/src/quant4x4_trellis.c; sourceTree = ""; }; + 71927CD61472DF74000AB618 /* quant4x4.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = quant4x4.c; path = lencod/src/quant4x4.c; sourceTree = ""; }; + 71927CD71472DF74000AB618 /* quant8x8_around.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = quant8x8_around.c; path = lencod/src/quant8x8_around.c; sourceTree = ""; }; + 71927CD81472DF74000AB618 /* quant8x8_normal.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = quant8x8_normal.c; path = lencod/src/quant8x8_normal.c; sourceTree = ""; }; + 71927CD91472DF74000AB618 /* quant8x8_trellis.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = quant8x8_trellis.c; path = lencod/src/quant8x8_trellis.c; sourceTree = ""; }; + 71927CDA1472DF74000AB618 /* quant8x8.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = quant8x8.c; path = lencod/src/quant8x8.c; sourceTree = ""; }; + 71927CDB1472DF74000AB618 /* quantChroma_2step.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = quantChroma_2step.c; path = lencod/src/quantChroma_2step.c; sourceTree = ""; }; + 71927CDC1472DF74000AB618 /* quantChroma_around.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = quantChroma_around.c; path = lencod/src/quantChroma_around.c; sourceTree = ""; }; + 71927CDD1472DF74000AB618 /* quantChroma_normal.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = quantChroma_normal.c; path = lencod/src/quantChroma_normal.c; sourceTree = ""; }; + 71927CDE1472DF74000AB618 /* quantChroma_trellis.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = quantChroma_trellis.c; path = lencod/src/quantChroma_trellis.c; sourceTree = ""; }; + 71927CDF1472DF74000AB618 /* quantChroma.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = quantChroma.c; path = lencod/src/quantChroma.c; sourceTree = ""; }; + 71927CE01472DF74000AB618 /* ratectl.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = ratectl.c; path = lencod/src/ratectl.c; sourceTree = ""; }; + 71927CE11472DF74000AB618 /* rc_quadratic.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = rc_quadratic.c; path = lencod/src/rc_quadratic.c; sourceTree = ""; }; + 71927CE21472DF74000AB618 /* rd_intra_jm_low.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = rd_intra_jm_low.c; path = lencod/src/rd_intra_jm_low.c; sourceTree = ""; }; + 71927CE31472DF74000AB618 /* rd_intra_jm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = rd_intra_jm.c; path = lencod/src/rd_intra_jm.c; sourceTree = ""; }; + 71927CE41472DF74000AB618 /* rd_intra_jm444.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = rd_intra_jm444.c; path = lencod/src/rd_intra_jm444.c; sourceTree = ""; }; + 71927CE51472DF74000AB618 /* rdopt_coding_state.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = rdopt_coding_state.c; path = lencod/src/rdopt_coding_state.c; sourceTree = ""; }; + 71927CE61472DF74000AB618 /* rdopt.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = rdopt.c; path = lencod/src/rdopt.c; sourceTree = ""; }; + 71927CE71472DF74000AB618 /* rdoq_cabac.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = rdoq_cabac.c; path = lencod/src/rdoq_cabac.c; sourceTree = ""; }; + 71927CE81472DF74000AB618 /* rdoq_cavlc.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = rdoq_cavlc.c; path = lencod/src/rdoq_cavlc.c; sourceTree = ""; }; + 71927CE91472DF74000AB618 /* rdoq.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = rdoq.c; path = lencod/src/rdoq.c; sourceTree = ""; }; + 71927CEA1472DF74000AB618 /* rdpicdecision.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = rdpicdecision.c; path = lencod/src/rdpicdecision.c; sourceTree = ""; }; + 71927CEB1472DF74000AB618 /* report.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = report.c; path = lencod/src/report.c; sourceTree = ""; }; + 71927CEC1472DF74000AB618 /* rtp.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = rtp.c; path = lencod/src/rtp.c; sourceTree = ""; }; + 71927CED1472DF74000AB618 /* sei.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = sei.c; path = lencod/src/sei.c; sourceTree = ""; }; + 71927CEE1472DF74000AB618 /* slice.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = slice.c; path = lencod/src/slice.c; sourceTree = ""; }; + 71927CEF1472DF74000AB618 /* symbol.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = symbol.c; path = lencod/src/symbol.c; sourceTree = ""; }; + 71927CF01472DF74000AB618 /* transform8x8_H444.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = transform8x8_H444.c; path = lencod/src/transform8x8_H444.c; sourceTree = ""; }; + 71927CF11472DF74000AB618 /* transform8x8.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = transform8x8.c; path = lencod/src/transform8x8.c; sourceTree = ""; }; + 71927CF21472DF74000AB618 /* vlc.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = vlc.c; path = lencod/src/vlc.c; sourceTree = ""; }; + 71927CF31472DF74000AB618 /* weighted_prediction.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = weighted_prediction.c; path = lencod/src/weighted_prediction.c; sourceTree = ""; }; + 71927CF41472DF74000AB618 /* wp_lms.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = wp_lms.c; path = lencod/src/wp_lms.c; sourceTree = ""; }; + 71927CF51472DF74000AB618 /* wp_mciter.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = wp_mciter.c; path = lencod/src/wp_mciter.c; sourceTree = ""; }; + 71927CF61472DF74000AB618 /* wp_mcprec.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = wp_mcprec.c; path = lencod/src/wp_mcprec.c; sourceTree = ""; }; + 71927D651472DF98000AB618 /* annexb.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = annexb.c; sourceTree = ""; }; + 71927D661472DF98000AB618 /* biaridecod.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = biaridecod.c; sourceTree = ""; }; + 71927D671472DF98000AB618 /* block.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = block.c; sourceTree = ""; }; + 71927D681472DF98000AB618 /* cabac.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = cabac.c; sourceTree = ""; }; + 71927D691472DF98000AB618 /* configfile.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = configfile.c; sourceTree = ""; }; + 71927D6A1472DF98000AB618 /* context_ini.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = context_ini.c; sourceTree = ""; }; + 71927D6B1472DF98000AB618 /* dec_statistics.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = dec_statistics.c; sourceTree = ""; }; + 71927D6C1472DF98000AB618 /* decoder_test.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = decoder_test.c; sourceTree = ""; }; + 71927D6D1472DF98000AB618 /* erc_api.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = erc_api.c; sourceTree = ""; }; + 71927D6E1472DF98000AB618 /* erc_do_i.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = erc_do_i.c; sourceTree = ""; }; + 71927D6F1472DF98000AB618 /* erc_do_p.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = erc_do_p.c; sourceTree = ""; }; + 71927D701472DF98000AB618 /* errorconcealment.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = errorconcealment.c; sourceTree = ""; }; + 71927D711472DF98000AB618 /* filehandle.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = filehandle.c; sourceTree = ""; }; + 71927D721472DF98000AB618 /* fmo.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = fmo.c; sourceTree = ""; }; + 71927D731472DF98000AB618 /* header.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = header.c; sourceTree = ""; }; + 71927D741472DF98000AB618 /* image.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = image.c; sourceTree = ""; }; + 71927D751472DF98000AB618 /* intra_chroma_pred_mbaff.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = intra_chroma_pred_mbaff.c; sourceTree = ""; }; + 71927D761472DF98000AB618 /* intra_chroma_pred.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = intra_chroma_pred.c; sourceTree = ""; }; + 71927D771472DF98000AB618 /* intra_pred_common.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = intra_pred_common.c; sourceTree = ""; }; + 71927D781472DF98000AB618 /* intra4x4_pred_mbaff.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = intra4x4_pred_mbaff.c; sourceTree = ""; }; + 71927D791472DF98000AB618 /* intra4x4_pred_normal.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = intra4x4_pred_normal.c; sourceTree = ""; }; + 71927D7A1472DF98000AB618 /* intra4x4_pred.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = intra4x4_pred.c; sourceTree = ""; }; + 71927D7B1472DF98000AB618 /* intra8x8_pred_mbaff.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = intra8x8_pred_mbaff.c; sourceTree = ""; }; + 71927D7C1472DF98000AB618 /* intra8x8_pred_normal.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = intra8x8_pred_normal.c; sourceTree = ""; }; + 71927D7D1472DF98000AB618 /* intra8x8_pred.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = intra8x8_pred.c; sourceTree = ""; }; + 71927D7E1472DF98000AB618 /* intra16x16_pred_mbaff.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = intra16x16_pred_mbaff.c; sourceTree = ""; }; + 71927D7F1472DF98000AB618 /* intra16x16_pred_normal.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = intra16x16_pred_normal.c; sourceTree = ""; }; + 71927D801472DF98000AB618 /* intra16x16_pred.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = intra16x16_pred.c; sourceTree = ""; }; + 71927D811472DF98000AB618 /* ldecod.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ldecod.c; sourceTree = ""; }; + 71927D821472DF98000AB618 /* leaky_bucket.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = leaky_bucket.c; sourceTree = ""; }; + 71927D831472DF98000AB618 /* loop_filter_mbaff.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = loop_filter_mbaff.c; sourceTree = ""; }; + 71927D841472DF98000AB618 /* loop_filter_normal.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = loop_filter_normal.c; sourceTree = ""; }; + 71927D851472DF98000AB618 /* loopFilter.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = loopFilter.c; sourceTree = ""; }; + 71927D861472DF98000AB618 /* macroblock.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = macroblock.c; sourceTree = ""; }; + 71927D871472DF98000AB618 /* mb_access.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = mb_access.c; sourceTree = ""; }; + 71927D881472DF98000AB618 /* mb_prediction.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = mb_prediction.c; sourceTree = ""; }; + 71927D891472DF98000AB618 /* mb_read.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = mb_read.c; sourceTree = ""; }; + 71927D8A1472DF98000AB618 /* mbuffer_mvc.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = mbuffer_mvc.c; sourceTree = ""; }; + 71927D8B1472DF98000AB618 /* mbuffer.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = mbuffer.c; sourceTree = ""; }; + 71927D8C1472DF98000AB618 /* mc_direct.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = mc_direct.c; sourceTree = ""; }; + 71927D8D1472DF98000AB618 /* mc_prediction.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = mc_prediction.c; sourceTree = ""; }; + 71927D8E1472DF98000AB618 /* nal.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = nal.c; sourceTree = ""; }; + 71927D8F1472DF98000AB618 /* nalu.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = nalu.c; sourceTree = ""; }; + 71927D901472DF98000AB618 /* output.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = output.c; sourceTree = ""; }; + 71927D911472DF98000AB618 /* parset.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = parset.c; sourceTree = ""; }; + 71927D921472DF98000AB618 /* quant.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = quant.c; sourceTree = ""; }; + 71927D931472DF98000AB618 /* read_comp_cabac.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = read_comp_cabac.c; sourceTree = ""; }; + 71927D941472DF98000AB618 /* read_comp_cavlc.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = read_comp_cavlc.c; sourceTree = ""; }; + 71927D951472DF98000AB618 /* rtp.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = rtp.c; sourceTree = ""; }; + 71927D961472DF98000AB618 /* sei.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = sei.c; sourceTree = ""; }; + 71927D971472DF98000AB618 /* transform8x8.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = transform8x8.c; sourceTree = ""; }; + 71927D981472DF98000AB618 /* vlc.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = vlc.c; sourceTree = ""; }; + 71927DCD1472DFB3000AB618 /* annexb.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = annexb.h; sourceTree = ""; }; + 71927DCE1472DFB3000AB618 /* biaridecod.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = biaridecod.h; sourceTree = ""; }; + 71927DCF1472DFB3000AB618 /* block.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = block.h; sourceTree = ""; }; + 71927DD01472DFB3000AB618 /* cabac.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = cabac.h; sourceTree = ""; }; + 71927DD11472DFB3000AB618 /* configfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = configfile.h; sourceTree = ""; }; + 71927DD21472DFB3000AB618 /* context_ini.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = context_ini.h; sourceTree = ""; }; + 71927DD31472DFB3000AB618 /* contributors.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = contributors.h; sourceTree = ""; }; + 71927DD41472DFB3000AB618 /* dec_statistics.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = dec_statistics.h; sourceTree = ""; }; + 71927DD51472DFB3000AB618 /* defines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = defines.h; sourceTree = ""; }; + 71927DD61472DFB3000AB618 /* elements.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = elements.h; sourceTree = ""; }; + 71927DD71472DFB3000AB618 /* erc_api.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = erc_api.h; sourceTree = ""; }; + 71927DD81472DFB3000AB618 /* erc_do.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = erc_do.h; sourceTree = ""; }; + 71927DD91472DFB3000AB618 /* erc_globals.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = erc_globals.h; sourceTree = ""; }; + 71927DDA1472DFB3000AB618 /* errorconcealment.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = errorconcealment.h; sourceTree = ""; }; + 71927DDB1472DFB3000AB618 /* filehandle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = filehandle.h; sourceTree = ""; }; + 71927DDC1472DFB3000AB618 /* fmo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = fmo.h; sourceTree = ""; }; + 71927DDD1472DFB3000AB618 /* global.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = global.h; sourceTree = ""; }; + 71927DDE1472DFB3000AB618 /* h264decoder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = h264decoder.h; sourceTree = ""; }; + 71927DDF1472DFB3000AB618 /* header.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = header.h; sourceTree = ""; }; + 71927DE01472DFB3000AB618 /* image.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = image.h; sourceTree = ""; }; + 71927DE11472DFB3000AB618 /* intra4x4_pred.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = intra4x4_pred.h; sourceTree = ""; }; + 71927DE21472DFB3000AB618 /* intra8x8_pred.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = intra8x8_pred.h; sourceTree = ""; }; + 71927DE31472DFB3000AB618 /* intra16x16_pred.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = intra16x16_pred.h; sourceTree = ""; }; + 71927DE41472DFB3000AB618 /* leaky_bucket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = leaky_bucket.h; sourceTree = ""; }; + 71927DE51472DFB3000AB618 /* loop_filter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = loop_filter.h; sourceTree = ""; }; + 71927DE61472DFB3000AB618 /* loopfilter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = loopfilter.h; sourceTree = ""; }; + 71927DE71472DFB3000AB618 /* macroblock.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = macroblock.h; sourceTree = ""; }; + 71927DE81472DFB3000AB618 /* mb_prediction.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mb_prediction.h; sourceTree = ""; }; + 71927DE91472DFB3000AB618 /* mbuffer_mvc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mbuffer_mvc.h; sourceTree = ""; }; + 71927DEA1472DFB3000AB618 /* mbuffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mbuffer.h; sourceTree = ""; }; + 71927DEB1472DFB3000AB618 /* mc_prediction.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mc_prediction.h; sourceTree = ""; }; + 71927DEC1472DFB3000AB618 /* nalu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = nalu.h; sourceTree = ""; }; + 71927DED1472DFB3000AB618 /* output.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = output.h; sourceTree = ""; }; + 71927DEE1472DFB3000AB618 /* parset.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = parset.h; sourceTree = ""; }; + 71927DEF1472DFB3000AB618 /* quant.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = quant.h; sourceTree = ""; }; + 71927DF01472DFB3000AB618 /* rtp.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = rtp.h; sourceTree = ""; }; + 71927DF11472DFB3000AB618 /* sei.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = sei.h; sourceTree = ""; }; + 71927DF21472DFB3000AB618 /* transform8x8.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = transform8x8.h; sourceTree = ""; }; + 71927DF31472DFB3000AB618 /* vlc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = vlc.h; sourceTree = ""; }; + 71927DF41472DFCB000AB618 /* blk_prediction.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = blk_prediction.h; sourceTree = ""; }; + 71927DF51472DFCB000AB618 /* config_common.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = config_common.h; sourceTree = ""; }; + 71927DF61472DFCB000AB618 /* ctx_tables.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ctx_tables.h; sourceTree = ""; }; + 71927DF71472DFCB000AB618 /* distortion.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = distortion.h; sourceTree = ""; }; + 71927DF81472DFCB000AB618 /* enc_statistics.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = enc_statistics.h; sourceTree = ""; }; + 71927DF91472DFCB000AB618 /* fast_memory.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = fast_memory.h; sourceTree = ""; }; + 71927DFA1472DFCB000AB618 /* frame.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = frame.h; sourceTree = ""; }; + 71927DFB1472DFCB000AB618 /* ifunctions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ifunctions.h; sourceTree = ""; }; + 71927DFC1472DFCB000AB618 /* img_io.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = img_io.h; sourceTree = ""; }; + 71927DFD1472DFCB000AB618 /* img_process_types.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = img_process_types.h; sourceTree = ""; }; + 71927DFE1472DFCB000AB618 /* img_process.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = img_process.h; sourceTree = ""; }; + 71927DFF1472DFCB000AB618 /* input.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = input.h; sourceTree = ""; }; + 71927E001472DFCB000AB618 /* io_image.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = io_image.h; sourceTree = ""; }; + 71927E011472DFCB000AB618 /* io_raw.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = io_raw.h; sourceTree = ""; }; + 71927E021472DFCB000AB618 /* io_tiff.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = io_tiff.h; sourceTree = ""; }; + 71927E031472DFCB000AB618 /* io_video.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = io_video.h; sourceTree = ""; }; + 71927E041472DFCB000AB618 /* lagrangian.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = lagrangian.h; sourceTree = ""; }; + 71927E051472DFCB000AB618 /* mb_access.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mb_access.h; sourceTree = ""; }; + 71927E061472DFCB000AB618 /* mbuffer_common.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mbuffer_common.h; sourceTree = ""; }; + 71927E071472DFCB000AB618 /* memalloc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = memalloc.h; sourceTree = ""; }; + 71927E081472DFCB000AB618 /* mv_prediction.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mv_prediction.h; sourceTree = ""; }; + 71927E091472DFCB000AB618 /* nalucommon.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = nalucommon.h; sourceTree = ""; }; + 71927E0A1472DFCB000AB618 /* parsetcommon.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = parsetcommon.h; sourceTree = ""; }; + 71927E0B1472DFCB000AB618 /* quant_params.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = quant_params.h; sourceTree = ""; }; + 71927E0C1472DFCB000AB618 /* report.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = report.h; sourceTree = ""; }; + 71927E0D1472DFCB000AB618 /* transform.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = transform.h; sourceTree = ""; }; + 71927E0E1472DFCB000AB618 /* typedefs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = typedefs.h; sourceTree = ""; }; + 71927E0F1472DFCB000AB618 /* types.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = types.h; sourceTree = ""; }; + 71927E101472DFCB000AB618 /* vui_params.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = vui_params.h; sourceTree = ""; }; + 71927E111472DFCB000AB618 /* win32.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = win32.h; sourceTree = ""; }; + 71927E121472DFE1000AB618 /* blk_prediction.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = blk_prediction.c; sourceTree = ""; }; + 71927E131472DFE1000AB618 /* config_common.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = config_common.c; sourceTree = ""; }; + 71927E141472DFE1000AB618 /* img_io.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = img_io.c; sourceTree = ""; }; + 71927E151472DFE1000AB618 /* img_process.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = img_process.c; sourceTree = ""; }; + 71927E161472DFE1000AB618 /* input.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = input.c; sourceTree = ""; }; + 71927E171472DFE1000AB618 /* io_raw.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = io_raw.c; sourceTree = ""; }; + 71927E181472DFE1000AB618 /* io_tiff.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = io_tiff.c; sourceTree = ""; }; + 71927E191472DFE1000AB618 /* mbuffer_common.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = mbuffer_common.c; sourceTree = ""; }; + 71927E1A1472DFE1000AB618 /* memalloc.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = memalloc.c; sourceTree = ""; }; + 71927E1B1472DFE1000AB618 /* mv_prediction.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = mv_prediction.c; sourceTree = ""; }; + 71927E1C1472DFE1000AB618 /* nalucommon.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = nalucommon.c; sourceTree = ""; }; + 71927E1D1472DFE1000AB618 /* parsetcommon.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = parsetcommon.c; sourceTree = ""; }; + 71927E1E1472DFE1000AB618 /* transform.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = transform.c; sourceTree = ""; }; + 71927E1F1472DFE1000AB618 /* win32.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = win32.c; sourceTree = ""; }; + 8DD76FB20486AB0100D96B5E /* lencod */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = lencod; sourceTree = BUILT_PRODUCTS_DIR; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 436FC2970D85FB4900D8E371 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 436FC2A50D85FC1C00D8E371 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 43FA4B100D4EECBB001709F1 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 8DD76FAD0486AB0100D96B5E /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 08FB7794FE84155DC02AAC07 /* JM */ = { + isa = PBXGroup; + children = ( + 71C9040E0FE2828A0095EA3A /* lcommon */, + 43FA4B190D4EED45001709F1 /* ldecod */, + 43FA4A0F0D4EE917001709F1 /* lencod */, + 436FC24E0D85FA8A00D8E371 /* rtp_loss */, + 436FC24F0D85FA9A00D8E371 /* rtpdump */, + 434D2ABF0D5C1C0D00610974 /* Configs */, + 1AB674ADFE9D54B511CA2CBB /* Products */, + ); + name = JM; + sourceTree = ""; + }; + 1AB674ADFE9D54B511CA2CBB /* Products */ = { + isa = PBXGroup; + children = ( + 8DD76FB20486AB0100D96B5E /* lencod */, + 43FA4B170D4EECBB001709F1 /* ldecod */, + 436FC29C0D85FB4900D8E371 /* rtpdump */, + 436FC2AA0D85FC1C00D8E371 /* rtp_loss */, + ); + name = Products; + sourceTree = ""; + }; + 434D2ABF0D5C1C0D00610974 /* Configs */ = { + isa = PBXGroup; + children = ( + 434D2AC20D5C1C3E00610974 /* decoder.cfg */, + 434D2AC40D5C1C3E00610974 /* encoder.cfg */, + 434D2AC50D5C1C3E00610974 /* encoder_baseline.cfg */, + 434D2AC60D5C1C3E00610974 /* encoder_extended.cfg */, + 434D2AC80D5C1C3E00610974 /* encoder_main.cfg */, + 434D2AC90D5C1C3E00610974 /* encoder_tonemapping.cfg */, + 434D2ACA0D5C1C3E00610974 /* encoder_yuv422.cfg */, + 434D2ACD0D5C1C3E00610974 /* leakybucketrate.cfg */, + 434D2ACE0D5C1C3E00610974 /* q_matrix.cfg */, + 434D2ACF0D5C1C3E00610974 /* q_matrix2.cfg */, + 434D2AD00D5C1C3E00610974 /* q_matrix_def.cfg */, + 434D2AD10D5C1C3E00610974 /* q_offset.cfg */, + 434D2AD30D5C1C3E00610974 /* sg0conf.cfg */, + 434D2AD40D5C1C3E00610974 /* sg2conf.cfg */, + 434D2AD50D5C1C3E00610974 /* sg6conf.cfg */, + 434D2AD80D5C1C3E00610974 /* ToneMapping.cfg */, + ); + name = Configs; + sourceTree = ""; + }; + 436FC24E0D85FA8A00D8E371 /* rtp_loss */ = { + isa = PBXGroup; + children = ( + 436FC2AE0D85FC4B00D8E371 /* ReadMe.txt */, + 436FC2AD0D85FC4B00D8E371 /* rtp_loss.cpp */, + 436FC2AC0D85FC4B00D8E371 /* rtp_loss.h */, + ); + name = rtp_loss; + sourceTree = ""; + }; + 436FC24F0D85FA9A00D8E371 /* rtpdump */ = { + isa = PBXGroup; + children = ( + 436FC25A0D85FB3900D8E371 /* rtpdump.cpp */, + ); + name = rtpdump; + sourceTree = ""; + }; + 43FA4A0F0D4EE917001709F1 /* lencod */ = { + isa = PBXGroup; + children = ( + 43FA4A100D4EE91C001709F1 /* inc */, + 43FA4A110D4EE920001709F1 /* src */, + ); + name = lencod; + sourceTree = ""; + }; + 43FA4A100D4EE91C001709F1 /* inc */ = { + isa = PBXGroup; + children = ( + 71927C2D1472DF4A000AB618 /* annexb.h */, + 71927C2E1472DF4A000AB618 /* biariencode.h */, + 71927C2F1472DF4A000AB618 /* block.h */, + 71927C301472DF4A000AB618 /* cabac.h */, + 71927C311472DF4A000AB618 /* cconv_yuv2rgb.h */, + 71927C321472DF4A000AB618 /* configfile.h */, + 71927C331472DF4A000AB618 /* conformance.h */, + 71927C341472DF4A000AB618 /* context_ini.h */, + 71927C351472DF4A000AB618 /* contributors.h */, + 71927C361472DF4A000AB618 /* defines.h */, + 71927C371472DF4A000AB618 /* elements.h */, + 71927C381472DF4A000AB618 /* errdo_dist_mhyp.h */, + 71927C391472DF4A000AB618 /* errdo.h */, + 71927C3A1472DF4A000AB618 /* explicit_gop.h */, + 71927C3B1472DF4A000AB618 /* explicit_seq.h */, + 71927C3C1472DF4A000AB618 /* filehandle.h */, + 71927C3D1472DF4A000AB618 /* fmo.h */, + 71927C3E1472DF4A000AB618 /* get_block_otf.h */, + 71927C3F1472DF4A000AB618 /* global.h */, + 71927C401472DF4A000AB618 /* header.h */, + 71927C411472DF4A000AB618 /* image.h */, + 71927C421472DF4A000AB618 /* img_chroma.h */, + 71927C431472DF4A000AB618 /* img_dist_ms_ssim.h */, + 71927C441472DF4A000AB618 /* img_dist_snr.h */, + 71927C451472DF4A000AB618 /* img_dist_ssim.h */, + 71927C461472DF4A000AB618 /* img_distortion.h */, + 71927C471472DF4A000AB618 /* img_luma.h */, + 71927C481472DF4A000AB618 /* intra4x4.h */, + 71927C491472DF4A000AB618 /* intra8x8.h */, + 71927C4A1472DF4A000AB618 /* intra16x16.h */, + 71927C4B1472DF4A000AB618 /* intrarefresh.h */, + 71927C4C1472DF4A000AB618 /* lambda.h */, + 71927C4D1472DF4A000AB618 /* leaky_bucket.h */, + 71927C4E1472DF4A000AB618 /* list_reorder.h */, + 71927C4F1472DF4A000AB618 /* lln_mc_prediction.h */, + 71927C501472DF4A000AB618 /* loop_filter.h */, + 71927C511472DF4A000AB618 /* macroblock_p444.h */, + 71927C521472DF4A000AB618 /* macroblock.h */, + 71927C531472DF4A000AB618 /* mbuffer.h */, + 71927C541472DF4A000AB618 /* mc_prediction_otf.h */, + 71927C551472DF4A000AB618 /* mc_prediction.h */, + 71927C561472DF4A000AB618 /* md_common.h */, + 71927C571472DF4A000AB618 /* md_distortion.h */, + 71927C581472DF4A000AB618 /* me_distortion_otf.h */, + 71927C591472DF4A000AB618 /* me_distortion.h */, + 71927C5A1472DF4A000AB618 /* me_epzs_common.h */, + 71927C5B1472DF4A000AB618 /* me_epzs_int.h */, + 71927C5C1472DF4A000AB618 /* me_epzs.h */, + 71927C5D1472DF4A000AB618 /* me_fullfast_otf.h */, + 71927C5E1472DF4A000AB618 /* me_fullfast.h */, + 71927C5F1472DF4A000AB618 /* me_fullsearch.h */, + 71927C601472DF4A000AB618 /* me_umhex.h */, + 71927C611472DF4A000AB618 /* me_umhexsmp.h */, + 71927C621472DF4A000AB618 /* mmco.h */, + 71927C631472DF4A000AB618 /* mode_decision_p8x8.h */, + 71927C641472DF4A000AB618 /* mode_decision.h */, + 71927C651472DF4A000AB618 /* mv_search.h */, + 71927C661472DF4A000AB618 /* nal.h */, + 71927C671472DF4A000AB618 /* nalu.h */, + 71927C681472DF4A000AB618 /* output.h */, + 71927C691472DF4A000AB618 /* params.h */, + 71927C6A1472DF4A000AB618 /* parset.h */, + 71927C6B1472DF4A000AB618 /* pred_struct_adapt_types.h */, + 71927C6C1472DF4A000AB618 /* pred_struct_adapt.h */, + 71927C6D1472DF4A000AB618 /* pred_struct_types.h */, + 71927C6E1472DF4A000AB618 /* pred_struct.h */, + 71927C6F1472DF4A000AB618 /* q_around.h */, + 71927C701472DF4A000AB618 /* q_matrix.h */, + 71927C711472DF4A000AB618 /* q_offsets.h */, + 71927C721472DF4A000AB618 /* quant4x4.h */, + 71927C731472DF4A000AB618 /* quant8x8.h */, + 71927C741472DF4A000AB618 /* quantChroma.h */, + 71927C751472DF4A000AB618 /* ratectl.h */, + 71927C761472DF4A000AB618 /* rc_quadratic.h */, + 71927C771472DF4A000AB618 /* rc_types.h */, + 71927C781472DF4A000AB618 /* rd_intra_jm.h */, + 71927C791472DF4A000AB618 /* rd_intra_jm444.h */, + 71927C7A1472DF4A000AB618 /* rdopt_coding_state.h */, + 71927C7B1472DF4A000AB618 /* rdopt.h */, + 71927C7C1472DF4A000AB618 /* rdoq.h */, + 71927C7D1472DF4A000AB618 /* refbuf_otf.h */, + 71927C7E1472DF4A000AB618 /* refbuf.h */, + 71927C7F1472DF4A000AB618 /* rtp.h */, + 71927C801472DF4A000AB618 /* sei.h */, + 71927C811472DF4A000AB618 /* slice.h */, + 71927C821472DF4A000AB618 /* symbol.h */, + 71927C831472DF4A000AB618 /* transform8x8.h */, + 71927C841472DF4A000AB618 /* vlc.h */, + 71927C851472DF4A000AB618 /* wp_lms.h */, + 71927C861472DF4A000AB618 /* wp_mciter.h */, + 71927C871472DF4A000AB618 /* wp_mcprec.h */, + 71927C881472DF4A000AB618 /* wp.h */, + ); + name = inc; + sourceTree = ""; + }; + 43FA4A110D4EE920001709F1 /* src */ = { + isa = PBXGroup; + children = ( + 71927C891472DF74000AB618 /* annexb.c */, + 71927C8A1472DF74000AB618 /* biariencode.c */, + 71927C8B1472DF74000AB618 /* block.c */, + 71927C8C1472DF74000AB618 /* cabac.c */, + 71927C8D1472DF74000AB618 /* cconv_yuv2rgb.c */, + 71927C8E1472DF74000AB618 /* configfile.c */, + 71927C8F1472DF74000AB618 /* conformance.c */, + 71927C901472DF74000AB618 /* context_ini.c */, + 71927C911472DF74000AB618 /* errdo_dist_mhyp.c */, + 71927C921472DF74000AB618 /* errdo.c */, + 71927C931472DF74000AB618 /* explicit_gop.c */, + 71927C941472DF74000AB618 /* explicit_seq.c */, + 71927C951472DF74000AB618 /* filehandle.c */, + 71927C961472DF74000AB618 /* fmo.c */, + 71927C971472DF74000AB618 /* get_block_otf.c */, + 71927C981472DF74000AB618 /* header.c */, + 71927C991472DF74000AB618 /* image_mp.c */, + 71927C9A1472DF74000AB618 /* image.c */, + 71927C9B1472DF74000AB618 /* img_chroma.c */, + 71927C9C1472DF74000AB618 /* img_dist_ms_ssim.c */, + 71927C9D1472DF74000AB618 /* img_dist_snr.c */, + 71927C9E1472DF74000AB618 /* img_dist_ssim.c */, + 71927C9F1472DF74000AB618 /* img_distortion.c */, + 71927CA01472DF74000AB618 /* img_luma.c */, + 71927CA11472DF74000AB618 /* intra_chroma.c */, + 71927CA21472DF74000AB618 /* intra4x4.c */, + 71927CA31472DF74000AB618 /* intra8x8.c */, + 71927CA41472DF74000AB618 /* intra16x16.c */, + 71927CA51472DF74000AB618 /* intrarefresh.c */, + 71927CA61472DF74000AB618 /* lambda.c */, + 71927CA71472DF74000AB618 /* leaky_bucket.c */, + 71927CA81472DF74000AB618 /* lencod.c */, + 71927CA91472DF74000AB618 /* list_reorder.c */, + 71927CAA1472DF74000AB618 /* lln_mc_prediction.c */, + 71927CAB1472DF74000AB618 /* loop_filter_mbaff.c */, + 71927CAC1472DF74000AB618 /* loop_filter_normal.c */, + 71927CAD1472DF74000AB618 /* loopFilter.c */, + 71927CAE1472DF74000AB618 /* macroblock_P444.c */, + 71927CAF1472DF74000AB618 /* macroblock.c */, + 71927CB01472DF74000AB618 /* mb_access.c */, + 71927CB11472DF74000AB618 /* mbuffer.c */, + 71927CB21472DF74000AB618 /* mc_prediction_otf.c */, + 71927CB31472DF74000AB618 /* mc_prediction.c */, + 71927CB41472DF74000AB618 /* md_common.c */, + 71927CB51472DF74000AB618 /* md_distortion.c */, + 71927CB61472DF74000AB618 /* md_high.c */, + 71927CB71472DF74000AB618 /* md_highfast.c */, + 71927CB81472DF74000AB618 /* md_highloss.c */, + 71927CB91472DF74000AB618 /* md_low.c */, + 71927CBA1472DF74000AB618 /* me_distortion_otf.c */, + 71927CBB1472DF74000AB618 /* me_distortion.c */, + 71927CBC1472DF74000AB618 /* me_epzs_common.c */, + 71927CBD1472DF74000AB618 /* me_epzs_int.c */, + 71927CBE1472DF74000AB618 /* me_epzs_sub.c */, + 71927CBF1472DF74000AB618 /* me_epzs.c */, + 71927CC01472DF74000AB618 /* me_fullfast_otf.c */, + 71927CC11472DF74000AB618 /* me_fullfast.c */, + 71927CC21472DF74000AB618 /* me_fullsearch.c */, + 71927CC31472DF74000AB618 /* me_umhex.c */, + 71927CC41472DF74000AB618 /* me_umhexsmp.c */, + 71927CC51472DF74000AB618 /* mmco.c */, + 71927CC61472DF74000AB618 /* mode_decision_P8x8.c */, + 71927CC71472DF74000AB618 /* mode_decision.c */, + 71927CC81472DF74000AB618 /* mv_direct.c */, + 71927CC91472DF74000AB618 /* mv_search.c */, + 71927CCA1472DF74000AB618 /* nal.c */, + 71927CCB1472DF74000AB618 /* nalu.c */, + 71927CCC1472DF74000AB618 /* output.c */, + 71927CCD1472DF74000AB618 /* parset.c */, + 71927CCE1472DF74000AB618 /* pred_struct.c */, + 71927CCF1472DF74000AB618 /* q_around.c */, + 71927CD01472DF74000AB618 /* q_matrix.c */, + 71927CD11472DF74000AB618 /* q_offsets.c */, + 71927CD21472DF74000AB618 /* quant4x4_2step.c */, + 71927CD31472DF74000AB618 /* quant4x4_around.c */, + 71927CD41472DF74000AB618 /* quant4x4_normal.c */, + 71927CD51472DF74000AB618 /* quant4x4_trellis.c */, + 71927CD61472DF74000AB618 /* quant4x4.c */, + 71927CD71472DF74000AB618 /* quant8x8_around.c */, + 71927CD81472DF74000AB618 /* quant8x8_normal.c */, + 71927CD91472DF74000AB618 /* quant8x8_trellis.c */, + 71927CDA1472DF74000AB618 /* quant8x8.c */, + 71927CDB1472DF74000AB618 /* quantChroma_2step.c */, + 71927CDC1472DF74000AB618 /* quantChroma_around.c */, + 71927CDD1472DF74000AB618 /* quantChroma_normal.c */, + 71927CDE1472DF74000AB618 /* quantChroma_trellis.c */, + 71927CDF1472DF74000AB618 /* quantChroma.c */, + 71927CE01472DF74000AB618 /* ratectl.c */, + 71927CE11472DF74000AB618 /* rc_quadratic.c */, + 71927CE21472DF74000AB618 /* rd_intra_jm_low.c */, + 71927CE31472DF74000AB618 /* rd_intra_jm.c */, + 71927CE41472DF74000AB618 /* rd_intra_jm444.c */, + 71927CE51472DF74000AB618 /* rdopt_coding_state.c */, + 71927CE61472DF74000AB618 /* rdopt.c */, + 71927CE71472DF74000AB618 /* rdoq_cabac.c */, + 71927CE81472DF74000AB618 /* rdoq_cavlc.c */, + 71927CE91472DF74000AB618 /* rdoq.c */, + 71927CEA1472DF74000AB618 /* rdpicdecision.c */, + 71927CEB1472DF74000AB618 /* report.c */, + 71927CEC1472DF74000AB618 /* rtp.c */, + 71927CED1472DF74000AB618 /* sei.c */, + 71927CEE1472DF74000AB618 /* slice.c */, + 71927CEF1472DF74000AB618 /* symbol.c */, + 71927CF01472DF74000AB618 /* transform8x8_H444.c */, + 71927CF11472DF74000AB618 /* transform8x8.c */, + 71927CF21472DF74000AB618 /* vlc.c */, + 71927CF31472DF74000AB618 /* weighted_prediction.c */, + 71927CF41472DF74000AB618 /* wp_lms.c */, + 71927CF51472DF74000AB618 /* wp_mciter.c */, + 71927CF61472DF74000AB618 /* wp_mcprec.c */, + ); + name = src; + sourceTree = ""; + }; + 43FA4B190D4EED45001709F1 /* ldecod */ = { + isa = PBXGroup; + children = ( + 71C904B60FE283800095EA3A /* inc */, + 71C904D80FE283800095EA3A /* src */, + ); + name = ldecod; + sourceTree = ""; + }; + 71C9040E0FE2828A0095EA3A /* lcommon */ = { + isa = PBXGroup; + children = ( + 71C904180FE283370095EA3A /* inc */, + 71C904320FE283370095EA3A /* src */, + ); + name = lcommon; + sourceTree = ""; + }; + 71C904180FE283370095EA3A /* inc */ = { + isa = PBXGroup; + children = ( + 71927DF41472DFCB000AB618 /* blk_prediction.h */, + 71927DF51472DFCB000AB618 /* config_common.h */, + 71927DF61472DFCB000AB618 /* ctx_tables.h */, + 71927DF71472DFCB000AB618 /* distortion.h */, + 71927DF81472DFCB000AB618 /* enc_statistics.h */, + 71927DF91472DFCB000AB618 /* fast_memory.h */, + 71927DFA1472DFCB000AB618 /* frame.h */, + 71927DFB1472DFCB000AB618 /* ifunctions.h */, + 71927DFC1472DFCB000AB618 /* img_io.h */, + 71927DFD1472DFCB000AB618 /* img_process_types.h */, + 71927DFE1472DFCB000AB618 /* img_process.h */, + 71927DFF1472DFCB000AB618 /* input.h */, + 71927E001472DFCB000AB618 /* io_image.h */, + 71927E011472DFCB000AB618 /* io_raw.h */, + 71927E021472DFCB000AB618 /* io_tiff.h */, + 71927E031472DFCB000AB618 /* io_video.h */, + 71927E041472DFCB000AB618 /* lagrangian.h */, + 71927E051472DFCB000AB618 /* mb_access.h */, + 71927E061472DFCB000AB618 /* mbuffer_common.h */, + 71927E071472DFCB000AB618 /* memalloc.h */, + 71927E081472DFCB000AB618 /* mv_prediction.h */, + 71927E091472DFCB000AB618 /* nalucommon.h */, + 71927E0A1472DFCB000AB618 /* parsetcommon.h */, + 71927E0B1472DFCB000AB618 /* quant_params.h */, + 71927E0C1472DFCB000AB618 /* report.h */, + 71927E0D1472DFCB000AB618 /* transform.h */, + 71927E0E1472DFCB000AB618 /* typedefs.h */, + 71927E0F1472DFCB000AB618 /* types.h */, + 71927E101472DFCB000AB618 /* vui_params.h */, + 71927E111472DFCB000AB618 /* win32.h */, + ); + name = inc; + path = lcommon/inc; + sourceTree = ""; + }; + 71C904320FE283370095EA3A /* src */ = { + isa = PBXGroup; + children = ( + 71927E121472DFE1000AB618 /* blk_prediction.c */, + 71927E131472DFE1000AB618 /* config_common.c */, + 71927E141472DFE1000AB618 /* img_io.c */, + 71927E151472DFE1000AB618 /* img_process.c */, + 71927E161472DFE1000AB618 /* input.c */, + 71927E171472DFE1000AB618 /* io_raw.c */, + 71927E181472DFE1000AB618 /* io_tiff.c */, + 71927E191472DFE1000AB618 /* mbuffer_common.c */, + 71927E1A1472DFE1000AB618 /* memalloc.c */, + 71927E1B1472DFE1000AB618 /* mv_prediction.c */, + 71927E1C1472DFE1000AB618 /* nalucommon.c */, + 71927E1D1472DFE1000AB618 /* parsetcommon.c */, + 71927E1E1472DFE1000AB618 /* transform.c */, + 71927E1F1472DFE1000AB618 /* win32.c */, + ); + name = src; + path = lcommon/src; + sourceTree = ""; + }; + 71C904B60FE283800095EA3A /* inc */ = { + isa = PBXGroup; + children = ( + 71927DCD1472DFB3000AB618 /* annexb.h */, + 71927DCE1472DFB3000AB618 /* biaridecod.h */, + 71927DCF1472DFB3000AB618 /* block.h */, + 71927DD01472DFB3000AB618 /* cabac.h */, + 71927DD11472DFB3000AB618 /* configfile.h */, + 71927DD21472DFB3000AB618 /* context_ini.h */, + 71927DD31472DFB3000AB618 /* contributors.h */, + 71927DD41472DFB3000AB618 /* dec_statistics.h */, + 71927DD51472DFB3000AB618 /* defines.h */, + 71927DD61472DFB3000AB618 /* elements.h */, + 71927DD71472DFB3000AB618 /* erc_api.h */, + 71927DD81472DFB3000AB618 /* erc_do.h */, + 71927DD91472DFB3000AB618 /* erc_globals.h */, + 71927DDA1472DFB3000AB618 /* errorconcealment.h */, + 71927DDB1472DFB3000AB618 /* filehandle.h */, + 71927DDC1472DFB3000AB618 /* fmo.h */, + 71927DDD1472DFB3000AB618 /* global.h */, + 71927DDE1472DFB3000AB618 /* h264decoder.h */, + 71927DDF1472DFB3000AB618 /* header.h */, + 71927DE01472DFB3000AB618 /* image.h */, + 71927DE11472DFB3000AB618 /* intra4x4_pred.h */, + 71927DE21472DFB3000AB618 /* intra8x8_pred.h */, + 71927DE31472DFB3000AB618 /* intra16x16_pred.h */, + 71927DE41472DFB3000AB618 /* leaky_bucket.h */, + 71927DE51472DFB3000AB618 /* loop_filter.h */, + 71927DE61472DFB3000AB618 /* loopfilter.h */, + 71927DE71472DFB3000AB618 /* macroblock.h */, + 71927DE81472DFB3000AB618 /* mb_prediction.h */, + 71927DE91472DFB3000AB618 /* mbuffer_mvc.h */, + 71927DEA1472DFB3000AB618 /* mbuffer.h */, + 71927DEB1472DFB3000AB618 /* mc_prediction.h */, + 71927DEC1472DFB3000AB618 /* nalu.h */, + 71927DED1472DFB3000AB618 /* output.h */, + 71927DEE1472DFB3000AB618 /* parset.h */, + 71927DEF1472DFB3000AB618 /* quant.h */, + 71927DF01472DFB3000AB618 /* rtp.h */, + 71927DF11472DFB3000AB618 /* sei.h */, + 71927DF21472DFB3000AB618 /* transform8x8.h */, + 71927DF31472DFB3000AB618 /* vlc.h */, + ); + name = inc; + path = ldecod/inc; + sourceTree = ""; + }; + 71C904D80FE283800095EA3A /* src */ = { + isa = PBXGroup; + children = ( + 71927D651472DF98000AB618 /* annexb.c */, + 71927D661472DF98000AB618 /* biaridecod.c */, + 71927D671472DF98000AB618 /* block.c */, + 71927D681472DF98000AB618 /* cabac.c */, + 71927D691472DF98000AB618 /* configfile.c */, + 71927D6A1472DF98000AB618 /* context_ini.c */, + 71927D6B1472DF98000AB618 /* dec_statistics.c */, + 71927D6C1472DF98000AB618 /* decoder_test.c */, + 71927D6D1472DF98000AB618 /* erc_api.c */, + 71927D6E1472DF98000AB618 /* erc_do_i.c */, + 71927D6F1472DF98000AB618 /* erc_do_p.c */, + 71927D701472DF98000AB618 /* errorconcealment.c */, + 71927D711472DF98000AB618 /* filehandle.c */, + 71927D721472DF98000AB618 /* fmo.c */, + 71927D731472DF98000AB618 /* header.c */, + 71927D741472DF98000AB618 /* image.c */, + 71927D751472DF98000AB618 /* intra_chroma_pred_mbaff.c */, + 71927D761472DF98000AB618 /* intra_chroma_pred.c */, + 71927D771472DF98000AB618 /* intra_pred_common.c */, + 71927D781472DF98000AB618 /* intra4x4_pred_mbaff.c */, + 71927D791472DF98000AB618 /* intra4x4_pred_normal.c */, + 71927D7A1472DF98000AB618 /* intra4x4_pred.c */, + 71927D7B1472DF98000AB618 /* intra8x8_pred_mbaff.c */, + 71927D7C1472DF98000AB618 /* intra8x8_pred_normal.c */, + 71927D7D1472DF98000AB618 /* intra8x8_pred.c */, + 71927D7E1472DF98000AB618 /* intra16x16_pred_mbaff.c */, + 71927D7F1472DF98000AB618 /* intra16x16_pred_normal.c */, + 71927D801472DF98000AB618 /* intra16x16_pred.c */, + 71927D811472DF98000AB618 /* ldecod.c */, + 71927D821472DF98000AB618 /* leaky_bucket.c */, + 71927D831472DF98000AB618 /* loop_filter_mbaff.c */, + 71927D841472DF98000AB618 /* loop_filter_normal.c */, + 71927D851472DF98000AB618 /* loopFilter.c */, + 71927D861472DF98000AB618 /* macroblock.c */, + 71927D871472DF98000AB618 /* mb_access.c */, + 71927D881472DF98000AB618 /* mb_prediction.c */, + 71927D891472DF98000AB618 /* mb_read.c */, + 71927D8A1472DF98000AB618 /* mbuffer_mvc.c */, + 71927D8B1472DF98000AB618 /* mbuffer.c */, + 71927D8C1472DF98000AB618 /* mc_direct.c */, + 71927D8D1472DF98000AB618 /* mc_prediction.c */, + 71927D8E1472DF98000AB618 /* nal.c */, + 71927D8F1472DF98000AB618 /* nalu.c */, + 71927D901472DF98000AB618 /* output.c */, + 71927D911472DF98000AB618 /* parset.c */, + 71927D921472DF98000AB618 /* quant.c */, + 71927D931472DF98000AB618 /* read_comp_cabac.c */, + 71927D941472DF98000AB618 /* read_comp_cavlc.c */, + 71927D951472DF98000AB618 /* rtp.c */, + 71927D961472DF98000AB618 /* sei.c */, + 71927D971472DF98000AB618 /* transform8x8.c */, + 71927D981472DF98000AB618 /* vlc.c */, + ); + name = src; + path = ldecod/src; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 436FC25B0D85FB4900D8E371 /* rtpdump */ = { + isa = PBXNativeTarget; + buildConfigurationList = 436FC2990D85FB4900D8E371 /* Build configuration list for PBXNativeTarget "rtpdump" */; + buildPhases = ( + 436FC25C0D85FB4900D8E371 /* Sources */, + 436FC2970D85FB4900D8E371 /* Frameworks */, + 436FC2980D85FB4900D8E371 /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = rtpdump; + productInstallPath = "$(HOME)/bin"; + productName = JM; + productReference = 436FC29C0D85FB4900D8E371 /* rtpdump */; + productType = "com.apple.product-type.tool"; + }; + 436FC2A20D85FC1C00D8E371 /* rtp_loss */ = { + isa = PBXNativeTarget; + buildConfigurationList = 436FC2A70D85FC1C00D8E371 /* Build configuration list for PBXNativeTarget "rtp_loss" */; + buildPhases = ( + 436FC2A30D85FC1C00D8E371 /* Sources */, + 436FC2A50D85FC1C00D8E371 /* Frameworks */, + 436FC2A60D85FC1C00D8E371 /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = rtp_loss; + productInstallPath = "$(HOME)/bin"; + productName = JM; + productReference = 436FC2AA0D85FC1C00D8E371 /* rtp_loss */; + productType = "com.apple.product-type.tool"; + }; + 43FA4AD50D4EECBB001709F1 /* ldecod */ = { + isa = PBXNativeTarget; + buildConfigurationList = 43FA4B140D4EECBB001709F1 /* Build configuration list for PBXNativeTarget "ldecod" */; + buildPhases = ( + 43FA4AD60D4EECBB001709F1 /* Sources */, + 43FA4B100D4EECBB001709F1 /* Frameworks */, + 43FA4B130D4EECBB001709F1 /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = ldecod; + productInstallPath = "$(HOME)/bin"; + productName = JM; + productReference = 43FA4B170D4EECBB001709F1 /* ldecod */; + productType = "com.apple.product-type.tool"; + }; + 8DD76FA90486AB0100D96B5E /* lencod */ = { + isa = PBXNativeTarget; + buildConfigurationList = 1DEB928508733DD80010E9CD /* Build configuration list for PBXNativeTarget "lencod" */; + buildPhases = ( + 8DD76FAB0486AB0100D96B5E /* Sources */, + 8DD76FAD0486AB0100D96B5E /* Frameworks */, + 8DD76FAF0486AB0100D96B5E /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = lencod; + productInstallPath = "$(HOME)/bin"; + productName = JM; + productReference = 8DD76FB20486AB0100D96B5E /* lencod */; + productType = "com.apple.product-type.tool"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 08FB7793FE84155DC02AAC07 /* Project object */ = { + isa = PBXProject; + buildConfigurationList = 1DEB928908733DD80010E9CD /* Build configuration list for PBXProject "JM" */; + compatibilityVersion = "Xcode 3.0"; + developmentRegion = English; + hasScannedForEncodings = 1; + knownRegions = ( + English, + Japanese, + French, + German, + ); + mainGroup = 08FB7794FE84155DC02AAC07 /* JM */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 8DD76FA90486AB0100D96B5E /* lencod */, + 43FA4AD50D4EECBB001709F1 /* ldecod */, + 436FC25B0D85FB4900D8E371 /* rtpdump */, + 436FC2A20D85FC1C00D8E371 /* rtp_loss */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXSourcesBuildPhase section */ + 436FC25C0D85FB4900D8E371 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 436FC29E0D85FBC500D8E371 /* rtpdump.cpp in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 436FC2A30D85FC1C00D8E371 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 436FC2B20D85FC9400D8E371 /* rtp_loss.cpp in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 43FA4AD60D4EECBB001709F1 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 71927D991472DF98000AB618 /* annexb.c in Sources */, + 71927D9A1472DF98000AB618 /* biaridecod.c in Sources */, + 71927D9B1472DF98000AB618 /* block.c in Sources */, + 71927D9C1472DF98000AB618 /* cabac.c in Sources */, + 71927D9D1472DF98000AB618 /* configfile.c in Sources */, + 71927D9E1472DF98000AB618 /* context_ini.c in Sources */, + 71927D9F1472DF98000AB618 /* dec_statistics.c in Sources */, + 71927DA01472DF98000AB618 /* decoder_test.c in Sources */, + 71927DA11472DF98000AB618 /* erc_api.c in Sources */, + 71927DA21472DF98000AB618 /* erc_do_i.c in Sources */, + 71927DA31472DF98000AB618 /* erc_do_p.c in Sources */, + 71927DA41472DF98000AB618 /* errorconcealment.c in Sources */, + 71927DA51472DF98000AB618 /* filehandle.c in Sources */, + 71927DA61472DF98000AB618 /* fmo.c in Sources */, + 71927DA71472DF98000AB618 /* header.c in Sources */, + 71927DA81472DF98000AB618 /* image.c in Sources */, + 71927DA91472DF98000AB618 /* intra_chroma_pred_mbaff.c in Sources */, + 71927DAA1472DF98000AB618 /* intra_chroma_pred.c in Sources */, + 71927DAB1472DF98000AB618 /* intra_pred_common.c in Sources */, + 71927DAC1472DF98000AB618 /* intra4x4_pred_mbaff.c in Sources */, + 71927DAD1472DF98000AB618 /* intra4x4_pred_normal.c in Sources */, + 71927DAE1472DF98000AB618 /* intra4x4_pred.c in Sources */, + 71927DAF1472DF98000AB618 /* intra8x8_pred_mbaff.c in Sources */, + 71927DB01472DF98000AB618 /* intra8x8_pred_normal.c in Sources */, + 71927DB11472DF98000AB618 /* intra8x8_pred.c in Sources */, + 71927DB21472DF98000AB618 /* intra16x16_pred_mbaff.c in Sources */, + 71927DB31472DF98000AB618 /* intra16x16_pred_normal.c in Sources */, + 71927DB41472DF98000AB618 /* intra16x16_pred.c in Sources */, + 71927DB51472DF98000AB618 /* ldecod.c in Sources */, + 71927DB61472DF98000AB618 /* leaky_bucket.c in Sources */, + 71927DB71472DF98000AB618 /* loop_filter_mbaff.c in Sources */, + 71927DB81472DF98000AB618 /* loop_filter_normal.c in Sources */, + 71927DB91472DF98000AB618 /* loopFilter.c in Sources */, + 71927DBA1472DF98000AB618 /* macroblock.c in Sources */, + 71927DBB1472DF98000AB618 /* mb_access.c in Sources */, + 71927DBC1472DF98000AB618 /* mb_prediction.c in Sources */, + 71927DBD1472DF98000AB618 /* mb_read.c in Sources */, + 71927DBE1472DF98000AB618 /* mbuffer_mvc.c in Sources */, + 71927DBF1472DF98000AB618 /* mbuffer.c in Sources */, + 71927DC01472DF98000AB618 /* mc_direct.c in Sources */, + 71927DC11472DF98000AB618 /* mc_prediction.c in Sources */, + 71927DC21472DF98000AB618 /* nal.c in Sources */, + 71927DC31472DF98000AB618 /* nalu.c in Sources */, + 71927DC41472DF98000AB618 /* output.c in Sources */, + 71927DC51472DF98000AB618 /* parset.c in Sources */, + 71927DC61472DF98000AB618 /* quant.c in Sources */, + 71927DC71472DF98000AB618 /* read_comp_cabac.c in Sources */, + 71927DC81472DF98000AB618 /* read_comp_cavlc.c in Sources */, + 71927DC91472DF98000AB618 /* rtp.c in Sources */, + 71927DCA1472DF98000AB618 /* sei.c in Sources */, + 71927DCB1472DF98000AB618 /* transform8x8.c in Sources */, + 71927DCC1472DF98000AB618 /* vlc.c in Sources */, + 71927E201472DFE1000AB618 /* blk_prediction.c in Sources */, + 71927E211472DFE1000AB618 /* config_common.c in Sources */, + 71927E221472DFE1000AB618 /* img_io.c in Sources */, + 71927E231472DFE1000AB618 /* img_process.c in Sources */, + 71927E241472DFE1000AB618 /* input.c in Sources */, + 71927E251472DFE1000AB618 /* io_raw.c in Sources */, + 71927E261472DFE1000AB618 /* io_tiff.c in Sources */, + 71927E271472DFE1000AB618 /* mbuffer_common.c in Sources */, + 71927E281472DFE1000AB618 /* memalloc.c in Sources */, + 71927E291472DFE1000AB618 /* mv_prediction.c in Sources */, + 71927E2A1472DFE1000AB618 /* nalucommon.c in Sources */, + 71927E2B1472DFE1000AB618 /* parsetcommon.c in Sources */, + 71927E2C1472DFE1000AB618 /* transform.c in Sources */, + 71927E2D1472DFE1000AB618 /* win32.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 8DD76FAB0486AB0100D96B5E /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 71927CF71472DF74000AB618 /* annexb.c in Sources */, + 71927CF81472DF74000AB618 /* biariencode.c in Sources */, + 71927CF91472DF74000AB618 /* block.c in Sources */, + 71927CFA1472DF74000AB618 /* cabac.c in Sources */, + 71927CFB1472DF74000AB618 /* cconv_yuv2rgb.c in Sources */, + 71927CFC1472DF74000AB618 /* configfile.c in Sources */, + 71927CFD1472DF74000AB618 /* conformance.c in Sources */, + 71927CFE1472DF74000AB618 /* context_ini.c in Sources */, + 71927CFF1472DF74000AB618 /* errdo_dist_mhyp.c in Sources */, + 71927D001472DF74000AB618 /* errdo.c in Sources */, + 71927D011472DF74000AB618 /* explicit_gop.c in Sources */, + 71927D021472DF74000AB618 /* explicit_seq.c in Sources */, + 71927D031472DF74000AB618 /* filehandle.c in Sources */, + 71927D041472DF74000AB618 /* fmo.c in Sources */, + 71927D051472DF74000AB618 /* get_block_otf.c in Sources */, + 71927D061472DF74000AB618 /* header.c in Sources */, + 71927D071472DF74000AB618 /* image_mp.c in Sources */, + 71927D081472DF74000AB618 /* image.c in Sources */, + 71927D091472DF74000AB618 /* img_chroma.c in Sources */, + 71927D0A1472DF74000AB618 /* img_dist_ms_ssim.c in Sources */, + 71927D0B1472DF74000AB618 /* img_dist_snr.c in Sources */, + 71927D0C1472DF74000AB618 /* img_dist_ssim.c in Sources */, + 71927D0D1472DF74000AB618 /* img_distortion.c in Sources */, + 71927D0E1472DF74000AB618 /* img_luma.c in Sources */, + 71927D0F1472DF74000AB618 /* intra_chroma.c in Sources */, + 71927D101472DF74000AB618 /* intra4x4.c in Sources */, + 71927D111472DF74000AB618 /* intra8x8.c in Sources */, + 71927D121472DF74000AB618 /* intra16x16.c in Sources */, + 71927D131472DF74000AB618 /* intrarefresh.c in Sources */, + 71927D141472DF74000AB618 /* lambda.c in Sources */, + 71927D151472DF74000AB618 /* leaky_bucket.c in Sources */, + 71927D161472DF74000AB618 /* lencod.c in Sources */, + 71927D171472DF74000AB618 /* list_reorder.c in Sources */, + 71927D181472DF74000AB618 /* lln_mc_prediction.c in Sources */, + 71927D191472DF74000AB618 /* loop_filter_mbaff.c in Sources */, + 71927D1A1472DF74000AB618 /* loop_filter_normal.c in Sources */, + 71927D1B1472DF74000AB618 /* loopFilter.c in Sources */, + 71927D1C1472DF74000AB618 /* macroblock_P444.c in Sources */, + 71927D1D1472DF74000AB618 /* macroblock.c in Sources */, + 71927D1E1472DF74000AB618 /* mb_access.c in Sources */, + 71927D1F1472DF74000AB618 /* mbuffer.c in Sources */, + 71927D201472DF74000AB618 /* mc_prediction_otf.c in Sources */, + 71927D211472DF74000AB618 /* mc_prediction.c in Sources */, + 71927D221472DF74000AB618 /* md_common.c in Sources */, + 71927D231472DF74000AB618 /* md_distortion.c in Sources */, + 71927D241472DF74000AB618 /* md_high.c in Sources */, + 71927D251472DF74000AB618 /* md_highfast.c in Sources */, + 71927D261472DF74000AB618 /* md_highloss.c in Sources */, + 71927D271472DF74000AB618 /* md_low.c in Sources */, + 71927D281472DF74000AB618 /* me_distortion_otf.c in Sources */, + 71927D291472DF74000AB618 /* me_distortion.c in Sources */, + 71927D2A1472DF74000AB618 /* me_epzs_common.c in Sources */, + 71927D2B1472DF74000AB618 /* me_epzs_int.c in Sources */, + 71927D2C1472DF74000AB618 /* me_epzs_sub.c in Sources */, + 71927D2D1472DF74000AB618 /* me_epzs.c in Sources */, + 71927D2E1472DF74000AB618 /* me_fullfast_otf.c in Sources */, + 71927D2F1472DF74000AB618 /* me_fullfast.c in Sources */, + 71927D301472DF74000AB618 /* me_fullsearch.c in Sources */, + 71927D311472DF74000AB618 /* me_umhex.c in Sources */, + 71927D321472DF74000AB618 /* me_umhexsmp.c in Sources */, + 71927D331472DF74000AB618 /* mmco.c in Sources */, + 71927D341472DF74000AB618 /* mode_decision_P8x8.c in Sources */, + 71927D351472DF74000AB618 /* mode_decision.c in Sources */, + 71927D361472DF74000AB618 /* mv_direct.c in Sources */, + 71927D371472DF74000AB618 /* mv_search.c in Sources */, + 71927D381472DF74000AB618 /* nal.c in Sources */, + 71927D391472DF74000AB618 /* nalu.c in Sources */, + 71927D3A1472DF74000AB618 /* output.c in Sources */, + 71927D3B1472DF74000AB618 /* parset.c in Sources */, + 71927D3C1472DF74000AB618 /* pred_struct.c in Sources */, + 71927D3D1472DF74000AB618 /* q_around.c in Sources */, + 71927D3E1472DF74000AB618 /* q_matrix.c in Sources */, + 71927D3F1472DF74000AB618 /* q_offsets.c in Sources */, + 71927D401472DF74000AB618 /* quant4x4_2step.c in Sources */, + 71927D411472DF74000AB618 /* quant4x4_around.c in Sources */, + 71927D421472DF74000AB618 /* quant4x4_normal.c in Sources */, + 71927D431472DF74000AB618 /* quant4x4_trellis.c in Sources */, + 71927D441472DF74000AB618 /* quant4x4.c in Sources */, + 71927D451472DF74000AB618 /* quant8x8_around.c in Sources */, + 71927D461472DF74000AB618 /* quant8x8_normal.c in Sources */, + 71927D471472DF74000AB618 /* quant8x8_trellis.c in Sources */, + 71927D481472DF74000AB618 /* quant8x8.c in Sources */, + 71927D491472DF74000AB618 /* quantChroma_2step.c in Sources */, + 71927D4A1472DF74000AB618 /* quantChroma_around.c in Sources */, + 71927D4B1472DF74000AB618 /* quantChroma_normal.c in Sources */, + 71927D4C1472DF74000AB618 /* quantChroma_trellis.c in Sources */, + 71927D4D1472DF74000AB618 /* quantChroma.c in Sources */, + 71927D4E1472DF74000AB618 /* ratectl.c in Sources */, + 71927D4F1472DF74000AB618 /* rc_quadratic.c in Sources */, + 71927D501472DF74000AB618 /* rd_intra_jm_low.c in Sources */, + 71927D511472DF74000AB618 /* rd_intra_jm.c in Sources */, + 71927D521472DF74000AB618 /* rd_intra_jm444.c in Sources */, + 71927D531472DF74000AB618 /* rdopt_coding_state.c in Sources */, + 71927D541472DF74000AB618 /* rdopt.c in Sources */, + 71927D551472DF74000AB618 /* rdoq_cabac.c in Sources */, + 71927D561472DF74000AB618 /* rdoq_cavlc.c in Sources */, + 71927D571472DF74000AB618 /* rdoq.c in Sources */, + 71927D581472DF74000AB618 /* rdpicdecision.c in Sources */, + 71927D591472DF74000AB618 /* report.c in Sources */, + 71927D5A1472DF74000AB618 /* rtp.c in Sources */, + 71927D5B1472DF74000AB618 /* sei.c in Sources */, + 71927D5C1472DF74000AB618 /* slice.c in Sources */, + 71927D5D1472DF74000AB618 /* symbol.c in Sources */, + 71927D5E1472DF74000AB618 /* transform8x8_H444.c in Sources */, + 71927D5F1472DF74000AB618 /* transform8x8.c in Sources */, + 71927D601472DF74000AB618 /* vlc.c in Sources */, + 71927D611472DF74000AB618 /* weighted_prediction.c in Sources */, + 71927D621472DF74000AB618 /* wp_lms.c in Sources */, + 71927D631472DF74000AB618 /* wp_mciter.c in Sources */, + 71927D641472DF74000AB618 /* wp_mcprec.c in Sources */, + 71927E2E1472DFE1000AB618 /* blk_prediction.c in Sources */, + 71927E2F1472DFE1000AB618 /* config_common.c in Sources */, + 71927E301472DFE1000AB618 /* img_io.c in Sources */, + 71927E311472DFE1000AB618 /* img_process.c in Sources */, + 71927E321472DFE1000AB618 /* input.c in Sources */, + 71927E331472DFE1000AB618 /* io_raw.c in Sources */, + 71927E341472DFE1000AB618 /* io_tiff.c in Sources */, + 71927E351472DFE1000AB618 /* mbuffer_common.c in Sources */, + 71927E361472DFE1000AB618 /* memalloc.c in Sources */, + 71927E371472DFE1000AB618 /* mv_prediction.c in Sources */, + 71927E381472DFE1000AB618 /* nalucommon.c in Sources */, + 71927E391472DFE1000AB618 /* parsetcommon.c in Sources */, + 71927E3A1472DFE1000AB618 /* transform.c in Sources */, + 71927E3B1472DFE1000AB618 /* win32.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 1DEB928608733DD80010E9CD /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ARCHS = "$(ARCHS_STANDARD_32_64_BIT_PRE_XCODE_3_1)"; + ARCHS_STANDARD_32_64_BIT_PRE_XCODE_3_1 = "x86_64 i386 ppc"; + COPY_PHASE_STRIP = NO; + GCC_C_LANGUAGE_STANDARD = c99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_FIX_AND_CONTINUE = YES; + GCC_MODEL_TUNING = G5; + GCC_OPTIMIZATION_LEVEL = 0; + INSTALL_PATH = ""; + LIBRARY_SEARCH_PATHS = ""; + ONLY_ACTIVE_ARCH = NO; + PRODUCT_NAME = lencod; + SDKROOT = "$(DEVELOPER_SDK_DIR)/MacOSX10.5.sdk"; + USER_HEADER_SEARCH_PATHS = lencod/inc; + ZERO_LINK = YES; + }; + name = Debug; + }; + 1DEB928708733DD80010E9CD /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ARCHS = "$(ARCHS_STANDARD_32_64_BIT_PRE_XCODE_3_1)"; + ARCHS_STANDARD_32_64_BIT_PRE_XCODE_3_1 = "x86_64 i386 ppc"; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + GCC_C_LANGUAGE_STANDARD = c99; + GCC_MODEL_TUNING = G5; + INSTALL_PATH = ""; + LIBRARY_SEARCH_PATHS = ""; + PRODUCT_NAME = lencod; + SDKROOT = "$(DEVELOPER_SDK_DIR)/MacOSX10.5.sdk"; + USER_HEADER_SEARCH_PATHS = lencod/inc; + }; + name = Release; + }; + 1DEB928A08733DD80010E9CD /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ENABLE_OPENMP_SUPPORT = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + PREBINDING = NO; + SDKROOT = "$(DEVELOPER_SDK_DIR)/MacOSX10.5.sdk"; + }; + name = Debug; + }; + 1DEB928B08733DD80010E9CD /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ARCHS = ( + ppc, + i386, + ); + ENABLE_OPENMP_SUPPORT = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + PREBINDING = NO; + SDKROOT = "$(DEVELOPER_SDK_DIR)/MacOSX10.5.sdk"; + }; + name = Release; + }; + 436FC29A0D85FB4900D8E371 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ARCHS = "$(ARCHS_STANDARD_32_BIT_PRE_XCODE_3_1)"; + ARCHS_STANDARD_32_BIT_PRE_XCODE_3_1 = "ppc i386"; + COPY_PHASE_STRIP = NO; + GCC_C_LANGUAGE_STANDARD = c99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_FIX_AND_CONTINUE = YES; + GCC_MODEL_TUNING = G5; + GCC_OPTIMIZATION_LEVEL = 0; + INSTALL_PATH = ""; + PRODUCT_NAME = rtpdump; + SDKROOT = "$(DEVELOPER_SDK_DIR)/MacOSX10.5.sdk"; + USER_HEADER_SEARCH_PATHS = ""; + ZERO_LINK = YES; + }; + name = Debug; + }; + 436FC29B0D85FB4900D8E371 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ARCHS = "$(ARCHS_STANDARD_32_BIT_PRE_XCODE_3_1)"; + ARCHS_STANDARD_32_BIT_PRE_XCODE_3_1 = "ppc i386"; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + GCC_C_LANGUAGE_STANDARD = c99; + GCC_MODEL_TUNING = G5; + INSTALL_PATH = ""; + PRODUCT_NAME = rtpdump; + SDKROOT = "$(DEVELOPER_SDK_DIR)/MacOSX10.5.sdk"; + USER_HEADER_SEARCH_PATHS = ""; + }; + name = Release; + }; + 436FC2A80D85FC1C00D8E371 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ARCHS = "$(ARCHS_STANDARD_32_BIT_PRE_XCODE_3_1)"; + ARCHS_STANDARD_32_BIT_PRE_XCODE_3_1 = "ppc i386"; + COPY_PHASE_STRIP = NO; + GCC_C_LANGUAGE_STANDARD = c99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_FIX_AND_CONTINUE = YES; + GCC_MODEL_TUNING = G5; + GCC_OPTIMIZATION_LEVEL = 0; + INSTALL_PATH = ""; + PRODUCT_NAME = rtp_loss; + SDKROOT = "$(DEVELOPER_SDK_DIR)/MacOSX10.5.sdk"; + USER_HEADER_SEARCH_PATHS = ""; + ZERO_LINK = YES; + }; + name = Debug; + }; + 436FC2A90D85FC1C00D8E371 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ARCHS = "$(ARCHS_STANDARD_32_BIT_PRE_XCODE_3_1)"; + ARCHS_STANDARD_32_BIT_PRE_XCODE_3_1 = "ppc i386"; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + GCC_C_LANGUAGE_STANDARD = c99; + GCC_MODEL_TUNING = G5; + INSTALL_PATH = ""; + PRODUCT_NAME = rtp_loss; + SDKROOT = "$(DEVELOPER_SDK_DIR)/MacOSX10.5.sdk"; + USER_HEADER_SEARCH_PATHS = ""; + }; + name = Release; + }; + 43FA4B150D4EECBB001709F1 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ARCHS = "$(ARCHS_STANDARD_32_64_BIT_PRE_XCODE_3_1)"; + ARCHS_STANDARD_32_64_BIT_PRE_XCODE_3_1 = "x86_64 i386 ppc"; + COPY_PHASE_STRIP = NO; + GCC_C_LANGUAGE_STANDARD = c99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_FIX_AND_CONTINUE = YES; + GCC_MODEL_TUNING = G5; + GCC_OPTIMIZATION_LEVEL = 0; + INSTALL_PATH = ""; + LIBRARY_SEARCH_PATHS = ""; + PRODUCT_NAME = ldecod; + SDKROOT = "$(DEVELOPER_SDK_DIR)/MacOSX10.5.sdk"; + USER_HEADER_SEARCH_PATHS = ldecod/inc; + ZERO_LINK = YES; + }; + name = Debug; + }; + 43FA4B160D4EECBB001709F1 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ARCHS = "$(ARCHS_STANDARD_32_64_BIT_PRE_XCODE_3_1)"; + ARCHS_STANDARD_32_64_BIT_PRE_XCODE_3_1 = "x86_64 i386 ppc"; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + GCC_C_LANGUAGE_STANDARD = c99; + GCC_MODEL_TUNING = G5; + INSTALL_PATH = ""; + LIBRARY_SEARCH_PATHS = ""; + PREBINDING = NO; + PRELINK_LIBS = ""; + PRODUCT_NAME = ldecod; + SDKROOT = "$(DEVELOPER_SDK_DIR)/MacOSX10.5.sdk"; + USER_HEADER_SEARCH_PATHS = ldecod/inc; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 1DEB928508733DD80010E9CD /* Build configuration list for PBXNativeTarget "lencod" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 1DEB928608733DD80010E9CD /* Debug */, + 1DEB928708733DD80010E9CD /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 1DEB928908733DD80010E9CD /* Build configuration list for PBXProject "JM" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 1DEB928A08733DD80010E9CD /* Debug */, + 1DEB928B08733DD80010E9CD /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 436FC2990D85FB4900D8E371 /* Build configuration list for PBXNativeTarget "rtpdump" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 436FC29A0D85FB4900D8E371 /* Debug */, + 436FC29B0D85FB4900D8E371 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 436FC2A70D85FC1C00D8E371 /* Build configuration list for PBXNativeTarget "rtp_loss" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 436FC2A80D85FC1C00D8E371 /* Debug */, + 436FC2A90D85FC1C00D8E371 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 43FA4B140D4EECBB001709F1 /* Build configuration list for PBXNativeTarget "ldecod" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 43FA4B150D4EECBB001709F1 /* Debug */, + 43FA4B160D4EECBB001709F1 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 08FB7793FE84155DC02AAC07 /* Project object */; +} diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..7cabcb6 --- /dev/null +++ b/Makefile @@ -0,0 +1,46 @@ +### +### Makefile for JM H.264/AVC encoder/decoder +### +### generated for UNIX/LINUX environments +### by Limin Wang(lance.lmwang@gmail.com) +### + +SUBDIRS := ldecod lencod rtpdump rtp_loss + +### include debug information: 1=yes, 0=no +DBG?= 0 +### enforce 32-bit build : 1=yes, 0=no +M32?= 0 +### static link : 1=yes, 0=no +STC?= 0 +### OPENMP support : 1=yes, 0=no +OPENMP?= 1 + +export DBG +export STC +export M32 +export OPENMP + +.PHONY: default all distclean clean tags depend $(SUBDIRS) + +default: all + +all: $(SUBDIRS) + +$(SUBDIRS): + $(MAKE) -C $@ + +clean depend: + @echo "Cleaning dependencies" + @for i in $(SUBDIRS); do make -C $$i $@; done + +tags: + @echo "update tag table at top directory" + @ctags -R . + @for i in $(SUBDIRS); do make -C $$i $@; done + +distclean: clean + @echo "Cleaning all" + @rm -f tags + @for i in $(SUBDIRS); do make -C $$i $@; done + diff --git a/Readme.txt b/Readme.txt new file mode 100644 index 0000000..1ee2889 --- /dev/null +++ b/Readme.txt @@ -0,0 +1,195 @@ +JM Reference Software README +============================ + +The latest version of this software can be obtained from: + + http://iphome.hhi.de/suehring/tml + +For reporting bugs please use the JM bug tracking system located at: + + https://ipbt.hhi.fraunhofer.de + + +Please send comments and additions to Karsten.Suehring (at) hhi.fraunhofer.de and alexis.tourapis@dolby.com + +====================================================================================== +NOTE: This file contains only a quick overview. + + More detailed information can be found the "JM Reference Software Manual" in the + doc/ subdirectory of this package. +====================================================================================== + +1. Compilation +2. Command line parameters +3. Input/Output file format +4. Configuration files +5. Platform specific notes + + +1. Compilation +-------------- + +1.1 Windows +----------- + + Workspaces for MS Visual C++ 2003/2005/2008/2010 are provided with the names + + jm_vc7.sln - MS Visual C++ 2003 + jm_vc8.sln - MS Visual C++ 2005 + jm_vc9.sln - MS Visual C++ 2008 + jm_vc10.sln - MS Visual C++ 2010 + + These contain encoder and decoder projects. + + +1.2 Unix +-------- + + Before compiling in a UNIX environment please run the "unixprep.sh" script which + will remove the DOS LF characters from the files and create object directories. + + Makefiles for GNU make are provided at the top level and in the lencod and ldecod directories. + + +1.3 MacOS X +----------- + + A workspace for XCode can be found in the main directory. The project can also be build + using the UNIX build process (make). + + +2. Command line parameters +-------------------------- + +2.1 Encoder +----------- + + lencod.exe [-h] [-d default-file] [-f file] [-p parameter=value] + + All Parameters are initially taken from DEFAULTCONFIGFILENAME, defined in + configfile.h (typically: "encoder.cfg") + + -h + Show help on parameters. + + -d default-file + Use the specified file as default configuration instead of the file in + DEFAULTCONFIGFILENAME. + + -f file + If an -f parameter is present in the command line then + this file is used to update the defaults of DEFAULTCONFIGFILENAME. + There can be more than one -f parameters present. + + -p parameter=value + + If -p parameters are present then + these overide the default and the additional config file's settings, + and are themselfes overridden by future -p parameters. There must + be whitespace between -f and -p commands and their respecitive + parameters. + -v + Show short version info. + -V + Show long version info. + +2.2 Decoder +----------- + + ldecod.exe [-h] [-d default-file] [-f file] [-p parameter=value] + + All Parameters are initially taken from DEFAULTCONFIGFILENAME, defined in + configfile.h (typically: "encoder.cfg") + + -h + Show help on parameters. + + -d default-file + Use the specified file as default configuration instead of the file in + DEFAULTCONFIGFILENAME. + + -f file + If an -f parameter is present in the command line then + this file is used to update the defaults of DEFAULTCONFIGFILENAME. + There can be more than one -f parameters present. + + -p parameter=value + + If -p parameters are present then + these overide the default and the additional config file's settings, + and are themselfes overridden by future -p parameters. There must + be whitespace between -f and -p commands and their respecitive + parameters. + -v + Show short version info. + -V + Show long version info. + + + +3. Input/Output file format +--------------------------- + + The source video material is read from raw YUV 4:2:0 data files. + For output the same format is used. + + +4. Configuration files +---------------------- + + Sample encoder and decode configuration files are provided in the bin/ directory. + These contain explanatory comments for each parameter. + + The generic structure is explained here. + +4.1 Encoder +----------- + = # Comments + + Whitespace is space and \t + + are the predefined names for Parameters and are case sensitive. + See configfile.h for the definition of those names and their + mapping to configinput->values. + + are either integers [0..9]* or strings. + Integers must fit into the wordlengths, signed values are generally + assumed. Strings containing no whitespace characters can be used directly. + Strings containing whitespace characters are to be inclosed in double + quotes ("string with whitespace") + The double quote character is forbidden (may want to implement something + smarter here). + + Any Parameters whose ParameterName is undefined lead to the termination of the program + with an error message. + + Known bug/Shortcoming: zero-length strings (i.e. to signal an non-existing file + have to be coded as "". + +4.2 Decoder +----------- + Beginning with JM 17.0 the decoder uses the same config file style like the encoder. + + +5. Platform specific notes +-------------------------- + This section contains hints for compiling and running the JM software on different + operating systems. + +5.1 MacOS X +----------- + MacOs X has a UNIX core so most of the UNIX compile process will work. You might need + the following modifications: + + a) Before Leopard (MacOS 10.5): in Makefile change "CC = $(shell which gcc)" to "CC = gcc" + (it seems "which" doesn't work) + + b) MacOS "Tiger" (MacOS 10.4) doesn't come with ftime. We suggest using a third party ftime + implementation, e.g. from: + + http://darwinsource.opendarwin.org/10.3.4/OpenSSL096-3/openssl/crypto/ftime.c + +5.2 FreeBSD +----------- + You might need to add "-lcompat" to LIBS in the Makefiles for correct linking. + diff --git a/bin/HM-like/encoder_JM_Intra_HE.cfg b/bin/HM-like/encoder_JM_Intra_HE.cfg new file mode 100644 index 0000000..b48c205 --- /dev/null +++ b/bin/HM-like/encoder_JM_Intra_HE.cfg @@ -0,0 +1,660 @@ +# New Input File Format is as follows +# = # Comment +# +# See configfile.h for a list of supported ParameterNames +# +# For bug reporting and known issues see: +# https://ipbt.hhi.fraunhofer.de + + +########################################################################################## +# Files +########################################################################################## +InputFile = "D:\origCfP\BQSquare_416x240_60.yuv" # Input sequence +InputHeaderLength = 0 # If the inputfile has a header, state it's length in byte here +StartFrame = 0 # Start frame for encoding. (0-N) +FramesToBeEncoded = 30 # Number of frames to be coded +FrameRate = 60.0 # Frame Rate per second (0.1-100.0) +Enable32Pulldown = 0 # Enable 'hard' 3:2 pulldown (modifying the inpur data) + # 0 = disabled + # 1 = A, B, Bt|Cb, Ct|Db, D + # 2 = A, B, C, Ct|Db, D +SEIVUI32Pulldown = 0 # Enable 3:2 pulldown through VUI and SEI metadata signaling. Five methods are supported: + # 0 = disabled + # 1 = A, Bt|Bb, Bt|Cb, Ct|Cb, D + # 2 = A, B, C, C, D + # 3 = At|Ab, Bt|Bb, Bt|Cb, Ct|Cb, Dt|Db + # 4 = A, Bt|Bb, Bt|Cb, Ct|Db, Dt|Db + # 5 = At|Ab, Bt|Bb, Bt|Cb, Ct|Db, Dt|Db + +SourceWidth = 416 # Source frame width +SourceHeight = 240 # Source frame height +SourceResize = 0 # Resize source size for output +OutputWidth = 416 # Output frame width +OutputHeight = 240 # Output frame height +ProcessInput = 0 # Filter Input Sequence +Interleaved = 0 # 0: Planar input, 1: Packed input +StandardRange = 0 # 0: Standard range 1: Full range (RGB input) +VideoCode = 1 # Video codes for RGB ==> YUV conversions + # 0 = NULL, + # 1 = ITU_REC709, + # 2 = CCIR_601, + # 3 = FCC, + # 4 = ITU_REC624BG, + # 5 = SMPTE_170M, + # 6 = SMPTE_240M, + # 7 = SMPTE_260M, + # 8 = ITU_REC709_EXACT + +TraceFile = "" # Trace file +ReconFile = "test_rec.yuv" # Reconstruction YUV file +OutputFile = "test.264" # Bitstream +StatsFile = "" # Coding statistics file + +NumberOfViews = 1 # Number of views to encode (1=1 view, 2=2 views) +View1ConfigFile = "encoder_view1.cfg" # Config file name for second view +########################################################################################## +# Encoder Control +########################################################################################## +Grayscale = 0 # Encode in grayscale (Currently only works for 8 bit YUV 420 input) +ProfileIDC = 100 # Profile IDC (66=baseline, 77=main, 88=extended; FREXT Profiles: 100=High, 110=High 10, 122=High 4:2:2, 244=High 4:4:4, 44=CAVLC 4:4:4 Intra, 118=Multiview High Profile, 128=Stereo High Profile) +IntraProfile = 0 # Activate Intra Profile for FRExt (0: false, 1: true) + # (e.g. ProfileIDC=110, IntraProfile=1 => High 10 Intra Profile) +LevelIDC = 51 # Level IDC (e.g. 20 = level 2.0) + +IntraPeriod = 1 # Period of I-pictures (0=only first) +IDRPeriod = 1 # Period of IDR pictures (0=only first) +AdaptiveIntraPeriod = 0 # Adaptive intra period +AdaptiveIDRPeriod = 0 # Adaptive IDR period +IntraDelay = 0 # Intra (IDR) picture delay (i.e. coding structure of PPIPPP... ) +EnableIDRGOP = 0 # Support for IDR closed GOPs (0: disabled, 1: enabled) +EnableOpenGOP = 0 # Support for open GOPs (0: disabled, 1: enabled) +QPISlice = 32 # Quant. param for I Slices (0-51) +QPPSlice = 33 # Quant. param for P Slices (0-51) +FrameSkip = 0 # Number of frames to be skipped in input (e.g 2 will code every third frame). + # Note that this now excludes intermediate (i.e. B) coded pictures +ChromaQPOffset = 0 # Chroma QP offset (-51..51) + +DisableSubpelME = 0 # Disable Subpixel Motion Estimation (0=off/default, 1=on) +SearchRange = 128 # Max search range + +MEDistortionFPel = 0 # Select error metric for Full-Pel ME (0: SAD, 1: SSE, 2: Hadamard SAD) +MEDistortionHPel = 2 # Select error metric for Half-Pel ME (0: SAD, 1: SSE, 2: Hadamard SAD) +MEDistortionQPel = 2 # Select error metric for Quarter-Pel ME (0: SAD, 1: SSE, 2: Hadamard SAD) +MDDistortion = 2 # Select error metric for Mode Decision (0: SAD, 1: SSE, 2: Hadamard SAD) +SkipDeBlockNonRef = 0 # Skip Deblocking (regardless of DFParametersFlag) for non-reference frames (0: off, 1: on) +ChromaMCBuffer = 1 # Calculate Color component interpolated values in advance and store them. + # Provides a trade-off between memory and computational complexity + # (0: disabled/default, 1: enabled) +ChromaMEEnable = 0 # Take into account Color component information during ME + # (0: only first component/default, + # 1: All Color components - Integer refinement only + # 2: All Color components - All refinements) +ChromaMEWeight = 1 # Weighting for chroma components. This parameter should have a relationship with color format. + +NumberReferenceFrames = 4 # Number of previous frames used for inter motion search (0-16) + +PList0References = 0 # P slice List 0 reference override (0 disable, N <= NumberReferenceFrames) +Log2MaxFNumMinus4 = 0 # Sets log2_max_frame_num_minus4 (-1 : based on FramesToBeEncoded/Auto, >=0 : Log2MaxFNumMinus4) +Log2MaxPOCLsbMinus4 = -1 # Sets log2_max_pic_order_cnt_lsb_minus4 (-1 : Auto, >=0 : Log2MaxPOCLsbMinus4) + +GenerateMultiplePPS = 1 # Transmit multiple parameter sets. Currently parameters basically enable all WP modes (0: diabled, 1: enabled) +SendAUD = 0 # Send Access Delimiter Unit NALU (for every access unit) +ResendSPS = 0 # Resend SPS (0: disabled, 1: all Intra pictures, 2: only for IDR, 3: for IDR and OpenGOP I) +ResendPPS = 0 # Resend PPS (with pic_parameter_set_id 0) for every coded Frame/Field pair (0: disabled, 1: enabled) + +MbLineIntraUpdate = 0 # Error robustness(extra intra macro block updates)(0=off, N: One GOB every N frames are intra coded) +RandomIntraMBRefresh = 0 # Forced intra MBs per picture + +########################################################################################## +# PSlice Mode types +########################################################################################## +PSliceSkip = 1 # P-Slice Skip mode consideration (0=disable, 1=enable) +PSliceSearch16x16 = 1 # P-Slice Inter block search 16x16 (0=disable, 1=enable) +PSliceSearch16x8 = 1 # P-Slice Inter block search 16x8 (0=disable, 1=enable) +PSliceSearch8x16 = 1 # P-Slice Inter block search 8x16 (0=disable, 1=enable) +PSliceSearch8x8 = 1 # P-Slice Inter block search 8x8 (0=disable, 1=enable) +PSliceSearch8x4 = 1 # P-Slice Inter block search 8x4 (0=disable, 1=enable) +PSliceSearch4x8 = 1 # P-Slice Inter block search 4x8 (0=disable, 1=enable) +PSliceSearch4x4 = 1 # P-Slice Inter block search 4x4 (0=disable, 1=enable) + +########################################################################################## +# BSlice Mode types +########################################################################################## + +BSliceDirect = 1 # B-Slice Skip mode consideration (0=disable, 1=enable) +BSliceSearch16x16 = 1 # B-Slice Inter block search 16x16 (0=disable, 1=enable) +BSliceSearch16x8 = 1 # B-Slice Inter block search 16x8 (0=disable, 1=enable) +BSliceSearch8x16 = 1 # B-Slice Inter block search 8x16 (0=disable, 1=enable) +BSliceSearch8x8 = 1 # B-Slice Inter block search 8x8 (0=disable, 1=enable) +BSliceSearch8x4 = 1 # B-Slice Inter block search 8x4 (0=disable, 1=enable) +BSliceSearch4x8 = 1 # B-Slice Inter block search 4x8 (0=disable, 1=enable) +BSliceSearch4x4 = 1 # B-Slice Inter block search 4x4 (0=disable, 1=enable) + +BiPredSearch16x16 = 1 # B-Slice Bi-prediction block search 16x16 (0=disable, 1=enable) +BiPredSearch16x8 = 1 # B-Slice Bi-prediction block search 16x8 (0=disable, 1=enable) +BiPredSearch8x16 = 1 # B-Slice Bi-prediction block search 8x16 (0=disable, 1=enable) +BiPredSearch8x8 = 0 # B-Slice Bi-prediction block search 8x8 (0=disable, 1=enable) + +DisableIntra4x4 = 0 # Disable Intra 4x4 modes +DisableIntra16x16 = 0 # Disable Intra 16x16 modes +DisableIntraInInter = 0 # Disable Intra modes for inter slices +IntraDisableInterOnly = 0 # Apply Disabling Intra conditions only to Inter Slices (0:disable/default,1: enable) +Intra4x4ParDisable = 0 # Disable Vertical & Horizontal 4x4 +Intra4x4DiagDisable = 0 # Disable Diagonal 45degree 4x4 +Intra4x4DirDisable = 0 # Disable Other Diagonal 4x4 +Intra16x16ParDisable = 0 # Disable Vertical & Horizontal 16x16 +Intra16x16PlaneDisable = 0 # Disable Planar 16x16 +ChromaIntraDisable = 0 # Disable Intra Chroma modes other than DC +EnableIPCM = 1 # Enable IPCM macroblock mode + +DisposableP = 0 # Enable Disposable P slices in the primary layer (0: disable/default, 1: enable) +DispPQPOffset = 0 # Quantizer offset for disposable P slices (0: default) + +PreferDispOrder = 1 # Prefer display order when building the prediction structure as opposed to coding order (affects intra and IDR periodic insertion, among others) +PreferPowerOfTwo = 0 # Prefer prediction structures that have lengths expressed as powers of two +FrmStructBufferLength = 16 # Length of the frame structure unit buffer; it can be overriden for certain cases + +ChangeQPFrame = 0 # Frame in display order from which to apply the Change QP offsets +ChangeQPI = 0 # Change QP offset value for I_SLICE +ChangeQPP = 0 # Change QP offset value for P_SLICE +ChangeQPB = 0 # Change QP offset value for B_SLICE +ChangeQPSI = 0 # Change QP offset value for SI_SLICE +ChangeQPSP = 0 # Change QP offset value for SP_SLICE + +########################################################################################## +# B Slices +########################################################################################## + +NumberBFrames = 0 # Number of B coded frames inserted (0=not used) +PReplaceBSlice = 0 # Replace B-coded slices with P-coded slices when NumberBFrames>0 +QPBSlice = 33 # Quant. param for B slices (0-51) +BRefPicQPOffset = 0 # Quantization offset for reference B coded pictures (-51..51) +DirectModeType = 1 # Direct Mode Type (0:Temporal 1:Spatial) +DirectInferenceFlag = 1 # Direct Inference Flag (0: Disable 1: Enable) +BList0References = 4 # B slice List 0 reference override (0 disable, N <= NumberReferenceFrames) +BList1References = 4 # B slice List 1 reference override (0 disable, N <= NumberReferenceFrames) + # 1 List1 reference is usually recommended for normal GOP Structures. + # A larger value is usually more appropriate if a more flexible + # structure is used (i.e. using HierarchicalCoding) + +BReferencePictures = 2 # Referenced B coded pictures (0=off, 1=B references for secondary layer, 2=B references for primary layer) + +HierarchicalCoding = 0 # B hierarchical coding (0= off, 1= 2 layers, 2= 2 full hierarchy, 3 = explicit) +HierarchyLevelQPEnable = 0 # Adjust QP based on hierarchy level (in increments of 1). Overrides BRefPicQPOffset behavior.(0=off, 1=on) +ExplicitHierarchyFormat = "B0r2B1r3B2r2" # Explicit Enhancement GOP. Format is {FrameDisplay_orderReferenceQP}. + # Valid values for reference type is r:reference, e:non reference. +ExplicitSeqCoding = 0 # Enable support for explicit sequence coding +ExplicitSeqFile = "explicit_seq.cfg" +LowDelay = 1 # Apply HierarchicalCoding without delay (i.e., encode in the captured/display order) +ReferenceReorder = 0 # Reorder References according to Poc distance for HierarchicalCoding (0=off, 1=enable, 2=use when LowDelay is set) +PocMemoryManagement = 0 # Memory management based on Poc Distances for HierarchicalCoding (0=off, 1=on, 2=use when LowDelay is set) +SetFirstAsLongTerm = 0 # Set first frame as long term + +BiPredMotionEstimation = 1 # Enable Bipredictive based Motion Estimation (0:disabled, 1:enabled) +BiPredMERefinements = 3 # Bipredictive ME extra refinements (0: single, N: N extra refinements (1 default) +BiPredMESearchRange = 64 # Bipredictive ME Search range (8 default). Note that range is halved for every extra refinement. +BiPredMESubPel = 2 # Bipredictive ME Subpixel Consideration (0: disabled, 1: single level, 2: dual level) + + +########################################################################################## +# SP Frames +########################################################################################## + +SPPicturePeriodicity = 0 # SP-Picture Periodicity (0=not used) +SPSwitchPeriod = 0 # Switch period (in terms of switching SP/SI frames) between bitstream 1 and bitstream 2 +QPSPSlice = 36 # Quant. param of SP-Slices for Prediction Error (0-51) +QPSISlice = 36 # Quant. param of SI-Slices for Prediction Error (0-51) +QPSP2Slice = 35 # Quant. param of SP/SI-Slices for Predicted Blocks (0-51) +SI_FRAMES = 0 # SI frame encoding flag (0=not used, 1=used) +SP_output = 0 # Controls whether coefficients will be output to encode switching SP frames (0=no, 1=yes) +SP_output_name = "low_quality.dat" # Filename for SP output coefficients +SP2_FRAMES = 0 # switching SP frame encoding flag (0=not used, 1=used) +SP2_input_name1 = "high_quality.dat" # Filename for the first swithed bitstream coefficients +SP2_input_name2 = "low_quality.dat" # Filename for the second switched bitstream coefficients + +########################################################################################## +# Output Control, NALs +########################################################################################## + +SymbolMode = 1 # Symbol mode (Entropy coding method: 0=UVLC, 1=CABAC) +OutFileMode = 0 # Output file mode, 0:Annex B, 1:RTP +PartitionMode = 0 # Partition Mode, 0: no DP, 1: 3 Partitions per Slice + +########################################################################################## +# CABAC context initialization +########################################################################################## + +ContextInitMethod = 1 # Context init (0: fixed, 1: adaptive) +FixedModelNumber = 0 # model number for fixed decision for inter slices ( 0, 1, or 2 ) + +########################################################################################## +# Interlace Handling +######################################################################################### + +PicInterlace = 0 # Picture AFF (0: frame coding, 1: field coding, 2:adaptive frame/field coding) +MbInterlace = 0 # Macroblock AFF (0: frame coding, 1: field coding, 2:adaptive frame/field coding, 3: frame MB-only AFF) +IntraBottom = 0 # Force Intra Bottom at GOP Period + +########################################################################################## +# Weighted Prediction +######################################################################################### + +WeightedPrediction = 1 # P picture Weighted Prediction (0=off, 1=explicit mode) +WeightedBiprediction = 1 # B picture Weighted Prediciton (0=off, 1=explicit mode, 2=implicit mode) +ChromaWeightSupport = 1 # Enable consideration of weights for Chroma components +UseWeightedReferenceME = 1 # Use weighted reference for ME (0=off, 1=on) +WPMethod = 1 # WP method (0: DC based, 1: LMS based) +WPIterMC = 0 # Iterative Motion compensated based weighted prediction method +EnhancedBWeightSupport = 0 # Enhanced B Weight support (needs revisit if we wish to merge with WPMethod) +WPMCPrecision = 0 # Improved Motion Compensation Precision using WP based methods. + # Clones WP references with slightly modified rounding offsets (Requires RDPictureDecision and GenerateMultiplePPS) : + # 0: disabled (default) + # 1: Up to one additional coding pass. Ref0 is 0, ref1 is 0 with a -1 offset + # 2: Up to two additional coding passes. (1) Ref0 is 0, ref1 is 0 with a -1 offset, (1) Ref0 is 0 with a -1 offset, ref1 is 0 +WPMCPrecFullRef = 0 # Increases the number of references in the reference picture lists to account + # for the lost reference slot when reordering is used during a coding pass in WPMCPrecision for reference replication. + # The number of references in non-reordered passes stays unchanged +WPMCPrecBSlice = 1 # 2: Apply rounding on every B slice. This efectively disables the evaluation of alternative QPs during RDPictureDecision. + # 1: Disable rounding for non-reference B slices. Non-reference B slices are evaluated for alternative QPs during RDPictureDecision. + # 0: Disable rounding for B slices. + +########################################################################################## +# Picture based Multi-pass encoding +######################################################################################### + +RDPictureDecision = 1 # Perform multiple pass coding and make RD optimal decision among them +RDPSliceBTest = 0 # Perform Slice level RD decision between P and B slices. +RDPSliceITest = 1 # Perform Slice level RD decision between P and I slices. Default value is 1 (enabled). +RDPictureMaxPassISlice = 1 # Max number of coding passes for I slices, valid values [1,3], default is 1 +RDPictureMaxPassPSlice = 2 # Max number of coding passes for P slices, valid values [1,6], default is 2 +RDPictureMaxPassBSlice = 3 # Max number of coding passes for B slices, valid values [1,6], default is 3 +RDPictureFrameQPPSlice = 0 # Perform additional frame level QP check (QP+/-1) for P slices, 0: disabled (default), 1: enabled +RDPictureFrameQPBSlice = 0 # Perform additional frame level QP check (QP+/-1) for B slices, 0: disabled, 1: enabled (default) +RDPictureDeblocking = 0 # Perform another coding pass to check non-deblocked picture, 0: disabled (default), 1: enabled +RDPictureDirectMode = 0 # Perform another coding pass to check the alternative direct mode for B slices, , 0: disabled (default), 1: enabled + +########################################################################################## +# Deblocking filter parameters +########################################################################################## + +DFParametersFlag = 0 # Configure deblocking filter (0=parameters below ignored, 1=parameters sent) + # Note that for pictures with multiple slice types, + # only the type of the first slice will be considered. +DFDisableRefISlice = 0 # Disable deblocking filter in reference I coded pictures (0=Filter, 1=No Filter). +DFAlphaRefISlice = 0 # Reference I coded pictures Alpha offset div. 2, {-6, -5, ... 0, +1, .. +6} +DFBetaRefISlice = 0 # Reference I coded pictures Beta offset div. 2, {-6, -5, ... 0, +1, .. +6} +DFDisableNRefISlice = 0 # Disable deblocking filter in non reference I coded pictures (0=Filter, 1=No Filter). +DFAlphaNRefISlice = 0 # Non Reference I coded pictures Alpha offset div. 2, {-6, -5, ... 0, +1, .. +6} +DFBetaNRefISlice = 0 # Non Reference I coded pictures Beta offset div. 2, {-6, -5, ... 0, +1, .. +6} +DFDisableRefPSlice = 0 # Disable deblocking filter in reference P coded pictures (0=Filter, 1=No Filter). +DFAlphaRefPSlice = 0 # Reference P coded pictures Alpha offset div. 2, {-6, -5, ... 0, +1, .. +6} +DFBetaRefPSlice = 0 # Reference P coded pictures Beta offset div. 2, {-6, -5, ... 0, +1, .. +6} +DFDisableNRefPSlice = 0 # Disable deblocking filter in non reference P coded pictures (0=Filter, 1=No Filter). +DFAlphaNRefPSlice = 0 # Non Reference P coded pictures Alpha offset div. 2, {-6, -5, ... 0, +1, .. +6} +DFBetaNRefPSlice = 0 # Non Reference P coded pictures Beta offset div. 2, {-6, -5, ... 0, +1, .. +6} +DFDisableRefBSlice = 0 # Disable deblocking filter in reference B coded pictures (0=Filter, 1=No Filter). +DFAlphaRefBSlice = 0 # Reference B coded pictures Alpha offset div. 2, {-6, -5, ... 0, +1, .. +6} +DFBetaRefBSlice = 0 # Reference B coded pictures Beta offset div. 2, {-6, -5, ... 0, +1, .. +6} +DFDisableNRefBSlice = 0 # Disable deblocking filter in non reference B coded pictures (0=Filter, 1=No Filter). +DFAlphaNRefBSlice = 0 # Non Reference B coded pictures Alpha offset div. 2, {-6, -5, ... 0, +1, .. +6} +DFBetaNRefBSlice = 0 # Non Reference B coded pictures Beta offset div. 2, {-6, -5, ... 0, +1, .. +6} + +########################################################################################## +# Error Resilience / Slices +########################################################################################## + +SliceMode = 0 # Slice mode (0=off 1=fixed #mb in slice 2=fixed #bytes in slice 3=use callback) +SliceArgument = 50 # Slice argument (Arguments to modes 1 and 2 above) + +num_slice_groups_minus1 = 0 # Number of Slice Groups Minus 1, 0 == no FMO, 1 == two slice groups, etc. +slice_group_map_type = 0 # 0: Interleave, 1: Dispersed, 2: Foreground with left-over, + # 3: Box-out, 4: Raster Scan 5: Wipe + # 6: Explicit, slice_group_id read from SliceGroupConfigFileName +slice_group_change_direction_flag = 0 # 0: box-out clockwise, raster scan or wipe right, + # 1: box-out counter clockwise, reverse raster scan or wipe left +slice_group_change_rate_minus1 = 85 # +SliceGroupConfigFileName = "sg0conf.cfg" # Used for slice_group_map_type 0, 2, 6 + +UseRedundantPicture = 0 # 0: not used, 1: enabled +NumRedundantHierarchy = 1 # 0-4 +PrimaryGOPLength = 10 # GOP length for redundant allocation (1-16) + # NumberReferenceFrames must be no less than PrimaryGOPLength when redundant slice enabled +NumRefPrimary = 1 # Actually used number of references for primary slices (1-16) + +########################################################################################## +# Search Range Restriction / RD Optimization +########################################################################################## + +RestrictSearchRange = 2 # restriction for (0: blocks and ref, 1: ref, 2: no restrictions) +RDOptimization = 1 # rd-optimized mode decision + # 0: RD-off (Low complexity mode) + # 1: RD-on (High complexity mode) + # 2: RD-on (Fast high complexity mode - not work in FREX Profiles) + # 3: with losses +I16RDOpt = 0 # perform rd-optimized mode decision for Intra 16x16 MB + # 0: SAD-based mode decision for Intra 16x16 MB + # 1: RD-based mode decision for Intra 16x16 MB +SubMBCodingState = 2 # submacroblock coding state + # 0: lowest complexity, do not store or reset coding state during sub-MB mode decision + # 1: medium complexity, reset to master coding state (for current mode) during sub-MB mode decision + # 2: highest complexity, store and reset coding state during sub-MB mode decision +DistortionSSIM = 0 # Compute SSIM distortion. (0: disabled/default, 1: enabled) +DistortionMS_SSIM = 0 # Compute Multiscale SSIM distortion. (0: disabled/default, 1: enabled) +SSIMOverlapSize = 8 # Overlap size to calculate SSIM distortion (1: pixel by pixel, 8: no overlap) +DistortionYUVtoRGB = 0 # Calculate distortion in RGB domain after conversion from YCbCr (0:off, 1:on) +CtxAdptLagrangeMult = 0 # Context Adaptive Lagrange Multiplier + # 0: disabled (default) + # 1: enabled (works best when RDOptimization=0) +FastCrIntraDecision = 0 # Fast Chroma intra mode decision (0:off, 1:on) +DisableThresholding = 0 # Disable Thresholding of Transform Coefficients (0:off, 1:on) +DisableBSkipRDO = 0 # Disable B Skip Mode consideration from RDO Mode decision (0:off, 1:on) +BiasSkipRDO = 0 # Negative Bias for Skip/DirectSkip modes (0: off, 1: on) +ForceTrueRateRDO = 0 # Force true rate (even zero values) during RDO process +SkipIntraInInterSlices = 0 # Skips Intra mode checking in inter slices if certain mode decisions are satisfied (0: off, 1: on) +WeightY = 1 # Luma weight for RDO +WeightCb = 1 # Cb weight for RDO +WeightCr = 1 # Cr weight for RDO + +########################################################################################## +# Explicit Lambda Usage +########################################################################################## +UseExplicitLambdaParams = 0 # Use explicit lambda scaling parameters (0:disabled, 1:enable lambda weight, 2: use explicit lambda value) +UpdateLambdaChromaME = 0 # Update lambda given Chroma ME consideration +FixedLambdaISlice = 0.1 # Fixed Lambda value for I slices +FixedLambdaPSlice = 0.1 # Fixed Lambda value for P slices +FixedLambdaBSlice = 0.1 # Fixed Lambda value for B slices +FixedLambdaRefBSlice = 0.1 # Fixed Lambda value for Referenced B slices +FixedLambdaSPSlice = 0.1 # Fixed Lambda value for SP slices +FixedLambdaSISlice = 0.1 # Fixed Lambda value for SI slices + +LambdaWeightISlice = 0.65 # scaling param for I slices. This will be used as a multiplier i.e. lambda=LambdaWeightISlice * 2^((QP-12)/3) +LambdaWeightPSlice = 0.68 # scaling param for P slices. This will be used as a multiplier i.e. lambda=LambdaWeightPSlice * 2^((QP-12)/3) +LambdaWeightBSlice = 0.68 # scaling param for B slices. This will be used as a multiplier i.e. lambda=LambdaWeightBSlice * 2^((QP-12)/3) +LambdaWeightRefBSlice = 0.68 # scaling param for Referenced B slices. This will be used as a multiplier i.e. lambda=LambdaWeightRefBSlice * 2^((QP-12)/3) +LambdaWeightSPSlice = 0.68 # scaling param for SP slices. This will be used as a multiplier i.e. lambda=LambdaWeightSPSlice * 2^((QP-12)/3) +LambdaWeightSISlice = 0.65 # scaling param for SI slices. This will be used as a multiplier i.e. lambda=LambdaWeightSISlice * 2^((QP-12)/3) + +LossRateA = 5 # expected packet loss rate of the channel for the first partition, only valid if RDOptimization = 3 +LossRateB = 0 # expected packet loss rate of the channel for the second partition, only valid if RDOptimization = 3 +LossRateC = 0 # expected packet loss rate of the channel for the third partition, only valid if RDOptimization = 3 +FirstFrameCorrect = 0 # If 1, the first frame is encoded under the assumption that it is always correctly received. +NumberOfDecoders = 30 # Numbers of decoders used to simulate the channel, only valid if RDOptimization = 3 +RestrictRefFrames = 0 # Doesnt allow reference to areas that have been intra updated in a later frame. + +########################################################################################## +# Additional Stuff +######################################################################################### + +UseConstrainedIntraPred = 0 # If 1, Inter pixels are not used for Intra macroblock prediction. + +NumberofLeakyBuckets = 8 # Number of Leaky Bucket values +LeakyBucketRateFile = "leakybucketrate.cfg" # File from which encoder derives rate values +LeakyBucketParamFile = "leakybucketparam.cfg" # File where encoder stores leakybucketparams + +NumFramesInELayerSubSeq = 0 # number of frames in the Enhanced Scalability Layer(0: no Enhanced Layer) + +SparePictureOption = 0 # (0: no spare picture info, 1: spare picture available) +SparePictureDetectionThr = 6 # Threshold for spare reference pictures detection +SparePicturePercentageThr = 92 # Threshold for the spare macroblock percentage + +PicOrderCntType = 0 # (0: POC mode 0, 1: POC mode 1, 2: POC mode 2) + +######################################################################################## +#Rate control +######################################################################################## + +RateControlEnable = 0 # 0 Disable, 1 Enable +Bitrate = 45020 # Bitrate(bps) +InitialQP = 0 # Initial Quantization Parameter for the first I frame + # InitialQp depends on two values: Bits Per Picture, + # and the GOP length +BasicUnit = 0 # Number of MBs in the basic unit + # should be a fraction of the total number + # of MBs in a frame ("0" sets a BU equal to a frame) +ChannelType = 0 # type of channel( 1=time varying channel; 0=Constant channel) +RCUpdateMode = 0 # Rate Control type. Modes supported : + # 0 = original JM rate control, + # 1 = rate control that is applied to all frames regardless of the slice type, + # 2 = original plus intelligent QP selection for I and B slices (including Hierarchical), + # 3 = original + hybrid quadratic rate control for I and B slice using bit rate statistics + # +RCISliceBitRatio = 1.0 # target ratio of bits for I-coded pictures compared to P-coded Pictures (for RCUpdateMode=3) +RCBSliceBitRatio0 = 0.5 # target ratio of bits for B-coded pictures compared to P-coded Pictures - temporal level 0 (for RCUpdateMode=3) +RCBSliceBitRatio1 = 0.25 # target ratio of bits for B-coded pictures compared to P-coded Pictures - temporal level 1 (for RCUpdateMode=3) +RCBSliceBitRatio2 = 0.25 # target ratio of bits for B-coded pictures compared to P-coded Pictures - temporal level 2 (for RCUpdateMode=3) +RCBSliceBitRatio3 = 0.25 # target ratio of bits for B-coded pictures compared to P-coded Pictures - temporal level 3 (for RCUpdateMode=3) +RCBSliceBitRatio4 = 0.25 # target ratio of bits for B-coded pictures compared to P-coded Pictures - temporal level 4 (for RCUpdateMode=3) +RCBoverPRatio = 0.45 # ratio of bit rate usage of a B-coded picture over a P-coded picture for the SAME QP (for RCUpdateMode=3) +RCIoverPRatio = 3.80 # ratio of bit rate usage of an I-coded picture over a P-coded picture for the SAME QP (for RCUpdateMode=3) +RCMinQPPSlice = 8 # minimum P Slice QP value for rate control +RCMaxQPPSlice = 42 # maximum P Slice QP value for rate control +RCMinQPBSlice = 8 # minimum B Slice QP value for rate control +RCMaxQPBSlice = 42 # maximum B Slice QP value for rate control +RCMinQPISlice = 8 # minimum I Slice QP value for rate control +RCMaxQPISlice = 42 # maximum I Slice QP value for rate control +RCMinQPSPSlice = 8 # minimum SP Slice QP value for rate control +RCMaxQPSPSlice = 40 # maximum SP Slice QP value for rate control +RCMinQPSISlice = 8 # minimum SI Slice QP value for rate control +RCMaxQPSISlice = 42 # maximum SI Slice QP value for rate control +RCMaxQPChange = 4 # maximum QP change for frames of the base layer + +######################################################################################## +#Fast Mode Decision +######################################################################################## +EarlySkipEnable = 1 # Early skip detection (0: Disable 1: Enable) +SelectiveIntraEnable = 1 # Selective Intra mode decision (0: Disable 1: Enable) + +######################################################################################## +#FREXT stuff +######################################################################################## + +YUVFormat = 1 # YUV format (0=4:0:0, 1=4:2:0, 2=4:2:2, 3=4:4:4) +RGBInput = 0 # 1=RGB input, 0=GBR or YUV input +SeparateColourPlane = 0 # 4:4:4 coding: 0=Common mode, 1=Independent mode +SourceBitDepthLuma = 8 # Source Bit Depth for Luma color component (8...14 bits) +SourceBitDepthChroma = 8 # Source Bit Depth for Chroma color components (8...14 bits) +SourceBitDepthRescale = 1 # Rescale bit depth of source for output (0: Disable 1: Enable) +OutputBitDepthLuma = 8 # Output Bit Depth for Luma color component (8...14 bits) +OutputBitDepthChroma = 8 # Output Bit Depth for Chroma color components (8...14 bits) + +CbQPOffset = 0 # Chroma QP offset for Cb-part (-51..51) +CrQPOffset = 0 # Chroma QP offset for Cr-part (-51..51) +Transform8x8Mode = 1 # (0: only 4x4 transform, 1: allow using 8x8 transform additionally, 2: only 8x8 transform) +ReportFrameStats = 0 # (0:Disable Frame Statistics 1: Enable) +DisplayEncParams = 0 # (0:Disable Display of Encoder Params 1: Enable) +Verbose = 1 # level of display verboseness + # 0: short, 1: normal (default), 2: detailed, 3: detailed/nvb +SkipGlobalStats = 0 # Disable global stat accumulation (Set to 1 to avoid bipred core dump) + +######################################################################################## +#Q-Matrix (FREXT) +######################################################################################## +QmatrixFile = "q_matrix.cfg" + +ScalingMatrixPresentFlag = 0 # Enable Q_Matrix (0 Not present, 1 Present in SPS, 2 Present in PPS, 3 Present in both SPS & PPS) +ScalingListPresentFlag0 = 3 # Intra4x4_Luma (0 Not present, 1 Present in SPS, 2 Present in PPS, 3 Present in both SPS & PPS) +ScalingListPresentFlag1 = 3 # Intra4x4_ChromaU (0 Not present, 1 Present in SPS, 2 Present in PPS, 3 Present in both SPS & PPS) +ScalingListPresentFlag2 = 3 # Intra4x4_chromaV (0 Not present, 1 Present in SPS, 2 Present in PPS, 3 Present in both SPS & PPS) +ScalingListPresentFlag3 = 3 # Inter4x4_Luma (0 Not present, 1 Present in SPS, 2 Present in PPS, 3 Present in both SPS & PPS) +ScalingListPresentFlag4 = 3 # Inter4x4_ChromaU (0 Not present, 1 Present in SPS, 2 Present in PPS, 3 Present in both SPS & PPS) +ScalingListPresentFlag5 = 3 # Inter4x4_ChromaV (0 Not present, 1 Present in SPS, 2 Present in PPS, 3 Present in both SPS & PPS) +ScalingListPresentFlag6 = 3 # Intra8x8_Luma (0 Not present, 1 Present in SPS, 2 Present in PPS, 3 Present in both SPS & PPS) +ScalingListPresentFlag7 = 3 # Inter8x8_Luma (0 Not present, 1 Present in SPS, 2 Present in PPS, 3 Present in both SPS & PPS) +ScalingListPresentFlag8 = 3 # Intra8x8_ChromaU for 4:4:4 (0 Not present, 1 Present in SPS, 2 Present in PPS, 3 Present in both SPS & PPS) +ScalingListPresentFlag9 = 3 # Inter8x8_ChromaU for 4:4:4 (0 Not present, 1 Present in SPS, 2 Present in PPS, 3 Present in both SPS & PPS) +ScalingListPresentFlag10 = 3 # Intra8x8_ChromaV for 4:4:4 (0 Not present, 1 Present in SPS, 2 Present in PPS, 3 Present in both SPS & PPS) +ScalingListPresentFlag11 = 3 # Inter8x8_ChromaV for 4:4:4 (0 Not present, 1 Present in SPS, 2 Present in PPS, 3 Present in both SPS & PPS) + +######################################################################################## +#Rounding Offset control +######################################################################################## + +OffsetMatrixPresentFlag = 0 # Enable Explicit Offset Quantization Matrices (0: disable 1: enable) +QOffsetMatrixFile = "q_offset.cfg" # Explicit Quantization Matrices file + +AdaptiveRounding = 0 # Enable Adaptive Rounding based on JVT-N011 (0: disable, 1: enable) +AdaptRoundingFixed = 0 # Enable Global Adaptive rounding for all qps (0: disable, 1: enable - default/old) +AdaptRndPeriod = 1 # Period in terms of MBs for updating rounding offsets. + # 0 performs update at the picture level. Default is 16. 1 is as in JVT-N011. +AdaptRndChroma = 1 # Enables coefficient rounding adaptation for chroma + +AdaptRndWFactorIRef = 8 # Adaptive Rounding Weight for I/SI slices in reference pictures /4096 +AdaptRndWFactorPRef = 8 # Adaptive Rounding Weight for P/SP slices in reference pictures /4096 +AdaptRndWFactorBRef = 8 # Adaptive Rounding Weight for B slices in reference pictures /4096 +AdaptRndWFactorINRef = 8 # Adaptive Rounding Weight for I/SI slices in non reference pictures /4096 +AdaptRndWFactorPNRef = 8 # Adaptive Rounding Weight for P/SP slices in non reference pictures /4096 +AdaptRndWFactorBNRef = 8 # Adaptive Rounding Weight for B slices in non reference pictures /4096 + +AdaptRndCrWFactorIRef = 8 # Chroma Adaptive Rounding Weight for I/SI slices in reference pictures /4096 +AdaptRndCrWFactorPRef = 8 # Chroma Adaptive Rounding Weight for P/SP slices in reference pictures /4096 +AdaptRndCrWFactorBRef = 8 # Chroma Adaptive Rounding Weight for B slices in reference pictures /4096 +AdaptRndCrWFactorINRef = 8 # Chroma Adaptive Rounding Weight for I/SI slices in non reference pictures /4096 +AdaptRndCrWFactorPNRef = 8 # Chroma Adaptive Rounding Weight for P/SP slices in non reference pictures /4096 +AdaptRndCrWFactorBNRef = 8 # Chroma Adaptive Rounding Weight for B slices in non reference pictures /4096 + +################################################################ +# Rate Distortion Optimized Quantization +################################################################ +UseRDOQuant = 1 # Use Rate Distortion Optimized Quantization (0=disable, 1=enable) +RDOQ_DC = 1 # Enable Rate Distortion Optimized Quantization for DC components (0=disable, 1=enable) +RDOQ_CR = 1 # Enable Rate Distortion Optimized Quantization for Chroma components (0=disable, 1=enable) +RDOQ_DC_CR = 1 # Enable Rate Distortion Optimized Quantization for Chroma DC components (0=disable, 1=enable) +RDOQ_QP_Num = 1 # 1-9: Number of QP tested in RDO_Q (I/P/B slice) +RDOQ_CP_Mode = 0 # copy Mode from first QP tested +RDOQ_CP_MV = 0 # copy MV from first QP tested +RDOQ_Fast = 1 # Fast RDOQ decision method for multiple QPs + +######################################################################################## +#Lossless Coding (FREXT) +######################################################################################## + +LosslessCoding = 0 # Enable lossless coding when qpprime_y is zero (0 Disabled, 1 Enabled) + +######################################################################################## +#Fast Motion Estimation Control Parameters +######################################################################################## + +SearchMode = 3 # Motion estimation mode + # -1 = Full Search + # 0 = Fast Full Search (default) + # 1 = UMHexagon Search + # 2 = Simplified UMHexagon Search + # 3 = Enhanced Predictive Zonal Search (EPZS) + +UMHexDSR = 1 # Use Search Range Prediction. Only for UMHexagonS method + # (0:disable, 1:enabled/default) +UMHexScale = 3 # Use Scale_factor for different image sizes. Only for UMHexagonS method + # (0:disable, 3:/default) + # Increasing value can speed up Motion Search. + +EPZSPattern = 2 # Select EPZS primary refinement pattern. + # (0: small diamond, 1: square, 2: extended diamond/default, + # 3: large diamond, 4: SBP Large Diamond, + # 5: PMVFAST ) +EPZSDualRefinement = 3 # Enables secondary refinement pattern. + # (0:disabled, 1: small diamond, 2: square, + # 3: extended diamond/default, 4: large diamond, + # 5: SBP Large Diamond, 6: PMVFAST ) +EPZSFixedPredictors = 2 # Enables Window based predictors + # (0:disabled, 1: P only, 2: P and B/default) +EPZSTemporal = 1 # Enables temporal predictors + # (0: disabled, 1: enabled/default) +EPZSSpatialMem = 1 # Enables spatial memory predictors + # (0: disabled, 1: enabled/default) +EPZSBlockType = 1 # Enables block type Predictors + # (0: disabled, 1: enabled/default) +EPZSMinThresScale = 0 # Scaler for EPZS minimum threshold (0 default). + # Increasing value can speed up encoding. +EPZSMedThresScale = 1 # Scaler for EPZS median threshold (1 default). + # Increasing value can speed up encoding. +EPZSMaxThresScale = 1 # Scaler for EPZS maximum threshold (1 default). + # Increasing value can speed up encoding. + +EPZSSubPelME = 1 # EPZS Subpel ME consideration +EPZSSubPelMEBiPred = 1 # EPZS Subpel ME consideration for BiPred partitions +EPZSSubPelThresScale = 2 # EPZS Subpel ME Threshold scaler +EPZSSubPelGrid = 1 # Perform EPZS using a subpixel grid + +######################################################################################## +# SEI Parameters +######################################################################################## + +ToneMappingSEIPresentFlag = 0 # Enable Tone mapping SEI (0 Not present, 1 Present) +ToneMappingFile = "ToneMapping.cfg" + +GenerateSEIMessage = 0 # Generate an SEI Text Message +SEIMessageText = "H.264/AVC Encoder" # Text SEI Message + +UseMVLimits = 0 # Use MV Limits +SetMVXLimit = 512 # Horizontal MV Limit (in integer units) +SetMVYLimit = 512 # Vertical MV Limit (in integer units) + +######################################################################################## +# VUI Parameters +######################################################################################## +# the variables below do not affect encoding and decoding +# (many are dummy variables but others can be useful when supported by the decoder) + +EnableVUISupport = 0 # Enable VUI Parameters + +# display parameters +VUI_aspect_ratio_info_present_flag = 0 +VUI_aspect_ratio_idc = 1 +VUI_sar_width = 0 +VUI_sar_height = 0 +VUI_overscan_info_present_flag = 0 +VUI_overscan_appropriate_flag = 0 +VUI_video_signal_type_present_flag = 0 +VUI_video_format = 5 +VUI_video_full_range_flag = 0 +VUI_colour_description_present_flag = 0 +VUI_colour_primaries = 2 +VUI_transfer_characteristics = 2 +VUI_matrix_coefficients = 2 +VUI_chroma_location_info_present_flag = 0 +VUI_chroma_sample_loc_type_top_field = 0 +VUI_chroma_sample_loc_type_bottom_field = 0 +VUI_timing_info_present_flag = 0 +VUI_num_units_in_tick = 1000 +VUI_time_scale = 60000 +VUI_fixed_frame_rate_flag = 0 + +# nal hrd parameters +VUI_nal_hrd_parameters_present_flag = 0 +VUI_nal_cpb_cnt_minus1 = 0 +VUI_nal_bit_rate_scale = 0 +VUI_nal_cpb_size_scale = 0 +VUI_nal_bit_rate_value_minus1 = 0 +VUI_nal_cpb_size_value_minus1 = 0 +VUI_nal_vbr_cbr_flag = 0 +VUI_nal_initial_cpb_removal_delay_length_minus1 = 23 +VUI_nal_cpb_removal_delay_length_minus1 = 23 +VUI_nal_dpb_output_delay_length_minus1 = 23 +VUI_nal_time_offset_length = 24 + +# vlc hrd parameters +VUI_vcl_hrd_parameters_present_flag = 0 +VUI_vcl_cpb_cnt_minus1 = 0 +VUI_vcl_bit_rate_scale = 0 +VUI_vcl_cpb_size_scale = 0 +VUI_vcl_bit_rate_value_minus1 = 0 +VUI_vcl_cpb_size_value_minus1 = 0 +VUI_vcl_vbr_cbr_flag = 0 +VUI_vcl_initial_cpb_removal_delay_length_minus1 = 23 +VUI_vcl_cpb_removal_delay_length_minus1 = 23 +VUI_vcl_dpb_output_delay_length_minus1 = 23 +VUI_vcl_time_offset_length = 24 +VUI_low_delay_hrd_flag = 0 + +# other parameters (i.e. bitsream restrictions) +VUI_pic_struct_present_flag = 0 +VUI_bitstream_restriction_flag = 0 +VUI_motion_vectors_over_pic_boundaries_flag = 1 +VUI_max_bytes_per_pic_denom = 0 +VUI_max_bits_per_mb_denom = 0 +VUI_log2_max_mv_length_vertical = 16 +VUI_log2_max_mv_length_horizontal = 16 +VUI_num_reorder_frames = 16 +VUI_max_dec_frame_buffering = 16 + diff --git a/bin/HM-like/encoder_JM_LP_HE.cfg b/bin/HM-like/encoder_JM_LP_HE.cfg new file mode 100644 index 0000000..a04cd30 --- /dev/null +++ b/bin/HM-like/encoder_JM_LP_HE.cfg @@ -0,0 +1,671 @@ +# New Input File Format is as follows +# = # Comment +# +# See configfile.h for a list of supported ParameterNames +# +# For bug reporting and known issues see: +# https://ipbt.hhi.fraunhofer.de + + +########################################################################################## +# Files +########################################################################################## +InputFile = "D:\origCfP\BQSquare_416x240_60.yuv" # Input sequence +InputHeaderLength = 0 # If the inputfile has a header, state it's length in byte here +StartFrame = 0 # Start frame for encoding. (0-N) +FramesToBeEncoded = 30 # Number of frames to be coded +FrameRate = 60.0 # Frame Rate per second (0.1-100.0) +Enable32Pulldown = 0 # Enable 'hard' 3:2 pulldown (modifying the inpur data) + # 0 = disabled + # 1 = A, B, Bt|Cb, Ct|Db, D + # 2 = A, B, C, Ct|Db, D +SEIVUI32Pulldown = 0 # Enable 3:2 pulldown through VUI and SEI metadata signaling. Five methods are supported: + # 0 = disabled + # 1 = A, Bt|Bb, Bt|Cb, Ct|Cb, D + # 2 = A, B, C, C, D + # 3 = At|Ab, Bt|Bb, Bt|Cb, Ct|Cb, Dt|Db + # 4 = A, Bt|Bb, Bt|Cb, Ct|Db, Dt|Db + # 5 = At|Ab, Bt|Bb, Bt|Cb, Ct|Db, Dt|Db + +SourceWidth = 416 # Source frame width +SourceHeight = 240 # Source frame height +SourceResize = 0 # Resize source size for output +OutputWidth = 416 # Output frame width +OutputHeight = 240 # Output frame height +ProcessInput = 0 # Filter Input Sequence +Interleaved = 0 # 0: Planar input, 1: Packed input +StandardRange = 0 # 0: Standard range 1: Full range (RGB input) +VideoCode = 1 # Video codes for RGB ==> YUV conversions + # 0 = NULL, + # 1 = ITU_REC709, + # 2 = CCIR_601, + # 3 = FCC, + # 4 = ITU_REC624BG, + # 5 = SMPTE_170M, + # 6 = SMPTE_240M, + # 7 = SMPTE_260M, + # 8 = ITU_REC709_EXACT + +TraceFile = "" # Trace file +ReconFile = "test_rec.yuv" # Reconstruction YUV file +OutputFile = "test.264" # Bitstream +StatsFile = "" # Coding statistics file + +NumberOfViews = 1 # Number of views to encode (1=1 view, 2=2 views) +View1ConfigFile = "encoder_view1.cfg" # Config file name for second view +########################################################################################## +# Encoder Control +########################################################################################## +Grayscale = 0 # Encode in grayscale (Currently only works for 8 bit YUV 420 input) +ProfileIDC = 100 # Profile IDC (66=baseline, 77=main, 88=extended; FREXT Profiles: 100=High, 110=High 10, 122=High 4:2:2, 244=High 4:4:4, 44=CAVLC 4:4:4 Intra, 118=Multiview High Profile, 128=Stereo High Profile) +IntraProfile = 0 # Activate Intra Profile for FRExt (0: false, 1: true) + # (e.g. ProfileIDC=110, IntraProfile=1 => High 10 Intra Profile) +LevelIDC = 51 # Level IDC (e.g. 20 = level 2.0) + +IntraPeriod = 0 # Period of I-pictures (0=only first) +IDRPeriod = 0 # Period of IDR pictures (0=only first) +AdaptiveIntraPeriod = 0 # Adaptive intra period +AdaptiveIDRPeriod = 0 # Adaptive IDR period +IntraDelay = 0 # Intra (IDR) picture delay (i.e. coding structure of PPIPPP... ) +EnableIDRGOP = 0 # Support for IDR closed GOPs (0: disabled, 1: enabled) +EnableOpenGOP = 0 # Support for open GOPs (0: disabled, 1: enabled) +QPISlice = 32 # Quant. param for I Slices (0-51) +QPPSlice = 33 # Quant. param for P Slices (0-51) +FrameSkip = 0 # Number of frames to be skipped in input (e.g 2 will code every third frame). + # Note that this now excludes intermediate (i.e. B) coded pictures +ChromaQPOffset = 0 # Chroma QP offset (-51..51) + +DisableSubpelME = 0 # Disable Subpixel Motion Estimation (0=off/default, 1=on) +SearchRange = 128 # Max search range + +MEDistortionFPel = 0 # Select error metric for Full-Pel ME (0: SAD, 1: SSE, 2: Hadamard SAD) +MEDistortionHPel = 2 # Select error metric for Half-Pel ME (0: SAD, 1: SSE, 2: Hadamard SAD) +MEDistortionQPel = 2 # Select error metric for Quarter-Pel ME (0: SAD, 1: SSE, 2: Hadamard SAD) +MDDistortion = 2 # Select error metric for Mode Decision (0: SAD, 1: SSE, 2: Hadamard SAD) +SkipDeBlockNonRef = 0 # Skip Deblocking (regardless of DFParametersFlag) for non-reference frames (0: off, 1: on) +ChromaMCBuffer = 1 # Calculate Color component interpolated values in advance and store them. + # Provides a trade-off between memory and computational complexity + # (0: disabled/default, 1: enabled) +ChromaMEEnable = 0 # Take into account Color component information during ME + # (0: only first component/default, + # 1: All Color components - Integer refinement only + # 2: All Color components - All refinements) +ChromaMEWeight = 1 # Weighting for chroma components. This parameter should have a relationship with color format. + +NumberReferenceFrames = 4 # Number of previous frames used for inter motion search (0-16) + +PList0References = 0 # P slice List 0 reference override (0 disable, N <= NumberReferenceFrames) +Log2MaxFNumMinus4 = 0 # Sets log2_max_frame_num_minus4 (-1 : based on FramesToBeEncoded/Auto, >=0 : Log2MaxFNumMinus4) +Log2MaxPOCLsbMinus4 = -1 # Sets log2_max_pic_order_cnt_lsb_minus4 (-1 : Auto, >=0 : Log2MaxPOCLsbMinus4) + +GenerateMultiplePPS = 1 # Transmit multiple parameter sets. Currently parameters basically enable all WP modes (0: diabled, 1: enabled) +SendAUD = 0 # Send Access Delimiter Unit NALU (for every access unit) +ResendSPS = 0 # Resend SPS (0: disabled, 1: all Intra pictures, 2: only for IDR, 3: for IDR and OpenGOP I) +ResendPPS = 0 # Resend PPS (with pic_parameter_set_id 0) for every coded Frame/Field pair (0: disabled, 1: enabled) + +MbLineIntraUpdate = 0 # Error robustness(extra intra macro block updates)(0=off, N: One GOB every N frames are intra coded) +RandomIntraMBRefresh = 0 # Forced intra MBs per picture + +########################################################################################## +# PSlice Mode types +########################################################################################## +PSliceSkip = 1 # P-Slice Skip mode consideration (0=disable, 1=enable) +PSliceSearch16x16 = 1 # P-Slice Inter block search 16x16 (0=disable, 1=enable) +PSliceSearch16x8 = 1 # P-Slice Inter block search 16x8 (0=disable, 1=enable) +PSliceSearch8x16 = 1 # P-Slice Inter block search 8x16 (0=disable, 1=enable) +PSliceSearch8x8 = 1 # P-Slice Inter block search 8x8 (0=disable, 1=enable) +PSliceSearch8x4 = 1 # P-Slice Inter block search 8x4 (0=disable, 1=enable) +PSliceSearch4x8 = 1 # P-Slice Inter block search 4x8 (0=disable, 1=enable) +PSliceSearch4x4 = 1 # P-Slice Inter block search 4x4 (0=disable, 1=enable) + +########################################################################################## +# BSlice Mode types +########################################################################################## + +BSliceDirect = 1 # B-Slice Skip mode consideration (0=disable, 1=enable) +BSliceSearch16x16 = 1 # B-Slice Inter block search 16x16 (0=disable, 1=enable) +BSliceSearch16x8 = 1 # B-Slice Inter block search 16x8 (0=disable, 1=enable) +BSliceSearch8x16 = 1 # B-Slice Inter block search 8x16 (0=disable, 1=enable) +BSliceSearch8x8 = 1 # B-Slice Inter block search 8x8 (0=disable, 1=enable) +BSliceSearch8x4 = 1 # B-Slice Inter block search 8x4 (0=disable, 1=enable) +BSliceSearch4x8 = 1 # B-Slice Inter block search 4x8 (0=disable, 1=enable) +BSliceSearch4x4 = 1 # B-Slice Inter block search 4x4 (0=disable, 1=enable) + +BiPredSearch16x16 = 1 # B-Slice Bi-prediction block search 16x16 (0=disable, 1=enable) +BiPredSearch16x8 = 1 # B-Slice Bi-prediction block search 16x8 (0=disable, 1=enable) +BiPredSearch8x16 = 1 # B-Slice Bi-prediction block search 8x16 (0=disable, 1=enable) +BiPredSearch8x8 = 1 # B-Slice Bi-prediction block search 8x8 (0=disable, 1=enable) + +DisableIntra4x4 = 0 # Disable Intra 4x4 modes +DisableIntra16x16 = 0 # Disable Intra 16x16 modes +DisableIntraInInter = 0 # Disable Intra modes for inter slices +IntraDisableInterOnly = 0 # Apply Disabling Intra conditions only to Inter Slices (0:disable/default,1: enable) +Intra4x4ParDisable = 0 # Disable Vertical & Horizontal 4x4 +Intra4x4DiagDisable = 0 # Disable Diagonal 45degree 4x4 +Intra4x4DirDisable = 0 # Disable Other Diagonal 4x4 +Intra16x16ParDisable = 0 # Disable Vertical & Horizontal 16x16 +Intra16x16PlaneDisable = 0 # Disable Planar 16x16 +ChromaIntraDisable = 0 # Disable Intra Chroma modes other than DC +EnableIPCM = 1 # Enable IPCM macroblock mode + +DisposableP = 0 # Enable Disposable P slices in the primary layer (0: disable/default, 1: enable) +DispPQPOffset = 0 # Quantizer offset for disposable P slices (0: default) + +PreferDispOrder = 0 # Prefer display order when building the prediction structure as opposed to coding order (affects intra and IDR periodic insertion, among others) +PreferPowerOfTwo = 0 # Prefer prediction structures that have lengths expressed as powers of two +FrmStructBufferLength = 16 # Length of the frame structure unit buffer; it can be overriden for certain cases + +ChangeQPFrame = 0 # Frame in display order from which to apply the Change QP offsets +ChangeQPI = 0 # Change QP offset value for I_SLICE +ChangeQPP = 0 # Change QP offset value for P_SLICE +ChangeQPB = 0 # Change QP offset value for B_SLICE +ChangeQPSI = 0 # Change QP offset value for SI_SLICE +ChangeQPSP = 0 # Change QP offset value for SP_SLICE + +########################################################################################## +# B Slices +########################################################################################## + +NumberBFrames = 3 # Number of B coded frames inserted (0=not used) +PReplaceBSlice = 1 # Replace B-coded slices with P-coded slices when NumberBFrames>0 +QPBSlice = 33 # Quant. param for B slices (0-51) +BRefPicQPOffset = 0 # Quantization offset for reference B coded pictures (-51..51) +DirectModeType = 1 # Direct Mode Type (0:Temporal 1:Spatial) +DirectInferenceFlag = 1 # Direct Inference Flag (0: Disable 1: Enable) +BList0References = 4 # B slice List 0 reference override (0 disable, N <= NumberReferenceFrames) +BList1References = 4 # B slice List 1 reference override (0 disable, N <= NumberReferenceFrames) + # 1 List1 reference is usually recommended for normal GOP Structures. + # A larger value is usually more appropriate if a more flexible + # structure is used (i.e. using HierarchicalCoding) + +BLevel0MoreRef = 0 # 0: Use the same number of reference pictures as the other B pictures + # 1: Use more reference frame pictures for B pictures at level 0 +BIdenticalList = 0 # 0: not used; 1: used for B pictures at level 0; 2: used for all B pictures + # 1 and 2 need to set ReferenceReorder to 1 +CRA = 0 # 0: do not use CRA like random access support; 1 use CRA like random access support + # only used for random access cases, LowDelay != 1 +HM50RefStructure = 0 # 0: do not use HM-5.0 like referencing structure; 1 use HM-5.0 like referencing structure for random access +LDRefSetting = 1 # 0: use nearest pictures as reference frames + # 1: use one nearest + POC%4==0 pictures as reference frames, as JCTVC-F701 + # Only low delay is support, please do not use it for other coding structure + +BReferencePictures = 0 # Referenced B coded pictures (0=off, 1=B references for secondary layer, 2=B references for primary layer) + +HierarchicalCoding = 3 # B hierarchical coding (0= off, 1= 2 layers, 2= 2 full hierarchy, 3 = explicit) +HierarchyLevelQPEnable = 0 # Adjust QP based on hierarchy level (in increments of 1). Overrides BRefPicQPOffset behavior.(0=off, 1=on) +ExplicitHierarchyFormat = "P0r2P1r1P2r2" # Explicit Enhancement GOP. Format is {FrameDisplay_orderReferenceQP}. + # Valid values for reference type is r:reference, e:non reference. +ExplicitSeqCoding = 0 # Enable support for explicit sequence coding +ExplicitSeqFile = "explicit_seq.cfg" +LowDelay = 1 # Apply HierarchicalCoding without delay (i.e., encode in the captured/display order) +ReferenceReorder = 0 # Reorder References according to Poc distance for HierarchicalCoding (0=off, 1=enable, 2=use when LowDelay is set) +PocMemoryManagement = 0 # Memory management based on Poc Distances for HierarchicalCoding (0=off, 1=on, 2=use when LowDelay is set) +SetFirstAsLongTerm = 0 # Set first frame as long term + +BiPredMotionEstimation = 1 # Enable Bipredictive based Motion Estimation (0:disabled, 1:enabled) +BiPredMERefinements = 3 # Bipredictive ME extra refinements (0: single, N: N extra refinements (1 default) +BiPredMESearchRange = 128 # Bipredictive ME Search range (8 default). Note that range is halved for every extra refinement. +BiPredMESubPel = 2 # Bipredictive ME Subpixel Consideration (0: disabled, 1: single level, 2: dual level) + + +########################################################################################## +# SP Frames +########################################################################################## + +SPPicturePeriodicity = 0 # SP-Picture Periodicity (0=not used) +SPSwitchPeriod = 0 # Switch period (in terms of switching SP/SI frames) between bitstream 1 and bitstream 2 +QPSPSlice = 36 # Quant. param of SP-Slices for Prediction Error (0-51) +QPSISlice = 36 # Quant. param of SI-Slices for Prediction Error (0-51) +QPSP2Slice = 35 # Quant. param of SP/SI-Slices for Predicted Blocks (0-51) +SI_FRAMES = 0 # SI frame encoding flag (0=not used, 1=used) +SP_output = 0 # Controls whether coefficients will be output to encode switching SP frames (0=no, 1=yes) +SP_output_name = "low_quality.dat" # Filename for SP output coefficients +SP2_FRAMES = 0 # switching SP frame encoding flag (0=not used, 1=used) +SP2_input_name1 = "high_quality.dat" # Filename for the first swithed bitstream coefficients +SP2_input_name2 = "low_quality.dat" # Filename for the second switched bitstream coefficients + +########################################################################################## +# Output Control, NALs +########################################################################################## + +SymbolMode = 1 # Symbol mode (Entropy coding method: 0=UVLC, 1=CABAC) +OutFileMode = 0 # Output file mode, 0:Annex B, 1:RTP +PartitionMode = 0 # Partition Mode, 0: no DP, 1: 3 Partitions per Slice + +########################################################################################## +# CABAC context initialization +########################################################################################## + +ContextInitMethod = 1 # Context init (0: fixed, 1: adaptive) +FixedModelNumber = 0 # model number for fixed decision for inter slices ( 0, 1, or 2 ) + +########################################################################################## +# Interlace Handling +######################################################################################### + +PicInterlace = 0 # Picture AFF (0: frame coding, 1: field coding, 2:adaptive frame/field coding) +MbInterlace = 0 # Macroblock AFF (0: frame coding, 1: field coding, 2:adaptive frame/field coding, 3: frame MB-only AFF) +IntraBottom = 0 # Force Intra Bottom at GOP Period + +########################################################################################## +# Weighted Prediction +######################################################################################### + +WeightedPrediction = 1 # P picture Weighted Prediction (0=off, 1=explicit mode) +WeightedBiprediction = 1 # B picture Weighted Prediciton (0=off, 1=explicit mode, 2=implicit mode) +ChromaWeightSupport = 1 # Enable consideration of weights for Chroma components +UseWeightedReferenceME = 1 # Use weighted reference for ME (0=off, 1=on) +WPMethod = 1 # WP method (0: DC based, 1: LMS based) +WPIterMC = 0 # Iterative Motion compensated based weighted prediction method +EnhancedBWeightSupport = 0 # Enhanced B Weight support (needs revisit if we wish to merge with WPMethod) +WPMCPrecision = 0 # Improved Motion Compensation Precision using WP based methods. + # Clones WP references with slightly modified rounding offsets (Requires RDPictureDecision and GenerateMultiplePPS) : + # 0: disabled (default) + # 1: Up to one additional coding pass. Ref0 is 0, ref1 is 0 with a -1 offset + # 2: Up to two additional coding passes. (1) Ref0 is 0, ref1 is 0 with a -1 offset, (1) Ref0 is 0 with a -1 offset, ref1 is 0 +WPMCPrecFullRef = 0 # Increases the number of references in the reference picture lists to account + # for the lost reference slot when reordering is used during a coding pass in WPMCPrecision for reference replication. + # The number of references in non-reordered passes stays unchanged +WPMCPrecBSlice = 1 # 2: Apply rounding on every B slice. This efectively disables the evaluation of alternative QPs during RDPictureDecision. + # 1: Disable rounding for non-reference B slices. Non-reference B slices are evaluated for alternative QPs during RDPictureDecision. + # 0: Disable rounding for B slices. + +########################################################################################## +# Picture based Multi-pass encoding +######################################################################################### + +RDPictureDecision = 1 # Perform multiple pass coding and make RD optimal decision among them +RDPSliceBTest = 0 # Perform Slice level RD decision between P and B slices. +RDPSliceITest = 1 # Perform Slice level RD decision between P and I slices. Default value is 1 (enabled). +RDPictureMaxPassISlice = 1 # Max number of coding passes for I slices, valid values [1,3], default is 1 +RDPictureMaxPassPSlice = 2 # Max number of coding passes for P slices, valid values [1,6], default is 2 +RDPictureMaxPassBSlice = 3 # Max number of coding passes for B slices, valid values [1,6], default is 3 +RDPictureFrameQPPSlice = 0 # Perform additional frame level QP check (QP+/-1) for P slices, 0: disabled (default), 1: enabled +RDPictureFrameQPBSlice = 0 # Perform additional frame level QP check (QP+/-1) for B slices, 0: disabled, 1: enabled (default) +RDPictureDeblocking = 0 # Perform another coding pass to check non-deblocked picture, 0: disabled (default), 1: enabled +RDPictureDirectMode = 0 # Perform another coding pass to check the alternative direct mode for B slices, , 0: disabled (default), 1: enabled + +########################################################################################## +# Deblocking filter parameters +########################################################################################## + +DFParametersFlag = 0 # Configure deblocking filter (0=parameters below ignored, 1=parameters sent) + # Note that for pictures with multiple slice types, + # only the type of the first slice will be considered. +DFDisableRefISlice = 0 # Disable deblocking filter in reference I coded pictures (0=Filter, 1=No Filter). +DFAlphaRefISlice = 0 # Reference I coded pictures Alpha offset div. 2, {-6, -5, ... 0, +1, .. +6} +DFBetaRefISlice = 0 # Reference I coded pictures Beta offset div. 2, {-6, -5, ... 0, +1, .. +6} +DFDisableNRefISlice = 0 # Disable deblocking filter in non reference I coded pictures (0=Filter, 1=No Filter). +DFAlphaNRefISlice = 0 # Non Reference I coded pictures Alpha offset div. 2, {-6, -5, ... 0, +1, .. +6} +DFBetaNRefISlice = 0 # Non Reference I coded pictures Beta offset div. 2, {-6, -5, ... 0, +1, .. +6} +DFDisableRefPSlice = 0 # Disable deblocking filter in reference P coded pictures (0=Filter, 1=No Filter). +DFAlphaRefPSlice = 0 # Reference P coded pictures Alpha offset div. 2, {-6, -5, ... 0, +1, .. +6} +DFBetaRefPSlice = 0 # Reference P coded pictures Beta offset div. 2, {-6, -5, ... 0, +1, .. +6} +DFDisableNRefPSlice = 0 # Disable deblocking filter in non reference P coded pictures (0=Filter, 1=No Filter). +DFAlphaNRefPSlice = 0 # Non Reference P coded pictures Alpha offset div. 2, {-6, -5, ... 0, +1, .. +6} +DFBetaNRefPSlice = 0 # Non Reference P coded pictures Beta offset div. 2, {-6, -5, ... 0, +1, .. +6} +DFDisableRefBSlice = 0 # Disable deblocking filter in reference B coded pictures (0=Filter, 1=No Filter). +DFAlphaRefBSlice = 0 # Reference B coded pictures Alpha offset div. 2, {-6, -5, ... 0, +1, .. +6} +DFBetaRefBSlice = 0 # Reference B coded pictures Beta offset div. 2, {-6, -5, ... 0, +1, .. +6} +DFDisableNRefBSlice = 0 # Disable deblocking filter in non reference B coded pictures (0=Filter, 1=No Filter). +DFAlphaNRefBSlice = 0 # Non Reference B coded pictures Alpha offset div. 2, {-6, -5, ... 0, +1, .. +6} +DFBetaNRefBSlice = 0 # Non Reference B coded pictures Beta offset div. 2, {-6, -5, ... 0, +1, .. +6} + +########################################################################################## +# Error Resilience / Slices +########################################################################################## + +SliceMode = 0 # Slice mode (0=off 1=fixed #mb in slice 2=fixed #bytes in slice 3=use callback) +SliceArgument = 50 # Slice argument (Arguments to modes 1 and 2 above) + +num_slice_groups_minus1 = 0 # Number of Slice Groups Minus 1, 0 == no FMO, 1 == two slice groups, etc. +slice_group_map_type = 0 # 0: Interleave, 1: Dispersed, 2: Foreground with left-over, + # 3: Box-out, 4: Raster Scan 5: Wipe + # 6: Explicit, slice_group_id read from SliceGroupConfigFileName +slice_group_change_direction_flag = 0 # 0: box-out clockwise, raster scan or wipe right, + # 1: box-out counter clockwise, reverse raster scan or wipe left +slice_group_change_rate_minus1 = 85 # +SliceGroupConfigFileName = "sg0conf.cfg" # Used for slice_group_map_type 0, 2, 6 + +UseRedundantPicture = 0 # 0: not used, 1: enabled +NumRedundantHierarchy = 1 # 0-4 +PrimaryGOPLength = 10 # GOP length for redundant allocation (1-16) + # NumberReferenceFrames must be no less than PrimaryGOPLength when redundant slice enabled +NumRefPrimary = 1 # Actually used number of references for primary slices (1-16) + +########################################################################################## +# Search Range Restriction / RD Optimization +########################################################################################## + +RestrictSearchRange = 2 # restriction for (0: blocks and ref, 1: ref, 2: no restrictions) +RDOptimization = 1 # rd-optimized mode decision + # 0: RD-off (Low complexity mode) + # 1: RD-on (High complexity mode) + # 2: RD-on (Fast high complexity mode - not work in FREX Profiles) + # 3: with losses +I16RDOpt = 1 # perform rd-optimized mode decision for Intra 16x16 MB + # 0: SAD-based mode decision for Intra 16x16 MB + # 1: RD-based mode decision for Intra 16x16 MB +SubMBCodingState = 2 # submacroblock coding state + # 0: lowest complexity, do not store or reset coding state during sub-MB mode decision + # 1: medium complexity, reset to master coding state (for current mode) during sub-MB mode decision + # 2: highest complexity, store and reset coding state during sub-MB mode decision +DistortionSSIM = 0 # Compute SSIM distortion. (0: disabled/default, 1: enabled) +DistortionMS_SSIM = 0 # Compute Multiscale SSIM distortion. (0: disabled/default, 1: enabled) +SSIMOverlapSize = 8 # Overlap size to calculate SSIM distortion (1: pixel by pixel, 8: no overlap) +DistortionYUVtoRGB = 0 # Calculate distortion in RGB domain after conversion from YCbCr (0:off, 1:on) +CtxAdptLagrangeMult = 0 # Context Adaptive Lagrange Multiplier + # 0: disabled (default) + # 1: enabled (works best when RDOptimization=0) +FastCrIntraDecision = 0 # Fast Chroma intra mode decision (0:off, 1:on) +DisableThresholding = 0 # Disable Thresholding of Transform Coefficients (0:off, 1:on) +DisableBSkipRDO = 0 # Disable B Skip Mode consideration from RDO Mode decision (0:off, 1:on) +BiasSkipRDO = 0 # Negative Bias for Skip/DirectSkip modes (0: off, 1: on) +ForceTrueRateRDO = 0 # Force true rate (even zero values) during RDO process +SkipIntraInInterSlices = 0 # Skips Intra mode checking in inter slices if certain mode decisions are satisfied (0: off, 1: on) +WeightY = 1 # Luma weight for RDO +WeightCb = 1 # Cb weight for RDO +WeightCr = 1 # Cr weight for RDO + +########################################################################################## +# Explicit Lambda Usage +########################################################################################## +UseExplicitLambdaParams = 0 # Use explicit lambda scaling parameters (0:disabled, 1:enable lambda weight, 2: use explicit lambda value) +UpdateLambdaChromaME = 0 # Update lambda given Chroma ME consideration +FixedLambdaISlice = 0.1 # Fixed Lambda value for I slices +FixedLambdaPSlice = 0.1 # Fixed Lambda value for P slices +FixedLambdaBSlice = 0.1 # Fixed Lambda value for B slices +FixedLambdaRefBSlice = 0.1 # Fixed Lambda value for Referenced B slices +FixedLambdaSPSlice = 0.1 # Fixed Lambda value for SP slices +FixedLambdaSISlice = 0.1 # Fixed Lambda value for SI slices + +LambdaWeightISlice = 0.65 # scaling param for I slices. This will be used as a multiplier i.e. lambda=LambdaWeightISlice * 2^((QP-12)/3) +LambdaWeightPSlice = 0.68 # scaling param for P slices. This will be used as a multiplier i.e. lambda=LambdaWeightPSlice * 2^((QP-12)/3) +LambdaWeightBSlice = 0.68 # scaling param for B slices. This will be used as a multiplier i.e. lambda=LambdaWeightBSlice * 2^((QP-12)/3) +LambdaWeightRefBSlice = 0.68 # scaling param for Referenced B slices. This will be used as a multiplier i.e. lambda=LambdaWeightRefBSlice * 2^((QP-12)/3) +LambdaWeightSPSlice = 0.68 # scaling param for SP slices. This will be used as a multiplier i.e. lambda=LambdaWeightSPSlice * 2^((QP-12)/3) +LambdaWeightSISlice = 0.65 # scaling param for SI slices. This will be used as a multiplier i.e. lambda=LambdaWeightSISlice * 2^((QP-12)/3) + +LossRateA = 5 # expected packet loss rate of the channel for the first partition, only valid if RDOptimization = 3 +LossRateB = 0 # expected packet loss rate of the channel for the second partition, only valid if RDOptimization = 3 +LossRateC = 0 # expected packet loss rate of the channel for the third partition, only valid if RDOptimization = 3 +FirstFrameCorrect = 0 # If 1, the first frame is encoded under the assumption that it is always correctly received. +NumberOfDecoders = 30 # Numbers of decoders used to simulate the channel, only valid if RDOptimization = 3 +RestrictRefFrames = 0 # Doesnt allow reference to areas that have been intra updated in a later frame. + +########################################################################################## +# Additional Stuff +######################################################################################### + +UseConstrainedIntraPred = 0 # If 1, Inter pixels are not used for Intra macroblock prediction. + +NumberofLeakyBuckets = 8 # Number of Leaky Bucket values +LeakyBucketRateFile = "leakybucketrate.cfg" # File from which encoder derives rate values +LeakyBucketParamFile = "leakybucketparam.cfg" # File where encoder stores leakybucketparams + +NumFramesInELayerSubSeq = 0 # number of frames in the Enhanced Scalability Layer(0: no Enhanced Layer) + +SparePictureOption = 0 # (0: no spare picture info, 1: spare picture available) +SparePictureDetectionThr = 6 # Threshold for spare reference pictures detection +SparePicturePercentageThr = 92 # Threshold for the spare macroblock percentage + +PicOrderCntType = 0 # (0: POC mode 0, 1: POC mode 1, 2: POC mode 2) + +######################################################################################## +#Rate control +######################################################################################## + +RateControlEnable = 0 # 0 Disable, 1 Enable +Bitrate = 45020 # Bitrate(bps) +InitialQP = 0 # Initial Quantization Parameter for the first I frame + # InitialQp depends on two values: Bits Per Picture, + # and the GOP length +BasicUnit = 0 # Number of MBs in the basic unit + # should be a fraction of the total number + # of MBs in a frame ("0" sets a BU equal to a frame) +ChannelType = 0 # type of channel( 1=time varying channel; 0=Constant channel) +RCUpdateMode = 0 # Rate Control type. Modes supported : + # 0 = original JM rate control, + # 1 = rate control that is applied to all frames regardless of the slice type, + # 2 = original plus intelligent QP selection for I and B slices (including Hierarchical), + # 3 = original + hybrid quadratic rate control for I and B slice using bit rate statistics + # +RCISliceBitRatio = 1.0 # target ratio of bits for I-coded pictures compared to P-coded Pictures (for RCUpdateMode=3) +RCBSliceBitRatio0 = 0.5 # target ratio of bits for B-coded pictures compared to P-coded Pictures - temporal level 0 (for RCUpdateMode=3) +RCBSliceBitRatio1 = 0.25 # target ratio of bits for B-coded pictures compared to P-coded Pictures - temporal level 1 (for RCUpdateMode=3) +RCBSliceBitRatio2 = 0.25 # target ratio of bits for B-coded pictures compared to P-coded Pictures - temporal level 2 (for RCUpdateMode=3) +RCBSliceBitRatio3 = 0.25 # target ratio of bits for B-coded pictures compared to P-coded Pictures - temporal level 3 (for RCUpdateMode=3) +RCBSliceBitRatio4 = 0.25 # target ratio of bits for B-coded pictures compared to P-coded Pictures - temporal level 4 (for RCUpdateMode=3) +RCBoverPRatio = 0.45 # ratio of bit rate usage of a B-coded picture over a P-coded picture for the SAME QP (for RCUpdateMode=3) +RCIoverPRatio = 3.80 # ratio of bit rate usage of an I-coded picture over a P-coded picture for the SAME QP (for RCUpdateMode=3) +RCMinQPPSlice = 8 # minimum P Slice QP value for rate control +RCMaxQPPSlice = 42 # maximum P Slice QP value for rate control +RCMinQPBSlice = 8 # minimum B Slice QP value for rate control +RCMaxQPBSlice = 42 # maximum B Slice QP value for rate control +RCMinQPISlice = 8 # minimum I Slice QP value for rate control +RCMaxQPISlice = 42 # maximum I Slice QP value for rate control +RCMinQPSPSlice = 8 # minimum SP Slice QP value for rate control +RCMaxQPSPSlice = 40 # maximum SP Slice QP value for rate control +RCMinQPSISlice = 8 # minimum SI Slice QP value for rate control +RCMaxQPSISlice = 42 # maximum SI Slice QP value for rate control +RCMaxQPChange = 4 # maximum QP change for frames of the base layer + +######################################################################################## +#Fast Mode Decision +######################################################################################## +EarlySkipEnable = 1 # Early skip detection (0: Disable 1: Enable) +SelectiveIntraEnable = 1 # Selective Intra mode decision (0: Disable 1: Enable) + +######################################################################################## +#FREXT stuff +######################################################################################## + +YUVFormat = 1 # YUV format (0=4:0:0, 1=4:2:0, 2=4:2:2, 3=4:4:4) +RGBInput = 0 # 1=RGB input, 0=GBR or YUV input +SeparateColourPlane = 0 # 4:4:4 coding: 0=Common mode, 1=Independent mode +SourceBitDepthLuma = 8 # Source Bit Depth for Luma color component (8...14 bits) +SourceBitDepthChroma = 8 # Source Bit Depth for Chroma color components (8...14 bits) +SourceBitDepthRescale = 0 # Rescale bit depth of source for output (0: Disable 1: Enable) +OutputBitDepthLuma = 8 # Output Bit Depth for Luma color component (8...14 bits) +OutputBitDepthChroma = 8 # Output Bit Depth for Chroma color components (8...14 bits) + +CbQPOffset = 0 # Chroma QP offset for Cb-part (-51..51) +CrQPOffset = 0 # Chroma QP offset for Cr-part (-51..51) +Transform8x8Mode = 1 # (0: only 4x4 transform, 1: allow using 8x8 transform additionally, 2: only 8x8 transform) +ReportFrameStats = 0 # (0:Disable Frame Statistics 1: Enable) +DisplayEncParams = 0 # (0:Disable Display of Encoder Params 1: Enable) +Verbose = 1 # level of display verboseness + # 0: short, 1: normal (default), 2: detailed, 3: detailed/nvb +SkipGlobalStats = 0 # Disable global stat accumulation (Set to 1 to avoid bipred core dump) + +######################################################################################## +#Q-Matrix (FREXT) +######################################################################################## +QmatrixFile = "q_matrix.cfg" + +ScalingMatrixPresentFlag = 0 # Enable Q_Matrix (0 Not present, 1 Present in SPS, 2 Present in PPS, 3 Present in both SPS & PPS) +ScalingListPresentFlag0 = 3 # Intra4x4_Luma (0 Not present, 1 Present in SPS, 2 Present in PPS, 3 Present in both SPS & PPS) +ScalingListPresentFlag1 = 3 # Intra4x4_ChromaU (0 Not present, 1 Present in SPS, 2 Present in PPS, 3 Present in both SPS & PPS) +ScalingListPresentFlag2 = 3 # Intra4x4_chromaV (0 Not present, 1 Present in SPS, 2 Present in PPS, 3 Present in both SPS & PPS) +ScalingListPresentFlag3 = 3 # Inter4x4_Luma (0 Not present, 1 Present in SPS, 2 Present in PPS, 3 Present in both SPS & PPS) +ScalingListPresentFlag4 = 3 # Inter4x4_ChromaU (0 Not present, 1 Present in SPS, 2 Present in PPS, 3 Present in both SPS & PPS) +ScalingListPresentFlag5 = 3 # Inter4x4_ChromaV (0 Not present, 1 Present in SPS, 2 Present in PPS, 3 Present in both SPS & PPS) +ScalingListPresentFlag6 = 3 # Intra8x8_Luma (0 Not present, 1 Present in SPS, 2 Present in PPS, 3 Present in both SPS & PPS) +ScalingListPresentFlag7 = 3 # Inter8x8_Luma (0 Not present, 1 Present in SPS, 2 Present in PPS, 3 Present in both SPS & PPS) +ScalingListPresentFlag8 = 3 # Intra8x8_ChromaU for 4:4:4 (0 Not present, 1 Present in SPS, 2 Present in PPS, 3 Present in both SPS & PPS) +ScalingListPresentFlag9 = 3 # Inter8x8_ChromaU for 4:4:4 (0 Not present, 1 Present in SPS, 2 Present in PPS, 3 Present in both SPS & PPS) +ScalingListPresentFlag10 = 3 # Intra8x8_ChromaV for 4:4:4 (0 Not present, 1 Present in SPS, 2 Present in PPS, 3 Present in both SPS & PPS) +ScalingListPresentFlag11 = 3 # Inter8x8_ChromaV for 4:4:4 (0 Not present, 1 Present in SPS, 2 Present in PPS, 3 Present in both SPS & PPS) + +######################################################################################## +#Rounding Offset control +######################################################################################## + +OffsetMatrixPresentFlag = 0 # Enable Explicit Offset Quantization Matrices (0: disable 1: enable) +QOffsetMatrixFile = "q_offset.cfg" # Explicit Quantization Matrices file + +AdaptiveRounding = 0 # Enable Adaptive Rounding based on JVT-N011 (0: disable, 1: enable) +AdaptRoundingFixed = 0 # Enable Global Adaptive rounding for all qps (0: disable, 1: enable - default/old) +AdaptRndPeriod = 1 # Period in terms of MBs for updating rounding offsets. + # 0 performs update at the picture level. Default is 16. 1 is as in JVT-N011. +AdaptRndChroma = 1 # Enables coefficient rounding adaptation for chroma + +AdaptRndWFactorIRef = 8 # Adaptive Rounding Weight for I/SI slices in reference pictures /4096 +AdaptRndWFactorPRef = 8 # Adaptive Rounding Weight for P/SP slices in reference pictures /4096 +AdaptRndWFactorBRef = 8 # Adaptive Rounding Weight for B slices in reference pictures /4096 +AdaptRndWFactorINRef = 8 # Adaptive Rounding Weight for I/SI slices in non reference pictures /4096 +AdaptRndWFactorPNRef = 8 # Adaptive Rounding Weight for P/SP slices in non reference pictures /4096 +AdaptRndWFactorBNRef = 8 # Adaptive Rounding Weight for B slices in non reference pictures /4096 + +AdaptRndCrWFactorIRef = 8 # Chroma Adaptive Rounding Weight for I/SI slices in reference pictures /4096 +AdaptRndCrWFactorPRef = 8 # Chroma Adaptive Rounding Weight for P/SP slices in reference pictures /4096 +AdaptRndCrWFactorBRef = 8 # Chroma Adaptive Rounding Weight for B slices in reference pictures /4096 +AdaptRndCrWFactorINRef = 8 # Chroma Adaptive Rounding Weight for I/SI slices in non reference pictures /4096 +AdaptRndCrWFactorPNRef = 8 # Chroma Adaptive Rounding Weight for P/SP slices in non reference pictures /4096 +AdaptRndCrWFactorBNRef = 8 # Chroma Adaptive Rounding Weight for B slices in non reference pictures /4096 + +################################################################ +# Rate Distortion Optimized Quantization +################################################################ +UseRDOQuant = 1 # Use Rate Distortion Optimized Quantization (0=disable, 1=enable) +RDOQ_DC = 1 # Enable Rate Distortion Optimized Quantization for DC components (0=disable, 1=enable) +RDOQ_CR = 1 # Enable Rate Distortion Optimized Quantization for Chroma components (0=disable, 1=enable) +RDOQ_DC_CR = 1 # Enable Rate Distortion Optimized Quantization for Chroma DC components (0=disable, 1=enable) +RDOQ_QP_Num = 1 # 1-9: Number of QP tested in RDO_Q (I/P/B slice) +RDOQ_CP_Mode = 0 # copy Mode from first QP tested +RDOQ_CP_MV = 0 # copy MV from first QP tested +RDOQ_Fast = 1 # Fast RDOQ decision method for multiple QPs + +######################################################################################## +#Lossless Coding (FREXT) +######################################################################################## + +LosslessCoding = 0 # Enable lossless coding when qpprime_y is zero (0 Disabled, 1 Enabled) + +######################################################################################## +#Fast Motion Estimation Control Parameters +######################################################################################## + +SearchMode = 3 # Motion estimation mode + # -1 = Full Search + # 0 = Fast Full Search (default) + # 1 = UMHexagon Search + # 2 = Simplified UMHexagon Search + # 3 = Enhanced Predictive Zonal Search (EPZS) + +UMHexDSR = 1 # Use Search Range Prediction. Only for UMHexagonS method + # (0:disable, 1:enabled/default) +UMHexScale = 3 # Use Scale_factor for different image sizes. Only for UMHexagonS method + # (0:disable, 3:/default) + # Increasing value can speed up Motion Search. + +EPZSPattern = 2 # Select EPZS primary refinement pattern. + # (0: small diamond, 1: square, 2: extended diamond/default, + # 3: large diamond, 4: SBP Large Diamond, + # 5: PMVFAST ) +EPZSDualRefinement = 3 # Enables secondary refinement pattern. + # (0:disabled, 1: small diamond, 2: square, + # 3: extended diamond/default, 4: large diamond, + # 5: SBP Large Diamond, 6: PMVFAST ) +EPZSFixedPredictors = 2 # Enables Window based predictors + # (0:disabled, 1: P only, 2: P and B/default) +EPZSTemporal = 1 # Enables temporal predictors + # (0: disabled, 1: enabled/default) +EPZSSpatialMem = 1 # Enables spatial memory predictors + # (0: disabled, 1: enabled/default) +EPZSBlockType = 1 # Enables block type Predictors + # (0: disabled, 1: enabled/default) +EPZSMinThresScale = 0 # Scaler for EPZS minimum threshold (0 default). + # Increasing value can speed up encoding. +EPZSMedThresScale = 1 # Scaler for EPZS median threshold (1 default). + # Increasing value can speed up encoding. +EPZSMaxThresScale = 1 # Scaler for EPZS maximum threshold (1 default). + # Increasing value can speed up encoding. + +EPZSSubPelME = 1 # EPZS Subpel ME consideration +EPZSSubPelMEBiPred = 1 # EPZS Subpel ME consideration for BiPred partitions +EPZSSubPelThresScale = 2 # EPZS Subpel ME Threshold scaler +EPZSSubPelGrid = 1 # Perform EPZS using a subpixel grid + +######################################################################################## +# SEI Parameters +######################################################################################## + +ToneMappingSEIPresentFlag = 0 # Enable Tone mapping SEI (0 Not present, 1 Present) +ToneMappingFile = "ToneMapping.cfg" + +GenerateSEIMessage = 0 # Generate an SEI Text Message +SEIMessageText = "H.264/AVC Encoder" # Text SEI Message + +UseMVLimits = 0 # Use MV Limits +SetMVXLimit = 512 # Horizontal MV Limit (in integer units) +SetMVYLimit = 512 # Vertical MV Limit (in integer units) + +######################################################################################## +# VUI Parameters +######################################################################################## +# the variables below do not affect encoding and decoding +# (many are dummy variables but others can be useful when supported by the decoder) + +EnableVUISupport = 0 # Enable VUI Parameters + +# display parameters +VUI_aspect_ratio_info_present_flag = 0 +VUI_aspect_ratio_idc = 1 +VUI_sar_width = 0 +VUI_sar_height = 0 +VUI_overscan_info_present_flag = 0 +VUI_overscan_appropriate_flag = 0 +VUI_video_signal_type_present_flag = 0 +VUI_video_format = 5 +VUI_video_full_range_flag = 0 +VUI_colour_description_present_flag = 0 +VUI_colour_primaries = 2 +VUI_transfer_characteristics = 2 +VUI_matrix_coefficients = 2 +VUI_chroma_location_info_present_flag = 0 +VUI_chroma_sample_loc_type_top_field = 0 +VUI_chroma_sample_loc_type_bottom_field = 0 +VUI_timing_info_present_flag = 0 +VUI_num_units_in_tick = 1000 +VUI_time_scale = 60000 +VUI_fixed_frame_rate_flag = 0 + +# nal hrd parameters +VUI_nal_hrd_parameters_present_flag = 0 +VUI_nal_cpb_cnt_minus1 = 0 +VUI_nal_bit_rate_scale = 0 +VUI_nal_cpb_size_scale = 0 +VUI_nal_bit_rate_value_minus1 = 0 +VUI_nal_cpb_size_value_minus1 = 0 +VUI_nal_vbr_cbr_flag = 0 +VUI_nal_initial_cpb_removal_delay_length_minus1 = 23 +VUI_nal_cpb_removal_delay_length_minus1 = 23 +VUI_nal_dpb_output_delay_length_minus1 = 23 +VUI_nal_time_offset_length = 24 + +# vlc hrd parameters +VUI_vcl_hrd_parameters_present_flag = 0 +VUI_vcl_cpb_cnt_minus1 = 0 +VUI_vcl_bit_rate_scale = 0 +VUI_vcl_cpb_size_scale = 0 +VUI_vcl_bit_rate_value_minus1 = 0 +VUI_vcl_cpb_size_value_minus1 = 0 +VUI_vcl_vbr_cbr_flag = 0 +VUI_vcl_initial_cpb_removal_delay_length_minus1 = 23 +VUI_vcl_cpb_removal_delay_length_minus1 = 23 +VUI_vcl_dpb_output_delay_length_minus1 = 23 +VUI_vcl_time_offset_length = 24 +VUI_low_delay_hrd_flag = 0 + +# other parameters (i.e. bitsream restrictions) +VUI_pic_struct_present_flag = 0 +VUI_bitstream_restriction_flag = 0 +VUI_motion_vectors_over_pic_boundaries_flag = 1 +VUI_max_bytes_per_pic_denom = 0 +VUI_max_bits_per_mb_denom = 0 +VUI_log2_max_mv_length_vertical = 16 +VUI_log2_max_mv_length_horizontal = 16 +VUI_num_reorder_frames = 16 +VUI_max_dec_frame_buffering = 16 + diff --git a/bin/HM-like/encoder_JM_RA_B_HE.cfg b/bin/HM-like/encoder_JM_RA_B_HE.cfg new file mode 100644 index 0000000..f194f83 --- /dev/null +++ b/bin/HM-like/encoder_JM_RA_B_HE.cfg @@ -0,0 +1,671 @@ +# New Input File Format is as follows +# = # Comment +# +# See configfile.h for a list of supported ParameterNames +# +# For bug reporting and known issues see: +# https://ipbt.hhi.fraunhofer.de + + +########################################################################################## +# Files +########################################################################################## +InputFile = "D:\origCfP\BQSquare_416x240_60.yuv" # Input sequence +InputHeaderLength = 0 # If the inputfile has a header, state it's length in byte here +StartFrame = 0 # Start frame for encoding. (0-N) +FramesToBeEncoded = 30 # Number of frames to be coded +FrameRate = 60.0 # Frame Rate per second (0.1-100.0) +Enable32Pulldown = 0 # Enable 'hard' 3:2 pulldown (modifying the inpur data) + # 0 = disabled + # 1 = A, B, Bt|Cb, Ct|Db, D + # 2 = A, B, C, Ct|Db, D +SEIVUI32Pulldown = 0 # Enable 3:2 pulldown through VUI and SEI metadata signaling. Five methods are supported: + # 0 = disabled + # 1 = A, Bt|Bb, Bt|Cb, Ct|Cb, D + # 2 = A, B, C, C, D + # 3 = At|Ab, Bt|Bb, Bt|Cb, Ct|Cb, Dt|Db + # 4 = A, Bt|Bb, Bt|Cb, Ct|Db, Dt|Db + # 5 = At|Ab, Bt|Bb, Bt|Cb, Ct|Db, Dt|Db + +SourceWidth = 416 # Source frame width +SourceHeight = 240 # Source frame height +SourceResize = 0 # Resize source size for output +OutputWidth = 416 # Output frame width +OutputHeight = 240 # Output frame height +ProcessInput = 0 # Filter Input Sequence +Interleaved = 0 # 0: Planar input, 1: Packed input +StandardRange = 0 # 0: Standard range 1: Full range (RGB input) +VideoCode = 1 # Video codes for RGB ==> YUV conversions + # 0 = NULL, + # 1 = ITU_REC709, + # 2 = CCIR_601, + # 3 = FCC, + # 4 = ITU_REC624BG, + # 5 = SMPTE_170M, + # 6 = SMPTE_240M, + # 7 = SMPTE_260M, + # 8 = ITU_REC709_EXACT + +TraceFile = "" # Trace file +ReconFile = "test_rec.yuv" # Reconstruction YUV file +OutputFile = "test.264" # Bitstream +StatsFile = "" # Coding statistics file + +NumberOfViews = 1 # Number of views to encode (1=1 view, 2=2 views) +View1ConfigFile = "encoder_view1.cfg" # Config file name for second view +########################################################################################## +# Encoder Control +########################################################################################## +Grayscale = 0 # Encode in grayscale (Currently only works for 8 bit YUV 420 input) +ProfileIDC = 100 # Profile IDC (66=baseline, 77=main, 88=extended; FREXT Profiles: 100=High, 110=High 10, 122=High 4:2:2, 244=High 4:4:4, 44=CAVLC 4:4:4 Intra, 118=Multiview High Profile, 128=Stereo High Profile) +IntraProfile = 0 # Activate Intra Profile for FRExt (0: false, 1: true) + # (e.g. ProfileIDC=110, IntraProfile=1 => High 10 Intra Profile) +LevelIDC = 51 # Level IDC (e.g. 20 = level 2.0) + +IntraPeriod = 0 # Period of I-pictures (0=only first) +IDRPeriod = 0 # Period of IDR pictures (0=only first) +AdaptiveIntraPeriod = 0 # Adaptive intra period +AdaptiveIDRPeriod = 0 # Adaptive IDR period +IntraDelay = 0 # Intra (IDR) picture delay (i.e. coding structure of PPIPPP... ) +EnableIDRGOP = 0 # Support for IDR closed GOPs (0: disabled, 1: enabled) +EnableOpenGOP = 0 # Support for open GOPs (0: disabled, 1: enabled) +QPISlice = 32 # Quant. param for I Slices (0-51) +QPPSlice = 33 # Quant. param for P Slices (0-51) +FrameSkip = 0 # Number of frames to be skipped in input (e.g 2 will code every third frame). + # Note that this now excludes intermediate (i.e. B) coded pictures +ChromaQPOffset = 0 # Chroma QP offset (-51..51) + +DisableSubpelME = 0 # Disable Subpixel Motion Estimation (0=off/default, 1=on) +SearchRange = 128 # Max search range + +MEDistortionFPel = 0 # Select error metric for Full-Pel ME (0: SAD, 1: SSE, 2: Hadamard SAD) +MEDistortionHPel = 2 # Select error metric for Half-Pel ME (0: SAD, 1: SSE, 2: Hadamard SAD) +MEDistortionQPel = 2 # Select error metric for Quarter-Pel ME (0: SAD, 1: SSE, 2: Hadamard SAD) +MDDistortion = 2 # Select error metric for Mode Decision (0: SAD, 1: SSE, 2: Hadamard SAD) +SkipDeBlockNonRef = 0 # Skip Deblocking (regardless of DFParametersFlag) for non-reference frames (0: off, 1: on) +ChromaMCBuffer = 1 # Calculate Color component interpolated values in advance and store them. + # Provides a trade-off between memory and computational complexity + # (0: disabled/default, 1: enabled) +ChromaMEEnable = 0 # Take into account Color component information during ME + # (0: only first component/default, + # 1: All Color components - Integer refinement only + # 2: All Color components - All refinements) +ChromaMEWeight = 1 # Weighting for chroma components. This parameter should have a relationship with color format. + +NumberReferenceFrames = 4 # Number of previous frames used for inter motion search (0-16) + +PList0References = 4 # P slice List 0 reference override (0 disable, N <= NumberReferenceFrames) +Log2MaxFNumMinus4 = 0 # Sets log2_max_frame_num_minus4 (-1 : based on FramesToBeEncoded/Auto, >=0 : Log2MaxFNumMinus4) +Log2MaxPOCLsbMinus4 = -1 # Sets log2_max_pic_order_cnt_lsb_minus4 (-1 : Auto, >=0 : Log2MaxPOCLsbMinus4) + +GenerateMultiplePPS = 1 # Transmit multiple parameter sets. Currently parameters basically enable all WP modes (0: diabled, 1: enabled) +SendAUD = 1 # Send Access Delimiter Unit NALU (for every access unit) +ResendSPS = 0 # Resend SPS (0: disabled, 1: all Intra pictures, 2: only for IDR, 3: for IDR and OpenGOP I) +ResendPPS = 0 # Resend PPS (with pic_parameter_set_id 0) for every coded Frame/Field pair (0: disabled, 1: enabled) + +MbLineIntraUpdate = 0 # Error robustness(extra intra macro block updates)(0=off, N: One GOB every N frames are intra coded) +RandomIntraMBRefresh = 0 # Forced intra MBs per picture + +########################################################################################## +# PSlice Mode types +########################################################################################## +PSliceSkip = 1 # P-Slice Skip mode consideration (0=disable, 1=enable) +PSliceSearch16x16 = 1 # P-Slice Inter block search 16x16 (0=disable, 1=enable) +PSliceSearch16x8 = 1 # P-Slice Inter block search 16x8 (0=disable, 1=enable) +PSliceSearch8x16 = 1 # P-Slice Inter block search 8x16 (0=disable, 1=enable) +PSliceSearch8x8 = 1 # P-Slice Inter block search 8x8 (0=disable, 1=enable) +PSliceSearch8x4 = 1 # P-Slice Inter block search 8x4 (0=disable, 1=enable) +PSliceSearch4x8 = 1 # P-Slice Inter block search 4x8 (0=disable, 1=enable) +PSliceSearch4x4 = 1 # P-Slice Inter block search 4x4 (0=disable, 1=enable) + +########################################################################################## +# BSlice Mode types +########################################################################################## + +BSliceDirect = 1 # B-Slice Skip mode consideration (0=disable, 1=enable) +BSliceSearch16x16 = 1 # B-Slice Inter block search 16x16 (0=disable, 1=enable) +BSliceSearch16x8 = 1 # B-Slice Inter block search 16x8 (0=disable, 1=enable) +BSliceSearch8x16 = 1 # B-Slice Inter block search 8x16 (0=disable, 1=enable) +BSliceSearch8x8 = 1 # B-Slice Inter block search 8x8 (0=disable, 1=enable) +BSliceSearch8x4 = 1 # B-Slice Inter block search 8x4 (0=disable, 1=enable) +BSliceSearch4x8 = 1 # B-Slice Inter block search 4x8 (0=disable, 1=enable) +BSliceSearch4x4 = 1 # B-Slice Inter block search 4x4 (0=disable, 1=enable) + +BiPredSearch16x16 = 1 # B-Slice Bi-prediction block search 16x16 (0=disable, 1=enable) +BiPredSearch16x8 = 1 # B-Slice Bi-prediction block search 16x8 (0=disable, 1=enable) +BiPredSearch8x16 = 1 # B-Slice Bi-prediction block search 8x16 (0=disable, 1=enable) +BiPredSearch8x8 = 1 # B-Slice Bi-prediction block search 8x8 (0=disable, 1=enable) + +DisableIntra4x4 = 0 # Disable Intra 4x4 modes +DisableIntra16x16 = 0 # Disable Intra 16x16 modes +DisableIntraInInter = 0 # Disable Intra modes for inter slices +IntraDisableInterOnly = 0 # Apply Disabling Intra conditions only to Inter Slices (0:disable/default,1: enable) +Intra4x4ParDisable = 0 # Disable Vertical & Horizontal 4x4 +Intra4x4DiagDisable = 0 # Disable Diagonal 45degree 4x4 +Intra4x4DirDisable = 0 # Disable Other Diagonal 4x4 +Intra16x16ParDisable = 0 # Disable Vertical & Horizontal 16x16 +Intra16x16PlaneDisable = 0 # Disable Planar 16x16 +ChromaIntraDisable = 0 # Disable Intra Chroma modes other than DC +EnableIPCM = 1 # Enable IPCM macroblock mode + +DisposableP = 0 # Enable Disposable P slices in the primary layer (0: disable/default, 1: enable) +DispPQPOffset = 0 # Quantizer offset for disposable P slices (0: default) + +PreferDispOrder = 1 # Prefer display order when building the prediction structure as opposed to coding order (affects intra and IDR periodic insertion, among others) +PreferPowerOfTwo = 0 # Prefer prediction structures that have lengths expressed as powers of two +FrmStructBufferLength = 16 # Length of the frame structure unit buffer; it can be overriden for certain cases + +ChangeQPFrame = 0 # Frame in display order from which to apply the Change QP offsets +ChangeQPI = 0 # Change QP offset value for I_SLICE +ChangeQPP = 0 # Change QP offset value for P_SLICE +ChangeQPB = 0 # Change QP offset value for B_SLICE +ChangeQPSI = 0 # Change QP offset value for SI_SLICE +ChangeQPSP = 0 # Change QP offset value for SP_SLICE + +########################################################################################## +# B Slices +########################################################################################## + +NumberBFrames = 7 # Number of B coded frames inserted (0=not used) +PReplaceBSlice = 0 # Replace B-coded slices with P-coded slices when NumberBFrames>0 +QPBSlice = 33 # Quant. param for B slices (0-51) +BRefPicQPOffset = 0 # Quantization offset for reference B coded pictures (-51..51) +DirectModeType = 1 # Direct Mode Type (0:Temporal 1:Spatial) +DirectInferenceFlag = 1 # Direct Inference Flag (0: Disable 1: Enable) +BList0References = 2 # B slice List 0 reference override (0 disable, N <= NumberReferenceFrames) +BList1References = 2 # B slice List 1 reference override (0 disable, N <= NumberReferenceFrames) + # 1 List1 reference is usually recommended for normal GOP Structures. + # A larger value is usually more appropriate if a more flexible + # structure is used (i.e. using HierarchicalCoding) + +BLevel0MoreRef = 1 # 0: Use the same number of reference pictures as the other B pictures + # 1: Use more reference frame pictures for B pictures at level 0 +BIdenticalList = 1 # 0: not used; 1: used for B pictures at level 0; 2: used for all B pictures + # 1 and 2 need to set ReferenceReorder to 1 +CRA = 1 # 0: do not use CRA like random access support; 1 use CRA like random access support + # only used for random access cases, LowDelay != 1 +HM50RefStructure = 1 # 0: do not use HM-5.0 like referencing structure; 1 use HM-5.0 like referencing structure for random access +LDRefSetting = 0 # 0: use nearest pictures as reference frames + # 1: use one nearest + POC%4==0 pictures as reference frames, as JCTVC-F701 + # Only low delay is support, please do not use it for other coding structure + +BReferencePictures = 2 # Referenced B coded pictures (0=off, 1=B references for secondary layer, 2=B references for primary layer) + +HierarchicalCoding = 3 # B hierarchical coding (0= off, 1= 2 layers, 2= 2 full hierarchy, 3 = explicit) +HierarchyLevelQPEnable = 0 # Adjust QP based on hierarchy level (in increments of 1). Overrides BRefPicQPOffset behavior.(0=off, 1=on) +ExplicitHierarchyFormat = "B3r1B1r2b0e3b2e3B5r2b4e3b6e3" # Explicit Enhancement GOP. Format is {FrameDisplay_orderReferenceQP}. + # Valid values for reference type is r:reference, e:non reference. +ExplicitSeqCoding = 0 # Enable support for explicit sequence coding +ExplicitSeqFile = "explicit_seq.cfg" +LowDelay = 0 # Apply HierarchicalCoding without delay (i.e., encode in the captured/display order) +ReferenceReorder = 1 # Reorder References according to Poc distance for HierarchicalCoding (0=off, 1=enable, 2=use when LowDelay is set) +PocMemoryManagement = 0 # Memory management based on Poc Distances for HierarchicalCoding (0=off, 1=on, 2=use when LowDelay is set) +SetFirstAsLongTerm = 0 # Set first frame as long term + +BiPredMotionEstimation = 1 # Enable Bipredictive based Motion Estimation (0:disabled, 1:enabled) +BiPredMERefinements = 3 # Bipredictive ME extra refinements (0: single, N: N extra refinements (1 default) +BiPredMESearchRange = 128 # Bipredictive ME Search range (8 default). Note that range is halved for every extra refinement. +BiPredMESubPel = 2 # Bipredictive ME Subpixel Consideration (0: disabled, 1: single level, 2: dual level) + + +########################################################################################## +# SP Frames +########################################################################################## + +SPPicturePeriodicity = 0 # SP-Picture Periodicity (0=not used) +SPSwitchPeriod = 0 # Switch period (in terms of switching SP/SI frames) between bitstream 1 and bitstream 2 +QPSPSlice = 36 # Quant. param of SP-Slices for Prediction Error (0-51) +QPSISlice = 36 # Quant. param of SI-Slices for Prediction Error (0-51) +QPSP2Slice = 35 # Quant. param of SP/SI-Slices for Predicted Blocks (0-51) +SI_FRAMES = 0 # SI frame encoding flag (0=not used, 1=used) +SP_output = 0 # Controls whether coefficients will be output to encode switching SP frames (0=no, 1=yes) +SP_output_name = "low_quality.dat" # Filename for SP output coefficients +SP2_FRAMES = 0 # switching SP frame encoding flag (0=not used, 1=used) +SP2_input_name1 = "high_quality.dat" # Filename for the first swithed bitstream coefficients +SP2_input_name2 = "low_quality.dat" # Filename for the second switched bitstream coefficients + +########################################################################################## +# Output Control, NALs +########################################################################################## + +SymbolMode = 1 # Symbol mode (Entropy coding method: 0=UVLC, 1=CABAC) +OutFileMode = 0 # Output file mode, 0:Annex B, 1:RTP +PartitionMode = 0 # Partition Mode, 0: no DP, 1: 3 Partitions per Slice + +########################################################################################## +# CABAC context initialization +########################################################################################## + +ContextInitMethod = 1 # Context init (0: fixed, 1: adaptive) +FixedModelNumber = 0 # model number for fixed decision for inter slices ( 0, 1, or 2 ) + +########################################################################################## +# Interlace Handling +######################################################################################### + +PicInterlace = 0 # Picture AFF (0: frame coding, 1: field coding, 2:adaptive frame/field coding) +MbInterlace = 0 # Macroblock AFF (0: frame coding, 1: field coding, 2:adaptive frame/field coding, 3: frame MB-only AFF) +IntraBottom = 0 # Force Intra Bottom at GOP Period + +########################################################################################## +# Weighted Prediction +######################################################################################### + +WeightedPrediction = 1 # P picture Weighted Prediction (0=off, 1=explicit mode) +WeightedBiprediction = 1 # B picture Weighted Prediciton (0=off, 1=explicit mode, 2=implicit mode) +ChromaWeightSupport = 1 # Enable consideration of weights for Chroma components +UseWeightedReferenceME = 1 # Use weighted reference for ME (0=off, 1=on) +WPMethod = 1 # WP method (0: DC based, 1: LMS based) +WPIterMC = 0 # Iterative Motion compensated based weighted prediction method +EnhancedBWeightSupport = 0 # Enhanced B Weight support (needs revisit if we wish to merge with WPMethod) +WPMCPrecision = 0 # Improved Motion Compensation Precision using WP based methods. + # Clones WP references with slightly modified rounding offsets (Requires RDPictureDecision and GenerateMultiplePPS) : + # 0: disabled (default) + # 1: Up to one additional coding pass. Ref0 is 0, ref1 is 0 with a -1 offset + # 2: Up to two additional coding passes. (1) Ref0 is 0, ref1 is 0 with a -1 offset, (1) Ref0 is 0 with a -1 offset, ref1 is 0 +WPMCPrecFullRef = 0 # Increases the number of references in the reference picture lists to account + # for the lost reference slot when reordering is used during a coding pass in WPMCPrecision for reference replication. + # The number of references in non-reordered passes stays unchanged +WPMCPrecBSlice = 1 # 2: Apply rounding on every B slice. This efectively disables the evaluation of alternative QPs during RDPictureDecision. + # 1: Disable rounding for non-reference B slices. Non-reference B slices are evaluated for alternative QPs during RDPictureDecision. + # 0: Disable rounding for B slices. + +########################################################################################## +# Picture based Multi-pass encoding +######################################################################################### + +RDPictureDecision = 1 # Perform multiple pass coding and make RD optimal decision among them +RDPSliceBTest = 0 # Perform Slice level RD decision between P and B slices. +RDPSliceITest = 1 # Perform Slice level RD decision between P and I slices. Default value is 1 (enabled). +RDPictureMaxPassISlice = 1 # Max number of coding passes for I slices, valid values [1,3], default is 1 +RDPictureMaxPassPSlice = 2 # Max number of coding passes for P slices, valid values [1,6], default is 2 +RDPictureMaxPassBSlice = 3 # Max number of coding passes for B slices, valid values [1,6], default is 3 +RDPictureFrameQPPSlice = 0 # Perform additional frame level QP check (QP+/-1) for P slices, 0: disabled (default), 1: enabled +RDPictureFrameQPBSlice = 0 # Perform additional frame level QP check (QP+/-1) for B slices, 0: disabled, 1: enabled (default) +RDPictureDeblocking = 0 # Perform another coding pass to check non-deblocked picture, 0: disabled (default), 1: enabled +RDPictureDirectMode = 0 # Perform another coding pass to check the alternative direct mode for B slices, , 0: disabled (default), 1: enabled + +########################################################################################## +# Deblocking filter parameters +########################################################################################## + +DFParametersFlag = 0 # Configure deblocking filter (0=parameters below ignored, 1=parameters sent) + # Note that for pictures with multiple slice types, + # only the type of the first slice will be considered. +DFDisableRefISlice = 0 # Disable deblocking filter in reference I coded pictures (0=Filter, 1=No Filter). +DFAlphaRefISlice = 0 # Reference I coded pictures Alpha offset div. 2, {-6, -5, ... 0, +1, .. +6} +DFBetaRefISlice = 0 # Reference I coded pictures Beta offset div. 2, {-6, -5, ... 0, +1, .. +6} +DFDisableNRefISlice = 0 # Disable deblocking filter in non reference I coded pictures (0=Filter, 1=No Filter). +DFAlphaNRefISlice = 0 # Non Reference I coded pictures Alpha offset div. 2, {-6, -5, ... 0, +1, .. +6} +DFBetaNRefISlice = 0 # Non Reference I coded pictures Beta offset div. 2, {-6, -5, ... 0, +1, .. +6} +DFDisableRefPSlice = 0 # Disable deblocking filter in reference P coded pictures (0=Filter, 1=No Filter). +DFAlphaRefPSlice = 0 # Reference P coded pictures Alpha offset div. 2, {-6, -5, ... 0, +1, .. +6} +DFBetaRefPSlice = 0 # Reference P coded pictures Beta offset div. 2, {-6, -5, ... 0, +1, .. +6} +DFDisableNRefPSlice = 0 # Disable deblocking filter in non reference P coded pictures (0=Filter, 1=No Filter). +DFAlphaNRefPSlice = 0 # Non Reference P coded pictures Alpha offset div. 2, {-6, -5, ... 0, +1, .. +6} +DFBetaNRefPSlice = 0 # Non Reference P coded pictures Beta offset div. 2, {-6, -5, ... 0, +1, .. +6} +DFDisableRefBSlice = 0 # Disable deblocking filter in reference B coded pictures (0=Filter, 1=No Filter). +DFAlphaRefBSlice = 0 # Reference B coded pictures Alpha offset div. 2, {-6, -5, ... 0, +1, .. +6} +DFBetaRefBSlice = 0 # Reference B coded pictures Beta offset div. 2, {-6, -5, ... 0, +1, .. +6} +DFDisableNRefBSlice = 0 # Disable deblocking filter in non reference B coded pictures (0=Filter, 1=No Filter). +DFAlphaNRefBSlice = 0 # Non Reference B coded pictures Alpha offset div. 2, {-6, -5, ... 0, +1, .. +6} +DFBetaNRefBSlice = 0 # Non Reference B coded pictures Beta offset div. 2, {-6, -5, ... 0, +1, .. +6} + +########################################################################################## +# Error Resilience / Slices +########################################################################################## + +SliceMode = 0 # Slice mode (0=off 1=fixed #mb in slice 2=fixed #bytes in slice 3=use callback) +SliceArgument = 50 # Slice argument (Arguments to modes 1 and 2 above) + +num_slice_groups_minus1 = 0 # Number of Slice Groups Minus 1, 0 == no FMO, 1 == two slice groups, etc. +slice_group_map_type = 0 # 0: Interleave, 1: Dispersed, 2: Foreground with left-over, + # 3: Box-out, 4: Raster Scan 5: Wipe + # 6: Explicit, slice_group_id read from SliceGroupConfigFileName +slice_group_change_direction_flag = 0 # 0: box-out clockwise, raster scan or wipe right, + # 1: box-out counter clockwise, reverse raster scan or wipe left +slice_group_change_rate_minus1 = 85 # +SliceGroupConfigFileName = "sg0conf.cfg" # Used for slice_group_map_type 0, 2, 6 + +UseRedundantPicture = 0 # 0: not used, 1: enabled +NumRedundantHierarchy = 1 # 0-4 +PrimaryGOPLength = 10 # GOP length for redundant allocation (1-16) + # NumberReferenceFrames must be no less than PrimaryGOPLength when redundant slice enabled +NumRefPrimary = 1 # Actually used number of references for primary slices (1-16) + +########################################################################################## +# Search Range Restriction / RD Optimization +########################################################################################## + +RestrictSearchRange = 2 # restriction for (0: blocks and ref, 1: ref, 2: no restrictions) +RDOptimization = 1 # rd-optimized mode decision + # 0: RD-off (Low complexity mode) + # 1: RD-on (High complexity mode) + # 2: RD-on (Fast high complexity mode - not work in FREX Profiles) + # 3: with losses +I16RDOpt = 1 # perform rd-optimized mode decision for Intra 16x16 MB + # 0: SAD-based mode decision for Intra 16x16 MB + # 1: RD-based mode decision for Intra 16x16 MB +SubMBCodingState = 2 # submacroblock coding state + # 0: lowest complexity, do not store or reset coding state during sub-MB mode decision + # 1: medium complexity, reset to master coding state (for current mode) during sub-MB mode decision + # 2: highest complexity, store and reset coding state during sub-MB mode decision +DistortionSSIM = 0 # Compute SSIM distortion. (0: disabled/default, 1: enabled) +DistortionMS_SSIM = 0 # Compute Multiscale SSIM distortion. (0: disabled/default, 1: enabled) +SSIMOverlapSize = 8 # Overlap size to calculate SSIM distortion (1: pixel by pixel, 8: no overlap) +DistortionYUVtoRGB = 0 # Calculate distortion in RGB domain after conversion from YCbCr (0:off, 1:on) +CtxAdptLagrangeMult = 0 # Context Adaptive Lagrange Multiplier + # 0: disabled (default) + # 1: enabled (works best when RDOptimization=0) +FastCrIntraDecision = 1 # Fast Chroma intra mode decision (0:off, 1:on) +DisableThresholding = 0 # Disable Thresholding of Transform Coefficients (0:off, 1:on) +DisableBSkipRDO = 0 # Disable B Skip Mode consideration from RDO Mode decision (0:off, 1:on) +BiasSkipRDO = 0 # Negative Bias for Skip/DirectSkip modes (0: off, 1: on) +ForceTrueRateRDO = 0 # Force true rate (even zero values) during RDO process +SkipIntraInInterSlices = 0 # Skips Intra mode checking in inter slices if certain mode decisions are satisfied (0: off, 1: on) +WeightY = 1 # Luma weight for RDO +WeightCb = 1 # Cb weight for RDO +WeightCr = 1 # Cr weight for RDO + +########################################################################################## +# Explicit Lambda Usage +########################################################################################## +UseExplicitLambdaParams = 0 # Use explicit lambda scaling parameters (0:disabled, 1:enable lambda weight, 2: use explicit lambda value) +UpdateLambdaChromaME = 0 # Update lambda given Chroma ME consideration +FixedLambdaISlice = 0.1 # Fixed Lambda value for I slices +FixedLambdaPSlice = 0.1 # Fixed Lambda value for P slices +FixedLambdaBSlice = 0.1 # Fixed Lambda value for B slices +FixedLambdaRefBSlice = 0.1 # Fixed Lambda value for Referenced B slices +FixedLambdaSPSlice = 0.1 # Fixed Lambda value for SP slices +FixedLambdaSISlice = 0.1 # Fixed Lambda value for SI slices + +LambdaWeightISlice = 0.65 # scaling param for I slices. This will be used as a multiplier i.e. lambda=LambdaWeightISlice * 2^((QP-12)/3) +LambdaWeightPSlice = 0.68 # scaling param for P slices. This will be used as a multiplier i.e. lambda=LambdaWeightPSlice * 2^((QP-12)/3) +LambdaWeightBSlice = 0.68 # scaling param for B slices. This will be used as a multiplier i.e. lambda=LambdaWeightBSlice * 2^((QP-12)/3) +LambdaWeightRefBSlice = 0.68 # scaling param for Referenced B slices. This will be used as a multiplier i.e. lambda=LambdaWeightRefBSlice * 2^((QP-12)/3) +LambdaWeightSPSlice = 0.68 # scaling param for SP slices. This will be used as a multiplier i.e. lambda=LambdaWeightSPSlice * 2^((QP-12)/3) +LambdaWeightSISlice = 0.65 # scaling param for SI slices. This will be used as a multiplier i.e. lambda=LambdaWeightSISlice * 2^((QP-12)/3) + +LossRateA = 5 # expected packet loss rate of the channel for the first partition, only valid if RDOptimization = 3 +LossRateB = 0 # expected packet loss rate of the channel for the second partition, only valid if RDOptimization = 3 +LossRateC = 0 # expected packet loss rate of the channel for the third partition, only valid if RDOptimization = 3 +FirstFrameCorrect = 0 # If 1, the first frame is encoded under the assumption that it is always correctly received. +NumberOfDecoders = 30 # Numbers of decoders used to simulate the channel, only valid if RDOptimization = 3 +RestrictRefFrames = 0 # Doesnt allow reference to areas that have been intra updated in a later frame. + +########################################################################################## +# Additional Stuff +######################################################################################### + +UseConstrainedIntraPred = 0 # If 1, Inter pixels are not used for Intra macroblock prediction. + +NumberofLeakyBuckets = 8 # Number of Leaky Bucket values +LeakyBucketRateFile = "leakybucketrate.cfg" # File from which encoder derives rate values +LeakyBucketParamFile = "leakybucketparam.cfg" # File where encoder stores leakybucketparams + +NumFramesInELayerSubSeq = 0 # number of frames in the Enhanced Scalability Layer(0: no Enhanced Layer) + +SparePictureOption = 0 # (0: no spare picture info, 1: spare picture available) +SparePictureDetectionThr = 6 # Threshold for spare reference pictures detection +SparePicturePercentageThr = 92 # Threshold for the spare macroblock percentage + +PicOrderCntType = 0 # (0: POC mode 0, 1: POC mode 1, 2: POC mode 2) + +######################################################################################## +#Rate control +######################################################################################## + +RateControlEnable = 0 # 0 Disable, 1 Enable +Bitrate = 45020 # Bitrate(bps) +InitialQP = 0 # Initial Quantization Parameter for the first I frame + # InitialQp depends on two values: Bits Per Picture, + # and the GOP length +BasicUnit = 0 # Number of MBs in the basic unit + # should be a fraction of the total number + # of MBs in a frame ("0" sets a BU equal to a frame) +ChannelType = 0 # type of channel( 1=time varying channel; 0=Constant channel) +RCUpdateMode = 0 # Rate Control type. Modes supported : + # 0 = original JM rate control, + # 1 = rate control that is applied to all frames regardless of the slice type, + # 2 = original plus intelligent QP selection for I and B slices (including Hierarchical), + # 3 = original + hybrid quadratic rate control for I and B slice using bit rate statistics + # +RCISliceBitRatio = 1.0 # target ratio of bits for I-coded pictures compared to P-coded Pictures (for RCUpdateMode=3) +RCBSliceBitRatio0 = 0.5 # target ratio of bits for B-coded pictures compared to P-coded Pictures - temporal level 0 (for RCUpdateMode=3) +RCBSliceBitRatio1 = 0.25 # target ratio of bits for B-coded pictures compared to P-coded Pictures - temporal level 1 (for RCUpdateMode=3) +RCBSliceBitRatio2 = 0.25 # target ratio of bits for B-coded pictures compared to P-coded Pictures - temporal level 2 (for RCUpdateMode=3) +RCBSliceBitRatio3 = 0.25 # target ratio of bits for B-coded pictures compared to P-coded Pictures - temporal level 3 (for RCUpdateMode=3) +RCBSliceBitRatio4 = 0.25 # target ratio of bits for B-coded pictures compared to P-coded Pictures - temporal level 4 (for RCUpdateMode=3) +RCBoverPRatio = 0.45 # ratio of bit rate usage of a B-coded picture over a P-coded picture for the SAME QP (for RCUpdateMode=3) +RCIoverPRatio = 3.80 # ratio of bit rate usage of an I-coded picture over a P-coded picture for the SAME QP (for RCUpdateMode=3) +RCMinQPPSlice = 8 # minimum P Slice QP value for rate control +RCMaxQPPSlice = 42 # maximum P Slice QP value for rate control +RCMinQPBSlice = 8 # minimum B Slice QP value for rate control +RCMaxQPBSlice = 42 # maximum B Slice QP value for rate control +RCMinQPISlice = 8 # minimum I Slice QP value for rate control +RCMaxQPISlice = 42 # maximum I Slice QP value for rate control +RCMinQPSPSlice = 8 # minimum SP Slice QP value for rate control +RCMaxQPSPSlice = 40 # maximum SP Slice QP value for rate control +RCMinQPSISlice = 8 # minimum SI Slice QP value for rate control +RCMaxQPSISlice = 42 # maximum SI Slice QP value for rate control +RCMaxQPChange = 4 # maximum QP change for frames of the base layer + +######################################################################################## +#Fast Mode Decision +######################################################################################## +EarlySkipEnable = 1 # Early skip detection (0: Disable 1: Enable) +SelectiveIntraEnable = 1 # Selective Intra mode decision (0: Disable 1: Enable) + +######################################################################################## +#FREXT stuff +######################################################################################## + +YUVFormat = 1 # YUV format (0=4:0:0, 1=4:2:0, 2=4:2:2, 3=4:4:4) +RGBInput = 0 # 1=RGB input, 0=GBR or YUV input +SeparateColourPlane = 0 # 4:4:4 coding: 0=Common mode, 1=Independent mode +SourceBitDepthLuma = 8 # Source Bit Depth for Luma color component (8...14 bits) +SourceBitDepthChroma = 8 # Source Bit Depth for Chroma color components (8...14 bits) +SourceBitDepthRescale = 1 # Rescale bit depth of source for output (0: Disable 1: Enable) +OutputBitDepthLuma = 8 # Output Bit Depth for Luma color component (8...14 bits) +OutputBitDepthChroma = 8 # Output Bit Depth for Chroma color components (8...14 bits) + +CbQPOffset = 0 # Chroma QP offset for Cb-part (-51..51) +CrQPOffset = 0 # Chroma QP offset for Cr-part (-51..51) +Transform8x8Mode = 1 # (0: only 4x4 transform, 1: allow using 8x8 transform additionally, 2: only 8x8 transform) +ReportFrameStats = 0 # (0:Disable Frame Statistics 1: Enable) +DisplayEncParams = 0 # (0:Disable Display of Encoder Params 1: Enable) +Verbose = 1 # level of display verboseness + # 0: short, 1: normal (default), 2: detailed, 3: detailed/nvb +SkipGlobalStats = 0 # Disable global stat accumulation (Set to 1 to avoid bipred core dump) + +######################################################################################## +#Q-Matrix (FREXT) +######################################################################################## +QmatrixFile = "q_matrix.cfg" + +ScalingMatrixPresentFlag = 0 # Enable Q_Matrix (0 Not present, 1 Present in SPS, 2 Present in PPS, 3 Present in both SPS & PPS) +ScalingListPresentFlag0 = 3 # Intra4x4_Luma (0 Not present, 1 Present in SPS, 2 Present in PPS, 3 Present in both SPS & PPS) +ScalingListPresentFlag1 = 3 # Intra4x4_ChromaU (0 Not present, 1 Present in SPS, 2 Present in PPS, 3 Present in both SPS & PPS) +ScalingListPresentFlag2 = 3 # Intra4x4_chromaV (0 Not present, 1 Present in SPS, 2 Present in PPS, 3 Present in both SPS & PPS) +ScalingListPresentFlag3 = 3 # Inter4x4_Luma (0 Not present, 1 Present in SPS, 2 Present in PPS, 3 Present in both SPS & PPS) +ScalingListPresentFlag4 = 3 # Inter4x4_ChromaU (0 Not present, 1 Present in SPS, 2 Present in PPS, 3 Present in both SPS & PPS) +ScalingListPresentFlag5 = 3 # Inter4x4_ChromaV (0 Not present, 1 Present in SPS, 2 Present in PPS, 3 Present in both SPS & PPS) +ScalingListPresentFlag6 = 3 # Intra8x8_Luma (0 Not present, 1 Present in SPS, 2 Present in PPS, 3 Present in both SPS & PPS) +ScalingListPresentFlag7 = 3 # Inter8x8_Luma (0 Not present, 1 Present in SPS, 2 Present in PPS, 3 Present in both SPS & PPS) +ScalingListPresentFlag8 = 3 # Intra8x8_ChromaU for 4:4:4 (0 Not present, 1 Present in SPS, 2 Present in PPS, 3 Present in both SPS & PPS) +ScalingListPresentFlag9 = 3 # Inter8x8_ChromaU for 4:4:4 (0 Not present, 1 Present in SPS, 2 Present in PPS, 3 Present in both SPS & PPS) +ScalingListPresentFlag10 = 3 # Intra8x8_ChromaV for 4:4:4 (0 Not present, 1 Present in SPS, 2 Present in PPS, 3 Present in both SPS & PPS) +ScalingListPresentFlag11 = 3 # Inter8x8_ChromaV for 4:4:4 (0 Not present, 1 Present in SPS, 2 Present in PPS, 3 Present in both SPS & PPS) + +######################################################################################## +#Rounding Offset control +######################################################################################## + +OffsetMatrixPresentFlag = 0 # Enable Explicit Offset Quantization Matrices (0: disable 1: enable) +QOffsetMatrixFile = "q_offset.cfg" # Explicit Quantization Matrices file + +AdaptiveRounding = 0 # Enable Adaptive Rounding based on JVT-N011 (0: disable, 1: enable) +AdaptRoundingFixed = 0 # Enable Global Adaptive rounding for all qps (0: disable, 1: enable - default/old) +AdaptRndPeriod = 1 # Period in terms of MBs for updating rounding offsets. + # 0 performs update at the picture level. Default is 16. 1 is as in JVT-N011. +AdaptRndChroma = 1 # Enables coefficient rounding adaptation for chroma + +AdaptRndWFactorIRef = 8 # Adaptive Rounding Weight for I/SI slices in reference pictures /4096 +AdaptRndWFactorPRef = 8 # Adaptive Rounding Weight for P/SP slices in reference pictures /4096 +AdaptRndWFactorBRef = 8 # Adaptive Rounding Weight for B slices in reference pictures /4096 +AdaptRndWFactorINRef = 8 # Adaptive Rounding Weight for I/SI slices in non reference pictures /4096 +AdaptRndWFactorPNRef = 8 # Adaptive Rounding Weight for P/SP slices in non reference pictures /4096 +AdaptRndWFactorBNRef = 8 # Adaptive Rounding Weight for B slices in non reference pictures /4096 + +AdaptRndCrWFactorIRef = 8 # Chroma Adaptive Rounding Weight for I/SI slices in reference pictures /4096 +AdaptRndCrWFactorPRef = 8 # Chroma Adaptive Rounding Weight for P/SP slices in reference pictures /4096 +AdaptRndCrWFactorBRef = 8 # Chroma Adaptive Rounding Weight for B slices in reference pictures /4096 +AdaptRndCrWFactorINRef = 8 # Chroma Adaptive Rounding Weight for I/SI slices in non reference pictures /4096 +AdaptRndCrWFactorPNRef = 8 # Chroma Adaptive Rounding Weight for P/SP slices in non reference pictures /4096 +AdaptRndCrWFactorBNRef = 8 # Chroma Adaptive Rounding Weight for B slices in non reference pictures /4096 + +################################################################ +# Rate Distortion Optimized Quantization +################################################################ +UseRDOQuant = 1 # Use Rate Distortion Optimized Quantization (0=disable, 1=enable) +RDOQ_DC = 1 # Enable Rate Distortion Optimized Quantization for DC components (0=disable, 1=enable) +RDOQ_CR = 1 # Enable Rate Distortion Optimized Quantization for Chroma components (0=disable, 1=enable) +RDOQ_DC_CR = 1 # Enable Rate Distortion Optimized Quantization for Chroma DC components (0=disable, 1=enable) +RDOQ_QP_Num = 1 # 1-9: Number of QP tested in RDO_Q (I/P/B slice) +RDOQ_CP_Mode = 0 # copy Mode from first QP tested +RDOQ_CP_MV = 0 # copy MV from first QP tested +RDOQ_Fast = 1 # Fast RDOQ decision method for multiple QPs + +######################################################################################## +#Lossless Coding (FREXT) +######################################################################################## + +LosslessCoding = 0 # Enable lossless coding when qpprime_y is zero (0 Disabled, 1 Enabled) + +######################################################################################## +#Fast Motion Estimation Control Parameters +######################################################################################## + +SearchMode = 3 # Motion estimation mode + # -1 = Full Search + # 0 = Fast Full Search (default) + # 1 = UMHexagon Search + # 2 = Simplified UMHexagon Search + # 3 = Enhanced Predictive Zonal Search (EPZS) + +UMHexDSR = 1 # Use Search Range Prediction. Only for UMHexagonS method + # (0:disable, 1:enabled/default) +UMHexScale = 3 # Use Scale_factor for different image sizes. Only for UMHexagonS method + # (0:disable, 3:/default) + # Increasing value can speed up Motion Search. + +EPZSPattern = 2 # Select EPZS primary refinement pattern. + # (0: small diamond, 1: square, 2: extended diamond/default, + # 3: large diamond, 4: SBP Large Diamond, + # 5: PMVFAST ) +EPZSDualRefinement = 3 # Enables secondary refinement pattern. + # (0:disabled, 1: small diamond, 2: square, + # 3: extended diamond/default, 4: large diamond, + # 5: SBP Large Diamond, 6: PMVFAST ) +EPZSFixedPredictors = 2 # Enables Window based predictors + # (0:disabled, 1: P only, 2: P and B/default) +EPZSTemporal = 1 # Enables temporal predictors + # (0: disabled, 1: enabled/default) +EPZSSpatialMem = 1 # Enables spatial memory predictors + # (0: disabled, 1: enabled/default) +EPZSBlockType = 1 # Enables block type Predictors + # (0: disabled, 1: enabled/default) +EPZSMinThresScale = 0 # Scaler for EPZS minimum threshold (0 default). + # Increasing value can speed up encoding. +EPZSMedThresScale = 1 # Scaler for EPZS median threshold (1 default). + # Increasing value can speed up encoding. +EPZSMaxThresScale = 1 # Scaler for EPZS maximum threshold (1 default). + # Increasing value can speed up encoding. + +EPZSSubPelME = 1 # EPZS Subpel ME consideration +EPZSSubPelMEBiPred = 1 # EPZS Subpel ME consideration for BiPred partitions +EPZSSubPelThresScale = 2 # EPZS Subpel ME Threshold scaler +EPZSSubPelGrid = 1 # Perform EPZS using a subpixel grid + +######################################################################################## +# SEI Parameters +######################################################################################## + +ToneMappingSEIPresentFlag = 0 # Enable Tone mapping SEI (0 Not present, 1 Present) +ToneMappingFile = "ToneMapping.cfg" + +GenerateSEIMessage = 0 # Generate an SEI Text Message +SEIMessageText = "H.264/AVC Encoder" # Text SEI Message + +UseMVLimits = 0 # Use MV Limits +SetMVXLimit = 512 # Horizontal MV Limit (in integer units) +SetMVYLimit = 512 # Vertical MV Limit (in integer units) + +######################################################################################## +# VUI Parameters +######################################################################################## +# the variables below do not affect encoding and decoding +# (many are dummy variables but others can be useful when supported by the decoder) + +EnableVUISupport = 0 # Enable VUI Parameters + +# display parameters +VUI_aspect_ratio_info_present_flag = 0 +VUI_aspect_ratio_idc = 1 +VUI_sar_width = 0 +VUI_sar_height = 0 +VUI_overscan_info_present_flag = 0 +VUI_overscan_appropriate_flag = 0 +VUI_video_signal_type_present_flag = 0 +VUI_video_format = 5 +VUI_video_full_range_flag = 0 +VUI_colour_description_present_flag = 0 +VUI_colour_primaries = 2 +VUI_transfer_characteristics = 2 +VUI_matrix_coefficients = 2 +VUI_chroma_location_info_present_flag = 0 +VUI_chroma_sample_loc_type_top_field = 0 +VUI_chroma_sample_loc_type_bottom_field = 0 +VUI_timing_info_present_flag = 0 +VUI_num_units_in_tick = 1000 +VUI_time_scale = 60000 +VUI_fixed_frame_rate_flag = 0 + +# nal hrd parameters +VUI_nal_hrd_parameters_present_flag = 0 +VUI_nal_cpb_cnt_minus1 = 0 +VUI_nal_bit_rate_scale = 0 +VUI_nal_cpb_size_scale = 0 +VUI_nal_bit_rate_value_minus1 = 0 +VUI_nal_cpb_size_value_minus1 = 0 +VUI_nal_vbr_cbr_flag = 0 +VUI_nal_initial_cpb_removal_delay_length_minus1 = 23 +VUI_nal_cpb_removal_delay_length_minus1 = 23 +VUI_nal_dpb_output_delay_length_minus1 = 23 +VUI_nal_time_offset_length = 24 + +# vlc hrd parameters +VUI_vcl_hrd_parameters_present_flag = 0 +VUI_vcl_cpb_cnt_minus1 = 0 +VUI_vcl_bit_rate_scale = 0 +VUI_vcl_cpb_size_scale = 0 +VUI_vcl_bit_rate_value_minus1 = 0 +VUI_vcl_cpb_size_value_minus1 = 0 +VUI_vcl_vbr_cbr_flag = 0 +VUI_vcl_initial_cpb_removal_delay_length_minus1 = 23 +VUI_vcl_cpb_removal_delay_length_minus1 = 23 +VUI_vcl_dpb_output_delay_length_minus1 = 23 +VUI_vcl_time_offset_length = 24 +VUI_low_delay_hrd_flag = 0 + +# other parameters (i.e. bitsream restrictions) +VUI_pic_struct_present_flag = 0 +VUI_bitstream_restriction_flag = 0 +VUI_motion_vectors_over_pic_boundaries_flag = 1 +VUI_max_bytes_per_pic_denom = 0 +VUI_max_bits_per_mb_denom = 0 +VUI_log2_max_mv_length_vertical = 16 +VUI_log2_max_mv_length_horizontal = 16 +VUI_num_reorder_frames = 16 +VUI_max_dec_frame_buffering = 16 + diff --git a/bin/HM-like/per-sequence_JM/BQMall.cfg b/bin/HM-like/per-sequence_JM/BQMall.cfg new file mode 100644 index 0000000..159d508 --- /dev/null +++ b/bin/HM-like/per-sequence_JM/BQMall.cfg @@ -0,0 +1,9 @@ +#======== File I/O =============== +InputFile = "../../origCfP/BQMall_832x480_60.yuv" +SourceBitDepthLuma = 8 # Source Bit Depth for Luma color component (8...14 bits) +SourceBitDepthChroma = 8 # Source Bit Depth for Chroma color components (8...14 bits) +FrameRate = 60 # Frame Rate per second +FrameSkip = 0 # Number of frames to be skipped in input +SourceWidth = 832 # Input frame width +SourceHeight = 480 # Input frame height +FramesToBeEncoded = 600 # Number of frames to be coded diff --git a/bin/HM-like/per-sequence_JM/BQSquare.cfg b/bin/HM-like/per-sequence_JM/BQSquare.cfg new file mode 100644 index 0000000..28b8d67 --- /dev/null +++ b/bin/HM-like/per-sequence_JM/BQSquare.cfg @@ -0,0 +1,9 @@ +#======== File I/O =============== +InputFile = "../../origCfP/BQSquare_416x240_60.yuv" +SourceBitDepthLuma = 8 # Source Bit Depth for Luma color component (8...14 bits) +SourceBitDepthChroma = 8 # Source Bit Depth for Chroma color components (8...14 bits) +FrameRate = 60 # Frame Rate per second +FrameSkip = 0 # Number of frames to be skipped in input +SourceWidth = 416 # Input frame width +SourceHeight = 240 # Input frame height +FramesToBeEncoded = 600 # Number of frames to be coded diff --git a/bin/HM-like/per-sequence_JM/BQTerrace.cfg b/bin/HM-like/per-sequence_JM/BQTerrace.cfg new file mode 100644 index 0000000..5f8777e --- /dev/null +++ b/bin/HM-like/per-sequence_JM/BQTerrace.cfg @@ -0,0 +1,9 @@ +#======== File I/O =============== +InputFile = "../../origCfP/BQTerrace_1920x1080_60.yuv" +SourceBitDepthLuma = 8 # Source Bit Depth for Luma color component (8...14 bits) +SourceBitDepthChroma = 8 # Source Bit Depth for Chroma color components (8...14 bits) +FrameRate = 60 # Frame Rate per second +FrameSkip = 0 # Number of frames to be skipped in input +SourceWidth = 1920 # Input frame width +SourceHeight = 1080 # Input frame height +FramesToBeEncoded = 600 # Number of frames to be coded diff --git a/bin/HM-like/per-sequence_JM/BasketballDrill.cfg b/bin/HM-like/per-sequence_JM/BasketballDrill.cfg new file mode 100644 index 0000000..5ef6435 --- /dev/null +++ b/bin/HM-like/per-sequence_JM/BasketballDrill.cfg @@ -0,0 +1,9 @@ +#======== File I/O =============== +InputFile = "../../origCfP/BasketballDrill_832x480_50.yuv" +SourceBitDepthLuma = 8 # Source Bit Depth for Luma color component (8...14 bits) +SourceBitDepthChroma = 8 # Source Bit Depth for Chroma color components (8...14 bits) +FrameRate = 50 # Frame Rate per second +FrameSkip = 0 # Number of frames to be skipped in input +SourceWidth = 832 # Input frame width +SourceHeight = 480 # Input frame height +FramesToBeEncoded = 500 # Number of frames to be coded diff --git a/bin/HM-like/per-sequence_JM/BasketballDrillText.cfg b/bin/HM-like/per-sequence_JM/BasketballDrillText.cfg new file mode 100644 index 0000000..0af9c81 --- /dev/null +++ b/bin/HM-like/per-sequence_JM/BasketballDrillText.cfg @@ -0,0 +1,9 @@ +#======== File I/O =============== +InputFile = "../../origCfP/BasketballDrillText_832x480_50.yuv" +SourceBitDepthLuma = 8 # Source Bit Depth for Luma color component (8...14 bits) +SourceBitDepthChroma = 8 # Source Bit Depth for Chroma color components (8...14 bits) +FrameRate = 50 # Frame Rate per second +FrameSkip = 0 # Number of frames to be skipped in input +SourceWidth = 832 # Input frame width +SourceHeight = 480 # Input frame height +FramesToBeEncoded = 500 # Number of frames to be coded diff --git a/bin/HM-like/per-sequence_JM/BasketballDrive.cfg b/bin/HM-like/per-sequence_JM/BasketballDrive.cfg new file mode 100644 index 0000000..9c74a16 --- /dev/null +++ b/bin/HM-like/per-sequence_JM/BasketballDrive.cfg @@ -0,0 +1,9 @@ +#======== File I/O =============== +InputFile = "../../origCfP/BasketballDrive_1920x1080_50.yuv" +SourceBitDepthLuma = 8 # Source Bit Depth for Luma color component (8...14 bits) +SourceBitDepthChroma = 8 # Source Bit Depth for Chroma color components (8...14 bits) +FrameRate = 50 # Frame Rate per second +FrameSkip = 0 # Number of frames to be skipped in input +SourceWidth = 1920 # Input frame width +SourceHeight = 1080 # Input frame height +FramesToBeEncoded = 500 # Number of frames to be coded diff --git a/bin/HM-like/per-sequence_JM/BasketballPass.cfg b/bin/HM-like/per-sequence_JM/BasketballPass.cfg new file mode 100644 index 0000000..f5ef023 --- /dev/null +++ b/bin/HM-like/per-sequence_JM/BasketballPass.cfg @@ -0,0 +1,9 @@ +#======== File I/O =============== +InputFile = "../../origCfP/BasketballPass_416x240_50.yuv" +SourceBitDepthLuma = 8 # Source Bit Depth for Luma color component (8...14 bits) +SourceBitDepthChroma = 8 # Source Bit Depth for Chroma color components (8...14 bits) +FrameRate = 50 # Frame Rate per second +FrameSkip = 0 # Number of frames to be skipped in input +SourceWidth = 416 # Input frame width +SourceHeight = 240 # Input frame height +FramesToBeEncoded = 500 # Number of frames to be coded diff --git a/bin/HM-like/per-sequence_JM/BlowingBubbles.cfg b/bin/HM-like/per-sequence_JM/BlowingBubbles.cfg new file mode 100644 index 0000000..90dcdb5 --- /dev/null +++ b/bin/HM-like/per-sequence_JM/BlowingBubbles.cfg @@ -0,0 +1,9 @@ +#======== File I/O =============== +InputFile = "../../origCfP/BlowingBubbles_416x240_50.yuv" +SourceBitDepthLuma = 8 # Source Bit Depth for Luma color component (8...14 bits) +SourceBitDepthChroma = 8 # Source Bit Depth for Chroma color components (8...14 bits) +FrameRate = 50 # Frame Rate per second +FrameSkip = 0 # Number of frames to be skipped in input +SourceWidth = 416 # Input frame width +SourceHeight = 240 # Input frame height +FramesToBeEncoded = 500 # Number of frames to be coded diff --git a/bin/HM-like/per-sequence_JM/Cactus.cfg b/bin/HM-like/per-sequence_JM/Cactus.cfg new file mode 100644 index 0000000..7523c8d --- /dev/null +++ b/bin/HM-like/per-sequence_JM/Cactus.cfg @@ -0,0 +1,9 @@ +#======== File I/O =============== +InputFile = "../../origCfP/Cactus_1920x1080_50.yuv" +SourceBitDepthLuma = 8 # Source Bit Depth for Luma color component (8...14 bits) +SourceBitDepthChroma = 8 # Source Bit Depth for Chroma color components (8...14 bits) +FrameRate = 50 # Frame Rate per second +FrameSkip = 0 # Number of frames to be skipped in input +SourceWidth = 1920 # Input frame width +SourceHeight = 1080 # Input frame height +FramesToBeEncoded = 500 # Number of frames to be coded diff --git a/bin/HM-like/per-sequence_JM/ChinaSpeed.cfg b/bin/HM-like/per-sequence_JM/ChinaSpeed.cfg new file mode 100644 index 0000000..c87e772 --- /dev/null +++ b/bin/HM-like/per-sequence_JM/ChinaSpeed.cfg @@ -0,0 +1,9 @@ +#======== File I/O =============== +InputFile = "../../origCfP/ChinaSpeed_1024x768_30.yuv" +SourceBitDepthLuma = 8 # Source Bit Depth for Luma color component (8...14 bits) +SourceBitDepthChroma = 8 # Source Bit Depth for Chroma color components (8...14 bits) +FrameRate = 30 # Frame Rate per second +FrameSkip = 0 # Number of frames to be skipped in input +SourceWidth = 1024 # Input frame width +SourceHeight = 768 # Input frame height +FramesToBeEncoded = 500 # Number of frames to be coded diff --git a/bin/HM-like/per-sequence_JM/Kimono.cfg b/bin/HM-like/per-sequence_JM/Kimono.cfg new file mode 100644 index 0000000..a552b66 --- /dev/null +++ b/bin/HM-like/per-sequence_JM/Kimono.cfg @@ -0,0 +1,9 @@ +#======== File I/O =============== +InputFile = "../../origCfP/Kimono1_1920x1080_24.yuv" +SourceBitDepthLuma = 8 # Source Bit Depth for Luma color component (8...14 bits) +SourceBitDepthChroma = 8 # Source Bit Depth for Chroma color components (8...14 bits) +FrameRate = 24 # Frame Rate per second +FrameSkip = 0 # Number of frames to be skipped in input +SourceWidth = 1920 # Input frame width +SourceHeight = 1080 # Input frame height +FramesToBeEncoded = 240 # Number of frames to be coded diff --git a/bin/HM-like/per-sequence_JM/NebutaFestival_10bit.cfg b/bin/HM-like/per-sequence_JM/NebutaFestival_10bit.cfg new file mode 100644 index 0000000..e5bd91b --- /dev/null +++ b/bin/HM-like/per-sequence_JM/NebutaFestival_10bit.cfg @@ -0,0 +1,9 @@ +#======== File I/O =============== +InputFile = "../../origCfP/NebutaFestival_2560x1600_60_10bit_crop.yuv" +SourceBitDepthLuma = 10 # Source Bit Depth for Luma color component (8...14 bits) +SourceBitDepthChroma = 10 # Source Bit Depth for Chroma color components (8...14 bits) +FrameRate = 60 # Frame Rate per second +FrameSkip = 0 # Number of frames to be skipped in input +SourceWidth = 2560 # Input frame width +SourceHeight = 1600 # Input frame height +FramesToBeEncoded = 300 # Number of frames to be coded diff --git a/bin/HM-like/per-sequence_JM/ParkScene.cfg b/bin/HM-like/per-sequence_JM/ParkScene.cfg new file mode 100644 index 0000000..986a5bc --- /dev/null +++ b/bin/HM-like/per-sequence_JM/ParkScene.cfg @@ -0,0 +1,9 @@ +#======== File I/O =============== +InputFile = "../../origCfP/ParkScene_1920x1080_24.yuv" +SourceBitDepthLuma = 8 # Source Bit Depth for Luma color component (8...14 bits) +SourceBitDepthChroma = 8 # Source Bit Depth for Chroma color components (8...14 bits) +FrameRate = 24 # Frame Rate per second +FrameSkip = 0 # Number of frames to be skipped in input +SourceWidth = 1920 # Input frame width +SourceHeight = 1080 # Input frame height +FramesToBeEncoded = 240 # Number of frames to be coded diff --git a/bin/HM-like/per-sequence_JM/PartyScene.cfg b/bin/HM-like/per-sequence_JM/PartyScene.cfg new file mode 100644 index 0000000..c0dc6dc --- /dev/null +++ b/bin/HM-like/per-sequence_JM/PartyScene.cfg @@ -0,0 +1,9 @@ +#======== File I/O =============== +InputFile = "../../origCfP/PartyScene_832x480_50.yuv" +SourceBitDepthLuma = 8 # Source Bit Depth for Luma color component (8...14 bits) +SourceBitDepthChroma = 8 # Source Bit Depth for Chroma color components (8...14 bits) +FrameRate = 50 # Frame Rate per second +FrameSkip = 0 # Number of frames to be skipped in input +SourceWidth = 832 # Input frame width +SourceHeight = 480 # Input frame height +FramesToBeEncoded = 500 # Number of frames to be coded diff --git a/bin/HM-like/per-sequence_JM/PeopleOnStreet.cfg b/bin/HM-like/per-sequence_JM/PeopleOnStreet.cfg new file mode 100644 index 0000000..f9f4e60 --- /dev/null +++ b/bin/HM-like/per-sequence_JM/PeopleOnStreet.cfg @@ -0,0 +1,9 @@ +#======== File I/O =============== +InputFile = "../../origCfP/PeopleOnStreet_2560x1600_30_crop.yuv" +SourceBitDepthLuma = 8 # Source Bit Depth for Luma color component (8...14 bits) +SourceBitDepthChroma = 8 # Source Bit Depth for Chroma color components (8...14 bits) +FrameRate = 30 # Frame Rate per second +FrameSkip = 0 # Number of frames to be skipped in input +SourceWidth = 2560 # Input frame width +SourceHeight = 1600 # Input frame height +FramesToBeEncoded = 150 # Number of frames to be coded diff --git a/bin/HM-like/per-sequence_JM/RaceHorses.cfg b/bin/HM-like/per-sequence_JM/RaceHorses.cfg new file mode 100644 index 0000000..40c0aeb --- /dev/null +++ b/bin/HM-like/per-sequence_JM/RaceHorses.cfg @@ -0,0 +1,9 @@ +#======== File I/O =============== +InputFile = "../../../origCfP/RaceHorses_416x240_30.yuv" +SourceBitDepthLuma = 8 # Source Bit Depth for Luma color component (8...14 bits) +SourceBitDepthChroma = 8 # Source Bit Depth for Chroma color components (8...14 bits) +FrameRate = 30 # Frame Rate per second +FrameSkip = 0 # Number of frames to be skipped in input +SourceWidth = 416 # Input frame width +SourceHeight = 240 # Input frame height +FramesToBeEncoded = 300 # Number of frames to be coded diff --git a/bin/HM-like/per-sequence_JM/RaceHorsesC.cfg b/bin/HM-like/per-sequence_JM/RaceHorsesC.cfg new file mode 100644 index 0000000..5c36847 --- /dev/null +++ b/bin/HM-like/per-sequence_JM/RaceHorsesC.cfg @@ -0,0 +1,9 @@ +#======== File I/O =============== +InputFile = "../../origCfP/RaceHorses_832x480_30.yuv" +SourceBitDepthLuma = 8 # Source Bit Depth for Luma color component (8...14 bits) +SourceBitDepthChroma = 8 # Source Bit Depth for Chroma color components (8...14 bits) +FrameRate = 30 # Frame Rate per second +FrameSkip = 0 # Number of frames to be skipped in input +SourceWidth = 832 # Input frame width +SourceHeight = 480 # Input frame height +FramesToBeEncoded = 300 # Number of frames to be coded diff --git a/bin/HM-like/per-sequence_JM/SlideEditing.cfg b/bin/HM-like/per-sequence_JM/SlideEditing.cfg new file mode 100644 index 0000000..cea6a29 --- /dev/null +++ b/bin/HM-like/per-sequence_JM/SlideEditing.cfg @@ -0,0 +1,9 @@ +#======== File I/O =============== +InputFile = "../../origCfP/SlideEditing_1280x720_30.yuv" +SourceBitDepthLuma = 8 # Source Bit Depth for Luma color component (8...14 bits) +SourceBitDepthChroma = 8 # Source Bit Depth for Chroma color components (8...14 bits) +FrameRate = 30 # Frame Rate per second +FrameSkip = 0 # Number of frames to be skipped in input +SourceWidth = 1280 # Input frame width +SourceHeight = 720 # Input frame height +FramesToBeEncoded = 300 # Number of frames to be coded diff --git a/bin/HM-like/per-sequence_JM/SlideShow.cfg b/bin/HM-like/per-sequence_JM/SlideShow.cfg new file mode 100644 index 0000000..a0f6da6 --- /dev/null +++ b/bin/HM-like/per-sequence_JM/SlideShow.cfg @@ -0,0 +1,9 @@ +#======== File I/O =============== +InputFile = "../../origCfP/SlideShow_1280x720_20.yuv" +SourceBitDepthLuma = 8 # Source Bit Depth for Luma color component (8...14 bits) +SourceBitDepthChroma = 8 # Source Bit Depth for Chroma color components (8...14 bits) +FrameRate = 20 # Frame Rate per second +FrameSkip = 0 # Number of frames to be skipped in input +SourceWidth = 1280 # Input frame width +SourceHeight = 720 # Input frame height +FramesToBeEncoded = 500 # Number of frames to be coded diff --git a/bin/HM-like/per-sequence_JM/SteamLocomotiveTrain_10bit.cfg b/bin/HM-like/per-sequence_JM/SteamLocomotiveTrain_10bit.cfg new file mode 100644 index 0000000..96c5607 --- /dev/null +++ b/bin/HM-like/per-sequence_JM/SteamLocomotiveTrain_10bit.cfg @@ -0,0 +1,9 @@ +#======== File I/O =============== +InputFile = "../../origCfP/SteamLocomotiveTrain_2560x1600_60_10bit_crop.yuv" +SourceBitDepthLuma = 10 # Source Bit Depth for Luma color component (8...14 bits) +SourceBitDepthChroma = 10 # Source Bit Depth for Chroma color components (8...14 bits) +FrameRate = 60 # Frame Rate per second +FrameSkip = 0 # Number of frames to be skipped in input +SourceWidth = 2560 # Input frame width +SourceHeight = 1600 # Input frame height +FramesToBeEncoded = 300 # Number of frames to be coded diff --git a/bin/HM-like/per-sequence_JM/Traffic.cfg b/bin/HM-like/per-sequence_JM/Traffic.cfg new file mode 100644 index 0000000..2b37a20 --- /dev/null +++ b/bin/HM-like/per-sequence_JM/Traffic.cfg @@ -0,0 +1,9 @@ +#======== File I/O =============== +InputFile = "../../origCfP/Traffic_2560x1600_30_crop.yuv" +SourceBitDepthLuma = 8 # Source Bit Depth for Luma color component (8...14 bits) +SourceBitDepthChroma = 8 # Source Bit Depth for Chroma color components (8...14 bits) +FrameRate = 30 # Frame Rate per second +FrameSkip = 0 # Number of frames to be skipped in input +SourceWidth = 2560 # Input frame width +SourceHeight = 1600 # Input frame height +FramesToBeEncoded = 150 # Number of frames to be coded diff --git a/bin/HM-like/per-sequence_JM/Vidyo1.cfg b/bin/HM-like/per-sequence_JM/Vidyo1.cfg new file mode 100644 index 0000000..866f05c --- /dev/null +++ b/bin/HM-like/per-sequence_JM/Vidyo1.cfg @@ -0,0 +1,9 @@ +#======== File I/O =============== +InputFile = "../../origCfP/vidyo1_1280x720_60.yuv" +SourceBitDepthLuma = 8 # Source Bit Depth for Luma color component (8...14 bits) +SourceBitDepthChroma = 8 # Source Bit Depth for Chroma color components (8...14 bits) +FrameRate = 60 # Frame Rate per second +FrameSkip = 0 # Number of frames to be skipped in input +SourceWidth = 1280 # Input frame width +SourceHeight = 720 # Input frame height +FramesToBeEncoded = 600 # Number of frames to be coded diff --git a/bin/HM-like/per-sequence_JM/Vidyo3.cfg b/bin/HM-like/per-sequence_JM/Vidyo3.cfg new file mode 100644 index 0000000..3957fcb --- /dev/null +++ b/bin/HM-like/per-sequence_JM/Vidyo3.cfg @@ -0,0 +1,9 @@ +#======== File I/O =============== +InputFile = "../../origCfP/vidyo3_1280x720_60.yuv" +SourceBitDepthLuma = 8 # Source Bit Depth for Luma color component (8...14 bits) +SourceBitDepthChroma = 8 # Source Bit Depth for Chroma color components (8...14 bits) +FrameRate = 60 # Frame Rate per second +FrameSkip = 0 # Number of frames to be skipped in input +SourceWidth = 1280 # Input frame width +SourceHeight = 720 # Input frame height +FramesToBeEncoded = 600 # Number of frames to be coded diff --git a/bin/HM-like/per-sequence_JM/Vidyo4.cfg b/bin/HM-like/per-sequence_JM/Vidyo4.cfg new file mode 100644 index 0000000..a940b20 --- /dev/null +++ b/bin/HM-like/per-sequence_JM/Vidyo4.cfg @@ -0,0 +1,9 @@ +#======== File I/O =============== +InputFile = "../../origCfP/vidyo4_1280x720_60.yuv" +SourceBitDepthLuma = 8 # Source Bit Depth for Luma color component (8...14 bits) +SourceBitDepthChroma = 8 # Source Bit Depth for Chroma color components (8...14 bits) +FrameRate = 60 # Frame Rate per second +FrameSkip = 0 # Number of frames to be skipped in input +SourceWidth = 1280 # Input frame width +SourceHeight = 720 # Input frame height +FramesToBeEncoded = 600 # Number of frames to be coded diff --git a/bin/ToneMapping.cfg b/bin/ToneMapping.cfg new file mode 100644 index 0000000..5972c63 --- /dev/null +++ b/bin/ToneMapping.cfg @@ -0,0 +1,25 @@ +## configure file for tone mapping SEI message, +## must follow the format "keyword = xx" exactly +## +tone_map_id = 0 +tone_map_cancel_flag = 0 +tone_map_repetition_period = 1 +coded_data_bit_depth = 10 +sei_bit_depth = 8 +model_id = 0 + +## model_id 0: linear mapping +min_value = 0 +max_value = 1023 + +## model_id 1: sigmoidal mapping +sigmoid_midpoint = 512 +sigmoid_width = 960 + +## model_id 2: user-defined table mapping, start_of_coded_interval has (1< = # Comment +# +########################################################################################## +# Files +########################################################################################## +InputFile = "ChampTower_Stereo.264" # H.264/AVC coded bitstream +OutputFile = "ChampTower_Stereo.yuv" # Output file, YUV/RGB +RefFile = "ChampTower_REF.yuv" # Ref sequence (for SNR) +WriteUV = 1 # Write 4:2:0 chroma components for monochrome streams +FileFormat = 0 # NAL mode (0=Annex B, 1: RTP packets) +RefOffset = 0 # SNR computation offset +POCScale = 2 # Poc Scale (1 or 2) +########################################################################################## +# HRD parameters +########################################################################################## +#R_decoder = 500000 # Rate_Decoder +#B_decoder = 104000 # B_decoder +#F_decoder = 73000 # F_decoder +#LeakyBucketParamFile = "leakybucketparam.cfg" # LeakyBucket Params +########################################################################################## +# decoder control parameters +########################################################################################## +DisplayDecParams = 1 # 1: Display parameters; +ConcealMode = 0 # Err Concealment(0:Off,1:Frame Copy,2:Motion Copy) +RefPOCGap = 2 # Reference POC gap (2: IPP (Default), 4: IbP / IpP) +POCGap = 2 # POC gap (2: IPP /IbP/IpP (Default), 4: IPP with frame skip = 1 etc.) +IDRPeriod = 10 # IDR Period +Silent = 0 # Silent decode +IntraProfileDeblocking = 1 # Enable Deblocking filter in intra only profiles (0=disable, 1=filter according to SPS parameters) +DecFrmNum = 0 # Number of frames to be decoded (-n) +########################################################################################## +# Frame Loss Concealment Parameters +########################################################################################## +ConcealMode = 1 # Err Concealment(0:Off,1:Frame Copy,2:Motion Copy) +# SearchMode Brief +# SearchMode = 0: Uses the previous reference pic for concealment of reference frames +# within the same view +# SearchMode = 1: Uses the closest frame to conceal (inc concealed frames) +# SearchMode = 2: Uses the closest frame to conceal (not concealed frames) +SearchModeRef = 2 +SearchModeNonRef = 1 +########################################################################################## +# MVC decoding parameters +########################################################################################## +DecodeAllLayers = 1 # Decode all views (-mpr) diff --git a/bin/decoder_stereo.cfg b/bin/decoder_stereo.cfg new file mode 100644 index 0000000..6866c4b --- /dev/null +++ b/bin/decoder_stereo.cfg @@ -0,0 +1,40 @@ +# This is a file containing input parameters to the JVT H.264/AVC decoder. +# The text line following each parameter is discarded by the decoder. +# +# For bug reporting and known issues see: +# https://ipbt.hhi.fraunhofer.de +# +# New Input File Format is as follows +# = # Comment +# +########################################################################################## +# Files +########################################################################################## +InputFile = "test.264" # H.264/AVC coded bitstream +OutputFile = "test_dec.yuv" # Output file, YUV/RGB +RefFile = "test_rec.yuv" # Ref sequence (for SNR) +WriteUV = 1 # Write 4:2:0 chroma components for monochrome streams +FileFormat = 0 # NAL mode (0=Annex B, 1: RTP packets) +RefOffset = 0 # SNR computation offset +POCScale = 2 # Poc Scale (1 or 2) +########################################################################################## +# HRD parameters +########################################################################################## +#R_decoder = 500000 # Rate_Decoder +#B_decoder = 104000 # B_decoder +#F_decoder = 73000 # F_decoder +#LeakyBucketParamFile = "leakybucketparam.cfg" # LeakyBucket Params +########################################################################################## +# decoder control parameters +########################################################################################## +DisplayDecParams = 0 # 1: Display parameters; +ConcealMode = 0 # Err Concealment(0:Off,1:Frame Copy,2:Motion Copy) +RefPOCGap = 2 # Reference POC gap (2: IPP (Default), 4: IbP / IpP) +POCGap = 2 # POC gap (2: IPP /IbP/IpP (Default), 4: IPP with frame skip = 1 etc.) +Silent = 0 # Silent decode +IntraProfileDeblocking = 1 # Enable Deblocking filter in intra only profiles (0=disable, 1=filter according to SPS parameters) +DecFrmNum = 0 # Number of frames to be decoded (-n) +########################################################################################## +# MVC decoding parameters +########################################################################################## +DecodeAllLayers = 1 # Decode all views (-mpr) diff --git a/bin/encoder.cfg b/bin/encoder.cfg new file mode 100644 index 0000000..c38f893 --- /dev/null +++ b/bin/encoder.cfg @@ -0,0 +1,671 @@ +# New Input File Format is as follows +# = # Comment +# +# See configfile.h for a list of supported ParameterNames +# +# For bug reporting and known issues see: +# https://ipbt.hhi.fraunhofer.de + + +########################################################################################## +# Files +########################################################################################## +InputFile = "foreman_part_qcif.yuv" # Input sequence +InputHeaderLength = 0 # If the inputfile has a header, state it's length in byte here +StartFrame = 0 # Start frame for encoding. (0-N) +FramesToBeEncoded = 3 # Number of frames to be coded +FrameRate = 30.0 # Frame Rate per second (0.1-100.0) +Enable32Pulldown = 0 # Enable 'hard' 3:2 pulldown (modifying the inpur data) + # 0 = disabled + # 1 = A, B, Bt|Cb, Ct|Db, D + # 2 = A, B, C, Ct|Db, D +SEIVUI32Pulldown = 0 # Enable 3:2 pulldown through VUI and SEI metadata signaling. Five methods are supported: + # 0 = disabled + # 1 = A, Bt|Bb, Bt|Cb, Ct|Cb, D + # 2 = A, B, C, C, D + # 3 = At|Ab, Bt|Bb, Bt|Cb, Ct|Cb, Dt|Db + # 4 = A, Bt|Bb, Bt|Cb, Ct|Db, Dt|Db + # 5 = At|Ab, Bt|Bb, Bt|Cb, Ct|Db, Dt|Db + +SourceWidth = 176 # Source frame width +SourceHeight = 144 # Source frame height +SourceResize = 0 # Resize source size for output +OutputWidth = 176 # Output frame width +OutputHeight = 144 # Output frame height +ProcessInput = 0 # Filter Input Sequence +Interleaved = 0 # 0: Planar input, 1: Packed input +PixelFormat = 0 # Pixel Format for 422 packed inputs + # 0: UYVY + # 1: YUY2/YUYV + # 2: YVYU + # 3: BGR (Unsupported) + # 4: V210 (Video Clarity) + +StandardRange = 0 # 0: Standard range 1: Full range (RGB input) +VideoCode = 1 # Video codes for RGB ==> YUV conversions + # 0 = NULL, + # 1 = ITU_REC709, + # 2 = CCIR_601, + # 3 = FCC, + # 4 = ITU_REC624BG, + # 5 = SMPTE_170M, + # 6 = SMPTE_240M, + # 7 = SMPTE_260M, + # 8 = ITU_REC709_EXACT + +TraceFile = "trace_enc.txt" # Trace file +ReconFile = "test_rec.yuv" # Reconstruction YUV file +OutputFile = "test.264" # Bitstream +StatsFile = "stats.dat" # Coding statistics file + +NumberOfViews = 1 # Number of views to encode (1=1 view, 2=2 views) +View1ConfigFile = "encoder_view1.cfg" # Config file name for second view +########################################################################################## +# Encoder Control +########################################################################################## +Grayscale = 0 # Encode in grayscale (Currently only works for 8 bit YUV 420 input) +ProfileIDC = 100 # Profile IDC (66=baseline, 77=main, 88=extended; FREXT Profiles: 100=High, 110=High 10, 122=High 4:2:2, 244=High 4:4:4, 44=CAVLC 4:4:4 Intra, 118=Multiview High Profile, 128=Stereo High Profile) +IntraProfile = 0 # Activate Intra Profile for FRExt (0: false, 1: true) + # (e.g. ProfileIDC=110, IntraProfile=1 => High 10 Intra Profile) +LevelIDC = 40 # Level IDC (e.g. 20 = level 2.0) + +IntraPeriod = 0 # Period of I-pictures (0=only first) +IDRPeriod = 0 # Period of IDR pictures (0=only first) +AdaptiveIntraPeriod = 1 # Adaptive intra period +AdaptiveIDRPeriod = 0 # Adaptive IDR period +IntraDelay = 0 # Intra (IDR) picture delay (i.e. coding structure of PPIPPP... ) +EnableIDRGOP = 0 # Support for IDR closed GOPs (0: disabled, 1: enabled) +EnableOpenGOP = 0 # Support for open GOPs (0: disabled, 1: enabled) +QPISlice = 28 # Quant. param for I Slices (0-51) +QPPSlice = 28 # Quant. param for P Slices (0-51) +FrameSkip = 0 # Number of frames to be skipped in input (e.g 2 will code every third frame). + # Note that this now excludes intermediate (i.e. B) coded pictures +ChromaQPOffset = 0 # Chroma QP offset (-51..51) + +DisableSubpelME = 0 # Disable Subpixel Motion Estimation (0=off/default, 1=on) +SearchRange = 32 # Max search range +MESoftenSSEMetric = 0 # soften lambda criterion for SSE ME +MEDistortionFPel = 0 # Select error metric for Full-Pel ME (0: SAD, 1: SSE, 2: Hadamard SAD) +MEDistortionHPel = 2 # Select error metric for Half-Pel ME (0: SAD, 1: SSE, 2: Hadamard SAD) +MEDistortionQPel = 2 # Select error metric for Quarter-Pel ME (0: SAD, 1: SSE, 2: Hadamard SAD) +MDDistortion = 2 # Select error metric for Mode Decision (0: SAD, 1: SSE, 2: Hadamard SAD) +SkipDeBlockNonRef = 0 # Skip Deblocking (regardless of DFParametersFlag) for non-reference frames (0: off, 1: on) +OnTheFlyFractMCP = 0 # Perform on-the-fly fractional pixel interpolation for Motion Compensation and Motion Estimation + # 0: Disable, interpolate & store all positions + # 1: Store full pel & interpolated 1/2 pel positions; 1/4 pel positions interpolate on-the-fly + # 2: Store only full pell positions; 1/2 & 1/4 pel positions interpolate on-the-fly +ChromaMCBuffer = 1 # Calculate Color component interpolated values in advance and store them. + # Provides a trade-off between memory and computational complexity + # (0: disabled/default, 1: enabled) +ChromaMEEnable = 0 # Take into account Color component information during ME + # (0: only first component/default, + # 1: All Color components - Integer refinement only + # 2: All Color components - All refinements) +ChromaMEWeight = 1 # Weighting for chroma components. This parameter should have a relationship with color format. + +NumberReferenceFrames = 5 # Number of previous frames used for inter motion search (0-16) + +PList0References = 0 # P slice List 0 reference override (0 disable, N <= NumberReferenceFrames) +Log2MaxFNumMinus4 = 0 # Sets log2_max_frame_num_minus4 (-1 : based on FramesToBeEncoded/Auto, >=0 : Log2MaxFNumMinus4) +Log2MaxPOCLsbMinus4 = -1 # Sets log2_max_pic_order_cnt_lsb_minus4 (-1 : Auto, >=0 : Log2MaxPOCLsbMinus4) + +GenerateMultiplePPS = 1 # Transmit multiple parameter sets. Currently parameters basically enable all WP modes (0: diabled, 1: enabled) +SendAUD = 0 # Send Access Delimiter Unit NALU (for every access unit) +ResendSPS = 2 # Resend SPS (0: disabled, 1: all Intra pictures, 2: only for IDR, 3: for IDR and OpenGOP I) +ResendPPS = 0 # Resend PPS (with pic_parameter_set_id 0) for every coded Frame/Field pair (0: disabled, 1: enabled) + +MbLineIntraUpdate = 0 # Error robustness(extra intra macro block updates)(0=off, N: One GOB every N frames are intra coded) +RandomIntraMBRefresh = 0 # Forced intra MBs per picture + +########################################################################################## +# PSlice Mode types +########################################################################################## +PSliceSkip = 1 # P-Slice Skip mode consideration (0=disable, 1=enable) +PSliceSearch16x16 = 1 # P-Slice Inter block search 16x16 (0=disable, 1=enable) +PSliceSearch16x8 = 1 # P-Slice Inter block search 16x8 (0=disable, 1=enable) +PSliceSearch8x16 = 1 # P-Slice Inter block search 8x16 (0=disable, 1=enable) +PSliceSearch8x8 = 1 # P-Slice Inter block search 8x8 (0=disable, 1=enable) +PSliceSearch8x4 = 1 # P-Slice Inter block search 8x4 (0=disable, 1=enable) +PSliceSearch4x8 = 1 # P-Slice Inter block search 4x8 (0=disable, 1=enable) +PSliceSearch4x4 = 1 # P-Slice Inter block search 4x4 (0=disable, 1=enable) + +########################################################################################## +# BSlice Mode types +########################################################################################## + +BSliceDirect = 1 # B-Slice Skip mode consideration (0=disable, 1=enable) +BSliceSearch16x16 = 1 # B-Slice Inter block search 16x16 (0=disable, 1=enable) +BSliceSearch16x8 = 1 # B-Slice Inter block search 16x8 (0=disable, 1=enable) +BSliceSearch8x16 = 1 # B-Slice Inter block search 8x16 (0=disable, 1=enable) +BSliceSearch8x8 = 1 # B-Slice Inter block search 8x8 (0=disable, 1=enable) +BSliceSearch8x4 = 1 # B-Slice Inter block search 8x4 (0=disable, 1=enable) +BSliceSearch4x8 = 1 # B-Slice Inter block search 4x8 (0=disable, 1=enable) +BSliceSearch4x4 = 1 # B-Slice Inter block search 4x4 (0=disable, 1=enable) + +BiPredSearch16x16 = 1 # B-Slice Bi-prediction block search 16x16 (0=disable, 1=enable) +BiPredSearch16x8 = 1 # B-Slice Bi-prediction block search 16x8 (0=disable, 1=enable) +BiPredSearch8x16 = 1 # B-Slice Bi-prediction block search 8x16 (0=disable, 1=enable) +BiPredSearch8x8 = 0 # B-Slice Bi-prediction block search 8x8 (0=disable, 1=enable) + +DisableIntra4x4 = 0 # Disable Intra 4x4 modes +DisableIntra16x16 = 0 # Disable Intra 16x16 modes +DisableIntraInInter = 0 # Disable Intra modes for inter slices +IntraDisableInterOnly = 0 # Apply Disabling Intra conditions only to Inter Slices (0:disable/default,1: enable) +Intra4x4ParDisable = 0 # Disable Vertical & Horizontal 4x4 +Intra4x4DiagDisable = 0 # Disable Diagonal 45degree 4x4 +Intra4x4DirDisable = 0 # Disable Other Diagonal 4x4 +Intra16x16ParDisable = 0 # Disable Vertical & Horizontal 16x16 +Intra16x16PlaneDisable = 0 # Disable Planar 16x16 +ChromaIntraDisable = 0 # Disable Intra Chroma modes other than DC +EnableIPCM = 1 # Enable IPCM macroblock mode + +DisposableP = 0 # Enable Disposable P slices in the primary layer (0: disable/default, 1: enable) +DispPQPOffset = 0 # Quantizer offset for disposable P slices (0: default) + +PreferDispOrder = 1 # Prefer display order when building the prediction structure as opposed to coding order (affects intra and IDR periodic insertion, among others) +PreferPowerOfTwo = 0 # Prefer prediction structures that have lengths expressed as powers of two +FrmStructBufferLength = 16 # Length of the frame structure unit buffer; it can be overriden for certain cases + +ChangeQPFrame = 0 # Frame in display order from which to apply the Change QP offsets +ChangeQPI = 0 # Change QP offset value for I_SLICE +ChangeQPP = 0 # Change QP offset value for P_SLICE +ChangeQPB = 0 # Change QP offset value for B_SLICE +ChangeQPSI = 0 # Change QP offset value for SI_SLICE +ChangeQPSP = 0 # Change QP offset value for SP_SLICE + +########################################################################################## +# B Slices +########################################################################################## + +NumberBFrames = 7 # Number of B coded frames inserted (0=not used) +PReplaceBSlice = 0 # Replace B-coded slices with P-coded slices when NumberBFrames>0 +QPBSlice = 30 # Quant. param for B slices (0-51) +BRefPicQPOffset = -1 # Quantization offset for reference B coded pictures (-51..51) +DirectModeType = 1 # Direct Mode Type (0:Temporal 1:Spatial) +DirectInferenceFlag = 1 # Direct Inference Flag (0: Disable 1: Enable) +BList0References = 0 # B slice List 0 reference override (0 disable, N <= NumberReferenceFrames) +BList1References = 1 # B slice List 1 reference override (0 disable, N <= NumberReferenceFrames) + # 1 List1 reference is usually recommended for normal GOP Structures. + # A larger value is usually more appropriate if a more flexible + # structure is used (i.e. using HierarchicalCoding) + +BReferencePictures = 0 # Referenced B coded pictures (0=off, 1=B references for secondary layer, 2=B references for primary layer) + +HierarchicalCoding = 3 # B hierarchical coding (0= off, 1= 2 layers, 2= 2 full hierarchy, 3 = explicit) +HierarchyLevelQPEnable = 1 # Adjust QP based on hierarchy level (in increments of 1). Overrides BRefPicQPOffset behavior.(0=off, 1=on) +ExplicitHierarchyFormat = "b3r0b1r1b0e2b2e2b5r1b4e2b6e2" # Explicit Enhancement GOP. Format is {FrameDisplay_orderReferenceQP}. + # Valid values for reference type is r:reference, e:non reference. +ExplicitSeqCoding = 0 # Enable support for explicit sequence coding +ExplicitSeqFile = "explicit_seq.cfg" +LowDelay = 0 # Apply HierarchicalCoding without delay (i.e., encode in the captured/display order) +ReferenceReorder = 1 # Reorder References according to Poc distance for HierarchicalCoding (0=off, 1=enable, 2=use when LowDelay is set) +PocMemoryManagement = 1 # Memory management based on Poc Distances for HierarchicalCoding (0=off, 1=on, 2=use when LowDelay is set) +SetFirstAsLongTerm = 0 # Set first frame as long term + +BiPredMotionEstimation = 1 # Enable Bipredictive based Motion Estimation (0:disabled, 1:enabled) +BiPredMERefinements = 3 # Bipredictive ME extra refinements (0: single, N: N extra refinements (1 default) +BiPredMESearchRange = 16 # Bipredictive ME Search range (8 default). Note that range is halved for every extra refinement. +BiPredMESubPel = 2 # Bipredictive ME Subpixel Consideration (0: disabled, 1: single level, 2: dual level) + + +########################################################################################## +# SP Frames +########################################################################################## + +SPPicturePeriodicity = 0 # SP-Picture Periodicity (0=not used) +SPSwitchPeriod = 0 # Switch period (in terms of switching SP/SI frames) between bitstream 1 and bitstream 2 +QPSPSlice = 36 # Quant. param of SP-Slices for Prediction Error (0-51) +QPSISlice = 36 # Quant. param of SI-Slices for Prediction Error (0-51) +QPSP2Slice = 35 # Quant. param of SP/SI-Slices for Predicted Blocks (0-51) +SI_FRAMES = 0 # SI frame encoding flag (0=not used, 1=used) +SP_output = 0 # Controls whether coefficients will be output to encode switching SP frames (0=no, 1=yes) +SP_output_name = "low_quality.dat" # Filename for SP output coefficients +SP2_FRAMES = 0 # switching SP frame encoding flag (0=not used, 1=used) +SP2_input_name1 = "high_quality.dat" # Filename for the first swithed bitstream coefficients +SP2_input_name2 = "low_quality.dat" # Filename for the second switched bitstream coefficients + +########################################################################################## +# Output Control, NALs +########################################################################################## + +SymbolMode = 1 # Symbol mode (Entropy coding method: 0=UVLC, 1=CABAC) +OutFileMode = 0 # Output file mode, 0:Annex B, 1:RTP +PartitionMode = 0 # Partition Mode, 0: no DP, 1: 3 Partitions per Slice + +########################################################################################## +# CABAC context initialization +########################################################################################## + +ContextInitMethod = 1 # Context init (0: fixed, 1: adaptive) +FixedModelNumber = 0 # model number for fixed decision for inter slices ( 0, 1, or 2 ) + +########################################################################################## +# Interlace Handling +######################################################################################### + +PicInterlace = 0 # Picture AFF (0: frame coding, 1: field coding, 2:adaptive frame/field coding) +MbInterlace = 0 # Macroblock AFF (0: frame coding, 1: field coding, 2:adaptive frame/field coding, 3: frame MB-only AFF) +IntraBottom = 0 # Force Intra Bottom at GOP Period + +########################################################################################## +# Weighted Prediction +######################################################################################### + +WeightedPrediction = 0 # P picture Weighted Prediction (0=off, 1=explicit mode) +WeightedBiprediction = 0 # B picture Weighted Prediciton (0=off, 1=explicit mode, 2=implicit mode) +ChromaWeightSupport = 1 # Enable consideration of weights for Chroma components +UseWeightedReferenceME = 1 # Use weighted reference for ME (0=off, 1=on) +WPMethod = 1 # WP method (0: DC based, 1: LMS based) +WPIterMC = 0 # Iterative Motion compensated based weighted prediction method +EnhancedBWeightSupport = 0 # Enhanced B Weight support (needs revisit if we wish to merge with WPMethod) +WPMCPrecision = 2 # Improved Motion Compensation Precision using WP based methods. + # Clones WP references with slightly modified rounding offsets (Requires RDPictureDecision and GenerateMultiplePPS) : + # 0: disabled (default) + # 1: Up to one additional coding pass. Ref0 is 0, ref1 is 0 with a -1 offset + # 2: Up to two additional coding passes. (1) Ref0 is 0, ref1 is 0 with a -1 offset, (1) Ref0 is 0 with a -1 offset, ref1 is 0 +WPMCPrecFullRef = 0 # Increases the number of references in the reference picture lists to account + # for the lost reference slot when reordering is used during a coding pass in WPMCPrecision for reference replication. + # The number of references in non-reordered passes stays unchanged +WPMCPrecBSlice = 1 # 2: Apply rounding on every B slice. This efectively disables the evaluation of alternative QPs during RDPictureDecision. + # 1: Disable rounding for non-reference B slices. Non-reference B slices are evaluated for alternative QPs during RDPictureDecision. + # 0: Disable rounding for B slices. + +########################################################################################## +# Picture based Multi-pass encoding +######################################################################################### + +RDPictureDecision = 1 # Perform multiple pass coding and make RD optimal decision among them +RDPSliceBTest = 0 # Perform Slice level RD decision between P and B slices. +RDPSliceITest = 1 # Perform Slice level RD decision between P and I slices. Default value is 1 (enabled). +RDPictureMaxPassISlice = 1 # Max number of coding passes for I slices, valid values [1,3], default is 1 +RDPictureMaxPassPSlice = 2 # Max number of coding passes for P slices, valid values [1,6], default is 2 +RDPictureMaxPassBSlice = 3 # Max number of coding passes for B slices, valid values [1,6], default is 3 +RDPictureFrameQPPSlice = 0 # Perform additional frame level QP check (QP+/-1) for P slices, 0: disabled (default), 1: enabled +RDPictureFrameQPBSlice = 0 # Perform additional frame level QP check (QP+/-1) for B slices, 0: disabled, 1: enabled (default) +RDPictureDeblocking = 0 # Perform another coding pass to check non-deblocked picture, 0: disabled (default), 1: enabled +RDPictureDirectMode = 0 # Perform another coding pass to check the alternative direct mode for B slices, , 0: disabled (default), 1: enabled + +########################################################################################## +# Deblocking filter parameters +########################################################################################## + +DFParametersFlag = 0 # Configure deblocking filter (0=parameters below ignored, 1=parameters sent) + # Note that for pictures with multiple slice types, + # only the type of the first slice will be considered. +DFDisableRefISlice = 0 # Disable deblocking filter in reference I coded pictures (0=Filter, 1=No Filter). +DFAlphaRefISlice = 0 # Reference I coded pictures Alpha offset div. 2, {-6, -5, ... 0, +1, .. +6} +DFBetaRefISlice = 0 # Reference I coded pictures Beta offset div. 2, {-6, -5, ... 0, +1, .. +6} +DFDisableNRefISlice = 0 # Disable deblocking filter in non reference I coded pictures (0=Filter, 1=No Filter). +DFAlphaNRefISlice = 0 # Non Reference I coded pictures Alpha offset div. 2, {-6, -5, ... 0, +1, .. +6} +DFBetaNRefISlice = 0 # Non Reference I coded pictures Beta offset div. 2, {-6, -5, ... 0, +1, .. +6} +DFDisableRefPSlice = 0 # Disable deblocking filter in reference P coded pictures (0=Filter, 1=No Filter). +DFAlphaRefPSlice = 0 # Reference P coded pictures Alpha offset div. 2, {-6, -5, ... 0, +1, .. +6} +DFBetaRefPSlice = 0 # Reference P coded pictures Beta offset div. 2, {-6, -5, ... 0, +1, .. +6} +DFDisableNRefPSlice = 0 # Disable deblocking filter in non reference P coded pictures (0=Filter, 1=No Filter). +DFAlphaNRefPSlice = 0 # Non Reference P coded pictures Alpha offset div. 2, {-6, -5, ... 0, +1, .. +6} +DFBetaNRefPSlice = 0 # Non Reference P coded pictures Beta offset div. 2, {-6, -5, ... 0, +1, .. +6} +DFDisableRefBSlice = 0 # Disable deblocking filter in reference B coded pictures (0=Filter, 1=No Filter). +DFAlphaRefBSlice = 0 # Reference B coded pictures Alpha offset div. 2, {-6, -5, ... 0, +1, .. +6} +DFBetaRefBSlice = 0 # Reference B coded pictures Beta offset div. 2, {-6, -5, ... 0, +1, .. +6} +DFDisableNRefBSlice = 0 # Disable deblocking filter in non reference B coded pictures (0=Filter, 1=No Filter). +DFAlphaNRefBSlice = 0 # Non Reference B coded pictures Alpha offset div. 2, {-6, -5, ... 0, +1, .. +6} +DFBetaNRefBSlice = 0 # Non Reference B coded pictures Beta offset div. 2, {-6, -5, ... 0, +1, .. +6} + +########################################################################################## +# Error Resilience / Slices +########################################################################################## + +SliceMode = 0 # Slice mode (0=off 1=fixed #mb in slice 2=fixed #bytes in slice 3=use callback) +SliceArgument = 50 # Slice argument (Arguments to modes 1 and 2 above) + +num_slice_groups_minus1 = 0 # Number of Slice Groups Minus 1, 0 == no FMO, 1 == two slice groups, etc. +slice_group_map_type = 0 # 0: Interleave, 1: Dispersed, 2: Foreground with left-over, + # 3: Box-out, 4: Raster Scan 5: Wipe + # 6: Explicit, slice_group_id read from SliceGroupConfigFileName +slice_group_change_direction_flag = 0 # 0: box-out clockwise, raster scan or wipe right, + # 1: box-out counter clockwise, reverse raster scan or wipe left +slice_group_change_rate_minus1 = 85 # +SliceGroupConfigFileName = "sg0conf.cfg" # Used for slice_group_map_type 0, 2, 6 + +UseRedundantPicture = 0 # 0: not used, 1: enabled +NumRedundantHierarchy = 1 # 0-4 +PrimaryGOPLength = 10 # GOP length for redundant allocation (1-16) + # NumberReferenceFrames must be no less than PrimaryGOPLength when redundant slice enabled +NumRefPrimary = 1 # Actually used number of references for primary slices (1-16) + +########################################################################################## +# Search Range Restriction / RD Optimization +########################################################################################## + +RestrictSearchRange = 2 # restriction for (0: blocks and ref, 1: ref, 2: no restrictions) +RDOptimization = 1 # rd-optimized mode decision + # 0: RD-off (Low complexity mode) + # 1: RD-on (High complexity mode) + # 2: RD-on (Fast high complexity mode - not work in FREX Profiles) + # 3: with losses +I16RDOpt = 0 # perform rd-optimized mode decision for Intra 16x16 MB + # 0: SAD-based mode decision for Intra 16x16 MB + # 1: RD-based mode decision for Intra 16x16 MB +SubMBCodingState = 1 # submacroblock coding state + # 0: lowest complexity, do not store or reset coding state during sub-MB mode decision + # 1: medium complexity, reset to master coding state (for current mode) during sub-MB mode decision + # 2: highest complexity, store and reset coding state during sub-MB mode decision +DistortionSSIM = 0 # Compute SSIM distortion. (0: disabled/default, 1: enabled) +DistortionMS_SSIM = 0 # Compute Multiscale SSIM distortion. (0: disabled/default, 1: enabled) +SSIMOverlapSize = 8 # Overlap size to calculate SSIM distortion (1: pixel by pixel, 8: no overlap) +DistortionYUVtoRGB = 0 # Calculate distortion in RGB domain after conversion from YCbCr (0:off, 1:on) +CtxAdptLagrangeMult = 0 # Context Adaptive Lagrange Multiplier + # 0: disabled (default) + # 1: enabled (works best when RDOptimization=0) +FastCrIntraDecision = 1 # Fast Chroma intra mode decision (0:off, 1:on) +DisableThresholding = 0 # Disable Thresholding of Transform Coefficients (0:off, 1:on) +DisableBSkipRDO = 0 # Disable B Skip Mode consideration from RDO Mode decision (0:off, 1:on) +BiasSkipRDO = 0 # Negative Bias for Skip/DirectSkip modes (0: off, 1: on) +ForceTrueRateRDO = 0 # Force true rate (even zero values) during RDO process +SkipIntraInInterSlices = 0 # Skips Intra mode checking in inter slices if certain mode decisions are satisfied (0: off, 1: on) +WeightY = 1 # Luma weight for RDO +WeightCb = 1 # Cb weight for RDO +WeightCr = 1 # Cr weight for RDO + +########################################################################################## +# Explicit Lambda Usage +########################################################################################## +UseExplicitLambdaParams = 0 # Use explicit lambda scaling parameters (0:disabled, 1:enable lambda weight, 2: use explicit lambda value) +UpdateLambdaChromaME = 0 # Update lambda given Chroma ME consideration +FixedLambdaISlice = 0.1 # Fixed Lambda value for I slices +FixedLambdaPSlice = 0.1 # Fixed Lambda value for P slices +FixedLambdaBSlice = 0.1 # Fixed Lambda value for B slices +FixedLambdaRefBSlice = 0.1 # Fixed Lambda value for Referenced B slices +FixedLambdaSPSlice = 0.1 # Fixed Lambda value for SP slices +FixedLambdaSISlice = 0.1 # Fixed Lambda value for SI slices + +LambdaWeightISlice = 0.65 # scaling param for I slices. This will be used as a multiplier i.e. lambda=LambdaWeightISlice * 2^((QP-12)/3) +LambdaWeightPSlice = 0.68 # scaling param for P slices. This will be used as a multiplier i.e. lambda=LambdaWeightPSlice * 2^((QP-12)/3) +LambdaWeightBSlice = 0.68 # scaling param for B slices. This will be used as a multiplier i.e. lambda=LambdaWeightBSlice * 2^((QP-12)/3) +LambdaWeightRefBSlice = 0.68 # scaling param for Referenced B slices. This will be used as a multiplier i.e. lambda=LambdaWeightRefBSlice * 2^((QP-12)/3) +LambdaWeightSPSlice = 0.68 # scaling param for SP slices. This will be used as a multiplier i.e. lambda=LambdaWeightSPSlice * 2^((QP-12)/3) +LambdaWeightSISlice = 0.65 # scaling param for SI slices. This will be used as a multiplier i.e. lambda=LambdaWeightSISlice * 2^((QP-12)/3) + +LossRateA = 5 # expected packet loss rate of the channel for the first partition, only valid if RDOptimization = 3 +LossRateB = 0 # expected packet loss rate of the channel for the second partition, only valid if RDOptimization = 3 +LossRateC = 0 # expected packet loss rate of the channel for the third partition, only valid if RDOptimization = 3 +FirstFrameCorrect = 0 # If 1, the first frame is encoded under the assumption that it is always correctly received. +NumberOfDecoders = 30 # Numbers of decoders used to simulate the channel, only valid if RDOptimization = 3 +RestrictRefFrames = 0 # Doesnt allow reference to areas that have been intra updated in a later frame. + +########################################################################################## +# Additional Stuff +######################################################################################### + +UseConstrainedIntraPred = 0 # If 1, Inter pixels are not used for Intra macroblock prediction. + +NumberofLeakyBuckets = 8 # Number of Leaky Bucket values +LeakyBucketRateFile = "leakybucketrate.cfg" # File from which encoder derives rate values +LeakyBucketParamFile = "leakybucketparam.cfg" # File where encoder stores leakybucketparams + +NumFramesInELayerSubSeq = 0 # number of frames in the Enhanced Scalability Layer(0: no Enhanced Layer) + +SparePictureOption = 0 # (0: no spare picture info, 1: spare picture available) +SparePictureDetectionThr = 6 # Threshold for spare reference pictures detection +SparePicturePercentageThr = 92 # Threshold for the spare macroblock percentage + +PicOrderCntType = 0 # (0: POC mode 0, 1: POC mode 1, 2: POC mode 2) + +######################################################################################## +#Rate control +######################################################################################## + +RateControlEnable = 0 # 0 Disable, 1 Enable +Bitrate = 45020 # Bitrate(bps) +InitialQP = 0 # Initial Quantization Parameter for the first I frame + # InitialQp depends on two values: Bits Per Picture, + # and the GOP length +BasicUnit = 0 # Number of MBs in the basic unit + # should be a fraction of the total number + # of MBs in a frame ("0" sets a BU equal to a frame) +ChannelType = 0 # type of channel( 1=time varying channel; 0=Constant channel) +RCUpdateMode = 0 # Rate Control type. Modes supported : + # 0 = original JM rate control, + # 1 = rate control that is applied to all frames regardless of the slice type, + # 2 = original plus intelligent QP selection for I and B slices (including Hierarchical), + # 3 = original + hybrid quadratic rate control for I and B slice using bit rate statistics + # +RCISliceBitRatio = 1.0 # target ratio of bits for I-coded pictures compared to P-coded Pictures (for RCUpdateMode=3) +RCBSliceBitRatio0 = 0.5 # target ratio of bits for B-coded pictures compared to P-coded Pictures - temporal level 0 (for RCUpdateMode=3) +RCBSliceBitRatio1 = 0.25 # target ratio of bits for B-coded pictures compared to P-coded Pictures - temporal level 1 (for RCUpdateMode=3) +RCBSliceBitRatio2 = 0.25 # target ratio of bits for B-coded pictures compared to P-coded Pictures - temporal level 2 (for RCUpdateMode=3) +RCBSliceBitRatio3 = 0.25 # target ratio of bits for B-coded pictures compared to P-coded Pictures - temporal level 3 (for RCUpdateMode=3) +RCBSliceBitRatio4 = 0.25 # target ratio of bits for B-coded pictures compared to P-coded Pictures - temporal level 4 (for RCUpdateMode=3) +RCBoverPRatio = 0.45 # ratio of bit rate usage of a B-coded picture over a P-coded picture for the SAME QP (for RCUpdateMode=3) +RCIoverPRatio = 3.80 # ratio of bit rate usage of an I-coded picture over a P-coded picture for the SAME QP (for RCUpdateMode=3) +RCMinQPPSlice = 8 # minimum P Slice QP value for rate control +RCMaxQPPSlice = 42 # maximum P Slice QP value for rate control +RCMinQPBSlice = 8 # minimum B Slice QP value for rate control +RCMaxQPBSlice = 42 # maximum B Slice QP value for rate control +RCMinQPISlice = 8 # minimum I Slice QP value for rate control +RCMaxQPISlice = 42 # maximum I Slice QP value for rate control +RCMinQPSPSlice = 8 # minimum SP Slice QP value for rate control +RCMaxQPSPSlice = 40 # maximum SP Slice QP value for rate control +RCMinQPSISlice = 8 # minimum SI Slice QP value for rate control +RCMaxQPSISlice = 42 # maximum SI Slice QP value for rate control +RCMaxQPChange = 4 # maximum QP change for frames of the base layer + +######################################################################################## +#Fast Mode Decision +######################################################################################## +EarlySkipEnable = 0 # Early skip detection (0: Disable 1: Enable) +SelectiveIntraEnable = 0 # Selective Intra mode decision (0: Disable 1: Enable) + +######################################################################################## +#FREXT stuff +######################################################################################## + +YUVFormat = 1 # YUV format (0=4:0:0, 1=4:2:0, 2=4:2:2, 3=4:4:4) +RGBInput = 0 # 1=RGB input, 0=GBR or YUV input +SeparateColourPlane = 0 # 4:4:4 coding: 0=Common mode, 1=Independent mode +SourceBitDepthLuma = 8 # Source Bit Depth for Luma color component (8...14 bits) +SourceBitDepthChroma = 8 # Source Bit Depth for Chroma color components (8...14 bits) +SourceBitDepthRescale = 0 # Rescale bit depth of source for output (0: Disable 1: Enable) +OutputBitDepthLuma = 8 # Output Bit Depth for Luma color component (8...14 bits) +OutputBitDepthChroma = 8 # Output Bit Depth for Chroma color components (8...14 bits) + +CbQPOffset = 0 # Chroma QP offset for Cb-part (-51..51) +CrQPOffset = 0 # Chroma QP offset for Cr-part (-51..51) +Transform8x8Mode = 1 # (0: only 4x4 transform, 1: allow using 8x8 transform additionally, 2: only 8x8 transform) +ReportFrameStats = 0 # (0:Disable Frame Statistics 1: Enable) +DisplayEncParams = 0 # (0:Disable Display of Encoder Params 1: Enable) +Verbose = 1 # level of display verboseness + # 0: short, 1: normal (default), 2: detailed, 3: detailed/nvb +SkipGlobalStats = 0 # Disable global stat accumulation (Set to 1 to avoid bipred core dump) + +######################################################################################## +#Q-Matrix (FREXT) +######################################################################################## +QmatrixFile = "q_matrix.cfg" + +ScalingMatrixPresentFlag = 0 # Enable Q_Matrix (0 Not present, 1 Present in SPS, 2 Present in PPS, 3 Present in both SPS & PPS) +ScalingListPresentFlag0 = 3 # Intra4x4_Luma (0 Not present, 1 Present in SPS, 2 Present in PPS, 3 Present in both SPS & PPS) +ScalingListPresentFlag1 = 3 # Intra4x4_ChromaU (0 Not present, 1 Present in SPS, 2 Present in PPS, 3 Present in both SPS & PPS) +ScalingListPresentFlag2 = 3 # Intra4x4_chromaV (0 Not present, 1 Present in SPS, 2 Present in PPS, 3 Present in both SPS & PPS) +ScalingListPresentFlag3 = 3 # Inter4x4_Luma (0 Not present, 1 Present in SPS, 2 Present in PPS, 3 Present in both SPS & PPS) +ScalingListPresentFlag4 = 3 # Inter4x4_ChromaU (0 Not present, 1 Present in SPS, 2 Present in PPS, 3 Present in both SPS & PPS) +ScalingListPresentFlag5 = 3 # Inter4x4_ChromaV (0 Not present, 1 Present in SPS, 2 Present in PPS, 3 Present in both SPS & PPS) +ScalingListPresentFlag6 = 3 # Intra8x8_Luma (0 Not present, 1 Present in SPS, 2 Present in PPS, 3 Present in both SPS & PPS) +ScalingListPresentFlag7 = 3 # Inter8x8_Luma (0 Not present, 1 Present in SPS, 2 Present in PPS, 3 Present in both SPS & PPS) +ScalingListPresentFlag8 = 3 # Intra8x8_ChromaU for 4:4:4 (0 Not present, 1 Present in SPS, 2 Present in PPS, 3 Present in both SPS & PPS) +ScalingListPresentFlag9 = 3 # Inter8x8_ChromaU for 4:4:4 (0 Not present, 1 Present in SPS, 2 Present in PPS, 3 Present in both SPS & PPS) +ScalingListPresentFlag10 = 3 # Intra8x8_ChromaV for 4:4:4 (0 Not present, 1 Present in SPS, 2 Present in PPS, 3 Present in both SPS & PPS) +ScalingListPresentFlag11 = 3 # Inter8x8_ChromaV for 4:4:4 (0 Not present, 1 Present in SPS, 2 Present in PPS, 3 Present in both SPS & PPS) + +######################################################################################## +#Rounding Offset control +######################################################################################## + +OffsetMatrixPresentFlag = 0 # Enable Explicit Offset Quantization Matrices (0: disable 1: enable) +QOffsetMatrixFile = "q_offset.cfg" # Explicit Quantization Matrices file + +AdaptiveRounding = 1 # Enable Adaptive Rounding based on JVT-N011 (0: disable, 1: enable) +AdaptRoundingFixed = 0 # Enable Global Adaptive rounding for all qps (0: disable, 1: enable - default/old) +AdaptRndPeriod = 16 # Period in terms of MBs for updating rounding offsets. + # 0 performs update at the picture level. Default is 16. 1 is as in JVT-N011. +AdaptRndChroma = 1 # Enables coefficient rounding adaptation for chroma + +AdaptRndWFactorIRef = 8 # Adaptive Rounding Weight for I/SI slices in reference pictures /4096 +AdaptRndWFactorPRef = 8 # Adaptive Rounding Weight for P/SP slices in reference pictures /4096 +AdaptRndWFactorBRef = 8 # Adaptive Rounding Weight for B slices in reference pictures /4096 +AdaptRndWFactorINRef = 8 # Adaptive Rounding Weight for I/SI slices in non reference pictures /4096 +AdaptRndWFactorPNRef = 8 # Adaptive Rounding Weight for P/SP slices in non reference pictures /4096 +AdaptRndWFactorBNRef = 8 # Adaptive Rounding Weight for B slices in non reference pictures /4096 + +AdaptRndCrWFactorIRef = 8 # Chroma Adaptive Rounding Weight for I/SI slices in reference pictures /4096 +AdaptRndCrWFactorPRef = 8 # Chroma Adaptive Rounding Weight for P/SP slices in reference pictures /4096 +AdaptRndCrWFactorBRef = 8 # Chroma Adaptive Rounding Weight for B slices in reference pictures /4096 +AdaptRndCrWFactorINRef = 8 # Chroma Adaptive Rounding Weight for I/SI slices in non reference pictures /4096 +AdaptRndCrWFactorPNRef = 8 # Chroma Adaptive Rounding Weight for P/SP slices in non reference pictures /4096 +AdaptRndCrWFactorBNRef = 8 # Chroma Adaptive Rounding Weight for B slices in non reference pictures /4096 + +################################################################ +# Rate Distortion Optimized Quantization +################################################################ +UseRDOQuant = 1 # Use Rate Distortion Optimized Quantization (0=disable, 1=enable) +RDOQ_DC = 1 # Enable Rate Distortion Optimized Quantization for DC components (0=disable, 1=enable) +RDOQ_CR = 1 # Enable Rate Distortion Optimized Quantization for Chroma components (0=disable, 1=enable) +RDOQ_DC_CR = 1 # Enable Rate Distortion Optimized Quantization for Chroma DC components (0=disable, 1=enable) +RDOQ_QP_Num = 5 # 1-9: Number of QP tested in RDO_Q (I/P/B slice) +RDOQ_CP_Mode = 0 # copy Mode from first QP tested +RDOQ_CP_MV = 0 # copy MV from first QP tested +RDOQ_Fast = 0 # Fast RDOQ decision method for multiple QPs + +######################################################################################## +#Lossless Coding (FREXT) +######################################################################################## + +LosslessCoding = 0 # Enable lossless coding when qpprime_y is zero (0 Disabled, 1 Enabled) + +######################################################################################## +#Fast Motion Estimation Control Parameters +######################################################################################## + +SearchMode = 0 # Motion estimation mode + # -1 = Full Search + # 0 = Fast Full Search (default) + # 1 = UMHexagon Search + # 2 = Simplified UMHexagon Search + # 3 = Enhanced Predictive Zonal Search (EPZS) + +UMHexDSR = 1 # Use Search Range Prediction. Only for UMHexagonS method + # (0:disable, 1:enabled/default) +UMHexScale = 3 # Use Scale_factor for different image sizes. Only for UMHexagonS method + # (0:disable, 3:/default) + # Increasing value can speed up Motion Search. + +EPZSPattern = 2 # Select EPZS primary refinement pattern. + # (0: small diamond, 1: square, 2: extended diamond/default, + # 3: large diamond, 4: SBP Large Diamond, + # 5: PMVFAST ) +EPZSDualRefinement = 3 # Enables secondary refinement pattern. + # (0:disabled, 1: small diamond, 2: square, + # 3: extended diamond/default, 4: large diamond, + # 5: SBP Large Diamond, 6: PMVFAST ) +EPZSFixedPredictors = 3 # Enables Window based predictors + # (0:disabled, 1: P only, 2: P and B, 3: Pic Boundary + P and B/default) +EPZSTemporal = 1 # Enables temporal predictors + # (0: disabled, 1: enabled/default) +EPZSSpatialMem = 1 # Enables spatial memory predictors + # (0: disabled, 1: enabled/default) +EPZSBlockType = 1 # Enables block type Predictors + # (0: disabled, 1: enabled/default) +EPZSMinThresScale = 0 # Scaler for EPZS minimum threshold (0 default). + # Increasing value can speed up encoding. +EPZSMedThresScale = 1 # Scaler for EPZS median threshold (1 default). + # Increasing value can speed up encoding. +EPZSMaxThresScale = 2 # Scaler for EPZS maximum threshold (1 default). + # Increasing value can speed up encoding. + +EPZSSubPelME = 1 # EPZS Subpel ME consideration +EPZSSubPelMEBiPred = 1 # EPZS Subpel ME consideration for BiPred partitions +EPZSSubPelThresScale = 1 # EPZS Subpel ME Threshold scaler +EPZSSubPelGrid = 1 # Perform EPZS using a subpixel grid + +######################################################################################## +# SEI Parameters +######################################################################################## + +ToneMappingSEIPresentFlag = 0 # Enable Tone mapping SEI (0 Not present, 1 Present) +ToneMappingFile = "ToneMapping.cfg" + +GenerateSEIMessage = 0 # Generate an SEI Text Message +SEIMessageText = "H.264/AVC Encoder" # Text SEI Message + +UseMVLimits = 0 # Use MV Limits +SetMVXLimit = 512 # Horizontal MV Limit (in integer units) +SetMVYLimit = 512 # Vertical MV Limit (in integer units) + +######################################################################################## +# VUI Parameters +######################################################################################## +# the variables below do not affect encoding and decoding +# (many are dummy variables but others can be useful when supported by the decoder) + +EnableVUISupport = 0 # Enable VUI Parameters + +# display parameters +VUI_aspect_ratio_info_present_flag = 0 +VUI_aspect_ratio_idc = 1 +VUI_sar_width = 0 +VUI_sar_height = 0 +VUI_overscan_info_present_flag = 0 +VUI_overscan_appropriate_flag = 0 +VUI_video_signal_type_present_flag = 0 +VUI_video_format = 5 +VUI_video_full_range_flag = 0 +VUI_colour_description_present_flag = 0 +VUI_colour_primaries = 2 +VUI_transfer_characteristics = 2 +VUI_matrix_coefficients = 2 +VUI_chroma_location_info_present_flag = 0 +VUI_chroma_sample_loc_type_top_field = 0 +VUI_chroma_sample_loc_type_bottom_field = 0 +VUI_timing_info_present_flag = 0 +VUI_num_units_in_tick = 1000 +VUI_time_scale = 60000 +VUI_fixed_frame_rate_flag = 0 + +# nal hrd parameters +VUI_nal_hrd_parameters_present_flag = 0 +VUI_nal_cpb_cnt_minus1 = 0 +VUI_nal_bit_rate_scale = 0 +VUI_nal_cpb_size_scale = 0 +VUI_nal_bit_rate_value_minus1 = 0 +VUI_nal_cpb_size_value_minus1 = 0 +VUI_nal_vbr_cbr_flag = 0 +VUI_nal_initial_cpb_removal_delay_length_minus1 = 23 +VUI_nal_cpb_removal_delay_length_minus1 = 23 +VUI_nal_dpb_output_delay_length_minus1 = 23 +VUI_nal_time_offset_length = 24 + +# vlc hrd parameters +VUI_vcl_hrd_parameters_present_flag = 0 +VUI_vcl_cpb_cnt_minus1 = 0 +VUI_vcl_bit_rate_scale = 0 +VUI_vcl_cpb_size_scale = 0 +VUI_vcl_bit_rate_value_minus1 = 0 +VUI_vcl_cpb_size_value_minus1 = 0 +VUI_vcl_vbr_cbr_flag = 0 +VUI_vcl_initial_cpb_removal_delay_length_minus1 = 23 +VUI_vcl_cpb_removal_delay_length_minus1 = 23 +VUI_vcl_dpb_output_delay_length_minus1 = 23 +VUI_vcl_time_offset_length = 24 +VUI_low_delay_hrd_flag = 0 + +# other parameters (i.e. bitsream restrictions) +VUI_pic_struct_present_flag = 0 +VUI_bitstream_restriction_flag = 0 +VUI_motion_vectors_over_pic_boundaries_flag = 1 +VUI_max_bytes_per_pic_denom = 0 +VUI_max_bits_per_mb_denom = 0 +VUI_log2_max_mv_length_vertical = 16 +VUI_log2_max_mv_length_horizontal = 16 +VUI_num_reorder_frames = 16 +VUI_max_dec_frame_buffering = 16 + diff --git a/bin/encoder_baseline.cfg b/bin/encoder_baseline.cfg new file mode 100644 index 0000000..e7c6e6d --- /dev/null +++ b/bin/encoder_baseline.cfg @@ -0,0 +1,376 @@ +# New Input File Format is as follows +# = # Comment +# +# See configfile.h for a list of supported ParameterNames +# +# For bug reporting and known issues see: +# https://ipbt.hhi.fraunhofer.de + + +########################################################################################## +# Files +########################################################################################## +InputFile = "foreman_part_qcif.yuv" # Input sequence +InputHeaderLength = 0 # If the inputfile has a header, state it's length in byte here +StartFrame = 0 # Start frame for encoding. (0-N) +FramesToBeEncoded = 3 # Number of frames to be coded +FrameRate = 30.0 # Frame Rate per second (0.1-100.0) +SourceWidth = 176 # Source frame width +SourceHeight = 144 # Source frame height +SourceResize = 0 # Resize source size for output +OutputWidth = 176 # Output frame width +OutputHeight = 144 # Output frame height + +TraceFile = "trace_enc.txt" # Trace file +ReconFile = "test_rec.yuv" # Reconstruction YUV file +OutputFile = "test.264" # Bitstream +StatsFile = "stats.dat" # Coding statistics file + +########################################################################################## +# Encoder Control +########################################################################################## +ProfileIDC = 66 # Profile IDC (66=baseline, 77=main, 88=extended; FREXT Profiles: 100=High, 110=High 10, 122=High 4:2:2, 244=High 4:4:4, 44=CAVLC 4:4:4 Intra) +IntraProfile = 0 # Activate Intra Profile for FRExt (0: false, 1: true) + # (e.g. ProfileIDC=110, IntraProfile=1 => High 10 Intra Profile) +LevelIDC = 40 # Level IDC (e.g. 20 = level 2.0) + +IntraPeriod = 0 # Period of I-pictures (0=only first) +IDRPeriod = 0 # Period of IDR pictures (0=only first) +AdaptiveIntraPeriod = 1 # Adaptive intra period +AdaptiveIDRPeriod = 0 # Adaptive IDR period +IntraDelay = 0 # Intra (IDR) picture delay (i.e. coding structure of PPIPPP... ) +EnableIDRGOP = 0 # Support for IDR closed GOPs (0: disabled, 1: enabled) +EnableOpenGOP = 0 # Support for open GOPs (0: disabled, 1: enabled) +QPISlice = 28 # Quant. param for I Slices (0-51) +QPPSlice = 28 # Quant. param for P Slices (0-51) +FrameSkip = 0 # Number of frames to be skipped in input (e.g 2 will code every third frame). + # Note that this now excludes intermediate (i.e. B) coded pictures +ChromaQPOffset = 0 # Chroma QP offset (-51..51) + +DisableSubpelME = 0 # Disable Subpixel Motion Estimation (0=off/default, 1=on) +SearchRange = 32 # Max search range + +MEDistortionFPel = 0 # Select error metric for Full-Pel ME (0: SAD, 1: SSE, 2: Hadamard SAD) +MEDistortionHPel = 2 # Select error metric for Half-Pel ME (0: SAD, 1: SSE, 2: Hadamard SAD) +MEDistortionQPel = 2 # Select error metric for Quarter-Pel ME (0: SAD, 1: SSE, 2: Hadamard SAD) +MDDistortion = 2 # Select error metric for Mode Decision (0: SAD, 1: SSE, 2: Hadamard SAD) +SkipDeBlockNonRef = 0 # Skip Deblocking (regardless of DFParametersFlag) for non-reference frames (0: off, 1: on) +ChromaMCBuffer = 1 # Calculate Color component interpolated values in advance and store them. + # Provides a trade-off between memory and computational complexity + # (0: disabled/default, 1: enabled) +ChromaMEEnable = 0 # Take into account Color component information during ME + # (0: only first component/default, + # 1: All Color components - Integer refinement only + # 2: All Color components - All refinements) +ChromaMEWeight = 1 # Weighting for chroma components. This parameter should have a relationship with color format. + +NumberReferenceFrames = 5 # Number of previous frames used for inter motion search (0-16) + +PList0References = 0 # P slice List 0 reference override (0 disable, N <= NumberReferenceFrames) +Log2MaxFNumMinus4 = 0 # Sets log2_max_frame_num_minus4 (-1 : based on FramesToBeEncoded/Auto, >=0 : Log2MaxFNumMinus4) +Log2MaxPOCLsbMinus4 = -1 # Sets log2_max_pic_order_cnt_lsb_minus4 (-1 : Auto, >=0 : Log2MaxPOCLsbMinus4) + +GenerateMultiplePPS = 0 # Transmit multiple parameter sets. Currently parameters basically enable all WP modes (0: diabled, 1: enabled) +SendAUD = 0 # Send Access Delimiter Unit NALU (for every access unit) +ResendSPS = 2 # Resend SPS (0: disabled, 1: all Intra pictures, 2: only for IDR, 3: for IDR and OpenGOP I) +ResendPPS = 0 # Resend PPS (with pic_parameter_set_id 0) for every coded Frame/Field pair (0: disabled, 1: enabled) + +MbLineIntraUpdate = 0 # Error robustness(extra intra macro block updates)(0=off, N: One GOB every N frames are intra coded) +RandomIntraMBRefresh = 0 # Forced intra MBs per picture + +########################################################################################## +# PSlice Mode types +########################################################################################## +PSliceSkip = 1 # P-Slice Skip mode consideration (0=disable, 1=enable) +PSliceSearch16x16 = 1 # P-Slice Inter block search 16x16 (0=disable, 1=enable) +PSliceSearch16x8 = 1 # P-Slice Inter block search 16x8 (0=disable, 1=enable) +PSliceSearch8x16 = 1 # P-Slice Inter block search 8x16 (0=disable, 1=enable) +PSliceSearch8x8 = 1 # P-Slice Inter block search 8x8 (0=disable, 1=enable) +PSliceSearch8x4 = 1 # P-Slice Inter block search 8x4 (0=disable, 1=enable) +PSliceSearch4x8 = 1 # P-Slice Inter block search 4x8 (0=disable, 1=enable) +PSliceSearch4x4 = 1 # P-Slice Inter block search 4x4 (0=disable, 1=enable) + +DisableIntra4x4 = 0 # Disable Intra 4x4 modes +DisableIntra16x16 = 0 # Disable Intra 16x16 modes +DisableIntraInInter = 0 # Disable Intra modes for inter slices +IntraDisableInterOnly = 0 # Apply Disabling Intra conditions only to Inter Slices (0:disable/default,1: enable) +Intra4x4ParDisable = 0 # Disable Vertical & Horizontal 4x4 +Intra4x4DiagDisable = 0 # Disable Diagonal 45degree 4x4 +Intra4x4DirDisable = 0 # Disable Other Diagonal 4x4 +Intra16x16ParDisable = 0 # Disable Vertical & Horizontal 16x16 +Intra16x16PlaneDisable = 0 # Disable Planar 16x16 +ChromaIntraDisable = 0 # Disable Intra Chroma modes other than DC +EnableIPCM = 1 # Enable IPCM macroblock mode + +DisposableP = 0 # Enable Disposable P slices in the primary layer (0: disable/default, 1: enable) +DispPQPOffset = 0 # Quantizer offset for disposable P slices (0: default) + +PreferDispOrder = 1 # Prefer display order when building the prediction structure as opposed to coding order (affects intra and IDR periodic insertion, among others) +PreferPowerOfTwo = 0 # Prefer prediction structures that have lengths expressed as powers of two +FrmStructBufferLength = 16 # Length of the frame structure unit buffer; it can be overriden for certain cases + +ChangeQPFrame = 0 # Frame in display order from which to apply the Change QP offsets +ChangeQPI = 0 # Change QP offset value for I_SLICE +ChangeQPP = 0 # Change QP offset value for P_SLICE +ChangeQPB = 0 # Change QP offset value for B_SLICE +ChangeQPSI = 0 # Change QP offset value for SI_SLICE +ChangeQPSP = 0 # Change QP offset value for SP_SLICE +########################################################################################## +# Output Control, NALs +########################################################################################## + +OutFileMode = 0 # Output file mode, 0:Annex B, 1:RTP + +########################################################################################## +# Picture based Multi-pass encoding +######################################################################################### + +RDPictureDecision = 0 # Perform multiple pass coding and make RD optimal decision among them +RDPSliceBTest = 0 # Perform Slice level RD decision between P and B slices. +RDPictureMaxPassISlice = 1 # Max number of coding passes for I slices, valid values [1,3], default is 1 +RDPictureMaxPassPSlice = 2 # Max number of coding passes for P slices, valid values [1,6], default is 2 +RDPictureMaxPassBSlice = 3 # Max number of coding passes for B slices, valid values [1,6], default is 3 +RDPictureFrameQPPSlice = 0 # Perform additional frame level QP check (QP+/-1) for P slices, 0: disabled (default), 1: enabled +RDPictureFrameQPBSlice = 0 # Perform additional frame level QP check (QP+/-1) for B slices, 0: disabled, 1: enabled (default) +RDPictureDeblocking = 0 # Perform another coding pass to check non-deblocked picture, 0: disabled (default), 1: enabled +RDPictureDirectMode = 0 # Perform another coding pass to check the alternative direct mode for B slices, , 0: disabled (default), 1: enabled + +########################################################################################## +# Deblocking filter parameters +########################################################################################## + +DFParametersFlag = 0 # Configure deblocking filter (0=parameters below ignored, 1=parameters sent) + # Note that for pictures with multiple slice types, + # only the type of the first slice will be considered. +DFDisableRefISlice = 0 # Disable deblocking filter in reference I coded pictures (0=Filter, 1=No Filter). +DFAlphaRefISlice = 0 # Reference I coded pictures Alpha offset div. 2, {-6, -5, ... 0, +1, .. +6} +DFBetaRefISlice = 0 # Reference I coded pictures Beta offset div. 2, {-6, -5, ... 0, +1, .. +6} +DFDisableNRefISlice = 0 # Disable deblocking filter in non reference I coded pictures (0=Filter, 1=No Filter). +DFAlphaNRefISlice = 0 # Non Reference I coded pictures Alpha offset div. 2, {-6, -5, ... 0, +1, .. +6} +DFBetaNRefISlice = 0 # Non Reference I coded pictures Beta offset div. 2, {-6, -5, ... 0, +1, .. +6} +DFDisableRefPSlice = 0 # Disable deblocking filter in reference P coded pictures (0=Filter, 1=No Filter). +DFAlphaRefPSlice = 0 # Reference P coded pictures Alpha offset div. 2, {-6, -5, ... 0, +1, .. +6} +DFBetaRefPSlice = 0 # Reference P coded pictures Beta offset div. 2, {-6, -5, ... 0, +1, .. +6} +DFDisableNRefPSlice = 0 # Disable deblocking filter in non reference P coded pictures (0=Filter, 1=No Filter). +DFAlphaNRefPSlice = 0 # Non Reference P coded pictures Alpha offset div. 2, {-6, -5, ... 0, +1, .. +6} +DFBetaNRefPSlice = 0 # Non Reference P coded pictures Beta offset div. 2, {-6, -5, ... 0, +1, .. +6} + +########################################################################################## +# Error Resilience / Slices +########################################################################################## + +SliceMode = 0 # Slice mode (0=off 1=fixed #mb in slice 2=fixed #bytes in slice 3=use callback) +SliceArgument = 50 # Slice argument (Arguments to modes 1 and 2 above) + +num_slice_groups_minus1 = 0 # Number of Slice Groups Minus 1, 0 == no FMO, 1 == two slice groups, etc. +slice_group_map_type = 0 # 0: Interleave, 1: Dispersed, 2: Foreground with left-over, + # 3: Box-out, 4: Raster Scan 5: Wipe + # 6: Explicit, slice_group_id read from SliceGroupConfigFileName +slice_group_change_direction_flag = 0 # 0: box-out clockwise, raster scan or wipe right, + # 1: box-out counter clockwise, reverse raster scan or wipe left +slice_group_change_rate_minus1 = 85 # +SliceGroupConfigFileName = "sg0conf.cfg" # Used for slice_group_map_type 0, 2, 6 + +UseRedundantPicture = 0 # 0: not used, 1: enabled +NumRedundantHierarchy = 1 # 0-4 +PrimaryGOPLength = 10 # GOP length for redundant allocation (1-16) + # NumberReferenceFrames must be no less than PrimaryGOPLength when redundant slice enabled +NumRefPrimary = 1 # Actually used number of references for primary slices (1-16) + +########################################################################################## +# Search Range Restriction / RD Optimization +########################################################################################## + +RestrictSearchRange = 2 # restriction for (0: blocks and ref, 1: ref, 2: no restrictions) +RDOptimization = 1 # rd-optimized mode decision + # 0: RD-off (Low complexity mode) + # 1: RD-on (High complexity mode) + # 2: RD-on (Fast high complexity mode - not work in FREX Profiles) + # 3: with losses +I16RDOpt = 0 # perform rd-optimized mode decision for Intra 16x16 MB + # 0: SAD-based mode decision for Intra 16x16 MB + # 1: RD-based mode decision for Intra 16x16 MB +SubMBCodingState = 1 # submacroblock coding state + # 0: lowest complexity, do not store or reset coding state during sub-MB mode decision + # 1: medium complexity, reset to master coding state (for current mode) during sub-MB mode decision + # 2: highest complexity, store and reset coding state during sub-MB mode decision +DistortionSSIM = 0 # Compute SSIM distortion. (0: disabled/default, 1: enabled) +DistortionMS_SSIM = 0 # Compute Multiscale SSIM distortion. (0: disabled/default, 1: enabled) +SSIMOverlapSize = 8 # Overlap size to calculate SSIM distortion (1: pixel by pixel, 8: no overlap) +DistortionYUVtoRGB = 0 # Calculate distortion in RGB domain after conversion from YCbCr (0:off, 1:on) +CtxAdptLagrangeMult = 0 # Context Adaptive Lagrange Multiplier + # 0: disabled (default) + # 1: enabled (works best when RDOptimization=0) +FastCrIntraDecision = 1 # Fast Chroma intra mode decision (0:off, 1:on) +DisableThresholding = 0 # Disable Thresholding of Transform Coefficients (0:off, 1:on) +SkipIntraInInterSlices = 0 # Skips Intra mode checking in inter slices if certain mode decisions are satisfied (0: off, 1: on) +WeightY = 1 # Luma weight for RDO +WeightCb = 1 # Cb weight for RDO +WeightCr = 1 # Cr weight for RDO + +########################################################################################## +# Explicit Lambda Usage +########################################################################################## +UseExplicitLambdaParams = 0 # Use explicit lambda scaling parameters (0:disabled, 1:enable lambda weight, 2: use explicit lambda value) +UpdateLambdaChromaME = 0 # Update lambda given Chroma ME consideration +FixedLambdaISlice = 0.1 # Fixed Lambda value for I slices +FixedLambdaPSlice = 0.1 # Fixed Lambda value for P slices + +LambdaWeightISlice = 0.65 # scaling param for I slices. This will be used as a multiplier i.e. lambda=LambdaWeightISlice * 2^((QP-12)/3) +LambdaWeightPSlice = 0.68 # scaling param for P slices. This will be used as a multiplier i.e. lambda=LambdaWeightPSlice * 2^((QP-12)/3) + +LossRateA = 5 # expected packet loss rate of the channel for the first partition, only valid if RDOptimization = 3 +LossRateB = 0 # expected packet loss rate of the channel for the second partition, only valid if RDOptimization = 3 +LossRateC = 0 # expected packet loss rate of the channel for the third partition, only valid if RDOptimization = 3 +FirstFrameCorrect = 0 # If 1, the first frame is encoded under the assumption that it is always correctly received. +NumberOfDecoders = 30 # Numbers of decoders used to simulate the channel, only valid if RDOptimization = 3 +RestrictRefFrames = 0 # Doesnt allow reference to areas that have been intra updated in a later frame. + +########################################################################################## +# Additional Stuff +######################################################################################### + +UseConstrainedIntraPred = 0 # If 1, Inter pixels are not used for Intra macroblock prediction. + +NumberofLeakyBuckets = 8 # Number of Leaky Bucket values +LeakyBucketRateFile = "leakybucketrate.cfg" # File from which encoder derives rate values +LeakyBucketParamFile = "leakybucketparam.cfg" # File where encoder stores leakybucketparams + +NumFramesInELayerSubSeq = 0 # number of frames in the Enhanced Scalability Layer(0: no Enhanced Layer) + +SparePictureOption = 0 # (0: no spare picture info, 1: spare picture available) +SparePictureDetectionThr = 6 # Threshold for spare reference pictures detection +SparePicturePercentageThr = 92 # Threshold for the spare macroblock percentage + +PicOrderCntType = 0 # (0: POC mode 0, 1: POC mode 1, 2: POC mode 2) + +######################################################################################## +#Rate control +######################################################################################## + +RateControlEnable = 0 # 0 Disable, 1 Enable +Bitrate = 45020 # Bitrate(bps) +InitialQP = 0 # Initial Quantization Parameter for the first I frame + # InitialQp depends on two values: Bits Per Picture, + # and the GOP length +BasicUnit = 0 # Number of MBs in the basic unit + # should be a fraction of the total number + # of MBs in a frame ("0" sets a BU equal to a frame) +ChannelType = 0 # type of channel( 1=time varying channel; 0=Constant channel) +RCUpdateMode = 0 # Rate Control type. Modes supported : + # 0 = original JM rate control, + # 1 = rate control that is applied to all frames regardless of the slice type, + # 2 = original plus intelligent QP selection for I and B slices (including Hierarchical), + # 3 = original + hybrid quadratic rate control for I and B slice using bit rate statistics + # +RCISliceBitRatio = 1.0 # target ratio of bits for I-coded pictures compared to P-coded Pictures (for RCUpdateMode=3) +RCBSliceBitRatio0 = 0.5 # target ratio of bits for B-coded pictures compared to P-coded Pictures - temporal level 0 (for RCUpdateMode=3) +RCBSliceBitRatio1 = 0.25 # target ratio of bits for B-coded pictures compared to P-coded Pictures - temporal level 1 (for RCUpdateMode=3) +RCBSliceBitRatio2 = 0.25 # target ratio of bits for B-coded pictures compared to P-coded Pictures - temporal level 2 (for RCUpdateMode=3) +RCBSliceBitRatio3 = 0.25 # target ratio of bits for B-coded pictures compared to P-coded Pictures - temporal level 3 (for RCUpdateMode=3) +RCBSliceBitRatio4 = 0.25 # target ratio of bits for B-coded pictures compared to P-coded Pictures - temporal level 4 (for RCUpdateMode=3) +RCBoverPRatio = 0.45 # ratio of bit rate usage of a B-coded picture over a P-coded picture for the SAME QP (for RCUpdateMode=3) +RCIoverPRatio = 3.80 # ratio of bit rate usage of an I-coded picture over a P-coded picture for the SAME QP (for RCUpdateMode=3) +RCMinQPPSlice = 8 # minimum P Slice QP value for rate control +RCMaxQPPSlice = 44 # maximum P Slice QP value for rate control +RCMinQPISlice = 8 # minimum I Slice QP value for rate control +RCMaxQPISlice = 36 # maximum I Slice QP value for rate control + +######################################################################################## +#Fast Mode Decision +######################################################################################## +EarlySkipEnable = 0 # Early skip detection (0: Disable 1: Enable) +SelectiveIntraEnable = 0 # Selective Intra mode decision (0: Disable 1: Enable) + +ReportFrameStats = 0 # (0:Disable Frame Statistics 1: Enable) +DisplayEncParams = 0 # (0:Disable Display of Encoder Params 1: Enable) +Verbose = 1 # level of display verboseness + # 0: short, 1: normal (default), 2: detailed, 3: detailed/nvb + # 4: with additional MB level lambda info + +######################################################################################## +#Rounding Offset control +######################################################################################## + +OffsetMatrixPresentFlag = 0 # Enable Explicit Offset Quantization Matrices (0: disable 1: enable) +QOffsetMatrixFile = "q_offset.cfg" # Explicit Quantization Matrices file + +AdaptiveRounding = 1 # Enable Adaptive Rounding based on JVT-N011 (0: disable, 1: enable) +AdaptRoundingFixed = 1 # Enable Global Adaptive rounding for all qps (0: disable, 1: enable - default/old) +AdaptRndPeriod = 16 # Period in terms of MBs for updating rounding offsets. + # 0 performs update at the picture level. Default is 16. 1 is as in JVT-N011. +AdaptRndChroma = 1 # Enables coefficient rounding adaptation for chroma + +AdaptRndWFactorIRef = 4 # Adaptive Rounding Weight for I/SI slices in reference pictures /4096 +AdaptRndWFactorPRef = 4 # Adaptive Rounding Weight for P/SP slices in reference pictures /4096 +AdaptRndWFactorINRef = 4 # Adaptive Rounding Weight for I/SI slices in non reference pictures /4096 +AdaptRndWFactorPNRef = 4 # Adaptive Rounding Weight for P/SP slices in non reference pictures /4096 + +AdaptRndCrWFactorIRef = 4 # Chroma Adaptive Rounding Weight for I/SI slices in reference pictures /4096 +AdaptRndCrWFactorPRef = 4 # Chroma Adaptive Rounding Weight for P/SP slices in reference pictures /4096 +AdaptRndCrWFactorINRef = 4 # Chroma Adaptive Rounding Weight for I/SI slices in non reference pictures /4096 +AdaptRndCrWFactorPNRef = 4 # Chroma Adaptive Rounding Weight for P/SP slices in non reference pictures /4096 + +######################################################################################## +#Fast Motion Estimation Control Parameters +######################################################################################## + +SearchMode = 0 # Motion estimation mode + # -1 = Full Search + # 0 = Fast Full Search (default) + # 1 = UMHexagon Search + # 2 = Simplified UMHexagon Search + # 3 = Enhanced Predictive Zonal Search (EPZS) + +UMHexDSR = 1 # Use Search Range Prediction. Only for UMHexagonS method + # (0:disable, 1:enabled/default) +UMHexScale = 3 # Use Scale_factor for different image sizes. Only for UMHexagonS method + # (0:disable, 3:/default) + # Increasing value can speed up Motion Search. + +EPZSPattern = 2 # Select EPZS primary refinement pattern. + # (0: small diamond, 1: square, 2: extended diamond/default, + # 3: large diamond, 4: SBP Large Diamond, + # 5: PMVFAST ) +EPZSDualRefinement = 3 # Enables secondary refinement pattern. + # (0:disabled, 1: small diamond, 2: square, + # 3: extended diamond/default, 4: large diamond, + # 5: SBP Large Diamond, 6: PMVFAST ) +EPZSFixedPredictors = 2 # Enables Window based predictors + # (0:disabled, 1: P only, 2: P and B/default) +EPZSTemporal = 1 # Enables temporal predictors + # (0: disabled, 1: enabled/default) +EPZSSpatialMem = 1 # Enables spatial memory predictors + # (0: disabled, 1: enabled/default) +EPZSBlockType = 1 # Enables block type Predictors + # (0: disabled, 1: enabled/default) +EPZSMinThresScale = 0 # Scaler for EPZS minimum threshold (0 default). + # Increasing value can speed up encoding. +EPZSMedThresScale = 1 # Scaler for EPZS median threshold (1 default). + # Increasing value can speed up encoding. +EPZSMaxThresScale = 2 # Scaler for EPZS maximum threshold (1 default). + # Increasing value can speed up encoding. +EPZSSubPelME = 1 # EPZS Subpel ME consideration +EPZSSubPelMEBiPred = 1 # EPZS Subpel ME consideration for BiPred partitions +EPZSSubPelThresScale = 2 # EPZS Subpel ME Threshold scaler +EPZSSubPelGrid = 1 # Perform EPZS using a subpixel grid + +######################################################################################## +# SEI Parameters +######################################################################################## + +GenerateSEIMessage = 0 # Generate an SEI Text Message +SEIMessageText = "H.264/AVC Encoder" # Text SEI Message + +UseMVLimits = 0 # Use MV Limits +SetMVXLimit = 512 # Horizontal MV Limit (in integer units) +SetMVYLimit = 512 # Vertical MV Limit (in integer units) + +######################################################################################## +# VUI Parameters +######################################################################################## +# the variables below do not affect encoding and decoding +# (many are dummy variables but others can be useful when supported by the decoder) + +EnableVUISupport = 0 # Enable VUI Parameters + diff --git a/bin/encoder_extended.cfg b/bin/encoder_extended.cfg new file mode 100644 index 0000000..088397c --- /dev/null +++ b/bin/encoder_extended.cfg @@ -0,0 +1,524 @@ +# New Input File Format is as follows +# = # Comment +# +# See configfile.h for a list of supported ParameterNames +# +# For bug reporting and known issues see: +# https://ipbt.hhi.fraunhofer.de + + +########################################################################################## +# Files +########################################################################################## +InputFile = "foreman_part_qcif.yuv" # Input sequence +InputHeaderLength = 0 # If the inputfile has a header, state it's length in byte here +StartFrame = 0 # Start frame for encoding. (0-N) +FramesToBeEncoded = 3 # Number of frames to be coded +FrameRate = 30.0 # Frame Rate per second (0.1-100.0) +SourceWidth = 176 # Source frame width +SourceHeight = 144 # Source frame height +SourceResize = 0 # Resize source size for output +OutputWidth = 176 # Output frame width +OutputHeight = 144 # Output frame height +ProcessInput = 0 # Filter Input Sequence +Interleaved = 0 # 0: Planar input, 1: Packed input +StandardRange = 0 # 0: Standard range 1: Full range (RGB input) +VideoCode = 1 # Video codes for RGB ==> YUV conversions + # 0 = NULL, + # 1 = ITU_REC709, + # 2 = CCIR_601, + # 3 = FCC, + # 4 = ITU_REC624BG, + # 5 = SMPTE_170M, + # 6 = SMPTE_240M, + # 7 = SMPTE_260M, + # 8 = ITU_REC709_EXACT + +TraceFile = "trace_enc.txt" # Trace file +ReconFile = "test_rec.yuv" # Recontruction YUV file +OutputFile = "test.264" # Bitstream +StatsFile = "stats.dat" # Coding statistics file + +########################################################################################## +# Encoder Control +########################################################################################## +ProfileIDC = 88 # Profile IDC (66=baseline, 77=main, 88=extended; FREXT Profiles: 100=High, 110=High 10, 122=High 4:2:2, 244=High 4:4:4, 44=CAVLC 4:4:4 Intra) +IntraProfile = 0 # Activate Intra Profile for FRExt (0: false, 1: true) + # (e.g. ProfileIDC=110, IntraProfile=1 => High 10 Intra Profile) +LevelIDC = 40 # Level IDC (e.g. 20 = level 2.0) + +IntraPeriod = 0 # Period of I-pictures (0=only first) +IDRPeriod = 0 # Period of IDR pictures (0=only first) +AdaptiveIntraPeriod = 1 # Adaptive intra period +AdaptiveIDRPeriod = 0 # Adaptive IDR period +IntraDelay = 0 # Intra (IDR) picture delay (i.e. coding structure of PPIPPP... ) +EnableIDRGOP = 0 # Support for IDR closed GOPs (0: disabled, 1: enabled) +EnableOpenGOP = 0 # Support for open GOPs (0: disabled, 1: enabled) +QPISlice = 28 # Quant. param for I Slices (0-51) +QPPSlice = 28 # Quant. param for P Slices (0-51) +FrameSkip = 0 # Number of frames to be skipped in input (e.g 2 will code every third frame). + # Note that this now excludes intermediate (i.e. B) coded pictures +ChromaQPOffset = 0 # Chroma QP offset (-51..51) + +DisableSubpelME = 0 # Disable Subpixel Motion Estimation (0=off/default, 1=on) +SearchRange = 32 # Max search range + +MEDistortionFPel = 0 # Select error metric for Full-Pel ME (0: SAD, 1: SSE, 2: Hadamard SAD) +MEDistortionHPel = 2 # Select error metric for Half-Pel ME (0: SAD, 1: SSE, 2: Hadamard SAD) +MEDistortionQPel = 2 # Select error metric for Quarter-Pel ME (0: SAD, 1: SSE, 2: Hadamard SAD) +MDDistortion = 2 # Select error metric for Mode Decision (0: SAD, 1: SSE, 2: Hadamard SAD) +SkipDeBlockNonRef = 0 # Skip Deblocking (regardless of DFParametersFlag) for non-reference frames (0: off, 1: on) +ChromaMCBuffer = 1 # Calculate Color component interpolated values in advance and store them. + # Provides a trade-off between memory and computational complexity + # (0: disabled/default, 1: enabled) +ChromaMEEnable = 0 # Take into account Color component information during ME + # (0: only first component/default, + # 1: All Color components - Integer refinement only + # 2: All Color components - All refinements) +ChromaMEWeight = 1 # Weighting for chroma components. This parameter should have a relationship with color format. + +NumberReferenceFrames = 5 # Number of previous frames used for inter motion search (0-16) + +PList0References = 0 # P slice List 0 reference override (0 disable, N <= NumberReferenceFrames) +Log2MaxFNumMinus4 = 0 # Sets log2_max_frame_num_minus4 (-1 : based on FramesToBeEncoded/Auto, >=0 : Log2MaxFNumMinus4) +Log2MaxPOCLsbMinus4 = -1 # Sets log2_max_pic_order_cnt_lsb_minus4 (-1 : Auto, >=0 : Log2MaxPOCLsbMinus4) + +GenerateMultiplePPS = 0 # Transmit multiple parameter sets. Currently parameters basically enable all WP modes (0: diabled, 1: enabled) +SendAUD = 0 # Send Access Delimiter Unit NALU (for every access unit) +ResendSPS = 2 # Resend SPS (0: disabled, 1: all Intra pictures, 2: only for IDR, 3: for IDR and OpenGOP I) +ResendPPS = 0 # Resend PPS (with pic_parameter_set_id 0) for every coded Frame/Field pair (0: disabled, 1: enabled) + +MbLineIntraUpdate = 0 # Error robustness(extra intra macro block updates)(0=off, N: One GOB every N frames are intra coded) +RandomIntraMBRefresh = 0 # Forced intra MBs per picture + +########################################################################################## +# PSlice Mode types +########################################################################################## +PSliceSkip = 1 # P-Slice Skip mode consideration (0=disable, 1=enable) +PSliceSearch16x16 = 1 # P-Slice Inter block search 16x16 (0=disable, 1=enable) +PSliceSearch16x8 = 1 # P-Slice Inter block search 16x8 (0=disable, 1=enable) +PSliceSearch8x16 = 1 # P-Slice Inter block search 8x16 (0=disable, 1=enable) +PSliceSearch8x8 = 1 # P-Slice Inter block search 8x8 (0=disable, 1=enable) +PSliceSearch8x4 = 1 # P-Slice Inter block search 8x4 (0=disable, 1=enable) +PSliceSearch4x8 = 1 # P-Slice Inter block search 4x8 (0=disable, 1=enable) +PSliceSearch4x4 = 1 # P-Slice Inter block search 4x4 (0=disable, 1=enable) + +########################################################################################## +# BSlice Mode types +########################################################################################## + +BSliceDirect = 1 # B-Slice Skip mode consideration (0=disable, 1=enable) +BSliceSearch16x16 = 1 # B-Slice Inter block search 16x16 (0=disable, 1=enable) +BSliceSearch16x8 = 1 # B-Slice Inter block search 16x8 (0=disable, 1=enable) +BSliceSearch8x16 = 1 # B-Slice Inter block search 8x16 (0=disable, 1=enable) +BSliceSearch8x8 = 1 # B-Slice Inter block search 8x8 (0=disable, 1=enable) +BSliceSearch8x4 = 1 # B-Slice Inter block search 8x4 (0=disable, 1=enable) +BSliceSearch4x8 = 1 # B-Slice Inter block search 4x8 (0=disable, 1=enable) +BSliceSearch4x4 = 1 # B-Slice Inter block search 4x4 (0=disable, 1=enable) + +BiPredSearch16x16 = 1 # B-Slice Bi-prediction block search 16x16 (0=disable, 1=enable) +BiPredSearch16x8 = 1 # B-Slice Bi-prediction block search 16x8 (0=disable, 1=enable) +BiPredSearch8x16 = 1 # B-Slice Bi-prediction block search 8x16 (0=disable, 1=enable) +BiPredSearch8x8 = 0 # B-Slice Bi-prediction block search 8x8 (0=disable, 1=enable) + +DisableIntra4x4 = 0 # Disable Intra 4x4 modes +DisableIntra16x16 = 0 # Disable Intra 16x16 modes +DisableIntraInInter = 0 # Disable Intra modes for inter slices +IntraDisableInterOnly = 0 # Apply Disabling Intra conditions only to Inter Slices (0:disable/default,1: enable) +Intra4x4ParDisable = 0 # Disable Vertical & Horizontal 4x4 +Intra4x4DiagDisable = 0 # Disable Diagonal 45degree 4x4 +Intra4x4DirDisable = 0 # Disable Other Diagonal 4x4 +Intra16x16ParDisable = 0 # Disable Vertical & Horizontal 16x16 +Intra16x16PlaneDisable = 0 # Disable Planar 16x16 +ChromaIntraDisable = 0 # Disable Intra Chroma modes other than DC +EnableIPCM = 1 # Enable IPCM macroblock mode + +DisposableP = 0 # Enable Disposable P slices in the primary layer (0: disable/default, 1: enable) +DispPQPOffset = 0 # Quantizer offset for disposable P slices (0: default) + +PreferDispOrder = 1 # Prefer display order when building the prediction structure as opposed to coding order (affects intra and IDR periodic insertion, among others) +PreferPowerOfTwo = 0 # Prefer prediction structures that have lengths expressed as powers of two +FrmStructBufferLength = 16 # Length of the frame structure unit buffer; it can be overriden for certain cases + +ChangeQPFrame = 0 # Frame in display order from which to apply the Change QP offsets +ChangeQPI = 0 # Change QP offset value for I_SLICE +ChangeQPP = 0 # Change QP offset value for P_SLICE +ChangeQPB = 0 # Change QP offset value for B_SLICE +ChangeQPSI = 0 # Change QP offset value for SI_SLICE +ChangeQPSP = 0 # Change QP offset value for SP_SLICE + + +########################################################################################## +# B Slices +########################################################################################## + +NumberBFrames = 1 # Number of B coded frames inserted (0=not used) +PReplaceBSlice = 0 # Replace B-coded slices with P-coded slices when NumberBFrames>0 +QPBSlice = 30 # Quant. param for B slices (0-51) +BRefPicQPOffset = -1 # Quantization offset for reference B coded pictures (-51..51) +DirectModeType = 1 # Direct Mode Type (0:Temporal 1:Spatial) +DirectInferenceFlag = 1 # Direct Inference Flag (0: Disable 1: Enable) +BList0References = 0 # B slice List 0 reference override (0 disable, N <= NumberReferenceFrames) +BList1References = 1 # B slice List 1 reference override (0 disable, N <= NumberReferenceFrames) + # 1 List1 reference is usually recommended for normal GOP Structures. + # A larger value is usually more appropriate if a more flexible + # structure is used (i.e. using HierarchicalCoding) + +BReferencePictures = 0 # Referenced B coded pictures (0=off, 1=B references for secondary layer, 2=B references for primary layer) + +HierarchicalCoding = 0 # B hierarchical coding (0= off, 1= 2 layers, 2= 2 full hierarchy, 3 = explicit) +HierarchyLevelQPEnable = 1 # Adjust QP based on hierarchy level (in increments of 1). Overrides BRefPicQPOffset behavior.(0=off, 1=on) +ExplicitHierarchyFormat = "b1r0b3r0b2e2b0e2b4r2" # Explicit Enhancement GOP. Format is {FrameDisplay_orderReferenceQP}. + # Valid values for reference type is r:reference, e:non reference. +ExplicitSeqCoding = 0 # Enable support for explicit sequence coding +ExplicitSeqFile = "explicit_seq.cfg" +LowDelay = 0 # Apply HierarchicalCoding without delay (i.e., encode in the captured/display order) +ReferenceReorder = 1 # Reorder References according to Poc distance for HierarchicalCoding (0=off, 1=enable, 2=use when LowDelay is set) +PocMemoryManagement = 1 # Memory management based on Poc Distances for HierarchicalCoding (0=off, 1=on, 2=use when LowDelay is set) +SetFirstAsLongTerm = 0 # Set first frame as long term + +BiPredMotionEstimation = 1 # Enable Bipredictive based Motion Estimation (0:disabled, 1:enabled) +BiPredMERefinements = 3 # Bipredictive ME extra refinements (0: single, N: N extra refinements (1 default) +BiPredMESearchRange = 16 # Bipredictive ME Search range (8 default). Note that range is halved for every extra refinement. +BiPredMESubPel = 2 # Bipredictive ME Subpixel Consideration (0: disabled, 1: single level, 2: dual level) + + +########################################################################################## +# SP Frames +########################################################################################## + +SPPicturePeriodicity = 0 # SP-Picture Periodicity (0=not used) +SPSwitchPeriod = 0 # Switch period (in terms of switching SP/SI frames) between bitstream 1 and bitstream 2 +QPSPSlice = 36 # Quant. param of SP-Slices for Prediction Error (0-51) +QPSISlice = 36 # Quant. param of SI-Slices for Prediction Error (0-51) +QPSP2Slice = 35 # Quant. param of SP/SI-Slices for Predicted Blocks (0-51) +SI_FRAMES = 0 # SI frame encoding flag (0=not used, 1=used) +SP_output = 0 # Controls whether coefficients will be output to encode switching SP frames (0=no, 1=yes) +SP_output_name = "low_quality.dat" # Filename for SP output coefficients +SP2_FRAMES = 0 # switching SP frame encoding flag (0=not used, 1=used) +SP2_input_name1 = "high_quality.dat" # Filename for the first swithed bitstream coefficients +SP2_input_name2 = "low_quality.dat" # Filename for the second switched bitstream coefficients + +########################################################################################## +# Output Control, NALs +########################################################################################## + +OutFileMode = 0 # Output file mode, 0:Annex B, 1:RTP +PartitionMode = 0 # Partition Mode, 0: no DP, 1: 3 Partitions per Slice + +########################################################################################## +# Interlace Handling +######################################################################################### + +PicInterlace = 0 # Picture AFF (0: frame coding, 1: field coding, 2:adaptive frame/field coding) +MbInterlace = 0 # Macroblock AFF (0: frame coding, 1: field coding, 2:adaptive frame/field coding, 3: frame MB-only AFF) +IntraBottom = 0 # Force Intra Bottom at GOP Period + +########################################################################################## +# Weighted Prediction +######################################################################################### + +WeightedPrediction = 0 # P picture Weighted Prediction (0=off, 1=explicit mode) +WeightedBiprediction = 0 # B picture Weighted Prediciton (0=off, 1=explicit mode, 2=implicit mode) +ChromaWeightSupport = 1 # Enable consideration of weights for Chroma components +UseWeightedReferenceME = 1 # Use weighted reference for ME (0=off, 1=on) +WPMethod = 1 # WP method (0: DC based, 1: LMS based) +WPIterMC = 0 # Iterative Motion compensated based weighted prediction method +EnhancedBWeightSupport = 0 # Enhanced B Weight support (needs revisit if we wish to merge with WPMethod) +WPMCPrecision = 0 # Improved Motion Compensation Precision using WP based methods. + # Clones WP references with slightly modified rounding offsets (Requires RDPictureDecision and GenerateMultiplePPS) : + # 0: disabled (default) + # 1: Up to one additional coding pass. Ref0 is 0, ref1 is 0 with a -1 offset + # 2: Up to two additional coding passes. (1) Ref0 is 0, ref1 is 0 with a -1 offset, (1) Ref0 is 0 with a -1 offset, ref1 is 0 +WPMCPrecFullRef = 0 # Increases the number of references in the reference picture lists to account + # for the lost reference slot when reordering is used during a coding pass in WPMCPrecision for reference replication. + # The number of references in non-reordered passes stays unchanged +WPMCPrecBSlice = 1 # 2: Apply rounding on every B slice. This efectively disables the evaluation of alternative QPs during RDPictureDecision. + # 1: Disable rounding for non-reference B slices. Non-reference B slices are evaluated for alternative QPs during RDPictureDecision. + # 0: Disable rounding for B slices. + +########################################################################################## +# Picture based Multi-pass encoding +######################################################################################### + +RDPictureDecision = 0 # Perform multiple pass coding and make RD optimal decision among them +RDPSliceBTest = 0 # Perform Slice level RD decision between P and B slices. +RDPictureMaxPassISlice = 1 # Max number of coding passes for I slices, valid values [1,3], default is 1 +RDPictureMaxPassPSlice = 2 # Max number of coding passes for P slices, valid values [1,6], default is 2 +RDPictureMaxPassBSlice = 3 # Max number of coding passes for B slices, valid values [1,6], default is 3 +RDPictureFrameQPPSlice = 0 # Perform additional frame level QP check (QP+/-1) for P slices, 0: disabled (default), 1: enabled +RDPictureFrameQPBSlice = 0 # Perform additional frame level QP check (QP+/-1) for B slices, 0: disabled, 1: enabled (default) +RDPictureDeblocking = 0 # Perform another coding pass to check non-deblocked picture, 0: disabled (default), 1: enabled +RDPictureDirectMode = 0 # Perform another coding pass to check the alternative direct mode for B slices, , 0: disabled (default), 1: enabled + +########################################################################################## +# Deblocking filter parameters +########################################################################################## + +DFParametersFlag = 0 # Configure deblocking filter (0=parameters below ignored, 1=parameters sent) + # Note that for pictures with multiple slice types, + # only the type of the first slice will be considered. +DFDisableRefISlice = 0 # Disable deblocking filter in reference I coded pictures (0=Filter, 1=No Filter). +DFAlphaRefISlice = 0 # Reference I coded pictures Alpha offset div. 2, {-6, -5, ... 0, +1, .. +6} +DFBetaRefISlice = 0 # Reference I coded pictures Beta offset div. 2, {-6, -5, ... 0, +1, .. +6} +DFDisableNRefISlice = 0 # Disable deblocking filter in non reference I coded pictures (0=Filter, 1=No Filter). +DFAlphaNRefISlice = 0 # Non Reference I coded pictures Alpha offset div. 2, {-6, -5, ... 0, +1, .. +6} +DFBetaNRefISlice = 0 # Non Reference I coded pictures Beta offset div. 2, {-6, -5, ... 0, +1, .. +6} +DFDisableRefPSlice = 0 # Disable deblocking filter in reference P coded pictures (0=Filter, 1=No Filter). +DFAlphaRefPSlice = 0 # Reference P coded pictures Alpha offset div. 2, {-6, -5, ... 0, +1, .. +6} +DFBetaRefPSlice = 0 # Reference P coded pictures Beta offset div. 2, {-6, -5, ... 0, +1, .. +6} +DFDisableNRefPSlice = 0 # Disable deblocking filter in non reference P coded pictures (0=Filter, 1=No Filter). +DFAlphaNRefPSlice = 0 # Non Reference P coded pictures Alpha offset div. 2, {-6, -5, ... 0, +1, .. +6} +DFBetaNRefPSlice = 0 # Non Reference P coded pictures Beta offset div. 2, {-6, -5, ... 0, +1, .. +6} +DFDisableRefBSlice = 0 # Disable deblocking filter in reference B coded pictures (0=Filter, 1=No Filter). +DFAlphaRefBSlice = 0 # Reference B coded pictures Alpha offset div. 2, {-6, -5, ... 0, +1, .. +6} +DFBetaRefBSlice = 0 # Reference B coded pictures Beta offset div. 2, {-6, -5, ... 0, +1, .. +6} +DFDisableNRefBSlice = 0 # Disable deblocking filter in non reference B coded pictures (0=Filter, 1=No Filter). +DFAlphaNRefBSlice = 0 # Non Reference B coded pictures Alpha offset div. 2, {-6, -5, ... 0, +1, .. +6} +DFBetaNRefBSlice = 0 # Non Reference B coded pictures Beta offset div. 2, {-6, -5, ... 0, +1, .. +6} + +########################################################################################## +# Error Resilience / Slices +########################################################################################## + +SliceMode = 0 # Slice mode (0=off 1=fixed #mb in slice 2=fixed #bytes in slice 3=use callback) +SliceArgument = 50 # Slice argument (Arguments to modes 1 and 2 above) + +UseRedundantPicture = 0 # 0: not used, 1: enabled +NumRedundantHierarchy = 1 # 0-4 +PrimaryGOPLength = 10 # GOP length for redundant allocation (1-16) + # NumberReferenceFrames must be no less than PrimaryGOPLength when redundant slice enabled +NumRefPrimary = 1 # Actually used number of references for primary slices (1-16) + +########################################################################################## +# Search Range Restriction / RD Optimization +########################################################################################## + +RestrictSearchRange = 2 # restriction for (0: blocks and ref, 1: ref, 2: no restrictions) +RDOptimization = 1 # rd-optimized mode decision + # 0: RD-off (Low complexity mode) + # 1: RD-on (High complexity mode) + # 2: RD-on (Fast high complexity mode - not work in FREX Profiles) + # 3: with losses +I16RDOpt = 0 # perform rd-optimized mode decision for Intra 16x16 MB + # 0: SAD-based mode decision for Intra 16x16 MB + # 1: RD-based mode decision for Intra 16x16 MB +SubMBCodingState = 1 # submacroblock coding state + # 0: lowest complexity, do not store or reset coding state during sub-MB mode decision + # 1: medium complexity, reset to master coding state (for current mode) during sub-MB mode decision + # 2: highest complexity, store and reset coding state during sub-MB mode decision +DistortionSSIM = 0 # Compute SSIM distortion. (0: disabled/default, 1: enabled) +DistortionMS_SSIM = 0 # Compute Multiscale SSIM distortion. (0: disabled/default, 1: enabled) +SSIMOverlapSize = 8 # Overlap size to calculate SSIM distortion (1: pixel by pixel, 8: no overlap) +DistortionYUVtoRGB = 0 # Calculate distortion in RGB domain after conversion from YCbCr (0:off, 1:on) +CtxAdptLagrangeMult = 0 # Context Adaptive Lagrange Multiplier + # 0: disabled (default) + # 1: enabled (works best when RDOptimization=0) +FastCrIntraDecision = 1 # Fast Chroma intra mode decision (0:off, 1:on) +DisableThresholding = 0 # Disable Thresholding of Transform Coefficients (0:off, 1:on) +DisableBSkipRDO = 0 # Disable B Skip Mode consideration from RDO Mode decision (0:off, 1:on) +BiasSkipRDO = 1 # Negative Bias for Skip/DirectSkip modes (0: off, 1: on) +ForceTrueRateRDO = 0 # Force true rate (even zero values) during RDO process +SkipIntraInInterSlices = 0 # Skips Intra mode checking in inter slices if certain mode decisions are satisfied (0: off, 1: on) +WeightY = 1 # Luma weight for RDO +WeightCb = 1 # Cb weight for RDO +WeightCr = 1 # Cr weight for RDO + +########################################################################################## +# Explicit Lambda Usage +########################################################################################## +UseExplicitLambdaParams = 0 # Use explicit lambda scaling parameters (0:disabled, 1:enable lambda weight, 2: use explicit lambda value) +UpdateLambdaChromaME = 0 # Update lambda given Chroma ME consideration +FixedLambdaISlice = 0.1 # Fixed Lambda value for I slices +FixedLambdaPSlice = 0.1 # Fixed Lambda value for P slices +FixedLambdaBSlice = 0.1 # Fixed Lambda value for B slices +FixedLambdaRefBSlice = 0.1 # Fixed Lambda value for Referenced B slices +FixedLambdaSPSlice = 0.1 # Fixed Lambda value for SP slices +FixedLambdaSISlice = 0.1 # Fixed Lambda value for SI slices + +LambdaWeightISlice = 0.65 # scaling param for I slices. This will be used as a multiplier i.e. lambda=LambdaWeightISlice * 2^((QP-12)/3) +LambdaWeightPSlice = 0.68 # scaling param for P slices. This will be used as a multiplier i.e. lambda=LambdaWeightPSlice * 2^((QP-12)/3) +LambdaWeightBSlice = 0.68 # scaling param for B slices. This will be used as a multiplier i.e. lambda=LambdaWeightBSlice * 2^((QP-12)/3) +LambdaWeightRefBSlice = 0.68 # scaling param for Referenced B slices. This will be used as a multiplier i.e. lambda=LambdaWeightRefBSlice * 2^((QP-12)/3) +LambdaWeightSPSlice = 0.68 # scaling param for SP slices. This will be used as a multiplier i.e. lambda=LambdaWeightSPSlice * 2^((QP-12)/3) +LambdaWeightSISlice = 0.65 # scaling param for SI slices. This will be used as a multiplier i.e. lambda=LambdaWeightSISlice * 2^((QP-12)/3) + +LossRateA = 5 # expected packet loss rate of the channel for the first partition, only valid if RDOptimization = 3 +LossRateB = 0 # expected packet loss rate of the channel for the second partition, only valid if RDOptimization = 3 +LossRateC = 0 # expected packet loss rate of the channel for the third partition, only valid if RDOptimization = 3 +FirstFrameCorrect = 0 # If 1, the first frame is encoded under the assumption that it is always correctly received. +NumberOfDecoders = 30 # Numbers of decoders used to simulate the channel, only valid if RDOptimization = 3 +RestrictRefFrames = 0 # Doesnt allow reference to areas that have been intra updated in a later frame. + +########################################################################################## +# Additional Stuff +######################################################################################### + +UseConstrainedIntraPred = 0 # If 1, Inter pixels are not used for Intra macroblock prediction. + +NumberofLeakyBuckets = 8 # Number of Leaky Bucket values +LeakyBucketRateFile = "leakybucketrate.cfg" # File from which encoder derives rate values +LeakyBucketParamFile = "leakybucketparam.cfg" # File where encoder stores leakybucketparams + +NumFramesInELayerSubSeq = 0 # number of frames in the Enhanced Scalability Layer(0: no Enhanced Layer) + +SparePictureOption = 0 # (0: no spare picture info, 1: spare picture available) +SparePictureDetectionThr = 6 # Threshold for spare reference pictures detection +SparePicturePercentageThr = 92 # Threshold for the spare macroblock percentage + +PicOrderCntType = 0 # (0: POC mode 0, 1: POC mode 1, 2: POC mode 2) + +######################################################################################## +#Rate control +######################################################################################## + +RateControlEnable = 0 # 0 Disable, 1 Enable +Bitrate = 45020 # Bitrate(bps) +InitialQP = 0 # Initial Quantization Parameter for the first I frame + # InitialQp depends on two values: Bits Per Picture, + # and the GOP length +BasicUnit = 0 # Number of MBs in the basic unit + # should be a fraction of the total number + # of MBs in a frame ("0" sets a BU equal to a frame) +ChannelType = 0 # type of channel( 1=time varying channel; 0=Constant channel) +RCUpdateMode = 0 # Rate Control type. Modes supported : + # 0 = original JM rate control, + # 1 = rate control that is applied to all frames regardless of the slice type, + # 2 = original plus intelligent QP selection for I and B slices (including Hierarchical), + # 3 = original + hybrid quadratic rate control for I and B slice using bit rate statistics + # +RCISliceBitRatio = 1.0 # target ratio of bits for I-coded pictures compared to P-coded Pictures (for RCUpdateMode=3) +RCBSliceBitRatio0 = 0.5 # target ratio of bits for B-coded pictures compared to P-coded Pictures - temporal level 0 (for RCUpdateMode=3) +RCBSliceBitRatio1 = 0.25 # target ratio of bits for B-coded pictures compared to P-coded Pictures - temporal level 1 (for RCUpdateMode=3) +RCBSliceBitRatio2 = 0.25 # target ratio of bits for B-coded pictures compared to P-coded Pictures - temporal level 2 (for RCUpdateMode=3) +RCBSliceBitRatio3 = 0.25 # target ratio of bits for B-coded pictures compared to P-coded Pictures - temporal level 3 (for RCUpdateMode=3) +RCBSliceBitRatio4 = 0.25 # target ratio of bits for B-coded pictures compared to P-coded Pictures - temporal level 4 (for RCUpdateMode=3) +RCBoverPRatio = 0.45 # ratio of bit rate usage of a B-coded picture over a P-coded picture for the SAME QP (for RCUpdateMode=3) +RCIoverPRatio = 3.80 # ratio of bit rate usage of an I-coded picture over a P-coded picture for the SAME QP (for RCUpdateMode=3) +RCMinQPPSlice = 8 # minimum P Slice QP value for rate control +RCMaxQPPSlice = 42 # maximum P Slice QP value for rate control +RCMinQPBSlice = 8 # minimum B Slice QP value for rate control +RCMaxQPBSlice = 42 # maximum B Slice QP value for rate control +RCMinQPISlice = 8 # minimum I Slice QP value for rate control +RCMaxQPISlice = 42 # maximum I Slice QP value for rate control +RCMinQPSPSlice = 8 # minimum SP Slice QP value for rate control +RCMaxQPSPSlice = 40 # maximum SP Slice QP value for rate control +RCMinQPSISlice = 8 # minimum SI Slice QP value for rate control +RCMaxQPSISlice = 42 # maximum SI Slice QP value for rate control +RCMaxQPChange = 4 # maximum QP change for frames of the base layer + +######################################################################################## +#Fast Mode Decision +######################################################################################## +EarlySkipEnable = 0 # Early skip detection (0: Disable 1: Enable) +SelectiveIntraEnable = 0 # Selective Intra mode decision (0: Disable 1: Enable) + +ReportFrameStats = 0 # (0:Disable Frame Statistics 1: Enable) +DisplayEncParams = 0 # (0:Disable Display of Encoder Params 1: Enable) +Verbose = 1 # level of display verboseness + # 0: short, 1: normal (default), 2: detailed, 3: detailed/nvb + + +######################################################################################## +#Rounding Offset control +######################################################################################## + +OffsetMatrixPresentFlag = 0 # Enable Explicit Offset Quantization Matrices (0: disable 1: enable) +QOffsetMatrixFile = "q_offset.cfg" # Explicit Quantization Matrices file + +AdaptiveRounding = 1 # Enable Adaptive Rounding based on JVT-N011 (0: disable, 1: enable) +AdaptRoundingFixed = 1 # Enable Global Adaptive rounding for all qps (0: disable, 1: enable - default/old) +AdaptRndPeriod = 16 # Period in terms of MBs for updating rounding offsets. + # 0 performs update at the picture level. Default is 16. 1 is as in JVT-N011. +AdaptRndChroma = 1 # Enables coefficient rounding adaptation for chroma + +AdaptRndWFactorIRef = 4 # Adaptive Rounding Weight for I/SI slices in reference pictures /4096 +AdaptRndWFactorPRef = 4 # Adaptive Rounding Weight for P/SP slices in reference pictures /4096 +AdaptRndWFactorBRef = 4 # Adaptive Rounding Weight for B slices in reference pictures /4096 +AdaptRndWFactorINRef = 4 # Adaptive Rounding Weight for I/SI slices in non reference pictures /4096 +AdaptRndWFactorPNRef = 4 # Adaptive Rounding Weight for P/SP slices in non reference pictures /4096 +AdaptRndWFactorBNRef = 4 # Adaptive Rounding Weight for B slices in non reference pictures /4096 + +AdaptRndCrWFactorIRef = 4 # Chroma Adaptive Rounding Weight for I/SI slices in reference pictures /4096 +AdaptRndCrWFactorPRef = 4 # Chroma Adaptive Rounding Weight for P/SP slices in reference pictures /4096 +AdaptRndCrWFactorBRef = 4 # Chroma Adaptive Rounding Weight for B slices in reference pictures /4096 +AdaptRndCrWFactorINRef = 4 # Chroma Adaptive Rounding Weight for I/SI slices in non reference pictures /4096 +AdaptRndCrWFactorPNRef = 4 # Chroma Adaptive Rounding Weight for P/SP slices in non reference pictures /4096 +AdaptRndCrWFactorBNRef = 4 # Chroma Adaptive Rounding Weight for B slices in non reference pictures /4096 + +################################################################ +# Rate Distortion Optimized Quantization +################################################################ +UseRDOQuant = 0 # Use Rate Distortion Optimized Quantization (0=disable, 1=enable) +RDOQ_DC = 0 # Enable Rate Distortion Optimized Quantization for DC components (0=disable, 1=enable) +RDOQ_CR = 0 # Enable Rate Distortion Optimized Quantization for Chroma components (0=disable, 1=enable) +RDOQ_DC_CR = 0 # Enable Rate Distortion Optimized Quantization for Chroma DC components (0=disable, 1=enable) +RDOQ_QP_Num = 1 # 1-5: Number of QP tested in RDO_Q (I/P/B slice) +RDOQ_CP_Mode = 1 # copy Mode from first QP tested +RDOQ_CP_MV = 1 # copy MV from first QP tested +RDOQ_Fast = 1 # Fast RDOQ decision method for multiple QPs + + +######################################################################################## +#Fast Motion Estimation Control Parameters +######################################################################################## + +SearchMode = 0 # Motion estimation mode + # -1 = Full Search + # 0 = Fast Full Search (default) + # 1 = UMHexagon Search + # 2 = Simplified UMHexagon Search + # 3 = Enhanced Predictive Zonal Search (EPZS) + +UMHexDSR = 1 # Use Search Range Prediction. Only for UMHexagonS method + # (0:disable, 1:enabled/default) +UMHexScale = 3 # Use Scale_factor for different image sizes. Only for UMHexagonS method + # (0:disable, 3:/default) + # Increasing value can speed up Motion Search. + +EPZSPattern = 2 # Select EPZS primary refinement pattern. + # (0: small diamond, 1: square, 2: extended diamond/default, + # 3: large diamond, 4: SBP Large Diamond, + # 5: PMVFAST ) +EPZSDualRefinement = 3 # Enables secondary refinement pattern. + # (0:disabled, 1: small diamond, 2: square, + # 3: extended diamond/default, 4: large diamond, + # 5: SBP Large Diamond, 6: PMVFAST ) +EPZSFixedPredictors = 2 # Enables Window based predictors + # (0:disabled, 1: P only, 2: P and B/default) +EPZSTemporal = 1 # Enables temporal predictors + # (0: disabled, 1: enabled/default) +EPZSSpatialMem = 1 # Enables spatial memory predictors + # (0: disabled, 1: enabled/default) +EPZSBlockType = 1 # Enables block type Predictors + # (0: disabled, 1: enabled/default) +EPZSMinThresScale = 0 # Scaler for EPZS minimum threshold (0 default). + # Increasing value can speed up encoding. +EPZSMedThresScale = 1 # Scaler for EPZS median threshold (1 default). + # Increasing value can speed up encoding. +EPZSMaxThresScale = 2 # Scaler for EPZS maximum threshold (1 default). + # Increasing value can speed up encoding. +EPZSSubPelME = 1 # EPZS Subpel ME consideration +EPZSSubPelMEBiPred = 1 # EPZS Subpel ME consideration for BiPred partitions +EPZSSubPelThresScale = 2 # EPZS Subpel ME Threshold scaler +EPZSSubPelGrid = 1 # Perform EPZS using a subpixel grid + +######################################################################################## +# SEI Parameters +######################################################################################## + +GenerateSEIMessage = 0 # Generate an SEI Text Message +SEIMessageText = "H.264/AVC Encoder" # Text SEI Message + +UseMVLimits = 0 # Use MV Limits +SetMVXLimit = 512 # Horizontal MV Limit (in integer units) +SetMVYLimit = 512 # Vertical MV Limit (in integer units) + +######################################################################################## +# VUI Parameters +######################################################################################## +# the variables below do not affect encoding and decoding +# (many are dummy variables but others can be useful when supported by the decoder) + +EnableVUISupport = 0 # Enable VUI Parameters + diff --git a/bin/encoder_fast.cfg b/bin/encoder_fast.cfg new file mode 100644 index 0000000..064b942 --- /dev/null +++ b/bin/encoder_fast.cfg @@ -0,0 +1,648 @@ +# New Input File Format is as follows +# = # Comment +# +# See configfile.h for a list of supported ParameterNames +# +# For bug reporting and known issues see: +# https://ipbt.hhi.fraunhofer.de + + +########################################################################################## +# Files +########################################################################################## +InputFile = "/data/foreman_176x144_30p.yuv" # Input sequence +InputHeaderLength = 0 # If the inputfile has a header, state it's length in byte here +StartFrame = 0 # Start frame for encoding. (0-N) +FramesToBeEncoded = 30 # Number of frames to be coded +FrameRate = 30.0 # Frame Rate per second (0.1-100.0) +Enable32Pulldown = 0 # Enforce 3:2 pulldown methods + # 0 = disabled + # 1 = A, B, Bt|Cb, Ct|Db, D + # 2 = A, B, C, Ct|Db, D +SourceWidth = 176 # Source frame width +SourceHeight = 144 # Source frame height +SourceResize = 0 # Resize source size for output +OutputWidth = 176 # Output frame width +OutputHeight = 144 # Output frame height +ProcessInput = 0 # Filter Input Sequence +Interleaved = 0 # 0: Planar input, 1: Packed input +StandardRange = 0 # 0: Standard range 1: Full range (RGB input) +VideoCode = 1 # Video codes for RGB ==> YUV conversions + # 0 = NULL, + # 1 = ITU_REC709, + # 2 = CCIR_601, + # 3 = FCC, + # 4 = ITU_REC624BG, + # 5 = SMPTE_170M, + # 6 = SMPTE_240M, + # 7 = SMPTE_260M, + # 8 = ITU_REC709_EXACT + +TraceFile = "trace_enc.txt" # Trace file +ReconFile = "test_rec.yuv" # Reconstruction YUV file +OutputFile = "test.264" # Bitstream +StatsFile = "stats.dat" # Coding statistics file + +########################################################################################## +# Encoder Control +########################################################################################## +Grayscale = 0 # Encode in grayscale (Currently only works for 8 bit YUV 420 input) +ProfileIDC = 100 # Profile IDC (66=baseline, 77=main, 88=extended; FREXT Profiles: 100=High, 110=High 10, 122=High 4:2:2, 244=High 4:4:4, 44=CAVLC 4:4:4 Intra, 118=Multiview High Profile, 128=Stereo High Profile) +IntraProfile = 0 # Activate Intra Profile for FRExt (0: false, 1: true) + # (e.g. ProfileIDC=110, IntraProfile=1 => High 10 Intra Profile) +LevelIDC = 40 # Level IDC (e.g. 20 = level 2.0) + +IntraPeriod = 0 # Period of I-pictures (0=only first) +IDRPeriod = 0 # Period of IDR pictures (0=only first) +AdaptiveIntraPeriod = 1 # Adaptive intra period +AdaptiveIDRPeriod = 0 # Adaptive IDR period +IntraDelay = 0 # Intra (IDR) picture delay (i.e. coding structure of PPIPPP... ) +EnableIDRGOP = 0 # Support for IDR closed GOPs (0: disabled, 1: enabled) +EnableOpenGOP = 0 # Support for open GOPs (0: disabled, 1: enabled) +QPISlice = 28 # Quant. param for I Slices (0-51) +QPPSlice = 28 # Quant. param for P Slices (0-51) +FrameSkip = 0 # Number of frames to be skipped in input (e.g 2 will code every third frame). + # Note that this now excludes intermediate (i.e. B) coded pictures +ChromaQPOffset = 0 # Chroma QP offset (-51..51) + +DisableSubpelME = 0 # Disable Subpixel Motion Estimation (0=off/default, 1=on) +SearchRange = 32 # Max search range + +MEDistortionFPel = 0 # Select error metric for Full-Pel ME (0: SAD, 1: SSE, 2: Hadamard SAD) +MEDistortionHPel = 2 # Select error metric for Half-Pel ME (0: SAD, 1: SSE, 2: Hadamard SAD) +MEDistortionQPel = 2 # Select error metric for Quarter-Pel ME (0: SAD, 1: SSE, 2: Hadamard SAD) +MDDistortion = 2 # Select error metric for Mode Decision (0: SAD, 1: SSE, 2: Hadamard SAD) +SkipDeBlockNonRef = 0 # Skip Deblocking (regardless of DFParametersFlag) for non-reference frames (0: off, 1: on) +ChromaMCBuffer = 1 # Calculate Color component interpolated values in advance and store them. + # Provides a trade-off between memory and computational complexity + # (0: disabled/default, 1: enabled) +ChromaMEEnable = 0 # Take into account Color component information during ME + # (0: only first component/default, + # 1: All Color components - Integer refinement only + # 2: All Color components - All refinements) +ChromaMEWeight = 0 # Weighting for chroma components. This parameter should have a relationship with color format. + +NumberReferenceFrames = 5 # Number of previous frames used for inter motion search (0-16) + +PList0References = 0 # P slice List 0 reference override (0 disable, N <= NumberReferenceFrames) +Log2MaxFNumMinus4 = 0 # Sets log2_max_frame_num_minus4 (-1 : based on FramesToBeEncoded/Auto, >=0 : Log2MaxFNumMinus4) +Log2MaxPOCLsbMinus4 = -1 # Sets log2_max_pic_order_cnt_lsb_minus4 (-1 : Auto, >=0 : Log2MaxPOCLsbMinus4) + +GenerateMultiplePPS = 1 # Transmit multiple parameter sets. Currently parameters basically enable all WP modes (0: diabled, 1: enabled) +SendAUD = 0 # Send Access Delimiter Unit NALU (for every access unit) +ResendSPS = 2 # Resend SPS (0: disabled, 1: all Intra pictures, 2: only for IDR, 3: for IDR and OpenGOP I) +ResendPPS = 0 # Resend PPS (with pic_parameter_set_id 0) for every coded Frame/Field pair (0: disabled, 1: enabled) + +MbLineIntraUpdate = 0 # Error robustness(extra intra macro block updates)(0=off, N: One GOB every N frames are intra coded) +RandomIntraMBRefresh = 0 # Forced intra MBs per picture + +########################################################################################## +# PSlice Mode types +########################################################################################## +PSliceSkip = 1 # P-Slice Skip mode consideration (0=disable, 1=enable) +PSliceSearch16x16 = 1 # P-Slice Inter block search 16x16 (0=disable, 1=enable) +PSliceSearch16x8 = 1 # P-Slice Inter block search 16x8 (0=disable, 1=enable) +PSliceSearch8x16 = 1 # P-Slice Inter block search 8x16 (0=disable, 1=enable) +PSliceSearch8x8 = 1 # P-Slice Inter block search 8x8 (0=disable, 1=enable) +PSliceSearch8x4 = 1 # P-Slice Inter block search 8x4 (0=disable, 1=enable) +PSliceSearch4x8 = 1 # P-Slice Inter block search 4x8 (0=disable, 1=enable) +PSliceSearch4x4 = 1 # P-Slice Inter block search 4x4 (0=disable, 1=enable) + +########################################################################################## +# BSlice Mode types +########################################################################################## + +BSliceDirect = 1 # B-Slice Skip mode consideration (0=disable, 1=enable) +BSliceSearch16x16 = 1 # B-Slice Inter block search 16x16 (0=disable, 1=enable) +BSliceSearch16x8 = 1 # B-Slice Inter block search 16x8 (0=disable, 1=enable) +BSliceSearch8x16 = 1 # B-Slice Inter block search 8x16 (0=disable, 1=enable) +BSliceSearch8x8 = 1 # B-Slice Inter block search 8x8 (0=disable, 1=enable) +BSliceSearch8x4 = 1 # B-Slice Inter block search 8x4 (0=disable, 1=enable) +BSliceSearch4x8 = 1 # B-Slice Inter block search 4x8 (0=disable, 1=enable) +BSliceSearch4x4 = 1 # B-Slice Inter block search 4x4 (0=disable, 1=enable) + +BiPredSearch16x16 = 1 # B-Slice Bi-prediction block search 16x16 (0=disable, 1=enable) +BiPredSearch16x8 = 1 # B-Slice Bi-prediction block search 16x8 (0=disable, 1=enable) +BiPredSearch8x16 = 1 # B-Slice Bi-prediction block search 8x16 (0=disable, 1=enable) +BiPredSearch8x8 = 0 # B-Slice Bi-prediction block search 8x8 (0=disable, 1=enable) + +DisableIntra4x4 = 0 # Disable Intra 4x4 modes +DisableIntra16x16 = 0 # Disable Intra 16x16 modes +DisableIntraInInter = 0 # Disable Intra modes for inter slices +IntraDisableInterOnly = 0 # Apply Disabling Intra conditions only to Inter Slices (0:disable/default,1: enable) +Intra4x4ParDisable = 0 # Disable Vertical & Horizontal 4x4 +Intra4x4DiagDisable = 0 # Disable Diagonal 45degree 4x4 +Intra4x4DirDisable = 0 # Disable Other Diagonal 4x4 +Intra16x16ParDisable = 0 # Disable Vertical & Horizontal 16x16 +Intra16x16PlaneDisable = 0 # Disable Planar 16x16 +ChromaIntraDisable = 0 # Disable Intra Chroma modes other than DC +EnableIPCM = 1 # Enable IPCM macroblock mode + +DisposableP = 0 # Enable Disposable P slices in the primary layer (0: disable/default, 1: enable) +DispPQPOffset = 0 # Quantizer offset for disposable P slices (0: default) + +PreferDispOrder = 1 # Prefer display order when building the prediction structure as opposed to coding order (affects intra and IDR periodic insertion, among others) +PreferPowerOfTwo = 0 # Prefer prediction structures that have lengths expressed as powers of two +FrmStructBufferLength = 16 # Length of the frame structure unit buffer; it can be overriden for certain cases + +ChangeQPFrame = 0 # Frame in display order from which to apply the Change QP offsets +ChangeQPI = 0 # Change QP offset value for I_SLICE +ChangeQPP = 0 # Change QP offset value for P_SLICE +ChangeQPB = 0 # Change QP offset value for B_SLICE +ChangeQPSI = 0 # Change QP offset value for SI_SLICE +ChangeQPSP = 0 # Change QP offset value for SP_SLICE + +########################################################################################## +# B Slices +########################################################################################## + +NumberBFrames = 7 # Number of B coded frames inserted (0=not used) +PReplaceBSlice = 0 # Replace B-coded slices with P-coded slices when NumberBFrames>0 +QPBSlice = 30 # Quant. param for B slices (0-51) +BRefPicQPOffset = -1 # Quantization offset for reference B coded pictures (-51..51) +DirectModeType = 1 # Direct Mode Type (0:Temporal 1:Spatial) +DirectInferenceFlag = 1 # Direct Inference Flag (0: Disable 1: Enable) +BList0References = 0 # B slice List 0 reference override (0 disable, N <= NumberReferenceFrames) +BList1References = 1 # B slice List 1 reference override (0 disable, N <= NumberReferenceFrames) + # 1 List1 reference is usually recommended for normal GOP Structures. + # A larger value is usually more appropriate if a more flexible + # structure is used (i.e. using HierarchicalCoding) + +BReferencePictures = 0 # Referenced B coded pictures (0=off, 1=B references for secondary layer, 2=B references for primary layer) + +HierarchicalCoding = 3 # B hierarchical coding (0= off, 1= 2 layers, 2= 2 full hierarchy, 3 = explicit) +HierarchyLevelQPEnable = 1 # Adjust QP based on hierarchy level (in increments of 1). Overrides BRefPicQPOffset behavior.(0=off, 1=on) +ExplicitHierarchyFormat = "b3r0b1r1b0e2b2e2b5r1b4e2b6e2" # Explicit Enhancement GOP. Format is {FrameDisplay_orderReferenceQP}. + # Valid values for reference type is r:reference, e:non reference. +ExplicitSeqCoding = 0 # Enable support for explicit sequence coding +ExplicitSeqFile = "explicit_seq.cfg" +LowDelay = 0 # Apply HierarchicalCoding without delay (i.e., encode in the captured/display order) +ReferenceReorder = 1 # Reorder References according to Poc distance for HierarchicalCoding (0=off, 1=enable, 2=use when LowDelay is set) +PocMemoryManagement = 1 # Memory management based on Poc Distances for HierarchicalCoding (0=off, 1=on, 2=use when LowDelay is set) +SetFirstAsLongTerm = 0 # Set first frame as long term + +BiPredMotionEstimation = 1 # Enable Bipredictive based Motion Estimation (0:disabled, 1:enabled) +BiPredMERefinements = 3 # Bipredictive ME extra refinements (0: single, N: N extra refinements (1 default) +BiPredMESearchRange = 16 # Bipredictive ME Search range (8 default). Note that range is halved for every extra refinement. +BiPredMESubPel = 2 # Bipredictive ME Subpixel Consideration (0: disabled, 1: single level, 2: dual level) + + +########################################################################################## +# SP Frames +########################################################################################## + +SPPicturePeriodicity = 0 # SP-Picture Periodicity (0=not used) +SPSwitchPeriod = 0 # Switch period (in terms of switching SP/SI frames) between bitstream 1 and bitstream 2 +QPSPSlice = 36 # Quant. param of SP-Slices for Prediction Error (0-51) +QPSISlice = 36 # Quant. param of SI-Slices for Prediction Error (0-51) +QPSP2Slice = 35 # Quant. param of SP/SI-Slices for Predicted Blocks (0-51) +SI_FRAMES = 0 # SI frame encoding flag (0=not used, 1=used) +SP_output = 0 # Controls whether coefficients will be output to encode switching SP frames (0=no, 1=yes) +SP_output_name = "low_quality.dat" # Filename for SP output coefficients +SP2_FRAMES = 0 # switching SP frame encoding flag (0=not used, 1=used) +SP2_input_name1 = "high_quality.dat" # Filename for the first swithed bitstream coefficients +SP2_input_name2 = "low_quality.dat" # Filename for the second switched bitstream coefficients + +########################################################################################## +# Output Control, NALs +########################################################################################## + +SymbolMode = 1 # Symbol mode (Entropy coding method: 0=UVLC, 1=CABAC) +OutFileMode = 0 # Output file mode, 0:Annex B, 1:RTP +PartitionMode = 0 # Partition Mode, 0: no DP, 1: 3 Partitions per Slice + +########################################################################################## +# CABAC context initialization +########################################################################################## + +ContextInitMethod = 1 # Context init (0: fixed, 1: adaptive) +FixedModelNumber = 0 # model number for fixed decision for inter slices ( 0, 1, or 2 ) + +########################################################################################## +# Interlace Handling +######################################################################################### + +PicInterlace = 0 # Picture AFF (0: frame coding, 1: field coding, 2:adaptive frame/field coding) +MbInterlace = 0 # Macroblock AFF (0: frame coding, 1: field coding, 2:adaptive frame/field coding, 3: frame MB-only AFF) +IntraBottom = 0 # Force Intra Bottom at GOP Period + +########################################################################################## +# Weighted Prediction +######################################################################################### + +WeightedPrediction = 0 # P picture Weighted Prediction (0=off, 1=explicit mode) +WeightedBiprediction = 0 # B picture Weighted Prediciton (0=off, 1=explicit mode, 2=implicit mode) +ChromaWeightSupport = 1 # Enable consideration of weights for Chroma components +UseWeightedReferenceME = 1 # Use weighted reference for ME (0=off, 1=on) +WPMethod = 1 # WP method (0: DC based, 1: LMS based) +WPIterMC = 0 # Iterative Motion compensated based weighted prediction method +EnhancedBWeightSupport = 0 # Enhanced B Weight support (needs revisit if we wish to merge with WPMethod) +WPMCPrecision = 0 # Improved Motion Compensation Precision using WP based methods. + # Clones WP references with slightly modified rounding offsets (Requires RDPictureDecision and GenerateMultiplePPS) : + # 0: disabled (default) + # 1: Up to one additional coding pass. Ref0 is 0, ref1 is 0 with a -1 offset + # 2: Up to two additional coding passes. (1) Ref0 is 0, ref1 is 0 with a -1 offset, (1) Ref0 is 0 with a -1 offset, ref1 is 0 +WPMCPrecFullRef = 0 # Increases the number of references in the reference picture lists to account + # for the lost reference slot when reordering is used during a coding pass in WPMCPrecision for reference replication. + # The number of references in non-reordered passes stays unchanged +WPMCPrecBSlice = 1 # 2: Apply rounding on every B slice. This efectively disables the evaluation of alternative QPs during RDPictureDecision. + # 1: Disable rounding for non-reference B slices. Non-reference B slices are evaluated for alternative QPs during RDPictureDecision. + # 0: Disable rounding for B slices. + +########################################################################################## +# Picture based Multi-pass encoding +######################################################################################### + +RDPictureDecision = 0 # Perform multiple pass coding and make RD optimal decision among them +RDPSliceBTest = 0 # Perform Slice level RD decision between P and B slices. +RDPictureMaxPassISlice = 1 # Max number of coding passes for I slices, valid values [1,3], default is 1 +RDPictureMaxPassPSlice = 2 # Max number of coding passes for P slices, valid values [1,6], default is 2 +RDPictureMaxPassBSlice = 3 # Max number of coding passes for B slices, valid values [1,6], default is 3 +RDPictureFrameQPPSlice = 0 # Perform additional frame level QP check (QP+/-1) for P slices, 0: disabled (default), 1: enabled +RDPictureFrameQPBSlice = 0 # Perform additional frame level QP check (QP+/-1) for B slices, 0: disabled, 1: enabled (default) +RDPictureDeblocking = 0 # Perform another coding pass to check non-deblocked picture, 0: disabled (default), 1: enabled +RDPictureDirectMode = 0 # Perform another coding pass to check the alternative direct mode for B slices, , 0: disabled (default), 1: enabled + +########################################################################################## +# Deblocking filter parameters +########################################################################################## + +DFParametersFlag = 0 # Configure deblocking filter (0=parameters below ignored, 1=parameters sent) + # Note that for pictures with multiple slice types, + # only the type of the first slice will be considered. +DFDisableRefISlice = 0 # Disable deblocking filter in reference I coded pictures (0=Filter, 1=No Filter). +DFAlphaRefISlice = 0 # Reference I coded pictures Alpha offset div. 2, {-6, -5, ... 0, +1, .. +6} +DFBetaRefISlice = 0 # Reference I coded pictures Beta offset div. 2, {-6, -5, ... 0, +1, .. +6} +DFDisableNRefISlice = 0 # Disable deblocking filter in non reference I coded pictures (0=Filter, 1=No Filter). +DFAlphaNRefISlice = 0 # Non Reference I coded pictures Alpha offset div. 2, {-6, -5, ... 0, +1, .. +6} +DFBetaNRefISlice = 0 # Non Reference I coded pictures Beta offset div. 2, {-6, -5, ... 0, +1, .. +6} +DFDisableRefPSlice = 0 # Disable deblocking filter in reference P coded pictures (0=Filter, 1=No Filter). +DFAlphaRefPSlice = 0 # Reference P coded pictures Alpha offset div. 2, {-6, -5, ... 0, +1, .. +6} +DFBetaRefPSlice = 0 # Reference P coded pictures Beta offset div. 2, {-6, -5, ... 0, +1, .. +6} +DFDisableNRefPSlice = 0 # Disable deblocking filter in non reference P coded pictures (0=Filter, 1=No Filter). +DFAlphaNRefPSlice = 0 # Non Reference P coded pictures Alpha offset div. 2, {-6, -5, ... 0, +1, .. +6} +DFBetaNRefPSlice = 0 # Non Reference P coded pictures Beta offset div. 2, {-6, -5, ... 0, +1, .. +6} +DFDisableRefBSlice = 0 # Disable deblocking filter in reference B coded pictures (0=Filter, 1=No Filter). +DFAlphaRefBSlice = 0 # Reference B coded pictures Alpha offset div. 2, {-6, -5, ... 0, +1, .. +6} +DFBetaRefBSlice = 0 # Reference B coded pictures Beta offset div. 2, {-6, -5, ... 0, +1, .. +6} +DFDisableNRefBSlice = 0 # Disable deblocking filter in non reference B coded pictures (0=Filter, 1=No Filter). +DFAlphaNRefBSlice = 0 # Non Reference B coded pictures Alpha offset div. 2, {-6, -5, ... 0, +1, .. +6} +DFBetaNRefBSlice = 0 # Non Reference B coded pictures Beta offset div. 2, {-6, -5, ... 0, +1, .. +6} + +########################################################################################## +# Error Resilience / Slices +########################################################################################## + +SliceMode = 0 # Slice mode (0=off 1=fixed #mb in slice 2=fixed #bytes in slice 3=use callback) +SliceArgument = 50 # Slice argument (Arguments to modes 1 and 2 above) + +num_slice_groups_minus1 = 0 # Number of Slice Groups Minus 1, 0 == no FMO, 1 == two slice groups, etc. +slice_group_map_type = 0 # 0: Interleave, 1: Dispersed, 2: Foreground with left-over, + # 3: Box-out, 4: Raster Scan 5: Wipe + # 6: Explicit, slice_group_id read from SliceGroupConfigFileName +slice_group_change_direction_flag = 0 # 0: box-out clockwise, raster scan or wipe right, + # 1: box-out counter clockwise, reverse raster scan or wipe left +slice_group_change_rate_minus1 = 85 # +SliceGroupConfigFileName = "sg0conf.cfg" # Used for slice_group_map_type 0, 2, 6 + +UseRedundantPicture = 0 # 0: not used, 1: enabled +NumRedundantHierarchy = 1 # 0-4 +PrimaryGOPLength = 10 # GOP length for redundant allocation (1-16) + # NumberReferenceFrames must be no less than PrimaryGOPLength when redundant slice enabled +NumRefPrimary = 1 # Actually used number of references for primary slices (1-16) + +########################################################################################## +# Search Range Restriction / RD Optimization +########################################################################################## + +RestrictSearchRange = 2 # restriction for (0: blocks and ref, 1: ref, 2: no restrictions) +RDOptimization = 1 # rd-optimized mode decision + # 0: RD-off (Low complexity mode) + # 1: RD-on (High complexity mode) + # 2: RD-on (Fast high complexity mode - not work in FREX Profiles) + # 3: with losses +I16RDOpt = 1 # perform rd-optimized mode decision for Intra 16x16 MB + # 0: SAD-based mode decision for Intra 16x16 MB + # 1: RD-based mode decision for Intra 16x16 MB +SubMBCodingState = 1 # submacroblock coding state + # 0: lowest complexity, do not store or reset coding state during sub-MB mode decision + # 1: medium complexity, reset to master coding state (for current mode) during sub-MB mode decision + # 2: highest complexity, store and reset coding state during sub-MB mode decision +DistortionSSIM = 0 # Compute SSIM distortion. (0: disabled/default, 1: enabled) +DistortionMS_SSIM = 0 # Compute Multiscale SSIM distortion. (0: disabled/default, 1: enabled) +SSIMOverlapSize = 8 # Overlap size to calculate SSIM distortion (1: pixel by pixel, 8: no overlap) +DistortionYUVtoRGB = 0 # Calculate distortion in RGB domain after conversion from YCbCr (0:off, 1:on) +CtxAdptLagrangeMult = 0 # Context Adaptive Lagrange Multiplier + # 0: disabled (default) + # 1: enabled (works best when RDOptimization=0) +FastCrIntraDecision = 1 # Fast Chroma intra mode decision (0:off, 1:on) +DisableThresholding = 1 # Disable Thresholding of Transform Coefficients (0:off, 1:on) +DisableBSkipRDO = 0 # Disable B Skip Mode consideration from RDO Mode decision (0:off, 1:on) +BiasSkipRDO = 0 # Negative Bias for Skip/DirectSkip modes (0: off, 1: on) +ForceTrueRateRDO = 0 # Force true rate (even zero values) during RDO process +SkipIntraInInterSlices = 0 # Skips Intra mode checking in inter slices if certain mode decisions are satisfied (0: off, 1: on) +WeightY = 1 # Luma weight for RDO +WeightCb = 1 # Cb weight for RDO +WeightCr = 1 # Cr weight for RDO + +########################################################################################## +# Explicit Lambda Usage +########################################################################################## +UseExplicitLambdaParams = 0 # Use explicit lambda scaling parameters (0:disabled, 1:enable lambda weight, 2: use explicit lambda value) +UpdateLambdaChromaME = 0 # Update lambda given Chroma ME consideration +FixedLambdaISlice = 0.1 # Fixed Lambda value for I slices +FixedLambdaPSlice = 0.1 # Fixed Lambda value for P slices +FixedLambdaBSlice = 0.1 # Fixed Lambda value for B slices +FixedLambdaRefBSlice = 0.1 # Fixed Lambda value for Referenced B slices +FixedLambdaSPSlice = 0.1 # Fixed Lambda value for SP slices +FixedLambdaSISlice = 0.1 # Fixed Lambda value for SI slices + +LambdaWeightISlice = 0.65 # scaling param for I slices. This will be used as a multiplier i.e. lambda=LambdaWeightISlice * 2^((QP-12)/3) +LambdaWeightPSlice = 0.68 # scaling param for P slices. This will be used as a multiplier i.e. lambda=LambdaWeightPSlice * 2^((QP-12)/3) +LambdaWeightBSlice = 0.68 # scaling param for B slices. This will be used as a multiplier i.e. lambda=LambdaWeightBSlice * 2^((QP-12)/3) +LambdaWeightRefBSlice = 0.68 # scaling param for Referenced B slices. This will be used as a multiplier i.e. lambda=LambdaWeightRefBSlice * 2^((QP-12)/3) +LambdaWeightSPSlice = 0.68 # scaling param for SP slices. This will be used as a multiplier i.e. lambda=LambdaWeightSPSlice * 2^((QP-12)/3) +LambdaWeightSISlice = 0.65 # scaling param for SI slices. This will be used as a multiplier i.e. lambda=LambdaWeightSISlice * 2^((QP-12)/3) + +LossRateA = 5 # expected packet loss rate of the channel for the first partition, only valid if RDOptimization = 3 +LossRateB = 0 # expected packet loss rate of the channel for the second partition, only valid if RDOptimization = 3 +LossRateC = 0 # expected packet loss rate of the channel for the third partition, only valid if RDOptimization = 3 +FirstFrameCorrect = 0 # If 1, the first frame is encoded under the assumption that it is always correctly received. +NumberOfDecoders = 30 # Numbers of decoders used to simulate the channel, only valid if RDOptimization = 3 +RestrictRefFrames = 0 # Doesnt allow reference to areas that have been intra updated in a later frame. + +########################################################################################## +# Additional Stuff +######################################################################################### + +UseConstrainedIntraPred = 0 # If 1, Inter pixels are not used for Intra macroblock prediction. + +NumberofLeakyBuckets = 8 # Number of Leaky Bucket values +LeakyBucketRateFile = "leakybucketrate.cfg" # File from which encoder derives rate values +LeakyBucketParamFile = "leakybucketparam.cfg" # File where encoder stores leakybucketparams + +NumFramesInELayerSubSeq = 0 # number of frames in the Enhanced Scalability Layer(0: no Enhanced Layer) + +SparePictureOption = 0 # (0: no spare picture info, 1: spare picture available) +SparePictureDetectionThr = 6 # Threshold for spare reference pictures detection +SparePicturePercentageThr = 92 # Threshold for the spare macroblock percentage + +PicOrderCntType = 0 # (0: POC mode 0, 1: POC mode 1, 2: POC mode 2) + +######################################################################################## +#Rate control +######################################################################################## + +RateControlEnable = 0 # 0 Disable, 1 Enable +Bitrate = 45020 # Bitrate(bps) +InitialQP = 0 # Initial Quantization Parameter for the first I frame + # InitialQp depends on two values: Bits Per Picture, + # and the GOP length +BasicUnit = 0 # Number of MBs in the basic unit + # should be a fraction of the total number + # of MBs in a frame ("0" sets a BU equal to a frame) +ChannelType = 0 # type of channel( 1=time varying channel; 0=Constant channel) +RCUpdateMode = 0 # Rate Control type. Modes supported : + # 0 = original JM rate control, + # 1 = rate control that is applied to all frames regardless of the slice type, + # 2 = original plus intelligent QP selection for I and B slices (including Hierarchical), + # 3 = original + hybrid quadratic rate control for I and B slice using bit rate statistics + # +RCISliceBitRatio = 1.0 # target ratio of bits for I-coded pictures compared to P-coded Pictures (for RCUpdateMode=3) +RCBSliceBitRatio0 = 0.5 # target ratio of bits for B-coded pictures compared to P-coded Pictures - temporal level 0 (for RCUpdateMode=3) +RCBSliceBitRatio1 = 0.25 # target ratio of bits for B-coded pictures compared to P-coded Pictures - temporal level 1 (for RCUpdateMode=3) +RCBSliceBitRatio2 = 0.25 # target ratio of bits for B-coded pictures compared to P-coded Pictures - temporal level 2 (for RCUpdateMode=3) +RCBSliceBitRatio3 = 0.25 # target ratio of bits for B-coded pictures compared to P-coded Pictures - temporal level 3 (for RCUpdateMode=3) +RCBSliceBitRatio4 = 0.25 # target ratio of bits for B-coded pictures compared to P-coded Pictures - temporal level 4 (for RCUpdateMode=3) +RCBoverPRatio = 0.45 # ratio of bit rate usage of a B-coded picture over a P-coded picture for the SAME QP (for RCUpdateMode=3) +RCIoverPRatio = 3.80 # ratio of bit rate usage of an I-coded picture over a P-coded picture for the SAME QP (for RCUpdateMode=3) +RCMinQPPSlice = 8 # minimum P Slice QP value for rate control +RCMaxQPPSlice = 42 # maximum P Slice QP value for rate control +RCMinQPBSlice = 8 # minimum B Slice QP value for rate control +RCMaxQPBSlice = 42 # maximum B Slice QP value for rate control +RCMinQPISlice = 8 # minimum I Slice QP value for rate control +RCMaxQPISlice = 42 # maximum I Slice QP value for rate control +RCMinQPSPSlice = 8 # minimum SP Slice QP value for rate control +RCMaxQPSPSlice = 40 # maximum SP Slice QP value for rate control +RCMinQPSISlice = 8 # minimum SI Slice QP value for rate control +RCMaxQPSISlice = 42 # maximum SI Slice QP value for rate control +RCMaxQPChange = 4 # maximum QP change for frames of the base layer + +######################################################################################## +#Fast Mode Decision +######################################################################################## +EarlySkipEnable = 0 # Early skip detection (0: Disable 1: Enable) +SelectiveIntraEnable = 0 # Selective Intra mode decision (0: Disable 1: Enable) + +######################################################################################## +#FREXT stuff +######################################################################################## + +YUVFormat = 1 # YUV format (0=4:0:0, 1=4:2:0, 2=4:2:2, 3=4:4:4) +RGBInput = 0 # 1=RGB input, 0=GBR or YUV input +SeparateColourPlane = 0 # 4:4:4 coding: 0=Common mode, 1=Independent mode +SourceBitDepthLuma = 8 # Source Bit Depth for Luma color component (8...14 bits) +SourceBitDepthChroma = 8 # Source Bit Depth for Chroma color components (8...14 bits) +SourceBitDepthRescale = 0 # Rescale bit depth of source for output (0: Disable 1: Enable) +OutputBitDepthLuma = 8 # Output Bit Depth for Luma color component (8...14 bits) +OutputBitDepthChroma = 8 # Output Bit Depth for Chroma color components (8...14 bits) + +CbQPOffset = 0 # Chroma QP offset for Cb-part (-51..51) +CrQPOffset = 0 # Chroma QP offset for Cr-part (-51..51) +Transform8x8Mode = 1 # (0: only 4x4 transform, 1: allow using 8x8 transform additionally, 2: only 8x8 transform) +ReportFrameStats = 0 # (0:Disable Frame Statistics 1: Enable) +DisplayEncParams = 0 # (0:Disable Display of Encoder Params 1: Enable) +Verbose = 1 # level of display verboseness + # 0: short, 1: normal (default), 2: detailed, 3: detailed/nvb +SkipGlobalStats = 0 # Disable global stat accumulation (Set to 1 to avoid bipred core dump) + +######################################################################################## +#Q-Matrix (FREXT) +######################################################################################## +QmatrixFile = "q_matrix.cfg" + +ScalingMatrixPresentFlag = 0 # Enable Q_Matrix (0 Not present, 1 Present in SPS, 2 Present in PPS, 3 Present in both SPS & PPS) +ScalingListPresentFlag0 = 3 # Intra4x4_Luma (0 Not present, 1 Present in SPS, 2 Present in PPS, 3 Present in both SPS & PPS) +ScalingListPresentFlag1 = 3 # Intra4x4_ChromaU (0 Not present, 1 Present in SPS, 2 Present in PPS, 3 Present in both SPS & PPS) +ScalingListPresentFlag2 = 3 # Intra4x4_chromaV (0 Not present, 1 Present in SPS, 2 Present in PPS, 3 Present in both SPS & PPS) +ScalingListPresentFlag3 = 3 # Inter4x4_Luma (0 Not present, 1 Present in SPS, 2 Present in PPS, 3 Present in both SPS & PPS) +ScalingListPresentFlag4 = 3 # Inter4x4_ChromaU (0 Not present, 1 Present in SPS, 2 Present in PPS, 3 Present in both SPS & PPS) +ScalingListPresentFlag5 = 3 # Inter4x4_ChromaV (0 Not present, 1 Present in SPS, 2 Present in PPS, 3 Present in both SPS & PPS) +ScalingListPresentFlag6 = 3 # Intra8x8_Luma (0 Not present, 1 Present in SPS, 2 Present in PPS, 3 Present in both SPS & PPS) +ScalingListPresentFlag7 = 3 # Inter8x8_Luma (0 Not present, 1 Present in SPS, 2 Present in PPS, 3 Present in both SPS & PPS) +ScalingListPresentFlag8 = 3 # Intra8x8_ChromaU for 4:4:4 (0 Not present, 1 Present in SPS, 2 Present in PPS, 3 Present in both SPS & PPS) +ScalingListPresentFlag9 = 3 # Inter8x8_ChromaU for 4:4:4 (0 Not present, 1 Present in SPS, 2 Present in PPS, 3 Present in both SPS & PPS) +ScalingListPresentFlag10 = 3 # Intra8x8_ChromaV for 4:4:4 (0 Not present, 1 Present in SPS, 2 Present in PPS, 3 Present in both SPS & PPS) +ScalingListPresentFlag11 = 3 # Inter8x8_ChromaV for 4:4:4 (0 Not present, 1 Present in SPS, 2 Present in PPS, 3 Present in both SPS & PPS) + +######################################################################################## +#Rounding Offset control +######################################################################################## + +OffsetMatrixPresentFlag = 0 # Enable Explicit Offset Quantization Matrices (0: disable 1: enable) +QOffsetMatrixFile = "q_offset.cfg" # Explicit Quantization Matrices file + +AdaptiveRounding = 1 # Enable Adaptive Rounding based on JVT-N011 (0: disable, 1: enable) +AdaptRoundingFixed = 0 # Enable Global Adaptive rounding for all qps (0: disable, 1: enable - default/old) +AdaptRndPeriod = 1 # Period in terms of MBs for updating rounding offsets. + # 0 performs update at the picture level. Default is 16. 1 is as in JVT-N011. +AdaptRndChroma = 1 # Enables coefficient rounding adaptation for chroma + +AdaptRndWFactorIRef = 8 # Adaptive Rounding Weight for I/SI slices in reference pictures /4096 +AdaptRndWFactorPRef = 8 # Adaptive Rounding Weight for P/SP slices in reference pictures /4096 +AdaptRndWFactorBRef = 8 # Adaptive Rounding Weight for B slices in reference pictures /4096 +AdaptRndWFactorINRef = 8 # Adaptive Rounding Weight for I/SI slices in non reference pictures /4096 +AdaptRndWFactorPNRef = 8 # Adaptive Rounding Weight for P/SP slices in non reference pictures /4096 +AdaptRndWFactorBNRef = 8 # Adaptive Rounding Weight for B slices in non reference pictures /4096 + +AdaptRndCrWFactorIRef = 8 # Chroma Adaptive Rounding Weight for I/SI slices in reference pictures /4096 +AdaptRndCrWFactorPRef = 8 # Chroma Adaptive Rounding Weight for P/SP slices in reference pictures /4096 +AdaptRndCrWFactorBRef = 8 # Chroma Adaptive Rounding Weight for B slices in reference pictures /4096 +AdaptRndCrWFactorINRef = 8 # Chroma Adaptive Rounding Weight for I/SI slices in non reference pictures /4096 +AdaptRndCrWFactorPNRef = 8 # Chroma Adaptive Rounding Weight for P/SP slices in non reference pictures /4096 +AdaptRndCrWFactorBNRef = 8 # Chroma Adaptive Rounding Weight for B slices in non reference pictures /4096 + +################################################################ +# Rate Distortion Optimized Quantization +################################################################ +UseRDOQuant = 0 # Use Rate Distortion Optimized Quantization (0=disable, 1=enable) +RDOQ_DC = 1 # Enable Rate Distortion Optimized Quantization for DC components (0=disable, 1=enable) +RDOQ_CR = 1 # Enable Rate Distortion Optimized Quantization for Chroma components (0=disable, 1=enable) +RDOQ_DC_CR = 1 # Enable Rate Distortion Optimized Quantization for Chroma DC components (0=disable, 1=enable) +RDOQ_QP_Num = 5 # 1-9: Number of QP tested in RDO_Q (I/P/B slice) +RDOQ_CP_Mode = 0 # copy Mode from first QP tested +RDOQ_CP_MV = 0 # copy MV from first QP tested +RDOQ_Fast = 0 # Fast RDOQ decision method for multiple QPs + +######################################################################################## +#Lossless Coding (FREXT) +######################################################################################## + +LosslessCoding = 0 # Enable lossless coding when qpprime_y is zero (0 Disabled, 1 Enabled) + +######################################################################################## +#Fast Motion Estimation Control Parameters +######################################################################################## + +SearchMode = 3 # Motion estimation mode + # -1 = Full Search + # 0 = Fast Full Search (default) + # 1 = UMHexagon Search + # 2 = Simplified UMHexagon Search + # 3 = Enhanced Predictive Zonal Search (EPZS) + +UMHexDSR = 1 # Use Search Range Prediction. Only for UMHexagonS method + # (0:disable, 1:enabled/default) +UMHexScale = 3 # Use Scale_factor for different image sizes. Only for UMHexagonS method + # (0:disable, 3:/default) + # Increasing value can speed up Motion Search. + +EPZSPattern = 2 # Select EPZS primary refinement pattern. + # (0: small diamond, 1: square, 2: extended diamond/default, + # 3: large diamond, 4: SBP Large Diamond, + # 5: PMVFAST ) +EPZSDualRefinement = 3 # Enables secondary refinement pattern. + # (0:disabled, 1: small diamond, 2: square, + # 3: extended diamond/default, 4: large diamond, + # 5: SBP Large Diamond, 6: PMVFAST ) +EPZSFixedPredictors = 2 # Enables Window based predictors + # (0:disabled, 1: P only, 2: P and B/default) +EPZSTemporal = 1 # Enables temporal predictors + # (0: disabled, 1: enabled/default) +EPZSSpatialMem = 1 # Enables spatial memory predictors + # (0: disabled, 1: enabled/default) +EPZSBlockType = 1 # Enables block type Predictors + # (0: disabled, 1: enabled/default) +EPZSMinThresScale = 0 # Scaler for EPZS minimum threshold (0 default). + # Increasing value can speed up encoding. +EPZSMedThresScale = 1 # Scaler for EPZS median threshold (1 default). + # Increasing value can speed up encoding. +EPZSMaxThresScale = 2 # Scaler for EPZS maximum threshold (1 default). + # Increasing value can speed up encoding. +EPZSSubPelME = 1 # EPZS Subpel ME consideration +EPZSSubPelMEBiPred = 1 # EPZS Subpel ME consideration for BiPred partitions +EPZSSubPelThresScale = 1 # EPZS Subpel ME Threshold scaler +EPZSSubPelGrid = 1 # Perform EPZS using a subpixel grid + +######################################################################################## +# SEI Parameters +######################################################################################## + +ToneMappingSEIPresentFlag = 0 # Enable Tone mapping SEI (0 Not present, 1 Present) +ToneMappingFile = "ToneMapping.cfg" + +GenerateSEIMessage = 0 # Generate an SEI Text Message +SEIMessageText = "H.264/AVC Encoder" # Text SEI Message + +UseMVLimits = 0 # Use MV Limits +SetMVXLimit = 512 # Horizontal MV Limit (in integer units) +SetMVYLimit = 512 # Vertical MV Limit (in integer units) + +######################################################################################## +# VUI Parameters +######################################################################################## +# the variables below do not affect encoding and decoding +# (many are dummy variables but others can be useful when supported by the decoder) + +EnableVUISupport = 0 # Enable VUI Parameters + +# display parameters +VUI_aspect_ratio_info_present_flag = 0 +VUI_aspect_ratio_idc = 1 +VUI_sar_width = 0 +VUI_sar_height = 0 +VUI_overscan_info_present_flag = 0 +VUI_overscan_appropriate_flag = 0 +VUI_video_signal_type_present_flag = 0 +VUI_video_format = 5 +VUI_video_full_range_flag = 0 +VUI_colour_description_present_flag = 0 +VUI_colour_primaries = 2 +VUI_transfer_characteristics = 2 +VUI_matrix_coefficients = 2 +VUI_chroma_location_info_present_flag = 0 +VUI_chroma_sample_loc_type_top_field = 0 +VUI_chroma_sample_loc_type_bottom_field = 0 +VUI_timing_info_present_flag = 0 +VUI_num_units_in_tick = 1000 +VUI_time_scale = 60000 +VUI_fixed_frame_rate_flag = 0 + +# nal hrd parameters +VUI_nal_hrd_parameters_present_flag = 0 +VUI_nal_cpb_cnt_minus1 = 0 +VUI_nal_bit_rate_scale = 0 +VUI_nal_cpb_size_scale = 0 +VUI_nal_bit_rate_value_minus1 = 0 +VUI_nal_cpb_size_value_minus1 = 0 +VUI_nal_vbr_cbr_flag = 0 +VUI_nal_initial_cpb_removal_delay_length_minus1 = 23 +VUI_nal_cpb_removal_delay_length_minus1 = 23 +VUI_nal_dpb_output_delay_length_minus1 = 23 +VUI_nal_time_offset_length = 24 + +# vlc hrd parameters +VUI_vcl_hrd_parameters_present_flag = 0 +VUI_vcl_cpb_cnt_minus1 = 0 +VUI_vcl_bit_rate_scale = 0 +VUI_vcl_cpb_size_scale = 0 +VUI_vcl_bit_rate_value_minus1 = 0 +VUI_vcl_cpb_size_value_minus1 = 0 +VUI_vcl_vbr_cbr_flag = 0 +VUI_vcl_initial_cpb_removal_delay_length_minus1 = 23 +VUI_vcl_cpb_removal_delay_length_minus1 = 23 +VUI_vcl_dpb_output_delay_length_minus1 = 23 +VUI_vcl_time_offset_length = 24 +VUI_low_delay_hrd_flag = 0 + +# other parameters (i.e. bitsream restrictions) +VUI_pic_struct_present_flag = 0 +VUI_bitstream_restriction_flag = 0 +VUI_motion_vectors_over_pic_boundaries_flag = 1 +VUI_max_bytes_per_pic_denom = 0 +VUI_max_bits_per_mb_denom = 0 +VUI_log2_max_mv_length_vertical = 16 +VUI_log2_max_mv_length_horizontal = 16 +VUI_num_reorder_frames = 16 +VUI_max_dec_frame_buffering = 16 + diff --git a/bin/encoder_main.cfg b/bin/encoder_main.cfg new file mode 100644 index 0000000..2b8109e --- /dev/null +++ b/bin/encoder_main.cfg @@ -0,0 +1,499 @@ +# New Input File Format is as follows +# = # Comment +# +# See configfile.h for a list of supported ParameterNames +# +# For bug reporting and known issues see: +# https://ipbt.hhi.fraunhofer.de + + +########################################################################################## +# Files +########################################################################################## +InputFile = "foreman_part_qcif.yuv" # Input sequence +InputHeaderLength = 0 # If the inputfile has a header, state it's length in byte here +StartFrame = 0 # Start frame for encoding. (0-N) +FramesToBeEncoded = 3 # Number of frames to be coded +FrameRate = 30.0 # Frame Rate per second (0.1-100.0) +SourceWidth = 176 # Source frame width +SourceHeight = 144 # Source frame height +SourceResize = 0 # Resize source size for output +OutputWidth = 176 # Output frame width +OutputHeight = 144 # Output frame height + +TraceFile = "trace_enc.txt" # Trace file +ReconFile = "test_rec.yuv" # Recontruction YUV file +OutputFile = "test.264" # Bitstream +StatsFile = "stats.dat" # Coding statistics file + +########################################################################################## +# Encoder Control +########################################################################################## +ProfileIDC = 77 # Profile IDC (66=baseline, 77=main, 88=extended; FREXT Profiles: 100=High, 110=High 10, 122=High 4:2:2, 244=High 4:4:4, 44=CAVLC 4:4:4 Intra) +IntraProfile = 0 # Activate Intra Profile for FRExt (0: false, 1: true) + # (e.g. ProfileIDC=110, IntraProfile=1 => High 10 Intra Profile) +LevelIDC = 40 # Level IDC (e.g. 20 = level 2.0) + +IntraPeriod = 0 # Period of I-pictures (0=only first) +IDRPeriod = 0 # Period of IDR pictures (0=only first) +AdaptiveIntraPeriod = 1 # Adaptive intra period +AdaptiveIDRPeriod = 0 # Adaptive IDR period +IntraDelay = 0 # Intra (IDR) picture delay (i.e. coding structure of PPIPPP... ) +EnableIDRGOP = 0 # Support for IDR closed GOPs (0: disabled, 1: enabled) +EnableOpenGOP = 0 # Support for open GOPs (0: disabled, 1: enabled) +QPISlice = 28 # Quant. param for I Slices (0-51) +QPPSlice = 28 # Quant. param for P Slices (0-51) +FrameSkip = 0 # Number of frames to be skipped in input (e.g 2 will code every third frame). + # Note that this now excludes intermediate (i.e. B) coded pictures +ChromaQPOffset = 0 # Chroma QP offset (-51..51) + +DisableSubpelME = 0 # Disable Subpixel Motion Estimation (0=off/default, 1=on) +SearchRange = 32 # Max search range + +MEDistortionFPel = 0 # Select error metric for Full-Pel ME (0: SAD, 1: SSE, 2: Hadamard SAD) +MEDistortionHPel = 2 # Select error metric for Half-Pel ME (0: SAD, 1: SSE, 2: Hadamard SAD) +MEDistortionQPel = 2 # Select error metric for Quarter-Pel ME (0: SAD, 1: SSE, 2: Hadamard SAD) +MDDistortion = 2 # Select error metric for Mode Decision (0: SAD, 1: SSE, 2: Hadamard SAD) +SkipDeBlockNonRef = 0 # Skip Deblocking (regardless of DFParametersFlag) for non-reference frames (0: off, 1: on) +ChromaMCBuffer = 1 # Calculate Color component interpolated values in advance and store them. + # Provides a trade-off between memory and computational complexity + # (0: disabled/default, 1: enabled) +ChromaMEEnable = 0 # Take into account Color component information during ME + # (0: only first component/default, + # 1: All Color components - Integer refinement only + # 2: All Color components - All refinements) +ChromaMEWeight = 1 # Weighting for chroma components. This parameter should have a relationship with color format. + +NumberReferenceFrames = 5 # Number of previous frames used for inter motion search (0-16) + +PList0References = 0 # P slice List 0 reference override (0 disable, N <= NumberReferenceFrames) +Log2MaxFNumMinus4 = 0 # Sets log2_max_frame_num_minus4 (-1 : based on FramesToBeEncoded/Auto, >=0 : Log2MaxFNumMinus4) +Log2MaxPOCLsbMinus4 = -1 # Sets log2_max_pic_order_cnt_lsb_minus4 (-1 : Auto, >=0 : Log2MaxPOCLsbMinus4) + +GenerateMultiplePPS = 0 # Transmit multiple parameter sets. Currently parameters basically enable all WP modes (0: diabled, 1: enabled) +ResendPPS = 0 # Resend PPS (with pic_parameter_set_id 0) for every coded Frame/Field pair (0: disabled, 1: enabled) + +MbLineIntraUpdate = 0 # Error robustness(extra intra macro block updates)(0=off, N: One GOB every N frames are intra coded) +RandomIntraMBRefresh = 0 # Forced intra MBs per picture + +########################################################################################## +# PSlice Mode types +########################################################################################## +PSliceSkip = 1 # P-Slice Skip mode consideration (0=disable, 1=enable) +PSliceSearch16x16 = 1 # P-Slice Inter block search 16x16 (0=disable, 1=enable) +PSliceSearch16x8 = 1 # P-Slice Inter block search 16x8 (0=disable, 1=enable) +PSliceSearch8x16 = 1 # P-Slice Inter block search 8x16 (0=disable, 1=enable) +PSliceSearch8x8 = 1 # P-Slice Inter block search 8x8 (0=disable, 1=enable) +PSliceSearch8x4 = 1 # P-Slice Inter block search 8x4 (0=disable, 1=enable) +PSliceSearch4x8 = 1 # P-Slice Inter block search 4x8 (0=disable, 1=enable) +PSliceSearch4x4 = 1 # P-Slice Inter block search 4x4 (0=disable, 1=enable) + +########################################################################################## +# BSlice Mode types +########################################################################################## + +BSliceDirect = 1 # B-Slice Skip mode consideration (0=disable, 1=enable) +BSliceSearch16x16 = 1 # B-Slice Inter block search 16x16 (0=disable, 1=enable) +BSliceSearch16x8 = 1 # B-Slice Inter block search 16x8 (0=disable, 1=enable) +BSliceSearch8x16 = 1 # B-Slice Inter block search 8x16 (0=disable, 1=enable) +BSliceSearch8x8 = 1 # B-Slice Inter block search 8x8 (0=disable, 1=enable) +BSliceSearch8x4 = 1 # B-Slice Inter block search 8x4 (0=disable, 1=enable) +BSliceSearch4x8 = 1 # B-Slice Inter block search 4x8 (0=disable, 1=enable) +BSliceSearch4x4 = 1 # B-Slice Inter block search 4x4 (0=disable, 1=enable) + +BiPredSearch16x16 = 1 # B-Slice Bi-prediction block search 16x16 (0=disable, 1=enable) +BiPredSearch16x8 = 1 # B-Slice Bi-prediction block search 16x8 (0=disable, 1=enable) +BiPredSearch8x16 = 1 # B-Slice Bi-prediction block search 8x16 (0=disable, 1=enable) +BiPredSearch8x8 = 0 # B-Slice Bi-prediction block search 8x8 (0=disable, 1=enable) + +DisableIntra4x4 = 0 # Disable Intra 4x4 modes +DisableIntra16x16 = 0 # Disable Intra 16x16 modes +DisableIntraInInter = 0 # Disable Intra modes for inter slices +IntraDisableInterOnly = 0 # Apply Disabling Intra conditions only to Inter Slices (0:disable/default,1: enable) +Intra4x4ParDisable = 0 # Disable Vertical & Horizontal 4x4 +Intra4x4DiagDisable = 0 # Disable Diagonal 45degree 4x4 +Intra4x4DirDisable = 0 # Disable Other Diagonal 4x4 +Intra16x16ParDisable = 0 # Disable Vertical & Horizontal 16x16 +Intra16x16PlaneDisable = 0 # Disable Planar 16x16 +ChromaIntraDisable = 0 # Disable Intra Chroma modes other than DC +EnableIPCM = 1 # Enable IPCM macroblock mode + +DisposableP = 0 # Enable Disposable P slices in the primary layer (0: disable/default, 1: enable) +DispPQPOffset = 0 # Quantizer offset for disposable P slices (0: default) + +PreferDispOrder = 1 # Prefer display order when building the prediction structure as opposed to coding order (affects intra and IDR periodic insertion, among others) +PreferPowerOfTwo = 0 # Prefer prediction structures that have lengths expressed as powers of two +FrmStructBufferLength = 16 # Length of the frame structure unit buffer; it can be overriden for certain cases + +ChangeQPFrame = 0 # Frame in display order from which to apply the Change QP offsets +ChangeQPI = 0 # Change QP offset value for I_SLICE +ChangeQPP = 0 # Change QP offset value for P_SLICE +ChangeQPB = 0 # Change QP offset value for B_SLICE +ChangeQPSI = 0 # Change QP offset value for SI_SLICE +ChangeQPSP = 0 # Change QP offset value for SP_SLICE + +########################################################################################## +# B Slices +########################################################################################## + +NumberBFrames = 1 # Number of B coded frames inserted (0=not used) +PReplaceBSlice = 0 # Replace B-coded slices with P-coded slices when NumberBFrames>0 +QPBSlice = 30 # Quant. param for B slices (0-51) +BRefPicQPOffset = -1 # Quantization offset for reference B coded pictures (-51..51) +DirectModeType = 1 # Direct Mode Type (0:Temporal 1:Spatial) +DirectInferenceFlag = 1 # Direct Inference Flag (0: Disable 1: Enable) +BList0References = 0 # B slice List 0 reference override (0 disable, N <= NumberReferenceFrames) +BList1References = 1 # B slice List 1 reference override (0 disable, N <= NumberReferenceFrames) + # 1 List1 reference is usually recommended for normal GOP Structures. + # A larger value is usually more appropriate if a more flexible + # structure is used (i.e. using HierarchicalCoding) + +BReferencePictures = 0 # Referenced B coded pictures (0=off, 1=B references for secondary layer, 2=B references for primary layer) + +HierarchicalCoding = 0 # B hierarchical coding (0= off, 1= 2 layers, 2= 2 full hierarchy, 3 = explicit) +HierarchyLevelQPEnable = 1 # Adjust QP based on hierarchy level (in increments of 1). Overrides BRefPicQPOffset behavior.(0=off, 1=on) +ExplicitHierarchyFormat = "b1r0b3r0b2e2b0e2b4r2" # Explicit Enhancement GOP. Format is {FrameDisplay_orderReferenceQP}. + # Valid values for reference type is r:reference, e:non reference. +ExplicitSeqCoding = 0 # Enable support for explicit sequence coding +ExplicitSeqFile = "explicit_seq.cfg" +LowDelay = 0 # Apply HierarchicalCoding without delay (i.e., encode in the captured/display order) +ReferenceReorder = 1 # Reorder References according to Poc distance for HierarchicalCoding (0=off, 1=enable, 2=use when LowDelay is set) +PocMemoryManagement = 1 # Memory management based on Poc Distances for HierarchicalCoding (0=off, 1=on, 2=use when LowDelay is set) +SetFirstAsLongTerm = 0 # Set first frame as long term + +BiPredMotionEstimation = 1 # Enable Bipredictive based Motion Estimation (0:disabled, 1:enabled) +BiPredMERefinements = 3 # Bipredictive ME extra refinements (0: single, N: N extra refinements (1 default) +BiPredMESearchRange = 16 # Bipredictive ME Search range (8 default). Note that range is halved for every extra refinement. +BiPredMESubPel = 2 # Bipredictive ME Subpixel Consideration (0: disabled, 1: single level, 2: dual level) + + +########################################################################################## +# Output Control, NALs +########################################################################################## + +SymbolMode = 1 # Symbol mode (Entropy coding method: 0=UVLC, 1=CABAC) +OutFileMode = 0 # Output file mode, 0:Annex B, 1:RTP + +########################################################################################## +# CABAC context initialization +########################################################################################## + +ContextInitMethod = 1 # Context init (0: fixed, 1: adaptive) +FixedModelNumber = 0 # model number for fixed decision for inter slices ( 0, 1, or 2 ) + +########################################################################################## +# Interlace Handling +######################################################################################### + +PicInterlace = 0 # Picture AFF (0: frame coding, 1: field coding, 2:adaptive frame/field coding) +MbInterlace = 0 # Macroblock AFF (0: frame coding, 1: field coding, 2:adaptive frame/field coding, 3: frame MB-only AFF) +IntraBottom = 0 # Force Intra Bottom at GOP Period + +########################################################################################## +# Weighted Prediction +######################################################################################### + +WeightedPrediction = 0 # P picture Weighted Prediction (0=off, 1=explicit mode) +WeightedBiprediction = 0 # B picture Weighted Prediciton (0=off, 1=explicit mode, 2=implicit mode) +ChromaWeightSupport = 1 # Enable consideration of weights for Chroma components +UseWeightedReferenceME = 1 # Use weighted reference for ME (0=off, 1=on) +WPMethod = 1 # WP method (0: DC based, 1: LMS based) +WPIterMC = 0 # Iterative Motion compensated based weighted prediction method +EnhancedBWeightSupport = 0 # Enhanced B Weight support (needs revisit if we wish to merge with WPMethod) +WPMCPrecision = 0 # Improved Motion Compensation Precision using WP based methods. + # Clones WP references with slightly modified rounding offsets (Requires RDPictureDecision and GenerateMultiplePPS) : + # 0: disabled (default) + # 1: Up to one additional coding pass. Ref0 is 0, ref1 is 0 with a -1 offset + # 2: Up to two additional coding passes. (1) Ref0 is 0, ref1 is 0 with a -1 offset, (1) Ref0 is 0 with a -1 offset, ref1 is 0 +WPMCPrecFullRef = 0 # Increases the number of references in the reference picture lists to account + # for the lost reference slot when reordering is used during a coding pass in WPMCPrecision for reference replication. + # The number of references in non-reordered passes stays unchanged +WPMCPrecBSlice = 1 # 2: Apply rounding on every B slice. This efectively disables the evaluation of alternative QPs during RDPictureDecision. + # 1: Disable rounding for non-reference B slices. Non-reference B slices are evaluated for alternative QPs during RDPictureDecision. + # 0: Disable rounding for B slices. + +########################################################################################## +# Picture based Multi-pass encoding +######################################################################################### + +RDPictureDecision = 0 # Perform multiple pass coding and make RD optimal decision among them +RDPSliceBTest = 0 # Perform Slice level RD decision between P and B slices. +RDPictureMaxPassISlice = 1 # Max number of coding passes for I slices, valid values [1,3], default is 1 +RDPictureMaxPassPSlice = 2 # Max number of coding passes for P slices, valid values [1,6], default is 2 +RDPictureMaxPassBSlice = 3 # Max number of coding passes for B slices, valid values [1,6], default is 3 +RDPictureFrameQPPSlice = 0 # Perform additional frame level QP check (QP+/-1) for P slices, 0: disabled (default), 1: enabled +RDPictureFrameQPBSlice = 0 # Perform additional frame level QP check (QP+/-1) for B slices, 0: disabled, 1: enabled (default) +RDPictureDeblocking = 0 # Perform another coding pass to check non-deblocked picture, 0: disabled (default), 1: enabled +RDPictureDirectMode = 0 # Perform another coding pass to check the alternative direct mode for B slices, , 0: disabled (default), 1: enabled + +########################################################################################## +# Deblocking filter parameters +########################################################################################## + +DFParametersFlag = 0 # Configure deblocking filter (0=parameters below ignored, 1=parameters sent) + # Note that for pictures with multiple slice types, + # only the type of the first slice will be considered. +DFDisableRefISlice = 0 # Disable deblocking filter in reference I coded pictures (0=Filter, 1=No Filter). +DFAlphaRefISlice = 0 # Reference I coded pictures Alpha offset div. 2, {-6, -5, ... 0, +1, .. +6} +DFBetaRefISlice = 0 # Reference I coded pictures Beta offset div. 2, {-6, -5, ... 0, +1, .. +6} +DFDisableNRefISlice = 0 # Disable deblocking filter in non reference I coded pictures (0=Filter, 1=No Filter). +DFAlphaNRefISlice = 0 # Non Reference I coded pictures Alpha offset div. 2, {-6, -5, ... 0, +1, .. +6} +DFBetaNRefISlice = 0 # Non Reference I coded pictures Beta offset div. 2, {-6, -5, ... 0, +1, .. +6} +DFDisableRefPSlice = 0 # Disable deblocking filter in reference P coded pictures (0=Filter, 1=No Filter). +DFAlphaRefPSlice = 0 # Reference P coded pictures Alpha offset div. 2, {-6, -5, ... 0, +1, .. +6} +DFBetaRefPSlice = 0 # Reference P coded pictures Beta offset div. 2, {-6, -5, ... 0, +1, .. +6} +DFDisableNRefPSlice = 0 # Disable deblocking filter in non reference P coded pictures (0=Filter, 1=No Filter). +DFAlphaNRefPSlice = 0 # Non Reference P coded pictures Alpha offset div. 2, {-6, -5, ... 0, +1, .. +6} +DFBetaNRefPSlice = 0 # Non Reference P coded pictures Beta offset div. 2, {-6, -5, ... 0, +1, .. +6} +DFDisableRefBSlice = 0 # Disable deblocking filter in reference B coded pictures (0=Filter, 1=No Filter). +DFAlphaRefBSlice = 0 # Reference B coded pictures Alpha offset div. 2, {-6, -5, ... 0, +1, .. +6} +DFBetaRefBSlice = 0 # Reference B coded pictures Beta offset div. 2, {-6, -5, ... 0, +1, .. +6} +DFDisableNRefBSlice = 0 # Disable deblocking filter in non reference B coded pictures (0=Filter, 1=No Filter). +DFAlphaNRefBSlice = 0 # Non Reference B coded pictures Alpha offset div. 2, {-6, -5, ... 0, +1, .. +6} +DFBetaNRefBSlice = 0 # Non Reference B coded pictures Beta offset div. 2, {-6, -5, ... 0, +1, .. +6} + +########################################################################################## +# Error Resilience / Slices +########################################################################################## + +SliceMode = 0 # Slice mode (0=off 1=fixed #mb in slice 2=fixed #bytes in slice 3=use callback) +SliceArgument = 50 # Slice argument (Arguments to modes 1 and 2 above) + +UseRedundantPicture = 0 # 0: not used, 1: enabled +NumRedundantHierarchy = 1 # 0-4 +PrimaryGOPLength = 10 # GOP length for redundant allocation (1-16) + # NumberReferenceFrames must be no less than PrimaryGOPLength when redundant slice enabled +NumRefPrimary = 1 # Actually used number of references for primary slices (1-16) + +########################################################################################## +# Search Range Restriction / RD Optimization +########################################################################################## + +RestrictSearchRange = 2 # restriction for (0: blocks and ref, 1: ref, 2: no restrictions) +RDOptimization = 1 # rd-optimized mode decision + # 0: RD-off (Low complexity mode) + # 1: RD-on (High complexity mode) + # 2: RD-on (Fast high complexity mode - not work in FREX Profiles) + # 3: with losses +I16RDOpt = 0 # perform rd-optimized mode decision for Intra 16x16 MB + # 0: SAD-based mode decision for Intra 16x16 MB + # 1: RD-based mode decision for Intra 16x16 MB +SubMBCodingState = 1 # submacroblock coding state + # 0: lowest complexity, do not store or reset coding state during sub-MB mode decision + # 1: medium complexity, reset to master coding state (for current mode) during sub-MB mode decision + # 2: highest complexity, store and reset coding state during sub-MB mode decision +DistortionSSIM = 0 # Compute SSIM distortion. (0: disabled/default, 1: enabled) +DistortionMS_SSIM = 0 # Compute Multiscale SSIM distortion. (0: disabled/default, 1: enabled) +SSIMOverlapSize = 8 # Overlap size to calculate SSIM distortion (1: pixel by pixel, 8: no overlap) +DistortionYUVtoRGB = 0 # Calculate distortion in RGB domain after conversion from YCbCr (0:off, 1:on) +CtxAdptLagrangeMult = 0 # Context Adaptive Lagrange Multiplier + # 0: disabled (default) + # 1: enabled (works best when RDOptimization=0) +FastCrIntraDecision = 1 # Fast Chroma intra mode decision (0:off, 1:on) +DisableThresholding = 0 # Disable Thresholding of Transform Coefficients (0:off, 1:on) +DisableBSkipRDO = 0 # Disable B Skip Mode consideration from RDO Mode decision (0:off, 1:on) +BiasSkipRDO = 1 # Negative Bias for Skip/DirectSkip modes (0: off, 1: on) +ForceTrueRateRDO = 0 # Force true rate (even zero values) during RDO process +SkipIntraInInterSlices = 0 # Skips Intra mode checking in inter slices if certain mode decisions are satisfied (0: off, 1: on) +WeightY = 1 # Luma weight for RDO +WeightCb = 1 # Cb weight for RDO +WeightCr = 1 # Cr weight for RDO + +########################################################################################## +# Explicit Lambda Usage +########################################################################################## +UseExplicitLambdaParams = 0 # Use explicit lambda scaling parameters (0:disabled, 1:enable lambda weight, 2: use explicit lambda value) +UpdateLambdaChromaME = 0 # Update lambda given Chroma ME consideration +FixedLambdaISlice = 0.1 # Fixed Lambda value for I slices +FixedLambdaPSlice = 0.1 # Fixed Lambda value for P slices +FixedLambdaBSlice = 0.1 # Fixed Lambda value for B slices +FixedLambdaRefBSlice = 0.1 # Fixed Lambda value for Referenced B slices +FixedLambdaSPSlice = 0.1 # Fixed Lambda value for SP slices +FixedLambdaSISlice = 0.1 # Fixed Lambda value for SI slices + +LambdaWeightISlice = 0.65 # scaling param for I slices. This will be used as a multiplier i.e. lambda=LambdaWeightISlice * 2^((QP-12)/3) +LambdaWeightPSlice = 0.68 # scaling param for P slices. This will be used as a multiplier i.e. lambda=LambdaWeightPSlice * 2^((QP-12)/3) +LambdaWeightBSlice = 0.68 # scaling param for B slices. This will be used as a multiplier i.e. lambda=LambdaWeightBSlice * 2^((QP-12)/3) +LambdaWeightRefBSlice = 0.68 # scaling param for Referenced B slices. This will be used as a multiplier i.e. lambda=LambdaWeightRefBSlice * 2^((QP-12)/3) +LambdaWeightSPSlice = 0.68 # scaling param for SP slices. This will be used as a multiplier i.e. lambda=LambdaWeightSPSlice * 2^((QP-12)/3) +LambdaWeightSISlice = 0.65 # scaling param for SI slices. This will be used as a multiplier i.e. lambda=LambdaWeightSISlice * 2^((QP-12)/3) + +LossRateA = 5 # expected packet loss rate of the channel for the first partition, only valid if RDOptimization = 3 +LossRateB = 0 # expected packet loss rate of the channel for the second partition, only valid if RDOptimization = 3 +LossRateC = 0 # expected packet loss rate of the channel for the third partition, only valid if RDOptimization = 3 +FirstFrameCorrect = 0 # If 1, the first frame is encoded under the assumption that it is always correctly received. +NumberOfDecoders = 30 # Numbers of decoders used to simulate the channel, only valid if RDOptimization = 3 +RestrictRefFrames = 0 # Doesnt allow reference to areas that have been intra updated in a later frame. + +########################################################################################## +# Additional Stuff +######################################################################################### + +UseConstrainedIntraPred = 0 # If 1, Inter pixels are not used for Intra macroblock prediction. + +NumberofLeakyBuckets = 8 # Number of Leaky Bucket values +LeakyBucketRateFile = "leakybucketrate.cfg" # File from which encoder derives rate values +LeakyBucketParamFile = "leakybucketparam.cfg" # File where encoder stores leakybucketparams + +NumFramesInELayerSubSeq = 0 # number of frames in the Enhanced Scalability Layer(0: no Enhanced Layer) + +SparePictureOption = 0 # (0: no spare picture info, 1: spare picture available) +SparePictureDetectionThr = 6 # Threshold for spare reference pictures detection +SparePicturePercentageThr = 92 # Threshold for the spare macroblock percentage + +PicOrderCntType = 0 # (0: POC mode 0, 1: POC mode 1, 2: POC mode 2) + +######################################################################################## +#Rate control +######################################################################################## + +RateControlEnable = 0 # 0 Disable, 1 Enable +Bitrate = 45020 # Bitrate(bps) +InitialQP = 0 # Initial Quantization Parameter for the first I frame + # InitialQp depends on two values: Bits Per Picture, + # and the GOP length +BasicUnit = 0 # Number of MBs in the basic unit + # should be a fraction of the total number + # of MBs in a frame ("0" sets a BU equal to a frame) +ChannelType = 0 # type of channel( 1=time varying channel; 0=Constant channel) +RCUpdateMode = 0 # Rate Control type. Modes supported : + # 0 = original JM rate control, + # 1 = rate control that is applied to all frames regardless of the slice type, + # 2 = original plus intelligent QP selection for I and B slices (including Hierarchical), + # 3 = original + hybrid quadratic rate control for I and B slice using bit rate statistics + # +RCISliceBitRatio = 1.0 # target ratio of bits for I-coded pictures compared to P-coded Pictures (for RCUpdateMode=3) +RCBSliceBitRatio0 = 0.5 # target ratio of bits for B-coded pictures compared to P-coded Pictures - temporal level 0 (for RCUpdateMode=3) +RCBSliceBitRatio1 = 0.25 # target ratio of bits for B-coded pictures compared to P-coded Pictures - temporal level 1 (for RCUpdateMode=3) +RCBSliceBitRatio2 = 0.25 # target ratio of bits for B-coded pictures compared to P-coded Pictures - temporal level 2 (for RCUpdateMode=3) +RCBSliceBitRatio3 = 0.25 # target ratio of bits for B-coded pictures compared to P-coded Pictures - temporal level 3 (for RCUpdateMode=3) +RCBSliceBitRatio4 = 0.25 # target ratio of bits for B-coded pictures compared to P-coded Pictures - temporal level 4 (for RCUpdateMode=3) +RCBoverPRatio = 0.45 # ratio of bit rate usage of a B-coded picture over a P-coded picture for the SAME QP (for RCUpdateMode=3) +RCIoverPRatio = 3.80 # ratio of bit rate usage of an I-coded picture over a P-coded picture for the SAME QP (for RCUpdateMode=3) +RCMinQPPSlice = 8 # minimum P Slice QP value for rate control +RCMaxQPPSlice = 42 # maximum P Slice QP value for rate control +RCMinQPBSlice = 8 # minimum B Slice QP value for rate control +RCMaxQPBSlice = 42 # maximum B Slice QP value for rate control +RCMinQPISlice = 8 # minimum I Slice QP value for rate control +RCMaxQPISlice = 42 # maximum I Slice QP value for rate control +RCMinQPSPSlice = 8 # minimum SP Slice QP value for rate control +RCMaxQPSPSlice = 40 # maximum SP Slice QP value for rate control +RCMinQPSISlice = 8 # minimum SI Slice QP value for rate control +RCMaxQPSISlice = 42 # maximum SI Slice QP value for rate control +RCMaxQPChange = 4 # maximum QP change for frames of the base layer + +######################################################################################## +#Fast Mode Decision +######################################################################################## +EarlySkipEnable = 0 # Early skip detection (0: Disable 1: Enable) +SelectiveIntraEnable = 0 # Selective Intra mode decision (0: Disable 1: Enable) + +ReportFrameStats = 0 # (0:Disable Frame Statistics 1: Enable) +DisplayEncParams = 0 # (0:Disable Display of Encoder Params 1: Enable) +Verbose = 1 # level of display verboseness + # 0: short, 1: normal (default), 2: detailed, 3: detailed/nvb + # 4: with additional MB level lambda info + +######################################################################################## +#Rounding Offset control +######################################################################################## + +OffsetMatrixPresentFlag = 0 # Enable Explicit Offset Quantization Matrices (0: disable 1: enable) +QOffsetMatrixFile = "q_offset.cfg" # Explicit Quantization Matrices file + +AdaptiveRounding = 1 # Enable Adaptive Rounding based on JVT-N011 (0: disable, 1: enable) +AdaptRoundingFixed = 1 # Enable Global Adaptive rounding for all qps (0: disable, 1: enable - default/old) +AdaptRndPeriod = 16 # Period in terms of MBs for updating rounding offsets. + # 0 performs update at the picture level. Default is 16. 1 is as in JVT-N011. +AdaptRndChroma = 1 # Enables coefficient rounding adaptation for chroma + +AdaptRndWFactorIRef = 4 # Adaptive Rounding Weight for I/SI slices in reference pictures /4096 +AdaptRndWFactorPRef = 4 # Adaptive Rounding Weight for P/SP slices in reference pictures /4096 +AdaptRndWFactorBRef = 4 # Adaptive Rounding Weight for B slices in reference pictures /4096 +AdaptRndWFactorINRef = 4 # Adaptive Rounding Weight for I/SI slices in non reference pictures /4096 +AdaptRndWFactorPNRef = 4 # Adaptive Rounding Weight for P/SP slices in non reference pictures /4096 +AdaptRndWFactorBNRef = 4 # Adaptive Rounding Weight for B slices in non reference pictures /4096 + +AdaptRndCrWFactorIRef = 4 # Chroma Adaptive Rounding Weight for I/SI slices in reference pictures /4096 +AdaptRndCrWFactorPRef = 4 # Chroma Adaptive Rounding Weight for P/SP slices in reference pictures /4096 +AdaptRndCrWFactorBRef = 4 # Chroma Adaptive Rounding Weight for B slices in reference pictures /4096 +AdaptRndCrWFactorINRef = 4 # Chroma Adaptive Rounding Weight for I/SI slices in non reference pictures /4096 +AdaptRndCrWFactorPNRef = 4 # Chroma Adaptive Rounding Weight for P/SP slices in non reference pictures /4096 +AdaptRndCrWFactorBNRef = 4 # Chroma Adaptive Rounding Weight for B slices in non reference pictures /4096 + +################################################################ +# Rate Distortion Optimized Quantization +################################################################ +UseRDOQuant = 0 # Use Rate Distortion Optimized Quantization (0=disable, 1=enable) +RDOQ_DC = 0 # Enable Rate Distortion Optimized Quantization for DC components (0=disable, 1=enable) +RDOQ_CR = 0 # Enable Rate Distortion Optimized Quantization for Chroma components (0=disable, 1=enable) +RDOQ_DC_CR = 0 # Enable Rate Distortion Optimized Quantization for Chroma DC components (0=disable, 1=enable) +RDOQ_QP_Num = 1 # 1-5: Number of QP tested in RDO_Q (I/P/B slice) +RDOQ_CP_Mode = 1 # copy Mode from first QP tested +RDOQ_CP_MV = 1 # copy MV from first QP tested +RDOQ_Fast = 1 # Fast RDOQ decision method for multiple QPs + + +######################################################################################## +#Fast Motion Estimation Control Parameters +######################################################################################## + +SearchMode = 0 # Motion estimation mode + # -1 = Full Search + # 0 = Fast Full Search (default) + # 1 = UMHexagon Search + # 2 = Simplified UMHexagon Search + # 3 = Enhanced Predictive Zonal Search (EPZS) + +UMHexDSR = 1 # Use Search Range Prediction. Only for UMHexagonS method + # (0:disable, 1:enabled/default) +UMHexScale = 3 # Use Scale_factor for different image sizes. Only for UMHexagonS method + # (0:disable, 3:/default) + # Increasing value can speed up Motion Search. + +EPZSPattern = 2 # Select EPZS primary refinement pattern. + # (0: small diamond, 1: square, 2: extended diamond/default, + # 3: large diamond, 4: SBP Large Diamond, + # 5: PMVFAST ) +EPZSDualRefinement = 3 # Enables secondary refinement pattern. + # (0:disabled, 1: small diamond, 2: square, + # 3: extended diamond/default, 4: large diamond, + # 5: SBP Large Diamond, 6: PMVFAST ) +EPZSFixedPredictors = 2 # Enables Window based predictors + # (0:disabled, 1: P only, 2: P and B/default) +EPZSTemporal = 1 # Enables temporal predictors + # (0: disabled, 1: enabled/default) +EPZSSpatialMem = 1 # Enables spatial memory predictors + # (0: disabled, 1: enabled/default) +EPZSBlockType = 1 # Enables block type Predictors + # (0: disabled, 1: enabled/default) +EPZSMinThresScale = 0 # Scaler for EPZS minimum threshold (0 default). + # Increasing value can speed up encoding. +EPZSMedThresScale = 1 # Scaler for EPZS median threshold (1 default). + # Increasing value can speed up encoding. +EPZSMaxThresScale = 2 # Scaler for EPZS maximum threshold (1 default). + # Increasing value can speed up encoding. +EPZSSubPelME = 1 # EPZS Subpel ME consideration +EPZSSubPelMEBiPred = 1 # EPZS Subpel ME consideration for BiPred partitions +EPZSSubPelThresScale = 2 # EPZS Subpel ME Threshold scaler +EPZSSubPelGrid = 1 # Perform EPZS using a subpixel grid + +######################################################################################## +# SEI Parameters +######################################################################################## + +GenerateSEIMessage = 0 # Generate an SEI Text Message +SEIMessageText = "H.264/AVC Encoder" # Text SEI Message + +UseMVLimits = 0 # Use MV Limits +SetMVXLimit = 512 # Horizontal MV Limit (in integer units) +SetMVYLimit = 512 # Vertical MV Limit (in integer units) + +######################################################################################## +# VUI Parameters +######################################################################################## +# the variables below do not affect encoding and decoding +# (many are dummy variables but others can be useful when supported by the decoder) + +EnableVUISupport = 0 # Enable VUI Parameters + diff --git a/bin/encoder_stereo.cfg b/bin/encoder_stereo.cfg new file mode 100644 index 0000000..e52ffe1 --- /dev/null +++ b/bin/encoder_stereo.cfg @@ -0,0 +1,650 @@ +# New Input File Format is as follows +# = # Comment +# +# See configfile.h for a list of supported ParameterNames +# +# For bug reporting and known issues see: +# https://ipbt.hhi.fraunhofer.de + + +########################################################################################## +# Files +########################################################################################## +InputFile = "sample_left_320x240.yuv" # Input sequence +InputHeaderLength = 0 # If the inputfile has a header, state it's length in byte here +StartFrame = 0 # Start frame for encoding. (0-N) +FramesToBeEncoded = 3 # Number of frames to be coded +FrameRate = 30.0 # Frame Rate per second (0.1-100.0) +Enable32Pulldown = 0 # Enforce 3:2 pulldown methods + # 0 = disabled + # 1 = A, B, Bt|Cb, Ct|Db, D + # 2 = A, B, C, Ct|Db, D +SourceWidth = 320 # Source frame width +SourceHeight = 240 # Source frame height +SourceResize = 0 # Resize source size for output +OutputWidth = 320 # Output frame width +OutputHeight = 240 # Output frame height +ProcessInput = 0 # Filter Input Sequence +Interleaved = 0 # 0: Planar input, 1: Packed input +StandardRange = 0 # 0: Standard range 1: Full range (RGB input) +VideoCode = 1 # Video codes for RGB ==> YUV conversions + # 0 = NULL, + # 1 = ITU_REC709, + # 2 = CCIR_601, + # 3 = FCC, + # 4 = ITU_REC624BG, + # 5 = SMPTE_170M, + # 6 = SMPTE_240M, + # 7 = SMPTE_260M, + # 8 = ITU_REC709_EXACT + +TraceFile = "trace_enc.txt" # Trace file +ReconFile = "test_rec.yuv" # Reconstruction YUV file +OutputFile = "test.264" # Bitstream +StatsFile = "stats.dat" # Coding statistics file + +NumberOfViews = 2 # Number of views to encode (1=1 view, 2=2 views) +View1ConfigFile = "encoder_view1.cfg" # Config file name for second view +########################################################################################## +# Encoder Control +########################################################################################## +Grayscale = 0 # Encode in grayscale (Currently only works for 8 bit YUV 420 input) +ProfileIDC = 128 # Profile IDC (66=baseline, 77=main, 88=extended; FREXT Profiles: 100=High, 110=High 10, 122=High 4:2:2, 244=High 4:4:4, 44=CAVLC 4:4:4 Intra, 118=Multiview High Profile, 128=Stereo High Profile) +IntraProfile = 0 # Activate Intra Profile for FRExt (0: false, 1: true) + # (e.g. ProfileIDC=110, IntraProfile=1 => High 10 Intra Profile) +LevelIDC = 40 # Level IDC (e.g. 20 = level 2.0) + +IntraPeriod = 0 # Period of I-pictures (0=only first) +IDRPeriod = 0 # Period of IDR pictures (0=only first) +AdaptiveIntraPeriod = 1 # Adaptive intra period +AdaptiveIDRPeriod = 0 # Adaptive IDR period +IntraDelay = 0 # Intra (IDR) picture delay (i.e. coding structure of PPIPPP... ) +EnableIDRGOP = 0 # Support for IDR closed GOPs (0: disabled, 1: enabled) +EnableOpenGOP = 0 # Support for open GOPs (0: disabled, 1: enabled) +QPISlice = 28 # Quant. param for I Slices (0-51) +QPPSlice = 28 # Quant. param for P Slices (0-51) +FrameSkip = 0 # Number of frames to be skipped in input (e.g 2 will code every third frame). + # Note that this now excludes intermediate (i.e. B) coded pictures +ChromaQPOffset = 0 # Chroma QP offset (-51..51) + +DisableSubpelME = 0 # Disable Subpixel Motion Estimation (0=off/default, 1=on) +SearchRange = 32 # Max search range + +MEDistortionFPel = 0 # Select error metric for Full-Pel ME (0: SAD, 1: SSE, 2: Hadamard SAD) +MEDistortionHPel = 2 # Select error metric for Half-Pel ME (0: SAD, 1: SSE, 2: Hadamard SAD) +MEDistortionQPel = 2 # Select error metric for Quarter-Pel ME (0: SAD, 1: SSE, 2: Hadamard SAD) +MDDistortion = 2 # Select error metric for Mode Decision (0: SAD, 1: SSE, 2: Hadamard SAD) +SkipDeBlockNonRef = 0 # Skip Deblocking (regardless of DFParametersFlag) for non-reference frames (0: off, 1: on) +ChromaMCBuffer = 1 # Calculate Color component interpolated values in advance and store them. + # Provides a trade-off between memory and computational complexity + # (0: disabled/default, 1: enabled) +ChromaMEEnable = 0 # Take into account Color component information during ME + # (0: only first component/default, + # 1: All Color components - Integer refinement only + # 2: All Color components - All refinements) +ChromaMEWeight = 1 # Weighting for chroma components. This parameter should have a relationship with color format. + +NumberReferenceFrames = 5 # Number of previous frames used for inter motion search (0-16) + +PList0References = 0 # P slice List 0 reference override (0 disable, N <= NumberReferenceFrames) +Log2MaxFNumMinus4 = 0 # Sets log2_max_frame_num_minus4 (-1 : based on FramesToBeEncoded/Auto, >=0 : Log2MaxFNumMinus4) +Log2MaxPOCLsbMinus4 = -1 # Sets log2_max_pic_order_cnt_lsb_minus4 (-1 : Auto, >=0 : Log2MaxPOCLsbMinus4) + +GenerateMultiplePPS = 1 # Transmit multiple parameter sets. Currently parameters basically enable all WP modes (0: diabled, 1: enabled) +SendAUD = 0 # Send Access Delimiter Unit NALU (for every access unit) +ResendSPS = 2 # Resend SPS (0: disabled, 1: all Intra pictures, 2: only for IDR, 3: for IDR and OpenGOP I) +ResendPPS = 0 # Resend PPS (with pic_parameter_set_id 0) for every coded Frame/Field pair (0: disabled, 1: enabled) + +MbLineIntraUpdate = 0 # Error robustness(extra intra macro block updates)(0=off, N: One GOB every N frames are intra coded) +RandomIntraMBRefresh = 0 # Forced intra MBs per picture + +########################################################################################## +# PSlice Mode types +########################################################################################## +PSliceSkip = 1 # P-Slice Skip mode consideration (0=disable, 1=enable) +PSliceSearch16x16 = 1 # P-Slice Inter block search 16x16 (0=disable, 1=enable) +PSliceSearch16x8 = 1 # P-Slice Inter block search 16x8 (0=disable, 1=enable) +PSliceSearch8x16 = 1 # P-Slice Inter block search 8x16 (0=disable, 1=enable) +PSliceSearch8x8 = 1 # P-Slice Inter block search 8x8 (0=disable, 1=enable) +PSliceSearch8x4 = 1 # P-Slice Inter block search 8x4 (0=disable, 1=enable) +PSliceSearch4x8 = 1 # P-Slice Inter block search 4x8 (0=disable, 1=enable) +PSliceSearch4x4 = 1 # P-Slice Inter block search 4x4 (0=disable, 1=enable) + +########################################################################################## +# BSlice Mode types +########################################################################################## + +BSliceDirect = 1 # B-Slice Skip mode consideration (0=disable, 1=enable) +BSliceSearch16x16 = 1 # B-Slice Inter block search 16x16 (0=disable, 1=enable) +BSliceSearch16x8 = 1 # B-Slice Inter block search 16x8 (0=disable, 1=enable) +BSliceSearch8x16 = 1 # B-Slice Inter block search 8x16 (0=disable, 1=enable) +BSliceSearch8x8 = 1 # B-Slice Inter block search 8x8 (0=disable, 1=enable) +BSliceSearch8x4 = 1 # B-Slice Inter block search 8x4 (0=disable, 1=enable) +BSliceSearch4x8 = 1 # B-Slice Inter block search 4x8 (0=disable, 1=enable) +BSliceSearch4x4 = 1 # B-Slice Inter block search 4x4 (0=disable, 1=enable) + +BiPredSearch16x16 = 1 # B-Slice Bi-prediction block search 16x16 (0=disable, 1=enable) +BiPredSearch16x8 = 1 # B-Slice Bi-prediction block search 16x8 (0=disable, 1=enable) +BiPredSearch8x16 = 1 # B-Slice Bi-prediction block search 8x16 (0=disable, 1=enable) +BiPredSearch8x8 = 0 # B-Slice Bi-prediction block search 8x8 (0=disable, 1=enable) + +DisableIntra4x4 = 0 # Disable Intra 4x4 modes +DisableIntra16x16 = 0 # Disable Intra 16x16 modes +DisableIntraInInter = 0 # Disable Intra modes for inter slices +IntraDisableInterOnly = 0 # Apply Disabling Intra conditions only to Inter Slices (0:disable/default,1: enable) +Intra4x4ParDisable = 0 # Disable Vertical & Horizontal 4x4 +Intra4x4DiagDisable = 0 # Disable Diagonal 45degree 4x4 +Intra4x4DirDisable = 0 # Disable Other Diagonal 4x4 +Intra16x16ParDisable = 0 # Disable Vertical & Horizontal 16x16 +Intra16x16PlaneDisable = 0 # Disable Planar 16x16 +ChromaIntraDisable = 0 # Disable Intra Chroma modes other than DC +EnableIPCM = 1 # Enable IPCM macroblock mode + +DisposableP = 0 # Enable Disposable P slices in the primary layer (0: disable/default, 1: enable) +DispPQPOffset = 0 # Quantizer offset for disposable P slices (0: default) + +PreferDispOrder = 1 # Prefer display order when building the prediction structure as opposed to coding order (affects intra and IDR periodic insertion, among others) +PreferPowerOfTwo = 0 # Prefer prediction structures that have lengths expressed as powers of two +FrmStructBufferLength = 16 # Length of the frame structure unit buffer; it can be overriden for certain cases + +ChangeQPFrame = 0 # Frame in display order from which to apply the Change QP offsets +ChangeQPI = 0 # Change QP offset value for I_SLICE +ChangeQPP = 0 # Change QP offset value for P_SLICE +ChangeQPB = 0 # Change QP offset value for B_SLICE +ChangeQPSI = 0 # Change QP offset value for SI_SLICE +ChangeQPSP = 0 # Change QP offset value for SP_SLICE + +########################################################################################## +# B Slices +########################################################################################## + +NumberBFrames = 7 # Number of B coded frames inserted (0=not used) +PReplaceBSlice = 0 # Replace B-coded slices with P-coded slices when NumberBFrames>0 +QPBSlice = 30 # Quant. param for B slices (0-51) +BRefPicQPOffset = -1 # Quantization offset for reference B coded pictures (-51..51) +DirectModeType = 1 # Direct Mode Type (0:Temporal 1:Spatial) +DirectInferenceFlag = 1 # Direct Inference Flag (0: Disable 1: Enable) +BList0References = 0 # B slice List 0 reference override (0 disable, N <= NumberReferenceFrames) +BList1References = 1 # B slice List 1 reference override (0 disable, N <= NumberReferenceFrames) + # 1 List1 reference is usually recommended for normal GOP Structures. + # A larger value is usually more appropriate if a more flexible + # structure is used (i.e. using HierarchicalCoding) + +BReferencePictures = 0 # Referenced B coded pictures (0=off, 1=B references for secondary layer, 2=B references for primary layer) + +HierarchicalCoding = 3 # B hierarchical coding (0= off, 1= 2 layers, 2= 2 full hierarchy, 3 = explicit) +HierarchyLevelQPEnable = 1 # Adjust QP based on hierarchy level (in increments of 1). Overrides BRefPicQPOffset behavior.(0=off, 1=on) +ExplicitHierarchyFormat = "b3r0b1r1b0e2b2e2b5r1b4e2b6e2" # Explicit Enhancement GOP. Format is {FrameDisplay_orderReferenceQP}. + # Valid values for reference type is r:reference, e:non reference. +ExplicitSeqCoding = 0 # Enable support for explicit sequence coding +ExplicitSeqFile = "explicit_seq.cfg" +LowDelay = 0 # Apply HierarchicalCoding without delay (i.e., encode in the captured/display order) +ReferenceReorder = 0 # Reorder References according to Poc distance for HierarchicalCoding (0=off, 1=enable, 2=use when LowDelay is set) +PocMemoryManagement = 0 # Memory management based on Poc Distances for HierarchicalCoding (0=off, 1=on, 2=use when LowDelay is set) +SetFirstAsLongTerm = 0 # Set first frame as long term + +BiPredMotionEstimation = 1 # Enable Bipredictive based Motion Estimation (0:disabled, 1:enabled) +BiPredMERefinements = 3 # Bipredictive ME extra refinements (0: single, N: N extra refinements (1 default) +BiPredMESearchRange = 16 # Bipredictive ME Search range (8 default). Note that range is halved for every extra refinement. +BiPredMESubPel = 2 # Bipredictive ME Subpixel Consideration (0: disabled, 1: single level, 2: dual level) + + +########################################################################################## +# SP Frames +########################################################################################## + +SPPicturePeriodicity = 0 # SP-Picture Periodicity (0=not used) +SPSwitchPeriod = 0 # Switch period (in terms of switching SP/SI frames) between bitstream 1 and bitstream 2 +QPSPSlice = 36 # Quant. param of SP-Slices for Prediction Error (0-51) +QPSISlice = 36 # Quant. param of SI-Slices for Prediction Error (0-51) +QPSP2Slice = 35 # Quant. param of SP/SI-Slices for Predicted Blocks (0-51) +SI_FRAMES = 0 # SI frame encoding flag (0=not used, 1=used) +SP_output = 0 # Controls whether coefficients will be output to encode switching SP frames (0=no, 1=yes) +SP_output_name = "low_quality.dat" # Filename for SP output coefficients +SP2_FRAMES = 0 # switching SP frame encoding flag (0=not used, 1=used) +SP2_input_name1 = "high_quality.dat" # Filename for the first swithed bitstream coefficients +SP2_input_name2 = "low_quality.dat" # Filename for the second switched bitstream coefficients + +########################################################################################## +# Output Control, NALs +########################################################################################## + +SymbolMode = 1 # Symbol mode (Entropy coding method: 0=UVLC, 1=CABAC) +OutFileMode = 0 # Output file mode, 0:Annex B, 1:RTP +PartitionMode = 0 # Partition Mode, 0: no DP, 1: 3 Partitions per Slice + +########################################################################################## +# CABAC context initialization +########################################################################################## + +ContextInitMethod = 1 # Context init (0: fixed, 1: adaptive) +FixedModelNumber = 0 # model number for fixed decision for inter slices ( 0, 1, or 2 ) + +########################################################################################## +# Interlace Handling +######################################################################################### + +PicInterlace = 0 # Picture AFF (0: frame coding, 1: field coding, 2:adaptive frame/field coding) +MbInterlace = 0 # Macroblock AFF (0: frame coding, 1: field coding, 2:adaptive frame/field coding, 3: frame MB-only AFF) +IntraBottom = 0 # Force Intra Bottom at GOP Period + +########################################################################################## +# Weighted Prediction +######################################################################################### + +WeightedPrediction = 0 # P picture Weighted Prediction (0=off, 1=explicit mode) +WeightedBiprediction = 0 # B picture Weighted Prediciton (0=off, 1=explicit mode, 2=implicit mode) +ChromaWeightSupport = 1 # Enable consideration of weights for Chroma components +UseWeightedReferenceME = 1 # Use weighted reference for ME (0=off, 1=on) +WPMethod = 1 # WP method (0: DC based, 1: LMS based) +WPIterMC = 0 # Iterative Motion compensated based weighted prediction method +EnhancedBWeightSupport = 0 # Enhanced B Weight support (needs revisit if we wish to merge with WPMethod) +WPMCPrecision = 0 # Improved Motion Compensation Precision using WP based methods. + # Clones WP references with slightly modified rounding offsets (Requires RDPictureDecision and GenerateMultiplePPS) : + # 0: disabled (default) + # 1: Up to one additional coding pass. Ref0 is 0, ref1 is 0 with a -1 offset + # 2: Up to two additional coding passes. (1) Ref0 is 0, ref1 is 0 with a -1 offset, (1) Ref0 is 0 with a -1 offset, ref1 is 0 +WPMCPrecFullRef = 0 # Increases the number of references in the reference picture lists to account + # for the lost reference slot when reordering is used during a coding pass in WPMCPrecision for reference replication. + # The number of references in non-reordered passes stays unchanged +WPMCPrecBSlice = 1 # 2: Apply rounding on every B slice. This efectively disables the evaluation of alternative QPs during RDPictureDecision. + # 1: Disable rounding for non-reference B slices. Non-reference B slices are evaluated for alternative QPs during RDPictureDecision. + # 0: Disable rounding for B slices. + +########################################################################################## +# Picture based Multi-pass encoding +######################################################################################### + +RDPictureDecision = 0 # Perform multiple pass coding and make RD optimal decision among them +RDPSliceBTest = 0 # Perform Slice level RD decision between P and B slices. +RDPictureMaxPassISlice = 1 # Max number of coding passes for I slices, valid values [1,3], default is 1 +RDPictureMaxPassPSlice = 2 # Max number of coding passes for P slices, valid values [1,6], default is 2 +RDPictureMaxPassBSlice = 3 # Max number of coding passes for B slices, valid values [1,6], default is 3 +RDPictureFrameQPPSlice = 0 # Perform additional frame level QP check (QP+/-1) for P slices, 0: disabled (default), 1: enabled +RDPictureFrameQPBSlice = 0 # Perform additional frame level QP check (QP+/-1) for B slices, 0: disabled, 1: enabled (default) +RDPictureDeblocking = 0 # Perform another coding pass to check non-deblocked picture, 0: disabled (default), 1: enabled +RDPictureDirectMode = 0 # Perform another coding pass to check the alternative direct mode for B slices, , 0: disabled (default), 1: enabled + +########################################################################################## +# Deblocking filter parameters +########################################################################################## + +DFParametersFlag = 0 # Configure deblocking filter (0=parameters below ignored, 1=parameters sent) + # Note that for pictures with multiple slice types, + # only the type of the first slice will be considered. +DFDisableRefISlice = 0 # Disable deblocking filter in reference I coded pictures (0=Filter, 1=No Filter). +DFAlphaRefISlice = 0 # Reference I coded pictures Alpha offset div. 2, {-6, -5, ... 0, +1, .. +6} +DFBetaRefISlice = 0 # Reference I coded pictures Beta offset div. 2, {-6, -5, ... 0, +1, .. +6} +DFDisableNRefISlice = 0 # Disable deblocking filter in non reference I coded pictures (0=Filter, 1=No Filter). +DFAlphaNRefISlice = 0 # Non Reference I coded pictures Alpha offset div. 2, {-6, -5, ... 0, +1, .. +6} +DFBetaNRefISlice = 0 # Non Reference I coded pictures Beta offset div. 2, {-6, -5, ... 0, +1, .. +6} +DFDisableRefPSlice = 0 # Disable deblocking filter in reference P coded pictures (0=Filter, 1=No Filter). +DFAlphaRefPSlice = 0 # Reference P coded pictures Alpha offset div. 2, {-6, -5, ... 0, +1, .. +6} +DFBetaRefPSlice = 0 # Reference P coded pictures Beta offset div. 2, {-6, -5, ... 0, +1, .. +6} +DFDisableNRefPSlice = 0 # Disable deblocking filter in non reference P coded pictures (0=Filter, 1=No Filter). +DFAlphaNRefPSlice = 0 # Non Reference P coded pictures Alpha offset div. 2, {-6, -5, ... 0, +1, .. +6} +DFBetaNRefPSlice = 0 # Non Reference P coded pictures Beta offset div. 2, {-6, -5, ... 0, +1, .. +6} +DFDisableRefBSlice = 0 # Disable deblocking filter in reference B coded pictures (0=Filter, 1=No Filter). +DFAlphaRefBSlice = 0 # Reference B coded pictures Alpha offset div. 2, {-6, -5, ... 0, +1, .. +6} +DFBetaRefBSlice = 0 # Reference B coded pictures Beta offset div. 2, {-6, -5, ... 0, +1, .. +6} +DFDisableNRefBSlice = 0 # Disable deblocking filter in non reference B coded pictures (0=Filter, 1=No Filter). +DFAlphaNRefBSlice = 0 # Non Reference B coded pictures Alpha offset div. 2, {-6, -5, ... 0, +1, .. +6} +DFBetaNRefBSlice = 0 # Non Reference B coded pictures Beta offset div. 2, {-6, -5, ... 0, +1, .. +6} + +########################################################################################## +# Error Resilience / Slices +########################################################################################## + +SliceMode = 0 # Slice mode (0=off 1=fixed #mb in slice 2=fixed #bytes in slice 3=use callback) +SliceArgument = 50 # Slice argument (Arguments to modes 1 and 2 above) + +num_slice_groups_minus1 = 0 # Number of Slice Groups Minus 1, 0 == no FMO, 1 == two slice groups, etc. +slice_group_map_type = 0 # 0: Interleave, 1: Dispersed, 2: Foreground with left-over, + # 3: Box-out, 4: Raster Scan 5: Wipe + # 6: Explicit, slice_group_id read from SliceGroupConfigFileName +slice_group_change_direction_flag = 0 # 0: box-out clockwise, raster scan or wipe right, + # 1: box-out counter clockwise, reverse raster scan or wipe left +slice_group_change_rate_minus1 = 85 # +SliceGroupConfigFileName = "sg0conf.cfg" # Used for slice_group_map_type 0, 2, 6 + +UseRedundantPicture = 0 # 0: not used, 1: enabled +NumRedundantHierarchy = 1 # 0-4 +PrimaryGOPLength = 10 # GOP length for redundant allocation (1-16) + # NumberReferenceFrames must be no less than PrimaryGOPLength when redundant slice enabled +NumRefPrimary = 1 # Actually used number of references for primary slices (1-16) + +########################################################################################## +# Search Range Restriction / RD Optimization +########################################################################################## + +RestrictSearchRange = 2 # restriction for (0: blocks and ref, 1: ref, 2: no restrictions) +RDOptimization = 1 # rd-optimized mode decision + # 0: RD-off (Low complexity mode) + # 1: RD-on (High complexity mode) + # 2: RD-on (Fast high complexity mode - not work in FREX Profiles) + # 3: with losses +I16RDOpt = 0 # perform rd-optimized mode decision for Intra 16x16 MB + # 0: SAD-based mode decision for Intra 16x16 MB + # 1: RD-based mode decision for Intra 16x16 MB +SubMBCodingState = 1 # submacroblock coding state + # 0: lowest complexity, do not store or reset coding state during sub-MB mode decision + # 1: medium complexity, reset to master coding state (for current mode) during sub-MB mode decision + # 2: highest complexity, store and reset coding state during sub-MB mode decision +DistortionSSIM = 0 # Compute SSIM distortion. (0: disabled/default, 1: enabled) +DistortionMS_SSIM = 0 # Compute Multiscale SSIM distortion. (0: disabled/default, 1: enabled) +SSIMOverlapSize = 8 # Overlap size to calculate SSIM distortion (1: pixel by pixel, 8: no overlap) +DistortionYUVtoRGB = 0 # Calculate distortion in RGB domain after conversion from YCbCr (0:off, 1:on) +CtxAdptLagrangeMult = 0 # Context Adaptive Lagrange Multiplier + # 0: disabled (default) + # 1: enabled (works best when RDOptimization=0) +FastCrIntraDecision = 0 # Fast Chroma intra mode decision (0:off, 1:on) +DisableThresholding = 0 # Disable Thresholding of Transform Coefficients (0:off, 1:on) +DisableBSkipRDO = 0 # Disable B Skip Mode consideration from RDO Mode decision (0:off, 1:on) +BiasSkipRDO = 0 # Negative Bias for Skip/DirectSkip modes (0: off, 1: on) +ForceTrueRateRDO = 0 # Force true rate (even zero values) during RDO process +SkipIntraInInterSlices = 0 # Skips Intra mode checking in inter slices if certain mode decisions are satisfied (0: off, 1: on) +WeightY = 1 # Luma weight for RDO +WeightCb = 1 # Cb weight for RDO +WeightCr = 1 # Cr weight for RDO + +########################################################################################## +# Explicit Lambda Usage +########################################################################################## +UseExplicitLambdaParams = 0 # Use explicit lambda scaling parameters (0:disabled, 1:enable lambda weight, 2: use explicit lambda value) +UpdateLambdaChromaME = 0 # Update lambda given Chroma ME consideration +FixedLambdaISlice = 0.1 # Fixed Lambda value for I slices +FixedLambdaPSlice = 0.1 # Fixed Lambda value for P slices +FixedLambdaBSlice = 0.1 # Fixed Lambda value for B slices +FixedLambdaRefBSlice = 0.1 # Fixed Lambda value for Referenced B slices +FixedLambdaSPSlice = 0.1 # Fixed Lambda value for SP slices +FixedLambdaSISlice = 0.1 # Fixed Lambda value for SI slices + +LambdaWeightISlice = 0.65 # scaling param for I slices. This will be used as a multiplier i.e. lambda=LambdaWeightISlice * 2^((QP-12)/3) +LambdaWeightPSlice = 0.68 # scaling param for P slices. This will be used as a multiplier i.e. lambda=LambdaWeightPSlice * 2^((QP-12)/3) +LambdaWeightBSlice = 0.68 # scaling param for B slices. This will be used as a multiplier i.e. lambda=LambdaWeightBSlice * 2^((QP-12)/3) +LambdaWeightRefBSlice = 0.68 # scaling param for Referenced B slices. This will be used as a multiplier i.e. lambda=LambdaWeightRefBSlice * 2^((QP-12)/3) +LambdaWeightSPSlice = 0.68 # scaling param for SP slices. This will be used as a multiplier i.e. lambda=LambdaWeightSPSlice * 2^((QP-12)/3) +LambdaWeightSISlice = 0.65 # scaling param for SI slices. This will be used as a multiplier i.e. lambda=LambdaWeightSISlice * 2^((QP-12)/3) + +LossRateA = 5 # expected packet loss rate of the channel for the first partition, only valid if RDOptimization = 3 +LossRateB = 0 # expected packet loss rate of the channel for the second partition, only valid if RDOptimization = 3 +LossRateC = 0 # expected packet loss rate of the channel for the third partition, only valid if RDOptimization = 3 +FirstFrameCorrect = 0 # If 1, the first frame is encoded under the assumption that it is always correctly received. +NumberOfDecoders = 30 # Numbers of decoders used to simulate the channel, only valid if RDOptimization = 3 +RestrictRefFrames = 0 # Doesnt allow reference to areas that have been intra updated in a later frame. + +########################################################################################## +# Additional Stuff +######################################################################################### + +UseConstrainedIntraPred = 0 # If 1, Inter pixels are not used for Intra macroblock prediction. + +NumberofLeakyBuckets = 8 # Number of Leaky Bucket values +LeakyBucketRateFile = "leakybucketrate.cfg" # File from which encoder derives rate values +LeakyBucketParamFile = "leakybucketparam.cfg" # File where encoder stores leakybucketparams + +NumFramesInELayerSubSeq = 0 # number of frames in the Enhanced Scalability Layer(0: no Enhanced Layer) + +SparePictureOption = 0 # (0: no spare picture info, 1: spare picture available) +SparePictureDetectionThr = 6 # Threshold for spare reference pictures detection +SparePicturePercentageThr = 92 # Threshold for the spare macroblock percentage + +PicOrderCntType = 0 # (0: POC mode 0, 1: POC mode 1, 2: POC mode 2) + +######################################################################################## +#Rate control +######################################################################################## + +RateControlEnable = 0 # 0 Disable, 1 Enable +Bitrate = 45020 # Bitrate(bps) +InitialQP = 0 # Initial Quantization Parameter for the first I frame + # InitialQp depends on two values: Bits Per Picture, + # and the GOP length +BasicUnit = 0 # Number of MBs in the basic unit + # should be a fraction of the total number + # of MBs in a frame ("0" sets a BU equal to a frame) +ChannelType = 0 # type of channel( 1=time varying channel; 0=Constant channel) +RCUpdateMode = 0 # Rate Control type. Modes supported : + # 0 = original JM rate control, + # 1 = rate control that is applied to all frames regardless of the slice type, + # 2 = original plus intelligent QP selection for I and B slices (including Hierarchical), + # 3 = original + hybrid quadratic rate control for I and B slice using bit rate statistics + # +RCISliceBitRatio = 1.0 # target ratio of bits for I-coded pictures compared to P-coded Pictures (for RCUpdateMode=3) +RCBSliceBitRatio0 = 0.5 # target ratio of bits for B-coded pictures compared to P-coded Pictures - temporal level 0 (for RCUpdateMode=3) +RCBSliceBitRatio1 = 0.25 # target ratio of bits for B-coded pictures compared to P-coded Pictures - temporal level 1 (for RCUpdateMode=3) +RCBSliceBitRatio2 = 0.25 # target ratio of bits for B-coded pictures compared to P-coded Pictures - temporal level 2 (for RCUpdateMode=3) +RCBSliceBitRatio3 = 0.25 # target ratio of bits for B-coded pictures compared to P-coded Pictures - temporal level 3 (for RCUpdateMode=3) +RCBSliceBitRatio4 = 0.25 # target ratio of bits for B-coded pictures compared to P-coded Pictures - temporal level 4 (for RCUpdateMode=3) +RCBoverPRatio = 0.45 # ratio of bit rate usage of a B-coded picture over a P-coded picture for the SAME QP (for RCUpdateMode=3) +RCIoverPRatio = 3.80 # ratio of bit rate usage of an I-coded picture over a P-coded picture for the SAME QP (for RCUpdateMode=3) +RCMinQPPSlice = 8 # minimum P Slice QP value for rate control +RCMaxQPPSlice = 42 # maximum P Slice QP value for rate control +RCMinQPBSlice = 8 # minimum B Slice QP value for rate control +RCMaxQPBSlice = 42 # maximum B Slice QP value for rate control +RCMinQPISlice = 8 # minimum I Slice QP value for rate control +RCMaxQPISlice = 42 # maximum I Slice QP value for rate control +RCMinQPSPSlice = 8 # minimum SP Slice QP value for rate control +RCMaxQPSPSlice = 40 # maximum SP Slice QP value for rate control +RCMinQPSISlice = 8 # minimum SI Slice QP value for rate control +RCMaxQPSISlice = 42 # maximum SI Slice QP value for rate control +RCMaxQPChange = 4 # maximum QP change for frames of the base layer + +######################################################################################## +#Fast Mode Decision +######################################################################################## +EarlySkipEnable = 0 # Early skip detection (0: Disable 1: Enable) +SelectiveIntraEnable = 0 # Selective Intra mode decision (0: Disable 1: Enable) + +######################################################################################## +#FREXT stuff +######################################################################################## + +YUVFormat = 1 # YUV format (0=4:0:0, 1=4:2:0, 2=4:2:2, 3=4:4:4) +RGBInput = 0 # 1=RGB input, 0=GBR or YUV input +SeparateColourPlane = 0 # 4:4:4 coding: 0=Common mode, 1=Independent mode +SourceBitDepthLuma = 8 # Source Bit Depth for Luma color component (8...14 bits) +SourceBitDepthChroma = 8 # Source Bit Depth for Chroma color components (8...14 bits) +SourceBitDepthRescale = 0 # Rescale bit depth of source for output (0: Disable 1: Enable) +OutputBitDepthLuma = 8 # Output Bit Depth for Luma color component (8...14 bits) +OutputBitDepthChroma = 8 # Output Bit Depth for Chroma color components (8...14 bits) + +CbQPOffset = 0 # Chroma QP offset for Cb-part (-51..51) +CrQPOffset = 0 # Chroma QP offset for Cr-part (-51..51) +Transform8x8Mode = 1 # (0: only 4x4 transform, 1: allow using 8x8 transform additionally, 2: only 8x8 transform) +ReportFrameStats = 0 # (0:Disable Frame Statistics 1: Enable) +DisplayEncParams = 0 # (0:Disable Display of Encoder Params 1: Enable) +Verbose = 1 # level of display verboseness + # 0: short, 1: normal (default), 2: detailed, 3: detailed/nvb +SkipGlobalStats = 0 # Disable global stat accumulation (Set to 1 to avoid bipred core dump) + +######################################################################################## +#Q-Matrix (FREXT) +######################################################################################## +QmatrixFile = "q_matrix.cfg" + +ScalingMatrixPresentFlag = 0 # Enable Q_Matrix (0 Not present, 1 Present in SPS, 2 Present in PPS, 3 Present in both SPS & PPS) +ScalingListPresentFlag0 = 3 # Intra4x4_Luma (0 Not present, 1 Present in SPS, 2 Present in PPS, 3 Present in both SPS & PPS) +ScalingListPresentFlag1 = 3 # Intra4x4_ChromaU (0 Not present, 1 Present in SPS, 2 Present in PPS, 3 Present in both SPS & PPS) +ScalingListPresentFlag2 = 3 # Intra4x4_chromaV (0 Not present, 1 Present in SPS, 2 Present in PPS, 3 Present in both SPS & PPS) +ScalingListPresentFlag3 = 3 # Inter4x4_Luma (0 Not present, 1 Present in SPS, 2 Present in PPS, 3 Present in both SPS & PPS) +ScalingListPresentFlag4 = 3 # Inter4x4_ChromaU (0 Not present, 1 Present in SPS, 2 Present in PPS, 3 Present in both SPS & PPS) +ScalingListPresentFlag5 = 3 # Inter4x4_ChromaV (0 Not present, 1 Present in SPS, 2 Present in PPS, 3 Present in both SPS & PPS) +ScalingListPresentFlag6 = 3 # Intra8x8_Luma (0 Not present, 1 Present in SPS, 2 Present in PPS, 3 Present in both SPS & PPS) +ScalingListPresentFlag7 = 3 # Inter8x8_Luma (0 Not present, 1 Present in SPS, 2 Present in PPS, 3 Present in both SPS & PPS) +ScalingListPresentFlag8 = 3 # Intra8x8_ChromaU for 4:4:4 (0 Not present, 1 Present in SPS, 2 Present in PPS, 3 Present in both SPS & PPS) +ScalingListPresentFlag9 = 3 # Inter8x8_ChromaU for 4:4:4 (0 Not present, 1 Present in SPS, 2 Present in PPS, 3 Present in both SPS & PPS) +ScalingListPresentFlag10 = 3 # Intra8x8_ChromaV for 4:4:4 (0 Not present, 1 Present in SPS, 2 Present in PPS, 3 Present in both SPS & PPS) +ScalingListPresentFlag11 = 3 # Inter8x8_ChromaV for 4:4:4 (0 Not present, 1 Present in SPS, 2 Present in PPS, 3 Present in both SPS & PPS) + +######################################################################################## +#Rounding Offset control +######################################################################################## + +OffsetMatrixPresentFlag = 0 # Enable Explicit Offset Quantization Matrices (0: disable 1: enable) +QOffsetMatrixFile = "q_offset.cfg" # Explicit Quantization Matrices file + +AdaptiveRounding = 1 # Enable Adaptive Rounding based on JVT-N011 (0: disable, 1: enable) +AdaptRoundingFixed = 0 # Enable Global Adaptive rounding for all qps (0: disable, 1: enable - default/old) +AdaptRndPeriod = 1 # Period in terms of MBs for updating rounding offsets. + # 0 performs update at the picture level. Default is 16. 1 is as in JVT-N011. +AdaptRndChroma = 1 # Enables coefficient rounding adaptation for chroma + +AdaptRndWFactorIRef = 8 # Adaptive Rounding Weight for I/SI slices in reference pictures /4096 +AdaptRndWFactorPRef = 8 # Adaptive Rounding Weight for P/SP slices in reference pictures /4096 +AdaptRndWFactorBRef = 8 # Adaptive Rounding Weight for B slices in reference pictures /4096 +AdaptRndWFactorINRef = 8 # Adaptive Rounding Weight for I/SI slices in non reference pictures /4096 +AdaptRndWFactorPNRef = 8 # Adaptive Rounding Weight for P/SP slices in non reference pictures /4096 +AdaptRndWFactorBNRef = 8 # Adaptive Rounding Weight for B slices in non reference pictures /4096 + +AdaptRndCrWFactorIRef = 8 # Chroma Adaptive Rounding Weight for I/SI slices in reference pictures /4096 +AdaptRndCrWFactorPRef = 8 # Chroma Adaptive Rounding Weight for P/SP slices in reference pictures /4096 +AdaptRndCrWFactorBRef = 8 # Chroma Adaptive Rounding Weight for B slices in reference pictures /4096 +AdaptRndCrWFactorINRef = 8 # Chroma Adaptive Rounding Weight for I/SI slices in non reference pictures /4096 +AdaptRndCrWFactorPNRef = 8 # Chroma Adaptive Rounding Weight for P/SP slices in non reference pictures /4096 +AdaptRndCrWFactorBNRef = 8 # Chroma Adaptive Rounding Weight for B slices in non reference pictures /4096 + +################################################################ +# Rate Distortion Optimized Quantization +################################################################ +UseRDOQuant = 0 # Use Rate Distortion Optimized Quantization (0=disable, 1=enable) +RDOQ_DC = 1 # Enable Rate Distortion Optimized Quantization for DC components (0=disable, 1=enable) +RDOQ_CR = 1 # Enable Rate Distortion Optimized Quantization for Chroma components (0=disable, 1=enable) +RDOQ_DC_CR = 1 # Enable Rate Distortion Optimized Quantization for Chroma DC components (0=disable, 1=enable) +RDOQ_QP_Num = 5 # 1-9: Number of QP tested in RDO_Q (I/P/B slice) +RDOQ_CP_Mode = 0 # copy Mode from first QP tested +RDOQ_CP_MV = 0 # copy MV from first QP tested +RDOQ_Fast = 0 # Fast RDOQ decision method for multiple QPs + +######################################################################################## +#Lossless Coding (FREXT) +######################################################################################## + +LosslessCoding = 0 # Enable lossless coding when qpprime_y is zero (0 Disabled, 1 Enabled) + +######################################################################################## +#Fast Motion Estimation Control Parameters +######################################################################################## + +SearchMode = 3 # Motion estimation mode + # -1 = Full Search + # 0 = Fast Full Search (default) + # 1 = UMHexagon Search + # 2 = Simplified UMHexagon Search + # 3 = Enhanced Predictive Zonal Search (EPZS) + +UMHexDSR = 1 # Use Search Range Prediction. Only for UMHexagonS method + # (0:disable, 1:enabled/default) +UMHexScale = 3 # Use Scale_factor for different image sizes. Only for UMHexagonS method + # (0:disable, 3:/default) + # Increasing value can speed up Motion Search. + +EPZSPattern = 2 # Select EPZS primary refinement pattern. + # (0: small diamond, 1: square, 2: extended diamond/default, + # 3: large diamond, 4: SBP Large Diamond, + # 5: PMVFAST ) +EPZSDualRefinement = 3 # Enables secondary refinement pattern. + # (0:disabled, 1: small diamond, 2: square, + # 3: extended diamond/default, 4: large diamond, + # 5: SBP Large Diamond, 6: PMVFAST ) +EPZSFixedPredictors = 2 # Enables Window based predictors + # (0:disabled, 1: P only, 2: P and B/default) +EPZSTemporal = 1 # Enables temporal predictors + # (0: disabled, 1: enabled/default) +EPZSSpatialMem = 1 # Enables spatial memory predictors + # (0: disabled, 1: enabled/default) +EPZSBlockType = 1 # Enables block type Predictors + # (0: disabled, 1: enabled/default) +EPZSMinThresScale = 0 # Scaler for EPZS minimum threshold (0 default). + # Increasing value can speed up encoding. +EPZSMedThresScale = 1 # Scaler for EPZS median threshold (1 default). + # Increasing value can speed up encoding. +EPZSMaxThresScale = 2 # Scaler for EPZS maximum threshold (1 default). + # Increasing value can speed up encoding. +EPZSSubPelME = 1 # EPZS Subpel ME consideration +EPZSSubPelMEBiPred = 1 # EPZS Subpel ME consideration for BiPred partitions +EPZSSubPelThresScale = 2 # EPZS Subpel ME Threshold scaler +EPZSSubPelGrid = 1 # Perform EPZS using a subpixel grid + +######################################################################################## +# SEI Parameters +######################################################################################## + +ToneMappingSEIPresentFlag = 0 # Enable Tone mapping SEI (0 Not present, 1 Present) +ToneMappingFile = "ToneMapping.cfg" + +GenerateSEIMessage = 0 # Generate an SEI Text Message +SEIMessageText = "H.264/AVC Encoder" # Text SEI Message + +UseMVLimits = 0 # Use MV Limits +SetMVXLimit = 512 # Horizontal MV Limit (in integer units) +SetMVYLimit = 512 # Vertical MV Limit (in integer units) + +######################################################################################## +# VUI Parameters +######################################################################################## +# the variables below do not affect encoding and decoding +# (many are dummy variables but others can be useful when supported by the decoder) + +EnableVUISupport = 0 # Enable VUI Parameters + +# display parameters +VUI_aspect_ratio_info_present_flag = 0 +VUI_aspect_ratio_idc = 1 +VUI_sar_width = 0 +VUI_sar_height = 0 +VUI_overscan_info_present_flag = 0 +VUI_overscan_appropriate_flag = 0 +VUI_video_signal_type_present_flag = 0 +VUI_video_format = 5 +VUI_video_full_range_flag = 0 +VUI_colour_description_present_flag = 0 +VUI_colour_primaries = 2 +VUI_transfer_characteristics = 2 +VUI_matrix_coefficients = 2 +VUI_chroma_location_info_present_flag = 0 +VUI_chroma_sample_loc_type_top_field = 0 +VUI_chroma_sample_loc_type_bottom_field = 0 +VUI_timing_info_present_flag = 0 +VUI_num_units_in_tick = 1000 +VUI_time_scale = 60000 +VUI_fixed_frame_rate_flag = 0 + +# nal hrd parameters +VUI_nal_hrd_parameters_present_flag = 0 +VUI_nal_cpb_cnt_minus1 = 0 +VUI_nal_bit_rate_scale = 0 +VUI_nal_cpb_size_scale = 0 +VUI_nal_bit_rate_value_minus1 = 0 +VUI_nal_cpb_size_value_minus1 = 0 +VUI_nal_vbr_cbr_flag = 0 +VUI_nal_initial_cpb_removal_delay_length_minus1 = 23 +VUI_nal_cpb_removal_delay_length_minus1 = 23 +VUI_nal_dpb_output_delay_length_minus1 = 23 +VUI_nal_time_offset_length = 24 + +# vlc hrd parameters +VUI_vcl_hrd_parameters_present_flag = 0 +VUI_vcl_cpb_cnt_minus1 = 0 +VUI_vcl_bit_rate_scale = 0 +VUI_vcl_cpb_size_scale = 0 +VUI_vcl_bit_rate_value_minus1 = 0 +VUI_vcl_cpb_size_value_minus1 = 0 +VUI_vcl_vbr_cbr_flag = 0 +VUI_vcl_initial_cpb_removal_delay_length_minus1 = 23 +VUI_vcl_cpb_removal_delay_length_minus1 = 23 +VUI_vcl_dpb_output_delay_length_minus1 = 23 +VUI_vcl_time_offset_length = 24 +VUI_low_delay_hrd_flag = 0 + +# other parameters (i.e. bitsream restrictions) +VUI_pic_struct_present_flag = 0 +VUI_bitstream_restriction_flag = 0 +VUI_motion_vectors_over_pic_boundaries_flag = 1 +VUI_max_bytes_per_pic_denom = 0 +VUI_max_bits_per_mb_denom = 0 +VUI_log2_max_mv_length_vertical = 16 +VUI_log2_max_mv_length_horizontal = 16 +VUI_num_reorder_frames = 16 +VUI_max_dec_frame_buffering = 16 + diff --git a/bin/encoder_tonemapping.cfg b/bin/encoder_tonemapping.cfg new file mode 100644 index 0000000..4696272 --- /dev/null +++ b/bin/encoder_tonemapping.cfg @@ -0,0 +1,570 @@ +# New Input File Format is as follows +# = # Comment +# +# See configfile.h for a list of supported ParameterNames +# +# For bug reporting and known issues see: +# https://ipbt.hhi.fraunhofer.de + + +########################################################################################## +# Files +########################################################################################## +InputFile = "Crew_10.rgb" # Input sequence +InputHeaderLength = 0 # If the inputfile has a header, state it's length in byte here +StartFrame = 0 # Start frame for encoding. (0-N) +FramesToBeEncoded = 10 # Number of frames to be coded +FrameRate = 30.0 # Frame Rate per second (0.1-100.0) +SourceWidth = 1280 # Source frame width +SourceHeight = 720 # Source frame height +SourceResize = 0 # Resize source size for output +OutputWidth = 1280 # Output frame width +OutputHeight = 720 # Output frame height + +TraceFile = "trace_enc.txt" # Trace file +ReconFile = "test_rec.yuv" # Reconstruction YUV file +OutputFile = "test.264" # Bitstream +StatsFile = "stats.dat" # Coding statistics file + +########################################################################################## +# Encoder Control +########################################################################################## +ProfileIDC = 244 # Profile IDC (66=baseline, 77=main, 88=extended; FREXT Profiles: 100=High, 110=High 10, 122=High 4:2:2, 244=High 4:4:4, 44=CAVLC 4:4:4 Intra) +IntraProfile = 0 # Activate Intra Profile for FRExt (0: false, 1: true) + # (e.g. ProfileIDC=110, IntraProfile=1 => High 10 Intra Profile) +LevelIDC = 51 # Level IDC (e.g. 20 = level 2.0) + +IntraPeriod = 1 # Period of I-pictures (0=only first) +IDRPeriod = 0 # Period of IDR pictures (0=only first) +AdaptiveIntraPeriod = 1 # Adaptive intra period +AdaptiveIDRPeriod = 0 # Adaptive IDR period +IntraDelay = 0 # Intra (IDR) picture delay (i.e. coding structure of PPIPPP... ) +EnableIDRGOP = 0 # Support for IDR closed GOPs (0: disabled, 1: enabled) +EnableOpenGOP = 0 # Support for open GOPs (0: disabled, 1: enabled) +QPISlice = 28 # Quant. param for I Slices (0-51) +QPPSlice = 28 # Quant. param for P Slices (0-51) +FrameSkip = 0 # Number of frames to be skipped in input (e.g 2 will code every third frame). + # Note that this now excludes intermediate (i.e. B) coded pictures +ChromaQPOffset = 0 # Chroma QP offset (-51..51) + +DisableSubpelME = 0 # Disable Subpixel Motion Estimation (0=off/default, 1=on) +SearchRange = 32 # Max search range + +MEDistortionFPel = 0 # Select error metric for Full-Pel ME (0: SAD, 1: SSE, 2: Hadamard SAD) +MEDistortionHPel = 2 # Select error metric for Half-Pel ME (0: SAD, 1: SSE, 2: Hadamard SAD) +MEDistortionQPel = 2 # Select error metric for Quarter-Pel ME (0: SAD, 1: SSE, 2: Hadamard SAD) +MDDistortion = 2 # Select error metric for Mode Decision (0: SAD, 1: SSE, 2: Hadamard SAD) +SkipDeBlockNonRef = 0 # Skip Deblocking (regardless of DFParametersFlag) for non-reference frames (0: off, 1: on) +ChromaMCBuffer = 1 # Calculate Color component interpolated values in advance and store them. + # Provides a trade-off between memory and computational complexity + # (0: disabled/default, 1: enabled) +ChromaMEEnable = 0 # Take into account Color component information during ME + # (0: only first component/default, + # 1: All Color components - Integer refinement only + # 2: All Color components - All refinements) +ChromaMEWeight = 1 # Weighting for chroma components. This parameter should have a relationship with color format. + +NumberReferenceFrames = 5 # Number of previous frames used for inter motion search (0-16) + +PList0References = 0 # P slice List 0 reference override (0 disable, N <= NumberReferenceFrames) +Log2MaxFNumMinus4 = 0 # Sets log2_max_frame_num_minus4 (-1 : based on FramesToBeEncoded/Auto, >=0 : Log2MaxFNumMinus4) +Log2MaxPOCLsbMinus4 = -1 # Sets log2_max_pic_order_cnt_lsb_minus4 (-1 : Auto, >=0 : Log2MaxPOCLsbMinus4) + +GenerateMultiplePPS = 0 # Transmit multiple parameter sets. Currently parameters basically enable all WP modes (0: diabled, 1: enabled) +SendAUD = 0 # Send Access Delimiter Unit NALU (for every access unit) +ResendSPS = 0 # Resend SPS (0: disabled, 1: all Intra pictures, 2: only for IDR, 3: for IDR and OpenGOP I) +ResendPPS = 0 # Resend PPS (with pic_parameter_set_id 0) for every coded Frame/Field pair (0: disabled, 1: enabled) + +MbLineIntraUpdate = 0 # Error robustness(extra intra macro block updates)(0=off, N: One GOB every N frames are intra coded) +RandomIntraMBRefresh = 0 # Forced intra MBs per picture + +########################################################################################## +# PSlice Mode types +########################################################################################## +PSliceSkip = 1 # P-Slice Skip mode consideration (0=disable, 1=enable) +PSliceSearch16x16 = 1 # P-Slice Inter block search 16x16 (0=disable, 1=enable) +PSliceSearch16x8 = 1 # P-Slice Inter block search 16x8 (0=disable, 1=enable) +PSliceSearch8x16 = 1 # P-Slice Inter block search 8x16 (0=disable, 1=enable) +PSliceSearch8x8 = 1 # P-Slice Inter block search 8x8 (0=disable, 1=enable) +PSliceSearch8x4 = 1 # P-Slice Inter block search 8x4 (0=disable, 1=enable) +PSliceSearch4x8 = 1 # P-Slice Inter block search 4x8 (0=disable, 1=enable) +PSliceSearch4x4 = 1 # P-Slice Inter block search 4x4 (0=disable, 1=enable) + +########################################################################################## +# BSlice Mode types +########################################################################################## + +BSliceDirect = 1 # B-Slice Skip mode consideration (0=disable, 1=enable) +BSliceSearch16x16 = 1 # B-Slice Inter block search 16x16 (0=disable, 1=enable) +BSliceSearch16x8 = 1 # B-Slice Inter block search 16x8 (0=disable, 1=enable) +BSliceSearch8x16 = 1 # B-Slice Inter block search 8x16 (0=disable, 1=enable) +BSliceSearch8x8 = 1 # B-Slice Inter block search 8x8 (0=disable, 1=enable) +BSliceSearch8x4 = 1 # B-Slice Inter block search 8x4 (0=disable, 1=enable) +BSliceSearch4x8 = 1 # B-Slice Inter block search 4x8 (0=disable, 1=enable) +BSliceSearch4x4 = 1 # B-Slice Inter block search 4x4 (0=disable, 1=enable) + +BiPredSearch16x16 = 1 # B-Slice Bi-prediction block search 16x16 (0=disable, 1=enable) +BiPredSearch16x8 = 1 # B-Slice Bi-prediction block search 16x8 (0=disable, 1=enable) +BiPredSearch8x16 = 1 # B-Slice Bi-prediction block search 8x16 (0=disable, 1=enable) +BiPredSearch8x8 = 0 # B-Slice Bi-prediction block search 8x8 (0=disable, 1=enable) + +DisableIntra4x4 = 0 # Disable Intra 4x4 modes +DisableIntra16x16 = 0 # Disable Intra 16x16 modes +DisableIntraInInter = 0 # Disable Intra modes for inter slices +IntraDisableInterOnly = 0 # Apply Disabling Intra conditions only to Inter Slices (0:disable/default,1: enable) +Intra4x4ParDisable = 0 # Disable Vertical & Horizontal 4x4 +Intra4x4DiagDisable = 0 # Disable Diagonal 45degree 4x4 +Intra4x4DirDisable = 0 # Disable Other Diagonal 4x4 +Intra16x16ParDisable = 0 # Disable Vertical & Horizontal 16x16 +Intra16x16PlaneDisable = 0 # Disable Planar 16x16 +ChromaIntraDisable = 0 # Disable Intra Chroma modes other than DC +EnableIPCM = 1 # Enable IPCM macroblock mode + +DisposableP = 0 # Enable Disposable P slices in the primary layer (0: disable/default, 1: enable) +DispPQPOffset = 0 # Quantizer offset for disposable P slices (0: default) + +PreferDispOrder = 1 # Prefer display order when building the prediction structure as opposed to coding order (affects intra and IDR periodic insertion, among others) +PreferPowerOfTwo = 0 # Prefer prediction structures that have lengths expressed as powers of two +FrmStructBufferLength = 16 # Length of the frame structure unit buffer; it can be overriden for certain cases + +ChangeQPFrame = 0 # Frame in display order from which to apply the Change QP offsets +ChangeQPI = 0 # Change QP offset value for I_SLICE +ChangeQPP = 0 # Change QP offset value for P_SLICE +ChangeQPB = 0 # Change QP offset value for B_SLICE +ChangeQPSI = 0 # Change QP offset value for SI_SLICE +ChangeQPSP = 0 # Change QP offset value for SP_SLICE + +########################################################################################## +# B Slices +########################################################################################## + +NumberBFrames = 0 # Number of B coded frames inserted (0=not used) +PReplaceBSlice = 0 # Replace B-coded slices with P-coded slices when NumberBFrames>0 +QPBSlice = 30 # Quant. param for B slices (0-51) +BRefPicQPOffset = -1 # Quantization offset for reference B coded pictures (-51..51) +DirectModeType = 1 # Direct Mode Type (0:Temporal 1:Spatial) +DirectInferenceFlag = 1 # Direct Inference Flag (0: Disable 1: Enable) +BList0References = 0 # B slice List 0 reference override (0 disable, N <= NumberReferenceFrames) +BList1References = 1 # B slice List 1 reference override (0 disable, N <= NumberReferenceFrames) + # 1 List1 reference is usually recommended for normal GOP Structures. + # A larger value is usually more appropriate if a more flexible + # structure is used (i.e. using HierarchicalCoding) + +BReferencePictures = 0 # Referenced B coded pictures (0=off, 1=B references for secondary layer, 2=B references for primary layer) + +HierarchicalCoding = 0 # B hierarchical coding (0= off, 1= 2 layers, 2= 2 full hierarchy, 3 = explicit) +HierarchyLevelQPEnable = 1 # Adjust QP based on hierarchy level (in increments of 1). Overrides BRefPicQPOffset behavior.(0=off, 1=on) +ExplicitHierarchyFormat = "b3r0b1r1b0e2b2e2b5r1b4e2b6e2" # Explicit Enhancement GOP. Format is {FrameDisplay_orderReferenceQP}. + # Valid values for reference type is r:reference, e:non reference. +ExplicitSeqCoding = 0 # Enable support for explicit sequence coding +ExplicitSeqFile = "explicit_seq.cfg" +LowDelay = 0 # Apply HierarchicalCoding without delay (i.e., encode in the captured/display order) +ReferenceReorder = 1 # Reorder References according to Poc distance for HierarchicalCoding (0=off, 1=enable, 2=use when LowDelay is set) +PocMemoryManagement = 1 # Memory management based on Poc Distances for HierarchicalCoding (0=off, 1=on, 2=use when LowDelay is set) +SetFirstAsLongTerm = 0 # Set first frame as long term + +BiPredMotionEstimation = 1 # Enable Bipredictive based Motion Estimation (0:disabled, 1:enabled) +BiPredMERefinements = 3 # Bipredictive ME extra refinements (0: single, N: N extra refinements (1 default) +BiPredMESearchRange = 16 # Bipredictive ME Search range (8 default). Note that range is halved for every extra refinement. +BiPredMESubPel = 2 # Bipredictive ME Subpixel Consideration (0: disabled, 1: single level, 2: dual level) + + +########################################################################################## +# SP Frames +########################################################################################## + +SPPicturePeriodicity = 0 # SP-Picture Periodicity (0=not used) +SPSwitchPeriod = 0 # Switch period (in terms of switching SP/SI frames) between bitstream 1 and bitstream 2 +QPSPSlice = 36 # Quant. param of SP-Slices for Prediction Error (0-51) +QPSISlice = 36 # Quant. param of SI-Slices for Prediction Error (0-51) +QPSP2Slice = 35 # Quant. param of SP/SI-Slices for Predicted Blocks (0-51) +SI_FRAMES = 0 # SI frame encoding flag (0=not used, 1=used) +SP_output = 0 # Controls whether coefficients will be output to encode switching SP frames (0=no, 1=yes) +SP_output_name = "low_quality.dat" # Filename for SP output coefficients +SP2_FRAMES = 0 # switching SP frame encoding flag (0=not used, 1=used) +SP2_input_name1 = "high_quality.dat" # Filename for the first swithed bitstream coefficients +SP2_input_name2 = "low_quality.dat" # Filename for the second switched bitstream coefficients + +########################################################################################## +# Output Control, NALs +########################################################################################## + +SymbolMode = 1 # Symbol mode (Entropy coding method: 0=UVLC, 1=CABAC) +OutFileMode = 0 # Output file mode, 0:Annex B, 1:RTP +PartitionMode = 0 # Partition Mode, 0: no DP, 1: 3 Partitions per Slice + +########################################################################################## +# CABAC context initialization +########################################################################################## + +ContextInitMethod = 1 # Context init (0: fixed, 1: adaptive) +FixedModelNumber = 0 # model number for fixed decision for inter slices ( 0, 1, or 2 ) + +########################################################################################## +# Interlace Handling +######################################################################################### + +PicInterlace = 0 # Picture AFF (0: frame coding, 1: field coding, 2:adaptive frame/field coding) +MbInterlace = 0 # Macroblock AFF (0: frame coding, 1: field coding, 2:adaptive frame/field coding, 3: frame MB-only AFF) +IntraBottom = 0 # Force Intra Bottom at GOP Period + +########################################################################################## +# Weighted Prediction +######################################################################################### + +WeightedPrediction = 0 # P picture Weighted Prediction (0=off, 1=explicit mode) +WeightedBiprediction = 0 # B picture Weighted Prediciton (0=off, 1=explicit mode, 2=implicit mode) +ChromaWeightSupport = 1 # Enable consideration of weights for Chroma components +UseWeightedReferenceME = 1 # Use weighted reference for ME (0=off, 1=on) +WPMethod = 1 # WP method (0: DC based, 1: LMS based) +WPIterMC = 0 # Iterative Motion compensated based weighted prediction method +EnhancedBWeightSupport = 0 # Enhanced B Weight support (needs revisit if we wish to merge with WPMethod) +WPMCPrecision = 2 # Improved Motion Compensation Precision using WP based methods. + # Clones WP references with slightly modified rounding offsets (Requires RDPictureDecision and GenerateMultiplePPS) : + # 0: disabled (default) + # 1: Up to one additional coding pass. Ref0 is 0, ref1 is 0 with a -1 offset + # 2: Up to two additional coding passes. (1) Ref0 is 0, ref1 is 0 with a -1 offset, (1) Ref0 is 0 with a -1 offset, ref1 is 0 +WPMCPrecFullRef = 0 # Increases the number of references in the reference picture lists to account + # for the lost reference slot when reordering is used during a coding pass in WPMCPrecision for reference replication. + # The number of references in non-reordered passes stays unchanged +WPMCPrecBSlice = 1 # 2: Apply rounding on every B slice. This efectively disables the evaluation of alternative QPs during RDPictureDecision. + # 1: Disable rounding for non-reference B slices. Non-reference B slices are evaluated for alternative QPs during RDPictureDecision. + # 0: Disable rounding for B slices. + +########################################################################################## +# Picture based Multi-pass encoding +######################################################################################### + +RDPictureDecision = 0 # Perform multiple pass coding and make RD optimal decision among them +RDPSliceBTest = 0 # Perform Slice level RD decision between P and B slices. +RDPictureMaxPassISlice = 1 # Max number of coding passes for I slices, valid values [1,3], default is 1 +RDPictureMaxPassPSlice = 2 # Max number of coding passes for P slices, valid values [1,6], default is 2 +RDPictureMaxPassBSlice = 3 # Max number of coding passes for B slices, valid values [1,6], default is 3 +RDPictureFrameQPPSlice = 0 # Perform additional frame level QP check (QP+/-1) for P slices, 0: disabled (default), 1: enabled +RDPictureFrameQPBSlice = 0 # Perform additional frame level QP check (QP+/-1) for B slices, 0: disabled, 1: enabled (default) +RDPictureDeblocking = 0 # Perform another coding pass to check non-deblocked picture, 0: disabled (default), 1: enabled +RDPictureDirectMode = 0 # Perform another coding pass to check the alternative direct mode for B slices, , 0: disabled (default), 1: enabled + +########################################################################################## +# Deblocking filter parameters +########################################################################################## + +DFParametersFlag = 0 # Configure deblocking filter (0=parameters below ignored, 1=parameters sent) + # Note that for pictures with multiple slice types, + # only the type of the first slice will be considered. +DFDisableRefISlice = 0 # Disable deblocking filter in reference I coded pictures (0=Filter, 1=No Filter). +DFAlphaRefISlice = 0 # Reference I coded pictures Alpha offset div. 2, {-6, -5, ... 0, +1, .. +6} +DFBetaRefISlice = 0 # Reference I coded pictures Beta offset div. 2, {-6, -5, ... 0, +1, .. +6} +DFDisableNRefISlice = 0 # Disable deblocking filter in non reference I coded pictures (0=Filter, 1=No Filter). +DFAlphaNRefISlice = 0 # Non Reference I coded pictures Alpha offset div. 2, {-6, -5, ... 0, +1, .. +6} +DFBetaNRefISlice = 0 # Non Reference I coded pictures Beta offset div. 2, {-6, -5, ... 0, +1, .. +6} +DFDisableRefPSlice = 0 # Disable deblocking filter in reference P coded pictures (0=Filter, 1=No Filter). +DFAlphaRefPSlice = 0 # Reference P coded pictures Alpha offset div. 2, {-6, -5, ... 0, +1, .. +6} +DFBetaRefPSlice = 0 # Reference P coded pictures Beta offset div. 2, {-6, -5, ... 0, +1, .. +6} +DFDisableNRefPSlice = 0 # Disable deblocking filter in non reference P coded pictures (0=Filter, 1=No Filter). +DFAlphaNRefPSlice = 0 # Non Reference P coded pictures Alpha offset div. 2, {-6, -5, ... 0, +1, .. +6} +DFBetaNRefPSlice = 0 # Non Reference P coded pictures Beta offset div. 2, {-6, -5, ... 0, +1, .. +6} +DFDisableRefBSlice = 0 # Disable deblocking filter in reference B coded pictures (0=Filter, 1=No Filter). +DFAlphaRefBSlice = 0 # Reference B coded pictures Alpha offset div. 2, {-6, -5, ... 0, +1, .. +6} +DFBetaRefBSlice = 0 # Reference B coded pictures Beta offset div. 2, {-6, -5, ... 0, +1, .. +6} +DFDisableNRefBSlice = 0 # Disable deblocking filter in non reference B coded pictures (0=Filter, 1=No Filter). +DFAlphaNRefBSlice = 0 # Non Reference B coded pictures Alpha offset div. 2, {-6, -5, ... 0, +1, .. +6} +DFBetaNRefBSlice = 0 # Non Reference B coded pictures Beta offset div. 2, {-6, -5, ... 0, +1, .. +6} + +########################################################################################## +# Error Resilience / Slices +########################################################################################## + +SliceMode = 0 # Slice mode (0=off 1=fixed #mb in slice 2=fixed #bytes in slice 3=use callback) +SliceArgument = 50 # Slice argument (Arguments to modes 1 and 2 above) + +num_slice_groups_minus1 = 0 # Number of Slice Groups Minus 1, 0 == no FMO, 1 == two slice groups, etc. +slice_group_map_type = 0 # 0: Interleave, 1: Dispersed, 2: Foreground with left-over, + # 3: Box-out, 4: Raster Scan 5: Wipe + # 6: Explicit, slice_group_id read from SliceGroupConfigFileName +slice_group_change_direction_flag = 0 # 0: box-out clockwise, raster scan or wipe right, + # 1: box-out counter clockwise, reverse raster scan or wipe left +slice_group_change_rate_minus1 = 85 # +SliceGroupConfigFileName = "sg0conf.cfg" # Used for slice_group_map_type 0, 2, 6 + +UseRedundantPicture = 0 # 0: not used, 1: enabled +NumRedundantHierarchy = 1 # 0-4 +PrimaryGOPLength = 10 # GOP length for redundant allocation (1-16) + # NumberReferenceFrames must be no less than PrimaryGOPLength when redundant slice enabled +NumRefPrimary = 1 # Actually used number of references for primary slices (1-16) + +########################################################################################## +# Search Range Restriction / RD Optimization +########################################################################################## + +RestrictSearchRange = 2 # restriction for (0: blocks and ref, 1: ref, 2: no restrictions) +RDOptimization = 1 # rd-optimized mode decision + # 0: RD-off (Low complexity mode) + # 1: RD-on (High complexity mode) + # 2: RD-on (Fast high complexity mode - not work in FREX Profiles) + # 3: with losses +I16RDOpt = 0 # perform rd-optimized mode decision for Intra 16x16 MB + # 0: SAD-based mode decision for Intra 16x16 MB + # 1: RD-based mode decision for Intra 16x16 MB +SubMBCodingState = 1 # submacroblock coding state + # 0: lowest complexity, do not store or reset coding state during sub-MB mode decision + # 1: medium complexity, reset to master coding state (for current mode) during sub-MB mode decision + # 2: highest complexity, store and reset coding state during sub-MB mode decision +DistortionSSIM = 0 # Compute SSIM distortion. (0: disabled/default, 1: enabled) +DistortionMS_SSIM = 0 # Compute Multiscale SSIM distortion. (0: disabled/default, 1: enabled) +SSIMOverlapSize = 8 # Overlap size to calculate SSIM distortion (1: pixel by pixel, 8: no overlap) +DistortionYUVtoRGB = 0 # Calculate distortion in RGB domain after conversion from YCbCr (0:off, 1:on) +CtxAdptLagrangeMult = 0 # Context Adaptive Lagrange Multiplier + # 0: disabled (default) + # 1: enabled (works best when RDOptimization=0) +FastCrIntraDecision = 0 # Fast Chroma intra mode decision (0:off, 1:on) +DisableThresholding = 0 # Disable Thresholding of Transform Coefficients (0:off, 1:on) +DisableBSkipRDO = 0 # Disable B Skip Mode consideration from RDO Mode decision (0:off, 1:on) +BiasSkipRDO = 0 # Negative Bias for Skip/DirectSkip modes (0: off, 1: on) +ForceTrueRateRDO = 0 # Force true rate (even zero values) during RDO process +SkipIntraInInterSlices = 0 # Skips Intra mode checking in inter slices if certain mode decisions are satisfied (0: off, 1: on) +WeightY = 1 # Luma weight for RDO +WeightCb = 1 # Cb weight for RDO +WeightCr = 1 # Cr weight for RDO + +########################################################################################## +# Explicit Lambda Usage +########################################################################################## +UseExplicitLambdaParams = 0 # Use explicit lambda scaling parameters (0:disabled, 1:enable lambda weight, 2: use explicit lambda value) +UpdateLambdaChromaME = 0 # Update lambda given Chroma ME consideration +FixedLambdaISlice = 0.1 # Fixed Lambda value for I slices +FixedLambdaPSlice = 0.1 # Fixed Lambda value for P slices +FixedLambdaBSlice = 0.1 # Fixed Lambda value for B slices +FixedLambdaRefBSlice = 0.1 # Fixed Lambda value for Referenced B slices +FixedLambdaSPSlice = 0.1 # Fixed Lambda value for SP slices +FixedLambdaSISlice = 0.1 # Fixed Lambda value for SI slices + +LambdaWeightISlice = 0.65 # scaling param for I slices. This will be used as a multiplier i.e. lambda=LambdaWeightISlice * 2^((QP-12)/3) +LambdaWeightPSlice = 0.68 # scaling param for P slices. This will be used as a multiplier i.e. lambda=LambdaWeightPSlice * 2^((QP-12)/3) +LambdaWeightBSlice = 0.68 # scaling param for B slices. This will be used as a multiplier i.e. lambda=LambdaWeightBSlice * 2^((QP-12)/3) +LambdaWeightRefBSlice = 0.68 # scaling param for Referenced B slices. This will be used as a multiplier i.e. lambda=LambdaWeightRefBSlice * 2^((QP-12)/3) +LambdaWeightSPSlice = 0.68 # scaling param for SP slices. This will be used as a multiplier i.e. lambda=LambdaWeightSPSlice * 2^((QP-12)/3) +LambdaWeightSISlice = 0.65 # scaling param for SI slices. This will be used as a multiplier i.e. lambda=LambdaWeightSISlice * 2^((QP-12)/3) + +LossRateA = 5 # expected packet loss rate of the channel for the first partition, only valid if RDOptimization = 3 +LossRateB = 0 # expected packet loss rate of the channel for the second partition, only valid if RDOptimization = 3 +LossRateC = 0 # expected packet loss rate of the channel for the third partition, only valid if RDOptimization = 3 +FirstFrameCorrect = 0 # If 1, the first frame is encoded under the assumption that it is always correctly received. +NumberOfDecoders = 30 # Numbers of decoders used to simulate the channel, only valid if RDOptimization = 3 +RestrictRefFrames = 0 # Doesnt allow reference to areas that have been intra updated in a later frame. + +########################################################################################## +# Additional Stuff +######################################################################################### + +UseConstrainedIntraPred = 0 # If 1, Inter pixels are not used for Intra macroblock prediction. + +NumberofLeakyBuckets = 8 # Number of Leaky Bucket values +LeakyBucketRateFile = "leakybucketrate.cfg" # File from which encoder derives rate values +LeakyBucketParamFile = "leakybucketparam.cfg" # File where encoder stores leakybucketparams + +NumFramesInELayerSubSeq = 0 # number of frames in the Enhanced Scalability Layer(0: no Enhanced Layer) + +SparePictureOption = 0 # (0: no spare picture info, 1: spare picture available) +SparePictureDetectionThr = 6 # Threshold for spare reference pictures detection +SparePicturePercentageThr = 92 # Threshold for the spare macroblock percentage + +PicOrderCntType = 0 # (0: POC mode 0, 1: POC mode 1, 2: POC mode 2) + +######################################################################################## +#Rate control +######################################################################################## + +RateControlEnable = 0 # 0 Disable, 1 Enable +Bitrate = 45020 # Bitrate(bps) +InitialQP = 0 # Initial Quantization Parameter for the first I frame + # InitialQp depends on two values: Bits Per Picture, + # and the GOP length +BasicUnit = 0 # Number of MBs in the basic unit + # should be a fraction of the total number + # of MBs in a frame ("0" sets a BU equal to a frame) +ChannelType = 0 # type of channel( 1=time varying channel; 0=Constant channel) +RCUpdateMode = 0 # Rate Control type. Modes supported : + # 0 = original JM rate control, + # 1 = rate control that is applied to all frames regardless of the slice type, + # 2 = original plus intelligent QP selection for I and B slices (including Hierarchical), + # 3 = original + hybrid quadratic rate control for I and B slice using bit rate statistics + # +RCISliceBitRatio = 1.0 # target ratio of bits for I-coded pictures compared to P-coded Pictures (for RCUpdateMode=3) +RCBSliceBitRatio0 = 0.5 # target ratio of bits for B-coded pictures compared to P-coded Pictures - temporal level 0 (for RCUpdateMode=3) +RCBSliceBitRatio1 = 0.25 # target ratio of bits for B-coded pictures compared to P-coded Pictures - temporal level 1 (for RCUpdateMode=3) +RCBSliceBitRatio2 = 0.25 # target ratio of bits for B-coded pictures compared to P-coded Pictures - temporal level 2 (for RCUpdateMode=3) +RCBSliceBitRatio3 = 0.25 # target ratio of bits for B-coded pictures compared to P-coded Pictures - temporal level 3 (for RCUpdateMode=3) +RCBSliceBitRatio4 = 0.25 # target ratio of bits for B-coded pictures compared to P-coded Pictures - temporal level 4 (for RCUpdateMode=3) +RCBoverPRatio = 0.45 # ratio of bit rate usage of a B-coded picture over a P-coded picture for the SAME QP (for RCUpdateMode=3) +RCIoverPRatio = 3.80 # ratio of bit rate usage of an I-coded picture over a P-coded picture for the SAME QP (for RCUpdateMode=3) +RCMinQPPSlice = 8 # minimum P Slice QP value for rate control +RCMaxQPPSlice = 42 # maximum P Slice QP value for rate control +RCMinQPBSlice = 8 # minimum B Slice QP value for rate control +RCMaxQPBSlice = 42 # maximum B Slice QP value for rate control +RCMinQPISlice = 8 # minimum I Slice QP value for rate control +RCMaxQPISlice = 42 # maximum I Slice QP value for rate control +RCMinQPSPSlice = 8 # minimum SP Slice QP value for rate control +RCMaxQPSPSlice = 40 # maximum SP Slice QP value for rate control +RCMinQPSISlice = 8 # minimum SI Slice QP value for rate control +RCMaxQPSISlice = 42 # maximum SI Slice QP value for rate control +RCMaxQPChange = 4 # maximum QP change for frames of the base layer + +######################################################################################## +#Fast Mode Decision +######################################################################################## +EarlySkipEnable = 0 # Early skip detection (0: Disable 1: Enable) +SelectiveIntraEnable = 0 # Selective Intra mode decision (0: Disable 1: Enable) + +######################################################################################## +#FREXT stuff +######################################################################################## + +YUVFormat = 3 # YUV format (0=4:0:0, 1=4:2:0, 2=4:2:2, 3=4:4:4) +RGBInput = 1 # 1=RGB input, 0=GBR or YUV input +SeparateColourPlane = 0 # 4:4:4 coding: 0=Common mode, 1=Independent mode +SourceBitDepthLuma = 10 # Source Bit Depth for Luma color component (8...14 bits) +SourceBitDepthChroma = 10 # Source Bit Depth for Chroma color components (8...14 bits) +SourceBitDepthRescale = 0 # Rescale bit depth of source for output (0: Disable 1: Enable) +OutputBitDepthLuma = 8 # Output Bit Depth for Luma color component (8...14 bits) +OutputBitDepthChroma = 8 # Output Bit Depth for Chroma color components (8...14 bits) + +CbQPOffset = 0 # Chroma QP offset for Cb-part (-51..51) +CrQPOffset = 0 # Chroma QP offset for Cr-part (-51..51) +Transform8x8Mode = 1 # (0: only 4x4 transform, 1: allow using 8x8 transform additionally, 2: only 8x8 transform) +ReportFrameStats = 0 # (0:Disable Frame Statistics 1: Enable) +DisplayEncParams = 0 # (0:Disable Display of Encoder Params 1: Enable) +Verbose = 1 # level of display verboseness + # 0: short, 1: normal (default), 2: detailed, 3: detailed/nvb +SkipGlobalStats = 0 # Disable global stat accumulation (Set to 1 to avoid bipred core dump) + +######################################################################################## +#Q-Matrix (FREXT) +######################################################################################## +QmatrixFile = "q_matrix.cfg" + +ScalingMatrixPresentFlag = 0 # Enable Q_Matrix (0 Not present, 1 Present in SPS, 2 Present in PPS, 3 Present in both SPS & PPS) +ScalingListPresentFlag0 = 3 # Intra4x4_Luma (0 Not present, 1 Present in SPS, 2 Present in PPS, 3 Present in both SPS & PPS) +ScalingListPresentFlag1 = 3 # Intra4x4_ChromaU (0 Not present, 1 Present in SPS, 2 Present in PPS, 3 Present in both SPS & PPS) +ScalingListPresentFlag2 = 3 # Intra4x4_chromaV (0 Not present, 1 Present in SPS, 2 Present in PPS, 3 Present in both SPS & PPS) +ScalingListPresentFlag3 = 3 # Inter4x4_Luma (0 Not present, 1 Present in SPS, 2 Present in PPS, 3 Present in both SPS & PPS) +ScalingListPresentFlag4 = 3 # Inter4x4_ChromaU (0 Not present, 1 Present in SPS, 2 Present in PPS, 3 Present in both SPS & PPS) +ScalingListPresentFlag5 = 3 # Inter4x4_ChromaV (0 Not present, 1 Present in SPS, 2 Present in PPS, 3 Present in both SPS & PPS) +ScalingListPresentFlag6 = 3 # Intra8x8_Luma (0 Not present, 1 Present in SPS, 2 Present in PPS, 3 Present in both SPS & PPS) +ScalingListPresentFlag7 = 3 # Inter8x8_Luma (0 Not present, 1 Present in SPS, 2 Present in PPS, 3 Present in both SPS & PPS) +ScalingListPresentFlag8 = 3 # Intra8x8_ChromaU for 4:4:4 (0 Not present, 1 Present in SPS, 2 Present in PPS, 3 Present in both SPS & PPS) +ScalingListPresentFlag9 = 3 # Inter8x8_ChromaU for 4:4:4 (0 Not present, 1 Present in SPS, 2 Present in PPS, 3 Present in both SPS & PPS) +ScalingListPresentFlag10 = 3 # Intra8x8_ChromaV for 4:4:4 (0 Not present, 1 Present in SPS, 2 Present in PPS, 3 Present in both SPS & PPS) +ScalingListPresentFlag11 = 3 # Inter8x8_ChromaV for 4:4:4 (0 Not present, 1 Present in SPS, 2 Present in PPS, 3 Present in both SPS & PPS) + +######################################################################################## +#Rounding Offset control +######################################################################################## + +OffsetMatrixPresentFlag = 0 # Enable Explicit Offset Quantization Matrices (0: disable 1: enable) +QOffsetMatrixFile = "q_offset.cfg" # Explicit Quantization Matrices file + +AdaptiveRounding = 1 # Enable Adaptive Rounding based on JVT-N011 (0: disable, 1: enable) +AdaptRoundingFixed = 1 # Enable Global Adaptive rounding for all qps (0: disable, 1: enable - default/old) +AdaptRndPeriod = 16 # Period in terms of MBs for updating rounding offsets. + # 0 performs update at the picture level. Default is 16. 1 is as in JVT-N011. +AdaptRndChroma = 1 # Enables coefficient rounding adaptation for chroma + +AdaptRndWFactorIRef = 8 # Adaptive Rounding Weight for I/SI slices in reference pictures /4096 +AdaptRndWFactorPRef = 8 # Adaptive Rounding Weight for P/SP slices in reference pictures /4096 +AdaptRndWFactorBRef = 8 # Adaptive Rounding Weight for B slices in reference pictures /4096 +AdaptRndWFactorINRef = 8 # Adaptive Rounding Weight for I/SI slices in non reference pictures /4096 +AdaptRndWFactorPNRef = 8 # Adaptive Rounding Weight for P/SP slices in non reference pictures /4096 +AdaptRndWFactorBNRef = 8 # Adaptive Rounding Weight for B slices in non reference pictures /4096 + +AdaptRndCrWFactorIRef = 8 # Chroma Adaptive Rounding Weight for I/SI slices in reference pictures /4096 +AdaptRndCrWFactorPRef = 8 # Chroma Adaptive Rounding Weight for P/SP slices in reference pictures /4096 +AdaptRndCrWFactorBRef = 8 # Chroma Adaptive Rounding Weight for B slices in reference pictures /4096 +AdaptRndCrWFactorINRef = 8 # Chroma Adaptive Rounding Weight for I/SI slices in non reference pictures /4096 +AdaptRndCrWFactorPNRef = 8 # Chroma Adaptive Rounding Weight for P/SP slices in non reference pictures /4096 +AdaptRndCrWFactorBNRef = 8 # Chroma Adaptive Rounding Weight for B slices in non reference pictures /4096 + +################################################################ +# Rate Distortion Optimized Quantization +################################################################ +UseRDOQuant = 0 # Use Rate Distortion Optimized Quantization (0=disable, 1=enable) +RDOQ_DC = 1 # Enable Rate Distortion Optimized Quantization for DC components (0=disable, 1=enable) +RDOQ_CR = 1 # Enable Rate Distortion Optimized Quantization for Chroma components (0=disable, 1=enable) +RDOQ_DC_CR = 1 # Enable Rate Distortion Optimized Quantization for Chroma DC components (0=disable, 1=enable) +RDOQ_QP_Num = 5 # 1-9: Number of QP tested in RDO_Q (I/P/B slice) +RDOQ_CP_Mode = 0 # copy Mode from first QP tested +RDOQ_CP_MV = 0 # copy MV from first QP tested +RDOQ_Fast = 0 # Fast RDOQ decision method for multiple QPs + +######################################################################################## +#Lossless Coding (FREXT) +######################################################################################## + +LosslessCoding = 0 # Enable lossless coding when qpprime_y is zero (0 Disabled, 1 Enabled) + +######################################################################################## +#Fast Motion Estimation Control Parameters +######################################################################################## + +SearchMode = 0 # Motion estimation mode + # -1 = Full Search + # 0 = Fast Full Search (default) + # 1 = UMHexagon Search + # 2 = Simplified UMHexagon Search + # 3 = Enhanced Predictive Zonal Search (EPZS) + +UMHexDSR = 1 # Use Search Range Prediction. Only for UMHexagonS method + # (0:disable, 1:enabled/default) +UMHexScale = 3 # Use Scale_factor for different image sizes. Only for UMHexagonS method + # (0:disable, 3:/default) + # Increasing value can speed up Motion Search. + +EPZSPattern = 2 # Select EPZS primary refinement pattern. + # (0: small diamond, 1: square, 2: extended diamond/default, + # 3: large diamond, 4: SBP Large Diamond, + # 5: PMVFAST ) +EPZSDualRefinement = 3 # Enables secondary refinement pattern. + # (0:disabled, 1: small diamond, 2: square, + # 3: extended diamond/default, 4: large diamond, + # 5: SBP Large Diamond, 6: PMVFAST ) +EPZSFixedPredictors = 2 # Enables Window based predictors + # (0:disabled, 1: P only, 2: P and B/default) +EPZSTemporal = 1 # Enables temporal predictors + # (0: disabled, 1: enabled/default) +EPZSSpatialMem = 1 # Enables spatial memory predictors + # (0: disabled, 1: enabled/default) +EPZSBlockType = 1 # Enables block type Predictors + # (0: disabled, 1: enabled/default) +EPZSMinThresScale = 0 # Scaler for EPZS minimum threshold (0 default). + # Increasing value can speed up encoding. +EPZSMedThresScale = 1 # Scaler for EPZS median threshold (1 default). + # Increasing value can speed up encoding. +EPZSMaxThresScale = 2 # Scaler for EPZS maximum threshold (1 default). + # Increasing value can speed up encoding. +EPZSSubPelME = 1 # EPZS Subpel ME consideration +EPZSSubPelMEBiPred = 1 # EPZS Subpel ME consideration for BiPred partitions +EPZSSubPelThresScale = 2 # EPZS Subpel ME Threshold scaler +EPZSSubPelGrid = 1 # Perform EPZS using a subpixel grid + +######################################################################################## +# SEI Parameters +######################################################################################## + +ToneMappingSEIPresentFlag = 1 # Enable Tone mapping SEI (0 Not present, 1 Present) +ToneMappingFile = "ToneMapping.cfg" + +GenerateSEIMessage = 0 # Generate an SEI Text Message +SEIMessageText = "H.264/AVC Encoder" # Text SEI Message + +UseMVLimits = 0 # Use MV Limits +SetMVXLimit = 512 # Horizontal MV Limit (in integer units) +SetMVYLimit = 512 # Vertical MV Limit (in integer units) + +######################################################################################## +# VUI Parameters +######################################################################################## +# the variables below do not affect encoding and decoding +# (many are dummy variables but others can be useful when supported by the decoder) + +EnableVUISupport = 0 # Enable VUI Parameters + diff --git a/bin/encoder_view1.cfg b/bin/encoder_view1.cfg new file mode 100644 index 0000000..318b2a0 --- /dev/null +++ b/bin/encoder_view1.cfg @@ -0,0 +1,102 @@ +# New Input File Format is as follows +# = # Comment +# +# See configfile.h for a list of supported ParameterNames +# +# For bug reporting and known issues see: +# https://ipbt.hhi.de + + +########################################################################################## +# Second View configuration +########################################################################################## +InputFile = "sample_right_320x240.yuv" # Input sequence 2 for the MVC profile +ReconFile = "test_rec2.yuv" # Reconstruction YUV file 2 for the MVC profile + +########################################################################################## +# Encoder Control +########################################################################################## +SearchRange = 32 # Max search range for view 1 (requires SepViewInterSearch) +DisableSubpelME = 0 # Disable Subpixel Motion Estimation for view 1 (0=off/default, 1=on) (requires SepViewInterSearch) +DisableIntraInInter = 0 # Disable Intra modes for inter slices for view 1 (requires SepViewInterSearch) + +PList0Refs = 0 # View 1 P slice List 0 reference override (0 disable, N <= (NumberReferenceFrames + 1)) (requires SepViewInterSearch) + +########################################################################################## +# PSlice Mode types +########################################################################################## +PSliceSkip = 1 # P-Slice Skip mode consideration (0=disable, 1=enable) (requires SepViewInterSearch) +PSliceSearch16x16 = 1 # P-Slice Inter block search 16x16 (0=disable, 1=enable) (requires SepViewInterSearch) +PSliceSearch16x8 = 1 # P-Slice Inter block search 16x8 (0=disable, 1=enable) (requires SepViewInterSearch) +PSliceSearch8x16 = 1 # P-Slice Inter block search 8x16 (0=disable, 1=enable) (requires SepViewInterSearch) +PSliceSearch8x8 = 1 # P-Slice Inter block search 8x8 (0=disable, 1=enable) (requires SepViewInterSearch) +PSliceSearch8x4 = 1 # P-Slice Inter block search 8x4 (0=disable, 1=enable) (requires SepViewInterSearch) +PSliceSearch4x8 = 1 # P-Slice Inter block search 4x8 (0=disable, 1=enable) (requires SepViewInterSearch) +PSliceSearch4x4 = 1 # P-Slice Inter block search 4x4 (0=disable, 1=enable) (requires SepViewInterSearch) + +########################################################################################## +# BSlice Mode types +########################################################################################## + +BSliceDirect = 1 # B-Slice Skip mode consideration (0=disable, 1=enable) (requires SepViewInterSearch) +BSliceSearch16x16 = 1 # B-Slice Inter block search 16x16 (0=disable, 1=enable) (requires SepViewInterSearch) +BSliceSearch16x8 = 1 # B-Slice Inter block search 16x8 (0=disable, 1=enable) (requires SepViewInterSearch) +BSliceSearch8x16 = 1 # B-Slice Inter block search 8x16 (0=disable, 1=enable) (requires SepViewInterSearch) +BSliceSearch8x8 = 1 # B-Slice Inter block search 8x8 (0=disable, 1=enable) (requires SepViewInterSearch) +BSliceSearch8x4 = 1 # B-Slice Inter block search 8x4 (0=disable, 1=enable) (requires SepViewInterSearch) +BSliceSearch4x8 = 1 # B-Slice Inter block search 4x8 (0=disable, 1=enable) (requires SepViewInterSearch) +BSliceSearch4x4 = 1 # B-Slice Inter block search 4x4 (0=disable, 1=enable) (requires SepViewInterSearch) + +MVCEnableInterViewFlag = 1 # enable inter view flag +MVCInterViewReorder = 1 # Reorder References according to interview pictures (0=off, 1=enable) +QPOffset = 0 # QP offset during rate control for View 1 +SepViewInterSearch = 0 # If set, allows different InterSearch modes to be set for each view (default is 0 - disabled) +NoResidueRDO = 1 # Test no residue case for View 1 during RDO (0: disabled, 1: enabled - default) +MVCInterViewForceB = 0 # Force B coded pictures for Enhancement layer (assuming list order is as desired, this may be able to provide some coding gains) + +########################################################################################## +# B Slices +########################################################################################## + +BList0Refs = 0 # View 1 B slice List 0 reference override (0 disable, N <= (NumberReferenceFrames + 1)) (requires SepViewInterSearch) +BList1Refs = 1 # View 1 B slice List 1 reference override (0 disable, N <= NumberReferenceFrames) (requires SepViewInterSearch) + +BiPredMESearchRange = 16 # Bipredictive ME Search range (8 default) for view 1. Note that range is halved for every extra refinement. Requires SepViewInterSearch. + +########################################################################################## +# Deblocking filter parameters +########################################################################################## + +DFDisableRefISlice = 0 # Disable deblocking filter in reference I coded pictures (0=Filter, 1=No Filter). +DFAlphaRefISlice = 0 # Reference I coded pictures Alpha offset div. 2, {-6, -5, ... 0, +1, .. +6} +DFBetaRefISlice = 0 # Reference I coded pictures Beta offset div. 2, {-6, -5, ... 0, +1, .. +6} +DFDisableNRefISlice = 0 # Disable deblocking filter in non reference I coded pictures (0=Filter, 1=No Filter). +DFAlphaNRefISlice = 0 # Non Reference I coded pictures Alpha offset div. 2, {-6, -5, ... 0, +1, .. +6} +DFBetaNRefISlice = 0 # Non Reference I coded pictures Beta offset div. 2, {-6, -5, ... 0, +1, .. +6} +DFDisableRefPSlice = 0 # Disable deblocking filter in reference P coded pictures (0=Filter, 1=No Filter). +DFAlphaRefPSlice = 0 # Reference P coded pictures Alpha offset div. 2, {-6, -5, ... 0, +1, .. +6} +DFBetaRefPSlice = 0 # Reference P coded pictures Beta offset div. 2, {-6, -5, ... 0, +1, .. +6} +DFDisableNRefPSlice = 0 # Disable deblocking filter in non reference P coded pictures (0=Filter, 1=No Filter). +DFAlphaNRefPSlice = 0 # Non Reference P coded pictures Alpha offset div. 2, {-6, -5, ... 0, +1, .. +6} +DFBetaNRefPSlice = 0 # Non Reference P coded pictures Beta offset div. 2, {-6, -5, ... 0, +1, .. +6} +DFDisableRefBSlice = 0 # Disable deblocking filter in reference B coded pictures (0=Filter, 1=No Filter). +DFAlphaRefBSlice = 0 # Reference B coded pictures Alpha offset div. 2, {-6, -5, ... 0, +1, .. +6} +DFBetaRefBSlice = 0 # Reference B coded pictures Beta offset div. 2, {-6, -5, ... 0, +1, .. +6} +DFDisableNRefBSlice = 0 # Disable deblocking filter in non reference B coded pictures (0=Filter, 1=No Filter). +DFAlphaNRefBSlice = 0 # Non Reference B coded pictures Alpha offset div. 2, {-6, -5, ... 0, +1, .. +6} +DFBetaNRefBSlice = 0 # Non Reference B coded pictures Beta offset div. 2, {-6, -5, ... 0, +1, .. +6} + +######################################################################################## +#Fast Motion Estimation Control Parameters +######################################################################################## + +SearchMode = 3 # Defined same as above, but for view 1 (requires SepViewInterSearch to be set) + +EPZSMinThresScale = 0 # Enh layer scaler for EPZS minimum threshold (0 default). + # Increasing value can speed up encoding. EnableEnhLayerEPZSScalers must be 1. +EPZSMedThresScale = 1 # Enh layer scaler for EPZS median threshold (1 default). + # Increasing value can speed up encoding. EnableEnhLayerEPZSScalers must be 1. +EPZSMaxThresScale = 2 # Enh layer scaler for EPZS maximum threshold (1 default). + # Increasing value can speed up encoding. EnableEnhLayerEPZSScalers must be 1. +EPZSSubPelThresScale = 1 # Enh layer EPZS Subpel ME Threshold scaler. EnableEnhLayerEPZSScalers must be 1. + diff --git a/bin/encoder_yuv422.cfg b/bin/encoder_yuv422.cfg new file mode 100644 index 0000000..4bd8b13 --- /dev/null +++ b/bin/encoder_yuv422.cfg @@ -0,0 +1,635 @@ +# New Input File Format is as follows +# = # Comment +# +# See configfile.h for a list of supported ParameterNames +# +# For bug reporting and known issues see: +# https://ipbt.hhi.fraunhofer.de + + +########################################################################################## +# Files +########################################################################################## +InputFile = "foreman_part_qcif_422.yuv" # Input sequence +InputHeaderLength = 0 # If the inputfile has a header, state it's length in byte here +StartFrame = 0 # Start frame for encoding. (0-N) +FramesToBeEncoded = 3 # Number of frames to be coded +FrameRate = 30.0 # Frame Rate per second (0.1-100.0) +SourceWidth = 176 # Source frame width +SourceHeight = 144 # Source frame height +SourceResize = 0 # Resize source size for output +OutputWidth = 176 # Output frame width +OutputHeight = 144 # Output frame height +ProcessInput = 0 # Filter Input Sequence +Interleaved = 0 # 0: Planar input, 1: Packed input +StandardRange = 0 # 0: Standard range 1: Full range (RGB input) +VideoCode = 1 # Video codes for RGB ==> YUV conversions + # 0 = NULL, + # 1 = ITU_REC709, + # 2 = CCIR_601, + # 3 = FCC, + # 4 = ITU_REC624BG, + # 5 = SMPTE_170M, + # 6 = SMPTE_240M, + # 7 = SMPTE_260M, + # 8 = ITU_REC709_EXACT + +TraceFile = "trace_enc.txt" # Trace file +ReconFile = "test_rec.yuv" # Reconstruction YUV file +OutputFile = "test.264" # Bitstream +StatsFile = "stats.dat" # Coding statistics file + +########################################################################################## +# Encoder Control +########################################################################################## +ProfileIDC = 122 # Profile IDC (66=baseline, 77=main, 88=extended; FREXT Profiles: 100=High, 110=High 10, 122=High 4:2:2, 244=High 4:4:4, 44=CAVLC 4:4:4 Intra) +IntraProfile = 0 # Activate Intra Profile for FRExt (0: false, 1: true) + # (e.g. ProfileIDC=110, IntraProfile=1 => High 10 Intra Profile) +LevelIDC = 40 # Level IDC (e.g. 20 = level 2.0) + +IntraPeriod = 0 # Period of I-pictures (0=only first) +IDRPeriod = 0 # Period of IDR pictures (0=only first) +AdaptiveIntraPeriod = 1 # Adaptive intra period +AdaptiveIDRPeriod = 0 # Adaptive IDR period +IntraDelay = 0 # Intra (IDR) picture delay (i.e. coding structure of PPIPPP... ) +EnableIDRGOP = 0 # Support for IDR closed GOPs (0: disabled, 1: enabled) +EnableOpenGOP = 0 # Support for open GOPs (0: disabled, 1: enabled) +QPISlice = 28 # Quant. param for I Slices (0-51) +QPPSlice = 28 # Quant. param for P Slices (0-51) +FrameSkip = 0 # Number of frames to be skipped in input (e.g 2 will code every third frame). + # Note that this now excludes intermediate (i.e. B) coded pictures +ChromaQPOffset = 0 # Chroma QP offset (-51..51) + +DisableSubpelME = 0 # Disable Subpixel Motion Estimation (0=off/default, 1=on) +SearchRange = 32 # Max search range + +MEDistortionFPel = 0 # Select error metric for Full-Pel ME (0: SAD, 1: SSE, 2: Hadamard SAD) +MEDistortionHPel = 2 # Select error metric for Half-Pel ME (0: SAD, 1: SSE, 2: Hadamard SAD) +MEDistortionQPel = 2 # Select error metric for Quarter-Pel ME (0: SAD, 1: SSE, 2: Hadamard SAD) +MDDistortion = 2 # Select error metric for Mode Decision (0: SAD, 1: SSE, 2: Hadamard SAD) +SkipDeBlockNonRef = 0 # Skip Deblocking (regardless of DFParametersFlag) for non-reference frames (0: off, 1: on) +ChromaMCBuffer = 1 # Calculate Color component interpolated values in advance and store them. + # Provides a trade-off between memory and computational complexity + # (0: disabled/default, 1: enabled) +ChromaMEEnable = 0 # Take into account Color component information during ME + # (0: only first component/default, + # 1: All Color components - Integer refinement only + # 2: All Color components - All refinements) +ChromaMEWeight = 1 # Weighting for chroma components. This parameter should have a relationship with color format. + +NumberReferenceFrames = 5 # Number of previous frames used for inter motion search (0-16) + +PList0References = 0 # P slice List 0 reference override (0 disable, N <= NumberReferenceFrames) +Log2MaxFNumMinus4 = 0 # Sets log2_max_frame_num_minus4 (-1 : based on FramesToBeEncoded/Auto, >=0 : Log2MaxFNumMinus4) +Log2MaxPOCLsbMinus4 = -1 # Sets log2_max_pic_order_cnt_lsb_minus4 (-1 : Auto, >=0 : Log2MaxPOCLsbMinus4) + +GenerateMultiplePPS = 0 # Transmit multiple parameter sets. Currently parameters basically enable all WP modes (0: diabled, 1: enabled) +ResendPPS = 0 # Resend PPS (with pic_parameter_set_id 0) for every coded Frame/Field pair (0: disabled, 1: enabled) + +MbLineIntraUpdate = 0 # Error robustness(extra intra macro block updates)(0=off, N: One GOB every N frames are intra coded) +RandomIntraMBRefresh = 0 # Forced intra MBs per picture + +########################################################################################## +# PSlice Mode types +########################################################################################## +PSliceSkip = 1 # P-Slice Skip mode consideration (0=disable, 1=enable) +PSliceSearch16x16 = 1 # P-Slice Inter block search 16x16 (0=disable, 1=enable) +PSliceSearch16x8 = 1 # P-Slice Inter block search 16x8 (0=disable, 1=enable) +PSliceSearch8x16 = 1 # P-Slice Inter block search 8x16 (0=disable, 1=enable) +PSliceSearch8x8 = 1 # P-Slice Inter block search 8x8 (0=disable, 1=enable) +PSliceSearch8x4 = 1 # P-Slice Inter block search 8x4 (0=disable, 1=enable) +PSliceSearch4x8 = 1 # P-Slice Inter block search 4x8 (0=disable, 1=enable) +PSliceSearch4x4 = 1 # P-Slice Inter block search 4x4 (0=disable, 1=enable) + +########################################################################################## +# BSlice Mode types +########################################################################################## + +BSliceDirect = 1 # B-Slice Skip mode consideration (0=disable, 1=enable) +BSliceSearch16x16 = 1 # B-Slice Inter block search 16x16 (0=disable, 1=enable) +BSliceSearch16x8 = 1 # B-Slice Inter block search 16x8 (0=disable, 1=enable) +BSliceSearch8x16 = 1 # B-Slice Inter block search 8x16 (0=disable, 1=enable) +BSliceSearch8x8 = 1 # B-Slice Inter block search 8x8 (0=disable, 1=enable) +BSliceSearch8x4 = 1 # B-Slice Inter block search 8x4 (0=disable, 1=enable) +BSliceSearch4x8 = 1 # B-Slice Inter block search 4x8 (0=disable, 1=enable) +BSliceSearch4x4 = 1 # B-Slice Inter block search 4x4 (0=disable, 1=enable) + +BiPredSearch16x16 = 1 # B-Slice Bi-prediction block search 16x16 (0=disable, 1=enable) +BiPredSearch16x8 = 1 # B-Slice Bi-prediction block search 16x8 (0=disable, 1=enable) +BiPredSearch8x16 = 1 # B-Slice Bi-prediction block search 8x16 (0=disable, 1=enable) +BiPredSearch8x8 = 0 # B-Slice Bi-prediction block search 8x8 (0=disable, 1=enable) + +DisableIntra4x4 = 0 # Disable Intra 4x4 modes +DisableIntra16x16 = 0 # Disable Intra 16x16 modes +DisableIntraInInter = 0 # Disable Intra modes for inter slices +IntraDisableInterOnly = 0 # Apply Disabling Intra conditions only to Inter Slices (0:disable/default,1: enable) +Intra4x4ParDisable = 0 # Disable Vertical & Horizontal 4x4 +Intra4x4DiagDisable = 0 # Disable Diagonal 45degree 4x4 +Intra4x4DirDisable = 0 # Disable Other Diagonal 4x4 +Intra16x16ParDisable = 0 # Disable Vertical & Horizontal 16x16 +Intra16x16PlaneDisable = 0 # Disable Planar 16x16 +ChromaIntraDisable = 0 # Disable Intra Chroma modes other than DC +EnableIPCM = 1 # Enable IPCM macroblock mode + +DisposableP = 0 # Enable Disposable P slices in the primary layer (0: disable/default, 1: enable) +DispPQPOffset = 0 # Quantizer offset for disposable P slices (0: default) + +PreferDispOrder = 1 # Prefer display order when building the prediction structure as opposed to coding order (affects intra and IDR periodic insertion, among others) +PreferPowerOfTwo = 0 # Prefer prediction structures that have lengths expressed as powers of two +FrmStructBufferLength = 16 # Length of the frame structure unit buffer; it can be overriden for certain cases + +ChangeQPFrame = 0 # Frame in display order from which to apply the Change QP offsets +ChangeQPI = 0 # Change QP offset value for I_SLICE +ChangeQPP = 0 # Change QP offset value for P_SLICE +ChangeQPB = 0 # Change QP offset value for B_SLICE +ChangeQPSI = 0 # Change QP offset value for SI_SLICE +ChangeQPSP = 0 # Change QP offset value for SP_SLICE + +########################################################################################## +# B Slices +########################################################################################## + +NumberBFrames = 1 # Number of B coded frames inserted (0=not used) +QPBSlice = 30 # Quant. param for B slices (0-51) +BRefPicQPOffset = -1 # Quantization offset for reference B coded pictures (-51..51) +DirectModeType = 1 # Direct Mode Type (0:Temporal 1:Spatial) +DirectInferenceFlag = 1 # Direct Inference Flag (0: Disable 1: Enable) +BList0References = 0 # B slice List 0 reference override (0 disable, N <= NumberReferenceFrames) +BList1References = 1 # B slice List 1 reference override (0 disable, N <= NumberReferenceFrames) + # 1 List1 reference is usually recommended for normal GOP Structures. + # A larger value is usually more appropriate if a more flexible + # structure is used (i.e. using HierarchicalCoding) + +BReferencePictures = 0 # Referenced B coded pictures (0=off, 1=B references for secondary layer, 2=B references for primary layer) + +HierarchicalCoding = 0 # B hierarchical coding (0= off, 1= 2 layers, 2= 2 full hierarchy, 3 = explicit) +HierarchyLevelQPEnable = 1 # Adjust QP based on hierarchy level (in increments of 1). Overrides BRefPicQPOffset behavior.(0=off, 1=on) +ExplicitHierarchyFormat = "b1r0b3r0b2e2b0e2b4r2" # Explicit Enhancement GOP. Format is {FrameDisplay_orderReferenceQP}. + # Valid values for reference type is r:reference, e:non reference. +ReferenceReorder = 1 # Reorder References according to Poc distance for HierarchicalCoding (0=off, 1=enable, 2=use when LowDelay is set) +PocMemoryManagement = 1 # Memory management based on Poc Distances for HierarchicalCoding (0=off, 1=on, 2=use when LowDelay is set) + +BiPredMotionEstimation = 1 # Enable Bipredictive based Motion Estimation (0:disabled, 1:enabled) +BiPredMERefinements = 3 # Bipredictive ME extra refinements (0: single, N: N extra refinements (1 default) +BiPredMESearchRange = 16 # Bipredictive ME Search range (8 default). Note that range is halved for every extra refinement. +BiPredMESubPel = 2 # Bipredictive ME Subpixel Consideration (0: disabled, 1: single level, 2: dual level) + + +########################################################################################## +# SP Frames +########################################################################################## + +SPPicturePeriodicity = 0 # SP-Picture Periodicity (0=not used) +QPSPSlice = 36 # Quant. param of SP-Slices for Prediction Error (0-51) +QPSISlice = 36 # Quant. param of SI-Slices for Prediction Error (0-51) +QPSP2Slice = 35 # Quant. param of SP/SI-Slices for Predicted Blocks (0-51) +SI_FRAMES = 0 # SI frame encoding flag (0=not used, 1=used) +SP_output = 0 # Controls whether coefficients will be output to encode switching SP frames (0=no, 1=yes) +SP_output_name = "low_quality.dat" # Filename for SP output coefficients +SP2_FRAMES = 0 # switching SP frame encoding flag (0=not used, 1=used) +SP2_input_name1 = "high_quality.dat" # Filename for the first swithed bitstream coefficients +SP2_input_name2 = "low_quality.dat" # Filename for the second switched bitstream coefficients + +########################################################################################## +# Output Control, NALs +########################################################################################## + +SymbolMode = 1 # Symbol mode (Entropy coding method: 0=UVLC, 1=CABAC) +OutFileMode = 0 # Output file mode, 0:Annex B, 1:RTP +PartitionMode = 0 # Partition Mode, 0: no DP, 1: 3 Partitions per Slice + +########################################################################################## +# CABAC context initialization +########################################################################################## + +ContextInitMethod = 1 # Context init (0: fixed, 1: adaptive) +FixedModelNumber = 0 # model number for fixed decision for inter slices ( 0, 1, or 2 ) + +########################################################################################## +# Interlace Handling +######################################################################################### + +PicInterlace = 0 # Picture AFF (0: frame coding, 1: field coding, 2:adaptive frame/field coding) +MbInterlace = 0 # Macroblock AFF (0: frame coding, 1: field coding, 2:adaptive frame/field coding, 3: frame MB-only AFF) +IntraBottom = 0 # Force Intra Bottom at GOP Period + +########################################################################################## +# Weighted Prediction +######################################################################################### + +WeightedPrediction = 0 # P picture Weighted Prediction (0=off, 1=explicit mode) +WeightedBiprediction = 0 # B picture Weighted Prediciton (0=off, 1=explicit mode, 2=implicit mode) +ChromaWeightSupport = 1 # Enable consideration of weights for Chroma components +UseWeightedReferenceME = 1 # Use weighted reference for ME (0=off, 1=on) +WPMethod = 1 # WP method (0: DC based, 1: LMS based) +WPIterMC = 0 # Iterative Motion compensated based weighted prediction method +EnhancedBWeightSupport = 0 # Enhanced B Weight support (needs revisit if we wish to merge with WPMethod) +WPMCPrecision = 0 # Improved Motion Compensation Precision using WP based methods. + # Clones WP references with slightly modified rounding offsets (Requires RDPictureDecision and GenerateMultiplePPS) : + # 0: disabled (default) + # 1: Up to one additional coding pass. Ref0 is 0, ref1 is 0 with a -1 offset + # 2: Up to two additional coding passes. (1) Ref0 is 0, ref1 is 0 with a -1 offset, (1) Ref0 is 0 with a -1 offset, ref1 is 0 +WPMCPrecFullRef = 0 # Increases the number of references in the reference picture lists to account + # for the lost reference slot when reordering is used during a coding pass in WPMCPrecision for reference replication. + # The number of references in non-reordered passes stays unchanged +WPMCPrecBSlice = 1 # 2: Apply rounding on every B slice. This efectively disables the evaluation of alternative QPs during RDPictureDecision. + # 1: Disable rounding for non-reference B slices. Non-reference B slices are evaluated for alternative QPs during RDPictureDecision. + # 0: Disable rounding for B slices. + +########################################################################################## +# Picture based Multi-pass encoding +######################################################################################### + +RDPictureDecision = 0 # Perform multiple pass coding and make RD optimal decision among them +RDPSliceBTest = 0 # Perform Slice level RD decision between P and B slices. +RDPictureMaxPassISlice = 1 # Max number of coding passes for I slices, valid values [1,3], default is 1 +RDPictureMaxPassPSlice = 2 # Max number of coding passes for P slices, valid values [1,6], default is 2 +RDPictureMaxPassBSlice = 3 # Max number of coding passes for B slices, valid values [1,6], default is 3 +RDPictureFrameQPPSlice = 0 # Perform additional frame level QP check (QP+/-1) for P slices, 0: disabled (default), 1: enabled +RDPictureFrameQPBSlice = 0 # Perform additional frame level QP check (QP+/-1) for B slices, 0: disabled, 1: enabled (default) +RDPictureDeblocking = 0 # Perform another coding pass to check non-deblocked picture, 0: disabled (default), 1: enabled +RDPictureDirectMode = 0 # Perform another coding pass to check the alternative direct mode for B slices, , 0: disabled (default), 1: enabled + +########################################################################################## +# Deblocking filter parameters +########################################################################################## + +DFParametersFlag = 0 # Configure deblocking filter (0=parameters below ignored, 1=parameters sent) + # Note that for pictures with multiple slice types, + # only the type of the first slice will be considered. +DFDisableRefISlice = 0 # Disable deblocking filter in reference I coded pictures (0=Filter, 1=No Filter). +DFAlphaRefISlice = 0 # Reference I coded pictures Alpha offset div. 2, {-6, -5, ... 0, +1, .. +6} +DFBetaRefISlice = 0 # Reference I coded pictures Beta offset div. 2, {-6, -5, ... 0, +1, .. +6} +DFDisableNRefISlice = 0 # Disable deblocking filter in non reference I coded pictures (0=Filter, 1=No Filter). +DFAlphaNRefISlice = 0 # Non Reference I coded pictures Alpha offset div. 2, {-6, -5, ... 0, +1, .. +6} +DFBetaNRefISlice = 0 # Non Reference I coded pictures Beta offset div. 2, {-6, -5, ... 0, +1, .. +6} +DFDisableRefPSlice = 0 # Disable deblocking filter in reference P coded pictures (0=Filter, 1=No Filter). +DFAlphaRefPSlice = 0 # Reference P coded pictures Alpha offset div. 2, {-6, -5, ... 0, +1, .. +6} +DFBetaRefPSlice = 0 # Reference P coded pictures Beta offset div. 2, {-6, -5, ... 0, +1, .. +6} +DFDisableNRefPSlice = 0 # Disable deblocking filter in non reference P coded pictures (0=Filter, 1=No Filter). +DFAlphaNRefPSlice = 0 # Non Reference P coded pictures Alpha offset div. 2, {-6, -5, ... 0, +1, .. +6} +DFBetaNRefPSlice = 0 # Non Reference P coded pictures Beta offset div. 2, {-6, -5, ... 0, +1, .. +6} +DFDisableRefBSlice = 0 # Disable deblocking filter in reference B coded pictures (0=Filter, 1=No Filter). +DFAlphaRefBSlice = 0 # Reference B coded pictures Alpha offset div. 2, {-6, -5, ... 0, +1, .. +6} +DFBetaRefBSlice = 0 # Reference B coded pictures Beta offset div. 2, {-6, -5, ... 0, +1, .. +6} +DFDisableNRefBSlice = 0 # Disable deblocking filter in non reference B coded pictures (0=Filter, 1=No Filter). +DFAlphaNRefBSlice = 0 # Non Reference B coded pictures Alpha offset div. 2, {-6, -5, ... 0, +1, .. +6} +DFBetaNRefBSlice = 0 # Non Reference B coded pictures Beta offset div. 2, {-6, -5, ... 0, +1, .. +6} + +########################################################################################## +# Error Resilience / Slices +########################################################################################## + +SliceMode = 0 # Slice mode (0=off 1=fixed #mb in slice 2=fixed #bytes in slice 3=use callback) +SliceArgument = 50 # Slice argument (Arguments to modes 1 and 2 above) + +num_slice_groups_minus1 = 0 # Number of Slice Groups Minus 1, 0 == no FMO, 1 == two slice groups, etc. +slice_group_map_type = 0 # 0: Interleave, 1: Dispersed, 2: Foreground with left-over, + # 3: Box-out, 4: Raster Scan 5: Wipe + # 6: Explicit, slice_group_id read from SliceGroupConfigFileName +slice_group_change_direction_flag = 0 # 0: box-out clockwise, raster scan or wipe right, + # 1: box-out counter clockwise, reverse raster scan or wipe left +slice_group_change_rate_minus1 = 85 # +SliceGroupConfigFileName = "sg0conf.cfg" # Used for slice_group_map_type 0, 2, 6 + +UseRedundantPicture = 0 # 0: not used, 1: enabled +NumRedundantHierarchy = 1 # 0-4 +PrimaryGOPLength = 10 # GOP length for redundant allocation (1-16) + # NumberReferenceFrames must be no less than PrimaryGOPLength when redundant slice enabled +NumRefPrimary = 1 # Actually used number of references for primary slices (1-16) + +########################################################################################## +# Search Range Restriction / RD Optimization +########################################################################################## + +RestrictSearchRange = 2 # restriction for (0: blocks and ref, 1: ref, 2: no restrictions) +RDOptimization = 1 # rd-optimized mode decision + # 0: RD-off (Low complexity mode) + # 1: RD-on (High complexity mode) + # 2: RD-on (Fast high complexity mode - not work in FREX Profiles) + # 3: with losses +I16RDOpt = 0 # perform rd-optimized mode decision for Intra 16x16 MB + # 0: SAD-based mode decision for Intra 16x16 MB + # 1: RD-based mode decision for Intra 16x16 MB +SubMBCodingState = 1 # submacroblock coding state + # 0: lowest complexity, do not store or reset coding state during sub-MB mode decision + # 1: medium complexity, reset to master coding state (for current mode) during sub-MB mode decision + # 2: highest complexity, store and reset coding state during sub-MB mode decision +DistortionSSIM = 0 # Compute SSIM distortion. (0: disabled/default, 1: enabled) +DistortionMS_SSIM = 0 # Compute Multiscale SSIM distortion. (0: disabled/default, 1: enabled) +SSIMOverlapSize = 8 # Overlap size to calculate SSIM distortion (1: pixel by pixel, 8: no overlap) +DistortionYUVtoRGB = 0 # Calculate distortion in RGB domain after conversion from YCbCr (0:off, 1:on) +CtxAdptLagrangeMult = 0 # Context Adaptive Lagrange Multiplier + # 0: disabled (default) + # 1: enabled (works best when RDOptimization=0) +FastCrIntraDecision = 1 # Fast Chroma intra mode decision (0:off, 1:on) +DisableThresholding = 0 # Disable Thresholding of Transform Coefficients (0:off, 1:on) +DisableBSkipRDO = 0 # Disable B Skip Mode consideration from RDO Mode decision (0:off, 1:on) +BiasSkipRDO = 0 # Negative Bias for Skip/DirectSkip modes (0: off, 1: on) +ForceTrueRateRDO = 0 # Force true rate (even zero values) during RDO process +SkipIntraInInterSlices = 0 # Skips Intra mode checking in inter slices if certain mode decisions are satisfied (0: off, 1: on) +WeightY = 1 # Luma weight for RDO +WeightCb = 1 # Cb weight for RDO +WeightCr = 1 # Cr weight for RDO + +########################################################################################## +# Explicit Lambda Usage +########################################################################################## +UseExplicitLambdaParams = 0 # Use explicit lambda scaling parameters (0:disabled, 1:enable lambda weight, 2: use explicit lambda value) +UpdateLambdaChromaME = 0 # Update lambda given Chroma ME consideration +FixedLambdaISlice = 0.1 # Fixed Lambda value for I slices +FixedLambdaPSlice = 0.1 # Fixed Lambda value for P slices +FixedLambdaBSlice = 0.1 # Fixed Lambda value for B slices +FixedLambdaRefBSlice = 0.1 # Fixed Lambda value for Referenced B slices +FixedLambdaSPSlice = 0.1 # Fixed Lambda value for SP slices +FixedLambdaSISlice = 0.1 # Fixed Lambda value for SI slices + +LambdaWeightISlice = 0.65 # scaling param for I slices. This will be used as a multiplier i.e. lambda=LambdaWeightISlice * 2^((QP-12)/3) +LambdaWeightPSlice = 0.68 # scaling param for P slices. This will be used as a multiplier i.e. lambda=LambdaWeightPSlice * 2^((QP-12)/3) +LambdaWeightBSlice = 0.68 # scaling param for B slices. This will be used as a multiplier i.e. lambda=LambdaWeightBSlice * 2^((QP-12)/3) +LambdaWeightRefBSlice = 0.68 # scaling param for Referenced B slices. This will be used as a multiplier i.e. lambda=LambdaWeightRefBSlice * 2^((QP-12)/3) +LambdaWeightSPSlice = 0.68 # scaling param for SP slices. This will be used as a multiplier i.e. lambda=LambdaWeightSPSlice * 2^((QP-12)/3) +LambdaWeightSISlice = 0.65 # scaling param for SI slices. This will be used as a multiplier i.e. lambda=LambdaWeightSISlice * 2^((QP-12)/3) + +LossRateA = 5 # expected packet loss rate of the channel for the first partition, only valid if RDOptimization = 3 +LossRateB = 0 # expected packet loss rate of the channel for the second partition, only valid if RDOptimization = 3 +LossRateC = 0 # expected packet loss rate of the channel for the third partition, only valid if RDOptimization = 3 +FirstFrameCorrect = 0 # If 1, the first frame is encoded under the assumption that it is always correctly received. +NumberOfDecoders = 30 # Numbers of decoders used to simulate the channel, only valid if RDOptimization = 3 +RestrictRefFrames = 0 # Doesnt allow reference to areas that have been intra updated in a later frame. + +########################################################################################## +# Additional Stuff +######################################################################################### + +UseConstrainedIntraPred = 0 # If 1, Inter pixels are not used for Intra macroblock prediction. + +NumberofLeakyBuckets = 8 # Number of Leaky Bucket values +LeakyBucketRateFile = "leakybucketrate.cfg" # File from which encoder derives rate values +LeakyBucketParamFile = "leakybucketparam.cfg" # File where encoder stores leakybucketparams + +NumFramesInELayerSubSeq = 0 # number of frames in the Enhanced Scalability Layer(0: no Enhanced Layer) + +SparePictureOption = 0 # (0: no spare picture info, 1: spare picture available) +SparePictureDetectionThr = 6 # Threshold for spare reference pictures detection +SparePicturePercentageThr = 92 # Threshold for the spare macroblock percentage + +PicOrderCntType = 0 # (0: POC mode 0, 1: POC mode 1, 2: POC mode 2) + +######################################################################################## +#Rate control +######################################################################################## + +RateControlEnable = 0 # 0 Disable, 1 Enable +Bitrate = 45020 # Bitrate(bps) +InitialQP = 0 # Initial Quantization Parameter for the first I frame + # InitialQp depends on two values: Bits Per Picture, + # and the GOP length +BasicUnit = 0 # Number of MBs in the basic unit + # should be a fraction of the total number + # of MBs in a frame ("0" sets a BU equal to a frame) +ChannelType = 0 # type of channel( 1=time varying channel; 0=Constant channel) +RCUpdateMode = 0 # Rate Control type. Modes supported : + # 0 = original JM rate control, + # 1 = rate control that is applied to all frames regardless of the slice type, + # 2 = original plus intelligent QP selection for I and B slices (including Hierarchical), + # 3 = original + hybrid quadratic rate control for I and B slice using bit rate statistics + # +RCISliceBitRatio = 1.0 # target ratio of bits for I-coded pictures compared to P-coded Pictures (for RCUpdateMode=3) +RCBSliceBitRatio0 = 0.5 # target ratio of bits for B-coded pictures compared to P-coded Pictures - temporal level 0 (for RCUpdateMode=3) +RCBSliceBitRatio1 = 0.25 # target ratio of bits for B-coded pictures compared to P-coded Pictures - temporal level 1 (for RCUpdateMode=3) +RCBSliceBitRatio2 = 0.25 # target ratio of bits for B-coded pictures compared to P-coded Pictures - temporal level 2 (for RCUpdateMode=3) +RCBSliceBitRatio3 = 0.25 # target ratio of bits for B-coded pictures compared to P-coded Pictures - temporal level 3 (for RCUpdateMode=3) +RCBSliceBitRatio4 = 0.25 # target ratio of bits for B-coded pictures compared to P-coded Pictures - temporal level 4 (for RCUpdateMode=3) +RCBoverPRatio = 0.45 # ratio of bit rate usage of a B-coded picture over a P-coded picture for the SAME QP (for RCUpdateMode=3) +RCIoverPRatio = 3.80 # ratio of bit rate usage of an I-coded picture over a P-coded picture for the SAME QP (for RCUpdateMode=3) +RCMinQPPSlice = 8 # minimum P Slice QP value for rate control +RCMaxQPPSlice = 42 # maximum P Slice QP value for rate control +RCMinQPBSlice = 8 # minimum B Slice QP value for rate control +RCMaxQPBSlice = 42 # maximum B Slice QP value for rate control +RCMinQPISlice = 8 # minimum I Slice QP value for rate control +RCMaxQPISlice = 42 # maximum I Slice QP value for rate control +RCMinQPSPSlice = 8 # minimum SP Slice QP value for rate control +RCMaxQPSPSlice = 40 # maximum SP Slice QP value for rate control +RCMinQPSISlice = 8 # minimum SI Slice QP value for rate control +RCMaxQPSISlice = 42 # maximum SI Slice QP value for rate control +RCMaxQPChange = 4 # maximum QP change for frames of the base layer + +######################################################################################## +#Fast Mode Decision +######################################################################################## +EarlySkipEnable = 0 # Early skip detection (0: Disable 1: Enable) +SelectiveIntraEnable = 0 # Selective Intra mode decision (0: Disable 1: Enable) + +######################################################################################## +#FREXT stuff +######################################################################################## + +YUVFormat = 2 # YUV format (0=4:0:0, 1=4:2:0, 2=4:2:2, 3=4:4:4) +RGBInput = 0 # 1=RGB input, 0=GBR or YUV input +SeparateColourPlane = 0 # 4:4:4 coding: 0=Common mode, 1=Independent mode +SourceBitDepthLuma = 8 # Source Bit Depth for Luma color component (8...14 bits) +SourceBitDepthChroma = 8 # Source Bit Depth for Chroma color components (8...14 bits) +SourceBitDepthRescale = 0 # Rescale bit depth of source for output (0: Disable 1: Enable) +OutputBitDepthLuma = 8 # Output Bit Depth for Luma color component (8...14 bits) +OutputBitDepthChroma = 8 # Output Bit Depth for Chroma color components (8...14 bits) + +CbQPOffset = 0 # Chroma QP offset for Cb-part (-51..51) +CrQPOffset = 0 # Chroma QP offset for Cr-part (-51..51) +Transform8x8Mode = 1 # (0: only 4x4 transform, 1: allow using 8x8 transform additionally, 2: only 8x8 transform) +ReportFrameStats = 0 # (0:Disable Frame Statistics 1: Enable) +DisplayEncParams = 0 # (0:Disable Display of Encoder Params 1: Enable) +Verbose = 1 # level of display verboseness + # 0: short, 1: normal (default), 2: detailed, 3: detailed/nvb +SkipGlobalStats = 0 # Disable global stat accumulation (Set to 1 to avoid bipred core dump) + +######################################################################################## +#Q-Matrix (FREXT) +######################################################################################## +QmatrixFile = "q_matrix.cfg" + +ScalingMatrixPresentFlag = 0 # Enable Q_Matrix (0 Not present, 1 Present in SPS, 2 Present in PPS, 3 Present in both SPS & PPS) +ScalingListPresentFlag0 = 3 # Intra4x4_Luma (0 Not present, 1 Present in SPS, 2 Present in PPS, 3 Present in both SPS & PPS) +ScalingListPresentFlag1 = 3 # Intra4x4_ChromaU (0 Not present, 1 Present in SPS, 2 Present in PPS, 3 Present in both SPS & PPS) +ScalingListPresentFlag2 = 3 # Intra4x4_chromaV (0 Not present, 1 Present in SPS, 2 Present in PPS, 3 Present in both SPS & PPS) +ScalingListPresentFlag3 = 3 # Inter4x4_Luma (0 Not present, 1 Present in SPS, 2 Present in PPS, 3 Present in both SPS & PPS) +ScalingListPresentFlag4 = 3 # Inter4x4_ChromaU (0 Not present, 1 Present in SPS, 2 Present in PPS, 3 Present in both SPS & PPS) +ScalingListPresentFlag5 = 3 # Inter4x4_ChromaV (0 Not present, 1 Present in SPS, 2 Present in PPS, 3 Present in both SPS & PPS) +ScalingListPresentFlag6 = 3 # Intra8x8_Luma (0 Not present, 1 Present in SPS, 2 Present in PPS, 3 Present in both SPS & PPS) +ScalingListPresentFlag7 = 3 # Inter8x8_Luma (0 Not present, 1 Present in SPS, 2 Present in PPS, 3 Present in both SPS & PPS) +ScalingListPresentFlag8 = 3 # Intra8x8_ChromaU for 4:4:4 (0 Not present, 1 Present in SPS, 2 Present in PPS, 3 Present in both SPS & PPS) +ScalingListPresentFlag9 = 3 # Inter8x8_ChromaU for 4:4:4 (0 Not present, 1 Present in SPS, 2 Present in PPS, 3 Present in both SPS & PPS) +ScalingListPresentFlag10 = 3 # Intra8x8_ChromaV for 4:4:4 (0 Not present, 1 Present in SPS, 2 Present in PPS, 3 Present in both SPS & PPS) +ScalingListPresentFlag11 = 3 # Inter8x8_ChromaV for 4:4:4 (0 Not present, 1 Present in SPS, 2 Present in PPS, 3 Present in both SPS & PPS) + +######################################################################################## +#Rounding Offset control +######################################################################################## + +OffsetMatrixPresentFlag = 1 # Enable Explicit Offset Quantization Matrices (0: disable 1: enable) +QOffsetMatrixFile = "q_offset.cfg" # Explicit Quantization Matrices file + +AdaptiveRounding = 1 # Enable Adaptive Rounding based on JVT-N011 (0: disable, 1: enable) +AdaptRoundingFixed = 1 # Enable Global Adaptive rounding for all qps (0: disable, 1: enable - default/old) +AdaptRndPeriod = 16 # Period in terms of MBs for updating rounding offsets. + # 0 performs update at the picture level. Default is 16. 1 is as in JVT-N011. +AdaptRndChroma = 1 # Enables coefficient rounding adaptation for chroma + +AdaptRndWFactorIRef = 4 # Adaptive Rounding Weight for I/SI slices in reference pictures /4096 +AdaptRndWFactorPRef = 4 # Adaptive Rounding Weight for P/SP slices in reference pictures /4096 +AdaptRndWFactorBRef = 4 # Adaptive Rounding Weight for B slices in reference pictures /4096 +AdaptRndWFactorINRef = 4 # Adaptive Rounding Weight for I/SI slices in non reference pictures /4096 +AdaptRndWFactorPNRef = 4 # Adaptive Rounding Weight for P/SP slices in non reference pictures /4096 +AdaptRndWFactorBNRef = 4 # Adaptive Rounding Weight for B slices in non reference pictures /4096 + +AdaptRndCrWFactorIRef = 4 # Chroma Adaptive Rounding Weight for I/SI slices in reference pictures /4096 +AdaptRndCrWFactorPRef = 4 # Chroma Adaptive Rounding Weight for P/SP slices in reference pictures /4096 +AdaptRndCrWFactorBRef = 4 # Chroma Adaptive Rounding Weight for B slices in reference pictures /4096 +AdaptRndCrWFactorINRef = 4 # Chroma Adaptive Rounding Weight for I/SI slices in non reference pictures /4096 +AdaptRndCrWFactorPNRef = 4 # Chroma Adaptive Rounding Weight for P/SP slices in non reference pictures /4096 +AdaptRndCrWFactorBNRef = 4 # Chroma Adaptive Rounding Weight for B slices in non reference pictures /4096 + +################################################################ +# Rate Distortion Optimized Quantization +################################################################ +UseRDOQuant = 0 # Use Rate Distortion Optimized Quantization (0=disable, 1=enable) +RDOQ_DC = 0 # Enable Rate Distortion Optimized Quantization for DC components (0=disable, 1=enable) +RDOQ_CR = 0 # Enable Rate Distortion Optimized Quantization for Chroma components (0=disable, 1=enable) +RDOQ_DC_CR = 0 # Enable Rate Distortion Optimized Quantization for Chroma DC components (0=disable, 1=enable) +RDOQ_QP_Num = 1 # 1-9: Number of QP tested in RDO_Q (I/P/B slice) +RDOQ_CP_Mode = 1 # copy Mode from first QP tested +RDOQ_CP_MV = 1 # copy MV from first QP tested +RDOQ_Fast = 1 # Fast RDOQ decision method for multiple QPs + +######################################################################################## +#Lossless Coding (FREXT) +######################################################################################## + +LosslessCoding = 0 # Enable lossless coding when qpprime_y is zero (0 Disabled, 1 Enabled) + +######################################################################################## +#Fast Motion Estimation Control Parameters +######################################################################################## + +SearchMode = 0 # Motion estimation mode + # -1 = Full Search + # 0 = Fast Full Search (default) + # 1 = UMHexagon Search + # 2 = Simplified UMHexagon Search + # 3 = Enhanced Predictive Zonal Search (EPZS) + +UMHexDSR = 1 # Use Search Range Prediction. Only for UMHexagonS method + # (0:disable, 1:enabled/default) +UMHexScale = 3 # Use Scale_factor for different image sizes. Only for UMHexagonS method + # (0:disable, 3:/default) + # Increasing value can speed up Motion Search. + +EPZSPattern = 2 # Select EPZS primary refinement pattern. + # (0: small diamond, 1: square, 2: extended diamond/default, + # 3: large diamond, 4: SBP Large Diamond, + # 5: PMVFAST ) +EPZSDualRefinement = 3 # Enables secondary refinement pattern. + # (0:disabled, 1: small diamond, 2: square, + # 3: extended diamond/default, 4: large diamond, + # 5: SBP Large Diamond, 6: PMVFAST ) +EPZSFixedPredictors = 2 # Enables Window based predictors + # (0:disabled, 1: P only, 2: P and B/default) +EPZSTemporal = 1 # Enables temporal predictors + # (0: disabled, 1: enabled/default) +EPZSSpatialMem = 1 # Enables spatial memory predictors + # (0: disabled, 1: enabled/default) +EPZSBlockType = 1 # Enables block type Predictors + # (0: disabled, 1: enabled/default) +EPZSMinThresScale = 0 # Scaler for EPZS minimum threshold (0 default). + # Increasing value can speed up encoding. +EPZSMedThresScale = 1 # Scaler for EPZS median threshold (1 default). + # Increasing value can speed up encoding. +EPZSMaxThresScale = 2 # Scaler for EPZS maximum threshold (1 default). + # Increasing value can speed up encoding. +EPZSSubPelME = 1 # EPZS Subpel ME consideration +EPZSSubPelMEBiPred = 1 # EPZS Subpel ME consideration for BiPred partitions +EPZSSubPelThresScale = 2 # EPZS Subpel ME Threshold scaler +EPZSSubPelGrid = 1 # Perform EPZS using a subpixel grid + +######################################################################################## +# SEI Parameters +######################################################################################## + +ToneMappingSEIPresentFlag = 0 # Enable Tone mapping SEI (0 Not present, 1 Present) +ToneMappingFile = "ToneMapping.cfg" + +GenerateSEIMessage = 0 # Generate an SEI Text Message +SEIMessageText = "H.264/AVC Encoder" # Text SEI Message + +UseMVLimits = 0 # Use MV Limits +SetMVXLimit = 512 # Horizontal MV Limit (in integer units) +SetMVYLimit = 512 # Vertical MV Limit (in integer units) + +######################################################################################## +# VUI Parameters +######################################################################################## +# the variables below do not affect encoding and decoding +# (many are dummy variables but others can be useful when supported by the decoder) + +EnableVUISupport = 0 # Enable VUI Parameters + +# display parameters +VUI_aspect_ratio_info_present_flag = 0 +VUI_aspect_ratio_idc = 1 +VUI_sar_width = 0 +VUI_sar_height = 0 +VUI_overscan_info_present_flag = 0 +VUI_overscan_appropriate_flag = 0 +VUI_video_signal_type_present_flag = 0 +VUI_video_format = 5 +VUI_video_full_range_flag = 0 +VUI_colour_description_present_flag = 0 +VUI_colour_primaries = 2 +VUI_transfer_characteristics = 2 +VUI_matrix_coefficients = 2 +VUI_chroma_location_info_present_flag = 0 +VUI_chroma_sample_loc_type_top_field = 0 +VUI_chroma_sample_loc_type_bottom_field = 0 +VUI_timing_info_present_flag = 0 +VUI_num_units_in_tick = 1000 +VUI_time_scale = 60000 +VUI_fixed_frame_rate_flag = 0 + +# nal hrd parameters +VUI_nal_hrd_parameters_present_flag = 0 +VUI_nal_cpb_cnt_minus1 = 0 +VUI_nal_bit_rate_scale = 0 +VUI_nal_cpb_size_scale = 0 +VUI_nal_bit_rate_value_minus1 = 0 +VUI_nal_cpb_size_value_minus1 = 0 +VUI_nal_vbr_cbr_flag = 0 +VUI_nal_initial_cpb_removal_delay_length_minus1 = 23 +VUI_nal_cpb_removal_delay_length_minus1 = 23 +VUI_nal_dpb_output_delay_length_minus1 = 23 +VUI_nal_time_offset_length = 24 + +# vlc hrd parameters +VUI_vcl_hrd_parameters_present_flag = 0 +VUI_vcl_cpb_cnt_minus1 = 0 +VUI_vcl_bit_rate_scale = 0 +VUI_vcl_cpb_size_scale = 0 +VUI_vcl_bit_rate_value_minus1 = 0 +VUI_vcl_cpb_size_value_minus1 = 0 +VUI_vcl_vbr_cbr_flag = 0 +VUI_vcl_initial_cpb_removal_delay_length_minus1 = 23 +VUI_vcl_cpb_removal_delay_length_minus1 = 23 +VUI_vcl_dpb_output_delay_length_minus1 = 23 +VUI_vcl_time_offset_length = 24 +VUI_low_delay_hrd_flag = 0 + +# other parameters (i.e. bitsream restrictions) +VUI_pic_struct_present_flag = 0 +VUI_bitstream_restriction_flag = 0 +VUI_motion_vectors_over_pic_boundaries_flag = 1 +VUI_max_bytes_per_pic_denom = 0 +VUI_max_bits_per_mb_denom = 0 +VUI_log2_max_mv_length_vertical = 16 +VUI_log2_max_mv_length_horizontal = 16 +VUI_num_reorder_frames = 16 +VUI_max_dec_frame_buffering = 16 + diff --git a/bin/explicit_seq.cfg b/bin/explicit_seq.cfg new file mode 100644 index 0000000..8ec728a --- /dev/null +++ b/bin/explicit_seq.cfg @@ -0,0 +1,136 @@ +Sequence { +FrameCount : 19 +Frame +{ +SeqNumber : 0 +SliceType : I +IDRPicture : 1 +Reference : 1 +} +Frame +{ +SeqNumber : 2 +SliceType : P +IDRPicture : 0 +Reference : 1 +} +Frame +{ +SeqNumber : 1 +SliceType : B +IDRPicture : 0 +Reference : 0 +} +Frame +{ +SeqNumber : 4 +SliceType : P +IDRPicture : 0 +Reference : 1 +} +Frame +{ +SeqNumber : 3 +SliceType : B +IDRPicture : 0 +Reference : 0 +} +Frame +{ +SeqNumber : 6 +SliceType : P +IDRPicture : 0 +Reference : 1 +} +Frame +{ +SeqNumber : 5 +SliceType : B +IDRPicture : 0 +Reference : 0 +} +Frame +{ +SeqNumber : 8 +SliceType : P +IDRPicture : 0 +Reference : 1 +} +Frame +{ +SeqNumber : 7 +SliceType : B +IDRPicture : 0 +Reference : 0 +} +Frame +{ +SeqNumber : 10 +SliceType : P +IDRPicture : 0 +Reference : 1 +} +Frame +{ +SeqNumber : 9 +SliceType : B +IDRPicture : 0 +Reference : 0 +} +Frame +{ +SeqNumber : 12 +SliceType : P +IDRPicture : 0 +Reference : 1 +} +Frame +{ +SeqNumber : 11 +SliceType : B +IDRPicture : 0 +Reference : 0 +} +Frame +{ +SeqNumber : 14 +SliceType : P +IDRPicture : 0 +Reference : 1 +} +Frame +{ +SeqNumber : 13 +SliceType : B +IDRPicture : 0 +Reference : 0 +} +Frame +{ +SeqNumber : 16 +SliceType : P +IDRPicture : 0 +Reference : 1 +} +Frame +{ +SeqNumber : 15 +SliceType : B +IDRPicture : 0 +Reference : 0 +} +Frame +{ +SeqNumber : 18 +SliceType : P +IDRPicture : 0 +Reference : 1 +} +Frame +{ +SeqNumber : 17 +SliceType : B +IDRPicture : 0 +Reference : 0 +} +} diff --git a/bin/foreman_part_qcif.yuv b/bin/foreman_part_qcif.yuv new file mode 100644 index 0000000..5c0b70b --- /dev/null +++ b/bin/foreman_part_qcif.yuv @@ -0,0 +1 @@ ++Øþùûþþýûüþþþþþýûþþþþþùîàââáßáâáßàãåß¿›œ¨¾ÖÛÖ×ÛÛÛØ×ÛÝÜÙÝÝÜÞÜÜßßßÛÖÕØÝáÔħ‘“””Œ�…tu†µßÞÝßÞÝÞÔÌÙÙÑÑÑÒÓ×Ô˜o�†}y§ÏÏÏÔÔØÖÔ××ÑÐÔÒËÀ¾Éº�€¢ÖÓÐÖ×ÙÙÕÐÎÏÉÌ®ww§ÈÍÒÐÏÏÈÃÏÉ“r¦ÑÏÊÁººÈÀ�i—ÀÆÖ¾1ÆÁÓäÍÕ¹ÓϺøÆËзØÄÆîкÕíâááâáààâæçÞÁ�’£¸ÉÑÔÕÒÔÙÛÙ×ÚÙÛÝÛÜÜÝÝÝÞßßÜØÔÝàÕÀ¤—““—šŒuv{Œ©ÐÞÝÝÜÜÛÚÛÇ¿ØÒÎÑÐÑÑÈÑΘu…ˆvvªÎÏÒÓ××ÒÓÓÑÎÏÏËÆÌν¬Ž~ªÖÕÔÖØÙÕÑÎÏÉÉÁœvyªÊÑÑÏÎÎÎÌѼ‚}´ÑÍÅÃÀÈÆ¦xn¦ÍÒ¾0±«Å­¹ˆ¿Ãгް±‡Ê°Š»Ë¦´çãááàßáäåÚÁ¢‘—¬ÉØÜÙÕÔÕ×ÙÓÎØÙÔÙÜÛÜÜÝÛÝÙØÙÙÝÛÓºŸ‘�‘——Š{qx”·ÖáãßÜÜÜÜÝÜÛÙÙÙÔÑÒÐÏÐÍÐͼ’t…„ms¤ÊÏÓ×ÖÓÓÔÒÑÐÌÌÏÎÏÂÀ¯„|­×ÓÔÙÙØÔÍÎÎÎÌÌ«z‚·ÎÌÍÍÏÊËÎÓ²w€ÀÓÊÆÄË˵˜os´Ô¼.Öᩱ½Æ Ëб¥­Ä¿œÑË®¡åΡÐçàÝáæàÖ¾¢™¡·ÐÙÙØËÉÖÖØÜÙÍÏÛØÖÛÚÛÛÛÝÖÓ×ÙÝÝͰ–‰‰—�’†{xƒ�ÁÙâáÞÛÜÝÝÜÝÝÛÚÜÚÖÔÓÒÏÑÑÒѸ°Â�o�†stŸÅÌÒÕÕÕÖÒÒÔÍÊÎÌÑȼé~z²ØÔÖØØ×ÐÍÐÐÈÌÒ±}‹¹ÂÉÍÎÆËÐÐÓ¬sŠÇÏÆÇÎп¶£o~ý)¹Ð´ËäâÈÖâáãäáàÒÝâçÚåÝÕßàáæãÔº¥›£½ÕÖÖÕÖÖÃÈÖÖØÚØ×ÙÙØÛÚÖÛÝÚÙÔØáÜˬ�…Š—™‘�tu‰«ÉÜâáÞÛÞÝÝÝÞßàßÞàßÛØÓÍÌÎÑÐÐÑǼÌË”p}…wv ÈÐÓÓÔ×ÖÒÓÏËÉÌÓÉ¿ÃÂ¥~·×ÒÕÕ×ÖÓÑÐÍËÌȨv�´ÅÌÍÍËÏÐÒÕ£k’ÉÏÌÏÒÇ¿À›n�²‚­²×æÝÔÜåãäåãââßâãèáàææååÕ´›™”²ÒÚ×ÔÔÓ×ÙרÖÙ×Ó×ÚÚØÖØØÙÚÙÛÛÜ×ç’……“œ’sw“¶ÑÚÙÝÛÙÍÖ×ÙâäæèéëêëêéèãÝÚØÔÏÐÏÑÉÄÓÏšmzŠ€|£ÌÒÑÖÔÕÕÎÐÐÊÐÖËÃÂÊ˨{·ÖÔÔÖÕÖÒÐÐÍËÆÆœqŽ¿ÌÍÍÊËÎÌÕÑ‘i›ÍÍÎÐǽ¼¶ŒlˆwÂØÓÕÛÞáßÜÛÚÚÚÚÝÜÚÔÜäãàÔ¶Ÿ˜�¨·ÒÛÚ×ÙÙÙÙÙÜ××ÙÙØØÙÚÛÙÙÖ×ÛßßѸ �‹�˜�~{v›¿Øáߨ×ÛÝÕÇÔÔáêëììëìììììíííìêäÜÖÍÏÓÏÍÐÏžq{Œ�{ ÌÒÕÓÒÖÏËÎÐÓÔÍÇÉÉÈÁ x~¸ØÔÕÓÒÔÑÐÍÏÊÈÈ™tŸÌÑÍËÍÍÌÍÔăl£ËÎÏʽº·°Šq~ÛàÙàãããâáàÜÜÝÞÝÝÞâæÝɶž˜¤¸ÈËÖØÕÛÝÛÙÛÙØÙÖØÛÞÛ××ר××ÖÙÜͱ“†ˆ‘–Ž}qp„£ÆÚÝÝÙØÜÝÛÜÜâåèëêëëëëëëëëëêêêëíìëçßÔÒÓÐÊÐÓ§wzŠ�x�ÊÓÒÕ×ÕÅÆÒÎÌËÈÌǼ¹·šu�¾ØÔÔÓÕÒÏÐÍÎÌÍË�s¯ÒÍÌÐÎÌÍÎÓÀ|p²ÑÎËÀû¾³ƒƒ×ßâááááâßÛÝÝÛÜÞâãÛä‘—©ÀÉÌÒ×רÛÝÝÛÙÖÖÓÓ×ÙÛÙÔÕÖÕÖÛÛÓĬ“ƒˆ�–‰uoqŒ±ÅÕáØÐÙÝÞÝØÛâçêìíëêëëìëëëëëìëëëëêëìíìå×ÐÐ˾ÉÓ¶|vˆu—ÆÑÑØÕÍÎÓÑÍÊÊÎÎÇ¿¿¼™x‡ÂÙÓÓÕÓÏÏÍÏÍÉÍÀ‡€¶ËËÏÎÍÎÎÎÖ¶u~½ÒÎÈÈÁ¼¿¡ˆØâßàááàÝÙÝÞÛÜâäÕ»¤•™£µÏÔÎÎÍÓÙÜÝÜÛ×Ô×ÙÓÔÙÛÕÓÕÕÙÛÚÕÀ£�‰‹“›Šzmw’³ÍÞÕÑØØÛÞÞÝÜßåêëëëêëëëììëìììììììëëëììëëíèÚÐμ½È¿¨qr‡~q•ÊÓÕÓÏÔÕÔÓÍÍÎÌÍÉÀÀ¹–r…ÂÖÐÔÖÖÔÒÏÍÌÉ´ÊÍÏÌÌÎÎÐ×­r„ÁÓÎÊ¿½½®ˆØãààáßÝÚÝØÓààÓ¸�‘–¦ÁÎÒÕÏËÎÑ×ÚÜÝÙØØÖÔÔØÖÙÚÕÐÙÝÛζŸ�ŠŽ–“‰yny›¿ÒÔÕÙØØÝßÞÞÝÝâéìëëëëëìëëëëììììììììëëììììììíçØÏÌÉǭ®sr…u–ÉÓÑÈÏÕÓÔÐÍËÊËÉý³�rˆÈ×ÒÖØÙÔÎËÍÎÊͱzƒ·ÊÏÊÈÏÎÒÖÔ£g‹ÊÑÐÈÂÀ«‡ÙáÞÝÞÞÝѵ›ºÒº¤�” ¶ÍÒÑ×ÖÊÍÎÑÕÛÝÞÝÚÙÙÃÅÖÒÓÕØÙÜδœŽŒ’•Œup€ ÂÚßÚÕ×ÜÝÝÕÖÞÞáæêëëëìëëëììëëëëëìëëìëëìëìëìììëëìå×ÐÍÍÅÂÓ«kqˆ~o�¿ÎÉÎÔÔÒÏÍÌÌËÊȽ·±—t�É×ÑÒÖÔÏÍÎÎÎÌɪx‡¶ËÅÄÎÍÒÒÔЖk˜ÌÐÌÇÅ«†Øàâææãα’q•¢“–£µ¼ÇÔÑÐÙÓÌÐÏÑÖÚÛÛÜÚÚÖÍÏÑÑÖÛÔȲ˜�”˜•‡xmtŒ¬ÌÞàÛ×ÙÝÜÜÝÛÑÕàäéëêêëëëëììììëëëëììëëëëëëìììëëëììëëå×ÎÍýÊÑ¥ns†}nнÑÓÕÖÔÏÎÍÎÌÈÈdz¬¶¶švŽÊÖÌÑÓÏÑÎÎÎÎÉÇ©wнÁ¾ËÏÏÒÑÖÏŽj¤ÐÉÈ̱‡Ûçáɾ¹£�™Œ�¥¸ÈÑÏÄÎÒÔÙÐÐÐÓÚÜÚÛÛØÖØÖØÕÔÚ×ħ“�“�šˆto{˜»ÓßáÛ×ÓÙÜÜÛÜÝÙÛãçêëêëëëëëëììëëëììììììëëììììììëëëëëëêéäØÏÈÊÌÏÓ¨qq…‚t‹ÂØÔÖ×ÑÐÐÏÍÉÅÇ¿³µ½ºžv‘ÎÔÏÖÓÑÐÑÎÌÌÈÇ¢s‘º½ÈÎÏÑÒÑØÈƒs´ÏÃdz‹ÒÀ¥Ž†…���Š–²ÉÏÓÖÑÐ×ÊÒÖÏÐÎÖÜÛÛÜÛÙÖÒÖÓÑͺ£’‘”——ˆurƒ¡ÃÚàÝ××ÙÚÙÜÖÒÜÝÝÞäéëëëëìëëëëëìëëëëëììëëëììììììììëëëëëëëèèä×ÎÎÎËÌѬonˆ„o…ÃÖÑÔÓÑÑÏÌËÅÂÉÀ½¾¸¸–u•ÎÖÒÐÑÕÔÑÍÍÌÇÈ�r‡¥ÃÌÎÐÒÓÓÙ¿€½¼¼²}¦‘�š„ov¡¯´ÌÖÕÑÐÑÕÙÔÏÖÒÍÉÏÙÙÚÜÜÖÔר֯®—Š‹”š�~tv�°ÍÜÞÚØ×ÙÞÝÛÚÛÕÓßÞÞåêëêëëìììëëëëëëëëììëëëìëëìëëìëëëëëëëëëëêèèäÕÌÏÍÊÌЯzp„€j‚¾ÓÐÑÑÑÑÌËÉÃÄÁ¿Àº®¨šs”ÎϽËÕÔÔÒÎÎÈÉÊ—i�ºÊÌÏÏÒÒÒÙ¹{ˆ·Ã°m¢¹ÉË¢ju²ØÚÚÖÔÓÔÕ×ÔÑÚÔÍÏÊÓØØÚÚ××ÛÖ锈ˆ’š�uoz�®ÕáßÛØØÜÜÜÕÔÛ×ÖÜÞרåéêêëëëììëìëëëëëëëìììëëììììêëëëëëëëëëëëêêéèéâÔÎÎÏÍËѹ|nƒ€kƒ¼ÔÍÏÑÑÎËÊÉÅÃÁÃÁ¶¯¸�o™ÏÈÊÓÓÕÖÒÎËÆÌÊ’oŸÅÊÍÎÑÑÒÕØ°vŽÅµÌÛßÜÊ­½ÐÏÑÓÖÔÒ×××ÔרÏÍÏÒ×ÖÕÙÜÛÖĨ�†‹’’†vju ÃÔÝàÚÛÓÐÜØÙÛÏÒÜÛÜÜÜ×ÞéêëëëëëëëëëëìëëììëëìììëìëìëëëëëëëëëëëëëëêéççèáÓÎÏÏÌÊм„o„„p�¸ÉÆÐÔÐÌÉÈÅÁÂÄÆÇ¾»®�qŸÐÎÐÔÔÖÕÒÏÊÆÑĆsªÉÌÏÐÑÒÓÕÖ¥r›´ŠÖØØÓÍ×ÙÔÕÖÕÖÑÐÖ×ÖÖØÑËÍÎÓÔ×ÛÜÓÀ§�ˆ�–�yhn„ŸÅÝáßÛÛÝßÕÑÜØ×ÚÙÛÜÝÞÜÝåêêëëëëëëìììëìììëìììëìììììëëëìììëìëëëëëëëëêéèèèèàÑÍÏÏÍÊо{fƒp~­ÆÍÒÒÏËÆÈÿÂÈËŽ°°’q¡ÏÓÔÓÕÕÔÐËÊËμƒ{´ÌÌÐÐÐÔÔÖЙu�†ÔÕÑÐËÎÏÔÖÔרÑÐÖØÓ××ÌËÎÏÕÛÜл¡Œ†Š�‡rej†¯ÑßàÚØÙÛÝÝÝÜÛÛÛÛÜÜÝÞÞÜÞäêëëëëëëëëììììëìëìììììëìììììììëìììììëëëëêëëëêééèéçÝÑÎÎÌÌËÏ»€d{‚p~²ÌËÐÑÏÎÌÆÂÁÁÉʼ¦²½�v¦ÒÔÔÖÕÕÒËËÎÌÏ¿€…¾ÊÍÏÐÒÔÓÔÉ’q‡ÕÔÎÌÃÌÓØÖÔÓÔÒÒØ×ÐÔÔÍÍÐÖØÌ´š�ˆ‰Šƒp]gŒ·ØâÛÔÕÛÞÝÜÛÜÜÚÚÚÚÛÝÜßÖÓÞãèêëëëëëìëëëìëëëëëììììììììììììììììëìëëëëëêëëëêééééèæÛÐÎÍÏÐËÓÉ‹ex‚t{¯ÏËÍÐÐÌÇÂÁ¾ÄÌì³Ç¿‹t¨ÑÒÒÓÓÔÏÍÑÎËи|‹ÂÍÌÐÐÑÒÑÐÇ�‰ÐÒÐËÆÏÖÖÖ×ÓÒÒÕÙÔÔÔÏÏÐÎÆ®•Š‹Œ‰~nhp�»ÚàÜØÖÔÛ×ÒÚÝÜÝÜÛÛÛÛÜÛÛÝÕÒáèéêëêëëëìëëëëëëëëëììëììììëëëìëëìììëëìëëëêëëëëêéèèèèçãÖÏÏÏÐÍÉÔÍ�dv€qy®ÌÌÌÎËÉÄÁÂÄÇË·¸É˱t¬ÓÓÔÔÔÓÍÎÎÍËÒ±|–ÈÎÏÑÑÒÒËͱˆÎÓÐÍÏÔÚÖÔØ×ÓÒØ×ÔÖÖÕÏ»¤’ŠŽ’‹{mfv—½ÙáÜ×ÕÙÛÜÝÖÑÜÝÚÙÛÜÚÚÜÜÛÜÛÝßåèéêëêêêëëëëëëëëëëëëëëëìëìëëëëëëìììëëëëëëëëëëëêéèèéèçæÝÐËÎÏÎÍËÓË–hr€rv§ÍÌÉÊÊÇÃÃÅÃÊÂÂÑË»«‚x³×ÔÕÕÖÓÑÑÏÍÌÒ³|›ÌÎÌÑÓÓÌǵ†ÐÔÎÍÎÖÙÔÖÙÕÒÔÕÖÛÛϹžŽ‰‹�Š{liwœÅÚßÛÖÖ×ÛÛÛÛÜÜÝØÕÚØÚÜÚÛÛÛÜÝÜÝãèèééêêêêêëëëëêëëëëëëëëëììììììëëëììëììëëëëëëëëêêêéééèçåâÕÊÌÍÎÎÎËÐÑ�hs€vw¨ÐÌÇÉÇÈÅÂÄÊÇÈÔÏ¿³¬w³ÓÐÒÔ××ÔÎÍÍÌÒ±{žËÊÎÑÑÎк†ÏÐÌÌÒØÔÒÔÕÑÒÖÛÙзž‰‚Ž–ŒuekªÁÔáÙÔ×ÚÚØÜÜÛÛÛÜÛÖÕÙÙÚÛÛÛÛÜÝÛÜàæèééêêêêêêëëëëëëëìëëëëëììëëëìëììëëëëììëëëëëëëêêêéééèèææåßÑÌÎÎÎÎÌÊÎÍ‹ax�rq¤ÏËÆÇÈÆÇÇÄ¿ÇÐÐË®²¦wxµÓÏÑÕØÖÐÏÒÎÌׯz£ÊÍÍÍÌм…ÍÏËÍ××ÒÓÓÏÔÜÚǬ”„„�•‹{st�²ÉßÚÔ×רÜÜÜÝÜÛÛÚÚÚÚÜÛØ×ÚÛÛØ×ÝÝÜÝãæèééêêêêêêêëëëëêëëëëëëëëëëëëëëëëëëììììëëêëëëëêêêêéèèèèçæäÙÎÏÏÍÌÌÍÉÌÁ•oqylmžÆÆÊÍÈÊËÄ·ÀÐÏÈ»²®§}{½ÓÐÓÖÕÕÖÖÒÎÏמv¨ÍÎÏÍÏ»…ÎÒÍÏÖÒÑÕÙØÖÆªŒ~ƒ�Ž„tlx™ÁÛáߨÖÙÙÚÛÜÜÜÛÚÚÚÛÚÙÚÛÛÛÚÛÛÛÙØÜÝÜßäæçèêêêêêêéêêêëêêêëëëëëëëëëëëëëìëëëìëëëëëëëëëëêêéééèèèèçæäßÑÌÎÌËÌËËÊÑÔ ms}ql“ÃÎÎÈÊÏÎÀÁÓÔʾ¶­º©w�¹ÒÓÒÓÖ×ÖÔÔÐÐÉ”|¯ÎÎÎи…ËÍÎÑÑÒÙÜ×Ĥˆ~†�‡{pl} ÃÛáÛÖØÛÜÛÙÛÜÝ××ÚÙÚÙÛÜÛÛÚÛÛÛÛÙÚÚÚÙØÜâåçèèééééêêêêêêëëêëêêëëëëëëëììëëìëëëëëëëëëëëëëëêêêééèèèèçæåãÖÍÎÍÍÌËÊËÊÏÕ®}v|qj–ÂÌËÍÎÏÇÇÓÑÌľµ´º¦yƒ¾ÖÒÔÕÕ×ÖÔÔÏÍÉ•~²ÍÍÑ·ƒÇÉÍÕÛÛ϶ ‰‡‹ƒrdm‰­ÍßáÝÖÖÚÛÚÜÝÜÜÛÛרÛÚÚÚÚÚÚÚÙÚÚÚÚÙÙØÖרÝäæççèééééééëëêêêëëëêëëêëëëëëììììììììëëëëëëëëëìëëëêêééèèççææäÞÔÏÍÍÌÌËËÌÉÐÙ°qpyqo’½ËËÌÎÊËÓÏÉÆÀ¸±³¹§|ŠÅØÒÕÖ××ÕÕÕÍÎË„¹ÏѶ|·ÉÙÞË«‘ƒ‚…Š‚nbp’¾ÖááÛØÙÜÜÛÛÜÜÝÞÜÚÚÛÜÛÛÛÛÙÙØØÙÚÙÛÚØÚÝàÞÛàäæçèéèéééêêëëëëëëëëëëëëëëëëììììíëìíìììëëëëëëëëëëëêêêééèççæåäâÛÓÎËËËÊÊËËÊÎÒ©uq{zt‘ºËÎÒÐÍÔÔÍżº·¯³º¥{ŽÈÕÔÖ×ÙØØØÔËÊÁŒˆ¼Ò¸k˜Å̰‘„�‹~ml|ŸÆÛáÞØÕÙÜÜÛÜÜÛÜÜÝÜÛÛÛÚØ×ÙØØÛÚÙÙÙØØØÛÞáÞËÃÙâäææççèéééêêëëëëìëëëêëëëëëëëëëëììëììììëëëììëëëëëëêêêêêèéççææåãÝÕÏÊËÊÊËÊÉËÍÒׯys‚{x•ÂÒÑÎÊÒÔÐÊ»¿¼¹¶´¡z‘É×ÓÕÙØÙÖÔÐÊÏ»ƒ�Ƽsžž•†‹�‡{oq�µÐááÚÕ×ÙØÛÛÚÛÜÛÚÚÜÞÛÙÛÛÙØÙÚ×ÖÙÚÙ×ÖÖÛßßÔÁ¨Ž©ÝâäåæççèéééêêêêêêêêëëêêëëëìëëêëìììëëëëëëëìëëëëëëëêêêêêéèéèççæåãßÕÍÍÌÊÉËÌÊËËÍÑÔ±yr�{u“ÂÓÌËÒÓÐÍȼ½¾À¹°µ§�–ÇÔÓÖרÖÓÐÍÊη—¶…¯€Š’ƒni~˜·ÝåáÙÑÖÚÚÛÛÛÚÚÜÝÙÚÝßÞÜÚÛÚÙÚÙØØØ××ÖÙÞÝ×DZ›Žˆ”ÆááäåæçèèéééêêêêêêéêëëëëëëëëìëëëëìëëëëëëìëëììëëëëëëêêêêééèèçææåäâÙÏÍÍÌÅÈÍÉÊÇÅÉËÒ±sl{|u“ÈÒËÑÔÑÊǽ¾ÄÀ²¶É­}˜ÌÖÑÔØ×ÔÐÎËËЭ‹�µˆwql|ŸÈÞãßÙØ×ÔÙÚÚÚÛÚÚÛרÛÜÝÞÜÚÛÚÙÙØ×ÖÕÔÕÜàÞζŸ�Š�œŸ±ØàáäåæçèèéééêêêêêëêêëêëëëëëëëëëììëëëììëììëëìëëëìëëëêêêêéèèççææåäãÞÔÍÍÍÇÈÎÊÉÉÈÌËÌѱwq�zs‘ÃÐÏÔÔÌÊÉÄÃÄô¸ÊÄ—~šÈÕÓרÕÐÐÎÌËЬwv¡zm…¬ÎÛÛÜØÙÜÜÚÚÙÙÛÛÚÙÚÚÕÖÛ×ÙÜØ×ÙÚÚÙÖÔÖÙÜÜÕÈ±Žƒ‰•Ÿ�˜Š®Ýßâäåæçèèéêéêêêêëëëëëêëëëëëëêëììììëëëììììëëëëëëëëëëêêêêêèèèçææåãâà×ÏÎÌËËÍÌËËÊÅÌÏÐÙ¿‚m{si�ÃÒÑÔÑÌËÇÁ½Á»¼Èì’zŸÍÔÓÖÕÒÒÑÎËËΤiˆ�¹ÔÝßÚ×ÙÜÝÝÜÝÜÙÙÛÛÚØÙÛÛÚÛÖØÛÙÙÙÙÖÖÙÜßÙ˸Ÿ’�‰’œ™Ž�zƒÀàßâäæççèèéêêêêêêëëëêëêëëëëëëëëìììëëìììììëëëëëëëëëëëêëêêééèèèçææäããÚÌÍÍÊËÌÊËÈÆÂÌÂ½ÏØÁƒqxqpŒ¾ÓÑÒÑÍÊÅÀ¾¼ÀËÆ°¢Œ|ŸÒÔÓÓÑÑÑÏÍÉ;u¾ÓÞÞØÖÚÜÛÝÝÛÜÜØÚÚÚÛØØÚÛÚÚÛÛÚÚÚÙ××ÛßÜÑ»¢‘Œ‘™›–Œ„}xw|®ÚÜàâäåæççéêêêêêêéêëëëêêëëëëëììëëììëëìììììììëëëëììëëëëëêêêéèèèçççåäãÞÐÅÈÉÊÌËÌÁÅÍË»¬ÈÌׄpyuk„¼ÒɽÉÐÉÅ¿ÄÊÍ· ž‹u¥ÔÔÑÐÏÑÑÎÍ͸†ÜßÔÕÙÙÙÛÚÜÝÜÛÚØÚÛÛÚØÚÚÚÚÚÚÚÙרÛàÝÒë•‹Ž™ž™�„yuvtsuˆÂÜÜàâäååæçèééêêëëêêëëëëêëëëëëëëëëììëìììëëëìëëëëëìëëëëëëëêéééèèèèçæåãáØÈÇÊÌÌÌÎÊÊÌÊÊÆÊËÍׯ�pvxi‚»Ç®ÂÒÉÄÇËËÌÑÁ­¥©ˆn¦ÒÒÐÒÐÑÏÍÍ·†ÓÙÖØÙÙÚÝÝÜÜÙÙÚÛÛÚÛÛØÙÙÙÙÙÖÖ×ÛÝÙÊ²šŠŠ’œ¡�Šzuusps{Œ¢ÀÑØÞàâäääæçèéêêëëëêêêêêêëëëìëëëëëëëììììììëëëëëìëëëëëëëëëëêééééèèçççåäãÜÐÊËÏÊÃÊÍÍÌÌÍÏËÊÌÏÖÌ—qzuk‚³ÈÊÎÌÄÆÌÏÏÐȶ¦««…q£ÑÐÐÓÕÒÏи…ÔÙ×ÙÚÛÜÜÝÝÛÓÖÛÚØØÙØØÙØØ×Ô×ßßÖ¿¥�„Ž™Ÿœ�…{vuuuvƒœºÑÞÞÖÚÞáããååæçéêëëêêëëêëêëëëëëëëììëìììììëëììëëëìëëëëëëëëëëëëëêêêéèèèççæåäßÑÈËÎÍÈÇÆÍÍËËËËÊÍÍÈÒÑ•pzvmªÆÍÍÈÆÄÌÑÏʺ¬­«¤…o¨ÕÑÒÕÒÐÔ»†ÕÚÚÚÚÜÜÚÛÝÜØØÚØÖ××Õ××Õ×ÛÜÛ̵žŽ‹“œŸ˜‹ƒ~zssu‚™°ÉÚßßÝÒÐÜßáãäåæçèêêêëëêëëëëëëêêëëëëëëëìììëëìëëëëëëëëêêêêêëëëêêëêêêêéèèèèçææåâÖËÌÎÏÎÌÊÍÎÌÈÊËÈÍÏÌÌÔÏšsuxszªËÉÅÅÀÈÑÑËÁÀ½­¤ª‰r§ÌÏÑÓÑÖ¿‡ÖÝÙÙÛÜÜÙÚÛÛÜÙÙØØØÓÓ×ÙÜÝÔĬ�…�œ¡œ’„~~|xv{Œ©¾ÔãÜÙØ×Ù×ÕÜßàâääæçèéêêëëêëêêêêêêêëêêëëëëëëëëëìëêêêêëëëêêêéêëëëëêëëëëêééèéèçææäâÙËÈËËËÎÎÎÌÌÌËÆÁÉÍÎÐÍÔÑžqu}vƒ¯Ç¼ÁÈÉÐÒÌÃÇÀ³°²°‡o¥ÐÒÒÒÕ¼†ÕÛרÙÙÚÛÛÛÚÙÙÙØ×ÕÓÙÞÙ̹¥•’”�¢™‹{zyz�Šž¼ÔáÕÒÙÐÔÛÚÙÖØÜÞàâäååçèêêêêëêééêêêëëëëììííììëìììíììíííííííîíííìììëìëëëêëëëêêêêêêéèæàÒËÍÌÊËËÌÌÍÏÍÆÂÈÌÍÐÎÍÓÒ¡qszs|¬¹½ÌÊÌÑËþ½»µ´¸º‘|±ÖÒÑÖ»†ÔÚØÙÚÙÚÚÚÚÙ×ÖÕÖÙØ×Ò«—’˜¡£œ’†}yxvx‚š¼ÐÚàßÙÔÕÕ×ÛÛÛÚÔ×ÝßàâäæææéêêééêëììëëéèæäáßÝÜÜÜÜÜÜÛÚÚÙÙØØÙÙÙÙØÖÕÓÒÐÎÍËÌÌËÊÉÉÉÉÊÌÏÑÔÕ×ÚÚÛÚØÚÙÖÖÓÏÎÎÍÎÌÅÄÈËÎÐÑÎÒÔšiszkxš·ÈËÎÎÎÆ¿Âº¾Áƽ‰|´ÕÑÔ»†ÓÙ×ÚÝÝÛÙÙÙÕÔÙÝÞØÃ¬š””›¤£™‰~|wswŠ¥ÁÖàÞÛÙ××ÚÛÚÛÛÚÛÛÕØÝßáãäåæçééêëììëèãÝØÑËÆÁ¿½¼¼¼º¸´¯¬«ª¥Ÿ™—˜—™››™—“���’”•˜›œžŸ £¥¥§¨ª®±³¶¸½ÀÃÈÌÑÕ×ÖÑÍÌÌÉÅÆÊËÎÐÏÍÓÏ¢utwir¥ÈÍÑÑÐÈÀÂÀ¿ÄÂÂÄ®w±ÓÓº…ÓÙÙÙÛÛÙÖØÙÚÝÛп§‘ˆ‹š£ •‡{ywyy…®ÏÞàÜØÒÖÜÚÚÛÚÛÛÜÚÚÚÖÚÞßáâäåæçéììéäÝÓÊÁºµ²¯¬©§¥£ž™“އ„|{vrppoopvxxtmkkmqux{…‰�’—œ¡¥§§¨§¦¨ª«ªª­±µºÀËÒÐÌÊËÊÈÌÍÌÍÍÏÍÏÌ¡lo{ky°ÐÍÑÐȿÿ»ÅÂÄÈ¿¨}xºØ¼„ÔÝÚØÕÖ×ÙÞÜÒÁ«œ�‰�˜›”�†~vsw~‹ ¯¿ÞßÚ×ÔØÙÚÛÙÛÛÙÙÜÛÙØÙÖÛÞàáãäæèééäÛÑÆ½·´²³³°¬¥�”Œ…zwsppojhihhhjkntvuoe_]adfjmruy}�„…‰’�©¯±±¯±±°°¯®¯¯°´ÂÍÎÌÊÉÈÌÍÌÌÍÎÎʶágrui{²ÐÏÍÈÆÇüÈÈÊËÄ¿¯‚�¾¿…Ó×Õ×ÛÝÜÓÅ´¡––š—’�މ�ywuvŠ¥·ÑÛÙÙÙרÙÑ×ÜÚÙÚÜÛÚØÌÐÛ×Ö×ÜÞßáãåèèâ×Ê¿¹¸¸¹¹µ¯¦œ’ˆ€ytrpmlkjkigc_\[[[\bhijd[Z[[Z[[`ehnqux{zxz…•¢®²±±³µ¶µ´´·´·ÆÍÍÍËÉËÍÌÍËÊÎÐÃÉÖ dlzmz°ÍÍÆÅÉÇÃÆÅÊÍÈþ°€…©†ÔרÞÙɲ ••˜žž”‹†�{uu€…’´ÐßààÛØÔ×ÚÙÚÚÛÛÙØÛÚÚÛ×ÌÓÜÚÖÙÜßàáäçâÖÇ¿½½½»¶¬¢˜�‡ƒ~wqlkhfdcb^[WTQOPSSOMSXWVSOONLMRUWUX^diouy{vx�„�£±µµ·¹¸¸¸º¸¸ÆÎÍÍÌÊËÎÍÍËËÏÏÏÌÑÕ empv¨ÎÈÀÇÆÃÇÁ¿ÈË÷¹«yy†ÔÓÉ»§™‘“™œ•�„|yxz~‡Ÿ¿ËÓâßÙÙÕ×Ù×ÙÛÛÜÝÜÛÛÜØÓÙÚÛÜÛÛÜ×ÙÞßáåäÙÊ¿½¿¾¸®¡•Œ†ƒ�}ypg^WRSX]ZTSWWVWZ^`^^a`][ZWND@BMX[WSPT\bhmsv~‰†�‚•®µ¸º¹º»¼¸¹ÇÌËÊËËÌÍÎÍÌÍÏÌÌÏÏÒÖ¤eq~ms¦ÉÃÁ¿ÀÍÈ¿ÇÌȹµ¶—s­ž™–—››•Œ†zwv|‰�¸ÎßãâÞ×Õ×רÚÚÙÚÜÛÛÚÛÜÛÜÚØÚÜÜÛÚÛÛØÚßáäßÎÁÀÀ¹«œ”Žˆ„ƒ‚€|xsoid]WVZaffglorsqpnmljihfedb]WTU^gigbUKKR\chms{„ˆ‚{�«µ¹»º»½¹»ÇËËËÌËÌÍÍÎÎÍÍËÍÑÓÏÑÙ©ko|ns©Ç½¹½ËÐÉÍÍËÁ½º¦•rŽˆ—œ˜Š�}zxvwƒ˜±ÆÕßáÝÚØÙ×ÕÚØÛÜÜÖ×ÙÛÙ×ÛÛÚÛÜÜÛÚÚÚÚÚØØÞâà×Ǿ¿¸¥�„�ƒ„„…‚~yrmjjjmpqtwywtuutvxyxwtrolmnljgeccfjjmlmhYHEKU`fkns}†|t’®³¹ºº»·ÁÌÈÊËÊËÌÍÌÍÍÌÍÍÎÐÒÒÏÒÙªorƒux­Å¼½ÈËÌÑÎÌÆÀÁ«žsž�‰‚{yyus~•ª¾ÖÞÝÝÙÍÓÝÛÚÛÛÛÛÜÜÜÛØ×ÜÛ×ÙÚÖÙÛÙÛÕÓÚÙÚÛÜÞÙÎÄ¿¶£�€„‡‡…‚zunjikosvy{}~}|zvuwxxxwwxwsqrpnmkijnnmnnool^QJMV_goqsvwr}¡¯³¸¹¸²£·ÌÇÈËËÎÌÌÍÆÉÎÌÍÑÑÑÑÏÎШsz‡v}©´¸ÉÇÈÑÑÑÌÃöŸs–�xutwy„š»×ßàÜ×רÛÖØÜÜÖÙÞÜÛÛÛÛÜÛØÕÛÚ×ÚרÜÚÙÙ×ÚÜÞÜØÑÉŽ¥‡{~‚†ˆ‡ƒ€zuqlimpruwyz{}||||{ywwxyyvuwxvsppqrompssttttqojcZSSYajqsstvx“¬¯²¶¸ª|²ÊÉÉÉÎÍÌÎËËÎÍÉÎÒÒÐÑÌÇ̬xtƒsvФÄÊÊÎÒÓÐÉÈ»§i�uvyzЦÄÜÜ×ÚÙ×ÙÛÚÛÞÜÛÛÚÚÜÜÚÙÛÚÛÜÙÕØÖØÛÜÛ×ÑÙÜÛÜÛÕÐÌË̹”wq~‡†„„�|wrnllptttvxzzyxuqpopqprtwyvstuurqqrpnnpqqqpnpqpmid\Z\aflsuvxx�§®°µµ™|o{¯ËÇÇËËËÍÍÌÎÍÉËÐÑÎÏÏÌËÕ²us|uu”ÀÇÌÏÑÑÏÌ̽ª^x�•©»ÏÜÞÜÏÍÚÚÚÚÙÙÚÚÜÛÛÜÛÛÛÛØÙÛÛÚÛܨרÙÛÛ×ÐÒ×ÖÒÍËÌÏϳƒot|„‡„�}xtqommruuvwxwusmd_^`begjmpsuvttsrrrrqqsqnmjgdcdhlmmjfa^aeiqsvz{�¥±¶µ˜‚{qz«ÌÉÆÉËÊÆÄÍÍÎÌÍÏÍÍÍÏÏÐØ¸}q�y|¦ÅÊÎÐÐÏËÌÆ°j¦ÈÛÞáßÙÕÖÙÚÛÚÙÙÙÛÛÙÜÛÛÚÚÚÛÜÛÚÚÚÚÙÔØÙØÚÙÙÙØÐÕÐÉÌÑÒήyiv||‚‡�|vrqppqstsuxvqjdc\VTV\_`bejmmqsrsrrstuuuuqkgc^YVTX_eikkhaaejmsy~ƒ‘¤²µ¼šn{ƒrx§ÊÊÉÊÊÈÅÉËÌÍÌÎÎÎÍÏÐÍÏØ¹zlyuu¤ÈÎÑÑÎÉÌ̵ˆÜÚÖÛØÖÙÚÚÛÛÙØÙÙÛÜÜÛÚÚÛÛÜÛÚÚÙÙÙØÚØÓ×ÙÙÙÙ×רØÖÎÐÒÒÌ­�ks}}|€ƒ�xnopprvuqqqng_XTPNPSUY^aachlknpprsstvxwusmhfb\WOJIKS]eijgcbfpy�„�—¤¯·ÈÄ—s{�znrŸÇËÈÈÌÌÉÉÊÍÌÌÎÍÎÐÎÍÍÍ×¹xk�tr¤ÎÐÐÐÇÊÏ´ŠÙÆÄÙØÚÚÚÚÙÚÙÙÙÙÛÜÜÛ×ÙÛ××ÚÙÙØÙÙØÕ×ÙÖרÔÖÙרØÔÏÒÓͳ�€xx}~~}€…}qllquvsqngbZSQOKOTWXZ^bcehlmnoqrrssuwxvrkhfb[VRLFCFNW^che`iyˆ‹’ ¬³ÃÎËÌ¥ro}|po”ÁËÆÊÊÈÉÉÊÌÊÌÊÍÏÌÍÎÌÎØ»|r�px«ÌÏÒÉÇε…ÓÒÒÙØÙÚÚÚÙÚÚ×ÕØØÙÛÚÚÛÜÖÔÙØ×ØØ×ÖÒÖØÖ××Ö×ÔÒ×ÖÑÎÒнž�‰{y~xy��sjpvxwqkd_][]bddcefdcddffhkmnprrqrrsvxvqkgdc^ZWNGFEEHQ]eigty|Œ’�«´¿ÌÎÎÓÓ¡su�€tm�»ÈÆÇÈÊËËËËËËÎÐÍÌÍÌÍÏÕ½€q€{{«ÍËÉËϹ…ÓÛÙÙÙØÚÚÙÙÙÚÓÒ×ÓÖÛÙÚÜØÖØ×ÓÓØØÖ××רÖÔ×ÚÚÓÓ×ÖÏÎÓÈ­˜Ž‡yw€}xqntsjnuwyumdabhmsuvyvtrppmkklmmmopssqqrrttsojgffb]ZVSRPNORW^hillz”£¯µÀÊËÑÑÏÐϸ‡u€vl„²ÈÇÈËÍÌËÌÌÊÍÏÎÌÎÌÌÏËÑ»~o†}~­ËÉÌѺ‡ÔÚ×ÙÙÙÙÙÖØÙØØØÙÙÙÙÙÙÙÕÔÙØÔÕÙÙ×ØÙØØÕÒØÚ×ÖÖÕÕÎÎϽ¤‘Œ‰xwxqlihcltvwxqkkpuz}|xx{ywutttrqrsrqrsutrrqrrqqnkiiheeggddfggfa_ceck‰¨±¹ÄÍÎÉËÌÏÒÓØ»�st€ym€±ËÊÊÀ·ÉÊËÊÌÍÎÌÍÊÌÑÌÈϸqƒ{€¬ÉÊι‡ÕØ×ÙØÚÙØÙÙ×ÖÙÚÙÚÙÖØØØØ×ØÙÚÙ×רÙÙØØØ××ÔÒÔרØÏÌ˸Ÿ‰ŒŠxz|rkfe]druwxuqrw~}{xxxvsrsrrsttssrstutqooqqppoljiiiklkjloooqspiii|¦¶»ÅÎÎÏŪÄÑÎÎÐÒÍ›sx‚}oy¨ÇȺ¶ÅÈÊËÌÍÍÌÌÉÌÏÎËËϾ‰o{y¡Äʶ†ÕÚ××ØØØÙÙÙÖØ××ÙØÙÙÙרØÖÓØÙØ×ÕÖÔÖØÖÕÓÕØÛÝ×˺©ÀÍ»£��ˆz{xnebZTiswyxuxz~€|zyvuutpmmnoqrssttttttqoooppnomjikkjhhiklmnquxwrwš¹ÂÌÐÎÎÐ̺ÊÔÍÏÑÒØÓ¤rqƒ|ls¡ÈÃÃÇÅÆËÊÌÍÌÍÎÍÊÇÇËÉÎÉ”s}w�È·†ÕÛ××ØØØØØÚÚØØÙ×Ö××ÙØØØÖÔØ××××ÖÒÒÕÖÙÜÚÔÊ»ª™Œ…¨È½¬šŽ…�}sh`]OXnvxyxx|}�~wuusrpnjebcfkmqsttttvvtqooppponkjkjfbaeiklorsw|z�¸ÉÎÐÐÌÌÐÓÕËÈÎÒÑÐÐÒͨwp‚}lr ÃÄÆÄÅÊÊÊÌÍÍÍÌÊÈÉËËÆÔÑ•oƒ�z¡´†ÔØÖÔÕØØØÙ×ÖØÙÙ×ÖÓÒØØØÖØÙØØÖÔÔÕÙÚÚÕÎŰŸ‘‰ŠŒ�‘›½Æµ¡�‡ˆ�pd]ULarxyzz{}~~yuspkgdb_\[YY^cilpqtuuvtrppqqqolige`YV[bhjloqqty}”ÂÏÌÎÑÍÍÎÐÎÄÂÊÑÑÏÐÐÑÖ¸|k|}no”¾ÇÁÇÊÉÉËÌÌÌÌÌÏÍËËÇÊÕÊ’o�ƒyއÏÒÕÑÑØÙÖÌÒÖÙÕÖÚØ××ÖÖ×ÕÖÖÔÖØØÚÚÖÍ»§™�…‡‹�”™™”‘˜·Â¯š�Œƒnc\PQdsy{zz~€}xtqkfcbcb^[XVUUX^cinqtvvvusrrqqkeb^WOLQVXYZ`ejlpw}’½ÊÉËÏÑÐÏÏÐÎÈÈÏÐÎÏÒÐÏØÂ†n€rj�»ÃÃÉÈÉÌÌËÌÍÊÊËÌÍÊËÏÒË–q�…u…ÑÖÖÖÖ×ÙÕÍÕØÙØÙÚרØÖÖÕ×ÚØØÛÚÓÆµ¢“‰‡Š�”™˜•�‡�ytn{£¹´¥•…pc[OTcsz||}€~zuogadhiijd^YUSSQTZ_diouxxwwwusnb\XOHHMSTURLJS^flu|•¾ÌÎÂÆÐÐÏÎÑÒÍÍÐÑÏÑÑÎÎÏÙÌ“t�qj‡µÅÇÉÈÌÍÌÍÍÊÉÊÊÏÎÍÑÍÐÎ�q~„…ÔÙרØ×ÙÙÚÕÖÚÛÙØ×ÕÓÓÖÚÝÔÏÌÁ¯ž�ˆ‰‰�•‹ŒŠ€yvrkggeeak�¬¸±�ŠvYKTcr{~�€€|uoe`cgiiigc^YQMOOOSX]bipwzzzzzwhXSKCDHNSW[]XOMT`hpz—ÁÉÉÇÉÎÊÍÏÏÐÑÐÐÐÐÏÏÌÏÐÎ×Кqu}ui~°ÆÆÉÊÌÌÌËÉËÊÈÌÌÍÏÎËÏΟov‡Ó×רÙÙÚÚÙÖÖÙ×ÓÓÕØÖ×ÖϽ¦¤Ÿ“‹‹�”™’Žƒwtnhigffghijljiv�£¤”uTLVcq}€�‚€~yrh`bedbcb`^ZTMFGLORW[aflu{||{|yeSJB@?CJQX]bc^SNXemx�ÄžÍÍÉËËÏÏÎÐÑÏÎÏÎÎÍÐÒÐÎÔÑ›nq|vi|ªÄÉÈÌÌËËÈÉÉËÌÈËÏÌÌÏÐÑ¥o‡ÕÙÖÕÖØÙÙØÔÑÕ×ÙÛÛØÉ¸®ŸŒ�ˆŒ“˜™—�‚wsmmjjlomjjllnnmqojlrzp[QNYao~‚ƒ‚|wodbdabcbcc`[UMHEHRXZ^agmtz}}~{iSGA>=ACBBCDEGNY][^iorw{ƒƒƒƒ|jYQHA??@@>>CDDLZcn…³ÇÈËÊÈÈËʾÆÎÍÐÎÎÏÐÐÎÎÎÑÐÍÑÕÑÎÔÍ�su}vo�ºÉÉÈËÌÍÊËÍÎÎÍÐÑÎѽ‡ÒÙÙÑIJŸ�‹�’˜Ÿ¡£’|x{{qkkgeimnonnnnnmmnqpopoorssqoopomkbUIKQVmƒ††„}vqmkie]TQTUVYYVRRRUVW[empty~�ƒ…†…‚ubSNLHFIOQUZ`e^]gq†±ÅÆÉÊÊÈÉËÅËÏÏÏÎÎÏÑÐÏÏÍÌÍÑÒÔÒÐÎÓÔ§qp{un†²ÇÇÊÍËÊÍÏÏÎÏÐÐÉν…ƹ¥•Œ‰�“› ¢›—Š{pfoqkgihkmnnonlllklmnopppqrqpqqooponq|hSJQWn†‰ˆ…€xvvvtqoljhhgd`[VVWY[`hmpty}€�ƒ‡‰‰†}obXTPLKOW^bgoqlpw„œ¹ÇÆÈËÊÉÈÎÏÑÏÏÑÎÎÐËÏÐÎÌÈÏÑÔÑÑÓÑÔÖ¬xlyun€«ÅÇÊÉËÌÍÏÐÏÑÏËλp”‰‰Ž˜¡¡ž•Šuonp{zk_]cedffdhlkjjjjknnmnnopqooponnonnwzxubSTXp‡‰‰ˆ…€~~}zyusolifb^[[[]bgnsuwz}€�ƒ†Š�‹ˆ�xqlb]ZUQRW\dkpuy~‡�‰·ËÈÆÉÊÊÍÎÎÍÇÄÌÏÌÊÑÐÐÎÏÎÐÓÓÒÑÓÓÕÚ»�mxwmz¤ÂÄÇÌËÊÎÐÐÐÐÐлnš�£¦ “�umkp|ˆž¼¿´¦„\RX]][\`cfhijikklmmonpqpppnnklmpwihsrhZXu‰‰ˆŠˆ…„‚�}}yrokfedbcdfimsxyz|€�€„ˆŒ��‹…~utpieb`\[\clqw~‚Šƒi|¯ËÊÆÈÌÌÌËÌŽÌÐÍÎÑÐÌÌÐÎÏÒÓÒÒÓÉÊÒØÃ‹nvwnx¢¿ÄÆÊÌÌÏÐÎÎÎѺx¦˜ˆ}uopx‡—­½ÄÔÚÇÆØÐ¡dKKORUY[^adffggijjkmnmmmmopnlkqshgmwte^z‹‹‹‹ˆ††…‚€~zvtqnnnoonqsvy{{{~���€‚ˆ�“”�‰�yuurnkkkkklpvz„ŠŠqcn—ÄÍÅÈÉÊÍÍÍÏÍÇÎÎÎÏÊÍÏÌÏÓÒÑÑÏ¿ÃÐÑÚɉlvxoy¢ÀÄÇËÌÍÏÏÎÏÓºezmnt†—¬ÀÍÔÔÏÈÐÍÀÂÐÖÓ­qK?CJORSVZ^`bcdehjllijkjlnpomsriioxwpn‚Œ‹ŒŒ‹‰ˆ‡…ƒ�}|zxwvvwwwxyz|}}~‚ƒ‚�‚…Š�’”�Š„yvvutrqqqrvy}€„‰Œ|p^]‡ºËÈÆËÌÍÍÐÌÇÍÎÐÎÌÏÎÌÌÑÐÐÎÌÍÏÐÔÒÙÊ�krvpw—»ÅÅÊÍÎÏÏÏѽ`„”«¿Ï×ÜÝÚØÔÍÌÓÐÁÁÄÀÆÍ±yQ?;AGJLQTV[]_behjijkjjkjlonssihs{tsyˆŠŠ�Ž�ŒŠˆˆ‡†…ƒ€|{|||{|~~€‚„„ƒ‚ƒ‡‰�›¡™Ž†€{wwvvxwvwwz}‚„…‹Švrrhc{«ÆÆÇÍÌÊÍÎËÉÎÑÏÌÏÐÏËÍÑÐÎÐÏÎÏÒÑÑÚÏ–mq|sq�¯ÀÃÉÌÏÐÍλyÈÚÝßßÜÙÕÖÖÖÔÖ×Ƚ¿ÂÀ¼½¿®ŒbE47@DHLOSUZ]`befghhhihilkpsiiv|x{~‰Š‰��Ž�Œ‹‹‹‹Š‡„„‚�‚�€~~€€€€‚ƒƒ„ƒ„‡‰�žµ»°Ÿ�‚|ywwuwz{}}}�…†‡�ŒmbpwpanšÁÌÉÊÊÊËËÉÌÍÏÇÄÏÐÎÎÏÎÏÐÎÍÏÏÏÓÏØÕ£rr|qp‹«ÀÆÇËÐÏй‚×Ý×Õ×ÖØÙ××ÖÓØÏÃÂÂÀ¾»½º»±ŽnO947>DIKMQUZ^acbceeefgihmthjx‚ƒ}}Š‹‰Ž���‘‘��Ž‹‰‰ˆ†…ƒƒ‚‚���€�ƒ�€€‚…‹’¢µÂû­š‡}ywwuvz{}�‚„‡ˆ‰Ž�oWYgvugiŒºÉÆÇÈÈËÌÌÌÎÉÆÎÏÎÑÎËÏÐÏÐÐÐÒÔÑÏÖØ¬tjuuv‰ªÂÅÃËÐÑ»…ÎÒ×ÚØÖÖÔÕ×ÓÓÒÀ»¼½ºÂ¼µºµ´©‰r[D427>CEINUXZ]__accdfffjtjiz†ƒin‰‹‹Ž��‘““’���ŒŒŒŠŠ‡…„……„ƒƒƒ�~~€‚ƒŒž®»ÁÀº°¤Ž~wttvyzz}�„…ˆ‹‹ŽŽpXQTetwmbx©ÆÈÆÉÉÊÍÎÌÌÏÎÎÎÐÐÍÎÑÐÑÏÏÒÓÔÓÐÓ×µ„ntxt|¦ÆÄÈÍÑ»…ÓÙרÔÒÓÍÔÓÏÕÏ¿º»»ºÀ·­ºº¸·Ÿ€paP<106=AGLOQVZ[]`abcbcdsni|‚uYb…‹‹Ž‘‘‘’““‘�������‹‡††…„„‚�~€�‚ˆ—¥¬´»½¸¯¥“€vqptz{|�…‡‰Š‹�ŽrZVRT\n{p^kšÂËÇÆÉÌÍËÊÍÍÎÍÍÐÐÍÍÏÑÐÏÐÓÔÓÓÏÑ×È“kqunw�»ÀÅͺ�ÕÞÙ×ÎÒÖÔÓÒÔ×ɼÁÁÀ¾²·¾¼¾º·«�}tk[F5/27=BFJOSVX[]^^^a`mpl}|jWg…ŠŠŽ’“”””•“‘‘’‘�ދЉ†…„‚‚€}}��…“¡¤©±¶·±ª¢•…xomptw|€�…ˆ‰Š‹�ŽpXYZYX]kvufd†¶ÉÆÄÉËÌÌËÌÏÐÑÏËËÍÍÎÍÎÐÒÒÑÑÑÐÏÔÍ›nlujiаÄǹ…ÕÚØØÔרÖÑÒÖÔó½ÁÂýµ¸¼¼»·®£‹vl`Q@4/18>BFKOSVXYYZ\_clo{xcZv‰ˆ‹�““•–”’““““‘��Šˆ‡„ƒ‚€€~z}„…„Œ™žž¤¬°¯ª¦¡˜�‚tlkmqw|€ƒ†‰Œ‹�Œr`cea\\\bqxi^v§ÇÈÆËÌÌÈÌÏÍÏÏÉÊÎÎÎÉÍÑÑÑÐÏÎÐÐÎÒѦokxol‘·Çµ†ÕÜ××ÖÖÕÖÖÖÙО¾Á¿²²»º¹¹¶³š�|smeXJ:0/28>CFKORTVWY^[dqzvba~‰ˆ��‘“““’‘“”‘‘‘�ŽŠ‡…‚‚€}zz€†††Œ“–𠦧§¦¤ š“Œ|mihmsy€ƒ„†Š‰‹Šviifd_\[[^kwpcm–¿ËÊÊËÉÅËÆÅÌÈÈËÎÏÎÏÐÑÑÒÑÏÐÑÐÌÏѰxkyrm…¶·…ÔÚ×ÖÔÖÖÖ×ÛͽÄÇÅù»º¶·º¸¸º¹ªŒzvqi\NC8/-39>AFJNQUXZYaqyvcd~†‡Œ�‘’“’’””“‘��Ž‹ˆ‡…�€}}zux}€‚„†‡ˆ�™¢¢ ¡ ›”އ|mgehpx}‚ƒ‚†‡Š‹tfgd`]ZZ^[]gtvic�±ËÈÇÈÁÇÌÍÈÇÍËÌÍÎÐÏÐÏÐÒÏÏÏÎÏÍÎ׸{juolƒŸ…ÏÔÖÖרØÖÙÕ¼·ÅÄÅÃÄÿ¿ºµ¸»»½¸±Ÿ…~yvph`VL?4-.3:>CFJQWWX_nyxji{…‡‹��‘“‘‘’’‘Ž�ŽŒ‰†…‚}}zvqorrhenx{~ˆ•˜”“’ކxoqmgdfnv{~�ƒ…†‹Špbdd`^]\][XYerulau¦ÄÆÆÈÉÎÏÊÏÌÉÊËËÌÎÏÎÎÏÏÍÍÍÏÑÑÏÕ¾ƒmsuju…Ó×ÕØÚÙÚÙÜÐÃÈÈÅÄÅÄÄÀÀÁ»·¼¾¿¹´°”‚}xsojc^SG:0,/5;?DJQXXZhx}}~w‡‰Ž‘‘’�����‹ŒŒŠˆ‡„€|{zyvngfcVJLXju{�„ƒ‚‚l[anqlgekt{~€�ƒ†‹†j`ddcb_]\[Y[]`gtufi’»ËÈÇÊÌÌÎÊÇÉÊÊÌÍÎÎÐÏÏÎÎÌËÐÑÐÎÖÆ•pqsj†ÓÚØÚÜÛÛÜÛÓÍÌÉÆÄÅÃÁÁ¿µ²»¿º¶µ¦‰�}xtokgbYJ@:1,06=BFOUWaty…‹zvƒ‡��‘�ŽŽŽŒ‹‹Š‰‰‰‡ƒ~ywxxxtlb]ZWPLRapuvuuvnXWfqwvojkry|~�„Š„h`cca^^``_]\ZX[guvid¯ÊÈÆÌÌËÌÊÄËÏÍÌÌÎÑÐÏÎÎÍÊËÍÏÐÏØÒ¡rot†ÓÛÛÛÜÜÝÝÙÍÊÊÊÊÈÆÇÇÆÆÅú­µ¿»´²°•‚�|yuoedcZRJA6-,184.-3:?DM^u|‹‹‡€ƒ�‘��‹‰ˆˆ‡„ƒ„ƒƒ�~|||~~~{vpkhhllkmmllicfmquz}€|wtwyz|~ƒŠyecb`fg_]][^`_[XXWYjytce�ºÈÈÇËÉð¼ÍËÉÊÍÎÐÏÌÍÎÍÍÐÐÏÑÏÕÕ°{‡×ÞÜÝÝÝÛÛÔÊËÍÏÍÍÎÌÈȼÀÃÁ½º¸´¸´´¡‡‚zutsnhddd^UME<30/4@PVs‡ˆ…~|�‹��‹Š‰ˆ‡†ƒ‚‚€�€€‚‚�€}|zxtpnmmkhfffdgmtx}€��‚€}{wvvx{�‡raaaacc_^ZY_`ba^ZWVYdprg_{«ÆÇÆÄÄÄÆÈÉÇÈÍÐÒÏÍÎËÌÍÏÐÍÏÑÏÒØ´‡ØßÝÝÜÜÝÛÏÉÊÊÍÍÉÈÌÊÈÇÅÄÅ¿»¹¹¶º·³®•…�|vsokihe`XWSKB:21>LPb‡‰ƒ€ƒ‚ˆ�Œ‹ŠŠ‰‡†„‚�€‚ƒ‚„…„‚‚�€€~}zxsnkhebbcgnuz~�ƒƒƒ„ƒ��|vtuz�…l^_bcb`]\\^_^ab`\YX[[^jvnbošÀÇÅÇÍËÄÃÄÇÍÏÑÏÎÏÎÌÍÏÏÏÑÒÑÑÓÁ‡×àÝÜÜÝÞÖÊËÍÌÊËÆÅÇÊÅÂÈÇÅÄþ»¸¶¶µ¶¨Šƒ€yvqkljd\SZ[VPIDAGGJP{އ‰Š���ŠŒ�‹ˆ‡„„„……‡ˆˆ‡„ƒ„„ƒƒ‚€~|vrlgeccfkqx}€�ƒ…†‡„„„zqs|ƒ…k_`aa_^]]_`__]]]]\Z\\W\lzyda‰·ÇÅÅÄÇÆÃÇÌÆËÐÎÏÌËÌÍÍÏÒÑÒÓÔº‡ÖÜÝÝÝÞÜÓÊÍÏÎÌÍËËÇÇÄÀÅÇÈÈÅÿº³´¸¸µ›„ƒ~|xqpof[]\XWZYROUONHb‰††…tt�ŽŠŠŒ‹‰ˆ…„†‡ˆ‹Œ‹‡„……„ƒƒ‚�€~ysld``chnsx|�„„„……�ƒƒ�~su~…�h``_\[\^__`^]_``__\YXZZ\euvf`|ªÅÈÅÈÇÄÇÊÇËÏÌÍËÊËËÎÏÑÒÐÑÕ½ˆÕÖÛÞÙÚÙÏÌÊËÌÍËÅËÉÆÆÆÇÈÈÅÀÁ¼º¹»»¸°ª’†ƒ�|xsokdeb[V[caZZXWNQuƒ�|gfˆ�‹‰‹Œ‹‰‡†ˆŠ‹Œ‹Š†ƒ„ƒƒ„„‚�€€}xrkdbfksy|€ˆ�‡‚���‚………}{‚‡}d_`_\Z]^^`_]]acb`_][Z\ZWV\hsocn›ÄÊÆÆÅÅÊÎÏÍÍÎÌÊÈÌÏÏÍÎÌËÔ¾ˆ×ÝÜÜØÖØÑÎÊÊÎÌÌÊËÉÉÉÈÉÉÇÄÁ½¸¹¼»»º²²¢‰†€|}xpnmifc_]`cb_\\YQWnvqee„�‹‹�Ž�‹‰‰‹Ž��‹‡…ƒ�‚ƒƒƒƒ†‰…~{xsqsux{~ƒ��ˆ}|„……‡ƒ€…ˆ{c`^]_\^`__``^```_]^\\ZYWWST`qtdf�¸ËÈÃÅËÍÎÍÍÍÊÉÊÆÃÌÊÆÍÏÓ½‡ØßÛÛÙ×ÎËÑÑÏÑÐÐÑÍÌÆÆÊÊÉÆÅÇĿ¾µ¶¹º·³˜‰�||ywrnljgdb__eec_\VJHMRdn�ŠŠ‹��ŒŠŠ��ŽŽŠ„�€}||~���…ƒxooprttrnmos|�~yurx‚…ˆ†ƒ‡ˆxb]X]baa`^_c`^b`]_^\Z[ZZZWUUT[mtjcy©ÇÄÃÊÌÌÎÇÅÊÉÊÆÀÉËÌÏÑÔ½†ÖÝÛÚÙÙÀ¶ÏÒÐÐÑÏÐÍËÈÀÇÇÇÈÇÈÉÆÄľ¹¸¹·¹ªŒƒ�{zvqomlihebcdd`ZSM@8>_r}ŒŠ‰Š‹‹�‹‹���Œ‡�{wvwwvtroliea]\__^\\\[]bikllmrz‚‡Š‰‡‰‰wda^ca^_^`bb^]__\^][\\]]\YWXYSVhuoal˜»ÄÆËËÌÊÈÈÆÈÊÉÉËÎÐÐÒ½‡ÖÞÚÙÚÖÉÅÎÑÓÒÑÐÐÏÏÎÄÆÇÆÇÇÅÅÄÃÅÅÂÀ¹µ·°’‚…ƒ{zurpolihfc`a`[RMH??\s|ЋЋ‹‹ŒŒ�ŽŒŽŠƒ}uqonle^Z[]a^[ZXSQRRTXVPOKHM[gpy�‡ˆŠ‰‰ˆuc`\[[[^`b^UZ^[[]^^^aa_^\ZWW[YUVcsugd…²ËËÉÉÌÍÉÆÇÉÆÇËÌÎÏÒ¼‰ÙÜÛÞØÐÎÒÑÒÔÒÑÐÎÎÏÎÍÉ¿ÂÄÆÆÃÃÃÂÃÅÆ¾µ¶´§�†…�|zywsjighjie``^YRLF@Yw}ˆ‹‹ŠŠŒŒ�Ž��Œˆ‚zqbPFCFJS]acbtxmffih`_aVG11;>Ibt}‚…‰Š‰‡pXRNJS^`ab^X]^]]`ca__^^^][Y[^_]XW`rykbx©ÉËÈÊËËÈÄÆÆÅÊÌÍÎÒ¼‡ÖÚÖ×ÎÇËÍÑÑÏÑÑÍÍÍÍÍÏŵ¿ÇÅÅÅÄÅÆÅÅź¹¹¹¤Œ‡…‚ywxummjiligb][YTMH?Ry�…‹‹ŠŠ‹ŒŽŽŽŽ‰†‚xcH:8:?ANZMI[{€tijmgWIEEGJY\UMUky…Š‹‹†kVQNLUaba_bed`]]`ba`^]__]^_^_^^^[Y_nyscl™ÂÍÉÉÈÇÇÄÇÆÅÊÍÏÑ»†ÔßÎÎÐÈÎÏÑÑÑÕÒÎÍÎÎÎÍÄ¿ÅÇÄÆÇÅÇÉÉÅÿ¾¾¼´›‰„}usttutoljhgd\YUPJDDFIKOMHIJIJNPRRTUSPLNRSRTX\[[\ZZZ]_bdda`^WUTVYXWWWRM…ÐÎÍÎÐÑÏËÍÏÐÐÑÐÏÐÎÍÐÏÎÎÍÌÊËÆÄËËÇÊÍÉÇÊÃÄÃÃÅ¿¾ÃÇÅÅÂÂÀ¿½¸Ÿ‘“��Œ†{‰‚iWRRQUUZbfjI10/16AIPUWZ^_ab```aabbba]YX]enu}…ŠƒVAEHMR]lfG:AEHJLKNQQMJNMLLLIHIMQRTUWXYYXY^cbcggfggb\XXYXVTU\YP…ÐÑÌËÎÑÑÍÎÍÍÑÒÐÏÍÌÍÏÎÎÎÌÉÉÌÃÄÊÊËÌÊÇÉÊÂÃÉÇÆÅÂÃ¿ÂÆÃÄÁ¿¼º©•‘��Œ„{||…�€cNMQSTR[biiD30//2:ELOPQSVYXXYYZ[YWQPW`fjpx†‹‚T?CCIOU`olM:>@@DGILQPOPKHHHKLLMQRSTTRSVWZ_bbdeddbc_[WVYZVTTXZV…ÏÐÊËÊÉÈÊËÈÊÎÑÏËÌÎÏÐÎÍÎÎÊÊÍÊÈÆÈÍÉÇÆÈÉÈÇÅÆÈÇÃÄ¿ÃÇÅÅþº¶¯ ”��Œ‚yrƒ„„ƒv]IINSQQ\akb@3///06;DIJKLNPRQQPOMLLV`dgntz‚‡Š‚SDLMQ[bnU972./125;BFGGHIILPSY^aekpu|�…†ˆ‚R:@AEJRZ_fkyyhtyM8;@FGILMLRVWZZYWRPOQSSTRRTW[\XXWYYXWTRTTPS^ca…ÎÏËÉÊÌÌÊËÍÎÎÍÐÏÍÍÏÑÐÏÌËÍÉÈÈÇÈÊÊÊÉÊÊÊËÈÈÇÄ¿½ÅÈÈÇÇÆÆÃ¾À½¼²§š’šœœ�ƒŒŠtnsvjS@?IMSZcjM9930024;BJOQRQSVY[]`elosy~ƒ†‡ˆ�R8=ACISX^ekq~�—¤Š^K@>EIKJHIORVYYXNGJLNNMOTTRWYSQPUSPQUY[]aa`aa…ÎÏÍÉÈÉËÌÆÊÏÌËÍÎÎËÌÐÎÏÎÍËÉËÉÊËËÉÊÌÉ»ÅÊÈÇÈÆÁÀÄÇÈÆÅÆ´°ÀÁ¾·«¦š�•œ�™�†‰nkloo\E>FNTXagG7753126=FMRTTVY[^afjnrx}�„‡‡ˆ�R6:=AHRX^gmq|�…›�|iK>@GLKLNPSUVWSIGMONMLONNOPSSU]ZXY\]_a[TUUY„ÏÏÍÉÊÌÌÎÊÍÐÏÎÌÈÊÎÎÊÊÎÌÎÏÐËÈÊËËÉÊÉÆ½ÅÈÈÈÆÆÄÃÅÈÈÇÇȶ±Àºµª¥¢ž“•¡¡£–‡ƒˆznegnvdNAAKSWcaB7645448@IOTWZ[^aejmpw|�ƒ†‡‡€Q49<@IRX_ekv‡”ƒMRs�†sQD?@GKKMPQSUWSNTWRSTSSUUSZ][ZUTTV^ZRg‡œ“[ƒÎÎÌÊÉÌÌÍÍÏÍÌÍÏÈÉÍÍËÎÎÌËÎÏÌËÊÌÌÉÆÅÉËÈÇÈÉÆÅÇÇÌÌþ½»º¹°£ž¡¢  —’ž¤§œŠ‚ˆ„tngcongYG@ENUh_?967788;CLTY[[^afkoty|~�„†††O28;?ISZadn„�‘„O3;J`ryv[I<><:99:?HPTVY]chlqtw|€ƒ†‡‡†P/5:?GS\al€Ž��xRG=77Dt™”Š^D>@HKJNQQTSQQNONPUTf�Ž}{›§£ƒožÒÎÚÝâȃÉÊÉÆÈËÊÉÉÅÇÍÍÊÇÇÉÈÊËÉÊÍÊÊÍÍÃÂÇÄÄÄÆÄÄÄÅÆÄ»´±¬©©§¥£�˜™›–Ž•™›™ˆˆ†xqy�‰‡ysntl`behkd\SP^[?:9;<=>?DJOV[_ehlpty~‚ƒ„†…ƒ~V/3;?HU_iŽ�ŽŠfCFJKC9Ce…–‹†zX<>DEAEEJQQZhr]Rh‘Öஞ¬Ÿyw²ÞÛÄÉÌÆ­‚ÉÈÅÃÃÊÈÇÆÆÍÍÍËÈÇÃÅÊÎËÈÍËÊÌÍÈÄÆÂÀÂÁÀÁº¯©¦¥¦ª®®¨ž–”—œ›�Š�™�ŽŒ˜†|Š‹†yglvpfa[]^ekt…ІoYK?98:?AFMUZ_eilpuxz|~‚€|wZ726=HR`yˆ‹‹‹VBCFILI=4AZt‹š‰iWG?P\`lx€œ¬§{^k�ÒÒ®§¤�…©Ä·®­ª§¥”‚ÉÅÁÅÈÊÍÎÇÅÌÊÆÈÊÉÉÊËÎÌÈÈÉÈÈËÉÇÅÃÃÀ½½»·´¬©ª¨¥¨®°¦˜‘‘•œ£ “‡…Žž”Œ—™›¢–‚„|q}€zy‚xjefjy�”œ¢¢˜ŠvaTMKOU[agntx|€�„ˆ‹��‹„‚fC=9?Md�“–˜˜•�jPOOQQSVPA>E[q•² t�½ ™–•§ÁǺ°¬‹¤ÇÁ¹º¹¸¿¼²±³´´´¶©�ÆÆÄÂÅÊÌÍÊÈÉÆÂÆÉÉËËËÊÉÈÇÈÈÈÉÆÅľ¸³¯«¨ª©©«ªª¯²©™ŒŠŽ— ¤ “‰…�•Š‹�š�u`WRSUQQRVSMHCGMNRSYgmzzmZA89@NW[iw|€ƒƒ�€}yumfcaadfda]XQG;5434@O]bachmqvy{|€mPEDDFKPW`g^OVeƒ¸ÈÉÉÌÏÎÍÐÐÎÎÎÎÎÎÑÑÎÎÖ»jw|nm’»ÉÈÉËÊÌÉÈÊÉÌÉÉÍÎÊÍÎÒ¿…‡ÔÙÕÕÔÖÙÙÖÕÒÔÙÛÚ×Óê�–‹��‹‘šš’‰qmkjjjkmmkjjlmnlmpqpkgeYECB:ALRXhyƒƒ„„�|ysmhda`YOHC?>:620002AVdc`cinrvy|€‚‚vVA>=?BFNYfiVTf”ÄÆÊÍÎÎÎÐÍËÏÎÏÏÐÎÑÐÎÎÐÖÇ�js~tnŒ¹ËÊÊËËÊËÊÈÍÌÈÍËÈÊËÌÔ·…ÑÚ××ÖÔÔÕÕØØÛÖË¿¬š‹‡‰‰�“‘�‹‰‚wlgddiljlkkllllopomoonqshSG?AB:AJOWiy�„………�|wrmjgfaVKEDCDFHIGEDIT\\[_glosw{‚„„~dC987987:I^YWo¨ËÊÉÌÏÑÏÏËÄÆÊÍÎÏÏÎÎÎÐÒÒÜÒŸrs}sj‚²ËÊÊÊÉËÌÊËÎÌÎÍÊÈÈÎμ…ÏÖÓÓÖÙÙÙ×ÑÅ·¦š�ŽŒ„�š–•”�„wmiffhjiimlllmmloonooooonmkkpkYMF@CLPWhz�„†ˆ†ƒ~wtsssqmid_\\\][WSPPRTUV\elprvz}€ƒ…†„tYHD@>=716BO\€ºÊÈÉËÊËÎÍÏËÆËÍÏÏÎÍÏÏÊÎÔÒÖÖ¯}oytl}­ÊÊÊÈÉÍÍËÌÍÎÐÎÍÍÎк…ÎÓ×ÚÛØÌºª�”��•œŸ•„�Œ‡~yrhefjjklnnmnllnqnnonoqppooomly�ucNCJQRWi}…†‡‰ˆ†„}~~|ywtqmkigc_ZVUUVVY_florvz}�ƒ†ˆˆ~fTMKLLIJOKK^ŽÅÉÇÉÌÿÌÍÎÏÎÏÎÐÎÎÍÏÐÎÏÓÓÎÐÔºƒoz|oy§ÆËÈÉÌÍËÊÌÍÍÍÍÑÏÑ»‡Ô×ÏÄ´¢”‹Š‘—› ¢›“ypyxnhhheimnpnmnmmmnnprpopoqrrpnoqoxxlqvqcUVTQYn€‡ˆ‡ˆ‰‰ˆ†…„‚}{wsokheb`]\[\^aekqsux{~€�‚„‡ŠŠ…r_UQQUY_gi^e”ÅÊÉÈËÈÇÎÏÏÎÍÍÎÐÎÎËËËÎÑÓÑÑÏÏÖĈjwzou�ÁÈÈÌÌÊËÍÎÍÍÎÑËÊ»ƒ»©—‹ˆŒ”›Ÿ¡�”ŠƒslgfkiehhhkklnnkklkklmmoopppqppqoonoqxkeksuslcXO]v„ˆ‰‰‰‹‹‰‰ˆ‡…‚�~{vqmjihgefggjmrvwy|}€€�ƒ†‰‹Œ‰pd\USY`iqqr—ÂÈÉÊÇÊÐÏÐÍÐÐÌÏÍÌÐÍÌÉÌÐÒÑÐÒÑÑÙÈŽmszor•½ÇÈÈÉËÌÏÏÍÐÐË̺o‘Š�–ž¥¡—‹~vpmn|�•†n[Z_abcabhiijkjjlnnnnopppnoonmmlrtjjkrvvsmd\k�‰ŠŠŠ‹�Œ‹‹‰‰‡„ƒ�}zxvtssrrqqsvxz{{|}€‚�€�‚†Œ�‘ކ{qid^X[dnu{šÄÆÆÉÊÌÎÎÌÉÁÇÏÎÊÏÑÐÏÎÏÏÑÓÒÑÒÓÓÙÑœorypp޶ÄÄÊËÊËÎÐÐÐÏкo ¤¥ –†xnkmx‹šªÇÒÇÄ·‰\OTXXY[^beghijkklmnnoponooollkttiijx{wtrpp|ˆ‹Š‰Š�Ž��ŽŒ‹‰‡†…‚€~}|{{{yyz|}}~~~‚ƒ‚�€‚‡Ž“•’‹�ytpmigipx‘¹ÎÈÆËÍËÌÊË¿ÅÐÎÍÐÐÎËÎÐÎÑÒÓÒÔÍÆÎÖÔ©vqyrnŒµÃÅÈËËÍÐÏÍÍмwž‹|snqxˆ›­»ÉÉÏÚÌÀÏÝÓŸ`IGKPSVZ\^adfffghjmnkkmmnppnkutijo~|stvux‚‰‹ŠˆŠ��‘��Ž�‹‹‹Š‰ˆ†„‚��€}~~€€€€‚„„‚�‚…‰�‘”“Ž…}zxttssuz‚ˆŠ«ÍÊÇÉÊÌÍÍÏÐÈÊÏÎÏÍÊÏÎÍÑÒÑÑÑȾËÐÕÛ«spyrpŒµÄÆÊÍÌÎÐÏÏÒ½avnu„—­¾ÏÖØ×ÓÉËÐÈÀÇËÑЩnG>AHLOQUY\_abceilmiikkklnonvwjlt€}z}|y{ƒ‹Œ‹ˆŠŽ�’‘�‘‘�‘���Œ‰ˆ†…„„„ƒ€€��€�‚„…„ƒƒ…‡‰Œ” ¢˜Š{{{zyyz}ƒŒwjžÇÌÆÈÌÍÌÎÐÈËÎÏÐÌÎÐÌËÏÐÏÐÍÍÏÏÓÓÔØ®umvsq…­ÃÅÈÌÍÐÐÐÑ»e–©ÀÏÚÝÞÝÚÔÔÓÐÒÔļ¿ÆÉ¨vT>8>EHLORVY\_behjijijjikmntxlmvƒ…ˆzp{}„‹ŒŒŠŠ��‘‘’”•••“‘�ŽŒŒŠˆ‡…„„ƒ‚‚‚��ƒ„„‚�ƒ„‡ŠŽš®»¹¬™„{{|~~~�…ŽˆihŽ»ÊÆÉÌÊËÎÍÉËÐÐÌÌÐÏÌÍÏÐÏÏÐÎÍÐÑÑÖÛµ{kyyn~¡ºÃÆÊÍÏÎϺvÊßáÝÛÙØ××Ö×ÔÔØÎÁÀÂý¹½¼¯ŠbD67>DHKNQUY\`cfefgghhijjoxmlx…‡„b^y~„ˆŠ‹Š‹Ž‘’“••——–•“‘‘��Œ‹Š‡……†……„ƒƒ‚�~�ƒ†‹š¯ÀĽ´¥�}yx{~€‚ƒ‡Œ�|ib}­ÊËÈÊÊÊÌÉËÍÍËÂËÑÏÎÏÎÎÐÐÎÍÏÍÒÑÒÛ¾ƒmyxm}žºÄÆÊÎÎк{Ì×Õ××רØ×רÓ×ÒÃÀÁ¿¾½º¼¹¹«‰lP:26?DGILPV\_a`aceedfhhkxqm{…‚qT\u€ƒˆŠ‹‰‹Ž�“•••———–”“”•“‘��‹ˆ‡†…„„„ƒ�~}€€€‚‡“¤³»½¹´ª•xy{}ƒ„ˆ��|wqer ÅÉÇÈÉÊÌÌÍÎÍÆËÐÎÐÑËÌÐÏÐÒÐÑÓÒÏÑÚÆ‰jqvt~›ºÄÄÉÎѹ‡ÓÔÔÚÙÖÕÒÐ×ÓÑÖǺº»¹½Â±´º´´ ƒpYD416BFJOSVYXZ]_]bsx€rcixˆ‰„�‚…ˆŠ�‘’“””“”••””’‘�ŽŒ‰†…ƒ�€}zxz~‚……†ˆ‹�“œ¡¡¢¡ ž•onsx}€„ŠŒv`^]]hvtbd‰¸ËÆÉÌÌÉÊÎÍÍÏËÈÍÏÏËËÏÐÐÑÐÏÎÑÐÏÕÀ†huwi~ªÃ´‡ÕÝÙ×ÕÖÕÕרÚÑÅÅÃÂÃÀ¿»²¹»¸¸¹·¯�ztndYI;2/19@AFKORUX[\]`ox€{kov~‰†€�…‡Š��’’““’’“’“��ŽŽ�‰‡…ƒ€~~~|wssttmiov{|�‹•˜—––”Šxljouz€ˆ‰ta]\\[cqtic|ªÇÌÉËËÆÅÊÄÊÉÇÊÎÐÎÎÐÐÏÑÒÏÎÑÑÎÌÓÅ‘ktxov¡´†Ô×Õ×ÕÖØÖÔÚÒ¸¼ÇÅÄÂÀ¼¿ºµ·¹¸º»·¡‡~yvpfYND8/-4:=BFINSX^]\hx~ƒ€„{p�ˆ‚‚„†‰��‘’’‘‘‘‘‘�ŽŒŒŒŠˆ‡…�}~~|ztlhibO?CRcpv{‚†‡††‚xojhlsx|†‡o_]Z^^[_mwqck—ÁÌÇÈÅÃËÍÌÆÊËËËÎÐÏÐÏÏÒÑÏÏÎÎÏÍÔΕjpslv�†ÓÕÔ××ÙØØÙÙÇºÄÆÄÅÃÄÁ¾¾¹¶»½¼¼´°–‚}ytniaVLA5//39>BFKPVZ[_rx{‡�‡ss…ƒ�…†ˆ���‘��ŽŽ�ŽŒŒŒ‹‰ˆˆ‡ƒ€|||{yvmc_\SHBBHUepvxwwwtsqkgipv{~„†n`_\]\YX_lsrffЏÆÅÈÉÊÐËËÎÉÉÊÍËÍÏÏÎÎÐÎÎÎÎÐÑÎÓОopwpj…ÔÚרÙÚÚÚÜØÌËËÆÅÅÄÂÀ¿ÂÀº¹¼¿¼¶¸ª�‚|vqnid^TF:2,.5<@DGJQUWfuoxŠ�†„‚€„†‰���Ž�ŒŒ‹Š‰ˆ‡†††††…‚}{{{}}|vla][YVSLKYippoonouwrihouz}„‚la`]]\ZZ\]`lxocy¦ÆËÆÈÌËÍÌÈÉÊËËÌÍÏÏÏÐÎÎÎÌÍÐÑÎÓÓ¬znul‡ÓÙØÚÛÛÜÜÛÕÌËÉÉÇÅÅÃÃÄÂÁ½¯±¾½¸µ´›ƒzwsoiebVID:/,.7?CEILSZr{x‰‘Ž‹ƒ€�„‡‰�Œ‹‹Œ‹‰‡‡‡†„‚ƒ„ƒƒ‚|||~��€~yqidbbdbcgkmlmmnsx|zqlovy|‚|g_^_`_`][ZY`nwqdj•ÁËÇÉÌÌÌÌÅÇÎÎÍËÍÐÑÐÏÎÎËÊÍÏÑÏÓÚº�ls‡ÔÚÙÚÛÛÝÝÜÑÈÊÉËÊÈÈÈÇÇÅÄ·²¼½¸³´©�‚|yukee`ZSJA6.-2:?EOVSf„‡‹Š†€~€€ƒ†ˆ‹‹‹ŠŠ‰ˆ‡‡‡‡„€�‚�‚�€‚‚‚ƒƒ‚€~zuplkkjihhijjmsx|€yrrvy{�xfea_aa`_[ZZ[_fuwgb…´ÈÈÈËÌÍǾÄÍÌÊÊÍÏÎÏÎÍÎÍÍÏÐÒÑÒÚ¿†n†ÖÝÛÛÜÜÜÚÚÑËÍÍÌËÌÌÉÈÆ¿¿Ãþ»»·¶¶´£ˆ‚|wurmfab_XPH@4//5ELTQTv�‹‡…ƒ†„€‚„ˆ‹‹Š‰ˆ‰‹Šˆ†„ƒ�€�‚�€ƒ††…„„ƒ‚€~|xtqlid`adgkrw{��~yvvvy€tdgb]^[[_`]XWYX`sxk_t¥ÃÈÇÉËȸ³ÉÍÊÈÌÎÏÐÎÍÎÎÍÎÐÏÐÏÑ×Ä�†ØßÜÜÜÝÝÜØÌÊÌÍÎÍÍÍËÈÇÀ¾Ã½¹¸¶¸¹´±•„~xwtqlheea[TLD?85CEJRNc†�‹ŠŒŽ„|�ƒ‰��Šˆ‰‹Œ�‹ˆ…„ƒƒƒ„„„…‡ˆ‡………„„ƒ‚�}{vqkfcbbhpv{~�‚ƒ‚~zvsxpbca^\Y]_bca[WWV]krm`h“½ÉÅÅÅÅÅÉÉÇÇËÏÑÐÍÎÌËÌÍÑÎÎÑÐÐ׿‡ÚàÝÝÜÜÝÜÑÈËËËÌÉÄÈËÉÆÈÇÅ¿¼º¸¸¹µµ¨�„�zusmjjfcZWXSQIBIJDMPUq‰‰ˆ‹‹{lzƒˆŒŒ‹‰ˆŠ��ŒŠ‰†……†‡ˆ‰‹‹Š‡†‡‡†„„ƒ�€~ytnhedfkqw|‚„…ƒ‚xqx~oca^[]]___`a]YYZ\[brufd‚±ÈÅÅËÍÇÄÄÅÊÎÐÑÍÎÎËËÍÏÏÐÒÑÑÓ½‡ÙàÞÝÝÝÝÚÌÊÎÍËËËÇÆÇÇÀÃÉÈÆÆÂ¾º´³´¶¶Ÿ„„}xwplng^VX[[`[QRSNLNO\v„†„€lYqƒ†Š�Œ‹ˆ‰Ž�ŽŒŠˆ††ˆŠ‹�ŽŒ‰‡†‡†……ƒ‚��~zuniddhlrw{�„„���yqz~k___]_a_^]\\]]\[\ZXdt{n]r¤ÃÅÅÄÅÈÅÆËÈÇÏÍÎÎËËÍÍÎÑÑÑÓÕ¹‡ØÙ×ßÜÛÝØÎÌÎÍÌÍÌËËÆÄÂÃÆÉÈÅÃÁ¾·µ¸¸µ­‘…‚~{urqm_^aZW]e^XXYUOKMVm|{r`Xo�…Š�Œ‹‰Œ�’’�Œ‰ˆ‰‹�Ž��Ї…„ƒ‚ƒ„ƒ‚€€ƒ‚{tnihkpuy|€€€ƒ~w€|e\]___`_\^``__^ZWZZ[_lwnaj”¼ÉÆÇÈÄÅÊÉÉÎÍÌËÊÊËÌÎÎÒÒÐÔ½ˆØÙ×ÝÛ×ÚÔÍËÈËËÌÆÆÊÈÈÇÆÆÈǾ¼·º¼½»³¯¤‹†„zxpljifa]\ceba^[ULD>@Pflegq}ƒˆŠ‹ŠŠ�‘””’�‹‰‹Ž�‘�Œ‡„ƒ~�‚‚�‚‰‘�†}xutuvx{||}{{}�…ƒ~„zb[[^^`__]^bb`_^][Z[XVYamrhd‚³ÊÈÆÆÄÈÌÏÎÍÍÌËÉÊÍÎÎÍÍÊÑ¿‡ÖßÝÛÚÖÕÑÏÎÊÍÏÎÎËËÉÉÉÈÈÇÅÄÁ¼¹½¹¸¹ºµ°™‡ƒ}~{tnmkihhedcdea\VOE<44Qmpos}ƒ‡‰Œ‹‹Ž‘••”“Ž‹Ž����ˆƒ€}zyz|€…‰ˆ€wrrstqmkmqtuvux€…†‚…tb^\__^_`_^^``]]^][YWVVSYhuk`w£ÄÊÅÂÈÌÍÎÍÌËÉÊÊÃÇÎÆÉÎÒ¾‡×ÞÛÛÙØÑ¾ÈÒÐÏÑÏÑÏÌÉÂÇÉÈÆÄÇÊĹ´¶¹º¸¬�„~~zxuomjjlifaab_]VNG@72Hjrrv{�…‰‹�Ž�’––—•’���ŽŽŠ„~yuuvvutrrqokea``bb`]\]cjmlls|„ˆ‡‡qcdaa^]ba_aa^^^\[[[\[XTUTUdspdh�¼ÆÁÆÌÌÎÌÅÈÈÉÊÂÅËÌÎÐÔ½†ÖÛÚÛØØÑ·ÂÒÑÐÐÏÏÏÌÌÄÁÈÆÇÇÇÈÇÃÄÄ¿º¸·¶¶œ…„ƒzytpnmolhdba`_ZQHA90?etuz|ƒ‡Š�“’’•™š˜–“��ŽŽ‡€yspomkh_[^^`a][ZXVVWWVSRRWahp{ƒˆ‰†sc_``_ab`\^_\\]\[\]^]ZXWXVR]pteb€¬ÃÄÉËÊËÊÉÈÈÊÊÊÉÍÐÏÒ¾‡ØÝÚÛÛÙÑÌÍÐÓÔÑÐÐÏÏÐÊÇÈÅÆÇÇÄÃÂÃÄÄÃÀ´µµ¥Šƒ…‚|{yusonnkhd`^_]VNC;39`xz~‚…‰’”“’“˜›››™•’‘Œ‡€xkYIGGEKTQIIXba]XUSLBFJ;28I_oy‚‡Š…iZ]]^``WW]][[]^^_a`]\[YWZ[VT[kvoap ÃÌÉÉËÎÊÇÆÈÇÅÈËÍÏÒ½ˆÙÜ×ÝÙÐÊÎÑÑÐÑÑÏÎÎÎÎÍο¼ÅÅÆÅÂÄÄÁÂÄĺ¶¶³¡Š…ƒ�|yywojlmle_[YXSKB966Wz‚€|ƒ‡Ž“”’“—œŸ£¢œ˜–�ˆ€oT?68951/57AJENdt}…Œ…\N[_`aa[\`^\^ba_``_]]\Z[]^_\WZjxsci’¿ÎÉÉÌËÉÄÄÆÅÈÌÌÍÑ»ˆÕÜÖÍÏÅÉÍÐÒÏÐÒÎÌÍÎÍÌÊ»»ÇÅÃÆÅÅÈÇÅÂÂÀ¹º»µ›‰†ƒ}vvwrnmklga]VSMD=<<8Mw‚„€{}�ƒˆ‘•”–™�¡§©¢�œ•ˆxaT[_`_`^ZQC601468;=<5&'In{jU[nzƒ‹€[R]ba^`dea^\^aa`_^__^]^_^^^_]Z\euyic‚´ËÉÉÉÇÆÄÆÉÅÈÌÍл‡ÓÝÚÏÓÊÍÐÎÐÑÕÖÒÏÌÌÍÍÈÃÆÈÆÆÈÇÇÈÉÈÄÃÁ¾¾¾»­‘…}vttsutoligda^TLFBDD>Fq„…‚{y}ƒŒ�‘”˜›ž£ª©¤ —†tgo{{x{„Ž–—ŒwbMBCEHIJD=Kn‹’„nemx‚‰|`\_[WSQUVUVRQZabb`a``____\[\\Z\\`oxqdt£ÇÌÅÃÅÆÉÉÆÇËÍй†ÓÜÝÝÒËÌÏÒÓÒÑÒÐÐÎÈÊÌÊÉÉÉÉÈÈÈÉÈÇÈÇŽ¼¿¼¹Ÿ†y{|zytqqiddc_VPNMKKBBjƒ…ƒ{wy{~‡��‘“—™�¥¬¨¡•…zz€ƒƒˆŽ�‘𢧩¥œ‘†€ylijmyˆ––‹€tqw†ya^YSRROQMJKMMV\ZUTW[_````a^\\Z[\XYfusch”ÀÊÅÃÅÈÇÇÈÇÉк‡ØßÜÖËÊÎÑÏÏÓÎÍÊËÏËËËÌÍÈÈÉÈÈÇÉÉÈÉÈÆÅÁ¼¾½»±•ˆ‡…�{uvuoife_\ZYSTPACf��{vvy{‚‹�Ž�’”–œ¥¥ž”‹†‡‰‡‹—œ˜’��”ž©³µ°­£•‘�‹†Š��Š„{vw|ƒr][XXXVUVSPPTWPNLFDHV^^__ada`_][Z[YW^mria€³ÉÆÁÂÃÄÈÅÇͺ†×ÝÙÕËÎÑÏÀÅÒÓÑÍÌÎÍÎÌÊÉÊÉÉÉÉÉÉÉÊÊÉÅÃÃÀ½½¼½«�І‚|yxutrpkeca][YNBDaz€€zsquwzƒ‹�Œ���‘–š˜––•—™˜˜˜˜–•“���”𠤍¦¡›”‹„ƒƒ†ˆ„}zx{~jZYYZYYWUTUVWYSLIGGP[^^]]_a`aa^\[[[ZV[hsnbs¡ÃÅÀÂÃÇÈËË·‡×ÜØÑÌÐÑÐÊËÒÓÍÎÑÎÎÎÌÌÊËÇÅÊÉÈÊÊÇÇÉż¸¼¾¼¼¼¶š‡†„‚€|ywtrqlfbbb`WOUTGk}zrnpsv|…ŠŠ‰Š�ŒŒ�’”˜ž¢¥§¤�—“����І†‰Œ�ŒŠ…‚€}}~‚ŒŒ{z~wd\[ZYX[\WUVTTTUQLOW__ZWRPQTVTW[]_\Y[[ZY_qteg�»ÊÅÃÇÈÊθˆÙÞÖÍÍÏÑÓÔÏÏÏÍÍÏÐÎÍÎÌÎÌÅÇÇÀÄÈÉÄÅÇȼ·¼¿¼»º·¤‡‡‡…ƒ€|urrplidhh`[Vnw@Rt|{tnkoru{ƒ†„‚‡Š‰‰Œ�–ž¡£¥¥¡š“Œ†‚~}{zzzzyzxvvwxŽ•‰€}~n^^\\[YY[XUUQRVUQUX]ZSLLLMNMLGKVXZ\]\Z[[Y\kvja€µÊÅÃÆÉ͹‡ØÝÒËÍÎÒÓÒÎÎÏÓÎÉÑÍËÍËËÌËÌÈÅÉÊÉÆÈÊÊÄÄý¼¼º·­”Œ�ˆ…ƒ}stxvoighdbc_s‰cFeuxtniknos|‚‚��„‰�“™� ¢¥¦¢š“�†~xtsqomnqrromnqw�Ž˜˜‘…xe]^]\\]][XWTSUWUW_\UQOKKNNPNJGKMKOY^[[\^^Y[gsnbt¥ÃÅÂÇÌ·‰ÕÖÌÊÌÏÓÔÐÏÍËÐÎÊÌËÍÍÎÌÍÎÌÍÍÌÌÌËÊÊÈþÁÀ¾¾½»¶­—�‹‡†‚}|{yslggfhicr……RUjttojiknqv{}}zy}ƒŠ•›ž¡¢¥¦¡š”�Š€ytpmihiiiiggkry„‘˜œ›Œr_^_^\\^^^\[Z[YWY^_XRQSTQPMONLLMJINZ`b`_][Z[X_qxkl‘ºÅÅÌ·ˆÖÓÉÊËÐÓÖÒÏÍÊÌÏÊÂËÏÏÎÎÍËÊËÊÌÎÎÌÊÈÉýÀ¿¾¾À¿»º¤ŽŒ‰†‚�‚~xsnmnpnjfuy�mO`krnkigjmpuxwutuy€Œš£¤¡  žš”‘�…}vqmkjiihggjpv|ˆ“—œœŒm^^__\]\]_^\^]\]]]XTTRQRRQSROPQPNJLX`eb^][YZZ[bq~n`|ªÄÊ·†ÖÔÉÊËÐÓÇÅÎÉÍÏÍËÉÌÍÏÍÍÍÉÉÇÈÌɺÄÊÉÌÌÇÀ½ÀÁÀ»º¼³™Š‡„€ƒ�{rqusqkjws‹ŠZZemnlkhfhkotvspry€Š˜ ¢¢ Ÿ�›˜–’�ƒzuqomjhggkpuy��™œ œ„f__^^]`_]^^]^^]\[XVVUUTSSRSRPRRRPKLQZab`]][YY\_]gvqaoœ¼¶‡ÔÑÊÍÌÌÓÅÀËÆÊÏÊÌÍËÍÍÍËËÌÊÆÈËÇ»ÃÉÊËÊÇÂÃÃÃÁ·´·¼ª�‡„�‚…ƒ‚€{wwusmjyr|•rZcimnlkhiijorpoqv€‹“™žŸŸŸ�œ›˜”‘‹„}vqmifgimqu{Œšž¡¡™{`]_]\^_`_^__^^^[YYYYYYXWUTSSQPORRLJO^dca_^^ZVW\[W`rtgj‹¥ˆÒÍËÐÑÏÐÓÏÐÎÂÂÅÉÌÎÊÌÏËÉËÊÈÈÉËÍËÊËÊÉÈÇÆÃÃÁ¹´·º¹¡�‹ƒƒ…‚€}~~zxunj{yo‘Šb^fjnonkhiijlnoor{†Œ’—›ž�ššœœ™—”�†~vpljknpu~Œšž¡Ÿ›�t]\^]]]_`___^]^__^\[\]\YYXWVWUSSRRMJQZXTX]__[YWWYYVWhtpir‡ÎËÍÑÔÓÒÒÑÌÍÌÄÄÊÌÇÈÐÑÎÊÊÌËÉÉÊÉËËÌËÊÈÅÆÄÅÁÀ¿À»¾³˜�ˆ…„�€}}~~|xqm}‚l‡›s\cglnomggihknnqtw‡Œ“™›œ›šž¢¢Ÿ�›™•�‰…��„‰‘›¡¢¡Ÿœ”‡l\[[_`__^^__^__^^]\\]\[ZYWVYYYZZTQSSPLGIPVZ[^`ZXVVWUWbqpc†ÊËËÎÒÑÐÑѸ»ÓÏËÊÊÆÌÑÎÎÉÁÊËËËËÊÊËÌËÉÇÅÆÇÿÀÂÀ¿¾»¥�Ž‹‡ƒ�||}|{tq~„ll˜�gbfggjmidffilnqrsyƒŠ��“–šœŸ¡¤¤¥§§§¦¤¡žž ¢¥§¤¤£Ÿ˜�‚dYYX]^_^^__^_``_^]]^][Z[[XX[[WVWTQTTMIHPX[\\]a`ZXYYZZUZjq…ÈËÏÏÑÐÍÐϾ¼ÊÏÐÌÉÎÍÍÌËËÅÊÊÌÌËÉÉÊËÉÊÈÇÆÆÁ¼¼»¼ÁÀ½µ›��‹…ƒ€~~}zzxv~„o\zœ~cfedejjcddeijklosz„Š�”™� ¢¤¤£¡£¦§¦¦§¦¥¤¤¢¡¥¤œ’�z]VWWXZ\]^^^__````^]_][\\\\]][WTUWUQNLKIJMPRXYY^^\\[[^]X[gƒÈÌÌÕÓÐÏÉÊÏÎËÌÏÎÏÐÏÍÌÌÎÎÌËÌÌÊËËÊÉÉÌÊÈÇÇÆ¾¼»¼ÁÁ¿¿®•��Іƒ�‚‚~|x}„ta_‹–ldhfddgebbcceghijmu‡�“™œž¡¢¡ žŸ¡¥¥¥¤¥§¦¥¢¤¦ •�‰mVTUVWY[[Z[\^^\]^_`_`_]]]]\]\[YXWVTSPONLJIGDPYZ\_^^[Z[]][XƒÈÎÌÏÏÍÍÆÊÍËËÌËËÐÑÑÏÎÍÌÊËËÌËËÎÍÊÊÉËÊÉÈÇÆ½¼Ã¿½¾»¸¢‘��‡„ƒ„„ƒ‚€{}ƒudZoœˆgfgeabda_`_adeeeiox…ŠŽ’˜™› ¢¡ ¢¥¤¢ £¥¦¥¢ Ÿ—Ž‹�cVWWXZZZ[Z[[[\Z[[[_`a`^^_]Z[\YYZXTTTQQSTSSPINY[^^]]\Z\^][WƒÊÏÑÈÊÎÎÎÎÎÊËÎÍÌÎÑÐÏÍËÎËÇÊËÊËÌÌÌÍÌÌÊÉÈÇÅ¿Àþ¼¿»»²˜��‰†‰ˆ…„ƒ‚€�ve]Z… |cehd^^a][^^_cdfhhmv~‚„ˆ�”˜�ž £¤¢Ÿ››š™��›—”‹„ƒt\WWWXZXWWWZZXWVUVWY[]]\]\\Z[YVWXWWVTSTUVTROMP[^]\\\``bb]ZX„ÎÑÏÏÒÓÎÎÎÏÒÏÎÏÐÑÑÎÌÎÌÉÉËÌÊËÌËËÌËÍÍÈÇÊÉÇÄÄþ¼À¿½¼½§‘މ‰Œ‰………„ƒ�‚zh]Ue•›zbac\Z[ZUZ]]`acefhlsz~€„Š‘•–›žž�›™—–—•‘��Šƒ{eTRSUUVVTSTWXXVURQSTUUSUUX[ZYVTTUVXWXYWTTXTOKR]`\\`__`aa^\[…ÎÑÑÓÔÓÏÍÍÍÏÏÍÏÏÎÏÍÍÏÎÆËÐÎÎÎÎÍÌÊÊÊÇÂÆËÊÆÅÄÆÄ¿ÃÀ¿À¾¸›��ŠŠŠ„…‡†„‚ƒ{j`XSp•™~ic]XWVPOV\^]\`cdfjpvxy{€…�“’”––——˜—�Œ‹ˆƒ}wjPLPQQTVVUUUUWVSUUQOOPQQQSWWURTSQQTZYXXVSSWUROW`]Y[]\YU\aa_Z„ÎÍÌÒÑÐÏÐÏÎÎËÉËÌÏÌËÍÎÏÍÈÌÏÎËÌÌËËÍÌÈÆÆÈÉÆÃÃÃÇÄÁ¾¿Â¿¿­“�Œ‹‹ˆ‰‰‡„„ujbZTXkƒŽ‹|cYUUQJJSZ]\\]_cfgkruusw„…‡Š�’““�‹‹‡€{vqbQJILOSRQSTSQPMKLLLKKOUWVWXVSUXZXUWXVVVUUVURQV^_[\YOQXWZ]^]U„ÐÑÏÔÓÑÑÐÐÐÎËÊÈÈÌÍÎÎÎÏÎÇËÎÍÌÌËÌËÌÎÌÉÅÇÈÄÄÆÃÄÿ¾¿ÀÁ¾¼¤‘��Œ‹ŒŠ†„…{podZUTYclv†q\SRQJBDOY^][Z^`acinpnlosuy~‚„†‡†ƒ€zurqj`a\PHINLLMKJHFIGFFHJJMTSUUVWW[ZZ\[[XYYYUSUUSV^_YY\XRSX_][\XV„ÏÕÔÔÔÒÒÐÍÌÍËÍÎÉÈÍÏÎÍÍÎÊÊËÌÎÏÍÍËÌÍËÊËËÇÆÇÅÆÅÄÀÁÂÁÁ¼¾²˜’�Œ‹Œ‰‡ŒŒˆxphZQQX]][lxfXNOMF?BMWZYXX[]`beiigefgkopsvxyvqnkkeUSU^cZIBFIIEBABFHHKKKKLNPOMORXZYXVVVTVVXUSUVW_a\UVYYXYY\\[ZQKƒÏÕÔÓÑÑÐÉÊÍÏÎÎÐÎÏÏÎÎÎÌÌËËÌÊÉÎÎÉÈÌÌÉÈÊÊÇÆÁÅÉÆÇÀ¿Â¾½·§–’ŽŽ�ˆŠ””‘’ye[SQTZZ`_prcTLMJC<@LSUVVVWZ\^bdedbcddefhjjjgebmdDIIMZfbLAEFECDFDDGIJNPQPSSSSQSVUTRSWVUWWUUUX[aa\WWXWUUYWXWRMK„ÐÒÎÐÐÑÏÈÊÎÎÐÏÏÏÐÏÏÏÐÏÍÍÍÌÉÈÈÉÊÇÈËÉÆÉÉÆÁ»ÆÅ¿ÂÅÂÃÃÂÁÀ¼¸ ’’‘�ЅДЖ�oZRRRWWYcerk_QFHGA:>JRSSRRTVY\]_aaba_^`abba^_j€mA@CGMWejTACIKMNJHIIJLORRRUTPMMQRSUX[[Z\[ZZZ^`dec`_YUUTXWXVVTP„ÐÑËÌÍÑÒÏÍÏÎÐÒÐÎÏÍÍÎÏÎÎÎËÈËÆ¿ÈËÉÊÌÊÈËÇÀÇÆÅ¿ÁÆÃÄÄÂÁÀ½¼­”‘‘��‰�ŠŠ‡“…gOLRSVTZbhsh]KBED>9=HNONNPSUUTUW[\[Z[]]\ZZ`n~‰oA<>CIOWhnV@CHIKLMOPQLLMLKLJHHNQRRUVVXXXX\abbeggghc\YXYXWVTY\S‚ÏÔÌËÌÍÏÍÌÌÊÎÒÑÍÍÍÍÎÎÌÍÍËÈÌÉÆÉÇÊËÊÇÆÉÇÅÇÇÇÈÃÄÁ¾ÅÅÄÃÀ¼º²œ’‘•¢ �w‡‡…„ybJIPRPP[akrcZJ??@>9;CJLLNOSSQPRSRSTSRQPVbn{‚†oB<>@DMU\jrZ=BEJS[blu[<79AECFHILNPSQLQQNOSTQPRSSTTVWY__ZZ[\[ZVTUWVTVSTW†ÐÓÎÌÉËÉÈÎÌËÐÏÎÎÏÐÐÏÎÎÌÎÇÇÈÄÄÆÈÌÌÉÉÉÊÇÇÊËÇÅÃÃÇÈÇÄÅÿ¿·´¶¥Ÿ Ÿ¤Ÿœ‰‡�}ttvhN?FNMQ[bqkZWJ<7:=>AEIHHIJIHHIKKLPV\bhow~�†p@9=DGJSY_eqy]BA=?CEEFIMPRWUUXWUUTPORUVUQOPSY\YVX[[\WUVUTQORW[…ÏÑÌÉÊËÍËÍÍÌÏÐÏÏÏÏÏÐÏÎËËËÌÅÅÇÆÇÊÌÇÇËÌÉÇÈÈÆÃ¿ÃÈÈÆÅÆÆÅÀ¿½¹±©›“�ž¡”‚ŒŒxntvkTA@JMRZbobUTK>68GMTXan]OOKA969BGKMNMNMNOQTX\aekpv{~�…p?59;BLRY`fnt{‰¢šmRD=AHJIIIMPSWYXRIKMMMLMRTRVZURORTNNSYZ[_bbb`„ÎÏÍÌÈÊËÎÉÇÎÏÍÌËÌÍÍÍËÎÏÍÍËËÉÊËËÉÊÉÊ»½ÊÇÉÉÈÄÀÁÅÈÈÇɼ¨¹¾º¯§¢Ÿ”•¢£¦›‰„‰{ofhnueLABKTYcjYOKHB<9DLLKMNQSUVUMHMONMLNNMPOQST[[WV\^^a^VTSWƒÍÐÌËÉÌËÌÌÍÐÏÏÏËÇÌÍËÊÏÌÌÎÐÎÊÊÊËÊÊÈÈÄÅÈÇÉÇÅÇÆÇËÇÂÁÁº¯±©¤¤¤¡¢™”Ÿ¥ª Œƒ‰‡vnhdpogXF@GOUhoXMGDCA=>EMPRRSRSV[_chlrux|~€‚o>06:BMU\_grwxuKDdz†~\JA>DJLLNQRTVUNQXTRUSRSTRW^]\VSTT[^TZz�•h…ÍÌÉËÈÉÊÍÌËÊÊËÎÌÊÎÌÊÍÎÌËÌÍÌÌËÌËÉÆÄÇÎÊÇÇÈÉÉÇůĹ­©§ª¬£›–˜žŸŸ›Ž�‘˜˜‰„‡uxoebdkm`THCHQhdVPIDBA?AIORUTSUW]bfknruz~€��p=/6:BLW\dtƒ‰‡vM65BVlv|fOA;BJMLMPRRTX^\\`]VVXSNPSS[bixu_i—Íáâ©„ËÉÉÊÈÉÊÏÌÇÄÉËËÉÉÌÌÊËÌÌÌËÉËÍÈÅÈÆÅÃÆÇÅÆÆÆÆÂ¹´±®¬¨¦¥žšœ�—�—›œšŠŠˆzsz€ˆ‰|torkacgikcZOJXdVRNIECABHLOQQTX[^cgjntx|}€rA-49BMVayˆŽ�ŒmKIB;6;_�—’oKB=EJILQQSTPPONNNTT^xŒƒs‘¦¦’m„ÄÏÔââÉ‚ÈÊÈÇÄÉËÆÈÇÈÌÎÌÉÆÅÃÇËËÇËÌÊÌÎÈ¿ÃÄÂÂÄÂÃý´­ª¨§«®°¬£™–™�ž‘‹‘›ž�Œ—†z‡�†‰knytid`bdins‚ˆˆwdYNFBCEFHKOSW\`bcglquxz|~~{rG-29BP]vˆŒŒ‹„_CDIMH<;Uy’�‹†hC?ACEHKMOQUY[]\WVU<+0):]gbUO\adkG8<<<=<>?8/.3JswZwœtcagsz~~xpnwwxyz}|x{~~}}~~tƒˆˆ‰‰ˆ‰‰‰‡ˆ‚y{zyxtpwyvtvvvxxxxyyxvspsvuv|€|yywvvwwvzywrqvwxvvvuvysswxwutywpstuuytruruuqv†’’“’”“““‘‘‡yzwssvxvtuvvvwxxyxxvrrstvx|~|zyz{{{{|{ywzyvrswxwwuuuuxttwxwvuyvpsutwysttuvtu€ƒ„ƒƒ‚€�€€�wtsuxwvvvvwwwwwxwtsrsuvz}|{yx{}~€€€€}{{xvttwxwuuvtvxstwvwuuxuqutswxrsutuu~||||zxxyxvsstvwxxvvvwwwwvutrstuwz}|zyyz}€€€€€€€~|ywsswxwvuutvxstwwvuvzuruttyxrttsv€}||zxxxtrsvwstvxwuuvwvvurrstux|}{yyz{}€€€€€€€€€€~yvsswwvvuutuwruxwvtwzsqtttzwrust}|zvstsswvusruwxwutuvtrqrtvy||zyyxz}€€€€€€€€€€}yvsrvwwuutrtwruwvuuxxsrsuwzurtu{vtrswwxvuusswxwwussqrttvz||zxxyy{~€€€€€€|ywsswxvuttstvsuvvvvxyrptvxztqu~vvtwxyvtuvurtwxwtqprsuxz}{yxxxxy{€€€€€€€€€|zyvtsvwwutsruwrtvwwvxyrsuvxxrt~{xvywvuvvvssuwurqqtux|~|yyyxyyx{€€€€€€€€€€zyzwtruwvusstvvruwwwuyyrtvvxxt€zutwwvuvutsttqqqru{}|{yyyyxyyy{€€€€€€€€€€€€€~xyzwtquvvusssvvsvwwvuywruuvxx€ytuwwvvvusqpqqruz}|yxyyyyyyzyz€€€€€€€€€€|vy{xsruvusrttxvrvxwvvyvrtutxxtvwwvvuropqsuy|{yxyyyxxyyyzz}€€€€€€€€€€xvy{wtruutssuvwusvxxwvzvrutwwuwwvtrpqrsv{|zxyyxyxxxyyyzz|€€€€€€€€€€|vuy{wtrtutttvuvvrvxxxwztrtw~wvxurpqrtwz|zyyzzxwxyxyyyyzz~€€€€€€€€€€~€xuvy{xurtvvuvuswuswyyxxytrw}vvsrqsux{{yxyyzzyxwxxyyyyyx{€€€€€€€€€€€{uvux|xtptwvwuttwusxyzywxru}trqtw{}zxxxyyy{yxxxxxyyxutx€�€€€€~€~vuvvy{wsquvwvuutwusxzzwwws|rtx|}|ywxxxxyyzyyxxwxvttuv}�€€€xuvvvx{xtruxwvtttxttyzxuvy|x|}{zzyxxxwyyyyyyxvuttuvvw~�€€€€€{uuvvux|xtruwvustwwruyxwuw}zzzzyyxxxxyyyxxustuuuuux}€€€€€€}vuuuvvx{yurtwwuvxwustwwvw{zz{{yxxxxxxxvtsttuuuwz|}|€~~~~}~~~€yuvvvvvx|zuruwuvxvtvrswvx|z{{zyxwwwwusstuutux{|}{z{€~}|zywvvuuuuuvwwxyzz{{||~{wwwvvvwx{yusstvxvttvrsvy{z|zyyxwtsssstttwz|||zz{z|~|zwurponmljkkkkkmoommoppqrstuvvvwwwwx|zurswwvttuwrsz{z{zxvurqstssuw{}|{{{{{{z}€€}{vsqookgghhikmooooprpmigghjlqqrrrrstvwvx|yuruxvuuuvvqv{yxusrsssttw{|{|{z{{{{zz{~€€|ysnjkiggefkoqrstuuuvtsttqlgdchoqrsttsssvvwx|zusvwuutuutt~wusstssuwz|||zyz{{{{zz{z{~}{xrngcbccehmprstrqqqqqsssstuqojdfnpqrsttttvwwxz}zvttttvutsv|utstuvyz||{{zzzzzzz{zzzzz{zwrjgkdabdinqrppqrpppoppqqppqqpqqlgkqpqrsstvwwwxyz|zvsstvwusu|ttvx{{|zzz{zzzzzz{z{zzzzyxunfbdkhdinrrqrqrrsrssrqqrqpppppprqjkppqrrsvwwwvwyyx{zvssuvwtu|xz{|zyzzzzzz{zzzzz{{zy{zxsmgdbbknnstrqrstuwwwwwussssrrrqrqrrkjqrrqstuwwvvwxwwy|zvstwwuv€|zzzzzyyzzzzzzzzzzzzzyyxrpmfcbcmtsssrstvwxzzyyxvutsssssstutsnlrrruwvstwvvvxwxxx|zvstxuv{zzzzzyyzzzyyzzyzzzzyxwroskeeeksttsrrtvvvvvwxxxwvuttttstuwwwsqrsuz{xwttwvvwwwwwx{zvrtuw|z{zzzyzzzzyzyyzzyzzyxuoqsjefmtuusssuuvuuvuvvwxwwvuuuuttuvvxvstwwxz|zwuuwvwwwwvwxzyuqsx|z{zzyyzyyyzzzyyyyxxuutpqsngltxutstuwvuuttuvuvxwwvuuuutuuttuvuvxwvxz|ywvtuvvvwwwvwyzuqt{yzzyyzyxyyyyzyxvvuvvwvrrtqkpvxuttuvwvuuuuuvvvxxwvvvuttuvuttuuvxxvvxyzywusvwwwwvvwvzxtt{yzyyyyyxyyxwvuuuvvwvuuusrrosxxuuuuvvttvvuvwvwwyzwvttvvvvvusstvwxwvxxxzzwusvwwwvwwwwywv{zzzxyxxwvutuuvwwvvvuvvvutssvxxuuvuuttutuvwyzywxzxuttvxxwwxvrtvwwwwwwwxzzwvsuwwvvwwvvxzzyyywwvttuvvvvvvvwwwvwwvuusvxyxvvvutttttvxyzyywwwutsrvyyyxxvsuwwwwvvwwwwz{xvsuwvvwwvvvyzxwuttuuuvvuuvvvwwxwvwwwvuuuvxwvuuutsstuuvvuvwwwusttsuyyxxzwsuwwvwwwwwwwxxzyvttvwwvwwvx~vuuuuuvuuuuvwwvvvwwwwwwwvtrstvvwwuutsrstssttuvvtsssttuxywuwyuuwvwwwwwwvwwwwzywttvvvwwwx}vuvuvwzzxuvwvwvwvvvvwvvvwrqtstvxvutsrrrrssttsttsstttttwyxwwxwuvvwwvwwwvvwxwwz{wutuvvwvx}wxz{{zzxyxuxzzxxwvwvwwvvwqqtstvxuuusrsrrtstsstsstttttsuyxwxxyvuvwwwwwwvvwxwvx|{vusuvwvy}}}{{zywwxvvy{{yxwwvvvwvwsqtqqvwutttssrsttssstssuvutrqrwwvxxyzvtuwvvwvvwwwwwxyzzwtsuvvx}|{{{zxwxwwvuwz|{xxvvwvvwtrsosvvvuttsrsstsstsssuvvurooptvuwxyyxwttvvwvvvvwwwwyyzzwusstx}{z{zywwwvwvtvyy{|{ywwvvvvsrotuvwvuttssssttssrtvutsqpnouwsuwxyvwxutuvvvwwwvvwxxwxyxtrsw€}{{{{ywxwuuttuyyxy{|zxwwwvusqssuxwvutttsstttssuvttssrppuwssuwyvtwxvtuvvvwvvwwxwwxxywtrt€|{{|ywxyxvvvvuvzzyxz|{zyxvuttssuwwwuutuuttttsstvuvwutssvwsrtwyvtuvwvttuvwwwwwwwwwxwyxtt€}|||zxxxxwwwvutwzzxxy{{{zxvuutrtwxwuuttutttsrtuvwwwvuuwyxtrsvxvuttuwwutvvvuvvwvwwwwwyxv}|||yxyxxwwvvvuvyzyyxxx{|zwwwurrvxwvttttttststuvwwvutuxzyvssvxuuuuuuuxwttvvvuwvwwvvvvwx€~||{xyyxxwvuuvvuwzyyxxxxxywwwvsquywvtttttssttuvvvvvvtuwyxxutvwuvuuutsvxvttuuuuuwwwvvvux€~}|zxzzyxwwwvvvuvyzyyyyxvvutvwtquxxvutsststttuvvvuuvvvwxxwwvvvuutuutttuwwttuvvuvwwvvvux€}|{yzzzyxxwxvvvvvwzzzyxxvttsswwstwxvutrrstvuuvvuutuvvvvvwvvwwvtuuuuttttsuwvtuvuvvvuuutw€}|zx{zzyyxwwwwwwuvy{zzzxuttstxyutwxvutqqruvuuuvwwxxxxxxwutvwxutuvuuttutstuvwutuvvuutttw€}|zz{{zyyxwxvwwvuuvzzzyxvtsstxzvsuxvttqpqsttuxzzz{zyxz{zwttwxttuuuuuututtstvxtsuvvutstv€|{y{{{zyyxwxwwwvvvvxyzzywvtsuxywtuwwutspoqrstvwwwxyyy{zyytrvxttuutuuuutttttsuvutuvutssw€}{z{{{zzyxwxxxxvvvuvxzzxwuuuuvwxttwxvttrpoqrrsstutvuvxyyxurvwttttttuuuttutssstvutuutssv€}z{{{{zzyxxxxxxwvwwuwzzyxwwvvvwzvtuwwutsqponoqrsuuvvuvwxwtsvurssttssttuututuutstuussssv€{y{{{zzzyyxxwxxxwwvuuyzzywvvwv|wttvwvtrrpnlmortuuuutttutqsvtstttssstttsttuttttrtwtrssv€{z||{zzzyyyxwxxwwwwvvwzzzyvvxwƒxtsuvwusrpnklmpsttutttttrotvsstttssttsssutsttttrstuusqu€{z||{zzyxyyxwxwxxwwvvvxz{{yyzw†{tssuuutsqommmlnqrstutsqnntutttttssssssssssssttssstttss{{}|{y{zyyyxxwwyxxwwwwwz{{{{{yˆ‚vttttttssrpnnlkloprsrpomqvutttttttsssssrssttttstsssstu|||{zzyzyyyyxxxyyxwwwwvx{{{{{z~†ˆ}tttsutstsqqomlkmmnoppootvtttttttsttsrsssstttttttrrrqs|{{zzzzzyyyyxxxxyxwwwwwvz{{{{{}„Š…wrstttstutsqonlmmmnnoprvurtttusttstsssrsssrsssstsrrqr€}||{{zzzyyyyyxxxyxxxxxwvx{|{{{|ƒ‰Š�ussutssuutsrpomnooopruwuttttttststsssrsssssrrrtsrrqr€}|{{{zzzzzyyyyyyyxxxxxwwwy|||||‚ˆ‰Š‚xtuvutttutssrqqrqqstwywuttttttsssssssssssssssusrrqr€}|{{{zzzzyyzyyzyyxxxyxwwvw{||}‚‡ˆ‰‹†wtuvvttuuttssstsstu{ƒ�yuututtttttsssrssssrrsssrrqr}|{{{zzzyyzyyyyyyxxxxxwwvwy{|~ƒ‚ƒ†ˆŠŠssuvwutuuttsttuttu}ƒ†…}wvvuttttttssrsrssssssrrrqr|z{{z{zyzyyyyxxxxwwxwwxwwwwz|€ƒ„‚‚†‰Šˆ{qtuwvutsssttuutuw}€‚‡ˆwvuttsstsrrrsrsssrsrqrqpq}zz{{{yyzyxxxxwxxxwxwwxwwvvx‚‚ƒƒƒ‚†ˆŠˆyqtwvutstttttttvw|…‰‰€vttutsssrtsrrsrrqqrqqqqr€|{{{zzyyzzxxxxxxyyxxvwxwwww|ƒƒ‚‚ƒ„„‚ƒˆ‹†wruwvsrrrsssstvwz}€…‡Šˆzqsutsssstsrsrqrrprrqqqq€|{{{zzyyyyyyyyyyyyyywxyxxz}�„ƒ„ƒ„„……‚‚‡‹ƒuruvutrrsrsstuw{}€…ˆˆŠ‚trtttsssssstsrrrrrqnqxw€|{{zzzyxyyyyyyyxyxxyz|~€‚‚‚„„„„„„„†…„‚„†|qtuutsssrsssuwz}…†…ˆ‚vuqttttstsrrqqswtkbl}ƒ�|{{zzyyxxxxxyxwwwy{~��‚‚ƒƒ��ƒƒ„ƒƒ„…†……„ƒ�~vrrsssssssssuvy|~ƒ…‡ˆ}ttuutvwuussj_ht}kX]mohf|||{zyxyxxyyxxx{~�‚‚‚‚‚‚ƒƒ��‚„„…ƒ‚……„ƒ„ƒ‚‚ƒ€zttttuuuuvvwy}~‚ˆŠ„|vvvvxw{|{vuhZhoh^Z[[ZY\xjmhikjhgmkv„‚‚‚‚„…†…„„„„„„„„ƒƒ„„„…††………„„‚‚‚ƒ„„ƒƒ„„…„…„„ƒƒ„„„……„„„„„„„………………†††…„††ˆ†ƒ„sPQNQKMNOSUg†‚ƒ„……†………„„…„„„„„„…………………„„ƒƒƒƒ‚‚‚ƒƒ‚ƒ„…„„„„„ƒƒ„………†…„„ƒ„……††……†††††„†††ˆ†ƒ}ststtusuvuy„ƒ………„…„……„„„…„„„………††…………ƒƒ„‚‚��€€�€��ƒ„„„„…„ƒƒ„„…„††„„„„„……††…††††…………†‡ˆ†�ƒƒ‚�‚ƒƒƒƒƒ„„…†…„ƒ„„„„ƒ„„„„„„……………„„„ƒƒƒƒ�€��€€���€��‚ƒƒ„„„ƒƒƒ„„……………„„„„…††………†…††„…‡ˆˆ€�‚‚‚ƒƒƒ„…………††„ƒ„„„„„„………††……………„„„ƒƒ‚�€€€��€€���€€���‚„ƒ„„ƒ„„„……††…„„„„…†…†………†…†…„†ˆ‡€�‚‚ƒ„„…†††……†…ƒƒ„……„„„…†………„„„„„ƒ„‚‚�€��€€���€€�€�€�€��‚„„„„„„„………†††„„„„†…††…††……‡……††��ƒ„…†……………„……ƒƒƒ„„………†……„…„„ƒ„„ƒƒ‚��€€€€€€€€�€€€€���€€€�ƒƒƒ„„ƒƒ„………†‡……„„………††…††……†……†�ƒ„…„……„…„„„……ƒƒƒ„…†…………„„„ƒƒƒ„„ƒ‚�€�€€€€€�€�€€€���€€€€€€‚ƒ„„„„ƒ„„„…†‡†…„„………††……†…„…‡…†�ƒƒ„……„„„„„……„ƒƒ„…†……„ƒ„ƒƒƒƒƒƒƒƒ‚��€�€€€€€��€€���€�€€€�€€€‚„„„„„„„…†††††………„„………†††…„…‡„€‚„……„„„„„„……„„…………„ƒƒƒƒƒƒƒƒƒ‚‚‚���€��€�€�€�€€�����€€€�€€€�ƒ„„„„…„„…†††††…„„„„…………………„††€‚„…„ƒ„„„„„†…†……„„ƒƒƒ„ƒƒƒƒ„ƒƒƒ‚�€�€��������€€���€€€���€€€€‚„ƒ„ƒ„„„„…††††………„„…„…†††………†�ƒ……„„„„ƒ„…†…†„„„ƒƒƒƒƒƒƒ„ƒƒƒƒƒ�€€€���������€���€��€���€€�€€€ƒ„„„„„„………††……†…„„„„……††…………�ƒ…„ƒ„„„……………ƒƒƒƒƒ‚ƒƒƒƒƒƒƒƒƒƒ‚€€€€����€€��€�������€€€�€��€€‚„„„„ƒ„„…………†…†……„„„„„……………„�„„ƒƒ………………„‚‚ƒƒ‚ƒƒƒƒƒƒƒƒƒƒƒ‚�€€€���€€€�€€�€�����€€�€€€€€€€€ƒ„„„ƒ„„……„………†††…„„ƒƒ„…†………‚…„„………„„ƒƒ‚‚‚‚‚‚ƒ‚ƒƒƒƒƒƒƒƒ‚‚€€€€€����€�€€�€€€€�€€€€€€€€€€€‚„„„ƒƒ„„………„„†‡‡†…„ƒ‚ƒƒ…†…„‚…………„ƒƒƒƒƒ‚‚‚‚‚‚‚‚ƒƒƒƒƒ‚ƒƒƒ€€€€€€�€€�€€€�€€€€€�€€€€�€€€€€€€�„„„„„„„„…„„„…†‡‡‡…„ƒƒ‚„††…‚…„„ƒ‚‚‚ƒ‚‚‚‚‚‚‚‚‚ƒƒƒƒ‚„„„„‚€€€€€€€€��€€€€€€€€�€€€�€�€�€€€€ƒ„„„„…„„„„„„……††‡†…„ƒ‚ƒ…††‚„ƒ‚‚‚‚‚ƒ‚‚‚‚‚‚‚‚ƒƒƒƒ„„„„„„‚€€€€€€€€�€€€€€€�€€€�€€€€€€€€€€€€‚„„„„„ƒ„„„„„„…†††††…„‚ƒ„……�‚‚�‚‚‚‚‚‚‚‚‚‚‚‚‚ƒ„…„„„„„ƒƒ�€€€€€€€€�€€€�€€€€€��€€����€€€€€€�„„„„„„ƒ„„„„…„…†…„††…ƒ„„…„€�‚‚�‚‚‚‚‚‚‚‚ƒƒƒ„„…„„„„ƒƒ‚‚€€€€€€€��€€��€�€€€�€€�����€�€€€€€ƒ„„„ƒ„„ƒ„„„……„…„ƒ†††„„„„ƒ���‚‚‚‚‚‚‚‚‚ƒ„…„„……„„ƒ‚‚‚‚�€€€€€€€€�€€��‚ƒƒ„………„……„„„ƒ‚‚‚‚‚�€€‚ƒƒ„„„„ƒƒƒ„„„……„ƒ…†‡…„„„ƒ�‚‚‚‚‚‚‚‚ƒ„„………„„„ƒƒ‚�‚‚‚‚�€€€€€��€�‚„…‡‰‰Š‹‹ŒŒ‹ŒŒŒ‹ŠŠ‰Š‰ˆˆˆ‡††……„„„„„ƒƒƒ„ƒ…†„„…†††…„„ƒ€‚‚‚‚‚ƒƒ„……„……„„„ƒ‚‚‚‚‚‚‚‚�€€€€��‚…‡ˆŠ‹Œ�����Ž�ŒŒŒŒŒŠ‹����Ž�ŒŠŠ‰‰ˆˆ‡†…ƒƒƒ„ƒ„…„ƒ…†††…†…„€�‚‚ƒ„„„„„„„„„‚‚‚‚‚‚‚‚‚‚‚‚�€€€€�ƒ…ŠŒ�Ž��‘����Ž���ŽŽŽ��ŒŒŒ�‘’‘�‹‰‰‰‰ˆˆ‰‡…„„„„„„„„„…………††„€‚ƒ„„„„„„ƒƒ‚‚‚‚‚‚‚‚‚‚‚‚‚‚�€€��ƒ‡Š�‘“’’‘�������’’’“““““’��‘�‘’�ŒŠŠ‰‰ˆˆˆ‡„„„„ƒ„„„„„†…………‡…�ƒ„„„„ƒƒ‚�‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚ƒ„†‹�Ž‘“’‘�ŽŽ�����‘‘’’“““”””“““’�‘‘�ŠŠ‰‰ˆˆ‡…„„„ƒ‚ƒ„„„„†…ƒ„…‡†‚„„„ƒ‚�‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚ƒƒ„…‰�‘’��‘�ŽŽŽ���ŽŽ����‘’’““““’’’‘��‘ŽŠŠŠ‰ˆ‡…ƒƒ„„„ƒƒ„„„„……„„„††‚‚‚��‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚�‚‚‚ƒ„†‰Ž‘’’�Œ�����Ž��Œ‹Œ�Ž��‘‘’’’’‘‘‘��Ž�ŽŠŠŠŠ‡……„„„„„ƒƒ„„ƒ„„……„„„…����‚��‚‚‚�‚‚‚‚‚‚‚‚‚‚‚‚ƒ„†‡Š��‘‘�Š�ŽŽŽŽŒ‹‹‹‹‹Œ��Ž��‘‘’‘‘����ŒŽ�ŠŠ‰‡„ƒ„„„„„„„„„„ƒƒ„„„„„„„€�‚�‚‚‚�‚‚‚‚‚ƒƒ‚‚‚‚‚‚ƒ‚ƒ†ˆ‡Œ����‹ŒŽŽŽ��Œ����Œ‹ŒŒ�Ž���‘���Ž�ŒŒ‹ŠŠŠ†„ƒ„„„„„„„ƒ„„„ƒƒ„„„„„„„€��‚‚‚‚‚‚‚‚‚‚ƒƒ‚‚‚ƒ‚‚‚ƒ„ˆˆ‡ŒŽŽ�ŠŒ�ŽŽ�Œ����ŽŽŽŒŒŒ��Ž������Ž�ŒŒ‹Šˆ…„„ƒ„ƒƒ„„„„„„„„„„ƒ„…„……„€��‚�‚‚‚‚‚‚‚‚‚ƒ‚ƒƒƒƒƒƒ„…ˆ‰‡Š�Œ‰‰Œ������Ž������ŽŒŒ�Ž�����ŽŒ�ŽŽ‹ˆ…ƒ„„„ƒƒ„ƒ„„„„„„„ƒƒ„„…„„„„€�‚�‚‚‚‚‚‚‚‚‚‚‚ƒƒƒ„„„„ƒ…‡ˆ‡ˆŒŠ‰Š‹Œ�ŒŒ��Ž�Ž�����Ž���Ž���Ž�Ž����‰…ƒ„„„ƒƒ„„ƒ„„„„„„„ƒ„„„„„…„€���‚‚‚‚‚‚‚‚ƒ‚ƒ„„„ƒƒƒ„„„…‡‡†‰Šˆ‰‹ŒŒ‹‹�Ž�ŽŽ�‘�����‹ŒŽ��Ž�Ž�����‰„ƒƒ„„ƒƒ„ƒƒƒƒ„„„„ƒƒ„ƒ„ƒ„…„€�‚�‚‚‚‚‚ƒƒ„ƒ„„„ƒ„„ƒƒ„„„„„††ˆ‡‡‰‹Œ‹‹Œ����ŽŽ�ŒŒŒŽŽ‹Œ�‘‘Ž�ŽŽŽ���ˆƒ„ƒ„ƒƒƒ„ƒƒ„ƒ„„„„ƒƒ„„„„ƒ„…€����‚‚ƒƒƒƒƒƒƒ„„„„„„„„„„„„…ˆˆ‡‡‰‹‹‹‹Œ�Ž�Ž��‹‹‹ŒŽ����‘’���Œ‹Š�ކƒ„ƒƒƒƒƒ„ƒƒ‚ƒƒƒƒ„ƒ„„ƒ„„„„ƒ€�‚‚ƒ„ƒ„„ƒ„„„„„„„„„„„…„„„„†��Œ‰ŠŠ‹ŒŒ�ŽŽŽŽŽŽ���Ž�Ž�‘‘‘’����‹ŠŒŒ…ƒ„„ƒ„„ƒƒƒƒƒƒ„„ƒ„„„ƒƒƒƒ„„ƒ�‚ƒƒ„„ƒƒƒƒ„ƒ„„„„„„„„„…„„ƒƒ‹”’�‹ŠŠ‹Œ���ŽŽŽ��������‘’‘’“‘��ŽŽ‹Š‰„ƒ„„ƒ„ƒƒ„ƒƒƒƒƒƒƒ„„„„ƒƒƒƒƒƒ�ƒƒƒƒ„ƒ‚‚ƒƒƒƒ„„„„„„„„„„„ƒƒŽ—‘Ž�ŠŠŒ�������‘���‘‘�‘’‘‘’““�Œ‹ŒŒ‹‰„ƒ„„ƒ„ƒƒ„ƒƒƒ‚ƒƒƒƒ„„„ƒƒƒƒƒƒ�ƒ‚‚���‚‚‚ƒƒƒƒƒ„„…„…„„„„ƒƒŽ˜’�Ž‹Š�ŽŽ�‘‘’’’’’’’‘’’’‘’“•–“�ŒŒ��‰…„„„„„„ƒ„„„ƒ‚ƒ„‚ƒ„„„ƒ„ƒƒƒƒ�€€���‚‚‚ƒƒ„„‚�‚„„„„„„„ƒ„ƒ�—’��ŒŠŒŽŽ�‘’“’“““““’““’‘’•™˜˜’ŽŽŽŽŠ…„…„„„„ƒ„„„„ƒƒƒƒƒƒ„„ƒ„„„ƒƒ€€€���‚‚‚‚ƒ„„„‚‚‚‚ƒ„„„„„…„Š•’““ŒŠ‹���‘’“““””“’’’‘’’”™›š™”���Ž‹†‚„„„„„„„ƒƒ„„ƒ‚ƒƒƒƒ„„ƒ„„„ƒ€€���‚�‚‚‚ƒ„„…ƒ�‚��‚ƒ……………ˆ‘”•’�ŠŠ‹�Ž‘‘’’“””““’‘’”•—™››š”���ŽŒ‡„ƒ„„„…„ƒƒƒƒ„ƒƒ„‚ƒƒƒ„„ƒƒƒƒ€�����‚‚‚ƒ„„„…„�‚‚‚‚‚„„………†�””��‹‰‹ŒŽ�‘‘’’““““’’”•””•—˜—‘�‘‘�Œ‡……„ƒ„…„ƒƒ„„„ƒƒ„ƒƒƒƒƒ„„„„„€����‚ƒƒƒƒ„„„„„ƒ‚‚‚ƒƒ‚‚ƒ„……Š’“‘‘�ŠŠ‹Ž���‘‘’’“’’’•–“‘‘’“’�Ž‘‘�Œ‡………„ƒƒ„„„„„„„ƒƒƒƒƒƒƒƒƒ„„„€€����‚ƒƒ‚ƒƒƒƒƒ„‚�‚‚ƒ„‚‚‚„„†Ž‘‘’ŽŠ‰‹�Ž���‘�‘’’’‘’”“’‘‘�ŽŒŽ���‹†……………ƒƒ„„„„„„„ƒƒƒƒƒƒ‚‚„……€€����‚‚‚‚ƒƒƒƒ„ƒ‚�‚‚‚ƒ„„ƒ‚ƒ†ˆ�’“‘‹‰ŠŒ�Ž����‘‘�‘‘������Ž‹ŠŒ��Ž‹†……………†…ƒ„„„„„„ƒƒƒƒƒ‚ƒƒƒƒ„€€���‚‚ƒƒ‚ƒƒƒ„„ƒƒ‚�‚‚‚ƒ„„„„††‹‘”“�ˆŠŒ�Ž����������Ž�����‹‰‹‹ŒŒŠ…………†…††…ƒ„„„…„„„„ƒƒƒ‚ƒƒƒƒ€€���‚‚ƒƒƒ„ƒƒ„„„„ƒ��‚‚‚ƒ„„†‡ˆ‰�“”�ˆŠ‹Œ�������ŽŽŽŽ�Ž�Ž��‹ŠŠŠŠ‹‰†……………†††…„ƒ„…„„„„ƒ‚ƒƒƒƒƒƒ€€€�‚‚‚‚ƒƒƒƒƒƒ„„„„‚���‚‚ƒ…‡ˆ‰ŠŠŒ�Ž‰ŠŒŒŽ����ŽŽ�Œ�Ž�’“‘���Œ‹‹ŠŠˆ………†……††…††…„„„„„„ƒƒƒ„ƒƒƒƒ€€��‚��‚‚‚ƒƒƒ„ƒƒ„ƒƒ����‚„†ˆ‡‰Š‡ˆŒ�ЉŒ�Ž�‘�������“““””“’‘���‹Š†………†……………†††…„„„„„„ƒƒƒƒƒƒƒ€€��‚��‚‚‚‚ƒƒ„ƒƒƒƒƒ‚�€�‚„…‡ˆ‰Š‡‡‹�ЉŒ�Ž�’’�ŽŽ��‘“’�Ž�‘��’‘ŽŒŠ†………………………††††…„ƒ„„„ƒƒƒƒ„ƒƒ€��‚‚�‚‚‚‚‚ƒƒƒƒƒƒ„ƒ„ƒ���ƒ…†‡‰‰ˆ‡ŠŽ‹Š‹�Ž�’“’���’•˜—“�����”���І………†…†……………††††…„„„„ƒ„ƒ„„„€��‚‚‚‚‚‚‚‚ƒƒƒƒƒƒ„„„„‚�‚ƒ…††ˆˆˆˆŠŽ�‹Œ�Ž��“’‘��“˜›œ›š˜–••“ŽŒ�Š…††††††††…††…†††††……„„„„……„€€�‚‚‚‚‚‚‚ƒƒƒƒƒƒƒ„„ƒ„‚��‚ƒ„…††‡‹Ž�ŽŽŽ�ŽŽ�’’“”•”•–˜šœ��œ™’��Œ‰†††††‡‡‡†††……†††††††„„„„……ƒ€��‚‚‚‚‚‚ƒƒƒ‚ƒƒƒƒ„„ƒƒƒ‚�‚ƒ„††††�š�Ž���Ž���’•—™˜”‘‘“–˜˜—”ŽŽ�Žˆ†††††‡†‡††††††…††††‡†„„„……„€���‚‚‚‚ƒƒƒƒƒƒƒƒƒƒƒ„ƒƒ‚��‚ƒ„…„„’¥™Ž‘“’‘��‘‘”—š™–’������ŽŽ‘”އ†††††††††‡‡‡‡††‡†††‡†…„„„…„€��‚‚‚‚‚ƒƒƒƒƒƒƒƒƒƒƒƒ„ƒƒ‚���‚‚‚‚“«¢’’”””’‘��’–˜™™–“’������“”Œ††…………†††††‡‡‡‡‡‡‡††††††„„„„���‚��ƒƒ‚ƒƒƒƒ„„ƒƒƒƒƒ„„„ƒ�������•®ª™’”––•“’‘‘”•—šš˜—•“’“•–•’Š…†††…†††††††‡‡‡‡‡‡†††‡‡‡††„„���‚‚‚ƒ„‚ƒƒƒƒ„ƒƒƒƒƒ„„„„„ƒ�����’¯¯¤””———–”‘‘‘’•˜™šššš™™š—“Žˆ††††…†††††‡††‡‡‡‡‡‡‡†‡†‡‡‡‡…€��‚ƒƒƒƒ‚‚‚ƒƒƒ„ƒƒƒ„„„„ƒƒ„�����Š«°¬�“˜™™™–“’��‘”–˜™›š™™š–�Ї†††††……††††‡††‡‡‡‡‡‡‡††‡‡‡‡‡€��‚ƒ‚ƒƒƒƒƒƒƒƒ„ƒƒƒ„ƒƒƒƒ„„ƒ�����„¡°¬ª›˜š™›š—“‘�Ž�’•˜™——˜—’Œˆ‡‡†‡‡‡‡†††…†‡‡‡‡‡‡‡‡ˆˆ‡†‡‡‡ˆ‡€�‚‚ƒ‚‚ƒƒƒƒƒƒƒ„„ƒ„„„„ƒƒ„„„‚����€•¬«©¨ ›š›œ™–“�Ž��‘”•”””’�‹�Ї‡†‡‡†‡‡†‡‡‡‡‡ˆ‡‡‡‡‡‡‡‡†‡‡ˆ‡€�‚‚ƒƒƒƒ„ƒƒƒ„ƒ„„ƒ„„„„„„„„„ƒ‚���~‡¢¬§£§Ÿ™››š˜–’�Ž�Ž�����ŽŠ’ œŽ‡‡‡‡‡‡‡†‡‡ˆ‡ˆ‡‡‡‡‡‡‡†‡‡‡‡ˆ‡€�‚‚ƒƒƒƒƒƒƒƒ„ƒ„„ƒƒ„„„„ƒ„„„„‚��€|~‘¨©£¡¡ššš™˜–”’�ŽŽŽŽ�Ž��‹—¥¨£“ˆ‡††‡‡‡‡‡‡‡ˆ‡‡‡‡‡‡†‡ˆˆˆˆˆˆ€‚ƒƒƒƒ‚ƒƒƒƒƒ„ƒ„„ƒ„„„ƒ„„„„„„ƒ‚€}{{‘£¥ŸŸž˜™˜—–”“‘������ŽŽ‹–¤§ª§—‰†‡†‡‡†‡‡‡‡‡‡‡‡‡†††‡ˆ‡‰ˆ‡€�‚‚ƒƒ‚ƒƒƒƒƒƒƒ„„„„„„ƒƒ„„„„„„‚}{{{|}ŠšŸŸŸœ———–““’‘������Œ‹•¢¤ª­©˜ˆ†††‡†††‡‡‡ˆˆ‡ˆ‡‡ˆ‡ˆˆ‰ˆ†€�ƒƒƒƒƒƒƒƒƒƒ„ƒ„ƒ„„„ƒ„„„„„„„„€{{{{{{|„‘œ�Ÿœ–•••”“’’‘��Ž��Œ“ ¥ª¬­¦Žƒ†‡‡††‡††‡ˆˆ‡ˆˆ‡ˆˆˆˆˆ‡‡‚ƒƒƒƒƒƒ„„„ƒ„ƒ„„„„„„„„…„ƒƒ‚€}zz{{z{{|€‹—�Ÿš••••”“’‘���ŽŽŒ“Ÿ§««¯¯–‚ƒ„…†‡‡‡‡‡‡‡‡ˆˆˆˆˆ‰Šˆƒ…€‚ƒƒƒ„„„„…ƒƒ„„„…„„„„ƒ‚�€~~}}|{z{{{{{{{|ƒ�˜š”””••“’‘��ŽŽ��‘�¦ª®»·•……„ƒ…†‡‡‡‡ˆˆ‰‰‡…‡�—��|€€‚ƒƒƒ„ƒ„„„„„„„„„…„ƒ‚}|||{|||{{{z{{zz{z{|�‡ˆŒ�“”“’����ŽŽ�’ž¥¬ºÂ¯Œ…††„ƒƒ‚…†‡…Ž™‘†�’ œŽŒ‘‘�‚ƒƒƒ„„„„„„„„„„ƒ‚€~}|}|{||{{||{zz{|{{{|{|}}}~ƒŠ�‘�������Œ�›¡¬³³Ÿ…†…†……„€€…„�ž“�“›�œœœ�š+×þùûþþýûüþþþþþýûþþþþþùîÞßááßÞááàßáåãÛ·˜žªÄØÚ×ÖÚÜÜÙØÙÚÜÙÜÜÜßÜÛÝÝÞÙÕÔ×Ûß×ɪ‘’“‘Œ�†vu‚ªÙàÞÞßÝÝÖÐÙÙÑÐÒÔÔÚËŠs‡„y€µÑÏÑÓÔÙ×ÔÖÕÑÎÒÑÉÀÀʳ‰‡¸ÛÒÔÕ×ÛØÒÏÎÏÌÊŸq†»ÌÏÒÏÎÑÊÆÒ»}~¹ÎÌËÁ¹ºÈµ|p¯ÅËÃ1ÆÁÓäÍÕ¹ÓϺøÆËзØÄÆîкÕíáàáááàßßâææÙº˜‘§¼ÍÓÓÔÒÕÙÚØ×ÙØÚÜÚÛÜÝÞÞÝÞßÜ×ÔÝßÕÁ¦˜““—š�vvy‡£ÊÝÞÝÝÛÚÙÜÇ¿ÙÓÎÑÐÑÑÌÓÅŒxˆ†v‚¸ÐÐÔÒÖÖÒÔÔÑÎÐÐÊÄÈͼ¡…‡½ÙÓÔÖÙ×ÓÐÎÎÇɸˆqйÍÑÑÏÏÍÉÍЩv�ÄÐËÿ½ÇÄœp~¶Ï¼0±«Ä­¹ˆ¿Ãгް²‡Ê°Š»Ë¦´çãàâàÞßâäâÔº�’ž³ÍÙÛ×ÔÔÕ×ÚÑÎÙÙÔÚÜÛÜÛÜÜÝØ×ÙÚÝÛÓ» ’��—˜Š{sx‘³ÑßâàÜÛÛÛÛÛÛÖÕÚÒÑÑÐÐÑÌÑÒºˆwŠ�k~´ÐÑÕÖ×ÓÒÓÒÏÏÌÌÎÍ、{‡ÂÚÑÔØØØÒÎÎÌÍËÀ’q’ÃÐÏÎÍÎÌÊÏÏœq–ÌÑÈÆÂÊɱŠe‡Ç»-Öݦµ¾ÄžÌѰ¥­Ä¾œÑ˯¢åÏ¡ÐåßÞÝâåÝѸ�š¦½ÕÚÙ×ÈÈÖÖØÛÙÍÐÛØÖÚÚÛÛÙÜÖÓÕØÝÜͱ–ŠŠ—�“‡{w�™¿×áãÞÛÚÜÜÜÝÝÜÚÛÚ×ÓÒÒÏÐÑÑз²º…u‡‚q€¯ÉÎÓÖÕÕÕÑÒÓÌËÐÍÏÆ¿À�w‰ÆØÓרØÖÏÌÐÐÉÏÊ›z�ÄÅÊÌÍÆËÎÒÊ’p¤ÒÍÄÅÍ̹®�i”¹(¶Ú¼ÙçÑÎÝáããâàÒÝâæÚåÛÕàÞÝáåàͳ š©ÄÖÔÔÓÕÕÄÊÕÖØÚØØØØ×ÚØÕÚÛÚ×ÓØàÛɬ‘…‹—™‘�ut…§ÇÚââßÛßÞÝÞÝÝÜÚØÛÛÛÞÝßàßßÞÛØÉ¿ÐÀ†s…†u~®ÌÏÓÓÕ×ÔÒÔÎËÉËÓȾļ–xŽÊÖÑÖÕ×ÔÐÐÏÉÉÐÖxŸ¼ÇÍÎËËÐÏÔɉp«ÐÊÉÏÑÆ¾ºˆo”‡Å±½ÛåÜÑáæäâãããßàáæàÞåãáæá̬™˜•·ÖÛÖÒÒÓÖØ××ÕÙ×Ô×ÚÚ×ÖØØÙÙ×ÛÛÛצ‘„…“œ’rv�²ÏÙÙÜÚ×ÍØØÙÝÝÛÝÞÞàäçéëëììíììëëçåÞßÌ�m€‹~„²ÐÑÒÕÔÕÔÎÏÎÇÏÕÇÁÁɘt�ÆÕÒÔÖÕÖÒÑÏÍÊÈ¿ˆu ÃËÍÎÊËÐÎ×Àys³ÐÌÏÐǼ¾¯}j}­ÄØÏÖÜáàÝÜÛÙÙÛÝÛÛ×ÖÞââÝË®›š¤­½ÖÚÙר×ÙÙÙÚÖÖØÙ××ÙÛÚØÙÖ×ÛÝÝ϶žŽ‹Ž—�~{u~š¾×áß×ÔÙÝÒÅÔÍØÞÝàäçéëìììììëëëëëëìíìëèíÙšx€Š�…²ÑÒÖÒÒÕÍÐÐÏÔÕÌÅÇÊÊ»�s�É×ÓÖÒÔÔÑÐÍÏÈȹ�x²ÎÎÌÊÌÍËÎÖ°o{ºÍÎÏǽº·¡y!u®ÝÞÛáâáááàÝÜÝÞÞÝÝàãäÕ¾­››ª½ËÏÙØÔÛÝÛÙÙØÙÙרÛÝÙÖ×רÖ×ÕÙÛÊ­’‡‰’—Œ}po†£ÅÙÝÝÙ×ÚÛÛÜÛÜÜÛßãçêëììëëëëëêêëëëëêëëëëìëðèÀ—†…€‚®ÐÔÒÔØÓÆÉÒÎÎÌÈÌÆ¼¼±Šr”ÎÖÓÔÓÔÑÏÏÎÍËκ{„ÃÒÌÌÎÌÌÍÐÒ¦k„ÂÐÏʾ¾¸º¢}ÌÞÞââààááÜÙÜÝÛÜßãáÒ¸��š°ÅËÌÑÖÖÙÛÝÜÚÙÕÕÒÓ×ÙÚ×ÕÖÕÕÖÜÛÒÁ¨‘ƒˆŽ—ˆunrްÅÖá×ÏØÛÜÛ×ÚÜÞßáçèéììëëëëëëëëëëëìëëììììëëëéîóã¼”…~©ÎÑÓÙÖÈÊÓÎËÊÉÍ̺¾²ŠtœÒ×ÓÔÖÒÏÎËÏÍÊЬu’ÇÍÌÎÍÍÍÍÑÒši–ËÒËÃÇ¿¾°‰ÛàßßáâáßÜÚÞÜÛÞáÞʰ›”Ÿ¬½ÑÓÎÍÍÔØÛÜÛÙÖÔרÓÔØÙÔÓÔÔØÛÙÒºŸŽ‰Œ”™ˆxmw“³ÍÞÒÒ××ÛÝÜÜÜÛÝßáåéëìëìëëëëìëëëììëëëëëëìëëëìíìèéðñتˆ|{©ÐÑ×ÔÐÓÔÓÐËÌÍÍËÅÁ°‡qœÑÔÑÕÖÓÑÐÏËÊÉ̦w—ÄÊÍÎÍÌÍÌÓÍ‘oŸÍÑËȾ¾­‡ØáßààßÞÜÙÚÒÑáÝʬ”‘š¯ÇÐÓÔÌÊÍÑ×ÚÛÜØ××ÖÓÓ×ÖÚÙÕÐÙÜÙ˲šŽŠ�—‘†wn|ž¿ÐÔÔÙרÜÞÝßÝÛÜÞâçêììëëììëëëìëëëëìëëëëëëëìëëëìììëêçëò縋|~ªÑÔÑÊÑÕÓÓÎÌÌÉÊÈÀ¼À©€o ÔÔÒÕ××ÓÎÌÌÌÊÉœt™ÄÌÏÊÊÎÎÏ×ȇk§ÒÎÌÂÁ®‡ÙáÞÝßßÞÛʨ�¸Ê°œ�™§½ÐÐÏÖÔÉÌÍÑÔÚÝÜÜÙØÕÁÉÖÐÒÖÙÙÚɯš��“•‹~rq„¤ÅÚÞÙÔÖÛÝÜÔ×ßßÜÜáæêìëëêëëëêëìëëëëëëìëëëëëëìììììììëëêêéèîí¿‘|x¤ËÏÉÐÔÒÑÎÍÌÌÊÉÅÀ¿·¨†t©ÖÕÑÓÖÒÎÍÍÎËÊ‘sŸÄÍÇÈÎÍÒÒÖ¾{tµÒÏËŲ‡ÖßßäåâÞÅ«”u‘›”š«»ÀÈÒÑÒÙÑÍÏÎÒ×ÚÛÚÚÙÚÔÍÑÐÒ×ÚÑ앎”˜“„tmw�±ÎßßÚ×ÙÛÛÛÛÙÏÖÞÜÞäéëëìëëëëëëëëìëëëìëìììëëëëëëììììëëëëëêëêèìí¿ŒzvžÇÐÒÔÔÑÏÎÍÍÊÉÉÅ´¯·¬‡v¨ÕÒÎÒÒÏÏÍÍÏÌȾ�v¡Ç¿¿ÌÍÑÒÑÖ¹v{¾ÑÊÈ·†ØäáØ¼®«œ‹•�”¯¿ÌÔÏÅÐÑÔØÏÐÎÒÚÜÚÛÛØ××Õ×ÔÔÙÔ¼¡’�•�–„qo}›¾ÔßàÛÖÔÙÜÛÚÜÝØÚÝÝáçêëëëììëëëëëìëëìëëëëëììììëëëëëìëëëëëëëëëêêèìꮇ€wžÏ×ÔÖÖÑÏÐÎËÈÆÇ¸°´½³Šv¬ØÎÐÔÑÏÎÏÍÌÊÊÀ‰t©Á¾ÉÎÏÒÒÒØ°o‡ÆÌȸˆÞÒ®—‹ƒ€Œ��Œ ¼ÎÑÒÔÒÔÖÈÓÕÍÎÍÖÛÚÚÛÛÙÕÒÖÓÐɳ��‘–—–…ss‡¦ÆÛßÛ××ÙÚØÛÕÒÜÝÝÝÞÞäêìëëëëëëëëëëëìëëìììììììíìëìëìììììëëëëëëêêëêêéïÚ˜Š�t�ÐÕÓÖÓÐÑÏÌÊÃÃȾ»½»´†w±ÖÒÔÓÑÓÓÏÌÎÉȼƒz¢³ÅÌÍÑÓÒÕÖ¤p—ɼ­�²ž”š¥…jp¦¾ÂÓØÕÏÐÑÖØÑÑÖÏÌÉÐÙÙÚÛÚÕÖ××ÔÀ§‘ˆ�˜˜�{sz”·ÐÝÝÚØ×ÚÞÜÚÙÚÔÓÞÞÜÝàæëëëëëêëëëëëëëëìëëìììììììììëììëììëìëëëëëëêêêêêêèî¿…Š{mšÍÒÑÓÑÑÐÌÌÆÂÅÁÁ¿¸±¤†x²ÖÌÁÎÖÔÒÐÎÍÆËº}m’ÀËÍÐÑÒÑÕÒ›s¢½«s™¨ÆÔÒ§qƒº×ÚØÖÕÔÖÖ×ÒÓÚÒÌÏÌÕØØÚÚÖØÛÒ¼¡�ˆ‹•™‡qp€”µÙáÝÚרÝÜÛÔÕÚ××ÜÞ××Ûßçëëëëëëëëëìììëëëììëììììììëëëëììëìììììëëëëêêéêêéééìä˜xˆzošÌÓÎÐÑÑÌÊÊÈÄÂÁÿ°®¯}x·ÑÀÍÓÒÔÕÐÍÊÆÎµwv²ÈËÎÏÐÑÒ×Αuªµ~ÆÓÚÞÛͼÊÓÎÐÓ×ÔÓר×Ô×ÖÎÎÍÒÖÕÕÚÜÙÑ»ŸŒ‡Ž•�€qj|©ÊØÜÞÙÛÑÏÜØÚÚÍÒÛÛÛÛÛÓÕáçêêëëìëììëììëëëëëììììììëììëììììììììììëêêêêêêéêéééêéîÊ~yŠ}p–ÆÊÉÑÓÎËÉÈÄÁÃÅŹ»¢z|¼ÒËÑÓÔÖÔÏÍÈÉÓ­q‡½ÊÌÏÏÐÒÒØÉ‡{¡‡×ÛÒ×ÑÌÙÖÓÖÕÔÕÐÐÕ×ÕÕØÏËÍÎÒÔ×ÛÚÍ·žŒ‰’–Šrer‹©ÌÞßÝÙÚÝßÓÐÚרÙÙÚÜÝÜÛÛÝáèêêëëëëëìëëëëëëëëëëëëììììììììììììììììëëêêêééééééééêêêë¸qs‡~o’½ÆÎÓÐÍÉÅÇÁ¿ÄÉÊü³¦|½ÒÐÔÓÓÓÒÏÊÉÌÌ q“ÃÌÍÏÎÐÓÒØ¿€v…ÒÛÐÎÏÊÍÐ×ÕÓ××ÐÐ××ÐÖÕÊËËÏÕÚØÈ±šˆ†ŒŽ‚mdr’»ÕÞÝ×רÚÛÜÜÛÛÚÙÙÛÜÛÝÜÛÜÜáçéêêêëêëëëëëëëëìëëëëëëëìììëììëìììììììëëëêêêêééèéèééééééÞ¨on„}q‘ÁÍÌÐÐÍËÊÄ¿ÂÄÊȺ§¶®z…ÁÖÒÓÔÓÓÑÊÊËÌË¢sžÈÉÎÐÏÒÓÒÕ³z†ÖÛÐÍÊÁÌÓØÖÔÒÔÑÒÙÕÏÔÑËÍÐÕÓÀ¦“ŠˆŠˆ|j^ršÃÜà×ÑÓÚÝÜÚÙÚÚÙÙÚÙÚÜÛÝÔÔÝÛßæèéêêêêêëëëëëëëëëëêìëëëëìíìëììììììëëëìëëëêêêêéééééèèèèéçâÙ³vl‚~q�ÄÎËÏÏÏËÆÁ¿¾Ç˽¨¶Æ«w†ÃÕÑÒÓÔÓÌÌÐÌÌÌ™w¦ÈËÎÏÐÒÑÐÏ©‡ÖÕÐÑËÈÑÕÔÕÕÑÒÑÕ×ÓÔÓÐÐÐ̼¢�‰ŒŒ†wkiyœÉßßÙÖÕÔÛÕÒÛÛÛÛÚÙÚÚÛÚÙÜÝÔÔÜÝäèèéêêêëëêëëëëëëëìëëëëëëëìíìììììëëëëìëëëëëëêêéêééèéèèèèèèåØ×ºwh~~o�ÃÎÌÌÍÊÈÃÀÀÄÉÆ±¸ÈÆŸqˆÅÓÏÓÓÒÏÍÎÌÉËË’z±ÍÍÐÐÐÒÐμ‰ÔÓÑÐÍÏÕÙÔÓ×ÖÒÒÖÔÓÖÕÓDzœŽ‹‘“†uik�¤ÇÞßÙÕÖÙÚÛÜÕÔÛÜÙ×ÛÚÚÚÚÚÚÛÛÜÛÝäççèéêêêëëêêëêëëëëëëëëëëëëììëììëìììììëëëëëëëêêêéééééèèèèèèçÜÏÕ¹|h{|n‰¿ÐÊËÊÉÅÀÂÃÃɼÀÏǸ™rŽÉÕÒÕÔÓÏÏÏÍËÏÈ’¸ÑÍÏÑÒÒ˳ˆÒÕÑÍÌÎÖÖÓÕ×ÓÑÒÕÖÛׯ¬’ŠŠ��‚tkmƒ­ÎÜÝØÕÖÖÙÚÚÚÙÜÜÕÕÙ×ÚÛÚÛÚÛÜÛÛÛÜâæçèèéêêêêëêêêëëëêêêëëëëììëëëëëìììììììëëììëêêêêêéééééèèèèèèçãÕÌÔÂj~}o†¾ÒÉÇÈÇÆÄÁÃÊÄÈÓʹ¶šr’ÍÔÑÒÓÕÔÒÍÎÊÐÈ�„½ÏËÐÐÑ͵„ÎÑÍÌÍÓØÑÑÖÒÐÓØÚÔÄ©’††’•†ofr�¸ÈÕÞÖÓØÛÚ×ÚÛÛÚÚÚÚÖÖרÚÛÛÛÚÜÛÚÛÛàåæççèééêêêêêêëëëêêêëëëììììëëëìëìììììëìëëëëëëêêêëêééééééèèèèçäÛÎËÓ¿vf€~nƒ¿ÏÇÈÇÈÇÅÆÆÁÊÑÐĬ³�m•ËÐÍÒÖ×ÔÎÏÏÊÏËŒ‡¿ÌÍÎÎй„ÎÐÍÌÏ×ÕÑÒÒÏØÜÓ¸žŠ‚‡““‚ts€£ÀÑà×Ò××ÙÛÜÛÚÚÚÚÙÙÙÚÛÛרÚÚÚØØÜÜÛÛÝãæçèçèééééêêêêêêëêêëìëëëëëëëëëëëëììììììëëëëëëëëêêêééééééèèèèèåàÓÌÉͬzn{xg¸ÌÅÉÉÈÊÉ¿¸ÇÐÎÈ´±¬‘r˜ÏÒÐÔÖÕÒÓÔÏÊÔÆ‚ŠÁÎÍÍÏ·†ÑÒÎÍÐÔÐÑ×ÙØÏ·™…‡’‰{nn…«ÎßàÜÖ×ÙØÚÚÛÚÚÚÚÙØØÙÙÚÚÚÚÙÛÚÚØ×ÛÜÛÛÞâåæççèèééééêêêêêêêëëëêëëëëëëëëëìëììëëëëëëëëëêëëêéêêêééèèéèèèèæâØÍËÉÒÃixyl{®ÉÍÍÆËÎǺÂÒÐÆ¼²®¸’p ÍÑÒÒÒÔÕÕÒÐÎÔ³z�ÅÏÎиˆÏËÊÎÐÑÕÚÙÍ´•��ŠŒ~qlr�³ÒàÞÖÕ×ÚÛÙÙÚÙÛÖ×ÙÙØØÚÚÚÚÙÙÙÙÚÙÙÚÙ×ÖÙÜàâåææççèéééééêêêêêêêëêëëëëëëëëìëëëëìëëëëëëìëêêêêêêéêêéééèéèèèèæâÜÏÉËÌÕÅ�u}zix°ÈËÊÌÎÎÄÆÓÑÊÀº²¹´ŒsŸÎÓÐÒÔÔÖÓÔÓÍͯ}–ÆÎз…ÄÃÇÐØÚÕÀ¦‘�ƒ‹ˆxhf{�¿×àÞÙÕ×ÚÚÚÜÛÜÛÚÚØÙÙÙÙØÙÙÙØØÙÚÚÙØØ×ÖÖØÙßääææåçççèéééêêêêêêêëëëëëëëëìëëëëëëëììëëìëëëëëëêêêêééêééééèèèèçæãàÕÊÌÌÊÖÑ–mvwly¨ÈÌÌËÍÈËÓÏÊÅ¿·²·µ�x¨ÓÓÓÓÓÖÕÔÔÐÊЬz�ÊÒ¶x­¯ÌÜÖº—…‚†ˆ„ygg‚ªÏÞáÜ×ÖØÚÚÚÚÛÜÜÜÛÚÙÜÚÙÙÙÙØ×ÕרÙÚÚÙØÙÝßßÕÖßãäåååæççèééééêêêêêëëëëëëëëëììëëëëëëëëëëìììëëëëêêêéêêêééééééèèçæäáÚÎËÌÊÊÓÈ‹ovzt}¨ÅÊÍÐÎÍÔÑËü¸³®¸¶�{¯ÕÒÓÖÖ×ÖÖÖÏÊʤ~¥Ð¶s•–Áºœ‡€‰Ž…tjx’µÔßÞÚÕÔÙÛÙÙÛÚÙÚÛÛÚÚÚÙÙÖըרÚÙרØ×רÛßßÚȱ¶ÓÛáâäååææèèééééêêéêêêêëëëëìììëëìëëëììëëëëììììëëëëëëêêêééééééééèèçåâÝÑÊËÉËÌÔÊ‘oz‚v�«ÌÐÐÍÊÒÓÎŽ»½¶¶·°Š{²ÔÔÔÖרØÖÔÌÉË�z­º}²Ÿ‘Œˆ�Œ~to}¨ÊÜâÜÖÕ×ÙÙÚÚÙÚÜÚÚÚÜÝÚØÚÙ×ÖרÖ×ÚÙØÖÕÖÛÞÜϺ¡Š†¹×ÚââääåææççèèééêêêéêêëêëìëìììëëëìëìíìëììëëëëëëëëëëëêêêééééééèéèèèæãßÕÌËÊËÌÎÖÊ’o}‚y€¬ÍÐÊËÑÓÏËú¿¿¾µ³®‹�µÓÑÓÖÖ×ÕÓÏËÌÈ•�¥yÊ®�Œwgo�°ËãâÜÕÏØÙØÚÚÙØÚÜÜØÛÝÞÞÛÙÚØ×ØÙרØ×ÖÖÚÜÛÓÁª–Œ‰�©ÖÚÞâããäåææçççèèéêêêêëêëëëëëëëëììëëìëëììììëëëëëëëëëëêëêëêêêéééèèèèèæäàÙÎÌÉÊÇÇËÏÊ“kv{v~­ÓÎÌÒÓÏÊÆ¿¼½Á¼±¸¿‘ƒ¶ÖÔÒÕØÖÓÏÎËÎÆ�{qº²‚mlw“·ÕâßÙר×ÕÙÙÙÙÙØØÙÕ×ÙÛÜÜÙØÚÙÙ×ÖÖÖÕÔÖÜÞÚÆ®šŽŠ“�¢ŸÁßÝàáããäåååçèèèééééêêêêêëìëëëëìììëìììëëììëëëëëëëìëëëëëêëêéêéééééèèèçäáÜÒÍÊÉÇÇÌÉÎÈŽhy‚v~¯ÐÍÐÕÑÊÉÆÂÂÄÀ³ºÍ¸ƒ‰ºÓÒÔØÖÓÏÎÌÊÎÀŒw£�w~�ÃÚÜÙ×ÖÙÜÚÚÚØÙÚÚÙ×ÙÙÔÖÙ×ÙÙ×ÖØÚÙ×ÔÓÖÙÛÚп¦ˆ„Œ™Ÿœ•‰�ËâßßáâããääåæçèèèéééêêêêêêëëëëììììëëëëìëììëìëëëëëëëëëëêêêêéêêééééèèèçäâÞÕÍËÊÉÊÈÌÍÓОru{nu¬ÐÏÑÓÍÊÉÄÀÀÁ¸¼È¿¡�„ºÔÑÕÖÔÐÐÏÍËдg‹Œ¥ÎÜÝÚÕÖÚÜÜÛÚÛÙØØÙÚØÕÙÚÚÙØÖØÙØØ××ÕÕÙÜÛÓï˜�‹‹–›–‰~vžÛßßàáâãâãååææçèèèéééééêêêëëììëììëëëëëìëììëìëëëëëëëëëêêêêééêéééééèèèçåãàÙÎÊËËÅÀËÆÈÖÓ pwuiz«ÎÑÑÒÏËÆÀ½¾»ÀÊÀ¬š~‹½ÔÑÓÓÑÒÐÍËË·nµÓÛÜØÖ×ÚÙÙÛÛÚÚÚÖØÙÙÙ××ÙÚÙÙÙÚÙÙØ×ÕÖÛÝ×ȯš�Œ“šš“ˆ€zwxx~½àÝßàááâããäååææçèèééééêêêëëëëëëëëììëëëììëëëëìëëêêëêëëêêêéééêéééééééèçæäàÛÏÊËÃÇÊË´±ÍÏÔ¢qswqt£ÏÐÊÈÍÌÅÿ¼ÂÌȯ –zŠÇÖÐÑÐÏÑÎÌ̶‡×ߨÓÖØÚÚÛÚÛÜÛÛØ×ÙÙÙÙרØÙÚÙÙÙ×Õ×ÜÞØË¹ �Š’��•Šwuurtyƒ¡ÓÞÞßàááâããäååææçèèèèééêêêêëëëëëëëìëëëëëêêêêëëëëêêêëëëëêéééééèéêéééééèæäâÝÔÎÌÅÉÍËľËÊÒÓ¥xsxpp¢Í¿®ÉÐÇÄÅÇÈÌι£¢›s‰ÈÔÐÑÏÐÐÎζ†Ñ×ÖÖרÙÛÝÝÜÚØÙÙÚÙØÚÙ×Ø×ØÚØÔÖÙÜÚÓ¾£‘‰Œ—�Ÿ—„wttrqt€”«ÈØÛÞÞßàáááãääääåææççèèéêéêëëëëëìëëêëêëëëëëìììììííìëëëêëëëëìëëêéééèéèèèèæåâàÔÉÍÍËÊËÏÎÊËÌÒÖ²{s}mr Â¿ÈÏÈÃÈÍÍÍÏÁ¯¦®˜o‰ÆÓÎÑÒÒÏй…ÓÙÕØØØÚÜÛÛÜØÐÖÚÙÕÖ×ÖרØÖÕÔÙÞÜÎ²š†…“œŸ—Š�xvuuvyŠ¥ÃÖßßÙÚÞÞßàáââãääääææçççèéêêêêëëëëëëëëëëìííííìêéèåäáßÝÜÜÛÛÛÛÝßáåçéëìëêëêéééçåãâÖÊÈÈÍËËËËÊÊÍËÍ×¶yuzpršÂÊÎÌÆÆÉÏÏÎÆ´¨ª­–p‰ÉÕÐÓÕÑѼ…ÔÚØÙÙÙÚÛØÚÛÚØØÙ×ÖÖÕÕÖÕÕØÛÛÓ¿§•Œ�™¡�‘†}yqsxŠ£ºÏÜßÜÚÐÒÝÞÞßááâãããäååæççèèèêéêêêêêêëëëííìëéæâÞÚÕÐÌÇÃÀ½¼»º»¼¼½¿ÁÃÄÆÊÎÓÙÝáæéêëêéèäáÛÐÇÆÌÎËÉÌËÉÎÍÊÎ×´|txrr�¾ÌÉÇÄÁÌÑÏȽ·´¨¤˜o�ÉÐÐÓÒÒ½…ÔÛØ×ØÚÚÚØÙÙÚÚÚÙ×ÙÖÒÔÙÚÛÙË·Ÿˆ‡–  ˜Œ�}|zwx‚—³ÅÙãÙÖ×ÖØ×ÙÞßÞàááâãäääåææççèéééééééêëìíìêæáÚÔÏÉÄÀ½º·´²°®­¬«¬®®¯°±±±±²²²²µ·¼ÃÊÒÙßäæçæáØÐÍËËÊÉÉÆÄÌÍÍÍÎ׸~oyww›ÃžÄÅÊÒÐÈÂǽ¯ª±›nˆÄÑÐÓÔ»†ÔÚØØ×ØÙÙÚÙÙØ×ØØÖÕÓÕÛÜÑ¿¬›”—š¡ž‘…}{yx|ˆ“ªÇÚàÒÔÖÎÔÚÙØØÛÞßÞàâââãäåååæçççèèèèêêìíìêæàÙÒËÆÁ¿½¹¹¹·´²¯«§£Ÿœ™˜˜—˜š™––𡦍©©¥¡¢£§¯¸ÀÈÐÕÙÛÛÚØÔÒÐËÿÅÊÍÐÍÎ׺�r{yw™¿¹ÃËÉÍÐÈÁÀ»·³²· v•ÌÔÐÔ»†ÔÚרÙÚÙÙÙØÙ×ÕÕÖØÚ×ÒdzŸ’”ž¤¡—‹�zxxwzЧÇÖÝßÛÖÔÕÔØÚÚÚÙØÜßßßàáâãäååæåææçèèêêëíìéåÝÕÎÈÂÀ½»¹¶²¬¦¡œ—‘Œ‡�|ywvtsrqrrstw|‚Š“˜™™›žžŸ ¡¥§®´»ÂÉÎÓ×ÙÙÔÍÊÊËÌÍÎÏ×½zixsn”­½ÉÊÌÎÌÁ¼À¿¶º¾Æ¦yœÏÓÓ½…Ó×ÕÖÚÜÛÚØ×ÖÕÖÛÞÛγœ�“˜Ÿ¥ž�ƒ~|{ws|—´ÍÛÞÚØØ××ÙÙÙÚÛÚÚÚØÝàßßàáâãääåæåæçééêëéæáÜÕÍÇÁ»·³®¨¡›–‘Œ†�|vpljigffddddbadedfit{}~}�Š“œ¢£¥¤££§ª®´¹½ÂÉÏÒÕÖÕÒÐÏÊÌÖ¶|ozoj‡·ÉÍÑÎÎÅÀÁÀ½ÄÂÄ¿—rœÎÔ¼†Ñ×רØÙÙÖÕ×ÚÛÚÒ®—Љ‘ž¡™�€{yxvz}“¾ØàÞÚÖÒ×ÛÚÚÙÙÙÚÛØÚÙÙÞààààâããååææèééèäáÜØÓÎÆ¾´ª¡›˜’Œˆ…‚�}{yvsld_]\\\[Z[\^]YZ\[\bospjcdjs~Š”œ¥«¬¬®±³´³°°³·¿ÇÎÓÖÕÓÐÍÔ¾�kwpl—ÈÎÏÒÏÆ¿Ã½¾ÄÁÄŶŒnœÒ½†ÒØÙÙÕÕÖØÚÜÕŰŸ•�‹”š˜�ˆƒzrsy†—®·ÊáÜØÖÓÙÚÚÚÙÚÚØÙÛØØ×ØÚßáááâäææçççåãßÚ×ÔÒËû°£—އƒƒ‚��ƒƒ€}xtpmjgbYOLJNTSMLLJLMMOMLXeihaVV_hlqy€‡’£®³¶¶¶¶³°­«­°´ºÃÊÒ×Ôǽ´wh|nl›ÊÍÏÎÇÃÆ¿¼ÈÅÉÊÁ´�t©½…Ò×ÒÕÚÜÜÕÆµ¥˜—š�˜‘�‰„|wvv|›¶ÆÚÜÚ×ÖÖØ×ÑØÜÚØÛÜÚÚÔÊÒÚÕÖÛßáããääãâàÞÚÕÑÏÏÎȼ® ’‡ƒ‚‚„……ƒƒ�~zuqmiedbcdaZXXZ]YSSSNMNNNKIPVVXXRRV\djpwzwƒž¯±²±±²°¯®®­­®²¶ºÁÐ͹ͼvduqm›ÉÐÌÆÇÊÄÀÇÇÌÍÅÁº”z˜…ÔÚÕÙÙÌ·¡•’—šž—Œ…�~wsy‹�£ÅÙáßÛØÕÒ×ÚØÙÚÚÛÙÙÚÙÛÛÔÌÖÜØ×ÝáââßÜÚØÕÐÌÌÎÑÏŲŸ‘‰†‚‚…‡‡†ƒ€}zuqnkhfffhkmoqrqqrpliikjjfb_]ZYXRPOJGJNVaipy{|Š ­¯±³´³²²°¯°°°²´´ºÊÍË׺td{vo”ÄÏÄÃÉÆÄÃÀÅÌÈÁº¸�p†ÐÐÅ»ª›•”–š—Ž†|wvw}ˆ—´ÏÔÙâÚÕÖÔ×××ÚÚÚÛÜÛÚÚÜÕÒÛÙÚÛÚÛØÙßàÛ×ÔÑÏÍÌÎÑÓ̽¥Ž~~…ˆˆ‡††„�~zuqmkihkllmoqtttuutvxxvspmmoonmkgc`\XSH>?HOU^eny~„�¥¯°³µµ¶µµ¶¶´³²¶·¶ÃÍÎÍÖ¿vc~ul‘ÄÉÁÅÁÃÊ¿½ÉÌô·®€�³š‘•˜š›–Œ„zwux…˜¯ÉÛâáßÙÔÔ×ÖØÚÙÙÚÚÚÚÚÚÚÚÚÙØÚÛÛÙÙÙØÚÛÕÒÐÎÍËÏÔÔǰ—„xwz„ŒŒˆ„‚€|xtpmkjjjlqsrrrrsttustvvuuusqnmnpppokhgd`[TPRXVW^epx€ˆ•¦¬¯´···¹ºº¹····¾ÇÍÐÐÎ׿wfzuj’Äļ»ÀÎÊÆÌÌÈ»¹²—n˜ŠŽ™˜Ž€{yxuu~‘«ÃÓÝßÚÙØ×ÚÖÖÙØÛÜÙÕרÚ×ÕÙÙÚÚÜÛÙÙÙÚÙÚÛÙÓÏÍÌÍÓÖϽ¥�|wz}€ƒˆ‡…�{vronmkjlmqssstttuutsqpomiggikmmnopnnoonlkgdcbbfeWYcju€†Ž ®´¸»¼½¼»»º»º¹ÁÊËÌÏÒÐÍÙÃ}h|yo–Ä¿·¾ÊÍÍÏÌÊÀÀ»œoŸš‹‚zxywtz�©¾ÎÝÞÛÚÖËÖÛÙÙÚÛÙÛÜÛÚÚ××ÛÚרØÕØÚØÙÒÓÚÛÚ×ÑÍÌÌÑÔÑ»™�xx}�‚}}ƒƒ‚{qooonnooqrrqqqstsplifdd`YTTWZ_cfjnqommonkjjhfgjkodPVeq{€ŠŸ®·º¼¿ÁÀ¾½½¼»ÀÊÍÌÎÐÑÑÐÎÓ¾€o†}rœÀº¾ÈÆËÐÎÍÄÀ£u�ˆzvtrw•²ÐààÝÖÔÖÙÛØÚÛÙÒØÜÛÚÛÚÚÜÛÖÔÛØ×ÙÕ×ÚØØØ×ÚÛÔÌÊÊÐÖÒ¿¢~pw~�‚}~~}{}~~zplnppqssspnpqoljga^ZVTQOLMNRVZ\`djmomlllkjlljknpsuZK[jw‰œ«´¹½ÁÂÁÁÀ¾ÀÇÉËÍËËÐÑÐÑÏÈË»…s�|s�Ÿ³ÈÈÉÏÑÒËÄÅ«h„usx�‰¡½ÖàÕÑ×××ÙÙÙÛÝÜÚÛÚÚÚÛÙØÙÚÚÛ×Õ×ÕØÚÛÚÕÑÙÛÚÙÑÊÈÌÓÖŧ‘}oy€~||}}yssxzwpkpstuttpkmolfa`\WSPMGDGJMOSXZZ\_eglnmlklllmllnoqto\[eu��ž«³¸½¿¾¾¿¾ÂÉÎÍÌÍËÈÍÑÐÏÐÎÈÍĆl~vp}¦ÅÉÍÎÑÑÍÊÈ«_x|—­ÀÐÛÞÚÖÍÒÙÙÚÙØÚÚÙÛÙÛÜÜÚÛÛØÙÚÙÚÚÚ×רÙÚÛÖÎÒØÖÏÈÉÍÓ̲”…Œ„u{}~~}|{wrmkkmommptuvvusomlfa]\[YXZ[ZXTTVWTUZ[[\`dfjmnnlnpoookhfdgnlgjvƒ‘¢¬²µ¶º¿½¼ÃÊÊÊÇÉÍÍÌËÎÏÍÎÏÏÌÕÇŒo{~v’¿ÇËÍÐÑÎË̱o©ÀØààÞÚÕÕ×ÛÜÙØØØÙÜÚÚÛÚÛÚÚÚÚÛÙÚÙÙÛ×ÓØ×ØÚØØÚÕÑÖÌÇÌÏÒÕw‚‘”ƒx|}}}|xqkhfdcfilosuuvusqpmfcccdghkmnornhgec`^]__`eiiknnnprrqnha^ZY_lqr}‰—¦®°¯´·¯ºÉÈÉÊÉÅÄÌÌÍÌÌÏÍÍÍÐÏÍ×ËŒmzxu”ÁËÍÐÑÍÉηˆÚâÍÏ×ÕÖÙÙÚÚÙØ×ØÙÚÚÛÛÙÚÚÛÚÚÙÙØÙÙÙÙÖÓ×רØ×֨רÒÇËÒÓÁ�jq���xz{}{ytkeca[V_jmpstvvusqpniimpstuxxtqrtspommlheedbdgiknmnorsrnfc_XOO\oxƒ’¡­®®³±�~Œ¾ÍÇÈÉËÉÉÈËÍËÍÍÍÍÏÎÍËÓȉgyzo’ÃÐÏÑÍÇ͹ˆÕÖÁÌÚØØÙÙÙØÙØØ×ÙÚÙÛÚרÚÕÖÚØØ×ØØÖÓרÕÖÕÓÖ×Ö×ÖÎÊÎÏÄ–qhy‹uyzyzvmd`^UKLVemprtvuttqpomqtuy{yxxvsrqrpnoooonmkjighiknnnnpssmfc_XPIJ_}� ®²¯­�ŒqkƒµËÇÆËÊÈÇÊÌÌÊÊÊÍÍÌÎÍÊÔÊ‹k€xk–ÈÎÐÐÅÊ»†ÒÖÑÖÚØØÙÙÙØÙÚÓÓ×ÖØÛÚÙÚÚÓÔØÖÖ××ÖÔÒÖ×ÖÕÔÕØÒÒÕÑÍÏÏÈ¢}wgguyy{yupg_[WM@AQ^hmrtuuutsqqruxwy{{{vsrqpopnllmmnnnnnmkkknppoonqpkgcb]UKHSt™­²´»»ˆh{‚xk|®ÈÆÇÈÇÈÉÊËÊÊÊÎÍÌÍÍËÌÓÉŽm|yt™ÇÏÍÈʺ†ÒØØÙÙ××ÙÙ×ØÙØÑÓÖÓØÙØÙÚÔÖÖÔÒÕÙ×Ö××ÖÖÓÒÖÙÙÒÔ×ÐÍÑÎ¹Ž‚zgdq{zyumbZXQC9:LZbkqstuttusstw{{{|}}zsqpolllkgefikmnppmklmnpomnnmmjhhhbYNHJe™²¶ÂÌ͹“w|‚{mv¥ÃÆÆÈÊÎÍÊËÊÊÎÏÌÌÍÊÍÍÐÈ�m~�v�ÈÊÇϺ…ÓÙÕ×ØØØØ×ÖØØ×ØÙØÙÙØØØ×ÒÔ××ÔÕØØ×Ø×××ÒÒØØÖÕÔÕÑÌÏÆª�‰}mjs{yskc\WK;89CR_gnstuvvuuttx{}~~|ytrpmjgeda^\]`eilnonnonnnlllmlmkijjhd\SL`œÀËÎÐÑ×Μzu||nsžÄÊÈËÃÂËÊÊÊÌÍÌÌÍËÍÏÈÎÆ‹l�‚xŸÇÉͽ…ÕÚ×××ÙÙ×ר×ÕÖÙÙÙÙØ×Ø×Ö××רÙÖÖ×××רØ×ÕÔÒÓÖרÓÍÍ¿¨–�‡untyukb]\Q<9>AJUajostuvwwvvwz}~~~}ytqle^ZWTQQRUVW\bgjmnooooomlkmlllijmnmjieg¦¾´ÌÎÍÐÐ̯zp€st˜ÂÉÆµ¶ÉÉÊËÌÍÍÍËÈÍÐÌÈÍÆ”p|~w™Â˹‡ÕÙØ×Ö×ר××ØØØÖØØÙÙØ×Õ××ÕÕר×ÖÕÕÔÖÖÕÔÒÖØÛÛÑÄ®µÎ¿¬œ—�ypvuoe`cXD@AADKUblpstuwxxzz|}€|xtnf]XUSQMHEEJOQT[`eilmopoqpnmnnmmlnonlnpsr¢¾¬ÎÐÌÏÐÓØ¹‚o|‚rl�¿Ç¼ÂÇÇÉËËÍÍÌÌËÌÌÉÊÊÍÌŸt}~t‘¾¸…ÓÙØÖ××רØÙÙÙÙÚÙÕÖÖÕÙ×××ÖÖØ×Ö××ÕÒÓÖ×ÙÛ×ÍÀ± “†“¼Åµ§ž‘xvrqi`aM=?A?BKXckqtvvy{{{}€~|wrk`ZZ\YVURNH@>DKOTY]chlnpqqrqppqomnmiefkosyŸÐÐÌÍÎÑÐÎÓÖ¹‚m}�rk�¾ÆÄÇÄÇËÊËÍÍÍÎÌÇÆÈÌÈÌÓ©v|„v“±…ÑÖÔÔÔÕרØ×ÔÓÙÙØ×ÖÓÕØØ×ÖØ××ÖÔÔÔ×ÚÚ×ÑÆ¸¢•Ї‹Ž‘’¢¾Å¸¢Ž…�yplhZL=9@CADP[ckqvxz|~~€€~|xsj^[]]\ZZ[[VQH>D@=DKTY`lvy}€‚ƒ‚��€~zuoc^_`a`\VQNLC60126?JQV[afjntwz{|}p[OF?<=ETajvŸÊÎÍÍÏÐÏÏÐÐÎÍÕÔ¥v{„xh|¬ÃÅÈÈÉÍÎËÍÍÊÊËÍÎÌÍÏÎÒ«vy‡„ÐÙ×ØÚØ×ØÙÚÕÖÚÙ×ÖÔÓÒÔØÚ×ËÉ´¡”Šˆ‹‹•’ˆŠƒ{urnhgfdeadz›±¡‰ƒpQ<:??;?DKOU`nz�…‡ˆ‡ˆ‡†�|wtrppolljie`\XWVPICDO\a_^cknptwy~‚…€bHDA@FUZT`ˆÀÏÎÍÎÌÎÎÏÏÏÎÑÑÏÌÐÔµ€jx}psšÁÊÉËÌËÊÉÊÉÍÌÇÌÎÊÌÎÍÔ±†ÒÚØÖÖÔÓÔÔÖ×ÚÚÒȹ¦•Šˆ‰Œ�Ž‘��‰skfehjjjkklmnmnponnonpl_nƒ„€t`K>?ELOPW`p~ƒ†ˆ‰‰ˆ‰ˆ‡‚}~|zxtpnljhge`ZURSWXXY_hnpruy|€„……pJBE:-3CNeœÉÏÏÏÌÆÊËÏÏÏÐÐÐÍÏÑÑÜÇ‹kv~ro‘¾ÌËËÊÉËÌÉËÍÊÍÌÉÇÇÍϾ…Ï×ÕÓÔררÕÍñ£•�Œƒˆ™—––”Œ~smhfeiiillllmmmoppoopponnim~wrwztdNKSSPRXbt~…ˆˆˆ‰ˆ‰‹‹‰ˆ‡†…‚|yuqonkgb\WUUWWVY`gnpqvy|}…††^LLE879Di«ËÎÎÍÎËÅÈËÍÏÏÍÎÏÌÏÓÓÚÏœss{qlˆ¹ÍÉÊÉÉÌÍËÌÌÎÎÍËÊÍϺ„ÎÑÖÙÚÛÓõ§™’Ž’˜œ˜Šƒ�Œ…~xmgfhikllmnnlkmonmonoqqqpponp}rfkotwsleaWNQXhz�ˆŠŠŠ‰ŠŒŽŒŒ‹‹Š‡†ƒ�~yuokheb`_]^_``elrstwz}€�…‡ŠˆpYXWVSLQ{¶ÅÁÊÌÍÏËÍÍÎÎÎÎÎÒÍÌÒÓÎÔÓ«wr|wn…¶ÊËÉÉÌÎÌÊÌÎÎÎÍÐÎн†Ó×ÓÌ¿­œ�‰�”—�¢Ÿ›„ty{skjheflmppnmnnnnnprpopopsrqoopnuxiknquvwvpi`TRau‚‡Š‹��Œ�����Œ��Šˆ‡„|zuqonljjhhikotxzyz~~�‚�‚…ˆŒ‹|a^dhkad•ÂÆÀËÎÍÎÍÎÏÐÎÎÎÍÏÏÐÒÒÐÌÏÔ´|n|xm€«ÇÇÇÍÍÊÊÌÎÍÎÎÐÎ̼„ô ‘ŠŠ�—œ ¡›’Œ|qhfmmhhiillmmnmllljmnmnpppqrqpqpooooxsimku|vqwtqkggu‚‡ŒŒ‹ŒŒ�Ž�’’‘���ŽŠ‰‡‚€~{ywuusrstv{}|}}~�ƒƒ‚��…Š��‡lY\cmlt¬ÆÇÎÏÏÏÐÐÍÏÏÌÏÍÌÈËÑÒÒÐÒÐÐØ¹~lwxmx¢ÂÈÉËÉËÌÎÏÎÐÑÌÊ»o•‹Œ’š£¥œ“†{tols‚Œƒm\[abdfcdijjikkjmnmmnopqpoopnnoonzskno}ƒwlvwuutx�†Š��‹‰Š��’““”’‘‘�ŽŽ�‹ˆ†„ƒ�~~{z{|~~~~�ƒƒ�€€ƒ‹�’�|e^`ip‚µÈÇÏÎÎËÈËÍÍÊËÐÎÏÌÍÎÑÓÑÑÒÑÒÙÊkuxou�¾ÆÇÊËÊÌÎÎÐÐÎϺnœ ¥£��rlkq�œ¹ÌȾ°‡\PW[[Z\`cfhijjlllmnooppnopomlmm|vmqt‚ƒzv}|xzz|‚ˆŒŒŽ�‰ŠŽ�“””““•”’“”““��Ї†„‚‚€~€‚�€�€€�‚‚�€�…Œ“•“ˆtmkls…´ËÌÊÌÌ̾¾ÏÏÌÎÏÏÌÏÐÎÏÓÓÒÒÒÎÐÙÊ“puxor–½ÃÅÊËËÌÐÐÎÎÑ»w¥•„xqor}� ±ÀÈÍÙÑÁËÜÒ bJJLPTWZ]`cfffghijlmlllmnopnlk{znsx…‡…ˆ€y|}~~„ŠŒ�ŽŽŒŒŽ�“••”•˜˜˜˜˜—•“�ŽŒŠ‡†…„ƒ��‚ƒ�€€��„ƒ��‚ƒ‡‹�–•Ž~urrv‚«ÉÊÉËÌÌÊÌËÊÏÎÎÍÉÎÏÍÐÒÓÒÒ̾ÇÐ×Óœmsxps—½ÅÅÊÍÌÏÐÎÍѾbzopz‹Ÿ³ÄÑ××ÔÊÉÐÊ¿ÅÎÒÑ«nG>BIMPSVY]`abdfiklihkkjlopomx{pszˆ‹Ž‰eg|‚„‰Š‹���Œ��“””–˜š››š™—–•’‘�ŒŠˆ…„…ƒ�€�€€�‚ƒƒƒƒƒ†‰‹Ž•™“†{xy{�¢ÆÇÉËÍÌÏÒÈÆÏÏÐÍËÏÍËÎÑÏÐÏËÇÌÓÒØÒ›oqwpt•¸ÆÇÊÍÎÐÐÏѽb�œ´ÆÔÚÝÝÛ×ÔÒÏÏÕɾ¾ÄʪuR>9?FJMPTW[]_behjijkjjjlmons|rt|‰‹ŠqOcz„€‚†ˆŠŒ�ŒŒŽ‘•™›žž��œ›š˜˜—––”‘�Œ‡„†…„‚�€�€€~~€�„‰�‘™¥«¥’�}}„˜ÀÊÅÊÌÊÊÍÍÊÌÏÒÍÎÑÏÌËÎÎÏÎÏÐÏÐÓÑ×Ö¢omxupН¿ÄÉÌÎÐÏÏ»uÂÛàÞÜÛØÖÖÕÖÕÔØÑÁÀÂÿº½½¯‰`C58?EIMPQVX[_cffhhhiijjkkjxwv~‰‰z]Veuƒ‡‚€„†ˆ‰‹Š‹�—¡§©««¬¨¤£žš——™˜–•’ŽŠ‡………ƒ€~€~~}}€ƒˆ�š®»º³¤‰~~�‡�—¹ÊÉÊÊÊÌÍÉÉÎÏÎÅËÑÎÍÎÏÏÏÑÎÍÏÎÐÑÖÙ¬uo~vn‚§½ÄÇÊÏÏϺ|É×ÕÕÖÖØÙ×Ö×ÔÕÕÅÀÀ¿¾¾»½¹»­ŠkM938@EHJMPV]_aabcdfefhiiclxx~ˆ„kZaezŽ‹…€€‚„†‡ˆ‹”£¬­­®³µ³±ª£Ÿ›™š˜”‘ŽŒŠˆ…ƒ�}~~~|}€€„Š”§¸»µ°«•}�‡‹s~°ÌÉÇÉÉÉËËËÍÎÃÇÐÎÏÑÌÌÏÏÐÐÏÎÑÒÍÕÚµzlvsr†¦ÀÅÅËÏÑ»†ÔÒÑØÙÖÔÓÐÖÔÐÔʺ»¼¹¹Ã´³»´µ£ƒoXB327>CEJQUWZ\]_bdcdefgb_v{‡d]fm‡’Œ†€€�‚„†ˆŒš¦¨©«­°²²³¯¬ª¥Ÿ™—•‘Ž‹ˆ…ƒ�|}}{yy|‚‚ƒˆ“ ©¯²°¬¨™ƒ}€†ŒzeqžÃÉÅÈÊÊËÌÎÌÌÍÍÍÏÐÌÌÐÏÏÐÏÐÒÒÒÏÑØº‚ksxv�žÀÆÄÌÒ»ƒËÙרÕÑÒÐÍÔÑÑÔɽ»¼½»»²²½º¹´–|naN:018>CGJMRWY[^abbbcdcYm~}‡~ffr{Š�‹†���‚„…†�™¡¤¦¨«­¯­¯°²°­¦ž˜”‘�‹‡ƒ€€|}|xw{‚†…†ˆ�™Ÿ¡£©ª¦£™„}„‰ƒwddŒºËÉÈÇÊÎÍÉËÎÍÍÍÍÏÏÎÏÐÑÐÏÑÓÓÒÐÏ×È™ppwpuœÀÃÅκ‚ÌÜÙÙÔÏÖÖÒÏÒÕÓÁ»ÁÀ»°º½½½º´¥‹{siXD5/16=BGJNTVX[^^]`cd^ezyƒ�mn|{��‹‡ƒ€€‚„…†�–�£¦¦©««¨ª­¯¯®ª£š”�Œ‰†„€~€|xx~…Љ††Œ”™� ¢£ Ÿ™ƒy~�ˆwwja|®ÉÇÅÈËËËËËÍÏÏÎÍÌÌÍÎÐÎÍÐÒÐÐÑÏÏ×Ò¢olupl‹­¼Çµ†ÖÚÖ×ÖÕÖÖÔÐÔØÐ¼·¿ÀÂú°¸½»»³®Ÿ†}tj^N?3/29?CGKPSVYYZ]adb`s|‡|wvq†Ž‰„�‚ƒ…†‹“˜ ¤¥§¨§¤¦©ªª©§¢œ–�Ї†ƒ~�ƒ€ytvyzzz{~�ƒ‡�–››šœ—�v{€†{fmwp_n�ÂÊÅÈÌÍËÊÎÎÑÑÊÇÍÏÎÌÊÎÐÑÐÐÐÎÏÎÒÓ®whvneƒ²Ç²…ÕÛØ×ÕÕÔÔ××ÙÕÈÃÁÀÁÀ¿½²¸»¹¹¹¶±‘~zsmdWH90-3:ACHKOTVWZ^bb]e||„‰‹�…mu‹‹„‚‚�„†…‰�”™ž¢£¢¡¡¡¤¥¤£¢¡�–�‹‰†‚€�„„€ulmmgO?I[ltuz„�‘���{sy~…}a]hvsdh�ºÊÉÊËÊÈÉËÆÊÌÇÉÎÐÎÌÏÐÐÑÑÏÎÐÑÎÏÔ´|kysp‡°¶…ÔØÕÖÕÕÖÕÔØ×¿»ÇÅÄ¿¹½»¶·º¸¹º·£‡~zvogYLB6..4:>DIMPSUY\^_\q|u~Š”‘~o�‹„‚�‚†‡‡‹Ž�’•˜™™™™›�žžž�›˜“�‹‰…€~‚„ƒ‚xh`b[?.2;I^lqv}€€}qpx}…y`^\drvkd{ªÈÉÇÉÅÀÊÇÊÈÇÊËÍÎÏÎÏÏÏÑÐÏÐÐÎÌÍÔÁ‚hvsj~Ÿ†ÒÔÔ×רÙ××Ú͸ÀÇÄÅÄÄÀ¾¾¹µ¹¼¼½µ±™ƒ}xsnh_UK>2-.4;@CHMOSUV\\bxvr}Ž•‘‰ˆ‡„��ƒ…‡Š�Ž��Ž�‘“’’’“•••”‘�ŽŒ‡„�}}�ƒ…„€tcYTLEEE=?Ujoqsrtqjnv}„v^^[Y`qvobnžÄÇÄÇÆÊÐÍÈÍÌÊÉÌÌÎÏÏÍÏÐÎÎÍÍÏÐÐÖ‡jstmq†ÓÙÖØÙÙÚÙÚÚÎÈËÇÃÃÅÂÁ¿ÁÀ»¹½¿½·¶«�‚|wqnic\QE91,/7<8;d€„‡…�€zvx|zx{…”žŸš–—™™œ£¤ •†}‚�™ž�š•�Š…ƒ„‚€„…ƒ€}wqr{�vlq}uYRQNOONONKU^`][\]_`aa`^]]\Z[]Y_nxobx«ËÊÂÄÆÈÈÇÇÉËϼ†×ßÛØÍÉÎÑÐÐÒÎÌÉËÎÊÊËÌÍÉÈÉÈÈÈÉÉÈÉÈÅÅÀ»½½»°”‡…„€|zrsurnkhfc^WPF@>:;Z{‚†„€€{srxywv|Š˜Ÿž˜••—–˜š—‘��™  žžžš•”“‘�ˆ…„ˆˆ…ƒƒƒ~|‚ƒzss|mVVUSTROMPSQTSLHKU^___`b`_]ZZ\ZWYfsocl™ÂÉÅÃÅÅÅÈÅÅλ…×ÞØÕÌÎÐÑÅÇÒÑÑÌÊÍÌÎÍÊÊÉÉÈÈÈÇÉÉÉÉÉÆÅÅÀ½½¼½«�‹ˆ‚~{wvvuqolid]^XFAED2Eq~ƒ„�}toruusu€�šœ—��”•’“’Ž‘˜›Ÿ¡žšœ�™––“މ‡‡†„ƒƒ�~z|‚„{wvxgZ[XVVSTTVZSLJFDIV^_`_`cbba^\ZZ[YU_otgdˆ¶ÆÂÀÁÂÇÅÇ̹‡×ÝØÒËÏÐÐÈÈÒÓÎÎÑÎÎÐÍÊÉÊÈÅÊËÇÉÌÇÇÊÆ¿»½½½½¼¸œ‰‡„‚~{wurplieehZG@Vy]8]z�†…�}xqpstsqv‚‘™—‘����‘‘�”˜›��œ™˜™—’‘ŽŠ‡ƒ~zxvtsqqv~‚~xzu`ZYZ[WTVUUVUOJKPX_][ZWWZ\[]\\]\Z[YW]gunbw¦ÄÅÂÃÅÈÌ͹‡ÙÞØÍÌÐÑÒÓÎÎÏÌÍÏÐÎÍÎÍÎÍÆÇÈÁÄÇËÅÄÈȽ¶»¿½»º·¦‰†‡…„�~wturookkgZRB`�–XIq†‡„€{topttqpxˆ”–‘‹†ˆŽ‘•™šš�Ÿž›–�Ž�Œˆ„~|vrommmlou}�€}}n\ZXZ]YUVTSVVSQU[]YQOMNNOPLNX\]]]\\\YXbrqcl™ÄÊÃÃÇÉι†ØÞÓËÌÎÑÓÒÍÌÏÒÎÊÑÍËÎÌÌÌÊÌÈÂÆÈÉÅÇÉÊÄÃý»¼»µ­“ŠŒˆ†„vuz{uoljd^XDd€�H`y…Šˆƒ€zsopqqpuŠ�އ‚�…‰‘œžžœž¢ž—�‡‰Šƒ|xsqpmjjjiiow~�€�{e\[[[ZXVUTSWVU[]YTNIJMNOOKHJRQQY^[Z[]^Y_ntfdŒºÇÂÂÆÌ¸†ÖØÎÊÌÍÑÓÐÎÍËÐÎËÎËÍÍÍÌÌÍËÊÍËËÊÉÉÊÈÄÁÃÀ¾¾¼¹´©–�‹‡†�||{{vnjjga[Ps†~�]Qo‚Љ„‚|uopqpqx‚‹�„~|„Œ—£¨¢ žœš“‹ŠŠ†€yspnjgedfkry~€ƒ…wc]\]]\[YYXXWW[a]UPQQNOLNNJJKJINX^]]]]]\Z[fsnez©ÄÅÄ˺‡ÖÔÉÊËÏÒÔÒÏÍÉÌÏËÃÉÎÏÎÍÍËËËËÌÍÎËÊÉÈùÁ¿¾¿¿º¹¤ŽŒ‰‡ƒ€�~ytpmomdcd€Šw‘�Tg}ˆŠ„�€€|uqqpoqy„‰„{y~†Œ“ž¥¨¥ œ˜˜—’�Œ‡€zqjfc`bgms{}�Ž‹sa]]]]_^\\[[[[^\VTRQSROPQONONLJNXadb_]YY[YYewzfk–¼ÆÉ¹†ÖÕÈÉÉÎÒËÈÎËÍÎÍËÉÌÍÎÍÍÌÉÊÈÉËʾÃÊÉËËÆÁ¾ÀÀ¿¼º»´™‹‰…€€‚€~ysqsphfkƒ�x—jg{„Š…ƒ…ƒ�}tqrqmrz‚‚|wx�‹’•�¥¥¦¡š˜˜•‘�ŽŠ„�}{zwy|€‚~‚�Œn^]__]^^]]^^^]YUUTTSRSRTTPRSRPKKRZaa_^]ZY[\]csyfbƒ°Æµ†ÔÒÊËËËÒÇ¿ÊÇËÐËÌÍÊÍÍÌËÊËÉÆÈÊȹÀÉÉËËÉÁÁøµ·ºª�ˆƒ�‚„‚�€zuvslfg‚�}m•†k|�‡‰‡‰Š…‚|vvsoory~xvz‡�‘–œ£§¥Ÿ™–••––˜š���›——™›šŠ�ˆ„i\_``^^__^__[YWWXWWWUTSSRPQRSRMKO[bca_^[YXX_\Yjxncx¢°†ÓÎÊÐÑÎÏÑÎÏÍÂÃÅÈÌÎÊËÎËÈÊÊÇÈÉÊËÊÊËÊÉÆÄÄÃú³´¸¸ �‰‚‚…‚€~~}zwpkg}Ž‚h†�xxƒ†ŠŠŠ��‡€ywvsnow|}}yw}‰�‘—žŸ ¡žž�œœž¢§¨¦ œš›šš›•…€„|d[]_````_]]^^\[ZZ[[YWVUTUSOQSRMIP]a^_`_^ZWWXYYXarrioˆ„ÏÌËÑÔÓÒÓÒÏÎÉÀÂÈÌÈÇÍÐÎÊÊËÊÈÈÊÊËÊËÊÉÇÅÅÃÄÁ¿¼¾»½²—�ˆƒ„‚€}|~~{tomzŠƒii—“w�……†‡�‘Žƒ{xwupntz||xvw�ŠŽ‘“–—� ¢¢¢¡¢¢Ÿ ž™••–““•™Ž�†zb_^``__`_^^^_]\Z[][ZYWWWXYWWUSPNPQNLPW[]]]ZWUWWR[ktlcƒËÌËÍÒÒÒÑѽ»ÑÎÊËÊÅÉÏÏÎËÂÈËÊÊËÊÊËËËÊÇÅÅÆÄÀÀÂÀ¾¾»¥�ŽŠ‡ƒ€~||~|yspy†‚l[uœ‡}‡†�‚†Ž‘‰}wvtrmouxusssxƒˆ�“—œ¢¡¡£¢¢¡›˜—˜”�“–””–�†‰t_``_^^_^^_`_]]]]][ZZ[XUZZYYYUQTVQJFKRWZ\_c`ZXVVWWWaok†ÊËÎÎÐÑÎÐѾ¹ÊÏÐÌÈÍÍÎÍËËÄÉÊÌÍËÊÊËËÊÊÈÇÆÆÁ¼¼¼»ÀÀ½´™�ŽŠ„‚€~}}z{wrw……p`\…œƒ‡Œ…€‚ˆ‘Ž�ywtqmlnqsqnmnsz„ŒŽ“˜œžžžžŸžš˜”“��Ž�•’�‹‹‡jY[]^_^^^_`a`__^^^[]\]ZZ\\YTUVSRQNKIMUYZ\Z\a^[[Z[^ZU^lƒÉÌÌÔÓÐÐÌËÍÍËÌÏÍÌÐÏÎÍÌÌÍÍËÌÌÊËÌÊÉÉËÊÈÇÆÄ¾»¹»ÁÁ¾¿®”��Šˆƒ‚ƒ�~}zww‚†tc[i˜–‡�‹ƒ�ƒ‹�Š~xurmhhkqnihiou}†‰ŒŽŽ•˜™�š——™“ŽŒŠŠ‹ŽŒ‰ˆ‰€bWY\\\\]___^_a_]^^\]]]]_][ZWWXVRPNLJHHHIRXX[]]]\[]^\Z^€ÈÏÍÐÐÌÎÇÉÍÌËÌËÌÑÑÐÏÎÎÌÊËËÌËÌÎÌÊÉÉËËÉÉÇǽ»Á¿¾¾¼¸¡‘��ˆ…„„„‚�€{{ƒve_Y{ ’ˆ‰…‚€‚ŠŠƒzrpnhgfikifhmos|„ˆŠˆŒ�“˜”’“‘‘�Œ‡‰Œ‡‡‡††t]YZ[\ZZ\\\[[]]_```^\^][\\[ZZXUSSRQPOONLFLW[\]^^[Z\\]\W�ÉÏÒÉÈÎÎÍÌÍÊÊÎÍËÏÒÐÏÍËÎËÈËËËÌÍËËÌËÌËÉÈÈÆ¿ÀÄþ»¿»º±˜�Žˆ†‡†„„„ƒ€~ƒzf\Xb‰ –Œ„€��„†}okljgdchiggklouz�…†…‡‰ŠŒŽŠˆ‹�‹‰Š‰†…‚ƒ�iZ[YXYY[[YYXXXX[^_^]^][XYZXXYXVUSRRTVUSPMOY^^^^]]^_`][Y‚ÎÒÏÎÐÓÎÎÎÎÐÏÍÎÐÑÒÏÌÍÍÊÈËËÊËËËËËËÌÍÊÈÉÉÇÄÅþ¼¾¿¾¼¼¨’�ŠˆŒŠ†……„ƒ€�„|g\XWg†—š•ˆ�}{‚|ofeggedecacgjkpu{ƒ††ƒƒ‡‡�€†ˆŠŒˆ„€{‚}_WXVVUVYYYWVSRTUXXXYYYZZZZVUVVVUVVVTUVRMJP[`_^__`aaa_\ZƒÎÒÑÓÓÑÎÎÍÌÏÏÎÎÐÏÏÎÍÏÎÅÉÏÍÍÎÍÌËÊÉËÈÁÅÉÊÇÅÄÅþ¿½¿¿·›ŽŒŠ‹‹„…‡†„€}xk_XV]dp�‹‘ˆ}uxzyl]Z`ccefb^_`cgjptx}ƒ†ƒ~~yvx|€…ˆ‚yxx~pVVVVUUTUXXVWVQPQQQPRSXZXUUTQQSXXXXWSRXWRLR]^\]a`[Z]`_]]ƒÌÎÍÒÑÏÏÐÎÍÎÌÊËÍÏÌÊÍÍÏËÉÌÎÎËÌÌÌËÌÌÆÃÆÉÉÆÄÃÄÆÃÁ¿¿Á¿¿«’ŽŒŠ‹‡ˆ‰‡ƒ†|pxn`XWZ_`cb{‰€vquwr^SW]`aedb_\]befjotz‚~xuutsruyyuppsv`QSSSTVUTSPPRRNLLNSTSTWURQUWURTYXUVVTTVTQQY`^[[WVWTX__^X…ÑÑÍÒÒÐÑÑÐÐÎËÈÈÉÍÍÍÍÎÎÍÆÈÎÎÌÌËÌËÌÎËÊÅÆÈÄÃÅÃÅÄÀ¾ÀÁ¿º¡��‹‹‹ŒŠ‡‰“ŒrsqcWTV]^ba`}‡}rosuiSMV]^^adb]YY]`ejlsy|}xsqqljjmmmkjniOKPNMONOLJJHGHIJJNUVWXXWUXZ[\ZZYXXWUTVTQRY`][\YPOX\ZZ\[W„ÏÔÓÔÓÑÒÒÎÌÌËÍÎÉÉÍÏÎÍÌÎÊÉÌÎÏÎÌÌËÌÌËÊÈÉÇÆÆÆÅÄÄÁÁÂÁ¾¾²–‘�Œ‹ŒŠˆ——•ŽxodYTTYZ[fai…†xjjpn]NMU[Z[]`^ZWX[]achmpppplhddeddefihcXHFJKJFDBCFHGIKLJLPPPQQUYZZYYYXVXZYVTUTU\a_VW[YVXY^^[]WS‚ÍÔÕÕÑÐÐÊËÌÍÍÎÏÍÎÏÏÎÌËÍÌËËÊÉÍÎÊÉÌËÉÉËËÇÇÄÄÈÆÆÀ¿ÃÂÿ½º¦–’ŽŒŒ‰‹™˜‹›™}bYVVWZVZhaq‰ƒqahphZOMTYXWZ[ZXWWWY\`bdfeffbabaaaafaVYe^JBHGDBACDGHJKMNOPQQPQQVXWVSTVUTUVUUVWY`bZTUYYVXYYZYXQK„ÏÒÏÐÏÏÏÇÊÍÎÐÏÎÎÐÑÐÍÎÎËËÌÌÉÉËËÊÇÇËÉÇÉÊÇÃ»ÄÆÁÃÃÁÃÃÂÁÀ¼¶Ÿ’’��Љ‹™‹‰ž�tWRWVVWT_ebzˆ�k\dg_XOKRXYWVUUTSVWXZ[\]ab`_aa`]\]UIEKVffNAEHIJKGEGHHLPRRSVWUPOSTRSUZZY[[XWXZ\ab_[[YVUSWWXWTPO…ÏÑÌÍÍÐÑÎÌÏÏÏÑÑÎÏÎÌÍÏÏÍÌÌÊÌÈ¿ÈÌÉÈÌËÈÊÇÁÅÄÄÁ¾¿ÆÅÅÃÁÁ¾»«”’‘Œ’’‚…‘†‰›‹gROTTVST_bf}†�dT^]YVMIOUTTQOOPRTVUVVVY\ZYZXTSUJ;:@GMWghN>EKMMLLONNKMOOOOMJJKORSTWYZ[[[[^``bddfdc\XVVYXWVYZRƒÏÔÍÊËÎÐÎÍËÊÍÑÒÍÌÌÌÍÍËÌÌËÉÍÈÁÉÉÉËÉÇÆÉÆÃÆÇÆÆÃÃÂÀÆÅÃÂÁ¼º²œ‘�—¦¥Žs…�…‡��iQJOSQQU_`k�…|^PVUVUMHJOUTPPPNPUSPQRQRSPNNSdeA5:=BKS[jmO:?CEGIMPSPOMIGHIIIKOQRSSSSVXX\bccfggefd]YWYZWUSW\W„ÐÑÌËÉÈÇÇËÉÇÊÎÏÌÊÎÏÏÎÍÎÍËÈÍËÉÆÆËËÇÆÆÉÇÈÆÆÈÈÄÄÂÂÈÆÄÃÀ¼·²¨––¤­©ŸŒ‚‹�ƒƒ‚ymTEGOOPV]ar�€y[KKMRSNIHKPRSQOOPPMMMOOMNR]n~sC68>CIRZ`npP9?DHJKKJNSVYZ[TLLNPOOQSTUY\WVUTWTRSRTVX[`fc„ÎÏÌÊÇÊÊÌÉÅÍÎËËÌÌÌËÍÍÍÏÍÍÊËÊÊËËÉÉÊ̽¼ÊÈÈÇÇÄÀÁÅÈÇÆÈ¾­·½º¯¨¥£•—¦ª­£‘‡‹—Œpkfpym^LDGNUXn|ka]YNDCLQOKJKMLKMPUYZ]elpsw|pB039CJRY_fmptw‚¡”ubJ=AGKLJKOQUXXSIGJLMMKPQNPSRQOUXSPW^^`d_ZX\‚ÎÐÌÊÈÌËÌÌÌÐÏÍÎÊÈÌÎËÉÎÍÌÏÑÎÈÊËÌÉÊÉÉÂÂÈÆÉÇÅÄÄÆÊÉÆÅžª©©¦¥¦¥¤™’›œ££�†…“�}vifhmofVLEHO\~‚k[YYULILPPQOOOMPSVY]bhlotwzqC.28CLUY^jv|}b@]~ЇtQA>CJLKMORSSUSKLRPQQOOOQPU\[^]YXXY^^YZ`hZƒÍÌÉËÉÉÊÌÌÍËÊÊÍÌÉÍÍËÌÎÍËÌÏÌËËËËÊÇÄÇÍÉÅÅÈÉÇÇÇÈɽ±°¯®¬£�—𢡡›Š�‰„„‚ƒ‡��…vspddllmh[RJIbyfYXWUPMOSVSRPQSWX\aehlptxyrE-18DNUZj}‰Œ‡a:0EJJHIJKIJNUWT[n Ã­‹¡°¢€vªßÔËÚØÂ�ÇÉÃÃÄÈÊÉÈÄËÍËËÊÉÇÆÉÍÍÉËÌÊÉÌÌÆÄÄÀÀ¾¼½¸°­¬©©­´°£™•˜�¥§¡‘�‡‘¢˜�–˜¡¦“„wuy}zsrx~qg^ahnpw}‰’™Ÿ�zdMHKKOSVXZ]^adfhknqqmjN.,5CXw„ˆ‹ˆ†wSAADHLH<6Hh}�žŠ\DA=:CJP`et‡“|Pa|ÀéÀ¡§—wv±ÓË»¸»·¡‚ÈÇÃÃÆÊÌÍÌÅÉËÅÂÈÈÉËÌÌÍÉÇÈÉÆÈËÈÆÄÀ»¶³¯®°«®¯«­³°¤•�‘— §§¡’ŠŠ‚�—�ŒŒ˜ž£¬†{Œ•�…‚‹‘yu‚‹……„|ww‚�š£ª‘h\[\^abbdfhkosvz{xusY:17Sz����‘‹•{PLNQRSTL>?Nd�¤œreŽ’‰�’�·ÉÆ©“�ŠÄÙÁ»»²­½Æºµ·µ²³§‚ÇÆÆÄÁÇÉÊÉÉÉÉÄÃÉÉÊÊÊÉÈÉÈÈÈÈÊÇÁ¼·³¯¬©«¯®«­¯±¶µª›ŽŠ�›£¦§¤—xƒ‡zŒ�Šˆ�—~la\gg`ZU[^RNMX`ahplbZPIT[p{ZEDCCDDDFHHJNQSUURQP?,()C_c[MS_\ib?8:;;<=@=2+/=Usv_e’|ggdi{„�|ˆsm�€|}|ƒ~vy{{|}~vƒˆˆ‰‰ˆ‰‰‰‡ˆ‚yzzzyvptzxuvuvwxxyyzxvuppuvuy€~yyxvwxxw{xvsrvxxwvwuuysswxxutywqsttuxssustuqv†’““’”“““‘‘‡xzxusuwwuuvwvwwxyxxwtsstuvz}}{yyzxxxzzyxzyvrrwxwwvuuuxttxxwuvzvpsutvxstttvtu€‚„ƒƒ‚€��€�xurswxvuvvvwxvvxxutsstux||{yxyz{|}~~~~{vstwxxuuvtuxstwwvuuytqusswxrtutuv||||zxyzxvtstvwwwwvvwwvwwvurrsuvx|}{yxyz{}€€€€�ƒ�ztswxvuvutuxstwwvuvxtsutsxxqttsv}||{xxyvrsvwttvxxvuvwvvvsrrsuw{}|zyyzz|~€€€€€‚„~tsxwvvuutvxsuwvvuvysrtttzvrusu}|{wstsruwvtruwxxutuvutrrtuw{|{zyxyz|~€€€€€„€tsvwwuutsuwruwwuuwxrqstwztrtv€|wurrvwwvuvtswxxwutsrrstuy||{xxyyy{~€€€€€€€€€€€ƒ~sswxvuutsuvruvvvuxxrqtvxztrv~wutvxyxuuvvrtwxxurqqrtvy||zyxxxyz|€€€€€€€�‚zsswwvutssuvsuvwwuxxrruvyyru~zyvxxvuuvvtsvwvsqrstv{}|zxyxxyyy|€€€€€€€€€€€€€‚wtsvwvusstvvruwwwvyyquvvxwtzutvvvuvvustusqprty||{yyzyyyyyz|€€€€€€€€€�{vtrvwvtsttvwsvwwwvzwruuuwx€zuuxwvvvvtqqqqrsx}}zxxyyyyyzyy|€€€€€€€€€€~{xtrvwussttxvrvxwvvyvruuux~ytvwwvvvsppqrtw{|zxyyyyxxyzyyz€€€€€€€€}z{wtsvutstuvxuswxxwvzurtuv~xtwwwusqqrruy{{yxyyyxxyyyyyzz}€€€€xxzwtrtuuttvuvvsvxxxwzsqtw~vuxvsqpqsvz|zyyyzyxxxyxyyyyy{€€€€{vy{xtruvuuvutwurwxyxxxrrw|uvtrqstw{{zxyzzzyyxxxxxyzzxx}€€€€€€wuy|xtruwvwuttwusxzzyxwrv}urrsvz|{yxyxyy{zxxxxxyyxwuuz€€€€€€zvvy{wsquwwvtttwttyzzwwwt~rsw{}}zxxxyyyzzyxyxxxwuttuy€€€€€€€€€}wvvy{wtruxwvuttxttyzwuwy|v|}{zzzyxyxyyyyyxywvtttvvu}�€€€€€€€€xuvvy{xtrvxvvttvwruyxwux|{zzzzyxyyxyyyyxvttutuuux{€�€€€zvvvwy|xtruvwuvwvusuxwvw€{zz{{yyxxxxyxwutttuutvx{}}€~~}|zzyyyyz{|}~€|uvvwwy{yuruxuvxvtvstwwy|z{{zyyxwwxvsstuuuuw{|||{{~}|zxvutsrqqqrqrrstuvy{}}xwvvvvx|zuruuuxvtuvstwy{z{zyyyxusssstttwz{||{z{{}€€€~}zwtrqpnkkkiiiijijknpqqqrstuvwwwwwy|yvrsvwuttuvsty{z{yxwvsqrtsstwy}}{z{{{{{~€~}|yuqljjiihfegjklnooomknmihilopqrrstvwwy{yusuyvtttvwrw{zyvtssssttuy{||{z{{{{zz{~€~~}|zxurmfcabceghknprtsstutttrrohddfmrrsssrrsvx|yusvwuuuuvts~wussststvy{|}{zz{{{{{{zz{~}{zywsmgjib`bcgkprrqqrrqqqqqrssstqkeemprrstssrsvz~zutuuuvuusv|ttsttuxz{{{zzzzzzzz{zzzz{{yyvpjdabhkeejnrsqppqrrqpqopopqqqqrpjemqqsssttsuwyz}yvsstvvvsu|tsuw{|{{zz{z{{zzzzz{zzz{zywskdca``hpnpttsrqrrsuuuutsrqqqpppoppjlqrrssttuvwyxy{yussvwxtv|wz{|{zzz{{zz{{zzyyz{yzzywtqqicbabdmtssssqqstuwxyyywuusrrrrqqppmnqrrrstvvvvxxwy|yvstwwuv€|{{z{zzz{z{zzzzzyzzzyyywrosoecccipttsssrstvvvvwxyyxxvttsrssssspprrrssuwvwvxxxxy|yvstxvw€{z{z{zyy{{{zyzzzzyyzyxxsnomfddgmuvutssrstvvutttuvwxxwvutttttvvtrrsvwwttvwvwwxxxy|zurtuw€|zzzzzyzzzyyyyzzzyyzyxupppkefjqwyvttssstvvwutttuuwxxwvututtvvwwssvz{ywuuwvvwwxwwxzytrsw|zzzzzzzyzyyyyyyzyxxwutprrmglrwzxttttstuvwvuuuuwvuwxvvututtuuvwuvxxz|zwutvvvvxwwwwzyuqu{yzzzzzyxyyzzyyxwvuuvvvrrtpkpvyywtuttuuuuutuvwvwxwxywuuvvuttttuuwvwxz{ywutuvwwwwwwwzytt{zzzxyzyyyyyxwuuuvwwwvvusttqrxzzwtutuuuuttuuvwxy{zyy{yvutvvvusstwwvxxx{zwusvwwwwwwwwyxv{zzzyyyyxvuuuuvwwwvvvvvvvsssvyyzwuuuuuttstuuxxz{|{wxzxvttwxxxvrsvwwwwwxzzwusuwvvvwwvvxy{yyyxxvutuuwwwvvvwwvwwvvvttuuwyxvutututssttuvvvuvxwxxutttvyyzyttxwvwxwwwzzwvsvwwwwxwvvyzxxvuuuuuwvuuvvvwwwvwwwwvvrrstuuuvuuttssrrstssstuwxwvsstttxwzzuuwwwwxwwwxzzxvttvwxxwwvx~vuvuvuvvuvvvvwvvvwwwwwwvwuqsssttvwvttsrrqrrrrsstttuuttutttxvuxuuwwwwwwwwxwxzywttwwwwwvx}vvvuwwyzxvvwvwvvwvwwwwvvxuqsrsssvwutttsrrqqrrsssstttttuttuwzwwuuwwwwwwwvwxww{zvttvwwwvx}vwyz{zzyyxuxyyywvvvvvvvvwupsspqsuvusstsrqqrrrsssttttttutttuxxxvvwwwwwwwvwwvvy|ywttuvwwy~|}}|{zyxxwvvy{{yxwwvvvvvvvqsqostuvuttsrqppqrrssstttstvvttrruxwxuuwvvwwwvwwwvwy{zwtstvvy}|{{{zxwxwwvux{|{yyvvwvuvvsrppustvwuurponnoqrstttsstuvutrposwvyxtuvvwwvvwwwwwyyzzwtrtux}|zzzzwxwvvvtuyy{|{xwwwuuussrrtrsvwvurppoonoqrtttssuvttsqporvuxywttvwvvvwwvwwyxwzzwtrsw}{zz{ywywuuttuxyxy||ywwvvuttrssrsuwvvtrqqpppprsssssvuuussrqrutwwwwutuvvvwwvwxxxwxyyvtqu|z{{zwxxwwuuvvvyyyy{}|zxwwuustsrsuwvvvttsrrrrrsssrtvuvyxvutuuswwtvxvtuuvwwvwwxwwwxwywtt|{||zxxwwwwwvvtxzyyyy{|{zxvuuusqruwwvvvutsssssssssuvvwwwutvxurvwutuxwttvvvvvwwvwwwwwxwv€}|}|yxyyxwwwwvuvyzyxxxy|{yvuvvtrqswwvuuvtsttssssttuvvvwwtuy{wtvvuutuvxutuvvvvvvwwwwwvxy€~}}|yyyyywwvvvvuwzzyyxwxvvuutvusprvvvttutstssrqrtuuuuuvvuvyzyvvvuutttwxvttuuuvvwwwvvvux}||{yzzyxwwwvuvvvyzzyxxxusstuxxsqsvvvutttssrqppqstuutttuwwxyxxxvututttuwwuuvvvuvwvvvvuw}|{zyzzyxwwwvvvwuwyzzzxxutrrux{urstuvusttsspoqrstuutsstuwwwwwxwuuuutttutvwutuvuvvvuuutx€}|zy{{zzyxwwwwvvvvxzzzywtssruz}vssuvuusrrsronqstuvvvvvvwxyyvvxwuuuuuuutttuwvuuuvvvutttx€}|zz{{zzzxxxwvvwvvwz{zywvsssuz|xsstvuvtrqrqnnpstuxxwwxwwxz}wsxwuuuuututttttwwttuvvutttw€|{y{{{zyzxxxxwwwvvux{yyxwuttvxzzussuuvvsqrrompssrsuvvwwvw{}wswwttuuuuuuuttssvwusuvuuttw€}{z{{{zzyyxxxwwvvvvvxyyxwvttvvxzwrstuvwuqrrqopqppprsuvvvvxzwsvusttuuuuutttsstuvutuutssw}zz{{{zzyyxxxxxwvwvvwzyxwwvvvtz}xtsstuvvtqrqponnpqrsututtuwwuutsssttttuuuttttttvvutsssv€|z||z{{zyyyyxxxxvwwvvyzyxwvuus~„ztrssuuvurrrpmllnqstuuuuuuvvvusssssstttututttusruvusssv{z}|{{{zyyyxxxxxwwwvuwzzzywvvu~†~tqqrstuusrsroklmnpsstsuuuuuvussttssttttsttutttsstvusrv€{z||{zzyxyyxxxxxxwwwvvyzzzyyyv~‡„vppqrsttsrrrpnmllmnooprssuuvuttttstsssssssttttssstuurt{{}|{z{zxyyxxxxxxxxwvwwz{{zzzx~…ˆ}ppqpqstsrqrrqonmlllmpqrruvvuutttstssrsrsrrtttsttsrsut||}{zzyzzyxyxxxxxxxxwwvx{{{{{y}„ˆ‡voqporsssqrsrqpnmmmmpqqrtvvttttttssrssrsssssttttrqrrs|||zzzzzzyyyxxxxyxxwwwwwz||||z{ƒ‡Š‚rnppqrrsrrtssrqpoooprrruwuttttuutsstssssssssuttsrqqp€}|{z{zzzzyyyxxxxyxwxxxwvx{|||{{‚ˆ‰Š€tppqqrrrrsssrrqqppqrstvwustsstsstsstssssssrssssrrqq€}}{{{zzzyyyyyyxyxxxxxxwvwz|||||�‡ˆŠŠ‡zpqqrssrsssssrrrrsstuvvtsttttsssssssssssssssstsrqr€}|{{{zyzzyyyyyyyxxwxxxwvwx{}|}�…‡‰ŠŠ…soqrttsrsssssrssstuuzyutuuuttttssrsssssrrrsssrrqr€}{{{zzzzzyyxxyyyxxwxxxxwwwy||}‚‚€…ˆ‰ŠŠ~oprsutsssssssssstuyƒ„{vvvuttststssrsssrrsrrrrqqr€}z{{{zzzzzzyyyyyxxxxxxxxwvwz|„„ƒ�„‰ŠŠ‰znqsttttstsststutx‚‡‡wvvuuttttssrrrssssssrrqqr€}zz{{zzyzyyyxxxxyxxxxwwwwvvz€‚‚„ƒƒ‚ƒ‡Š‹†umstrststttttutw{~€…‰ˆwuuutttsrssrrssrsrrqqqpr|zz{zzzzzyyyxxxxyyxxwwxwwvv}ƒ„ƒƒƒƒ„„‚…‰‹‚rnssssssuuuttuwz}€…‰‹‡wruvtttsstsrsrrqrqqrqrqq€}{|{zzzzzyyzyyyxyyyywxyxx{}�„„…„„„„†„‚ƒ‡Š~nqstrrstttttuw{}†ˆˆ‰}sstuuussstttsrrrrrrpquu€}{{{zzyyyyyyyzzxxxxzz|~~‚‚ƒ„„„„…„„†……ƒ‚ƒ€roqsssttssstuw{~€…††ˆ~vuqtuutsttsssrstsnfjzƒ�€|{|{zzyxyyyxyyxwxy{�‚‚‚ƒƒ�‚ƒ„„„ƒ„………„……‚�}xtrrtsssssuvvz}~ƒ…ˆ†zuuuvtuwuussnejr~p[\owrp€|||{zzyxxyxxyyy{}�‚ƒ‚‚‚‚„ƒ�€ƒ„…†„ƒ„ƒ„„„…ƒ‚�ƒ�ztstuuuuvwxy}ƒ‰‰ƒyvvvwxw{{ytujZhrpbZ]_][^xjmhikjhgmkv„‚‚‚ƒƒ…†…………„„„„„ƒƒ„„„„††……„…„ƒƒƒƒ„„„ƒƒ„„„„…„ƒƒ„„„…†…„„„ƒ„…………………†…†„„††ˆ‡ƒ„sPQNQKMNOSUg†‚ƒƒ„……………………„„„„„„„………†………„ƒƒƒƒƒƒƒƒƒƒƒ„„ƒ„……„„ƒ„………†…„„ƒƒ„……†……†††††……††ˆ†ƒ}ststtustvuy„ƒ„……„„„……„„„„„ƒƒ„…………………„ƒƒƒ‚‚‚‚‚�€�€€‚‚ƒƒ„…„„ƒƒ„………†…„„ƒ„„……†…†…††…†„……‡ˆ…�ƒƒ‚‚‚‚ƒƒ‚ƒ„…………„ƒ„„„„ƒƒ„„„„…†…………„„ƒƒƒƒƒ‚‚���€€�€€€€���ƒ„„„ƒ„„„„…………„„„……†………††…†…„…†ˆˆ�‚‚‚‚ƒƒ„ƒ„………††„ƒƒƒ„„„„„„……†……………„„„ƒƒƒ‚���������€€�€�€�€‚„…ƒ„„„…„…†…„…„„………†…†††…†…„†‡†€��‚ƒ„………………„†…ƒƒƒ„…„„…………………„„„„ƒƒƒ‚‚�€�€€���������€���€€‚„„„„„„……††……„„„……†††††……†……††€�ƒƒ…†……„……„……„ƒƒƒ„………†…………„„ƒƒ„ƒƒƒ‚��€€���€��€������€�€�€�‚„„ƒƒ„„…†††……„„………††…†………†………�ƒ„„……………„„„……„ƒƒ„……†……„„„„„„ƒ„„ƒ‚‚�€�€�€€���€€�€�€��€€�€€€€ƒ„„„„„„…†††………………†††………„…†……�ƒƒ„……„„„„„„…„ƒƒ„…†………„ƒƒƒƒƒƒ„„ƒƒ‚����€��€€����€€€���€€�€€€€‚„„„„„„…††††………„„…†††…†…„…†…€�„……„„„„ƒ„„„„„…………„„ƒƒƒƒƒƒƒƒƒƒ‚‚����€€€€€€����€€€���€€€€€€€�ƒƒ„…„„……†…††……„„„…………†……„††€‚„…„ƒ„„„„„…†…………„ƒƒƒƒƒƒƒƒƒƒ‚ƒƒ‚���€€��€�€€€��€€€�€€€�€€€€€€€ƒ„„„„„„…††…††…„„„………†……………†€‚„„ƒƒƒ„ƒ„……†…„„ƒƒƒƒƒ‚ƒƒ„ƒƒ‚‚ƒ‚�€�€€�€€€€€€���€€��€���€€€€€€€�ƒƒƒ„„„…††…†…†…„„„„……†……………€ƒ…„ƒƒ„……………„ƒ‚ƒƒƒƒƒƒƒƒƒƒƒ‚‚‚‚�€€€€€�€�€€€€�€�€€€�€���€€€€€€€€‚„„„„„……††……††…„„ƒ„„…………†„€„„ƒƒ„……………„‚‚‚‚‚‚‚ƒƒƒƒƒƒƒ‚ƒƒ‚€€€€€€€€€€�€€���€��€€���€€�€€€€€�„ƒ„„„…………†…†‡†…„„ƒ„„……………�…„„……†…„ƒƒƒ‚ƒƒ‚‚‚‚ƒ‚ƒƒƒ‚ƒ‚ƒƒ‚€€€€�€€€€€�€���€€��€����€€�€€€€€ƒ„„ƒ„………„…„†‡‡†…„„ƒƒ„…††…‚…………„„ƒ‚‚‚‚‚‚‚‚‚ƒ‚ƒƒƒƒƒ‚‚ƒ„ƒ�€€€€�€€€€€�€�€€€€€��€€€€€�€€€€€€€‚„„„„„……„„„…‡‡‡‡…„ƒ‚ƒ„††„‚……„ƒ‚‚ƒƒƒ‚‚‚ƒ‚‚‚ƒ‚ƒƒƒƒ„„„„„�€€€€€€€�€€€�€€�€�������€€€�€€€€€€�„„„„„„„„„ƒ„††‡‡……„ƒ‚ƒ…††‚„ƒ‚‚‚‚‚ƒƒ‚ƒ‚‚‚‚‚ƒƒƒƒ„…„„„„‚€€€€€€�€€€€��������€��€�€€€��€€€€ƒ„…„ƒ„„„„„„…†††††„ƒƒ„„……��‚‚‚‚‚‚‚‚‚‚‚‚‚ƒ‚ƒƒ„„„„„„ƒƒ�€€€€€€€�€€€€€�€€���€€€�€�€€€��€€€€‚„„„„ƒ„„„„………………††„ƒƒ„…„��‚‚‚‚‚‚‚‚‚‚‚‚ƒƒ„„„„„„„„ƒ‚‚€€€€€€€�€€€€€€������‚‚ƒ‚ƒ‚‚��€€�€€�„ƒ„„„ƒ„ƒ„…„„…„„†‡†„„„„„€���‚‚‚‚‚‚‚‚‚„„„„„„„„ƒƒ‚‚‚�€€€€€€€€€€€€€€��‚ƒ……†‡‡‡ˆˆˆˆ‡‡††…ƒƒ�€�„„„„„„„ƒƒ„„„†…ƒ…†††„„„ƒ€�‚‚‚‚‚‚‚ƒƒ„„…„„„„„ƒ‚‚‚‚‚‚�€€€€€€€€€€��‚ƒ…‡ˆ‰‹Œ����ŽŽ����‹Š‰ˆˆ‡‡†……„„„„„ƒƒ„„„†…„…†††……„ƒ€�‚�‚‚ƒƒ„……„„†…„„ƒ‚�‚��‚‚��€€€€€�€�‚‚„†ŠŒŒ�Ž��‘���ŽŽ���ŽŽŒŒŽŽ�ŒŒŠ‰‰ˆ††…ƒƒƒ„„„…„ƒ„…†…†…„„€�‚‚ƒ„„……„„„„„‚ƒƒ‚‚‚‚‚‚‚‚‚€€€€��‚ƒ„‡ŠŽ‘“’’‘‘�����Ž���ŽŽŽŽ�ŒŽ�’‘�‹Š‰‰‰‰ˆ‡†…„„„„„ƒ„…………††…€‚ƒ„„…„„„„ƒ‚‚‚‚‚‚‚�‚�‚‚‚‚�€�‚‚ƒ„†‰�Ž�’““’�����‘‘‘‘’’’““’““‘ŽŽ�’�ŒŠŠ‰‰‰ˆˆˆˆ„ƒƒ„„„„……………‡†�ƒƒƒ„„„ƒ‚‚‚�‚‚‚‚‚‚‚‚‚‚‚�‚�‚ƒƒ„‡‹Ž‘’�Ž‘‘��ŽŽ�������‘‘‘’““””“’‘‘’‘‹‰Š‰‰ˆˆˆˆ†ƒ‚‚ƒ„ƒ„……„„…‡†�„ƒƒƒ‚‚���‚���‚‚‚‚‚‚‚‚‚��‚ƒ„†‰Ž‘’’’�Œ�Ž�Ž���Ž�����Ž���‘’““’’’’��ŒŠŠ‰ˆˆˆ‡†„„ƒƒƒƒƒ„„…„ƒ„†…�ƒ‚����‚‚����‚�‚‚‚‚‚‚‚‚‚ƒ„…‡ˆ�‘’’‘�Ž‹ŒŽŽŽ�Ž��Œ‹Š‹‹Œ����‘’’‘‘’���ŒŠŠˆ‰ˆ†…„„ƒƒƒ„ƒƒƒ„„„ƒƒ„…����‚��‚‚‚�‚‚‚‚‚‚‚‚‚‚‚‚ƒ„†ˆˆŠ��‘�ŽŒŒ�Ž�ŽŽŽ����ŒŒŒ‹‹Œ�����‘‘‘��Ž�‹ŠŠŠˆ†…„„„„„„ƒƒƒƒ„„„„ƒ„„€�‚�‚��‚‚‚�‚‚‚ƒ‚‚‚‚‚‚‚ƒ„†ˆˆŠŽ���Œ‰‹�ŽŽ�ŽŽ���ŽŽŽŽŽ�ŒŒŒŒŽ���‘��ŽŒ‹ŠŠˆ‡„„„ƒ„„„„„ƒƒƒƒ„„„„„„„€�‚�‚��‚‚�‚‚‚‚‚‚��‚‚�‚‚„‡ˆˆŒŽŽŒŠ‡ˆŒ�ŽŽŽ���Œ�����ŽŽ�Œ�ŒŒ�����ŽŒ‹Š‰‡„ƒƒƒ„ƒ„„„ƒ„ƒƒ„ƒƒ„„„„„ƒ€�‚����‚‚‚‚‚‚‚‚‚‚‚‚‚‚ƒƒ…‡‡‡‹Œ‹ˆˆ‡‰ŒŒ�ŽŽ�Œ��Ž�������Ž��Ž����ŽŒ‹‹‹ˆ„ƒƒƒƒƒƒ„„……„ƒƒ„ƒƒ„„„„„„€��‚‚‚�‚‚‚��‚‚‚‚ƒƒ„ƒƒ„ƒ…‡‡†‰‹Šˆ‡ˆ‰‹���ŒŒ��Ž������ŽŽ���ŽŽ����ŒŽ��ˆ„„ƒƒƒƒƒƒ„…„„ƒƒƒƒ„„„„„„„€���‚‚‚‚‚‚‚‚‚ƒƒ„„„„ƒƒ„ƒ„…‡††ˆ‰‡‡ˆŠ‹�Ž��Œ�Ž�����Œ‰‰‹ŽŽŒŒŽ��Ž�����‰ƒ„„„„ƒƒ„ƒ„„„„ƒƒƒ„„„„„„„€���‚‚‚‚‚‚‚ƒƒ„„ƒƒƒ„„ƒ„„„„„…†ˆˆ‡‡ˆ‰‹ŒŽ����Ž���Ž�Œ‰ˆŠ��‹Œ��‘���ŽŽ�Š„ƒƒ„„ƒ‚ƒ„ƒ„„„ƒƒƒ„„ƒƒƒ„„€�‚‚‚‚‚ƒƒƒƒƒƒƒ„„ƒ„„ƒ„„„„„„…‰��Œ‰‰Š‹Œ����Ž��ŽŽŽ���ŽŽ����‘‘’��‹‰‹Žˆƒƒ„ƒƒƒƒƒƒƒ„ƒ„„„„ƒƒƒƒƒƒƒ€�‚‚ƒ„„„ƒƒ„ƒ„„„„……„„…„„„„„†‘–’��‹ŒŒ�ŽŽŽ�������������Ž�‘‘‘‘‘Ž�Š‹‹†„„„„„ƒ„ƒƒƒ„„„„„„ƒƒƒƒƒƒƒ�‚ƒƒƒƒƒ„ƒƒ„„„„„„……„„„…„„„ƒˆ–•��ŽŒ‹Œ������’‘‘‘‘��������‘�‘‘’���Љ„„„„„ƒƒƒ„ƒƒ„„„„„„„ƒƒƒƒƒ„�‚‚ƒƒƒƒ‚‚‚ƒ„ƒ„„ƒ„„„„„„„…„ƒ‰–”����‹‹Ž����‘’’““’’‘‘‘��‘‘‘‘’““‘��‹‡ƒƒƒ„ƒƒƒƒ„ƒ‚ƒƒƒƒ„„„ƒƒƒƒƒƒ€‚‚‚����‚�‚ƒƒƒƒƒƒ„„„„„„„„ƒ‰•”‘��Ž‹‹�����‘’““”“““’’‘‘’’‘‘’”•”���ˆƒƒ„ƒ„ƒƒƒ„ƒ‚ƒƒ‚‚ƒƒ„ƒƒƒƒƒ‚€€€€€���‚‚ƒƒƒƒ‚ƒƒƒƒ„„„„„„ƒˆ“”““’Ž‹ŠŒŽ��‘“••••”“““’’’“’‘’“•—˜”Ž�‰„„ƒ„„ƒƒƒ„ƒƒƒƒƒ‚ƒ„ƒƒ„„ƒƒƒ€€€��‚‚‚‚ƒƒƒ„„‚‚‚‚ƒ„„„„„„„†�””•‘��Š‹�Ž�“•–——–•”“““’’‘’““”—™š—�ŽŠ„ƒƒ„„ƒƒƒƒƒƒƒ‚ƒ‚‚ƒ„ƒƒ„ƒƒƒ€€€��‚‚‚‚ƒƒ„ƒ„ƒ�‚��‚ƒ„…„„……Œ“•“�‘Ž‹‹Œ��“”––——–””““’’’”••–˜š›—�ŽŒ…ƒ„„„ƒƒƒƒƒƒƒƒƒƒƒ‚‚ƒƒ„ƒƒƒ€€���‚�‚�‚ƒ„„…„��‚���ƒ„„„„…‰�•’’’ދЋŒŽ‘“””•–—”“’’’’“–“‘’’•—”��Œ†ƒƒ„„„ƒƒƒ„ƒƒƒƒƒƒƒ‚ƒ„„„„ƒ€����‚ƒ‚‚‚ƒ„„„„ƒ��‚ƒ‚‚‚ƒ„„…†Œ”’““�ŒŠ‹ŒŒ�‘’’“””“‘‘’“’”–”���‘‘���Œ‡…„ƒ„„„„„„„ƒƒƒƒƒƒƒƒƒ„……„€€����‚‚ƒ‚ƒƒ„„„„‚€�‚‚ƒ‚�ƒ„……ˆ�’””’�Š‹ŒŒŽ���’’‘‘‘‘‘’‘’’“’���ŽŒ��‹†…†…ƒ„„„„„„„ƒƒƒƒ‚ƒƒƒƒƒ„„€€€€€�‚‚‚‚ƒƒƒ„ƒ„ƒ��‚�ƒ„ƒ‚ƒ…†‡Š‘”•”�Š‹Œ���Ž�����‘‘‘‘‘‘�ŽŽŽ�ŽŠ‰�ŽŠ†…………„„„„„„„„ƒƒƒƒ„ƒ‚ƒ‚‚ƒ€€���‚‚‚ƒƒƒƒƒƒ„„„‚�‚�‚ƒƒ„…†‡‡‡Œ“––’‹‹Œ��������’“’‘�������ŒŒ‰ˆ‰‹‰††……†…„„„„„„…„„ƒƒ‚ƒƒ‚ƒƒƒ€€���‚�‚ƒƒƒƒƒƒ„„„„����‚ƒ„†ˆ‰‰‰‰�““�‹Š‹�ŽŽŽ��‘’“”“�Ž�����Ž�Œ‰ˆ‰‰‡…††…††……ƒ„„„„„„„„‚ƒƒƒƒƒƒ€€���‚�‚‚‚‚ƒƒƒƒƒƒ„ƒ����‚„†ˆ‰‹Šˆˆ�ŽŽ‹‹‹���Ž�‘’““’‘Ž�ŽŽ�’’’�ŽŒ‰Šˆ†……†………†………ƒ„„„„„ƒƒƒƒƒƒƒƒ�€��‚�‚‚‚‚‚ƒ„ƒƒƒƒƒƒ�€��‚„‡ˆŠŠŠ†…‹Ž�Œ‹‹�����‘””“‘�ŽŽ�“””•”’�ŽŒ‹‰†……………†††††…„„ƒ„„ƒƒƒƒƒ„„ƒ€€��‚�‚‚‚‚‚ƒƒƒ„ƒƒƒ„‚���‚„†ˆŠŠˆ†…‰Ž��ŒŒ��‘��‘”•“��‘“–———™˜•‘��Œˆ………†……††††††…„„„„„ƒƒƒ„„„ƒ€�‚‚‚‚�‚‚‚‚ƒƒƒ„ƒ„„„ƒ‚�‚‚„…‡ˆ‰ˆ‡‡ˆ��Ž����‘‘��“”“��“–™šššœ›˜“�ŒŽˆ…………………†††††††…„„„ƒƒƒ„„„ƒ€��‚�‚�‚‚‚‚ƒƒƒƒƒƒƒ„ƒ„‚�‚„…†‡ˆˆˆˆˆ�‘����Ž�‘��’“’�’•—˜˜˜™šš™”�‹�‰…††††………††…††††…„„ƒƒƒ„„„ƒ€€���‚‚‚‚�‚ƒƒƒƒƒ„„ƒƒ„‚��‚„……†‡‰�“��‘‘‘����‘‘’’“”•–•”’‘’’’’�‹‹‹ˆ†††††…†…††……†††††„„ƒƒ„„„ƒ€€���‚‚‚‚ƒ‚‚ƒƒƒƒ„„ƒƒƒƒ‚�‚ƒ„…‡ˆˆ’¥›‘”“’’�����‘‘“–˜˜•’�ŽŽŽŽŽ�‹ŒŠ†††††††††††††…†††††…ƒ„„„„ƒ€�����‚‚‚ƒ‚‚ƒƒƒƒƒƒƒƒƒƒ‚��‚ƒ……†…•¬¥–––”““’‘‘���’–˜˜—”’�����ŽŒŒ‰†††††††…‡††‡†††‡††††…„„…„„€�‚‚�‚‚‚ƒƒƒ‚ƒƒƒƒƒƒƒƒƒ„ƒ‚���‚‚ƒ„–±¬œ–™–”“’’’���’”—˜˜—––––””’�Œ‰……††††††‡†‡‡†‡‡‡†††††…„„„„€�‚��‚ƒƒ‚ƒƒƒƒƒ„ƒƒ„ƒƒƒƒ„ƒ�����‚‚’³°£™™˜—”“““‘��‘‘“•—˜˜™™š™˜–�Šˆ………†††††††‡‡‡‡‡††††††††…„„���‚‚‚ƒ„ƒƒƒƒƒƒ„ƒƒ„ƒƒ„„„„‚����‚�Œ¯³©¡™š™–”””“‘����‘”–—˜˜™——–�‰‡††…†††††††‡†‡‡‡‡†‡††††‡‡†…���‚‚‚ƒƒƒ‚ƒƒƒƒ„ƒƒ„ƒ„„„ƒ„ƒ������…¦´ª© š™˜••–•“‘�����’“•––••”�ˆ††††††………††††‡‡ˆ‡†‡††‡‡‡‡‡‡���‚ƒ‚‚‚‚‚ƒƒƒƒ„ƒƒƒ„„„ƒƒ„„ƒ�€��€�›²«¨¨¢›˜—•——”’‘�ŽŽŽ��’““’“�‹‡…†‡††††††††…†‡‡‡†‡‡‡‡‡‡‡†‡‡€��‚ƒƒƒƒƒƒƒƒƒƒƒƒ„„„„ƒƒƒ„„„‚����€�ª®©£§¤™˜–˜˜•“’��Ž�ŽŽ������Ї†††‡†††‡‡‡††‡‡‡‡‡ˆ‡‡‡‡‡‡‡‡‡€��‚ƒƒƒƒƒƒƒƒƒƒƒ„ƒ„„„„ƒƒ„„„ƒ�€��~„�­ª¤ £�˜——˜–”“’��Ž�Œ��Ž�ŒŒ‘އ†††††‡‡‡‡‡‡†††‡‡‡‡‡†‡‡‡‡‡‡€�‚ƒƒƒ‚ƒƒ‚‚ƒƒƒ„„ƒ„„„„„ƒƒ„„„‚�€€||‰ ©¦¢Ÿ ›™—––•“’‘���Ž�����“¢¢‘‡†††‡††‡‡‡‡‡‡†‡‡‡‡‡‡ˆˆ‡ˆ‡‡€‚ƒƒƒƒƒƒƒƒƒƒƒ„„„ƒ„„ƒ„„„„„„„„‚€}|{|…™¥£ ŸŸ™™˜”•”’‘‘�������’Ÿ¦ª¦”‡†††‡‡‡‡‡‡‡ˆ‡‡‡‡‡‡‡ˆˆˆˆˆ‡€‚ƒƒƒƒƒƒƒ‚ƒƒƒ„„„„…„„„„„„„„„…‚}{{{{|��œ¡Ÿ œ–™–•”’‘‘‘��‘�ŽŒ“ ¤©¬§”‡†††‡‡†‡‡‡ˆˆ‡‡‡‡‡‡ˆˆˆˆˆ‡€‚ƒƒƒƒƒƒƒƒƒƒ„ƒ„„„„„„„„…„„„…„€{{{{{{{}‡–ŸžŸ˜•––”’‘‘‘‘����‹‘ž¤ª«¬£‹„††‡†††‡‡‡ˆˆˆ‡‡ˆˆˆ‡ˆˆˆ‡€‚ƒƒƒ„ƒ„„ƒƒ„„ƒ„„„„„„„……ƒƒƒ‚€}z{{{{{{{|‚�˜Ÿ�•””•”“’‘‘����Œ�œ¥ªª±¬�‚ƒ…‡‡†‡††‡‡‡ˆˆˆˆ‰ˆ‰‰ˆ††€‚ƒƒƒƒ„„„„„ƒƒ„„„…„„…ƒ‚‚€~|}|zz{{{z{z{{}ƒŒ•“’“”““’‘���ŽŽ��™¥©°¾°�…„„„…†‡‡‡‡‡ˆˆˆˆ†‡Œ“�„|~€‚ƒƒƒ„„„„„„„„„„……„ƒ‚}||||{||{{z{{{{{z{{z}‚†Š�’“’���Ž����™£®½Á¥ˆ†††„ƒƒƒ††‡†Œ”�‡€�œœ�†Š‹�ƒƒƒ„……„„„…„„„„ƒ‚€~}}|||||{|}|{{{{|{{{||{|}}}~ƒŠ��ŽŽŽ�‹‹ŒŽ—¡®³±–‚………†„‚€�‚†„Ž�’‰Œ˜�›˜šœ™ \ No newline at end of file diff --git a/bin/foreman_part_qcif_422.yuv b/bin/foreman_part_qcif_422.yuv new file mode 100644 index 0000000..ed359e4 --- /dev/null +++ b/bin/foreman_part_qcif_422.yuv @@ -0,0 +1 @@ ++Øþùûþþýûüþþþþþýûþþþþþùîàââáßáâáßàãåß¿›œ¨¾ÖÛÖ×ÛÛÛØ×ÛÝÜÙÝÝÜÞÜÜßßßÛÖÕØÝáÔħ‘“””Œ�…tu†µßÞÝßÞÝÞÔÌÙÙÑÑÑÒÓ×Ô˜o�†}y§ÏÏÏÔÔØÖÔ××ÑÐÔÒËÀ¾Éº�€¢ÖÓÐÖ×ÙÙÕÐÎÏÉÌ®ww§ÈÍÒÐÏÏÈÃÏÉ“r¦ÑÏÊÁººÈÀ�i—ÀÆÖ¾1ÆÁÓäÍÕ¹ÓϺøÆËзØÄÆîкÕíâááâáààâæçÞÁ�’£¸ÉÑÔÕÒÔÙÛÙ×ÚÙÛÝÛÜÜÝÝÝÞßßÜØÔÝàÕÀ¤—““—šŒuv{Œ©ÐÞÝÝÜÜÛÚÛÇ¿ØÒÎÑÐÑÑÈÑΘu…ˆvvªÎÏÒÓ××ÒÓÓÑÎÏÏËÆÌν¬Ž~ªÖÕÔÖØÙÕÑÎÏÉÉÁœvyªÊÑÑÏÎÎÎÌѼ‚}´ÑÍÅÃÀÈÆ¦xn¦ÍÒ¾0±«Å­¹ˆ¿Ãгް±‡Ê°Š»Ë¦´çãááàßáäåÚÁ¢‘—¬ÉØÜÙÕÔÕ×ÙÓÎØÙÔÙÜÛÜÜÝÛÝÙØÙÙÝÛÓºŸ‘�‘——Š{qx”·ÖáãßÜÜÜÜÝÜÛÙÙÙÔÑÒÐÏÐÍÐͼ’t…„ms¤ÊÏÓ×ÖÓÓÔÒÑÐÌÌÏÎÏÂÀ¯„|­×ÓÔÙÙØÔÍÎÎÎÌÌ«z‚·ÎÌÍÍÏÊËÎÓ²w€ÀÓÊÆÄË˵˜os´Ô¼.Öᩱ½Æ Ëб¥­Ä¿œÑË®¡åΡÐçàÝáæàÖ¾¢™¡·ÐÙÙØËÉÖÖØÜÙÍÏÛØÖÛÚÛÛÛÝÖÓ×ÙÝÝͰ–‰‰—�’†{xƒ�ÁÙâáÞÛÜÝÝÜÝÝÛÚÜÚÖÔÓÒÏÑÑÒѸ°Â�o�†stŸÅÌÒÕÕÕÖÒÒÔÍÊÎÌÑȼé~z²ØÔÖØØ×ÐÍÐÐÈÌÒ±}‹¹ÂÉÍÎÆËÐÐÓ¬sŠÇÏÆÇÎп¶£o~ý)¹Ð´ËäâÈÖâáãäáàÒÝâçÚåÝÕßàáæãÔº¥›£½ÕÖÖÕÖÖÃÈÖÖØÚØ×ÙÙØÛÚÖÛÝÚÙÔØáÜˬ�…Š—™‘�tu‰«ÉÜâáÞÛÞÝÝÝÞßàßÞàßÛØÓÍÌÎÑÐÐÑǼÌË”p}…wv ÈÐÓÓÔ×ÖÒÓÏËÉÌÓÉ¿ÃÂ¥~·×ÒÕÕ×ÖÓÑÐÍËÌȨv�´ÅÌÍÍËÏÐÒÕ£k’ÉÏÌÏÒÇ¿À›n�²‚­²×æÝÔÜåãäåãââßâãèáàææååÕ´›™”²ÒÚ×ÔÔÓ×ÙרÖÙ×Ó×ÚÚØÖØØÙÚÙÛÛÜ×ç’……“œ’sw“¶ÑÚÙÝÛÙÍÖ×ÙâäæèéëêëêéèãÝÚØÔÏÐÏÑÉÄÓÏšmzŠ€|£ÌÒÑÖÔÕÕÎÐÐÊÐÖËÃÂÊ˨{·ÖÔÔÖÕÖÒÐÐÍËÆÆœqŽ¿ÌÍÍÊËÎÌÕÑ‘i›ÍÍÎÐǽ¼¶ŒlˆwÂØÓÕÛÞáßÜÛÚÚÚÚÝÜÚÔÜäãàÔ¶Ÿ˜�¨·ÒÛÚ×ÙÙÙÙÙÜ××ÙÙØØÙÚÛÙÙÖ×ÛßßѸ �‹�˜�~{v›¿Øáߨ×ÛÝÕÇÔÔáêëììëìììììíííìêäÜÖÍÏÓÏÍÐÏžq{Œ�{ ÌÒÕÓÒÖÏËÎÐÓÔÍÇÉÉÈÁ x~¸ØÔÕÓÒÔÑÐÍÏÊÈÈ™tŸÌÑÍËÍÍÌÍÔăl£ËÎÏʽº·°Šq~ÛàÙàãããâáàÜÜÝÞÝÝÞâæÝɶž˜¤¸ÈËÖØÕÛÝÛÙÛÙØÙÖØÛÞÛ××ר××ÖÙÜͱ“†ˆ‘–Ž}qp„£ÆÚÝÝÙØÜÝÛÜÜâåèëêëëëëëëëëëêêêëíìëçßÔÒÓÐÊÐÓ§wzŠ�x�ÊÓÒÕ×ÕÅÆÒÎÌËÈÌǼ¹·šu�¾ØÔÔÓÕÒÏÐÍÎÌÍË�s¯ÒÍÌÐÎÌÍÎÓÀ|p²ÑÎËÀû¾³ƒƒ×ßâááááâßÛÝÝÛÜÞâãÛä‘—©ÀÉÌÒ×רÛÝÝÛÙÖÖÓÓ×ÙÛÙÔÕÖÕÖÛÛÓĬ“ƒˆ�–‰uoqŒ±ÅÕáØÐÙÝÞÝØÛâçêìíëêëëìëëëëëìëëëëêëìíìå×ÐÐ˾ÉÓ¶|vˆu—ÆÑÑØÕÍÎÓÑÍÊÊÎÎÇ¿¿¼™x‡ÂÙÓÓÕÓÏÏÍÏÍÉÍÀ‡€¶ËËÏÎÍÎÎÎÖ¶u~½ÒÎÈÈÁ¼¿¡ˆØâßàááàÝÙÝÞÛÜâäÕ»¤•™£µÏÔÎÎÍÓÙÜÝÜÛ×Ô×ÙÓÔÙÛÕÓÕÕÙÛÚÕÀ£�‰‹“›Šzmw’³ÍÞÕÑØØÛÞÞÝÜßåêëëëêëëëììëìììììììëëëììëëíèÚÐμ½È¿¨qr‡~q•ÊÓÕÓÏÔÕÔÓÍÍÎÌÍÉÀÀ¹–r…ÂÖÐÔÖÖÔÒÏÍÌÉ´ÊÍÏÌÌÎÎÐ×­r„ÁÓÎÊ¿½½®ˆØãààáßÝÚÝØÓààÓ¸�‘–¦ÁÎÒÕÏËÎÑ×ÚÜÝÙØØÖÔÔØÖÙÚÕÐÙÝÛζŸ�ŠŽ–“‰yny›¿ÒÔÕÙØØÝßÞÞÝÝâéìëëëëëìëëëëììììììììëëììììììíçØÏÌÉǭ®sr…u–ÉÓÑÈÏÕÓÔÐÍËÊËÉý³�rˆÈ×ÒÖØÙÔÎËÍÎÊͱzƒ·ÊÏÊÈÏÎÒÖÔ£g‹ÊÑÐÈÂÀ«‡ÙáÞÝÞÞÝѵ›ºÒº¤�” ¶ÍÒÑ×ÖÊÍÎÑÕÛÝÞÝÚÙÙÃÅÖÒÓÕØÙÜδœŽŒ’•Œup€ ÂÚßÚÕ×ÜÝÝÕÖÞÞáæêëëëìëëëììëëëëëìëëìëëìëìëìììëëìå×ÐÍÍÅÂÓ«kqˆ~o�¿ÎÉÎÔÔÒÏÍÌÌËÊȽ·±—t�É×ÑÒÖÔÏÍÎÎÎÌɪx‡¶ËÅÄÎÍÒÒÔЖk˜ÌÐÌÇÅ«†Øàâææãα’q•¢“–£µ¼ÇÔÑÐÙÓÌÐÏÑÖÚÛÛÜÚÚÖÍÏÑÑÖÛÔȲ˜�”˜•‡xmtŒ¬ÌÞàÛ×ÙÝÜÜÝÛÑÕàäéëêêëëëëììììëëëëììëëëëëëìììëëëììëëå×ÎÍýÊÑ¥ns†}nнÑÓÕÖÔÏÎÍÎÌÈÈdz¬¶¶švŽÊÖÌÑÓÏÑÎÎÎÎÉÇ©wнÁ¾ËÏÏÒÑÖÏŽj¤ÐÉÈ̱‡Ûçáɾ¹£�™Œ�¥¸ÈÑÏÄÎÒÔÙÐÐÐÓÚÜÚÛÛØÖØÖØÕÔÚ×ħ“�“�šˆto{˜»ÓßáÛ×ÓÙÜÜÛÜÝÙÛãçêëêëëëëëëììëëëììììììëëììììììëëëëëëêéäØÏÈÊÌÏÓ¨qq…‚t‹ÂØÔÖ×ÑÐÐÏÍÉÅÇ¿³µ½ºžv‘ÎÔÏÖÓÑÐÑÎÌÌÈÇ¢s‘º½ÈÎÏÑÒÑØÈƒs´ÏÃdz‹ÒÀ¥Ž†…���Š–²ÉÏÓÖÑÐ×ÊÒÖÏÐÎÖÜÛÛÜÛÙÖÒÖÓÑͺ£’‘”——ˆurƒ¡ÃÚàÝ××ÙÚÙÜÖÒÜÝÝÞäéëëëëìëëëëëìëëëëëììëëëììììììììëëëëëëëèèä×ÎÎÎËÌѬonˆ„o…ÃÖÑÔÓÑÑÏÌËÅÂÉÀ½¾¸¸–u•ÎÖÒÐÑÕÔÑÍÍÌÇÈ�r‡¥ÃÌÎÐÒÓÓÙ¿€½¼¼²}¦‘�š„ov¡¯´ÌÖÕÑÐÑÕÙÔÏÖÒÍÉÏÙÙÚÜÜÖÔר֯®—Š‹”š�~tv�°ÍÜÞÚØ×ÙÞÝÛÚÛÕÓßÞÞåêëêëëìììëëëëëëëëììëëëìëëìëëìëëëëëëëëëëêèèäÕÌÏÍÊÌЯzp„€j‚¾ÓÐÑÑÑÑÌËÉÃÄÁ¿Àº®¨šs”ÎϽËÕÔÔÒÎÎÈÉÊ—i�ºÊÌÏÏÒÒÒÙ¹{ˆ·Ã°m¢¹ÉË¢ju²ØÚÚÖÔÓÔÕ×ÔÑÚÔÍÏÊÓØØÚÚ××ÛÖ锈ˆ’š�uoz�®ÕáßÛØØÜÜÜÕÔÛ×ÖÜÞרåéêêëëëììëìëëëëëëëìììëëììììêëëëëëëëëëëëêêéèéâÔÎÎÏÍËѹ|nƒ€kƒ¼ÔÍÏÑÑÎËÊÉÅÃÁÃÁ¶¯¸�o™ÏÈÊÓÓÕÖÒÎËÆÌÊ’oŸÅÊÍÎÑÑÒÕØ°vŽÅµÌÛßÜÊ­½ÐÏÑÓÖÔÒ×××ÔרÏÍÏÒ×ÖÕÙÜÛÖĨ�†‹’’†vju ÃÔÝàÚÛÓÐÜØÙÛÏÒÜÛÜÜÜ×ÞéêëëëëëëëëëëìëëììëëìììëìëìëëëëëëëëëëëëëëêéççèáÓÎÏÏÌÊм„o„„p�¸ÉÆÐÔÐÌÉÈÅÁÂÄÆÇ¾»®�qŸÐÎÐÔÔÖÕÒÏÊÆÑĆsªÉÌÏÐÑÒÓÕÖ¥r›´ŠÖØØÓÍ×ÙÔÕÖÕÖÑÐÖ×ÖÖØÑËÍÎÓÔ×ÛÜÓÀ§�ˆ�–�yhn„ŸÅÝáßÛÛÝßÕÑÜØ×ÚÙÛÜÝÞÜÝåêêëëëëëëìììëìììëìììëìììììëëëìììëìëëëëëëëëêéèèèèàÑÍÏÏÍÊо{fƒp~­ÆÍÒÒÏËÆÈÿÂÈËŽ°°’q¡ÏÓÔÓÕÕÔÐËÊËμƒ{´ÌÌÐÐÐÔÔÖЙu�†ÔÕÑÐËÎÏÔÖÔרÑÐÖØÓ××ÌËÎÏÕÛÜл¡Œ†Š�‡rej†¯ÑßàÚØÙÛÝÝÝÜÛÛÛÛÜÜÝÞÞÜÞäêëëëëëëëëììììëìëìììììëìììììììëìììììëëëëêëëëêééèéçÝÑÎÎÌÌËÏ»€d{‚p~²ÌËÐÑÏÎÌÆÂÁÁÉʼ¦²½�v¦ÒÔÔÖÕÕÒËËÎÌÏ¿€…¾ÊÍÏÐÒÔÓÔÉ’q‡ÕÔÎÌÃÌÓØÖÔÓÔÒÒØ×ÐÔÔÍÍÐÖØÌ´š�ˆ‰Šƒp]gŒ·ØâÛÔÕÛÞÝÜÛÜÜÚÚÚÚÛÝÜßÖÓÞãèêëëëëëìëëëìëëëëëììììììììììììììììëìëëëëëêëëëêééééèæÛÐÎÍÏÐËÓÉ‹ex‚t{¯ÏËÍÐÐÌÇÂÁ¾ÄÌì³Ç¿‹t¨ÑÒÒÓÓÔÏÍÑÎËи|‹ÂÍÌÐÐÑÒÑÐÇ�‰ÐÒÐËÆÏÖÖÖ×ÓÒÒÕÙÔÔÔÏÏÐÎÆ®•Š‹Œ‰~nhp�»ÚàÜØÖÔÛ×ÒÚÝÜÝÜÛÛÛÛÜÛÛÝÕÒáèéêëêëëëìëëëëëëëëëììëììììëëëìëëìììëëìëëëêëëëëêéèèèèçãÖÏÏÏÐÍÉÔÍ�dv€qy®ÌÌÌÎËÉÄÁÂÄÇË·¸É˱t¬ÓÓÔÔÔÓÍÎÎÍËÒ±|–ÈÎÏÑÑÒÒËͱˆÎÓÐÍÏÔÚÖÔØ×ÓÒØ×ÔÖÖÕÏ»¤’ŠŽ’‹{mfv—½ÙáÜ×ÕÙÛÜÝÖÑÜÝÚÙÛÜÚÚÜÜÛÜÛÝßåèéêëêêêëëëëëëëëëëëëëëëìëìëëëëëëìììëëëëëëëëëëëêéèèéèçæÝÐËÎÏÎÍËÓË–hr€rv§ÍÌÉÊÊÇÃÃÅÃÊÂÂÑË»«‚x³×ÔÕÕÖÓÑÑÏÍÌÒ³|›ÌÎÌÑÓÓÌǵ†ÐÔÎÍÎÖÙÔÖÙÕÒÔÕÖÛÛϹžŽ‰‹�Š{liwœÅÚßÛÖÖ×ÛÛÛÛÜÜÝØÕÚØÚÜÚÛÛÛÜÝÜÝãèèééêêêêêëëëëêëëëëëëëëëììììììëëëììëììëëëëëëëëêêêéééèçåâÕÊÌÍÎÎÎËÐÑ�hs€vw¨ÐÌÇÉÇÈÅÂÄÊÇÈÔÏ¿³¬w³ÓÐÒÔ××ÔÎÍÍÌÒ±{žËÊÎÑÑÎк†ÏÐÌÌÒØÔÒÔÕÑÒÖÛÙзž‰‚Ž–ŒuekªÁÔáÙÔ×ÚÚØÜÜÛÛÛÜÛÖÕÙÙÚÛÛÛÛÜÝÛÜàæèééêêêêêêëëëëëëëìëëëëëììëëëìëììëëëëììëëëëëëëêêêéééèèææåßÑÌÎÎÎÎÌÊÎÍ‹ax�rq¤ÏËÆÇÈÆÇÇÄ¿ÇÐÐË®²¦wxµÓÏÑÕØÖÐÏÒÎÌׯz£ÊÍÍÍÌм…ÍÏËÍ××ÒÓÓÏÔÜÚǬ”„„�•‹{st�²ÉßÚÔ×רÜÜÜÝÜÛÛÚÚÚÚÜÛØ×ÚÛÛØ×ÝÝÜÝãæèééêêêêêêêëëëëêëëëëëëëëëëëëëëëëëëììììëëêëëëëêêêêéèèèèçæäÙÎÏÏÍÌÌÍÉÌÁ•oqylmžÆÆÊÍÈÊËÄ·ÀÐÏÈ»²®§}{½ÓÐÓÖÕÕÖÖÒÎÏמv¨ÍÎÏÍÏ»…ÎÒÍÏÖÒÑÕÙØÖÆªŒ~ƒ�Ž„tlx™ÁÛáߨÖÙÙÚÛÜÜÜÛÚÚÚÛÚÙÚÛÛÛÚÛÛÛÙØÜÝÜßäæçèêêêêêêéêêêëêêêëëëëëëëëëëëëëìëëëìëëëëëëëëëëêêéééèèèèçæäßÑÌÎÌËÌËËÊÑÔ ms}ql“ÃÎÎÈÊÏÎÀÁÓÔʾ¶­º©w�¹ÒÓÒÓÖ×ÖÔÔÐÐÉ”|¯ÎÎÎи…ËÍÎÑÑÒÙÜ×Ĥˆ~†�‡{pl} ÃÛáÛÖØÛÜÛÙÛÜÝ××ÚÙÚÙÛÜÛÛÚÛÛÛÛÙÚÚÚÙØÜâåçèèééééêêêêêêëëêëêêëëëëëëëììëëìëëëëëëëëëëëëëëêêêééèèèèçæåãÖÍÎÍÍÌËÊËÊÏÕ®}v|qj–ÂÌËÍÎÏÇÇÓÑÌľµ´º¦yƒ¾ÖÒÔÕÕ×ÖÔÔÏÍÉ•~²ÍÍÑ·ƒÇÉÍÕÛÛ϶ ‰‡‹ƒrdm‰­ÍßáÝÖÖÚÛÚÜÝÜÜÛÛרÛÚÚÚÚÚÚÚÙÚÚÚÚÙÙØÖרÝäæççèééééééëëêêêëëëêëëêëëëëëììììììììëëëëëëëëëìëëëêêééèèççææäÞÔÏÍÍÌÌËËÌÉÐÙ°qpyqo’½ËËÌÎÊËÓÏÉÆÀ¸±³¹§|ŠÅØÒÕÖ××ÕÕÕÍÎË„¹ÏѶ|·ÉÙÞË«‘ƒ‚…Š‚nbp’¾ÖááÛØÙÜÜÛÛÜÜÝÞÜÚÚÛÜÛÛÛÛÙÙØØÙÚÙÛÚØÚÝàÞÛàäæçèéèéééêêëëëëëëëëëëëëëëëëììììíëìíìììëëëëëëëëëëëêêêééèççæåäâÛÓÎËËËÊÊËËÊÎÒ©uq{zt‘ºËÎÒÐÍÔÔÍżº·¯³º¥{ŽÈÕÔÖ×ÙØØØÔËÊÁŒˆ¼Ò¸k˜Å̰‘„�‹~ml|ŸÆÛáÞØÕÙÜÜÛÜÜÛÜÜÝÜÛÛÛÚØ×ÙØØÛÚÙÙÙØØØÛÞáÞËÃÙâäææççèéééêêëëëëìëëëêëëëëëëëëëëììëììììëëëììëëëëëëêêêêêèéççææåãÝÕÏÊËÊÊËÊÉËÍÒׯys‚{x•ÂÒÑÎÊÒÔÐÊ»¿¼¹¶´¡z‘É×ÓÕÙØÙÖÔÐÊÏ»ƒ�Ƽsžž•†‹�‡{oq�µÐááÚÕ×ÙØÛÛÚÛÜÛÚÚÜÞÛÙÛÛÙØÙÚ×ÖÙÚÙ×ÖÖÛßßÔÁ¨Ž©ÝâäåæççèéééêêêêêêêêëëêêëëëìëëêëìììëëëëëëëìëëëëëëëêêêêêéèéèççæåãßÕÍÍÌÊÉËÌÊËËÍÑÔ±yr�{u“ÂÓÌËÒÓÐÍȼ½¾À¹°µ§�–ÇÔÓÖרÖÓÐÍÊη—¶…¯€Š’ƒni~˜·ÝåáÙÑÖÚÚÛÛÛÚÚÜÝÙÚÝßÞÜÚÛÚÙÚÙØØØ××ÖÙÞÝ×DZ›Žˆ”ÆááäåæçèèéééêêêêêêéêëëëëëëëëìëëëëìëëëëëëìëëììëëëëëëêêêêééèèçææåäâÙÏÍÍÌÅÈÍÉÊÇÅÉËÒ±sl{|u“ÈÒËÑÔÑÊǽ¾ÄÀ²¶É­}˜ÌÖÑÔØ×ÔÐÎËËЭ‹�µˆwql|ŸÈÞãßÙØ×ÔÙÚÚÚÛÚÚÛרÛÜÝÞÜÚÛÚÙÙØ×ÖÕÔÕÜàÞζŸ�Š�œŸ±ØàáäåæçèèéééêêêêêëêêëêëëëëëëëëëììëëëììëììëëìëëëìëëëêêêêéèèççææåäãÞÔÍÍÍÇÈÎÊÉÉÈÌËÌѱwq�zs‘ÃÐÏÔÔÌÊÉÄÃÄô¸ÊÄ—~šÈÕÓרÕÐÐÎÌËЬwv¡zm…¬ÎÛÛÜØÙÜÜÚÚÙÙÛÛÚÙÚÚÕÖÛ×ÙÜØ×ÙÚÚÙÖÔÖÙÜÜÕÈ±Žƒ‰•Ÿ�˜Š®Ýßâäåæçèèéêéêêêêëëëëëêëëëëëëêëììììëëëììììëëëëëëëëëëêêêêêèèèçææåãâà×ÏÎÌËËÍÌËËÊÅÌÏÐÙ¿‚m{si�ÃÒÑÔÑÌËÇÁ½Á»¼Èì’zŸÍÔÓÖÕÒÒÑÎËËΤiˆ�¹ÔÝßÚ×ÙÜÝÝÜÝÜÙÙÛÛÚØÙÛÛÚÛÖØÛÙÙÙÙÖÖÙÜßÙ˸Ÿ’�‰’œ™Ž�zƒÀàßâäæççèèéêêêêêêëëëêëêëëëëëëëëìììëëìììììëëëëëëëëëëëêëêêééèèèçææäããÚÌÍÍÊËÌÊËÈÆÂÌÂ½ÏØÁƒqxqpŒ¾ÓÑÒÑÍÊÅÀ¾¼ÀËÆ°¢Œ|ŸÒÔÓÓÑÑÑÏÍÉ;u¾ÓÞÞØÖÚÜÛÝÝÛÜÜØÚÚÚÛØØÚÛÚÚÛÛÚÚÚÙ××ÛßÜÑ»¢‘Œ‘™›–Œ„}xw|®ÚÜàâäåæççéêêêêêêéêëëëêêëëëëëììëëììëëìììììììëëëëììëëëëëêêêéèèèçççåäãÞÐÅÈÉÊÌËÌÁÅÍË»¬ÈÌׄpyuk„¼ÒɽÉÐÉÅ¿ÄÊÍ· ž‹u¥ÔÔÑÐÏÑÑÎÍ͸†ÜßÔÕÙÙÙÛÚÜÝÜÛÚØÚÛÛÚØÚÚÚÚÚÚÚÙרÛàÝÒë•‹Ž™ž™�„yuvtsuˆÂÜÜàâäååæçèééêêëëêêëëëëêëëëëëëëëëììëìììëëëìëëëëëìëëëëëëëêéééèèèèçæåãáØÈÇÊÌÌÌÎÊÊÌÊÊÆÊËÍׯ�pvxi‚»Ç®ÂÒÉÄÇËËÌÑÁ­¥©ˆn¦ÒÒÐÒÐÑÏÍÍ·†ÓÙÖØÙÙÚÝÝÜÜÙÙÚÛÛÚÛÛØÙÙÙÙÙÖÖ×ÛÝÙÊ²šŠŠ’œ¡�Šzuusps{Œ¢ÀÑØÞàâäääæçèéêêëëëêêêêêêëëëìëëëëëëëììììììëëëëëìëëëëëëëëëëêééééèèçççåäãÜÐÊËÏÊÃÊÍÍÌÌÍÏËÊÌÏÖÌ—qzuk‚³ÈÊÎÌÄÆÌÏÏÐȶ¦««…q£ÑÐÐÓÕÒÏи…ÔÙ×ÙÚÛÜÜÝÝÛÓÖÛÚØØÙØØÙØØ×Ô×ßßÖ¿¥�„Ž™Ÿœ�…{vuuuvƒœºÑÞÞÖÚÞáããååæçéêëëêêëëêëêëëëëëëëììëìììììëëììëëëìëëëëëëëëëëëëëêêêéèèèççæåäßÑÈËÎÍÈÇÆÍÍËËËËÊÍÍÈÒÑ•pzvmªÆÍÍÈÆÄÌÑÏʺ¬­«¤…o¨ÕÑÒÕÒÐÔ»†ÕÚÚÚÚÜÜÚÛÝÜØØÚØÖ××Õ××Õ×ÛÜÛ̵žŽ‹“œŸ˜‹ƒ~zssu‚™°ÉÚßßÝÒÐÜßáãäåæçèêêêëëêëëëëëëêêëëëëëëëìììëëìëëëëëëëëêêêêêëëëêêëêêêêéèèèèçææåâÖËÌÎÏÎÌÊÍÎÌÈÊËÈÍÏÌÌÔÏšsuxszªËÉÅÅÀÈÑÑËÁÀ½­¤ª‰r§ÌÏÑÓÑÖ¿‡ÖÝÙÙÛÜÜÙÚÛÛÜÙÙØØØÓÓ×ÙÜÝÔĬ�…�œ¡œ’„~~|xv{Œ©¾ÔãÜÙØ×Ù×ÕÜßàâääæçèéêêëëêëêêêêêêêëêêëëëëëëëëëìëêêêêëëëêêêéêëëëëêëëëëêééèéèçææäâÙËÈËËËÎÎÎÌÌÌËÆÁÉÍÎÐÍÔÑžqu}vƒ¯Ç¼ÁÈÉÐÒÌÃÇÀ³°²°‡o¥ÐÒÒÒÕ¼†ÕÛרÙÙÚÛÛÛÚÙÙÙØ×ÕÓÙÞÙ̹¥•’”�¢™‹{zyz�Šž¼ÔáÕÒÙÐÔÛÚÙÖØÜÞàâäååçèêêêêëêééêêêëëëëììííììëìììíììíííííííîíííìììëìëëëêëëëêêêêêêéèæàÒËÍÌÊËËÌÌÍÏÍÆÂÈÌÍÐÎÍÓÒ¡qszs|¬¹½ÌÊÌÑËþ½»µ´¸º‘|±ÖÒÑÖ»†ÔÚØÙÚÙÚÚÚÚÙ×ÖÕÖÙØ×Ò«—’˜¡£œ’†}yxvx‚š¼ÐÚàßÙÔÕÕ×ÛÛÛÚÔ×ÝßàâäæææéêêééêëììëëéèæäáßÝÜÜÜÜÜÜÛÚÚÙÙØØÙÙÙÙØÖÕÓÒÐÎÍËÌÌËÊÉÉÉÉÊÌÏÑÔÕ×ÚÚÛÚØÚÙÖÖÓÏÎÎÍÎÌÅÄÈËÎÐÑÎÒÔšiszkxš·ÈËÎÎÎÆ¿Âº¾Áƽ‰|´ÕÑÔ»†ÓÙ×ÚÝÝÛÙÙÙÕÔÙÝÞØÃ¬š””›¤£™‰~|wswŠ¥ÁÖàÞÛÙ××ÚÛÚÛÛÚÛÛÕØÝßáãäåæçééêëììëèãÝØÑËÆÁ¿½¼¼¼º¸´¯¬«ª¥Ÿ™—˜—™››™—“���’”•˜›œžŸ £¥¥§¨ª®±³¶¸½ÀÃÈÌÑÕ×ÖÑÍÌÌÉÅÆÊËÎÐÏÍÓÏ¢utwir¥ÈÍÑÑÐÈÀÂÀ¿ÄÂÂÄ®w±ÓÓº…ÓÙÙÙÛÛÙÖØÙÚÝÛп§‘ˆ‹š£ •‡{ywyy…®ÏÞàÜØÒÖÜÚÚÛÚÛÛÜÚÚÚÖÚÞßáâäåæçéììéäÝÓÊÁºµ²¯¬©§¥£ž™“އ„|{vrppoopvxxtmkkmqux{…‰�’—œ¡¥§§¨§¦¨ª«ªª­±µºÀËÒÐÌÊËÊÈÌÍÌÍÍÏÍÏÌ¡lo{ky°ÐÍÑÐȿÿ»ÅÂÄÈ¿¨}xºØ¼„ÔÝÚØÕÖ×ÙÞÜÒÁ«œ�‰�˜›”�†~vsw~‹ ¯¿ÞßÚ×ÔØÙÚÛÙÛÛÙÙÜÛÙØÙÖÛÞàáãäæèééäÛÑÆ½·´²³³°¬¥�”Œ…zwsppojhihhhjkntvuoe_]adfjmruy}�„…‰’�©¯±±¯±±°°¯®¯¯°´ÂÍÎÌÊÉÈÌÍÌÌÍÎÎʶágrui{²ÐÏÍÈÆÇüÈÈÊËÄ¿¯‚�¾¿…Ó×Õ×ÛÝÜÓÅ´¡––š—’�މ�ywuvŠ¥·ÑÛÙÙÙרÙÑ×ÜÚÙÚÜÛÚØÌÐÛ×Ö×ÜÞßáãåèèâ×Ê¿¹¸¸¹¹µ¯¦œ’ˆ€ytrpmlkjkigc_\[[[\bhijd[Z[[Z[[`ehnqux{zxz…•¢®²±±³µ¶µ´´·´·ÆÍÍÍËÉËÍÌÍËÊÎÐÃÉÖ dlzmz°ÍÍÆÅÉÇÃÆÅÊÍÈþ°€…©†ÔרÞÙɲ ••˜žž”‹†�{uu€…’´ÐßààÛØÔ×ÚÙÚÚÛÛÙØÛÚÚÛ×ÌÓÜÚÖÙÜßàáäçâÖÇ¿½½½»¶¬¢˜�‡ƒ~wqlkhfdcb^[WTQOPSSOMSXWVSOONLMRUWUX^diouy{vx�„�£±µµ·¹¸¸¸º¸¸ÆÎÍÍÌÊËÎÍÍËËÏÏÏÌÑÕ empv¨ÎÈÀÇÆÃÇÁ¿ÈË÷¹«yy†ÔÓÉ»§™‘“™œ•�„|yxz~‡Ÿ¿ËÓâßÙÙÕ×Ù×ÙÛÛÜÝÜÛÛÜØÓÙÚÛÜÛÛÜ×ÙÞßáåäÙÊ¿½¿¾¸®¡•Œ†ƒ�}ypg^WRSX]ZTSWWVWZ^`^^a`][ZWND@BMX[WSPT\bhmsv~‰†�‚•®µ¸º¹º»¼¸¹ÇÌËÊËËÌÍÎÍÌÍÏÌÌÏÏÒÖ¤eq~ms¦ÉÃÁ¿ÀÍÈ¿ÇÌȹµ¶—s­ž™–—››•Œ†zwv|‰�¸ÎßãâÞ×Õ×רÚÚÙÚÜÛÛÚÛÜÛÜÚØÚÜÜÛÚÛÛØÚßáäßÎÁÀÀ¹«œ”Žˆ„ƒ‚€|xsoid]WVZaffglorsqpnmljihfedb]WTU^gigbUKKR\chms{„ˆ‚{�«µ¹»º»½¹»ÇËËËÌËÌÍÍÎÎÍÍËÍÑÓÏÑÙ©ko|ns©Ç½¹½ËÐÉÍÍËÁ½º¦•rŽˆ—œ˜Š�}zxvwƒ˜±ÆÕßáÝÚØÙ×ÕÚØÛÜÜÖ×ÙÛÙ×ÛÛÚÛÜÜÛÚÚÚÚÚØØÞâà×Ǿ¿¸¥�„�ƒ„„…‚~yrmjjjmpqtwywtuutvxyxwtrolmnljgeccfjjmlmhYHEKU`fkns}†|t’®³¹ºº»·ÁÌÈÊËÊËÌÍÌÍÍÌÍÍÎÐÒÒÏÒÙªorƒux­Å¼½ÈËÌÑÎÌÆÀÁ«žsž�‰‚{yyus~•ª¾ÖÞÝÝÙÍÓÝÛÚÛÛÛÛÜÜÜÛØ×ÜÛ×ÙÚÖÙÛÙÛÕÓÚÙÚÛÜÞÙÎÄ¿¶£�€„‡‡…‚zunjikosvy{}~}|zvuwxxxwwxwsqrpnmkijnnmnnool^QJMV_goqsvwr}¡¯³¸¹¸²£·ÌÇÈËËÎÌÌÍÆÉÎÌÍÑÑÑÑÏÎШsz‡v}©´¸ÉÇÈÑÑÑÌÃöŸs–�xutwy„š»×ßàÜ×רÛÖØÜÜÖÙÞÜÛÛÛÛÜÛØÕÛÚ×ÚרÜÚÙÙ×ÚÜÞÜØÑÉŽ¥‡{~‚†ˆ‡ƒ€zuqlimpruwyz{}||||{ywwxyyvuwxvsppqrompssttttqojcZSSYajqsstvx“¬¯²¶¸ª|²ÊÉÉÉÎÍÌÎËËÎÍÉÎÒÒÐÑÌÇ̬xtƒsvФÄÊÊÎÒÓÐÉÈ»§i�uvyzЦÄÜÜ×ÚÙ×ÙÛÚÛÞÜÛÛÚÚÜÜÚÙÛÚÛÜÙÕØÖØÛÜÛ×ÑÙÜÛÜÛÕÐÌË̹”wq~‡†„„�|wrnllptttvxzzyxuqpopqprtwyvstuurqqrpnnpqqqpnpqpmid\Z\aflsuvxx�§®°µµ™|o{¯ËÇÇËËËÍÍÌÎÍÉËÐÑÎÏÏÌËÕ²us|uu”ÀÇÌÏÑÑÏÌ̽ª^x�•©»ÏÜÞÜÏÍÚÚÚÚÙÙÚÚÜÛÛÜÛÛÛÛØÙÛÛÚÛܨרÙÛÛ×ÐÒ×ÖÒÍËÌÏϳƒot|„‡„�}xtqommruuvwxwusmd_^`begjmpsuvttsrrrrqqsqnmjgdcdhlmmjfa^aeiqsvz{�¥±¶µ˜‚{qz«ÌÉÆÉËÊÆÄÍÍÎÌÍÏÍÍÍÏÏÐØ¸}q�y|¦ÅÊÎÐÐÏËÌÆ°j¦ÈÛÞáßÙÕÖÙÚÛÚÙÙÙÛÛÙÜÛÛÚÚÚÛÜÛÚÚÚÚÙÔØÙØÚÙÙÙØÐÕÐÉÌÑÒήyiv||‚‡�|vrqppqstsuxvqjdc\VTV\_`bejmmqsrsrrstuuuuqkgc^YVTX_eikkhaaejmsy~ƒ‘¤²µ¼šn{ƒrx§ÊÊÉÊÊÈÅÉËÌÍÌÎÎÎÍÏÐÍÏØ¹zlyuu¤ÈÎÑÑÎÉÌ̵ˆÜÚÖÛØÖÙÚÚÛÛÙØÙÙÛÜÜÛÚÚÛÛÜÛÚÚÙÙÙØÚØÓ×ÙÙÙÙ×רØÖÎÐÒÒÌ­�ks}}|€ƒ�xnopprvuqqqng_XTPNPSUY^aachlknpprsstvxwusmhfb\WOJIKS]eijgcbfpy�„�—¤¯·ÈÄ—s{�znrŸÇËÈÈÌÌÉÉÊÍÌÌÎÍÎÐÎÍÍÍ×¹xk�tr¤ÎÐÐÐÇÊÏ´ŠÙÆÄÙØÚÚÚÚÙÚÙÙÙÙÛÜÜÛ×ÙÛ××ÚÙÙØÙÙØÕ×ÙÖרÔÖÙרØÔÏÒÓͳ�€xx}~~}€…}qllquvsqngbZSQOKOTWXZ^bcehlmnoqrrssuwxvrkhfb[VRLFCFNW^che`iyˆ‹’ ¬³ÃÎËÌ¥ro}|po”ÁËÆÊÊÈÉÉÊÌÊÌÊÍÏÌÍÎÌÎØ»|r�px«ÌÏÒÉÇε…ÓÒÒÙØÙÚÚÚÙÚÚ×ÕØØÙÛÚÚÛÜÖÔÙØ×ØØ×ÖÒÖØÖ××Ö×ÔÒ×ÖÑÎÒнž�‰{y~xy��sjpvxwqkd_][]bddcefdcddffhkmnprrqrrsvxvqkgdc^ZWNGFEEHQ]eigty|Œ’�«´¿ÌÎÎÓÓ¡su�€tm�»ÈÆÇÈÊËËËËËËÎÐÍÌÍÌÍÏÕ½€q€{{«ÍËÉËϹ…ÓÛÙÙÙØÚÚÙÙÙÚÓÒ×ÓÖÛÙÚÜØÖØ×ÓÓØØÖ××רÖÔ×ÚÚÓÓ×ÖÏÎÓÈ­˜Ž‡yw€}xqntsjnuwyumdabhmsuvyvtrppmkklmmmopssqqrrttsojgffb]ZVSRPNORW^hillz”£¯µÀÊËÑÑÏÐϸ‡u€vl„²ÈÇÈËÍÌËÌÌÊÍÏÎÌÎÌÌÏËÑ»~o†}~­ËÉÌѺ‡ÔÚ×ÙÙÙÙÙÖØÙØØØÙÙÙÙÙÙÙÕÔÙØÔÕÙÙ×ØÙØØÕÒØÚ×ÖÖÕÕÎÎϽ¤‘Œ‰xwxqlihcltvwxqkkpuz}|xx{ywutttrqrsrqrsutrrqrrqqnkiiheeggddfggfa_ceck‰¨±¹ÄÍÎÉËÌÏÒÓØ»�st€ym€±ËÊÊÀ·ÉÊËÊÌÍÎÌÍÊÌÑÌÈϸqƒ{€¬ÉÊι‡ÕØ×ÙØÚÙØÙÙ×ÖÙÚÙÚÙÖØØØØ×ØÙÚÙ×רÙÙØØØ××ÔÒÔרØÏÌ˸Ÿ‰ŒŠxz|rkfe]druwxuqrw~}{xxxvsrsrrsttssrstutqooqqppoljiiiklkjloooqspiii|¦¶»ÅÎÎÏŪÄÑÎÎÐÒÍ›sx‚}oy¨ÇȺ¶ÅÈÊËÌÍÍÌÌÉÌÏÎËËϾ‰o{y¡Äʶ†ÕÚ××ØØØÙÙÙÖØ××ÙØÙÙÙרØÖÓØÙØ×ÕÖÔÖØÖÕÓÕØÛÝ×˺©ÀÍ»£��ˆz{xnebZTiswyxuxz~€|zyvuutpmmnoqrssttttttqoooppnomjikkjhhiklmnquxwrwš¹ÂÌÐÎÎÐ̺ÊÔÍÏÑÒØÓ¤rqƒ|ls¡ÈÃÃÇÅÆËÊÌÍÌÍÎÍÊÇÇËÉÎÉ”s}w�È·†ÕÛ××ØØØØØÚÚØØÙ×Ö××ÙØØØÖÔØ××××ÖÒÒÕÖÙÜÚÔÊ»ª™Œ…¨È½¬šŽ…�}sh`]OXnvxyxx|}�~wuusrpnjebcfkmqsttttvvtqooppponkjkjfbaeiklorsw|z�¸ÉÎÐÐÌÌÐÓÕËÈÎÒÑÐÐÒͨwp‚}lr ÃÄÆÄÅÊÊÊÌÍÍÍÌÊÈÉËËÆÔÑ•oƒ�z¡´†ÔØÖÔÕØØØÙ×ÖØÙÙ×ÖÓÒØØØÖØÙØØÖÔÔÕÙÚÚÕÎŰŸ‘‰ŠŒ�‘›½Æµ¡�‡ˆ�pd]ULarxyzz{}~~yuspkgdb_\[YY^cilpqtuuvtrppqqqolige`YV[bhjloqqty}”ÂÏÌÎÑÍÍÎÐÎÄÂÊÑÑÏÐÐÑÖ¸|k|}no”¾ÇÁÇÊÉÉËÌÌÌÌÌÏÍËËÇÊÕÊ’o�ƒyއÏÒÕÑÑØÙÖÌÒÖÙÕÖÚØ××ÖÖ×ÕÖÖÔÖØØÚÚÖÍ»§™�…‡‹�”™™”‘˜·Â¯š�Œƒnc\PQdsy{zz~€}xtqkfcbcb^[XVUUX^cinqtvvvusrrqqkeb^WOLQVXYZ`ejlpw}’½ÊÉËÏÑÐÏÏÐÎÈÈÏÐÎÏÒÐÏØÂ†n€rj�»ÃÃÉÈÉÌÌËÌÍÊÊËÌÍÊËÏÒË–q�…u…ÑÖÖÖÖ×ÙÕÍÕØÙØÙÚרØÖÖÕ×ÚØØÛÚÓÆµ¢“‰‡Š�”™˜•�‡�ytn{£¹´¥•…pc[OTcsz||}€~zuogadhiijd^YUSSQTZ_diouxxwwwusnb\XOHHMSTURLJS^flu|•¾ÌÎÂÆÐÐÏÎÑÒÍÍÐÑÏÑÑÎÎÏÙÌ“t�qj‡µÅÇÉÈÌÍÌÍÍÊÉÊÊÏÎÍÑÍÐÎ�q~„…ÔÙרØ×ÙÙÚÕÖÚÛÙØ×ÕÓÓÖÚÝÔÏÌÁ¯ž�ˆ‰‰�•‹ŒŠ€yvrkggeeak�¬¸±�ŠvYKTcr{~�€€|uoe`cgiiigc^YQMOOOSX]bipwzzzzzwhXSKCDHNSW[]XOMT`hpz—ÁÉÉÇÉÎÊÍÏÏÐÑÐÐÐÐÏÏÌÏÐÎ×Кqu}ui~°ÆÆÉÊÌÌÌËÉËÊÈÌÌÍÏÎËÏΟov‡Ó×רÙÙÚÚÙÖÖÙ×ÓÓÕØÖ×ÖϽ¦¤Ÿ“‹‹�”™’Žƒwtnhigffghijljiv�£¤”uTLVcq}€�‚€~yrh`bedbcb`^ZTMFGLORW[aflu{||{|yeSJB@?CJQX]bc^SNXemx�ÄžÍÍÉËËÏÏÎÐÑÏÎÏÎÎÍÐÒÐÎÔÑ›nq|vi|ªÄÉÈÌÌËËÈÉÉËÌÈËÏÌÌÏÐÑ¥o‡ÕÙÖÕÖØÙÙØÔÑÕ×ÙÛÛØÉ¸®ŸŒ�ˆŒ“˜™—�‚wsmmjjlomjjllnnmqojlrzp[QNYao~‚ƒ‚|wodbdabcbcc`[UMHEHRXZ^agmtz}}~{iSGA>=ACBBCDEGNY][^iorw{ƒƒƒƒ|jYQHA??@@>>CDDLZcn…³ÇÈËÊÈÈËʾÆÎÍÐÎÎÏÐÐÎÎÎÑÐÍÑÕÑÎÔÍ�su}vo�ºÉÉÈËÌÍÊËÍÎÎÍÐÑÎѽ‡ÒÙÙÑIJŸ�‹�’˜Ÿ¡£’|x{{qkkgeimnonnnnnmmnqpopoorssqoopomkbUIKQVmƒ††„}vqmkie]TQTUVYYVRRRUVW[empty~�ƒ…†…‚ubSNLHFIOQUZ`e^]gq†±ÅÆÉÊÊÈÉËÅËÏÏÏÎÎÏÑÐÏÏÍÌÍÑÒÔÒÐÎÓÔ§qp{un†²ÇÇÊÍËÊÍÏÏÎÏÐÐÉν…ƹ¥•Œ‰�“› ¢›—Š{pfoqkgihkmnnonlllklmnopppqrqpqqooponq|hSJQWn†‰ˆ…€xvvvtqoljhhgd`[VVWY[`hmpty}€�ƒ‡‰‰†}obXTPLKOW^bgoqlpw„œ¹ÇÆÈËÊÉÈÎÏÑÏÏÑÎÎÐËÏÐÎÌÈÏÑÔÑÑÓÑÔÖ¬xlyun€«ÅÇÊÉËÌÍÏÐÏÑÏËλp”‰‰Ž˜¡¡ž•Šuonp{zk_]cedffdhlkjjjjknnmnnopqooponnonnwzxubSTXp‡‰‰ˆ…€~~}zyusolifb^[[[]bgnsuwz}€�ƒ†Š�‹ˆ�xqlb]ZUQRW\dkpuy~‡�‰·ËÈÆÉÊÊÍÎÎÍÇÄÌÏÌÊÑÐÐÎÏÎÐÓÓÒÑÓÓÕÚ»�mxwmz¤ÂÄÇÌËÊÎÐÐÐÐÐлnš�£¦ “�umkp|ˆž¼¿´¦„\RX]][\`cfhijikklmmonpqpppnnklmpwihsrhZXu‰‰ˆŠˆ…„‚�}}yrokfedbcdfimsxyz|€�€„ˆŒ��‹…~utpieb`\[\clqw~‚Šƒi|¯ËÊÆÈÌÌÌËÌŽÌÐÍÎÑÐÌÌÐÎÏÒÓÒÒÓÉÊÒØÃ‹nvwnx¢¿ÄÆÊÌÌÏÐÎÎÎѺx¦˜ˆ}uopx‡—­½ÄÔÚÇÆØÐ¡dKKORUY[^adffggijjkmnmmmmopnlkqshgmwte^z‹‹‹‹ˆ††…‚€~zvtqnnnoonqsvy{{{~���€‚ˆ�“”�‰�yuurnkkkkklpvz„ŠŠqcn—ÄÍÅÈÉÊÍÍÍÏÍÇÎÎÎÏÊÍÏÌÏÓÒÑÑÏ¿ÃÐÑÚɉlvxoy¢ÀÄÇËÌÍÏÏÎÏÓºezmnt†—¬ÀÍÔÔÏÈÐÍÀÂÐÖÓ­qK?CJORSVZ^`bcdehjllijkjlnpomsriioxwpn‚Œ‹ŒŒ‹‰ˆ‡…ƒ�}|zxwvvwwwxyz|}}~‚ƒ‚�‚…Š�’”�Š„yvvutrqqqrvy}€„‰Œ|p^]‡ºËÈÆËÌÍÍÐÌÇÍÎÐÎÌÏÎÌÌÑÐÐÎÌÍÏÐÔÒÙÊ�krvpw—»ÅÅÊÍÎÏÏÏѽ`„”«¿Ï×ÜÝÚØÔÍÌÓÐÁÁÄÀÆÍ±yQ?;AGJLQTV[]_behjijkjjkjlonssihs{tsyˆŠŠ�Ž�ŒŠˆˆ‡†…ƒ€|{|||{|~~€‚„„ƒ‚ƒ‡‰�›¡™Ž†€{wwvvxwvwwz}‚„…‹Švrrhc{«ÆÆÇÍÌÊÍÎËÉÎÑÏÌÏÐÏËÍÑÐÎÐÏÎÏÒÑÑÚÏ–mq|sq�¯ÀÃÉÌÏÐÍλyÈÚÝßßÜÙÕÖÖÖÔÖ×Ƚ¿ÂÀ¼½¿®ŒbE47@DHLOSUZ]`befghhhihilkpsiiv|x{~‰Š‰��Ž�Œ‹‹‹‹Š‡„„‚�‚�€~~€€€€‚ƒƒ„ƒ„‡‰�žµ»°Ÿ�‚|ywwuwz{}}}�…†‡�ŒmbpwpanšÁÌÉÊÊÊËËÉÌÍÏÇÄÏÐÎÎÏÎÏÐÎÍÏÏÏÓÏØÕ£rr|qp‹«ÀÆÇËÐÏй‚×Ý×Õ×ÖØÙ××ÖÓØÏÃÂÂÀ¾»½º»±ŽnO947>DIKMQUZ^acbceeefgihmthjx‚ƒ}}Š‹‰Ž���‘‘��Ž‹‰‰ˆ†…ƒƒ‚‚���€�ƒ�€€‚…‹’¢µÂû­š‡}ywwuvz{}�‚„‡ˆ‰Ž�oWYgvugiŒºÉÆÇÈÈËÌÌÌÎÉÆÎÏÎÑÎËÏÐÏÐÐÐÒÔÑÏÖØ¬tjuuv‰ªÂÅÃËÐÑ»…ÎÒ×ÚØÖÖÔÕ×ÓÓÒÀ»¼½ºÂ¼µºµ´©‰r[D427>CEINUXZ]__accdfffjtjiz†ƒin‰‹‹Ž��‘““’���ŒŒŒŠŠ‡…„……„ƒƒƒ�~~€‚ƒŒž®»ÁÀº°¤Ž~wttvyzz}�„…ˆ‹‹ŽŽpXQTetwmbx©ÆÈÆÉÉÊÍÎÌÌÏÎÎÎÐÐÍÎÑÐÑÏÏÒÓÔÓÐÓ×µ„ntxt|¦ÆÄÈÍÑ»…ÓÙרÔÒÓÍÔÓÏÕÏ¿º»»ºÀ·­ºº¸·Ÿ€paP<106=AGLOQVZ[]`abcbcdsni|‚uYb…‹‹Ž‘‘‘’““‘�������‹‡††…„„‚�~€�‚ˆ—¥¬´»½¸¯¥“€vqptz{|�…‡‰Š‹�ŽrZVRT\n{p^kšÂËÇÆÉÌÍËÊÍÍÎÍÍÐÐÍÍÏÑÐÏÐÓÔÓÓÏÑ×È“kqunw�»ÀÅͺ�ÕÞÙ×ÎÒÖÔÓÒÔ×ɼÁÁÀ¾²·¾¼¾º·«�}tk[F5/27=BFJOSVX[]^^^a`mpl}|jWg…ŠŠŽ’“”””•“‘‘’‘�ދЉ†…„‚‚€}}��…“¡¤©±¶·±ª¢•…xomptw|€�…ˆ‰Š‹�ŽpXYZYX]kvufd†¶ÉÆÄÉËÌÌËÌÏÐÑÏËËÍÍÎÍÎÐÒÒÑÑÑÐÏÔÍ›nlujiаÄǹ…ÕÚØØÔרÖÑÒÖÔó½ÁÂýµ¸¼¼»·®£‹vl`Q@4/18>BFKOSVXYYZ\_clo{xcZv‰ˆ‹�““•–”’““““‘��Šˆ‡„ƒ‚€€~z}„…„Œ™žž¤¬°¯ª¦¡˜�‚tlkmqw|€ƒ†‰Œ‹�Œr`cea\\\bqxi^v§ÇÈÆËÌÌÈÌÏÍÏÏÉÊÎÎÎÉÍÑÑÑÐÏÎÐÐÎÒѦokxol‘·Çµ†ÕÜ××ÖÖÕÖÖÖÙО¾Á¿²²»º¹¹¶³š�|smeXJ:0/28>CFKORTVWY^[dqzvba~‰ˆ��‘“““’‘“”‘‘‘�ŽŠ‡…‚‚€}zz€†††Œ“–𠦧§¦¤ š“Œ|mihmsy€ƒ„†Š‰‹Šviifd_\[[^kwpcm–¿ËÊÊËÉÅËÆÅÌÈÈËÎÏÎÏÐÑÑÒÑÏÐÑÐÌÏѰxkyrm…¶·…ÔÚ×ÖÔÖÖÖ×ÛͽÄÇÅù»º¶·º¸¸º¹ªŒzvqi\NC8/-39>AFJNQUXZYaqyvcd~†‡Œ�‘’“’’””“‘��Ž‹ˆ‡…�€}}zux}€‚„†‡ˆ�™¢¢ ¡ ›”އ|mgehpx}‚ƒ‚†‡Š‹tfgd`]ZZ^[]gtvic�±ËÈÇÈÁÇÌÍÈÇÍËÌÍÎÐÏÐÏÐÒÏÏÏÎÏÍÎ׸{juolƒŸ…ÏÔÖÖרØÖÙÕ¼·ÅÄÅÃÄÿ¿ºµ¸»»½¸±Ÿ…~yvph`VL?4-.3:>CFJQWWX_nyxji{…‡‹��‘“‘‘’’‘Ž�ŽŒ‰†…‚}}zvqorrhenx{~ˆ•˜”“’ކxoqmgdfnv{~�ƒ…†‹Špbdd`^]\][XYerulau¦ÄÆÆÈÉÎÏÊÏÌÉÊËËÌÎÏÎÎÏÏÍÍÍÏÑÑÏÕ¾ƒmsuju…Ó×ÕØÚÙÚÙÜÐÃÈÈÅÄÅÄÄÀÀÁ»·¼¾¿¹´°”‚}xsojc^SG:0,/5;?DJQXXZhx}}~w‡‰Ž‘‘’�����‹ŒŒŠˆ‡„€|{zyvngfcVJLXju{�„ƒ‚‚l[anqlgekt{~€�ƒ†‹†j`ddcb_]\[Y[]`gtufi’»ËÈÇÊÌÌÎÊÇÉÊÊÌÍÎÎÐÏÏÎÎÌËÐÑÐÎÖÆ•pqsj†ÓÚØÚÜÛÛÜÛÓÍÌÉÆÄÅÃÁÁ¿µ²»¿º¶µ¦‰�}xtokgbYJ@:1,06=BFOUWaty…‹zvƒ‡��‘�ŽŽŽŒ‹‹Š‰‰‰‡ƒ~ywxxxtlb]ZWPLRapuvuuvnXWfqwvojkry|~�„Š„h`cca^^``_]\ZX[guvid¯ÊÈÆÌÌËÌÊÄËÏÍÌÌÎÑÐÏÎÎÍÊËÍÏÐÏØÒ¡rot†ÓÛÛÛÜÜÝÝÙÍÊÊÊÊÈÆÇÇÆÆÅú­µ¿»´²°•‚�|yuoedcZRJA6-,184.-3:?DM^u|‹‹‡€ƒ�‘��‹‰ˆˆ‡„ƒ„ƒƒ�~|||~~~{vpkhhllkmmllicfmquz}€|wtwyz|~ƒŠyecb`fg_]][^`_[XXWYjytce�ºÈÈÇËÉð¼ÍËÉÊÍÎÐÏÌÍÎÍÍÐÐÏÑÏÕÕ°{‡×ÞÜÝÝÝÛÛÔÊËÍÏÍÍÎÌÈȼÀÃÁ½º¸´¸´´¡‡‚zutsnhddd^UME<30/4@PVs‡ˆ…~|�‹��‹Š‰ˆ‡†ƒ‚‚€�€€‚‚�€}|zxtpnmmkhfffdgmtx}€��‚€}{wvvx{�‡raaaacc_^ZY_`ba^ZWVYdprg_{«ÆÇÆÄÄÄÆÈÉÇÈÍÐÒÏÍÎËÌÍÏÐÍÏÑÏÒØ´‡ØßÝÝÜÜÝÛÏÉÊÊÍÍÉÈÌÊÈÇÅÄÅ¿»¹¹¶º·³®•…�|vsokihe`XWSKB:21>LPb‡‰ƒ€ƒ‚ˆ�Œ‹ŠŠ‰‡†„‚�€‚ƒ‚„…„‚‚�€€~}zxsnkhebbcgnuz~�ƒƒƒ„ƒ��|vtuz�…l^_bcb`]\\^_^ab`\YX[[^jvnbošÀÇÅÇÍËÄÃÄÇÍÏÑÏÎÏÎÌÍÏÏÏÑÒÑÑÓÁ‡×àÝÜÜÝÞÖÊËÍÌÊËÆÅÇÊÅÂÈÇÅÄþ»¸¶¶µ¶¨Šƒ€yvqkljd\SZ[VPIDAGGJP{އ‰Š���ŠŒ�‹ˆ‡„„„……‡ˆˆ‡„ƒ„„ƒƒ‚€~|vrlgeccfkqx}€�ƒ…†‡„„„zqs|ƒ…k_`aa_^]]_`__]]]]\Z\\W\lzyda‰·ÇÅÅÄÇÆÃÇÌÆËÐÎÏÌËÌÍÍÏÒÑÒÓÔº‡ÖÜÝÝÝÞÜÓÊÍÏÎÌÍËËÇÇÄÀÅÇÈÈÅÿº³´¸¸µ›„ƒ~|xqpof[]\XWZYROUONHb‰††…tt�ŽŠŠŒ‹‰ˆ…„†‡ˆ‹Œ‹‡„……„ƒƒ‚�€~ysld``chnsx|�„„„……�ƒƒ�~su~…�h``_\[\^__`^]_``__\YXZZ\euvf`|ªÅÈÅÈÇÄÇÊÇËÏÌÍËÊËËÎÏÑÒÐÑÕ½ˆÕÖÛÞÙÚÙÏÌÊËÌÍËÅËÉÆÆÆÇÈÈÅÀÁ¼º¹»»¸°ª’†ƒ�|xsokdeb[V[caZZXWNQuƒ�|gfˆ�‹‰‹Œ‹‰‡†ˆŠ‹Œ‹Š†ƒ„ƒƒ„„‚�€€}xrkdbfksy|€ˆ�‡‚���‚………}{‚‡}d_`_\Z]^^`_]]acb`_][Z\ZWV\hsocn›ÄÊÆÆÅÅÊÎÏÍÍÎÌÊÈÌÏÏÍÎÌËÔ¾ˆ×ÝÜÜØÖØÑÎÊÊÎÌÌÊËÉÉÉÈÉÉÇÄÁ½¸¹¼»»º²²¢‰†€|}xpnmifc_]`cb_\\YQWnvqee„�‹‹�Ž�‹‰‰‹Ž��‹‡…ƒ�‚ƒƒƒƒ†‰…~{xsqsux{~ƒ��ˆ}|„……‡ƒ€…ˆ{c`^]_\^`__``^```_]^\\ZYWWST`qtdf�¸ËÈÃÅËÍÎÍÍÍÊÉÊÆÃÌÊÆÍÏÓ½‡ØßÛÛÙ×ÎËÑÑÏÑÐÐÑÍÌÆÆÊÊÉÆÅÇĿ¾µ¶¹º·³˜‰�||ywrnljgdb__eec_\VJHMRdn�ŠŠ‹��ŒŠŠ��ŽŽŠ„�€}||~���…ƒxooprttrnmos|�~yurx‚…ˆ†ƒ‡ˆxb]X]baa`^_c`^b`]_^\Z[ZZZWUUT[mtjcy©ÇÄÃÊÌÌÎÇÅÊÉÊÆÀÉËÌÏÑÔ½†ÖÝÛÚÙÙÀ¶ÏÒÐÐÑÏÐÍËÈÀÇÇÇÈÇÈÉÆÄľ¹¸¹·¹ªŒƒ�{zvqomlihebcdd`ZSM@8>_r}ŒŠ‰Š‹‹�‹‹���Œ‡�{wvwwvtroliea]\__^\\\[]bikllmrz‚‡Š‰‡‰‰wda^ca^_^`bb^]__\^][\\]]\YWXYSVhuoal˜»ÄÆËËÌÊÈÈÆÈÊÉÉËÎÐÐÒ½‡ÖÞÚÙÚÖÉÅÎÑÓÒÑÐÐÏÏÎÄÆÇÆÇÇÅÅÄÃÅÅÂÀ¹µ·°’‚…ƒ{zurpolihfc`a`[RMH??\s|ЋЋ‹‹ŒŒ�ŽŒŽŠƒ}uqonle^Z[]a^[ZXSQRRTXVPOKHM[gpy�‡ˆŠ‰‰ˆuc`\[[[^`b^UZ^[[]^^^aa_^\ZWW[YUVcsugd…²ËËÉÉÌÍÉÆÇÉÆÇËÌÎÏÒ¼‰ÙÜÛÞØÐÎÒÑÒÔÒÑÐÎÎÏÎÍÉ¿ÂÄÆÆÃÃÃÂÃÅÆ¾µ¶´§�†…�|zywsjighjie``^YRLF@Yw}ˆ‹‹ŠŠŒŒ�Ž��Œˆ‚zqbPFCFJS]acbtxmffih`_aVG11;>Ibt}‚…‰Š‰‡pXRNJS^`ab^X]^]]`ca__^^^][Y[^_]XW`rykbx©ÉËÈÊËËÈÄÆÆÅÊÌÍÎÒ¼‡ÖÚÖ×ÎÇËÍÑÑÏÑÑÍÍÍÍÍÏŵ¿ÇÅÅÅÄÅÆÅÅź¹¹¹¤Œ‡…‚ywxummjiligb][YTMH?Ry�…‹‹ŠŠ‹ŒŽŽŽŽ‰†‚xcH:8:?ANZMI[{€tijmgWIEEGJY\UMUky…Š‹‹†kVQNLUaba_bed`]]`ba`^]__]^_^_^^^[Y_nyscl™ÂÍÉÉÈÇÇÄÇÆÅÊÍÏÑ»†ÔßÎÎÐÈÎÏÑÑÑÕÒÎÍÎÎÎÍÄ¿ÅÇÄÆÇÅÇÉÉÅÿ¾¾¼´›‰„}usttutoljhgd\YUPJDDFIKOMHIJIJNPRRTUSPLNRSRTX\[[\ZZZ]_bdda`^WUTVYXWWWRM…ÐÎÍÎÐÑÏËÍÏÐÐÑÐÏÐÎÍÐÏÎÎÍÌÊËÆÄËËÇÊÍÉÇÊÃÄÃÃÅ¿¾ÃÇÅÅÂÂÀ¿½¸Ÿ‘“��Œ†{‰‚iWRRQUUZbfjI10/16AIPUWZ^_ab```aabbba]YX]enu}…ŠƒVAEHMR]lfG:AEHJLKNQQMJNMLLLIHIMQRTUWXYYXY^cbcggfggb\XXYXVTU\YP…ÐÑÌËÎÑÑÍÎÍÍÑÒÐÏÍÌÍÏÎÎÎÌÉÉÌÃÄÊÊËÌÊÇÉÊÂÃÉÇÆÅÂÃ¿ÂÆÃÄÁ¿¼º©•‘��Œ„{||…�€cNMQSTR[biiD30//2:ELOPQSVYXXYYZ[YWQPW`fjpx†‹‚T?CCIOU`olM:>@@DGILQPOPKHHHKLLMQRSTTRSVWZ_bbdeddbc_[WVYZVTTXZV…ÏÐÊËÊÉÈÊËÈÊÎÑÏËÌÎÏÐÎÍÎÎÊÊÍÊÈÆÈÍÉÇÆÈÉÈÇÅÆÈÇÃÄ¿ÃÇÅÅþº¶¯ ”��Œ‚yrƒ„„ƒv]IINSQQ\akb@3///06;DIJKLNPRQQPOMLLV`dgntz‚‡Š‚SDLMQ[bnU972./125;BFGGHIILPSY^aekpu|�…†ˆ‚R:@AEJRZ_fkyyhtyM8;@FGILMLRVWZZYWRPOQSSTRRTW[\XXWYYXWTRTTPS^ca…ÎÏËÉÊÌÌÊËÍÎÎÍÐÏÍÍÏÑÐÏÌËÍÉÈÈÇÈÊÊÊÉÊÊÊËÈÈÇÄ¿½ÅÈÈÇÇÆÆÃ¾À½¼²§š’šœœ�ƒŒŠtnsvjS@?IMSZcjM9930024;BJOQRQSVY[]`elosy~ƒ†‡ˆ�R8=ACISX^ekq~�—¤Š^K@>EIKJHIORVYYXNGJLNNMOTTRWYSQPUSPQUY[]aa`aa…ÎÏÍÉÈÉËÌÆÊÏÌËÍÎÎËÌÐÎÏÎÍËÉËÉÊËËÉÊÌÉ»ÅÊÈÇÈÆÁÀÄÇÈÆÅÆ´°ÀÁ¾·«¦š�•œ�™�†‰nkloo\E>FNTXagG7753126=FMRTTVY[^afjnrx}�„‡‡ˆ�R6:=AHRX^gmq|�…›�|iK>@GLKLNPSUVWSIGMONMLONNOPSSU]ZXY\]_a[TUUY„ÏÏÍÉÊÌÌÎÊÍÐÏÎÌÈÊÎÎÊÊÎÌÎÏÐËÈÊËËÉÊÉÆ½ÅÈÈÈÆÆÄÃÅÈÈÇÇȶ±Àºµª¥¢ž“•¡¡£–‡ƒˆznegnvdNAAKSWcaB7645448@IOTWZ[^aejmpw|�ƒ†‡‡€Q49<@IRX_ekv‡”ƒMRs�†sQD?@GKKMPQSUWSNTWRSTSSUUSZ][ZUTTV^ZRg‡œ“[ƒÎÎÌÊÉÌÌÍÍÏÍÌÍÏÈÉÍÍËÎÎÌËÎÏÌËÊÌÌÉÆÅÉËÈÇÈÉÆÅÇÇÌÌþ½»º¹°£ž¡¢  —’ž¤§œŠ‚ˆ„tngcongYG@ENUh_?967788;CLTY[[^afkoty|~�„†††O28;?ISZadn„�‘„O3;J`ryv[I<><:99:?HPTVY]chlqtw|€ƒ†‡‡†P/5:?GS\al€Ž��xRG=77Dt™”Š^D>@HKJNQQTSQQNONPUTf�Ž}{›§£ƒožÒÎÚÝâȃÉÊÉÆÈËÊÉÉÅÇÍÍÊÇÇÉÈÊËÉÊÍÊÊÍÍÃÂÇÄÄÄÆÄÄÄÅÆÄ»´±¬©©§¥£�˜™›–Ž•™›™ˆˆ†xqy�‰‡ysntl`behkd\SP^[?:9;<=>?DJOV[_ehlpty~‚ƒ„†…ƒ~V/3;?HU_iŽ�ŽŠfCFJKC9Ce…–‹†zX<>DEAEEJQQZhr]Rh‘Öஞ¬Ÿyw²ÞÛÄÉÌÆ­‚ÉÈÅÃÃÊÈÇÆÆÍÍÍËÈÇÃÅÊÎËÈÍËÊÌÍÈÄÆÂÀÂÁÀÁº¯©¦¥¦ª®®¨ž–”—œ›�Š�™�ŽŒ˜†|Š‹†yglvpfa[]^ekt…ІoYK?98:?AFMUZ_eilpuxz|~‚€|wZ726=HR`yˆ‹‹‹VBCFILI=4AZt‹š‰iWG?P\`lx€œ¬§{^k�ÒÒ®§¤�…©Ä·®­ª§¥”‚ÉÅÁÅÈÊÍÎÇÅÌÊÆÈÊÉÉÊËÎÌÈÈÉÈÈËÉÇÅÃÃÀ½½»·´¬©ª¨¥¨®°¦˜‘‘•œ£ “‡…Žž”Œ—™›¢–‚„|q}€zy‚xjefjy�”œ¢¢˜ŠvaTMKOU[agntx|€�„ˆ‹��‹„‚fC=9?Md�“–˜˜•�jPOOQQSVPA>E[q•² t�½ ™–•§ÁǺ°¬‹¤ÇÁ¹º¹¸¿¼²±³´´´¶©�ÆÆÄÂÅÊÌÍÊÈÉÆÂÆÉÉËËËÊÉÈÇÈÈÈÉÆÅľ¸³¯«¨ª©©«ªª¯²©™ŒŠŽ— ¤ “‰…�•Š‹�š�u`WRSUQQRVSMHCGMNRSYgmzzmZA89@NW[iw|€ƒƒ�€}yumfcaadfda]XQG;5434@O]bachmqvy{|€mPEDDFKPW`g^OVeƒ¸ÈÉÉÌÏÎÍÐÐÎÎÎÎÎÎÑÑÎÎÖ»jw|nm’»ÉÈÉËÊÌÉÈÊÉÌÉÉÍÎÊÍÎÒ¿…‡ÔÙÕÕÔÖÙÙÖÕÒÔÙÛÚ×Óê�–‹��‹‘šš’‰qmkjjjkmmkjjlmnlmpqpkgeYECB:ALRXhyƒƒ„„�|ysmhda`YOHC?>:620002AVdc`cinrvy|€‚‚vVA>=?BFNYfiVTf”ÄÆÊÍÎÎÎÐÍËÏÎÏÏÐÎÑÐÎÎÐÖÇ�js~tnŒ¹ËÊÊËËÊËÊÈÍÌÈÍËÈÊËÌÔ·…ÑÚ××ÖÔÔÕÕØØÛÖË¿¬š‹‡‰‰�“‘�‹‰‚wlgddiljlkkllllopomoonqshSG?AB:AJOWiy�„………�|wrmjgfaVKEDCDFHIGEDIT\\[_glosw{‚„„~dC987987:I^YWo¨ËÊÉÌÏÑÏÏËÄÆÊÍÎÏÏÎÎÎÐÒÒÜÒŸrs}sj‚²ËÊÊÊÉËÌÊËÎÌÎÍÊÈÈÎμ…ÏÖÓÓÖÙÙÙ×ÑÅ·¦š�ŽŒ„�š–•”�„wmiffhjiimlllmmloonooooonmkkpkYMF@CLPWhz�„†ˆ†ƒ~wtsssqmid_\\\][WSPPRTUV\elprvz}€ƒ…†„tYHD@>=716BO\€ºÊÈÉËÊËÎÍÏËÆËÍÏÏÎÍÏÏÊÎÔÒÖÖ¯}oytl}­ÊÊÊÈÉÍÍËÌÍÎÐÎÍÍÎк…ÎÓ×ÚÛØÌºª�”��•œŸ•„�Œ‡~yrhefjjklnnmnllnqnnonoqppooomly�ucNCJQRWi}…†‡‰ˆ†„}~~|ywtqmkigc_ZVUUVVY_florvz}�ƒ†ˆˆ~fTMKLLIJOKK^ŽÅÉÇÉÌÿÌÍÎÏÎÏÎÐÎÎÍÏÐÎÏÓÓÎÐÔºƒoz|oy§ÆËÈÉÌÍËÊÌÍÍÍÍÑÏÑ»‡Ô×ÏÄ´¢”‹Š‘—› ¢›“ypyxnhhheimnpnmnmmmnnprpopoqrrpnoqoxxlqvqcUVTQYn€‡ˆ‡ˆ‰‰ˆ†…„‚}{wsokheb`]\[\^aekqsux{~€�‚„‡ŠŠ…r_UQQUY_gi^e”ÅÊÉÈËÈÇÎÏÏÎÍÍÎÐÎÎËËËÎÑÓÑÑÏÏÖĈjwzou�ÁÈÈÌÌÊËÍÎÍÍÎÑËÊ»ƒ»©—‹ˆŒ”›Ÿ¡�”ŠƒslgfkiehhhkklnnkklkklmmoopppqppqoonoqxkeksuslcXO]v„ˆ‰‰‰‹‹‰‰ˆ‡…‚�~{vqmjihgefggjmrvwy|}€€�ƒ†‰‹Œ‰pd\USY`iqqr—ÂÈÉÊÇÊÐÏÐÍÐÐÌÏÍÌÐÍÌÉÌÐÒÑÐÒÑÑÙÈŽmszor•½ÇÈÈÉËÌÏÏÍÐÐË̺o‘Š�–ž¥¡—‹~vpmn|�•†n[Z_abcabhiijkjjlnnnnopppnoonmmlrtjjkrvvsmd\k�‰ŠŠŠ‹�Œ‹‹‰‰‡„ƒ�}zxvtssrrqqsvxz{{|}€‚�€�‚†Œ�‘ކ{qid^X[dnu{šÄÆÆÉÊÌÎÎÌÉÁÇÏÎÊÏÑÐÏÎÏÏÑÓÒÑÒÓÓÙÑœorypp޶ÄÄÊËÊËÎÐÐÐÏкo ¤¥ –†xnkmx‹šªÇÒÇÄ·‰\OTXXY[^beghijkklmnnoponooollkttiijx{wtrpp|ˆ‹Š‰Š�Ž��ŽŒ‹‰‡†…‚€~}|{{{yyz|}}~~~‚ƒ‚�€‚‡Ž“•’‹�ytpmigipx‘¹ÎÈÆËÍËÌÊË¿ÅÐÎÍÐÐÎËÎÐÎÑÒÓÒÔÍÆÎÖÔ©vqyrnŒµÃÅÈËËÍÐÏÍÍмwž‹|snqxˆ›­»ÉÉÏÚÌÀÏÝÓŸ`IGKPSVZ\^adfffghjmnkkmmnppnkutijo~|stvux‚‰‹ŠˆŠ��‘��Ž�‹‹‹Š‰ˆ†„‚��€}~~€€€€‚„„‚�‚…‰�‘”“Ž…}zxttssuz‚ˆŠ«ÍÊÇÉÊÌÍÍÏÐÈÊÏÎÏÍÊÏÎÍÑÒÑÑÑȾËÐÕÛ«spyrpŒµÄÆÊÍÌÎÐÏÏÒ½avnu„—­¾ÏÖØ×ÓÉËÐÈÀÇËÑЩnG>AHLOQUY\_abceilmiikkklnonvwjlt€}z}|y{ƒ‹Œ‹ˆŠŽ�’‘�‘‘�‘���Œ‰ˆ†…„„„ƒ€€��€�‚„…„ƒƒ…‡‰Œ” ¢˜Š{{{zyyz}ƒŒwjžÇÌÆÈÌÍÌÎÐÈËÎÏÐÌÎÐÌËÏÐÏÐÍÍÏÏÓÓÔØ®umvsq…­ÃÅÈÌÍÐÐÐÑ»e–©ÀÏÚÝÞÝÚÔÔÓÐÒÔļ¿ÆÉ¨vT>8>EHLORVY\_behjijijjikmntxlmvƒ…ˆzp{}„‹ŒŒŠŠ��‘‘’”•••“‘�ŽŒŒŠˆ‡…„„ƒ‚‚‚��ƒ„„‚�ƒ„‡ŠŽš®»¹¬™„{{|~~~�…ŽˆihŽ»ÊÆÉÌÊËÎÍÉËÐÐÌÌÐÏÌÍÏÐÏÏÐÎÍÐÑÑÖÛµ{kyyn~¡ºÃÆÊÍÏÎϺvÊßáÝÛÙØ××Ö×ÔÔØÎÁÀÂý¹½¼¯ŠbD67>DHKNQUY\`cfefgghhijjoxmlx…‡„b^y~„ˆŠ‹Š‹Ž‘’“••——–•“‘‘��Œ‹Š‡……†……„ƒƒ‚�~�ƒ†‹š¯ÀĽ´¥�}yx{~€‚ƒ‡Œ�|ib}­ÊËÈÊÊÊÌÉËÍÍËÂËÑÏÎÏÎÎÐÐÎÍÏÍÒÑÒÛ¾ƒmyxm}žºÄÆÊÎÎк{Ì×Õ××רØ×רÓ×ÒÃÀÁ¿¾½º¼¹¹«‰lP:26?DGILPV\_a`aceedfhhkxqm{…‚qT\u€ƒˆŠ‹‰‹Ž�“•••———–”“”•“‘��‹ˆ‡†…„„„ƒ�~}€€€‚‡“¤³»½¹´ª•xy{}ƒ„ˆ��|wqer ÅÉÇÈÉÊÌÌÍÎÍÆËÐÎÐÑËÌÐÏÐÒÐÑÓÒÏÑÚÆ‰jqvt~›ºÄÄÉÎѹ‡ÓÔÔÚÙÖÕÒÐ×ÓÑÖǺº»¹½Â±´º´´ ƒpYD416BFJOSVYXZ]_]bsx€rcixˆ‰„�‚…ˆŠ�‘’“””“”••””’‘�ŽŒ‰†…ƒ�€}zxz~‚……†ˆ‹�“œ¡¡¢¡ ž•onsx}€„ŠŒv`^]]hvtbd‰¸ËÆÉÌÌÉÊÎÍÍÏËÈÍÏÏËËÏÐÐÑÐÏÎÑÐÏÕÀ†huwi~ªÃ´‡ÕÝÙ×ÕÖÕÕרÚÑÅÅÃÂÃÀ¿»²¹»¸¸¹·¯�ztndYI;2/19@AFKORUX[\]`ox€{kov~‰†€�…‡Š��’’““’’“’“��ŽŽ�‰‡…ƒ€~~~|wssttmiov{|�‹•˜—––”Šxljouz€ˆ‰ta]\\[cqtic|ªÇÌÉËËÆÅÊÄÊÉÇÊÎÐÎÎÐÐÏÑÒÏÎÑÑÎÌÓÅ‘ktxov¡´†Ô×Õ×ÕÖØÖÔÚÒ¸¼ÇÅÄÂÀ¼¿ºµ·¹¸º»·¡‡~yvpfYND8/-4:=BFINSX^]\hx~ƒ€„{p�ˆ‚‚„†‰��‘’’‘‘‘‘‘�ŽŒŒŒŠˆ‡…�}~~|ztlhibO?CRcpv{‚†‡††‚xojhlsx|†‡o_]Z^^[_mwqck—ÁÌÇÈÅÃËÍÌÆÊËËËÎÐÏÐÏÏÒÑÏÏÎÎÏÍÔΕjpslv�†ÓÕÔ××ÙØØÙÙÇºÄÆÄÅÃÄÁ¾¾¹¶»½¼¼´°–‚}ytniaVLA5//39>BFKPVZ[_rx{‡�‡ss…ƒ�…†ˆ���‘��ŽŽ�ŽŒŒŒ‹‰ˆˆ‡ƒ€|||{yvmc_\SHBBHUepvxwwwtsqkgipv{~„†n`_\]\YX_lsrffЏÆÅÈÉÊÐËËÎÉÉÊÍËÍÏÏÎÎÐÎÎÎÎÐÑÎÓОopwpj…ÔÚרÙÚÚÚÜØÌËËÆÅÅÄÂÀ¿ÂÀº¹¼¿¼¶¸ª�‚|vqnid^TF:2,.5<@DGJQUWfuoxŠ�†„‚€„†‰���Ž�ŒŒ‹Š‰ˆ‡†††††…‚}{{{}}|vla][YVSLKYippoonouwrihouz}„‚la`]]\ZZ\]`lxocy¦ÆËÆÈÌËÍÌÈÉÊËËÌÍÏÏÏÐÎÎÎÌÍÐÑÎÓÓ¬znul‡ÓÙØÚÛÛÜÜÛÕÌËÉÉÇÅÅÃÃÄÂÁ½¯±¾½¸µ´›ƒzwsoiebVID:/,.7?CEILSZr{x‰‘Ž‹ƒ€�„‡‰�Œ‹‹Œ‹‰‡‡‡†„‚ƒ„ƒƒ‚|||~��€~yqidbbdbcgkmlmmnsx|zqlovy|‚|g_^_`_`][ZY`nwqdj•ÁËÇÉÌÌÌÌÅÇÎÎÍËÍÐÑÐÏÎÎËÊÍÏÑÏÓÚº�ls‡ÔÚÙÚÛÛÝÝÜÑÈÊÉËÊÈÈÈÇÇÅÄ·²¼½¸³´©�‚|yukee`ZSJA6.-2:?EOVSf„‡‹Š†€~€€ƒ†ˆ‹‹‹ŠŠ‰ˆ‡‡‡‡„€�‚�‚�€‚‚‚ƒƒ‚€~zuplkkjihhijjmsx|€yrrvy{�xfea_aa`_[ZZ[_fuwgb…´ÈÈÈËÌÍǾÄÍÌÊÊÍÏÎÏÎÍÎÍÍÏÐÒÑÒÚ¿†n†ÖÝÛÛÜÜÜÚÚÑËÍÍÌËÌÌÉÈÆ¿¿Ãþ»»·¶¶´£ˆ‚|wurmfab_XPH@4//5ELTQTv�‹‡…ƒ†„€‚„ˆ‹‹Š‰ˆ‰‹Šˆ†„ƒ�€�‚�€ƒ††…„„ƒ‚€~|xtqlid`adgkrw{��~yvvvy€tdgb]^[[_`]XWYX`sxk_t¥ÃÈÇÉËȸ³ÉÍÊÈÌÎÏÐÎÍÎÎÍÎÐÏÐÏÑ×Ä�†ØßÜÜÜÝÝÜØÌÊÌÍÎÍÍÍËÈÇÀ¾Ã½¹¸¶¸¹´±•„~xwtqlheea[TLD?85CEJRNc†�‹ŠŒŽ„|�ƒ‰��Šˆ‰‹Œ�‹ˆ…„ƒƒƒ„„„…‡ˆ‡………„„ƒ‚�}{vqkfcbbhpv{~�‚ƒ‚~zvsxpbca^\Y]_bca[WWV]krm`h“½ÉÅÅÅÅÅÉÉÇÇËÏÑÐÍÎÌËÌÍÑÎÎÑÐÐ׿‡ÚàÝÝÜÜÝÜÑÈËËËÌÉÄÈËÉÆÈÇÅ¿¼º¸¸¹µµ¨�„�zusmjjfcZWXSQIBIJDMPUq‰‰ˆ‹‹{lzƒˆŒŒ‹‰ˆŠ��ŒŠ‰†……†‡ˆ‰‹‹Š‡†‡‡†„„ƒ�€~ytnhedfkqw|‚„…ƒ‚xqx~oca^[]]___`a]YYZ\[brufd‚±ÈÅÅËÍÇÄÄÅÊÎÐÑÍÎÎËËÍÏÏÐÒÑÑÓ½‡ÙàÞÝÝÝÝÚÌÊÎÍËËËÇÆÇÇÀÃÉÈÆÆÂ¾º´³´¶¶Ÿ„„}xwplng^VX[[`[QRSNLNO\v„†„€lYqƒ†Š�Œ‹ˆ‰Ž�ŽŒŠˆ††ˆŠ‹�ŽŒ‰‡†‡†……ƒ‚��~zuniddhlrw{�„„���yqz~k___]_a_^]\\]]\[\ZXdt{n]r¤ÃÅÅÄÅÈÅÆËÈÇÏÍÎÎËËÍÍÎÑÑÑÓÕ¹‡ØÙ×ßÜÛÝØÎÌÎÍÌÍÌËËÆÄÂÃÆÉÈÅÃÁ¾·µ¸¸µ­‘…‚~{urqm_^aZW]e^XXYUOKMVm|{r`Xo�…Š�Œ‹‰Œ�’’�Œ‰ˆ‰‹�Ž��Ї…„ƒ‚ƒ„ƒ‚€€ƒ‚{tnihkpuy|€€€ƒ~w€|e\]___`_\^``__^ZWZZ[_lwnaj”¼ÉÆÇÈÄÅÊÉÉÎÍÌËÊÊËÌÎÎÒÒÐÔ½ˆØÙ×ÝÛ×ÚÔÍËÈËËÌÆÆÊÈÈÇÆÆÈǾ¼·º¼½»³¯¤‹†„zxpljifa]\ceba^[ULD>@Pflegq}ƒˆŠ‹ŠŠ�‘””’�‹‰‹Ž�‘�Œ‡„ƒ~�‚‚�‚‰‘�†}xutuvx{||}{{}�…ƒ~„zb[[^^`__]^bb`_^][Z[XVYamrhd‚³ÊÈÆÆÄÈÌÏÎÍÍÌËÉÊÍÎÎÍÍÊÑ¿‡ÖßÝÛÚÖÕÑÏÎÊÍÏÎÎËËÉÉÉÈÈÇÅÄÁ¼¹½¹¸¹ºµ°™‡ƒ}~{tnmkihhedcdea\VOE<44Qmpos}ƒ‡‰Œ‹‹Ž‘••”“Ž‹Ž����ˆƒ€}zyz|€…‰ˆ€wrrstqmkmqtuvux€…†‚…tb^\__^_`_^^``]]^][YWVVSYhuk`w£ÄÊÅÂÈÌÍÎÍÌËÉÊÊÃÇÎÆÉÎÒ¾‡×ÞÛÛÙØÑ¾ÈÒÐÏÑÏÑÏÌÉÂÇÉÈÆÄÇÊĹ´¶¹º¸¬�„~~zxuomjjlifaab_]VNG@72Hjrrv{�…‰‹�Ž�’––—•’���ŽŽŠ„~yuuvvutrrqokea``bb`]\]cjmlls|„ˆ‡‡qcdaa^]ba_aa^^^\[[[\[XTUTUdspdh�¼ÆÁÆÌÌÎÌÅÈÈÉÊÂÅËÌÎÐÔ½†ÖÛÚÛØØÑ·ÂÒÑÐÐÏÏÏÌÌÄÁÈÆÇÇÇÈÇÃÄÄ¿º¸·¶¶œ…„ƒzytpnmolhdba`_ZQHA90?etuz|ƒ‡Š�“’’•™š˜–“��ŽŽ‡€yspomkh_[^^`a][ZXVVWWVSRRWahp{ƒˆ‰†sc_``_ab`\^_\\]\[\]^]ZXWXVR]pteb€¬ÃÄÉËÊËÊÉÈÈÊÊÊÉÍÐÏÒ¾‡ØÝÚÛÛÙÑÌÍÐÓÔÑÐÐÏÏÐÊÇÈÅÆÇÇÄÃÂÃÄÄÃÀ´µµ¥Šƒ…‚|{yusonnkhd`^_]VNC;39`xz~‚…‰’”“’“˜›››™•’‘Œ‡€xkYIGGEKTQIIXba]XUSLBFJ;28I_oy‚‡Š…iZ]]^``WW]][[]^^_a`]\[YWZ[VT[kvoap ÃÌÉÉËÎÊÇÆÈÇÅÈËÍÏÒ½ˆÙÜ×ÝÙÐÊÎÑÑÐÑÑÏÎÎÎÎÍο¼ÅÅÆÅÂÄÄÁÂÄĺ¶¶³¡Š…ƒ�|yywojlmle_[YXSKB966Wz‚€|ƒ‡Ž“”’“—œŸ£¢œ˜–�ˆ€oT?68951/57AJENdt}…Œ…\N[_`aa[\`^\^ba_``_]]\Z[]^_\WZjxsci’¿ÎÉÉÌËÉÄÄÆÅÈÌÌÍÑ»ˆÕÜÖÍÏÅÉÍÐÒÏÐÒÎÌÍÎÍÌÊ»»ÇÅÃÆÅÅÈÇÅÂÂÀ¹º»µ›‰†ƒ}vvwrnmklga]VSMD=<<8Mw‚„€{}�ƒˆ‘•”–™�¡§©¢�œ•ˆxaT[_`_`^ZQC601468;=<5&'In{jU[nzƒ‹€[R]ba^`dea^\^aa`_^__^]^_^^^_]Z\euyic‚´ËÉÉÉÇÆÄÆÉÅÈÌÍл‡ÓÝÚÏÓÊÍÐÎÐÑÕÖÒÏÌÌÍÍÈÃÆÈÆÆÈÇÇÈÉÈÄÃÁ¾¾¾»­‘…}vttsutoligda^TLFBDD>Fq„…‚{y}ƒŒ�‘”˜›ž£ª©¤ —†tgo{{x{„Ž–—ŒwbMBCEHIJD=Kn‹’„nemx‚‰|`\_[WSQUVUVRQZabb`a``____\[\\Z\\`oxqdt£ÇÌÅÃÅÆÉÉÆÇËÍй†ÓÜÝÝÒËÌÏÒÓÒÑÒÐÐÎÈÊÌÊÉÉÉÉÈÈÈÉÈÇÈÇŽ¼¿¼¹Ÿ†y{|zytqqiddc_VPNMKKBBjƒ…ƒ{wy{~‡��‘“—™�¥¬¨¡•…zz€ƒƒˆŽ�‘𢧩¥œ‘†€ylijmyˆ––‹€tqw†ya^YSRROQMJKMMV\ZUTW[_````a^\\Z[\XYfusch”ÀÊÅÃÅÈÇÇÈÇÉк‡ØßÜÖËÊÎÑÏÏÓÎÍÊËÏËËËÌÍÈÈÉÈÈÇÉÉÈÉÈÆÅÁ¼¾½»±•ˆ‡…�{uvuoife_\ZYSTPACf��{vvy{‚‹�Ž�’”–œ¥¥ž”‹†‡‰‡‹—œ˜’��”ž©³µ°­£•‘�‹†Š��Š„{vw|ƒr][XXXVUVSPPTWPNLFDHV^^__ada`_][Z[YW^mria€³ÉÆÁÂÃÄÈÅÇͺ†×ÝÙÕËÎÑÏÀÅÒÓÑÍÌÎÍÎÌÊÉÊÉÉÉÉÉÉÉÊÊÉÅÃÃÀ½½¼½«�І‚|yxutrpkeca][YNBDaz€€zsquwzƒ‹�Œ���‘–š˜––•—™˜˜˜˜–•“���”𠤍¦¡›”‹„ƒƒ†ˆ„}zx{~jZYYZYYWUTUVWYSLIGGP[^^]]_a`aa^\[[[ZV[hsnbs¡ÃÅÀÂÃÇÈËË·‡×ÜØÑÌÐÑÐÊËÒÓÍÎÑÎÎÎÌÌÊËÇÅÊÉÈÊÊÇÇÉż¸¼¾¼¼¼¶š‡†„‚€|ywtrqlfbbb`WOUTGk}zrnpsv|…ŠŠ‰Š�ŒŒ�’”˜ž¢¥§¤�—“����І†‰Œ�ŒŠ…‚€}}~‚ŒŒ{z~wd\[ZYX[\WUVTTTUQLOW__ZWRPQTVTW[]_\Y[[ZY_qteg�»ÊÅÃÇÈÊθˆÙÞÖÍÍÏÑÓÔÏÏÏÍÍÏÐÎÍÎÌÎÌÅÇÇÀÄÈÉÄÅÇȼ·¼¿¼»º·¤‡‡‡…ƒ€|urrplidhh`[Vnw@Rt|{tnkoru{ƒ†„‚‡Š‰‰Œ�–ž¡£¥¥¡š“Œ†‚~}{zzzzyzxvvwxŽ•‰€}~n^^\\[YY[XUUQRVUQUX]ZSLLLMNMLGKVXZ\]\Z[[Y\kvja€µÊÅÃÆÉ͹‡ØÝÒËÍÎÒÓÒÎÎÏÓÎÉÑÍËÍËËÌËÌÈÅÉÊÉÆÈÊÊÄÄý¼¼º·­”Œ�ˆ…ƒ}stxvoighdbc_s‰cFeuxtniknos|‚‚��„‰�“™� ¢¥¦¢š“�†~xtsqomnqrromnqw�Ž˜˜‘…xe]^]\\]][XWTSUWUW_\UQOKKNNPNJGKMKOY^[[\^^Y[gsnbt¥ÃÅÂÇÌ·‰ÕÖÌÊÌÏÓÔÐÏÍËÐÎÊÌËÍÍÎÌÍÎÌÍÍÌÌÌËÊÊÈþÁÀ¾¾½»¶­—�‹‡†‚}|{yslggfhicr……RUjttojiknqv{}}zy}ƒŠ•›ž¡¢¥¦¡š”�Š€ytpmihiiiiggkry„‘˜œ›Œr_^_^\\^^^\[Z[YWY^_XRQSTQPMONLLMJINZ`b`_][Z[X_qxkl‘ºÅÅÌ·ˆÖÓÉÊËÐÓÖÒÏÍÊÌÏÊÂËÏÏÎÎÍËÊËÊÌÎÎÌÊÈÉýÀ¿¾¾À¿»º¤ŽŒ‰†‚�‚~xsnmnpnjfuy�mO`krnkigjmpuxwutuy€Œš£¤¡  žš”‘�…}vqmkjiihggjpv|ˆ“—œœŒm^^__\]\]_^\^]\]]]XTTRQRRQSROPQPNJLX`eb^][YZZ[bq~n`|ªÄÊ·†ÖÔÉÊËÐÓÇÅÎÉÍÏÍËÉÌÍÏÍÍÍÉÉÇÈÌɺÄÊÉÌÌÇÀ½ÀÁÀ»º¼³™Š‡„€ƒ�{rqusqkjws‹ŠZZemnlkhfhkotvspry€Š˜ ¢¢ Ÿ�›˜–’�ƒzuqomjhggkpuy��™œ œ„f__^^]`_]^^]^^]\[XVVUUTSSRSRPRRRPKLQZab`]][YY\_]gvqaoœ¼¶‡ÔÑÊÍÌÌÓÅÀËÆÊÏÊÌÍËÍÍÍËËÌÊÆÈËÇ»ÃÉÊËÊÇÂÃÃÃÁ·´·¼ª�‡„�‚…ƒ‚€{wwusmjyr|•rZcimnlkhiijorpoqv€‹“™žŸŸŸ�œ›˜”‘‹„}vqmifgimqu{Œšž¡¡™{`]_]\^_`_^__^^^[YYYYYYXWUTSSQPORRLJO^dca_^^ZVW\[W`rtgj‹¥ˆÒÍËÐÑÏÐÓÏÐÎÂÂÅÉÌÎÊÌÏËÉËÊÈÈÉËÍËÊËÊÉÈÇÆÃÃÁ¹´·º¹¡�‹ƒƒ…‚€}~~zxunj{yo‘Šb^fjnonkhiijlnoor{†Œ’—›ž�ššœœ™—”�†~vpljknpu~Œšž¡Ÿ›�t]\^]]]_`___^]^__^\[\]\YYXWVWUSSRRMJQZXTX]__[YWWYYVWhtpir‡ÎËÍÑÔÓÒÒÑÌÍÌÄÄÊÌÇÈÐÑÎÊÊÌËÉÉÊÉËËÌËÊÈÅÆÄÅÁÀ¿À»¾³˜�ˆ…„�€}}~~|xqm}‚l‡›s\cglnomggihknnqtw‡Œ“™›œ›šž¢¢Ÿ�›™•�‰…��„‰‘›¡¢¡Ÿœ”‡l\[[_`__^^__^__^^]\\]\[ZYWVYYYZZTQSSPLGIPVZ[^`ZXVVWUWbqpc†ÊËËÎÒÑÐÑѸ»ÓÏËÊÊÆÌÑÎÎÉÁÊËËËËÊÊËÌËÉÇÅÆÇÿÀÂÀ¿¾»¥�Ž‹‡ƒ�||}|{tq~„ll˜�gbfggjmidffilnqrsyƒŠ��“–šœŸ¡¤¤¥§§§¦¤¡žž ¢¥§¤¤£Ÿ˜�‚dYYX]^_^^__^_``_^]]^][Z[[XX[[WVWTQTTMIHPX[\\]a`ZXYYZZUZjq…ÈËÏÏÑÐÍÐϾ¼ÊÏÐÌÉÎÍÍÌËËÅÊÊÌÌËÉÉÊËÉÊÈÇÆÆÁ¼¼»¼ÁÀ½µ›��‹…ƒ€~~}zzxv~„o\zœ~cfedejjcddeijklosz„Š�”™� ¢¤¤£¡£¦§¦¦§¦¥¤¤¢¡¥¤œ’�z]VWWXZ\]^^^__````^]_][\\\\]][WTUWUQNLKIJMPRXYY^^\\[[^]X[gƒÈÌÌÕÓÐÏÉÊÏÎËÌÏÎÏÐÏÍÌÌÎÎÌËÌÌÊËËÊÉÉÌÊÈÇÇÆ¾¼»¼ÁÁ¿¿®•��Іƒ�‚‚~|x}„ta_‹–ldhfddgebbcceghijmu‡�“™œž¡¢¡ žŸ¡¥¥¥¤¥§¦¥¢¤¦ •�‰mVTUVWY[[Z[\^^\]^_`_`_]]]]\]\[YXWVTSPONLJIGDPYZ\_^^[Z[]][XƒÈÎÌÏÏÍÍÆÊÍËËÌËËÐÑÑÏÎÍÌÊËËÌËËÎÍÊÊÉËÊÉÈÇÆ½¼Ã¿½¾»¸¢‘��‡„ƒ„„ƒ‚€{}ƒudZoœˆgfgeabda_`_adeeeiox…ŠŽ’˜™› ¢¡ ¢¥¤¢ £¥¦¥¢ Ÿ—Ž‹�cVWWXZZZ[Z[[[\Z[[[_`a`^^_]Z[\YYZXTTTQQSTSSPINY[^^]]\Z\^][WƒÊÏÑÈÊÎÎÎÎÎÊËÎÍÌÎÑÐÏÍËÎËÇÊËÊËÌÌÌÍÌÌÊÉÈÇÅ¿Àþ¼¿»»²˜��‰†‰ˆ…„ƒ‚€�ve]Z… |cehd^^a][^^_cdfhhmv~‚„ˆ�”˜�ž £¤¢Ÿ››š™��›—”‹„ƒt\WWWXZXWWWZZXWVUVWY[]]\]\\Z[YVWXWWVTSTUVTROMP[^]\\\``bb]ZX„ÎÑÏÏÒÓÎÎÎÏÒÏÎÏÐÑÑÎÌÎÌÉÉËÌÊËÌËËÌËÍÍÈÇÊÉÇÄÄþ¼À¿½¼½§‘މ‰Œ‰………„ƒ�‚zh]Ue•›zbac\Z[ZUZ]]`acefhlsz~€„Š‘•–›žž�›™—–—•‘��Šƒ{eTRSUUVVTSTWXXVURQSTUUSUUX[ZYVTTUVXWXYWTTXTOKR]`\\`__`aa^\[…ÎÑÑÓÔÓÏÍÍÍÏÏÍÏÏÎÏÍÍÏÎÆËÐÎÎÎÎÍÌÊÊÊÇÂÆËÊÆÅÄÆÄ¿ÃÀ¿À¾¸›��ŠŠŠ„…‡†„‚ƒ{j`XSp•™~ic]XWVPOV\^]\`cdfjpvxy{€…�“’”––——˜—�Œ‹ˆƒ}wjPLPQQTVVUUUUWVSUUQOOPQQQSWWURTSQQTZYXXVSSWUROW`]Y[]\YU\aa_Z„ÎÍÌÒÑÐÏÐÏÎÎËÉËÌÏÌËÍÎÏÍÈÌÏÎËÌÌËËÍÌÈÆÆÈÉÆÃÃÃÇÄÁ¾¿Â¿¿­“�Œ‹‹ˆ‰‰‡„„ujbZTXkƒŽ‹|cYUUQJJSZ]\\]_cfgkruusw„…‡Š�’““�‹‹‡€{vqbQJILOSRQSTSQPMKLLLKKOUWVWXVSUXZXUWXVVVUUVURQV^_[\YOQXWZ]^]U„ÐÑÏÔÓÑÑÐÐÐÎËÊÈÈÌÍÎÎÎÏÎÇËÎÍÌÌËÌËÌÎÌÉÅÇÈÄÄÆÃÄÿ¾¿ÀÁ¾¼¤‘��Œ‹ŒŠ†„…{podZUTYclv†q\SRQJBDOY^][Z^`acinpnlosuy~‚„†‡†ƒ€zurqj`a\PHINLLMKJHFIGFFHJJMTSUUVWW[ZZ\[[XYYYUSUUSV^_YY\XRSX_][\XV„ÏÕÔÔÔÒÒÐÍÌÍËÍÎÉÈÍÏÎÍÍÎÊÊËÌÎÏÍÍËÌÍËÊËËÇÆÇÅÆÅÄÀÁÂÁÁ¼¾²˜’�Œ‹Œ‰‡ŒŒˆxphZQQX]][lxfXNOMF?BMWZYXX[]`beiigefgkopsvxyvqnkkeUSU^cZIBFIIEBABFHHKKKKLNPOMORXZYXVVVTVVXUSUVW_a\UVYYXYY\\[ZQKƒÏÕÔÓÑÑÐÉÊÍÏÎÎÐÎÏÏÎÎÎÌÌËËÌÊÉÎÎÉÈÌÌÉÈÊÊÇÆÁÅÉÆÇÀ¿Â¾½·§–’ŽŽ�ˆŠ””‘’ye[SQTZZ`_prcTLMJC<@LSUVVVWZ\^bdedbcddefhjjjgebmdDIIMZfbLAEFECDFDDGIJNPQPSSSSQSVUTRSWVUWWUUUX[aa\WWXWUUYWXWRMK„ÐÒÎÐÐÑÏÈÊÎÎÐÏÏÏÐÏÏÏÐÏÍÍÍÌÉÈÈÉÊÇÈËÉÆÉÉÆÁ»ÆÅ¿ÂÅÂÃÃÂÁÀ¼¸ ’’‘�ЅДЖ�oZRRRWWYcerk_QFHGA:>JRSSRRTVY\]_aaba_^`abba^_j€mA@CGMWejTACIKMNJHIIJLORRRUTPMMQRSUX[[Z\[ZZZ^`dec`_YUUTXWXVVTP„ÐÑËÌÍÑÒÏÍÏÎÐÒÐÎÏÍÍÎÏÎÎÎËÈËÆ¿ÈËÉÊÌÊÈËÇÀÇÆÅ¿ÁÆÃÄÄÂÁÀ½¼­”‘‘��‰�ŠŠ‡“…gOLRSVTZbhsh]KBED>9=HNONNPSUUTUW[\[Z[]]\ZZ`n~‰oA<>CIOWhnV@CHIKLMOPQLLMLKLJHHNQRRUVVXXXX\abbeggghc\YXYXWVTY\S‚ÏÔÌËÌÍÏÍÌÌÊÎÒÑÍÍÍÍÎÎÌÍÍËÈÌÉÆÉÇÊËÊÇÆÉÇÅÇÇÇÈÃÄÁ¾ÅÅÄÃÀ¼º²œ’‘•¢ �w‡‡…„ybJIPRPP[akrcZJ??@>9;CJLLNOSSQPRSRSTSRQPVbn{‚†oB<>@DMU\jrZ=BEJS[blu[<79AECFHILNPSQLQQNOSTQPRSSTTVWY__ZZ[\[ZVTUWVTVSTW†ÐÓÎÌÉËÉÈÎÌËÐÏÎÎÏÐÐÏÎÎÌÎÇÇÈÄÄÆÈÌÌÉÉÉÊÇÇÊËÇÅÃÃÇÈÇÄÅÿ¿·´¶¥Ÿ Ÿ¤Ÿœ‰‡�}ttvhN?FNMQ[bqkZWJ<7:=>AEIHHIJIHHIKKLPV\bhow~�†p@9=DGJSY_eqy]BA=?CEEFIMPRWUUXWUUTPORUVUQOPSY\YVX[[\WUVUTQORW[…ÏÑÌÉÊËÍËÍÍÌÏÐÏÏÏÏÏÐÏÎËËËÌÅÅÇÆÇÊÌÇÇËÌÉÇÈÈÆÃ¿ÃÈÈÆÅÆÆÅÀ¿½¹±©›“�ž¡”‚ŒŒxntvkTA@JMRZbobUTK>68GMTXan]OOKA969BGKMNMNMNOQTX\aekpv{~�…p?59;BLRY`fnt{‰¢šmRD=AHJIIIMPSWYXRIKMMMLMRTRVZURORTNNSYZ[_bbb`„ÎÏÍÌÈÊËÎÉÇÎÏÍÌËÌÍÍÍËÎÏÍÍËËÉÊËËÉÊÉÊ»½ÊÇÉÉÈÄÀÁÅÈÈÇɼ¨¹¾º¯§¢Ÿ”•¢£¦›‰„‰{ofhnueLABKTYcjYOKHB<9DLLKMNQSUVUMHMONMLNNMPOQST[[WV\^^a^VTSWƒÍÐÌËÉÌËÌÌÍÐÏÏÏËÇÌÍËÊÏÌÌÎÐÎÊÊÊËÊÊÈÈÄÅÈÇÉÇÅÇÆÇËÇÂÁÁº¯±©¤¤¤¡¢™”Ÿ¥ª Œƒ‰‡vnhdpogXF@GOUhoXMGDCA=>EMPRRSRSV[_chlrux|~€‚o>06:BMU\_grwxuKDdz†~\JA>DJLLNQRTVUNQXTRUSRSTRW^]\VSTT[^TZz�•h…ÍÌÉËÈÉÊÍÌËÊÊËÎÌÊÎÌÊÍÎÌËÌÍÌÌËÌËÉÆÄÇÎÊÇÇÈÉÉÇůĹ­©§ª¬£›–˜žŸŸ›Ž�‘˜˜‰„‡uxoebdkm`THCHQhdVPIDBA?AIORUTSUW]bfknruz~€��p=/6:BLW\dtƒ‰‡vM65BVlv|fOA;BJMLMPRRTX^\\`]VVXSNPSS[bixu_i—Íáâ©„ËÉÉÊÈÉÊÏÌÇÄÉËËÉÉÌÌÊËÌÌÌËÉËÍÈÅÈÆÅÃÆÇÅÆÆÆÆÂ¹´±®¬¨¦¥žšœ�—�—›œšŠŠˆzsz€ˆ‰|torkacgikcZOJXdVRNIECABHLOQQTX[^cgjntx|}€rA-49BMVayˆŽ�ŒmKIB;6;_�—’oKB=EJILQQSTPPONNNTT^xŒƒs‘¦¦’m„ÄÏÔââÉ‚ÈÊÈÇÄÉËÆÈÇÈÌÎÌÉÆÅÃÇËËÇËÌÊÌÎÈ¿ÃÄÂÂÄÂÃý´­ª¨§«®°¬£™–™�ž‘‹‘›ž�Œ—†z‡�†‰knytid`bdins‚ˆˆwdYNFBCEFHKOSW\`bcglquxz|~~{rG-29BP]vˆŒŒ‹„_CDIMH<;Uy’�‹†hC?ACEHKMOQUY[]\WVU<+0):]gbUO\adkG8<<<=<>?8/.3JswZwœtcagsz~~xpnwwxyz}|x{~~}}~~tƒˆˆ‰‰ˆ‰‰‰‡ˆ‚y{zyxtpwyvtvvvxxxxyyxvspsvuv|€|yywvvwwvzywrqvwxvvvuvysswxwutywpstuuytruruuqvƒˆˆ‰‰ˆ‰‰‰‡ˆ‚y{zyxtpwyvtvvvxxxxyyxvspsvuv|€|yywvvwwvzywrqvwxvvvuvysswxwutywpstuuytruruuqv†’’“’”“““‘‘‡yzwssvxvtuvvvwxxyxxvrrstvx|~|zyz{{{{|{ywzyvrswxwwuuuuxttwxwvuyvpsutwysttuvtu†’’“’”“““‘‘‡yzwssvxvtuvvvwxxyxxvrrstvx|~|zyz{{{{|{ywzyvrswxwwuuuuxttwxwvuyvpsutwysttuvtu€ƒ„ƒƒ‚€�€€�wtsuxwvvvvwwwwwxwtsrsuvz}|{yx{}~€€€€}{{xvttwxwuuvtvxstwvwuuxuqutswxrsutuu€ƒ„ƒƒ‚€�€€�wtsuxwvvvvwwwwwxwtsrsuvz}|{yx{}~€€€€}{{xvttwxwuuvtvxstwvwuuxuqutswxrsutuu~||||zxxyxvsstvwxxvvvwwwwvutrstuwz}|zyyz}€€€€€€€~|ywsswxwvuutvxstwwvuvzuruttyxrttsv~||||zxxyxvsstvwxxvvvwwwwvutrstuwz}|zyyz}€€€€€€€~|ywsswxwvuutvxstwwvuvzuruttyxrttsv€}||zxxxtrsvwstvxwuuvwvvurrstux|}{yyz{}€€€€€€€€€€~yvsswwvvuutuwruxwvtwzsqtttzwrust€}||zxxxtrsvwstvxwuuvwvvurrstux|}{yyz{}€€€€€€€€€€~yvsswwvvuutuwruxwvtwzsqtttzwrust}|zvstsswvusruwxwutuvtrqrtvy||zyyxz}€€€€€€€€€€}yvsrvwwuutrtwruwvuuxxsrsuwzurtu}|zvstsswvusruwxwutuvtrqrtvy||zyyxz}€€€€€€€€€€}yvsrvwwuutrtwruwvuuxxsrsuwzurtu{vtrswwxvuusswxwwussqrttvz||zxxyy{~€€€€€€|ywsswxvuttstvsuvvvvxyrptvxztqu{vtrswwxvuusswxwwussqrttvz||zxxyy{~€€€€€€|ywsswxvuttstvsuvvvvxyrptvxztqu~vvtwxyvtuvurtwxwtqprsuxz}{yxxxxy{€€€€€€€€€|zyvtsvwwutsruwrtvwwvxyrsuvxxrt~vvtwxyvtuvurtwxwtqprsuxz}{yxxxxy{€€€€€€€€€|zyvtsvwwutsruwrtvwwvxyrsuvxxrt~{xvywvuvvvssuwurqqtux|~|yyyxyyx{€€€€€€€€€€zyzwtruwvusstvvruwwwuyyrtvvxxt~{xvywvuvvvssuwurqqtux|~|yyyxyyx{€€€€€€€€€€zyzwtruwvusstvvruwwwuyyrtvvxxt€zutwwvuvutsttqqqru{}|{yyyyxyyy{€€€€€€€€€€€€€~xyzwtquvvusssvvsvwwvuywruuvxx€zutwwvuvutsttqqqru{}|{yyyyxyyy{€€€€€€€€€€€€€~xyzwtquvvusssvvsvwwvuywruuvxx€ytuwwvvvusqpqqruz}|yxyyyyyyzyz€€€€€€€€€€|vy{xsruvusrttxvrvxwvvyvrtutx€ytuwwvvvusqpqqruz}|yxyyyyyyzyz€€€€€€€€€€|vy{xsruvusrttxvrvxwvvyvrtutxxtvwwvvuropqsuy|{yxyyyxxyyyzz}€€€€€€€€€€xvy{wtruutssuvwusvxxwvzvrutwxtvwwvvuropqsuy|{yxyyyxxyyyzz}€€€€€€€€€€xvy{wtruutssuvwusvxxwvzvrutwwuwwvtrpqrsv{|zxyyxyxxxyyyzz|€€€€€€€€€€|vuy{wtrtutttvuvvrvxxxwztrtwwuwwvtrpqrsv{|zxyyxyxxxyyyzz|€€€€€€€€€€|vuy{wtrtutttvuvvrvxxxwztrtw~wvxurpqrtwz|zyyzzxwxyxyyyyzz~€€€€€€€€€€~€xuvy{xurtvvuvuswuswyyxxytrw~wvxurpqrtwz|zyyzzxwxyxyyyyzz~€€€€€€€€€€~€xuvy{xurtvvuvuswuswyyxxytrw}vvsrqsux{{yxyyzzyxwxxyyyyyx{€€€€€€€€€€€{uvux|xtptwvwuttwusxyzywxru}vvsrqsux{{yxyyzzyxwxxyyyyyx{€€€€€€€€€€€{uvux|xtptwvwuttwusxyzywxru}trqtw{}zxxxyyy{yxxxxxyyxutx€�€€€€~€~vuvvy{wsquvwvuutwusxzzwwws}trqtw{}zxxxyyy{yxxxxxyyxutx€�€€€€~€~vuvvy{wsquvwvuutwusxzzwwws|rtx|}|ywxxxxyyzyyxxwxvttuv}�€€€xuvvvx{xtruxwvtttxttyzxuvy|rtx|}|ywxxxxyyzyyxxwxvttuv}�€€€xuvvvx{xtruxwvtttxttyzxuvy|x|}{zzyxxxwyyyyyyxvuttuvvw~�€€€€€{uuvvux|xtruwvustwwruyxwuw|x|}{zzyxxxwyyyyyyxvuttuvvw~�€€€€€{uuvvux|xtruwvustwwruyxwuw}zzzzyyxxxxyyyxxustuuuuux}€€€€€€}vuuuvvx{yurtwwuvxwustwwvw}zzzzyyxxxxyyyxxustuuuuux}€€€€€€}vuuuvvx{yurtwwuvxwustwwvw{zz{{yxxxxxxxvtsttuuuwz|}|€~~~~}~~~€yuvvvvvx|zuruwuvxvtvrswvx{zz{{yxxxxxxxvtsttuuuwz|}|€~~~~}~~~€yuvvvvvx|zuruwuvxvtvrswvx|z{{zyxwwwwusstuutux{|}{z{€~}|zywvvuuuuuvwwxyzz{{||~{wwwvvvwx{yusstvxvttvrsvy|z{{zyxwwwwusstuutux{|}{z{€~}|zywvvuuuuuvwwxyzz{{||~{wwwvvvwx{yusstvxvttvrsvy{z|zyyxwtsssstttwz|||zz{z|~|zwurponmljkkkkkmoommoppqrstuvvvwwwwx|zurswwvttuwrsz{z|zyyxwtsssstttwz|||zz{z|~|zwurponmljkkkkkmoommoppqrstuvvvwwwwx|zurswwvttuwrsz{z{zxvurqstssuw{}|{{{{{{z}€€}{vsqookgghhikmooooprpmigghjlqqrrrrstvwvx|yuruxvuuuvvqv{z{zxvurqstssuw{}|{{{{{{z}€€}{vsqookgghhikmooooprpmigghjlqqrrrrstvwvx|yuruxvuuuvvqv{yxusrsssttw{|{|{z{{{{zz{~€€|ysnjkiggefkoqrstuuuvtsttqlgdchoqrsttsssvvwx|zusvwuutuutt{yxusrsssttw{|{|{z{{{{zz{~€€|ysnjkiggefkoqrstuuuvtsttqlgdchoqrsttsssvvwx|zusvwuutuutt~wusstssuwz|||zyz{{{{zz{z{~}{xrngcbccehmprstrqqqqqsssstuqojdfnpqrsttttvwwxz}zvttttvutsv~wusstssuwz|||zyz{{{{zz{z{~}{xrngcbccehmprstrqqqqqsssstuqojdfnpqrsttttvwwxz}zvttttvutsv|utstuvyz||{{zzzzzzz{zzzzz{zwrjgkdabdinqrppqrpppoppqqppqqpqqlgkqpqrsstvwwwxyz|zvsstvwusu|utstuvyz||{{zzzzzzz{zzzzz{zwrjgkdabdinqrppqrpppoppqqppqqpqqlgkqpqrsstvwwwxyz|zvsstvwusu|ttvx{{|zzz{zzzzzz{z{zzzzyxunfbdkhdinrrqrqrrsrssrqqrqpppppprqjkppqrrsvwwwvwyyx{zvssuvwtu|ttvx{{|zzz{zzzzzz{z{zzzzyxunfbdkhdinrrqrqrrsrssrqqrqpppppprqjkppqrrsvwwwvwyyx{zvssuvwtu|xz{|zyzzzzzz{zzzzz{{zy{zxsmgdbbknnstrqrstuwwwwwussssrrrqrqrrkjqrrqstuwwvvwxwwy|zvstwwuv|xz{|zyzzzzzz{zzzzz{{zy{zxsmgdbbknnstrqrstuwwwwwussssrrrqrqrrkjqrrqstuwwvvwxwwy|zvstwwuv€|zzzzzyyzzzzzzzzzzzzzyyxrpmfcbcmtsssrstvwxzzyyxvutsssssstutsnlrrruwvstwvvvxwxxx|zvstxuv€|zzzzzyyzzzzzzzzzzzzzyyxrpmfcbcmtsssrstvwxzzyyxvutsssssstutsnlrrruwvstwvvvxwxxx|zvstxuv{zzzzzyyzzzyyzzyzzzzyxwroskeeeksttsrrtvvvvvwxxxwvuttttstuwwwsqrsuz{xwttwvvwwwwwx{zvrtuw{zzzzzyyzzzyyzzyzzzzyxwroskeeeksttsrrtvvvvvwxxxwvuttttstuwwwsqrsuz{xwttwvvwwwwwx{zvrtuw|z{zzzyzzzzyzyyzzyzzyxuoqsjefmtuusssuuvuuvuvvwxwwvuuuuttuvvxvstwwxz|zwuuwvwwwwvwxzyuqsx|z{zzzyzzzzyzyyzzyzzyxuoqsjefmtuusssuuvuuvuvvwxwwvuuuuttuvvxvstwwxz|zwuuwvwwwwvwxzyuqsx|z{zzyyzyyyzzzyyyyxxuutpqsngltxutstuwvuuttuvuvxwwvuuuutuuttuvuvxwvxz|ywvtuvvvwwwvwyzuqt|z{zzyyzyyyzzzyyyyxxuutpqsngltxutstuwvuuttuvuvxwwvuuuutuuttuvuvxwvxz|ywvtuvvvwwwvwyzuqt{yzzyyzyxyyyyzyxvvuvvwvrrtqkpvxuttuvwvuuuuuvvvxxwvvvuttuvuttuuvxxvvxyzywusvwwwwvvwvzxtt{yzzyyzyxyyyyzyxvvuvvwvrrtqkpvxuttuvwvuuuuuvvvxxwvvvuttuvuttuuvxxvvxyzywusvwwwwvvwvzxtt{yzyyyyyxyyxwvuuuvvwvuuusrrosxxuuuuvvttvvuvwvwwyzwvttvvvvvusstvwxwvxxxzzwusvwwwvwwwwywv{yzyyyyyxyyxwvuuuvvwvuuusrrosxxuuuuvvttvvuvwvwwyzwvttvvvvvusstvwxwvxxxzzwusvwwwvwwwwywv{zzzxyxxwvutuuvwwvvvuvvvutssvxxuuvuuttutuvwyzywxzxuttvxxwwxvrtvwwwwwwwxzzwvsuwwvvwwvvxz{zzzxyxxwvutuuvwwvvvuvvvutssvxxuuvuuttutuvwyzywxzxuttvxxwwxvrtvwwwwwwwxzzwvsuwwvvwwvvxzzyyywwvttuvvvvvvvwwwvwwvuusvxyxvvvutttttvxyzyywwwutsrvyyyxxvsuwwwwvvwwwwz{xvsuwvvwwvvvyzyyywwvttuvvvvvvvwwwvwwvuusvxyxvvvutttttvxyzyywwwutsrvyyyxxvsuwwwwvvwwwwz{xvsuwvvwwvvvyzxwuttuuuvvuuvvvwwxwvwwwvuuuvxwvuuutsstuuvvuvwwwusttsuyyxxzwsuwwvwwwwwwwxxzyvttvwwvwwvxzxwuttuuuvvuuvvvwwxwvwwwvuuuvxwvuuutsstuuvvuvwwwusttsuyyxxzwsuwwvwwwwwwwxxzyvttvwwvwwvx~vuuuuuvuuuuvwwvvvwwwwwwwvtrstvvwwuutsrstssttuvvtsssttuxywuwyuuwvwwwwwwvwwwwzywttvvvwwwx~vuuuuuvuuuuvwwvvvwwwwwwwvtrstvvwwuutsrstssttuvvtsssttuxywuwyuuwvwwwwwwvwwwwzywttvvvwwwx}vuvuvwzzxuvwvwvwvvvvwvvvwrqtstvxvutsrrrrssttsttsstttttwyxwwxwuvvwwvwwwvvwxwwz{wutuvvwvx}vuvuvwzzxuvwvwvwvvvvwvvvwrqtstvxvutsrrrrssttsttsstttttwyxwwxwuvvwwvwwwvvwxwwz{wutuvvwvx}wxz{{zzxyxuxzzxxwvwvwwvvwqqtstvxuuusrsrrtstsstsstttttsuyxwxxyvuvwwwwwwvvwxwvx|{vusuvwvy}wxz{{zzxyxuxzzxxwvwvwwvvwqqtstvxuuusrsrrtstsstsstttttsuyxwxxyvuvwwwwwwvvwxwvx|{vusuvwvy}}}{{zywwxvvy{{yxwwvvvwvwsqtqqvwutttssrsttssstssuvutrqrwwvxxyzvtuwvvwvvwwwwwxyzzwtsuvvx}}}{{zywwxvvy{{yxwwvvvwvwsqtqqvwutttssrsttssstssuvutrqrwwvxxyzvtuwvvwvvwwwwwxyzzwtsuvvx}|{{{zxwxwwvuwz|{xxvvwvvwtrsosvvvuttsrsstsstsssuvvurooptvuwxyyxwttvvwvvvvwwwwyyzzwusstx}|{{{zxwxwwvuwz|{xxvvwvvwtrsosvvvuttsrsstsstsssuvvurooptvuwxyyxwttvvwvvvvwwwwyyzzwusstx}{z{zywwwvwvtvyy{|{ywwvvvvsrotuvwvuttssssttssrtvutsqpnouwsuwxyvwxutuvvvwwwvvwxxwxyxtrsw}{z{zywwwvwvtvyy{|{ywwvvvvsrotuvwvuttssssttssrtvutsqpnouwsuwxyvwxutuvvvwwwvvwxxwxyxtrsw€}{{{{ywxwuuttuyyxy{|zxwwwvusqssuxwvutttsstttssuvttssrppuwssuwyvtwxvtuvvvwvvwwxwwxxywtrt€}{{{{ywxwuuttuyyxy{|zxwwwvusqssuxwvutttsstttssuvttssrppuwssuwyvtwxvtuvvvwvvwwxwwxxywtrt€|{{|ywxyxvvvvuvzzyxz|{zyxvuttssuwwwuutuuttttsstvuvwutssvwsrtwyvtuvwvttuvwwwwwwwwwxwyxtt€|{{|ywxyxvvvvuvzzyxz|{zyxvuttssuwwwuutuuttttsstvuvwutssvwsrtwyvtuvwvttuvwwwwwwwwwxwyxtt€}|||zxxxxwwwvutwzzxxy{{{zxvuutrtwxwuuttutttsrtuvwwwvuuwyxtrsvxvuttuwwutvvvuvvwvwwwwwyxv€}|||zxxxxwwwvutwzzxxy{{{zxvuutrtwxwuuttutttsrtuvwwwvuuwyxtrsvxvuttuwwutvvvuvvwvwwwwwyxv}|||yxyxxwwvvvuvyzyyxxx{|zwwwurrvxwvttttttststuvwwvutuxzyvssvxuuuuuuuxwttvvvuwvwwvvvvwx}|||yxyxxwwvvvuvyzyyxxx{|zwwwurrvxwvttttttststuvwwvutuxzyvssvxuuuuuuuxwttvvvuwvwwvvvvwx€~||{xyyxxwvuuvvuwzyyxxxxxywwwvsquywvtttttssttuvvvvvvtuwyxxutvwuvuuutsvxvttuuuuuwwwvvvux€~||{xyyxxwvuuvvuwzyyxxxxxywwwvsquywvtttttssttuvvvvvvtuwyxxutvwuvuuutsvxvttuuuuuwwwvvvux€~}|zxzzyxwwwvvvuvyzyyyyxvvutvwtquxxvutsststttuvvvuuvvvwxxwwvvvuutuutttuwwttuvvuvwwvvvux€~}|zxzzyxwwwvvvuvyzyyyyxvvutvwtquxxvutsststttuvvvuuvvvwxxwwvvvuutuutttuwwttuvvuvwwvvvux€}|{yzzzyxxwxvvvvvwzzzyxxvttsswwstwxvutrrstvuuvvuutuvvvvvwvvwwvtuuuuttttsuwvtuvuvvvuuutw€}|{yzzzyxxwxvvvvvwzzzyxxvttsswwstwxvutrrstvuuvvuutuvvvvvwvvwwvtuuuuttttsuwvtuvuvvvuuutw€}|zx{zzyyxwwwwwwuvy{zzzxuttstxyutwxvutqqruvuuuvwwxxxxxxwutvwxutuvuuttutstuvwutuvvuutttw€}|zx{zzyyxwwwwwwuvy{zzzxuttstxyutwxvutqqruvuuuvwwxxxxxxwutvwxutuvuuttutstuvwutuvvuutttw€}|zz{{zyyxwxvwwvuuvzzzyxvtsstxzvsuxvttqpqsttuxzzz{zyxz{zwttwxttuuuuuututtstvxtsuvvutstv€}|zz{{zyyxwxvwwvuuvzzzyxvtsstxzvsuxvttqpqsttuxzzz{zyxz{zwttwxttuuuuuututtstvxtsuvvutstv€|{y{{{zyyxwxwwwvvvvxyzzywvtsuxywtuwwutspoqrstvwwwxyyy{zyytrvxttuutuuuutttttsuvutuvutssw€|{y{{{zyyxwxwwwvvvvxyzzywvtsuxywtuwwutspoqrstvwwwxyyy{zyytrvxttuutuuuutttttsuvutuvutssw€}{z{{{zzyxwxxxxvvvuvxzzxwuuuuvwxttwxvttrpoqrrsstutvuvxyyxurvwttttttuuuttutssstvutuutssv€}{z{{{zzyxwxxxxvvvuvxzzxwuuuuvwxttwxvttrpoqrrsstutvuvxyyxurvwttttttuuuttutssstvutuutssv€}z{{{{zzyxxxxxxwvwwuwzzyxwwvvvwzvtuwwutsqponoqrsuuvvuvwxwtsvurssttssttuututuutstuussssv€}z{{{{zzyxxxxxxwvwwuwzzyxwwvvvwzvtuwwutsqponoqrsuuvvuvwxwtsvurssttssttuututuutstuussssv€{y{{{zzzyyxxwxxxwwvuuyzzywvvwv|wttvwvtrrpnlmortuuuutttutqsvtstttssstttsttuttttrtwtrssv€{y{{{zzzyyxxwxxxwwvuuyzzywvvwv|wttvwvtrrpnlmortuuuutttutqsvtstttssstttsttuttttrtwtrssv€{z||{zzzyyyxwxxwwwwvvwzzzyvvxwƒxtsuvwusrpnklmpsttutttttrotvsstttssttsssutsttttrstuusqu€{z||{zzzyyyxwxxwwwwvvwzzzyvvxwƒxtsuvwusrpnklmpsttutttttrotvsstttssttsssutsttttrstuusqu€{z||{zzyxyyxwxwxxwwvvvxz{{yyzw†{tssuuutsqommmlnqrstutsqnntutttttssssssssssssttssstttss€{z||{zzyxyyxwxwxxwwvvvxz{{yyzw†{tssuuutsqommmlnqrstutsqnntutttttssssssssssssttssstttss{{}|{y{zyyyxxwwyxxwwwwwz{{{{{yˆ‚vttttttssrpnnlkloprsrpomqvutttttttsssssrssttttstsssstu{{}|{y{zyyyxxwwyxxwwwwwz{{{{{yˆ‚vttttttssrpnnlkloprsrpomqvutttttttsssssrssttttstsssstu|||{zzyzyyyyxxxyyxwwwwvx{{{{{z~†ˆ}tttsutstsqqomlkmmnoppootvtttttttsttsrsssstttttttrrrqs|||{zzyzyyyyxxxyyxwwwwvx{{{{{z~†ˆ}tttsutstsqqomlkmmnoppootvtttttttsttsrsssstttttttrrrqs|{{zzzzzyyyyxxxxyxwwwwwvz{{{{{}„Š…wrstttstutsqonlmmmnnoprvurtttusttstsssrsssrsssstsrrqr|{{zzzzzyyyyxxxxyxwwwwwvz{{{{{}„Š…wrstttstutsqonlmmmnnoprvurtttusttstsssrsssrsssstsrrqr€}||{{zzzyyyyyxxxyxxxxxwvx{|{{{|ƒ‰Š�ussutssuutsrpomnooopruwuttttttststsssrsssssrrrtsrrqr€}||{{zzzyyyyyxxxyxxxxxwvx{|{{{|ƒ‰Š�ussutssuutsrpomnooopruwuttttttststsssrsssssrrrtsrrqr€}|{{{zzzzzyyyyyyyxxxxxwwwy|||||‚ˆ‰Š‚xtuvutttutssrqqrqqstwywuttttttsssssssssssssssusrrqr€}|{{{zzzzzyyyyyyyxxxxxwwwy|||||‚ˆ‰Š‚xtuvutttutssrqqrqqstwywuttttttsssssssssssssssusrrqr€}|{{{zzzzyyzyyzyyxxxyxwwvw{||}‚‡ˆ‰‹†wtuvvttuuttssstsstu{ƒ�yuututtttttsssrssssrrsssrrqr€}|{{{zzzzyyzyyzyyxxxyxwwvw{||}‚‡ˆ‰‹†wtuvvttuuttssstsstu{ƒ�yuututtttttsssrssssrrsssrrqr}|{{{zzzyyzyyyyyyxxxxxwwvwy{|~ƒ‚ƒ†ˆŠŠssuvwutuuttsttuttu}ƒ†…}wvvuttttttssrsrssssssrrrqr}|{{{zzzyyzyyyyyyxxxxxwwvwy{|~ƒ‚ƒ†ˆŠŠssuvwutuuttsttuttu}ƒ†…}wvvuttttttssrsrssssssrrrqr|z{{z{zyzyyyyxxxxwwxwwxwwwwz|€ƒ„‚‚†‰Šˆ{qtuwvutsssttuutuw}€‚‡ˆwvuttsstsrrrsrsssrsrqrqpq|z{{z{zyzyyyyxxxxwwxwwxwwwwz|€ƒ„‚‚†‰Šˆ{qtuwvutsssttuutuw}€‚‡ˆwvuttsstsrrrsrsssrsrqrqpq}zz{{{yyzyxxxxwxxxwxwwxwwvvx‚‚ƒƒƒ‚†ˆŠˆyqtwvutstttttttvw|…‰‰€vttutsssrtsrrsrrqqrqqqqr}zz{{{yyzyxxxxwxxxwxwwxwwvvx‚‚ƒƒƒ‚†ˆŠˆyqtwvutstttttttvw|…‰‰€vttutsssrtsrrsrrqqrqqqqr€|{{{zzyyzzxxxxxxyyxxvwxwwww|ƒƒ‚‚ƒ„„‚ƒˆ‹†wruwvsrrrsssstvwz}€…‡Šˆzqsutsssstsrsrqrrprrqqqq€|{{{zzyyzzxxxxxxyyxxvwxwwww|ƒƒ‚‚ƒ„„‚ƒˆ‹†wruwvsrrrsssstvwz}€…‡Šˆzqsutsssstsrsrqrrprrqqqq€|{{{zzyyyyyyyyyyyyyywxyxxz}�„ƒ„ƒ„„……‚‚‡‹ƒuruvutrrsrsstuw{}€…ˆˆŠ‚trtttsssssstsrrrrrqnqxw€|{{{zzyyyyyyyyyyyyyywxyxxz}�„ƒ„ƒ„„……‚‚‡‹ƒuruvutrrsrsstuw{}€…ˆˆŠ‚trtttsssssstsrrrrrqnqxw€|{{zzzyxyyyyyyyxyxxyz|~€‚‚‚„„„„„„„†…„‚„†|qtuutsssrsssuwz}…†…ˆ‚vuqttttstsrrqqswtkbl}ƒ�€|{{zzzyxyyyyyyyxyxxyz|~€‚‚‚„„„„„„„†…„‚„†|qtuutsssrsssuwz}…†…ˆ‚vuqttttstsrrqqswtkbl}ƒ�|{{zzyyxxxxxyxwwwy{~��‚‚ƒƒ��ƒƒ„ƒƒ„…†……„ƒ�~vrrsssssssssuvy|~ƒ…‡ˆ}ttuutvwuussj_ht}kX]mohf|{{zzyyxxxxxyxwwwy{~��‚‚ƒƒ��ƒƒ„ƒƒ„…†……„ƒ�~vrrsssssssssuvy|~ƒ…‡ˆ}ttuutvwuussj_ht}kX]mohf|||{zyxyxxyyxxx{~�‚‚‚‚‚‚ƒƒ��‚„„…ƒ‚……„ƒ„ƒ‚‚ƒ€zttttuuuuvvwy}~‚ˆŠ„|vvvvxw{|{vuhZhoh^Z[[ZY\|||{zyxyxxyyxxx{~�‚‚‚‚‚‚ƒƒ��‚„„…ƒ‚……„ƒ„ƒ‚‚ƒ€zttttuuuuvvwy}~‚ˆŠ„|vvvvxw{|{vuhZhoh^Z[[ZY\xjmhikjhgmkv„‚‚‚‚„…†…„„„„„„„„ƒƒ„„„…††………„„‚‚‚ƒ„„ƒƒ„„…„…„„ƒƒ„„„……„„„„„„„………………†††…„††ˆ†ƒ„xjmhikjhgmkv„‚‚‚‚„…†…„„„„„„„„ƒƒ„„„…††………„„‚‚‚ƒ„„ƒƒ„„…„…„„ƒƒ„„„……„„„„„„„………………†††…„††ˆ†ƒ„sPQNQKMNOSUg†‚ƒ„……†………„„…„„„„„„…………………„„ƒƒƒƒ‚‚‚ƒƒ‚ƒ„…„„„„„ƒƒ„………†…„„ƒ„……††……†††††„†††ˆ†ƒsPQNQKMNOSUg†‚ƒ„……†………„„…„„„„„„…………………„„ƒƒƒƒ‚‚‚ƒƒ‚ƒ„…„„„„„ƒƒ„………†…„„ƒ„……††……†††††„†††ˆ†ƒ}ststtusuvuy„ƒ………„…„……„„„…„„„………††…………ƒƒ„‚‚��€€�€��ƒ„„„„…„ƒƒ„„…„††„„„„„……††…††††…………†‡ˆ†}ststtusuvuy„ƒ………„…„……„„„…„„„………††…………ƒƒ„‚‚��€€�€��ƒ„„„„…„ƒƒ„„…„††„„„„„……††…††††…………†‡ˆ†�ƒƒ‚�‚ƒƒƒƒƒ„„…†…„ƒ„„„„ƒ„„„„„„……………„„„ƒƒƒƒ�€��€€���€��‚ƒƒ„„„ƒƒƒ„„……………„„„„…††………†…††„…‡ˆˆ�ƒƒ‚�‚ƒƒƒƒƒ„„…†…„ƒ„„„„ƒ„„„„„„……………„„„ƒƒƒƒ�€��€€���€��‚ƒƒ„„„ƒƒƒ„„……………„„„„…††………†…††„…‡ˆˆ€�‚‚‚ƒƒƒ„…………††„ƒ„„„„„„………††……………„„„ƒƒ‚�€€€��€€���€€���‚„ƒ„„ƒ„„„……††…„„„„…†…†………†…†…„†ˆ‡€�‚‚‚ƒƒƒ„…………††„ƒ„„„„„„………††……………„„„ƒƒ‚�€€€��€€���€€���‚„ƒ„„ƒ„„„……††…„„„„…†…†………†…†…„†ˆ‡€�‚‚ƒ„„…†††……†…ƒƒ„……„„„…†………„„„„„ƒ„‚‚�€��€€���€€�€�€�€��‚„„„„„„„………†††„„„„†…††…††……‡……††€�‚‚ƒ„„…†††……†…ƒƒ„……„„„…†………„„„„„ƒ„‚‚�€��€€���€€�€�€�€��‚„„„„„„„………†††„„„„†…††…††……‡……††��ƒ„…†……………„……ƒƒƒ„„………†……„…„„ƒ„„ƒƒ‚��€€€€€€€€�€€€€���€€€�ƒƒƒ„„ƒƒ„………†‡……„„………††…††……†……†��ƒ„…†……………„……ƒƒƒ„„………†……„…„„ƒ„„ƒƒ‚��€€€€€€€€�€€€€���€€€�ƒƒƒ„„ƒƒ„………†‡……„„………††…††……†……†�ƒ„…„……„…„„„……ƒƒƒ„…†…………„„„ƒƒƒ„„ƒ‚�€�€€€€€�€�€€€���€€€€€€‚ƒ„„„„ƒ„„„…†‡†…„„………††……†…„…‡…†�ƒ„…„……„…„„„……ƒƒƒ„…†…………„„„ƒƒƒ„„ƒ‚�€�€€€€€�€�€€€���€€€€€€‚ƒ„„„„ƒ„„„…†‡†…„„………††……†…„…‡…†�ƒƒ„……„„„„„……„ƒƒ„…†……„ƒ„ƒƒƒƒƒƒƒƒ‚��€�€€€€€��€€���€�€€€�€€€‚„„„„„„„…†††††………„„………†††…„…‡„�ƒƒ„……„„„„„……„ƒƒ„…†……„ƒ„ƒƒƒƒƒƒƒƒ‚��€�€€€€€��€€���€�€€€�€€€‚„„„„„„„…†††††………„„………†††…„…‡„€‚„……„„„„„„……„„…………„ƒƒƒƒƒƒƒƒƒ‚‚‚���€��€�€�€�€€�����€€€�€€€�ƒ„„„„…„„…†††††…„„„„…………………„††€‚„……„„„„„„……„„…………„ƒƒƒƒƒƒƒƒƒ‚‚‚���€��€�€�€�€€�����€€€�€€€�ƒ„„„„…„„…†††††…„„„„…………………„††€‚„…„ƒ„„„„„†…†……„„ƒƒƒ„ƒƒƒƒ„ƒƒƒ‚�€�€��������€€���€€€���€€€€‚„ƒ„ƒ„„„„…††††………„„…„…†††………†€‚„…„ƒ„„„„„†…†……„„ƒƒƒ„ƒƒƒƒ„ƒƒƒ‚�€�€��������€€���€€€���€€€€‚„ƒ„ƒ„„„„…††††………„„…„…†††………†�ƒ……„„„„ƒ„…†…†„„„ƒƒƒƒƒƒƒ„ƒƒƒƒƒ�€€€���������€���€��€���€€�€€€ƒ„„„„„„………††……†…„„„„……††…………�ƒ……„„„„ƒ„…†…†„„„ƒƒƒƒƒƒƒ„ƒƒƒƒƒ�€€€���������€���€��€���€€�€€€ƒ„„„„„„………††……†…„„„„……††…………�ƒ…„ƒ„„„……………ƒƒƒƒƒ‚ƒƒƒƒƒƒƒƒƒƒ‚€€€€����€€��€�������€€€�€��€€‚„„„„ƒ„„…………†…†……„„„„„……………„�ƒ…„ƒ„„„……………ƒƒƒƒƒ‚ƒƒƒƒƒƒƒƒƒƒ‚€€€€����€€��€�������€€€�€��€€‚„„„„ƒ„„…………†…†……„„„„„……………„�„„ƒƒ………………„‚‚ƒƒ‚ƒƒƒƒƒƒƒƒƒƒƒ‚�€€€���€€€�€€�€�����€€�€€€€€€€€ƒ„„„ƒ„„……„………†††…„„ƒƒ„…†………�„„ƒƒ………………„‚‚ƒƒ‚ƒƒƒƒƒƒƒƒƒƒƒ‚�€€€���€€€�€€�€�����€€�€€€€€€€€ƒ„„„ƒ„„……„………†††…„„ƒƒ„…†………‚…„„………„„ƒƒ‚‚‚‚‚‚ƒ‚ƒƒƒƒƒƒƒƒ‚‚€€€€€����€�€€�€€€€�€€€€€€€€€€€‚„„„ƒƒ„„………„„†‡‡†…„ƒ‚ƒƒ…†…„‚…„„………„„ƒƒ‚‚‚‚‚‚ƒ‚ƒƒƒƒƒƒƒƒ‚‚€€€€€����€�€€�€€€€�€€€€€€€€€€€‚„„„ƒƒ„„………„„†‡‡†…„ƒ‚ƒƒ…†…„‚…………„ƒƒƒƒƒ‚‚‚‚‚‚‚‚ƒƒƒƒƒ‚ƒƒƒ€€€€€€�€€�€€€�€€€€€�€€€€�€€€€€€€�„„„„„„„„…„„„…†‡‡‡…„ƒƒ‚„††…‚…………„ƒƒƒƒƒ‚‚‚‚‚‚‚‚ƒƒƒƒƒ‚ƒƒƒ€€€€€€�€€�€€€�€€€€€�€€€€�€€€€€€€�„„„„„„„„…„„„…†‡‡‡…„ƒƒ‚„††…‚…„„ƒ‚‚‚ƒ‚‚‚‚‚‚‚‚‚ƒƒƒƒ‚„„„„‚€€€€€€€€��€€€€€€€€�€€€�€�€�€€€€ƒ„„„„…„„„„„„……††‡†…„ƒ‚ƒ…††‚…„„ƒ‚‚‚ƒ‚‚‚‚‚‚‚‚‚ƒƒƒƒ‚„„„„‚€€€€€€€€��€€€€€€€€�€€€�€�€�€€€€ƒ„„„„…„„„„„„……††‡†…„ƒ‚ƒ…††‚„ƒ‚‚‚‚‚ƒ‚‚‚‚‚‚‚‚ƒƒƒƒ„„„„„„‚€€€€€€€€�€€€€€€�€€€�€€€€€€€€€€€€‚„„„„„ƒ„„„„„„…†††††…„‚ƒ„……‚„ƒ‚‚‚‚‚ƒ‚‚‚‚‚‚‚‚ƒƒƒƒ„„„„„„‚€€€€€€€€�€€€€€€�€€€�€€€€€€€€€€€€‚„„„„„ƒ„„„„„„…†††††…„‚ƒ„……�‚‚�‚‚‚‚‚‚‚‚‚‚‚‚‚ƒ„…„„„„„ƒƒ�€€€€€€€€�€€€�€€€€€��€€����€€€€€€�„„„„„„ƒ„„„„…„…†…„††…ƒ„„…„�‚‚�‚‚‚‚‚‚‚‚‚‚‚‚‚ƒ„…„„„„„ƒƒ�€€€€€€€€�€€€�€€€€€��€€����€€€€€€�„„„„„„ƒ„„„„…„…†…„††…ƒ„„…„€�‚‚�‚‚‚‚‚‚‚‚ƒƒƒ„„…„„„„ƒƒ‚‚€€€€€€€��€€��€�€€€�€€�����€�€€€€€ƒ„„„ƒ„„ƒ„„„……„…„ƒ†††„„„„ƒ€�‚‚�‚‚‚‚‚‚‚‚ƒƒƒ„„…„„„„ƒƒ‚‚€€€€€€€��€€��€�€€€�€€�����€�€€€€€ƒ„„„ƒ„„ƒ„„„……„…„ƒ†††„„„„ƒ���‚‚‚‚‚‚‚‚‚ƒ„…„„……„„ƒ‚‚‚‚�€€€€€€€€�€€��‚ƒƒ„………„……„„„ƒ‚‚‚‚‚�€€‚ƒƒ„„„„ƒƒƒ„„„……„ƒ…†‡…„„„ƒ���‚‚‚‚‚‚‚‚‚ƒ„…„„……„„ƒ‚‚‚‚�€€€€€€€€�€€��‚ƒƒ„………„……„„„ƒ‚‚‚‚‚�€€‚ƒƒ„„„„ƒƒƒ„„„……„ƒ…†‡…„„„ƒ�‚‚‚‚‚‚‚‚ƒ„„………„„„ƒƒ‚�‚‚‚‚�€€€€€��€�‚„…‡‰‰Š‹‹ŒŒ‹ŒŒŒ‹ŠŠ‰Š‰ˆˆˆ‡††……„„„„„ƒƒƒ„ƒ…†„„…†††…„„ƒ�‚‚‚‚‚‚‚‚ƒ„„………„„„ƒƒ‚�‚‚‚‚�€€€€€��€�‚„…‡‰‰Š‹‹ŒŒ‹ŒŒŒ‹ŠŠ‰Š‰ˆˆˆ‡††……„„„„„ƒƒƒ„ƒ…†„„…†††…„„ƒ€‚‚‚‚‚ƒƒ„……„……„„„ƒ‚‚‚‚‚‚‚‚�€€€€��‚…‡ˆŠ‹Œ�����Ž�ŒŒŒŒŒŠ‹����Ž�ŒŠŠ‰‰ˆˆ‡†…ƒƒƒ„ƒ„…„ƒ…†††…†…„€‚‚‚‚‚ƒƒ„……„……„„„ƒ‚‚‚‚‚‚‚‚�€€€€��‚…‡ˆŠ‹Œ�����Ž�ŒŒŒŒŒŠ‹����Ž�ŒŠŠ‰‰ˆˆ‡†…ƒƒƒ„ƒ„…„ƒ…†††…†…„€�‚‚ƒ„„„„„„„„„‚‚‚‚‚‚‚‚‚‚‚‚�€€€€�ƒ…ŠŒ�Ž��‘����Ž���ŽŽŽ��ŒŒŒ�‘’‘�‹‰‰‰‰ˆˆ‰‡…„„„„„„„„„…………††„€�‚‚ƒ„„„„„„„„„‚‚‚‚‚‚‚‚‚‚‚‚�€€€€�ƒ…ŠŒ�Ž��‘����Ž���ŽŽŽ��ŒŒŒ�‘’‘�‹‰‰‰‰ˆˆ‰‡…„„„„„„„„„…………††„€‚ƒ„„„„„„ƒƒ‚‚‚‚‚‚‚‚‚‚‚‚‚‚�€€��ƒ‡Š�‘“’’‘�������’’’“““““’��‘�‘’�ŒŠŠ‰‰ˆˆˆ‡„„„„ƒ„„„„„†…………‡…€‚ƒ„„„„„„ƒƒ‚‚‚‚‚‚‚‚‚‚‚‚‚‚�€€��ƒ‡Š�‘“’’‘�������’’’“““““’��‘�‘’�ŒŠŠ‰‰ˆˆˆ‡„„„„ƒ„„„„„†…………‡…�ƒ„„„„ƒƒ‚�‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚ƒ„†‹�Ž‘“’‘�ŽŽ�����‘‘’’“““”””“““’�‘‘�ŠŠ‰‰ˆˆ‡…„„„ƒ‚ƒ„„„„†…ƒ„…‡†�ƒ„„„„ƒƒ‚�‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚ƒ„†‹�Ž‘“’‘�ŽŽ�����‘‘’’“““”””“““’�‘‘�ŠŠ‰‰ˆˆ‡…„„„ƒ‚ƒ„„„„†…ƒ„…‡†‚„„„ƒ‚�‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚ƒƒ„…‰�‘’��‘�ŽŽŽ���ŽŽ����‘’’““““’’’‘��‘ŽŠŠŠ‰ˆ‡…ƒƒ„„„ƒƒ„„„„……„„„††‚„„„ƒ‚�‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚ƒƒ„…‰�‘’��‘�ŽŽŽ���ŽŽ����‘’’““““’’’‘��‘ŽŠŠŠ‰ˆ‡…ƒƒ„„„ƒƒ„„„„……„„„††‚‚‚��‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚�‚‚‚ƒ„†‰Ž‘’’�Œ�����Ž��Œ‹Œ�Ž��‘‘’’’’‘‘‘��Ž�ŽŠŠŠŠ‡……„„„„„ƒƒ„„ƒ„„……„„„…‚‚‚��‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚�‚‚‚ƒ„†‰Ž‘’’�Œ�����Ž��Œ‹Œ�Ž��‘‘’’’’‘‘‘��Ž�ŽŠŠŠŠ‡……„„„„„ƒƒ„„ƒ„„……„„„…����‚��‚‚‚�‚‚‚‚‚‚‚‚‚‚‚‚ƒ„†‡Š��‘‘�Š�ŽŽŽŽŒ‹‹‹‹‹Œ��Ž��‘‘’‘‘����ŒŽ�ŠŠ‰‡„ƒ„„„„„„„„„„ƒƒ„„„„„„„����‚��‚‚‚�‚‚‚‚‚‚‚‚‚‚‚‚ƒ„†‡Š��‘‘�Š�ŽŽŽŽŒ‹‹‹‹‹Œ��Ž��‘‘’‘‘����ŒŽ�ŠŠ‰‡„ƒ„„„„„„„„„„ƒƒ„„„„„„„€�‚�‚‚‚�‚‚‚‚‚ƒƒ‚‚‚‚‚‚ƒ‚ƒ†ˆ‡Œ����‹ŒŽŽŽ��Œ����Œ‹ŒŒ�Ž���‘���Ž�ŒŒ‹ŠŠŠ†„ƒ„„„„„„„ƒ„„„ƒƒ„„„„„„„€�‚�‚‚‚�‚‚‚‚‚ƒƒ‚‚‚‚‚‚ƒ‚ƒ†ˆ‡Œ����‹ŒŽŽŽ��Œ����Œ‹ŒŒ�Ž���‘���Ž�ŒŒ‹ŠŠŠ†„ƒ„„„„„„„ƒ„„„ƒƒ„„„„„„„€��‚‚‚‚‚‚‚‚‚‚ƒƒ‚‚‚ƒ‚‚‚ƒ„ˆˆ‡ŒŽŽ�ŠŒ�ŽŽ�Œ����ŽŽŽŒŒŒ��Ž������Ž�ŒŒ‹Šˆ…„„ƒ„ƒƒ„„„„„„„„„„ƒ„…„……„€��‚‚‚‚‚‚‚‚‚‚ƒƒ‚‚‚ƒ‚‚‚ƒ„ˆˆ‡ŒŽŽ�ŠŒ�ŽŽ�Œ����ŽŽŽŒŒŒ��Ž������Ž�ŒŒ‹Šˆ…„„ƒ„ƒƒ„„„„„„„„„„ƒ„…„……„€��‚�‚‚‚‚‚‚‚‚‚ƒ‚ƒƒƒƒƒƒ„…ˆ‰‡Š�Œ‰‰Œ������Ž������ŽŒŒ�Ž�����ŽŒ�ŽŽ‹ˆ…ƒ„„„ƒƒ„ƒ„„„„„„„ƒƒ„„…„„„„€��‚�‚‚‚‚‚‚‚‚‚ƒ‚ƒƒƒƒƒƒ„…ˆ‰‡Š�Œ‰‰Œ������Ž������ŽŒŒ�Ž�����ŽŒ�ŽŽ‹ˆ…ƒ„„„ƒƒ„ƒ„„„„„„„ƒƒ„„…„„„„€�‚�‚‚‚‚‚‚‚‚‚‚‚ƒƒƒ„„„„ƒ…‡ˆ‡ˆŒŠ‰Š‹Œ�ŒŒ��Ž�Ž�����Ž���Ž���Ž�Ž����‰…ƒ„„„ƒƒ„„ƒ„„„„„„„ƒ„„„„„…„€�‚�‚‚‚‚‚‚‚‚‚‚‚ƒƒƒ„„„„ƒ…‡ˆ‡ˆŒŠ‰Š‹Œ�ŒŒ��Ž�Ž�����Ž���Ž���Ž�Ž����‰…ƒ„„„ƒƒ„„ƒ„„„„„„„ƒ„„„„„…„€���‚‚‚‚‚‚‚‚ƒ‚ƒ„„„ƒƒƒ„„„…‡‡†‰Šˆ‰‹ŒŒ‹‹�Ž�ŽŽ�‘�����‹ŒŽ��Ž�Ž�����‰„ƒƒ„„ƒƒ„ƒƒƒƒ„„„„ƒƒ„ƒ„ƒ„…„€���‚‚‚‚‚‚‚‚ƒ‚ƒ„„„ƒƒƒ„„„…‡‡†‰Šˆ‰‹ŒŒ‹‹�Ž�ŽŽ�‘�����‹ŒŽ��Ž�Ž�����‰„ƒƒ„„ƒƒ„ƒƒƒƒ„„„„ƒƒ„ƒ„ƒ„…„€�‚�‚‚‚‚‚ƒƒ„ƒ„„„ƒ„„ƒƒ„„„„„††ˆ‡‡‰‹Œ‹‹Œ����ŽŽ�ŒŒŒŽŽ‹Œ�‘‘Ž�ŽŽŽ���ˆƒ„ƒ„ƒƒƒ„ƒƒ„ƒ„„„„ƒƒ„„„„ƒ„…€�‚�‚‚‚‚‚ƒƒ„ƒ„„„ƒ„„ƒƒ„„„„„††ˆ‡‡‰‹Œ‹‹Œ����ŽŽ�ŒŒŒŽŽ‹Œ�‘‘Ž�ŽŽŽ���ˆƒ„ƒ„ƒƒƒ„ƒƒ„ƒ„„„„ƒƒ„„„„ƒ„…€����‚‚ƒƒƒƒƒƒƒ„„„„„„„„„„„„…ˆˆ‡‡‰‹‹‹‹Œ�Ž�Ž��‹‹‹ŒŽ����‘’���Œ‹Š�ކƒ„ƒƒƒƒƒ„ƒƒ‚ƒƒƒƒ„ƒ„„ƒ„„„„ƒ€����‚‚ƒƒƒƒƒƒƒ„„„„„„„„„„„„…ˆˆ‡‡‰‹‹‹‹Œ�Ž�Ž��‹‹‹ŒŽ����‘’���Œ‹Š�ކƒ„ƒƒƒƒƒ„ƒƒ‚ƒƒƒƒ„ƒ„„ƒ„„„„ƒ€�‚‚ƒ„ƒ„„ƒ„„„„„„„„„„„…„„„„†��Œ‰ŠŠ‹ŒŒ�ŽŽŽŽŽŽ���Ž�Ž�‘‘‘’����‹ŠŒŒ…ƒ„„ƒ„„ƒƒƒƒƒƒ„„ƒ„„„ƒƒƒƒ„„ƒ€�‚‚ƒ„ƒ„„ƒ„„„„„„„„„„„…„„„„†��Œ‰ŠŠ‹ŒŒ�ŽŽŽŽŽŽ���Ž�Ž�‘‘‘’����‹ŠŒŒ…ƒ„„ƒ„„ƒƒƒƒƒƒ„„ƒ„„„ƒƒƒƒ„„ƒ�‚ƒƒ„„ƒƒƒƒ„ƒ„„„„„„„„„…„„ƒƒ‹”’�‹ŠŠ‹Œ���ŽŽŽ��������‘’‘’“‘��ŽŽ‹Š‰„ƒ„„ƒ„ƒƒ„ƒƒƒƒƒƒƒ„„„„ƒƒƒƒƒƒ�‚ƒƒ„„ƒƒƒƒ„ƒ„„„„„„„„„…„„ƒƒ‹”’�‹ŠŠ‹Œ���ŽŽŽ��������‘’‘’“‘��ŽŽ‹Š‰„ƒ„„ƒ„ƒƒ„ƒƒƒƒƒƒƒ„„„„ƒƒƒƒƒƒ�ƒƒƒƒ„ƒ‚‚ƒƒƒƒ„„„„„„„„„„„ƒƒŽ—‘Ž�ŠŠŒ�������‘���‘‘�‘’‘‘’““�Œ‹ŒŒ‹‰„ƒ„„ƒ„ƒƒ„ƒƒƒ‚ƒƒƒƒ„„„ƒƒƒƒƒƒ�ƒƒƒƒ„ƒ‚‚ƒƒƒƒ„„„„„„„„„„„ƒƒŽ—‘Ž�ŠŠŒ�������‘���‘‘�‘’‘‘’““�Œ‹ŒŒ‹‰„ƒ„„ƒ„ƒƒ„ƒƒƒ‚ƒƒƒƒ„„„ƒƒƒƒƒƒ�ƒ‚‚���‚‚‚ƒƒƒƒƒ„„…„…„„„„ƒƒŽ˜’�Ž‹Š�ŽŽ�‘‘’’’’’’’‘’’’‘’“•–“�ŒŒ��‰…„„„„„„ƒ„„„ƒ‚ƒ„‚ƒ„„„ƒ„ƒƒƒƒ�ƒ‚‚���‚‚‚ƒƒƒƒƒ„„…„…„„„„ƒƒŽ˜’�Ž‹Š�ŽŽ�‘‘’’’’’’’‘’’’‘’“•–“�ŒŒ��‰…„„„„„„ƒ„„„ƒ‚ƒ„‚ƒ„„„ƒ„ƒƒƒƒ�€€���‚‚‚ƒƒ„„‚�‚„„„„„„„ƒ„ƒ�—’��ŒŠŒŽŽ�‘’“’“““““’““’‘’•™˜˜’ŽŽŽŽŠ…„…„„„„ƒ„„„„ƒƒƒƒƒƒ„„ƒ„„„ƒƒ�€€���‚‚‚ƒƒ„„‚�‚„„„„„„„ƒ„ƒ�—’��ŒŠŒŽŽ�‘’“’“““““’““’‘’•™˜˜’ŽŽŽŽŠ…„…„„„„ƒ„„„„ƒƒƒƒƒƒ„„ƒ„„„ƒƒ€€€���‚‚‚‚ƒ„„„‚‚‚‚ƒ„„„„„…„Š•’““ŒŠ‹���‘’“““””“’’’‘’’”™›š™”���Ž‹†‚„„„„„„„ƒƒ„„ƒ‚ƒƒƒƒ„„ƒ„„„ƒ€€€���‚‚‚‚ƒ„„„‚‚‚‚ƒ„„„„„…„Š•’““ŒŠ‹���‘’“““””“’’’‘’’”™›š™”���Ž‹†‚„„„„„„„ƒƒ„„ƒ‚ƒƒƒƒ„„ƒ„„„ƒ€€���‚�‚‚‚ƒ„„…ƒ�‚��‚ƒ……………ˆ‘”•’�ŠŠ‹�Ž‘‘’’“””““’‘’”•—™››š”���ŽŒ‡„ƒ„„„…„ƒƒƒƒ„ƒƒ„‚ƒƒƒ„„ƒƒƒƒ€€���‚�‚‚‚ƒ„„…ƒ�‚��‚ƒ……………ˆ‘”•’�ŠŠ‹�Ž‘‘’’“””““’‘’”•—™››š”���ŽŒ‡„ƒ„„„…„ƒƒƒƒ„ƒƒ„‚ƒƒƒ„„ƒƒƒƒ€�����‚‚‚ƒ„„„…„�‚‚‚‚‚„„………†�””��‹‰‹ŒŽ�‘‘’’““““’’”•””•—˜—‘�‘‘�Œ‡……„ƒ„…„ƒƒ„„„ƒƒ„ƒƒƒƒƒ„„„„„€�����‚‚‚ƒ„„„…„�‚‚‚‚‚„„………†�””��‹‰‹ŒŽ�‘‘’’““““’’”•””•—˜—‘�‘‘�Œ‡……„ƒ„…„ƒƒ„„„ƒƒ„ƒƒƒƒƒ„„„„„€����‚ƒƒƒƒ„„„„„ƒ‚‚‚ƒƒ‚‚ƒ„……Š’“‘‘�ŠŠ‹Ž���‘‘’’“’’’•–“‘‘’“’�Ž‘‘�Œ‡………„ƒƒ„„„„„„„ƒƒƒƒƒƒƒƒƒ„„„€����‚ƒƒƒƒ„„„„„ƒ‚‚‚ƒƒ‚‚ƒ„……Š’“‘‘�ŠŠ‹Ž���‘‘’’“’’’•–“‘‘’“’�Ž‘‘�Œ‡………„ƒƒ„„„„„„„ƒƒƒƒƒƒƒƒƒ„„„€€����‚ƒƒ‚ƒƒƒƒƒ„‚�‚‚ƒ„‚‚‚„„†Ž‘‘’ŽŠ‰‹�Ž���‘�‘’’’‘’”“’‘‘�ŽŒŽ���‹†……………ƒƒ„„„„„„„ƒƒƒƒƒƒ‚‚„……€€����‚ƒƒ‚ƒƒƒƒƒ„‚�‚‚ƒ„‚‚‚„„†Ž‘‘’ŽŠ‰‹�Ž���‘�‘’’’‘’”“’‘‘�ŽŒŽ���‹†……………ƒƒ„„„„„„„ƒƒƒƒƒƒ‚‚„……€€����‚‚‚‚ƒƒƒƒ„ƒ‚�‚‚‚ƒ„„ƒ‚ƒ†ˆ�’“‘‹‰ŠŒ�Ž����‘‘�‘‘������Ž‹ŠŒ��Ž‹†……………†…ƒ„„„„„„ƒƒƒƒƒ‚ƒƒƒƒ„€€����‚‚‚‚ƒƒƒƒ„ƒ‚�‚‚‚ƒ„„ƒ‚ƒ†ˆ�’“‘‹‰ŠŒ�Ž����‘‘�‘‘������Ž‹ŠŒ��Ž‹†……………†…ƒ„„„„„„ƒƒƒƒƒ‚ƒƒƒƒ„€€���‚‚ƒƒ‚ƒƒƒ„„ƒƒ‚�‚‚‚ƒ„„„„††‹‘”“�ˆŠŒ�Ž����������Ž�����‹‰‹‹ŒŒŠ…………†…††…ƒ„„„…„„„„ƒƒƒ‚ƒƒƒƒ€€���‚‚ƒƒ‚ƒƒƒ„„ƒƒ‚�‚‚‚ƒ„„„„††‹‘”“�ˆŠŒ�Ž����������Ž�����‹‰‹‹ŒŒŠ…………†…††…ƒ„„„…„„„„ƒƒƒ‚ƒƒƒƒ€€���‚‚ƒƒƒ„ƒƒ„„„„ƒ��‚‚‚ƒ„„†‡ˆ‰�“”�ˆŠ‹Œ�������ŽŽŽŽ�Ž�Ž��‹ŠŠŠŠ‹‰†……………†††…„ƒ„…„„„„ƒ‚ƒƒƒƒƒƒ€€���‚‚ƒƒƒ„ƒƒ„„„„ƒ��‚‚‚ƒ„„†‡ˆ‰�“”�ˆŠ‹Œ�������ŽŽŽŽ�Ž�Ž��‹ŠŠŠŠ‹‰†……………†††…„ƒ„…„„„„ƒ‚ƒƒƒƒƒƒ€€€�‚‚‚‚ƒƒƒƒƒƒ„„„„‚���‚‚ƒ…‡ˆ‰ŠŠŒ�Ž‰ŠŒŒŽ����ŽŽ�Œ�Ž�’“‘���Œ‹‹ŠŠˆ………†……††…††…„„„„„„ƒƒƒ„ƒƒƒƒ€€€�‚‚‚‚ƒƒƒƒƒƒ„„„„‚���‚‚ƒ…‡ˆ‰ŠŠŒ�Ž‰ŠŒŒŽ����ŽŽ�Œ�Ž�’“‘���Œ‹‹ŠŠˆ………†……††…††…„„„„„„ƒƒƒ„ƒƒƒƒ€€��‚��‚‚‚ƒƒƒ„ƒƒ„ƒƒ����‚„†ˆ‡‰Š‡ˆŒ�ЉŒ�Ž�‘�������“““””“’‘���‹Š†………†……………†††…„„„„„„ƒƒƒƒƒƒƒ€€��‚��‚‚‚ƒƒƒ„ƒƒ„ƒƒ����‚„†ˆ‡‰Š‡ˆŒ�ЉŒ�Ž�‘�������“““””“’‘���‹Š†………†……………†††…„„„„„„ƒƒƒƒƒƒƒ€€��‚��‚‚‚‚ƒƒ„ƒƒƒƒƒ‚�€�‚„…‡ˆ‰Š‡‡‹�ЉŒ�Ž�’’�ŽŽ��‘“’�Ž�‘��’‘ŽŒŠ†………………………††††…„ƒ„„„ƒƒƒƒ„ƒƒ€€��‚��‚‚‚‚ƒƒ„ƒƒƒƒƒ‚�€�‚„…‡ˆ‰Š‡‡‹�ЉŒ�Ž�’’�ŽŽ��‘“’�Ž�‘��’‘ŽŒŠ†………………………††††…„ƒ„„„ƒƒƒƒ„ƒƒ€��‚‚�‚‚‚‚‚ƒƒƒƒƒƒ„ƒ„ƒ���ƒ…†‡‰‰ˆ‡ŠŽ‹Š‹�Ž�’“’���’•˜—“�����”���І………†…†……………††††…„„„„ƒ„ƒ„„„€��‚‚�‚‚‚‚‚ƒƒƒƒƒƒ„ƒ„ƒ���ƒ…†‡‰‰ˆ‡ŠŽ‹Š‹�Ž�’“’���’•˜—“�����”���І………†…†……………††††…„„„„ƒ„ƒ„„„€��‚‚‚‚‚‚‚‚ƒƒƒƒƒƒ„„„„‚�‚ƒ…††ˆˆˆˆŠŽ�‹Œ�Ž��“’‘��“˜›œ›š˜–••“ŽŒ�Š…††††††††…††…†††††……„„„„……„€��‚‚‚‚‚‚‚‚ƒƒƒƒƒƒ„„„„‚�‚ƒ…††ˆˆˆˆŠŽ�‹Œ�Ž��“’‘��“˜›œ›š˜–••“ŽŒ�Š…††††††††…††…†††††……„„„„……„€€�‚‚‚‚‚‚‚ƒƒƒƒƒƒƒ„„ƒ„‚��‚ƒ„…††‡‹Ž�ŽŽŽ�ŽŽ�’’“”•”•–˜šœ��œ™’��Œ‰†††††‡‡‡†††……†††††††„„„„……ƒ€€�‚‚‚‚‚‚‚ƒƒƒƒƒƒƒ„„ƒ„‚��‚ƒ„…††‡‹Ž�ŽŽŽ�ŽŽ�’’“”•”•–˜šœ��œ™’��Œ‰†††††‡‡‡†††……†††††††„„„„……ƒ€��‚‚‚‚‚‚ƒƒƒ‚ƒƒƒƒ„„ƒƒƒ‚�‚ƒ„††††�š�Ž���Ž���’•—™˜”‘‘“–˜˜—”ŽŽ�Žˆ†††††‡†‡††††††…††††‡†„„„……„€��‚‚‚‚‚‚ƒƒƒ‚ƒƒƒƒ„„ƒƒƒ‚�‚ƒ„††††�š�Ž���Ž���’•—™˜”‘‘“–˜˜—”ŽŽ�Žˆ†††††‡†‡††††††…††††‡†„„„……„€���‚‚‚‚ƒƒƒƒƒƒƒƒƒƒƒ„ƒƒ‚��‚ƒ„…„„’¥™Ž‘“’‘��‘‘”—š™–’������ŽŽ‘”އ†††††††††‡‡‡‡††‡†††‡†…„„„…„€���‚‚‚‚ƒƒƒƒƒƒƒƒƒƒƒ„ƒƒ‚��‚ƒ„…„„’¥™Ž‘“’‘��‘‘”—š™–’������ŽŽ‘”އ†††††††††‡‡‡‡††‡†††‡†…„„„…„€��‚‚‚‚‚ƒƒƒƒƒƒƒƒƒƒƒƒ„ƒƒ‚���‚‚‚‚“«¢’’”””’‘��’–˜™™–“’������“”Œ††…………†††††‡‡‡‡‡‡‡††††††„„„„€��‚‚‚‚‚ƒƒƒƒƒƒƒƒƒƒƒƒ„ƒƒ‚���‚‚‚‚“«¢’’”””’‘��’–˜™™–“’������“”Œ††…………†††††‡‡‡‡‡‡‡††††††„„„„���‚��ƒƒ‚ƒƒƒƒ„„ƒƒƒƒƒ„„„ƒ�������•®ª™’”––•“’‘‘”•—šš˜—•“’“•–•’Š…†††…†††††††‡‡‡‡‡‡†††‡‡‡††„„���‚��ƒƒ‚ƒƒƒƒ„„ƒƒƒƒƒ„„„ƒ�������•®ª™’”––•“’‘‘”•—šš˜—•“’“•–•’Š…†††…†††††††‡‡‡‡‡‡†††‡‡‡††„„���‚‚‚ƒ„‚ƒƒƒƒ„ƒƒƒƒƒ„„„„„ƒ�����’¯¯¤””———–”‘‘‘’•˜™šššš™™š—“Žˆ††††…†††††‡††‡‡‡‡‡‡‡†‡†‡‡‡‡…���‚‚‚ƒ„‚ƒƒƒƒ„ƒƒƒƒƒ„„„„„ƒ�����’¯¯¤””———–”‘‘‘’•˜™šššš™™š—“Žˆ††††…†††††‡††‡‡‡‡‡‡‡†‡†‡‡‡‡…€��‚ƒƒƒƒ‚‚‚ƒƒƒ„ƒƒƒ„„„„ƒƒ„�����Š«°¬�“˜™™™–“’��‘”–˜™›š™™š–�Ї†††††……††††‡††‡‡‡‡‡‡‡††‡‡‡‡‡€��‚ƒƒƒƒ‚‚‚ƒƒƒ„ƒƒƒ„„„„ƒƒ„�����Š«°¬�“˜™™™–“’��‘”–˜™›š™™š–�Ї†††††……††††‡††‡‡‡‡‡‡‡††‡‡‡‡‡€��‚ƒ‚ƒƒƒƒƒƒƒƒ„ƒƒƒ„ƒƒƒƒ„„ƒ�����„¡°¬ª›˜š™›š—“‘�Ž�’•˜™——˜—’Œˆ‡‡†‡‡‡‡†††…†‡‡‡‡‡‡‡‡ˆˆ‡†‡‡‡ˆ‡€��‚ƒ‚ƒƒƒƒƒƒƒƒ„ƒƒƒ„ƒƒƒƒ„„ƒ�����„¡°¬ª›˜š™›š—“‘�Ž�’•˜™——˜—’Œˆ‡‡†‡‡‡‡†††…†‡‡‡‡‡‡‡‡ˆˆ‡†‡‡‡ˆ‡€�‚‚ƒ‚‚ƒƒƒƒƒƒƒ„„ƒ„„„„ƒƒ„„„‚����€•¬«©¨ ›š›œ™–“�Ž��‘”•”””’�‹�Ї‡†‡‡†‡‡†‡‡‡‡‡ˆ‡‡‡‡‡‡‡‡†‡‡ˆ‡€�‚‚ƒ‚‚ƒƒƒƒƒƒƒ„„ƒ„„„„ƒƒ„„„‚����€•¬«©¨ ›š›œ™–“�Ž��‘”•”””’�‹�Ї‡†‡‡†‡‡†‡‡‡‡‡ˆ‡‡‡‡‡‡‡‡†‡‡ˆ‡€�‚‚ƒƒƒƒ„ƒƒƒ„ƒ„„ƒ„„„„„„„„„ƒ‚���~‡¢¬§£§Ÿ™››š˜–’�Ž�Ž�����ŽŠ’ œŽ‡‡‡‡‡‡‡†‡‡ˆ‡ˆ‡‡‡‡‡‡‡†‡‡‡‡ˆ‡€�‚‚ƒƒƒƒ„ƒƒƒ„ƒ„„ƒ„„„„„„„„„ƒ‚���~‡¢¬§£§Ÿ™››š˜–’�Ž�Ž�����ŽŠ’ œŽ‡‡‡‡‡‡‡†‡‡ˆ‡ˆ‡‡‡‡‡‡‡†‡‡‡‡ˆ‡€�‚‚ƒƒƒƒƒƒƒƒ„ƒ„„ƒƒ„„„„ƒ„„„„‚��€|~‘¨©£¡¡ššš™˜–”’�ŽŽŽŽ�Ž��‹—¥¨£“ˆ‡††‡‡‡‡‡‡‡ˆ‡‡‡‡‡‡†‡ˆˆˆˆˆˆ€�‚‚ƒƒƒƒƒƒƒƒ„ƒ„„ƒƒ„„„„ƒ„„„„‚��€|~‘¨©£¡¡ššš™˜–”’�ŽŽŽŽ�Ž��‹—¥¨£“ˆ‡††‡‡‡‡‡‡‡ˆ‡‡‡‡‡‡†‡ˆˆˆˆˆˆ€‚ƒƒƒƒ‚ƒƒƒƒƒ„ƒ„„ƒ„„„ƒ„„„„„„ƒ‚€}{{‘£¥ŸŸž˜™˜—–”“‘������ŽŽ‹–¤§ª§—‰†‡†‡‡†‡‡‡‡‡‡‡‡‡†††‡ˆ‡‰ˆ‡€‚ƒƒƒƒ‚ƒƒƒƒƒ„ƒ„„ƒ„„„ƒ„„„„„„ƒ‚€}{{‘£¥ŸŸž˜™˜—–”“‘������ŽŽ‹–¤§ª§—‰†‡†‡‡†‡‡‡‡‡‡‡‡‡†††‡ˆ‡‰ˆ‡€�‚‚ƒƒ‚ƒƒƒƒƒƒƒ„„„„„„ƒƒ„„„„„„‚}{{{|}ŠšŸŸŸœ———–““’‘������Œ‹•¢¤ª­©˜ˆ†††‡†††‡‡‡ˆˆ‡ˆ‡‡ˆ‡ˆˆ‰ˆ†€�‚‚ƒƒ‚ƒƒƒƒƒƒƒ„„„„„„ƒƒ„„„„„„‚}{{{|}ŠšŸŸŸœ———–““’‘������Œ‹•¢¤ª­©˜ˆ†††‡†††‡‡‡ˆˆ‡ˆ‡‡ˆ‡ˆˆ‰ˆ†€�ƒƒƒƒƒƒƒƒƒƒ„ƒ„ƒ„„„ƒ„„„„„„„„€{{{{{{|„‘œ�Ÿœ–•••”“’’‘��Ž��Œ“ ¥ª¬­¦Žƒ†‡‡††‡††‡ˆˆ‡ˆˆ‡ˆˆˆˆˆ‡‡€�ƒƒƒƒƒƒƒƒƒƒ„ƒ„ƒ„„„ƒ„„„„„„„„€{{{{{{|„‘œ�Ÿœ–•••”“’’‘��Ž��Œ“ ¥ª¬­¦Žƒ†‡‡††‡††‡ˆˆ‡ˆˆ‡ˆˆˆˆˆ‡‡‚ƒƒƒƒƒƒ„„„ƒ„ƒ„„„„„„„„…„ƒƒ‚€}zz{{z{{|€‹—�Ÿš••••”“’‘���ŽŽŒ“Ÿ§««¯¯–‚ƒ„…†‡‡‡‡‡‡‡‡ˆˆˆˆˆ‰Šˆƒ…‚ƒƒƒƒƒƒ„„„ƒ„ƒ„„„„„„„„…„ƒƒ‚€}zz{{z{{|€‹—�Ÿš••••”“’‘���ŽŽŒ“Ÿ§««¯¯–‚ƒ„…†‡‡‡‡‡‡‡‡ˆˆˆˆˆ‰Šˆƒ…€‚ƒƒƒ„„„„…ƒƒ„„„…„„„„ƒ‚�€~~}}|{z{{{{{{{|ƒ�˜š”””••“’‘��ŽŽ��‘�¦ª®»·•……„ƒ…†‡‡‡‡ˆˆ‰‰‡…‡�—��|€€‚ƒƒƒ„„„„…ƒƒ„„„…„„„„ƒ‚�€~~}}|{z{{{{{{{|ƒ�˜š”””••“’‘��ŽŽ��‘�¦ª®»·•……„ƒ…†‡‡‡‡ˆˆ‰‰‡…‡�—��|€€‚ƒƒƒ„ƒ„„„„„„„„„…„ƒ‚}|||{|||{{{z{{zz{z{|�‡ˆŒ�“”“’����ŽŽ�’ž¥¬ºÂ¯Œ…††„ƒƒ‚…†‡…Ž™‘†�’ œŽŒ‘‘€‚ƒƒƒ„ƒ„„„„„„„„„…„ƒ‚}|||{|||{{{z{{zz{z{|�‡ˆŒ�“”“’����ŽŽ�’ž¥¬ºÂ¯Œ…††„ƒƒ‚…†‡…Ž™‘†�’ œŽŒ‘‘�‚ƒƒƒ„„„„„„„„„„ƒ‚€~}|}|{||{{||{zz{|{{{|{|}}}~ƒŠ�‘�������Œ�›¡¬³³Ÿ…†…†……„€€…„�ž“�“›�œœœ�š�‚ƒƒƒ„„„„„„„„„„ƒ‚€~}|}|{||{{||{zz{|{{{|{|}}}~ƒŠ�‘�������Œ�›¡¬³³Ÿ…†…†……„€€…„�ž“�“›�œœœ�š+×þùûþþýûüþþþþþýûþþþþþùîÞßááßÞááàßáåãÛ·˜žªÄØÚ×ÖÚÜÜÙØÙÚÜÙÜÜÜßÜÛÝÝÞÙÕÔ×Ûß×ɪ‘’“‘Œ�†vu‚ªÙàÞÞßÝÝÖÐÙÙÑÐÒÔÔÚËŠs‡„y€µÑÏÑÓÔÙ×ÔÖÕÑÎÒÑÉÀÀʳ‰‡¸ÛÒÔÕ×ÛØÒÏÎÏÌÊŸq†»ÌÏÒÏÎÑÊÆÒ»}~¹ÎÌËÁ¹ºÈµ|p¯ÅËÃ1ÆÁÓäÍÕ¹ÓϺøÆËзØÄÆîкÕíáàáááàßßâææÙº˜‘§¼ÍÓÓÔÒÕÙÚØ×ÙØÚÜÚÛÜÝÞÞÝÞßÜ×ÔÝßÕÁ¦˜““—š�vvy‡£ÊÝÞÝÝÛÚÙÜÇ¿ÙÓÎÑÐÑÑÌÓÅŒxˆ†v‚¸ÐÐÔÒÖÖÒÔÔÑÎÐÐÊÄÈͼ¡…‡½ÙÓÔÖÙ×ÓÐÎÎÇɸˆqйÍÑÑÏÏÍÉÍЩv�ÄÐËÿ½ÇÄœp~¶Ï¼0±«Ä­¹ˆ¿Ãгް²‡Ê°Š»Ë¦´çãàâàÞßâäâÔº�’ž³ÍÙÛ×ÔÔÕ×ÚÑÎÙÙÔÚÜÛÜÛÜÜÝØ×ÙÚÝÛÓ» ’��—˜Š{sx‘³ÑßâàÜÛÛÛÛÛÛÖÕÚÒÑÑÐÐÑÌÑÒºˆwŠ�k~´ÐÑÕÖ×ÓÒÓÒÏÏÌÌÎÍ、{‡ÂÚÑÔØØØÒÎÎÌÍËÀ’q’ÃÐÏÎÍÎÌÊÏÏœq–ÌÑÈÆÂÊɱŠe‡Ç»-Öݦµ¾ÄžÌѰ¥­Ä¾œÑ˯¢åÏ¡ÐåßÞÝâåÝѸ�š¦½ÕÚÙ×ÈÈÖÖØÛÙÍÐÛØÖÚÚÛÛÙÜÖÓÕØÝÜͱ–ŠŠ—�“‡{w�™¿×áãÞÛÚÜÜÜÝÝÜÚÛÚ×ÓÒÒÏÐÑÑз²º…u‡‚q€¯ÉÎÓÖÕÕÕÑÒÓÌËÐÍÏÆ¿À�w‰ÆØÓרØÖÏÌÐÐÉÏÊ›z�ÄÅÊÌÍÆËÎÒÊ’p¤ÒÍÄÅÍ̹®�i”¹(¶Ú¼ÙçÑÎÝáããâàÒÝâæÚåÛÕàÞÝáåàͳ š©ÄÖÔÔÓÕÕÄÊÕÖØÚØØØØ×ÚØÕÚÛÚ×ÓØàÛɬ‘…‹—™‘�ut…§ÇÚââßÛßÞÝÞÝÝÜÚØÛÛÛÞÝßàßßÞÛØÉ¿ÐÀ†s…†u~®ÌÏÓÓÕ×ÔÒÔÎËÉËÓȾļ–xŽÊÖÑÖÕ×ÔÐÐÏÉÉÐÖxŸ¼ÇÍÎËËÐÏÔɉp«ÐÊÉÏÑÆ¾ºˆo”‡Å±½ÛåÜÑáæäâãããßàáæàÞåãáæá̬™˜•·ÖÛÖÒÒÓÖØ××ÕÙ×Ô×ÚÚ×ÖØØÙÙ×ÛÛÛצ‘„…“œ’rv�²ÏÙÙÜÚ×ÍØØÙÝÝÛÝÞÞàäçéëëììíììëëçåÞßÌ�m€‹~„²ÐÑÒÕÔÕÔÎÏÎÇÏÕÇÁÁɘt�ÆÕÒÔÖÕÖÒÑÏÍÊÈ¿ˆu ÃËÍÎÊËÐÎ×Àys³ÐÌÏÐǼ¾¯}j}­ÄØÏÖÜáàÝÜÛÙÙÛÝÛÛ×ÖÞââÝË®›š¤­½ÖÚÙר×ÙÙÙÚÖÖØÙ××ÙÛÚØÙÖ×ÛÝÝ϶žŽ‹Ž—�~{u~š¾×áß×ÔÙÝÒÅÔÍØÞÝàäçéëìììììëëëëëëìíìëèíÙšx€Š�…²ÑÒÖÒÒÕÍÐÐÏÔÕÌÅÇÊÊ»�s�É×ÓÖÒÔÔÑÐÍÏÈȹ�x²ÎÎÌÊÌÍËÎÖ°o{ºÍÎÏǽº·¡y!u®ÝÞÛáâáááàÝÜÝÞÞÝÝàãäÕ¾­››ª½ËÏÙØÔÛÝÛÙÙØÙÙרÛÝÙÖ×רÖ×ÕÙÛÊ­’‡‰’—Œ}po†£ÅÙÝÝÙ×ÚÛÛÜÛÜÜÛßãçêëììëëëëëêêëëëëêëëëëìëðèÀ—†…€‚®ÐÔÒÔØÓÆÉÒÎÎÌÈÌÆ¼¼±Šr”ÎÖÓÔÓÔÑÏÏÎÍËκ{„ÃÒÌÌÎÌÌÍÐÒ¦k„ÂÐÏʾ¾¸º¢}ÌÞÞââààááÜÙÜÝÛÜßãáÒ¸��š°ÅËÌÑÖÖÙÛÝÜÚÙÕÕÒÓ×ÙÚ×ÕÖÕÕÖÜÛÒÁ¨‘ƒˆŽ—ˆunrްÅÖá×ÏØÛÜÛ×ÚÜÞßáçèéììëëëëëëëëëëëìëëììììëëëéîóã¼”…~©ÎÑÓÙÖÈÊÓÎËÊÉÍ̺¾²ŠtœÒ×ÓÔÖÒÏÎËÏÍÊЬu’ÇÍÌÎÍÍÍÍÑÒši–ËÒËÃÇ¿¾°‰ÛàßßáâáßÜÚÞÜÛÞáÞʰ›”Ÿ¬½ÑÓÎÍÍÔØÛÜÛÙÖÔרÓÔØÙÔÓÔÔØÛÙÒºŸŽ‰Œ”™ˆxmw“³ÍÞÒÒ××ÛÝÜÜÜÛÝßáåéëìëìëëëëìëëëììëëëëëëìëëëìíìèéðñتˆ|{©ÐÑ×ÔÐÓÔÓÐËÌÍÍËÅÁ°‡qœÑÔÑÕÖÓÑÐÏËÊÉ̦w—ÄÊÍÎÍÌÍÌÓÍ‘oŸÍÑËȾ¾­‡ØáßààßÞÜÙÚÒÑáÝʬ”‘š¯ÇÐÓÔÌÊÍÑ×ÚÛÜØ××ÖÓÓ×ÖÚÙÕÐÙÜÙ˲šŽŠ�—‘†wn|ž¿ÐÔÔÙרÜÞÝßÝÛÜÞâçêììëëììëëëìëëëëìëëëëëëëìëëëìììëêçëò縋|~ªÑÔÑÊÑÕÓÓÎÌÌÉÊÈÀ¼À©€o ÔÔÒÕ××ÓÎÌÌÌÊÉœt™ÄÌÏÊÊÎÎÏ×ȇk§ÒÎÌÂÁ®‡ÙáÞÝßßÞÛʨ�¸Ê°œ�™§½ÐÐÏÖÔÉÌÍÑÔÚÝÜÜÙØÕÁÉÖÐÒÖÙÙÚɯš��“•‹~rq„¤ÅÚÞÙÔÖÛÝÜÔ×ßßÜÜáæêìëëêëëëêëìëëëëëëìëëëëëëìììììììëëêêéèîí¿‘|x¤ËÏÉÐÔÒÑÎÍÌÌÊÉÅÀ¿·¨†t©ÖÕÑÓÖÒÎÍÍÎËÊ‘sŸÄÍÇÈÎÍÒÒÖ¾{tµÒÏËŲ‡ÖßßäåâÞÅ«”u‘›”š«»ÀÈÒÑÒÙÑÍÏÎÒ×ÚÛÚÚÙÚÔÍÑÐÒ×ÚÑ앎”˜“„tmw�±ÎßßÚ×ÙÛÛÛÛÙÏÖÞÜÞäéëëìëëëëëëëëìëëëìëìììëëëëëëììììëëëëëêëêèìí¿ŒzvžÇÐÒÔÔÑÏÎÍÍÊÉÉÅ´¯·¬‡v¨ÕÒÎÒÒÏÏÍÍÏÌȾ�v¡Ç¿¿ÌÍÑÒÑÖ¹v{¾ÑÊÈ·†ØäáØ¼®«œ‹•�”¯¿ÌÔÏÅÐÑÔØÏÐÎÒÚÜÚÛÛØ××Õ×ÔÔÙÔ¼¡’�•�–„qo}›¾ÔßàÛÖÔÙÜÛÚÜÝØÚÝÝáçêëëëììëëëëëìëëìëëëëëììììëëëëëìëëëëëëëëëêêèìꮇ€wžÏ×ÔÖÖÑÏÐÎËÈÆÇ¸°´½³Šv¬ØÎÐÔÑÏÎÏÍÌÊÊÀ‰t©Á¾ÉÎÏÒÒÒØ°o‡ÆÌȸˆÞÒ®—‹ƒ€Œ��Œ ¼ÎÑÒÔÒÔÖÈÓÕÍÎÍÖÛÚÚÛÛÙÕÒÖÓÐɳ��‘–—–…ss‡¦ÆÛßÛ××ÙÚØÛÕÒÜÝÝÝÞÞäêìëëëëëëëëëëëìëëìììììììíìëìëìììììëëëëëëêêëêêéïÚ˜Š�t�ÐÕÓÖÓÐÑÏÌÊÃÃȾ»½»´†w±ÖÒÔÓÑÓÓÏÌÎÉȼƒz¢³ÅÌÍÑÓÒÕÖ¤p—ɼ­�²ž”š¥…jp¦¾ÂÓØÕÏÐÑÖØÑÑÖÏÌÉÐÙÙÚÛÚÕÖ××ÔÀ§‘ˆ�˜˜�{sz”·ÐÝÝÚØ×ÚÞÜÚÙÚÔÓÞÞÜÝàæëëëëëêëëëëëëëëìëëìììììììììëììëììëìëëëëëëêêêêêêèî¿…Š{mšÍÒÑÓÑÑÐÌÌÆÂÅÁÁ¿¸±¤†x²ÖÌÁÎÖÔÒÐÎÍÆËº}m’ÀËÍÐÑÒÑÕÒ›s¢½«s™¨ÆÔÒ§qƒº×ÚØÖÕÔÖÖ×ÒÓÚÒÌÏÌÕØØÚÚÖØÛÒ¼¡�ˆ‹•™‡qp€”µÙáÝÚרÝÜÛÔÕÚ××ÜÞ××Ûßçëëëëëëëëëìììëëëììëììììììëëëëììëìììììëëëëêêéêêéééìä˜xˆzošÌÓÎÐÑÑÌÊÊÈÄÂÁÿ°®¯}x·ÑÀÍÓÒÔÕÐÍÊÆÎµwv²ÈËÎÏÐÑÒ×Αuªµ~ÆÓÚÞÛͼÊÓÎÐÓ×ÔÓר×Ô×ÖÎÎÍÒÖÕÕÚÜÙÑ»ŸŒ‡Ž•�€qj|©ÊØÜÞÙÛÑÏÜØÚÚÍÒÛÛÛÛÛÓÕáçêêëëìëììëììëëëëëììììììëììëììììììììììëêêêêêêéêéééêéîÊ~yŠ}p–ÆÊÉÑÓÎËÉÈÄÁÃÅŹ»¢z|¼ÒËÑÓÔÖÔÏÍÈÉÓ­q‡½ÊÌÏÏÐÒÒØÉ‡{¡‡×ÛÒ×ÑÌÙÖÓÖÕÔÕÐÐÕ×ÕÕØÏËÍÎÒÔ×ÛÚÍ·žŒ‰’–Šrer‹©ÌÞßÝÙÚÝßÓÐÚרÙÙÚÜÝÜÛÛÝáèêêëëëëëìëëëëëëëëëëëëììììììììììììììììëëêêêééééééééêêêë¸qs‡~o’½ÆÎÓÐÍÉÅÇÁ¿ÄÉÊü³¦|½ÒÐÔÓÓÓÒÏÊÉÌÌ q“ÃÌÍÏÎÐÓÒØ¿€v…ÒÛÐÎÏÊÍÐ×ÕÓ××ÐÐ××ÐÖÕÊËËÏÕÚØÈ±šˆ†ŒŽ‚mdr’»ÕÞÝ×רÚÛÜÜÛÛÚÙÙÛÜÛÝÜÛÜÜáçéêêêëêëëëëëëëëìëëëëëëëìììëììëìììììììëëëêêêêééèéèééééééÞ¨on„}q‘ÁÍÌÐÐÍËÊÄ¿ÂÄÊȺ§¶®z…ÁÖÒÓÔÓÓÑÊÊËÌË¢sžÈÉÎÐÏÒÓÒÕ³z†ÖÛÐÍÊÁÌÓØÖÔÒÔÑÒÙÕÏÔÑËÍÐÕÓÀ¦“ŠˆŠˆ|j^ršÃÜà×ÑÓÚÝÜÚÙÚÚÙÙÚÙÚÜÛÝÔÔÝÛßæèéêêêêêëëëëëëëëëëêìëëëëìíìëììììììëëëìëëëêêêêéééééèèèèéçâÙ³vl‚~q�ÄÎËÏÏÏËÆÁ¿¾Ç˽¨¶Æ«w†ÃÕÑÒÓÔÓÌÌÐÌÌÌ™w¦ÈËÎÏÐÒÑÐÏ©‡ÖÕÐÑËÈÑÕÔÕÕÑÒÑÕ×ÓÔÓÐÐÐ̼¢�‰ŒŒ†wkiyœÉßßÙÖÕÔÛÕÒÛÛÛÛÚÙÚÚÛÚÙÜÝÔÔÜÝäèèéêêêëëêëëëëëëëìëëëëëëëìíìììììëëëëìëëëëëëêêéêééèéèèèèèèåØ×ºwh~~o�ÃÎÌÌÍÊÈÃÀÀÄÉÆ±¸ÈÆŸqˆÅÓÏÓÓÒÏÍÎÌÉËË’z±ÍÍÐÐÐÒÐμ‰ÔÓÑÐÍÏÕÙÔÓ×ÖÒÒÖÔÓÖÕÓDzœŽ‹‘“†uik�¤ÇÞßÙÕÖÙÚÛÜÕÔÛÜÙ×ÛÚÚÚÚÚÚÛÛÜÛÝäççèéêêêëëêêëêëëëëëëëëëëëëììëììëìììììëëëëëëëêêêéééééèèèèèèçÜÏÕ¹|h{|n‰¿ÐÊËÊÉÅÀÂÃÃɼÀÏǸ™rŽÉÕÒÕÔÓÏÏÏÍËÏÈ’¸ÑÍÏÑÒÒ˳ˆÒÕÑÍÌÎÖÖÓÕ×ÓÑÒÕÖÛׯ¬’ŠŠ��‚tkmƒ­ÎÜÝØÕÖÖÙÚÚÚÙÜÜÕÕÙ×ÚÛÚÛÚÛÜÛÛÛÜâæçèèéêêêêëêêêëëëêêêëëëëììëëëëëìììììììëëììëêêêêêéééééèèèèèèçãÕÌÔÂj~}o†¾ÒÉÇÈÇÆÄÁÃÊÄÈÓʹ¶šr’ÍÔÑÒÓÕÔÒÍÎÊÐÈ�„½ÏËÐÐÑ͵„ÎÑÍÌÍÓØÑÑÖÒÐÓØÚÔÄ©’††’•†ofr�¸ÈÕÞÖÓØÛÚ×ÚÛÛÚÚÚÚÖÖרÚÛÛÛÚÜÛÚÛÛàåæççèééêêêêêêëëëêêêëëëììììëëëìëìììììëìëëëëëëêêêëêééééééèèèèçäÛÎËÓ¿vf€~nƒ¿ÏÇÈÇÈÇÅÆÆÁÊÑÐĬ³�m•ËÐÍÒÖ×ÔÎÏÏÊÏËŒ‡¿ÌÍÎÎй„ÎÐÍÌÏ×ÕÑÒÒÏØÜÓ¸žŠ‚‡““‚ts€£ÀÑà×Ò××ÙÛÜÛÚÚÚÚÙÙÙÚÛÛרÚÚÚØØÜÜÛÛÝãæçèçèééééêêêêêêëêêëìëëëëëëëëëëëëììììììëëëëëëëëêêêééééééèèèèèåàÓÌÉͬzn{xg¸ÌÅÉÉÈÊÉ¿¸ÇÐÎÈ´±¬‘r˜ÏÒÐÔÖÕÒÓÔÏÊÔÆ‚ŠÁÎÍÍÏ·†ÑÒÎÍÐÔÐÑ×ÙØÏ·™…‡’‰{nn…«ÎßàÜÖ×ÙØÚÚÛÚÚÚÚÙØØÙÙÚÚÚÚÙÛÚÚØ×ÛÜÛÛÞâåæççèèééééêêêêêêêëëëêëëëëëëëëëìëììëëëëëëëëëêëëêéêêêééèèéèèèèæâØÍËÉÒÃixyl{®ÉÍÍÆËÎǺÂÒÐÆ¼²®¸’p ÍÑÒÒÒÔÕÕÒÐÎÔ³z�ÅÏÎиˆÏËÊÎÐÑÕÚÙÍ´•��ŠŒ~qlr�³ÒàÞÖÕ×ÚÛÙÙÚÙÛÖ×ÙÙØØÚÚÚÚÙÙÙÙÚÙÙÚÙ×ÖÙÜàâåææççèéééééêêêêêêêëêëëëëëëëëìëëëëìëëëëëëìëêêêêêêéêêéééèéèèèèæâÜÏÉËÌÕÅ�u}zix°ÈËÊÌÎÎÄÆÓÑÊÀº²¹´ŒsŸÎÓÐÒÔÔÖÓÔÓÍͯ}–ÆÎз…ÄÃÇÐØÚÕÀ¦‘�ƒ‹ˆxhf{�¿×àÞÙÕ×ÚÚÚÜÛÜÛÚÚØÙÙÙÙØÙÙÙØØÙÚÚÙØØ×ÖÖØÙßääææåçççèéééêêêêêêêëëëëëëëëìëëëëëëëììëëìëëëëëëêêêêééêééééèèèèçæãàÕÊÌÌÊÖÑ–mvwly¨ÈÌÌËÍÈËÓÏÊÅ¿·²·µ�x¨ÓÓÓÓÓÖÕÔÔÐÊЬz�ÊÒ¶x­¯ÌÜÖº—…‚†ˆ„ygg‚ªÏÞáÜ×ÖØÚÚÚÚÛÜÜÜÛÚÙÜÚÙÙÙÙØ×ÕרÙÚÚÙØÙÝßßÕÖßãäåååæççèééééêêêêêëëëëëëëëëììëëëëëëëëëëìììëëëëêêêéêêêééééééèèçæäáÚÎËÌÊÊÓÈ‹ovzt}¨ÅÊÍÐÎÍÔÑËü¸³®¸¶�{¯ÕÒÓÖÖ×ÖÖÖÏÊʤ~¥Ð¶s•–Áºœ‡€‰Ž…tjx’µÔßÞÚÕÔÙÛÙÙÛÚÙÚÛÛÚÚÚÙÙÖըרÚÙרØ×רÛßßÚȱ¶ÓÛáâäååææèèééééêêéêêêêëëëëìììëëìëëëììëëëëììììëëëëëëêêêééééééééèèçåâÝÑÊËÉËÌÔÊ‘oz‚v�«ÌÐÐÍÊÒÓÎŽ»½¶¶·°Š{²ÔÔÔÖרØÖÔÌÉË�z­º}²Ÿ‘Œˆ�Œ~to}¨ÊÜâÜÖÕ×ÙÙÚÚÙÚÜÚÚÚÜÝÚØÚÙ×ÖרÖ×ÚÙØÖÕÖÛÞÜϺ¡Š†¹×ÚââääåææççèèééêêêéêêëêëìëìììëëëìëìíìëììëëëëëëëëëëëêêêééééééèéèèèæãßÕÌËÊËÌÎÖÊ’o}‚y€¬ÍÐÊËÑÓÏËú¿¿¾µ³®‹�µÓÑÓÖÖ×ÕÓÏËÌÈ•�¥yÊ®�Œwgo�°ËãâÜÕÏØÙØÚÚÙØÚÜÜØÛÝÞÞÛÙÚØ×ØÙרØ×ÖÖÚÜÛÓÁª–Œ‰�©ÖÚÞâããäåææçççèèéêêêêëêëëëëëëëëììëëìëëììììëëëëëëëëëëêëêëêêêéééèèèèèæäàÙÎÌÉÊÇÇËÏÊ“kv{v~­ÓÎÌÒÓÏÊÆ¿¼½Á¼±¸¿‘ƒ¶ÖÔÒÕØÖÓÏÎËÎÆ�{qº²‚mlw“·ÕâßÙר×ÕÙÙÙÙÙØØÙÕ×ÙÛÜÜÙØÚÙÙ×ÖÖÖÕÔÖÜÞÚÆ®šŽŠ“�¢ŸÁßÝàáããäåååçèèèééééêêêêêëìëëëëìììëìììëëììëëëëëëëìëëëëëêëêéêéééééèèèçäáÜÒÍÊÉÇÇÌÉÎÈŽhy‚v~¯ÐÍÐÕÑÊÉÆÂÂÄÀ³ºÍ¸ƒ‰ºÓÒÔØÖÓÏÎÌÊÎÀŒw£�w~�ÃÚÜÙ×ÖÙÜÚÚÚØÙÚÚÙ×ÙÙÔÖÙ×ÙÙ×ÖØÚÙ×ÔÓÖÙÛÚп¦ˆ„Œ™Ÿœ•‰�ËâßßáâããääåæçèèèéééêêêêêêëëëëììììëëëëìëììëìëëëëëëëëëëêêêêéêêééééèèèçäâÞÕÍËÊÉÊÈÌÍÓОru{nu¬ÐÏÑÓÍÊÉÄÀÀÁ¸¼È¿¡�„ºÔÑÕÖÔÐÐÏÍËдg‹Œ¥ÎÜÝÚÕÖÚÜÜÛÚÛÙØØÙÚØÕÙÚÚÙØÖØÙØØ××ÕÕÙÜÛÓï˜�‹‹–›–‰~vžÛßßàáâãâãååææçèèèéééééêêêëëììëììëëëëëìëììëìëëëëëëëëëêêêêééêéééééèèèçåãàÙÎÊËËÅÀËÆÈÖÓ pwuiz«ÎÑÑÒÏËÆÀ½¾»ÀÊÀ¬š~‹½ÔÑÓÓÑÒÐÍËË·nµÓÛÜØÖ×ÚÙÙÛÛÚÚÚÖØÙÙÙ××ÙÚÙÙÙÚÙÙØ×ÕÖÛÝ×ȯš�Œ“šš“ˆ€zwxx~½àÝßàááâããäååææçèèééééêêêëëëëëëëëììëëëììëëëëìëëêêëêëëêêêéééêéééééééèçæäàÛÏÊËÃÇÊË´±ÍÏÔ¢qswqt£ÏÐÊÈÍÌÅÿ¼ÂÌȯ –zŠÇÖÐÑÐÏÑÎÌ̶‡×ߨÓÖØÚÚÛÚÛÜÛÛØ×ÙÙÙÙרØÙÚÙÙÙ×Õ×ÜÞØË¹ �Š’��•Šwuurtyƒ¡ÓÞÞßàááâããäååææçèèèèééêêêêëëëëëëëìëëëëëêêêêëëëëêêêëëëëêéééééèéêéééééèæäâÝÔÎÌÅÉÍËľËÊÒÓ¥xsxpp¢Í¿®ÉÐÇÄÅÇÈÌι£¢›s‰ÈÔÐÑÏÐÐÎζ†Ñ×ÖÖרÙÛÝÝÜÚØÙÙÚÙØÚÙ×Ø×ØÚØÔÖÙÜÚÓ¾£‘‰Œ—�Ÿ—„wttrqt€”«ÈØÛÞÞßàáááãääääåææççèèéêéêëëëëëìëëêëêëëëëëìììììííìëëëêëëëëìëëêéééèéèèèèæåâàÔÉÍÍËÊËÏÎÊËÌÒÖ²{s}mr Â¿ÈÏÈÃÈÍÍÍÏÁ¯¦®˜o‰ÆÓÎÑÒÒÏй…ÓÙÕØØØÚÜÛÛÜØÐÖÚÙÕÖ×ÖרØÖÕÔÙÞÜÎ²š†…“œŸ—Š�xvuuvyŠ¥ÃÖßßÙÚÞÞßàáââãääääææçççèéêêêêëëëëëëëëëëìííííìêéèåäáßÝÜÜÛÛÛÛÝßáåçéëìëêëêéééçåãâÖÊÈÈÍËËËËÊÊÍËÍ×¶yuzpršÂÊÎÌÆÆÉÏÏÎÆ´¨ª­–p‰ÉÕÐÓÕÑѼ…ÔÚØÙÙÙÚÛØÚÛÚØØÙ×ÖÖÕÕÖÕÕØÛÛÓ¿§•Œ�™¡�‘†}yqsxŠ£ºÏÜßÜÚÐÒÝÞÞßááâãããäååæççèèèêéêêêêêêëëëííìëéæâÞÚÕÐÌÇÃÀ½¼»º»¼¼½¿ÁÃÄÆÊÎÓÙÝáæéêëêéèäáÛÐÇÆÌÎËÉÌËÉÎÍÊÎ×´|txrr�¾ÌÉÇÄÁÌÑÏȽ·´¨¤˜o�ÉÐÐÓÒÒ½…ÔÛØ×ØÚÚÚØÙÙÚÚÚÙ×ÙÖÒÔÙÚÛÙË·Ÿˆ‡–  ˜Œ�}|zwx‚—³ÅÙãÙÖ×ÖØ×ÙÞßÞàááâãäääåææççèéééééééêëìíìêæáÚÔÏÉÄÀ½º·´²°®­¬«¬®®¯°±±±±²²²²µ·¼ÃÊÒÙßäæçæáØÐÍËËÊÉÉÆÄÌÍÍÍÎ׸~oyww›ÃžÄÅÊÒÐÈÂǽ¯ª±›nˆÄÑÐÓÔ»†ÔÚØØ×ØÙÙÚÙÙØ×ØØÖÕÓÕÛÜÑ¿¬›”—š¡ž‘…}{yx|ˆ“ªÇÚàÒÔÖÎÔÚÙØØÛÞßÞàâââãäåååæçççèèèèêêìíìêæàÙÒËÆÁ¿½¹¹¹·´²¯«§£Ÿœ™˜˜—˜š™––𡦍©©¥¡¢£§¯¸ÀÈÐÕÙÛÛÚØÔÒÐËÿÅÊÍÐÍÎ׺�r{yw™¿¹ÃËÉÍÐÈÁÀ»·³²· v•ÌÔÐÔ»†ÔÚרÙÚÙÙÙØÙ×ÕÕÖØÚ×ÒdzŸ’”ž¤¡—‹�zxxwzЧÇÖÝßÛÖÔÕÔØÚÚÚÙØÜßßßàáâãäååæåææçèèêêëíìéåÝÕÎÈÂÀ½»¹¶²¬¦¡œ—‘Œ‡�|ywvtsrqrrstw|‚Š“˜™™›žžŸ ¡¥§®´»ÂÉÎÓ×ÙÙÔÍÊÊËÌÍÎÏ×½zixsn”­½ÉÊÌÎÌÁ¼À¿¶º¾Æ¦yœÏÓÓ½…Ó×ÕÖÚÜÛÚØ×ÖÕÖÛÞÛγœ�“˜Ÿ¥ž�ƒ~|{ws|—´ÍÛÞÚØØ××ÙÙÙÚÛÚÚÚØÝàßßàáâãääåæåæçééêëéæáÜÕÍÇÁ»·³®¨¡›–‘Œ†�|vpljigffddddbadedfit{}~}�Š“œ¢£¥¤££§ª®´¹½ÂÉÏÒÕÖÕÒÐÏÊÌÖ¶|ozoj‡·ÉÍÑÎÎÅÀÁÀ½ÄÂÄ¿—rœÎÔ¼†Ñ×רØÙÙÖÕ×ÚÛÚÒ®—Љ‘ž¡™�€{yxvz}“¾ØàÞÚÖÒ×ÛÚÚÙÙÙÚÛØÚÙÙÞààààâããååææèééèäáÜØÓÎÆ¾´ª¡›˜’Œˆ…‚�}{yvsld_]\\\[Z[\^]YZ\[\bospjcdjs~Š”œ¥«¬¬®±³´³°°³·¿ÇÎÓÖÕÓÐÍÔ¾�kwpl—ÈÎÏÒÏÆ¿Ã½¾ÄÁÄŶŒnœÒ½†ÒØÙÙÕÕÖØÚÜÕŰŸ•�‹”š˜�ˆƒzrsy†—®·ÊáÜØÖÓÙÚÚÚÙÚÚØÙÛØØ×ØÚßáááâäææçççåãßÚ×ÔÒËû°£—އƒƒ‚��ƒƒ€}xtpmjgbYOLJNTSMLLJLMMOMLXeihaVV_hlqy€‡’£®³¶¶¶¶³°­«­°´ºÃÊÒ×Ôǽ´wh|nl›ÊÍÏÎÇÃÆ¿¼ÈÅÉÊÁ´�t©½…Ò×ÒÕÚÜÜÕÆµ¥˜—š�˜‘�‰„|wvv|›¶ÆÚÜÚ×ÖÖØ×ÑØÜÚØÛÜÚÚÔÊÒÚÕÖÛßáããääãâàÞÚÕÑÏÏÎȼ® ’‡ƒ‚‚„……ƒƒ�~zuqmiedbcdaZXXZ]YSSSNMNNNKIPVVXXRRV\djpwzwƒž¯±²±±²°¯®®­­®²¶ºÁÐ͹ͼvduqm›ÉÐÌÆÇÊÄÀÇÇÌÍÅÁº”z˜…ÔÚÕÙÙÌ·¡•’—šž—Œ…�~wsy‹�£ÅÙáßÛØÕÒ×ÚØÙÚÚÛÙÙÚÙÛÛÔÌÖÜØ×ÝáââßÜÚØÕÐÌÌÎÑÏŲŸ‘‰†‚‚…‡‡†ƒ€}zuqnkhfffhkmoqrqqrpliikjjfb_]ZYXRPOJGJNVaipy{|Š ­¯±³´³²²°¯°°°²´´ºÊÍË׺td{vo”ÄÏÄÃÉÆÄÃÀÅÌÈÁº¸�p†ÐÐÅ»ª›•”–š—Ž†|wvw}ˆ—´ÏÔÙâÚÕÖÔ×××ÚÚÚÛÜÛÚÚÜÕÒÛÙÚÛÚÛØÙßàÛ×ÔÑÏÍÌÎÑÓ̽¥Ž~~…ˆˆ‡††„�~zuqmkihkllmoqtttuutvxxvspmmoonmkgc`\XSH>?HOU^eny~„�¥¯°³µµ¶µµ¶¶´³²¶·¶ÃÍÎÍÖ¿vc~ul‘ÄÉÁÅÁÃÊ¿½ÉÌô·®€�³š‘•˜š›–Œ„zwux…˜¯ÉÛâáßÙÔÔ×ÖØÚÙÙÚÚÚÚÚÚÚÚÚÙØÚÛÛÙÙÙØÚÛÕÒÐÎÍËÏÔÔǰ—„xwz„ŒŒˆ„‚€|xtpmkjjjlqsrrrrsttustvvuuusqnmnpppokhgd`[TPRXVW^epx€ˆ•¦¬¯´···¹ºº¹····¾ÇÍÐÐÎ׿wfzuj’Äļ»ÀÎÊÆÌÌÈ»¹²—n˜ŠŽ™˜Ž€{yxuu~‘«ÃÓÝßÚÙØ×ÚÖÖÙØÛÜÙÕרÚ×ÕÙÙÚÚÜÛÙÙÙÚÙÚÛÙÓÏÍÌÍÓÖϽ¥�|wz}€ƒˆ‡…�{vronmkjlmqssstttuutsqpomiggikmmnopnnoonlkgdcbbfeWYcju€†Ž ®´¸»¼½¼»»º»º¹ÁÊËÌÏÒÐÍÙÃ}h|yo–Ä¿·¾ÊÍÍÏÌÊÀÀ»œoŸš‹‚zxywtz�©¾ÎÝÞÛÚÖËÖÛÙÙÚÛÙÛÜÛÚÚ××ÛÚרØÕØÚØÙÒÓÚÛÚ×ÑÍÌÌÑÔÑ»™�xx}�‚}}ƒƒ‚{qooonnooqrrqqqstsplifdd`YTTWZ_cfjnqommonkjjhfgjkodPVeq{€ŠŸ®·º¼¿ÁÀ¾½½¼»ÀÊÍÌÎÐÑÑÐÎÓ¾€o†}rœÀº¾ÈÆËÐÎÍÄÀ£u�ˆzvtrw•²ÐààÝÖÔÖÙÛØÚÛÙÒØÜÛÚÛÚÚÜÛÖÔÛØ×ÙÕ×ÚØØØ×ÚÛÔÌÊÊÐÖÒ¿¢~pw~�‚}~~}{}~~zplnppqssspnpqoljga^ZVTQOLMNRVZ\`djmomlllkjlljknpsuZK[jw‰œ«´¹½ÁÂÁÁÀ¾ÀÇÉËÍËËÐÑÐÑÏÈË»…s�|s�Ÿ³ÈÈÉÏÑÒËÄÅ«h„usx�‰¡½ÖàÕÑ×××ÙÙÙÛÝÜÚÛÚÚÚÛÙØÙÚÚÛ×Õ×ÕØÚÛÚÕÑÙÛÚÙÑÊÈÌÓÖŧ‘}oy€~||}}yssxzwpkpstuttpkmolfa`\WSPMGDGJMOSXZZ\_eglnmlklllmllnoqto\[eu��ž«³¸½¿¾¾¿¾ÂÉÎÍÌÍËÈÍÑÐÏÐÎÈÍĆl~vp}¦ÅÉÍÎÑÑÍÊÈ«_x|—­ÀÐÛÞÚÖÍÒÙÙÚÙØÚÚÙÛÙÛÜÜÚÛÛØÙÚÙÚÚÚ×רÙÚÛÖÎÒØÖÏÈÉÍÓ̲”…Œ„u{}~~}|{wrmkkmommptuvvusomlfa]\[YXZ[ZXTTVWTUZ[[\`dfjmnnlnpoookhfdgnlgjvƒ‘¢¬²µ¶º¿½¼ÃÊÊÊÇÉÍÍÌËÎÏÍÎÏÏÌÕÇŒo{~v’¿ÇËÍÐÑÎË̱o©ÀØààÞÚÕÕ×ÛÜÙØØØÙÜÚÚÛÚÛÚÚÚÚÛÙÚÙÙÛ×ÓØ×ØÚØØÚÕÑÖÌÇÌÏÒÕw‚‘”ƒx|}}}|xqkhfdcfilosuuvusqpmfcccdghkmnornhgec`^]__`eiiknnnprrqnha^ZY_lqr}‰—¦®°¯´·¯ºÉÈÉÊÉÅÄÌÌÍÌÌÏÍÍÍÐÏÍ×ËŒmzxu”ÁËÍÐÑÍÉηˆÚâÍÏ×ÕÖÙÙÚÚÙØ×ØÙÚÚÛÛÙÚÚÛÚÚÙÙØÙÙÙÙÖÓ×רØ×֨רÒÇËÒÓÁ�jq���xz{}{ytkeca[V_jmpstvvusqpniimpstuxxtqrtspommlheedbdgiknmnorsrnfc_XOO\oxƒ’¡­®®³±�~Œ¾ÍÇÈÉËÉÉÈËÍËÍÍÍÍÏÎÍËÓȉgyzo’ÃÐÏÑÍÇ͹ˆÕÖÁÌÚØØÙÙÙØÙØØ×ÙÚÙÛÚרÚÕÖÚØØ×ØØÖÓרÕÖÕÓÖ×Ö×ÖÎÊÎÏÄ–qhy‹uyzyzvmd`^UKLVemprtvuttqpomqtuy{yxxvsrqrpnoooonmkjighiknnnnpssmfc_XPIJ_}� ®²¯­�ŒqkƒµËÇÆËÊÈÇÊÌÌÊÊÊÍÍÌÎÍÊÔÊ‹k€xk–ÈÎÐÐÅÊ»†ÒÖÑÖÚØØÙÙÙØÙÚÓÓ×ÖØÛÚÙÚÚÓÔØÖÖ××ÖÔÒÖ×ÖÕÔÕØÒÒÕÑÍÏÏÈ¢}wgguyy{yupg_[WM@AQ^hmrtuuutsqqruxwy{{{vsrqpopnllmmnnnnnmkkknppoonqpkgcb]UKHSt™­²´»»ˆh{‚xk|®ÈÆÇÈÇÈÉÊËÊÊÊÎÍÌÍÍËÌÓÉŽm|yt™ÇÏÍÈʺ†ÒØØÙÙ××ÙÙ×ØÙØÑÓÖÓØÙØÙÚÔÖÖÔÒÕÙ×Ö××ÖÖÓÒÖÙÙÒÔ×ÐÍÑÎ¹Ž‚zgdq{zyumbZXQC9:LZbkqstuttusstw{{{|}}zsqpolllkgefikmnppmklmnpomnnmmjhhhbYNHJe™²¶ÂÌ͹“w|‚{mv¥ÃÆÆÈÊÎÍÊËÊÊÎÏÌÌÍÊÍÍÐÈ�m~�v�ÈÊÇϺ…ÓÙÕ×ØØØØ×ÖØØ×ØÙØÙÙØØØ×ÒÔ××ÔÕØØ×Ø×××ÒÒØØÖÕÔÕÑÌÏÆª�‰}mjs{yskc\WK;89CR_gnstuvvuuttx{}~~|ytrpmjgeda^\]`eilnonnonnnlllmlmkijjhd\SL`œÀËÎÐÑ×Μzu||nsžÄÊÈËÃÂËÊÊÊÌÍÌÌÍËÍÏÈÎÆ‹l�‚xŸÇÉͽ…ÕÚ×××ÙÙ×ר×ÕÖÙÙÙÙØ×Ø×Ö××רÙÖÖ×××רØ×ÕÔÒÓÖרÓÍÍ¿¨–�‡untyukb]\Q<9>AJUajostuvwwvvwz}~~~}ytqle^ZWTQQRUVW\bgjmnooooomlkmlllijmnmjieg¦¾´ÌÎÍÐÐ̯zp€st˜ÂÉÆµ¶ÉÉÊËÌÍÍÍËÈÍÐÌÈÍÆ”p|~w™Â˹‡ÕÙØ×Ö×ר××ØØØÖØØÙÙØ×Õ××ÕÕר×ÖÕÕÔÖÖÕÔÒÖØÛÛÑÄ®µÎ¿¬œ—�ypvuoe`cXD@AADKUblpstuwxxzz|}€|xtnf]XUSQMHEEJOQT[`eilmopoqpnmnnmmlnonlnpsr¢¾¬ÎÐÌÏÐÓØ¹‚o|‚rl�¿Ç¼ÂÇÇÉËËÍÍÌÌËÌÌÉÊÊÍÌŸt}~t‘¾¸…ÓÙØÖ××רØÙÙÙÙÚÙÕÖÖÕÙ×××ÖÖØ×Ö××ÕÒÓÖ×ÙÛ×ÍÀ± “†“¼Åµ§ž‘xvrqi`aM=?A?BKXckqtvvy{{{}€~|wrk`ZZ\YVURNH@>DKOTY]chlnpqqrqppqomnmiefkosyŸÐÐÌÍÎÑÐÎÓÖ¹‚m}�rk�¾ÆÄÇÄÇËÊËÍÍÍÎÌÇÆÈÌÈÌÓ©v|„v“±…ÑÖÔÔÔÕרØ×ÔÓÙÙØ×ÖÓÕØØ×ÖØ××ÖÔÔÔ×ÚÚ×ÑÆ¸¢•Ї‹Ž‘’¢¾Å¸¢Ž…�yplhZL=9@CADP[ckqvxz|~~€€~|xsj^[]]\ZZ[[VQH>D@=DKTY`lvy}€‚ƒ‚��€~zuoc^_`a`\VQNLC60126?JQV[afjntwz{|}p[OF?<=ETajvŸÊÎÍÍÏÐÏÏÐÐÎÍÕÔ¥v{„xh|¬ÃÅÈÈÉÍÎËÍÍÊÊËÍÎÌÍÏÎÒ«vy‡„ÐÙ×ØÚØ×ØÙÚÕÖÚÙ×ÖÔÓÒÔØÚ×ËÉ´¡”Šˆ‹‹•’ˆŠƒ{urnhgfdeadz›±¡‰ƒpQ<:??;?DKOU`nz�…‡ˆ‡ˆ‡†�|wtrppolljie`\XWVPICDO\a_^cknptwy~‚…€bHDA@FUZT`ˆÀÏÎÍÎÌÎÎÏÏÏÎÑÑÏÌÐÔµ€jx}psšÁÊÉËÌËÊÉÊÉÍÌÇÌÎÊÌÎÍÔ±†ÒÚØÖÖÔÓÔÔÖ×ÚÚÒȹ¦•Šˆ‰Œ�Ž‘��‰skfehjjjkklmnmnponnonpl_nƒ„€t`K>?ELOPW`p~ƒ†ˆ‰‰ˆ‰ˆ‡‚}~|zxtpnljhge`ZURSWXXY_hnpruy|€„……pJBE:-3CNeœÉÏÏÏÌÆÊËÏÏÏÐÐÐÍÏÑÑÜÇ‹kv~ro‘¾ÌËËÊÉËÌÉËÍÊÍÌÉÇÇÍϾ…Ï×ÕÓÔררÕÍñ£•�Œƒˆ™—––”Œ~smhfeiiillllmmmoppoopponnim~wrwztdNKSSPRXbt~…ˆˆˆ‰ˆ‰‹‹‰ˆ‡†…‚|yuqonkgb\WUUWWVY`gnpqvy|}…††^LLE879Di«ËÎÎÍÎËÅÈËÍÏÏÍÎÏÌÏÓÓÚÏœss{qlˆ¹ÍÉÊÉÉÌÍËÌÌÎÎÍËÊÍϺ„ÎÑÖÙÚÛÓõ§™’Ž’˜œ˜Šƒ�Œ…~xmgfhikllmnnlkmonmonoqqqpponp}rfkotwsleaWNQXhz�ˆŠŠŠ‰ŠŒŽŒŒ‹‹Š‡†ƒ�~yuokheb`_]^_``elrstwz}€�…‡ŠˆpYXWVSLQ{¶ÅÁÊÌÍÏËÍÍÎÎÎÎÎÒÍÌÒÓÎÔÓ«wr|wn…¶ÊËÉÉÌÎÌÊÌÎÎÎÍÐÎн†Ó×ÓÌ¿­œ�‰�”—�¢Ÿ›„ty{skjheflmppnmnnnnnprpopopsrqoopnuxiknquvwvpi`TRau‚‡Š‹��Œ�����Œ��Šˆ‡„|zuqonljjhhikotxzyz~~�‚�‚…ˆŒ‹|a^dhkad•ÂÆÀËÎÍÎÍÎÏÐÎÎÎÍÏÏÐÒÒÐÌÏÔ´|n|xm€«ÇÇÇÍÍÊÊÌÎÍÎÎÐÎ̼„ô ‘ŠŠ�—œ ¡›’Œ|qhfmmhhiillmmnmllljmnmnpppqrqpqpooooxsimku|vqwtqkggu‚‡ŒŒ‹ŒŒ�Ž�’’‘���ŽŠ‰‡‚€~{ywuusrstv{}|}}~�ƒƒ‚��…Š��‡lY\cmlt¬ÆÇÎÏÏÏÐÐÍÏÏÌÏÍÌÈËÑÒÒÐÒÐÐØ¹~lwxmx¢ÂÈÉËÉËÌÎÏÎÐÑÌÊ»o•‹Œ’š£¥œ“†{tols‚Œƒm\[abdfcdijjikkjmnmmnopqpoopnnoonzskno}ƒwlvwuutx�†Š��‹‰Š��’““”’‘‘�ŽŽ�‹ˆ†„ƒ�~~{z{|~~~~�ƒƒ�€€ƒ‹�’�|e^`ip‚µÈÇÏÎÎËÈËÍÍÊËÐÎÏÌÍÎÑÓÑÑÒÑÒÙÊkuxou�¾ÆÇÊËÊÌÎÎÐÐÎϺnœ ¥£��rlkq�œ¹ÌȾ°‡\PW[[Z\`cfhijjlllmnooppnopomlmm|vmqt‚ƒzv}|xzz|‚ˆŒŒŽ�‰ŠŽ�“””““•”’“”““��Ї†„‚‚€~€‚�€�€€�‚‚�€�…Œ“•“ˆtmkls…´ËÌÊÌÌ̾¾ÏÏÌÎÏÏÌÏÐÎÏÓÓÒÒÒÎÐÙÊ“puxor–½ÃÅÊËËÌÐÐÎÎÑ»w¥•„xqor}� ±ÀÈÍÙÑÁËÜÒ bJJLPTWZ]`cfffghijlmlllmnopnlk{znsx…‡…ˆ€y|}~~„ŠŒ�ŽŽŒŒŽ�“••”•˜˜˜˜˜—•“�ŽŒŠ‡†…„ƒ��‚ƒ�€€��„ƒ��‚ƒ‡‹�–•Ž~urrv‚«ÉÊÉËÌÌÊÌËÊÏÎÎÍÉÎÏÍÐÒÓÒÒ̾ÇÐ×Óœmsxps—½ÅÅÊÍÌÏÐÎÍѾbzopz‹Ÿ³ÄÑ××ÔÊÉÐÊ¿ÅÎÒÑ«nG>BIMPSVY]`abdfiklihkkjlopomx{pszˆ‹Ž‰eg|‚„‰Š‹���Œ��“””–˜š››š™—–•’‘�ŒŠˆ…„…ƒ�€�€€�‚ƒƒƒƒƒ†‰‹Ž•™“†{xy{�¢ÆÇÉËÍÌÏÒÈÆÏÏÐÍËÏÍËÎÑÏÐÏËÇÌÓÒØÒ›oqwpt•¸ÆÇÊÍÎÐÐÏѽb�œ´ÆÔÚÝÝÛ×ÔÒÏÏÕɾ¾ÄʪuR>9?FJMPTW[]_behjijkjjjlmons|rt|‰‹ŠqOcz„€‚†ˆŠŒ�ŒŒŽ‘•™›žž��œ›š˜˜—––”‘�Œ‡„†…„‚�€�€€~~€�„‰�‘™¥«¥’�}}„˜ÀÊÅÊÌÊÊÍÍÊÌÏÒÍÎÑÏÌËÎÎÏÎÏÐÏÐÓÑ×Ö¢omxupН¿ÄÉÌÎÐÏÏ»uÂÛàÞÜÛØÖÖÕÖÕÔØÑÁÀÂÿº½½¯‰`C58?EIMPQVX[_cffhhhiijjkkjxwv~‰‰z]Veuƒ‡‚€„†ˆ‰‹Š‹�—¡§©««¬¨¤£žš——™˜–•’ŽŠ‡………ƒ€~€~~}}€ƒˆ�š®»º³¤‰~~�‡�—¹ÊÉÊÊÊÌÍÉÉÎÏÎÅËÑÎÍÎÏÏÏÑÎÍÏÎÐÑÖÙ¬uo~vn‚§½ÄÇÊÏÏϺ|É×ÕÕÖÖØÙ×Ö×ÔÕÕÅÀÀ¿¾¾»½¹»­ŠkM938@EHJMPV]_aabcdfefhiiclxx~ˆ„kZaezŽ‹…€€‚„†‡ˆ‹”£¬­­®³µ³±ª£Ÿ›™š˜”‘ŽŒŠˆ…ƒ�}~~~|}€€„Š”§¸»µ°«•}�‡‹s~°ÌÉÇÉÉÉËËËÍÎÃÇÐÎÏÑÌÌÏÏÐÐÏÎÑÒÍÕÚµzlvsr†¦ÀÅÅËÏÑ»†ÔÒÑØÙÖÔÓÐÖÔÐÔʺ»¼¹¹Ã´³»´µ£ƒoXB327>CEJQUWZ\]_bdcdefgb_v{‡d]fm‡’Œ†€€�‚„†ˆŒš¦¨©«­°²²³¯¬ª¥Ÿ™—•‘Ž‹ˆ…ƒ�|}}{yy|‚‚ƒˆ“ ©¯²°¬¨™ƒ}€†ŒzeqžÃÉÅÈÊÊËÌÎÌÌÍÍÍÏÐÌÌÐÏÏÐÏÐÒÒÒÏÑØº‚ksxv�žÀÆÄÌÒ»ƒËÙרÕÑÒÐÍÔÑÑÔɽ»¼½»»²²½º¹´–|naN:018>CGJMRWY[^abbbcdcYm~}‡~ffr{Š�‹†���‚„…†�™¡¤¦¨«­¯­¯°²°­¦ž˜”‘�‹‡ƒ€€|}|xw{‚†…†ˆ�™Ÿ¡£©ª¦£™„}„‰ƒwddŒºËÉÈÇÊÎÍÉËÎÍÍÍÍÏÏÎÏÐÑÐÏÑÓÓÒÐÏ×È™ppwpuœÀÃÅκ‚ÌÜÙÙÔÏÖÖÒÏÒÕÓÁ»ÁÀ»°º½½½º´¥‹{siXD5/16=BGJNTVX[^^]`cd^ezyƒ�mn|{��‹‡ƒ€€‚„…†�–�£¦¦©««¨ª­¯¯®ª£š”�Œ‰†„€~€|xx~…Љ††Œ”™� ¢£ Ÿ™ƒy~�ˆwwja|®ÉÇÅÈËËËËËÍÏÏÎÍÌÌÍÎÐÎÍÐÒÐÐÑÏÏ×Ò¢olupl‹­¼Çµ†ÖÚÖ×ÖÕÖÖÔÐÔØÐ¼·¿ÀÂú°¸½»»³®Ÿ†}tj^N?3/29?CGKPSVYYZ]adb`s|‡|wvq†Ž‰„�‚ƒ…†‹“˜ ¤¥§¨§¤¦©ªª©§¢œ–�Ї†ƒ~�ƒ€ytvyzzz{~�ƒ‡�–››šœ—�v{€†{fmwp_n�ÂÊÅÈÌÍËÊÎÎÑÑÊÇÍÏÎÌÊÎÐÑÐÐÐÎÏÎÒÓ®whvneƒ²Ç²…ÕÛØ×ÕÕÔÔ××ÙÕÈÃÁÀÁÀ¿½²¸»¹¹¹¶±‘~zsmdWH90-3:ACHKOTVWZ^bb]e||„‰‹�…mu‹‹„‚‚�„†…‰�”™ž¢£¢¡¡¡¤¥¤£¢¡�–�‹‰†‚€�„„€ulmmgO?I[ltuz„�‘���{sy~…}a]hvsdh�ºÊÉÊËÊÈÉËÆÊÌÇÉÎÐÎÌÏÐÐÑÑÏÎÐÑÎÏÔ´|kysp‡°¶…ÔØÕÖÕÕÖÕÔØ×¿»ÇÅÄ¿¹½»¶·º¸¹º·£‡~zvogYLB6..4:>DIMPSUY\^_\q|u~Š”‘~o�‹„‚�‚†‡‡‹Ž�’•˜™™™™›�žžž�›˜“�‹‰…€~‚„ƒ‚xh`b[?.2;I^lqv}€€}qpx}…y`^\drvkd{ªÈÉÇÉÅÀÊÇÊÈÇÊËÍÎÏÎÏÏÏÑÐÏÐÐÎÌÍÔÁ‚hvsj~Ÿ†ÒÔÔ×רÙ××Ú͸ÀÇÄÅÄÄÀ¾¾¹µ¹¼¼½µ±™ƒ}xsnh_UK>2-.4;@CHMOSUV\\bxvr}Ž•‘‰ˆ‡„��ƒ…‡Š�Ž��Ž�‘“’’’“•••”‘�ŽŒ‡„�}}�ƒ…„€tcYTLEEE=?Ujoqsrtqjnv}„v^^[Y`qvobnžÄÇÄÇÆÊÐÍÈÍÌÊÉÌÌÎÏÏÍÏÐÎÎÍÍÏÐÐÖ‡jstmq†ÓÙÖØÙÙÚÙÚÚÎÈËÇÃÃÅÂÁ¿ÁÀ»¹½¿½·¶«�‚|wqnic\QE91,/7<8;d€„‡…�€zvx|zx{…”žŸš–—™™œ£¤ •†}‚�™ž�š•�Š…ƒ„‚€„…ƒ€}wqr{�vlq}uYRQNOONONKU^`][\]_`aa`^]]\Z[]Y_nxobx«ËÊÂÄÆÈÈÇÇÉËϼ†×ßÛØÍÉÎÑÐÐÒÎÌÉËÎÊÊËÌÍÉÈÉÈÈÈÉÉÈÉÈÅÅÀ»½½»°”‡…„€|zrsurnkhfc^WPF@>:;Z{‚†„€€{srxywv|Š˜Ÿž˜••—–˜š—‘��™  žžžš•”“‘�ˆ…„ˆˆ…ƒƒƒ~|‚ƒzss|mVVUSTROMPSQTSLHKU^___`b`_]ZZ\ZWYfsocl™ÂÉÅÃÅÅÅÈÅÅλ…×ÞØÕÌÎÐÑÅÇÒÑÑÌÊÍÌÎÍÊÊÉÉÈÈÈÇÉÉÉÉÉÆÅÅÀ½½¼½«�‹ˆ‚~{wvvuqolid]^XFAED2Eq~ƒ„�}toruusu€�šœ—��”•’“’Ž‘˜›Ÿ¡žšœ�™––“މ‡‡†„ƒƒ�~z|‚„{wvxgZ[XVVSTTVZSLJFDIV^_`_`cbba^\ZZ[YU_otgdˆ¶ÆÂÀÁÂÇÅÇ̹‡×ÝØÒËÏÐÐÈÈÒÓÎÎÑÎÎÐÍÊÉÊÈÅÊËÇÉÌÇÇÊÆ¿»½½½½¼¸œ‰‡„‚~{wurplieehZG@Vy]8]z�†…�}xqpstsqv‚‘™—‘����‘‘�”˜›��œ™˜™—’‘ŽŠ‡ƒ~zxvtsqqv~‚~xzu`ZYZ[WTVUUVUOJKPX_][ZWWZ\[]\\]\Z[YW]gunbw¦ÄÅÂÃÅÈÌ͹‡ÙÞØÍÌÐÑÒÓÎÎÏÌÍÏÐÎÍÎÍÎÍÆÇÈÁÄÇËÅÄÈȽ¶»¿½»º·¦‰†‡…„�~wturookkgZRB`�–XIq†‡„€{topttqpxˆ”–‘‹†ˆŽ‘•™šš�Ÿž›–�Ž�Œˆ„~|vrommmlou}�€}}n\ZXZ]YUVTSVVSQU[]YQOMNNOPLNX\]]]\\\YXbrqcl™ÄÊÃÃÇÉι†ØÞÓËÌÎÑÓÒÍÌÏÒÎÊÑÍËÎÌÌÌÊÌÈÂÆÈÉÅÇÉÊÄÃý»¼»µ­“ŠŒˆ†„vuz{uoljd^XDd€�H`y…Šˆƒ€zsopqqpuŠ�އ‚�…‰‘œžžœž¢ž—�‡‰Šƒ|xsqpmjjjiiow~�€�{e\[[[ZXVUTSWVU[]YTNIJMNOOKHJRQQY^[Z[]^Y_ntfdŒºÇÂÂÆÌ¸†ÖØÎÊÌÍÑÓÐÎÍËÐÎËÎËÍÍÍÌÌÍËÊÍËËÊÉÉÊÈÄÁÃÀ¾¾¼¹´©–�‹‡†�||{{vnjjga[Ps†~�]Qo‚Љ„‚|uopqpqx‚‹�„~|„Œ—£¨¢ žœš“‹ŠŠ†€yspnjgedfkry~€ƒ…wc]\]]\[YYXXWW[a]UPQQNOLNNJJKJINX^]]]]]\Z[fsnez©ÄÅÄ˺‡ÖÔÉÊËÏÒÔÒÏÍÉÌÏËÃÉÎÏÎÍÍËËËËÌÍÎËÊÉÈùÁ¿¾¿¿º¹¤ŽŒ‰‡ƒ€�~ytpmomdcd€Šw‘�Tg}ˆŠ„�€€|uqqpoqy„‰„{y~†Œ“ž¥¨¥ œ˜˜—’�Œ‡€zqjfc`bgms{}�Ž‹sa]]]]_^\\[[[[^\VTRQSROPQONONLJNXadb_]YY[YYewzfk–¼ÆÉ¹†ÖÕÈÉÉÎÒËÈÎËÍÎÍËÉÌÍÎÍÍÌÉÊÈÉËʾÃÊÉËËÆÁ¾ÀÀ¿¼º»´™‹‰…€€‚€~ysqsphfkƒ�x—jg{„Š…ƒ…ƒ�}tqrqmrz‚‚|wx�‹’•�¥¥¦¡š˜˜•‘�ŽŠ„�}{zwy|€‚~‚�Œn^]__]^^]]^^^]YUUTTSRSRTTPRSRPKKRZaa_^]ZY[\]csyfbƒ°Æµ†ÔÒÊËËËÒÇ¿ÊÇËÐËÌÍÊÍÍÌËÊËÉÆÈÊȹÀÉÉËËÉÁÁøµ·ºª�ˆƒ�‚„‚�€zuvslfg‚�}m•†k|�‡‰‡‰Š…‚|vvsoory~xvz‡�‘–œ£§¥Ÿ™–••––˜š���›——™›šŠ�ˆ„i\_``^^__^__[YWWXWWWUTSSRPQRSRMKO[bca_^[YXX_\Yjxncx¢°†ÓÎÊÐÑÎÏÑÎÏÍÂÃÅÈÌÎÊËÎËÈÊÊÇÈÉÊËÊÊËÊÉÆÄÄÃú³´¸¸ �‰‚‚…‚€~~}zwpkg}Ž‚h†�xxƒ†ŠŠŠ��‡€ywvsnow|}}yw}‰�‘—žŸ ¡žž�œœž¢§¨¦ œš›šš›•…€„|d[]_````_]]^^\[ZZ[[YWVUTUSOQSRMIP]a^_`_^ZWWXYYXarrioˆ„ÏÌËÑÔÓÒÓÒÏÎÉÀÂÈÌÈÇÍÐÎÊÊËÊÈÈÊÊËÊËÊÉÇÅÅÃÄÁ¿¼¾»½²—�ˆƒ„‚€}|~~{tomzŠƒii—“w�……†‡�‘Žƒ{xwupntz||xvw�ŠŽ‘“–—� ¢¢¢¡¢¢Ÿ ž™••–““•™Ž�†zb_^``__`_^^^_]\Z[][ZYWWWXYWWUSPNPQNLPW[]]]ZWUWWR[ktlcƒËÌËÍÒÒÒÑѽ»ÑÎÊËÊÅÉÏÏÎËÂÈËÊÊËÊÊËËËÊÇÅÅÆÄÀÀÂÀ¾¾»¥�ŽŠ‡ƒ€~||~|yspy†‚l[uœ‡}‡†�‚†Ž‘‰}wvtrmouxusssxƒˆ�“—œ¢¡¡£¢¢¡›˜—˜”�“–””–�†‰t_``_^^_^^_`_]]]]][ZZ[XUZZYYYUQTVQJFKRWZ\_c`ZXVVWWWaok†ÊËÎÎÐÑÎÐѾ¹ÊÏÐÌÈÍÍÎÍËËÄÉÊÌÍËÊÊËËÊÊÈÇÆÆÁ¼¼¼»ÀÀ½´™�ŽŠ„‚€~}}z{wrw……p`\…œƒ‡Œ…€‚ˆ‘Ž�ywtqmlnqsqnmnsz„ŒŽ“˜œžžžžŸžš˜”“��Ž�•’�‹‹‡jY[]^_^^^_`a`__^^^[]\]ZZ\\YTUVSRQNKIMUYZ\Z\a^[[Z[^ZU^lƒÉÌÌÔÓÐÐÌËÍÍËÌÏÍÌÐÏÎÍÌÌÍÍËÌÌÊËÌÊÉÉËÊÈÇÆÄ¾»¹»ÁÁ¾¿®”��Šˆƒ‚ƒ�~}zww‚†tc[i˜–‡�‹ƒ�ƒ‹�Š~xurmhhkqnihiou}†‰ŒŽŽ•˜™�š——™“ŽŒŠŠ‹ŽŒ‰ˆ‰€bWY\\\\]___^_a_]^^\]]]]_][ZWWXVRPNLJHHHIRXX[]]]\[]^\Z^€ÈÏÍÐÐÌÎÇÉÍÌËÌËÌÑÑÐÏÎÎÌÊËËÌËÌÎÌÊÉÉËËÉÉÇǽ»Á¿¾¾¼¸¡‘��ˆ…„„„‚�€{{ƒve_Y{ ’ˆ‰…‚€‚ŠŠƒzrpnhgfikifhmos|„ˆŠˆŒ�“˜”’“‘‘�Œ‡‰Œ‡‡‡††t]YZ[\ZZ\\\[[]]_```^\^][\\[ZZXUSSRQPOONLFLW[\]^^[Z\\]\W�ÉÏÒÉÈÎÎÍÌÍÊÊÎÍËÏÒÐÏÍËÎËÈËËËÌÍËËÌËÌËÉÈÈÆ¿ÀÄþ»¿»º±˜�Žˆ†‡†„„„ƒ€~ƒzf\Xb‰ –Œ„€��„†}okljgdchiggklouz�…†…‡‰ŠŒŽŠˆ‹�‹‰Š‰†…‚ƒ�iZ[YXYY[[YYXXXX[^_^]^][XYZXXYXVUSRRTVUSPMOY^^^^]]^_`][Y‚ÎÒÏÎÐÓÎÎÎÎÐÏÍÎÐÑÒÏÌÍÍÊÈËËÊËËËËËËÌÍÊÈÉÉÇÄÅþ¼¾¿¾¼¼¨’�ŠˆŒŠ†……„ƒ€�„|g\XWg†—š•ˆ�}{‚|ofeggedecacgjkpu{ƒ††ƒƒ‡‡�€†ˆŠŒˆ„€{‚}_WXVVUVYYYWVSRTUXXXYYYZZZZVUVVVUVVVTUVRMJP[`_^__`aaa_\ZƒÎÒÑÓÓÑÎÎÍÌÏÏÎÎÐÏÏÎÍÏÎÅÉÏÍÍÎÍÌËÊÉËÈÁÅÉÊÇÅÄÅþ¿½¿¿·›ŽŒŠ‹‹„…‡†„€}xk_XV]dp�‹‘ˆ}uxzyl]Z`ccefb^_`cgjptx}ƒ†ƒ~~yvx|€…ˆ‚yxx~pVVVVUUTUXXVWVQPQQQPRSXZXUUTQQSXXXXWSRXWRLR]^\]a`[Z]`_]]ƒÌÎÍÒÑÏÏÐÎÍÎÌÊËÍÏÌÊÍÍÏËÉÌÎÎËÌÌÌËÌÌÆÃÆÉÉÆÄÃÄÆÃÁ¿¿Á¿¿«’ŽŒŠ‹‡ˆ‰‡ƒ†|pxn`XWZ_`cb{‰€vquwr^SW]`aedb_\]befjotz‚~xuutsruyyuppsv`QSSSTVUTSPPRRNLLNSTSTWURQUWURTYXUVVTTVTQQY`^[[WVWTX__^X…ÑÑÍÒÒÐÑÑÐÐÎËÈÈÉÍÍÍÍÎÎÍÆÈÎÎÌÌËÌËÌÎËÊÅÆÈÄÃÅÃÅÄÀ¾ÀÁ¿º¡��‹‹‹ŒŠ‡‰“ŒrsqcWTV]^ba`}‡}rosuiSMV]^^adb]YY]`ejlsy|}xsqqljjmmmkjniOKPNMONOLJJHGHIJJNUVWXXWUXZ[\ZZYXXWUTVTQRY`][\YPOX\ZZ\[W„ÏÔÓÔÓÑÒÒÎÌÌËÍÎÉÉÍÏÎÍÌÎÊÉÌÎÏÎÌÌËÌÌËÊÈÉÇÆÆÆÅÄÄÁÁÂÁ¾¾²–‘�Œ‹ŒŠˆ——•ŽxodYTTYZ[fai…†xjjpn]NMU[Z[]`^ZWX[]achmpppplhddeddefihcXHFJKJFDBCFHGIKLJLPPPQQUYZZYYYXVXZYVTUTU\a_VW[YVXY^^[]WS‚ÍÔÕÕÑÐÐÊËÌÍÍÎÏÍÎÏÏÎÌËÍÌËËÊÉÍÎÊÉÌËÉÉËËÇÇÄÄÈÆÆÀ¿ÃÂÿ½º¦–’ŽŒŒ‰‹™˜‹›™}bYVVWZVZhaq‰ƒqahphZOMTYXWZ[ZXWWWY\`bdfeffbabaaaafaVYe^JBHGDBACDGHJKMNOPQQPQQVXWVSTVUTUVUUVWY`bZTUYYVXYYZYXQK„ÏÒÏÐÏÏÏÇÊÍÎÐÏÎÎÐÑÐÍÎÎËËÌÌÉÉËËÊÇÇËÉÇÉÊÇÃ»ÄÆÁÃÃÁÃÃÂÁÀ¼¶Ÿ’’��Љ‹™‹‰ž�tWRWVVWT_ebzˆ�k\dg_XOKRXYWVUUTSVWXZ[\]ab`_aa`]\]UIEKVffNAEHIJKGEGHHLPRRSVWUPOSTRSUZZY[[XWXZ\ab_[[YVUSWWXWTPO…ÏÑÌÍÍÐÑÎÌÏÏÏÑÑÎÏÎÌÍÏÏÍÌÌÊÌÈ¿ÈÌÉÈÌËÈÊÇÁÅÄÄÁ¾¿ÆÅÅÃÁÁ¾»«”’‘Œ’’‚…‘†‰›‹gROTTVST_bf}†�dT^]YVMIOUTTQOOPRTVUVVVY\ZYZXTSUJ;:@GMWghN>EKMMLLONNKMOOOOMJJKORSTWYZ[[[[^``bddfdc\XVVYXWVYZRƒÏÔÍÊËÎÐÎÍËÊÍÑÒÍÌÌÌÍÍËÌÌËÉÍÈÁÉÉÉËÉÇÆÉÆÃÆÇÆÆÃÃÂÀÆÅÃÂÁ¼º²œ‘�—¦¥Žs…�…‡��iQJOSQQU_`k�…|^PVUVUMHJOUTPPPNPUSPQRQRSPNNSdeA5:=BKS[jmO:?CEGIMPSPOMIGHIIIKOQRSSSSVXX\bccfggefd]YWYZWUSW\W„ÐÑÌËÉÈÇÇËÉÇÊÎÏÌÊÎÏÏÎÍÎÍËÈÍËÉÆÆËËÇÆÆÉÇÈÆÆÈÈÄÄÂÂÈÆÄÃÀ¼·²¨––¤­©ŸŒ‚‹�ƒƒ‚ymTEGOOPV]ar�€y[KKMRSNIHKPRSQOOPPMMMOOMNR]n~sC68>CIRZ`npP9?DHJKKJNSVYZ[TLLNPOOQSTUY\WVUTWTRSRTVX[`fc„ÎÏÌÊÇÊÊÌÉÅÍÎËËÌÌÌËÍÍÍÏÍÍÊËÊÊËËÉÉÊ̽¼ÊÈÈÇÇÄÀÁÅÈÇÆÈ¾­·½º¯¨¥£•—¦ª­£‘‡‹—Œpkfpym^LDGNUXn|ka]YNDCLQOKJKMLKMPUYZ]elpsw|pB039CJRY_fmptw‚¡”ubJ=AGKLJKOQUXXSIGJLMMKPQNPSRQOUXSPW^^`d_ZX\‚ÎÐÌÊÈÌËÌÌÌÐÏÍÎÊÈÌÎËÉÎÍÌÏÑÎÈÊËÌÉÊÉÉÂÂÈÆÉÇÅÄÄÆÊÉÆÅžª©©¦¥¦¥¤™’›œ££�†…“�}vifhmofVLEHO\~‚k[YYULILPPQOOOMPSVY]bhlotwzqC.28CLUY^jv|}b@]~ЇtQA>CJLKMORSSUSKLRPQQOOOQPU\[^]YXXY^^YZ`hZƒÍÌÉËÉÉÊÌÌÍËÊÊÍÌÉÍÍËÌÎÍËÌÏÌËËËËÊÇÄÇÍÉÅÅÈÉÇÇÇÈɽ±°¯®¬£�—𢡡›Š�‰„„‚ƒ‡��…vspddllmh[RJIbyfYXWUPMOSVSRPQSWX\aehlptxyrE-18DNUZj}‰Œ‡a:0EJJHIJKIJNUWT[n Ã­‹¡°¢€vªßÔËÚØÂ�ÇÉÃÃÄÈÊÉÈÄËÍËËÊÉÇÆÉÍÍÉËÌÊÉÌÌÆÄÄÀÀ¾¼½¸°­¬©©­´°£™•˜�¥§¡‘�‡‘¢˜�–˜¡¦“„wuy}zsrx~qg^ahnpw}‰’™Ÿ�zdMHKKOSVXZ]^adfhknqqmjN.,5CXw„ˆ‹ˆ†wSAADHLH<6Hh}�žŠ\DA=:CJP`et‡“|Pa|ÀéÀ¡§—wv±ÓË»¸»·¡‚ÈÇÃÃÆÊÌÍÌÅÉËÅÂÈÈÉËÌÌÍÉÇÈÉÆÈËÈÆÄÀ»¶³¯®°«®¯«­³°¤•�‘— §§¡’ŠŠ‚�—�ŒŒ˜ž£¬†{Œ•�…‚‹‘yu‚‹……„|ww‚�š£ª‘h\[\^abbdfhkosvz{xusY:17Sz����‘‹•{PLNQRSTL>?Nd�¤œreŽ’‰�’�·ÉÆ©“�ŠÄÙÁ»»²­½Æºµ·µ²³§‚ÇÆÆÄÁÇÉÊÉÉÉÉÄÃÉÉÊÊÊÉÈÉÈÈÈÈÊÇÁ¼·³¯¬©«¯®«­¯±¶µª›ŽŠ�›£¦§¤—xƒ‡zŒ�Šˆ�—~la\gg`ZU[^RNMX`ahplbZPIT[p{ZEDCCDDDFHHJNQSUURQP?,()C_c[MS_\ib?8:;;<=@=2+/=Usv_e’|ggdi{„�|ˆsm�€|}|ƒ~vy{{|}~vƒˆˆ‰‰ˆ‰‰‰‡ˆ‚yzzzyvptzxuvuvwxxyyzxvuppuvuy€~yyxvwxxw{xvsrvxxwvwuuysswxxutywqsttuxssustuqvƒˆˆ‰‰ˆ‰‰‰‡ˆ‚yzzzyvptzxuvuvwxxyyzxvuppuvuy€~yyxvwxxw{xvsrvxxwvwuuysswxxutywqsttuxssustuqv†’““’”“““‘‘‡xzxusuwwuuvwvwwxyxxwtsstuvz}}{yyzxxxzzyxzyvrrwxwwvuuuxttxxwuvzvpsutvxstttvtu†’““’”“““‘‘‡xzxusuwwuuvwvwwxyxxwtsstuvz}}{yyzxxxzzyxzyvrrwxwwvuuuxttxxwuvzvpsutvxstttvtu€‚„ƒƒ‚€��€�xurswxvuvvvwxvvxxutsstux||{yxyz{|}~~~~{vstwxxuuvtuxstwwvuuytqusswxrtutuv€‚„ƒƒ‚€��€�xurswxvuvvvwxvvxxutsstux||{yxyz{|}~~~~{vstwxxuuvtuxstwwvuuytqusswxrtutuv||||zxyzxvtstvwwwwvvwwvwwvurrsuvx|}{yxyz{}€€€€�ƒ�ztswxvuvutuxstwwvuvxtsutsxxqttsv||||zxyzxvtstvwwwwvvwwvwwvurrsuvx|}{yxyz{}€€€€�ƒ�ztswxvuvutuxstwwvuvxtsutsxxqttsv}||{xxyvrsvwttvxxvuvwvvvsrrsuw{}|zyyzz|~€€€€€‚„~tsxwvvuutvxsuwvvuvysrtttzvrusu}||{xxyvrsvwttvxxvuvwvvvsrrsuw{}|zyyzz|~€€€€€‚„~tsxwvvuutvxsuwvvuvysrtttzvrusu}|{wstsruwvtruwxxutuvutrrtuw{|{zyxyz|~€€€€€„€tsvwwuutsuwruwwuuwxrqstwztrtv}|{wstsruwvtruwxxutuvutrrtuw{|{zyxyz|~€€€€€„€tsvwwuutsuwruwwuuwxrqstwztrtv€|wurrvwwvuvtswxxwutsrrstuy||{xxyyy{~€€€€€€€€€€€ƒ~sswxvuutsuvruvvvuxxrqtvxztrv€|wurrvwwvuvtswxxwutsrrstuy||{xxyyy{~€€€€€€€€€€€ƒ~sswxvuutsuvruvvvuxxrqtvxztrv~wutvxyxuuvvrtwxxurqqrtvy||zyxxxyz|€€€€€€€�‚zsswwvutssuvsuvwwuxxrruvyyru~wutvxyxuuvvrtwxxurqqrtvy||zyxxxyz|€€€€€€€�‚zsswwvutssuvsuvwwuxxrruvyyru~zyvxxvuuvvtsvwvsqrstv{}|zxyxxyyy|€€€€€€€€€€€€€‚wtsvwvusstvvruwwwvyyquvvxwt~zyvxxvuuvvtsvwvsqrstv{}|zxyxxyyy|€€€€€€€€€€€€€‚wtsvwvusstvvruwwwvyyquvvxwtzutvvvuvvustusqprty||{yyzyyyyyz|€€€€€€€€€�{vtrvwvtsttvwsvwwwvzwruuuwxzutvvvuvvustusqprty||{yyzyyyyyz|€€€€€€€€€�{vtrvwvtsttvwsvwwwvzwruuuwx€zuuxwvvvvtqqqqrsx}}zxxyyyyyzyy|€€€€€€€€€€~{xtrvwussttxvrvxwvvyvruuux€zuuxwvvvvtqqqqrsx}}zxxyyyyyzyy|€€€€€€€€€€~{xtrvwussttxvrvxwvvyvruuux~ytvwwvvvsppqrtw{|zxyyyyxxyzyyz€€€€€€€€}z{wtsvutstuvxuswxxwvzurtuv~ytvwwvvvsppqrtw{|zxyyyyxxyzyyz€€€€€€€€}z{wtsvutstuvxuswxxwvzurtuv~xtwwwusqqrruy{{yxyyyxxyyyyyzz}€€€€xxzwtrtuuttvuvvsvxxxwzsqtw~xtwwwusqqrruy{{yxyyyxxyyyyyzz}€€€€xxzwtrtuuttvuvvsvxxxwzsqtw~vuxvsqpqsvz|zyyyzyxxxyxyyyyy{€€€€{vy{xtruvuuvutwurwxyxxxrrw~vuxvsqpqsvz|zyyyzyxxxyxyyyyy{€€€€{vy{xtruvuuvutwurwxyxxxrrw|uvtrqstw{{zxyzzzyyxxxxxyzzxx}€€€€€€wuy|xtruwvwuttwusxzzyxwrv|uvtrqstw{{zxyzzzyyxxxxxyzzxx}€€€€€€wuy|xtruwvwuttwusxzzyxwrv}urrsvz|{yxyxyy{zxxxxxyyxwuuz€€€€€€zvvy{wsquwwvtttwttyzzwwwt}urrsvz|{yxyxyy{zxxxxxyyxwuuz€€€€€€zvvy{wsquwwvtttwttyzzwwwt~rsw{}}zxxxyyyzzyxyxxxwuttuy€€€€€€€€€}wvvy{wtruxwvuttxttyzwuwy~rsw{}}zxxxyyyzzyxyxxxwuttuy€€€€€€€€€}wvvy{wtruxwvuttxttyzwuwy|v|}{zzzyxyxyyyyyxywvtttvvu}�€€€€€€€€xuvvy{xtrvxvvttvwruyxwux|v|}{zzzyxyxyyyyyxywvtttvvu}�€€€€€€€€xuvvy{xtrvxvvttvwruyxwux|{zzzzyxyyxyyyyxvttutuuux{€�€€€zvvvwy|xtruvwuvwvusuxwvw|{zzzzyxyyxyyyyxvttutuuux{€�€€€zvvvwy|xtruvwuvwvusuxwvw€{zz{{yyxxxxyxwutttuutvx{}}€~~}|zzyyyyz{|}~€|uvvwwy{yuruxuvxvtvstwwy€{zz{{yyxxxxyxwutttuutvx{}}€~~}|zzyyyyz{|}~€|uvvwwy{yuruxuvxvtvstwwy|z{{zyyxwwxvsstuuuuw{|||{{~}|zxvutsrqqqrqrrstuvy{}}xwvvvvx|zuruuuxvtuvstwy|z{{zyyxwwxvsstuuuuw{|||{{~}|zxvutsrqqqrqrrstuvy{}}xwvvvvx|zuruuuxvtuvstwy{z{zyyyxusssstttwz{||{z{{}€€€~}zwtrqpnkkkiiiijijknpqqqrstuvwwwwwy|yvrsvwuttuvsty{z{zyyyxusssstttwz{||{z{{}€€€~}zwtrqpnkkkiiiijijknpqqqrstuvwwwwwy|yvrsvwuttuvsty{z{yxwvsqrtsstwy}}{z{{{{{~€~}|yuqljjiihfegjklnooomknmihilopqrrstvwwy{yusuyvtttvwrw{z{yxwvsqrtsstwy}}{z{{{{{~€~}|yuqljjiihfegjklnooomknmihilopqrrstvwwy{yusuyvtttvwrw{zyvtssssttuy{||{z{{{{zz{~€~~}|zxurmfcabceghknprtsstutttrrohddfmrrsssrrsvx|yusvwuuuuvts{zyvtssssttuy{||{z{{{{zz{~€~~}|zxurmfcabceghknprtsstutttrrohddfmrrsssrrsvx|yusvwuuuuvts~wussststvy{|}{zz{{{{{{zz{~}{zywsmgjib`bcgkprrqqrrqqqqqrssstqkeemprrstssrsvz~zutuuuvuusv~wussststvy{|}{zz{{{{{{zz{~}{zywsmgjib`bcgkprrqqrrqqqqqrssstqkeemprrstssrsvz~zutuuuvuusv|ttsttuxz{{{zzzzzzzz{zzzz{{yyvpjdabhkeejnrsqppqrrqpqopopqqqqrpjemqqsssttsuwyz}yvsstvvvsu|ttsttuxz{{{zzzzzzzz{zzzz{{yyvpjdabhkeejnrsqppqrrqpqopopqqqqrpjemqqsssttsuwyz}yvsstvvvsu|tsuw{|{{zz{z{{zzzzz{zzz{zywskdca``hpnpttsrqrrsuuuutsrqqqpppoppjlqrrssttuvwyxy{yussvwxtv|tsuw{|{{zz{z{{zzzzz{zzz{zywskdca``hpnpttsrqrrsuuuutsrqqqpppoppjlqrrssttuvwyxy{yussvwxtv|wz{|{zzz{{zz{{zzyyz{yzzywtqqicbabdmtssssqqstuwxyyywuusrrrrqqppmnqrrrstvvvvxxwy|yvstwwuv|wz{|{zzz{{zz{{zzyyz{yzzywtqqicbabdmtssssqqstuwxyyywuusrrrrqqppmnqrrrstvvvvxxwy|yvstwwuv€|{{z{zzz{z{zzzzzyzzzyyywrosoecccipttsssrstvvvvwxyyxxvttsrssssspprrrssuwvwvxxxxy|yvstxvw€|{{z{zzz{z{zzzzzyzzzyyywrosoecccipttsssrstvvvvwxyyxxvttsrssssspprrrssuwvwvxxxxy|yvstxvw€{z{z{zyy{{{zyzzzzyyzyxxsnomfddgmuvutssrstvvutttuvwxxwvutttttvvtrrsvwwttvwvwwxxxy|zurtuw€{z{z{zyy{{{zyzzzzyyzyxxsnomfddgmuvutssrstvvutttuvwxxwvutttttvvtrrsvwwttvwvwwxxxy|zurtuw€|zzzzzyzzzyyyyzzzyyzyxupppkefjqwyvttssstvvwutttuuwxxwvututtvvwwssvz{ywuuwvvwwxwwxzytrsw€|zzzzzyzzzyyyyzzzyyzyxupppkefjqwyvttssstvvwutttuuwxxwvututtvvwwssvz{ywuuwvvwwxwwxzytrsw|zzzzzzzyzyyyyyyzyxxwutprrmglrwzxttttstuvwvuuuuwvuwxvvututtuuvwuvxxz|zwutvvvvxwwwwzyuqu|zzzzzzzyzyyyyyyzyxxwutprrmglrwzxttttstuvwvuuuuwvuwxvvututtuuvwuvxxz|zwutvvvvxwwwwzyuqu{yzzzzzyxyyzzyyxwvuuvvvrrtpkpvyywtuttuuuuutuvwvwxwxywuuvvuttttuuwvwxz{ywutuvwwwwwwwzytt{yzzzzzyxyyzzyyxwvuuvvvrrtpkpvyywtuttuuuuutuvwvwxwxywuuvvuttttuuwvwxz{ywutuvwwwwwwwzytt{zzzxyzyyyyyxwuuuvwwwvvusttqrxzzwtutuuuuttuuvwxy{zyy{yvutvvvusstwwvxxx{zwusvwwwwwwwwyxv{zzzxyzyyyyyxwuuuvwwwvvusttqrxzzwtutuuuuttuuvwxy{zyy{yvutvvvusstwwvxxx{zwusvwwwwwwwwyxv{zzzyyyyxvuuuuvwwwvvvvvvvsssvyyzwuuuuuttstuuxxz{|{wxzxvttwxxxvrsvwwwwwxzzwusuwvvvwwvvxy{zzzyyyyxvuuuuvwwwvvvvvvvsssvyyzwuuuuuttstuuxxz{|{wxzxvttwxxxvrsvwwwwwxzzwusuwvvvwwvvxy{yyyxxvutuuwwwvvvwwvwwvvvttuuwyxvutututssttuvvvuvxwxxutttvyyzyttxwvwxwwwzzwvsvwwwwxwvvy{yyyxxvutuuwwwvvvwwvwwvvvttuuwyxvutututssttuvvvuvxwxxutttvyyzyttxwvwxwwwzzwvsvwwwwxwvvyzxxvuuuuuwvuuvvvwwwvwwwwvvrrstuuuvuuttssrrstssstuwxwvsstttxwzzuuwwwwxwwwxzzxvttvwxxwwvxzxxvuuuuuwvuuvvvwwwvwwwwvvrrstuuuvuuttssrrstssstuwxwvsstttxwzzuuwwwwxwwwxzzxvttvwxxwwvx~vuvuvuvvuvvvvwvvvwwwwwwvwuqsssttvwvttsrrqrrrrsstttuuttutttxvuxuuwwwwwwwwxwxzywttwwwwwvx~vuvuvuvvuvvvvwvvvwwwwwwvwuqsssttvwvttsrrqrrrrsstttuuttutttxvuxuuwwwwwwwwxwxzywttwwwwwvx}vvvuwwyzxvvwvwvvwvwwwwvvxuqsrsssvwutttsrrqqrrsssstttttuttuwzwwuuwwwwwwwvwxww{zvttvwwwvx}vvvuwwyzxvvwvwvvwvwwwwvvxuqsrsssvwutttsrrqqrrsssstttttuttuwzwwuuwwwwwwwvwxww{zvttvwwwvx}vwyz{zzyyxuxyyywvvvvvvvvwupsspqsuvusstsrqqrrrsssttttttutttuxxxvvwwwwwwwvwwvvy|ywttuvwwy}vwyz{zzyyxuxyyywvvvvvvvvwupsspqsuvusstsrqqrrrsssttttttutttuxxxvvwwwwwwwvwwvvy|ywttuvwwy~|}}|{zyxxwvvy{{yxwwvvvvvvvqsqostuvuttsrqppqrrssstttstvvttrruxwxuuwvvwwwvwwwvwy{zwtstvvy~|}}|{zyxxwvvy{{yxwwvvvvvvvqsqostuvuttsrqppqrrssstttstvvttrruxwxuuwvvwwwvwwwvwy{zwtstvvy}|{{{zxwxwwvux{|{yyvvwvuvvsrppustvwuurponnoqrstttsstuvutrposwvyxtuvvwwvvwwwwwyyzzwtrtux}|{{{zxwxwwvux{|{yyvvwvuvvsrppustvwuurponnoqrstttsstuvutrposwvyxtuvvwwvvwwwwwyyzzwtrtux}|zzzzwxwvvvtuyy{|{xwwwuuussrrtrsvwvurppoonoqrtttssuvttsqporvuxywttvwvvvwwvwwyxwzzwtrsw}|zzzzwxwvvvtuyy{|{xwwwuuussrrtrsvwvurppoonoqrtttssuvttsqporvuxywttvwvvvwwvwwyxwzzwtrsw}{zz{ywywuuttuxyxy||ywwvvuttrssrsuwvvtrqqpppprsssssvuuussrqrutwwwwutuvvvwwvwxxxwxyyvtqu}{zz{ywywuuttuxyxy||ywwvvuttrssrsuwvvtrqqpppprsssssvuuussrqrutwwwwutuvvvwwvwxxxwxyyvtqu|z{{zwxxwwuuvvvyyyy{}|zxwwuustsrsuwvvvttsrrrrrsssrtvuvyxvutuuswwtvxvtuuvwwvwwxwwwxwywtt|z{{zwxxwwuuvvvyyyy{}|zxwwuustsrsuwvvvttsrrrrrsssrtvuvyxvutuuswwtvxvtuuvwwvwwxwwwxwywtt|{||zxxwwwwwvvtxzyyyy{|{zxvuuusqruwwvvvutsssssssssuvvwwwutvxurvwutuxwttvvvvvwwvwwwwwxwv|{||zxxwwwwwvvtxzyyyy{|{zxvuuusqruwwvvvutsssssssssuvvwwwutvxurvwutuxwttvvvvvwwvwwwwwxwv€}|}|yxyyxwwwwvuvyzyxxxy|{yvuvvtrqswwvuuvtsttssssttuvvvwwtuy{wtvvuutuvxutuvvvvvvwwwwwvxy€}|}|yxyyxwwwwvuvyzyxxxy|{yvuvvtrqswwvuuvtsttssssttuvvvwwtuy{wtvvuutuvxutuvvvvvvwwwwwvxy€~}}|yyyyywwvvvvuwzzyyxwxvvuutvusprvvvttutstssrqrtuuuuuvvuvyzyvvvuutttwxvttuuuvvwwwvvvux€~}}|yyyyywwvvvvuwzzyyxwxvvuutvusprvvvttutstssrqrtuuuuuvvuvyzyvvvuutttwxvttuuuvvwwwvvvux}||{yzzyxwwwvuvvvyzzyxxxusstuxxsqsvvvutttssrqppqstuutttuwwxyxxxvututttuwwuuvvvuvwvvvvuw}||{yzzyxwwwvuvvvyzzyxxxusstuxxsqsvvvutttssrqppqstuutttuwwxyxxxvututttuwwuuvvvuvwvvvvuw}|{zyzzyxwwwvvvwuwyzzzxxutrrux{urstuvusttsspoqrstuutsstuwwwwwxwuuuutttutvwutuvuvvvuuutx}|{zyzzyxwwwvvvwuwyzzzxxutrrux{urstuvusttsspoqrstuutsstuwwwwwxwuuuutttutvwutuvuvvvuuutx€}|zy{{zzyxwwwwvvvvxzzzywtssruz}vssuvuusrrsronqstuvvvvvvwxyyvvxwuuuuuuutttuwvuuuvvvutttx€}|zy{{zzyxwwwwvvvvxzzzywtssruz}vssuvuusrrsronqstuvvvvvvwxyyvvxwuuuuuuutttuwvuuuvvvutttx€}|zz{{zzzxxxwvvwvvwz{zywvsssuz|xsstvuvtrqrqnnpstuxxwwxwwxz}wsxwuuuuututttttwwttuvvutttw€}|zz{{zzzxxxwvvwvvwz{zywvsssuz|xsstvuvtrqrqnnpstuxxwwxwwxz}wsxwuuuuututttttwwttuvvutttw€|{y{{{zyzxxxxwwwvvux{yyxwuttvxzzussuuvvsqrrompssrsuvvwwvw{}wswwttuuuuuuuttssvwusuvuuttw€|{y{{{zyzxxxxwwwvvux{yyxwuttvxzzussuuvvsqrrompssrsuvvwwvw{}wswwttuuuuuuuttssvwusuvuuttw€}{z{{{zzyyxxxwwvvvvvxyyxwvttvvxzwrstuvwuqrrqopqppprsuvvvvxzwsvusttuuuuutttsstuvutuutssw€}{z{{{zzyyxxxwwvvvvvxyyxwvttvvxzwrstuvwuqrrqopqppprsuvvvvxzwsvusttuuuuutttsstuvutuutssw}zz{{{zzyyxxxxxwvwvvwzyxwwvvvtz}xtsstuvvtqrqponnpqrsututtuwwuutsssttttuuuttttttvvutsssv}zz{{{zzyyxxxxxwvwvvwzyxwwvvvtz}xtsstuvvtqrqponnpqrsututtuwwuutsssttttuuuttttttvvutsssv€|z||z{{zyyyyxxxxvwwvvyzyxwvuus~„ztrssuuvurrrpmllnqstuuuuuuvvvusssssstttututttusruvusssv€|z||z{{zyyyyxxxxvwwvvyzyxwvuus~„ztrssuuvurrrpmllnqstuuuuuuvvvusssssstttututttusruvusssv{z}|{{{zyyyxxxxxwwwvuwzzzywvvu~†~tqqrstuusrsroklmnpsstsuuuuuvussttssttttsttutttsstvusrv{z}|{{{zyyyxxxxxwwwvuwzzzywvvu~†~tqqrstuusrsroklmnpsstsuuuuuvussttssttttsttutttsstvusrv€{z||{zzyxyyxxxxxxwwwvvyzzzyyyv~‡„vppqrsttsrrrpnmllmnooprssuuvuttttstsssssssttttssstuurt€{z||{zzyxyyxxxxxxwwwvvyzzzyyyv~‡„vppqrsttsrrrpnmllmnooprssuuvuttttstsssssssttttssstuurt{{}|{z{zxyyxxxxxxxxwvwwz{{zzzx~…ˆ}ppqpqstsrqrrqonmlllmpqrruvvuutttstssrsrsrrtttsttsrsut{{}|{z{zxyyxxxxxxxxwvwwz{{zzzx~…ˆ}ppqpqstsrqrrqonmlllmpqrruvvuutttstssrsrsrrtttsttsrsut||}{zzyzzyxyxxxxxxxxwwvx{{{{{y}„ˆ‡voqporsssqrsrqpnmmmmpqqrtvvttttttssrssrsssssttttrqrrs||}{zzyzzyxyxxxxxxxxwwvx{{{{{y}„ˆ‡voqporsssqrsrqpnmmmmpqqrtvvttttttssrssrsssssttttrqrrs|||zzzzzzyyyxxxxyxxwwwwwz||||z{ƒ‡Š‚rnppqrrsrrtssrqpoooprrruwuttttuutsstssssssssuttsrqqp|||zzzzzzyyyxxxxyxxwwwwwz||||z{ƒ‡Š‚rnppqrrsrrtssrqpoooprrruwuttttuutsstssssssssuttsrqqp€}|{z{zzzzyyyxxxxyxwxxxwvx{|||{{‚ˆ‰Š€tppqqrrrrsssrrqqppqrstvwustsstsstsstssssssrssssrrqq€}|{z{zzzzyyyxxxxyxwxxxwvx{|||{{‚ˆ‰Š€tppqqrrrrsssrrqqppqrstvwustsstsstsstssssssrssssrrqq€}}{{{zzzyyyyyyxyxxxxxxwvwz|||||�‡ˆŠŠ‡zpqqrssrsssssrrrrsstuvvtsttttsssssssssssssssstsrqr€}}{{{zzzyyyyyyxyxxxxxxwvwz|||||�‡ˆŠŠ‡zpqqrssrsssssrrrrsstuvvtsttttsssssssssssssssstsrqr€}|{{{zyzzyyyyyyyxxwxxxwvwx{}|}�…‡‰ŠŠ…soqrttsrsssssrssstuuzyutuuuttttssrsssssrrrsssrrqr€}|{{{zyzzyyyyyyyxxwxxxwvwx{}|}�…‡‰ŠŠ…soqrttsrsssssrssstuuzyutuuuttttssrsssssrrrsssrrqr€}{{{zzzzzyyxxyyyxxwxxxxwwwy||}‚‚€…ˆ‰ŠŠ~oprsutsssssssssstuyƒ„{vvvuttststssrsssrrsrrrrqqr€}{{{zzzzzyyxxyyyxxwxxxxwwwy||}‚‚€…ˆ‰ŠŠ~oprsutsssssssssstuyƒ„{vvvuttststssrsssrrsrrrrqqr€}z{{{zzzzzzyyyyyxxxxxxxxwvwz|„„ƒ�„‰ŠŠ‰znqsttttstsststutx‚‡‡wvvuuttttssrrrssssssrrqqr€}z{{{zzzzzzyyyyyxxxxxxxxwvwz|„„ƒ�„‰ŠŠ‰znqsttttstsststutx‚‡‡wvvuuttttssrrrssssssrrqqr€}zz{{zzyzyyyxxxxyxxxxwwwwvvz€‚‚„ƒƒ‚ƒ‡Š‹†umstrststttttutw{~€…‰ˆwuuutttsrssrrssrsrrqqqpr€}zz{{zzyzyyyxxxxyxxxxwwwwvvz€‚‚„ƒƒ‚ƒ‡Š‹†umstrststttttutw{~€…‰ˆwuuutttsrssrrssrsrrqqqpr|zz{zzzzzyyyxxxxyyxxwwxwwvv}ƒ„ƒƒƒƒ„„‚…‰‹‚rnssssssuuuttuwz}€…‰‹‡wruvtttsstsrsrrqrqqrqrqq|zz{zzzzzyyyxxxxyyxxwwxwwvv}ƒ„ƒƒƒƒ„„‚…‰‹‚rnssssssuuuttuwz}€…‰‹‡wruvtttsstsrsrrqrqqrqrqq€}{|{zzzzzyyzyyyxyyyywxyxx{}�„„…„„„„†„‚ƒ‡Š~nqstrrstttttuw{}†ˆˆ‰}sstuuussstttsrrrrrrpquu€}{|{zzzzzyyzyyyxyyyywxyxx{}�„„…„„„„†„‚ƒ‡Š~nqstrrstttttuw{}†ˆˆ‰}sstuuussstttsrrrrrrpquu€}{{{zzyyyyyyyzzxxxxzz|~~‚‚ƒ„„„„…„„†……ƒ‚ƒ€roqsssttssstuw{~€…††ˆ~vuqtuutsttsssrstsnfjzƒ�€}{{{zzyyyyyyyzzxxxxzz|~~‚‚ƒ„„„„…„„†……ƒ‚ƒ€roqsssttssstuw{~€…††ˆ~vuqtuutsttsssrstsnfjzƒ�€|{|{zzyxyyyxyyxwxy{�‚‚‚ƒƒ�‚ƒ„„„ƒ„………„……‚�}xtrrtsssssuvvz}~ƒ…ˆ†zuuuvtuwuussnejr~p[\owrp€|{|{zzyxyyyxyyxwxy{�‚‚‚ƒƒ�‚ƒ„„„ƒ„………„……‚�}xtrrtsssssuvvz}~ƒ…ˆ†zuuuvtuwuussnejr~p[\owrp€|||{zzyxxyxxyyy{}�‚ƒ‚‚‚‚„ƒ�€ƒ„…†„ƒ„ƒ„„„…ƒ‚�ƒ�ztstuuuuvwxy}ƒ‰‰ƒyvvvwxw{{ytujZhrpbZ]_][^€|||{zzyxxyxxyyy{}�‚ƒ‚‚‚‚„ƒ�€ƒ„…†„ƒ„ƒ„„„…ƒ‚�ƒ�ztstuuuuvwxy}ƒ‰‰ƒyvvvwxw{{ytujZhrpbZ]_][^xjmhikjhgmkv„‚‚‚ƒƒ…†…………„„„„„ƒƒ„„„„††……„…„ƒƒƒƒ„„„ƒƒ„„„„…„ƒƒ„„„…†…„„„ƒ„…………………†…†„„††ˆ‡ƒ„xjmhikjhgmkv„‚‚‚ƒƒ…†…………„„„„„ƒƒ„„„„††……„…„ƒƒƒƒ„„„ƒƒ„„„„…„ƒƒ„„„…†…„„„ƒ„…………………†…†„„††ˆ‡ƒ„sPQNQKMNOSUg†‚ƒƒ„……………………„„„„„„„………†………„ƒƒƒƒƒƒƒƒƒƒƒ„„ƒ„……„„ƒ„………†…„„ƒƒ„……†……†††††……††ˆ†ƒsPQNQKMNOSUg†‚ƒƒ„……………………„„„„„„„………†………„ƒƒƒƒƒƒƒƒƒƒƒ„„ƒ„……„„ƒ„………†…„„ƒƒ„……†……†††††……††ˆ†ƒ}ststtustvuy„ƒ„……„„„……„„„„„ƒƒ„…………………„ƒƒƒ‚‚‚‚‚�€�€€‚‚ƒƒ„…„„ƒƒ„………†…„„ƒ„„……†…†…††…†„……‡ˆ…}ststtustvuy„ƒ„……„„„……„„„„„ƒƒ„…………………„ƒƒƒ‚‚‚‚‚�€�€€‚‚ƒƒ„…„„ƒƒ„………†…„„ƒ„„……†…†…††…†„……‡ˆ…�ƒƒ‚‚‚‚ƒƒ‚ƒ„…………„ƒ„„„„ƒƒ„„„„…†…………„„ƒƒƒƒƒ‚‚���€€�€€€€���ƒ„„„ƒ„„„„…………„„„……†………††…†…„…†ˆˆ�ƒƒ‚‚‚‚ƒƒ‚ƒ„…………„ƒ„„„„ƒƒ„„„„…†…………„„ƒƒƒƒƒ‚‚���€€�€€€€���ƒ„„„ƒ„„„„…………„„„……†………††…†…„…†ˆˆ�‚‚‚‚ƒƒ„ƒ„………††„ƒƒƒ„„„„„„……†……………„„„ƒƒƒ‚���������€€�€�€�€‚„…ƒ„„„…„…†…„…„„………†…†††…†…„†‡†�‚‚‚‚ƒƒ„ƒ„………††„ƒƒƒ„„„„„„……†……………„„„ƒƒƒ‚���������€€�€�€�€‚„…ƒ„„„…„…†…„…„„………†…†††…†…„†‡†€��‚ƒ„………………„†…ƒƒƒ„…„„…………………„„„„ƒƒƒ‚‚�€�€€���������€���€€‚„„„„„„……††……„„„……†††††……†……††€��‚ƒ„………………„†…ƒƒƒ„…„„…………………„„„„ƒƒƒ‚‚�€�€€���������€���€€‚„„„„„„……††……„„„……†††††……†……††€�ƒƒ…†……„……„……„ƒƒƒ„………†…………„„ƒƒ„ƒƒƒ‚��€€���€��€������€�€�€�‚„„ƒƒ„„…†††……„„………††…†………†………€�ƒƒ…†……„……„……„ƒƒƒ„………†…………„„ƒƒ„ƒƒƒ‚��€€���€��€������€�€�€�‚„„ƒƒ„„…†††……„„………††…†………†………�ƒ„„……………„„„……„ƒƒ„……†……„„„„„„ƒ„„ƒ‚‚�€�€�€€���€€�€�€��€€�€€€€ƒ„„„„„„…†††………………†††………„…†……�ƒ„„……………„„„……„ƒƒ„……†……„„„„„„ƒ„„ƒ‚‚�€�€�€€���€€�€�€��€€�€€€€ƒ„„„„„„…†††………………†††………„…†……�ƒƒ„……„„„„„„…„ƒƒ„…†………„ƒƒƒƒƒƒ„„ƒƒ‚����€��€€����€€€���€€�€€€€‚„„„„„„…††††………„„…†††…†…„…†…�ƒƒ„……„„„„„„…„ƒƒ„…†………„ƒƒƒƒƒƒ„„ƒƒ‚����€��€€����€€€���€€�€€€€‚„„„„„„…††††………„„…†††…†…„…†…€�„……„„„„ƒ„„„„„…………„„ƒƒƒƒƒƒƒƒƒƒ‚‚����€€€€€€����€€€���€€€€€€€�ƒƒ„…„„……†…††……„„„…………†……„††€�„……„„„„ƒ„„„„„…………„„ƒƒƒƒƒƒƒƒƒƒ‚‚����€€€€€€����€€€���€€€€€€€�ƒƒ„…„„……†…††……„„„…………†……„††€‚„…„ƒ„„„„„…†…………„ƒƒƒƒƒƒƒƒƒƒ‚ƒƒ‚���€€��€�€€€��€€€�€€€�€€€€€€€ƒ„„„„„„…††…††…„„„………†……………†€‚„…„ƒ„„„„„…†…………„ƒƒƒƒƒƒƒƒƒƒ‚ƒƒ‚���€€��€�€€€��€€€�€€€�€€€€€€€ƒ„„„„„„…††…††…„„„………†……………†€‚„„ƒƒƒ„ƒ„……†…„„ƒƒƒƒƒ‚ƒƒ„ƒƒ‚‚ƒ‚�€�€€�€€€€€€���€€��€���€€€€€€€�ƒƒƒ„„„…††…†…†…„„„„……†……………€‚„„ƒƒƒ„ƒ„……†…„„ƒƒƒƒƒ‚ƒƒ„ƒƒ‚‚ƒ‚�€�€€�€€€€€€���€€��€���€€€€€€€�ƒƒƒ„„„…††…†…†…„„„„……†……………€ƒ…„ƒƒ„……………„ƒ‚ƒƒƒƒƒƒƒƒƒƒƒ‚‚‚‚�€€€€€�€�€€€€�€�€€€�€���€€€€€€€€‚„„„„„……††……††…„„ƒ„„…………†„€ƒ…„ƒƒ„……………„ƒ‚ƒƒƒƒƒƒƒƒƒƒƒ‚‚‚‚�€€€€€�€�€€€€�€�€€€�€���€€€€€€€€‚„„„„„……††……††…„„ƒ„„…………†„€„„ƒƒ„……………„‚‚‚‚‚‚‚ƒƒƒƒƒƒƒ‚ƒƒ‚€€€€€€€€€€�€€���€��€€���€€�€€€€€�„ƒ„„„…………†…†‡†…„„ƒ„„……………€„„ƒƒ„……………„‚‚‚‚‚‚‚ƒƒƒƒƒƒƒ‚ƒƒ‚€€€€€€€€€€�€€���€��€€���€€�€€€€€�„ƒ„„„…………†…†‡†…„„ƒ„„……………�…„„……†…„ƒƒƒ‚ƒƒ‚‚‚‚ƒ‚ƒƒƒ‚ƒ‚ƒƒ‚€€€€�€€€€€�€���€€��€����€€�€€€€€ƒ„„ƒ„………„…„†‡‡†…„„ƒƒ„…††…�…„„……†…„ƒƒƒ‚ƒƒ‚‚‚‚ƒ‚ƒƒƒ‚ƒ‚ƒƒ‚€€€€�€€€€€�€���€€��€����€€�€€€€€ƒ„„ƒ„………„…„†‡‡†…„„ƒƒ„…††…‚…………„„ƒ‚‚‚‚‚‚‚‚‚ƒ‚ƒƒƒƒƒ‚‚ƒ„ƒ�€€€€�€€€€€�€�€€€€€��€€€€€�€€€€€€€‚„„„„„……„„„…‡‡‡‡…„ƒ‚ƒ„††„‚…………„„ƒ‚‚‚‚‚‚‚‚‚ƒ‚ƒƒƒƒƒ‚‚ƒ„ƒ�€€€€�€€€€€�€�€€€€€��€€€€€�€€€€€€€‚„„„„„……„„„…‡‡‡‡…„ƒ‚ƒ„††„‚……„ƒ‚‚ƒƒƒ‚‚‚ƒ‚‚‚ƒ‚ƒƒƒƒ„„„„„�€€€€€€€�€€€�€€�€�������€€€�€€€€€€�„„„„„„„„„ƒ„††‡‡……„ƒ‚ƒ…††‚……„ƒ‚‚ƒƒƒ‚‚‚ƒ‚‚‚ƒ‚ƒƒƒƒ„„„„„�€€€€€€€�€€€�€€�€�������€€€�€€€€€€�„„„„„„„„„ƒ„††‡‡……„ƒ‚ƒ…††‚„ƒ‚‚‚‚‚ƒƒ‚ƒ‚‚‚‚‚ƒƒƒƒ„…„„„„‚€€€€€€�€€€€��������€��€�€€€��€€€€ƒ„…„ƒ„„„„„„…†††††„ƒƒ„„……‚„ƒ‚‚‚‚‚ƒƒ‚ƒ‚‚‚‚‚ƒƒƒƒ„…„„„„‚€€€€€€�€€€€��������€��€�€€€��€€€€ƒ„…„ƒ„„„„„„…†††††„ƒƒ„„……��‚‚‚‚‚‚‚‚‚‚‚‚‚ƒ‚ƒƒ„„„„„„ƒƒ�€€€€€€€�€€€€€�€€���€€€�€�€€€��€€€€‚„„„„ƒ„„„„………………††„ƒƒ„…„��‚‚‚‚‚‚‚‚‚‚‚‚‚ƒ‚ƒƒ„„„„„„ƒƒ�€€€€€€€�€€€€€�€€���€€€�€�€€€��€€€€‚„„„„ƒ„„„„………………††„ƒƒ„…„��‚‚‚‚‚‚‚‚‚‚‚‚ƒƒ„„„„„„„„ƒ‚‚€€€€€€€�€€€€€€������‚‚ƒ‚ƒ‚‚��€€�€€�„ƒ„„„ƒ„ƒ„…„„…„„†‡†„„„„„��‚‚‚‚‚‚‚‚‚‚‚‚ƒƒ„„„„„„„„ƒ‚‚€€€€€€€�€€€€€€������‚‚ƒ‚ƒ‚‚��€€�€€�„ƒ„„„ƒ„ƒ„…„„…„„†‡†„„„„„€���‚‚‚‚‚‚‚‚‚„„„„„„„„ƒƒ‚‚‚�€€€€€€€€€€€€€€��‚ƒ……†‡‡‡ˆˆˆˆ‡‡††…ƒƒ�€�„„„„„„„ƒƒ„„„†…ƒ…†††„„„ƒ€���‚‚‚‚‚‚‚‚‚„„„„„„„„ƒƒ‚‚‚�€€€€€€€€€€€€€€��‚ƒ……†‡‡‡ˆˆˆˆ‡‡††…ƒƒ�€�„„„„„„„ƒƒ„„„†…ƒ…†††„„„ƒ€�‚‚‚‚‚‚‚ƒƒ„„…„„„„„ƒ‚‚‚‚‚‚�€€€€€€€€€€��‚ƒ…‡ˆ‰‹Œ����ŽŽ����‹Š‰ˆˆ‡‡†……„„„„„ƒƒ„„„†…„…†††……„ƒ€�‚‚‚‚‚‚‚ƒƒ„„…„„„„„ƒ‚‚‚‚‚‚�€€€€€€€€€€��‚ƒ…‡ˆ‰‹Œ����ŽŽ����‹Š‰ˆˆ‡‡†……„„„„„ƒƒ„„„†…„…†††……„ƒ€�‚�‚‚ƒƒ„……„„†…„„ƒ‚�‚��‚‚��€€€€€�€�‚‚„†ŠŒŒ�Ž��‘���ŽŽ���ŽŽŒŒŽŽ�ŒŒŠ‰‰ˆ††…ƒƒƒ„„„…„ƒ„…†…†…„„€�‚�‚‚ƒƒ„……„„†…„„ƒ‚�‚��‚‚��€€€€€�€�‚‚„†ŠŒŒ�Ž��‘���ŽŽ���ŽŽŒŒŽŽ�ŒŒŠ‰‰ˆ††…ƒƒƒ„„„…„ƒ„…†…†…„„€�‚‚ƒ„„……„„„„„‚ƒƒ‚‚‚‚‚‚‚‚‚€€€€��‚ƒ„‡ŠŽ‘“’’‘‘�����Ž���ŽŽŽŽ�ŒŽ�’‘�‹Š‰‰‰‰ˆ‡†…„„„„„ƒ„…………††…€�‚‚ƒ„„……„„„„„‚ƒƒ‚‚‚‚‚‚‚‚‚€€€€��‚ƒ„‡ŠŽ‘“’’‘‘�����Ž���ŽŽŽŽ�ŒŽ�’‘�‹Š‰‰‰‰ˆ‡†…„„„„„ƒ„…………††…€‚ƒ„„…„„„„ƒ‚‚‚‚‚‚‚�‚�‚‚‚‚�€�‚‚ƒ„†‰�Ž�’““’�����‘‘‘‘’’’““’““‘ŽŽ�’�ŒŠŠ‰‰‰ˆˆˆˆ„ƒƒ„„„„……………‡†€‚ƒ„„…„„„„ƒ‚‚‚‚‚‚‚�‚�‚‚‚‚�€�‚‚ƒ„†‰�Ž�’““’�����‘‘‘‘’’’““’““‘ŽŽ�’�ŒŠŠ‰‰‰ˆˆˆˆ„ƒƒ„„„„……………‡†�ƒƒƒ„„„ƒ‚‚‚�‚‚‚‚‚‚‚‚‚‚‚�‚�‚ƒƒ„‡‹Ž‘’�Ž‘‘��ŽŽ�������‘‘‘’““””“’‘‘’‘‹‰Š‰‰ˆˆˆˆ†ƒ‚‚ƒ„ƒ„……„„…‡†�ƒƒƒ„„„ƒ‚‚‚�‚‚‚‚‚‚‚‚‚‚‚�‚�‚ƒƒ„‡‹Ž‘’�Ž‘‘��ŽŽ�������‘‘‘’““””“’‘‘’‘‹‰Š‰‰ˆˆˆˆ†ƒ‚‚ƒ„ƒ„……„„…‡†�„ƒƒƒ‚‚���‚���‚‚‚‚‚‚‚‚‚��‚ƒ„†‰Ž‘’’’�Œ�Ž�Ž���Ž�����Ž���‘’““’’’’��ŒŠŠ‰ˆˆˆ‡†„„ƒƒƒƒƒ„„…„ƒ„†…�„ƒƒƒ‚‚���‚���‚‚‚‚‚‚‚‚‚��‚ƒ„†‰Ž‘’’’�Œ�Ž�Ž���Ž�����Ž���‘’““’’’’��ŒŠŠ‰ˆˆˆ‡†„„ƒƒƒƒƒ„„…„ƒ„†…�ƒ‚����‚‚����‚�‚‚‚‚‚‚‚‚‚ƒ„…‡ˆ�‘’’‘�Ž‹ŒŽŽŽ�Ž��Œ‹Š‹‹Œ����‘’’‘‘’���ŒŠŠˆ‰ˆ†…„„ƒƒƒ„ƒƒƒ„„„ƒƒ„…�ƒ‚����‚‚����‚�‚‚‚‚‚‚‚‚‚ƒ„…‡ˆ�‘’’‘�Ž‹ŒŽŽŽ�Ž��Œ‹Š‹‹Œ����‘’’‘‘’���ŒŠŠˆ‰ˆ†…„„ƒƒƒ„ƒƒƒ„„„ƒƒ„…����‚��‚‚‚�‚‚‚‚‚‚‚‚‚‚‚‚ƒ„†ˆˆŠ��‘�ŽŒŒ�Ž�ŽŽŽ����ŒŒŒ‹‹Œ�����‘‘‘��Ž�‹ŠŠŠˆ†…„„„„„„ƒƒƒƒ„„„„ƒ„„����‚��‚‚‚�‚‚‚‚‚‚‚‚‚‚‚‚ƒ„†ˆˆŠ��‘�ŽŒŒ�Ž�ŽŽŽ����ŒŒŒ‹‹Œ�����‘‘‘��Ž�‹ŠŠŠˆ†…„„„„„„ƒƒƒƒ„„„„ƒ„„€�‚�‚��‚‚‚�‚‚‚ƒ‚‚‚‚‚‚‚ƒ„†ˆˆŠŽ���Œ‰‹�ŽŽ�ŽŽ���ŽŽŽŽŽ�ŒŒŒŒŽ���‘��ŽŒ‹ŠŠˆ‡„„„ƒ„„„„„ƒƒƒƒ„„„„„„„€�‚�‚��‚‚‚�‚‚‚ƒ‚‚‚‚‚‚‚ƒ„†ˆˆŠŽ���Œ‰‹�ŽŽ�ŽŽ���ŽŽŽŽŽ�ŒŒŒŒŽ���‘��ŽŒ‹ŠŠˆ‡„„„ƒ„„„„„ƒƒƒƒ„„„„„„„€�‚�‚��‚‚�‚‚‚‚‚‚��‚‚�‚‚„‡ˆˆŒŽŽŒŠ‡ˆŒ�ŽŽŽ���Œ�����ŽŽ�Œ�ŒŒ�����ŽŒ‹Š‰‡„ƒƒƒ„ƒ„„„ƒ„ƒƒ„ƒƒ„„„„„ƒ€�‚�‚��‚‚�‚‚‚‚‚‚��‚‚�‚‚„‡ˆˆŒŽŽŒŠ‡ˆŒ�ŽŽŽ���Œ�����ŽŽ�Œ�ŒŒ�����ŽŒ‹Š‰‡„ƒƒƒ„ƒ„„„ƒ„ƒƒ„ƒƒ„„„„„ƒ€�‚����‚‚‚‚‚‚‚‚‚‚‚‚‚‚ƒƒ…‡‡‡‹Œ‹ˆˆ‡‰ŒŒ�ŽŽ�Œ��Ž�������Ž��Ž����ŽŒ‹‹‹ˆ„ƒƒƒƒƒƒ„„……„ƒƒ„ƒƒ„„„„„„€�‚����‚‚‚‚‚‚‚‚‚‚‚‚‚‚ƒƒ…‡‡‡‹Œ‹ˆˆ‡‰ŒŒ�ŽŽ�Œ��Ž�������Ž��Ž����ŽŒ‹‹‹ˆ„ƒƒƒƒƒƒ„„……„ƒƒ„ƒƒ„„„„„„€��‚‚‚�‚‚‚��‚‚‚‚ƒƒ„ƒƒ„ƒ…‡‡†‰‹Šˆ‡ˆ‰‹���ŒŒ��Ž������ŽŽ���ŽŽ����ŒŽ��ˆ„„ƒƒƒƒƒƒ„…„„ƒƒƒƒ„„„„„„„€��‚‚‚�‚‚‚��‚‚‚‚ƒƒ„ƒƒ„ƒ…‡‡†‰‹Šˆ‡ˆ‰‹���ŒŒ��Ž������ŽŽ���ŽŽ����ŒŽ��ˆ„„ƒƒƒƒƒƒ„…„„ƒƒƒƒ„„„„„„„€���‚‚‚‚‚‚‚‚‚ƒƒ„„„„ƒƒ„ƒ„…‡††ˆ‰‡‡ˆŠ‹�Ž��Œ�Ž�����Œ‰‰‹ŽŽŒŒŽ��Ž�����‰ƒ„„„„ƒƒ„ƒ„„„„ƒƒƒ„„„„„„„€���‚‚‚‚‚‚‚‚‚ƒƒ„„„„ƒƒ„ƒ„…‡††ˆ‰‡‡ˆŠ‹�Ž��Œ�Ž�����Œ‰‰‹ŽŽŒŒŽ��Ž�����‰ƒ„„„„ƒƒ„ƒ„„„„ƒƒƒ„„„„„„„€���‚‚‚‚‚‚‚ƒƒ„„ƒƒƒ„„ƒ„„„„„…†ˆˆ‡‡ˆ‰‹ŒŽ����Ž���Ž�Œ‰ˆŠ��‹Œ��‘���ŽŽ�Š„ƒƒ„„ƒ‚ƒ„ƒ„„„ƒƒƒ„„ƒƒƒ„„€���‚‚‚‚‚‚‚ƒƒ„„ƒƒƒ„„ƒ„„„„„…†ˆˆ‡‡ˆ‰‹ŒŽ����Ž���Ž�Œ‰ˆŠ��‹Œ��‘���ŽŽ�Š„ƒƒ„„ƒ‚ƒ„ƒ„„„ƒƒƒ„„ƒƒƒ„„€�‚‚‚‚‚ƒƒƒƒƒƒƒ„„ƒ„„ƒ„„„„„„…‰��Œ‰‰Š‹Œ����Ž��ŽŽŽ���ŽŽ����‘‘’��‹‰‹Žˆƒƒ„ƒƒƒƒƒƒƒ„ƒ„„„„ƒƒƒƒƒƒƒ€�‚‚‚‚‚ƒƒƒƒƒƒƒ„„ƒ„„ƒ„„„„„„…‰��Œ‰‰Š‹Œ����Ž��ŽŽŽ���ŽŽ����‘‘’��‹‰‹Žˆƒƒ„ƒƒƒƒƒƒƒ„ƒ„„„„ƒƒƒƒƒƒƒ€�‚‚ƒ„„„ƒƒ„ƒ„„„„……„„…„„„„„†‘–’��‹ŒŒ�ŽŽŽ�������������Ž�‘‘‘‘‘Ž�Š‹‹†„„„„„ƒ„ƒƒƒ„„„„„„ƒƒƒƒƒƒƒ€�‚‚ƒ„„„ƒƒ„ƒ„„„„……„„…„„„„„†‘–’��‹ŒŒ�ŽŽŽ�������������Ž�‘‘‘‘‘Ž�Š‹‹†„„„„„ƒ„ƒƒƒ„„„„„„ƒƒƒƒƒƒƒ�‚ƒƒƒƒƒ„ƒƒ„„„„„„……„„„…„„„ƒˆ–•��ŽŒ‹Œ������’‘‘‘‘��������‘�‘‘’���Љ„„„„„ƒƒƒ„ƒƒ„„„„„„„ƒƒƒƒƒ„�‚ƒƒƒƒƒ„ƒƒ„„„„„„……„„„…„„„ƒˆ–•��ŽŒ‹Œ������’‘‘‘‘��������‘�‘‘’���Љ„„„„„ƒƒƒ„ƒƒ„„„„„„„ƒƒƒƒƒ„�‚‚ƒƒƒƒ‚‚‚ƒ„ƒ„„ƒ„„„„„„„…„ƒ‰–”����‹‹Ž����‘’’““’’‘‘‘��‘‘‘‘’““‘��‹‡ƒƒƒ„ƒƒƒƒ„ƒ‚ƒƒƒƒ„„„ƒƒƒƒƒƒ�‚‚ƒƒƒƒ‚‚‚ƒ„ƒ„„ƒ„„„„„„„…„ƒ‰–”����‹‹Ž����‘’’““’’‘‘‘��‘‘‘‘’““‘��‹‡ƒƒƒ„ƒƒƒƒ„ƒ‚ƒƒƒƒ„„„ƒƒƒƒƒƒ€‚‚‚����‚�‚ƒƒƒƒƒƒ„„„„„„„„ƒ‰•”‘��Ž‹‹�����‘’““”“““’’‘‘’’‘‘’”•”���ˆƒƒ„ƒ„ƒƒƒ„ƒ‚ƒƒ‚‚ƒƒ„ƒƒƒƒƒ‚€‚‚‚����‚�‚ƒƒƒƒƒƒ„„„„„„„„ƒ‰•”‘��Ž‹‹�����‘’““”“““’’‘‘’’‘‘’”•”���ˆƒƒ„ƒ„ƒƒƒ„ƒ‚ƒƒ‚‚ƒƒ„ƒƒƒƒƒ‚€€€€€���‚‚ƒƒƒƒ‚ƒƒƒƒ„„„„„„ƒˆ“”““’Ž‹ŠŒŽ��‘“••••”“““’’’“’‘’“•—˜”Ž�‰„„ƒ„„ƒƒƒ„ƒƒƒƒƒ‚ƒ„ƒƒ„„ƒƒƒ€€€€€���‚‚ƒƒƒƒ‚ƒƒƒƒ„„„„„„ƒˆ“”““’Ž‹ŠŒŽ��‘“••••”“““’’’“’‘’“•—˜”Ž�‰„„ƒ„„ƒƒƒ„ƒƒƒƒƒ‚ƒ„ƒƒ„„ƒƒƒ€€€��‚‚‚‚ƒƒƒ„„‚‚‚‚ƒ„„„„„„„†�””•‘��Š‹�Ž�“•–——–•”“““’’‘’““”—™š—�ŽŠ„ƒƒ„„ƒƒƒƒƒƒƒ‚ƒ‚‚ƒ„ƒƒ„ƒƒƒ€€€��‚‚‚‚ƒƒƒ„„‚‚‚‚ƒ„„„„„„„†�””•‘��Š‹�Ž�“•–——–•”“““’’‘’““”—™š—�ŽŠ„ƒƒ„„ƒƒƒƒƒƒƒ‚ƒ‚‚ƒ„ƒƒ„ƒƒƒ€€€��‚‚‚‚ƒƒ„ƒ„ƒ�‚��‚ƒ„…„„……Œ“•“�‘Ž‹‹Œ��“”––——–””““’’’”••–˜š›—�ŽŒ…ƒ„„„ƒƒƒƒƒƒƒƒƒƒƒ‚‚ƒƒ„ƒƒƒ€€€��‚‚‚‚ƒƒ„ƒ„ƒ�‚��‚ƒ„…„„……Œ“•“�‘Ž‹‹Œ��“”––——–””““’’’”••–˜š›—�ŽŒ…ƒ„„„ƒƒƒƒƒƒƒƒƒƒƒ‚‚ƒƒ„ƒƒƒ€€���‚�‚�‚ƒ„„…„��‚���ƒ„„„„…‰�•’’’ދЋŒŽ‘“””•–—”“’’’’“–“‘’’•—”��Œ†ƒƒ„„„ƒƒƒ„ƒƒƒƒƒƒƒ‚ƒ„„„„ƒ€€���‚�‚�‚ƒ„„…„��‚���ƒ„„„„…‰�•’’’ދЋŒŽ‘“””•–—”“’’’’“–“‘’’•—”��Œ†ƒƒ„„„ƒƒƒ„ƒƒƒƒƒƒƒ‚ƒ„„„„ƒ€����‚ƒ‚‚‚ƒ„„„„ƒ��‚ƒ‚‚‚ƒ„„…†Œ”’““�ŒŠ‹ŒŒ�‘’’“””“‘‘’“’”–”���‘‘���Œ‡…„ƒ„„„„„„„ƒƒƒƒƒƒƒƒƒ„……„€����‚ƒ‚‚‚ƒ„„„„ƒ��‚ƒ‚‚‚ƒ„„…†Œ”’““�ŒŠ‹ŒŒ�‘’’“””“‘‘’“’”–”���‘‘���Œ‡…„ƒ„„„„„„„ƒƒƒƒƒƒƒƒƒ„……„€€����‚‚ƒ‚ƒƒ„„„„‚€�‚‚ƒ‚�ƒ„……ˆ�’””’�Š‹ŒŒŽ���’’‘‘‘‘‘’‘’’“’���ŽŒ��‹†…†…ƒ„„„„„„„ƒƒƒƒ‚ƒƒƒƒƒ„„€€����‚‚ƒ‚ƒƒ„„„„‚€�‚‚ƒ‚�ƒ„……ˆ�’””’�Š‹ŒŒŽ���’’‘‘‘‘‘’‘’’“’���ŽŒ��‹†…†…ƒ„„„„„„„ƒƒƒƒ‚ƒƒƒƒƒ„„€€€€€�‚‚‚‚ƒƒƒ„ƒ„ƒ��‚�ƒ„ƒ‚ƒ…†‡Š‘”•”�Š‹Œ���Ž�����‘‘‘‘‘‘�ŽŽŽ�ŽŠ‰�ŽŠ†…………„„„„„„„„ƒƒƒƒ„ƒ‚ƒ‚‚ƒ€€€€€�‚‚‚‚ƒƒƒ„ƒ„ƒ��‚�ƒ„ƒ‚ƒ…†‡Š‘”•”�Š‹Œ���Ž�����‘‘‘‘‘‘�ŽŽŽ�ŽŠ‰�ŽŠ†…………„„„„„„„„ƒƒƒƒ„ƒ‚ƒ‚‚ƒ€€���‚‚‚ƒƒƒƒƒƒ„„„‚�‚�‚ƒƒ„…†‡‡‡Œ“––’‹‹Œ��������’“’‘�������ŒŒ‰ˆ‰‹‰††……†…„„„„„„…„„ƒƒ‚ƒƒ‚ƒƒƒ€€���‚‚‚ƒƒƒƒƒƒ„„„‚�‚�‚ƒƒ„…†‡‡‡Œ“––’‹‹Œ��������’“’‘�������ŒŒ‰ˆ‰‹‰††……†…„„„„„„…„„ƒƒ‚ƒƒ‚ƒƒƒ€€���‚�‚ƒƒƒƒƒƒ„„„„����‚ƒ„†ˆ‰‰‰‰�““�‹Š‹�ŽŽŽ��‘’“”“�Ž�����Ž�Œ‰ˆ‰‰‡…††…††……ƒ„„„„„„„„‚ƒƒƒƒƒƒ€€���‚�‚ƒƒƒƒƒƒ„„„„����‚ƒ„†ˆ‰‰‰‰�““�‹Š‹�ŽŽŽ��‘’“”“�Ž�����Ž�Œ‰ˆ‰‰‡…††…††……ƒ„„„„„„„„‚ƒƒƒƒƒƒ€€���‚�‚‚‚‚ƒƒƒƒƒƒ„ƒ����‚„†ˆ‰‹Šˆˆ�ŽŽ‹‹‹���Ž�‘’““’‘Ž�ŽŽ�’’’�ŽŒ‰Šˆ†……†………†………ƒ„„„„„ƒƒƒƒƒƒƒƒ€€���‚�‚‚‚‚ƒƒƒƒƒƒ„ƒ����‚„†ˆ‰‹Šˆˆ�ŽŽ‹‹‹���Ž�‘’““’‘Ž�ŽŽ�’’’�ŽŒ‰Šˆ†……†………†………ƒ„„„„„ƒƒƒƒƒƒƒƒ�€��‚�‚‚‚‚‚ƒ„ƒƒƒƒƒƒ�€��‚„‡ˆŠŠŠ†…‹Ž�Œ‹‹�����‘””“‘�ŽŽ�“””•”’�ŽŒ‹‰†……………†††††…„„ƒ„„ƒƒƒƒƒ„„ƒ�€��‚�‚‚‚‚‚ƒ„ƒƒƒƒƒƒ�€��‚„‡ˆŠŠŠ†…‹Ž�Œ‹‹�����‘””“‘�ŽŽ�“””•”’�ŽŒ‹‰†……………†††††…„„ƒ„„ƒƒƒƒƒ„„ƒ€€��‚�‚‚‚‚‚ƒƒƒ„ƒƒƒ„‚���‚„†ˆŠŠˆ†…‰Ž��ŒŒ��‘��‘”•“��‘“–———™˜•‘��Œˆ………†……††††††…„„„„„ƒƒƒ„„„ƒ€€��‚�‚‚‚‚‚ƒƒƒ„ƒƒƒ„‚���‚„†ˆŠŠˆ†…‰Ž��ŒŒ��‘��‘”•“��‘“–———™˜•‘��Œˆ………†……††††††…„„„„„ƒƒƒ„„„ƒ€�‚‚‚‚�‚‚‚‚ƒƒƒ„ƒ„„„ƒ‚�‚‚„…‡ˆ‰ˆ‡‡ˆ��Ž����‘‘��“”“��“–™šššœ›˜“�ŒŽˆ…………………†††††††…„„„ƒƒƒ„„„ƒ€�‚‚‚‚�‚‚‚‚ƒƒƒ„ƒ„„„ƒ‚�‚‚„…‡ˆ‰ˆ‡‡ˆ��Ž����‘‘��“”“��“–™šššœ›˜“�ŒŽˆ…………………†††††††…„„„ƒƒƒ„„„ƒ€��‚�‚�‚‚‚‚ƒƒƒƒƒƒƒ„ƒ„‚�‚„…†‡ˆˆˆˆˆ�‘����Ž�‘��’“’�’•—˜˜˜™šš™”�‹�‰…††††………††…††††…„„ƒƒƒ„„„ƒ€��‚�‚�‚‚‚‚ƒƒƒƒƒƒƒ„ƒ„‚�‚„…†‡ˆˆˆˆˆ�‘����Ž�‘��’“’�’•—˜˜˜™šš™”�‹�‰…††††………††…††††…„„ƒƒƒ„„„ƒ€€���‚‚‚‚�‚ƒƒƒƒƒ„„ƒƒ„‚��‚„……†‡‰�“��‘‘‘����‘‘’’“”•–•”’‘’’’’�‹‹‹ˆ†††††…†…††……†††††„„ƒƒ„„„ƒ€€���‚‚‚‚�‚ƒƒƒƒƒ„„ƒƒ„‚��‚„……†‡‰�“��‘‘‘����‘‘’’“”•–•”’‘’’’’�‹‹‹ˆ†††††…†…††……†††††„„ƒƒ„„„ƒ€€���‚‚‚‚ƒ‚‚ƒƒƒƒ„„ƒƒƒƒ‚�‚ƒ„…‡ˆˆ’¥›‘”“’’�����‘‘“–˜˜•’�ŽŽŽŽŽ�‹ŒŠ†††††††††††††…†††††…ƒ„„„„ƒ€€���‚‚‚‚ƒ‚‚ƒƒƒƒ„„ƒƒƒƒ‚�‚ƒ„…‡ˆˆ’¥›‘”“’’�����‘‘“–˜˜•’�ŽŽŽŽŽ�‹ŒŠ†††††††††††††…†††††…ƒ„„„„ƒ€�����‚‚‚ƒ‚‚ƒƒƒƒƒƒƒƒƒƒ‚��‚ƒ……†…•¬¥–––”““’‘‘���’–˜˜—”’�����ŽŒŒ‰†††††††…‡††‡†††‡††††…„„…„„€�����‚‚‚ƒ‚‚ƒƒƒƒƒƒƒƒƒƒ‚��‚ƒ……†…•¬¥–––”““’‘‘���’–˜˜—”’�����ŽŒŒ‰†††††††…‡††‡†††‡††††…„„…„„€�‚‚�‚‚‚ƒƒƒ‚ƒƒƒƒƒƒƒƒƒ„ƒ‚���‚‚ƒ„–±¬œ–™–”“’’’���’”—˜˜—––––””’�Œ‰……††††††‡†‡‡†‡‡‡†††††…„„„„€�‚‚�‚‚‚ƒƒƒ‚ƒƒƒƒƒƒƒƒƒ„ƒ‚���‚‚ƒ„–±¬œ–™–”“’’’���’”—˜˜—––––””’�Œ‰……††††††‡†‡‡†‡‡‡†††††…„„„„€�‚��‚ƒƒ‚ƒƒƒƒƒ„ƒƒ„ƒƒƒƒ„ƒ�����‚‚’³°£™™˜—”“““‘��‘‘“•—˜˜™™š™˜–�Šˆ………†††††††‡‡‡‡‡††††††††…„„€�‚��‚ƒƒ‚ƒƒƒƒƒ„ƒƒ„ƒƒƒƒ„ƒ�����‚‚’³°£™™˜—”“““‘��‘‘“•—˜˜™™š™˜–�Šˆ………†††††††‡‡‡‡‡††††††††…„„���‚‚‚ƒ„ƒƒƒƒƒƒ„ƒƒ„ƒƒ„„„„‚����‚�Œ¯³©¡™š™–”””“‘����‘”–—˜˜™——–�‰‡††…†††††††‡†‡‡‡‡†‡††††‡‡†…���‚‚‚ƒ„ƒƒƒƒƒƒ„ƒƒ„ƒƒ„„„„‚����‚�Œ¯³©¡™š™–”””“‘����‘”–—˜˜™——–�‰‡††…†††††††‡†‡‡‡‡†‡††††‡‡†…���‚‚‚ƒƒƒ‚ƒƒƒƒ„ƒƒ„ƒ„„„ƒ„ƒ������…¦´ª© š™˜••–•“‘�����’“•––••”�ˆ††††††………††††‡‡ˆ‡†‡††‡‡‡‡‡‡���‚‚‚ƒƒƒ‚ƒƒƒƒ„ƒƒ„ƒ„„„ƒ„ƒ������…¦´ª© š™˜••–•“‘�����’“•––••”�ˆ††††††………††††‡‡ˆ‡†‡††‡‡‡‡‡‡���‚ƒ‚‚‚‚‚ƒƒƒƒ„ƒƒƒ„„„ƒƒ„„ƒ�€��€�›²«¨¨¢›˜—•——”’‘�ŽŽŽ��’““’“�‹‡…†‡††††††††…†‡‡‡†‡‡‡‡‡‡‡†‡‡���‚ƒ‚‚‚‚‚ƒƒƒƒ„ƒƒƒ„„„ƒƒ„„ƒ�€��€�›²«¨¨¢›˜—•——”’‘�ŽŽŽ��’““’“�‹‡…†‡††††††††…†‡‡‡†‡‡‡‡‡‡‡†‡‡€��‚ƒƒƒƒƒƒƒƒƒƒƒƒ„„„„ƒƒƒ„„„‚����€�ª®©£§¤™˜–˜˜•“’��Ž�ŽŽ������Ї†††‡†††‡‡‡††‡‡‡‡‡ˆ‡‡‡‡‡‡‡‡‡€��‚ƒƒƒƒƒƒƒƒƒƒƒƒ„„„„ƒƒƒ„„„‚����€�ª®©£§¤™˜–˜˜•“’��Ž�ŽŽ������Ї†††‡†††‡‡‡††‡‡‡‡‡ˆ‡‡‡‡‡‡‡‡‡€��‚ƒƒƒƒƒƒƒƒƒƒƒ„ƒ„„„„ƒƒ„„„ƒ�€��~„�­ª¤ £�˜——˜–”“’��Ž�Œ��Ž�ŒŒ‘އ†††††‡‡‡‡‡‡†††‡‡‡‡‡†‡‡‡‡‡‡€��‚ƒƒƒƒƒƒƒƒƒƒƒ„ƒ„„„„ƒƒ„„„ƒ�€��~„�­ª¤ £�˜——˜–”“’��Ž�Œ��Ž�ŒŒ‘އ†††††‡‡‡‡‡‡†††‡‡‡‡‡†‡‡‡‡‡‡€�‚ƒƒƒ‚ƒƒ‚‚ƒƒƒ„„ƒ„„„„„ƒƒ„„„‚�€€||‰ ©¦¢Ÿ ›™—––•“’‘���Ž�����“¢¢‘‡†††‡††‡‡‡‡‡‡†‡‡‡‡‡‡ˆˆ‡ˆ‡‡€�‚ƒƒƒ‚ƒƒ‚‚ƒƒƒ„„ƒ„„„„„ƒƒ„„„‚�€€||‰ ©¦¢Ÿ ›™—––•“’‘���Ž�����“¢¢‘‡†††‡††‡‡‡‡‡‡†‡‡‡‡‡‡ˆˆ‡ˆ‡‡€‚ƒƒƒƒƒƒƒƒƒƒƒ„„„ƒ„„ƒ„„„„„„„„‚€}|{|…™¥£ ŸŸ™™˜”•”’‘‘�������’Ÿ¦ª¦”‡†††‡‡‡‡‡‡‡ˆ‡‡‡‡‡‡‡ˆˆˆˆˆ‡€‚ƒƒƒƒƒƒƒƒƒƒƒ„„„ƒ„„ƒ„„„„„„„„‚€}|{|…™¥£ ŸŸ™™˜”•”’‘‘�������’Ÿ¦ª¦”‡†††‡‡‡‡‡‡‡ˆ‡‡‡‡‡‡‡ˆˆˆˆˆ‡€‚ƒƒƒƒƒƒƒ‚ƒƒƒ„„„„…„„„„„„„„„…‚}{{{{|��œ¡Ÿ œ–™–•”’‘‘‘��‘�ŽŒ“ ¤©¬§”‡†††‡‡†‡‡‡ˆˆ‡‡‡‡‡‡ˆˆˆˆˆ‡€‚ƒƒƒƒƒƒƒ‚ƒƒƒ„„„„…„„„„„„„„„…‚}{{{{|��œ¡Ÿ œ–™–•”’‘‘‘��‘�ŽŒ“ ¤©¬§”‡†††‡‡†‡‡‡ˆˆ‡‡‡‡‡‡ˆˆˆˆˆ‡€‚ƒƒƒƒƒƒƒƒƒƒ„ƒ„„„„„„„„…„„„…„€{{{{{{{}‡–ŸžŸ˜•––”’‘‘‘‘����‹‘ž¤ª«¬£‹„††‡†††‡‡‡ˆˆˆ‡‡ˆˆˆ‡ˆˆˆ‡€‚ƒƒƒƒƒƒƒƒƒƒ„ƒ„„„„„„„„…„„„…„€{{{{{{{}‡–ŸžŸ˜•––”’‘‘‘‘����‹‘ž¤ª«¬£‹„††‡†††‡‡‡ˆˆˆ‡‡ˆˆˆ‡ˆˆˆ‡€‚ƒƒƒ„ƒ„„ƒƒ„„ƒ„„„„„„„……ƒƒƒ‚€}z{{{{{{{|‚�˜Ÿ�•””•”“’‘‘����Œ�œ¥ªª±¬�‚ƒ…‡‡†‡††‡‡‡ˆˆˆˆ‰ˆ‰‰ˆ††€‚ƒƒƒ„ƒ„„ƒƒ„„ƒ„„„„„„„……ƒƒƒ‚€}z{{{{{{{|‚�˜Ÿ�•””•”“’‘‘����Œ�œ¥ªª±¬�‚ƒ…‡‡†‡††‡‡‡ˆˆˆˆ‰ˆ‰‰ˆ††€‚ƒƒƒƒ„„„„„ƒƒ„„„…„„…ƒ‚‚€~|}|zz{{{z{z{{}ƒŒ•“’“”““’‘���ŽŽ��™¥©°¾°�…„„„…†‡‡‡‡‡ˆˆˆˆ†‡Œ“�„|~€‚ƒƒƒƒ„„„„„ƒƒ„„„…„„…ƒ‚‚€~|}|zz{{{z{z{{}ƒŒ•“’“”““’‘���ŽŽ��™¥©°¾°�…„„„…†‡‡‡‡‡ˆˆˆˆ†‡Œ“�„|~€‚ƒƒƒ„„„„„„„„„„……„ƒ‚}||||{||{{z{{{{{z{{z}‚†Š�’“’���Ž����™£®½Á¥ˆ†††„ƒƒƒ††‡†Œ”�‡€�œœ�†Š‹€‚ƒƒƒ„„„„„„„„„„……„ƒ‚}||||{||{{z{{{{{z{{z}‚†Š�’“’���Ž����™£®½Á¥ˆ†††„ƒƒƒ††‡†Œ”�‡€�œœ�†Š‹�ƒƒƒ„……„„„…„„„„ƒ‚€~}}|||||{|}|{{{{|{{{||{|}}}~ƒŠ��ŽŽŽ�‹‹ŒŽ—¡®³±–‚………†„‚€�‚†„Ž�’‰Œ˜�›˜šœ™�ƒƒƒ„……„„„…„„„„ƒ‚€~}}|||||{|}|{{{{|{{{||{|}}}~ƒŠ��ŽŽŽ�‹‹ŒŽ—¡®³±–‚………†„‚€�‚†„Ž�’‰Œ˜�›˜šœ™ \ No newline at end of file diff --git a/bin/foreman_part_qcif_444.yuv b/bin/foreman_part_qcif_444.yuv new file mode 100644 index 0000000..0e43f4a --- /dev/null +++ b/bin/foreman_part_qcif_444.yuv @@ -0,0 +1 @@ ++Øþùûþþýûüþþþþþýûþþþþþùîàââáßáâáßàãåß¿›œ¨¾ÖÛÖ×ÛÛÛØ×ÛÝÜÙÝÝÜÞÜÜßßßÛÖÕØÝáÔħ‘“””Œ�…tu†µßÞÝßÞÝÞÔÌÙÙÑÑÑÒÓ×Ô˜o�†}y§ÏÏÏÔÔØÖÔ××ÑÐÔÒËÀ¾Éº�€¢ÖÓÐÖ×ÙÙÕÐÎÏÉÌ®ww§ÈÍÒÐÏÏÈÃÏÉ“r¦ÑÏÊÁººÈÀ�i—ÀÆÖ¾1ÆÁÓäÍÕ¹ÓϺøÆËзØÄÆîкÕíâááâáààâæçÞÁ�’£¸ÉÑÔÕÒÔÙÛÙ×ÚÙÛÝÛÜÜÝÝÝÞßßÜØÔÝàÕÀ¤—““—šŒuv{Œ©ÐÞÝÝÜÜÛÚÛÇ¿ØÒÎÑÐÑÑÈÑΘu…ˆvvªÎÏÒÓ××ÒÓÓÑÎÏÏËÆÌν¬Ž~ªÖÕÔÖØÙÕÑÎÏÉÉÁœvyªÊÑÑÏÎÎÎÌѼ‚}´ÑÍÅÃÀÈÆ¦xn¦ÍÒ¾0±«Å­¹ˆ¿Ãгް±‡Ê°Š»Ë¦´çãááàßáäåÚÁ¢‘—¬ÉØÜÙÕÔÕ×ÙÓÎØÙÔÙÜÛÜÜÝÛÝÙØÙÙÝÛÓºŸ‘�‘——Š{qx”·ÖáãßÜÜÜÜÝÜÛÙÙÙÔÑÒÐÏÐÍÐͼ’t…„ms¤ÊÏÓ×ÖÓÓÔÒÑÐÌÌÏÎÏÂÀ¯„|­×ÓÔÙÙØÔÍÎÎÎÌÌ«z‚·ÎÌÍÍÏÊËÎÓ²w€ÀÓÊÆÄË˵˜os´Ô¼.Öᩱ½Æ Ëб¥­Ä¿œÑË®¡åΡÐçàÝáæàÖ¾¢™¡·ÐÙÙØËÉÖÖØÜÙÍÏÛØÖÛÚÛÛÛÝÖÓ×ÙÝÝͰ–‰‰—�’†{xƒ�ÁÙâáÞÛÜÝÝÜÝÝÛÚÜÚÖÔÓÒÏÑÑÒѸ°Â�o�†stŸÅÌÒÕÕÕÖÒÒÔÍÊÎÌÑȼé~z²ØÔÖØØ×ÐÍÐÐÈÌÒ±}‹¹ÂÉÍÎÆËÐÐÓ¬sŠÇÏÆÇÎп¶£o~ý)¹Ð´ËäâÈÖâáãäáàÒÝâçÚåÝÕßàáæãÔº¥›£½ÕÖÖÕÖÖÃÈÖÖØÚØ×ÙÙØÛÚÖÛÝÚÙÔØáÜˬ�…Š—™‘�tu‰«ÉÜâáÞÛÞÝÝÝÞßàßÞàßÛØÓÍÌÎÑÐÐÑǼÌË”p}…wv ÈÐÓÓÔ×ÖÒÓÏËÉÌÓÉ¿ÃÂ¥~·×ÒÕÕ×ÖÓÑÐÍËÌȨv�´ÅÌÍÍËÏÐÒÕ£k’ÉÏÌÏÒÇ¿À›n�²‚­²×æÝÔÜåãäåãââßâãèáàææååÕ´›™”²ÒÚ×ÔÔÓ×ÙרÖÙ×Ó×ÚÚØÖØØÙÚÙÛÛÜ×ç’……“œ’sw“¶ÑÚÙÝÛÙÍÖ×ÙâäæèéëêëêéèãÝÚØÔÏÐÏÑÉÄÓÏšmzŠ€|£ÌÒÑÖÔÕÕÎÐÐÊÐÖËÃÂÊ˨{·ÖÔÔÖÕÖÒÐÐÍËÆÆœqŽ¿ÌÍÍÊËÎÌÕÑ‘i›ÍÍÎÐǽ¼¶ŒlˆwÂØÓÕÛÞáßÜÛÚÚÚÚÝÜÚÔÜäãàÔ¶Ÿ˜�¨·ÒÛÚ×ÙÙÙÙÙÜ××ÙÙØØÙÚÛÙÙÖ×ÛßßѸ �‹�˜�~{v›¿Øáߨ×ÛÝÕÇÔÔáêëììëìììììíííìêäÜÖÍÏÓÏÍÐÏžq{Œ�{ ÌÒÕÓÒÖÏËÎÐÓÔÍÇÉÉÈÁ x~¸ØÔÕÓÒÔÑÐÍÏÊÈÈ™tŸÌÑÍËÍÍÌÍÔăl£ËÎÏʽº·°Šq~ÛàÙàãããâáàÜÜÝÞÝÝÞâæÝɶž˜¤¸ÈËÖØÕÛÝÛÙÛÙØÙÖØÛÞÛ××ר××ÖÙÜͱ“†ˆ‘–Ž}qp„£ÆÚÝÝÙØÜÝÛÜÜâåèëêëëëëëëëëëêêêëíìëçßÔÒÓÐÊÐÓ§wzŠ�x�ÊÓÒÕ×ÕÅÆÒÎÌËÈÌǼ¹·šu�¾ØÔÔÓÕÒÏÐÍÎÌÍË�s¯ÒÍÌÐÎÌÍÎÓÀ|p²ÑÎËÀû¾³ƒƒ×ßâááááâßÛÝÝÛÜÞâãÛä‘—©ÀÉÌÒ×רÛÝÝÛÙÖÖÓÓ×ÙÛÙÔÕÖÕÖÛÛÓĬ“ƒˆ�–‰uoqŒ±ÅÕáØÐÙÝÞÝØÛâçêìíëêëëìëëëëëìëëëëêëìíìå×ÐÐ˾ÉÓ¶|vˆu—ÆÑÑØÕÍÎÓÑÍÊÊÎÎÇ¿¿¼™x‡ÂÙÓÓÕÓÏÏÍÏÍÉÍÀ‡€¶ËËÏÎÍÎÎÎÖ¶u~½ÒÎÈÈÁ¼¿¡ˆØâßàááàÝÙÝÞÛÜâäÕ»¤•™£µÏÔÎÎÍÓÙÜÝÜÛ×Ô×ÙÓÔÙÛÕÓÕÕÙÛÚÕÀ£�‰‹“›Šzmw’³ÍÞÕÑØØÛÞÞÝÜßåêëëëêëëëììëìììììììëëëììëëíèÚÐμ½È¿¨qr‡~q•ÊÓÕÓÏÔÕÔÓÍÍÎÌÍÉÀÀ¹–r…ÂÖÐÔÖÖÔÒÏÍÌÉ´ÊÍÏÌÌÎÎÐ×­r„ÁÓÎÊ¿½½®ˆØãààáßÝÚÝØÓààÓ¸�‘–¦ÁÎÒÕÏËÎÑ×ÚÜÝÙØØÖÔÔØÖÙÚÕÐÙÝÛζŸ�ŠŽ–“‰yny›¿ÒÔÕÙØØÝßÞÞÝÝâéìëëëëëìëëëëììììììììëëììììììíçØÏÌÉǭ®sr…u–ÉÓÑÈÏÕÓÔÐÍËÊËÉý³�rˆÈ×ÒÖØÙÔÎËÍÎÊͱzƒ·ÊÏÊÈÏÎÒÖÔ£g‹ÊÑÐÈÂÀ«‡ÙáÞÝÞÞÝѵ›ºÒº¤�” ¶ÍÒÑ×ÖÊÍÎÑÕÛÝÞÝÚÙÙÃÅÖÒÓÕØÙÜδœŽŒ’•Œup€ ÂÚßÚÕ×ÜÝÝÕÖÞÞáæêëëëìëëëììëëëëëìëëìëëìëìëìììëëìå×ÐÍÍÅÂÓ«kqˆ~o�¿ÎÉÎÔÔÒÏÍÌÌËÊȽ·±—t�É×ÑÒÖÔÏÍÎÎÎÌɪx‡¶ËÅÄÎÍÒÒÔЖk˜ÌÐÌÇÅ«†Øàâææãα’q•¢“–£µ¼ÇÔÑÐÙÓÌÐÏÑÖÚÛÛÜÚÚÖÍÏÑÑÖÛÔȲ˜�”˜•‡xmtŒ¬ÌÞàÛ×ÙÝÜÜÝÛÑÕàäéëêêëëëëììììëëëëììëëëëëëìììëëëììëëå×ÎÍýÊÑ¥ns†}nнÑÓÕÖÔÏÎÍÎÌÈÈdz¬¶¶švŽÊÖÌÑÓÏÑÎÎÎÎÉÇ©wнÁ¾ËÏÏÒÑÖÏŽj¤ÐÉÈ̱‡Ûçáɾ¹£�™Œ�¥¸ÈÑÏÄÎÒÔÙÐÐÐÓÚÜÚÛÛØÖØÖØÕÔÚ×ħ“�“�šˆto{˜»ÓßáÛ×ÓÙÜÜÛÜÝÙÛãçêëêëëëëëëììëëëììììììëëììììììëëëëëëêéäØÏÈÊÌÏÓ¨qq…‚t‹ÂØÔÖ×ÑÐÐÏÍÉÅÇ¿³µ½ºžv‘ÎÔÏÖÓÑÐÑÎÌÌÈÇ¢s‘º½ÈÎÏÑÒÑØÈƒs´ÏÃdz‹ÒÀ¥Ž†…���Š–²ÉÏÓÖÑÐ×ÊÒÖÏÐÎÖÜÛÛÜÛÙÖÒÖÓÑͺ£’‘”——ˆurƒ¡ÃÚàÝ××ÙÚÙÜÖÒÜÝÝÞäéëëëëìëëëëëìëëëëëììëëëììììììììëëëëëëëèèä×ÎÎÎËÌѬonˆ„o…ÃÖÑÔÓÑÑÏÌËÅÂÉÀ½¾¸¸–u•ÎÖÒÐÑÕÔÑÍÍÌÇÈ�r‡¥ÃÌÎÐÒÓÓÙ¿€½¼¼²}¦‘�š„ov¡¯´ÌÖÕÑÐÑÕÙÔÏÖÒÍÉÏÙÙÚÜÜÖÔר֯®—Š‹”š�~tv�°ÍÜÞÚØ×ÙÞÝÛÚÛÕÓßÞÞåêëêëëìììëëëëëëëëììëëëìëëìëëìëëëëëëëëëëêèèäÕÌÏÍÊÌЯzp„€j‚¾ÓÐÑÑÑÑÌËÉÃÄÁ¿Àº®¨šs”ÎϽËÕÔÔÒÎÎÈÉÊ—i�ºÊÌÏÏÒÒÒÙ¹{ˆ·Ã°m¢¹ÉË¢ju²ØÚÚÖÔÓÔÕ×ÔÑÚÔÍÏÊÓØØÚÚ××ÛÖ锈ˆ’š�uoz�®ÕáßÛØØÜÜÜÕÔÛ×ÖÜÞרåéêêëëëììëìëëëëëëëìììëëììììêëëëëëëëëëëëêêéèéâÔÎÎÏÍËѹ|nƒ€kƒ¼ÔÍÏÑÑÎËÊÉÅÃÁÃÁ¶¯¸�o™ÏÈÊÓÓÕÖÒÎËÆÌÊ’oŸÅÊÍÎÑÑÒÕØ°vŽÅµÌÛßÜÊ­½ÐÏÑÓÖÔÒ×××ÔרÏÍÏÒ×ÖÕÙÜÛÖĨ�†‹’’†vju ÃÔÝàÚÛÓÐÜØÙÛÏÒÜÛÜÜÜ×ÞéêëëëëëëëëëëìëëììëëìììëìëìëëëëëëëëëëëëëëêéççèáÓÎÏÏÌÊм„o„„p�¸ÉÆÐÔÐÌÉÈÅÁÂÄÆÇ¾»®�qŸÐÎÐÔÔÖÕÒÏÊÆÑĆsªÉÌÏÐÑÒÓÕÖ¥r›´ŠÖØØÓÍ×ÙÔÕÖÕÖÑÐÖ×ÖÖØÑËÍÎÓÔ×ÛÜÓÀ§�ˆ�–�yhn„ŸÅÝáßÛÛÝßÕÑÜØ×ÚÙÛÜÝÞÜÝåêêëëëëëëìììëìììëìììëìììììëëëìììëìëëëëëëëëêéèèèèàÑÍÏÏÍÊо{fƒp~­ÆÍÒÒÏËÆÈÿÂÈËŽ°°’q¡ÏÓÔÓÕÕÔÐËÊËμƒ{´ÌÌÐÐÐÔÔÖЙu�†ÔÕÑÐËÎÏÔÖÔרÑÐÖØÓ××ÌËÎÏÕÛÜл¡Œ†Š�‡rej†¯ÑßàÚØÙÛÝÝÝÜÛÛÛÛÜÜÝÞÞÜÞäêëëëëëëëëììììëìëìììììëìììììììëìììììëëëëêëëëêééèéçÝÑÎÎÌÌËÏ»€d{‚p~²ÌËÐÑÏÎÌÆÂÁÁÉʼ¦²½�v¦ÒÔÔÖÕÕÒËËÎÌÏ¿€…¾ÊÍÏÐÒÔÓÔÉ’q‡ÕÔÎÌÃÌÓØÖÔÓÔÒÒØ×ÐÔÔÍÍÐÖØÌ´š�ˆ‰Šƒp]gŒ·ØâÛÔÕÛÞÝÜÛÜÜÚÚÚÚÛÝÜßÖÓÞãèêëëëëëìëëëìëëëëëììììììììììììììììëìëëëëëêëëëêééééèæÛÐÎÍÏÐËÓÉ‹ex‚t{¯ÏËÍÐÐÌÇÂÁ¾ÄÌì³Ç¿‹t¨ÑÒÒÓÓÔÏÍÑÎËи|‹ÂÍÌÐÐÑÒÑÐÇ�‰ÐÒÐËÆÏÖÖÖ×ÓÒÒÕÙÔÔÔÏÏÐÎÆ®•Š‹Œ‰~nhp�»ÚàÜØÖÔÛ×ÒÚÝÜÝÜÛÛÛÛÜÛÛÝÕÒáèéêëêëëëìëëëëëëëëëììëììììëëëìëëìììëëìëëëêëëëëêéèèèèçãÖÏÏÏÐÍÉÔÍ�dv€qy®ÌÌÌÎËÉÄÁÂÄÇË·¸É˱t¬ÓÓÔÔÔÓÍÎÎÍËÒ±|–ÈÎÏÑÑÒÒËͱˆÎÓÐÍÏÔÚÖÔØ×ÓÒØ×ÔÖÖÕÏ»¤’ŠŽ’‹{mfv—½ÙáÜ×ÕÙÛÜÝÖÑÜÝÚÙÛÜÚÚÜÜÛÜÛÝßåèéêëêêêëëëëëëëëëëëëëëëìëìëëëëëëìììëëëëëëëëëëëêéèèéèçæÝÐËÎÏÎÍËÓË–hr€rv§ÍÌÉÊÊÇÃÃÅÃÊÂÂÑË»«‚x³×ÔÕÕÖÓÑÑÏÍÌÒ³|›ÌÎÌÑÓÓÌǵ†ÐÔÎÍÎÖÙÔÖÙÕÒÔÕÖÛÛϹžŽ‰‹�Š{liwœÅÚßÛÖÖ×ÛÛÛÛÜÜÝØÕÚØÚÜÚÛÛÛÜÝÜÝãèèééêêêêêëëëëêëëëëëëëëëììììììëëëììëììëëëëëëëëêêêéééèçåâÕÊÌÍÎÎÎËÐÑ�hs€vw¨ÐÌÇÉÇÈÅÂÄÊÇÈÔÏ¿³¬w³ÓÐÒÔ××ÔÎÍÍÌÒ±{žËÊÎÑÑÎк†ÏÐÌÌÒØÔÒÔÕÑÒÖÛÙзž‰‚Ž–ŒuekªÁÔáÙÔ×ÚÚØÜÜÛÛÛÜÛÖÕÙÙÚÛÛÛÛÜÝÛÜàæèééêêêêêêëëëëëëëìëëëëëììëëëìëììëëëëììëëëëëëëêêêéééèèææåßÑÌÎÎÎÎÌÊÎÍ‹ax�rq¤ÏËÆÇÈÆÇÇÄ¿ÇÐÐË®²¦wxµÓÏÑÕØÖÐÏÒÎÌׯz£ÊÍÍÍÌм…ÍÏËÍ××ÒÓÓÏÔÜÚǬ”„„�•‹{st�²ÉßÚÔ×רÜÜÜÝÜÛÛÚÚÚÚÜÛØ×ÚÛÛØ×ÝÝÜÝãæèééêêêêêêêëëëëêëëëëëëëëëëëëëëëëëëììììëëêëëëëêêêêéèèèèçæäÙÎÏÏÍÌÌÍÉÌÁ•oqylmžÆÆÊÍÈÊËÄ·ÀÐÏÈ»²®§}{½ÓÐÓÖÕÕÖÖÒÎÏמv¨ÍÎÏÍÏ»…ÎÒÍÏÖÒÑÕÙØÖÆªŒ~ƒ�Ž„tlx™ÁÛáߨÖÙÙÚÛÜÜÜÛÚÚÚÛÚÙÚÛÛÛÚÛÛÛÙØÜÝÜßäæçèêêêêêêéêêêëêêêëëëëëëëëëëëëëìëëëìëëëëëëëëëëêêéééèèèèçæäßÑÌÎÌËÌËËÊÑÔ ms}ql“ÃÎÎÈÊÏÎÀÁÓÔʾ¶­º©w�¹ÒÓÒÓÖ×ÖÔÔÐÐÉ”|¯ÎÎÎи…ËÍÎÑÑÒÙÜ×Ĥˆ~†�‡{pl} ÃÛáÛÖØÛÜÛÙÛÜÝ××ÚÙÚÙÛÜÛÛÚÛÛÛÛÙÚÚÚÙØÜâåçèèééééêêêêêêëëêëêêëëëëëëëììëëìëëëëëëëëëëëëëëêêêééèèèèçæåãÖÍÎÍÍÌËÊËÊÏÕ®}v|qj–ÂÌËÍÎÏÇÇÓÑÌľµ´º¦yƒ¾ÖÒÔÕÕ×ÖÔÔÏÍÉ•~²ÍÍÑ·ƒÇÉÍÕÛÛ϶ ‰‡‹ƒrdm‰­ÍßáÝÖÖÚÛÚÜÝÜÜÛÛרÛÚÚÚÚÚÚÚÙÚÚÚÚÙÙØÖרÝäæççèééééééëëêêêëëëêëëêëëëëëììììììììëëëëëëëëëìëëëêêééèèççææäÞÔÏÍÍÌÌËËÌÉÐÙ°qpyqo’½ËËÌÎÊËÓÏÉÆÀ¸±³¹§|ŠÅØÒÕÖ××ÕÕÕÍÎË„¹ÏѶ|·ÉÙÞË«‘ƒ‚…Š‚nbp’¾ÖááÛØÙÜÜÛÛÜÜÝÞÜÚÚÛÜÛÛÛÛÙÙØØÙÚÙÛÚØÚÝàÞÛàäæçèéèéééêêëëëëëëëëëëëëëëëëììììíëìíìììëëëëëëëëëëëêêêééèççæåäâÛÓÎËËËÊÊËËÊÎÒ©uq{zt‘ºËÎÒÐÍÔÔÍżº·¯³º¥{ŽÈÕÔÖ×ÙØØØÔËÊÁŒˆ¼Ò¸k˜Å̰‘„�‹~ml|ŸÆÛáÞØÕÙÜÜÛÜÜÛÜÜÝÜÛÛÛÚØ×ÙØØÛÚÙÙÙØØØÛÞáÞËÃÙâäææççèéééêêëëëëìëëëêëëëëëëëëëëììëììììëëëììëëëëëëêêêêêèéççææåãÝÕÏÊËÊÊËÊÉËÍÒׯys‚{x•ÂÒÑÎÊÒÔÐÊ»¿¼¹¶´¡z‘É×ÓÕÙØÙÖÔÐÊÏ»ƒ�Ƽsžž•†‹�‡{oq�µÐááÚÕ×ÙØÛÛÚÛÜÛÚÚÜÞÛÙÛÛÙØÙÚ×ÖÙÚÙ×ÖÖÛßßÔÁ¨Ž©ÝâäåæççèéééêêêêêêêêëëêêëëëìëëêëìììëëëëëëëìëëëëëëëêêêêêéèéèççæåãßÕÍÍÌÊÉËÌÊËËÍÑÔ±yr�{u“ÂÓÌËÒÓÐÍȼ½¾À¹°µ§�–ÇÔÓÖרÖÓÐÍÊη—¶…¯€Š’ƒni~˜·ÝåáÙÑÖÚÚÛÛÛÚÚÜÝÙÚÝßÞÜÚÛÚÙÚÙØØØ××ÖÙÞÝ×DZ›Žˆ”ÆááäåæçèèéééêêêêêêéêëëëëëëëëìëëëëìëëëëëëìëëììëëëëëëêêêêééèèçææåäâÙÏÍÍÌÅÈÍÉÊÇÅÉËÒ±sl{|u“ÈÒËÑÔÑÊǽ¾ÄÀ²¶É­}˜ÌÖÑÔØ×ÔÐÎËËЭ‹�µˆwql|ŸÈÞãßÙØ×ÔÙÚÚÚÛÚÚÛרÛÜÝÞÜÚÛÚÙÙØ×ÖÕÔÕÜàÞζŸ�Š�œŸ±ØàáäåæçèèéééêêêêêëêêëêëëëëëëëëëììëëëììëììëëìëëëìëëëêêêêéèèççææåäãÞÔÍÍÍÇÈÎÊÉÉÈÌËÌѱwq�zs‘ÃÐÏÔÔÌÊÉÄÃÄô¸ÊÄ—~šÈÕÓרÕÐÐÎÌËЬwv¡zm…¬ÎÛÛÜØÙÜÜÚÚÙÙÛÛÚÙÚÚÕÖÛ×ÙÜØ×ÙÚÚÙÖÔÖÙÜÜÕÈ±Žƒ‰•Ÿ�˜Š®Ýßâäåæçèèéêéêêêêëëëëëêëëëëëëêëììììëëëììììëëëëëëëëëëêêêêêèèèçææåãâà×ÏÎÌËËÍÌËËÊÅÌÏÐÙ¿‚m{si�ÃÒÑÔÑÌËÇÁ½Á»¼Èì’zŸÍÔÓÖÕÒÒÑÎËËΤiˆ�¹ÔÝßÚ×ÙÜÝÝÜÝÜÙÙÛÛÚØÙÛÛÚÛÖØÛÙÙÙÙÖÖÙÜßÙ˸Ÿ’�‰’œ™Ž�zƒÀàßâäæççèèéêêêêêêëëëêëêëëëëëëëëìììëëìììììëëëëëëëëëëëêëêêééèèèçææäããÚÌÍÍÊËÌÊËÈÆÂÌÂ½ÏØÁƒqxqpŒ¾ÓÑÒÑÍÊÅÀ¾¼ÀËÆ°¢Œ|ŸÒÔÓÓÑÑÑÏÍÉ;u¾ÓÞÞØÖÚÜÛÝÝÛÜÜØÚÚÚÛØØÚÛÚÚÛÛÚÚÚÙ××ÛßÜÑ»¢‘Œ‘™›–Œ„}xw|®ÚÜàâäåæççéêêêêêêéêëëëêêëëëëëììëëììëëìììììììëëëëììëëëëëêêêéèèèçççåäãÞÐÅÈÉÊÌËÌÁÅÍË»¬ÈÌׄpyuk„¼ÒɽÉÐÉÅ¿ÄÊÍ· ž‹u¥ÔÔÑÐÏÑÑÎÍ͸†ÜßÔÕÙÙÙÛÚÜÝÜÛÚØÚÛÛÚØÚÚÚÚÚÚÚÙרÛàÝÒë•‹Ž™ž™�„yuvtsuˆÂÜÜàâäååæçèééêêëëêêëëëëêëëëëëëëëëììëìììëëëìëëëëëìëëëëëëëêéééèèèèçæåãáØÈÇÊÌÌÌÎÊÊÌÊÊÆÊËÍׯ�pvxi‚»Ç®ÂÒÉÄÇËËÌÑÁ­¥©ˆn¦ÒÒÐÒÐÑÏÍÍ·†ÓÙÖØÙÙÚÝÝÜÜÙÙÚÛÛÚÛÛØÙÙÙÙÙÖÖ×ÛÝÙÊ²šŠŠ’œ¡�Šzuusps{Œ¢ÀÑØÞàâäääæçèéêêëëëêêêêêêëëëìëëëëëëëììììììëëëëëìëëëëëëëëëëêééééèèçççåäãÜÐÊËÏÊÃÊÍÍÌÌÍÏËÊÌÏÖÌ—qzuk‚³ÈÊÎÌÄÆÌÏÏÐȶ¦««…q£ÑÐÐÓÕÒÏи…ÔÙ×ÙÚÛÜÜÝÝÛÓÖÛÚØØÙØØÙØØ×Ô×ßßÖ¿¥�„Ž™Ÿœ�…{vuuuvƒœºÑÞÞÖÚÞáããååæçéêëëêêëëêëêëëëëëëëììëìììììëëììëëëìëëëëëëëëëëëëëêêêéèèèççæåäßÑÈËÎÍÈÇÆÍÍËËËËÊÍÍÈÒÑ•pzvmªÆÍÍÈÆÄÌÑÏʺ¬­«¤…o¨ÕÑÒÕÒÐÔ»†ÕÚÚÚÚÜÜÚÛÝÜØØÚØÖ××Õ××Õ×ÛÜÛ̵žŽ‹“œŸ˜‹ƒ~zssu‚™°ÉÚßßÝÒÐÜßáãäåæçèêêêëëêëëëëëëêêëëëëëëëìììëëìëëëëëëëëêêêêêëëëêêëêêêêéèèèèçææåâÖËÌÎÏÎÌÊÍÎÌÈÊËÈÍÏÌÌÔÏšsuxszªËÉÅÅÀÈÑÑËÁÀ½­¤ª‰r§ÌÏÑÓÑÖ¿‡ÖÝÙÙÛÜÜÙÚÛÛÜÙÙØØØÓÓ×ÙÜÝÔĬ�…�œ¡œ’„~~|xv{Œ©¾ÔãÜÙØ×Ù×ÕÜßàâääæçèéêêëëêëêêêêêêêëêêëëëëëëëëëìëêêêêëëëêêêéêëëëëêëëëëêééèéèçææäâÙËÈËËËÎÎÎÌÌÌËÆÁÉÍÎÐÍÔÑžqu}vƒ¯Ç¼ÁÈÉÐÒÌÃÇÀ³°²°‡o¥ÐÒÒÒÕ¼†ÕÛרÙÙÚÛÛÛÚÙÙÙØ×ÕÓÙÞÙ̹¥•’”�¢™‹{zyz�Šž¼ÔáÕÒÙÐÔÛÚÙÖØÜÞàâäååçèêêêêëêééêêêëëëëììííììëìììíììíííííííîíííìììëìëëëêëëëêêêêêêéèæàÒËÍÌÊËËÌÌÍÏÍÆÂÈÌÍÐÎÍÓÒ¡qszs|¬¹½ÌÊÌÑËþ½»µ´¸º‘|±ÖÒÑÖ»†ÔÚØÙÚÙÚÚÚÚÙ×ÖÕÖÙØ×Ò«—’˜¡£œ’†}yxvx‚š¼ÐÚàßÙÔÕÕ×ÛÛÛÚÔ×ÝßàâäæææéêêééêëììëëéèæäáßÝÜÜÜÜÜÜÛÚÚÙÙØØÙÙÙÙØÖÕÓÒÐÎÍËÌÌËÊÉÉÉÉÊÌÏÑÔÕ×ÚÚÛÚØÚÙÖÖÓÏÎÎÍÎÌÅÄÈËÎÐÑÎÒÔšiszkxš·ÈËÎÎÎÆ¿Âº¾Áƽ‰|´ÕÑÔ»†ÓÙ×ÚÝÝÛÙÙÙÕÔÙÝÞØÃ¬š””›¤£™‰~|wswŠ¥ÁÖàÞÛÙ××ÚÛÚÛÛÚÛÛÕØÝßáãäåæçééêëììëèãÝØÑËÆÁ¿½¼¼¼º¸´¯¬«ª¥Ÿ™—˜—™››™—“���’”•˜›œžŸ £¥¥§¨ª®±³¶¸½ÀÃÈÌÑÕ×ÖÑÍÌÌÉÅÆÊËÎÐÏÍÓÏ¢utwir¥ÈÍÑÑÐÈÀÂÀ¿ÄÂÂÄ®w±ÓÓº…ÓÙÙÙÛÛÙÖØÙÚÝÛп§‘ˆ‹š£ •‡{ywyy…®ÏÞàÜØÒÖÜÚÚÛÚÛÛÜÚÚÚÖÚÞßáâäåæçéììéäÝÓÊÁºµ²¯¬©§¥£ž™“އ„|{vrppoopvxxtmkkmqux{…‰�’—œ¡¥§§¨§¦¨ª«ªª­±µºÀËÒÐÌÊËÊÈÌÍÌÍÍÏÍÏÌ¡lo{ky°ÐÍÑÐȿÿ»ÅÂÄÈ¿¨}xºØ¼„ÔÝÚØÕÖ×ÙÞÜÒÁ«œ�‰�˜›”�†~vsw~‹ ¯¿ÞßÚ×ÔØÙÚÛÙÛÛÙÙÜÛÙØÙÖÛÞàáãäæèééäÛÑÆ½·´²³³°¬¥�”Œ…zwsppojhihhhjkntvuoe_]adfjmruy}�„…‰’�©¯±±¯±±°°¯®¯¯°´ÂÍÎÌÊÉÈÌÍÌÌÍÎÎʶágrui{²ÐÏÍÈÆÇüÈÈÊËÄ¿¯‚�¾¿…Ó×Õ×ÛÝÜÓÅ´¡––š—’�މ�ywuvŠ¥·ÑÛÙÙÙרÙÑ×ÜÚÙÚÜÛÚØÌÐÛ×Ö×ÜÞßáãåèèâ×Ê¿¹¸¸¹¹µ¯¦œ’ˆ€ytrpmlkjkigc_\[[[\bhijd[Z[[Z[[`ehnqux{zxz…•¢®²±±³µ¶µ´´·´·ÆÍÍÍËÉËÍÌÍËÊÎÐÃÉÖ dlzmz°ÍÍÆÅÉÇÃÆÅÊÍÈþ°€…©†ÔרÞÙɲ ••˜žž”‹†�{uu€…’´ÐßààÛØÔ×ÚÙÚÚÛÛÙØÛÚÚÛ×ÌÓÜÚÖÙÜßàáäçâÖÇ¿½½½»¶¬¢˜�‡ƒ~wqlkhfdcb^[WTQOPSSOMSXWVSOONLMRUWUX^diouy{vx�„�£±µµ·¹¸¸¸º¸¸ÆÎÍÍÌÊËÎÍÍËËÏÏÏÌÑÕ empv¨ÎÈÀÇÆÃÇÁ¿ÈË÷¹«yy†ÔÓÉ»§™‘“™œ•�„|yxz~‡Ÿ¿ËÓâßÙÙÕ×Ù×ÙÛÛÜÝÜÛÛÜØÓÙÚÛÜÛÛÜ×ÙÞßáåäÙÊ¿½¿¾¸®¡•Œ†ƒ�}ypg^WRSX]ZTSWWVWZ^`^^a`][ZWND@BMX[WSPT\bhmsv~‰†�‚•®µ¸º¹º»¼¸¹ÇÌËÊËËÌÍÎÍÌÍÏÌÌÏÏÒÖ¤eq~ms¦ÉÃÁ¿ÀÍÈ¿ÇÌȹµ¶—s­ž™–—››•Œ†zwv|‰�¸ÎßãâÞ×Õ×רÚÚÙÚÜÛÛÚÛÜÛÜÚØÚÜÜÛÚÛÛØÚßáäßÎÁÀÀ¹«œ”Žˆ„ƒ‚€|xsoid]WVZaffglorsqpnmljihfedb]WTU^gigbUKKR\chms{„ˆ‚{�«µ¹»º»½¹»ÇËËËÌËÌÍÍÎÎÍÍËÍÑÓÏÑÙ©ko|ns©Ç½¹½ËÐÉÍÍËÁ½º¦•rŽˆ—œ˜Š�}zxvwƒ˜±ÆÕßáÝÚØÙ×ÕÚØÛÜÜÖ×ÙÛÙ×ÛÛÚÛÜÜÛÚÚÚÚÚØØÞâà×Ǿ¿¸¥�„�ƒ„„…‚~yrmjjjmpqtwywtuutvxyxwtrolmnljgeccfjjmlmhYHEKU`fkns}†|t’®³¹ºº»·ÁÌÈÊËÊËÌÍÌÍÍÌÍÍÎÐÒÒÏÒÙªorƒux­Å¼½ÈËÌÑÎÌÆÀÁ«žsž�‰‚{yyus~•ª¾ÖÞÝÝÙÍÓÝÛÚÛÛÛÛÜÜÜÛØ×ÜÛ×ÙÚÖÙÛÙÛÕÓÚÙÚÛÜÞÙÎÄ¿¶£�€„‡‡…‚zunjikosvy{}~}|zvuwxxxwwxwsqrpnmkijnnmnnool^QJMV_goqsvwr}¡¯³¸¹¸²£·ÌÇÈËËÎÌÌÍÆÉÎÌÍÑÑÑÑÏÎШsz‡v}©´¸ÉÇÈÑÑÑÌÃöŸs–�xutwy„š»×ßàÜ×רÛÖØÜÜÖÙÞÜÛÛÛÛÜÛØÕÛÚ×ÚרÜÚÙÙ×ÚÜÞÜØÑÉŽ¥‡{~‚†ˆ‡ƒ€zuqlimpruwyz{}||||{ywwxyyvuwxvsppqrompssttttqojcZSSYajqsstvx“¬¯²¶¸ª|²ÊÉÉÉÎÍÌÎËËÎÍÉÎÒÒÐÑÌÇ̬xtƒsvФÄÊÊÎÒÓÐÉÈ»§i�uvyzЦÄÜÜ×ÚÙ×ÙÛÚÛÞÜÛÛÚÚÜÜÚÙÛÚÛÜÙÕØÖØÛÜÛ×ÑÙÜÛÜÛÕÐÌË̹”wq~‡†„„�|wrnllptttvxzzyxuqpopqprtwyvstuurqqrpnnpqqqpnpqpmid\Z\aflsuvxx�§®°µµ™|o{¯ËÇÇËËËÍÍÌÎÍÉËÐÑÎÏÏÌËÕ²us|uu”ÀÇÌÏÑÑÏÌ̽ª^x�•©»ÏÜÞÜÏÍÚÚÚÚÙÙÚÚÜÛÛÜÛÛÛÛØÙÛÛÚÛܨרÙÛÛ×ÐÒ×ÖÒÍËÌÏϳƒot|„‡„�}xtqommruuvwxwusmd_^`begjmpsuvttsrrrrqqsqnmjgdcdhlmmjfa^aeiqsvz{�¥±¶µ˜‚{qz«ÌÉÆÉËÊÆÄÍÍÎÌÍÏÍÍÍÏÏÐØ¸}q�y|¦ÅÊÎÐÐÏËÌÆ°j¦ÈÛÞáßÙÕÖÙÚÛÚÙÙÙÛÛÙÜÛÛÚÚÚÛÜÛÚÚÚÚÙÔØÙØÚÙÙÙØÐÕÐÉÌÑÒήyiv||‚‡�|vrqppqstsuxvqjdc\VTV\_`bejmmqsrsrrstuuuuqkgc^YVTX_eikkhaaejmsy~ƒ‘¤²µ¼šn{ƒrx§ÊÊÉÊÊÈÅÉËÌÍÌÎÎÎÍÏÐÍÏØ¹zlyuu¤ÈÎÑÑÎÉÌ̵ˆÜÚÖÛØÖÙÚÚÛÛÙØÙÙÛÜÜÛÚÚÛÛÜÛÚÚÙÙÙØÚØÓ×ÙÙÙÙ×רØÖÎÐÒÒÌ­�ks}}|€ƒ�xnopprvuqqqng_XTPNPSUY^aachlknpprsstvxwusmhfb\WOJIKS]eijgcbfpy�„�—¤¯·ÈÄ—s{�znrŸÇËÈÈÌÌÉÉÊÍÌÌÎÍÎÐÎÍÍÍ×¹xk�tr¤ÎÐÐÐÇÊÏ´ŠÙÆÄÙØÚÚÚÚÙÚÙÙÙÙÛÜÜÛ×ÙÛ××ÚÙÙØÙÙØÕ×ÙÖרÔÖÙרØÔÏÒÓͳ�€xx}~~}€…}qllquvsqngbZSQOKOTWXZ^bcehlmnoqrrssuwxvrkhfb[VRLFCFNW^che`iyˆ‹’ ¬³ÃÎËÌ¥ro}|po”ÁËÆÊÊÈÉÉÊÌÊÌÊÍÏÌÍÎÌÎØ»|r�px«ÌÏÒÉÇε…ÓÒÒÙØÙÚÚÚÙÚÚ×ÕØØÙÛÚÚÛÜÖÔÙØ×ØØ×ÖÒÖØÖ××Ö×ÔÒ×ÖÑÎÒнž�‰{y~xy��sjpvxwqkd_][]bddcefdcddffhkmnprrqrrsvxvqkgdc^ZWNGFEEHQ]eigty|Œ’�«´¿ÌÎÎÓÓ¡su�€tm�»ÈÆÇÈÊËËËËËËÎÐÍÌÍÌÍÏÕ½€q€{{«ÍËÉËϹ…ÓÛÙÙÙØÚÚÙÙÙÚÓÒ×ÓÖÛÙÚÜØÖØ×ÓÓØØÖ××רÖÔ×ÚÚÓÓ×ÖÏÎÓÈ­˜Ž‡yw€}xqntsjnuwyumdabhmsuvyvtrppmkklmmmopssqqrrttsojgffb]ZVSRPNORW^hillz”£¯µÀÊËÑÑÏÐϸ‡u€vl„²ÈÇÈËÍÌËÌÌÊÍÏÎÌÎÌÌÏËÑ»~o†}~­ËÉÌѺ‡ÔÚ×ÙÙÙÙÙÖØÙØØØÙÙÙÙÙÙÙÕÔÙØÔÕÙÙ×ØÙØØÕÒØÚ×ÖÖÕÕÎÎϽ¤‘Œ‰xwxqlihcltvwxqkkpuz}|xx{ywutttrqrsrqrsutrrqrrqqnkiiheeggddfggfa_ceck‰¨±¹ÄÍÎÉËÌÏÒÓØ»�st€ym€±ËÊÊÀ·ÉÊËÊÌÍÎÌÍÊÌÑÌÈϸqƒ{€¬ÉÊι‡ÕØ×ÙØÚÙØÙÙ×ÖÙÚÙÚÙÖØØØØ×ØÙÚÙ×רÙÙØØØ××ÔÒÔרØÏÌ˸Ÿ‰ŒŠxz|rkfe]druwxuqrw~}{xxxvsrsrrsttssrstutqooqqppoljiiiklkjloooqspiii|¦¶»ÅÎÎÏŪÄÑÎÎÐÒÍ›sx‚}oy¨ÇȺ¶ÅÈÊËÌÍÍÌÌÉÌÏÎËËϾ‰o{y¡Äʶ†ÕÚ××ØØØÙÙÙÖØ××ÙØÙÙÙרØÖÓØÙØ×ÕÖÔÖØÖÕÓÕØÛÝ×˺©ÀÍ»£��ˆz{xnebZTiswyxuxz~€|zyvuutpmmnoqrssttttttqoooppnomjikkjhhiklmnquxwrwš¹ÂÌÐÎÎÐ̺ÊÔÍÏÑÒØÓ¤rqƒ|ls¡ÈÃÃÇÅÆËÊÌÍÌÍÎÍÊÇÇËÉÎÉ”s}w�È·†ÕÛ××ØØØØØÚÚØØÙ×Ö××ÙØØØÖÔØ××××ÖÒÒÕÖÙÜÚÔÊ»ª™Œ…¨È½¬šŽ…�}sh`]OXnvxyxx|}�~wuusrpnjebcfkmqsttttvvtqooppponkjkjfbaeiklorsw|z�¸ÉÎÐÐÌÌÐÓÕËÈÎÒÑÐÐÒͨwp‚}lr ÃÄÆÄÅÊÊÊÌÍÍÍÌÊÈÉËËÆÔÑ•oƒ�z¡´†ÔØÖÔÕØØØÙ×ÖØÙÙ×ÖÓÒØØØÖØÙØØÖÔÔÕÙÚÚÕÎŰŸ‘‰ŠŒ�‘›½Æµ¡�‡ˆ�pd]ULarxyzz{}~~yuspkgdb_\[YY^cilpqtuuvtrppqqqolige`YV[bhjloqqty}”ÂÏÌÎÑÍÍÎÐÎÄÂÊÑÑÏÐÐÑÖ¸|k|}no”¾ÇÁÇÊÉÉËÌÌÌÌÌÏÍËËÇÊÕÊ’o�ƒyއÏÒÕÑÑØÙÖÌÒÖÙÕÖÚØ××ÖÖ×ÕÖÖÔÖØØÚÚÖÍ»§™�…‡‹�”™™”‘˜·Â¯š�Œƒnc\PQdsy{zz~€}xtqkfcbcb^[XVUUX^cinqtvvvusrrqqkeb^WOLQVXYZ`ejlpw}’½ÊÉËÏÑÐÏÏÐÎÈÈÏÐÎÏÒÐÏØÂ†n€rj�»ÃÃÉÈÉÌÌËÌÍÊÊËÌÍÊËÏÒË–q�…u…ÑÖÖÖÖ×ÙÕÍÕØÙØÙÚרØÖÖÕ×ÚØØÛÚÓÆµ¢“‰‡Š�”™˜•�‡�ytn{£¹´¥•…pc[OTcsz||}€~zuogadhiijd^YUSSQTZ_diouxxwwwusnb\XOHHMSTURLJS^flu|•¾ÌÎÂÆÐÐÏÎÑÒÍÍÐÑÏÑÑÎÎÏÙÌ“t�qj‡µÅÇÉÈÌÍÌÍÍÊÉÊÊÏÎÍÑÍÐÎ�q~„…ÔÙרØ×ÙÙÚÕÖÚÛÙØ×ÕÓÓÖÚÝÔÏÌÁ¯ž�ˆ‰‰�•‹ŒŠ€yvrkggeeak�¬¸±�ŠvYKTcr{~�€€|uoe`cgiiigc^YQMOOOSX]bipwzzzzzwhXSKCDHNSW[]XOMT`hpz—ÁÉÉÇÉÎÊÍÏÏÐÑÐÐÐÐÏÏÌÏÐÎ×Кqu}ui~°ÆÆÉÊÌÌÌËÉËÊÈÌÌÍÏÎËÏΟov‡Ó×רÙÙÚÚÙÖÖÙ×ÓÓÕØÖ×ÖϽ¦¤Ÿ“‹‹�”™’Žƒwtnhigffghijljiv�£¤”uTLVcq}€�‚€~yrh`bedbcb`^ZTMFGLORW[aflu{||{|yeSJB@?CJQX]bc^SNXemx�ÄžÍÍÉËËÏÏÎÐÑÏÎÏÎÎÍÐÒÐÎÔÑ›nq|vi|ªÄÉÈÌÌËËÈÉÉËÌÈËÏÌÌÏÐÑ¥o‡ÕÙÖÕÖØÙÙØÔÑÕ×ÙÛÛØÉ¸®ŸŒ�ˆŒ“˜™—�‚wsmmjjlomjjllnnmqojlrzp[QNYao~‚ƒ‚|wodbdabcbcc`[UMHEHRXZ^agmtz}}~{iSGA>=ACBBCDEGNY][^iorw{ƒƒƒƒ|jYQHA??@@>>CDDLZcn…³ÇÈËÊÈÈËʾÆÎÍÐÎÎÏÐÐÎÎÎÑÐÍÑÕÑÎÔÍ�su}vo�ºÉÉÈËÌÍÊËÍÎÎÍÐÑÎѽ‡ÒÙÙÑIJŸ�‹�’˜Ÿ¡£’|x{{qkkgeimnonnnnnmmnqpopoorssqoopomkbUIKQVmƒ††„}vqmkie]TQTUVYYVRRRUVW[empty~�ƒ…†…‚ubSNLHFIOQUZ`e^]gq†±ÅÆÉÊÊÈÉËÅËÏÏÏÎÎÏÑÐÏÏÍÌÍÑÒÔÒÐÎÓÔ§qp{un†²ÇÇÊÍËÊÍÏÏÎÏÐÐÉν…ƹ¥•Œ‰�“› ¢›—Š{pfoqkgihkmnnonlllklmnopppqrqpqqooponq|hSJQWn†‰ˆ…€xvvvtqoljhhgd`[VVWY[`hmpty}€�ƒ‡‰‰†}obXTPLKOW^bgoqlpw„œ¹ÇÆÈËÊÉÈÎÏÑÏÏÑÎÎÐËÏÐÎÌÈÏÑÔÑÑÓÑÔÖ¬xlyun€«ÅÇÊÉËÌÍÏÐÏÑÏËλp”‰‰Ž˜¡¡ž•Šuonp{zk_]cedffdhlkjjjjknnmnnopqooponnonnwzxubSTXp‡‰‰ˆ…€~~}zyusolifb^[[[]bgnsuwz}€�ƒ†Š�‹ˆ�xqlb]ZUQRW\dkpuy~‡�‰·ËÈÆÉÊÊÍÎÎÍÇÄÌÏÌÊÑÐÐÎÏÎÐÓÓÒÑÓÓÕÚ»�mxwmz¤ÂÄÇÌËÊÎÐÐÐÐÐлnš�£¦ “�umkp|ˆž¼¿´¦„\RX]][\`cfhijikklmmonpqpppnnklmpwihsrhZXu‰‰ˆŠˆ…„‚�}}yrokfedbcdfimsxyz|€�€„ˆŒ��‹…~utpieb`\[\clqw~‚Šƒi|¯ËÊÆÈÌÌÌËÌŽÌÐÍÎÑÐÌÌÐÎÏÒÓÒÒÓÉÊÒØÃ‹nvwnx¢¿ÄÆÊÌÌÏÐÎÎÎѺx¦˜ˆ}uopx‡—­½ÄÔÚÇÆØÐ¡dKKORUY[^adffggijjkmnmmmmopnlkqshgmwte^z‹‹‹‹ˆ††…‚€~zvtqnnnoonqsvy{{{~���€‚ˆ�“”�‰�yuurnkkkkklpvz„ŠŠqcn—ÄÍÅÈÉÊÍÍÍÏÍÇÎÎÎÏÊÍÏÌÏÓÒÑÑÏ¿ÃÐÑÚɉlvxoy¢ÀÄÇËÌÍÏÏÎÏÓºezmnt†—¬ÀÍÔÔÏÈÐÍÀÂÐÖÓ­qK?CJORSVZ^`bcdehjllijkjlnpomsriioxwpn‚Œ‹ŒŒ‹‰ˆ‡…ƒ�}|zxwvvwwwxyz|}}~‚ƒ‚�‚…Š�’”�Š„yvvutrqqqrvy}€„‰Œ|p^]‡ºËÈÆËÌÍÍÐÌÇÍÎÐÎÌÏÎÌÌÑÐÐÎÌÍÏÐÔÒÙÊ�krvpw—»ÅÅÊÍÎÏÏÏѽ`„”«¿Ï×ÜÝÚØÔÍÌÓÐÁÁÄÀÆÍ±yQ?;AGJLQTV[]_behjijkjjkjlonssihs{tsyˆŠŠ�Ž�ŒŠˆˆ‡†…ƒ€|{|||{|~~€‚„„ƒ‚ƒ‡‰�›¡™Ž†€{wwvvxwvwwz}‚„…‹Švrrhc{«ÆÆÇÍÌÊÍÎËÉÎÑÏÌÏÐÏËÍÑÐÎÐÏÎÏÒÑÑÚÏ–mq|sq�¯ÀÃÉÌÏÐÍλyÈÚÝßßÜÙÕÖÖÖÔÖ×Ƚ¿ÂÀ¼½¿®ŒbE47@DHLOSUZ]`befghhhihilkpsiiv|x{~‰Š‰��Ž�Œ‹‹‹‹Š‡„„‚�‚�€~~€€€€‚ƒƒ„ƒ„‡‰�žµ»°Ÿ�‚|ywwuwz{}}}�…†‡�ŒmbpwpanšÁÌÉÊÊÊËËÉÌÍÏÇÄÏÐÎÎÏÎÏÐÎÍÏÏÏÓÏØÕ£rr|qp‹«ÀÆÇËÐÏй‚×Ý×Õ×ÖØÙ××ÖÓØÏÃÂÂÀ¾»½º»±ŽnO947>DIKMQUZ^acbceeefgihmthjx‚ƒ}}Š‹‰Ž���‘‘��Ž‹‰‰ˆ†…ƒƒ‚‚���€�ƒ�€€‚…‹’¢µÂû­š‡}ywwuvz{}�‚„‡ˆ‰Ž�oWYgvugiŒºÉÆÇÈÈËÌÌÌÎÉÆÎÏÎÑÎËÏÐÏÐÐÐÒÔÑÏÖØ¬tjuuv‰ªÂÅÃËÐÑ»…ÎÒ×ÚØÖÖÔÕ×ÓÓÒÀ»¼½ºÂ¼µºµ´©‰r[D427>CEINUXZ]__accdfffjtjiz†ƒin‰‹‹Ž��‘““’���ŒŒŒŠŠ‡…„……„ƒƒƒ�~~€‚ƒŒž®»ÁÀº°¤Ž~wttvyzz}�„…ˆ‹‹ŽŽpXQTetwmbx©ÆÈÆÉÉÊÍÎÌÌÏÎÎÎÐÐÍÎÑÐÑÏÏÒÓÔÓÐÓ×µ„ntxt|¦ÆÄÈÍÑ»…ÓÙרÔÒÓÍÔÓÏÕÏ¿º»»ºÀ·­ºº¸·Ÿ€paP<106=AGLOQVZ[]`abcbcdsni|‚uYb…‹‹Ž‘‘‘’““‘�������‹‡††…„„‚�~€�‚ˆ—¥¬´»½¸¯¥“€vqptz{|�…‡‰Š‹�ŽrZVRT\n{p^kšÂËÇÆÉÌÍËÊÍÍÎÍÍÐÐÍÍÏÑÐÏÐÓÔÓÓÏÑ×È“kqunw�»ÀÅͺ�ÕÞÙ×ÎÒÖÔÓÒÔ×ɼÁÁÀ¾²·¾¼¾º·«�}tk[F5/27=BFJOSVX[]^^^a`mpl}|jWg…ŠŠŽ’“”””•“‘‘’‘�ދЉ†…„‚‚€}}��…“¡¤©±¶·±ª¢•…xomptw|€�…ˆ‰Š‹�ŽpXYZYX]kvufd†¶ÉÆÄÉËÌÌËÌÏÐÑÏËËÍÍÎÍÎÐÒÒÑÑÑÐÏÔÍ›nlujiаÄǹ…ÕÚØØÔרÖÑÒÖÔó½ÁÂýµ¸¼¼»·®£‹vl`Q@4/18>BFKOSVXYYZ\_clo{xcZv‰ˆ‹�““•–”’““““‘��Šˆ‡„ƒ‚€€~z}„…„Œ™žž¤¬°¯ª¦¡˜�‚tlkmqw|€ƒ†‰Œ‹�Œr`cea\\\bqxi^v§ÇÈÆËÌÌÈÌÏÍÏÏÉÊÎÎÎÉÍÑÑÑÐÏÎÐÐÎÒѦokxol‘·Çµ†ÕÜ××ÖÖÕÖÖÖÙО¾Á¿²²»º¹¹¶³š�|smeXJ:0/28>CFKORTVWY^[dqzvba~‰ˆ��‘“““’‘“”‘‘‘�ŽŠ‡…‚‚€}zz€†††Œ“–𠦧§¦¤ š“Œ|mihmsy€ƒ„†Š‰‹Šviifd_\[[^kwpcm–¿ËÊÊËÉÅËÆÅÌÈÈËÎÏÎÏÐÑÑÒÑÏÐÑÐÌÏѰxkyrm…¶·…ÔÚ×ÖÔÖÖÖ×ÛͽÄÇÅù»º¶·º¸¸º¹ªŒzvqi\NC8/-39>AFJNQUXZYaqyvcd~†‡Œ�‘’“’’””“‘��Ž‹ˆ‡…�€}}zux}€‚„†‡ˆ�™¢¢ ¡ ›”އ|mgehpx}‚ƒ‚†‡Š‹tfgd`]ZZ^[]gtvic�±ËÈÇÈÁÇÌÍÈÇÍËÌÍÎÐÏÐÏÐÒÏÏÏÎÏÍÎ׸{juolƒŸ…ÏÔÖÖרØÖÙÕ¼·ÅÄÅÃÄÿ¿ºµ¸»»½¸±Ÿ…~yvph`VL?4-.3:>CFJQWWX_nyxji{…‡‹��‘“‘‘’’‘Ž�ŽŒ‰†…‚}}zvqorrhenx{~ˆ•˜”“’ކxoqmgdfnv{~�ƒ…†‹Špbdd`^]\][XYerulau¦ÄÆÆÈÉÎÏÊÏÌÉÊËËÌÎÏÎÎÏÏÍÍÍÏÑÑÏÕ¾ƒmsuju…Ó×ÕØÚÙÚÙÜÐÃÈÈÅÄÅÄÄÀÀÁ»·¼¾¿¹´°”‚}xsojc^SG:0,/5;?DJQXXZhx}}~w‡‰Ž‘‘’�����‹ŒŒŠˆ‡„€|{zyvngfcVJLXju{�„ƒ‚‚l[anqlgekt{~€�ƒ†‹†j`ddcb_]\[Y[]`gtufi’»ËÈÇÊÌÌÎÊÇÉÊÊÌÍÎÎÐÏÏÎÎÌËÐÑÐÎÖÆ•pqsj†ÓÚØÚÜÛÛÜÛÓÍÌÉÆÄÅÃÁÁ¿µ²»¿º¶µ¦‰�}xtokgbYJ@:1,06=BFOUWaty…‹zvƒ‡��‘�ŽŽŽŒ‹‹Š‰‰‰‡ƒ~ywxxxtlb]ZWPLRapuvuuvnXWfqwvojkry|~�„Š„h`cca^^``_]\ZX[guvid¯ÊÈÆÌÌËÌÊÄËÏÍÌÌÎÑÐÏÎÎÍÊËÍÏÐÏØÒ¡rot†ÓÛÛÛÜÜÝÝÙÍÊÊÊÊÈÆÇÇÆÆÅú­µ¿»´²°•‚�|yuoedcZRJA6-,184.-3:?DM^u|‹‹‡€ƒ�‘��‹‰ˆˆ‡„ƒ„ƒƒ�~|||~~~{vpkhhllkmmllicfmquz}€|wtwyz|~ƒŠyecb`fg_]][^`_[XXWYjytce�ºÈÈÇËÉð¼ÍËÉÊÍÎÐÏÌÍÎÍÍÐÐÏÑÏÕÕ°{‡×ÞÜÝÝÝÛÛÔÊËÍÏÍÍÎÌÈȼÀÃÁ½º¸´¸´´¡‡‚zutsnhddd^UME<30/4@PVs‡ˆ…~|�‹��‹Š‰ˆ‡†ƒ‚‚€�€€‚‚�€}|zxtpnmmkhfffdgmtx}€��‚€}{wvvx{�‡raaaacc_^ZY_`ba^ZWVYdprg_{«ÆÇÆÄÄÄÆÈÉÇÈÍÐÒÏÍÎËÌÍÏÐÍÏÑÏÒØ´‡ØßÝÝÜÜÝÛÏÉÊÊÍÍÉÈÌÊÈÇÅÄÅ¿»¹¹¶º·³®•…�|vsokihe`XWSKB:21>LPb‡‰ƒ€ƒ‚ˆ�Œ‹ŠŠ‰‡†„‚�€‚ƒ‚„…„‚‚�€€~}zxsnkhebbcgnuz~�ƒƒƒ„ƒ��|vtuz�…l^_bcb`]\\^_^ab`\YX[[^jvnbošÀÇÅÇÍËÄÃÄÇÍÏÑÏÎÏÎÌÍÏÏÏÑÒÑÑÓÁ‡×àÝÜÜÝÞÖÊËÍÌÊËÆÅÇÊÅÂÈÇÅÄþ»¸¶¶µ¶¨Šƒ€yvqkljd\SZ[VPIDAGGJP{އ‰Š���ŠŒ�‹ˆ‡„„„……‡ˆˆ‡„ƒ„„ƒƒ‚€~|vrlgeccfkqx}€�ƒ…†‡„„„zqs|ƒ…k_`aa_^]]_`__]]]]\Z\\W\lzyda‰·ÇÅÅÄÇÆÃÇÌÆËÐÎÏÌËÌÍÍÏÒÑÒÓÔº‡ÖÜÝÝÝÞÜÓÊÍÏÎÌÍËËÇÇÄÀÅÇÈÈÅÿº³´¸¸µ›„ƒ~|xqpof[]\XWZYROUONHb‰††…tt�ŽŠŠŒ‹‰ˆ…„†‡ˆ‹Œ‹‡„……„ƒƒ‚�€~ysld``chnsx|�„„„……�ƒƒ�~su~…�h``_\[\^__`^]_``__\YXZZ\euvf`|ªÅÈÅÈÇÄÇÊÇËÏÌÍËÊËËÎÏÑÒÐÑÕ½ˆÕÖÛÞÙÚÙÏÌÊËÌÍËÅËÉÆÆÆÇÈÈÅÀÁ¼º¹»»¸°ª’†ƒ�|xsokdeb[V[caZZXWNQuƒ�|gfˆ�‹‰‹Œ‹‰‡†ˆŠ‹Œ‹Š†ƒ„ƒƒ„„‚�€€}xrkdbfksy|€ˆ�‡‚���‚………}{‚‡}d_`_\Z]^^`_]]acb`_][Z\ZWV\hsocn›ÄÊÆÆÅÅÊÎÏÍÍÎÌÊÈÌÏÏÍÎÌËÔ¾ˆ×ÝÜÜØÖØÑÎÊÊÎÌÌÊËÉÉÉÈÉÉÇÄÁ½¸¹¼»»º²²¢‰†€|}xpnmifc_]`cb_\\YQWnvqee„�‹‹�Ž�‹‰‰‹Ž��‹‡…ƒ�‚ƒƒƒƒ†‰…~{xsqsux{~ƒ��ˆ}|„……‡ƒ€…ˆ{c`^]_\^`__``^```_]^\\ZYWWST`qtdf�¸ËÈÃÅËÍÎÍÍÍÊÉÊÆÃÌÊÆÍÏÓ½‡ØßÛÛÙ×ÎËÑÑÏÑÐÐÑÍÌÆÆÊÊÉÆÅÇĿ¾µ¶¹º·³˜‰�||ywrnljgdb__eec_\VJHMRdn�ŠŠ‹��ŒŠŠ��ŽŽŠ„�€}||~���…ƒxooprttrnmos|�~yurx‚…ˆ†ƒ‡ˆxb]X]baa`^_c`^b`]_^\Z[ZZZWUUT[mtjcy©ÇÄÃÊÌÌÎÇÅÊÉÊÆÀÉËÌÏÑÔ½†ÖÝÛÚÙÙÀ¶ÏÒÐÐÑÏÐÍËÈÀÇÇÇÈÇÈÉÆÄľ¹¸¹·¹ªŒƒ�{zvqomlihebcdd`ZSM@8>_r}ŒŠ‰Š‹‹�‹‹���Œ‡�{wvwwvtroliea]\__^\\\[]bikllmrz‚‡Š‰‡‰‰wda^ca^_^`bb^]__\^][\\]]\YWXYSVhuoal˜»ÄÆËËÌÊÈÈÆÈÊÉÉËÎÐÐÒ½‡ÖÞÚÙÚÖÉÅÎÑÓÒÑÐÐÏÏÎÄÆÇÆÇÇÅÅÄÃÅÅÂÀ¹µ·°’‚…ƒ{zurpolihfc`a`[RMH??\s|ЋЋ‹‹ŒŒ�ŽŒŽŠƒ}uqonle^Z[]a^[ZXSQRRTXVPOKHM[gpy�‡ˆŠ‰‰ˆuc`\[[[^`b^UZ^[[]^^^aa_^\ZWW[YUVcsugd…²ËËÉÉÌÍÉÆÇÉÆÇËÌÎÏÒ¼‰ÙÜÛÞØÐÎÒÑÒÔÒÑÐÎÎÏÎÍÉ¿ÂÄÆÆÃÃÃÂÃÅÆ¾µ¶´§�†…�|zywsjighjie``^YRLF@Yw}ˆ‹‹ŠŠŒŒ�Ž��Œˆ‚zqbPFCFJS]acbtxmffih`_aVG11;>Ibt}‚…‰Š‰‡pXRNJS^`ab^X]^]]`ca__^^^][Y[^_]XW`rykbx©ÉËÈÊËËÈÄÆÆÅÊÌÍÎÒ¼‡ÖÚÖ×ÎÇËÍÑÑÏÑÑÍÍÍÍÍÏŵ¿ÇÅÅÅÄÅÆÅÅź¹¹¹¤Œ‡…‚ywxummjiligb][YTMH?Ry�…‹‹ŠŠ‹ŒŽŽŽŽ‰†‚xcH:8:?ANZMI[{€tijmgWIEEGJY\UMUky…Š‹‹†kVQNLUaba_bed`]]`ba`^]__]^_^_^^^[Y_nyscl™ÂÍÉÉÈÇÇÄÇÆÅÊÍÏÑ»†ÔßÎÎÐÈÎÏÑÑÑÕÒÎÍÎÎÎÍÄ¿ÅÇÄÆÇÅÇÉÉÅÿ¾¾¼´›‰„}usttutoljhgd\YUPJDDFIKOMHIJIJNPRRTUSPLNRSRTX\[[\ZZZ]_bdda`^WUTVYXWWWRM…ÐÎÍÎÐÑÏËÍÏÐÐÑÐÏÐÎÍÐÏÎÎÍÌÊËÆÄËËÇÊÍÉÇÊÃÄÃÃÅ¿¾ÃÇÅÅÂÂÀ¿½¸Ÿ‘“��Œ†{‰‚iWRRQUUZbfjI10/16AIPUWZ^_ab```aabbba]YX]enu}…ŠƒVAEHMR]lfG:AEHJLKNQQMJNMLLLIHIMQRTUWXYYXY^cbcggfggb\XXYXVTU\YP…ÐÑÌËÎÑÑÍÎÍÍÑÒÐÏÍÌÍÏÎÎÎÌÉÉÌÃÄÊÊËÌÊÇÉÊÂÃÉÇÆÅÂÃ¿ÂÆÃÄÁ¿¼º©•‘��Œ„{||…�€cNMQSTR[biiD30//2:ELOPQSVYXXYYZ[YWQPW`fjpx†‹‚T?CCIOU`olM:>@@DGILQPOPKHHHKLLMQRSTTRSVWZ_bbdeddbc_[WVYZVTTXZV…ÏÐÊËÊÉÈÊËÈÊÎÑÏËÌÎÏÐÎÍÎÎÊÊÍÊÈÆÈÍÉÇÆÈÉÈÇÅÆÈÇÃÄ¿ÃÇÅÅþº¶¯ ”��Œ‚yrƒ„„ƒv]IINSQQ\akb@3///06;DIJKLNPRQQPOMLLV`dgntz‚‡Š‚SDLMQ[bnU972./125;BFGGHIILPSY^aekpu|�…†ˆ‚R:@AEJRZ_fkyyhtyM8;@FGILMLRVWZZYWRPOQSSTRRTW[\XXWYYXWTRTTPS^ca…ÎÏËÉÊÌÌÊËÍÎÎÍÐÏÍÍÏÑÐÏÌËÍÉÈÈÇÈÊÊÊÉÊÊÊËÈÈÇÄ¿½ÅÈÈÇÇÆÆÃ¾À½¼²§š’šœœ�ƒŒŠtnsvjS@?IMSZcjM9930024;BJOQRQSVY[]`elosy~ƒ†‡ˆ�R8=ACISX^ekq~�—¤Š^K@>EIKJHIORVYYXNGJLNNMOTTRWYSQPUSPQUY[]aa`aa…ÎÏÍÉÈÉËÌÆÊÏÌËÍÎÎËÌÐÎÏÎÍËÉËÉÊËËÉÊÌÉ»ÅÊÈÇÈÆÁÀÄÇÈÆÅÆ´°ÀÁ¾·«¦š�•œ�™�†‰nkloo\E>FNTXagG7753126=FMRTTVY[^afjnrx}�„‡‡ˆ�R6:=AHRX^gmq|�…›�|iK>@GLKLNPSUVWSIGMONMLONNOPSSU]ZXY\]_a[TUUY„ÏÏÍÉÊÌÌÎÊÍÐÏÎÌÈÊÎÎÊÊÎÌÎÏÐËÈÊËËÉÊÉÆ½ÅÈÈÈÆÆÄÃÅÈÈÇÇȶ±Àºµª¥¢ž“•¡¡£–‡ƒˆznegnvdNAAKSWcaB7645448@IOTWZ[^aejmpw|�ƒ†‡‡€Q49<@IRX_ekv‡”ƒMRs�†sQD?@GKKMPQSUWSNTWRSTSSUUSZ][ZUTTV^ZRg‡œ“[ƒÎÎÌÊÉÌÌÍÍÏÍÌÍÏÈÉÍÍËÎÎÌËÎÏÌËÊÌÌÉÆÅÉËÈÇÈÉÆÅÇÇÌÌþ½»º¹°£ž¡¢  —’ž¤§œŠ‚ˆ„tngcongYG@ENUh_?967788;CLTY[[^afkoty|~�„†††O28;?ISZadn„�‘„O3;J`ryv[I<><:99:?HPTVY]chlqtw|€ƒ†‡‡†P/5:?GS\al€Ž��xRG=77Dt™”Š^D>@HKJNQQTSQQNONPUTf�Ž}{›§£ƒožÒÎÚÝâȃÉÊÉÆÈËÊÉÉÅÇÍÍÊÇÇÉÈÊËÉÊÍÊÊÍÍÃÂÇÄÄÄÆÄÄÄÅÆÄ»´±¬©©§¥£�˜™›–Ž•™›™ˆˆ†xqy�‰‡ysntl`behkd\SP^[?:9;<=>?DJOV[_ehlpty~‚ƒ„†…ƒ~V/3;?HU_iŽ�ŽŠfCFJKC9Ce…–‹†zX<>DEAEEJQQZhr]Rh‘Öஞ¬Ÿyw²ÞÛÄÉÌÆ­‚ÉÈÅÃÃÊÈÇÆÆÍÍÍËÈÇÃÅÊÎËÈÍËÊÌÍÈÄÆÂÀÂÁÀÁº¯©¦¥¦ª®®¨ž–”—œ›�Š�™�ŽŒ˜†|Š‹†yglvpfa[]^ekt…ІoYK?98:?AFMUZ_eilpuxz|~‚€|wZ726=HR`yˆ‹‹‹VBCFILI=4AZt‹š‰iWG?P\`lx€œ¬§{^k�ÒÒ®§¤�…©Ä·®­ª§¥”‚ÉÅÁÅÈÊÍÎÇÅÌÊÆÈÊÉÉÊËÎÌÈÈÉÈÈËÉÇÅÃÃÀ½½»·´¬©ª¨¥¨®°¦˜‘‘•œ£ “‡…Žž”Œ—™›¢–‚„|q}€zy‚xjefjy�”œ¢¢˜ŠvaTMKOU[agntx|€�„ˆ‹��‹„‚fC=9?Md�“–˜˜•�jPOOQQSVPA>E[q•² t�½ ™–•§ÁǺ°¬‹¤ÇÁ¹º¹¸¿¼²±³´´´¶©�ÆÆÄÂÅÊÌÍÊÈÉÆÂÆÉÉËËËÊÉÈÇÈÈÈÉÆÅľ¸³¯«¨ª©©«ªª¯²©™ŒŠŽ— ¤ “‰…�•Š‹�š�u`WRSUQQRVSMHCGMNRSYgmzzmZA89@NW[iw|€ƒƒ�€}yumfcaadfda]XQG;5434@O]bachmqvy{|€mPEDDFKPW`g^OVeƒ¸ÈÉÉÌÏÎÍÐÐÎÎÎÎÎÎÑÑÎÎÖ»jw|nm’»ÉÈÉËÊÌÉÈÊÉÌÉÉÍÎÊÍÎÒ¿…‡ÔÙÕÕÔÖÙÙÖÕÒÔÙÛÚ×Óê�–‹��‹‘šš’‰qmkjjjkmmkjjlmnlmpqpkgeYECB:ALRXhyƒƒ„„�|ysmhda`YOHC?>:620002AVdc`cinrvy|€‚‚vVA>=?BFNYfiVTf”ÄÆÊÍÎÎÎÐÍËÏÎÏÏÐÎÑÐÎÎÐÖÇ�js~tnŒ¹ËÊÊËËÊËÊÈÍÌÈÍËÈÊËÌÔ·…ÑÚ××ÖÔÔÕÕØØÛÖË¿¬š‹‡‰‰�“‘�‹‰‚wlgddiljlkkllllopomoonqshSG?AB:AJOWiy�„………�|wrmjgfaVKEDCDFHIGEDIT\\[_glosw{‚„„~dC987987:I^YWo¨ËÊÉÌÏÑÏÏËÄÆÊÍÎÏÏÎÎÎÐÒÒÜÒŸrs}sj‚²ËÊÊÊÉËÌÊËÎÌÎÍÊÈÈÎμ…ÏÖÓÓÖÙÙÙ×ÑÅ·¦š�ŽŒ„�š–•”�„wmiffhjiimlllmmloonooooonmkkpkYMF@CLPWhz�„†ˆ†ƒ~wtsssqmid_\\\][WSPPRTUV\elprvz}€ƒ…†„tYHD@>=716BO\€ºÊÈÉËÊËÎÍÏËÆËÍÏÏÎÍÏÏÊÎÔÒÖÖ¯}oytl}­ÊÊÊÈÉÍÍËÌÍÎÐÎÍÍÎк…ÎÓ×ÚÛØÌºª�”��•œŸ•„�Œ‡~yrhefjjklnnmnllnqnnonoqppooomly�ucNCJQRWi}…†‡‰ˆ†„}~~|ywtqmkigc_ZVUUVVY_florvz}�ƒ†ˆˆ~fTMKLLIJOKK^ŽÅÉÇÉÌÿÌÍÎÏÎÏÎÐÎÎÍÏÐÎÏÓÓÎÐÔºƒoz|oy§ÆËÈÉÌÍËÊÌÍÍÍÍÑÏÑ»‡Ô×ÏÄ´¢”‹Š‘—› ¢›“ypyxnhhheimnpnmnmmmnnprpopoqrrpnoqoxxlqvqcUVTQYn€‡ˆ‡ˆ‰‰ˆ†…„‚}{wsokheb`]\[\^aekqsux{~€�‚„‡ŠŠ…r_UQQUY_gi^e”ÅÊÉÈËÈÇÎÏÏÎÍÍÎÐÎÎËËËÎÑÓÑÑÏÏÖĈjwzou�ÁÈÈÌÌÊËÍÎÍÍÎÑËÊ»ƒ»©—‹ˆŒ”›Ÿ¡�”ŠƒslgfkiehhhkklnnkklkklmmoopppqppqoonoqxkeksuslcXO]v„ˆ‰‰‰‹‹‰‰ˆ‡…‚�~{vqmjihgefggjmrvwy|}€€�ƒ†‰‹Œ‰pd\USY`iqqr—ÂÈÉÊÇÊÐÏÐÍÐÐÌÏÍÌÐÍÌÉÌÐÒÑÐÒÑÑÙÈŽmszor•½ÇÈÈÉËÌÏÏÍÐÐË̺o‘Š�–ž¥¡—‹~vpmn|�•†n[Z_abcabhiijkjjlnnnnopppnoonmmlrtjjkrvvsmd\k�‰ŠŠŠ‹�Œ‹‹‰‰‡„ƒ�}zxvtssrrqqsvxz{{|}€‚�€�‚†Œ�‘ކ{qid^X[dnu{šÄÆÆÉÊÌÎÎÌÉÁÇÏÎÊÏÑÐÏÎÏÏÑÓÒÑÒÓÓÙÑœorypp޶ÄÄÊËÊËÎÐÐÐÏкo ¤¥ –†xnkmx‹šªÇÒÇÄ·‰\OTXXY[^beghijkklmnnoponooollkttiijx{wtrpp|ˆ‹Š‰Š�Ž��ŽŒ‹‰‡†…‚€~}|{{{yyz|}}~~~‚ƒ‚�€‚‡Ž“•’‹�ytpmigipx‘¹ÎÈÆËÍËÌÊË¿ÅÐÎÍÐÐÎËÎÐÎÑÒÓÒÔÍÆÎÖÔ©vqyrnŒµÃÅÈËËÍÐÏÍÍмwž‹|snqxˆ›­»ÉÉÏÚÌÀÏÝÓŸ`IGKPSVZ\^adfffghjmnkkmmnppnkutijo~|stvux‚‰‹ŠˆŠ��‘��Ž�‹‹‹Š‰ˆ†„‚��€}~~€€€€‚„„‚�‚…‰�‘”“Ž…}zxttssuz‚ˆŠ«ÍÊÇÉÊÌÍÍÏÐÈÊÏÎÏÍÊÏÎÍÑÒÑÑÑȾËÐÕÛ«spyrpŒµÄÆÊÍÌÎÐÏÏÒ½avnu„—­¾ÏÖØ×ÓÉËÐÈÀÇËÑЩnG>AHLOQUY\_abceilmiikkklnonvwjlt€}z}|y{ƒ‹Œ‹ˆŠŽ�’‘�‘‘�‘���Œ‰ˆ†…„„„ƒ€€��€�‚„…„ƒƒ…‡‰Œ” ¢˜Š{{{zyyz}ƒŒwjžÇÌÆÈÌÍÌÎÐÈËÎÏÐÌÎÐÌËÏÐÏÐÍÍÏÏÓÓÔØ®umvsq…­ÃÅÈÌÍÐÐÐÑ»e–©ÀÏÚÝÞÝÚÔÔÓÐÒÔļ¿ÆÉ¨vT>8>EHLORVY\_behjijijjikmntxlmvƒ…ˆzp{}„‹ŒŒŠŠ��‘‘’”•••“‘�ŽŒŒŠˆ‡…„„ƒ‚‚‚��ƒ„„‚�ƒ„‡ŠŽš®»¹¬™„{{|~~~�…ŽˆihŽ»ÊÆÉÌÊËÎÍÉËÐÐÌÌÐÏÌÍÏÐÏÏÐÎÍÐÑÑÖÛµ{kyyn~¡ºÃÆÊÍÏÎϺvÊßáÝÛÙØ××Ö×ÔÔØÎÁÀÂý¹½¼¯ŠbD67>DHKNQUY\`cfefgghhijjoxmlx…‡„b^y~„ˆŠ‹Š‹Ž‘’“••——–•“‘‘��Œ‹Š‡……†……„ƒƒ‚�~�ƒ†‹š¯ÀĽ´¥�}yx{~€‚ƒ‡Œ�|ib}­ÊËÈÊÊÊÌÉËÍÍËÂËÑÏÎÏÎÎÐÐÎÍÏÍÒÑÒÛ¾ƒmyxm}žºÄÆÊÎÎк{Ì×Õ××רØ×רÓ×ÒÃÀÁ¿¾½º¼¹¹«‰lP:26?DGILPV\_a`aceedfhhkxqm{…‚qT\u€ƒˆŠ‹‰‹Ž�“•••———–”“”•“‘��‹ˆ‡†…„„„ƒ�~}€€€‚‡“¤³»½¹´ª•xy{}ƒ„ˆ��|wqer ÅÉÇÈÉÊÌÌÍÎÍÆËÐÎÐÑËÌÐÏÐÒÐÑÓÒÏÑÚÆ‰jqvt~›ºÄÄÉÎѹ‡ÓÔÔÚÙÖÕÒÐ×ÓÑÖǺº»¹½Â±´º´´ ƒpYD416BFJOSVYXZ]_]bsx€rcixˆ‰„�‚…ˆŠ�‘’“””“”••””’‘�ŽŒ‰†…ƒ�€}zxz~‚……†ˆ‹�“œ¡¡¢¡ ž•onsx}€„ŠŒv`^]]hvtbd‰¸ËÆÉÌÌÉÊÎÍÍÏËÈÍÏÏËËÏÐÐÑÐÏÎÑÐÏÕÀ†huwi~ªÃ´‡ÕÝÙ×ÕÖÕÕרÚÑÅÅÃÂÃÀ¿»²¹»¸¸¹·¯�ztndYI;2/19@AFKORUX[\]`ox€{kov~‰†€�…‡Š��’’““’’“’“��ŽŽ�‰‡…ƒ€~~~|wssttmiov{|�‹•˜—––”Šxljouz€ˆ‰ta]\\[cqtic|ªÇÌÉËËÆÅÊÄÊÉÇÊÎÐÎÎÐÐÏÑÒÏÎÑÑÎÌÓÅ‘ktxov¡´†Ô×Õ×ÕÖØÖÔÚÒ¸¼ÇÅÄÂÀ¼¿ºµ·¹¸º»·¡‡~yvpfYND8/-4:=BFINSX^]\hx~ƒ€„{p�ˆ‚‚„†‰��‘’’‘‘‘‘‘�ŽŒŒŒŠˆ‡…�}~~|ztlhibO?CRcpv{‚†‡††‚xojhlsx|†‡o_]Z^^[_mwqck—ÁÌÇÈÅÃËÍÌÆÊËËËÎÐÏÐÏÏÒÑÏÏÎÎÏÍÔΕjpslv�†ÓÕÔ××ÙØØÙÙÇºÄÆÄÅÃÄÁ¾¾¹¶»½¼¼´°–‚}ytniaVLA5//39>BFKPVZ[_rx{‡�‡ss…ƒ�…†ˆ���‘��ŽŽ�ŽŒŒŒ‹‰ˆˆ‡ƒ€|||{yvmc_\SHBBHUepvxwwwtsqkgipv{~„†n`_\]\YX_lsrffЏÆÅÈÉÊÐËËÎÉÉÊÍËÍÏÏÎÎÐÎÎÎÎÐÑÎÓОopwpj…ÔÚרÙÚÚÚÜØÌËËÆÅÅÄÂÀ¿ÂÀº¹¼¿¼¶¸ª�‚|vqnid^TF:2,.5<@DGJQUWfuoxŠ�†„‚€„†‰���Ž�ŒŒ‹Š‰ˆ‡†††††…‚}{{{}}|vla][YVSLKYippoonouwrihouz}„‚la`]]\ZZ\]`lxocy¦ÆËÆÈÌËÍÌÈÉÊËËÌÍÏÏÏÐÎÎÎÌÍÐÑÎÓÓ¬znul‡ÓÙØÚÛÛÜÜÛÕÌËÉÉÇÅÅÃÃÄÂÁ½¯±¾½¸µ´›ƒzwsoiebVID:/,.7?CEILSZr{x‰‘Ž‹ƒ€�„‡‰�Œ‹‹Œ‹‰‡‡‡†„‚ƒ„ƒƒ‚|||~��€~yqidbbdbcgkmlmmnsx|zqlovy|‚|g_^_`_`][ZY`nwqdj•ÁËÇÉÌÌÌÌÅÇÎÎÍËÍÐÑÐÏÎÎËÊÍÏÑÏÓÚº�ls‡ÔÚÙÚÛÛÝÝÜÑÈÊÉËÊÈÈÈÇÇÅÄ·²¼½¸³´©�‚|yukee`ZSJA6.-2:?EOVSf„‡‹Š†€~€€ƒ†ˆ‹‹‹ŠŠ‰ˆ‡‡‡‡„€�‚�‚�€‚‚‚ƒƒ‚€~zuplkkjihhijjmsx|€yrrvy{�xfea_aa`_[ZZ[_fuwgb…´ÈÈÈËÌÍǾÄÍÌÊÊÍÏÎÏÎÍÎÍÍÏÐÒÑÒÚ¿†n†ÖÝÛÛÜÜÜÚÚÑËÍÍÌËÌÌÉÈÆ¿¿Ãþ»»·¶¶´£ˆ‚|wurmfab_XPH@4//5ELTQTv�‹‡…ƒ†„€‚„ˆ‹‹Š‰ˆ‰‹Šˆ†„ƒ�€�‚�€ƒ††…„„ƒ‚€~|xtqlid`adgkrw{��~yvvvy€tdgb]^[[_`]XWYX`sxk_t¥ÃÈÇÉËȸ³ÉÍÊÈÌÎÏÐÎÍÎÎÍÎÐÏÐÏÑ×Ä�†ØßÜÜÜÝÝÜØÌÊÌÍÎÍÍÍËÈÇÀ¾Ã½¹¸¶¸¹´±•„~xwtqlheea[TLD?85CEJRNc†�‹ŠŒŽ„|�ƒ‰��Šˆ‰‹Œ�‹ˆ…„ƒƒƒ„„„…‡ˆ‡………„„ƒ‚�}{vqkfcbbhpv{~�‚ƒ‚~zvsxpbca^\Y]_bca[WWV]krm`h“½ÉÅÅÅÅÅÉÉÇÇËÏÑÐÍÎÌËÌÍÑÎÎÑÐÐ׿‡ÚàÝÝÜÜÝÜÑÈËËËÌÉÄÈËÉÆÈÇÅ¿¼º¸¸¹µµ¨�„�zusmjjfcZWXSQIBIJDMPUq‰‰ˆ‹‹{lzƒˆŒŒ‹‰ˆŠ��ŒŠ‰†……†‡ˆ‰‹‹Š‡†‡‡†„„ƒ�€~ytnhedfkqw|‚„…ƒ‚xqx~oca^[]]___`a]YYZ\[brufd‚±ÈÅÅËÍÇÄÄÅÊÎÐÑÍÎÎËËÍÏÏÐÒÑÑÓ½‡ÙàÞÝÝÝÝÚÌÊÎÍËËËÇÆÇÇÀÃÉÈÆÆÂ¾º´³´¶¶Ÿ„„}xwplng^VX[[`[QRSNLNO\v„†„€lYqƒ†Š�Œ‹ˆ‰Ž�ŽŒŠˆ††ˆŠ‹�ŽŒ‰‡†‡†……ƒ‚��~zuniddhlrw{�„„���yqz~k___]_a_^]\\]]\[\ZXdt{n]r¤ÃÅÅÄÅÈÅÆËÈÇÏÍÎÎËËÍÍÎÑÑÑÓÕ¹‡ØÙ×ßÜÛÝØÎÌÎÍÌÍÌËËÆÄÂÃÆÉÈÅÃÁ¾·µ¸¸µ­‘…‚~{urqm_^aZW]e^XXYUOKMVm|{r`Xo�…Š�Œ‹‰Œ�’’�Œ‰ˆ‰‹�Ž��Ї…„ƒ‚ƒ„ƒ‚€€ƒ‚{tnihkpuy|€€€ƒ~w€|e\]___`_\^``__^ZWZZ[_lwnaj”¼ÉÆÇÈÄÅÊÉÉÎÍÌËÊÊËÌÎÎÒÒÐÔ½ˆØÙ×ÝÛ×ÚÔÍËÈËËÌÆÆÊÈÈÇÆÆÈǾ¼·º¼½»³¯¤‹†„zxpljifa]\ceba^[ULD>@Pflegq}ƒˆŠ‹ŠŠ�‘””’�‹‰‹Ž�‘�Œ‡„ƒ~�‚‚�‚‰‘�†}xutuvx{||}{{}�…ƒ~„zb[[^^`__]^bb`_^][Z[XVYamrhd‚³ÊÈÆÆÄÈÌÏÎÍÍÌËÉÊÍÎÎÍÍÊÑ¿‡ÖßÝÛÚÖÕÑÏÎÊÍÏÎÎËËÉÉÉÈÈÇÅÄÁ¼¹½¹¸¹ºµ°™‡ƒ}~{tnmkihhedcdea\VOE<44Qmpos}ƒ‡‰Œ‹‹Ž‘••”“Ž‹Ž����ˆƒ€}zyz|€…‰ˆ€wrrstqmkmqtuvux€…†‚…tb^\__^_`_^^``]]^][YWVVSYhuk`w£ÄÊÅÂÈÌÍÎÍÌËÉÊÊÃÇÎÆÉÎÒ¾‡×ÞÛÛÙØÑ¾ÈÒÐÏÑÏÑÏÌÉÂÇÉÈÆÄÇÊĹ´¶¹º¸¬�„~~zxuomjjlifaab_]VNG@72Hjrrv{�…‰‹�Ž�’––—•’���ŽŽŠ„~yuuvvutrrqokea``bb`]\]cjmlls|„ˆ‡‡qcdaa^]ba_aa^^^\[[[\[XTUTUdspdh�¼ÆÁÆÌÌÎÌÅÈÈÉÊÂÅËÌÎÐÔ½†ÖÛÚÛØØÑ·ÂÒÑÐÐÏÏÏÌÌÄÁÈÆÇÇÇÈÇÃÄÄ¿º¸·¶¶œ…„ƒzytpnmolhdba`_ZQHA90?etuz|ƒ‡Š�“’’•™š˜–“��ŽŽ‡€yspomkh_[^^`a][ZXVVWWVSRRWahp{ƒˆ‰†sc_``_ab`\^_\\]\[\]^]ZXWXVR]pteb€¬ÃÄÉËÊËÊÉÈÈÊÊÊÉÍÐÏÒ¾‡ØÝÚÛÛÙÑÌÍÐÓÔÑÐÐÏÏÐÊÇÈÅÆÇÇÄÃÂÃÄÄÃÀ´µµ¥Šƒ…‚|{yusonnkhd`^_]VNC;39`xz~‚…‰’”“’“˜›››™•’‘Œ‡€xkYIGGEKTQIIXba]XUSLBFJ;28I_oy‚‡Š…iZ]]^``WW]][[]^^_a`]\[YWZ[VT[kvoap ÃÌÉÉËÎÊÇÆÈÇÅÈËÍÏÒ½ˆÙÜ×ÝÙÐÊÎÑÑÐÑÑÏÎÎÎÎÍο¼ÅÅÆÅÂÄÄÁÂÄĺ¶¶³¡Š…ƒ�|yywojlmle_[YXSKB966Wz‚€|ƒ‡Ž“”’“—œŸ£¢œ˜–�ˆ€oT?68951/57AJENdt}…Œ…\N[_`aa[\`^\^ba_``_]]\Z[]^_\WZjxsci’¿ÎÉÉÌËÉÄÄÆÅÈÌÌÍÑ»ˆÕÜÖÍÏÅÉÍÐÒÏÐÒÎÌÍÎÍÌÊ»»ÇÅÃÆÅÅÈÇÅÂÂÀ¹º»µ›‰†ƒ}vvwrnmklga]VSMD=<<8Mw‚„€{}�ƒˆ‘•”–™�¡§©¢�œ•ˆxaT[_`_`^ZQC601468;=<5&'In{jU[nzƒ‹€[R]ba^`dea^\^aa`_^__^]^_^^^_]Z\euyic‚´ËÉÉÉÇÆÄÆÉÅÈÌÍл‡ÓÝÚÏÓÊÍÐÎÐÑÕÖÒÏÌÌÍÍÈÃÆÈÆÆÈÇÇÈÉÈÄÃÁ¾¾¾»­‘…}vttsutoligda^TLFBDD>Fq„…‚{y}ƒŒ�‘”˜›ž£ª©¤ —†tgo{{x{„Ž–—ŒwbMBCEHIJD=Kn‹’„nemx‚‰|`\_[WSQUVUVRQZabb`a``____\[\\Z\\`oxqdt£ÇÌÅÃÅÆÉÉÆÇËÍй†ÓÜÝÝÒËÌÏÒÓÒÑÒÐÐÎÈÊÌÊÉÉÉÉÈÈÈÉÈÇÈÇŽ¼¿¼¹Ÿ†y{|zytqqiddc_VPNMKKBBjƒ…ƒ{wy{~‡��‘“—™�¥¬¨¡•…zz€ƒƒˆŽ�‘𢧩¥œ‘†€ylijmyˆ––‹€tqw†ya^YSRROQMJKMMV\ZUTW[_````a^\\Z[\XYfusch”ÀÊÅÃÅÈÇÇÈÇÉк‡ØßÜÖËÊÎÑÏÏÓÎÍÊËÏËËËÌÍÈÈÉÈÈÇÉÉÈÉÈÆÅÁ¼¾½»±•ˆ‡…�{uvuoife_\ZYSTPACf��{vvy{‚‹�Ž�’”–œ¥¥ž”‹†‡‰‡‹—œ˜’��”ž©³µ°­£•‘�‹†Š��Š„{vw|ƒr][XXXVUVSPPTWPNLFDHV^^__ada`_][Z[YW^mria€³ÉÆÁÂÃÄÈÅÇͺ†×ÝÙÕËÎÑÏÀÅÒÓÑÍÌÎÍÎÌÊÉÊÉÉÉÉÉÉÉÊÊÉÅÃÃÀ½½¼½«�І‚|yxutrpkeca][YNBDaz€€zsquwzƒ‹�Œ���‘–š˜––•—™˜˜˜˜–•“���”𠤍¦¡›”‹„ƒƒ†ˆ„}zx{~jZYYZYYWUTUVWYSLIGGP[^^]]_a`aa^\[[[ZV[hsnbs¡ÃÅÀÂÃÇÈËË·‡×ÜØÑÌÐÑÐÊËÒÓÍÎÑÎÎÎÌÌÊËÇÅÊÉÈÊÊÇÇÉż¸¼¾¼¼¼¶š‡†„‚€|ywtrqlfbbb`WOUTGk}zrnpsv|…ŠŠ‰Š�ŒŒ�’”˜ž¢¥§¤�—“����І†‰Œ�ŒŠ…‚€}}~‚ŒŒ{z~wd\[ZYX[\WUVTTTUQLOW__ZWRPQTVTW[]_\Y[[ZY_qteg�»ÊÅÃÇÈÊθˆÙÞÖÍÍÏÑÓÔÏÏÏÍÍÏÐÎÍÎÌÎÌÅÇÇÀÄÈÉÄÅÇȼ·¼¿¼»º·¤‡‡‡…ƒ€|urrplidhh`[Vnw@Rt|{tnkoru{ƒ†„‚‡Š‰‰Œ�–ž¡£¥¥¡š“Œ†‚~}{zzzzyzxvvwxŽ•‰€}~n^^\\[YY[XUUQRVUQUX]ZSLLLMNMLGKVXZ\]\Z[[Y\kvja€µÊÅÃÆÉ͹‡ØÝÒËÍÎÒÓÒÎÎÏÓÎÉÑÍËÍËËÌËÌÈÅÉÊÉÆÈÊÊÄÄý¼¼º·­”Œ�ˆ…ƒ}stxvoighdbc_s‰cFeuxtniknos|‚‚��„‰�“™� ¢¥¦¢š“�†~xtsqomnqrromnqw�Ž˜˜‘…xe]^]\\]][XWTSUWUW_\UQOKKNNPNJGKMKOY^[[\^^Y[gsnbt¥ÃÅÂÇÌ·‰ÕÖÌÊÌÏÓÔÐÏÍËÐÎÊÌËÍÍÎÌÍÎÌÍÍÌÌÌËÊÊÈþÁÀ¾¾½»¶­—�‹‡†‚}|{yslggfhicr……RUjttojiknqv{}}zy}ƒŠ•›ž¡¢¥¦¡š”�Š€ytpmihiiiiggkry„‘˜œ›Œr_^_^\\^^^\[Z[YWY^_XRQSTQPMONLLMJINZ`b`_][Z[X_qxkl‘ºÅÅÌ·ˆÖÓÉÊËÐÓÖÒÏÍÊÌÏÊÂËÏÏÎÎÍËÊËÊÌÎÎÌÊÈÉýÀ¿¾¾À¿»º¤ŽŒ‰†‚�‚~xsnmnpnjfuy�mO`krnkigjmpuxwutuy€Œš£¤¡  žš”‘�…}vqmkjiihggjpv|ˆ“—œœŒm^^__\]\]_^\^]\]]]XTTRQRRQSROPQPNJLX`eb^][YZZ[bq~n`|ªÄÊ·†ÖÔÉÊËÐÓÇÅÎÉÍÏÍËÉÌÍÏÍÍÍÉÉÇÈÌɺÄÊÉÌÌÇÀ½ÀÁÀ»º¼³™Š‡„€ƒ�{rqusqkjws‹ŠZZemnlkhfhkotvspry€Š˜ ¢¢ Ÿ�›˜–’�ƒzuqomjhggkpuy��™œ œ„f__^^]`_]^^]^^]\[XVVUUTSSRSRPRRRPKLQZab`]][YY\_]gvqaoœ¼¶‡ÔÑÊÍÌÌÓÅÀËÆÊÏÊÌÍËÍÍÍËËÌÊÆÈËÇ»ÃÉÊËÊÇÂÃÃÃÁ·´·¼ª�‡„�‚…ƒ‚€{wwusmjyr|•rZcimnlkhiijorpoqv€‹“™žŸŸŸ�œ›˜”‘‹„}vqmifgimqu{Œšž¡¡™{`]_]\^_`_^__^^^[YYYYYYXWUTSSQPORRLJO^dca_^^ZVW\[W`rtgj‹¥ˆÒÍËÐÑÏÐÓÏÐÎÂÂÅÉÌÎÊÌÏËÉËÊÈÈÉËÍËÊËÊÉÈÇÆÃÃÁ¹´·º¹¡�‹ƒƒ…‚€}~~zxunj{yo‘Šb^fjnonkhiijlnoor{†Œ’—›ž�ššœœ™—”�†~vpljknpu~Œšž¡Ÿ›�t]\^]]]_`___^]^__^\[\]\YYXWVWUSSRRMJQZXTX]__[YWWYYVWhtpir‡ÎËÍÑÔÓÒÒÑÌÍÌÄÄÊÌÇÈÐÑÎÊÊÌËÉÉÊÉËËÌËÊÈÅÆÄÅÁÀ¿À»¾³˜�ˆ…„�€}}~~|xqm}‚l‡›s\cglnomggihknnqtw‡Œ“™›œ›šž¢¢Ÿ�›™•�‰…��„‰‘›¡¢¡Ÿœ”‡l\[[_`__^^__^__^^]\\]\[ZYWVYYYZZTQSSPLGIPVZ[^`ZXVVWUWbqpc†ÊËËÎÒÑÐÑѸ»ÓÏËÊÊÆÌÑÎÎÉÁÊËËËËÊÊËÌËÉÇÅÆÇÿÀÂÀ¿¾»¥�Ž‹‡ƒ�||}|{tq~„ll˜�gbfggjmidffilnqrsyƒŠ��“–šœŸ¡¤¤¥§§§¦¤¡žž ¢¥§¤¤£Ÿ˜�‚dYYX]^_^^__^_``_^]]^][Z[[XX[[WVWTQTTMIHPX[\\]a`ZXYYZZUZjq…ÈËÏÏÑÐÍÐϾ¼ÊÏÐÌÉÎÍÍÌËËÅÊÊÌÌËÉÉÊËÉÊÈÇÆÆÁ¼¼»¼ÁÀ½µ›��‹…ƒ€~~}zzxv~„o\zœ~cfedejjcddeijklosz„Š�”™� ¢¤¤£¡£¦§¦¦§¦¥¤¤¢¡¥¤œ’�z]VWWXZ\]^^^__````^]_][\\\\]][WTUWUQNLKIJMPRXYY^^\\[[^]X[gƒÈÌÌÕÓÐÏÉÊÏÎËÌÏÎÏÐÏÍÌÌÎÎÌËÌÌÊËËÊÉÉÌÊÈÇÇÆ¾¼»¼ÁÁ¿¿®•��Іƒ�‚‚~|x}„ta_‹–ldhfddgebbcceghijmu‡�“™œž¡¢¡ žŸ¡¥¥¥¤¥§¦¥¢¤¦ •�‰mVTUVWY[[Z[\^^\]^_`_`_]]]]\]\[YXWVTSPONLJIGDPYZ\_^^[Z[]][XƒÈÎÌÏÏÍÍÆÊÍËËÌËËÐÑÑÏÎÍÌÊËËÌËËÎÍÊÊÉËÊÉÈÇÆ½¼Ã¿½¾»¸¢‘��‡„ƒ„„ƒ‚€{}ƒudZoœˆgfgeabda_`_adeeeiox…ŠŽ’˜™› ¢¡ ¢¥¤¢ £¥¦¥¢ Ÿ—Ž‹�cVWWXZZZ[Z[[[\Z[[[_`a`^^_]Z[\YYZXTTTQQSTSSPINY[^^]]\Z\^][WƒÊÏÑÈÊÎÎÎÎÎÊËÎÍÌÎÑÐÏÍËÎËÇÊËÊËÌÌÌÍÌÌÊÉÈÇÅ¿Àþ¼¿»»²˜��‰†‰ˆ…„ƒ‚€�ve]Z… |cehd^^a][^^_cdfhhmv~‚„ˆ�”˜�ž £¤¢Ÿ››š™��›—”‹„ƒt\WWWXZXWWWZZXWVUVWY[]]\]\\Z[YVWXWWVTSTUVTROMP[^]\\\``bb]ZX„ÎÑÏÏÒÓÎÎÎÏÒÏÎÏÐÑÑÎÌÎÌÉÉËÌÊËÌËËÌËÍÍÈÇÊÉÇÄÄþ¼À¿½¼½§‘މ‰Œ‰………„ƒ�‚zh]Ue•›zbac\Z[ZUZ]]`acefhlsz~€„Š‘•–›žž�›™—–—•‘��Šƒ{eTRSUUVVTSTWXXVURQSTUUSUUX[ZYVTTUVXWXYWTTXTOKR]`\\`__`aa^\[…ÎÑÑÓÔÓÏÍÍÍÏÏÍÏÏÎÏÍÍÏÎÆËÐÎÎÎÎÍÌÊÊÊÇÂÆËÊÆÅÄÆÄ¿ÃÀ¿À¾¸›��ŠŠŠ„…‡†„‚ƒ{j`XSp•™~ic]XWVPOV\^]\`cdfjpvxy{€…�“’”––——˜—�Œ‹ˆƒ}wjPLPQQTVVUUUUWVSUUQOOPQQQSWWURTSQQTZYXXVSSWUROW`]Y[]\YU\aa_Z„ÎÍÌÒÑÐÏÐÏÎÎËÉËÌÏÌËÍÎÏÍÈÌÏÎËÌÌËËÍÌÈÆÆÈÉÆÃÃÃÇÄÁ¾¿Â¿¿­“�Œ‹‹ˆ‰‰‡„„ujbZTXkƒŽ‹|cYUUQJJSZ]\\]_cfgkruusw„…‡Š�’““�‹‹‡€{vqbQJILOSRQSTSQPMKLLLKKOUWVWXVSUXZXUWXVVVUUVURQV^_[\YOQXWZ]^]U„ÐÑÏÔÓÑÑÐÐÐÎËÊÈÈÌÍÎÎÎÏÎÇËÎÍÌÌËÌËÌÎÌÉÅÇÈÄÄÆÃÄÿ¾¿ÀÁ¾¼¤‘��Œ‹ŒŠ†„…{podZUTYclv†q\SRQJBDOY^][Z^`acinpnlosuy~‚„†‡†ƒ€zurqj`a\PHINLLMKJHFIGFFHJJMTSUUVWW[ZZ\[[XYYYUSUUSV^_YY\XRSX_][\XV„ÏÕÔÔÔÒÒÐÍÌÍËÍÎÉÈÍÏÎÍÍÎÊÊËÌÎÏÍÍËÌÍËÊËËÇÆÇÅÆÅÄÀÁÂÁÁ¼¾²˜’�Œ‹Œ‰‡ŒŒˆxphZQQX]][lxfXNOMF?BMWZYXX[]`beiigefgkopsvxyvqnkkeUSU^cZIBFIIEBABFHHKKKKLNPOMORXZYXVVVTVVXUSUVW_a\UVYYXYY\\[ZQKƒÏÕÔÓÑÑÐÉÊÍÏÎÎÐÎÏÏÎÎÎÌÌËËÌÊÉÎÎÉÈÌÌÉÈÊÊÇÆÁÅÉÆÇÀ¿Â¾½·§–’ŽŽ�ˆŠ””‘’ye[SQTZZ`_prcTLMJC<@LSUVVVWZ\^bdedbcddefhjjjgebmdDIIMZfbLAEFECDFDDGIJNPQPSSSSQSVUTRSWVUWWUUUX[aa\WWXWUUYWXWRMK„ÐÒÎÐÐÑÏÈÊÎÎÐÏÏÏÐÏÏÏÐÏÍÍÍÌÉÈÈÉÊÇÈËÉÆÉÉÆÁ»ÆÅ¿ÂÅÂÃÃÂÁÀ¼¸ ’’‘�ЅДЖ�oZRRRWWYcerk_QFHGA:>JRSSRRTVY\]_aaba_^`abba^_j€mA@CGMWejTACIKMNJHIIJLORRRUTPMMQRSUX[[Z\[ZZZ^`dec`_YUUTXWXVVTP„ÐÑËÌÍÑÒÏÍÏÎÐÒÐÎÏÍÍÎÏÎÎÎËÈËÆ¿ÈËÉÊÌÊÈËÇÀÇÆÅ¿ÁÆÃÄÄÂÁÀ½¼­”‘‘��‰�ŠŠ‡“…gOLRSVTZbhsh]KBED>9=HNONNPSUUTUW[\[Z[]]\ZZ`n~‰oA<>CIOWhnV@CHIKLMOPQLLMLKLJHHNQRRUVVXXXX\abbeggghc\YXYXWVTY\S‚ÏÔÌËÌÍÏÍÌÌÊÎÒÑÍÍÍÍÎÎÌÍÍËÈÌÉÆÉÇÊËÊÇÆÉÇÅÇÇÇÈÃÄÁ¾ÅÅÄÃÀ¼º²œ’‘•¢ �w‡‡…„ybJIPRPP[akrcZJ??@>9;CJLLNOSSQPRSRSTSRQPVbn{‚†oB<>@DMU\jrZ=BEJS[blu[<79AECFHILNPSQLQQNOSTQPRSSTTVWY__ZZ[\[ZVTUWVTVSTW†ÐÓÎÌÉËÉÈÎÌËÐÏÎÎÏÐÐÏÎÎÌÎÇÇÈÄÄÆÈÌÌÉÉÉÊÇÇÊËÇÅÃÃÇÈÇÄÅÿ¿·´¶¥Ÿ Ÿ¤Ÿœ‰‡�}ttvhN?FNMQ[bqkZWJ<7:=>AEIHHIJIHHIKKLPV\bhow~�†p@9=DGJSY_eqy]BA=?CEEFIMPRWUUXWUUTPORUVUQOPSY\YVX[[\WUVUTQORW[…ÏÑÌÉÊËÍËÍÍÌÏÐÏÏÏÏÏÐÏÎËËËÌÅÅÇÆÇÊÌÇÇËÌÉÇÈÈÆÃ¿ÃÈÈÆÅÆÆÅÀ¿½¹±©›“�ž¡”‚ŒŒxntvkTA@JMRZbobUTK>68GMTXan]OOKA969BGKMNMNMNOQTX\aekpv{~�…p?59;BLRY`fnt{‰¢šmRD=AHJIIIMPSWYXRIKMMMLMRTRVZURORTNNSYZ[_bbb`„ÎÏÍÌÈÊËÎÉÇÎÏÍÌËÌÍÍÍËÎÏÍÍËËÉÊËËÉÊÉÊ»½ÊÇÉÉÈÄÀÁÅÈÈÇɼ¨¹¾º¯§¢Ÿ”•¢£¦›‰„‰{ofhnueLABKTYcjYOKHB<9DLLKMNQSUVUMHMONMLNNMPOQST[[WV\^^a^VTSWƒÍÐÌËÉÌËÌÌÍÐÏÏÏËÇÌÍËÊÏÌÌÎÐÎÊÊÊËÊÊÈÈÄÅÈÇÉÇÅÇÆÇËÇÂÁÁº¯±©¤¤¤¡¢™”Ÿ¥ª Œƒ‰‡vnhdpogXF@GOUhoXMGDCA=>EMPRRSRSV[_chlrux|~€‚o>06:BMU\_grwxuKDdz†~\JA>DJLLNQRTVUNQXTRUSRSTRW^]\VSTT[^TZz�•h…ÍÌÉËÈÉÊÍÌËÊÊËÎÌÊÎÌÊÍÎÌËÌÍÌÌËÌËÉÆÄÇÎÊÇÇÈÉÉÇůĹ­©§ª¬£›–˜žŸŸ›Ž�‘˜˜‰„‡uxoebdkm`THCHQhdVPIDBA?AIORUTSUW]bfknruz~€��p=/6:BLW\dtƒ‰‡vM65BVlv|fOA;BJMLMPRRTX^\\`]VVXSNPSS[bixu_i—Íáâ©„ËÉÉÊÈÉÊÏÌÇÄÉËËÉÉÌÌÊËÌÌÌËÉËÍÈÅÈÆÅÃÆÇÅÆÆÆÆÂ¹´±®¬¨¦¥žšœ�—�—›œšŠŠˆzsz€ˆ‰|torkacgikcZOJXdVRNIECABHLOQQTX[^cgjntx|}€rA-49BMVayˆŽ�ŒmKIB;6;_�—’oKB=EJILQQSTPPONNNTT^xŒƒs‘¦¦’m„ÄÏÔââÉ‚ÈÊÈÇÄÉËÆÈÇÈÌÎÌÉÆÅÃÇËËÇËÌÊÌÎÈ¿ÃÄÂÂÄÂÃý´­ª¨§«®°¬£™–™�ž‘‹‘›ž�Œ—†z‡�†‰knytid`bdins‚ˆˆwdYNFBCEFHKOSW\`bcglquxz|~~{rG-29BP]vˆŒŒ‹„_CDIMH<;Uy’�‹†hC?ACEHKMOQUY[]\WVU<+0):]gbUO\adkG8<<<=<>?8/.3JswZwœtcagsz~~xpnwwxyz}|x{~~}}~~tƒƒˆˆˆˆ‰‰‰‰ˆˆ‰‰‰‰‰‰‡‡ˆˆ‚‚yy{{zzyyxxttppwwyyvvttvvvvvvxxxxxxxxyyyyxxvvssppssvvuuvv||€€||yyyywwvvvvwwwwvvzzyywwrrqqvvwwxxvvvvvvuuvvyysssswwxxwwuuttyywwppssttuuuuyyttrruurruuuuqqvvƒƒˆˆˆˆ‰‰‰‰ˆˆ‰‰‰‰‰‰‡‡ˆˆ‚‚yy{{zzyyxxttppwwyyvvttvvvvvvxxxxxxxxyyyyxxvvssppssvvuuvv||€€||yyyywwvvvvwwwwvvzzyywwrrqqvvwwxxvvvvvvuuvvyysssswwxxwwuuttyywwppssttuuuuyyttrruurruuuuqqvv††’’’’““’’””““““““‘‘‘‘‡‡yyzzwwssssvvxxvvttuuvvvvvvwwxxxxyyxxxxvvrrrrssttvvxx||~~||zzyyzz{{{{{{{{||{{yywwzzyyvvrrsswwxxwwwwuuuuuuuuxxttttwwxxwwvvuuyyvvppssuuttwwyyssttttuuvvttuu††’’’’““’’””““““““‘‘‘‘‡‡yyzzwwssssvvxxvvttuuvvvvvvwwxxxxyyxxxxvvrrrrssttvvxx||~~||zzyyzz{{{{{{{{||{{yywwzzyyvvrrsswwxxwwwwuuuuuuuuxxttttwwxxwwvvuuyyvvppssuuttwwyyssttttuuvvttuu€€ƒƒ„„ƒƒƒƒ‚‚€€��€€€€��wwttssuuxxwwvvvvvvvvwwwwwwwwwwxxwwttssrrssuuvvzz}}||{{yyxx{{}}~~€€€€€€€€}}{{{{xxvvttttwwxxwwuuuuvvttvvxxssttwwvvwwuuuuxxuuqquuttsswwxxrrssuuttuuuu€€ƒƒ„„ƒƒƒƒ‚‚€€��€€€€��wwttssuuxxwwvvvvvvvvwwwwwwwwwwxxwwttssrrssuuvvzz}}||{{yyxx{{}}~~€€€€€€€€}}{{{{xxvvttttwwxxwwuuuuvvttvvxxssttwwvvwwuuuuxxuuqquuttsswwxxrrssuuttuuuu~~||||||||zzxxxxyyxxvvssssttvvwwxxxxvvvvvvwwwwwwwwvvuuttrrssttuuwwzz}}||zzyyyyzz}}€€€€€€€€€€€€€€~~||yywwsssswwxxwwvvuuuuttvvxxssttwwwwvvuuvvzzuurruuttttyyxxrrttttssvv~~||||||||zzxxxxyyxxvvssssttvvwwxxxxvvvvvvwwwwwwwwvvuuttrrssttuuwwzz}}||zzyyyyzz}}€€€€€€€€€€€€€€~~||yywwsssswwxxwwvvuuuuttvvxxssttwwwwvvuuvvzzuurruuttttyyxxrrttttssvv€€}}||||zzxxxxxxttrrssvvwwssttvvxxwwuuuuvvwwvvvvuurrrrssttuuxx||}}{{yyyyzz{{}}€€€€€€€€€€€€€€€€€€€€~~yyvvsssswwwwvvvvuuuuttuuwwrruuxxwwvvttwwzzssqqttttttzzwwrruusstt€€}}||||zzxxxxxxttrrssvvwwssttvvxxwwuuuuvvwwvvvvuurrrrssttuuxx||}}{{yyyyzz{{}}€€€€€€€€€€€€€€€€€€€€~~yyvvsssswwwwvvvvuuuuttuuwwrruuxxwwvvttwwzzssqqttttttzzwwrruusstt}}||zzvvssttsssswwvvuussrruuwwxxwwuuttuuvvttrrqqrrttvvyy||||zzyyyyxxzz}}€€€€€€€€€€€€€€€€€€€€}}yyvvssrrvvwwwwuuuuttrrttwwrruuwwvvuuuuxxxxssrrssuuwwzzuurrttuu}}||zzvvssttsssswwvvuussrruuwwxxwwuuttuuvvttrrqqrrttvvyy||||zzyyyyxxzz}}€€€€€€€€€€€€€€€€€€€€}}yyvvssrrvvwwwwuuuuttrrttwwrruuwwvvuuuuxxxxssrrssuuwwzzuurrttuu{{vvttrrsswwwwxxvvuuuusssswwxxwwwwuussssqqrrttttvvzz||||zzxxxxyyyy{{~~€€€€€€€€€€€€||yywwsssswwxxvvuuttttssttvvssuuvvvvvvvvxxyyrrppttvvxxzzttqquu{{vvttrrsswwwwxxvvuuuusssswwxxwwwwuussssqqrrttttvvzz||||zzxxxxyyyy{{~~€€€€€€€€€€€€||yywwsssswwxxvvuuttttssttvvssuuvvvvvvvvxxyyrrppttvvxxzzttqquu~~vvvvttwwxxyyvvttuuvvuurrttwwxxwwttqqpprrssuuxxzz}}{{yyxxxxxxxxyy{{€€€€€€€€€€€€€€€€€€||zzyyvvttssvvwwwwuuttssrruuwwrrttvvwwwwvvxxyyrrssuuvvxxxxrrtt~~vvvvttwwxxyyvvttuuvvuurrttwwxxwwttqqpprrssuuxxzz}}{{yyxxxxxxxxyy{{€€€€€€€€€€€€€€€€€€||zzyyvvttssvvwwwwuuttssrruuwwrrttvvwwwwvvxxyyrrssuuvvxxxxrrtt~~{{xxvvyywwvvuuvvvvvvssssuuwwuurrqqqqttuuxx||~~||yyyyyyxxyyyyxx{{€€€€€€€€€€€€€€€€€€€€zzyyzzwwttrruuwwvvuussssttvvvvrruuwwwwwwuuyyyyrrttvvvvxxxxtt~~{{xxvvyywwvvuuvvvvvvssssuuwwuurrqqqqttuuxx||~~||yyyyyyxxyyyyxx{{€€€€€€€€€€€€€€€€€€€€zzyyzzwwttrruuwwvvuussssttvvvvrruuwwwwwwuuyyyyrrttvvvvxxxxtt€€zzuuttwwwwvvuuvvuuttssttttqqqqqqrruu{{}}||{{yyyyyyyyxxyyyyyy{{€€€€€€€€€€€€€€€€€€€€€€€€€€~~xxyyzzwwttqquuvvvvuussssssvvvvssvvwwwwvvuuyywwrruuuuvvxxxx€€zzuuttwwwwvvuuvvuuttssttttqqqqqqrruu{{}}||{{yyyyyyyyxxyyyyyy{{€€€€€€€€€€€€€€€€€€€€€€€€€€~~xxyyzzwwttqquuvvvvuussssssvvvvssvvwwwwvvuuyywwrruuuuvvxxxx€€yyttuuwwwwvvvvvvuussqqppqqqqrruuzz}}||yyxxyyyyyyyyyyyyzzyyzz€€€€€€€€€€€€€€€€€€€€||vvyy{{xxssrruuvvuussrrttttxxvvrrvvxxwwvvvvyyvvrrttuuttxx€€yyttuuwwwwvvvvvvuussqqppqqqqrruuzz}}||yyxxyyyyyyyyyyyyzzyyzz€€€€€€€€€€€€€€€€€€€€||vvyy{{xxssrruuvvuussrrttttxxvvrrvvxxwwvvvvyyvvrrttuuttxxxxttvvwwwwvvvvuurrooppqqssuuyy||{{yyxxyyyyyyxxxxyyyyyyzzzz}}€€€€€€€€€€€€€€€€€€€€xxvvyy{{wwttrruuuuttssssuuvvwwuussvvxxxxwwvvzzvvrruuttwwxxttvvwwwwvvvvuurrooppqqssuuyy||{{yyxxyyyyyyxxxxyyyyyyzzzz}}€€€€€€€€€€€€€€€€€€€€xxvvyy{{wwttrruuuuttssssuuvvwwuussvvxxxxwwvvzzvvrruuttwwwwuuwwwwvvttrrppqqrrssvv{{||zzxxyyyyxxyyxxxxxxyyyyyyzzzz||€€€€€€€€€€€€€€€€€€€€||vvuuyy{{wwttrrttuuttttttvvuuvvvvrrvvxxxxxxwwzzttrrttwwwwuuwwwwvvttrrppqqrrssvv{{||zzxxyyyyxxyyxxxxxxyyyyyyzzzz||€€€€€€€€€€€€€€€€€€€€||vvuuyy{{wwttrrttuuttttttvvuuvvvvrrvvxxxxxxwwzzttrrttww~~wwvvxxuurrppqqrrttwwzz||zzyyyyzzzzxxwwxxyyxxyyyyyyyyzzzz~~€€€€€€€€€€€€€€€€€€€€~~€€xxuuvvyy{{xxuurrttvvvvuuvvuusswwuusswwyyyyxxxxyyttrrww~~wwvvxxuurrppqqrrttwwzz||zzyyyyzzzzxxwwxxyyxxyyyyyyyyzzzz~~€€€€€€€€€€€€€€€€€€€€~~€€xxuuvvyy{{xxuurrttvvvvuuvvuusswwuusswwyyyyxxxxyyttrrww}}vvvvssrrqqssuuxx{{{{yyxxyyyyzzzzyyxxwwxxxxyyyyyyyyyyxx{{€€€€€€€€€€€€€€€€€€€€€€{{uuvvuuxx||xxttppttwwvvwwuuttttwwuussxxyyzzyywwxxrruu}}vvvvssrrqqssuuxx{{{{yyxxyyyyzzzzyyxxwwxxxxyyyyyyyyyyxx{{€€€€€€€€€€€€€€€€€€€€€€{{uuvvuuxx||xxttppttwwvvwwuuttttwwuussxxyyzzyywwxxrruu}}ttrrqqttww{{}}zzxxxxxxyyyyyy{{yyxxxxxxxxxxyyyyxxuuttxx€€��€€€€€€€€~~€€~~vvuuvvvvyy{{wwssqquuvvwwvvuuuuttwwuussxxzzzzwwwwwwss}}ttrrqqttww{{}}zzxxxxxxyyyyyy{{yyxxxxxxxxxxyyyyxxuuttxx€€��€€€€€€€€~~€€~~vvuuvvvvyy{{wwssqquuvvwwvvuuuuttwwuussxxzzzzwwwwwwss||rrttxx||}}||yywwxxxxxxxxyyyyzzyyyyxxxxwwxxvvttttuuvv}}��€€€€€€xxuuvvvvvvxx{{xxttrruuxxwwvvttttttxxttttyyzzxxuuvvyy||rrttxx||}}||yywwxxxxxxxxyyyyzzyyyyxxxxwwxxvvttttuuvv}}��€€€€€€xxuuvvvvvvxx{{xxttrruuxxwwvvttttttxxttttyyzzxxuuvvyy||xx||}}{{zzzzyyxxxxxxwwyyyyyyyyyyyyxxvvuuttttuuvvvvww~~��€€€€€€€€€€{{uuuuvvvvuuxx||xxttrruuwwvvuussttwwwwrruuyyxxwwuuww||xx||}}{{zzzzyyxxxxxxwwyyyyyyyyyyyyxxvvuuttttuuvvvvww~~��€€€€€€€€€€{{uuuuvvvvuuxx||xxttrruuwwvvuussttwwwwrruuyyxxwwuuww}}zzzzzzzzyyyyxxxxxxxxyyyyyyxxxxuussttuuuuuuuuuuxx}}€€€€€€€€€€€€}}vvuuuuuuvvvvxx{{yyuurrttwwwwuuvvxxwwuussttwwwwvvww}}zzzzzzzzyyyyxxxxxxxxyyyyyyxxxxuussttuuuuuuuuuuxx}}€€€€€€€€€€€€}}vvuuuuuuvvvvxx{{yyuurrttwwwwuuvvxxwwuussttwwwwvvww{{zzzz{{{{yyxxxxxxxxxxxxxxvvttssttttuuuuuuwwzz||}}||€€~~~~~~~~}}~~~~~~€€yyuuvvvvvvvvvvxx||zzuurruuwwuuvvxxvvttvvrrsswwvvxx{{zzzz{{{{yyxxxxxxxxxxxxxxvvttssttttuuuuuuwwzz||}}||€€~~~~~~~~}}~~~~~~€€yyuuvvvvvvvvvvxx||zzuurruuwwuuvvxxvvttvvrrsswwvvxx||zz{{{{zzyyxxwwwwwwwwuussssttuuuuttuuxx{{||}}{{zz{{€€~~}}||zzyywwvvvvuuuuuuuuuuvvwwwwxxyyzzzz{{{{||||~~{{wwwwwwvvvvvvwwxx{{yyuussssttvvxxvvttttvvrrssvvyy||zz{{{{zzyyxxwwwwwwwwuussssttuuuuttuuxx{{||}}{{zz{{€€~~}}||zzyywwvvvvuuuuuuuuuuvvwwwwxxyyzzzz{{{{||||~~{{wwwwwwvvvvvvwwxx{{yyuussssttvvxxvvttttvvrrssvvyy{{zz||zzyyyyxxwwttssssssssttttttwwzz||||||zzzz{{zz||~~||zzwwuurrppoonnmmlljjkkkkkkkkkkmmoooommmmooppppqqrrssttuuvvvvvvwwwwwwwwxx||zzuurrsswwwwvvttttuuwwrrsszz{{zz||zzyyyyxxwwttssssssssttttttwwzz||||||zzzz{{zz||~~||zzwwuurrppoonnmmlljjkkkkkkkkkkmmoooommmmooppppqqrrssttuuvvvvvvwwwwwwwwxx||zzuurrsswwwwvvttttuuwwrrsszz{{zz{{zzxxvvuurrqqssttssssuuww{{}}||{{{{{{{{{{{{zz}}€€€€}}{{vvssqqooookkgggghhhhiikkmmoooooooopprrppmmiigggghhjjllqqqqrrrrrrrrssttvvwwvvxx||yyuurruuxxvvuuuuuuvvvvqqvv{{zz{{zzxxvvuurrqqssttssssuuww{{}}||{{{{{{{{{{{{zz}}€€€€}}{{vvssqqooookkgggghhhhiikkmmoooooooopprrppmmiigggghhjjllqqqqrrrrrrrrssttvvwwvvxx||yyuurruuxxvvuuuuuuvvvvqqvv{{yyxxuussrrssssssttttww{{||{{||{{zz{{{{{{{{zzzz{{~~€€€€||yyssnnjjkkiiggggeeffkkooqqrrssttuuuuuuvvttssttttqqllggddcchhooqqrrssttttssssssvvvvwwxx||zzuussvvwwuuuuttuuuutttt{{yyxxuussrrssssssttttww{{||{{||{{zz{{{{{{{{zzzz{{~~€€€€||yyssnnjjkkiiggggeeffkkooqqrrssttuuuuuuvvttssttttqqllggddcchhooqqrrssttttssssssvvvvwwxx||zzuussvvwwuuuuttuuuutttt~~wwuussssttssssuuwwzz||||||zzyyzz{{{{{{{{zzzz{{zz{{~~}}{{xxrrnnggccbbcccceehhmmpprrssttrrqqqqqqqqqqssssssssttuuqqoojjddffnnppqqrrssttttttttvvwwwwxxzz}}zzvvttttttttvvuuttssvv~~wwuussssttssssuuwwzz||||||zzyyzz{{{{{{{{zzzz{{zz{{~~}}{{xxrrnnggccbbcccceehhmmpprrssttrrqqqqqqqqqqssssssssttuuqqoojjddffnnppqqrrssttttttttvvwwwwxxzz}}zzvvttttttttvvuuttssvv||uuttssttuuvvyyzz||||{{{{zzzzzzzzzzzzzz{{zzzzzzzzzz{{zzwwrrjjggkkddaabbddiinnqqrrppppqqrrppppppooppppqqqqppppqqqqppqqqqllggkkqqppqqrrssssttvvwwwwwwxxyyzz||zzvvssssttvvwwuussuu||uuttssttuuvvyyzz||||{{{{zzzzzzzzzzzzzz{{zzzzzzzzzz{{zzwwrrjjggkkddaabbddiinnqqrrppppqqrrppppppooppppqqqqppppqqqqppqqqqllggkkqqppqqrrssssttvvwwwwwwxxyyzz||zzvvssssttvvwwuussuu||ttttvvxx{{{{||zzzzzz{{zzzzzzzzzzzz{{zz{{zzzzzzzzyyxxuunnffbbddkkhhddiinnrrrrqqrrqqrrrrssrrssssrrqqqqrrqqpppppppppppprrqqjjkkppppqqrrrrssvvwwwwwwvvwwyyyyxx{{zzvvssssuuvvwwttuu||ttttvvxx{{{{||zzzzzz{{zzzzzzzzzzzz{{zz{{zzzzzzzzyyxxuunnffbbddkkhhddiinnrrrrqqrrqqrrrrssrrssssrrqqqqrrqqpppppppppppprrqqjjkkppppqqrrrrssvvwwwwwwvvwwyyyyxx{{zzvvssssuuvvwwttuu||xxzz{{||zzyyzzzzzzzzzzzz{{zzzzzzzzzz{{{{zzyy{{zzxxssmmggddbbbbkknnnnssttrrqqrrssttuuwwwwwwwwwwuussssssssrrrrrrqqrrqqrrrrkkjjqqrrrrqqssttuuwwwwvvvvwwxxwwwwyy||zzvvssttwwwwuuvv||xxzz{{||zzyyzzzzzzzzzzzz{{zzzzzzzzzz{{{{zzyy{{zzxxssmmggddbbbbkknnnnssttrrqqrrssttuuwwwwwwwwwwuussssssssrrrrrrqqrrqqrrrrkkjjqqrrrrqqssttuuwwwwvvvvwwxxwwwwyy||zzvvssttwwwwuuvv€€||zzzzzzzzzzyyyyzzzzzzzzzzzzzzzzzzzzzzzzzzyyyyxxrrppmmffccbbccmmttssssssrrssttvvwwxxzzzzyyyyxxvvuuttssssssssssssttuuttssnnllrrrrrruuwwvvssttwwvvvvvvxxwwxxxxxx||zzvvssttxxuuvv€€||zzzzzzzzzzyyyyzzzzzzzzzzzzzzzzzzzzzzzzzzyyyyxxrrppmmffccbbccmmttssssssrrssttvvwwxxzzzzyyyyxxvvuuttssssssssssssttuuttssnnllrrrrrruuwwvvssttwwvvvvvvxxwwxxxxxx||zzvvssttxxuuvv{{zzzzzzzzzzyyyyzzzzzzyyyyzzzzyyzzzzzzzzyyxxwwrroosskkeeeeeekkssttttssrrrrttvvvvvvvvvvwwxxxxxxwwvvuuttttttttssttuuwwwwwwssqqrrssuuzz{{xxwwttttwwvvvvwwwwwwwwwwxx{{zzvvrrttuuww{{zzzzzzzzzzyyyyzzzzzzyyyyzzzzyyzzzzzzzzyyxxwwrroosskkeeeeeekkssttttssrrrrttvvvvvvvvvvwwxxxxxxwwvvuuttttttttssttuuwwwwwwssqqrrssuuzz{{xxwwttttwwvvvvwwwwwwwwwwxx{{zzvvrrttuuww||zz{{zzzzzzyyzzzzzzzzyyzzyyyyzzzzyyzzzzyyxxuuooqqssjjeeffmmttuuuussssssuuuuvvuuuuvvuuvvvvwwxxwwwwvvuuuuuuuuttttuuvvvvxxvvssttwwwwxxzz||zzwwuuuuwwvvwwwwwwwwvvwwxxzzyyuuqqssxx||zz{{zzzzzzyyzzzzzzzzyyzzyyyyzzzzyyzzzzyyxxuuooqqssjjeeffmmttuuuussssssuuuuvvuuuuvvuuvvvvwwxxwwwwvvuuuuuuuuttttuuvvvvxxvvssttwwwwxxzz||zzwwuuuuwwvvwwwwwwwwvvwwxxzzyyuuqqssxx||zz{{zzzzyyyyzzyyyyyyzzzzzzyyyyyyyyxxxxuuuuttppqqssnnggllttxxuuttssttuuwwvvuuuuttttuuvvuuvvxxwwwwvvuuuuuuuuttuuuuttttuuvvuuvvxxwwvvxxzz||yywwvvttuuvvvvvvwwwwwwvvwwyyzzuuqqtt||zz{{zzzzyyyyzzyyyyyyzzzzzzyyyyyyyyxxxxuuuuttppqqssnnggllttxxuuttssttuuwwvvuuuuttttuuvvuuvvxxwwwwvvuuuuuuuuttuuuuttttuuvvuuvvxxwwvvxxzz||yywwvvttuuvvvvvvwwwwwwvvwwyyzzuuqqtt{{yyzzzzyyyyzzyyxxyyyyyyyyzzyyxxvvvvuuvvvvwwvvrrrrttqqkkppvvxxuuttttuuvvwwvvuuuuuuuuuuvvvvvvxxxxwwvvvvvvuuttttuuvvuuttttuuuuvvxxxxvvvvxxyyzzyywwuussvvwwwwwwwwvvvvwwvvzzxxtttt{{yyzzzzyyyyzzyyxxyyyyyyyyzzyyxxvvvvuuvvvvwwvvrrrrttqqkkppvvxxuuttttuuvvwwvvuuuuuuuuuuvvvvvvxxxxwwvvvvvvuuttttuuvvuuttttuuuuvvxxxxvvvvxxyyzzyywwuussvvwwwwwwwwvvvvwwvvzzxxtttt{{yyzzyyyyyyyyyyxxyyyyxxwwvvuuuuuuvvvvwwvvuuuuuussrrrroossxxxxuuuuuuuuvvvvttttvvvvuuvvwwvvwwwwyyzzwwvvttttvvvvvvvvvvuussssttvvwwxxwwvvxxxxxxzzzzwwuussvvwwwwwwvvwwwwwwwwyywwvv{{yyzzyyyyyyyyyyxxyyyyxxwwvvuuuuuuvvvvwwvvuuuuuussrrrroossxxxxuuuuuuuuvvvvttttvvvvuuvvwwvvwwwwyyzzwwvvttttvvvvvvvvvvuussssttvvwwxxwwvvxxxxxxzzzzwwuussvvwwwwwwvvwwwwwwwwyywwvv{{zzzzzzxxyyxxxxwwvvuuttuuuuvvwwwwvvvvvvuuvvvvvvuuttssssvvxxxxuuuuvvuuuuttttuuttuuvvwwyyzzyywwxxzzxxuuttttvvxxxxwwwwxxvvrrttvvwwwwwwwwwwwwwwxxzzzzwwvvssuuwwwwvvvvwwwwvvvvxxzz{{zzzzzzxxyyxxxxwwvvuuttuuuuvvwwwwvvvvvvuuvvvvvvuuttssssvvxxxxuuuuvvuuuuttttuuttuuvvwwyyzzyywwxxzzxxuuttttvvxxxxwwwwxxvvrrttvvwwwwwwwwwwwwwwxxzzzzwwvvssuuwwwwvvvvwwwwvvvvxxzzzzyyyyyywwwwvvttttuuvvvvvvvvvvvvvvwwwwwwvvwwwwvvuuuussvvxxyyxxvvvvvvuuttttttttttvvxxyyzzyyyywwwwwwuuttssrrvvyyyyyyxxxxvvssuuwwwwwwwwvvvvwwwwwwwwzz{{xxvvssuuwwvvvvwwwwvvvvvvyyzzyyyyyywwwwvvttttuuvvvvvvvvvvvvvvwwwwwwvvwwwwvvuuuussvvxxyyxxvvvvvvuuttttttttttvvxxyyzzyyyywwwwwwuuttssrrvvyyyyyyxxxxvvssuuwwwwwwwwvvvvwwwwwwwwzz{{xxvvssuuwwvvvvwwwwvvvvvvyyzzxxwwuuttttuuuuuuvvvvuuuuvvvvvvwwwwxxwwvvwwwwwwvvuuuuuuvvxxwwvvuuuuuuttssssttuuuuvvvvuuvvwwwwwwuussttttssuuyyyyxxxxzzwwssuuwwwwvvwwwwwwwwwwwwwwxxxxzzyyvvttttvvwwwwvvwwwwvvxxzzxxwwuuttttuuuuuuvvvvuuuuvvvvvvwwwwxxwwvvwwwwwwvvuuuuuuvvxxwwvvuuuuuuttssssttuuuuvvvvuuvvwwwwwwuussttttssuuyyyyxxxxzzwwssuuwwwwvvwwwwwwwwwwwwwwxxxxzzyyvvttttvvwwwwvvwwwwvvxx~~vvuuuuuuuuuuvvuuuuuuuuvvwwwwvvvvvvwwwwwwwwwwwwwwvvttrrssttvvvvwwwwuuuuttssrrssttssssttttuuvvvvttssssssttttuuxxyywwuuwwyyuuuuwwvvwwwwwwwwwwwwvvwwwwwwwwzzyywwttttvvvvvvwwwwwwxx~~vvuuuuuuuuuuvvuuuuuuuuvvwwwwvvvvvvwwwwwwwwwwwwwwvvttrrssttvvvvwwwwuuuuttssrrssttssssttttuuvvvvttssssssttttuuxxyywwuuwwyyuuuuwwvvwwwwwwwwwwwwvvwwwwwwwwzzyywwttttvvvvvvwwwwwwxx}}vvuuvvuuvvwwzzzzxxuuvvwwvvwwvvwwvvvvvvvvwwvvvvvvwwrrqqttssttvvxxvvuuttssrrrrrrrrssssttttssttttssssttttttttttwwyyxxwwwwxxwwuuvvvvwwwwvvwwwwwwvvvvwwxxwwwwzz{{wwuuttuuvvvvwwvvxx}}vvuuvvuuvvwwzzzzxxuuvvwwvvwwvvwwvvvvvvvvwwvvvvvvwwrrqqttssttvvxxvvuuttssrrrrrrrrssssttttssttttssssttttttttttwwyyxxwwwwxxwwuuvvvvwwwwvvwwwwwwvvvvwwxxwwwwzz{{wwuuttuuvvvvwwvvxx}}wwxxzz{{{{zzzzxxyyxxuuxxzzzzxxxxwwvvwwvvwwwwvvvvwwqqqqttssttvvxxuuuuuussrrssrrrrttssttssssttssssttttttttttssuuyyxxwwxxxxyyvvuuvvwwwwwwwwwwwwvvvvwwxxwwvvxx||{{vvuussuuvvwwvvyy}}wwxxzz{{{{zzzzxxyyxxuuxxzzzzxxxxwwvvwwvvwwwwvvvvwwqqqqttssttvvxxuuuuuussrrssrrrrttssttssssttssssttttttttttssuuyyxxwwxxxxyyvvuuvvwwwwwwwwwwwwvvvvwwxxwwvvxx||{{vvuussuuvvwwvvyy}}}}}}{{{{zzyywwwwxxvvvvyy{{{{yyxxwwwwvvvvvvwwvvwwssqqttqqqqvvwwuuttttttssssrrssttttssssssttssssuuvvuuttrrqqrrwwwwvvxxxxyyzzvvttuuwwvvvvwwvvvvwwwwwwwwwwxxyyzzzzwwttssuuvvvvxx}}}}}}{{{{zzyywwwwxxvvvvyy{{{{yyxxwwwwvvvvvvwwvvwwssqqttqqqqvvwwuuttttttssssrrssttttssssssttssssuuvvuuttrrqqrrwwwwvvxxxxyyzzvvttuuwwvvvvwwvvvvwwwwwwwwwwxxyyzzzzwwttssuuvvvvxx}}||{{{{{{zzxxwwxxwwwwvvuuwwzz||{{xxxxvvvvwwvvvvwwttrrssoossvvvvvvuuttttssrrssssttssssttssssssuuvvvvuurrooooppttvvuuwwxxyyyyxxwwttttvvvvwwvvvvvvvvwwwwwwwwyyyyzzzzwwuussssttxx}}||{{{{{{zzxxwwxxwwwwvvuuwwzz||{{xxxxvvvvwwvvvvwwttrrssoossvvvvvvuuttttssrrssssttssssttssssssuuvvvvuurrooooppttvvuuwwxxyyyyxxwwttttvvvvwwvvvvvvvvwwwwwwwwyyyyzzzzwwuussssttxx}}{{zz{{zzyywwwwwwvvwwvvttvvyyyy{{||{{yywwwwvvvvvvvvssrroottuuvvwwvvuuttttssssssssttttssssrrttvvuuttssqqppnnoouuwwssuuwwxxyyvvwwxxuuttuuvvvvvvwwwwwwvvvvwwxxxxwwxxyyxxttrrssww}}{{zz{{zzyywwwwwwvvwwvvttvvyyyy{{||{{yywwwwvvvvvvvvssrroottuuvvwwvvuuttttssssssssttttssssrrttvvuuttssqqppnnoouuwwssuuwwxxyyvvwwxxuuttuuvvvvvvwwwwwwvvvvwwxxxxwwxxyyxxttrrssww€€}}{{{{{{{{yywwxxwwuuuuttttuuyyyyxxyy{{||zzxxwwwwwwvvuussqqssssuuxxwwvvuuttttttssssttttttssssuuvvttttssssrrppppuuwwssssuuwwyyvvttwwxxvvttuuvvvvvvwwvvvvwwwwxxwwwwxxxxyywwttrrtt€€}}{{{{{{{{yywwxxwwuuuuttttuuyyyyxxyy{{||zzxxwwwwwwvvuussqqssssuuxxwwvvuuttttttssssttttttssssuuvvttttssssrrppppuuwwssssuuwwyyvvttwwxxvvttuuvvvvvvwwvvvvwwwwxxwwwwxxxxyywwttrrtt€€||{{{{||yywwxxyyxxvvvvvvvvuuvvzzzzyyxxzz||{{zzyyxxvvuuttttssssuuwwwwwwuuuuttuuuuttttttttssssttvvuuvvwwuuttssssvvwwssrrttwwyyvvttuuvvwwvvttttuuvvwwwwwwwwwwwwwwwwwwxxwwyyxxtttt€€||{{{{||yywwxxyyxxvvvvvvvvuuvvzzzzyyxxzz||{{zzyyxxvvuuttttssssuuwwwwwwuuuuttuuuuttttttttssssttvvuuvvwwuuttssssvvwwssrrttwwyyvvttuuvvwwvvttttuuvvwwwwwwwwwwwwwwwwwwxxwwyyxxtttt€€}}||||||zzxxxxxxxxwwwwwwvvuuttwwzzzzxxxxyy{{{{{{zzxxvvuuuuttrrttwwxxwwuuuuttttuuttttttssrrttuuvvwwwwwwvvuuuuwwyyxxttrrssvvxxvvuuttttuuwwwwuuttvvvvvvuuvvvvwwvvwwwwwwwwwwyyxxvv€€}}||||||zzxxxxxxxxwwwwwwvvuuttwwzzzzxxxxyy{{{{{{zzxxvvuuuuttrrttwwxxwwuuuuttttuuttttttssrrttuuvvwwwwwwvvuuuuwwyyxxttrrssvvxxvvuuttttuuwwwwuuttvvvvvvuuvvvvwwvvwwwwwwwwwwyyxxvv}}||||||yyxxyyxxxxwwwwvvvvvvuuvvyyzzyyyyxxxxxx{{||zzwwwwwwuurrrrvvxxwwvvttttttttttttssttssttuuvvwwwwvvuuttuuxxzzyyvvssssvvxxuuuuuuuuuuuuuuxxwwttttvvvvvvuuwwvvwwwwvvvvvvvvwwxx}}||||||yyxxyyxxxxwwwwvvvvvvuuvvyyzzyyyyxxxxxx{{||zzwwwwwwuurrrrvvxxwwvvttttttttttttssttssttuuvvwwwwvvuuttuuxxzzyyvvssssvvxxuuuuuuuuuuuuuuxxwwttttvvvvvvuuwwvvwwwwvvvvvvvvwwxx€€~~||||{{xxyyyyxxxxwwvvuuuuvvvvuuwwzzyyyyxxxxxxxxxxyywwwwwwvvssqquuyywwvvttttttttttssssttttuuvvvvvvvvvvvvttuuwwyyxxxxuuttvvwwuuvvuuuuuuttssvvxxvvttttuuuuuuuuuuwwwwwwvvvvvvuuxx€€~~||||{{xxyyyyxxxxwwvvuuuuvvvvuuwwzzyyyyxxxxxxxxxxyywwwwwwvvssqquuyywwvvttttttttttssssttttuuvvvvvvvvvvvvttuuwwyyxxxxuuttvvwwuuvvuuuuuuttssvvxxvvttttuuuuuuuuuuwwwwwwvvvvvvuuxx€€~~}}||zzxxzzzzyyxxwwwwwwvvvvvvuuvvyyzzyyyyyyyyxxvvvvuuttvvwwttqquuxxxxvvuuttssssttssttttttuuvvvvvvuuuuvvvvvvwwxxxxwwwwvvvvvvuuuuttuuuuttttttuuwwwwttttuuvvvvuuvvwwwwvvvvvvuuxx€€~~}}||zzxxzzzzyyxxwwwwwwvvvvvvuuvvyyzzyyyyyyyyxxvvvvuuttvvwwttqquuxxxxvvuuttssssttssttttttuuvvvvvvuuuuvvvvvvwwxxxxwwwwvvvvvvuuuuttuuuuttttttuuwwwwttttuuvvvvuuvvwwwwvvvvvvuuxx€€}}||{{yyzzzzzzyyxxxxwwxxvvvvvvvvvvwwzzzzzzyyxxxxvvttttsssswwwwssttwwxxvvuuttrrrrssttvvuuuuvvvvuuuuttuuvvvvvvvvvvwwvvvvwwwwvvttuuuuuuuuttttttttssuuwwvvttuuvvuuvvvvvvuuuuuuttww€€}}||{{yyzzzzzzyyxxxxwwxxvvvvvvvvvvwwzzzzzzyyxxxxvvttttsssswwwwssttwwxxvvuuttrrrrssttvvuuuuvvvvuuuuttuuvvvvvvvvvvwwvvvvwwwwvvttuuuuuuuuttttttttssuuwwvvttuuvvuuvvvvvvuuuuuuttww€€}}||zzxx{{zzzzyyyyxxwwwwwwwwwwwwuuvvyy{{zzzzzzxxuuttttssttxxyyuuttwwxxvvuuttqqqqrruuvvuuuuuuvvwwwwxxxxxxxxxxxxwwuuttvvwwxxuuttuuvvuuuuttttuuttssttuuvvwwuuttuuvvvvuuuuttttttww€€}}||zzxx{{zzzzyyyyxxwwwwwwwwwwwwuuvvyy{{zzzzzzxxuuttttssttxxyyuuttwwxxvvuuttqqqqrruuvvuuuuuuvvwwwwxxxxxxxxxxxxwwuuttvvwwxxuuttuuvvuuuuttttuuttssttuuvvwwuuttuuvvvvuuuuttttttww€€}}||zzzz{{{{zzyyyyxxwwxxvvwwwwvvuuuuvvzzzzzzyyxxvvttssssttxxzzvvssuuxxvvttttqqppqqssttttuuxxzzzzzz{{zzyyxxzz{{zzwwttttwwxxttttuuuuuuuuuuuuttuuttttssttvvxxttssuuvvvvuuttssttvv€€}}||zzzz{{{{zzyyyyxxwwxxvvwwwwvvuuuuvvzzzzzzyyxxvvttssssttxxzzvvssuuxxvvttttqqppqqssttttuuxxzzzzzz{{zzyyxxzz{{zzwwttttwwxxttttuuuuuuuuuuuuttuuttttssttvvxxttssuuvvvvuuttssttvv€€||{{yy{{{{{{zzyyyyxxwwxxwwwwwwvvvvvvvvxxyyzzzzyywwvvttssuuxxyywwttuuwwwwuuttssppooqqrrssttvvwwwwwwxxyyyyyy{{zzyyyyttrrvvxxttttuuuuttuuuuuuuuttttttttttssuuvvuuttuuvvuuttssssww€€||{{yy{{{{{{zzyyyyxxwwxxwwwwwwvvvvvvvvxxyyzzzzyywwvvttssuuxxyywwttuuwwwwuuttssppooqqrrssttvvwwwwwwxxyyyyyy{{zzyyyyttrrvvxxttttuuuuttuuuuuuuuttttttttttssuuvvuuttuuvvuuttssssww€€}}{{zz{{{{{{zzzzyyxxwwxxxxxxxxvvvvvvuuvvxxzzzzxxwwuuuuuuuuvvwwxxttttwwxxvvttttrrppooqqrrrrssssttuuttvvuuvvxxyyyyxxuurrvvwwttttttttttttuuuuuuttttuuttssssssttvvuuttuuuuttssssvv€€}}{{zz{{{{{{zzzzyyxxwwxxxxxxxxvvvvvvuuvvxxzzzzxxwwuuuuuuuuvvwwxxttttwwxxvvttttrrppooqqrrrrssssttuuttvvuuvvxxyyyyxxuurrvvwwttttttttttttuuuuuuttttuuttssssssttvvuuttuuuuttssssvv€€}}zz{{{{{{{{zzzzyyxxxxxxxxxxxxwwvvwwwwuuwwzzzzyyxxwwwwvvvvvvwwzzvvttuuwwwwuuttssqqppoonnooqqrrssuuuuvvvvuuvvwwxxwwttssvvuurrssssttttssssttttuuuuttuuttuuuuttssttuuuussssssssvv€€}}zz{{{{{{{{zzzzyyxxxxxxxxxxxxwwvvwwwwuuwwzzzzyyxxwwwwvvvvvvwwzzvvttuuwwwwuuttssqqppoonnooqqrrssuuuuvvvvuuvvwwxxwwttssvvuurrssssttttssssttttuuuuttuuttuuuuttssttuuuussssssssvv€€{{yy{{{{{{zzzzzzyyyyxxxxwwxxxxxxwwwwvvuuuuyyzzzzyywwvvvvwwvv||wwttttvvwwvvttrrrrppnnllmmoorrttuuuuuuuuttttttuuttqqssvvttssttttttssssssttttttssttttuuttttttttrrttwwttrrssssvv€€{{yy{{{{{{zzzzzzyyyyxxxxwwxxxxxxwwwwvvuuuuyyzzzzyywwvvvvwwvv||wwttttvvwwvvttrrrrppnnllmmoorrttuuuuuuuuttttttuuttqqssvvttssttttttssssssttttttssttttuuttttttttrrttwwttrrssssvv€€{{zz||||{{zzzzzzyyyyyyxxwwxxxxwwwwwwwwvvvvwwzzzzzzyyvvvvxxwwƒƒxxttssuuvvwwuussrrppnnkkllmmppssttttuuttttttttttrroottvvssssttttttssssttttssssssuuttssttttttttrrssttuuuussqquu€€{{zz||||{{zzzzzzyyyyyyxxwwxxxxwwwwwwwwvvvvwwzzzzzzyyvvvvxxwwƒƒxxttssuuvvwwuussrrppnnkkllmmppssttttuuttttttttttrroottvvssssttttttssssttttssssssuuttssttttttttrrssttuuuussqquu€€{{zz||||{{zzzzyyxxyyyyxxwwxxwwxxxxwwwwvvvvvvxxzz{{{{yyyyzzww††{{ttssssuuuuuuttssqqoommmmmmllnnqqrrssttuuttssqqnnnnttuuttttttttttssssssssssssssssssssssssttttssssssttttttssss€€{{zz||||{{zzzzyyxxyyyyxxwwxxwwxxxxwwwwvvvvvvxxzz{{{{yyyyzzww††{{ttssssuuuuuuttssqqoommmmmmllnnqqrrssttuuttssqqnnnnttuuttttttttttssssssssssssssssssssssssttttssssssttttttssss{{{{}}||{{yy{{zzyyyyyyxxxxwwwwyyxxxxwwwwwwwwwwzz{{{{{{{{{{yyˆˆ‚‚vvttttttttttttssssrrppnnnnllkklloopprrssrrppoommqqvvuuttttttttttttttssssssssssrrssssttttttttssttssssssssttuu{{{{}}||{{yy{{zzyyyyyyxxxxwwwwyyxxxxwwwwwwwwwwzz{{{{{{{{{{yyˆˆ‚‚vvttttttttttttssssrrppnnnnllkklloopprrssrrppoommqqvvuuttttttttttttttssssssssssrrssssttttttttssttssssssssttuu||||||{{zzzzyyzzyyyyyyyyxxxxxxyyyyxxwwwwwwwwvvxx{{{{{{{{{{zz~~††ˆˆ}}ttttttssuuttssttssqqqqoommllkkmmmmnnooppppoooottvvttttttttttttttssttttssrrssssssssttttttttttttttrrrrrrqqss||||||{{zzzzyyzzyyyyyyyyxxxxxxyyyyxxwwwwwwwwvvxx{{{{{{{{{{zz~~††ˆˆ}}ttttttssuuttssttssqqqqoommllkkmmmmnnooppppoooottvvttttttttttttttssttttssrrssssssssttttttttttttttrrrrrrqqss||{{{{zzzzzzzzzzyyyyyyyyxxxxxxxxyyxxwwwwwwwwwwvvzz{{{{{{{{{{}}„„ŠŠ……wwrrssttttttssttuuttssqqoonnllmmmmmmnnnnoopprrvvuurrttttttuussttttssttssssssrrssssssrrssssssssttssrrrrqqrr||{{{{zzzzzzzzzzyyyyyyyyxxxxxxxxyyxxwwwwwwwwwwvvzz{{{{{{{{{{}}„„ŠŠ……wwrrssttttttssttuuttssqqoonnllmmmmmmnnnnoopprrvvuurrttttttuussttttssttssssssrrssssssrrssssssssttssrrrrqqrr€€}}||||{{{{zzzzzzyyyyyyyyyyxxxxxxyyxxxxxxxxxxwwvvxx{{||{{{{{{||ƒƒ‰‰ŠŠ��uussssuuttssssuuuuttssrrppoommnnoooooopprruuwwuuttttttttttttssttssttssssssrrssssssssssrrrrrrttssrrrrqqrr€€}}||||{{{{zzzzzzyyyyyyyyyyxxxxxxyyxxxxxxxxxxwwvvxx{{||{{{{{{||ƒƒ‰‰ŠŠ��uussssuuttssssuuuuttssrrppoommnnoooooopprruuwwuuttttttttttttssttssttssssssrrssssssssssrrrrrrttssrrrrqqrr€€}}||{{{{{{zzzzzzzzzzyyyyyyyyyyyyyyxxxxxxxxxxwwwwwwyy||||||||||‚‚ˆˆ‰‰ŠŠ‚‚xxttuuvvuuttttttuuttssssrrqqqqrrqqqqssttwwyywwuuttttttttttttssssssssssssssssssssssssssssssuussrrrrqqrr€€}}||{{{{{{zzzzzzzzzzyyyyyyyyyyyyyyxxxxxxxxxxwwwwwwyy||||||||||‚‚ˆˆ‰‰ŠŠ‚‚xxttuuvvuuttttttuuttssssrrqqqqrrqqqqssttwwyywwuuttttttttttttssssssssssssssssssssssssssssssuussrrrrqqrr€€}}||{{{{{{zzzzzzzzyyyyzzyyyyzzyyyyxxxxxxyyxxwwwwvvww{{||||}}‚‚‡‡ˆˆ‰‰‹‹††wwttuuvvvvttttuuuuttttssssssttssssttuu{{ƒƒ��yyuuuuttuuttttttttttttssssssrrssssssssrrrrssssssrrrrqqrr€€}}||{{{{{{zzzzzzzzyyyyzzyyyyzzyyyyxxxxxxyyxxwwwwvvww{{||||}}‚‚‡‡ˆˆ‰‰‹‹††wwttuuvvvvttttuuuuttttssssssttssssttuu{{ƒƒ��yyuuuuttuuttttttttttttssssssrrssssssssrrrrssssssrrrrqqrr}}||{{{{{{zzzzzzyyyyzzyyyyyyyyyyyyxxxxxxxxxxwwwwvvwwyy{{||~~ƒƒ‚‚ƒƒ††ˆˆŠŠŠŠssssuuvvwwuuttuuuuttttssttttuuttttuu}}ƒƒ††……}}wwvvvvuuttttttttttttssssrrssrrssssssssssssrrrrrrqqrr}}||{{{{{{zzzzzzyyyyzzyyyyyyyyyyyyxxxxxxxxxxwwwwvvwwyy{{||~~ƒƒ‚‚ƒƒ††ˆˆŠŠŠŠssssuuvvwwuuttuuuuttttssttttuuttttuu}}ƒƒ††……}}wwvvvvuuttttttttttttssssrrssrrssssssssssssrrrrrrqqrr||zz{{{{zz{{zzyyzzyyyyyyyyxxxxxxxxwwwwxxwwwwxxwwwwwwwwzz||€€ƒƒ„„‚‚‚‚††‰‰ŠŠˆˆ{{qqttuuwwvvuuttssssssttttuuuuttuuww}}€€‚‚‡‡ˆˆwwvvuuttttssssttssrrrrrrssrrssssssrrssrrqqrrqqppqq||zz{{{{zz{{zzyyzzyyyyyyyyxxxxxxxxwwwwxxwwwwxxwwwwwwwwzz||€€ƒƒ„„‚‚‚‚††‰‰ŠŠˆˆ{{qqttuuwwvvuuttssssssttttuuuuttuuww}}€€‚‚‡‡ˆˆwwvvuuttttssssttssrrrrrrssrrssssssrrssrrqqrrqqppqq}}zzzz{{{{{{yyyyzzyyxxxxxxxxwwxxxxxxwwxxwwwwxxwwwwvvvvxx‚‚‚‚ƒƒƒƒƒƒ‚‚††ˆˆŠŠˆˆyyqqttwwvvuuttssttttttttttttttvvww||……‰‰‰‰€€vvttttuuttssssssrrttssrrrrssrrrrqqqqrrqqqqqqqqrr}}zzzz{{{{{{yyyyzzyyxxxxxxxxwwxxxxxxwwxxwwwwxxwwwwvvvvxx‚‚‚‚ƒƒƒƒƒƒ‚‚††ˆˆŠŠˆˆyyqqttwwvvuuttssttttttttttttttvvww||……‰‰‰‰€€vvttttuuttssssssrrttssrrrrssrrrrqqqqrrqqqqqqqqrr€€||{{{{{{zzzzyyyyzzzzxxxxxxxxxxxxyyyyxxxxvvwwxxwwwwwwww||ƒƒƒƒ‚‚‚‚ƒƒ„„„„‚‚ƒƒˆˆ‹‹††wwrruuwwvvssrrrrrrssssssssttvvwwzz}}€€……‡‡ŠŠˆˆzzqqssuuttssssssssttssrrssrrqqrrrrpprrrrqqqqqqqq€€||{{{{{{zzzzyyyyzzzzxxxxxxxxxxxxyyyyxxxxvvwwxxwwwwwwww||ƒƒƒƒ‚‚‚‚ƒƒ„„„„‚‚ƒƒˆˆ‹‹††wwrruuwwvvssrrrrrrssssssssttvvwwzz}}€€……‡‡ŠŠˆˆzzqqssuuttssssssssttssrrssrrqqrrrrpprrrrqqqqqqqq€€||{{{{{{zzzzyyyyyyyyyyyyyyyyyyyyyyyyyyyywwxxyyxxxxzz}}��„„ƒƒ„„ƒƒ„„„„…………‚‚‚‚‡‡‹‹ƒƒuurruuvvuuttrrrrssrrssssttuuww{{}}€€……ˆˆˆˆŠŠ‚‚ttrrttttttssssssssssssttssrrrrrrrrrrqqnnqqxxww€€||{{{{{{zzzzyyyyyyyyyyyyyyyyyyyyyyyyyyyywwxxyyxxxxzz}}��„„ƒƒ„„ƒƒ„„„„…………‚‚‚‚‡‡‹‹ƒƒuurruuvvuuttrrrrssrrssssttuuww{{}}€€……ˆˆˆˆŠŠ‚‚ttrrttttttssssssssssssttssrrrrrrrrrrqqnnqqxxww€€||{{{{zzzzzzyyxxyyyyyyyyyyyyyyxxyyxxxxyyzz||~~€€‚‚‚‚‚‚„„„„„„„„„„„„„„††……„„‚‚„„††||qqttuuuuttssssssrrssssssuuwwzz}}……††……ˆˆ‚‚vvuuqqttttttttssttssrrrrqqqqsswwttkkbbll}}ƒƒ��€€||{{{{zzzzzzyyxxyyyyyyyyyyyyyyxxyyxxxxyyzz||~~€€‚‚‚‚‚‚„„„„„„„„„„„„„„††……„„‚‚„„††||qqttuuuuttssssssrrssssssuuwwzz}}……††……ˆˆ‚‚vvuuqqttttttttssttssrrrrqqqqsswwttkkbbll}}ƒƒ��||{{{{zzzzyyyyxxxxxxxxxxyyxxwwwwwwyy{{~~����‚‚‚‚ƒƒƒƒ����ƒƒƒƒ„„ƒƒƒƒ„„……††…………„„ƒƒ��~~vvrrrrssssssssssssssssssuuvvyy||~~ƒƒ……‡‡ˆˆ}}ttttuuuuttvvwwuuuussssjj__hhtt}}kkXX]]mmoohhff||{{{{zzzzyyyyxxxxxxxxxxyyxxwwwwwwyy{{~~����‚‚‚‚ƒƒƒƒ����ƒƒƒƒ„„ƒƒƒƒ„„……††…………„„ƒƒ��~~vvrrrrssssssssssssssssssuuvvyy||~~ƒƒ……‡‡ˆˆ}}ttttuuuuttvvwwuuuussssjj__hhtt}}kkXX]]mmoohhff||||||{{zzyyxxyyxxxxyyyyxxxxxx{{~~��‚‚‚‚‚‚‚‚‚‚‚‚ƒƒƒƒ����‚‚„„„„……ƒƒ‚‚…………„„ƒƒ„„ƒƒ‚‚‚‚ƒƒ€€zzttttttttuuuuuuuuvvvvwwyy}}~~‚‚ˆˆŠŠ„„||vvvvvvvvxxww{{||{{vvuuhhZZhhoohh^^ZZ[[[[ZZYY\\||||||{{zzyyxxyyxxxxyyyyxxxxxx{{~~��‚‚‚‚‚‚‚‚‚‚‚‚ƒƒƒƒ����‚‚„„„„……ƒƒ‚‚…………„„ƒƒ„„ƒƒ‚‚‚‚ƒƒ€€zzttttttttuuuuuuuuvvvvwwyy}}~~‚‚ˆˆŠŠ„„||vvvvvvvvxxww{{||{{vvuuhhZZhhoohh^^ZZ[[[[ZZYY\\xxjjmmhhiikkjjhhggmmkkvv„„‚‚‚‚‚‚‚‚„„……††……„„„„„„„„„„„„„„„„ƒƒƒƒ„„„„„„……††††………………„„„„‚‚‚‚‚‚ƒƒ„„„„ƒƒƒƒ„„„„……„„……„„„„ƒƒƒƒ„„„„„„…………„„„„„„„„„„„„„„………………………………††††††……„„††††ˆˆ††ƒƒ„„xxjjmmhhiikkjjhhggmmkkvv„„‚‚‚‚‚‚‚‚„„……††……„„„„„„„„„„„„„„„„ƒƒƒƒ„„„„„„……††††………………„„„„‚‚‚‚‚‚ƒƒ„„„„ƒƒƒƒ„„„„……„„……„„„„ƒƒƒƒ„„„„„„…………„„„„„„„„„„„„„„………………………………††††††……„„††††ˆˆ††ƒƒ„„ssPPQQNNQQKKMMNNOOSSUUgg††‚‚ƒƒ„„…………††………………„„„„……„„„„„„„„„„„„……………………………………„„„„ƒƒƒƒƒƒƒƒ‚‚‚‚‚‚ƒƒƒƒ‚‚ƒƒ„„……„„„„„„„„„„ƒƒƒƒ„„………………††……„„„„ƒƒ„„…………††††…………††††††††††„„††††††ˆˆ††ƒƒssPPQQNNQQKKMMNNOOSSUUgg††‚‚ƒƒ„„…………††………………„„„„……„„„„„„„„„„„„……………………………………„„„„ƒƒƒƒƒƒƒƒ‚‚‚‚‚‚ƒƒƒƒ‚‚ƒƒ„„……„„„„„„„„„„ƒƒƒƒ„„………………††……„„„„ƒƒ„„…………††††…………††††††††††„„††††††ˆˆ††ƒƒ}}ssttssttttuussuuvvuuyy„„ƒƒ………………„„……„„…………„„„„„„……„„„„„„………………††††……………………ƒƒƒƒ„„‚‚‚‚����€€€€��€€����ƒƒ„„„„„„„„……„„ƒƒƒƒ„„„„……„„††††„„„„„„„„„„…………††††……††††††††……………………††‡‡ˆˆ††}}ssttssttttuussuuvvuuyy„„ƒƒ………………„„……„„…………„„„„„„……„„„„„„………………††††……………………ƒƒƒƒ„„‚‚‚‚����€€€€��€€����ƒƒ„„„„„„„„……„„ƒƒƒƒ„„„„……„„††††„„„„„„„„„„…………††††……††††††††……………………††‡‡ˆˆ††��ƒƒƒƒ‚‚��‚‚ƒƒƒƒƒƒƒƒƒƒ„„„„……††……„„ƒƒ„„„„„„„„ƒƒ„„„„„„„„„„„„…………………………„„„„„„ƒƒƒƒƒƒƒƒ��€€����€€€€������€€����‚‚ƒƒƒƒ„„„„„„ƒƒƒƒƒƒ„„„„…………………………„„„„„„„„……††††………………††……††††„„……‡‡ˆˆˆˆ��ƒƒƒƒ‚‚��‚‚ƒƒƒƒƒƒƒƒƒƒ„„„„……††……„„ƒƒ„„„„„„„„ƒƒ„„„„„„„„„„„„…………………………„„„„„„ƒƒƒƒƒƒƒƒ��€€����€€€€������€€����‚‚ƒƒƒƒ„„„„„„ƒƒƒƒƒƒ„„„„…………………………„„„„„„„„……††††………………††……††††„„……‡‡ˆˆˆˆ€€��‚‚‚‚‚‚ƒƒƒƒƒƒ„„……………………††††„„ƒƒ„„„„„„„„„„„„………………††††…………………………„„„„„„ƒƒƒƒ‚‚��€€€€€€����€€€€������€€€€������‚‚„„ƒƒ„„„„ƒƒ„„„„„„…………††††……„„„„„„„„……††……††………………††……††……„„††ˆˆ‡‡€€��‚‚‚‚‚‚ƒƒƒƒƒƒ„„……………………††††„„ƒƒ„„„„„„„„„„„„………………††††…………………………„„„„„„ƒƒƒƒ‚‚��€€€€€€����€€€€������€€€€������‚‚„„ƒƒ„„„„ƒƒ„„„„„„…………††††……„„„„„„„„……††……††………………††……††……„„††ˆˆ‡‡€€��‚‚‚‚ƒƒ„„„„……††††††…………††……ƒƒƒƒ„„…………„„„„„„……††………………„„„„„„„„„„ƒƒ„„‚‚‚‚��€€����€€€€������€€€€��€€��€€��€€����‚‚„„„„„„„„„„„„„„………………††††††„„„„„„„„††……††††……††††…………‡‡…………††††€€��‚‚‚‚ƒƒ„„„„……††††††…………††……ƒƒƒƒ„„…………„„„„„„……††………………„„„„„„„„„„ƒƒ„„‚‚‚‚��€€����€€€€������€€€€��€€��€€��€€����‚‚„„„„„„„„„„„„„„………………††††††„„„„„„„„††……††††……††††…………‡‡…………††††����ƒƒ„„……††…………………………„„…………ƒƒƒƒƒƒ„„„„………………††…………„„……„„„„ƒƒ„„„„ƒƒƒƒ‚‚����€€€€€€€€€€€€€€€€��€€€€€€€€������€€€€€€��ƒƒƒƒƒƒ„„„„ƒƒƒƒ„„………………††‡‡…………„„„„………………††††……††††…………††…………††����ƒƒ„„……††…………………………„„…………ƒƒƒƒƒƒ„„„„………………††…………„„……„„„„ƒƒ„„„„ƒƒƒƒ‚‚����€€€€€€€€€€€€€€€€��€€€€€€€€������€€€€€€��ƒƒƒƒƒƒ„„„„ƒƒƒƒ„„………………††‡‡…………„„„„………………††††……††††…………††…………††��ƒƒ„„……„„…………„„……„„„„„„…………ƒƒƒƒƒƒ„„……††……………………„„„„„„ƒƒƒƒƒƒ„„„„ƒƒ‚‚��€€��€€€€€€€€€€��€€��€€€€€€������€€€€€€€€€€€€‚‚ƒƒ„„„„„„„„ƒƒ„„„„„„……††‡‡††……„„„„………………††††…………††……„„……‡‡……††��ƒƒ„„……„„…………„„……„„„„„„…………ƒƒƒƒƒƒ„„……††……………………„„„„„„ƒƒƒƒƒƒ„„„„ƒƒ‚‚��€€��€€€€€€€€€€��€€��€€€€€€������€€€€€€€€€€€€‚‚ƒƒ„„„„„„„„ƒƒ„„„„„„……††‡‡††……„„„„………………††††…………††……„„……‡‡……††��ƒƒƒƒ„„…………„„„„„„„„„„…………„„ƒƒƒƒ„„……††…………„„ƒƒ„„ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒ‚‚����€€��€€€€€€€€€€����€€€€������€€��€€€€€€��€€€€€€‚‚„„„„„„„„„„„„„„……††††††††††………………„„„„………………††††††……„„……‡‡„„��ƒƒƒƒ„„…………„„„„„„„„„„…………„„ƒƒƒƒ„„……††…………„„ƒƒ„„ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒ‚‚����€€��€€€€€€€€€€����€€€€������€€��€€€€€€��€€€€€€‚‚„„„„„„„„„„„„„„……††††††††††………………„„„„………………††††††……„„……‡‡„„€€‚‚„„…………„„„„„„„„„„„„…………„„„„……………………„„ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒ‚‚‚‚‚‚������€€����€€��€€��€€��€€€€����������€€€€€€��€€€€€€��ƒƒ„„„„„„„„……„„„„……††††††††††……„„„„„„„„……………………………………„„††††€€‚‚„„…………„„„„„„„„„„„„…………„„„„……………………„„ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒ‚‚‚‚‚‚������€€����€€��€€��€€��€€€€����������€€€€€€��€€€€€€��ƒƒ„„„„„„„„……„„„„……††††††††††……„„„„„„„„……………………………………„„††††€€‚‚„„……„„ƒƒ„„„„„„„„„„††……††…………„„„„ƒƒƒƒƒƒ„„ƒƒƒƒƒƒƒƒ„„ƒƒƒƒƒƒ‚‚��€€��€€����������������€€€€������€€€€€€������€€€€€€€€‚‚„„ƒƒ„„ƒƒ„„„„„„„„……††††††††………………„„„„……„„……††††††………………††€€‚‚„„……„„ƒƒ„„„„„„„„„„††……††…………„„„„ƒƒƒƒƒƒ„„ƒƒƒƒƒƒƒƒ„„ƒƒƒƒƒƒ‚‚��€€��€€����������������€€€€������€€€€€€������€€€€€€€€‚‚„„ƒƒ„„ƒƒ„„„„„„„„……††††††††………………„„„„……„„……††††††………………††��ƒƒ…………„„„„„„„„ƒƒ„„……††……††„„„„„„ƒƒƒƒƒƒƒƒƒƒƒƒƒƒ„„ƒƒƒƒƒƒƒƒƒƒ��€€€€€€������������������€€������€€����€€������€€€€��€€€€€€ƒƒ„„„„„„„„„„„„………………††††…………††……„„„„„„„„…………††††……………………��ƒƒ…………„„„„„„„„ƒƒ„„……††……††„„„„„„ƒƒƒƒƒƒƒƒƒƒƒƒƒƒ„„ƒƒƒƒƒƒƒƒƒƒ��€€€€€€������������������€€������€€����€€������€€€€��€€€€€€ƒƒ„„„„„„„„„„„„………………††††…………††……„„„„„„„„…………††††……………………��ƒƒ……„„ƒƒ„„„„„„…………………………ƒƒƒƒƒƒƒƒƒƒ‚‚ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒ‚‚€€€€€€€€��������€€€€����€€��������������€€€€€€��€€����€€€€‚‚„„„„„„„„ƒƒ„„„„……………………††……††…………„„„„„„„„„„…………………………„„��ƒƒ……„„ƒƒ„„„„„„…………………………ƒƒƒƒƒƒƒƒƒƒ‚‚ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒ‚‚€€€€€€€€��������€€€€����€€��������������€€€€€€��€€����€€€€‚‚„„„„„„„„ƒƒ„„„„……………………††……††…………„„„„„„„„„„…………………………„„��„„„„ƒƒƒƒ………………………………„„‚‚‚‚ƒƒƒƒ‚‚ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒ‚‚��€€€€€€������€€€€€€��€€€€��€€����������€€€€��€€€€€€€€€€€€€€€€ƒƒ„„„„„„ƒƒ„„„„…………„„………………††††††……„„„„ƒƒƒƒ„„……††………………��„„„„ƒƒƒƒ………………………………„„‚‚‚‚ƒƒƒƒ‚‚ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒ‚‚��€€€€€€������€€€€€€��€€€€��€€����������€€€€��€€€€€€€€€€€€€€€€ƒƒ„„„„„„ƒƒ„„„„…………„„………………††††††……„„„„ƒƒƒƒ„„……††………………‚‚……„„„„………………„„„„ƒƒƒƒ‚‚‚‚‚‚‚‚‚‚‚‚ƒƒ‚‚ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒ‚‚‚‚€€€€€€€€€€��������€€��€€€€��€€€€€€€€��€€€€€€€€€€€€€€€€€€€€€€‚‚„„„„„„ƒƒƒƒ„„„„………………„„„„††‡‡‡‡††……„„ƒƒ‚‚ƒƒƒƒ……††……„„‚‚……„„„„………………„„„„ƒƒƒƒ‚‚‚‚‚‚‚‚‚‚‚‚ƒƒ‚‚ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒ‚‚‚‚€€€€€€€€€€��������€€��€€€€��€€€€€€€€��€€€€€€€€€€€€€€€€€€€€€€‚‚„„„„„„ƒƒƒƒ„„„„………………„„„„††‡‡‡‡††……„„ƒƒ‚‚ƒƒƒƒ……††……„„‚‚……………………„„ƒƒƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚ƒƒƒƒƒƒƒƒƒƒ‚‚ƒƒƒƒƒƒ€€€€€€€€€€€€��€€€€��€€€€€€��€€€€€€€€€€��€€€€€€€€��€€€€€€€€€€€€€€��„„„„„„„„„„„„„„„„……„„„„„„……††‡‡‡‡‡‡……„„ƒƒƒƒ‚‚„„††††……‚‚……………………„„ƒƒƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚ƒƒƒƒƒƒƒƒƒƒ‚‚ƒƒƒƒƒƒ€€€€€€€€€€€€��€€€€��€€€€€€��€€€€€€€€€€��€€€€€€€€��€€€€€€€€€€€€€€��„„„„„„„„„„„„„„„„……„„„„„„……††‡‡‡‡‡‡……„„ƒƒƒƒ‚‚„„††††……‚‚……„„„„ƒƒ‚‚‚‚‚‚ƒƒ‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚ƒƒƒƒƒƒƒƒ‚‚„„„„„„„„‚‚€€€€€€€€€€€€€€€€����€€€€€€€€€€€€€€€€��€€€€€€��€€��€€��€€€€€€€€ƒƒ„„„„„„„„……„„„„„„„„„„„„…………††††‡‡††……„„ƒƒ‚‚ƒƒ……††††‚‚……„„„„ƒƒ‚‚‚‚‚‚ƒƒ‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚ƒƒƒƒƒƒƒƒ‚‚„„„„„„„„‚‚€€€€€€€€€€€€€€€€����€€€€€€€€€€€€€€€€��€€€€€€��€€��€€��€€€€€€€€ƒƒ„„„„„„„„……„„„„„„„„„„„„…………††††‡‡††……„„ƒƒ‚‚ƒƒ……††††‚‚„„ƒƒ‚‚‚‚‚‚‚‚‚‚ƒƒ‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚ƒƒƒƒƒƒƒƒ„„„„„„„„„„„„‚‚€€€€€€€€€€€€€€€€��€€€€€€€€€€€€��€€€€€€��€€€€€€€€€€€€€€€€€€€€€€€€‚‚„„„„„„„„„„ƒƒ„„„„„„„„„„„„……††††††††††……„„‚‚ƒƒ„„…………‚‚„„ƒƒ‚‚‚‚‚‚‚‚‚‚ƒƒ‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚ƒƒƒƒƒƒƒƒ„„„„„„„„„„„„‚‚€€€€€€€€€€€€€€€€��€€€€€€€€€€€€��€€€€€€��€€€€€€€€€€€€€€€€€€€€€€€€‚‚„„„„„„„„„„ƒƒ„„„„„„„„„„„„……††††††††††……„„‚‚ƒƒ„„…………��‚‚‚‚��‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚ƒƒ„„……„„„„„„„„„„ƒƒƒƒ��€€€€€€€€€€€€€€€€��€€€€€€��€€€€€€€€€€����€€€€��������€€€€€€€€€€€€��„„„„„„„„„„„„ƒƒ„„„„„„„„……„„……††……„„††††……ƒƒ„„„„……„„��‚‚‚‚��‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚ƒƒ„„……„„„„„„„„„„ƒƒƒƒ��€€€€€€€€€€€€€€€€��€€€€€€��€€€€€€€€€€����€€€€��������€€€€€€€€€€€€��„„„„„„„„„„„„ƒƒ„„„„„„„„……„„……††……„„††††……ƒƒ„„„„……„„€€��‚‚‚‚��‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚ƒƒƒƒƒƒ„„„„……„„„„„„„„ƒƒƒƒ‚‚‚‚€€€€€€€€€€€€€€����€€€€����€€��€€€€€€��€€€€����������€€��€€€€€€€€€€ƒƒ„„„„„„ƒƒ„„„„ƒƒ„„„„„„…………„„……„„ƒƒ††††††„„„„„„„„ƒƒ€€��‚‚‚‚��‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚ƒƒƒƒƒƒ„„„„……„„„„„„„„ƒƒƒƒ‚‚‚‚€€€€€€€€€€€€€€����€€€€����€€��€€€€€€��€€€€����������€€��€€€€€€€€€€ƒƒ„„„„„„ƒƒ„„„„ƒƒ„„„„„„…………„„……„„ƒƒ††††††„„„„„„„„ƒƒ������‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚ƒƒ„„……„„„„…………„„„„ƒƒ‚‚‚‚‚‚‚‚��€€€€€€€€€€€€€€€€��€€€€����‚‚ƒƒƒƒ„„………………„„…………„„„„„„ƒƒ‚‚‚‚‚‚‚‚‚‚��€€€€‚‚ƒƒƒƒ„„„„„„„„ƒƒƒƒƒƒ„„„„„„…………„„ƒƒ……††‡‡……„„„„„„ƒƒ������‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚ƒƒ„„……„„„„…………„„„„ƒƒ‚‚‚‚‚‚‚‚��€€€€€€€€€€€€€€€€��€€€€����‚‚ƒƒƒƒ„„………………„„…………„„„„„„ƒƒ‚‚‚‚‚‚‚‚‚‚��€€€€‚‚ƒƒƒƒ„„„„„„„„ƒƒƒƒƒƒ„„„„„„…………„„ƒƒ……††‡‡……„„„„„„ƒƒ��‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚ƒƒ„„„„………………„„„„„„ƒƒƒƒ‚‚��‚‚‚‚‚‚‚‚��€€€€€€€€€€����€€��‚‚„„……‡‡‰‰‰‰ŠŠ‹‹‹‹ŒŒŒŒ‹‹ŒŒŒŒŒŒ‹‹ŠŠŠŠ‰‰ŠŠ‰‰ˆˆˆˆˆˆ‡‡††††…………„„„„„„„„„„ƒƒƒƒƒƒ„„ƒƒ……††„„„„……††††††……„„„„ƒƒ��‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚ƒƒ„„„„………………„„„„„„ƒƒƒƒ‚‚��‚‚‚‚‚‚‚‚��€€€€€€€€€€����€€��‚‚„„……‡‡‰‰‰‰ŠŠ‹‹‹‹ŒŒŒŒ‹‹ŒŒŒŒŒŒ‹‹ŠŠŠŠ‰‰ŠŠ‰‰ˆˆˆˆˆˆ‡‡††††…………„„„„„„„„„„ƒƒƒƒƒƒ„„ƒƒ……††„„„„……††††††……„„„„ƒƒ€€‚‚‚‚‚‚‚‚‚‚ƒƒƒƒ„„…………„„…………„„„„„„ƒƒ‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚��€€€€€€€€����‚‚……‡‡ˆˆŠŠ‹‹ŒŒ����������ŽŽ��ŒŒŒŒŒŒŒŒŒŒŠŠ‹‹��������ŽŽ��ŒŒŠŠŠŠ‰‰‰‰ˆˆˆˆ‡‡††……ƒƒƒƒƒƒ„„ƒƒ„„……„„ƒƒ……††††††……††……„„€€‚‚‚‚‚‚‚‚‚‚ƒƒƒƒ„„…………„„…………„„„„„„ƒƒ‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚��€€€€€€€€����‚‚……‡‡ˆˆŠŠ‹‹ŒŒ����������ŽŽ��ŒŒŒŒŒŒŒŒŒŒŠŠ‹‹��������ŽŽ��ŒŒŠŠŠŠ‰‰‰‰ˆˆˆˆ‡‡††……ƒƒƒƒƒƒ„„ƒƒ„„……„„ƒƒ……††††††……††……„„€€��‚‚‚‚ƒƒ„„„„„„„„„„„„„„„„„„‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚��€€€€€€€€��ƒƒ……ŠŠŒŒ��ŽŽ����‘‘��������ŽŽ������ŽŽŽŽŽŽ����ŒŒŒŒŒŒ��‘‘’’‘‘��‹‹‰‰‰‰‰‰‰‰ˆˆˆˆ‰‰‡‡……„„„„„„„„„„„„„„„„„„……………………††††„„€€��‚‚‚‚ƒƒ„„„„„„„„„„„„„„„„„„‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚��€€€€€€€€��ƒƒ……ŠŠŒŒ��ŽŽ����‘‘��������ŽŽ������ŽŽŽŽŽŽ����ŒŒŒŒŒŒ��‘‘’’‘‘��‹‹‰‰‰‰‰‰‰‰ˆˆˆˆ‰‰‡‡……„„„„„„„„„„„„„„„„„„……………………††††„„€€‚‚ƒƒ„„„„„„„„„„„„ƒƒƒƒ‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚��€€€€����ƒƒ‡‡ŠŠ��‘‘““’’’’‘‘��������������’’’’’’““““““““““’’����‘‘��‘‘’’��ŒŒŠŠŠŠ‰‰‰‰ˆˆˆˆˆˆ‡‡„„„„„„„„ƒƒ„„„„„„„„„„††……………………‡‡……€€‚‚ƒƒ„„„„„„„„„„„„ƒƒƒƒ‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚��€€€€����ƒƒ‡‡ŠŠ��‘‘““’’’’‘‘��������������’’’’’’““““““““““’’����‘‘��‘‘’’��ŒŒŠŠŠŠ‰‰‰‰ˆˆˆˆˆˆ‡‡„„„„„„„„ƒƒ„„„„„„„„„„††……………………‡‡……��ƒƒ„„„„„„„„ƒƒƒƒ‚‚��‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚ƒƒ„„††‹‹��ŽŽ‘‘““’’‘‘��ŽŽŽŽ����������‘‘‘‘’’’’““““““””””””““““““’’��‘‘‘‘��ŠŠŠŠ‰‰‰‰ˆˆˆˆ‡‡……„„„„„„ƒƒ‚‚ƒƒ„„„„„„„„††……ƒƒ„„……‡‡††��ƒƒ„„„„„„„„ƒƒƒƒ‚‚��‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚ƒƒ„„††‹‹��ŽŽ‘‘““’’‘‘��ŽŽŽŽ����������‘‘‘‘’’’’““““““””””””““““““’’��‘‘‘‘��ŠŠŠŠ‰‰‰‰ˆˆˆˆ‡‡……„„„„„„ƒƒ‚‚ƒƒ„„„„„„„„††……ƒƒ„„……‡‡††‚‚„„„„„„ƒƒ‚‚��‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚ƒƒƒƒ„„……‰‰��‘‘’’����‘‘��ŽŽŽŽŽŽ������ŽŽŽŽ��������‘‘’’’’““““““““’’’’’’‘‘����‘‘ŽŽŠŠŠŠŠŠ‰‰ˆˆ‡‡……ƒƒƒƒ„„„„„„ƒƒƒƒ„„„„„„„„…………„„„„„„††††‚‚„„„„„„ƒƒ‚‚��‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚ƒƒƒƒ„„……‰‰��‘‘’’����‘‘��ŽŽŽŽŽŽ������ŽŽŽŽ��������‘‘’’’’““““““““’’’’’’‘‘����‘‘ŽŽŠŠŠŠŠŠ‰‰ˆˆ‡‡……ƒƒƒƒ„„„„„„ƒƒƒƒ„„„„„„„„…………„„„„„„††††‚‚‚‚‚‚����‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚��‚‚‚‚‚‚ƒƒ„„††‰‰ŽŽ‘‘’’’’��ŒŒ����������ŽŽ����ŒŒ‹‹ŒŒ��ŽŽ����‘‘‘‘’’’’’’’’‘‘‘‘‘‘����ŽŽ��ŽŽŠŠŠŠŠŠŠŠ‡‡…………„„„„„„„„„„ƒƒƒƒ„„„„ƒƒ„„„„…………„„„„„„……‚‚‚‚‚‚����‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚��‚‚‚‚‚‚ƒƒ„„††‰‰ŽŽ‘‘’’’’��ŒŒ����������ŽŽ����ŒŒ‹‹ŒŒ��ŽŽ����‘‘‘‘’’’’’’’’‘‘‘‘‘‘����ŽŽ��ŽŽŠŠŠŠŠŠŠŠ‡‡…………„„„„„„„„„„ƒƒƒƒ„„„„ƒƒ„„„„…………„„„„„„……��������‚‚����‚‚‚‚‚‚��‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚ƒƒ„„††‡‡ŠŠ����‘‘‘‘��ŠŠ��ŽŽŽŽŽŽŽŽŒŒ‹‹‹‹‹‹‹‹‹‹ŒŒ����ŽŽ����‘‘‘‘’’‘‘‘‘��������ŒŒŽŽ��ŠŠŠŠ‰‰‡‡„„ƒƒ„„„„„„„„„„„„„„„„„„„„ƒƒƒƒ„„„„„„„„„„„„„„��������‚‚����‚‚‚‚‚‚��‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚ƒƒ„„††‡‡ŠŠ����‘‘‘‘��ŠŠ��ŽŽŽŽŽŽŽŽŒŒ‹‹‹‹‹‹‹‹‹‹ŒŒ����ŽŽ����‘‘‘‘’’‘‘‘‘��������ŒŒŽŽ��ŠŠŠŠ‰‰‡‡„„ƒƒ„„„„„„„„„„„„„„„„„„„„ƒƒƒƒ„„„„„„„„„„„„„„€€��‚‚��‚‚‚‚‚‚��‚‚‚‚‚‚‚‚‚‚ƒƒƒƒ‚‚‚‚‚‚‚‚‚‚‚‚ƒƒ‚‚ƒƒ††ˆˆ‡‡ŒŒ��������‹‹ŒŒŽŽŽŽŽŽ����ŒŒ��������ŒŒ‹‹ŒŒŒŒ��ŽŽ������‘‘������ŽŽ��ŒŒŒŒ‹‹ŠŠŠŠŠŠ††„„ƒƒ„„„„„„„„„„„„„„ƒƒ„„„„„„ƒƒƒƒ„„„„„„„„„„„„„„€€��‚‚��‚‚‚‚‚‚��‚‚‚‚‚‚‚‚‚‚ƒƒƒƒ‚‚‚‚‚‚‚‚‚‚‚‚ƒƒ‚‚ƒƒ††ˆˆ‡‡ŒŒ��������‹‹ŒŒŽŽŽŽŽŽ����ŒŒ��������ŒŒ‹‹ŒŒŒŒ��ŽŽ������‘‘������ŽŽ��ŒŒŒŒ‹‹ŠŠŠŠŠŠ††„„ƒƒ„„„„„„„„„„„„„„ƒƒ„„„„„„ƒƒƒƒ„„„„„„„„„„„„„„€€����‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚ƒƒƒƒ‚‚‚‚‚‚ƒƒ‚‚‚‚‚‚ƒƒ„„ˆˆˆˆ‡‡ŒŒŽŽŽŽ��ŠŠŒŒ��ŽŽŽŽ��ŒŒ��������ŽŽŽŽŽŽŒŒŒŒŒŒ����ŽŽ������������ŽŽ��ŒŒŒŒ‹‹ŠŠˆˆ……„„„„ƒƒ„„ƒƒƒƒ„„„„„„„„„„„„„„„„„„„„ƒƒ„„……„„…………„„€€����‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚ƒƒƒƒ‚‚‚‚‚‚ƒƒ‚‚‚‚‚‚ƒƒ„„ˆˆˆˆ‡‡ŒŒŽŽŽŽ��ŠŠŒŒ��ŽŽŽŽ��ŒŒ��������ŽŽŽŽŽŽŒŒŒŒŒŒ����ŽŽ������������ŽŽ��ŒŒŒŒ‹‹ŠŠˆˆ……„„„„ƒƒ„„ƒƒƒƒ„„„„„„„„„„„„„„„„„„„„ƒƒ„„……„„…………„„€€����‚‚��‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚ƒƒ‚‚ƒƒƒƒƒƒƒƒƒƒƒƒ„„……ˆˆ‰‰‡‡ŠŠ��ŒŒ‰‰‰‰ŒŒ������������ŽŽ������������ŽŽŒŒŒŒ��ŽŽ����������ŽŽŒŒ��ŽŽŽŽ‹‹ˆˆ……ƒƒ„„„„„„ƒƒƒƒ„„ƒƒ„„„„„„„„„„„„„„ƒƒƒƒ„„„„……„„„„„„„„€€����‚‚��‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚ƒƒ‚‚ƒƒƒƒƒƒƒƒƒƒƒƒ„„……ˆˆ‰‰‡‡ŠŠ��ŒŒ‰‰‰‰ŒŒ������������ŽŽ������������ŽŽŒŒŒŒ��ŽŽ����������ŽŽŒŒ��ŽŽŽŽ‹‹ˆˆ……ƒƒ„„„„„„ƒƒƒƒ„„ƒƒ„„„„„„„„„„„„„„ƒƒƒƒ„„„„……„„„„„„„„€€��‚‚��‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚ƒƒƒƒƒƒ„„„„„„„„ƒƒ……‡‡ˆˆ‡‡ˆˆŒŒŠŠ‰‰ŠŠ‹‹ŒŒ��ŒŒŒŒ����ŽŽ��ŽŽ����������ŽŽ������ŽŽ������ŽŽ��ŽŽ��������‰‰……ƒƒ„„„„„„ƒƒƒƒ„„„„ƒƒ„„„„„„„„„„„„„„ƒƒ„„„„„„„„„„……„„€€��‚‚��‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚ƒƒƒƒƒƒ„„„„„„„„ƒƒ……‡‡ˆˆ‡‡ˆˆŒŒŠŠ‰‰ŠŠ‹‹ŒŒ��ŒŒŒŒ����ŽŽ��ŽŽ����������ŽŽ������ŽŽ������ŽŽ��ŽŽ��������‰‰……ƒƒ„„„„„„ƒƒƒƒ„„„„ƒƒ„„„„„„„„„„„„„„ƒƒ„„„„„„„„„„……„„€€������‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚ƒƒ‚‚ƒƒ„„„„„„ƒƒƒƒƒƒ„„„„„„……‡‡‡‡††‰‰ŠŠˆˆ‰‰‹‹ŒŒŒŒ‹‹‹‹��ŽŽ��ŽŽŽŽ��‘‘����������‹‹ŒŒŽŽ����ŽŽ��ŽŽ����������‰‰„„ƒƒƒƒ„„„„ƒƒƒƒ„„ƒƒƒƒƒƒƒƒ„„„„„„„„ƒƒƒƒ„„ƒƒ„„ƒƒ„„……„„€€������‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚ƒƒ‚‚ƒƒ„„„„„„ƒƒƒƒƒƒ„„„„„„……‡‡‡‡††‰‰ŠŠˆˆ‰‰‹‹ŒŒŒŒ‹‹‹‹��ŽŽ��ŽŽŽŽ��‘‘����������‹‹ŒŒŽŽ����ŽŽ��ŽŽ����������‰‰„„ƒƒƒƒ„„„„ƒƒƒƒ„„ƒƒƒƒƒƒƒƒ„„„„„„„„ƒƒƒƒ„„ƒƒ„„ƒƒ„„……„„€€��‚‚��‚‚‚‚‚‚‚‚‚‚ƒƒƒƒ„„ƒƒ„„„„„„ƒƒ„„„„ƒƒƒƒ„„„„„„„„„„††††ˆˆ‡‡‡‡‰‰‹‹ŒŒ‹‹‹‹ŒŒ��������ŽŽŽŽ��ŒŒŒŒŒŒŽŽŽŽ‹‹ŒŒ��‘‘‘‘ŽŽ��ŽŽŽŽŽŽ������ˆˆƒƒ„„ƒƒ„„ƒƒƒƒƒƒ„„ƒƒƒƒ„„ƒƒ„„„„„„„„ƒƒƒƒ„„„„„„„„ƒƒ„„……€€��‚‚��‚‚‚‚‚‚‚‚‚‚ƒƒƒƒ„„ƒƒ„„„„„„ƒƒ„„„„ƒƒƒƒ„„„„„„„„„„††††ˆˆ‡‡‡‡‰‰‹‹ŒŒ‹‹‹‹ŒŒ��������ŽŽŽŽ��ŒŒŒŒŒŒŽŽŽŽ‹‹ŒŒ��‘‘‘‘ŽŽ��ŽŽŽŽŽŽ������ˆˆƒƒ„„ƒƒ„„ƒƒƒƒƒƒ„„ƒƒƒƒ„„ƒƒ„„„„„„„„ƒƒƒƒ„„„„„„„„ƒƒ„„……€€��������‚‚‚‚ƒƒƒƒƒƒƒƒƒƒƒƒƒƒ„„„„„„„„„„„„„„„„„„„„„„„„……ˆˆˆˆ‡‡‡‡‰‰‹‹‹‹‹‹‹‹ŒŒ��ŽŽ��ŽŽ����‹‹‹‹‹‹ŒŒŽŽ��������‘‘’’������ŒŒ‹‹ŠŠ��ŽŽ††ƒƒ„„ƒƒƒƒƒƒƒƒƒƒ„„ƒƒƒƒ‚‚ƒƒƒƒƒƒƒƒ„„ƒƒ„„„„ƒƒ„„„„„„„„ƒƒ€€��������‚‚‚‚ƒƒƒƒƒƒƒƒƒƒƒƒƒƒ„„„„„„„„„„„„„„„„„„„„„„„„……ˆˆˆˆ‡‡‡‡‰‰‹‹‹‹‹‹‹‹ŒŒ��ŽŽ��ŽŽ����‹‹‹‹‹‹ŒŒŽŽ��������‘‘’’������ŒŒ‹‹ŠŠ��ŽŽ††ƒƒ„„ƒƒƒƒƒƒƒƒƒƒ„„ƒƒƒƒ‚‚ƒƒƒƒƒƒƒƒ„„ƒƒ„„„„ƒƒ„„„„„„„„ƒƒ€€��‚‚‚‚ƒƒ„„ƒƒ„„„„ƒƒ„„„„„„„„„„„„„„„„„„„„„„……„„„„„„„„††����ŒŒ‰‰ŠŠŠŠ‹‹ŒŒŒŒ��ŽŽŽŽŽŽŽŽŽŽŽŽ������ŽŽ��ŽŽ��‘‘‘‘‘‘’’��������‹‹ŠŠŒŒŒŒ……ƒƒ„„„„ƒƒ„„„„ƒƒƒƒƒƒƒƒƒƒƒƒ„„„„ƒƒ„„„„„„ƒƒƒƒƒƒƒƒ„„„„ƒƒ€€��‚‚‚‚ƒƒ„„ƒƒ„„„„ƒƒ„„„„„„„„„„„„„„„„„„„„„„……„„„„„„„„††����ŒŒ‰‰ŠŠŠŠ‹‹ŒŒŒŒ��ŽŽŽŽŽŽŽŽŽŽŽŽ������ŽŽ��ŽŽ��‘‘‘‘‘‘’’��������‹‹ŠŠŒŒŒŒ……ƒƒ„„„„ƒƒ„„„„ƒƒƒƒƒƒƒƒƒƒƒƒ„„„„ƒƒ„„„„„„ƒƒƒƒƒƒƒƒ„„„„ƒƒ��‚‚ƒƒƒƒ„„„„ƒƒƒƒƒƒƒƒ„„ƒƒ„„„„„„„„„„„„„„„„„„……„„„„ƒƒƒƒ‹‹””’’��‹‹ŠŠŠŠ‹‹ŒŒ������ŽŽŽŽŽŽ����������������‘‘’’‘‘’’““‘‘����ŽŽŽŽ‹‹ŠŠ‰‰„„ƒƒ„„„„ƒƒ„„ƒƒƒƒ„„ƒƒƒƒƒƒƒƒƒƒƒƒƒƒ„„„„„„„„ƒƒƒƒƒƒƒƒƒƒƒƒ��‚‚ƒƒƒƒ„„„„ƒƒƒƒƒƒƒƒ„„ƒƒ„„„„„„„„„„„„„„„„„„……„„„„ƒƒƒƒ‹‹””’’��‹‹ŠŠŠŠ‹‹ŒŒ������ŽŽŽŽŽŽ����������������‘‘’’‘‘’’““‘‘����ŽŽŽŽ‹‹ŠŠ‰‰„„ƒƒ„„„„ƒƒ„„ƒƒƒƒ„„ƒƒƒƒƒƒƒƒƒƒƒƒƒƒ„„„„„„„„ƒƒƒƒƒƒƒƒƒƒƒƒ��ƒƒƒƒƒƒƒƒ„„ƒƒ‚‚‚‚ƒƒƒƒƒƒƒƒ„„„„„„„„„„„„„„„„„„„„„„ƒƒƒƒŽŽ——‘‘ŽŽ��ŠŠŠŠŒŒ��������������‘‘������‘‘‘‘��‘‘’’‘‘‘‘’’““““��ŒŒ‹‹ŒŒŒŒ‹‹‰‰„„ƒƒ„„„„ƒƒ„„ƒƒƒƒ„„ƒƒƒƒƒƒ‚‚ƒƒƒƒƒƒƒƒ„„„„„„ƒƒƒƒƒƒƒƒƒƒƒƒ��ƒƒƒƒƒƒƒƒ„„ƒƒ‚‚‚‚ƒƒƒƒƒƒƒƒ„„„„„„„„„„„„„„„„„„„„„„ƒƒƒƒŽŽ——‘‘ŽŽ��ŠŠŠŠŒŒ��������������‘‘������‘‘‘‘��‘‘’’‘‘‘‘’’““““��ŒŒ‹‹ŒŒŒŒ‹‹‰‰„„ƒƒ„„„„ƒƒ„„ƒƒƒƒ„„ƒƒƒƒƒƒ‚‚ƒƒƒƒƒƒƒƒ„„„„„„ƒƒƒƒƒƒƒƒƒƒƒƒ��ƒƒ‚‚‚‚������‚‚‚‚‚‚ƒƒƒƒƒƒƒƒƒƒ„„„„……„„……„„„„„„„„ƒƒƒƒŽŽ˜˜’’��ŽŽ‹‹ŠŠ��ŽŽŽŽ��‘‘‘‘’’’’’’’’’’’’’’‘‘’’’’’’‘‘’’““••––““��ŒŒŒŒ����‰‰……„„„„„„„„„„„„ƒƒ„„„„„„ƒƒ‚‚ƒƒ„„‚‚ƒƒ„„„„„„ƒƒ„„ƒƒƒƒƒƒƒƒ��ƒƒ‚‚‚‚������‚‚‚‚‚‚ƒƒƒƒƒƒƒƒƒƒ„„„„……„„……„„„„„„„„ƒƒƒƒŽŽ˜˜’’��ŽŽ‹‹ŠŠ��ŽŽŽŽ��‘‘‘‘’’’’’’’’’’’’’’‘‘’’’’’’‘‘’’““••––““��ŒŒŒŒ����‰‰……„„„„„„„„„„„„ƒƒ„„„„„„ƒƒ‚‚ƒƒ„„‚‚ƒƒ„„„„„„ƒƒ„„ƒƒƒƒƒƒƒƒ��€€€€������‚‚‚‚‚‚ƒƒƒƒ„„„„‚‚��‚‚„„„„„„„„„„„„„„ƒƒ„„ƒƒ��——’’����ŒŒŠŠŒŒŽŽŽŽ��‘‘’’““’’““““““““““’’““““’’‘‘’’••™™˜˜˜˜’’ŽŽŽŽŽŽŽŽŠŠ……„„……„„„„„„„„ƒƒ„„„„„„„„ƒƒƒƒƒƒƒƒƒƒƒƒ„„„„ƒƒ„„„„„„ƒƒƒƒ��€€€€������‚‚‚‚‚‚ƒƒƒƒ„„„„‚‚��‚‚„„„„„„„„„„„„„„ƒƒ„„ƒƒ��——’’����ŒŒŠŠŒŒŽŽŽŽ��‘‘’’““’’““““““““““’’““““’’‘‘’’••™™˜˜˜˜’’ŽŽŽŽŽŽŽŽŠŠ……„„……„„„„„„„„ƒƒ„„„„„„„„ƒƒƒƒƒƒƒƒƒƒƒƒ„„„„ƒƒ„„„„„„ƒƒƒƒ€€€€€€������‚‚‚‚‚‚‚‚ƒƒ„„„„„„‚‚‚‚‚‚‚‚ƒƒ„„„„„„„„„„……„„ŠŠ••’’““““ŒŒŠŠ‹‹������‘‘’’““““““””””““’’’’’’‘‘’’’’””™™››šš™™””������ŽŽ‹‹††‚‚„„„„„„„„„„„„„„ƒƒƒƒ„„„„ƒƒ‚‚ƒƒƒƒƒƒƒƒ„„„„ƒƒ„„„„„„ƒƒ€€€€€€������‚‚‚‚‚‚‚‚ƒƒ„„„„„„‚‚‚‚‚‚‚‚ƒƒ„„„„„„„„„„……„„ŠŠ••’’““““ŒŒŠŠ‹‹������‘‘’’““““““””””““’’’’’’‘‘’’’’””™™››šš™™””������ŽŽ‹‹††‚‚„„„„„„„„„„„„„„ƒƒƒƒ„„„„ƒƒ‚‚ƒƒƒƒƒƒƒƒ„„„„ƒƒ„„„„„„ƒƒ€€€€������‚‚��‚‚‚‚‚‚ƒƒ„„„„……ƒƒ��‚‚����‚‚ƒƒ…………………………ˆˆ‘‘””••’’��ŠŠŠŠ‹‹��ŽŽ‘‘‘‘’’’’““””””““““’’‘‘’’””••——™™››››šš””������ŽŽŒŒ‡‡„„ƒƒ„„„„„„……„„ƒƒƒƒƒƒƒƒ„„ƒƒƒƒ„„‚‚ƒƒƒƒƒƒ„„„„ƒƒƒƒƒƒƒƒ€€€€������‚‚��‚‚‚‚‚‚ƒƒ„„„„……ƒƒ��‚‚����‚‚ƒƒ…………………………ˆˆ‘‘””••’’��ŠŠŠŠ‹‹��ŽŽ‘‘‘‘’’’’““””””““““’’‘‘’’””••——™™››››šš””������ŽŽŒŒ‡‡„„ƒƒ„„„„„„……„„ƒƒƒƒƒƒƒƒ„„ƒƒƒƒ„„‚‚ƒƒƒƒƒƒ„„„„ƒƒƒƒƒƒƒƒ€€����������‚‚‚‚‚‚ƒƒ„„„„„„……„„��‚‚‚‚‚‚‚‚‚‚„„„„………………††��””””����‹‹‰‰‹‹ŒŒŽŽ��‘‘‘‘’’’’““““““““’’’’””••””””••——˜˜——‘‘��‘‘‘‘��ŒŒ‡‡…………„„ƒƒ„„……„„ƒƒƒƒ„„„„„„ƒƒƒƒ„„ƒƒƒƒƒƒƒƒƒƒ„„„„„„„„„„€€����������‚‚‚‚‚‚ƒƒ„„„„„„……„„��‚‚‚‚‚‚‚‚‚‚„„„„………………††��””””����‹‹‰‰‹‹ŒŒŽŽ��‘‘‘‘’’’’““““““““’’’’””••””””••——˜˜——‘‘��‘‘‘‘��ŒŒ‡‡…………„„ƒƒ„„……„„ƒƒƒƒ„„„„„„ƒƒƒƒ„„ƒƒƒƒƒƒƒƒƒƒ„„„„„„„„„„€€��������‚‚ƒƒƒƒƒƒƒƒ„„„„„„„„„„ƒƒ‚‚‚‚‚‚ƒƒƒƒ‚‚‚‚ƒƒ„„…………ŠŠ’’““‘‘‘‘��ŠŠŠŠ‹‹ŽŽ������‘‘‘‘’’’’““’’’’’’••––““‘‘‘‘’’““’’��ŽŽ‘‘‘‘��ŒŒ‡‡………………„„ƒƒƒƒ„„„„„„„„„„„„„„ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒ„„„„„„€€��������‚‚ƒƒƒƒƒƒƒƒ„„„„„„„„„„ƒƒ‚‚‚‚‚‚ƒƒƒƒ‚‚‚‚ƒƒ„„…………ŠŠ’’““‘‘‘‘��ŠŠŠŠ‹‹ŽŽ������‘‘‘‘’’’’““’’’’’’••––““‘‘‘‘’’““’’��ŽŽ‘‘‘‘��ŒŒ‡‡………………„„ƒƒƒƒ„„„„„„„„„„„„„„ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒ„„„„„„€€€€��������‚‚ƒƒƒƒ‚‚ƒƒƒƒƒƒƒƒƒƒ„„‚‚��‚‚‚‚ƒƒ„„‚‚‚‚‚‚„„„„††ŽŽ‘‘‘‘’’ŽŽŠŠ‰‰‹‹��ŽŽ������‘‘��‘‘’’’’’’‘‘’’””““’’‘‘‘‘��ŽŽŒŒŽŽ������‹‹††…………………………ƒƒƒƒ„„„„„„„„„„„„„„ƒƒƒƒƒƒƒƒƒƒƒƒ‚‚‚‚„„…………€€€€��������‚‚ƒƒƒƒ‚‚ƒƒƒƒƒƒƒƒƒƒ„„‚‚��‚‚‚‚ƒƒ„„‚‚‚‚‚‚„„„„††ŽŽ‘‘‘‘’’ŽŽŠŠ‰‰‹‹��ŽŽ������‘‘��‘‘’’’’’’‘‘’’””““’’‘‘‘‘��ŽŽŒŒŽŽ������‹‹††…………………………ƒƒƒƒ„„„„„„„„„„„„„„ƒƒƒƒƒƒƒƒƒƒƒƒ‚‚‚‚„„…………€€€€��������‚‚‚‚‚‚‚‚ƒƒƒƒƒƒƒƒ„„ƒƒ‚‚��‚‚‚‚‚‚ƒƒ„„„„ƒƒ‚‚ƒƒ††ˆˆ��’’““‘‘‹‹‰‰ŠŠŒŒ��ŽŽ��������‘‘‘‘��‘‘‘‘������������ŽŽ‹‹ŠŠŒŒ����ŽŽ‹‹††…………………………††……ƒƒ„„„„„„„„„„„„ƒƒƒƒƒƒƒƒƒƒ‚‚ƒƒƒƒƒƒƒƒ„„€€€€��������‚‚‚‚‚‚‚‚ƒƒƒƒƒƒƒƒ„„ƒƒ‚‚��‚‚‚‚‚‚ƒƒ„„„„ƒƒ‚‚ƒƒ††ˆˆ��’’““‘‘‹‹‰‰ŠŠŒŒ��ŽŽ��������‘‘‘‘��‘‘‘‘������������ŽŽ‹‹ŠŠŒŒ����ŽŽ‹‹††…………………………††……ƒƒ„„„„„„„„„„„„ƒƒƒƒƒƒƒƒƒƒ‚‚ƒƒƒƒƒƒƒƒ„„€€€€������‚‚‚‚ƒƒƒƒ‚‚ƒƒƒƒƒƒ„„„„ƒƒƒƒ‚‚��‚‚‚‚‚‚ƒƒ„„„„„„„„††††‹‹‘‘””““��ˆˆŠŠŒŒ��ŽŽ��������������������ŽŽ����������‹‹‰‰‹‹‹‹ŒŒŒŒŠŠ……………………††……††††……ƒƒ„„„„„„……„„„„„„„„ƒƒƒƒƒƒ‚‚ƒƒƒƒƒƒƒƒ€€€€������‚‚‚‚ƒƒƒƒ‚‚ƒƒƒƒƒƒ„„„„ƒƒƒƒ‚‚��‚‚‚‚‚‚ƒƒ„„„„„„„„††††‹‹‘‘””““��ˆˆŠŠŒŒ��ŽŽ��������������������ŽŽ����������‹‹‰‰‹‹‹‹ŒŒŒŒŠŠ……………………††……††††……ƒƒ„„„„„„……„„„„„„„„ƒƒƒƒƒƒ‚‚ƒƒƒƒƒƒƒƒ€€€€������‚‚‚‚ƒƒƒƒƒƒ„„ƒƒƒƒ„„„„„„„„ƒƒ����‚‚‚‚‚‚ƒƒ„„„„††‡‡ˆˆ‰‰��““””��ˆˆŠŠ‹‹ŒŒ��������������ŽŽŽŽŽŽŽŽ��ŽŽ��ŽŽ����‹‹ŠŠŠŠŠŠŠŠ‹‹‰‰††…………………………††††††……„„ƒƒ„„……„„„„„„„„ƒƒ‚‚ƒƒƒƒƒƒƒƒƒƒƒƒ€€€€������‚‚‚‚ƒƒƒƒƒƒ„„ƒƒƒƒ„„„„„„„„ƒƒ����‚‚‚‚‚‚ƒƒ„„„„††‡‡ˆˆ‰‰��““””��ˆˆŠŠ‹‹ŒŒ��������������ŽŽŽŽŽŽŽŽ��ŽŽ��ŽŽ����‹‹ŠŠŠŠŠŠŠŠ‹‹‰‰††…………………………††††††……„„ƒƒ„„……„„„„„„„„ƒƒ‚‚ƒƒƒƒƒƒƒƒƒƒƒƒ€€€€€€��‚‚‚‚‚‚‚‚ƒƒƒƒƒƒƒƒƒƒƒƒ„„„„„„„„‚‚������‚‚‚‚ƒƒ……‡‡ˆˆ‰‰ŠŠŠŠŒŒ��ŽŽ‰‰ŠŠŒŒŒŒŽŽ��������ŽŽŽŽ��ŒŒ��ŽŽ��’’““‘‘������ŒŒ‹‹‹‹ŠŠŠŠˆˆ………………††…………††††……††††……„„„„„„„„„„„„ƒƒƒƒƒƒ„„ƒƒƒƒƒƒƒƒ€€€€€€��‚‚‚‚‚‚‚‚ƒƒƒƒƒƒƒƒƒƒƒƒ„„„„„„„„‚‚������‚‚‚‚ƒƒ……‡‡ˆˆ‰‰ŠŠŠŠŒŒ��ŽŽ‰‰ŠŠŒŒŒŒŽŽ��������ŽŽŽŽ��ŒŒ��ŽŽ��’’““‘‘������ŒŒ‹‹‹‹ŠŠŠŠˆˆ………………††…………††††……††††……„„„„„„„„„„„„ƒƒƒƒƒƒ„„ƒƒƒƒƒƒƒƒ€€€€����‚‚����‚‚‚‚‚‚ƒƒƒƒƒƒ„„ƒƒƒƒ„„ƒƒƒƒ��������‚‚„„††ˆˆ‡‡‰‰ŠŠ‡‡ˆˆŒŒ��ŠŠ‰‰ŒŒ��ŽŽ��‘‘��������������““““““””””““’’‘‘������‹‹ŠŠ††………………††…………………………††††††……„„„„„„„„„„„„ƒƒƒƒƒƒƒƒƒƒƒƒƒƒ€€€€����‚‚����‚‚‚‚‚‚ƒƒƒƒƒƒ„„ƒƒƒƒ„„ƒƒƒƒ��������‚‚„„††ˆˆ‡‡‰‰ŠŠ‡‡ˆˆŒŒ��ŠŠ‰‰ŒŒ��ŽŽ��‘‘��������������““““““””””““’’‘‘������‹‹ŠŠ††………………††…………………………††††††……„„„„„„„„„„„„ƒƒƒƒƒƒƒƒƒƒƒƒƒƒ€€€€����‚‚����‚‚‚‚‚‚‚‚ƒƒƒƒ„„ƒƒƒƒƒƒƒƒƒƒ‚‚��€€��‚‚„„……‡‡ˆˆ‰‰ŠŠ‡‡‡‡‹‹��ŠŠ‰‰ŒŒ��ŽŽ��’’’’��ŽŽŽŽ����‘‘““’’��ŽŽ��‘‘����’’‘‘ŽŽŒŒŠŠ††………………………………………………††††††††……„„ƒƒ„„„„„„ƒƒƒƒƒƒƒƒ„„ƒƒƒƒ€€€€����‚‚����‚‚‚‚‚‚‚‚ƒƒƒƒ„„ƒƒƒƒƒƒƒƒƒƒ‚‚��€€��‚‚„„……‡‡ˆˆ‰‰ŠŠ‡‡‡‡‹‹��ŠŠ‰‰ŒŒ��ŽŽ��’’’’��ŽŽŽŽ����‘‘““’’��ŽŽ��‘‘����’’‘‘ŽŽŒŒŠŠ††………………………………………………††††††††……„„ƒƒ„„„„„„ƒƒƒƒƒƒƒƒ„„ƒƒƒƒ€€����‚‚‚‚��‚‚‚‚‚‚‚‚‚‚ƒƒƒƒƒƒƒƒƒƒƒƒ„„ƒƒ„„ƒƒ������ƒƒ……††‡‡‰‰‰‰ˆˆ‡‡ŠŠŽŽ‹‹ŠŠ‹‹��ŽŽ��’’““’’������’’••˜˜——““����������””������ŠŠ††………………††……††…………………………††††††††……„„„„„„„„ƒƒ„„ƒƒ„„„„„„€€����‚‚‚‚��‚‚‚‚‚‚‚‚‚‚ƒƒƒƒƒƒƒƒƒƒƒƒ„„ƒƒ„„ƒƒ������ƒƒ……††‡‡‰‰‰‰ˆˆ‡‡ŠŠŽŽ‹‹ŠŠ‹‹��ŽŽ��’’““’’������’’••˜˜——““����������””������ŠŠ††………………††……††…………………………††††††††……„„„„„„„„ƒƒ„„ƒƒ„„„„„„€€����‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚ƒƒƒƒƒƒƒƒƒƒƒƒ„„„„„„„„‚‚��‚‚ƒƒ……††††ˆˆˆˆˆˆˆˆŠŠŽŽ��‹‹ŒŒ��ŽŽ����““’’‘‘����““˜˜››œœ››šš˜˜––••••““ŽŽŒŒ��ŠŠ……††††††††††††††††……††††……††††††††††…………„„„„„„„„…………„„€€����‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚ƒƒƒƒƒƒƒƒƒƒƒƒ„„„„„„„„‚‚��‚‚ƒƒ……††††ˆˆˆˆˆˆˆˆŠŠŽŽ��‹‹ŒŒ��ŽŽ����““’’‘‘����““˜˜››œœ››šš˜˜––••••““ŽŽŒŒ��ŠŠ……††††††††††††††††……††††……††††††††††…………„„„„„„„„…………„„€€€€��‚‚‚‚‚‚‚‚‚‚‚‚‚‚ƒƒƒƒƒƒƒƒƒƒƒƒƒƒ„„„„ƒƒ„„‚‚����‚‚ƒƒ„„……††††‡‡‹‹ŽŽ��ŽŽŽŽŽŽ��ŽŽŽŽ��’’’’““””••””••––˜˜ššœœ����œœ™™’’����ŒŒ‰‰††††††††††‡‡‡‡‡‡††††††…………††††††††††††††„„„„„„„„…………ƒƒ€€€€��‚‚‚‚‚‚‚‚‚‚‚‚‚‚ƒƒƒƒƒƒƒƒƒƒƒƒƒƒ„„„„ƒƒ„„‚‚����‚‚ƒƒ„„……††††‡‡‹‹ŽŽ��ŽŽŽŽŽŽ��ŽŽŽŽ��’’’’““””••””••––˜˜ššœœ����œœ™™’’����ŒŒ‰‰††††††††††‡‡‡‡‡‡††††††…………††††††††††††††„„„„„„„„…………ƒƒ€€����‚‚‚‚‚‚‚‚‚‚‚‚ƒƒƒƒƒƒ‚‚ƒƒƒƒƒƒƒƒ„„„„ƒƒƒƒƒƒ‚‚��‚‚ƒƒ„„††††††††��šš��ŽŽ������ŽŽ������’’••——™™˜˜””‘‘‘‘““––˜˜˜˜——””ŽŽŽŽ��ŽŽˆˆ††††††††††‡‡††‡‡††††††††††††……††††††††‡‡††„„„„„„…………„„€€����‚‚‚‚‚‚‚‚‚‚‚‚ƒƒƒƒƒƒ‚‚ƒƒƒƒƒƒƒƒ„„„„ƒƒƒƒƒƒ‚‚��‚‚ƒƒ„„††††††††��šš��ŽŽ������ŽŽ������’’••——™™˜˜””‘‘‘‘““––˜˜˜˜——””ŽŽŽŽ��ŽŽˆˆ††††††††††‡‡††‡‡††††††††††††……††††††††‡‡††„„„„„„…………„„€€������‚‚‚‚‚‚‚‚ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒ„„ƒƒƒƒ‚‚����‚‚ƒƒ„„……„„„„’’¥¥™™ŽŽ‘‘““’’‘‘����‘‘‘‘””——šš™™––’’������������ŽŽŽŽ‘‘””ŽŽ‡‡††††††††††††††††††‡‡‡‡‡‡‡‡††††‡‡††††††‡‡††……„„„„„„……„„€€������‚‚‚‚‚‚‚‚ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒ„„ƒƒƒƒ‚‚����‚‚ƒƒ„„……„„„„’’¥¥™™ŽŽ‘‘““’’‘‘����‘‘‘‘””——šš™™––’’������������ŽŽŽŽ‘‘””ŽŽ‡‡††††††††††††††††††‡‡‡‡‡‡‡‡††††‡‡††††††‡‡††……„„„„„„……„„€€����‚‚‚‚‚‚‚‚‚‚ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒ„„ƒƒƒƒ‚‚������‚‚‚‚‚‚‚‚““««¢¢’’’’””””””’’‘‘����’’––˜˜™™™™––““’’������������““””ŒŒ††††……………………††††††††††‡‡‡‡‡‡‡‡‡‡‡‡‡‡††††††††††††„„„„„„„„€€����‚‚‚‚‚‚‚‚‚‚ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒ„„ƒƒƒƒ‚‚������‚‚‚‚‚‚‚‚““««¢¢’’’’””””””’’‘‘����’’––˜˜™™™™––““’’������������““””ŒŒ††††……………………††††††††††‡‡‡‡‡‡‡‡‡‡‡‡‡‡††††††††††††„„„„„„„„������‚‚����ƒƒƒƒ‚‚ƒƒƒƒƒƒƒƒ„„„„ƒƒƒƒƒƒƒƒƒƒ„„„„„„ƒƒ��������������••®®ªª™™’’””––––••““’’‘‘‘‘””••——šššš˜˜——••““’’““••––••’’ŠŠ……††††††……††††††††††††††‡‡‡‡‡‡‡‡‡‡‡‡††††††‡‡‡‡‡‡††††„„„„������‚‚����ƒƒƒƒ‚‚ƒƒƒƒƒƒƒƒ„„„„ƒƒƒƒƒƒƒƒƒƒ„„„„„„ƒƒ��������������••®®ªª™™’’””––––••““’’‘‘‘‘””••——šššš˜˜——••““’’““••––••’’ŠŠ……††††††……††††††††††††††‡‡‡‡‡‡‡‡‡‡‡‡††††††‡‡‡‡‡‡††††„„„„������‚‚‚‚‚‚ƒƒ„„‚‚ƒƒƒƒƒƒƒƒ„„ƒƒƒƒƒƒƒƒƒƒ„„„„„„„„„„ƒƒ����������’’¯¯¯¯¤¤””””——————––””‘‘‘‘‘‘’’••˜˜™™šššššššš™™™™šš——““ŽŽˆˆ††††††††……††††††††††‡‡††††‡‡‡‡‡‡‡‡‡‡‡‡‡‡††‡‡††‡‡‡‡‡‡‡‡……������‚‚‚‚‚‚ƒƒ„„‚‚ƒƒƒƒƒƒƒƒ„„ƒƒƒƒƒƒƒƒƒƒ„„„„„„„„„„ƒƒ����������’’¯¯¯¯¤¤””””——————––””‘‘‘‘‘‘’’••˜˜™™šššššššš™™™™šš——““ŽŽˆˆ††††††††……††††††††††‡‡††††‡‡‡‡‡‡‡‡‡‡‡‡‡‡††‡‡††‡‡‡‡‡‡‡‡……€€����‚‚ƒƒƒƒƒƒƒƒ‚‚‚‚‚‚ƒƒƒƒƒƒ„„ƒƒƒƒƒƒ„„„„„„„„ƒƒƒƒ„„����������ŠŠ««°°¬¬��““˜˜™™™™™™––““’’����‘‘””––˜˜™™››šš™™™™šš––��ŠŠ‡‡††††††††††…………††††††††‡‡††††‡‡‡‡‡‡‡‡‡‡‡‡‡‡††††‡‡‡‡‡‡‡‡‡‡€€����‚‚ƒƒƒƒƒƒƒƒ‚‚‚‚‚‚ƒƒƒƒƒƒ„„ƒƒƒƒƒƒ„„„„„„„„ƒƒƒƒ„„����������ŠŠ««°°¬¬��““˜˜™™™™™™––““’’����‘‘””––˜˜™™››šš™™™™šš––��ŠŠ‡‡††††††††††…………††††††††‡‡††††‡‡‡‡‡‡‡‡‡‡‡‡‡‡††††‡‡‡‡‡‡‡‡‡‡€€����‚‚ƒƒ‚‚ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒ„„ƒƒƒƒƒƒ„„ƒƒƒƒƒƒƒƒ„„„„ƒƒ����������„„¡¡°°¬¬ªª››˜˜šš™™››šš——““‘‘��ŽŽ��’’••˜˜™™————˜˜——’’ŒŒˆˆ‡‡‡‡††‡‡‡‡‡‡‡‡††††††……††‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡ˆˆˆˆ‡‡††‡‡‡‡‡‡ˆˆ‡‡€€����‚‚ƒƒ‚‚ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒ„„ƒƒƒƒƒƒ„„ƒƒƒƒƒƒƒƒ„„„„ƒƒ����������„„¡¡°°¬¬ªª››˜˜šš™™››šš——““‘‘��ŽŽ��’’••˜˜™™————˜˜——’’ŒŒˆˆ‡‡‡‡††‡‡‡‡‡‡‡‡††††††……††‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡ˆˆˆˆ‡‡††‡‡‡‡‡‡ˆˆ‡‡€€��‚‚‚‚ƒƒ‚‚‚‚ƒƒƒƒƒƒƒƒƒƒƒƒƒƒ„„„„ƒƒ„„„„„„„„ƒƒƒƒ„„„„„„‚‚��������€€••¬¬««©©¨¨  ››šš››œœ™™––““��ŽŽ����‘‘””••””””””’’��‹‹��ŠŠ‡‡‡‡††‡‡‡‡††‡‡‡‡††‡‡‡‡‡‡‡‡‡‡ˆˆ‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡††‡‡‡‡ˆˆ‡‡€€��‚‚‚‚ƒƒ‚‚‚‚ƒƒƒƒƒƒƒƒƒƒƒƒƒƒ„„„„ƒƒ„„„„„„„„ƒƒƒƒ„„„„„„‚‚��������€€••¬¬««©©¨¨  ››šš››œœ™™––““��ŽŽ����‘‘””••””””””’’��‹‹��ŠŠ‡‡‡‡††‡‡‡‡††‡‡‡‡††‡‡‡‡‡‡‡‡‡‡ˆˆ‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡††‡‡‡‡ˆˆ‡‡€€��‚‚‚‚ƒƒƒƒƒƒƒƒ„„ƒƒƒƒƒƒ„„ƒƒ„„„„ƒƒ„„„„„„„„„„„„„„„„„„ƒƒ‚‚������~~‡‡¢¢¬¬§§££§§ŸŸ™™››››šš˜˜––’’��ŽŽ��ŽŽ����������ŽŽŠŠ’’  œœŽŽ‡‡‡‡‡‡‡‡‡‡‡‡‡‡††‡‡‡‡ˆˆ‡‡ˆˆ‡‡‡‡‡‡‡‡‡‡‡‡‡‡††‡‡‡‡‡‡‡‡ˆˆ‡‡€€��‚‚‚‚ƒƒƒƒƒƒƒƒ„„ƒƒƒƒƒƒ„„ƒƒ„„„„ƒƒ„„„„„„„„„„„„„„„„„„ƒƒ‚‚������~~‡‡¢¢¬¬§§££§§ŸŸ™™››››šš˜˜––’’��ŽŽ��ŽŽ����������ŽŽŠŠ’’  œœŽŽ‡‡‡‡‡‡‡‡‡‡‡‡‡‡††‡‡‡‡ˆˆ‡‡ˆˆ‡‡‡‡‡‡‡‡‡‡‡‡‡‡††‡‡‡‡‡‡‡‡ˆˆ‡‡€€��‚‚‚‚ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒ„„ƒƒ„„„„ƒƒƒƒ„„„„„„„„ƒƒ„„„„„„„„‚‚����€€||~~‘‘¨¨©©££¡¡¡¡šššššš™™˜˜––””’’��ŽŽŽŽŽŽŽŽ��ŽŽ����‹‹——¥¥¨¨££““ˆˆ‡‡††††‡‡‡‡‡‡‡‡‡‡‡‡‡‡ˆˆ‡‡‡‡‡‡‡‡‡‡‡‡††‡‡ˆˆˆˆˆˆˆˆˆˆˆˆ€€��‚‚‚‚ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒ„„ƒƒ„„„„ƒƒƒƒ„„„„„„„„ƒƒ„„„„„„„„‚‚����€€||~~‘‘¨¨©©££¡¡¡¡šššššš™™˜˜––””’’��ŽŽŽŽŽŽŽŽ��ŽŽ����‹‹——¥¥¨¨££““ˆˆ‡‡††††‡‡‡‡‡‡‡‡‡‡‡‡‡‡ˆˆ‡‡‡‡‡‡‡‡‡‡‡‡††‡‡ˆˆˆˆˆˆˆˆˆˆˆˆ€€‚‚ƒƒƒƒƒƒƒƒ‚‚ƒƒƒƒƒƒƒƒƒƒ„„ƒƒ„„„„ƒƒ„„„„„„ƒƒ„„„„„„„„„„„„ƒƒ‚‚€€}}{{{{‘‘££¥¥ŸŸŸŸžž˜˜™™˜˜——––””““‘‘������������ŽŽŽŽ‹‹––¤¤§§ªª§§——‰‰††‡‡††‡‡‡‡††‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡††††††‡‡ˆˆ‡‡‰‰ˆˆ‡‡€€‚‚ƒƒƒƒƒƒƒƒ‚‚ƒƒƒƒƒƒƒƒƒƒ„„ƒƒ„„„„ƒƒ„„„„„„ƒƒ„„„„„„„„„„„„ƒƒ‚‚€€}}{{{{‘‘££¥¥ŸŸŸŸžž˜˜™™˜˜——––””““‘‘������������ŽŽŽŽ‹‹––¤¤§§ªª§§——‰‰††‡‡††‡‡‡‡††‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡††††††‡‡ˆˆ‡‡‰‰ˆˆ‡‡€€��‚‚‚‚ƒƒƒƒ‚‚ƒƒƒƒƒƒƒƒƒƒƒƒƒƒ„„„„„„„„„„„„ƒƒƒƒ„„„„„„„„„„„„‚‚}}{{{{{{||}}ŠŠššŸŸŸŸŸŸœœ——————––““““’’‘‘������������ŒŒ‹‹••¢¢¤¤ªª­­©©˜˜ˆˆ††††††‡‡††††††‡‡‡‡‡‡ˆˆˆˆ‡‡ˆˆ‡‡‡‡ˆˆ‡‡ˆˆˆˆ‰‰ˆˆ††€€��‚‚‚‚ƒƒƒƒ‚‚ƒƒƒƒƒƒƒƒƒƒƒƒƒƒ„„„„„„„„„„„„ƒƒƒƒ„„„„„„„„„„„„‚‚}}{{{{{{||}}ŠŠššŸŸŸŸŸŸœœ——————––““““’’‘‘������������ŒŒ‹‹••¢¢¤¤ªª­­©©˜˜ˆˆ††††††‡‡††††††‡‡‡‡‡‡ˆˆˆˆ‡‡ˆˆ‡‡‡‡ˆˆ‡‡ˆˆˆˆ‰‰ˆˆ††€€��ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒ„„ƒƒ„„ƒƒ„„„„„„ƒƒ„„„„„„„„„„„„„„„„€€{{{{{{{{{{{{||„„‘‘œœ��ŸŸœœ––••••••””““’’’’‘‘����ŽŽ����ŒŒ““  ¥¥ªª¬¬­­¦¦ŽŽƒƒ††‡‡‡‡††††‡‡††††‡‡ˆˆˆˆ‡‡ˆˆˆˆ‡‡ˆˆˆˆˆˆˆˆˆˆ‡‡‡‡€€��ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒ„„ƒƒ„„ƒƒ„„„„„„ƒƒ„„„„„„„„„„„„„„„„€€{{{{{{{{{{{{||„„‘‘œœ��ŸŸœœ––••••••””““’’’’‘‘����ŽŽ����ŒŒ““  ¥¥ªª¬¬­­¦¦ŽŽƒƒ††‡‡‡‡††††‡‡††††‡‡ˆˆˆˆ‡‡ˆˆˆˆ‡‡ˆˆˆˆˆˆˆˆˆˆ‡‡‡‡‚‚ƒƒƒƒƒƒƒƒƒƒƒƒ„„„„„„ƒƒ„„ƒƒ„„„„„„„„„„„„„„„„……„„ƒƒƒƒ‚‚€€}}zzzz{{{{zz{{{{||€€‹‹——��ŸŸšš••••••••””““’’‘‘������ŽŽŽŽŒŒ““ŸŸ§§««««¯¯¯¯––‚‚ƒƒ„„……††‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡ˆˆˆˆˆˆˆˆˆˆ‰‰ŠŠˆˆƒƒ……‚‚ƒƒƒƒƒƒƒƒƒƒƒƒ„„„„„„ƒƒ„„ƒƒ„„„„„„„„„„„„„„„„……„„ƒƒƒƒ‚‚€€}}zzzz{{{{zz{{{{||€€‹‹——��ŸŸšš••••••••””““’’‘‘������ŽŽŽŽŒŒ““ŸŸ§§««««¯¯¯¯––‚‚ƒƒ„„……††‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡ˆˆˆˆˆˆˆˆˆˆ‰‰ŠŠˆˆƒƒ……€€‚‚ƒƒƒƒƒƒ„„„„„„„„……ƒƒƒƒ„„„„„„……„„„„„„„„ƒƒ‚‚��€€~~~~}}}}||{{zz{{{{{{{{{{{{{{||ƒƒ��˜˜šš””””””••••““’’‘‘����ŽŽŽŽ����‘‘��¦¦ªª®®»»··••…………„„ƒƒ……††‡‡‡‡‡‡‡‡ˆˆˆˆ‰‰‰‰‡‡……‡‡��——����||€€€€‚‚ƒƒƒƒƒƒ„„„„„„„„……ƒƒƒƒ„„„„„„……„„„„„„„„ƒƒ‚‚��€€~~~~}}}}||{{zz{{{{{{{{{{{{{{||ƒƒ��˜˜šš””””””••••““’’‘‘����ŽŽŽŽ����‘‘��¦¦ªª®®»»··••…………„„ƒƒ……††‡‡‡‡‡‡‡‡ˆˆˆˆ‰‰‰‰‡‡……‡‡��——����||€€€€‚‚ƒƒƒƒƒƒ„„ƒƒ„„„„„„„„„„„„„„„„„„……„„ƒƒ‚‚}}||||||{{||||||{{{{{{zz{{{{zzzz{{zz{{||��‡‡ˆˆŒŒ��““””““’’��������ŽŽŽŽ��’’žž¥¥¬¬ººÂ¯¯ŒŒ……††††„„ƒƒƒƒ‚‚……††‡‡……ŽŽ™™‘‘††��’’  œœŽŽŒŒ‘‘‘‘€€‚‚ƒƒƒƒƒƒ„„ƒƒ„„„„„„„„„„„„„„„„„„……„„ƒƒ‚‚}}||||||{{||||||{{{{{{zz{{{{zzzz{{zz{{||��‡‡ˆˆŒŒ��““””““’’��������ŽŽŽŽ��’’žž¥¥¬¬ººÂ¯¯ŒŒ……††††„„ƒƒƒƒ‚‚……††‡‡……ŽŽ™™‘‘††��’’  œœŽŽŒŒ‘‘‘‘��‚‚ƒƒƒƒƒƒ„„„„„„„„„„„„„„„„„„„„ƒƒ‚‚€€~~}}||}}||{{||||{{{{||||{{zzzz{{||{{{{{{||{{||}}}}}}~~ƒƒŠŠ��‘‘��������������ŒŒ��››¡¡¬¬³³³³ŸŸ……††……††…………„„€€€€……„„��žž““��““››��œœœœœœ��šš��‚‚ƒƒƒƒƒƒ„„„„„„„„„„„„„„„„„„„„ƒƒ‚‚€€~~}}||}}||{{||||{{{{||||{{zzzz{{||{{{{{{||{{||}}}}}}~~ƒƒŠŠ��‘‘��������������ŒŒ��››¡¡¬¬³³³³ŸŸ……††……††…………„„€€€€……„„��žž““��““››��œœœœœœ��šš+×þùûþþýûüþþþþþýûþþþþþùîÞßááßÞááàßáåãÛ·˜žªÄØÚ×ÖÚÜÜÙØÙÚÜÙÜÜÜßÜÛÝÝÞÙÕÔ×Ûß×ɪ‘’“‘Œ�†vu‚ªÙàÞÞßÝÝÖÐÙÙÑÐÒÔÔÚËŠs‡„y€µÑÏÑÓÔÙ×ÔÖÕÑÎÒÑÉÀÀʳ‰‡¸ÛÒÔÕ×ÛØÒÏÎÏÌÊŸq†»ÌÏÒÏÎÑÊÆÒ»}~¹ÎÌËÁ¹ºÈµ|p¯ÅËÃ1ÆÁÓäÍÕ¹ÓϺøÆËзØÄÆîкÕíáàáááàßßâææÙº˜‘§¼ÍÓÓÔÒÕÙÚØ×ÙØÚÜÚÛÜÝÞÞÝÞßÜ×ÔÝßÕÁ¦˜““—š�vvy‡£ÊÝÞÝÝÛÚÙÜÇ¿ÙÓÎÑÐÑÑÌÓÅŒxˆ†v‚¸ÐÐÔÒÖÖÒÔÔÑÎÐÐÊÄÈͼ¡…‡½ÙÓÔÖÙ×ÓÐÎÎÇɸˆqйÍÑÑÏÏÍÉÍЩv�ÄÐËÿ½ÇÄœp~¶Ï¼0±«Ä­¹ˆ¿Ãгް²‡Ê°Š»Ë¦´çãàâàÞßâäâÔº�’ž³ÍÙÛ×ÔÔÕ×ÚÑÎÙÙÔÚÜÛÜÛÜÜÝØ×ÙÚÝÛÓ» ’��—˜Š{sx‘³ÑßâàÜÛÛÛÛÛÛÖÕÚÒÑÑÐÐÑÌÑÒºˆwŠ�k~´ÐÑÕÖ×ÓÒÓÒÏÏÌÌÎÍ、{‡ÂÚÑÔØØØÒÎÎÌÍËÀ’q’ÃÐÏÎÍÎÌÊÏÏœq–ÌÑÈÆÂÊɱŠe‡Ç»-Öݦµ¾ÄžÌѰ¥­Ä¾œÑ˯¢åÏ¡ÐåßÞÝâåÝѸ�š¦½ÕÚÙ×ÈÈÖÖØÛÙÍÐÛØÖÚÚÛÛÙÜÖÓÕØÝÜͱ–ŠŠ—�“‡{w�™¿×áãÞÛÚÜÜÜÝÝÜÚÛÚ×ÓÒÒÏÐÑÑз²º…u‡‚q€¯ÉÎÓÖÕÕÕÑÒÓÌËÐÍÏÆ¿À�w‰ÆØÓרØÖÏÌÐÐÉÏÊ›z�ÄÅÊÌÍÆËÎÒÊ’p¤ÒÍÄÅÍ̹®�i”¹(¶Ú¼ÙçÑÎÝáããâàÒÝâæÚåÛÕàÞÝáåàͳ š©ÄÖÔÔÓÕÕÄÊÕÖØÚØØØØ×ÚØÕÚÛÚ×ÓØàÛɬ‘…‹—™‘�ut…§ÇÚââßÛßÞÝÞÝÝÜÚØÛÛÛÞÝßàßßÞÛØÉ¿ÐÀ†s…†u~®ÌÏÓÓÕ×ÔÒÔÎËÉËÓȾļ–xŽÊÖÑÖÕ×ÔÐÐÏÉÉÐÖxŸ¼ÇÍÎËËÐÏÔɉp«ÐÊÉÏÑÆ¾ºˆo”‡Å±½ÛåÜÑáæäâãããßàáæàÞåãáæá̬™˜•·ÖÛÖÒÒÓÖØ××ÕÙ×Ô×ÚÚ×ÖØØÙÙ×ÛÛÛצ‘„…“œ’rv�²ÏÙÙÜÚ×ÍØØÙÝÝÛÝÞÞàäçéëëììíììëëçåÞßÌ�m€‹~„²ÐÑÒÕÔÕÔÎÏÎÇÏÕÇÁÁɘt�ÆÕÒÔÖÕÖÒÑÏÍÊÈ¿ˆu ÃËÍÎÊËÐÎ×Àys³ÐÌÏÐǼ¾¯}j}­ÄØÏÖÜáàÝÜÛÙÙÛÝÛÛ×ÖÞââÝË®›š¤­½ÖÚÙר×ÙÙÙÚÖÖØÙ××ÙÛÚØÙÖ×ÛÝÝ϶žŽ‹Ž—�~{u~š¾×áß×ÔÙÝÒÅÔÍØÞÝàäçéëìììììëëëëëëìíìëèíÙšx€Š�…²ÑÒÖÒÒÕÍÐÐÏÔÕÌÅÇÊÊ»�s�É×ÓÖÒÔÔÑÐÍÏÈȹ�x²ÎÎÌÊÌÍËÎÖ°o{ºÍÎÏǽº·¡y!u®ÝÞÛáâáááàÝÜÝÞÞÝÝàãäÕ¾­››ª½ËÏÙØÔÛÝÛÙÙØÙÙרÛÝÙÖ×רÖ×ÕÙÛÊ­’‡‰’—Œ}po†£ÅÙÝÝÙ×ÚÛÛÜÛÜÜÛßãçêëììëëëëëêêëëëëêëëëëìëðèÀ—†…€‚®ÐÔÒÔØÓÆÉÒÎÎÌÈÌÆ¼¼±Šr”ÎÖÓÔÓÔÑÏÏÎÍËκ{„ÃÒÌÌÎÌÌÍÐÒ¦k„ÂÐÏʾ¾¸º¢}ÌÞÞââààááÜÙÜÝÛÜßãáÒ¸��š°ÅËÌÑÖÖÙÛÝÜÚÙÕÕÒÓ×ÙÚ×ÕÖÕÕÖÜÛÒÁ¨‘ƒˆŽ—ˆunrްÅÖá×ÏØÛÜÛ×ÚÜÞßáçèéììëëëëëëëëëëëìëëììììëëëéîóã¼”…~©ÎÑÓÙÖÈÊÓÎËÊÉÍ̺¾²ŠtœÒ×ÓÔÖÒÏÎËÏÍÊЬu’ÇÍÌÎÍÍÍÍÑÒši–ËÒËÃÇ¿¾°‰ÛàßßáâáßÜÚÞÜÛÞáÞʰ›”Ÿ¬½ÑÓÎÍÍÔØÛÜÛÙÖÔרÓÔØÙÔÓÔÔØÛÙÒºŸŽ‰Œ”™ˆxmw“³ÍÞÒÒ××ÛÝÜÜÜÛÝßáåéëìëìëëëëìëëëììëëëëëëìëëëìíìèéðñتˆ|{©ÐÑ×ÔÐÓÔÓÐËÌÍÍËÅÁ°‡qœÑÔÑÕÖÓÑÐÏËÊÉ̦w—ÄÊÍÎÍÌÍÌÓÍ‘oŸÍÑËȾ¾­‡ØáßààßÞÜÙÚÒÑáÝʬ”‘š¯ÇÐÓÔÌÊÍÑ×ÚÛÜØ××ÖÓÓ×ÖÚÙÕÐÙÜÙ˲šŽŠ�—‘†wn|ž¿ÐÔÔÙרÜÞÝßÝÛÜÞâçêììëëììëëëìëëëëìëëëëëëëìëëëìììëêçëò縋|~ªÑÔÑÊÑÕÓÓÎÌÌÉÊÈÀ¼À©€o ÔÔÒÕ××ÓÎÌÌÌÊÉœt™ÄÌÏÊÊÎÎÏ×ȇk§ÒÎÌÂÁ®‡ÙáÞÝßßÞÛʨ�¸Ê°œ�™§½ÐÐÏÖÔÉÌÍÑÔÚÝÜÜÙØÕÁÉÖÐÒÖÙÙÚɯš��“•‹~rq„¤ÅÚÞÙÔÖÛÝÜÔ×ßßÜÜáæêìëëêëëëêëìëëëëëëìëëëëëëìììììììëëêêéèîí¿‘|x¤ËÏÉÐÔÒÑÎÍÌÌÊÉÅÀ¿·¨†t©ÖÕÑÓÖÒÎÍÍÎËÊ‘sŸÄÍÇÈÎÍÒÒÖ¾{tµÒÏËŲ‡ÖßßäåâÞÅ«”u‘›”š«»ÀÈÒÑÒÙÑÍÏÎÒ×ÚÛÚÚÙÚÔÍÑÐÒ×ÚÑ앎”˜“„tmw�±ÎßßÚ×ÙÛÛÛÛÙÏÖÞÜÞäéëëìëëëëëëëëìëëëìëìììëëëëëëììììëëëëëêëêèìí¿ŒzvžÇÐÒÔÔÑÏÎÍÍÊÉÉÅ´¯·¬‡v¨ÕÒÎÒÒÏÏÍÍÏÌȾ�v¡Ç¿¿ÌÍÑÒÑÖ¹v{¾ÑÊÈ·†ØäáØ¼®«œ‹•�”¯¿ÌÔÏÅÐÑÔØÏÐÎÒÚÜÚÛÛØ××Õ×ÔÔÙÔ¼¡’�•�–„qo}›¾ÔßàÛÖÔÙÜÛÚÜÝØÚÝÝáçêëëëììëëëëëìëëìëëëëëììììëëëëëìëëëëëëëëëêêèìꮇ€wžÏ×ÔÖÖÑÏÐÎËÈÆÇ¸°´½³Šv¬ØÎÐÔÑÏÎÏÍÌÊÊÀ‰t©Á¾ÉÎÏÒÒÒØ°o‡ÆÌȸˆÞÒ®—‹ƒ€Œ��Œ ¼ÎÑÒÔÒÔÖÈÓÕÍÎÍÖÛÚÚÛÛÙÕÒÖÓÐɳ��‘–—–…ss‡¦ÆÛßÛ××ÙÚØÛÕÒÜÝÝÝÞÞäêìëëëëëëëëëëëìëëìììììììíìëìëìììììëëëëëëêêëêêéïÚ˜Š�t�ÐÕÓÖÓÐÑÏÌÊÃÃȾ»½»´†w±ÖÒÔÓÑÓÓÏÌÎÉȼƒz¢³ÅÌÍÑÓÒÕÖ¤p—ɼ­�²ž”š¥…jp¦¾ÂÓØÕÏÐÑÖØÑÑÖÏÌÉÐÙÙÚÛÚÕÖ××ÔÀ§‘ˆ�˜˜�{sz”·ÐÝÝÚØ×ÚÞÜÚÙÚÔÓÞÞÜÝàæëëëëëêëëëëëëëëìëëìììììììììëììëììëìëëëëëëêêêêêêèî¿…Š{mšÍÒÑÓÑÑÐÌÌÆÂÅÁÁ¿¸±¤†x²ÖÌÁÎÖÔÒÐÎÍÆËº}m’ÀËÍÐÑÒÑÕÒ›s¢½«s™¨ÆÔÒ§qƒº×ÚØÖÕÔÖÖ×ÒÓÚÒÌÏÌÕØØÚÚÖØÛÒ¼¡�ˆ‹•™‡qp€”µÙáÝÚרÝÜÛÔÕÚ××ÜÞ××Ûßçëëëëëëëëëìììëëëììëììììììëëëëììëìììììëëëëêêéêêéééìä˜xˆzošÌÓÎÐÑÑÌÊÊÈÄÂÁÿ°®¯}x·ÑÀÍÓÒÔÕÐÍÊÆÎµwv²ÈËÎÏÐÑÒ×Αuªµ~ÆÓÚÞÛͼÊÓÎÐÓ×ÔÓר×Ô×ÖÎÎÍÒÖÕÕÚÜÙÑ»ŸŒ‡Ž•�€qj|©ÊØÜÞÙÛÑÏÜØÚÚÍÒÛÛÛÛÛÓÕáçêêëëìëììëììëëëëëììììììëììëììììììììììëêêêêêêéêéééêéîÊ~yŠ}p–ÆÊÉÑÓÎËÉÈÄÁÃÅŹ»¢z|¼ÒËÑÓÔÖÔÏÍÈÉÓ­q‡½ÊÌÏÏÐÒÒØÉ‡{¡‡×ÛÒ×ÑÌÙÖÓÖÕÔÕÐÐÕ×ÕÕØÏËÍÎÒÔ×ÛÚÍ·žŒ‰’–Šrer‹©ÌÞßÝÙÚÝßÓÐÚרÙÙÚÜÝÜÛÛÝáèêêëëëëëìëëëëëëëëëëëëììììììììììììììììëëêêêééééééééêêêë¸qs‡~o’½ÆÎÓÐÍÉÅÇÁ¿ÄÉÊü³¦|½ÒÐÔÓÓÓÒÏÊÉÌÌ q“ÃÌÍÏÎÐÓÒØ¿€v…ÒÛÐÎÏÊÍÐ×ÕÓ××ÐÐ××ÐÖÕÊËËÏÕÚØÈ±šˆ†ŒŽ‚mdr’»ÕÞÝ×רÚÛÜÜÛÛÚÙÙÛÜÛÝÜÛÜÜáçéêêêëêëëëëëëëëìëëëëëëëìììëììëìììììììëëëêêêêééèéèééééééÞ¨on„}q‘ÁÍÌÐÐÍËÊÄ¿ÂÄÊȺ§¶®z…ÁÖÒÓÔÓÓÑÊÊËÌË¢sžÈÉÎÐÏÒÓÒÕ³z†ÖÛÐÍÊÁÌÓØÖÔÒÔÑÒÙÕÏÔÑËÍÐÕÓÀ¦“ŠˆŠˆ|j^ršÃÜà×ÑÓÚÝÜÚÙÚÚÙÙÚÙÚÜÛÝÔÔÝÛßæèéêêêêêëëëëëëëëëëêìëëëëìíìëììììììëëëìëëëêêêêéééééèèèèéçâÙ³vl‚~q�ÄÎËÏÏÏËÆÁ¿¾Ç˽¨¶Æ«w†ÃÕÑÒÓÔÓÌÌÐÌÌÌ™w¦ÈËÎÏÐÒÑÐÏ©‡ÖÕÐÑËÈÑÕÔÕÕÑÒÑÕ×ÓÔÓÐÐÐ̼¢�‰ŒŒ†wkiyœÉßßÙÖÕÔÛÕÒÛÛÛÛÚÙÚÚÛÚÙÜÝÔÔÜÝäèèéêêêëëêëëëëëëëìëëëëëëëìíìììììëëëëìëëëëëëêêéêééèéèèèèèèåØ×ºwh~~o�ÃÎÌÌÍÊÈÃÀÀÄÉÆ±¸ÈÆŸqˆÅÓÏÓÓÒÏÍÎÌÉËË’z±ÍÍÐÐÐÒÐμ‰ÔÓÑÐÍÏÕÙÔÓ×ÖÒÒÖÔÓÖÕÓDzœŽ‹‘“†uik�¤ÇÞßÙÕÖÙÚÛÜÕÔÛÜÙ×ÛÚÚÚÚÚÚÛÛÜÛÝäççèéêêêëëêêëêëëëëëëëëëëëëììëììëìììììëëëëëëëêêêéééééèèèèèèçÜÏÕ¹|h{|n‰¿ÐÊËÊÉÅÀÂÃÃɼÀÏǸ™rŽÉÕÒÕÔÓÏÏÏÍËÏÈ’¸ÑÍÏÑÒÒ˳ˆÒÕÑÍÌÎÖÖÓÕ×ÓÑÒÕÖÛׯ¬’ŠŠ��‚tkmƒ­ÎÜÝØÕÖÖÙÚÚÚÙÜÜÕÕÙ×ÚÛÚÛÚÛÜÛÛÛÜâæçèèéêêêêëêêêëëëêêêëëëëììëëëëëìììììììëëììëêêêêêéééééèèèèèèçãÕÌÔÂj~}o†¾ÒÉÇÈÇÆÄÁÃÊÄÈÓʹ¶šr’ÍÔÑÒÓÕÔÒÍÎÊÐÈ�„½ÏËÐÐÑ͵„ÎÑÍÌÍÓØÑÑÖÒÐÓØÚÔÄ©’††’•†ofr�¸ÈÕÞÖÓØÛÚ×ÚÛÛÚÚÚÚÖÖרÚÛÛÛÚÜÛÚÛÛàåæççèééêêêêêêëëëêêêëëëììììëëëìëìììììëìëëëëëëêêêëêééééééèèèèçäÛÎËÓ¿vf€~nƒ¿ÏÇÈÇÈÇÅÆÆÁÊÑÐĬ³�m•ËÐÍÒÖ×ÔÎÏÏÊÏËŒ‡¿ÌÍÎÎй„ÎÐÍÌÏ×ÕÑÒÒÏØÜÓ¸žŠ‚‡““‚ts€£ÀÑà×Ò××ÙÛÜÛÚÚÚÚÙÙÙÚÛÛרÚÚÚØØÜÜÛÛÝãæçèçèééééêêêêêêëêêëìëëëëëëëëëëëëììììììëëëëëëëëêêêééééééèèèèèåàÓÌÉͬzn{xg¸ÌÅÉÉÈÊÉ¿¸ÇÐÎÈ´±¬‘r˜ÏÒÐÔÖÕÒÓÔÏÊÔÆ‚ŠÁÎÍÍÏ·†ÑÒÎÍÐÔÐÑ×ÙØÏ·™…‡’‰{nn…«ÎßàÜÖ×ÙØÚÚÛÚÚÚÚÙØØÙÙÚÚÚÚÙÛÚÚØ×ÛÜÛÛÞâåæççèèééééêêêêêêêëëëêëëëëëëëëëìëììëëëëëëëëëêëëêéêêêééèèéèèèèæâØÍËÉÒÃixyl{®ÉÍÍÆËÎǺÂÒÐÆ¼²®¸’p ÍÑÒÒÒÔÕÕÒÐÎÔ³z�ÅÏÎиˆÏËÊÎÐÑÕÚÙÍ´•��ŠŒ~qlr�³ÒàÞÖÕ×ÚÛÙÙÚÙÛÖ×ÙÙØØÚÚÚÚÙÙÙÙÚÙÙÚÙ×ÖÙÜàâåææççèéééééêêêêêêêëêëëëëëëëëìëëëëìëëëëëëìëêêêêêêéêêéééèéèèèèæâÜÏÉËÌÕÅ�u}zix°ÈËÊÌÎÎÄÆÓÑÊÀº²¹´ŒsŸÎÓÐÒÔÔÖÓÔÓÍͯ}–ÆÎз…ÄÃÇÐØÚÕÀ¦‘�ƒ‹ˆxhf{�¿×àÞÙÕ×ÚÚÚÜÛÜÛÚÚØÙÙÙÙØÙÙÙØØÙÚÚÙØØ×ÖÖØÙßääææåçççèéééêêêêêêêëëëëëëëëìëëëëëëëììëëìëëëëëëêêêêééêééééèèèèçæãàÕÊÌÌÊÖÑ–mvwly¨ÈÌÌËÍÈËÓÏÊÅ¿·²·µ�x¨ÓÓÓÓÓÖÕÔÔÐÊЬz�ÊÒ¶x­¯ÌÜÖº—…‚†ˆ„ygg‚ªÏÞáÜ×ÖØÚÚÚÚÛÜÜÜÛÚÙÜÚÙÙÙÙØ×ÕרÙÚÚÙØÙÝßßÕÖßãäåååæççèééééêêêêêëëëëëëëëëììëëëëëëëëëëìììëëëëêêêéêêêééééééèèçæäáÚÎËÌÊÊÓÈ‹ovzt}¨ÅÊÍÐÎÍÔÑËü¸³®¸¶�{¯ÕÒÓÖÖ×ÖÖÖÏÊʤ~¥Ð¶s•–Áºœ‡€‰Ž…tjx’µÔßÞÚÕÔÙÛÙÙÛÚÙÚÛÛÚÚÚÙÙÖըרÚÙרØ×רÛßßÚȱ¶ÓÛáâäååææèèééééêêéêêêêëëëëìììëëìëëëììëëëëììììëëëëëëêêêééééééééèèçåâÝÑÊËÉËÌÔÊ‘oz‚v�«ÌÐÐÍÊÒÓÎŽ»½¶¶·°Š{²ÔÔÔÖרØÖÔÌÉË�z­º}²Ÿ‘Œˆ�Œ~to}¨ÊÜâÜÖÕ×ÙÙÚÚÙÚÜÚÚÚÜÝÚØÚÙ×ÖרÖ×ÚÙØÖÕÖÛÞÜϺ¡Š†¹×ÚââääåææççèèééêêêéêêëêëìëìììëëëìëìíìëììëëëëëëëëëëëêêêééééééèéèèèæãßÕÌËÊËÌÎÖÊ’o}‚y€¬ÍÐÊËÑÓÏËú¿¿¾µ³®‹�µÓÑÓÖÖ×ÕÓÏËÌÈ•�¥yÊ®�Œwgo�°ËãâÜÕÏØÙØÚÚÙØÚÜÜØÛÝÞÞÛÙÚØ×ØÙרØ×ÖÖÚÜÛÓÁª–Œ‰�©ÖÚÞâããäåææçççèèéêêêêëêëëëëëëëëììëëìëëììììëëëëëëëëëëêëêëêêêéééèèèèèæäàÙÎÌÉÊÇÇËÏÊ“kv{v~­ÓÎÌÒÓÏÊÆ¿¼½Á¼±¸¿‘ƒ¶ÖÔÒÕØÖÓÏÎËÎÆ�{qº²‚mlw“·ÕâßÙר×ÕÙÙÙÙÙØØÙÕ×ÙÛÜÜÙØÚÙÙ×ÖÖÖÕÔÖÜÞÚÆ®šŽŠ“�¢ŸÁßÝàáããäåååçèèèééééêêêêêëìëëëëìììëìììëëììëëëëëëëìëëëëëêëêéêéééééèèèçäáÜÒÍÊÉÇÇÌÉÎÈŽhy‚v~¯ÐÍÐÕÑÊÉÆÂÂÄÀ³ºÍ¸ƒ‰ºÓÒÔØÖÓÏÎÌÊÎÀŒw£�w~�ÃÚÜÙ×ÖÙÜÚÚÚØÙÚÚÙ×ÙÙÔÖÙ×ÙÙ×ÖØÚÙ×ÔÓÖÙÛÚп¦ˆ„Œ™Ÿœ•‰�ËâßßáâããääåæçèèèéééêêêêêêëëëëììììëëëëìëììëìëëëëëëëëëëêêêêéêêééééèèèçäâÞÕÍËÊÉÊÈÌÍÓОru{nu¬ÐÏÑÓÍÊÉÄÀÀÁ¸¼È¿¡�„ºÔÑÕÖÔÐÐÏÍËдg‹Œ¥ÎÜÝÚÕÖÚÜÜÛÚÛÙØØÙÚØÕÙÚÚÙØÖØÙØØ××ÕÕÙÜÛÓï˜�‹‹–›–‰~vžÛßßàáâãâãååææçèèèéééééêêêëëììëììëëëëëìëììëìëëëëëëëëëêêêêééêéééééèèèçåãàÙÎÊËËÅÀËÆÈÖÓ pwuiz«ÎÑÑÒÏËÆÀ½¾»ÀÊÀ¬š~‹½ÔÑÓÓÑÒÐÍËË·nµÓÛÜØÖ×ÚÙÙÛÛÚÚÚÖØÙÙÙ××ÙÚÙÙÙÚÙÙØ×ÕÖÛÝ×ȯš�Œ“šš“ˆ€zwxx~½àÝßàááâããäååææçèèééééêêêëëëëëëëëììëëëììëëëëìëëêêëêëëêêêéééêéééééééèçæäàÛÏÊËÃÇÊË´±ÍÏÔ¢qswqt£ÏÐÊÈÍÌÅÿ¼ÂÌȯ –zŠÇÖÐÑÐÏÑÎÌ̶‡×ߨÓÖØÚÚÛÚÛÜÛÛØ×ÙÙÙÙרØÙÚÙÙÙ×Õ×ÜÞØË¹ �Š’��•Šwuurtyƒ¡ÓÞÞßàááâããäååææçèèèèééêêêêëëëëëëëìëëëëëêêêêëëëëêêêëëëëêéééééèéêéééééèæäâÝÔÎÌÅÉÍËľËÊÒÓ¥xsxpp¢Í¿®ÉÐÇÄÅÇÈÌι£¢›s‰ÈÔÐÑÏÐÐÎζ†Ñ×ÖÖרÙÛÝÝÜÚØÙÙÚÙØÚÙ×Ø×ØÚØÔÖÙÜÚÓ¾£‘‰Œ—�Ÿ—„wttrqt€”«ÈØÛÞÞßàáááãääääåææççèèéêéêëëëëëìëëêëêëëëëëìììììííìëëëêëëëëìëëêéééèéèèèèæåâàÔÉÍÍËÊËÏÎÊËÌÒÖ²{s}mr Â¿ÈÏÈÃÈÍÍÍÏÁ¯¦®˜o‰ÆÓÎÑÒÒÏй…ÓÙÕØØØÚÜÛÛÜØÐÖÚÙÕÖ×ÖרØÖÕÔÙÞÜÎ²š†…“œŸ—Š�xvuuvyŠ¥ÃÖßßÙÚÞÞßàáââãääääææçççèéêêêêëëëëëëëëëëìííííìêéèåäáßÝÜÜÛÛÛÛÝßáåçéëìëêëêéééçåãâÖÊÈÈÍËËËËÊÊÍËÍ×¶yuzpršÂÊÎÌÆÆÉÏÏÎÆ´¨ª­–p‰ÉÕÐÓÕÑѼ…ÔÚØÙÙÙÚÛØÚÛÚØØÙ×ÖÖÕÕÖÕÕØÛÛÓ¿§•Œ�™¡�‘†}yqsxŠ£ºÏÜßÜÚÐÒÝÞÞßááâãããäååæççèèèêéêêêêêêëëëííìëéæâÞÚÕÐÌÇÃÀ½¼»º»¼¼½¿ÁÃÄÆÊÎÓÙÝáæéêëêéèäáÛÐÇÆÌÎËÉÌËÉÎÍÊÎ×´|txrr�¾ÌÉÇÄÁÌÑÏȽ·´¨¤˜o�ÉÐÐÓÒÒ½…ÔÛØ×ØÚÚÚØÙÙÚÚÚÙ×ÙÖÒÔÙÚÛÙË·Ÿˆ‡–  ˜Œ�}|zwx‚—³ÅÙãÙÖ×ÖØ×ÙÞßÞàááâãäääåææççèéééééééêëìíìêæáÚÔÏÉÄÀ½º·´²°®­¬«¬®®¯°±±±±²²²²µ·¼ÃÊÒÙßäæçæáØÐÍËËÊÉÉÆÄÌÍÍÍÎ׸~oyww›ÃžÄÅÊÒÐÈÂǽ¯ª±›nˆÄÑÐÓÔ»†ÔÚØØ×ØÙÙÚÙÙØ×ØØÖÕÓÕÛÜÑ¿¬›”—š¡ž‘…}{yx|ˆ“ªÇÚàÒÔÖÎÔÚÙØØÛÞßÞàâââãäåååæçççèèèèêêìíìêæàÙÒËÆÁ¿½¹¹¹·´²¯«§£Ÿœ™˜˜—˜š™––𡦍©©¥¡¢£§¯¸ÀÈÐÕÙÛÛÚØÔÒÐËÿÅÊÍÐÍÎ׺�r{yw™¿¹ÃËÉÍÐÈÁÀ»·³²· v•ÌÔÐÔ»†ÔÚרÙÚÙÙÙØÙ×ÕÕÖØÚ×ÒdzŸ’”ž¤¡—‹�zxxwzЧÇÖÝßÛÖÔÕÔØÚÚÚÙØÜßßßàáâãäååæåææçèèêêëíìéåÝÕÎÈÂÀ½»¹¶²¬¦¡œ—‘Œ‡�|ywvtsrqrrstw|‚Š“˜™™›žžŸ ¡¥§®´»ÂÉÎÓ×ÙÙÔÍÊÊËÌÍÎÏ×½zixsn”­½ÉÊÌÎÌÁ¼À¿¶º¾Æ¦yœÏÓÓ½…Ó×ÕÖÚÜÛÚØ×ÖÕÖÛÞÛγœ�“˜Ÿ¥ž�ƒ~|{ws|—´ÍÛÞÚØØ××ÙÙÙÚÛÚÚÚØÝàßßàáâãääåæåæçééêëéæáÜÕÍÇÁ»·³®¨¡›–‘Œ†�|vpljigffddddbadedfit{}~}�Š“œ¢£¥¤££§ª®´¹½ÂÉÏÒÕÖÕÒÐÏÊÌÖ¶|ozoj‡·ÉÍÑÎÎÅÀÁÀ½ÄÂÄ¿—rœÎÔ¼†Ñ×רØÙÙÖÕ×ÚÛÚÒ®—Љ‘ž¡™�€{yxvz}“¾ØàÞÚÖÒ×ÛÚÚÙÙÙÚÛØÚÙÙÞààààâããååææèééèäáÜØÓÎÆ¾´ª¡›˜’Œˆ…‚�}{yvsld_]\\\[Z[\^]YZ\[\bospjcdjs~Š”œ¥«¬¬®±³´³°°³·¿ÇÎÓÖÕÓÐÍÔ¾�kwpl—ÈÎÏÒÏÆ¿Ã½¾ÄÁÄŶŒnœÒ½†ÒØÙÙÕÕÖØÚÜÕŰŸ•�‹”š˜�ˆƒzrsy†—®·ÊáÜØÖÓÙÚÚÚÙÚÚØÙÛØØ×ØÚßáááâäææçççåãßÚ×ÔÒËû°£—އƒƒ‚��ƒƒ€}xtpmjgbYOLJNTSMLLJLMMOMLXeihaVV_hlqy€‡’£®³¶¶¶¶³°­«­°´ºÃÊÒ×Ôǽ´wh|nl›ÊÍÏÎÇÃÆ¿¼ÈÅÉÊÁ´�t©½…Ò×ÒÕÚÜÜÕÆµ¥˜—š�˜‘�‰„|wvv|›¶ÆÚÜÚ×ÖÖØ×ÑØÜÚØÛÜÚÚÔÊÒÚÕÖÛßáããääãâàÞÚÕÑÏÏÎȼ® ’‡ƒ‚‚„……ƒƒ�~zuqmiedbcdaZXXZ]YSSSNMNNNKIPVVXXRRV\djpwzwƒž¯±²±±²°¯®®­­®²¶ºÁÐ͹ͼvduqm›ÉÐÌÆÇÊÄÀÇÇÌÍÅÁº”z˜…ÔÚÕÙÙÌ·¡•’—šž—Œ…�~wsy‹�£ÅÙáßÛØÕÒ×ÚØÙÚÚÛÙÙÚÙÛÛÔÌÖÜØ×ÝáââßÜÚØÕÐÌÌÎÑÏŲŸ‘‰†‚‚…‡‡†ƒ€}zuqnkhfffhkmoqrqqrpliikjjfb_]ZYXRPOJGJNVaipy{|Š ­¯±³´³²²°¯°°°²´´ºÊÍË׺td{vo”ÄÏÄÃÉÆÄÃÀÅÌÈÁº¸�p†ÐÐÅ»ª›•”–š—Ž†|wvw}ˆ—´ÏÔÙâÚÕÖÔ×××ÚÚÚÛÜÛÚÚÜÕÒÛÙÚÛÚÛØÙßàÛ×ÔÑÏÍÌÎÑÓ̽¥Ž~~…ˆˆ‡††„�~zuqmkihkllmoqtttuutvxxvspmmoonmkgc`\XSH>?HOU^eny~„�¥¯°³µµ¶µµ¶¶´³²¶·¶ÃÍÎÍÖ¿vc~ul‘ÄÉÁÅÁÃÊ¿½ÉÌô·®€�³š‘•˜š›–Œ„zwux…˜¯ÉÛâáßÙÔÔ×ÖØÚÙÙÚÚÚÚÚÚÚÚÚÙØÚÛÛÙÙÙØÚÛÕÒÐÎÍËÏÔÔǰ—„xwz„ŒŒˆ„‚€|xtpmkjjjlqsrrrrsttustvvuuusqnmnpppokhgd`[TPRXVW^epx€ˆ•¦¬¯´···¹ºº¹····¾ÇÍÐÐÎ׿wfzuj’Äļ»ÀÎÊÆÌÌÈ»¹²—n˜ŠŽ™˜Ž€{yxuu~‘«ÃÓÝßÚÙØ×ÚÖÖÙØÛÜÙÕרÚ×ÕÙÙÚÚÜÛÙÙÙÚÙÚÛÙÓÏÍÌÍÓÖϽ¥�|wz}€ƒˆ‡…�{vronmkjlmqssstttuutsqpomiggikmmnopnnoonlkgdcbbfeWYcju€†Ž ®´¸»¼½¼»»º»º¹ÁÊËÌÏÒÐÍÙÃ}h|yo–Ä¿·¾ÊÍÍÏÌÊÀÀ»œoŸš‹‚zxywtz�©¾ÎÝÞÛÚÖËÖÛÙÙÚÛÙÛÜÛÚÚ××ÛÚרØÕØÚØÙÒÓÚÛÚ×ÑÍÌÌÑÔÑ»™�xx}�‚}}ƒƒ‚{qooonnooqrrqqqstsplifdd`YTTWZ_cfjnqommonkjjhfgjkodPVeq{€ŠŸ®·º¼¿ÁÀ¾½½¼»ÀÊÍÌÎÐÑÑÐÎÓ¾€o†}rœÀº¾ÈÆËÐÎÍÄÀ£u�ˆzvtrw•²ÐààÝÖÔÖÙÛØÚÛÙÒØÜÛÚÛÚÚÜÛÖÔÛØ×ÙÕ×ÚØØØ×ÚÛÔÌÊÊÐÖÒ¿¢~pw~�‚}~~}{}~~zplnppqssspnpqoljga^ZVTQOLMNRVZ\`djmomlllkjlljknpsuZK[jw‰œ«´¹½ÁÂÁÁÀ¾ÀÇÉËÍËËÐÑÐÑÏÈË»…s�|s�Ÿ³ÈÈÉÏÑÒËÄÅ«h„usx�‰¡½ÖàÕÑ×××ÙÙÙÛÝÜÚÛÚÚÚÛÙØÙÚÚÛ×Õ×ÕØÚÛÚÕÑÙÛÚÙÑÊÈÌÓÖŧ‘}oy€~||}}yssxzwpkpstuttpkmolfa`\WSPMGDGJMOSXZZ\_eglnmlklllmllnoqto\[eu��ž«³¸½¿¾¾¿¾ÂÉÎÍÌÍËÈÍÑÐÏÐÎÈÍĆl~vp}¦ÅÉÍÎÑÑÍÊÈ«_x|—­ÀÐÛÞÚÖÍÒÙÙÚÙØÚÚÙÛÙÛÜÜÚÛÛØÙÚÙÚÚÚ×רÙÚÛÖÎÒØÖÏÈÉÍÓ̲”…Œ„u{}~~}|{wrmkkmommptuvvusomlfa]\[YXZ[ZXTTVWTUZ[[\`dfjmnnlnpoookhfdgnlgjvƒ‘¢¬²µ¶º¿½¼ÃÊÊÊÇÉÍÍÌËÎÏÍÎÏÏÌÕÇŒo{~v’¿ÇËÍÐÑÎË̱o©ÀØààÞÚÕÕ×ÛÜÙØØØÙÜÚÚÛÚÛÚÚÚÚÛÙÚÙÙÛ×ÓØ×ØÚØØÚÕÑÖÌÇÌÏÒÕw‚‘”ƒx|}}}|xqkhfdcfilosuuvusqpmfcccdghkmnornhgec`^]__`eiiknnnprrqnha^ZY_lqr}‰—¦®°¯´·¯ºÉÈÉÊÉÅÄÌÌÍÌÌÏÍÍÍÐÏÍ×ËŒmzxu”ÁËÍÐÑÍÉηˆÚâÍÏ×ÕÖÙÙÚÚÙØ×ØÙÚÚÛÛÙÚÚÛÚÚÙÙØÙÙÙÙÖÓ×רØ×֨רÒÇËÒÓÁ�jq���xz{}{ytkeca[V_jmpstvvusqpniimpstuxxtqrtspommlheedbdgiknmnorsrnfc_XOO\oxƒ’¡­®®³±�~Œ¾ÍÇÈÉËÉÉÈËÍËÍÍÍÍÏÎÍËÓȉgyzo’ÃÐÏÑÍÇ͹ˆÕÖÁÌÚØØÙÙÙØÙØØ×ÙÚÙÛÚרÚÕÖÚØØ×ØØÖÓרÕÖÕÓÖ×Ö×ÖÎÊÎÏÄ–qhy‹uyzyzvmd`^UKLVemprtvuttqpomqtuy{yxxvsrqrpnoooonmkjighiknnnnpssmfc_XPIJ_}� ®²¯­�ŒqkƒµËÇÆËÊÈÇÊÌÌÊÊÊÍÍÌÎÍÊÔÊ‹k€xk–ÈÎÐÐÅÊ»†ÒÖÑÖÚØØÙÙÙØÙÚÓÓ×ÖØÛÚÙÚÚÓÔØÖÖ××ÖÔÒÖ×ÖÕÔÕØÒÒÕÑÍÏÏÈ¢}wgguyy{yupg_[WM@AQ^hmrtuuutsqqruxwy{{{vsrqpopnllmmnnnnnmkkknppoonqpkgcb]UKHSt™­²´»»ˆh{‚xk|®ÈÆÇÈÇÈÉÊËÊÊÊÎÍÌÍÍËÌÓÉŽm|yt™ÇÏÍÈʺ†ÒØØÙÙ××ÙÙ×ØÙØÑÓÖÓØÙØÙÚÔÖÖÔÒÕÙ×Ö××ÖÖÓÒÖÙÙÒÔ×ÐÍÑÎ¹Ž‚zgdq{zyumbZXQC9:LZbkqstuttusstw{{{|}}zsqpolllkgefikmnppmklmnpomnnmmjhhhbYNHJe™²¶ÂÌ͹“w|‚{mv¥ÃÆÆÈÊÎÍÊËÊÊÎÏÌÌÍÊÍÍÐÈ�m~�v�ÈÊÇϺ…ÓÙÕ×ØØØØ×ÖØØ×ØÙØÙÙØØØ×ÒÔ××ÔÕØØ×Ø×××ÒÒØØÖÕÔÕÑÌÏÆª�‰}mjs{yskc\WK;89CR_gnstuvvuuttx{}~~|ytrpmjgeda^\]`eilnonnonnnlllmlmkijjhd\SL`œÀËÎÐÑ×Μzu||nsžÄÊÈËÃÂËÊÊÊÌÍÌÌÍËÍÏÈÎÆ‹l�‚xŸÇÉͽ…ÕÚ×××ÙÙ×ר×ÕÖÙÙÙÙØ×Ø×Ö××רÙÖÖ×××רØ×ÕÔÒÓÖרÓÍÍ¿¨–�‡untyukb]\Q<9>AJUajostuvwwvvwz}~~~}ytqle^ZWTQQRUVW\bgjmnooooomlkmlllijmnmjieg¦¾´ÌÎÍÐÐ̯zp€st˜ÂÉÆµ¶ÉÉÊËÌÍÍÍËÈÍÐÌÈÍÆ”p|~w™Â˹‡ÕÙØ×Ö×ר××ØØØÖØØÙÙØ×Õ××ÕÕר×ÖÕÕÔÖÖÕÔÒÖØÛÛÑÄ®µÎ¿¬œ—�ypvuoe`cXD@AADKUblpstuwxxzz|}€|xtnf]XUSQMHEEJOQT[`eilmopoqpnmnnmmlnonlnpsr¢¾¬ÎÐÌÏÐÓØ¹‚o|‚rl�¿Ç¼ÂÇÇÉËËÍÍÌÌËÌÌÉÊÊÍÌŸt}~t‘¾¸…ÓÙØÖ××רØÙÙÙÙÚÙÕÖÖÕÙ×××ÖÖØ×Ö××ÕÒÓÖ×ÙÛ×ÍÀ± “†“¼Åµ§ž‘xvrqi`aM=?A?BKXckqtvvy{{{}€~|wrk`ZZ\YVURNH@>DKOTY]chlnpqqrqppqomnmiefkosyŸÐÐÌÍÎÑÐÎÓÖ¹‚m}�rk�¾ÆÄÇÄÇËÊËÍÍÍÎÌÇÆÈÌÈÌÓ©v|„v“±…ÑÖÔÔÔÕרØ×ÔÓÙÙØ×ÖÓÕØØ×ÖØ××ÖÔÔÔ×ÚÚ×ÑÆ¸¢•Ї‹Ž‘’¢¾Å¸¢Ž…�yplhZL=9@CADP[ckqvxz|~~€€~|xsj^[]]\ZZ[[VQH>D@=DKTY`lvy}€‚ƒ‚��€~zuoc^_`a`\VQNLC60126?JQV[afjntwz{|}p[OF?<=ETajvŸÊÎÍÍÏÐÏÏÐÐÎÍÕÔ¥v{„xh|¬ÃÅÈÈÉÍÎËÍÍÊÊËÍÎÌÍÏÎÒ«vy‡„ÐÙ×ØÚØ×ØÙÚÕÖÚÙ×ÖÔÓÒÔØÚ×ËÉ´¡”Šˆ‹‹•’ˆŠƒ{urnhgfdeadz›±¡‰ƒpQ<:??;?DKOU`nz�…‡ˆ‡ˆ‡†�|wtrppolljie`\XWVPICDO\a_^cknptwy~‚…€bHDA@FUZT`ˆÀÏÎÍÎÌÎÎÏÏÏÎÑÑÏÌÐÔµ€jx}psšÁÊÉËÌËÊÉÊÉÍÌÇÌÎÊÌÎÍÔ±†ÒÚØÖÖÔÓÔÔÖ×ÚÚÒȹ¦•Šˆ‰Œ�Ž‘��‰skfehjjjkklmnmnponnonpl_nƒ„€t`K>?ELOPW`p~ƒ†ˆ‰‰ˆ‰ˆ‡‚}~|zxtpnljhge`ZURSWXXY_hnpruy|€„……pJBE:-3CNeœÉÏÏÏÌÆÊËÏÏÏÐÐÐÍÏÑÑÜÇ‹kv~ro‘¾ÌËËÊÉËÌÉËÍÊÍÌÉÇÇÍϾ…Ï×ÕÓÔררÕÍñ£•�Œƒˆ™—––”Œ~smhfeiiillllmmmoppoopponnim~wrwztdNKSSPRXbt~…ˆˆˆ‰ˆ‰‹‹‰ˆ‡†…‚|yuqonkgb\WUUWWVY`gnpqvy|}…††^LLE879Di«ËÎÎÍÎËÅÈËÍÏÏÍÎÏÌÏÓÓÚÏœss{qlˆ¹ÍÉÊÉÉÌÍËÌÌÎÎÍËÊÍϺ„ÎÑÖÙÚÛÓõ§™’Ž’˜œ˜Šƒ�Œ…~xmgfhikllmnnlkmonmonoqqqpponp}rfkotwsleaWNQXhz�ˆŠŠŠ‰ŠŒŽŒŒ‹‹Š‡†ƒ�~yuokheb`_]^_``elrstwz}€�…‡ŠˆpYXWVSLQ{¶ÅÁÊÌÍÏËÍÍÎÎÎÎÎÒÍÌÒÓÎÔÓ«wr|wn…¶ÊËÉÉÌÎÌÊÌÎÎÎÍÐÎн†Ó×ÓÌ¿­œ�‰�”—�¢Ÿ›„ty{skjheflmppnmnnnnnprpopopsrqoopnuxiknquvwvpi`TRau‚‡Š‹��Œ�����Œ��Šˆ‡„|zuqonljjhhikotxzyz~~�‚�‚…ˆŒ‹|a^dhkad•ÂÆÀËÎÍÎÍÎÏÐÎÎÎÍÏÏÐÒÒÐÌÏÔ´|n|xm€«ÇÇÇÍÍÊÊÌÎÍÎÎÐÎ̼„ô ‘ŠŠ�—œ ¡›’Œ|qhfmmhhiillmmnmllljmnmnpppqrqpqpooooxsimku|vqwtqkggu‚‡ŒŒ‹ŒŒ�Ž�’’‘���ŽŠ‰‡‚€~{ywuusrstv{}|}}~�ƒƒ‚��…Š��‡lY\cmlt¬ÆÇÎÏÏÏÐÐÍÏÏÌÏÍÌÈËÑÒÒÐÒÐÐØ¹~lwxmx¢ÂÈÉËÉËÌÎÏÎÐÑÌÊ»o•‹Œ’š£¥œ“†{tols‚Œƒm\[abdfcdijjikkjmnmmnopqpoopnnoonzskno}ƒwlvwuutx�†Š��‹‰Š��’““”’‘‘�ŽŽ�‹ˆ†„ƒ�~~{z{|~~~~�ƒƒ�€€ƒ‹�’�|e^`ip‚µÈÇÏÎÎËÈËÍÍÊËÐÎÏÌÍÎÑÓÑÑÒÑÒÙÊkuxou�¾ÆÇÊËÊÌÎÎÐÐÎϺnœ ¥£��rlkq�œ¹ÌȾ°‡\PW[[Z\`cfhijjlllmnooppnopomlmm|vmqt‚ƒzv}|xzz|‚ˆŒŒŽ�‰ŠŽ�“””““•”’“”““��Ї†„‚‚€~€‚�€�€€�‚‚�€�…Œ“•“ˆtmkls…´ËÌÊÌÌ̾¾ÏÏÌÎÏÏÌÏÐÎÏÓÓÒÒÒÎÐÙÊ“puxor–½ÃÅÊËËÌÐÐÎÎÑ»w¥•„xqor}� ±ÀÈÍÙÑÁËÜÒ bJJLPTWZ]`cfffghijlmlllmnopnlk{znsx…‡…ˆ€y|}~~„ŠŒ�ŽŽŒŒŽ�“••”•˜˜˜˜˜—•“�ŽŒŠ‡†…„ƒ��‚ƒ�€€��„ƒ��‚ƒ‡‹�–•Ž~urrv‚«ÉÊÉËÌÌÊÌËÊÏÎÎÍÉÎÏÍÐÒÓÒÒ̾ÇÐ×Óœmsxps—½ÅÅÊÍÌÏÐÎÍѾbzopz‹Ÿ³ÄÑ××ÔÊÉÐÊ¿ÅÎÒÑ«nG>BIMPSVY]`abdfiklihkkjlopomx{pszˆ‹Ž‰eg|‚„‰Š‹���Œ��“””–˜š››š™—–•’‘�ŒŠˆ…„…ƒ�€�€€�‚ƒƒƒƒƒ†‰‹Ž•™“†{xy{�¢ÆÇÉËÍÌÏÒÈÆÏÏÐÍËÏÍËÎÑÏÐÏËÇÌÓÒØÒ›oqwpt•¸ÆÇÊÍÎÐÐÏѽb�œ´ÆÔÚÝÝÛ×ÔÒÏÏÕɾ¾ÄʪuR>9?FJMPTW[]_behjijkjjjlmons|rt|‰‹ŠqOcz„€‚†ˆŠŒ�ŒŒŽ‘•™›žž��œ›š˜˜—––”‘�Œ‡„†…„‚�€�€€~~€�„‰�‘™¥«¥’�}}„˜ÀÊÅÊÌÊÊÍÍÊÌÏÒÍÎÑÏÌËÎÎÏÎÏÐÏÐÓÑ×Ö¢omxupН¿ÄÉÌÎÐÏÏ»uÂÛàÞÜÛØÖÖÕÖÕÔØÑÁÀÂÿº½½¯‰`C58?EIMPQVX[_cffhhhiijjkkjxwv~‰‰z]Veuƒ‡‚€„†ˆ‰‹Š‹�—¡§©««¬¨¤£žš——™˜–•’ŽŠ‡………ƒ€~€~~}}€ƒˆ�š®»º³¤‰~~�‡�—¹ÊÉÊÊÊÌÍÉÉÎÏÎÅËÑÎÍÎÏÏÏÑÎÍÏÎÐÑÖÙ¬uo~vn‚§½ÄÇÊÏÏϺ|É×ÕÕÖÖØÙ×Ö×ÔÕÕÅÀÀ¿¾¾»½¹»­ŠkM938@EHJMPV]_aabcdfefhiiclxx~ˆ„kZaezŽ‹…€€‚„†‡ˆ‹”£¬­­®³µ³±ª£Ÿ›™š˜”‘ŽŒŠˆ…ƒ�}~~~|}€€„Š”§¸»µ°«•}�‡‹s~°ÌÉÇÉÉÉËËËÍÎÃÇÐÎÏÑÌÌÏÏÐÐÏÎÑÒÍÕÚµzlvsr†¦ÀÅÅËÏÑ»†ÔÒÑØÙÖÔÓÐÖÔÐÔʺ»¼¹¹Ã´³»´µ£ƒoXB327>CEJQUWZ\]_bdcdefgb_v{‡d]fm‡’Œ†€€�‚„†ˆŒš¦¨©«­°²²³¯¬ª¥Ÿ™—•‘Ž‹ˆ…ƒ�|}}{yy|‚‚ƒˆ“ ©¯²°¬¨™ƒ}€†ŒzeqžÃÉÅÈÊÊËÌÎÌÌÍÍÍÏÐÌÌÐÏÏÐÏÐÒÒÒÏÑØº‚ksxv�žÀÆÄÌÒ»ƒËÙרÕÑÒÐÍÔÑÑÔɽ»¼½»»²²½º¹´–|naN:018>CGJMRWY[^abbbcdcYm~}‡~ffr{Š�‹†���‚„…†�™¡¤¦¨«­¯­¯°²°­¦ž˜”‘�‹‡ƒ€€|}|xw{‚†…†ˆ�™Ÿ¡£©ª¦£™„}„‰ƒwddŒºËÉÈÇÊÎÍÉËÎÍÍÍÍÏÏÎÏÐÑÐÏÑÓÓÒÐÏ×È™ppwpuœÀÃÅκ‚ÌÜÙÙÔÏÖÖÒÏÒÕÓÁ»ÁÀ»°º½½½º´¥‹{siXD5/16=BGJNTVX[^^]`cd^ezyƒ�mn|{��‹‡ƒ€€‚„…†�–�£¦¦©««¨ª­¯¯®ª£š”�Œ‰†„€~€|xx~…Љ††Œ”™� ¢£ Ÿ™ƒy~�ˆwwja|®ÉÇÅÈËËËËËÍÏÏÎÍÌÌÍÎÐÎÍÐÒÐÐÑÏÏ×Ò¢olupl‹­¼Çµ†ÖÚÖ×ÖÕÖÖÔÐÔØÐ¼·¿ÀÂú°¸½»»³®Ÿ†}tj^N?3/29?CGKPSVYYZ]adb`s|‡|wvq†Ž‰„�‚ƒ…†‹“˜ ¤¥§¨§¤¦©ªª©§¢œ–�Ї†ƒ~�ƒ€ytvyzzz{~�ƒ‡�–››šœ—�v{€†{fmwp_n�ÂÊÅÈÌÍËÊÎÎÑÑÊÇÍÏÎÌÊÎÐÑÐÐÐÎÏÎÒÓ®whvneƒ²Ç²…ÕÛØ×ÕÕÔÔ××ÙÕÈÃÁÀÁÀ¿½²¸»¹¹¹¶±‘~zsmdWH90-3:ACHKOTVWZ^bb]e||„‰‹�…mu‹‹„‚‚�„†…‰�”™ž¢£¢¡¡¡¤¥¤£¢¡�–�‹‰†‚€�„„€ulmmgO?I[ltuz„�‘���{sy~…}a]hvsdh�ºÊÉÊËÊÈÉËÆÊÌÇÉÎÐÎÌÏÐÐÑÑÏÎÐÑÎÏÔ´|kysp‡°¶…ÔØÕÖÕÕÖÕÔØ×¿»ÇÅÄ¿¹½»¶·º¸¹º·£‡~zvogYLB6..4:>DIMPSUY\^_\q|u~Š”‘~o�‹„‚�‚†‡‡‹Ž�’•˜™™™™›�žžž�›˜“�‹‰…€~‚„ƒ‚xh`b[?.2;I^lqv}€€}qpx}…y`^\drvkd{ªÈÉÇÉÅÀÊÇÊÈÇÊËÍÎÏÎÏÏÏÑÐÏÐÐÎÌÍÔÁ‚hvsj~Ÿ†ÒÔÔ×רÙ××Ú͸ÀÇÄÅÄÄÀ¾¾¹µ¹¼¼½µ±™ƒ}xsnh_UK>2-.4;@CHMOSUV\\bxvr}Ž•‘‰ˆ‡„��ƒ…‡Š�Ž��Ž�‘“’’’“•••”‘�ŽŒ‡„�}}�ƒ…„€tcYTLEEE=?Ujoqsrtqjnv}„v^^[Y`qvobnžÄÇÄÇÆÊÐÍÈÍÌÊÉÌÌÎÏÏÍÏÐÎÎÍÍÏÐÐÖ‡jstmq†ÓÙÖØÙÙÚÙÚÚÎÈËÇÃÃÅÂÁ¿ÁÀ»¹½¿½·¶«�‚|wqnic\QE91,/7<8;d€„‡…�€zvx|zx{…”žŸš–—™™œ£¤ •†}‚�™ž�š•�Š…ƒ„‚€„…ƒ€}wqr{�vlq}uYRQNOONONKU^`][\]_`aa`^]]\Z[]Y_nxobx«ËÊÂÄÆÈÈÇÇÉËϼ†×ßÛØÍÉÎÑÐÐÒÎÌÉËÎÊÊËÌÍÉÈÉÈÈÈÉÉÈÉÈÅÅÀ»½½»°”‡…„€|zrsurnkhfc^WPF@>:;Z{‚†„€€{srxywv|Š˜Ÿž˜••—–˜š—‘��™  žžžš•”“‘�ˆ…„ˆˆ…ƒƒƒ~|‚ƒzss|mVVUSTROMPSQTSLHKU^___`b`_]ZZ\ZWYfsocl™ÂÉÅÃÅÅÅÈÅÅλ…×ÞØÕÌÎÐÑÅÇÒÑÑÌÊÍÌÎÍÊÊÉÉÈÈÈÇÉÉÉÉÉÆÅÅÀ½½¼½«�‹ˆ‚~{wvvuqolid]^XFAED2Eq~ƒ„�}toruusu€�šœ—��”•’“’Ž‘˜›Ÿ¡žšœ�™––“މ‡‡†„ƒƒ�~z|‚„{wvxgZ[XVVSTTVZSLJFDIV^_`_`cbba^\ZZ[YU_otgdˆ¶ÆÂÀÁÂÇÅÇ̹‡×ÝØÒËÏÐÐÈÈÒÓÎÎÑÎÎÐÍÊÉÊÈÅÊËÇÉÌÇÇÊÆ¿»½½½½¼¸œ‰‡„‚~{wurplieehZG@Vy]8]z�†…�}xqpstsqv‚‘™—‘����‘‘�”˜›��œ™˜™—’‘ŽŠ‡ƒ~zxvtsqqv~‚~xzu`ZYZ[WTVUUVUOJKPX_][ZWWZ\[]\\]\Z[YW]gunbw¦ÄÅÂÃÅÈÌ͹‡ÙÞØÍÌÐÑÒÓÎÎÏÌÍÏÐÎÍÎÍÎÍÆÇÈÁÄÇËÅÄÈȽ¶»¿½»º·¦‰†‡…„�~wturookkgZRB`�–XIq†‡„€{topttqpxˆ”–‘‹†ˆŽ‘•™šš�Ÿž›–�Ž�Œˆ„~|vrommmlou}�€}}n\ZXZ]YUVTSVVSQU[]YQOMNNOPLNX\]]]\\\YXbrqcl™ÄÊÃÃÇÉι†ØÞÓËÌÎÑÓÒÍÌÏÒÎÊÑÍËÎÌÌÌÊÌÈÂÆÈÉÅÇÉÊÄÃý»¼»µ­“ŠŒˆ†„vuz{uoljd^XDd€�H`y…Šˆƒ€zsopqqpuŠ�އ‚�…‰‘œžžœž¢ž—�‡‰Šƒ|xsqpmjjjiiow~�€�{e\[[[ZXVUTSWVU[]YTNIJMNOOKHJRQQY^[Z[]^Y_ntfdŒºÇÂÂÆÌ¸†ÖØÎÊÌÍÑÓÐÎÍËÐÎËÎËÍÍÍÌÌÍËÊÍËËÊÉÉÊÈÄÁÃÀ¾¾¼¹´©–�‹‡†�||{{vnjjga[Ps†~�]Qo‚Љ„‚|uopqpqx‚‹�„~|„Œ—£¨¢ žœš“‹ŠŠ†€yspnjgedfkry~€ƒ…wc]\]]\[YYXXWW[a]UPQQNOLNNJJKJINX^]]]]]\Z[fsnez©ÄÅÄ˺‡ÖÔÉÊËÏÒÔÒÏÍÉÌÏËÃÉÎÏÎÍÍËËËËÌÍÎËÊÉÈùÁ¿¾¿¿º¹¤ŽŒ‰‡ƒ€�~ytpmomdcd€Šw‘�Tg}ˆŠ„�€€|uqqpoqy„‰„{y~†Œ“ž¥¨¥ œ˜˜—’�Œ‡€zqjfc`bgms{}�Ž‹sa]]]]_^\\[[[[^\VTRQSROPQONONLJNXadb_]YY[YYewzfk–¼ÆÉ¹†ÖÕÈÉÉÎÒËÈÎËÍÎÍËÉÌÍÎÍÍÌÉÊÈÉËʾÃÊÉËËÆÁ¾ÀÀ¿¼º»´™‹‰…€€‚€~ysqsphfkƒ�x—jg{„Š…ƒ…ƒ�}tqrqmrz‚‚|wx�‹’•�¥¥¦¡š˜˜•‘�ŽŠ„�}{zwy|€‚~‚�Œn^]__]^^]]^^^]YUUTTSRSRTTPRSRPKKRZaa_^]ZY[\]csyfbƒ°Æµ†ÔÒÊËËËÒÇ¿ÊÇËÐËÌÍÊÍÍÌËÊËÉÆÈÊȹÀÉÉËËÉÁÁøµ·ºª�ˆƒ�‚„‚�€zuvslfg‚�}m•†k|�‡‰‡‰Š…‚|vvsoory~xvz‡�‘–œ£§¥Ÿ™–••––˜š���›——™›šŠ�ˆ„i\_``^^__^__[YWWXWWWUTSSRPQRSRMKO[bca_^[YXX_\Yjxncx¢°†ÓÎÊÐÑÎÏÑÎÏÍÂÃÅÈÌÎÊËÎËÈÊÊÇÈÉÊËÊÊËÊÉÆÄÄÃú³´¸¸ �‰‚‚…‚€~~}zwpkg}Ž‚h†�xxƒ†ŠŠŠ��‡€ywvsnow|}}yw}‰�‘—žŸ ¡žž�œœž¢§¨¦ œš›šš›•…€„|d[]_````_]]^^\[ZZ[[YWVUTUSOQSRMIP]a^_`_^ZWWXYYXarrioˆ„ÏÌËÑÔÓÒÓÒÏÎÉÀÂÈÌÈÇÍÐÎÊÊËÊÈÈÊÊËÊËÊÉÇÅÅÃÄÁ¿¼¾»½²—�ˆƒ„‚€}|~~{tomzŠƒii—“w�……†‡�‘Žƒ{xwupntz||xvw�ŠŽ‘“–—� ¢¢¢¡¢¢Ÿ ž™••–““•™Ž�†zb_^``__`_^^^_]\Z[][ZYWWWXYWWUSPNPQNLPW[]]]ZWUWWR[ktlcƒËÌËÍÒÒÒÑѽ»ÑÎÊËÊÅÉÏÏÎËÂÈËÊÊËÊÊËËËÊÇÅÅÆÄÀÀÂÀ¾¾»¥�ŽŠ‡ƒ€~||~|yspy†‚l[uœ‡}‡†�‚†Ž‘‰}wvtrmouxusssxƒˆ�“—œ¢¡¡£¢¢¡›˜—˜”�“–””–�†‰t_``_^^_^^_`_]]]]][ZZ[XUZZYYYUQTVQJFKRWZ\_c`ZXVVWWWaok†ÊËÎÎÐÑÎÐѾ¹ÊÏÐÌÈÍÍÎÍËËÄÉÊÌÍËÊÊËËÊÊÈÇÆÆÁ¼¼¼»ÀÀ½´™�ŽŠ„‚€~}}z{wrw……p`\…œƒ‡Œ…€‚ˆ‘Ž�ywtqmlnqsqnmnsz„ŒŽ“˜œžžžžŸžš˜”“��Ž�•’�‹‹‡jY[]^_^^^_`a`__^^^[]\]ZZ\\YTUVSRQNKIMUYZ\Z\a^[[Z[^ZU^lƒÉÌÌÔÓÐÐÌËÍÍËÌÏÍÌÐÏÎÍÌÌÍÍËÌÌÊËÌÊÉÉËÊÈÇÆÄ¾»¹»ÁÁ¾¿®”��Šˆƒ‚ƒ�~}zww‚†tc[i˜–‡�‹ƒ�ƒ‹�Š~xurmhhkqnihiou}†‰ŒŽŽ•˜™�š——™“ŽŒŠŠ‹ŽŒ‰ˆ‰€bWY\\\\]___^_a_]^^\]]]]_][ZWWXVRPNLJHHHIRXX[]]]\[]^\Z^€ÈÏÍÐÐÌÎÇÉÍÌËÌËÌÑÑÐÏÎÎÌÊËËÌËÌÎÌÊÉÉËËÉÉÇǽ»Á¿¾¾¼¸¡‘��ˆ…„„„‚�€{{ƒve_Y{ ’ˆ‰…‚€‚ŠŠƒzrpnhgfikifhmos|„ˆŠˆŒ�“˜”’“‘‘�Œ‡‰Œ‡‡‡††t]YZ[\ZZ\\\[[]]_```^\^][\\[ZZXUSSRQPOONLFLW[\]^^[Z\\]\W�ÉÏÒÉÈÎÎÍÌÍÊÊÎÍËÏÒÐÏÍËÎËÈËËËÌÍËËÌËÌËÉÈÈÆ¿ÀÄþ»¿»º±˜�Žˆ†‡†„„„ƒ€~ƒzf\Xb‰ –Œ„€��„†}okljgdchiggklouz�…†…‡‰ŠŒŽŠˆ‹�‹‰Š‰†…‚ƒ�iZ[YXYY[[YYXXXX[^_^]^][XYZXXYXVUSRRTVUSPMOY^^^^]]^_`][Y‚ÎÒÏÎÐÓÎÎÎÎÐÏÍÎÐÑÒÏÌÍÍÊÈËËÊËËËËËËÌÍÊÈÉÉÇÄÅþ¼¾¿¾¼¼¨’�ŠˆŒŠ†……„ƒ€�„|g\XWg†—š•ˆ�}{‚|ofeggedecacgjkpu{ƒ††ƒƒ‡‡�€†ˆŠŒˆ„€{‚}_WXVVUVYYYWVSRTUXXXYYYZZZZVUVVVUVVVTUVRMJP[`_^__`aaa_\ZƒÎÒÑÓÓÑÎÎÍÌÏÏÎÎÐÏÏÎÍÏÎÅÉÏÍÍÎÍÌËÊÉËÈÁÅÉÊÇÅÄÅþ¿½¿¿·›ŽŒŠ‹‹„…‡†„€}xk_XV]dp�‹‘ˆ}uxzyl]Z`ccefb^_`cgjptx}ƒ†ƒ~~yvx|€…ˆ‚yxx~pVVVVUUTUXXVWVQPQQQPRSXZXUUTQQSXXXXWSRXWRLR]^\]a`[Z]`_]]ƒÌÎÍÒÑÏÏÐÎÍÎÌÊËÍÏÌÊÍÍÏËÉÌÎÎËÌÌÌËÌÌÆÃÆÉÉÆÄÃÄÆÃÁ¿¿Á¿¿«’ŽŒŠ‹‡ˆ‰‡ƒ†|pxn`XWZ_`cb{‰€vquwr^SW]`aedb_\]befjotz‚~xuutsruyyuppsv`QSSSTVUTSPPRRNLLNSTSTWURQUWURTYXUVVTTVTQQY`^[[WVWTX__^X…ÑÑÍÒÒÐÑÑÐÐÎËÈÈÉÍÍÍÍÎÎÍÆÈÎÎÌÌËÌËÌÎËÊÅÆÈÄÃÅÃÅÄÀ¾ÀÁ¿º¡��‹‹‹ŒŠ‡‰“ŒrsqcWTV]^ba`}‡}rosuiSMV]^^adb]YY]`ejlsy|}xsqqljjmmmkjniOKPNMONOLJJHGHIJJNUVWXXWUXZ[\ZZYXXWUTVTQRY`][\YPOX\ZZ\[W„ÏÔÓÔÓÑÒÒÎÌÌËÍÎÉÉÍÏÎÍÌÎÊÉÌÎÏÎÌÌËÌÌËÊÈÉÇÆÆÆÅÄÄÁÁÂÁ¾¾²–‘�Œ‹ŒŠˆ——•ŽxodYTTYZ[fai…†xjjpn]NMU[Z[]`^ZWX[]achmpppplhddeddefihcXHFJKJFDBCFHGIKLJLPPPQQUYZZYYYXVXZYVTUTU\a_VW[YVXY^^[]WS‚ÍÔÕÕÑÐÐÊËÌÍÍÎÏÍÎÏÏÎÌËÍÌËËÊÉÍÎÊÉÌËÉÉËËÇÇÄÄÈÆÆÀ¿ÃÂÿ½º¦–’ŽŒŒ‰‹™˜‹›™}bYVVWZVZhaq‰ƒqahphZOMTYXWZ[ZXWWWY\`bdfeffbabaaaafaVYe^JBHGDBACDGHJKMNOPQQPQQVXWVSTVUTUVUUVWY`bZTUYYVXYYZYXQK„ÏÒÏÐÏÏÏÇÊÍÎÐÏÎÎÐÑÐÍÎÎËËÌÌÉÉËËÊÇÇËÉÇÉÊÇÃ»ÄÆÁÃÃÁÃÃÂÁÀ¼¶Ÿ’’��Љ‹™‹‰ž�tWRWVVWT_ebzˆ�k\dg_XOKRXYWVUUTSVWXZ[\]ab`_aa`]\]UIEKVffNAEHIJKGEGHHLPRRSVWUPOSTRSUZZY[[XWXZ\ab_[[YVUSWWXWTPO…ÏÑÌÍÍÐÑÎÌÏÏÏÑÑÎÏÎÌÍÏÏÍÌÌÊÌÈ¿ÈÌÉÈÌËÈÊÇÁÅÄÄÁ¾¿ÆÅÅÃÁÁ¾»«”’‘Œ’’‚…‘†‰›‹gROTTVST_bf}†�dT^]YVMIOUTTQOOPRTVUVVVY\ZYZXTSUJ;:@GMWghN>EKMMLLONNKMOOOOMJJKORSTWYZ[[[[^``bddfdc\XVVYXWVYZRƒÏÔÍÊËÎÐÎÍËÊÍÑÒÍÌÌÌÍÍËÌÌËÉÍÈÁÉÉÉËÉÇÆÉÆÃÆÇÆÆÃÃÂÀÆÅÃÂÁ¼º²œ‘�—¦¥Žs…�…‡��iQJOSQQU_`k�…|^PVUVUMHJOUTPPPNPUSPQRQRSPNNSdeA5:=BKS[jmO:?CEGIMPSPOMIGHIIIKOQRSSSSVXX\bccfggefd]YWYZWUSW\W„ÐÑÌËÉÈÇÇËÉÇÊÎÏÌÊÎÏÏÎÍÎÍËÈÍËÉÆÆËËÇÆÆÉÇÈÆÆÈÈÄÄÂÂÈÆÄÃÀ¼·²¨––¤­©ŸŒ‚‹�ƒƒ‚ymTEGOOPV]ar�€y[KKMRSNIHKPRSQOOPPMMMOOMNR]n~sC68>CIRZ`npP9?DHJKKJNSVYZ[TLLNPOOQSTUY\WVUTWTRSRTVX[`fc„ÎÏÌÊÇÊÊÌÉÅÍÎËËÌÌÌËÍÍÍÏÍÍÊËÊÊËËÉÉÊ̽¼ÊÈÈÇÇÄÀÁÅÈÇÆÈ¾­·½º¯¨¥£•—¦ª­£‘‡‹—Œpkfpym^LDGNUXn|ka]YNDCLQOKJKMLKMPUYZ]elpsw|pB039CJRY_fmptw‚¡”ubJ=AGKLJKOQUXXSIGJLMMKPQNPSRQOUXSPW^^`d_ZX\‚ÎÐÌÊÈÌËÌÌÌÐÏÍÎÊÈÌÎËÉÎÍÌÏÑÎÈÊËÌÉÊÉÉÂÂÈÆÉÇÅÄÄÆÊÉÆÅžª©©¦¥¦¥¤™’›œ££�†…“�}vifhmofVLEHO\~‚k[YYULILPPQOOOMPSVY]bhlotwzqC.28CLUY^jv|}b@]~ЇtQA>CJLKMORSSUSKLRPQQOOOQPU\[^]YXXY^^YZ`hZƒÍÌÉËÉÉÊÌÌÍËÊÊÍÌÉÍÍËÌÎÍËÌÏÌËËËËÊÇÄÇÍÉÅÅÈÉÇÇÇÈɽ±°¯®¬£�—𢡡›Š�‰„„‚ƒ‡��…vspddllmh[RJIbyfYXWUPMOSVSRPQSWX\aehlptxyrE-18DNUZj}‰Œ‡a:0EJJHIJKIJNUWT[n Ã­‹¡°¢€vªßÔËÚØÂ�ÇÉÃÃÄÈÊÉÈÄËÍËËÊÉÇÆÉÍÍÉËÌÊÉÌÌÆÄÄÀÀ¾¼½¸°­¬©©­´°£™•˜�¥§¡‘�‡‘¢˜�–˜¡¦“„wuy}zsrx~qg^ahnpw}‰’™Ÿ�zdMHKKOSVXZ]^adfhknqqmjN.,5CXw„ˆ‹ˆ†wSAADHLH<6Hh}�žŠ\DA=:CJP`et‡“|Pa|ÀéÀ¡§—wv±ÓË»¸»·¡‚ÈÇÃÃÆÊÌÍÌÅÉËÅÂÈÈÉËÌÌÍÉÇÈÉÆÈËÈÆÄÀ»¶³¯®°«®¯«­³°¤•�‘— §§¡’ŠŠ‚�—�ŒŒ˜ž£¬†{Œ•�…‚‹‘yu‚‹……„|ww‚�š£ª‘h\[\^abbdfhkosvz{xusY:17Sz����‘‹•{PLNQRSTL>?Nd�¤œreŽ’‰�’�·ÉÆ©“�ŠÄÙÁ»»²­½Æºµ·µ²³§‚ÇÆÆÄÁÇÉÊÉÉÉÉÄÃÉÉÊÊÊÉÈÉÈÈÈÈÊÇÁ¼·³¯¬©«¯®«­¯±¶µª›ŽŠ�›£¦§¤—xƒ‡zŒ�Šˆ�—~la\gg`ZU[^RNMX`ahplbZPIT[p{ZEDCCDDDFHHJNQSUURQP?,()C_c[MS_\ib?8:;;<=@=2+/=Usv_e’|ggdi{„�|ˆsm�€|}|ƒ~vy{{|}~vƒƒˆˆˆˆ‰‰‰‰ˆˆ‰‰‰‰‰‰‡‡ˆˆ‚‚yyzzzzzzyyvvppttzzxxuuvvuuvvwwxxxxyyyyzzxxvvuuppppuuvvuuyy€€~~yyyyxxvvwwxxxxww{{xxvvssrrvvxxxxwwvvwwuuuuyysssswwxxxxuuttyywwqqssttttuuxxssssuussttuuqqvvƒƒˆˆˆˆ‰‰‰‰ˆˆ‰‰‰‰‰‰‡‡ˆˆ‚‚yyzzzzzzyyvvppttzzxxuuvvuuvvwwxxxxyyyyzzxxvvuuppppuuvvuuyy€€~~yyyyxxvvwwxxxxww{{xxvvssrrvvxxxxwwvvwwuuuuyysssswwxxxxuuttyywwqqssttttuuxxssssuussttuuqqvv††’’““““’’””““““““‘‘‘‘‡‡xxzzxxuussuuwwwwuuuuvvwwvvwwwwxxyyxxxxwwttssssttuuvvzz}}}}{{yyyyzzxxxxxxzzzzyyxxzzyyvvrrrrwwxxwwwwvvuuuuuuxxttttxxxxwwuuvvzzvvppssuuttvvxxssttttttvvttuu††’’““““’’””““““““‘‘‘‘‡‡xxzzxxuussuuwwwwuuuuvvwwvvwwwwxxyyxxxxwwttssssttuuvvzz}}}}{{yyyyzzxxxxxxzzzzyyxxzzyyvvrrrrwwxxwwwwvvuuuuuuxxttttxxxxwwuuvvzzvvppssuuttvvxxssttttttvvttuu€€‚‚„„ƒƒƒƒ‚‚€€����€€��xxuurrsswwxxvvuuvvvvvvwwxxvvvvxxxxuuttssssttuuxx||||{{yyxxyyzz{{||}}~~~~~~~~{{vvssttwwxxxxuuuuvvttuuxxssttwwwwvvuuuuyyttqquusssswwxxrrttuuttuuvv€€‚‚„„ƒƒƒƒ‚‚€€����€€��xxuurrsswwxxvvuuvvvvvvwwxxvvvvxxxxuuttssssttuuxx||||{{yyxxyyzz{{||}}~~~~~~~~{{vvssttwwxxxxuuuuvvttuuxxssttwwwwvvuuuuyyttqquusssswwxxrrttuuttuuvv||||||||zzxxyyzzxxvvttssttvvwwwwwwwwvvvvwwwwvvwwwwvvuurrrrssuuvvxx||}}{{yyxxyyzz{{}}€€€€€€€€��ƒƒ��zzttsswwxxvvuuvvuuttuuxxssttwwwwvvuuvvxxttssuuttssxxxxqqttttssvv||||||||zzxxyyzzxxvvttssttvvwwwwwwwwvvvvwwwwvvwwwwvvuurrrrssuuvvxx||}}{{yyxxyyzz{{}}€€€€€€€€��ƒƒ��zzttsswwxxvvuuvvuuttuuxxssttwwwwvvuuvvxxttssuuttssxxxxqqttttssvv}}||||{{xxxxyyvvrrssvvwwttttvvxxxxvvuuvvwwvvvvvvssrrrrssuuww{{}}||zzyyyyzzzz||~~€€€€€€€€€€‚‚„„~~ttssxxwwvvvvuuuuttvvxxssuuwwvvvvuuvvyyssrrttttttzzvvrruussuu}}||||{{xxxxyyvvrrssvvwwttttvvxxxxvvuuvvwwvvvvvvssrrrrssuuww{{}}||zzyyyyzzzz||~~€€€€€€€€€€‚‚„„~~ttssxxwwvvvvuuuuttvvxxssuuwwvvvvuuvvyyssrrttttttzzvvrruussuu}}||{{wwssttssrruuwwvvttrruuwwxxxxuuttuuvvuuttrrrrttuuww{{||{{zzyyxxyyzz||~~€€€€€€€€€€„„€€ttssvvwwwwuuuuttssuuwwrruuwwwwuuuuwwxxrrqqssttwwzzttrrttvv}}||{{wwssttssrruuwwvvttrruuwwxxxxuuttuuvvuuttrrrrttuuww{{||{{zzyyxxyyzz||~~€€€€€€€€€€„„€€ttssvvwwwwuuuuttssuuwwrruuwwwwuuuuwwxxrrqqssttwwzzttrrttvv€€||wwuurrrrvvwwwwvvuuvvttsswwxxxxwwuuttssrrrrssttuuyy||||{{xxxxyyyyyy{{~~€€€€€€€€€€€€€€€€€€€€€€ƒƒ~~sssswwxxvvuuuuttssuuvvrruuvvvvvvuuxxxxrrqqttvvxxzzttrrvv€€||wwuurrrrvvwwwwvvuuvvttsswwxxxxwwuuttssrrrrssttuuyy||||{{xxxxyyyyyy{{~~€€€€€€€€€€€€€€€€€€€€€€ƒƒ~~sssswwxxvvuuuuttssuuvvrruuvvvvvvuuxxxxrrqqttvvxxzzttrrvv~~wwuuttvvxxyyxxuuuuvvvvrrttwwxxxxuurrqqqqrrttvvyy||||zzyyxxxxxxyyzz||€€€€€€€€€€€€€€��‚‚zzsssswwwwvvuuttssssuuvvssuuvvwwwwuuxxxxrrrruuvvyyyyrruu~~wwuuttvvxxyyxxuuuuvvvvrrttwwxxxxuurrqqqqrrttvvyy||||zzyyxxxxxxyyzz||€€€€€€€€€€€€€€��‚‚zzsssswwwwvvuuttssssuuvvssuuvvwwwwuuxxxxrrrruuvvyyyyrruu~~zzyyvvxxxxvvuuuuvvvvttssvvwwvvssqqrrssttvv{{}}||zzxxyyxxxxyyyyyy||€€€€€€€€€€€€€€€€€€€€€€€€€€‚‚wwttssvvwwvvuussssttvvvvrruuwwwwwwvvyyyyqquuvvvvxxwwtt~~zzyyvvxxxxvvuuuuvvvvttssvvwwvvssqqrrssttvv{{}}||zzxxyyxxxxyyyyyy||€€€€€€€€€€€€€€€€€€€€€€€€€€‚‚wwttssvvwwvvuussssttvvvvrruuwwwwwwvvyyyyqquuvvvvxxwwttzzuuttvvvvvvuuvvvvuussttuussqqpprrttyy||||{{yyyyzzyyyyyyyyyyzz||€€€€€€€€€€€€€€€€€€��{{vvttrrvvwwvvttssttttvvwwssvvwwwwwwvvzzwwrruuuuuuwwxxzzuuttvvvvvvuuvvvvuussttuussqqpprrttyy||||{{yyyyzzyyyyyyyyyyzz||€€€€€€€€€€€€€€€€€€��{{vvttrrvvwwvvttssttttvvwwssvvwwwwwwvvzzwwrruuuuuuwwxx€€zzuuuuxxwwvvvvvvvvttqqqqqqqqrrssxx}}}}zzxxxxyyyyyyyyyyzzyyyy||€€€€€€€€€€€€€€€€€€€€~~{{xxttrrvvwwuussssttttxxvvrrvvxxwwvvvvyyvvrruuuuuuxx€€zzuuuuxxwwvvvvvvvvttqqqqqqqqrrssxx}}}}zzxxxxyyyyyyyyyyzzyyyy||€€€€€€€€€€€€€€€€€€€€~~{{xxttrrvvwwuussssttttxxvvrrvvxxwwvvvvyyvvrruuuuuuxx~~yyttvvwwwwvvvvvvssppppqqrrttww{{||zzxxyyyyyyyyxxxxyyzzyyyyzz€€€€€€€€€€€€€€€€}}zz{{wwttssvvuuttssttuuvvxxuusswwxxxxwwvvzzuurrttuuvv~~yyttvvwwwwvvvvvvssppppqqrrttww{{||zzxxyyyyyyyyxxxxyyzzyyyyzz€€€€€€€€€€€€€€€€}}zz{{wwttssvvuuttssttuuvvxxuusswwxxxxwwvvzzuurrttuuvv~~xxttwwwwwwuussqqqqrrrruuyy{{{{yyxxyyyyyyxxxxyyyyyyyyyyzzzz}}€€€€€€€€xxxxzzwwttrrttuuuuttttvvuuvvvvssvvxxxxxxwwzzssqqttww~~xxttwwwwwwuussqqqqrrrruuyy{{{{yyxxyyyyyyxxxxyyyyyyyyyyzzzz}}€€€€€€€€xxxxzzwwttrrttuuuuttttvvuuvvvvssvvxxxxxxwwzzssqqttww~~vvuuxxvvssqqppqqssvvzz||zzyyyyyyzzyyxxxxxxyyxxyyyyyyyyyy{{€€€€€€€€{{vvyy{{xxttrruuvvuuuuvvuuttwwuurrwwxxyyxxxxxxrrrrww~~vvuuxxvvssqqppqqssvvzz||zzyyyyyyzzyyxxxxxxyyxxyyyyyyyyyy{{€€€€€€€€{{vvyy{{xxttrruuvvuuuuvvuuttwwuurrwwxxyyxxxxxxrrrrww||uuvvttrrqqssttww{{{{zzxxyyzzzzzzyyyyxxxxxxxxxxyyzzzzxxxx}}€€€€€€€€€€€€wwuuyy||xxttrruuwwvvwwuuttttwwuussxxzzzzyyxxwwrrvv||uuvvttrrqqssttww{{{{zzxxyyzzzzzzyyyyxxxxxxxxxxyyzzzzxxxx}}€€€€€€€€€€€€wwuuyy||xxttrruuwwvvwwuuttttwwuussxxzzzzyyxxwwrrvv}}uurrrrssvvzz||{{yyxxyyxxyyyy{{zzxxxxxxxxxxyyyyxxwwuuuuzz€€€€€€€€€€€€zzvvvvyy{{wwssqquuwwwwvvttttttwwttttyyzzzzwwwwwwtt}}uurrrrssvvzz||{{yyxxyyxxyyyy{{zzxxxxxxxxxxyyyyxxwwuuuuzz€€€€€€€€€€€€zzvvvvyy{{wwssqquuwwwwvvttttttwwttttyyzzzzwwwwwwtt~~rrssww{{}}}}zzxxxxxxyyyyyyzzzzyyxxyyxxxxxxwwuuttttuuyy€€€€€€€€€€€€€€€€€€}}wwvvvvyy{{wwttrruuxxwwvvuuttttxxttttyyzzwwuuwwyy~~rrssww{{}}}}zzxxxxxxyyyyyyzzzzyyxxyyxxxxxxwwuuttttuuyy€€€€€€€€€€€€€€€€€€}}wwvvvvyy{{wwttrruuxxwwvvuuttttxxttttyyzzwwuuwwyy||vv||}}{{zzzzzzyyxxyyxxyyyyyyyyyyxxyywwvvttttttvvvvuu}}��€€€€€€€€€€€€€€€€xxuuvvvvyy{{xxttrrvvxxvvvvttttvvwwrruuyyxxwwuuxx||vv||}}{{zzzzzzyyxxyyxxyyyyyyyyyyxxyywwvvttttttvvvvuu}}��€€€€€€€€€€€€€€€€xxuuvvvvyy{{xxttrrvvxxvvvvttttvvwwrruuyyxxwwuuxx||{{zzzzzzzzyyxxyyyyxxyyyyyyyyxxvvttttuuttuuuuuuxx{{€€��€€€€€€zzvvvvvvwwyy||xxttrruuvvwwuuvvwwvvuussuuxxwwvvww||{{zzzzzzzzyyxxyyyyxxyyyyyyyyxxvvttttuuttuuuuuuxx{{€€��€€€€€€zzvvvvvvwwyy||xxttrruuvvwwuuvvwwvvuussuuxxwwvvww€€{{zzzz{{{{yyyyxxxxxxxxyyxxwwuuttttttuuuuttvvxx{{}}}}€€~~~~}}||zzzzyyyyyyyyzz{{||}}~~€€||uuvvvvwwwwyy{{yyuurruuxxuuvvxxvvttvvssttwwwwyy€€{{zzzz{{{{yyyyxxxxxxxxyyxxwwuuttttttuuuuttvvxx{{}}}}€€~~~~}}||zzzzyyyyyyyyzz{{||}}~~€€||uuvvvvwwwwyy{{yyuurruuxxuuvvxxvvttvvssttwwwwyy||zz{{{{zzyyyyxxwwwwxxvvssssttuuuuuuuuww{{||||||{{{{~~}}||zzxxvvuuttssrrqqqqqqrrqqrrrrssttuuvvyy{{}}}}xxwwvvvvvvvvxx||zzuurruuuuuuxxvvttuuvvssttwwyy||zz{{{{zzyyyyxxwwwwxxvvssssttuuuuuuuuww{{||||||{{{{~~}}||zzxxvvuuttssrrqqqqqqrrqqrrrrssttuuvvyy{{}}}}xxwwvvvvvvvvxx||zzuurruuuuuuxxvvttuuvvssttwwyy{{zz{{zzyyyyyyxxuussssssssttttttwwzz{{||||{{zz{{{{}}€€€€€€~~}}zzwwttrrqqppnnkkkkkkiiiiiiiijjiijjkknnppqqqqqqrrssttuuvvwwwwwwwwwwyy||yyvvrrssvvwwuuttttuuvvssttyy{{zz{{zzyyyyyyxxuussssssssttttttwwzz{{||||{{zz{{{{}}€€€€€€~~}}zzwwttrrqqppnnkkkkkkiiiiiiiijjiijjkknnppqqqqqqrrssttuuvvwwwwwwwwwwyy||yyvvrrssvvwwuuttttuuvvssttyy{{zz{{yyxxwwvvssqqrrttssssttwwyy}}}}{{zz{{{{{{{{{{~~€€~~}}||yyuuqqlljjjjiiiihhffeeggjjkkllnnoooooommkknnmmiihhiillooppqqrrrrssttvvwwwwyy{{yyuussuuyyvvttttttvvwwrrww{{zz{{yyxxwwvvssqqrrttssssttwwyy}}}}{{zz{{{{{{{{{{~~€€~~}}||yyuuqqlljjjjiiiihhffeeggjjkkllnnoooooommkknnmmiihhiillooppqqrrrrssttvvwwwwyy{{yyuussuuyyvvttttttvvwwrrww{{zzyyvvttssssssssttttuuyy{{||||{{zz{{{{{{{{zzzz{{~~€€~~~~}}||zzxxuurrmmffccaabbcceegghhkknnpprrttssssttuuttttttrrrroohhddddffmmrrrrssssssrrrrssvvxx||yyuussvvwwuuuuuuuuvvttss{{zzyyvvttssssssssttttuuyy{{||||{{zz{{{{{{{{zzzz{{~~€€~~~~}}||zzxxuurrmmffccaabbcceegghhkknnpprrttssssttuuttttttrrrroohhddddffmmrrrrssssssrrrrssvvxx||yyuussvvwwuuuuuuuuvvttss~~wwuussssssttssttvvyy{{||}}{{zzzz{{{{{{{{{{{{zzzz{{~~}}{{zzyywwssmmggjjiibb``bbccggkkpprrrrqqqqrrrrqqqqqqqqqqrrssssssttqqkkeeeemmpprrrrssttssssrrssvvzz~~zzuuttuuuuuuvvuuuussvv~~wwuussssssttssttvvyy{{||}}{{zzzz{{{{{{{{{{{{zzzz{{~~}}{{zzyywwssmmggjjiibb``bbccggkkpprrrrqqqqrrrrqqqqqqqqqqrrssssssttqqkkeeeemmpprrrrssttssssrrssvvzz~~zzuuttuuuuuuvvuuuussvv||ttttssttttuuxxzz{{{{{{zzzzzzzzzzzzzzzz{{zzzzzzzz{{{{yyyyvvppjjddaabbhhkkeeeejjnnrrssqqppppqqrrrrqqppqqooppooppqqqqqqqqrrppjjeemmqqqqssssssttttssuuwwyyzz}}yyvvssssttvvvvvvssuu||ttttssttttuuxxzz{{{{{{zzzzzzzzzzzzzzzz{{zzzzzzzz{{{{yyyyvvppjjddaabbhhkkeeeejjnnrrssqqppppqqrrrrqqppqqooppooppqqqqqqqqrrppjjeemmqqqqssssssttttssuuwwyyzz}}yyvvssssttvvvvvvssuu||ttssuuww{{||{{{{zzzz{{zz{{{{zzzzzzzzzz{{zzzzzz{{zzyywwsskkddccaa````hhppnnppttttssrrqqrrrrssuuuuuuuuttssrrqqqqqqppppppooppppjjllqqrrrrssssttttuuvvwwyyxxyy{{yyuussssvvwwxxttvv||ttssuuww{{||{{{{zzzz{{zz{{{{zzzzzzzzzz{{zzzzzz{{zzyywwsskkddccaa````hhppnnppttttssrrqqrrrrssuuuuuuuuttssrrqqqqqqppppppooppppjjllqqrrrrssssttttuuvvwwyyxxyy{{yyuussssvvwwxxttvv||wwzz{{||{{zzzzzz{{{{zzzz{{{{zzzzyyyyzz{{yyzzzzyywwttqqqqiiccbbaabbddmmttssssssssqqqqssttuuwwxxyyyyyywwuuuussrrrrrrrrqqqqppppmmnnqqrrrrrrssttvvvvvvvvxxxxwwyy||yyvvssttwwwwuuvv||wwzz{{||{{zzzzzz{{{{zzzz{{{{zzzzyyyyzz{{yyzzzzyywwttqqqqiiccbbaabbddmmttssssssssqqqqssttuuwwxxyyyyyywwuuuussrrrrrrrrqqqqppppmmnnqqrrrrrrssttvvvvvvvvxxxxwwyy||yyvvssttwwwwuuvv€€||{{{{zz{{zzzzzz{{zz{{zzzzzzzzzzyyzzzzzzyyyyyywwrroossooeecccccciippttttssssssrrssttvvvvvvvvwwxxyyyyxxxxvvttttssrrsssssssssspppprrrrrrssssuuwwvvwwvvxxxxxxxxyy||yyvvssttxxvvww€€||{{{{zz{{zzzzzz{{zz{{zzzzzzzzzzyyzzzzzzyyyyyywwrroossooeecccccciippttttssssssrrssttvvvvvvvvwwxxyyyyxxxxvvttttssrrsssssssssspppprrrrrrssssuuwwvvwwvvxxxxxxxxyy||yyvvssttxxvvww€€{{zz{{zz{{zzyyyy{{{{{{zzyyzzzzzzzzyyyyzzyyxxxxssnnoommffddddggmmuuvvuuttssssrrssttvvvvuuttttttuuvvwwxxxxwwvvuuttttttttttvvvvttrrrrssvvwwwwttttvvwwvvwwwwxxxxxxyy||zzuurrttuuww€€{{zz{{zz{{zzyyyy{{{{{{zzyyzzzzzzzzyyyyzzyyxxxxssnnoommffddddggmmuuvvuuttssssrrssttvvvvuuttttttuuvvwwxxxxwwvvuuttttttttttvvvvttrrrrssvvwwwwttttvvwwvvwwwwxxxxxxyy||zzuurrttuuww€€||zzzzzzzzzzyyzzzzzzyyyyyyyyzzzzzzyyyyzzyyxxuuppppppkkeeffjjqqwwyyvvttttssssssttvvvvwwuuttttttuuuuwwxxxxwwvvuuttuuttttvvvvwwwwssssvvzz{{yywwuuuuwwvvvvwwwwxxwwwwxxzzyyttrrssww€€||zzzzzzzzzzyyzzzzzzyyyyyyyyzzzzzzyyyyzzyyxxuuppppppkkeeffjjqqwwyyvvttttssssssttvvvvwwuuttttttuuuuwwxxxxwwvvuuttuuttttvvvvwwwwssssvvzz{{yywwuuuuwwvvvvwwwwxxwwwwxxzzyyttrrssww||zzzzzzzzzzzzzzyyzzyyyyyyyyyyyyzzyyxxxxwwuuttpprrrrmmggllrrwwzzxxttttttttssttuuvvwwvvuuuuuuuuwwvvuuwwxxvvvvuuttuuttttuuuuvvwwuuvvxxxxzz||zzwwuuttvvvvvvvvxxwwwwwwwwzzyyuuqquu||zzzzzzzzzzzzzzyyzzyyyyyyyyyyyyzzyyxxxxwwuuttpprrrrmmggllrrwwzzxxttttttttssttuuvvwwvvuuuuuuuuwwvvuuwwxxvvvvuuttuuttttuuuuvvwwuuvvxxxxzz||zzwwuuttvvvvvvvvxxwwwwwwwwzzyyuuqquu{{yyzzzzzzzzzzyyxxyyyyzzzzyyyyxxwwvvuuuuvvvvvvrrrrttppkkppvvyyyywwttuuttttuuuuuuuuuuttuuvvwwvvwwxxwwxxyywwuuuuvvvvuuttttttttuuuuwwvvwwxxzz{{yywwuuttuuvvwwwwwwwwwwwwwwzzyytttt{{yyzzzzzzzzzzyyxxyyyyzzzzyyyyxxwwvvuuuuvvvvvvrrrrttppkkppvvyyyywwttuuttttuuuuuuuuuuttuuvvwwvvwwxxwwxxyywwuuuuvvvvuuttttttttuuuuwwvvwwxxzz{{yywwuuttuuvvwwwwwwwwwwwwwwzzyytttt{{zzzzzzxxyyzzyyyyyyyyyyxxwwuuuuuuvvwwwwwwvvvvuussttttqqrrxxzzzzwwttuuttuuuuuuuuttttuuuuvvwwxxyy{{zzyyyy{{yyvvuuttvvvvvvuussssttwwwwvvxxxxxx{{zzwwuussvvwwwwwwwwwwwwwwwwyyxxvv{{zzzzzzxxyyzzyyyyyyyyyyxxwwuuuuuuvvwwwwwwvvvvuussttttqqrrxxzzzzwwttuuttuuuuuuuuttttuuuuvvwwxxyy{{zzyyyy{{yyvvuuttvvvvvvuussssttwwwwvvxxxxxx{{zzwwuussvvwwwwwwwwwwwwwwwwyyxxvv{{zzzzzzyyyyyyyyxxvvuuuuuuuuvvwwwwwwvvvvvvvvvvvvvvssssssvvyyyyzzwwuuuuuuuuuuttttssttuuuuxxxxzz{{||{{wwxxzzxxvvttttwwxxxxxxvvrrssvvwwwwwwwwwwxxzzzzwwuussuuwwvvvvvvwwwwvvvvxxyy{{zzzzzzyyyyyyyyxxvvuuuuuuuuvvwwwwwwvvvvvvvvvvvvvvssssssvvyyyyzzwwuuuuuuuuuuttttssttuuuuxxxxzz{{||{{wwxxzzxxvvttttwwxxxxxxvvrrssvvwwwwwwwwwwxxzzzzwwuussuuwwvvvvvvwwwwvvvvxxyy{{yyyyyyxxxxvvuuttuuuuwwwwwwvvvvvvwwwwvvwwwwvvvvvvttttuuuuwwyyxxvvuuttuuttuuttssssttttuuvvvvvvuuvvxxwwxxxxuuttttttvvyyyyzzyyttttxxwwvvwwxxwwwwwwzzzzwwvvssvvwwwwwwwwxxwwvvvvyy{{yyyyyyxxxxvvuuttuuuuwwwwwwvvvvvvwwwwvvwwwwvvvvvvttttuuuuwwyyxxvvuuttuuttuuttssssttttuuvvvvvvuuvvxxwwxxxxuuttttttvvyyyyzzyyttttxxwwvvwwxxwwwwwwzzzzwwvvssvvwwwwwwwwxxwwvvvvyyzzxxxxvvuuuuuuuuuuwwvvuuuuvvvvvvwwwwwwvvwwwwwwwwvvvvrrrrssttuuuuuuvvuuuuttttssssrrrrssttssssssttuuwwxxwwvvssssttttttxxwwzzzzuuuuwwwwwwwwxxwwwwwwxxzzzzxxvvttttvvwwxxxxwwwwvvxxzzxxxxvvuuuuuuuuuuwwvvuuuuvvvvvvwwwwwwvvwwwwwwwwvvvvrrrrssttuuuuuuvvuuuuttttssssrrrrssttssssssttuuwwxxwwvvssssttttttxxwwzzzzuuuuwwwwwwwwxxwwwwwwxxzzzzxxvvttttvvwwxxxxwwwwvvxx~~vvuuvvuuvvuuvvvvuuvvvvvvvvwwvvvvvvwwwwwwwwwwwwvvwwuuqqssssssttttvvwwvvttttssrrrrqqrrrrrrrrssssttttttuuuuttttuuttttttxxvvuuxxuuuuwwwwwwwwwwwwwwwwxxwwxxzzyywwttttwwwwwwwwwwvvxx~~vvuuvvuuvvuuvvvvuuvvvvvvvvwwvvvvvvwwwwwwwwwwwwvvwwuuqqssssssttttvvwwvvttttssrrrrqqrrrrrrrrssssttttttuuuuttttuuttttttxxvvuuxxuuuuwwwwwwwwwwwwwwwwxxwwxxzzyywwttttwwwwwwwwwwvvxx}}vvvvvvuuwwwwyyzzxxvvvvwwvvwwvvvvwwvvwwwwwwwwvvvvxxuuqqssrrssssssvvwwuuttttttssrrrrqqqqrrrrssssssssttttttttttuuttttuuwwzzwwwwuuuuwwwwwwwwwwwwwwvvwwxxwwww{{zzvvttttvvwwwwwwvvxx}}vvvvvvuuwwwwyyzzxxvvvvwwvvwwvvvvwwvvwwwwwwwwvvvvxxuuqqssrrssssssvvwwuuttttttssrrrrqqqqrrrrssssssssttttttttttuuttttuuwwzzwwwwuuuuwwwwwwwwwwwwwwvvwwxxwwww{{zzvvttttvvwwwwwwvvxx}}vvwwyyzz{{zzzzyyyyxxuuxxyyyyyywwvvvvvvvvvvvvvvvvwwuuppssssppqqssuuvvuussssttssrrqqqqrrrrrrssssssttttttttttttuuttttttuuxxxxxxvvvvwwwwwwwwwwwwwwvvwwwwvvvvyy||yywwttttuuvvwwwwyy}}vvwwyyzz{{zzzzyyyyxxuuxxyyyyyywwvvvvvvvvvvvvvvvvwwuuppssssppqqssuuvvuussssttssrrqqqqrrrrrrssssssttttttttttttuuttttttuuxxxxxxvvvvwwwwwwwwwwwwwwvvwwwwvvvvyy||yywwttttuuvvwwwwyy~~||}}}}||{{zzyyxxxxwwvvvvyy{{{{yyxxwwwwvvvvvvvvvvvvvvqqssqqoossttuuvvuuttttssrrqqppppqqrrrrssssssttttttssttvvvvttttrrrruuxxwwxxuuuuwwvvvvwwwwwwvvwwwwwwvvwwyy{{zzwwttssttvvvvyy~~||}}}}||{{zzyyxxxxwwvvvvyy{{{{yyxxwwwwvvvvvvvvvvvvvvqqssqqoossttuuvvuuttttssrrqqppppqqrrrrssssssttttttssttvvvvttttrrrruuxxwwxxuuuuwwvvvvwwwwwwvvwwwwwwvvwwyy{{zzwwttssttvvvvyy}}||{{{{{{zzxxwwxxwwwwvvuuxx{{||{{yyyyvvvvwwvvuuvvvvssrrppppuussttvvwwuuuurrppoonnnnooqqrrssttttttssssttuuvvuuttrrppoosswwvvyyxxttuuvvvvwwwwvvvvwwwwwwwwwwyyyyzzzzwwttrrttuuxx}}||{{{{{{zzxxwwxxwwwwvvuuxx{{||{{yyyyvvvvwwvvuuvvvvssrrppppuussttvvwwuuuurrppoonnnnooqqrrssttttttssssttuuvvuuttrrppoosswwvvyyxxttuuvvvvwwwwvvvvwwwwwwwwwwyyyyzzzzwwttrrttuuxx}}||zzzzzzzzwwxxwwvvvvvvttuuyyyy{{||{{xxwwwwwwuuuuuussssrrrrttrrssvvwwvvuurrppppoooonnooqqrrttttttssssuuvvttttssqqppoorrvvuuxxyywwttttvvwwvvvvvvwwwwvvwwwwyyxxwwzzzzwwttrrssww}}||zzzzzzzzwwxxwwvvvvvvttuuyyyy{{||{{xxwwwwwwuuuuuussssrrrrttrrssvvwwvvuurrppppoooonnooqqrrttttttssssuuvvttttssqqppoorrvvuuxxyywwttttvvwwvvvvvvwwwwvvwwwwyyxxwwzzzzwwttrrssww}}{{zzzz{{yywwyywwuuuuttttuuxxyyxxyy||||yywwwwvvvvuuttttrrssssrrssuuwwvvvvttrrqqqqpppppppprrssssssssssvvuuuuuussssrrqqrruuttwwwwwwwwuuttuuvvvvvvwwwwvvwwxxxxxxwwxxyyyyvvttqquu}}{{zzzz{{yywwyywwuuuuttttuuxxyyxxyy||||yywwwwvvvvuuttttrrssssrrssuuwwvvvvttrrqqqqpppppppprrssssssssssvvuuuuuussssrrqqrruuttwwwwwwwwuuttuuvvvvvvwwwwvvwwxxxxxxwwxxyyyyvvttqquu||zz{{{{zzwwxxxxwwwwuuuuvvvvvvyyyyyyyy{{}}||zzxxwwwwuuuussttssrrssuuwwvvvvvvttttssrrrrrrrrrrssssssrrttvvuuvvyyxxvvuuttuuuusswwwwttvvxxvvttuuuuvvwwwwvvwwwwxxwwwwwwxxwwyywwtttt||zz{{{{zzwwxxxxwwwwuuuuvvvvvvyyyyyyyy{{}}||zzxxwwwwuuuussttssrrssuuwwvvvvvvttttssrrrrrrrrrrssssssrrttvvuuvvyyxxvvuuttuuuusswwwwttvvxxvvttuuuuvvwwwwvvwwwwxxwwwwwwxxwwyywwtttt||{{||||zzxxxxwwwwwwwwwwvvvvttxxzzyyyyyyyy{{||{{zzxxvvuuuuuussqqrruuwwwwvvvvvvuuttssssssssssssssssssuuvvvvwwwwwwuuttvvxxuurrvvwwuuttuuxxwwttttvvvvvvvvvvwwwwvvwwwwwwwwwwxxwwvv||{{||||zzxxxxwwwwwwwwwwvvvvttxxzzyyyyyyyy{{||{{zzxxvvuuuuuussqqrruuwwwwvvvvvvuuttssssssssssssssssssuuvvvvwwwwwwuuttvvxxuurrvvwwuuttuuxxwwttttvvvvvvvvvvwwwwvvwwwwwwwwwwxxwwvv€€}}||}}||yyxxyyyyxxwwwwwwwwvvuuvvyyzzyyxxxxxxyy||{{yyvvuuvvvvttrrqqsswwwwvvuuuuvvttssttttssssssssttttuuvvvvvvwwwwttuuyy{{wwttvvvvuuuuttuuvvxxuuttuuvvvvvvvvvvvvwwwwwwwwwwvvxxyy€€}}||}}||yyxxyyyyxxwwwwwwwwvvuuvvyyzzyyxxxxxxyy||{{yyvvuuvvvvttrrqqsswwwwvvuuuuvvttssttttssssssssttttuuvvvvvvwwwwttuuyy{{wwttvvvvuuuuttuuvvxxuuttuuvvvvvvvvvvvvwwwwwwwwwwvvxxyy€€~~}}}}||yyyyyyyyyywwwwvvvvvvvvuuwwzzzzyyyyxxwwxxvvvvuuuuttvvuusspprrvvvvvvttttuuttssttssssrrqqrrttuuuuuuuuuuvvvvuuvvyyzzyyvvvvvvuuuuttttttwwxxvvttttuuuuuuvvvvwwwwwwvvvvvvuuxx€€~~}}}}||yyyyyyyyyywwwwvvvvvvvvuuwwzzzzyyyyxxwwxxvvvvuuuuttvvuusspprrvvvvvvttttuuttssttssssrrqqrrttuuuuuuuuuuvvvvuuvvyyzzyyvvvvvvuuuuttttttwwxxvvttttuuuuuuvvvvwwwwwwvvvvvvuuxx}}||||{{yyzzzzyyxxwwwwwwvvuuvvvvvvyyzzzzyyxxxxxxuussssttuuxxxxssqqssvvvvvvuuttttttssssrrqqppppqqssttuuuuttttttuuwwwwxxyyxxxxxxvvuuttuuttttttuuwwwwuuuuvvvvvvuuvvwwvvvvvvvvuuww}}||||{{yyzzzzyyxxwwwwwwvvuuvvvvvvyyzzzzyyxxxxxxuussssttuuxxxxssqqssvvvvvvuuttttttssssrrqqppppqqssttuuuuttttttuuwwwwxxyyxxxxxxvvuuttuuttttttuuwwwwuuuuvvvvvvuuvvwwvvvvvvvvuuww}}||{{zzyyzzzzyyxxwwwwwwvvvvvvwwuuwwyyzzzzzzxxxxuuttrrrruuxx{{uurrssttuuvvuussttttssssppooqqrrssttuuuuttssssttuuwwwwwwwwwwxxwwuuuuuuuuttttttuuttvvwwuuttuuvvuuvvvvvvuuuuuuttxx}}||{{zzyyzzzzyyxxwwwwwwvvvvvvwwuuwwyyzzzzzzxxxxuuttrrrruuxx{{uurrssttuuvvuussttttssssppooqqrrssttuuuuttssssttuuwwwwwwwwwwxxwwuuuuuuuuttttttuuttvvwwuuttuuvvuuvvvvvvuuuuuuttxx€€}}||zzyy{{{{zzzzyyxxwwwwwwwwvvvvvvvvxxzzzzzzyywwttssssrruuzz}}vvssssuuvvuuuussrrrrssrroonnqqssttuuvvvvvvvvvvvvwwxxyyyyvvvvxxwwuuuuuuuuuuuuuuttttttuuwwvvuuuuuuvvvvvvuuttttttxx€€}}||zzyy{{{{zzzzyyxxwwwwwwwwvvvvvvvvxxzzzzzzyywwttssssrruuzz}}vvssssuuvvuuuussrrrrssrroonnqqssttuuvvvvvvvvvvvvwwxxyyyyvvvvxxwwuuuuuuuuuuuuuuttttttuuwwvvuuuuuuvvvvvvuuttttttxx€€}}||zzzz{{{{zzzzzzxxxxxxwwvvvvwwvvvvwwzz{{zzyywwvvssssssuuzz||xxssssttvvuuvvttrrqqrrqqnnnnppssttuuxxxxwwwwxxwwwwxxzz}}wwssxxwwuuuuuuuuuuttuuttttttttttwwwwttttuuvvvvuuttttttww€€}}||zzzz{{{{zzzzzzxxxxxxwwvvvvwwvvvvwwzz{{zzyywwvvssssssuuzz||xxssssttvvuuvvttrrqqrrqqnnnnppssttuuxxxxwwwwxxwwwwxxzz}}wwssxxwwuuuuuuuuuuttuuttttttttttwwwwttttuuvvvvuuttttttww€€||{{yy{{{{{{zzyyzzxxxxxxxxwwwwwwvvvvuuxx{{yyyyxxwwuuttttvvxxzzzzuussssuuuuvvvvssqqrrrroommppssssrrssuuvvvvwwwwvvww{{}}wwsswwwwttttuuuuuuuuuuuuuuttttssssvvwwuussuuvvuuuuttttww€€||{{yy{{{{{{zzyyzzxxxxxxxxwwwwwwvvvvuuxx{{yyyyxxwwuuttttvvxxzzzzuussssuuuuvvvvssqqrrrroommppssssrrssuuvvvvwwwwvvww{{}}wwsswwwwttttuuuuuuuuuuuuuuttttssssvvwwuussuuvvuuuuttttww€€}}{{zz{{{{{{zzzzyyyyxxxxxxwwwwvvvvvvvvvvxxyyyyxxwwvvttttvvvvxxzzwwrrssttuuvvwwuuqqrrrrqqooppqqpppppprrssuuvvvvvvvvxxzzwwssvvuussttttuuuuuuuuuuttttttssssttuuvvuuttuuuuttssssww€€}}{{zz{{{{{{zzzzyyyyxxxxxxwwwwvvvvvvvvvvxxyyyyxxwwvvttttvvvvxxzzwwrrssttuuvvwwuuqqrrrrqqooppqqpppppprrssuuvvvvvvvvxxzzwwssvvuussttttuuuuuuuuuuttttttssssttuuvvuuttuuuuttssssww}}zzzz{{{{{{zzzzyyyyxxxxxxxxxxwwvvwwvvvvwwzzyyxxwwwwvvvvvvttzz}}xxttssssttuuvvvvttqqrrqqppoonnnnppqqrrssuuttuuttttuuwwwwuuuuttssssssttttttttuuuuuuttttttttttttvvvvuuttssssssvv}}zzzz{{{{{{zzzzyyyyxxxxxxxxxxwwvvwwvvvvwwzzyyxxwwwwvvvvvvttzz}}xxttssssttuuvvvvttqqrrqqppoonnnnppqqrrssuuttuuttttuuwwwwuuuuttssssssttttttttuuuuuuttttttttttttvvvvuuttssssssvv€€||zz||||zz{{{{zzyyyyyyyyxxxxxxxxvvwwwwvvvvyyzzyyxxwwvvuuuuss~~„„zzttrrssssuuuuvvuurrrrrrppmmllllnnqqssttuuuuuuuuuuuuvvvvvvuussssssssssssttttttuuttuuttttttuussrruuvvuussssssvv€€||zz||||zz{{{{zzyyyyyyyyxxxxxxxxvvwwwwvvvvyyzzyyxxwwvvuuuuss~~„„zzttrrssssuuuuvvuurrrrrrppmmllllnnqqssttuuuuuuuuuuuuvvvvvvuussssssssssssttttttuuttuuttttttuussrruuvvuussssssvv{{zz}}||{{{{{{zzyyyyyyxxxxxxxxxxwwwwwwvvuuwwzzzzzzyywwvvvvuu~~††~~ttqqqqrrssttuuuussrrssrrookkllmmnnppssssttssuuuuuuuuuuvvuussssttttssssttttttttssttttuuttttttssssttvvuussrrvv{{zz}}||{{{{{{zzyyyyyyxxxxxxxxxxwwwwwwvvuuwwzzzzzzyywwvvvvuu~~††~~ttqqqqrrssttuuuussrrssrrookkllmmnnppssssttssuuuuuuuuuuvvuussssttttssssttttttttssttttuuttttttssssttvvuussrrvv€€{{zz||||{{zzzzyyxxyyyyxxxxxxxxxxxxwwwwwwvvvvyyzzzzzzyyyyyyvv~~‡‡„„vvppppqqrrssttttssrrrrrrppnnmmllllmmnnoooopprrssssuuuuvvuuttttttttssttssssssssssssssttttttttssssssttuuuurrtt€€{{zz||||{{zzzzyyxxyyyyxxxxxxxxxxxxwwwwwwvvvvyyzzzzzzyyyyyyvv~~‡‡„„vvppppqqrrssttttssrrrrrrppnnmmllllmmnnoooopprrssssuuuuvvuuttttttttssttssssssssssssssttttttttssssssttuuuurrtt{{{{}}||{{zz{{zzxxyyyyxxxxxxxxxxxxxxxxwwvvwwwwzz{{{{zzzzzzxx~~……ˆˆ}}ppppqqppqqssttssrrqqrrrrqqoonnmmllllllmmppqqrrrruuvvvvuuuuttttttssttssssrrssrrssrrrrttttttssttttssrrssuutt{{{{}}||{{zz{{zzxxyyyyxxxxxxxxxxxxxxxxwwvvwwwwzz{{{{zzzzzzxx~~……ˆˆ}}ppppqqppqqssttssrrqqrrrrqqoonnmmllllllmmppqqrrrruuvvvvuuuuttttttssttssssrrssrrssrrrrttttttssttttssrrssuutt||||}}{{zzzzyyzzzzyyxxyyxxxxxxxxxxxxxxxxwwwwvvxx{{{{{{{{{{yy}}„„ˆˆ‡‡vvooqqppoorrssssssqqrrssrrqqppnnmmmmmmmmppqqqqrrttvvvvttttttttttttssssrrssssrrssssssssssttttttttrrqqrrrrss||||}}{{zzzzyyzzzzyyxxyyxxxxxxxxxxxxxxxxwwwwvvxx{{{{{{{{{{yy}}„„ˆˆ‡‡vvooqqppoorrssssssqqrrssrrqqppnnmmmmmmmmppqqqqrrttvvvvttttttttttttssssrrssssrrssssssssssttttttttrrqqrrrrss||||||zzzzzzzzzzzzyyyyyyxxxxxxxxyyxxxxwwwwwwwwwwzz||||||||zz{{ƒƒ‡‡ŠŠ‚‚rrnnppppqqrrrrssrrrrttssssrrqqppoooooopprrrrrruuwwuuttttttttuuuuttssssttssssssssssssssssuuttttssrrqqqqpp||||||zzzzzzzzzzzzyyyyyyxxxxxxxxyyxxxxwwwwwwwwwwzz||||||||zz{{ƒƒ‡‡ŠŠ‚‚rrnnppppqqrrrrssrrrrttssssrrqqppoooooopprrrrrruuwwuuttttttttuuuuttssssttssssssssssssssssuuttttssrrqqqqpp€€}}||{{zz{{zzzzzzzzyyyyyyxxxxxxxxyyxxwwxxxxxxwwvvxx{{||||||{{{{‚‚ˆˆ‰‰ŠŠ€€ttppppqqqqrrrrrrrrssssssrrrrqqqqppppqqrrssttvvwwuussttssssttssssttssssttssssssssssssrrssssssssrrrrqqqq€€}}||{{zz{{zzzzzzzzyyyyyyxxxxxxxxyyxxwwxxxxxxwwvvxx{{||||||{{{{‚‚ˆˆ‰‰ŠŠ€€ttppppqqqqrrrrrrrrssssssrrrrqqqqppppqqrrssttvvwwuussttssssttssssttssssttssssssssssssrrssssssssrrrrqqqq€€}}}}{{{{{{zzzzzzyyyyyyyyyyyyxxyyxxxxxxxxxxxxwwvvwwzz||||||||||��‡‡ˆˆŠŠŠŠ‡‡zzppqqqqrrssssrrssssssssssrrrrrrrrssssttuuvvvvttssttttttttssssssssssssssssssssssssssssssssttssrrqqrr€€}}}}{{{{{{zzzzzzyyyyyyyyyyyyxxyyxxxxxxxxxxxxwwvvwwzz||||||||||��‡‡ˆˆŠŠŠŠ‡‡zzppqqqqrrssssrrssssssssssrrrrrrrrssssttuuvvvvttssttttttttssssssssssssssssssssssssssssssssttssrrqqrr€€}}||{{{{{{zzyyzzzzyyyyyyyyyyyyyyxxxxwwxxxxxxwwvvwwxx{{}}||}}��……‡‡‰‰ŠŠŠŠ……ssooqqrrttttssrrssssssssssrrssssssttuuuuzzyyuuttuuuuuuttttttttssssrrssssssssssrrrrrrssssssrrrrqqrr€€}}||{{{{{{zzyyzzzzyyyyyyyyyyyyyyxxxxwwxxxxxxwwvvwwxx{{}}||}}��……‡‡‰‰ŠŠŠŠ……ssooqqrrttttssrrssssssssssrrssssssttuuuuzzyyuuttuuuuuuttttttttssssrrssssssssssrrrrrrssssssrrrrqqrr€€}}{{{{{{zzzzzzzzzzyyyyxxxxyyyyyyxxxxwwxxxxxxxxwwwwwwyy||||}}‚‚‚‚€€……ˆˆ‰‰ŠŠŠŠ~~oopprrssuuttssssssssssssssssssssttuuyyƒƒ„„{{vvvvvvuuttttssttssttssssrrssssssrrrrssrrrrrrrrqqqqrr€€}}{{{{{{zzzzzzzzzzyyyyxxxxyyyyyyxxxxwwxxxxxxxxwwwwwwyy||||}}‚‚‚‚€€……ˆˆ‰‰ŠŠŠŠ~~oopprrssuuttssssssssssssssssssssttuuyyƒƒ„„{{vvvvvvuuttttssttssttssssrrssssssrrrrssrrrrrrrrqqqqrr€€}}zz{{{{{{zzzzzzzzzzzzyyyyyyyyyyxxxxxxxxxxxxxxxxwwvvwwzz||„„„„ƒƒ��„„‰‰ŠŠŠŠ‰‰zznnqqssttttttttssttssssttssttuuttxx‚‚‡‡‡‡wwvvvvuuuuttttttttssssrrrrrrssssssssssssrrrrqqqqrr€€}}zz{{{{{{zzzzzzzzzzzzyyyyyyyyyyxxxxxxxxxxxxxxxxwwvvwwzz||„„„„ƒƒ��„„‰‰ŠŠŠŠ‰‰zznnqqssttttttttssttssssttssttuuttxx‚‚‡‡‡‡wwvvvvuuuuttttttttssssrrrrrrssssssssssssrrrrqqqqrr€€}}zzzz{{{{zzzzyyzzyyyyyyxxxxxxxxyyxxxxxxxxwwwwwwwwvvvvzz€€‚‚‚‚„„ƒƒƒƒ‚‚ƒƒ‡‡ŠŠ‹‹††uummssttrrssttssttttttttttuuttww{{~~€€……‰‰ˆˆwwuuuuuuttttttssrrssssrrrrssssrrssrrrrqqqqqqpprr€€}}zzzz{{{{zzzzyyzzyyyyyyxxxxxxxxyyxxxxxxxxwwwwwwwwvvvvzz€€‚‚‚‚„„ƒƒƒƒ‚‚ƒƒ‡‡ŠŠ‹‹††uummssttrrssttssttttttttttuuttww{{~~€€……‰‰ˆˆwwuuuuuuttttttssrrssssrrrrssssrrssrrrrqqqqqqpprr||zzzz{{zzzzzzzzzzyyyyyyxxxxxxxxyyyyxxxxwwwwxxwwwwvvvv}}ƒƒ„„ƒƒƒƒƒƒƒƒ„„„„‚‚……‰‰‹‹‚‚rrnnssssssssssssuuuuuuttttuuwwzz}}€€……‰‰‹‹‡‡wwrruuvvttttttssssttssrrssrrrrqqrrqqqqrrqqrrqqqq||zzzz{{zzzzzzzzzzyyyyyyxxxxxxxxyyyyxxxxwwwwxxwwwwvvvv}}ƒƒ„„ƒƒƒƒƒƒƒƒ„„„„‚‚……‰‰‹‹‚‚rrnnssssssssssssuuuuuuttttuuwwzz}}€€……‰‰‹‹‡‡wwrruuvvttttttssssttssrrssrrrrqqrrqqqqrrqqrrqqqq€€}}{{||{{zzzzzzzzzzyyyyzzyyyyyyxxyyyyyyyywwxxyyxxxx{{}}��„„„„……„„„„„„„„††„„‚‚ƒƒ‡‡ŠŠ~~nnqqssttrrrrssttttttttttuuww{{}}††ˆˆˆˆ‰‰}}ssssttuuuuuussssssttttttssrrrrrrrrrrrrppqquuuu€€}}{{||{{zzzzzzzzzzyyyyzzyyyyyyxxyyyyyyyywwxxyyxxxx{{}}��„„„„……„„„„„„„„††„„‚‚ƒƒ‡‡ŠŠ~~nnqqssttrrrrssttttttttttuuww{{}}††ˆˆˆˆ‰‰}}ssssttuuuuuussssssttttttssrrrrrrrrrrrrppqquuuu€€}}{{{{{{zzzzyyyyyyyyyyyyyyzzzzxxxxxxxxzzzz||~~~~‚‚‚‚ƒƒ„„„„„„„„……„„„„††…………ƒƒ‚‚ƒƒ€€rrooqqssssssttttssssssttuuww{{~~€€……††††ˆˆ~~vvuuqqttuuuuttssttttssssssrrssttssnnffjjzzƒƒ��€€}}{{{{{{zzzzyyyyyyyyyyyyyyzzzzxxxxxxxxzzzz||~~~~‚‚‚‚ƒƒ„„„„„„„„……„„„„††…………ƒƒ‚‚ƒƒ€€rrooqqssssssttttssssssttuuww{{~~€€……††††ˆˆ~~vvuuqqttuuuuttssttttssssssrrssttssnnffjjzzƒƒ��€€||{{||{{zzzzyyxxyyyyyyxxyyyyxxwwxxyy{{��‚‚‚‚‚‚ƒƒƒƒ��‚‚ƒƒ„„„„„„ƒƒ„„………………„„…………‚‚��}}xxttrrrrttssssssssssuuvvvvzz}}~~ƒƒ……ˆˆ††zzuuuuuuvvttuuwwuuuussssnneejjrr~~pp[[\\oowwrrpp€€||{{||{{zzzzyyxxyyyyyyxxyyyyxxwwxxyy{{��‚‚‚‚‚‚ƒƒƒƒ��‚‚ƒƒ„„„„„„ƒƒ„„………………„„…………‚‚��}}xxttrrrrttssssssssssuuvvvvzz}}~~ƒƒ……ˆˆ††zzuuuuuuvvttuuwwuuuussssnneejjrr~~pp[[\\oowwrrpp€€||||||{{zzzzyyxxxxyyxxxxyyyyyy{{}}��‚‚ƒƒ‚‚‚‚‚‚‚‚„„ƒƒ��€€ƒƒ„„……††„„ƒƒ„„ƒƒ„„„„„„……ƒƒ‚‚��ƒƒ��zzttssttuuuuuuuuvvwwxxyy}}ƒƒ‰‰‰‰ƒƒyyvvvvvvwwxxww{{{{yyttuujjZZhhrrppbbZZ]]__]][[^^€€||||||{{zzzzyyxxxxyyxxxxyyyyyy{{}}��‚‚ƒƒ‚‚‚‚‚‚‚‚„„ƒƒ��€€ƒƒ„„……††„„ƒƒ„„ƒƒ„„„„„„……ƒƒ‚‚��ƒƒ��zzttssttuuuuuuuuvvwwxxyy}}ƒƒ‰‰‰‰ƒƒyyvvvvvvwwxxww{{{{yyttuujjZZhhrrppbbZZ]]__]][[^^xxjjmmhhiikkjjhhggmmkkvv„„‚‚‚‚‚‚ƒƒƒƒ……††……………………„„„„„„„„„„ƒƒƒƒ„„„„„„„„††††…………„„……„„ƒƒƒƒƒƒƒƒ„„„„„„ƒƒƒƒ„„„„„„„„……„„ƒƒƒƒ„„„„„„……††……„„„„„„ƒƒ„„……………………………………††……††„„„„††††ˆˆ‡‡ƒƒ„„xxjjmmhhiikkjjhhggmmkkvv„„‚‚‚‚‚‚ƒƒƒƒ……††……………………„„„„„„„„„„ƒƒƒƒ„„„„„„„„††††…………„„……„„ƒƒƒƒƒƒƒƒ„„„„„„ƒƒƒƒ„„„„„„„„……„„ƒƒƒƒ„„„„„„……††……„„„„„„ƒƒ„„……………………………………††……††„„„„††††ˆˆ‡‡ƒƒ„„ssPPQQNNQQKKMMNNOOSSUUgg††‚‚ƒƒƒƒ„„…………………………………………„„„„„„„„„„„„„„………………††………………„„ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒ„„„„ƒƒ„„…………„„„„ƒƒ„„………………††……„„„„ƒƒƒƒ„„…………††…………††††††††††…………††††ˆˆ††ƒƒssPPQQNNQQKKMMNNOOSSUUgg††‚‚ƒƒƒƒ„„…………………………………………„„„„„„„„„„„„„„………………††………………„„ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒ„„„„ƒƒ„„…………„„„„ƒƒ„„………………††……„„„„ƒƒƒƒ„„…………††…………††††††††††…………††††ˆˆ††ƒƒ}}ssttssttttuussttvvuuyy„„ƒƒ„„…………„„„„„„…………„„„„„„„„„„ƒƒƒƒ„„……………………………………„„ƒƒƒƒƒƒ‚‚‚‚‚‚‚‚‚‚��€€��€€€€‚‚‚‚ƒƒƒƒ„„……„„„„ƒƒƒƒ„„………………††……„„„„ƒƒ„„„„…………††……††……††††……††„„…………‡‡ˆˆ……}}ssttssttttuussttvvuuyy„„ƒƒ„„…………„„„„„„…………„„„„„„„„„„ƒƒƒƒ„„……………………………………„„ƒƒƒƒƒƒ‚‚‚‚‚‚‚‚‚‚��€€��€€€€‚‚‚‚ƒƒƒƒ„„……„„„„ƒƒƒƒ„„………………††……„„„„ƒƒ„„„„…………††……††……††††……††„„…………‡‡ˆˆ……��ƒƒƒƒ‚‚‚‚‚‚‚‚ƒƒƒƒ‚‚ƒƒ„„……………………„„ƒƒ„„„„„„„„ƒƒƒƒ„„„„„„„„……††……………………„„„„ƒƒƒƒƒƒƒƒƒƒ‚‚‚‚������€€€€��€€€€€€€€������ƒƒ„„„„„„ƒƒ„„„„„„„„……………………„„„„„„…………††………………††††……††……„„……††ˆˆˆˆ��ƒƒƒƒ‚‚‚‚‚‚‚‚ƒƒƒƒ‚‚ƒƒ„„……………………„„ƒƒ„„„„„„„„ƒƒƒƒ„„„„„„„„……††……………………„„„„ƒƒƒƒƒƒƒƒƒƒ‚‚‚‚������€€€€��€€€€€€€€������ƒƒ„„„„„„ƒƒ„„„„„„„„……………………„„„„„„…………††………………††††……††……„„……††ˆˆˆˆ��‚‚‚‚‚‚‚‚ƒƒƒƒ„„ƒƒ„„………………††††„„ƒƒƒƒƒƒ„„„„„„„„„„„„…………††…………………………„„„„„„ƒƒƒƒƒƒ‚‚������������������€€€€��€€��€€��€€‚‚„„……ƒƒ„„„„„„……„„……††……„„……„„„„………………††……††††††……††……„„††‡‡††��‚‚‚‚‚‚‚‚ƒƒƒƒ„„ƒƒ„„………………††††„„ƒƒƒƒƒƒ„„„„„„„„„„„„…………††…………………………„„„„„„ƒƒƒƒƒƒ‚‚������������������€€€€��€€��€€��€€‚‚„„……ƒƒ„„„„„„……„„……††……„„……„„„„………………††……††††††……††……„„††‡‡††€€����‚‚ƒƒ„„………………………………„„††……ƒƒƒƒƒƒ„„……„„„„……………………………………„„„„„„„„ƒƒƒƒƒƒ‚‚‚‚��€€��€€€€������������������€€������€€€€‚‚„„„„„„„„„„„„…………††††…………„„„„„„…………††††††††††…………††…………††††€€����‚‚ƒƒ„„………………………………„„††……ƒƒƒƒƒƒ„„……„„„„……………………………………„„„„„„„„ƒƒƒƒƒƒ‚‚‚‚��€€��€€€€������������������€€������€€€€‚‚„„„„„„„„„„„„…………††††…………„„„„„„…………††††††††††…………††…………††††€€��ƒƒƒƒ……††…………„„…………„„…………„„ƒƒƒƒƒƒ„„………………††……………………„„„„ƒƒƒƒ„„ƒƒƒƒƒƒ‚‚����€€€€������€€����€€������������€€��€€��€€��‚‚„„„„ƒƒƒƒ„„„„……††††††…………„„„„………………††††……††………………††………………€€��ƒƒƒƒ……††…………„„…………„„…………„„ƒƒƒƒƒƒ„„………………††……………………„„„„ƒƒƒƒ„„ƒƒƒƒƒƒ‚‚����€€€€������€€����€€������������€€��€€��€€��‚‚„„„„ƒƒƒƒ„„„„……††††††…………„„„„………………††††……††………………††………………��ƒƒ„„„„…………………………„„„„„„…………„„ƒƒƒƒ„„…………††…………„„„„„„„„„„„„ƒƒ„„„„ƒƒ‚‚‚‚��€€��€€��€€€€������€€€€��€€��€€����€€€€��€€€€€€€€ƒƒ„„„„„„„„„„„„……††††††………………………………††††††………………„„……††…………��ƒƒ„„„„…………………………„„„„„„…………„„ƒƒƒƒ„„…………††…………„„„„„„„„„„„„ƒƒ„„„„ƒƒ‚‚‚‚��€€��€€��€€€€������€€€€��€€��€€����€€€€��€€€€€€€€ƒƒ„„„„„„„„„„„„……††††††………………………………††††††………………„„……††…………��ƒƒƒƒ„„…………„„„„„„„„„„„„……„„ƒƒƒƒ„„……††………………„„ƒƒƒƒƒƒƒƒƒƒƒƒ„„„„ƒƒƒƒ‚‚��������€€����€€€€��������€€€€€€������€€€€��€€€€€€€€‚‚„„„„„„„„„„„„……††††††††………………„„„„……††††††……††……„„……††……��ƒƒƒƒ„„…………„„„„„„„„„„„„……„„ƒƒƒƒ„„……††………………„„ƒƒƒƒƒƒƒƒƒƒƒƒ„„„„ƒƒƒƒ‚‚��������€€����€€€€��������€€€€€€������€€€€��€€€€€€€€‚‚„„„„„„„„„„„„……††††††††………………„„„„……††††††……††……„„……††……€€��„„…………„„„„„„„„ƒƒ„„„„„„„„„„……………………„„„„ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒ‚‚‚‚��������€€€€€€€€€€€€��������€€€€€€������€€€€€€€€€€€€€€��ƒƒƒƒ„„……„„„„…………††……††††…………„„„„„„……………………††…………„„††††€€��„„…………„„„„„„„„ƒƒ„„„„„„„„„„……………………„„„„ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒ‚‚‚‚��������€€€€€€€€€€€€��������€€€€€€������€€€€€€€€€€€€€€��ƒƒƒƒ„„……„„„„…………††……††††…………„„„„„„……………………††…………„„††††€€‚‚„„……„„ƒƒ„„„„„„„„„„……††……………………„„ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒ‚‚ƒƒƒƒ‚‚������€€€€����€€��€€€€€€����€€€€€€��€€€€€€��€€€€€€€€€€€€€€ƒƒ„„„„„„„„„„„„……††††……††††……„„„„„„………………††…………………………††€€‚‚„„……„„ƒƒ„„„„„„„„„„……††……………………„„ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒ‚‚ƒƒƒƒ‚‚������€€€€����€€��€€€€€€����€€€€€€��€€€€€€��€€€€€€€€€€€€€€ƒƒ„„„„„„„„„„„„……††††……††††……„„„„„„………………††…………………………††€€‚‚„„„„ƒƒƒƒƒƒ„„ƒƒ„„…………††……„„„„ƒƒƒƒƒƒƒƒƒƒ‚‚ƒƒƒƒ„„ƒƒƒƒ‚‚‚‚ƒƒ‚‚��€€��€€€€��€€€€€€€€€€€€������€€€€����€€������€€€€€€€€€€€€€€��ƒƒƒƒƒƒ„„„„„„……††††……††……††……„„„„„„„„…………††…………………………€€‚‚„„„„ƒƒƒƒƒƒ„„ƒƒ„„…………††……„„„„ƒƒƒƒƒƒƒƒƒƒ‚‚ƒƒƒƒ„„ƒƒƒƒ‚‚‚‚ƒƒ‚‚��€€��€€€€��€€€€€€€€€€€€������€€€€����€€������€€€€€€€€€€€€€€��ƒƒƒƒƒƒ„„„„„„……††††……††……††……„„„„„„„„…………††…………………………€€ƒƒ……„„ƒƒƒƒ„„…………………………„„ƒƒ‚‚ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚��€€€€€€€€€€��€€��€€€€€€€€��€€��€€€€€€��€€������€€€€€€€€€€€€€€€€‚‚„„„„„„„„„„…………††††…………††††……„„„„ƒƒ„„„„……………………††„„€€ƒƒ……„„ƒƒƒƒ„„…………………………„„ƒƒ‚‚ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚��€€€€€€€€€€��€€��€€€€€€€€��€€��€€€€€€��€€������€€€€€€€€€€€€€€€€‚‚„„„„„„„„„„…………††††…………††††……„„„„ƒƒ„„„„……………………††„„€€„„„„ƒƒƒƒ„„…………………………„„‚‚‚‚‚‚‚‚‚‚‚‚‚‚ƒƒƒƒƒƒƒƒƒƒƒƒƒƒ‚‚ƒƒƒƒ‚‚€€€€€€€€€€€€€€€€€€€€��€€€€������€€����€€€€������€€€€��€€€€€€€€€€��„„ƒƒ„„„„„„……………………††……††‡‡††……„„„„ƒƒ„„„„…………………………€€„„„„ƒƒƒƒ„„…………………………„„‚‚‚‚‚‚‚‚‚‚‚‚‚‚ƒƒƒƒƒƒƒƒƒƒƒƒƒƒ‚‚ƒƒƒƒ‚‚€€€€€€€€€€€€€€€€€€€€��€€€€������€€����€€€€������€€€€��€€€€€€€€€€��„„ƒƒ„„„„„„……………………††……††‡‡††……„„„„ƒƒ„„„„…………………………��……„„„„…………††……„„ƒƒƒƒƒƒ‚‚ƒƒƒƒ‚‚‚‚‚‚‚‚ƒƒ‚‚ƒƒƒƒƒƒ‚‚ƒƒ‚‚ƒƒƒƒ‚‚€€€€€€€€��€€€€€€€€€€��€€������€€€€����€€��������€€€€��€€€€€€€€€€ƒƒ„„„„ƒƒ„„………………„„……„„††‡‡‡‡††……„„„„ƒƒƒƒ„„……††††……��……„„„„…………††……„„ƒƒƒƒƒƒ‚‚ƒƒƒƒ‚‚‚‚‚‚‚‚ƒƒ‚‚ƒƒƒƒƒƒ‚‚ƒƒ‚‚ƒƒƒƒ‚‚€€€€€€€€��€€€€€€€€€€��€€������€€€€����€€��������€€€€��€€€€€€€€€€ƒƒ„„„„ƒƒ„„………………„„……„„††‡‡‡‡††……„„„„ƒƒƒƒ„„……††††……‚‚……………………„„„„ƒƒ‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚ƒƒ‚‚ƒƒƒƒƒƒƒƒƒƒ‚‚‚‚ƒƒ„„ƒƒ��€€€€€€€€��€€€€€€€€€€��€€��€€€€€€€€€€����€€€€€€€€€€��€€€€€€€€€€€€€€‚‚„„„„„„„„„„…………„„„„„„……‡‡‡‡‡‡‡‡……„„ƒƒ‚‚ƒƒ„„††††„„‚‚……………………„„„„ƒƒ‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚ƒƒ‚‚ƒƒƒƒƒƒƒƒƒƒ‚‚‚‚ƒƒ„„ƒƒ��€€€€€€€€��€€€€€€€€€€��€€��€€€€€€€€€€����€€€€€€€€€€��€€€€€€€€€€€€€€‚‚„„„„„„„„„„…………„„„„„„……‡‡‡‡‡‡‡‡……„„ƒƒ‚‚ƒƒ„„††††„„‚‚…………„„ƒƒ‚‚‚‚ƒƒƒƒƒƒ‚‚‚‚‚‚ƒƒ‚‚‚‚‚‚ƒƒ‚‚ƒƒƒƒƒƒƒƒ„„„„„„„„„„��€€€€€€€€€€€€€€��€€€€€€��€€€€��€€��������������€€€€€€��€€€€€€€€€€€€��„„„„„„„„„„„„„„„„„„ƒƒ„„††††‡‡‡‡…………„„ƒƒ‚‚ƒƒ……††††‚‚…………„„ƒƒ‚‚‚‚ƒƒƒƒƒƒ‚‚‚‚‚‚ƒƒ‚‚‚‚‚‚ƒƒ‚‚ƒƒƒƒƒƒƒƒ„„„„„„„„„„��€€€€€€€€€€€€€€��€€€€€€��€€€€��€€��������������€€€€€€��€€€€€€€€€€€€��„„„„„„„„„„„„„„„„„„ƒƒ„„††††‡‡‡‡…………„„ƒƒ‚‚ƒƒ……††††‚‚„„ƒƒ‚‚‚‚‚‚‚‚‚‚ƒƒƒƒ‚‚ƒƒ‚‚‚‚‚‚‚‚‚‚ƒƒƒƒƒƒƒƒ„„……„„„„„„„„‚‚€€€€€€€€€€€€��€€€€€€€€����������������€€����€€��€€€€€€����€€€€€€€€ƒƒ„„……„„ƒƒ„„„„„„„„„„„„……††††††††††„„ƒƒƒƒ„„„„…………‚‚„„ƒƒ‚‚‚‚‚‚‚‚‚‚ƒƒƒƒ‚‚ƒƒ‚‚‚‚‚‚‚‚‚‚ƒƒƒƒƒƒƒƒ„„……„„„„„„„„‚‚€€€€€€€€€€€€��€€€€€€€€����������������€€����€€��€€€€€€����€€€€€€€€ƒƒ„„……„„ƒƒ„„„„„„„„„„„„……††††††††††„„ƒƒƒƒ„„„„…………����‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚ƒƒ‚‚ƒƒƒƒ„„„„„„„„„„„„ƒƒƒƒ��€€€€€€€€€€€€€€��€€€€€€€€€€��€€€€������€€€€€€��€€��€€€€€€����€€€€€€€€‚‚„„„„„„„„ƒƒ„„„„„„„„………………………………††††„„ƒƒƒƒ„„……„„����‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚ƒƒ‚‚ƒƒƒƒ„„„„„„„„„„„„ƒƒƒƒ��€€€€€€€€€€€€€€��€€€€€€€€€€��€€€€������€€€€€€��€€��€€€€€€����€€€€€€€€‚‚„„„„„„„„ƒƒ„„„„„„„„………………………………††††„„ƒƒƒƒ„„……„„����‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚ƒƒƒƒ„„„„„„„„„„„„„„„„ƒƒ‚‚‚‚€€€€€€€€€€€€€€��€€€€€€€€€€€€������������‚‚‚‚ƒƒ‚‚ƒƒ‚‚‚‚����€€€€��€€€€��„„ƒƒ„„„„„„ƒƒ„„ƒƒ„„……„„„„……„„„„††‡‡††„„„„„„„„„„����‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚ƒƒƒƒ„„„„„„„„„„„„„„„„ƒƒ‚‚‚‚€€€€€€€€€€€€€€��€€€€€€€€€€€€������������‚‚‚‚ƒƒ‚‚ƒƒ‚‚‚‚����€€€€��€€€€��„„ƒƒ„„„„„„ƒƒ„„ƒƒ„„……„„„„……„„„„††‡‡††„„„„„„„„„„€€������‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚„„„„„„„„„„„„„„„„ƒƒƒƒ‚‚‚‚‚‚��€€€€€€€€€€€€€€€€€€€€€€€€€€€€����‚‚ƒƒ…………††‡‡‡‡‡‡ˆˆˆˆˆˆˆˆ‡‡‡‡††††……ƒƒƒƒ��€€��„„„„„„„„„„„„„„ƒƒƒƒ„„„„„„††……ƒƒ……††††††„„„„„„ƒƒ€€������‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚„„„„„„„„„„„„„„„„ƒƒƒƒ‚‚‚‚‚‚��€€€€€€€€€€€€€€€€€€€€€€€€€€€€����‚‚ƒƒ…………††‡‡‡‡‡‡ˆˆˆˆˆˆˆˆ‡‡‡‡††††……ƒƒƒƒ��€€��„„„„„„„„„„„„„„ƒƒƒƒ„„„„„„††……ƒƒ……††††††„„„„„„ƒƒ€€��‚‚‚‚‚‚‚‚‚‚‚‚‚‚ƒƒƒƒ„„„„……„„„„„„„„„„ƒƒ‚‚‚‚‚‚‚‚‚‚‚‚��€€€€€€€€€€€€€€€€€€€€����‚‚ƒƒ……‡‡ˆˆ‰‰‹‹ŒŒ��������ŽŽŽŽ��������‹‹ŠŠ‰‰ˆˆˆˆ‡‡‡‡††…………„„„„„„„„„„ƒƒƒƒ„„„„„„††……„„……††††††…………„„ƒƒ€€��‚‚‚‚‚‚‚‚‚‚‚‚‚‚ƒƒƒƒ„„„„……„„„„„„„„„„ƒƒ‚‚‚‚‚‚‚‚‚‚‚‚��€€€€€€€€€€€€€€€€€€€€����‚‚ƒƒ……‡‡ˆˆ‰‰‹‹ŒŒ��������ŽŽŽŽ��������‹‹ŠŠ‰‰ˆˆˆˆ‡‡‡‡††…………„„„„„„„„„„ƒƒƒƒ„„„„„„††……„„……††††††…………„„ƒƒ€€��‚‚��‚‚‚‚ƒƒƒƒ„„…………„„„„††……„„„„ƒƒ‚‚��‚‚����‚‚‚‚����€€€€€€€€€€��€€��‚‚‚‚„„††ŠŠŒŒŒŒ��ŽŽ����‘‘������ŽŽŽŽ������ŽŽŽŽŒŒŒŒŽŽŽŽ��ŒŒŒŒŠŠ‰‰‰‰ˆˆ††††……ƒƒƒƒƒƒ„„„„„„……„„ƒƒ„„……††……††……„„„„€€��‚‚��‚‚‚‚ƒƒƒƒ„„…………„„„„††……„„„„ƒƒ‚‚��‚‚����‚‚‚‚����€€€€€€€€€€��€€��‚‚‚‚„„††ŠŠŒŒŒŒ��ŽŽ����‘‘������ŽŽŽŽ������ŽŽŽŽŒŒŒŒŽŽŽŽ��ŒŒŒŒŠŠ‰‰‰‰ˆˆ††††……ƒƒƒƒƒƒ„„„„„„……„„ƒƒ„„……††……††……„„„„€€��‚‚‚‚ƒƒ„„„„…………„„„„„„„„„„‚‚ƒƒƒƒ‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚€€€€€€€€����‚‚ƒƒ„„‡‡ŠŠŽŽ‘‘““’’’’‘‘‘‘����������ŽŽ������ŽŽŽŽŽŽŽŽ��ŒŒŽŽ��’’‘‘��‹‹ŠŠ‰‰‰‰‰‰‰‰ˆˆ‡‡††……„„„„„„„„„„ƒƒ„„……………………††††……€€��‚‚‚‚ƒƒ„„„„…………„„„„„„„„„„‚‚ƒƒƒƒ‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚€€€€€€€€����‚‚ƒƒ„„‡‡ŠŠŽŽ‘‘““’’’’‘‘‘‘����������ŽŽ������ŽŽŽŽŽŽŽŽ��ŒŒŽŽ��’’‘‘��‹‹ŠŠ‰‰‰‰‰‰‰‰ˆˆ‡‡††……„„„„„„„„„„ƒƒ„„……………………††††……€€‚‚ƒƒ„„„„……„„„„„„„„ƒƒ‚‚‚‚‚‚‚‚‚‚‚‚‚‚��‚‚��‚‚‚‚‚‚‚‚��€€��‚‚‚‚ƒƒ„„††‰‰��ŽŽ��’’““““’’����������‘‘‘‘‘‘‘‘’’’’’’““““’’““““‘‘ŽŽŽŽ��’’��ŒŒŠŠŠŠ‰‰‰‰‰‰ˆˆˆˆˆˆˆˆ„„ƒƒƒƒ„„„„„„„„…………………………‡‡††€€‚‚ƒƒ„„„„……„„„„„„„„ƒƒ‚‚‚‚‚‚‚‚‚‚‚‚‚‚��‚‚��‚‚‚‚‚‚‚‚��€€��‚‚‚‚ƒƒ„„††‰‰��ŽŽ��’’““““’’����������‘‘‘‘‘‘‘‘’’’’’’““““’’““““‘‘ŽŽŽŽ��’’��ŒŒŠŠŠŠ‰‰‰‰‰‰ˆˆˆˆˆˆˆˆ„„ƒƒƒƒ„„„„„„„„…………………………‡‡††��ƒƒƒƒƒƒ„„„„„„ƒƒ‚‚‚‚‚‚��‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚��‚‚��‚‚ƒƒƒƒ„„‡‡‹‹ŽŽ‘‘’’��ŽŽ‘‘‘‘����ŽŽŽŽ��������������‘‘‘‘‘‘’’““““””””““’’‘‘‘‘’’‘‘‹‹‰‰ŠŠ‰‰‰‰ˆˆˆˆˆˆˆˆ††ƒƒ‚‚‚‚ƒƒ„„ƒƒ„„…………„„„„……‡‡††��ƒƒƒƒƒƒ„„„„„„ƒƒ‚‚‚‚‚‚��‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚��‚‚��‚‚ƒƒƒƒ„„‡‡‹‹ŽŽ‘‘’’��ŽŽ‘‘‘‘����ŽŽŽŽ��������������‘‘‘‘‘‘’’““““””””““’’‘‘‘‘’’‘‘‹‹‰‰ŠŠ‰‰‰‰ˆˆˆˆˆˆˆˆ††ƒƒ‚‚‚‚ƒƒ„„ƒƒ„„…………„„„„……‡‡††��„„ƒƒƒƒƒƒ‚‚‚‚������‚‚������‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚����‚‚ƒƒ„„††‰‰ŽŽ‘‘’’’’’’��ŒŒ��ŽŽ��ŽŽ������ŽŽ����������ŽŽ������‘‘’’““““’’’’’’’’����ŒŒŠŠŠŠ‰‰ˆˆˆˆˆˆ‡‡††„„„„ƒƒƒƒƒƒƒƒƒƒ„„„„……„„ƒƒ„„††……��„„ƒƒƒƒƒƒ‚‚‚‚������‚‚������‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚����‚‚ƒƒ„„††‰‰ŽŽ‘‘’’’’’’��ŒŒ��ŽŽ��ŽŽ������ŽŽ����������ŽŽ������‘‘’’““““’’’’’’’’����ŒŒŠŠŠŠ‰‰ˆˆˆˆˆˆ‡‡††„„„„ƒƒƒƒƒƒƒƒƒƒ„„„„……„„ƒƒ„„††……��ƒƒ‚‚��������‚‚‚‚��������‚‚��‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚ƒƒ„„……‡‡ˆˆ��‘‘’’’’‘‘��ŽŽ‹‹ŒŒŽŽŽŽŽŽ��ŽŽ����ŒŒ‹‹ŠŠ‹‹‹‹ŒŒ��������‘‘’’’’‘‘‘‘’’������ŒŒŠŠŠŠˆˆ‰‰ˆˆ††……„„„„ƒƒƒƒƒƒ„„ƒƒƒƒƒƒ„„„„„„ƒƒƒƒ„„……��ƒƒ‚‚��������‚‚‚‚��������‚‚��‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚ƒƒ„„……‡‡ˆˆ��‘‘’’’’‘‘��ŽŽ‹‹ŒŒŽŽŽŽŽŽ��ŽŽ����ŒŒ‹‹ŠŠ‹‹‹‹ŒŒ��������‘‘’’’’‘‘‘‘’’������ŒŒŠŠŠŠˆˆ‰‰ˆˆ††……„„„„ƒƒƒƒƒƒ„„ƒƒƒƒƒƒ„„„„„„ƒƒƒƒ„„……��������‚‚����‚‚‚‚‚‚��‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚ƒƒ„„††ˆˆˆˆŠŠ����‘‘��ŽŽŒŒŒŒ��ŽŽ��ŽŽŽŽŽŽ��������ŒŒŒŒŒŒ‹‹‹‹ŒŒ����������‘‘‘‘‘‘����ŽŽ��‹‹ŠŠŠŠŠŠˆˆ††……„„„„„„„„„„„„ƒƒƒƒƒƒƒƒ„„„„„„„„ƒƒ„„„„��������‚‚����‚‚‚‚‚‚��‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚ƒƒ„„††ˆˆˆˆŠŠ����‘‘��ŽŽŒŒŒŒ��ŽŽ��ŽŽŽŽŽŽ��������ŒŒŒŒŒŒ‹‹‹‹ŒŒ����������‘‘‘‘‘‘����ŽŽ��‹‹ŠŠŠŠŠŠˆˆ††……„„„„„„„„„„„„ƒƒƒƒƒƒƒƒ„„„„„„„„ƒƒ„„„„€€��‚‚��‚‚����‚‚‚‚‚‚��‚‚‚‚‚‚ƒƒ‚‚‚‚‚‚‚‚‚‚‚‚‚‚ƒƒ„„††ˆˆˆˆŠŠŽŽ������ŒŒ‰‰‹‹��ŽŽŽŽ��ŽŽŽŽ������ŽŽŽŽŽŽŽŽŽŽ��ŒŒŒŒŒŒŒŒŽŽ������‘‘����ŽŽŒŒ‹‹ŠŠŠŠˆˆ‡‡„„„„„„ƒƒ„„„„„„„„„„ƒƒƒƒƒƒƒƒ„„„„„„„„„„„„„„€€��‚‚��‚‚����‚‚‚‚‚‚��‚‚‚‚‚‚ƒƒ‚‚‚‚‚‚‚‚‚‚‚‚‚‚ƒƒ„„††ˆˆˆˆŠŠŽŽ������ŒŒ‰‰‹‹��ŽŽŽŽ��ŽŽŽŽ������ŽŽŽŽŽŽŽŽŽŽ��ŒŒŒŒŒŒŒŒŽŽ������‘‘����ŽŽŒŒ‹‹ŠŠŠŠˆˆ‡‡„„„„„„ƒƒ„„„„„„„„„„ƒƒƒƒƒƒƒƒ„„„„„„„„„„„„„„€€��‚‚��‚‚����‚‚‚‚��‚‚‚‚‚‚‚‚‚‚‚‚����‚‚‚‚��‚‚‚‚„„‡‡ˆˆˆˆŒŒŽŽŽŽŒŒŠŠ‡‡ˆˆŒŒ��ŽŽŽŽŽŽ������ŒŒ����������ŽŽŽŽ��ŒŒ��ŒŒŒŒ����������ŽŽŒŒ‹‹ŠŠ‰‰‡‡„„ƒƒƒƒƒƒ„„ƒƒ„„„„„„ƒƒ„„ƒƒƒƒ„„ƒƒƒƒ„„„„„„„„„„ƒƒ€€��‚‚��‚‚����‚‚‚‚��‚‚‚‚‚‚‚‚‚‚‚‚����‚‚‚‚��‚‚‚‚„„‡‡ˆˆˆˆŒŒŽŽŽŽŒŒŠŠ‡‡ˆˆŒŒ��ŽŽŽŽŽŽ������ŒŒ����������ŽŽŽŽ��ŒŒ��ŒŒŒŒ����������ŽŽŒŒ‹‹ŠŠ‰‰‡‡„„ƒƒƒƒƒƒ„„ƒƒ„„„„„„ƒƒ„„ƒƒƒƒ„„ƒƒƒƒ„„„„„„„„„„ƒƒ€€��‚‚��������‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚ƒƒƒƒ……‡‡‡‡‡‡‹‹ŒŒ‹‹ˆˆˆˆ‡‡‰‰ŒŒŒŒ��ŽŽŽŽ��ŒŒ����ŽŽ��������������ŽŽ����ŽŽ��������ŽŽŒŒ‹‹‹‹‹‹ˆˆ„„ƒƒƒƒƒƒƒƒƒƒƒƒ„„„„…………„„ƒƒƒƒ„„ƒƒƒƒ„„„„„„„„„„„„€€��‚‚��������‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚ƒƒƒƒ……‡‡‡‡‡‡‹‹ŒŒ‹‹ˆˆˆˆ‡‡‰‰ŒŒŒŒ��ŽŽŽŽ��ŒŒ����ŽŽ��������������ŽŽ����ŽŽ��������ŽŽŒŒ‹‹‹‹‹‹ˆˆ„„ƒƒƒƒƒƒƒƒƒƒƒƒ„„„„…………„„ƒƒƒƒ„„ƒƒƒƒ„„„„„„„„„„„„€€����‚‚‚‚‚‚��‚‚‚‚‚‚����‚‚‚‚‚‚‚‚ƒƒƒƒ„„ƒƒƒƒ„„ƒƒ……‡‡‡‡††‰‰‹‹ŠŠˆˆ‡‡ˆˆ‰‰‹‹������ŒŒŒŒ����ŽŽ������������ŽŽŽŽ������ŽŽŽŽ��������ŒŒŽŽ����ˆˆ„„„„ƒƒƒƒƒƒƒƒƒƒƒƒ„„……„„„„ƒƒƒƒƒƒƒƒ„„„„„„„„„„„„„„€€����‚‚‚‚‚‚��‚‚‚‚‚‚����‚‚‚‚‚‚‚‚ƒƒƒƒ„„ƒƒƒƒ„„ƒƒ……‡‡‡‡††‰‰‹‹ŠŠˆˆ‡‡ˆˆ‰‰‹‹������ŒŒŒŒ����ŽŽ������������ŽŽŽŽ������ŽŽŽŽ��������ŒŒŽŽ����ˆˆ„„„„ƒƒƒƒƒƒƒƒƒƒƒƒ„„……„„„„ƒƒƒƒƒƒƒƒ„„„„„„„„„„„„„„€€������‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚ƒƒƒƒ„„„„„„„„ƒƒƒƒ„„ƒƒ„„……‡‡††††ˆˆ‰‰‡‡‡‡ˆˆŠŠ‹‹��ŽŽ����ŒŒ��ŽŽ����������ŒŒ‰‰‰‰‹‹ŽŽŽŽŒŒŒŒŽŽ����ŽŽ����������‰‰ƒƒ„„„„„„„„ƒƒƒƒ„„ƒƒ„„„„„„„„ƒƒƒƒƒƒ„„„„„„„„„„„„„„€€������‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚ƒƒƒƒ„„„„„„„„ƒƒƒƒ„„ƒƒ„„……‡‡††††ˆˆ‰‰‡‡‡‡ˆˆŠŠ‹‹��ŽŽ����ŒŒ��ŽŽ����������ŒŒ‰‰‰‰‹‹ŽŽŽŽŒŒŒŒŽŽ����ŽŽ����������‰‰ƒƒ„„„„„„„„ƒƒƒƒ„„ƒƒ„„„„„„„„ƒƒƒƒƒƒ„„„„„„„„„„„„„„€€������‚‚‚‚‚‚‚‚‚‚‚‚‚‚ƒƒƒƒ„„„„ƒƒƒƒƒƒ„„„„ƒƒ„„„„„„„„„„……††ˆˆˆˆ‡‡‡‡ˆˆ‰‰‹‹ŒŒŽŽ��������ŽŽ������ŽŽ��ŒŒ‰‰ˆˆŠŠ����‹‹ŒŒ����‘‘������ŽŽŽŽ��ŠŠ„„ƒƒƒƒ„„„„ƒƒ‚‚ƒƒ„„ƒƒ„„„„„„ƒƒƒƒƒƒ„„„„ƒƒƒƒƒƒ„„„„€€������‚‚‚‚‚‚‚‚‚‚‚‚‚‚ƒƒƒƒ„„„„ƒƒƒƒƒƒ„„„„ƒƒ„„„„„„„„„„……††ˆˆˆˆ‡‡‡‡ˆˆ‰‰‹‹ŒŒŽŽ��������ŽŽ������ŽŽ��ŒŒ‰‰ˆˆŠŠ����‹‹ŒŒ����‘‘������ŽŽŽŽ��ŠŠ„„ƒƒƒƒ„„„„ƒƒ‚‚ƒƒ„„ƒƒ„„„„„„ƒƒƒƒƒƒ„„„„ƒƒƒƒƒƒ„„„„€€��‚‚‚‚‚‚‚‚‚‚ƒƒƒƒƒƒƒƒƒƒƒƒƒƒ„„„„ƒƒ„„„„ƒƒ„„„„„„„„„„„„……‰‰����ŒŒ‰‰‰‰ŠŠ‹‹ŒŒ��������ŽŽ����ŽŽŽŽŽŽ������ŽŽŽŽ��������‘‘‘‘’’����‹‹‰‰‹‹ŽŽˆˆƒƒƒƒ„„ƒƒƒƒƒƒƒƒƒƒƒƒƒƒ„„ƒƒ„„„„„„„„ƒƒƒƒƒƒƒƒƒƒƒƒƒƒ€€��‚‚‚‚‚‚‚‚‚‚ƒƒƒƒƒƒƒƒƒƒƒƒƒƒ„„„„ƒƒ„„„„ƒƒ„„„„„„„„„„„„……‰‰����ŒŒ‰‰‰‰ŠŠ‹‹ŒŒ��������ŽŽ����ŽŽŽŽŽŽ������ŽŽŽŽ��������‘‘‘‘’’����‹‹‰‰‹‹ŽŽˆˆƒƒƒƒ„„ƒƒƒƒƒƒƒƒƒƒƒƒƒƒ„„ƒƒ„„„„„„„„ƒƒƒƒƒƒƒƒƒƒƒƒƒƒ€€��‚‚‚‚ƒƒ„„„„„„ƒƒƒƒ„„ƒƒ„„„„„„„„…………„„„„……„„„„„„„„„„††‘‘––’’����‹‹ŒŒŒŒ��ŽŽŽŽŽŽ��������������������������ŽŽ��‘‘‘‘‘‘‘‘‘‘ŽŽ��ŠŠ‹‹‹‹††„„„„„„„„„„ƒƒ„„ƒƒƒƒƒƒ„„„„„„„„„„„„ƒƒƒƒƒƒƒƒƒƒƒƒƒƒ€€��‚‚‚‚ƒƒ„„„„„„ƒƒƒƒ„„ƒƒ„„„„„„„„…………„„„„……„„„„„„„„„„††‘‘––’’����‹‹ŒŒŒŒ��ŽŽŽŽŽŽ��������������������������ŽŽ��‘‘‘‘‘‘‘‘‘‘ŽŽ��ŠŠ‹‹‹‹††„„„„„„„„„„ƒƒ„„ƒƒƒƒƒƒ„„„„„„„„„„„„ƒƒƒƒƒƒƒƒƒƒƒƒƒƒ��‚‚ƒƒƒƒƒƒƒƒƒƒ„„ƒƒƒƒ„„„„„„„„„„„„…………„„„„„„……„„„„„„ƒƒˆˆ––••����ŽŽŒŒ‹‹ŒŒ������������’’‘‘‘‘‘‘‘‘����������������‘‘��‘‘‘‘’’������ŠŠ‰‰„„„„„„„„„„ƒƒƒƒƒƒ„„ƒƒƒƒ„„„„„„„„„„„„„„ƒƒƒƒƒƒƒƒƒƒ„„��‚‚ƒƒƒƒƒƒƒƒƒƒ„„ƒƒƒƒ„„„„„„„„„„„„…………„„„„„„……„„„„„„ƒƒˆˆ––••����ŽŽŒŒ‹‹ŒŒ������������’’‘‘‘‘‘‘‘‘����������������‘‘��‘‘‘‘’’������ŠŠ‰‰„„„„„„„„„„ƒƒƒƒƒƒ„„ƒƒƒƒ„„„„„„„„„„„„„„ƒƒƒƒƒƒƒƒƒƒ„„��‚‚‚‚ƒƒƒƒƒƒƒƒ‚‚‚‚‚‚ƒƒ„„ƒƒ„„„„ƒƒ„„„„„„„„„„„„„„……„„ƒƒ‰‰––””��������‹‹‹‹ŽŽ��������‘‘’’’’““““’’’’‘‘‘‘‘‘����‘‘‘‘‘‘‘‘’’““““‘‘����‹‹‡‡ƒƒƒƒƒƒ„„ƒƒƒƒƒƒƒƒ„„ƒƒ‚‚ƒƒƒƒƒƒƒƒ„„„„„„ƒƒƒƒƒƒƒƒƒƒƒƒ��‚‚‚‚ƒƒƒƒƒƒƒƒ‚‚‚‚‚‚ƒƒ„„ƒƒ„„„„ƒƒ„„„„„„„„„„„„„„……„„ƒƒ‰‰––””��������‹‹‹‹ŽŽ��������‘‘’’’’““““’’’’‘‘‘‘‘‘����‘‘‘‘‘‘‘‘’’““““‘‘����‹‹‡‡ƒƒƒƒƒƒ„„ƒƒƒƒƒƒƒƒ„„ƒƒ‚‚ƒƒƒƒƒƒƒƒ„„„„„„ƒƒƒƒƒƒƒƒƒƒƒƒ€€‚‚‚‚‚‚��������‚‚��‚‚ƒƒƒƒƒƒƒƒƒƒƒƒ„„„„„„„„„„„„„„„„ƒƒ‰‰••””‘‘����ŽŽ‹‹‹‹����������‘‘’’““““””““““““’’’’‘‘‘‘’’’’‘‘‘‘’’””••””������ˆˆƒƒƒƒ„„ƒƒ„„ƒƒƒƒƒƒ„„ƒƒ‚‚ƒƒƒƒ‚‚‚‚ƒƒƒƒ„„ƒƒƒƒƒƒƒƒƒƒ‚‚€€‚‚‚‚‚‚��������‚‚��‚‚ƒƒƒƒƒƒƒƒƒƒƒƒ„„„„„„„„„„„„„„„„ƒƒ‰‰••””‘‘����ŽŽ‹‹‹‹����������‘‘’’““““””““““““’’’’‘‘‘‘’’’’‘‘‘‘’’””••””������ˆˆƒƒƒƒ„„ƒƒ„„ƒƒƒƒƒƒ„„ƒƒ‚‚ƒƒƒƒ‚‚‚‚ƒƒƒƒ„„ƒƒƒƒƒƒƒƒƒƒ‚‚€€€€€€€€€€������‚‚‚‚ƒƒƒƒƒƒƒƒ‚‚ƒƒƒƒƒƒƒƒ„„„„„„„„„„„„ƒƒˆˆ““””““““’’ŽŽ‹‹ŠŠŒŒŽŽ����‘‘““••••••••””““““““’’’’’’““’’‘‘’’““••——˜˜””ŽŽ��‰‰„„„„ƒƒ„„„„ƒƒƒƒƒƒ„„ƒƒƒƒƒƒƒƒƒƒ‚‚ƒƒ„„ƒƒƒƒ„„„„ƒƒƒƒƒƒ€€€€€€€€€€������‚‚‚‚ƒƒƒƒƒƒƒƒ‚‚ƒƒƒƒƒƒƒƒ„„„„„„„„„„„„ƒƒˆˆ““””““““’’ŽŽ‹‹ŠŠŒŒŽŽ����‘‘““••••••••””““““““’’’’’’““’’‘‘’’““••——˜˜””ŽŽ��‰‰„„„„ƒƒ„„„„ƒƒƒƒƒƒ„„ƒƒƒƒƒƒƒƒƒƒ‚‚ƒƒ„„ƒƒƒƒ„„„„ƒƒƒƒƒƒ€€€€€€����‚‚‚‚‚‚‚‚ƒƒƒƒƒƒ„„„„‚‚‚‚‚‚‚‚ƒƒ„„„„„„„„„„„„„„††��””””••‘‘����ŠŠ‹‹��ŽŽ��““••––————––••””““““““’’’’‘‘’’““““””——™™šš——��ŽŽŠŠ„„ƒƒƒƒ„„„„ƒƒƒƒƒƒƒƒƒƒƒƒƒƒ‚‚ƒƒ‚‚‚‚ƒƒ„„ƒƒƒƒ„„ƒƒƒƒƒƒ€€€€€€����‚‚‚‚‚‚‚‚ƒƒƒƒƒƒ„„„„‚‚‚‚‚‚‚‚ƒƒ„„„„„„„„„„„„„„††��””””••‘‘����ŠŠ‹‹��ŽŽ��““••––————––••””““““““’’’’‘‘’’““““””——™™šš——��ŽŽŠŠ„„ƒƒƒƒ„„„„ƒƒƒƒƒƒƒƒƒƒƒƒƒƒ‚‚ƒƒ‚‚‚‚ƒƒ„„ƒƒƒƒ„„ƒƒƒƒƒƒ€€€€€€����‚‚‚‚‚‚‚‚ƒƒƒƒ„„ƒƒ„„ƒƒ��‚‚����‚‚ƒƒ„„……„„„„…………ŒŒ““••““��‘‘ŽŽ‹‹‹‹ŒŒ����““””––––————––””””““““’’’’’’””••••––˜˜šš››——��ŽŽŒŒ……ƒƒ„„„„„„ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒ‚‚‚‚ƒƒƒƒ„„ƒƒƒƒƒƒ€€€€€€����‚‚‚‚‚‚‚‚ƒƒƒƒ„„ƒƒ„„ƒƒ��‚‚����‚‚ƒƒ„„……„„„„…………ŒŒ““••““��‘‘ŽŽ‹‹‹‹ŒŒ����““””––––————––””””““““’’’’’’””••••––˜˜šš››——��ŽŽŒŒ……ƒƒ„„„„„„ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒ‚‚‚‚ƒƒƒƒ„„ƒƒƒƒƒƒ€€€€������‚‚��‚‚��‚‚ƒƒ„„„„……„„����‚‚������ƒƒ„„„„„„„„……‰‰��••’’’’’’ŽŽ‹‹ŠŠ‹‹ŒŒŽŽ‘‘““””””••––——””““’’’’’’’’““––““‘‘’’’’••——””����ŒŒ††ƒƒƒƒ„„„„„„ƒƒƒƒƒƒ„„ƒƒƒƒƒƒƒƒƒƒƒƒƒƒ‚‚ƒƒ„„„„„„„„ƒƒ€€€€������‚‚��‚‚��‚‚ƒƒ„„„„……„„����‚‚������ƒƒ„„„„„„„„……‰‰��••’’’’’’ŽŽ‹‹ŠŠ‹‹ŒŒŽŽ‘‘““””””••––——””““’’’’’’’’““––““‘‘’’’’••——””����ŒŒ††ƒƒƒƒ„„„„„„ƒƒƒƒƒƒ„„ƒƒƒƒƒƒƒƒƒƒƒƒƒƒ‚‚ƒƒ„„„„„„„„ƒƒ€€��������‚‚ƒƒ‚‚‚‚‚‚ƒƒ„„„„„„„„ƒƒ����‚‚ƒƒ‚‚‚‚‚‚ƒƒ„„„„……††ŒŒ””’’““““��ŒŒŠŠ‹‹ŒŒŒŒ��‘‘’’’’““””””““‘‘‘‘’’““’’””––””������‘‘‘‘������ŒŒ‡‡……„„ƒƒ„„„„„„„„„„„„„„ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒ„„…………„„€€��������‚‚ƒƒ‚‚‚‚‚‚ƒƒ„„„„„„„„ƒƒ����‚‚ƒƒ‚‚‚‚‚‚ƒƒ„„„„……††ŒŒ””’’““““��ŒŒŠŠ‹‹ŒŒŒŒ��‘‘’’’’““””””““‘‘‘‘’’““’’””––””������‘‘‘‘������ŒŒ‡‡……„„ƒƒ„„„„„„„„„„„„„„ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒ„„…………„„€€€€��������‚‚‚‚ƒƒ‚‚ƒƒƒƒ„„„„„„„„‚‚€€��‚‚‚‚ƒƒ‚‚��ƒƒ„„…………ˆˆ��’’””””’’��ŠŠ‹‹ŒŒŒŒŽŽ������’’’’‘‘‘‘‘‘‘‘‘‘’’‘‘’’’’““’’������ŽŽŒŒ����‹‹††……††……ƒƒ„„„„„„„„„„„„„„ƒƒƒƒƒƒƒƒ‚‚ƒƒƒƒƒƒƒƒƒƒ„„„„€€€€��������‚‚‚‚ƒƒ‚‚ƒƒƒƒ„„„„„„„„‚‚€€��‚‚‚‚ƒƒ‚‚��ƒƒ„„…………ˆˆ��’’””””’’��ŠŠ‹‹ŒŒŒŒŽŽ������’’’’‘‘‘‘‘‘‘‘‘‘’’‘‘’’’’““’’������ŽŽŒŒ����‹‹††……††……ƒƒ„„„„„„„„„„„„„„ƒƒƒƒƒƒƒƒ‚‚ƒƒƒƒƒƒƒƒƒƒ„„„„€€€€€€€€€€��‚‚‚‚‚‚‚‚ƒƒƒƒƒƒ„„ƒƒ„„ƒƒ����‚‚��ƒƒ„„ƒƒ‚‚ƒƒ……††‡‡ŠŠ‘‘””••””��ŠŠ‹‹ŒŒ������ŽŽ����������‘‘‘‘‘‘‘‘‘‘‘‘��ŽŽŽŽŽŽ��ŽŽŠŠ‰‰��ŽŽŠŠ††……………………„„„„„„„„„„„„„„„„ƒƒƒƒƒƒƒƒ„„ƒƒ‚‚ƒƒ‚‚‚‚ƒƒ€€€€€€€€€€��‚‚‚‚‚‚‚‚ƒƒƒƒƒƒ„„ƒƒ„„ƒƒ����‚‚��ƒƒ„„ƒƒ‚‚ƒƒ……††‡‡ŠŠ‘‘””••””��ŠŠ‹‹ŒŒ������ŽŽ����������‘‘‘‘‘‘‘‘‘‘‘‘��ŽŽŽŽŽŽ��ŽŽŠŠ‰‰��ŽŽŠŠ††……………………„„„„„„„„„„„„„„„„ƒƒƒƒƒƒƒƒ„„ƒƒ‚‚ƒƒ‚‚‚‚ƒƒ€€€€������‚‚‚‚‚‚ƒƒƒƒƒƒƒƒƒƒƒƒ„„„„„„‚‚��‚‚��‚‚ƒƒƒƒ„„……††‡‡‡‡‡‡ŒŒ““––––’’‹‹‹‹ŒŒ����������������’’““’’‘‘��������������ŒŒŒŒ‰‰ˆˆ‰‰‹‹‰‰††††…………††……„„„„„„„„„„„„……„„„„ƒƒƒƒ‚‚ƒƒƒƒ‚‚ƒƒƒƒƒƒ€€€€������‚‚‚‚‚‚ƒƒƒƒƒƒƒƒƒƒƒƒ„„„„„„‚‚��‚‚��‚‚ƒƒƒƒ„„……††‡‡‡‡‡‡ŒŒ““––––’’‹‹‹‹ŒŒ����������������’’““’’‘‘��������������ŒŒŒŒ‰‰ˆˆ‰‰‹‹‰‰††††…………††……„„„„„„„„„„„„……„„„„ƒƒƒƒ‚‚ƒƒƒƒ‚‚ƒƒƒƒƒƒ€€€€������‚‚��‚‚ƒƒƒƒƒƒƒƒƒƒƒƒ„„„„„„„„��������‚‚ƒƒ„„††ˆˆ‰‰‰‰‰‰‰‰��““““��‹‹ŠŠ‹‹��ŽŽŽŽŽŽ����‘‘’’““””““��ŽŽ����������ŽŽ��ŒŒ‰‰ˆˆ‰‰‰‰‡‡……††††……††††…………ƒƒ„„„„„„„„„„„„„„„„‚‚ƒƒƒƒƒƒƒƒƒƒƒƒ€€€€������‚‚��‚‚ƒƒƒƒƒƒƒƒƒƒƒƒ„„„„„„„„��������‚‚ƒƒ„„††ˆˆ‰‰‰‰‰‰‰‰��““““��‹‹ŠŠ‹‹��ŽŽŽŽŽŽ����‘‘’’““””““��ŽŽ����������ŽŽ��ŒŒ‰‰ˆˆ‰‰‰‰‡‡……††††……††††…………ƒƒ„„„„„„„„„„„„„„„„‚‚ƒƒƒƒƒƒƒƒƒƒƒƒ€€€€������‚‚��‚‚‚‚‚‚‚‚ƒƒƒƒƒƒƒƒƒƒƒƒ„„ƒƒ��������‚‚„„††ˆˆ‰‰‹‹ŠŠˆˆˆˆ��ŽŽŽŽ‹‹‹‹‹‹������ŽŽ��‘‘’’““““’’‘‘ŽŽ��ŽŽŽŽ��’’’’’’��ŽŽŒŒ‰‰ŠŠˆˆ††…………††………………††………………ƒƒ„„„„„„„„„„ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒ€€€€������‚‚��‚‚‚‚‚‚‚‚ƒƒƒƒƒƒƒƒƒƒƒƒ„„ƒƒ��������‚‚„„††ˆˆ‰‰‹‹ŠŠˆˆˆˆ��ŽŽŽŽ‹‹‹‹‹‹������ŽŽ��‘‘’’““““’’‘‘ŽŽ��ŽŽŽŽ��’’’’’’��ŽŽŒŒ‰‰ŠŠˆˆ††…………††………………††………………ƒƒ„„„„„„„„„„ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒ��€€����‚‚��‚‚‚‚‚‚‚‚‚‚ƒƒ„„ƒƒƒƒƒƒƒƒƒƒƒƒ��€€����‚‚„„‡‡ˆˆŠŠŠŠŠŠ††……‹‹ŽŽ��ŒŒ‹‹‹‹����������‘‘””””““‘‘��ŽŽŽŽ��““””””••””’’��ŽŽŒŒ‹‹‰‰††…………………………††††††††††……„„„„ƒƒ„„„„ƒƒƒƒƒƒƒƒƒƒ„„„„ƒƒ��€€����‚‚��‚‚‚‚‚‚‚‚‚‚ƒƒ„„ƒƒƒƒƒƒƒƒƒƒƒƒ��€€����‚‚„„‡‡ˆˆŠŠŠŠŠŠ††……‹‹ŽŽ��ŒŒ‹‹‹‹����������‘‘””””““‘‘��ŽŽŽŽ��““””””••””’’��ŽŽŒŒ‹‹‰‰††…………………………††††††††††……„„„„ƒƒ„„„„ƒƒƒƒƒƒƒƒƒƒ„„„„ƒƒ€€€€����‚‚��‚‚‚‚‚‚‚‚‚‚ƒƒƒƒƒƒ„„ƒƒƒƒƒƒ„„‚‚������‚‚„„††ˆˆŠŠŠŠˆˆ††……‰‰ŽŽ����ŒŒŒŒ����‘‘����‘‘””••““����‘‘““––——————™™˜˜••‘‘����ŒŒˆˆ………………††…………††††††††††††……„„„„„„„„„„ƒƒƒƒƒƒ„„„„„„ƒƒ€€€€����‚‚��‚‚‚‚‚‚‚‚‚‚ƒƒƒƒƒƒ„„ƒƒƒƒƒƒ„„‚‚������‚‚„„††ˆˆŠŠŠŠˆˆ††……‰‰ŽŽ����ŒŒŒŒ����‘‘����‘‘””••““����‘‘““––——————™™˜˜••‘‘����ŒŒˆˆ………………††…………††††††††††††……„„„„„„„„„„ƒƒƒƒƒƒ„„„„„„ƒƒ€€��‚‚‚‚‚‚‚‚��‚‚‚‚‚‚‚‚ƒƒƒƒƒƒ„„ƒƒ„„„„„„ƒƒ‚‚��‚‚‚‚„„……‡‡ˆˆ‰‰ˆˆ‡‡‡‡ˆˆ����ŽŽ��������‘‘‘‘����““””““����““––™™ššššššœœ››˜˜““��ŒŒŽŽˆˆ……………………………………††††††††††††††……„„„„„„ƒƒƒƒƒƒ„„„„„„ƒƒ€€��‚‚‚‚‚‚‚‚��‚‚‚‚‚‚‚‚ƒƒƒƒƒƒ„„ƒƒ„„„„„„ƒƒ‚‚��‚‚‚‚„„……‡‡ˆˆ‰‰ˆˆ‡‡‡‡ˆˆ����ŽŽ��������‘‘‘‘����““””““����““––™™ššššššœœ››˜˜““��ŒŒŽŽˆˆ……………………………………††††††††††††††……„„„„„„ƒƒƒƒƒƒ„„„„„„ƒƒ€€����‚‚��‚‚��‚‚‚‚‚‚‚‚ƒƒƒƒƒƒƒƒƒƒƒƒƒƒ„„ƒƒ„„‚‚��‚‚„„……††‡‡ˆˆˆˆˆˆˆˆˆˆ��‘‘��������ŽŽ��‘‘����’’““’’��’’••——˜˜˜˜˜˜™™šššš™™””��‹‹��‰‰……††††††††………………††††……††††††††……„„„„ƒƒƒƒƒƒ„„„„„„ƒƒ€€����‚‚��‚‚��‚‚‚‚‚‚‚‚ƒƒƒƒƒƒƒƒƒƒƒƒƒƒ„„ƒƒ„„‚‚��‚‚„„……††‡‡ˆˆˆˆˆˆˆˆˆˆ��‘‘��������ŽŽ��‘‘����’’““’’��’’••——˜˜˜˜˜˜™™šššš™™””��‹‹��‰‰……††††††††………………††††……††††††††……„„„„ƒƒƒƒƒƒ„„„„„„ƒƒ€€€€������‚‚‚‚‚‚‚‚��‚‚ƒƒƒƒƒƒƒƒƒƒ„„„„ƒƒƒƒ„„‚‚����‚‚„„…………††‡‡‰‰��““����‘‘‘‘‘‘��������‘‘‘‘’’’’““””••––••””’’‘‘’’’’’’’’��‹‹‹‹‹‹ˆˆ††††††††††……††……††††…………††††††††††„„„„ƒƒƒƒ„„„„„„ƒƒ€€€€������‚‚‚‚‚‚‚‚��‚‚ƒƒƒƒƒƒƒƒƒƒ„„„„ƒƒƒƒ„„‚‚����‚‚„„…………††‡‡‰‰��““����‘‘‘‘‘‘��������‘‘‘‘’’’’““””••––••””’’‘‘’’’’’’’’��‹‹‹‹‹‹ˆˆ††††††††††……††……††††…………††††††††††„„„„ƒƒƒƒ„„„„„„ƒƒ€€€€������‚‚‚‚‚‚‚‚ƒƒ‚‚‚‚ƒƒƒƒƒƒƒƒ„„„„ƒƒƒƒƒƒƒƒ‚‚��‚‚ƒƒ„„……‡‡ˆˆˆˆ’’¥¥››‘‘””““’’’’����������‘‘‘‘““––˜˜˜˜••’’��ŽŽŽŽŽŽŽŽŽŽ��‹‹ŒŒŠŠ††††††††††††††††††††††††††……††††††††††……ƒƒ„„„„„„„„ƒƒ€€€€������‚‚‚‚‚‚‚‚ƒƒ‚‚‚‚ƒƒƒƒƒƒƒƒ„„„„ƒƒƒƒƒƒƒƒ‚‚��‚‚ƒƒ„„……‡‡ˆˆˆˆ’’¥¥››‘‘””““’’’’����������‘‘‘‘““––˜˜˜˜••’’��ŽŽŽŽŽŽŽŽŽŽ��‹‹ŒŒŠŠ††††††††††††††††††††††††††……††††††††††……ƒƒ„„„„„„„„ƒƒ€€����������‚‚‚‚‚‚ƒƒ‚‚‚‚ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒ‚‚����‚‚ƒƒ…………††……••¬¬¥¥––––––””““““’’‘‘‘‘������’’––˜˜˜˜——””’’����������ŽŽŒŒŒŒ‰‰††††††††††††††……‡‡††††‡‡††††††‡‡††††††††……„„„„……„„„„€€����������‚‚‚‚‚‚ƒƒ‚‚‚‚ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒ‚‚����‚‚ƒƒ…………††……••¬¬¥¥––––––””““““’’‘‘‘‘������’’––˜˜˜˜——””’’����������ŽŽŒŒŒŒ‰‰††††††††††††††……‡‡††††‡‡††††††‡‡††††††††……„„„„……„„„„€€��‚‚‚‚��‚‚‚‚‚‚ƒƒƒƒƒƒ‚‚ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒ„„ƒƒ‚‚������‚‚‚‚ƒƒ„„––±±¬¬œœ––™™––””““’’’’’’������’’””——˜˜˜˜——––––––––””””’’��ŒŒ‰‰…………††††††††††††‡‡††‡‡‡‡††‡‡‡‡‡‡††††††††††……„„„„„„„„€€��‚‚‚‚��‚‚‚‚‚‚ƒƒƒƒƒƒ‚‚ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒ„„ƒƒ‚‚������‚‚‚‚ƒƒ„„––±±¬¬œœ––™™––””““’’’’’’������’’””——˜˜˜˜——––––––––””””’’��ŒŒ‰‰…………††††††††††††‡‡††‡‡‡‡††‡‡‡‡‡‡††††††††††……„„„„„„„„€€��‚‚����‚‚ƒƒƒƒ‚‚ƒƒƒƒƒƒƒƒƒƒ„„ƒƒƒƒ„„ƒƒƒƒƒƒƒƒ„„ƒƒ����������‚‚‚‚’’³³°°££™™™™˜˜——””““““““‘‘����‘‘‘‘““••——˜˜˜˜™™™™šš™™˜˜––��ŠŠˆˆ………………††††††††††††††‡‡‡‡‡‡‡‡‡‡††††††††††††††††……„„„„€€��‚‚����‚‚ƒƒƒƒ‚‚ƒƒƒƒƒƒƒƒƒƒ„„ƒƒƒƒ„„ƒƒƒƒƒƒƒƒ„„ƒƒ����������‚‚‚‚’’³³°°££™™™™˜˜——””““““““‘‘����‘‘‘‘““••——˜˜˜˜™™™™šš™™˜˜––��ŠŠˆˆ………………††††††††††††††‡‡‡‡‡‡‡‡‡‡††††††††††††††††……„„„„������‚‚‚‚‚‚ƒƒ„„ƒƒƒƒƒƒƒƒƒƒƒƒ„„ƒƒƒƒ„„ƒƒƒƒ„„„„„„„„‚‚��������‚‚��ŒŒ¯¯³³©©¡¡™™šš™™––””””””““‘‘��������‘‘””––——˜˜˜˜™™————––��‰‰‡‡††††……††††††††††††††‡‡††‡‡‡‡‡‡‡‡††‡‡††††††††‡‡‡‡††……������‚‚‚‚‚‚ƒƒ„„ƒƒƒƒƒƒƒƒƒƒƒƒ„„ƒƒƒƒ„„ƒƒƒƒ„„„„„„„„‚‚��������‚‚��ŒŒ¯¯³³©©¡¡™™šš™™––””””””““‘‘��������‘‘””––——˜˜˜˜™™————––��‰‰‡‡††††……††††††††††††††‡‡††‡‡‡‡‡‡‡‡††‡‡††††††††‡‡‡‡††……������‚‚‚‚‚‚ƒƒƒƒƒƒ‚‚ƒƒƒƒƒƒƒƒ„„ƒƒƒƒ„„ƒƒ„„„„„„ƒƒ„„ƒƒ������������……¦¦´´ªª©©  šš™™˜˜••••––••““‘‘����������’’““••––––••••””��ˆˆ††††††††††††………………††††††††‡‡‡‡ˆˆ‡‡††‡‡††††‡‡‡‡‡‡‡‡‡‡‡‡������‚‚‚‚‚‚ƒƒƒƒƒƒ‚‚ƒƒƒƒƒƒƒƒ„„ƒƒƒƒ„„ƒƒ„„„„„„ƒƒ„„ƒƒ������������……¦¦´´ªª©©  šš™™˜˜••••––••““‘‘����������’’““••––––••••””��ˆˆ††††††††††††………………††††††††‡‡‡‡ˆˆ‡‡††‡‡††††‡‡‡‡‡‡‡‡‡‡‡‡������‚‚ƒƒ‚‚‚‚‚‚‚‚‚‚ƒƒƒƒƒƒƒƒ„„ƒƒƒƒƒƒ„„„„„„ƒƒƒƒ„„„„ƒƒ��€€����€€��››²²««¨¨¨¨¢¢››˜˜——••————””’’‘‘��ŽŽŽŽŽŽ����’’““““’’““��‹‹‡‡……††‡‡††††††††††††††††……††‡‡‡‡‡‡††‡‡‡‡‡‡‡‡‡‡‡‡‡‡††‡‡‡‡������‚‚ƒƒ‚‚‚‚‚‚‚‚‚‚ƒƒƒƒƒƒƒƒ„„ƒƒƒƒƒƒ„„„„„„ƒƒƒƒ„„„„ƒƒ��€€����€€��››²²««¨¨¨¨¢¢››˜˜——••————””’’‘‘��ŽŽŽŽŽŽ����’’““““’’““��‹‹‡‡……††‡‡††††††††††††††††……††‡‡‡‡‡‡††‡‡‡‡‡‡‡‡‡‡‡‡‡‡††‡‡‡‡€€����‚‚ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒ„„„„„„„„ƒƒƒƒƒƒ„„„„„„‚‚��������€€��ªª®®©©££§§¤¤™™˜˜––˜˜˜˜••““’’����ŽŽ��ŽŽŽŽ������������ŠŠ‡‡††††††‡‡††††††‡‡‡‡‡‡††††‡‡‡‡‡‡‡‡‡‡ˆˆ‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡€€����‚‚ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒ„„„„„„„„ƒƒƒƒƒƒ„„„„„„‚‚��������€€��ªª®®©©££§§¤¤™™˜˜––˜˜˜˜••““’’����ŽŽ��ŽŽŽŽ������������ŠŠ‡‡††††††‡‡††††††‡‡‡‡‡‡††††‡‡‡‡‡‡‡‡‡‡ˆˆ‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡€€����‚‚ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒ„„ƒƒ„„„„„„„„ƒƒƒƒ„„„„„„ƒƒ��€€����~~„„��­­ªª¤¤  ££��˜˜————˜˜––””““’’����ŽŽ��ŒŒ����ŽŽ��ŒŒŒŒ‘‘ŽŽ‡‡††††††††††‡‡‡‡‡‡‡‡‡‡‡‡††††††‡‡‡‡‡‡‡‡‡‡††‡‡‡‡‡‡‡‡‡‡‡‡€€����‚‚ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒ„„ƒƒ„„„„„„„„ƒƒƒƒ„„„„„„ƒƒ��€€����~~„„��­­ªª¤¤  ££��˜˜————˜˜––””““’’����ŽŽ��ŒŒ����ŽŽ��ŒŒŒŒ‘‘ŽŽ‡‡††††††††††‡‡‡‡‡‡‡‡‡‡‡‡††††††‡‡‡‡‡‡‡‡‡‡††‡‡‡‡‡‡‡‡‡‡‡‡€€��‚‚ƒƒƒƒƒƒ‚‚ƒƒƒƒ‚‚‚‚ƒƒƒƒƒƒ„„„„ƒƒ„„„„„„„„„„ƒƒƒƒ„„„„„„‚‚��€€€€||||‰‰  ©©¦¦¢¢ŸŸ  ››™™——––––••““’’‘‘������ŽŽ����������““¢¢¢¢‘‘‡‡††††††‡‡††††‡‡‡‡‡‡‡‡‡‡‡‡††‡‡‡‡‡‡‡‡‡‡‡‡ˆˆˆˆ‡‡ˆˆ‡‡‡‡€€��‚‚ƒƒƒƒƒƒ‚‚ƒƒƒƒ‚‚‚‚ƒƒƒƒƒƒ„„„„ƒƒ„„„„„„„„„„ƒƒƒƒ„„„„„„‚‚��€€€€||||‰‰  ©©¦¦¢¢ŸŸ  ››™™——––––••““’’‘‘������ŽŽ����������““¢¢¢¢‘‘‡‡††††††‡‡††††‡‡‡‡‡‡‡‡‡‡‡‡††‡‡‡‡‡‡‡‡‡‡‡‡ˆˆˆˆ‡‡ˆˆ‡‡‡‡€€‚‚ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒ„„„„„„ƒƒ„„„„ƒƒ„„„„„„„„„„„„„„„„‚‚€€}}||{{||……™™¥¥££  ŸŸŸŸ™™™™˜˜””••””’’‘‘‘‘��������������’’ŸŸ¦¦ªª¦¦””‡‡††††††‡‡‡‡‡‡‡‡‡‡‡‡‡‡ˆˆ‡‡‡‡‡‡‡‡‡‡‡‡‡‡ˆˆˆˆˆˆˆˆˆˆ‡‡€€‚‚ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒ„„„„„„ƒƒ„„„„ƒƒ„„„„„„„„„„„„„„„„‚‚€€}}||{{||……™™¥¥££  ŸŸŸŸ™™™™˜˜””••””’’‘‘‘‘��������������’’ŸŸ¦¦ªª¦¦””‡‡††††††‡‡‡‡‡‡‡‡‡‡‡‡‡‡ˆˆ‡‡‡‡‡‡‡‡‡‡‡‡‡‡ˆˆˆˆˆˆˆˆˆˆ‡‡€€‚‚ƒƒƒƒƒƒƒƒƒƒƒƒƒƒ‚‚ƒƒƒƒƒƒ„„„„„„„„……„„„„„„„„„„„„„„„„„„……‚‚}}{{{{{{{{||����œœ¡¡ŸŸ  œœ––™™––••””’’‘‘‘‘‘‘����‘‘��ŽŽŒŒ““  ¤¤©©¬¬§§””‡‡††††††‡‡‡‡††‡‡‡‡‡‡ˆˆˆˆ‡‡‡‡‡‡‡‡‡‡‡‡ˆˆˆˆˆˆˆˆˆˆ‡‡€€‚‚ƒƒƒƒƒƒƒƒƒƒƒƒƒƒ‚‚ƒƒƒƒƒƒ„„„„„„„„……„„„„„„„„„„„„„„„„„„……‚‚}}{{{{{{{{||����œœ¡¡ŸŸ  œœ––™™––••””’’‘‘‘‘‘‘����‘‘��ŽŽŒŒ““  ¤¤©©¬¬§§””‡‡††††††‡‡‡‡††‡‡‡‡‡‡ˆˆˆˆ‡‡‡‡‡‡‡‡‡‡‡‡ˆˆˆˆˆˆˆˆˆˆ‡‡€€‚‚ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒ„„ƒƒ„„„„„„„„„„„„„„„„……„„„„„„……„„€€{{{{{{{{{{{{{{}}‡‡––ŸŸžžŸŸ˜˜••––––””’’‘‘‘‘‘‘‘‘��������‹‹‘‘žž¤¤ªª««¬¬££‹‹„„††††‡‡††††††‡‡‡‡‡‡ˆˆˆˆˆˆ‡‡‡‡ˆˆˆˆˆˆ‡‡ˆˆˆˆˆˆ‡‡€€‚‚ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒ„„ƒƒ„„„„„„„„„„„„„„„„……„„„„„„……„„€€{{{{{{{{{{{{{{}}‡‡––ŸŸžžŸŸ˜˜••––––””’’‘‘‘‘‘‘‘‘��������‹‹‘‘žž¤¤ªª««¬¬££‹‹„„††††‡‡††††††‡‡‡‡‡‡ˆˆˆˆˆˆ‡‡‡‡ˆˆˆˆˆˆ‡‡ˆˆˆˆˆˆ‡‡€€‚‚ƒƒƒƒƒƒ„„ƒƒ„„„„ƒƒƒƒ„„„„ƒƒ„„„„„„„„„„„„„„…………ƒƒƒƒƒƒ‚‚€€}}zz{{{{{{{{{{{{{{||‚‚��˜˜ŸŸ��••””””••””““’’‘‘‘‘��������ŒŒ��œœ¥¥ªªªª±±¬¬��‚‚ƒƒ……‡‡‡‡††‡‡††††‡‡‡‡‡‡ˆˆˆˆˆˆˆˆ‰‰ˆˆ‰‰‰‰ˆˆ††††€€‚‚ƒƒƒƒƒƒ„„ƒƒ„„„„ƒƒƒƒ„„„„ƒƒ„„„„„„„„„„„„„„…………ƒƒƒƒƒƒ‚‚€€}}zz{{{{{{{{{{{{{{||‚‚��˜˜ŸŸ��••””””••””““’’‘‘‘‘��������ŒŒ��œœ¥¥ªªªª±±¬¬��‚‚ƒƒ……‡‡‡‡††‡‡††††‡‡‡‡‡‡ˆˆˆˆˆˆˆˆ‰‰ˆˆ‰‰‰‰ˆˆ††††€€‚‚ƒƒƒƒƒƒƒƒ„„„„„„„„„„ƒƒƒƒ„„„„„„……„„„„……ƒƒ‚‚‚‚€€~~||}}||zzzz{{{{{{zz{{zz{{{{}}ƒƒŒŒ••““’’““””““““’’‘‘������ŽŽŽŽ����™™¥¥©©°°¾¾°°��……„„„„„„……††‡‡‡‡‡‡‡‡‡‡ˆˆˆˆˆˆˆˆ††‡‡ŒŒ““��„„||~~€€‚‚ƒƒƒƒƒƒƒƒ„„„„„„„„„„ƒƒƒƒ„„„„„„……„„„„……ƒƒ‚‚‚‚€€~~||}}||zzzz{{{{{{zz{{zz{{{{}}ƒƒŒŒ••““’’““””““““’’‘‘������ŽŽŽŽ����™™¥¥©©°°¾¾°°��……„„„„„„……††‡‡‡‡‡‡‡‡‡‡ˆˆˆˆˆˆˆˆ††‡‡ŒŒ““��„„||~~€€‚‚ƒƒƒƒƒƒ„„„„„„„„„„„„„„„„„„„„…………„„ƒƒ‚‚}}||||||||{{||||{{{{zz{{{{{{{{{{zz{{{{zz}}‚‚††ŠŠ��’’““’’������ŽŽ��������™™££®®½½ÁÁ¥¥ˆˆ††††††„„ƒƒƒƒƒƒ††††‡‡††ŒŒ””��‡‡€€��œœœœ��††ŠŠ‹‹€€‚‚ƒƒƒƒƒƒ„„„„„„„„„„„„„„„„„„„„…………„„ƒƒ‚‚}}||||||||{{||||{{{{zz{{{{{{{{{{zz{{{{zz}}‚‚††ŠŠ��’’““’’������ŽŽ��������™™££®®½½ÁÁ¥¥ˆˆ††††††„„ƒƒƒƒƒƒ††††‡‡††ŒŒ””��‡‡€€��œœœœ��††ŠŠ‹‹��ƒƒƒƒƒƒ„„…………„„„„„„……„„„„„„„„ƒƒ‚‚€€~~}}}}||||||||||{{||}}||{{{{{{{{||{{{{{{||||{{||}}}}}}~~ƒƒŠŠ����ŽŽŽŽŽŽ��‹‹‹‹ŒŒŽŽ——¡¡®®³³±±––‚‚………………††„„‚‚€€��‚‚††„„ŽŽ��’’‰‰ŒŒ˜˜��››˜˜ššœœ™™��ƒƒƒƒƒƒ„„…………„„„„„„……„„„„„„„„ƒƒ‚‚€€~~}}}}||||||||||{{||}}||{{{{{{{{||{{{{{{||||{{||}}}}}}~~ƒƒŠŠ����ŽŽŽŽŽŽ��‹‹‹‹ŒŒŽŽ——¡¡®®³³±±––‚‚………………††„„‚‚€€��‚‚††„„ŽŽ��’’‰‰ŒŒ˜˜��››˜˜ššœœ™™ \ No newline at end of file diff --git a/bin/q_matrix.cfg b/bin/q_matrix.cfg new file mode 100644 index 0000000..147b061 --- /dev/null +++ b/bin/q_matrix.cfg @@ -0,0 +1,118 @@ +# This is the configuration file for initialising the Q matrix. +# Altogether 6 matrix for 4x4 block and 2 matrix for 8x8 block +# The values range from 1 to 255 +# If first value of matrix is equal to 0, default values will be used for that matrix +#################################################################################### + +INTRA4X4_LUMA = +17,17,16,16, +17,16,15,15, +16,15,15,15, +16,15,15,15 + +INTRA4X4_CHROMAU = + 6,12,19,26, +12,19,26,31, +19,26,31,35, +26,31,35,39 + +INTRA4X4_CHROMAV = + 6,12,19,26, +12,19,26,31, +19,26,31,35, +26,31,35,40 + +INTER4X4_LUMA = +17,17,16,16, +17,16,15,15, +16,15,15,15, +16,15,15,14 + +INTER4X4_CHROMAU = + 9,13,18,21, +13,18,21,24, +18,21,24,27, +21,24,27,30 + +INTER4X4_CHROMAU = + 0,13,18,21, +13,18,21,24, +18,21,24,27, +21,24,27,30 + +INTER4X4_CHROMAV = + 9,13,18,21, +13,18,21,24, +18,21,24,27, +21,24,27,27 + +INTRA8X8_LUMA = +16,16,17,16,16,16,16,16, +16,17,16,16,16,15,15,15, +17,16,16,16,15,15,15,15, +16,16,16,15,15,15,15,15, +16,16,15,15,15,15,15,15, +16,15,15,15,15,15,15,16, +16,15,15,15,15,15,16,16, +16,15,15,15,15,16,16,16 + + +INTER8X8_LUMA = +15,16,17,16,16,16,16,16, +16,17,16,16,16,15,15,15, +17,16,16,16,15,15,15,15, +16,16,16,15,15,15,15,15, +16,16,15,15,15,15,15,15, +16,15,15,15,15,15,15,16, +16,15,15,15,15,15,16,16, +16,15,15,15,15,16,16,16 + +INTRA8X8_CHROMAU = +14,16,17,16,16,16,16,16, +16,17,16,16,16,15,15,15, +17,16,16,16,15,15,15,15, +16,16,16,15,15,15,15,15, +16,16,15,15,15,15,15,15, +16,15,15,15,15,15,15,16, +16,15,15,15,15,15,16,16, +16,15,15,15,15,16,16,16 + +INTER8X8_CHROMAU = +13,16,17,16,16,16,16,16, +16,17,16,16,16,15,15,15, +17,16,16,16,15,15,15,15, +16,16,16,15,15,15,15,15, +16,16,15,15,15,15,15,15, +16,15,15,15,15,15,15,16, +16,15,15,15,15,15,16,16, +16,15,15,15,15,16,16,16 + +INTER8X8_CHROMAU = + 0,16,17,16,16,16,16,16, +16,17,16,16,16,15,15,15, +17,16,16,16,15,15,15,15, +16,16,16,15,15,15,15,15, +16,16,15,15,15,15,15,15, +16,15,15,15,15,15,15,16, +16,15,15,15,15,15,16,16, +16,15,15,15,15,16,16,16 + +INTRA8X8_CHROMAV = +12,16,17,16,16,16,16,16, +16,17,16,16,16,15,15,15, +17,16,16,16,15,15,15,15, +16,16,16,15,15,15,15,15, +16,16,15,15,15,15,15,15, +16,15,15,15,15,15,15,16, +16,15,15,15,15,15,16,16, +16,15,15,15,15,16,16,16 + +INTER8X8_CHROMAV = +11,16,17,16,16,16,16,16, +16,17,16,16,16,15,15,15, +17,16,16,16,15,15,15,15, +16,16,16,15,15,15,15,15, +16,16,15,15,15,15,15,15, +16,15,15,15,15,15,15,16, +16,15,15,15,15,15,16,16, +16,15,15,15,15,16,16,16 diff --git a/bin/q_matrix2.cfg b/bin/q_matrix2.cfg new file mode 100644 index 0000000..3ab068c --- /dev/null +++ b/bin/q_matrix2.cfg @@ -0,0 +1,61 @@ +# This is the configuration file for initialising the Q matrix. +# Altogether 6 matrix for 4x4 block and 2 matrix for 8x8 block +# The values range from 1 to 255 +# If first value of matrix is equal to 0, default values will be used for that matrix +#################################################################################### + +INTRA4X4_LUMA = + 7,16,22,24, +16,22,24,28, +18,22,27,33, +22,24,32,47 + +INTRA4X4_CHROMAU = + 7,16,22,24, +16,22,24,28, +18,22,27,33, +22,24,32,47 + +INTRA4X4_CHROMAV = + 7,16,22,24, +16,22,24,28, +18,22,27,33, +22,24,32,47 + +INTER4X4_LUMA = +13,15,17,18, +15,17,18,20, +17,18,21,22, +18,20,22,25 + +INTER4X4_CHROMAU = +13,15,17,18, +15,17,18,20, +17,18,21,22, +18,20,22,25 + +INTER4X4_CHROMAV = +13,15,17,18, +15,17,18,20, +17,18,21,22, +18,20,22,25 + +INTRA8X8_LUMA = + 7,13,16,18,22,22,24,28, +13,13,18,20,22,24,28,31, +16,18,22,22,24,28,28,32, +18,18,22,22,24,28,31,33, +18,22,22,24,27,29,33,40, +22,22,24,27,29,33,40,48, +22,22,24,28,32,38,47,57, +22,24,29,32,38,47,57,69 + +INTER8X8_LUMA = +13,14,15,16,17,17,18,19, +14,15,16,17,17,18,19,20, +15,16,17,17,18,19,20,21, +16,17,17,18,19,20,21,22, +17,17,18,19,21,22,22,23, +17,18,19,20,22,22,23,25, +18,19,20,22,22,23,25,26, +19,20,21,22,23,25,26,27 diff --git a/bin/q_matrix_def.cfg b/bin/q_matrix_def.cfg new file mode 100644 index 0000000..046d2d2 --- /dev/null +++ b/bin/q_matrix_def.cfg @@ -0,0 +1,61 @@ +# This is the configuration file for initialising the Q matrix. +# Altogether 6 matrix for 4x4 block and 2 matrix for 8x8 block +# The values range from 1 to 255 +# If first value of matrix is equal to 0, default values will be used for that matrix +#################################################################################### + +INTRA4X4_LUMA = + 0,12,19,26, +12,19,26,31, +19,26,31,35, +26,31,35,39 + +INTRA4X4_CHROMAU = + 0,12,19,26, +12,19,26,31, +19,26,31,35, +26,31,35,39 + +INTRA4X4_CHROMAV = + 0,12,19,26, +12,19,26,31, +19,26,31,35, +26,31,35,39 + +INTER4X4_LUMA = + 0,13,18,21, +13,18,21,24, +18,21,24,27, +21,24,27,30 + +INTER4X4_CHROMAU = + 0,13,18,21, +13,18,21,24, +18,21,24,27, +21,24,27,30 + +INTER4X4_CHROMAV = + 0,13,18,21, +13,18,21,24, +18,21,24,27, +21,24,27,30 + +INTRA8X8_LUMA = + 0,10,13,16,19,24,26,28 +10,12,16,19,24,26,28,31 +13,16,19,24,26,28,31,33 +16,19,24,26,28,31,33,35 +19,24,26,28,31,33,35,37 +24,26,28,31,33,35,37,39 +26,28,31,33,35,37,39,42 +28,31,33,35,37,39,42,44 + +INTER8X8_LUMA = + 0,12,14,16,18,19,21,22, +12,13,16,18,19,21,22,24, +14,16,18,19,21,22,24,25, +16,18,19,21,22,24,25,27, +18,19,21,22,24,25,27,28, +19,21,22,24,25,27,28,30, +21,22,24,25,27,28,30,31, +22,24,25,27,28,30,31,33 diff --git a/bin/q_offset.cfg b/bin/q_offset.cfg new file mode 100644 index 0000000..ef46109 --- /dev/null +++ b/bin/q_offset.cfg @@ -0,0 +1,147 @@ +# This is the configuration file for initialising the Q Offset matrices. +# Altogether 6 matrix for 4x4 block and 2 matrix for 8x8 block +# The values can take any value, positive or negative and represent the numerator of +# a fractional offset which is in the form of value/2048 (extended from 1024) +# FOR THE 444 COMMON CASE, THE OFFSET MATRICES FOR LUMA WILL BE USED THE STARTING POINT +# FOR ALL THREE CHANNELS. +#################################################################################### + +INTRA4X4_LUMA_INTRA = +1024,742,742,742, +742,742,742,742, +742,742,742,742, +742,742,742,742 + +INTRA4X4_CHROMAU_INTRA = +742,742,742,742, +742,742,742,742, +742,742,742,742, +742,742,742,742 + +INTRA4X4_CHROMAV_INTRA = +742,742,742,742, +742,742,742,742, +742,742,742,742, +742,742,742,742 + +INTRA4X4_LUMA_INTERP = +400,400,400,400, +400,400,400,400, +400,400,400,400, +400,400,400,400 + +INTRA4X4_CHROMAU_INTERP = +400,400,400,400, +400,400,400,400, +400,400,400,400, +400,400,400,400 + +INTRA4X4_CHROMAV_INTERP = +400,400,400,400, +400,400,400,400, +400,400,400,400, +400,400,400,400 + +INTRA4X4_LUMA_INTERB = +400,400,400,400, +400,400,400,400, +400,400,400,400, +400,400,400,400 + +INTRA4X4_CHROMAU_INTERB = +400,400,400,400, +400,400,400,400, +400,400,400,400, +400,400,400,400 + +INTRA4X4_CHROMAV_INTERB = +400,400,400,400, +400,400,400,400, +400,400,400,400, +400,400,400,400 + +INTER4X4_LUMA_INTERP = +342,342,342,342, +342,342,342,342, +342,342,342,342, +342,342,342,342 + +INTER4X4_CHROMAU_INTERP = +342,342,342,342, +342,342,342,342, +342,342,342,342, +342,342,342,342 + +INTER4X4_CHROMAV_INTERP = +342,342,342,342, +342,342,342,342, +342,342,342,342, +342,342,342,342 + +INTER4X4_LUMA_INTERB = +342,342,342,342, +342,342,342,342, +342,342,342,342, +342,342,342,342 + +INTER4X4_CHROMAU_INTERB = +342,342,342,342, +342,342,342,342, +342,342,342,342, +342,342,342,342 + +INTER4X4_CHROMAV_INTERB = +342,342,342,342, +342,342,342,342, +342,342,342,342, +342,342,342,342 + +INTRA8X8_LUMA_INTRA = +1024,742,742,742,742,742,742,742, +742,742,742,742,742,742,742,742, +742,742,742,742,742,742,742,742, +742,742,742,742,742,742,742,742, +742,742,742,742,742,742,742,742, +742,742,742,742,742,742,742,742, +742,742,742,742,742,742,742,742, +742,742,742,742,742,742,742,742 + +INTRA8X8_LUMA_INTERP = +742,742,742,742,742,742,742,742, +742,742,742,742,742,742,742,742, +742,742,742,742,742,742,742,742, +742,742,742,742,742,742,742,742, +742,742,742,742,742,742,742,742, +742,742,742,742,742,742,742,742, +742,742,742,742,742,742,742,742, +742,742,742,742,742,742,742,742 + +INTRA8X8_LUMA_INTERB = +742,742,742,742,742,742,742,742, +742,742,742,742,742,742,742,742, +742,742,742,742,742,742,742,742, +742,742,742,742,742,742,742,742, +742,742,742,742,742,742,742,742, +742,742,742,742,742,742,742,742, +742,742,742,742,742,742,742,742, +742,742,742,742,742,742,742,742 + +INTER8X8_LUMA_INTERP = +342,342,342,342,342,342,342,342, +342,342,342,342,342,342,342,342, +342,342,342,342,342,342,342,342, +342,342,342,342,342,342,342,342, +342,342,342,342,342,342,342,342, +342,342,342,342,342,342,342,342, +342,342,342,342,342,342,342,342, +342,342,342,342,342,342,342,342 + +INTER8X8_LUMA_INTERB = +342,342,342,342,342,342,342,342, +342,342,342,342,342,342,342,342, +342,342,342,342,342,342,342,342, +342,342,342,342,342,342,342,342, +342,342,342,342,342,342,342,342, +342,342,342,342,342,342,342,342, +342,342,342,342,342,342,342,342, +342,342,342,342,342,342,342,342 diff --git a/bin/sg0conf.cfg b/bin/sg0conf.cfg new file mode 100644 index 0000000..a1d8d15 --- /dev/null +++ b/bin/sg0conf.cfg @@ -0,0 +1,9 @@ +24 +20 +10 +15 + + + +each line contains one run_length_minus1 + diff --git a/bin/sg2conf.cfg b/bin/sg2conf.cfg new file mode 100644 index 0000000..30a66da --- /dev/null +++ b/bin/sg2conf.cfg @@ -0,0 +1,11 @@ +24 +122 +58 +108 + + + +odd number of rows: top left mapunit number +even number of rows: bottom right of mapunit number + + diff --git a/bin/sg6conf.cfg b/bin/sg6conf.cfg new file mode 100644 index 0000000..ada326f --- /dev/null +++ b/bin/sg6conf.cfg @@ -0,0 +1,396 @@ +0 +1 +1 +0 +1 +1 +1 +1 +1 +0 +1 +1 +1 +1 +1 +0 +1 +1 +1 +1 +1 +1 +0 +1 +1 +1 +1 +1 +1 +1 +1 +1 +1 +1 +1 +1 +1 +0 +0 +1 +1 +1 +1 +1 +1 +1 +1 +1 +1 +1 +0 +0 +1 +1 +1 +1 +0 +1 +1 +1 +0 +1 +0 +1 +1 +1 +1 +1 +1 +0 +1 +1 +1 +1 +0 +0 +0 +1 +1 +0 +0 +1 +1 +1 +0 +1 +1 +1 +0 +1 +0 +0 +1 +1 +1 +0 +0 +1 +1 +1 +1 +0 +1 +0 +0 +1 +0 +0 +1 +1 +1 +1 +0 +0 +0 +0 +1 +1 +0 +1 +0 +0 +1 +1 +1 +0 +0 +1 +1 +1 +1 +1 +1 +1 +1 +0 +1 +1 +0 +1 +1 +0 +1 +1 +0 +1 +0 +1 +1 +1 +1 +0 +0 +0 +1 +0 +1 +1 +0 +0 +1 +1 +1 +1 +0 +1 +1 +0 +1 +1 +0 +1 +0 +1 +1 +1 +0 +1 +1 +0 +1 +1 +1 +0 +1 +1 +0 +0 +1 +1 +1 +0 +0 +1 +1 +1 +0 +0 +1 +0 +1 +1 +0 +0 +1 +1 +0 +0 +1 +1 +1 +1 +1 +1 +1 +0 +0 +1 +0 +0 +0 +1 +1 +1 +1 +0 +1 +0 +0 +1 +1 +0 +0 +1 +1 +0 +0 +0 +0 +0 +1 +1 +0 +0 +0 +1 +1 +1 +1 +0 +0 +1 +1 +1 +1 +0 +0 +1 +0 +1 +1 +1 +1 +1 +0 +1 +1 +1 +1 +0 +1 +1 +1 +1 +1 +0 +1 +1 +0 +0 +1 +1 +0 +1 +1 +1 +1 +0 +0 +1 +0 +1 +1 +1 +0 +0 +1 +1 +1 +1 +1 +0 +1 +0 +1 +1 +1 +1 +1 +1 +1 +1 +1 +1 +1 +0 +1 +1 +0 +0 +1 +1 +1 +1 +1 +1 +1 +0 +1 +0 +0 +0 +1 +1 +0 +1 +1 +1 +1 +1 +0 +0 +0 +1 +0 +1 +0 +1 +1 +0 +1 +0 +0 +1 +1 +1 +1 +1 +1 +1 +1 +1 +1 +0 +1 +1 +0 +1 +1 +0 +1 +0 +0 +1 +1 +1 +1 +0 +1 +1 +1 +0 +1 +1 +1 +0 +0 +0 +1 +0 +0 +1 +0 +1 +1 +0 diff --git a/disclaimer.txt b/disclaimer.txt new file mode 100644 index 0000000..7d84da2 --- /dev/null +++ b/disclaimer.txt @@ -0,0 +1,25 @@ +/* + * Disclaimer of Warranty + * + * These software programs are available to the user without any + * license fee or royalty on an "as is" basis. The ITU-T + * disclaims any and all warranties, whether express, implied, or + * statutory, including any implied warranties of merchantability + * or of fitness for a particular purpose. In no event shall the + * copyright-holder be liable for any incidental, punitive, or + * consequential damages of any kind whatsoever arising from the + * use of these programs. + * + * This disclaimer of warranty extends to the user of these programs + * and user's customers, employees, agents, transferees, successors, + * and assigns. + * + * The ITU-T does not represent or warrant that the programs furnished + * hereunder are free of infringement of any third-party patents. + * Commercial implementations of ITU-T standards, including shareware, + * may be subject to royalty fees to patent holders. Information + * regarding the ITU patent policy is available from the ITU + * Web site at http://www.itu.int. + * + */ + diff --git a/doc/JM Reference Software Manual.pdf b/doc/JM Reference Software Manual.pdf new file mode 100644 index 0000000000000000000000000000000000000000..d1b1d0bad63795ab59e827dc587d5300a661d132 GIT binary patch literal 1195417 zcmdSBby!u~_CG8o9nz8;q+`S0bVzqeH%NDPH_{;?ASo@~4HAO10wU5S-I9XHy9v+H zd(XM``~LItJh0ho%{As&qvo9BGv=g}7ZGCw0ohQIC~F}( z&X`G9-$~!f)|5$J-_+O<0EXOFWRlUhF{LrKVN_9~1u!W&IXD|SDLWV&E85yR0l=K> zH*I9~9V{hnOl%=^uKzVQ0zlds^Fe-4k&JDOZu+>U_FHE+oiGX8I@>q_xHy=k&5azj z04&$^0ASYJj~nU$R_2>;thX0g?=EuQTx7ew$9BUjfbF&k+ierJ+a~O{P1tXnaNIVz zWgNhH+l1@33D<2CE{+@0TzB`lZoYw-Z`**Fucf9405RV-1~K2V3}U_|3}R-xyMN0B zi20Tpi20Tri205l=#C!fjvnZa9_Wr9=#C!fjvnZa9_Wr9=#C!fjvn}q9{7$P_>La< zj^3S2LEt-j;5&NYJ9^+ddf+>HEO+!+?&z`H(PO!z$8yIX%N;$IJ9;d4^jPlbvE0$S z(;|rVjvnhBJ=QyVcj^VP-qE|$Gl=z$9_t-F);oH4ng_An(YrGN5ZfKSJ6iy;-O;-< z3J}{Jy*ujwvE9+TGZXM#d+^}c!kU})?J@ij498>j0J z5a8w=nZ&?=o6eZTSO7P@F^RDPZtaLkj16#WK}=%ofLq()<71L{ur*XNcG6;kga#&M zV>c%zNo$Dj3*CMQ-+oA5Gsz@qV`J+C@z6UvyEC&pE4wu^9RQQ8v5~pHkgb~*=2{o-~vOg3qx3iyo4j*MtV%*4z|vAKcmjg7bYbpWe0s5N4x8nHFReZR$>w{b}=_J zRumV4^scOL;0S4Q8-H#E;s{{=Rd&~ef67kG+{($=fl16t-^p0S*wEI>2&wS4YG&U^cTQ^pF}{;rMd)OZlB7We?xCYYBZ!3Yf~FqDF7L1B$|x@iiZfuo zBPBdg_lKqKEQCd|V|!2%5uL7+2A;qdwKHT5mK2FWv(zG-e9wg-H&(u62igUqbp z@okL0Mu==ltYR67UG6hs%^azbf?`1!Ew6TPJaNvQKMqUc3>TC04ew3Yz?*=phKd_$W-E2Nhf|>wdDc3kSBrLUM}!emxNe}k37&UTkx5=ae!5{`posVREaNe z+l#As7u~f85MWyRYx4w~vQkb=7>9=EP$`T3KC|YO=$*5^x#w9{ZjyWs0w&?OcUQTb z=8>t;_0_~Y3!Y9l3}``#(Zvf>gTDOG%E8^+RnZl=r=f&lA1TGl>wqxm^Tqq!Ea}td z>mNh*o=0x-6k2zqb-76i<-N$53cTTS zDHuVnqcm?sSqA8n?-J4%ziB=e>Y z3Xle?;4Ruo)GJN`a*^j^HeL*~oohue0j+H-rV2?+T-rVyDV=gQ(iPis<{2Ex4h{G9 z@h>U9fu4Se{hBNsCpG*v8wqwjK08UJ0YRwNO4%nEZ%3sx-{`p^5>gAoZ{PE)vDnEd zUO*9Bb>$lxSA}i#wr!|$*E6R_Q;=B3!j6k#`QD!*64jMAb5hX5yl*(zj!&K7CIpE4 zV5ryW`ry@~Qa#6yaNM~}GOHN4aIZ3saTCMEq7RZI_|ZX~X?T3bC+~!6)Z97cNTt*#=~5rX$!6@Nc2i50WpUlY?3D>2wC?`erf)SIpyTxi+!Uqv%p$%4ptl zX?y5+n39l5x*VW4IT^wV4Nwfg4+3_+ISCiqo+1_O7Lpk0X{BGyWt-)1ccP^%+B>u4 zkDf-4b{c!7i60Mg4+VNpuCZx-ps1CrAz%)l)o{k~Z;5+26G^{h5E!YFjh>*PU*N}u$RyMgNtw5+V!U<1gF!XY1M`8*;(@Lq2t8!*FtG#0 z?-d@3`NSftB)yOQ8c5n`vA`GNW;S~kV}G345E_mDOd{da1NxxV^U%e{k#!A(o~(G` zQx`;h-s$n7#_3#MAA8)8P&Y+NcS2ns9nPlO%qoMNt67GboF=r6hTe*W9X+(y3Q%a& zGcPmx^AFh8tBv+TQ>LOB^2gjo;D?#PdWAHky^`yqNa3=RE2Tkoi;oBP7Yc<91?H4` zae{3Y+PsJ!Ay;qShi5^0#|DLgL+jxM|J)U=7H1|>%pIsr7+gvj7iX0z^**lEhj?vY zDipyGv+=E>M7lsY{Zy~Biy!W$yhd55hs% ziG_K5B}Q9UWK$l4a)Vh0t-X>TXdSMadOTyV3}W{fVCQ4MxDyVhALojC*qPoXC+VV z1)41SS0h~wnh3Nz8g5#D_}-_A>2;J*O>7p?ef>+%S$_B@^|Nf8gnRAw=Gpik$paOMIC1vN_9-nwtY*JO+)wLqEq1cg zDBFn1N49NwRCooimTVt#vjG1&+x@u~zg{lGq(}6aWwzq`*lMRV@E>=b2}$n z2f)oLiAl`d!O=(0)}K1?*0dX*ddP_Ihi>EI5@a&TK+ntzrOHKT>eS=X8Ci& z;xBALru?Rk*PTd0G8yKEf;OgB#sFp}K}W;uB@`Dk8)Q{+`#&QX#0*Jz*hv_ho0>TR zIKZ4tf-a^v>;PCfxFCrPx7#mZcF3*kw^F-n$;`~8e)klZjTy4C(7zT0fQ1FJJTbOb zg*<)JT)qrve}j z!Th&Umo$P{mAR8Uqr{zA8R^?NU7Ly{BtHb9tH`8mt72n*y#T$o@SDZ?ud=;qbkn&O z;5HAW1Nbvzc5~$?Yrp&8AN&bRid^@1?XKY49NzU;Cdg%FTX9JdS$#XEpG-1|s6n`b z$PRK-$=wlRs@Ewyz-`{?Mqqzx6>yt{Vp6+i`zDp}8{5~Zh@Xr-aWFD=fY?9H&we0j zLPcX!b4bi_rxAqY{*15lhR$|&R>s!XKKR?B{id6rl>UfBfAVp|)}KE9{}1cBVH%Qx zFoq;QIBv6uze(@cOvNAX{D(&WA^NiZX?)7&*2a#Hz|3;Su8_Zqw$}PK|8W4g%_9Cy z1OT&ve~AFs>?!`n@LvuWf2E|q;_zPw1Wqo{uMwI3mx#>H#qvu;X8tuGu(AIZk^e3r zu!4U^Wab;4+(l%_)7Jrk{Wc;)y7@gK-=xX@$tM3!KWzU}KMc3S(0{I`yZqf>50~Iy z)pQfme^b+64kiDgntn3+m)c?ku>)Cuj-mfjV~|#VjG@fnKSMZ(lLd66g1a&Fztk8j z#EWh;24aDvpV_%s|3PDaIY#}R#@PS0#{R3>&294iZ`=y>tG;f->~H$|%aP|l)z=N1 zf2plMW;_2%Qyd(>XbQ~nr=}qJ@85ij<8KDrKYa`Qn{PqV=D+wB2gmQ4x=9}Y^sS%s zO&N0=OURI6=mdd(*jRxONwIRW0wMW&$TSQHL1@^RS%7TUNn{}I`z4bImv<-dnueqt)WfH6Oz7l=sy3z+5K0w}lm-Tx7Sa)X8ZTov9>{%Q7q z523JevR_N==bF+jElRn=W||qL6~32$pS8EWQzDx}T0jBdoX#LP<`R}l5=R_ROYCCi zI%(|7FDb_}5DnjTk5S9(%Q6;i@?2MUx04P-x&Kq$&T(fbwQ)DrbV3ZI#(+&@Ud;B( zM*kG;HxU`_4xiglcO z0ZMF3a4)8t&Dlq+9`X%z+O|)1?OkSTw-+#&R+Nxb*~*vp%*i{q_B>Ci7tEbQdP_xD88qv99Z@tfB$9rjyOZA$2SOv}*>o<2e;5oU}g>TEc*(4)_v(W&^mF9Jpj3oGRNQ!@H< zpPM;{Z^tXG5|v7O!`X7Zv#(y45t8Q~_wRgYR1Dl8dI@_UYgc?Sv*Yw}>Q1;|UtfKZ zoW;=_VLvDoiPR4^hzCOzC0xORaksDcbL0_1veDG z%<@KmTDDrQP7*G{1ER=d8a*!YSSg1e5$TCvmg_y=wjnj)NS7#*6*yq`XAz%B!A+Ej z;C8$m&<^p-CB|f1N3=FLJ3e?apWX?Fv3E`S!0JxAw)P>>)^6Zu!{a>akZeZ%4MtL(^{%;-frynwjaR6>X z?LWa#h;NFzIf*MdL4ZQYXd?>(D5nG#X{ zX2$ot;!Trn+xh0w*2Tr;_|Rh0w{J_A8z*~sdsoh$4QKXO2V?fWR~t?(Emm#r$JU2K z7fDP&UDu({(K?4ex(4uC^jG=kjzE$yCeu7S63mc z>+rMHs_0^`zLbUqgMFi$7E2Z7_Pa=R+s-ytB37I0=h^XkwY?mh-tb>)W<-+^hY?H< zi!adkd}&YK#aB8hFxgJs{i^M4%-@`_3Ll@e))`(o;K=v#X+jh4L|uC7Rfsg(g+yzc z_B)npnud>zPu={7oA95#Fpn!-En4XkI(k>_SUH`UzBMa;@g8c2?m`tve6Fl5oXj7i zgP4xfgWEaNk||)Goee&jeQ6H#sVQFBsrU65uNc422C5=viAYW1LcHkTrf#ksEaT=$ zYUa6!x!5tf0(oxB?1HBUcZr85&Kube`5BJz9*sPVZxvTdHZFOz^!9#k6`<=MSWlR796v3OiXJj6Q@+WkgGS5O8$ z>E;jXYcJe$>;vKv*Wqoa7N|=e)_>h*O=Ucow2o{|T^2rkQ{aO8DHCk7vE~pV4Mp~} zd7R;KFV~1Wb%SClk<_7uYUF5;yYtH)=JKh*7qg+kVLLgbtEr`+ulINZ!WyC4*iy9z$s6|hEU{uT7fMukvEGyq8S%}`r?pwhP$s=!3DZDm z_4*=ne*x3P8myZvX^jO!S|4VUjK{=RX0nwdk(YAn&qtpNV*WmLntC`XtDqeXRjD0n z$ebBGH_xxJ-O6(1(mYn_?)+&74K1X+?Q_dYJmtc)=X?v>@ntm^BTw}Y$AMVzH>j!P z0v#;>d={=WR^=JwGini^=E;!=%lj@GHI9_l?0(MZdaEzlT$^4%8&33-?Co*HTDn}A zjxZd32!F6cyxPt-O}oCLC@?VcjN@c&`SXNS(7jHP=6syXZy1ZveD@0l@Tur9S|Xu` zhaA2dWlx$5eSQ0mb#h%BDU_N}A17vTAXTMe#vNEs>}uWhV(Z%jF-}q)uL-f(bnpNR z05*tMxFR95qZOL}$p(~TeDz68H8L{L%8!%|QKXd;%U1d&&hz~XWb#VM_W=bn_vc>C z;3|B^XwrxevwfjDKMezm7re)a|EAgP5Qr+4$3<8c$;FEe#Q?yD^}!++_1rJ>JF!&M zyf`gj;oFXmcHZ^gDRA*rBV~P)Y2U`OB<$=C!Hy`y#8A%KnrjOA9S|==)S(HMhj;f;(VG7wOh&J?fttHdQgCa5t zr!rz{iW1*JByG>Ixa|9B_IeITk%7o(`q|L>M1=x&EJzsm;ubtE$sDhy74!({m7J&B zpf^$tiCD(8$r8F0`d?`xCd8{xt`Z>xtfd)XJN0udWSRzK2WB{8!asE79Ds|B7LyFI z>5rF_W%v#ZP;bytjuc6%LTgCcd1;vbwvlKjD@~Yf^<6x^Bl}$1;}FCrmN*?~u`t8# zG)&KUl)C*qCDw?_Q;sLXoZEdLFSowRw>%tE{Mc&9CM@7^FMxZJU*ru78#|Fp(X9A@ z`)+-XsUSuppU&fw@#Gjhi!@xl?)N8$fy*M)OO$EC1(pR@!Zq#CMPIZ(9ZFRNU~I+|3(g0aG?ulB z^cOyhC1`RP#IH`VRdjAA0`srL0K>&6DSP3D(7bjS6Ne{51ACa(;&r3V;wsVg>wA+N1 zh9wWsAP$7XS}T%Bu^5bZFo+cBSH{XnpbsDhzz4|Eup8%phCQVVAF^u4QtWXHv66FW ze_iUvD^>s8s8RiDsXRl6BcJYBB@y|oF!Z8B*s7ZYDY0DuB~MOJ2>Q}nCMqYLV<|SC zF4p$PRM=Y-PqR9c7cW$v1LV*?%P7-ao9zVRzVi_bj`70GWjpw zm2EXN^mcgRZ0c#ZqvPWs z8g|EryO-!RaEjJJiW$5={GgeAkYG&u75B>mpeBA?W)6WCWIo!z-B>JhcNR+Vw|e5R zQ$}9CQyTs$WP0oIM=wso5o@#}l_R!>(4D3!tP(F+YQWt~fF z(rV#a_o7IS>BD%ji&x`@vsALr62iM4x>EU8-y_52kAnGrpq2ZEs>#qjCE;+}UZfF2 zhK7l%g>#QLEA2P6SH`8P9v1>E0~YlM;REeTibs(~o5lINKHjexHJshr8b6j&@=s59 z9CH1!N%37jhG)pu;`1!@@~%JfT1bYR^6oB)jqQ`&_zO3HC4UI_@K!UAcPQqebn2&P z%yeZXJA|jy(}K>SxLL=~@xN@!A0PkNozqRQp35jhA6C~--W@!3n{C+NvE4I-hMsUs zCFS#~@SP=I>eg}%J3X>%L95-47tccYKn&DWD=(oZMHluY({IOqtK2fpO(Mn? zq+AENW_W2x{>hpcT^!Y{bHNmzp9F}|&TJ3GkEY$;%!O!0ioR7=K#X2oqw4HUA06V% zvGlHscKM?w;+ItQNvfv^(62OA%xgCLVk3AC{UaNi&J`2Zy4GPAsFE|NKJXOET?%yyH}l*c?&uXC5t z9K{^vud|oY{P5sgnl(wh7-OEHBL7pjQca9~7%{j`GkO07Gi<=eqVUP$2_+Ruwz?s3 z_hwV|6&O#Bub-2X{L9#@FT(6tpQ%CBLwtIMs^_o#Kcq}CouI)z+|6Fgd@K6&`!^+} z7h=l~>Ug2XgSz%;nK7m5c=1!d+K%HnGkx?8^Q=84oy=moN5Ax7A4+EMt2Zxs3KH{1 zRP*{b2|~^db1x$UR+Q!ttpuUr&`%r&3w0z zjyG5$MWn0an+GK{TewRzDPcC^l#rZBBi|pexIT7^n|@NevEeo`Q)-j@6?G#_w{@%E ziaXsgJ(g-+<#ADSnVa9v^nnMq3Wnp3&GJ3M2@K3}$hD}Gm~U$J_I{7e+qV>wGt?P| z+YX7K6=L^cx+y#5?ZOr;5<=NFWX#eZB3#7FCv3_nXl|wsAO|g4ZzEa6timbNlQBw^ zSQWtO1>D>v;8BVgecK_Q;G!i7HJk7r(ZXP{9`o~O1}W!!8b&9bg`(Dl52k5aCa@>v zZ?Y!ujlY<5?^4GJ^+VM2mw&k_W3IWK_UK_zAad7M!0RN_fKmMSG4+7kDuM=)GrAOnitkH z#L8}#8CP9z0#13P*aKlk@aCuA95h8)jJ}mwld*ZkrVao&C%v8uPj{3^kCMzMYOq|$ z)>QgoBeWc(J?ZLDI+Xc-xbbtV^>&7qx9G@O(%g*BV@%@{k0C1UAUhpm1U1jaG?*Q@ z;STJ*##G`;@ zldEY!MwD=GytGG}=6Z^RH5#Axu+}2+S7M{=JdtaA@h5YMYy*V2%ypQZsTAdwo}z1a zanKZW#ulW#lxWBO%3eoHU5X^7TlZ1k#%&OB`H5vIy~D(F^v zy>*_C%tkewBHPYgJ-G82Z+>}3l#M*Gb@B}rG*nFow14w?jW(U@u$|hb-mWL~1hs3( zV&D}t@yv+t3h4@DE+zxddJ`19$=gSOVrtNWsaNf2I*W#%o zOkjKkOhi{|E8aW}h_D{x)K^Y>|whbraEt-zcC&JxCf=4B`=mgL+diEAhxtH)}M~7)mPG&TXrApFFYB-%j;PmFkI?_ zbWvgfh%n3EkCmL))gzVZV8FxbUXxiO$d?hr&R`8Y$sB2-;j?rY^{Yl7l=JttgW{J_ z21LT(AlQgOL0_!v7wJS0GAh9&o&!I>BME)z&cpWk*e#k*B*^L+vVJB-`pe0DQf9%d zOfn_d?qTI~Ng^XVO5I2S{*8x5t+Y@0*(&#^5v;66-^bFbBk4@wec{QlAD&N8%OWX) zYo2r(--ki+gM&UsNwLWL%q9N#?1fz#7Ji5C)_VVJ3tY_SrVM`kE`FY35n3I8#aF#5WZ=Bh9M=G87Ih&MMDXymZjAT5TZ>9r*g?xlv)A=!W$u+ zJ?Ang;aH)~qr&we^pCQZ>QiP7F5{AtOe=N=;!Gx+CWTAD3fx1+cIm@r?;hrI>2Q7L zSRD~Y%23lL`~E@ErCNGLl6)VOF074C<9Zu05(moJ3G@)ePZU?Dq*)<;q7h~J&iwus z>9^_HBr__DcJuot1p+J$ex#74OpiYjEJw~ge-nyKy5S|ceq~@@TcaZjt@YX0HIwyf z%3jsZMh%D^8Mnjw8~4a8V)7_yP}yS?BqGhaTpBy&%qNz9u&z_iL}u`5d@WS3QQR1t zt0CUHJ2xkiNn>mB&JmAx@KfqT47;j`45RXPH-+68wTtAmfcTBpqEUwe2c& zhXgMx-IflR3Ff@~7P~N(n$ErV{$r*CS!4ykJ( z>v>y~OQ(s>Al1*c71RVr^FGNB8NfM}278bEdhKS<)hM!`r{Im&**ZCGN;@U6RAayJ z`SGMb^hr%e9*;5E{lFL(P_Q12YtB;;LABzPGf8%V(%Rc`S}rx_x)eutS-%O)?^wEm zrt^KvOjx?J4VFIpx*vKdu?pbNUL2R=$t|nxBvvq0tbB~sSioO8uWavd<1*?3XCkDp zw6N@I7wCAfx$fFF=ed&b&&}?}#E&1CtBPM(C8emGeIIzYKmBxG{mFt3w*IQ-=|cH9 z%H=`Hv`*#>%GFn<)Q>f41y8Ggynou>m$B5!8k%)XXJR_Z`&zZKN)1Mu-Z)$-@>P{g z4_y5_=8{^by445gzES`!v{rBQ)1UzLjQ)pDn5Ugdo)4$SGRlVW^&?gZdndS8bd7CC z&9s_%n8f&f=3~YpU-Ex|Xyhq=n2v|B!tAm5Fnq)*FW(;H%K)iJTSP5S=+DTPEKMfsDH)8Sa&{sp725*x^GnX0Nh{{qkA8T;xwO6l|o`yNn&?a>kTP# zU#npZLSH|&Y_DwZlj^nj^_0g*6vBEmvAnslDA93b?z<1s`i$~O;=-1XW4GT)`+ori z8|@Ji$E^)}^Ur#qqN_h9N_D+zOVNF>8WSn6oUcVHe({moz^pSm_=CKBN#2mNf?FiKu(XF=2dk`}p5~t3pF53! z8O(H^3k4IEZ$XVhwtLKt6@H2`?E85OQuk2fYc=4pB{JTG@CoETV>^5SzNHYJVD*@Maq0(0nf%2!V$@8uoGW*6^j&T7F{ zC!ry%TcDSk?{nwxTkxF5JX2c<1w7`E%cMSL^(0$P-&R<_ffjC8>rnK96Yo3YK1F$_ zR;3tI{`SDb>v);+UdaM&yT|!?JG!i?H)7Y2G-X%W(Z# ztN`?#_SX-P2=T~8{579Terj(-Dop1Q56~pbG{V_8tO_TBZQf9SqP#Us;ShgZsHFd# zYnUuXWD>q`UFEwSXW578X%5f}Tv?=d{W>-^6ABM~s9K1cdzcWLLONHiOg}oov7Hnv za*6d7ZXjDE_ZD0}WP>RaL&a-(-T2Tm{~8(i zC)EFsR6diCER&qAgSEaDlcD}UV7vc>y8qQv!h*j)b%PuHhCJ@H z4CWt-CV$pS{YBx}?S8vo3&%Kbw+#LYQ{FVX;Rk}5-)s!~wQ%g_%1>_pC>;A+u71A7 z|9dUj?cTtfdVoLc7y!3B1+Qz#IB$#GetVN&G2#EGTC$sNe!le|bya`zal_V4E!p2n z;{O-*6gTg2S6;?>TW0v1K!2^)`l;GqN-V(a9FQHgEI=@Tg$oSifK*vPs(V2HOFh7C zd4;u;fVr`;G0@h*^q=8>j^8-@wV?NJIAh`D074KmRxk??vXK%}CIn>pUn)QT!2Vs$ zA={5#9fAL*2H*za`U#BPKy^P2>F@aDfNZ3OFv!Ao%_j?_UWFA>*!y2ssQfP_6OD9iZV0KS0kA--D%c_QQ~x!qw#HTl`p(t@PR`bUS?K}#t+4Y3;QV(o zWPvaQDewTXT^IYXutADb{s+GF|I7Lr&~K$aH@lL5^7;P=FGGreZujW^vDfy$1CIYK z7JR#P`v0i(1N2)V(GBUJa`;zZFx!p!{aO4`s$*?C!x{I31aS4r|6;V&&-g%=soel- z+T0W@VU@W|!I3nL-j*8=iv2tpxrST+#z!MTlTMMYN^=#MKUNokPFs`_B3dOvocZ0W z<-*}&!~S^U4M`?1-<;36_o@5w!P)BKyZIQ~A6M^o&o)oCW-oV^XS*)vf7Ee%+t+co zy8qZdYtgI~)he}V3ycRb~(H6)7kIm z-zPue?g5Ulg|_mUafKeL{aDt7&ATt0gER){GJxhE6S6K6$`Wc}PsuN&pgVSOeWS27 z`+n%T#=MV~b@az`9IqMMqT?jlY9BxT??Llk^Pw)N-tcc4*J?hjf8EF3<B#kh*x8!q7lxLq*&n7J z`g|KWG`;%vebVA$)yi4s?EOa9@^aT{9x!)0+u|Jgo3Uf=&g|njOTq*5AyDM|}EAEJT z^!;OGFW$~~{2by!Wwz;tk*c=aw$r=yr@l5$r#plr43o)1b-L9;YxuHT*$-sK@@Y^9 zJ|aADS;Wx}wmeol`}|xq@qS&jXb1X0mHGCQOun+c-9m?G)I#-*ZO1bgxSj;dW%eWB zYb*XC`+-V5XD_%PSF79O5h@SQ8kk?j*Q+Q@pKmJS7_+%GIy*GF+N`8$IX|5hM<&g6 zsJLPvi=x2(kH$m`#LNSk^XE7h0-l`17bmjk|xEjYDu)2&w`qmKPPl73?r=ymcAa z*xKoL>0W8PY~!xB@O9UJGi$}fZsL9DBKF#pz|1xg#OG4u-`{M4Oe@qoCrIDbYVz1| zNGu_^Of^w882_{Ph4Z5z<6~cujg7<(9G)iB_LC;HQLsk&Hq%Rg>-bM8DW7(jM&|It z$Rpl5s}7Fz^of=;4P%wQ4K?Q-89er$4nq`5roqH{di`_h;U4k8hkbQ>T`Ki%&vh@v)^}zOy-fYRf}CnQ;S2%Y6dOB6b##m@ zt@mDtCX%yIT+gGvv>3v1q3_a-Be0dI*p^GDsOzn|DXK99>*Nf0E5Y8(^M#+VRYE($ z6N3x=mUTP0Hf%Zxtj(Y(TLRupf)*H2pa@@0OmkqMd?nDAB7{CuPbv{hZZZoD*t33A zH}S!Df2b(Hx*uMZ{$5`6>7tnyPGWoA6IBF_iQuTImdzw7Sf_|y=sfEz)2}uCp}6XV z0^Nz-TY4{QLgPN*;(#MR;o>wX3u#Ntn7^fJ_$uVFh^^!Rm;W3CHw5|-IwpRkeQf(e zfSR`d>r00T=9Z_Ls#i^q1Iy)1laX=&1SlWWk;b^qJK^fr2uF?xmIHJql6TUq`D z5f#a`-)=}1wZAk!`B^bR?S6hwt3_d;GK?WnEt4S{zV&Kh_UEsd?tT9`FS7 zzKo9Z7U}po(TP&>!4s{7QFwkswj%KEZcr%!fm79UgIRII63G`@5iu@XW|qcJ^runc zOFjiZZ8#5>FQP*(tr9S|o@IJcjKcZ+ zH;$1j%u+1*N6S!bM3UIFzQUlya6?-4Jloc(#7@jp5xsNDo zk%jD3D1wXnkB`hQoRBn16wk()@?uLOiiV-{2J+?IHS8B zgkfUBfuSCCv_ksRoUgD`i4h_N8cs*P>=f0LWue>vN3!Dk1&6GYABefa~(He~ivLaHT3c z3-em2Ch*8%p-b(SDD?^zA=VBGR?9`&e<;Di<2CC* z!E+jG)UPDY_o%2|dlC8UTS4k+N2ab-L$g;D@NFD!-6xfRfPxPVW3N)CPYK6l6LhL) z-eQ-Us#I&e9Ajgg6_V1ZIjm$X@r|l`@^nnmm@HruZ%aypl@47QIfaf@QAoq?svU z|M2{nQr;;x#{OYOmQBNyu`dDcq5Htv=rHQ^lx-j3Fr|b7Uep0H8fu6R+^KN#dvUV} zdqUgFALyo^bF7~of3?YvDt=0ba4EwBugpJ|{|a+6z%#S}Sl#E7-gI^pUW{BP`x?1U zMS+sp%K6odS$N;j`B)UgAikBWx$oDk8h-Spg%*D0^Bn2bW;ahm@#FZ3ldY|&jJHUE z!)B=T8M+6lHvGl!;kTC(Bk+Kdm)lkIKdKBI#!I`7&sr#i)E_bXSX0DUPe?sD*&cJV zG8*{^i5GnZ*bJY(_<@Cl7x?|dt2uUo)!R{DP(2buxEMQ1x~gSAOjJkhI@7xB0yZ8i z$=IA^m7!RXZ8P~QoMeUAOV};oqz%-jBBG&`=zGG2M3v!PvR}gE?}s41YvyaTV|KKM zbzId?i-@d+dXTpvvWv(<_9jGBWfef!QW2NKW99dceF#ndJ~zG<8(bif~+UNG^dwM?btzHX$g4HZj?3 zLoh6kq(@LQ9J#I84Q~7jPdZBvOQVOL#Cklp3=Ww)(Wxv%!EN(tv#dq=g3vLf(u~Up z(14!t-K-e3bUxrpgQo^rRVb;X7Aq;xwz{NPa;QWG#k^Gs9**gBz(MVz%>I#J(o(kt z8igWlbrx4Yov3C0kUi?Urv3d=1nQ$Sc8yaE@&+zK#W*&ZV0D{v3z)v`-jlvZ(7Zy7 zFrOIl>}QxWLdV>T{lmLOiKg>Kh^Bi))6(mu=2~g<7l%y~{f~%z`HIm}-y4K>tyLSE z(|@sj+F@wL)O^gaK~=abxob^r=Ww~j$>_}A{BBOK$^vKP@uiq3SGNBbsdD!7$~sKk zPTuJE2Joz zlw-{v)iKVmUG%=5SrKqDKQPt3+H=O|aT|a5lH*JdZ;q3k> z6QxiJRDL{?97s}g$c#3EU%o&_u#7X91tOW789tBfDU=kfpJV1*a>0^h(&MY$QeDiM z<(aAj;PkS0DK+opp66|jmFlheZK_p#lecF);EpVyRQ4%H>cbw#e9!)hGNUy5YexE< zw8uDhwF%INMj{K9Sv%L$oKc0d;9E5 za{?0K(9?2X)s}8mP-lpB)d6evi=WDL-FIPh5a@7^tX&nNuqX!KT`(mb^BE(#c%W)0KB3zNmLn84z-^rAh24s$ED`OiMXWVxb2rff zZFmCQm>h_4bXHm`chbA`vuz+K$@pf7^HafHpeYVYxNY0dk?t>&Bzy3_xv0xgl)MoC z+Df03M>ylRVPJ?38o~)$}?YW7)grHW#tt+V<|@PV)!QZ z*CS@3{vsGzdAj|Onk%a&y|{)p7dFw(_plIK@)*^NR*uptH5;=oN+~RTQ`%z%yLv@Y zk}lNKjP5mSVxd>;WmZtJ1d3FSd7U8QMBB%!3qe|#k3RDKP~+%QphDPqJJ-YEsl2be z{*rC<-6=uVN-~{w3RL6UB2PxC3zFjoD6}++$z?|EsV=LUgT)tSL0HY1`&s?FBtDBI z3BKRcXEMMx#z<-zoTj*wtK6oaihanGeTS{k3V)L%4LjSIv) zK|<>ise5>z2DLay_gK2|5o!w!IeY@JH0z6bq75Bq-lO}80L)!$lIP@&*8bGpVUV{F zf%HIL={PNsnP$K)L*6lM=4AEl6+x&I8mR!8>xYrkSyB%4u0_UXM!SAg-As}ms4bR8 z|JMMcW4Zzk9yV>(s-cR>jbVo4qNfOXbTEw>5Yd+bs4p=JITc!f%MXI3-dBkr@={7+ zzh@VO{Qg*ib@9R7P*;-Kt@PZN7sb)iDU{4^I9}a#V)pp(prU(GB@W*y*1AmaU9k@> zS(6|&WN2SE^549l4w>6YH`HrrTaZ)l!VP1uVb;moBTkDQ#uB)YU~a@iD291fjqdwl z4&|9FjN#&K|8g3?tM!PuKVUfw2(bvD4F*`@#n+}yav(8!*BZxi{ zs(ZL9)>N4I6f(^-Mj_fsgpn#nDx-%_@Gl8PR1AO|&*8ouPSk?epq8fdnY|lmMQDo0 z!xG}uMN4(M@EDOPJ2bZVxIhfO%K!#NW0nwyzrAN?)--fuo$XnXeLdQ^Sd6;th~5|q z%nKeuWVN;gk=f_!4`a`ynI(Au_?TuYOe~Jk`_azq$=AV;YW!Jl4m}`yM|}UR9X!BHhdpzg{Poaho7s>9MR1)7$z<5Ms1F?AEpVTn{pihlkW?1jVhrK>hisBMH@t2de$X{Q%mDx$5`Ks{oqrZ&owMc>9+wU)c)pB6!+{L_ zX~aMhV>dO!Gu9U1=jn*k(>PUL)x2JTjCA$c>1rrnQ6xZQnT%TK?Fg${P$>OVsScpP zpx?)LAzZOd`*tL>aso5&WPXUoQF@(5jH0JfeGZYu`eIlhC2zwS*e9EaVTWnvKx{ae zgx`4BaPbWlGg|xp=oMFh0%22jk#%!x5oUycFZ+Z=mfEEON#ja8BrtgB;nLItBpy-r z1~tFz%a`a8S&+Bqh6p;d!a6{HI{|T&(o~?43u_;uXc1?sUtE|~tIN{oF!6^93gGBpC`RW@Gd zykE3Y%DJ4DlP?mh?P%A(jC$tiOf0F!8dVEfRcwqkeo9B*7;nQU;>QESEBgYCAYEzq zLZ(dvcGN5yxP3JUX$-?)h+RCaldXtMw-7vEg0MaTEr%0I3D;z6*xBuQwyrwq3Y2wm zd$qnslS1bT&0NNCz&? zSi`}>1 zdcr)>AuB=D7Hs86+_rKpBYrP>2=`~Wq;!HfGy1$`y%Hp%3qboSy=iPMnwf_aCe(_t z(QFX$>k&fhYeo=Y30F1{_nB>^55J?&4= z4|1hi@f%IF<9i<;jKK+Pz-Z zl`GiI$bu?ILS~cBi^fLOeLZDxZ+ISiTEIY8rjN)(S!u!)a(iv#Vh~=)qCpt573&aA z@M1q+d@r0ngd~1s6!zMKT?UdEWs2es*F2h-3iAMQ?9GpBI6Qg&Uke_}*oeXKVke@s zv1W=R=KyCSMLw{-Tn%ruTaiu~4c3N`2#pBFHbvZ}p^_$Vgm23c(Fl;mVt6HQ%KI|i zQ{Y=%*LRNcNae48i3{H(;eV#G|G)wNZZ|mRpS!^Yg@i>!Md^hh2gevVnE%In_HPd` z{hK}DkdtVBo^Zo?d#1*(=*`We|HSHFL6-l(+rI`XIUuLfTobuIZiDgqh?-xHxq*E9 zV~ai;j=2GGu(I4hi+{64|MqMf$Vm~`$oJ2a6d;G+UUvXFL<9n)K)QwCH`llS zKqvp{EQy;_UVnSz|9v~(pC_9BL|*={@s`_TRsV1H-hqDG3Vn0R?N2%U3%rFDbhADA z7rZ51CuXLn^4T8TI{)B`%smAw7Y=?ufL~h<3~d}GF}45mh+~gWx*}8K8c|qh^Q5Zr zEG6Ux#Zoij&kuH+RvG0gGxV#!v41=3n_YzLy8Bx3?J$#+pNj|x^qsz{`nGXUFkOD- z@qHua|6}eggYpWZEm1tUySoRsAi?d!-QC@S1Sjy}F2UU;xD(t-fZ*;9!6i6zl3Tat z-d9soujx4WgdJzpC3V0GSjUu_nMy{aAL)6!`+ii0q4_U6db}$f=;#E$Ill{dsCQe#wS5=RHf3*uy!XdxtL{QWr*Z2i(Ob<1 zf}~D6qa*38uHm6@WjLK>0^+_P@o z`0zd-T)#{Q+)=7a+^g_3zR&m?!XMAFWdTiewL5G@biddCYWt^5G5gn_{_#E1*KEp< z>wR%r7p{S+Z)c=!wmu1jW|7`2Z%st6-q%ko$ijXT_L_4;1;-B^RcVvBg3tap!-_K zM*b42QT|ClSU0JmVZi!9Mr-Vm9MZwaDbR-%Ot;c-#B{7yjI!(HNx ziS02yw)(@GW=OOZyLt;ye z81|lAilS&$^P?rr8Z`31MJYF^Ldmu+GbR(l(JZS7%JFG_K^U&*YO>96C25QQ-N?sH zIzbAPrAwlS6Cl`EUIaajZRa7;cccX(L6e139X3k@&pIoQF(NQ;sh)&-&_H9I(vi{< zGxdg;>7-?;%_Q566k8S#mWMX5B9xwm%#?N*5KqX2HMm^%>>Bfd>UU*bUlQ=Zgn_$} z)%iyG~bodl?C0r?8=d60bs+>gb;htn3D5L~@gQ zg(>%PxroFb%!+TQ#j&ChH<(J8h$$js`OMN9<@@wAzH+jqgJm@${R$$~u@zX-lQrG9 zyS@qwAWYVP?|}=Swm&_P`hVx!PVv7_94?oef#M6eP%hwlpK(R1I0S&)YXX|U)0a@>;bt;1yG`)V~dm7=rs6lhl>)l$FcLWCmK;;QbzYGajeK`~R&1cpR?gD$raDaalO2H=f%wjby5 zD}`fNNc&Gt8@K#=0&kb_^^ix- zlt#7n;nT;S*2~+8cWisJD|8)WC;?9g)tZh9@~N2N$DDKXH^IETK06&BPc|Ok0+!p@ z?B6}_jaMHh6V2qlK3GcHRMdYtN4Xj*$3H4n&%=B9`fwVBR$C)%+gx@$*?pjwOkzKb zIN_%9yR#duvnJB=u5I4`n$PIonraRsNPvrtF4TRw9WEk=7d_ zu&%+w5Fh|CcwH#TLnrIFpneM{DOm`39Nl3wGxB_rfmjIVvJf=Hy(1L?8Q1LAFiB0vJQi)0HPG2t?K;{A`b;LmURlY-#5(G{sS`B+4m#OQ7!41@G;FBXhE7%hrFtC9!w};KoX@ zf8LC!W$O$!{Z`^B^O&N#a}yge%hqW|!7YCd4}ubHUFVmh+964ALRUhMO@Z&LV-^)X zB^mYD11vxi@v=GWi7YDmk@@u8_)@Xy1)RH2QC=keS6XEHjuyVKktQM+#fDKMm0!ju zr(y>74bDRj-b-7O3_^p=X!4xx`+2&Tg%ce{i%aH(G5t6Q_JSg@%MQpkHAYp^5ra4{ zq?oa7Ee0q7J&BQGMk=2Qx1i+4uhV9Lq}F#Nk%@LQpO+Ps;nRdd7``Ge&!$?+(YLkD zrozy)b)7+(@lL_JNQ{*~%DZuE|Ba%CYBF##_x|Uw-+0}0(Jc?P$=6CAh046er(jsO zOu$tb_QX?4P>_qn_M(g%H0qGS0yWeZA(d4-5p{7t2_p{>hCzZ`+Fwh{JmSrLT*}Lv z;s@%XyA5+nLrIQ=HUbp!u3ynn75So^oioJ7fnT0K;T+$88hc?^ovd(^iV7uk4MI<3Lh0#e@2+3<>PpV0f1j8 z=sm-<*~E8N>_i~Pc#R(<5&d$SG-`0;wW$ysp zo}c+#^1l<1p9e@0n=%%u{<0U#2aw8v9Kg5ghCvAATdpT(a{c!dmj9`h%t)$*cjD$g zuI1&o;xO8=LkjAA?W-0LM@N4GLpb5d74j{~w|+Af$C4AskcigD6N+vZ!NfbaxdVrP z!*%E`$pMp66aNS^_l1bl~L^GPnbY4qPlTWh`arjER+yLVr zS_T+LNNU7~bGlWAR&l}xjEECo_sWjqG^p=hiBY05WgeS=9~sO=Ly`)#s{I8eRmmdQ zEKRZWLwM|V3_0|~Oc-D@odVq^R?3xlfcdO_{bCG9d_szv@r!Po+`1R_Sf>iGuUd)p zXohbH&Sj9Eko!bO!}@f4elU}*U~J!*W&rSZLfnkKBCp>v#TtuE(ZOPKo>U&xnmw@t zSBvE&*h?M&t4Od(HMJvl`Za&ds0GnwDQwyxa_=Bm0c+hI6*(SB0?&@9Nr5&Iv~-8D zc52hOu_v)&Q@r}>NLHR~!z7zYQZsbJz+rP5+ptxNKW3y~$>(~)-kb-i#@5Dlqj4aaHR+m0wSQ09WD%T!|R>zq-Y8uhumv-e47#(m{lZnd3jKc*bq`mg^e^2E7KJ zf8MQ&1|{l#pdUHVjvCv{tBofl!NyfB3^t{p5&0rl6&&KC{IfkI3}zS6vC7%(po1aRkrs2dn?0`LyxzCSMxoXaj@?S9XqXby^B#|bA@+ZQ$2qw$9E`^UB zE&`AsKEPjqJaMzgL&iMw?~s%#?kLO^=*}6;m`+B5-LD?4LSiYT>A$D?YeOwm+N^eB z?Xr|rE4dYS-Kvra7x;8!VyfAY%3Wfx%G2vFC-v}&R|h{eZ@B%Wk&&@)zN|#G+_LIu zR@6HNb?MAm!f9aC*3VBvpgB|zA_VCM_ZrRQ#fatwZPCc4dA@UZ)`G*f3zXC1LQ&|( zO>Gb9;MA4@Z}z;s38_N8Z#4($POM00|-G;V4~;I><7H1aj6h-$!8RW&iG*WSry z5m<9IKIav}(j{A`n~q!e9-YfZ1KrCwCKt&}(czS3t2{<1&G~ke>Fndd4uV`4qMt0=-~E~^!{)GT z*iYx?URiuX5Ws)9>YN4M0ZBqd@JtP?gWw!{Jb%tHz6?oSFSnYh+qwvwvG`NSFx)U` z@s~kmzmx-wG7hA0nU)=WnQ_4TTzT#eqe}wuUgAu=nVr>(apJTD;@aQ_IjS~5%&5a= zYAmdXM**3XcHN+$$qSY%5ijL;!ovTR-`Q6fY)13xFo$Hy#W%PT1^S-_9P*BZZ905W z=a5NKPmcZZM6+vUz6Rdx5@BW>T$~GNIs94Z?sHd0aPT@`?o6FW8~ykISl3gn;(Fo4N! z9I@K5H=Z6{gEiujPDzb^IU`bzOQFt^YwPAu5g;EjfTqjLd|(Ko)wiTkh)y?||0t-y zr&$o;5gb{P$HVy#5X;a@1aByXq;wOPg*mjNbbqZUfz}|GMZ*%KgXWo4nVO1e8k$Co zKLH`G1jZPY5Bhe+J)+wRMNnxLI3Os*Rz-ens2%mRQy)@TVy1i%&r(#_CbfUg=+1bP z0KTXGFC z|4(*(`;QXxe}3Ka?>@EvHih7Im$gydHxCI$esC{)wnM3{RbT~ zZ6cuSgB!+guqFXr-*SCi)$gU}rCko_#&vR_;>gCh=N~x?Xp-@8W7!qB1*-$Vks~$hrhy_gE`=wNfj2TUd?s zeHW@83!F4t$c(z!DqZgvs+KG0nObZO)Mn?7@2-E~?Hy}9UcLOl6Ry$945)H^JK2kB z>Hi^(;7PxwuQeMS^6lj$z%X-D!>4~4*L@f{3i^SH`;)sG{vBnNqPrFsj?dhmmEjlz zr(B+zbUaw|Eb;IvzlspV+BrZv}2$;xvxIF*Vd z9^mqn`*b-uykp^Y4z3Kjs4TP5YRqQirZ$*P#nITk-C)(a>5p59%hZaA3!xw-J#V=Q zaJt+=@_S0?5}ZkWTlvm0_c-xx$lhJ(xtT2B>HcXbX2UO1z4G|D;8?P$Z!=9{rGPWf zMk|@yTsPIv#C&D-SI0%uYf6`WA#0O{$r2L-@{jtH(A={CL*ZwEI}S8xADyF9w-68U zp}LWM_cY3BR7d{MuVKdSF5+7Oeh+818jq5z8pQM3(jk}3D9X_48u;GH)!uQ=UWnarIaQvKK+3PUh(V+YqAvv*`J~r~F zPI+uzW2#A~D2wC~EZ9^L!z7Ofl8kvOs3;0X`{IuH70u|J^v|WK_V?d|jN(PG6qpRH zwy3rQPv(`C>wl^_5DATx)QSt*F;&*SMPKq=#QwPJN|hjHJJF5*TH+Ljh7SAnk)GL- zf!Qs+O?&HnTTq>YUZKcbIYT4?WTV*XL?N$Fj72~s3u-Ngim>+!Ng%X<`F!1AA56&+ zjb>ZQJ}n~^>$>H08}@XtG3@f2tuZ1&H6>IEt>Exb#;n#Ms;!>{7mXQ!) zH{R6|;-pBIp~^WtfT1)uqrLoi7i;xu9A>Ht8Ckh}#&^3#yPA!&BC-|rnmhiqoC^vnb1iL#q-NQPS zKMGqYO|48^6_I6CXA!+h{(>@?dK{@`&MQG7^1>7u8F?FuRzH7$>xQieW9BsC^P7e5 zo%>1Ud(o-tw{Vm;xIG?vayYOJTp**P&qwSDoa)p;R&YTDlA2Y5tA#=1XWPB@5@!W5 zkV)hdq1hO1TB1p^W#o#j{AhS!nk$=HI8cC&;m}BxES`YAfmG|7x#U^!ytxkW9lmY} z;u5xg@>TFlA_=(XiQY6dtd=)O?yQpd&S@Ljyw^;?U)by{h1;z>)@-2CtW#v&*W#X* z+=uc9XGkGwQz{gcLF8EJLZyejT>@?s)$a)rNG4jrafiy(Ra}y)ZKJUFZzYER{N|um z^LF$m@mn22VD6XAYyqXpr%-izF?dta@GKe#J5b;KO6-(%LwT1$E!(3whzGeJZgF4v3;!+NhKx(FVoKmv#t8+27Vrv6A zaHJkGGCGcnVO3LHlILh3Qf{abtn+sr-CboA;`mhK2sg#HS2`tNM{8L!&=%58Mi z{*2HS%RpyqNv0?xdJ6}gw2x8AMD4#>l)e$a&zDTWhH5Khhw6X{;y9d!an*|vAwWeB z5|St1hH8$Cq$QaZZ)lx07k{6G3I*k<&S2dn$ttHhV%pp`f;q^VoG~&+!JUvIIhKT@ z21=qKMoF;1IzkX-qZ!3)M${4IBRALK6NYq!#FE0oK5izHx-swnmRu+)fxwWGc&v^XM=HUmROsqUQ?{4x$44UA%spg=c z4<$#hx_St7=1IpBJ+(`SCHkwm2 zy@iSw72P~0l{!9+xg>ZzAqizG=(L9{y`JYt_4hAz>=QPRf&Ukd0-NfEW=l9(MRV)=)WWpk>jzMaCPLDsC&A9@ttsNeWmNY z^v`-)?^>DRO1hfS0%2Sgio9LBRmD|+g#XamqE8~af-#y2>c0?M2uE$FLe0#A?HXi0 z@LFf7GJ%QD*qDP^686wxCu)tZ0AF0<)gDwN_ z*AK4+j=iBGt2dqx1&ZR7R-F3J{i8gni_brK8Ej|AUP215SRQ_2)Lj8kN;$4Vdq!5R zzWn??Fc}K%4>p_Q^DQ7gd-6H>X0^u{9?wdNSD*ZqRQxRL_iLv(ff z0jn98?-_rarfXqD@K%25tw!*R%Hi{7f?XM{gY*u@Wgl56NFU{`b1<@`C{~wom0VJ* z_!h0|8T{(d-AM>{f6g=ijrB!1Q9=?D82ILX|%DMjdsy!+zZ;cBA5!Qy14z2HqP;S{q`4h<}S^g-d2;Ly2`$rf@V zWMj_N$h(lV(febwjqEqh9%E*pVeh{snC%2SBiXV;61uya})*mE$;xkQnRr9Z4{LW4qKpwv;U zW<*$w`>gq6HW2MFMKl=lnm^)X>}QIWw5LslT}2byDL3JT&da z;boSS)jvP^TiC}D^90vqwvru{O9gaB8^_jfLoxO`;Y555aDRP@(~kJm zS0F37{;C6Z^%FZX|C_%=n~V8#vyx?|-9{T06xNn=<{c#|R&3063eoobpGV%6$nYa!aO*)y(8Wspf!2fk^gtobjP ztz^rn6}zL^i6&WblS0& zKtTmYJ>F^!6x*`Th!Hb)f=dIZv{s-v%uN|>I7)pnL!qH=P)<1M8?vfEGoav$h{f2=txHCqI|_m zfSvR>(9xT9`E1_(#V*N%_!L%t-1jFP{dWv|10g|&^W4Hp&G+w}5nU`ep&Q1V(nYbm zu?t-|#R2R%#c>(B(P&ZjhUBI$eZq7+|JVwiW%= z@a@SR0Qvz1)ba?TEza;vmIM`y5KtnKNu@JZBl7@CmI~xQQsOiNEl|KjKK#5ODKNd3 z&ZPw~2Vcp{sDw+Vt`9{eI2*uvBAv*KF9u1XFH=U!;Kdln@ml8Y6HOp4VXf5vEk_DG z`BhWRYVF8(knU{gPydg(`wWMM%fTq&q#6LQE&#CmL{&ti)n0s~i0DZ2qG?|jy%<&k zICLXtDcD^o4>;Zo6_caQgW0jk>`+%rZJ-J2mZ1Ha9Q!USNRVgk{NSZeccWf`Hx zFGk1IGR{QNi8>?0mNhdRsnQ_uW<=pHP63W%A|Z}+BUF9 z?K$oFJ-LR^{{{!!<*5|$G44ibWi8dRE&skU*&+>?pv02U72Da7`H-@png5viw?0ja zsY*3+gXoNwna{{*r0J&zr@k`EH_KFJLUiAa_Ym;_njzw;JgX`WG3f&tj8?0$DkAf$ z&L$2^ghT|n$aJH#UJUM13D$cD#xey2E0Bod=9qOwNbxF;JE$=3%Jma59`P3r!*_&| zeZq2)-UF9MLbeSKaK7HIRrLN09}Q%Y8a@>1@3efQQbiP8)gTv)7ziiVoxX6^Lm^!% zz!!A3gxQ6ujxLds=lwdcRUTU;#pYEoDjqxWhJO6oJGvpZRa1Py5VW#*+EY?fri2bP z)*JtmXJItH;J#QsMp}G7K4T*EC$Kw-%Ag;bg-YNMw?j-@&-|rF&XwLC2Q`?!hc9G? z8I`2OIecH|m&2lhtzjX5qE+32vtV6-NhBtT(}A}R^q!XhU;k|1nxi zSw`fxD#dV)7;16}S7JZX!brM9Lqb2m_bh<#u8Q%hBU>!cqq0RRF!_nJVJo(~BVPwc zg0AVw>70QsV+92Y8ezlU?{-^fMk73VaZrRL+m^AW>@!az!tuJXGmawYgJ;kFK5iri z4B&(?2A6mEj3Wdi$qS|fUGz}c2w?Gzohb+N`;$A;r{p6er+Ltv-XlR|axzqtT3Z}C z3U^=F`223&ei-rf@Or&tcx+Xh#_leQtoI3C+F$OZh%_2oZS|oFn{D*jfnflXpRrfv zR$v}simn#114=4YZ$c5x$Y%uQ3Z*55Mf;3`8kU+2i$89qLfoN788Lr%^e%w+x^&Ut zaK<66in7hIAN$;a0e{CK?L*Pf3Y#bm7~zaLK0-oM3?0UX7Ko}L_A?O?EF^8tx=?=! zv`=%d*N0Yvf|O>h^jo)aHuvkJtjZ@}Xdfld2F?{^fkmH3E=T9ok^kp4ol1N>*3&dRm_vp;(lnwB{pbI zG}Cl3(=yY<7|aTG$S~$Jvf=FxWMcTu=4o=cD4`e}ox=6ur#iGW7sEgd7>f}7owEs7 zZZDmG1t-!=#45A*1%`$Ssc)J?^*b_^XVbvvU?tS@quv7;ARp;7BNvPK8A|S$h>V~I zTaahM9=Z7w?l0NK?b&V!vND8gIU2SWgC`wjcyBm)pF+qpY6Z+$>RR~)%frX$qbdQ% z*scI_2pNADZUBhZNI3>#m#`pW8V;;4J+f{C4&1Tk9j3&+=?L*ytsEKYA_ovD!c4TX*NHk_Sg6Qv}55VMP>P6WTH)ml+5aipW)Uc2pEL2Nz_h&=o0@$x+ zN29eEHdJuYo978h%I7}$r5kx(v_bjdPe_Gj zisrO1CP^j0>k0O#C78Zq>nAWc0JS_@v1h&5R95=Mnt#=POQ4aXR8Fi|V`u5c$6qnK zjgqT_E#YX`ZHDubh1SjEQW&hS@{vF^q!%j&=G-k3iP?rMx>&>j%vrKt2Vju?e=x`* z&tl@HjHUiiR@!1DbrJ#XH4k=jwzqzIyjmQ|X=5F!h0A}head}d-U#z!W=@>in(8_e zt1p8>^ZRA~utZK0i;jKeW1q+$W*{4=wlGl$EFDyi?ZjsnHCSOlJN%p@p|>Gen8VVf z&jiV&o-KAX8}pBxiShaOyyhOanu1_FNIGpsgrD6P0?6_OeQ$f$&WAj>Xcbf1dEIG* zqMT=btPNghWmkck=qyNwzldqaGoeRAxy?7vkCAUU&8)(ZRG!{W;Orqq9U&U+QaRC zb9nj#$N!^*-IVS0w^_~5#=8;E!_+g_f^h6|;U3eV0T+(8s3iNu^j?!%eDiD%Q}X5p zMXNtor*V?ydK#)pzPkIT#y-DuR1;X{l?p&;<-c(TAyx@`}g|a*=n>0 z%vMZEmXBAkyPE)iyX}9scg*Sc-Rpi6Xjl12R*Cj@d(oRN@ZHm7pC#G`=|o?Bp6qQn zB7?Yzw^74H&rwIYw_iS*s>&cfWIiu+aKN5}cw z(YFC$gxp6jgg`+QW{dtHQHPKH^4NMyNJ1DGgR~Nk-WFNJ&vvaFS-(4LX zoqG8AbaH{^wPsH*!k+8>%~uaUPSa%+XwQV@#|=lmMlHRq-@ZLKceQqRvgz{qV{_>Zjh+6~qcYoPLt*{=TALO*Z!tOaJ4lzWw3G`u5BF7cW^dy~q2>$AoD*GC$L3 z&4aU*>bJZ7;pH1)Ka@{$EzQ5O59hf)NX?X3<5(}_7K7!#seJcQAzWXUp@Tm0t`K$Bv0y$?#3*rN^cSHIjWTOrDD5s`ofm$Fz`0 z?~HHsg2?9#)YLIzZ*mH+Hxwrw!^s1_JeCSKIT}vW?1cKSJD$N;9vFpPYms+0F+}5h zgPSa3d412@$*ZTagk6Fa!+N_raW%GoVSe0f?LMtWj7mxsj)G+=PAh?p;+R1$9!m)o zD%mUYdOzh5wt}`gA0C6;Q7X z1%+`e_n5kZCkUg&{=)nE`KzVHr?w-fv|Kn=UX-syw5S&r$KTkJhWhi1%}cUEHtr=?9kGfzfSMACIQF|0T0OG8SI z=jWsGQOthJ82#H|$QenNBTCtAtxuL2+mgr(>h8=Y`hmi^4;u9E&*JR&_i%jj&K7sB zejh)fCgtDUPY6@D-{6e=d97sPeM8yv-zjbQKF3C9kk9Ct37K4NO}O-|s(M_{rUBsI zDk@y9g`n;=Lax~uTT7`%jf&KwFh%*Uc~~-ase}Oh+(Q5|UeqQ}_80Svikg^7Ry) z%erv3{X?Ep(9jWaZe%{y)FPwc8=wd6@8xq0m+G(EgxpggqQdZ@D`Ns_+!#=CTNHw5 zuppr-gL}oqdU99`K`1BHDErD=mWGSS<2QyK8*!rY9m2&L^?T0wGwA3)dBpQ)er6br zX*(c96IVx~+ysa9mIgwlQCpL9Ey*iRBim4CSBRu#YKeJ8SaViQ2ku(IpT>@zj;}0`&~{ zf$Q)vd`CV-6%Na*?M7gLdxCMuMsg>g3lh;z_N5G}Xs4b7W~n7A8RVr_?6WTR&k;ay zS^}!E&M6|`Fk~|tN~rWA8Dd<=%mPVN<`ASRs35T(;zAB|x$QUY(GH;k+AVhq8}rp{ zUpgXGDziw{MN5&P`x8PZe+G_uT#eMhlyck04%gOtN8Mi|@wb;CHeABE;OYKwO7lb& zm+!3(h^*-_(1wgsK|wea*Uplc9A%UPgyIaCLzQw`)NB>YLvfT&tojLuhnF84#+4Hf z4=L)c{n<4vRF;^lV(K76n^l8$zF^0H0F&IXvQ@4#>R(8z0z84$3c z_7@9`HQLJ#GX9JfE?=e9!we9}dfCxrDvkK1JFv9XSo!H5Uk4Jlni=d!w`L&%LU0 zEL+m=b4zmD_%m>Gp`F*a7FcwCk`*DjvM>xr-?rftJ~7q(K;lTa8LXl9ZQ4a^LG z?!)~s1|uvgvxiqChFO&AXo=cDbn{a9*7!zJ8gjMAKwdTGz=fCSz+(8cNg}n2DIkyd zA1ctE=GNs!+kfDn&j69hzlDRWm>nx9125;u%%#9)oHh7M`%xV92jxD2;O zx>1RRx?o|j$*aY13yT?B8Sd<501gwcGro2w}jX(c-cqz zJ|#gDyR=_15V4=SJ*~A348`d3LH{;|4}+r!6OTA9*jkbrImte~BALsv@T6T~CQ)%pqlnY+S-tr3}7hpjA*89%T^xXYN_JHGYBq`A#8K;l}i z{KzQ)n37|Fu@-`}*LOB&N~jRo>VaMWGw0nhvCSSr7yz@nEV9sr4}{(!?5z0m+`?1t zv3<9vuz8whoG{Y@mSLi;h^|cPSC~FbFZ=@m+ z{>4gl5B22WVk%D_ZIxJ5@wgx-2xUoZI@?4HBy?;msaUKDSr9%H@y)&fmu0LP3!w6$M6 z31=+5#_hb67#)u2PBcxcJhRKy9i{T`J9FQUY}pP&#t}4|DN0PR6Fl96vsX$);;Etn zfAzSc{-g5&TO)_PVJ?B_FIjZoy8SVwxVWC6{eG#@JGJYrDV`PIG~RCPY?F^!y>pHq zquW8o{X1Y2EDSq&4w_h$76lV3X`DVdTssjUPOH>~s?5_Fqi0^q_S9_f#uEF4XKlT@ zxV2mg;NLy%X61raHHlRBBTM=_unBY5fj&_Vhze8O^$*3>x^muu#MSbr2TZa`2&!IL zY^s!mF|w`$vH+5*yVj^h>Qf=1+=Tzcu+YRBQp>)?Vi$Husd9pMh4s_r1(9t4qC(l~ z`}AxsbmqGSoKve&`||);_R!{2>~Yq;-nDAR%)u0f_t_Wqt+sZ$GT0s|#*xE>W{i{@ zX+c|dq5^DQi(F|3o#6zWOd=r9sKQAd>>M zak6rbizF8?MO*vjSAu*7bx^hZ(XVgDehsbC?lZ6%Ob!t zXm3FNb+cO%90n3otoFMB z{|(VVz)HaC*_{RAsd9z8Bq7rl7rUf+b6ZJx0bsaC22)h67i{)ZhF;P*tRX`QDh}Be zKA%;d!5m_Ra3O6fsK5V{oAMRT`&Hf1yygR!q@cky;VNvtr{u~X+6w1y49WKFUE{Ev z<4xmWYd;1uzjT4H%KV?%{d3CuIc=c4LGkE(oT+g6dWKja)a3)AP7G;29Kc)8w2&}i zY+juczHsh~^@GCO^WNdff+x}A9*6b;k9rl>dpe#sv*tG2ZD69&x7?S+)uC4os9FNn zUO&mL+rvTTiF7X3fc^uOOn5GmD$IXzg)w35z9lFm|4798nHha)DjM@EZ^q(OG*Bd* zlIQA9!|N^tjNKn=3HXpehJrrL<~HQeW+c5V?E`jL)Jn-_q76hZER*a%7!!*MnvAcP zCEcPECGPp8WGPXz9A(Iq6RoHHn60PQ44ZUG!x5w^62~03dm`j_7uz&Up6{Er&oN))4NOpy0P9Ke=&43o33d)I`4G8z(h9I}Oc8ml~#DL-MN8MXgF!3OzAfmz$_WPrP@D){B+e!Pe$iJqB; z4%7Tvq1IhClHxweWZNS}u+fBO;EJlxeaB5^WgG2#&N= zQ7+Jx#(Ct7h(efD=``OMhtzt}o zK&v&M7iv~`M}PI1D}S~cgsszyTBSU?NC%Zq(IQL&;br3vV^f1 zNNRX+9B7mJQ_5eEbRo46Y`uy^Y$^GZWitlK0Ufm64Md&gx?Bwf(<`I5TB%1A*t9MA z)|^Vcega;M)~~NvA74H>#HJPBLR^Gm348@M-2}b@v9@peTssjkzL_kb6tOgv<|DB* zy_bCQ+7huGvgLdx0H|p{p`zKe<2cZ?xBaHe$R(4|O<#v<6KZiOe&V52f6*fMe)8tF zVY)2w0Jb?4P62;bzIC#l+_Lpj^8uKhxs28ZG9E09Ht75$(ZnHJ!lwra&{ui5@b@S` zH2(qXDB027nBC26Z_E_`s!pFQ9eeyW_+;jh)d@j7gY6D{)nPo>!BnC7s1&Z3=D^|- z1!QCqovk!sm7=S_xWiEl2cs6|U0+nxRuqm-{$T z9<=Kb@cpe(W}i%+Tg7ejgX`G{aXhk82_NEk1-3BeVbvxLC=6;1(`nmZU-#N=6%D_+ zFCf)V7}hw{gg=tipk8OD+qh>L%2&RUCUC>pHhr&6A>+OK>3M7~nOoYC*17KRqcLuq zsVty#+sFRk^`O@9e%z<)=KIV^7pK>9_LoJYi0q(>cyoybtld63@(+*YGjaIgTfb+d zCNXfB@kC$!&+s*(?ne*4U(JxBYo5Zc9hfE1dC}RftrdSFHB$N^@Aj<}=Nl_cfsxW* z*UaIGs`M9f7|c8+>_Ja8PJgLn!r&+A1c!7;DyDnl#nMc1&YF-DE{NinZ(@tPJ;KyTV)IFM+t6Z z3jwNaVRsR5*DIYVMv0120gqL-(Awq;#zLY22>HW?+la}7~tDMYy@(QPrkij%?OyZ!-Z<0{tL%(gDk*Xa+EWFwSJqfNpTUOziy44&wT&F)uE>f-WSeX_M?D`hkFO zF=8^f8AL<-Pe4t@>}E|Hx>R~3@#4D*7P`w9oW{wdthEvH9zcPcgGJQgURs!hZ&O^U z1G&6ivuZULlbbf^_oYIEqgyIay%F^C<|PtsTO|ngyE3M;Jy)22&EM_@hht}!)RfN5 zHuC>GHU5A5)&n;?Fj>|AQ(F)J?b7srL;t58{&!mseE+T)_@Afeo3b68h(06^ z|J{1{C1>a2$h-8L&bUq_t8|<~Q82;^&L?=VZ%2irk4AA~YSrRwhAe7LRZE5PK`W{y z%H>(vpeXW|YU#J|5}pz%&k0(KXXc>MoA;%8LtE#Q-|fnFYE&kx(c5R2>w|^4!Q0c} z+m}3v)(S(50{0m zmkC|peV-3g8wRg(qcQ^mo)6nPeVtA24De@gn%q_9&K`{>l0D}pYO5pL%|Zuro}jt8 z+|}qkFf>csnFz-I*+9`{fG@{&M=rsuA!% zsC%oZI-_P=kl^m_?(R+q8el_kcPBt_cXuZQNJ4OzARBjgcL)~TgL{AQ-_xhN#~pX{ z!+q#}VPxc?c701~tu<%WEa~65Yb#VdWgt%KQ|>jD3`KNP zC|eJoE>B8x=qhZf_b-IvS>5&h+?!AJ+KTntT7Ng5!#!Qz{TQxAo4*_nJ`S173jYph z78A%>W+Kbs64=nrYEzBc6=yTz-BO;nfAZr2!+2{yDrln zz=d!@A|j#;kN={63ad&Dk-R2%E5Z)xmg;Ibc%r*JeyexoZMv78aaCt0vkyt=lWMb< zt1z!@sm3T2G|}(n@7p1>n^x{3Z{6v01Sf^6%o|InJYUF--&V(!V0qoHIXtvrkSpNb zICC&E`svTI=wF$K%+C*OPcj9kdl^J~5=%)#qJ-ZEXHc3kW+^1M% zmTvc-GyeT`Fpp_{5iHS&eB8wdhOp|U%9+2M#86%+z)u*BdOy*LPX6xO<25~uofIFJ zG2&%h5LrdIBjw14qW(h9I!!eP7b|sT*OhCR_cow<_MF?sfQ5$?4S;M=ld73zuJDqW z{ufXRttmXQpimPNKvVNS;NfXwh*CyV2zr?0(2xLuIeHzGRZ{7Rk|3-7Rd;+to^sd7 ze_cL2wPT&OTxUEZPafwrcQ(=BvCW~PilTorLzgEOe}Pk3Uw8yxUp<61{gY!8@Zexg zM@H9&EkHte=d73Ove@j~+AhsGY5wo$BYx)WGg+Jvt+3ziJZ(=C=(t zD2;AQ^sAR&^#{5Xc^NUFZrtP_rsVxr%*i5h&NMU3Eo5{Vm ztz*}fh5xpNYF>H}B^adPyFNCMz5Ua);oRmT^&~p%i^Ryxi8d-5bA;yc*A?Ig&Z{xF z>*#$73gyy5RRo07t6g#pVU>P3<@zkB2C?8;A(x8=eH|#^*B;@9`8og-!ld7&_m1q9 zUC_9e0RP4b-{G%#mRkGMTo}_Wi>n8&w079c57)dUdKgT%4ug3He=2A8EhrtTmC^*< zchUSNTt`UeecULA-Y0=fQQ!*Ak*@972S5v)>G0_0I0^)q00R8$8-wXftkE24$H=N9 ziGaw_iX03Zi?GO1RK*6MF&YjIWz>Gus{oe#m9`#%x}cN^oV2hl$1NfZ}nJKx%vB_u;rE09$>zRP;8MBw&;@7?eHv) z#T6g{oRo9_1Jm1fE|0Dl-5gR=SKcjFx5Em<)M}D*0B8!wIVt|+Vw~NWYY4}= z?(YEerBtGfB5S9*&@;z(k0mheI3{_AFb2Gi#w5=R9lruM1^@}6viw=fZ%NsKZmBMb zzL~F)!eyMfJ|YhlQiG@w@`lQNlm%@Dii}8$ljR^OyUeq$5K|#-%(wRDk>ix#ogK=8 z9c$Er?M-i>G@_gGA{lP3u}h>@RXz_a@hPfoBKp^>XSRgdOrn5VE_SFn&F4`F)1!Ak zrQXNU_KXZB&_1~4us?M_BT30>HnBgQW8}aTRIf!R6t;;oPJHX4-`HFGg~$z>)a<7# z0N*gSD(vwoeJv4QNO{O#DwwHjxEE0?A-T>F)ma{#!PaLZyzk*C%z?A2o^tMwY;t_! z*<_g9e(?N$bWRmTwm29n2F|*vAMD}-@tG#Ha&|b%D2Yv4tsw+!O?4?PV zO#LCNg7$~ftz(%tG24)wa2$|8O4=W_x$DFs95=dQ<3pU;JA1}|4Qwe@U`wfC;3TFg zoLghpgB)p+-$5dM#Sni*Gf9*|LYCKA1fFIflbk{~WK#FmiPgb_hn?Az!mFLA8Pv=* z>x&SsdlrwENnCXrB&9t?fJ@jYruSpD3I4|&UaIWn6;le$D{S`dmOBUI-+uw7L~cFO zz17PdgcPp;Yzan5Y_>f|;3ze=Q}p!VY3Z7kWRp{7MA+}WX`<+nD!9M%j^oPYmh#86 zRxRu63Z>kaM`4Q=pGparyr`qHViiE+4o(f61=*Gj4W-cR!bbS)7>n^~RE|c#^2c8+ zLuao#f_tS2hWxAwwQAZ!yP+(mo?D)Rtrl9e{B!y3mD*a7&-=>hB--9u!r*!>61^pDgzs9ZZ@XECf?&SEmt`iu@o$%{Zc=^w?SkSsB{1d8d8Y~U zD~89|$65OUOAAj0PD8IB#8&C1RLWNxuB6K`r||vo1=t$3L0kOdsmZ_LshXeQ7aAdo{tYcioUl{8@-ZSxnZR9F&%#sHne)g~Rl^yIi>d&8bRn$(F+~iBT|$-M0Si<9^l6d{_`Fztfs+sLeOl!RtnSlJT0sxz+S&u*j9YcS02q z4X#6iLwJB)+I7$<=ThKJkaeU$6+~P460vH6G_7XHn2CBIZw$i1ez${#8`9IBJo$b36Bav3q6r6qa+Nm4o(*Y@`p{P0BNn@+xNrq5CyqJb%Y$m5Q&z-Fz}OYMp=I2vsxJ1>HS$Jq!+ci#Eskp9;+wKruN2g;ATZ za{_Nc`A4!)GowIOY3l>EJ7*Su=xI9Dp8b=ZoxkAi4r~R{p02ioOo}m4!S-y#hLK;O?fA!O3_UD)L8j6Bb_}a4ND+l6sW$KmVCy$}F5eukV#l%P zz8E0DN<>yN=`7=>BA_ZQO{~N({%ix=6-KzZVE2x@&dyTDj%^Ha*IIqQPB9AaM=+(M z^g5rS8AGiH{if2zGJ(TweKS8PFC~N@9FJNr=612z_@|sta`CrsNm438%{2cQ?K1LF z?F>icre)3a}oSw6!LJ8^E-=QBL8q>Hi0Qspg?@GqOg)hICsHW3(&Y^9Wkc z&FP4VCY87HEOv*OoQ%*X4aR5V4_z?JMBzAZRFYs0E@d%c&_w9*_)gw)A5q;qT*7a% z!W;U{76kp9R0I6#8xuB%S3#1V`I!MeKI9%uV)t0iV<2-u+81hsl7k$zYB}*BMt42r z8j&C5vq^rc*kbeQYl(hb;Y`zRQe-zN0pa2B!E``LulF5hIDp8mC!!#9Ldg@n9X~07 z+#w*R&Q9-#jq@|3?s0guN)^PPgS>D=dTP~QF!NmbGX#|SG#wTW0%b0tpin3< zEeZ4WE-D$o`Oe8ZJ`A9lULr1^*Jr|dt!D$+)gETalxIblxYV!6w6fF( zDWUbGL&TGQ#@O+DW7qmdr@xZ*U7wdn>r)!yH!ZC*{RfjKHrY=Y zLQHS=W#pRoHJXolQ(?I&fLr4#eR?!S8=YN6$`~xcX|#?u!0U!^u>hZ^>f4)3B?NsJ zwdgioNnssTCa2K!5iQW}!M&)Q_`LcuGVFeyHqe>CjyGz#ZPbgU(gJoo2CyuT;c4}0 zwEn(>)sC^EYWW*oGhnC6JS2>-UAEQJu?hbM&c;pD5->V+HTG|cNA}pOKknvFS(aYO zEU7&?Dak7Wz9&wq=uz_`*x{J2=eM~aPTDoj0p}Tkl&v|lP<5=o7dPqv zXT{$tNTR*rSBL+TAajGRUfqV%*RMux)}t2Y2kSNZIxR(x>&>vjH zFM<0w($AqMsx2vGHvB>wa_WqN5iku{>%HSHbw3cyfGa8j zFp3`MMu8quJ#+>`J(iHbJfzvG+R{RlD!uUtk3Gkzito4)l>8pSUe77tbQ}P8B?wg;PErltf z#CXX$bVG(1t4f*CaVl-juuo$H_j(EoMxeqOIzUL8hR%MFqe_d5gR2p;R|?&1gVufu?)Zrlqs$Wb=`8gOep0 zFN3C|i)6}sH%drsHshQ#_+@Aj15=aR4#+o3f$_a^Z7*|`71#OJkC5NK-niMS$Jtvy z!>2D=A~?T+uhQhc{`5Em5`eDefC4~0ov`{b;3D7VdV;Ts;Nmgx-i0y=lTCAT55U_6 zff6DYqmKx$gk@^V1QVy+vHEgIhGc4-kz-)o9hn$QIn^Wme`H4;3kFU1OcQCOzC}=VgdeUX z6T*rswfzoTr1L<=E~Kh(z1&o^AT@8bLP6v8@3XGmYA##T!FB7E-XIUT_uZS&oLI{K z1JsdJ-3qEb5#XMHz`Rxm z4~E!cnctFnlwXNn>-kTb2(}�AGE8#-_cw4vd*VwK_0nS8xCs(kMMk7R8%NgNJ!f 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

IbK_s{mGtCN%So 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

@ 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%0D3@}lkH-&J7o#v43X2STk#EZOyVOGr#eSBO+|?M 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™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%0Dw|Sd`klJ(k);JUBT>vtD8? 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 z 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 F{|2;oyxjl* literal 0 HcmV?d00001 diff --git a/doc/coding_style.doc b/doc/coding_style.doc new file mode 100644 index 0000000000000000000000000000000000000000..f48935b7ff1da601ec431dabed00eccb460339ee GIT binary patch literal 54272 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 +Documentation generated with DoxyGen
diff --git a/doc/ldecod.dox b/doc/ldecod.dox new file mode 100644 index 0000000..8148cac --- /dev/null +++ b/doc/ldecod.dox @@ -0,0 +1,234 @@ +# Doxyfile 1.5.4 + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- +DOXYFILE_ENCODING = UTF-8 +PROJECT_NAME = "H.264/AVC Reference Software Decoder " +PROJECT_NUMBER = +OUTPUT_DIRECTORY = ./ldec +CREATE_SUBDIRS = NO +OUTPUT_LANGUAGE = English +BRIEF_MEMBER_DESC = NO +REPEAT_BRIEF = YES +ABBREVIATE_BRIEF = +ALWAYS_DETAILED_SEC = NO +INLINE_INHERITED_MEMB = NO +FULL_PATH_NAMES = NO +STRIP_FROM_PATH = +STRIP_FROM_INC_PATH = +SHORT_NAMES = NO +JAVADOC_AUTOBRIEF = NO +QT_AUTOBRIEF = NO +MULTILINE_CPP_IS_BRIEF = NO +DETAILS_AT_TOP = NO +INHERIT_DOCS = YES +SEPARATE_MEMBER_PAGES = NO +TAB_SIZE = 8 +ALIASES = +OPTIMIZE_OUTPUT_FOR_C = YES +OPTIMIZE_OUTPUT_JAVA = NO +BUILTIN_STL_SUPPORT = NO +CPP_CLI_SUPPORT = NO +SIP_SUPPORT = NO +DISTRIBUTE_GROUP_DOC = NO +SUBGROUPING = YES +TYPEDEF_HIDES_STRUCT = NO +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- +EXTRACT_ALL = NO +EXTRACT_PRIVATE = NO +EXTRACT_STATIC = YES +EXTRACT_LOCAL_CLASSES = YES +EXTRACT_LOCAL_METHODS = NO +EXTRACT_ANON_NSPACES = NO +HIDE_UNDOC_MEMBERS = NO +HIDE_UNDOC_CLASSES = NO +HIDE_FRIEND_COMPOUNDS = NO +HIDE_IN_BODY_DOCS = NO +INTERNAL_DOCS = NO +CASE_SENSE_NAMES = YES +HIDE_SCOPE_NAMES = YES +SHOW_INCLUDE_FILES = YES +INLINE_INFO = YES +SORT_MEMBER_DOCS = YES +SORT_BRIEF_DOCS = NO +SORT_BY_SCOPE_NAME = NO +GENERATE_TODOLIST = YES +GENERATE_TESTLIST = YES +GENERATE_BUGLIST = YES +GENERATE_DEPRECATEDLIST= YES +ENABLED_SECTIONS = +MAX_INITIALIZER_LINES = 30 +SHOW_USED_FILES = YES +SHOW_DIRECTORIES = NO +FILE_VERSION_FILTER = +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- +QUIET = NO +WARNINGS = YES +WARN_IF_UNDOCUMENTED = NO +WARN_IF_DOC_ERROR = YES +WARN_NO_PARAMDOC = NO +WARN_FORMAT = "$file:$line: $text " +WARN_LOGFILE = warning.log +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- +INPUT = ../ldecod/ \ + ../lcommon/ +INPUT_ENCODING = ISO-8859-1 +FILE_PATTERNS = *.c \ + *.h +RECURSIVE = YES +EXCLUDE = +EXCLUDE_SYMLINKS = NO +EXCLUDE_PATTERNS = +EXCLUDE_SYMBOLS = +EXAMPLE_PATH = +EXAMPLE_PATTERNS = +EXAMPLE_RECURSIVE = NO +IMAGE_PATH = +INPUT_FILTER = +FILTER_PATTERNS = +FILTER_SOURCE_FILES = NO +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- +SOURCE_BROWSER = YES +INLINE_SOURCES = NO +STRIP_CODE_COMMENTS = YES +REFERENCED_BY_RELATION = YES +REFERENCES_RELATION = YES +REFERENCES_LINK_SOURCE = YES +USE_HTAGS = NO +VERBATIM_HEADERS = YES +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- +ALPHABETICAL_INDEX = NO +COLS_IN_ALPHA_INDEX = 5 +IGNORE_PREFIX = +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- +GENERATE_HTML = YES +HTML_OUTPUT = html +HTML_FILE_EXTENSION = .html +HTML_HEADER = +HTML_FOOTER = ./foot.html +HTML_ALIGN_MEMBERS = YES +GENERATE_HTMLHELP = NO +HTML_DYNAMIC_SECTIONS = NO +CHM_FILE = +HHC_LOCATION = +GENERATE_CHI = NO +BINARY_TOC = NO +TOC_EXPAND = NO +DISABLE_INDEX = NO +ENUM_VALUES_PER_LINE = 4 +GENERATE_TREEVIEW = NO +TREEVIEW_WIDTH = 250 +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- +GENERATE_LATEX = NO +LATEX_OUTPUT = latex +LATEX_CMD_NAME = latex +MAKEINDEX_CMD_NAME = makeindex +COMPACT_LATEX = NO +PAPER_TYPE = a4wide +EXTRA_PACKAGES = +LATEX_HEADER = +PDF_HYPERLINKS = NO +USE_PDFLATEX = NO +LATEX_BATCHMODE = NO +LATEX_HIDE_INDICES = NO +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- +GENERATE_RTF = NO +RTF_OUTPUT = rtf +COMPACT_RTF = NO +RTF_HYPERLINKS = NO +RTF_STYLESHEET_FILE = +RTF_EXTENSIONS_FILE = +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- +GENERATE_MAN = NO +MAN_OUTPUT = man +MAN_EXTENSION = .3 +MAN_LINKS = NO +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- +GENERATE_XML = NO +XML_OUTPUT = xml +XML_SCHEMA = +XML_DTD = +XML_PROGRAMLISTING = YES +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- +GENERATE_AUTOGEN_DEF = NO +#--------------------------------------------------------------------------- +# configuration options related to the Perl module output +#--------------------------------------------------------------------------- +GENERATE_PERLMOD = NO +PERLMOD_LATEX = NO +PERLMOD_PRETTY = YES +PERLMOD_MAKEVAR_PREFIX = +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- +ENABLE_PREPROCESSING = YES +MACRO_EXPANSION = NO +EXPAND_ONLY_PREDEF = NO +SEARCH_INCLUDES = YES +INCLUDE_PATH = +INCLUDE_FILE_PATTERNS = +PREDEFINED = +EXPAND_AS_DEFINED = +SKIP_FUNCTION_MACROS = YES +#--------------------------------------------------------------------------- +# Configuration::additions related to external references +#--------------------------------------------------------------------------- +TAGFILES = +GENERATE_TAGFILE = +ALLEXTERNALS = NO +EXTERNAL_GROUPS = YES +PERL_PATH = /usr/bin/perl +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- +CLASS_DIAGRAMS = YES +MSCGEN_PATH = +HIDE_UNDOC_RELATIONS = YES +HAVE_DOT = YES +CLASS_GRAPH = YES +COLLABORATION_GRAPH = YES +GROUP_GRAPHS = YES +UML_LOOK = NO +TEMPLATE_RELATIONS = YES +INCLUDE_GRAPH = YES +INCLUDED_BY_GRAPH = YES +CALL_GRAPH = YES +CALLER_GRAPH = NO +GRAPHICAL_HIERARCHY = YES +DIRECTORY_GRAPH = YES +DOT_IMAGE_FORMAT = png +DOT_PATH = +DOTFILE_DIRS = +DOT_GRAPH_MAX_NODES = 50 +MAX_DOT_GRAPH_DEPTH = 5 +DOT_TRANSPARENT = YES +DOT_MULTI_TARGETS = NO +GENERATE_LEGEND = YES +DOT_CLEANUP = YES +#--------------------------------------------------------------------------- +# Configuration::additions related to the search engine +#--------------------------------------------------------------------------- +SEARCHENGINE = NO diff --git a/doc/lencod.dox b/doc/lencod.dox new file mode 100644 index 0000000..195126f --- /dev/null +++ b/doc/lencod.dox @@ -0,0 +1,234 @@ +# Doxyfile 1.5.4 + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- +DOXYFILE_ENCODING = UTF-8 +PROJECT_NAME = "H.264/AVC Reference Software Encoder " +PROJECT_NUMBER = +OUTPUT_DIRECTORY = ./lenc +CREATE_SUBDIRS = NO +OUTPUT_LANGUAGE = English +BRIEF_MEMBER_DESC = NO +REPEAT_BRIEF = YES +ABBREVIATE_BRIEF = +ALWAYS_DETAILED_SEC = NO +INLINE_INHERITED_MEMB = NO +FULL_PATH_NAMES = NO +STRIP_FROM_PATH = +STRIP_FROM_INC_PATH = +SHORT_NAMES = NO +JAVADOC_AUTOBRIEF = NO +QT_AUTOBRIEF = NO +MULTILINE_CPP_IS_BRIEF = NO +DETAILS_AT_TOP = NO +INHERIT_DOCS = YES +SEPARATE_MEMBER_PAGES = NO +TAB_SIZE = 8 +ALIASES = +OPTIMIZE_OUTPUT_FOR_C = YES +OPTIMIZE_OUTPUT_JAVA = NO +BUILTIN_STL_SUPPORT = NO +CPP_CLI_SUPPORT = NO +SIP_SUPPORT = NO +DISTRIBUTE_GROUP_DOC = NO +SUBGROUPING = YES +TYPEDEF_HIDES_STRUCT = NO +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- +EXTRACT_ALL = NO +EXTRACT_PRIVATE = NO +EXTRACT_STATIC = YES +EXTRACT_LOCAL_CLASSES = YES +EXTRACT_LOCAL_METHODS = NO +EXTRACT_ANON_NSPACES = NO +HIDE_UNDOC_MEMBERS = NO +HIDE_UNDOC_CLASSES = NO +HIDE_FRIEND_COMPOUNDS = NO +HIDE_IN_BODY_DOCS = NO +INTERNAL_DOCS = NO +CASE_SENSE_NAMES = YES +HIDE_SCOPE_NAMES = YES +SHOW_INCLUDE_FILES = YES +INLINE_INFO = YES +SORT_MEMBER_DOCS = YES +SORT_BRIEF_DOCS = NO +SORT_BY_SCOPE_NAME = NO +GENERATE_TODOLIST = YES +GENERATE_TESTLIST = YES +GENERATE_BUGLIST = YES +GENERATE_DEPRECATEDLIST= YES +ENABLED_SECTIONS = +MAX_INITIALIZER_LINES = 30 +SHOW_USED_FILES = YES +SHOW_DIRECTORIES = NO +FILE_VERSION_FILTER = +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- +QUIET = NO +WARNINGS = YES +WARN_IF_UNDOCUMENTED = NO +WARN_IF_DOC_ERROR = YES +WARN_NO_PARAMDOC = NO +WARN_FORMAT = "$file:$line: $text " +WARN_LOGFILE = warning.log +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- +INPUT = ../lencod/ \ + ../lcommon/ +INPUT_ENCODING = ISO-8859-1 +FILE_PATTERNS = *.c \ + *.h +RECURSIVE = YES +EXCLUDE = +EXCLUDE_SYMLINKS = NO +EXCLUDE_PATTERNS = +EXCLUDE_SYMBOLS = +EXAMPLE_PATH = +EXAMPLE_PATTERNS = +EXAMPLE_RECURSIVE = NO +IMAGE_PATH = +INPUT_FILTER = +FILTER_PATTERNS = +FILTER_SOURCE_FILES = NO +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- +SOURCE_BROWSER = YES +INLINE_SOURCES = NO +STRIP_CODE_COMMENTS = NO +REFERENCED_BY_RELATION = YES +REFERENCES_RELATION = YES +REFERENCES_LINK_SOURCE = YES +USE_HTAGS = NO +VERBATIM_HEADERS = YES +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- +ALPHABETICAL_INDEX = NO +COLS_IN_ALPHA_INDEX = 5 +IGNORE_PREFIX = +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- +GENERATE_HTML = YES +HTML_OUTPUT = html +HTML_FILE_EXTENSION = .html +HTML_HEADER = +HTML_FOOTER = ./foot.html +HTML_ALIGN_MEMBERS = YES +GENERATE_HTMLHELP = NO +HTML_DYNAMIC_SECTIONS = NO +CHM_FILE = +HHC_LOCATION = +GENERATE_CHI = NO +BINARY_TOC = NO +TOC_EXPAND = NO +DISABLE_INDEX = NO +ENUM_VALUES_PER_LINE = 4 +GENERATE_TREEVIEW = NO +TREEVIEW_WIDTH = 250 +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- +GENERATE_LATEX = NO +LATEX_OUTPUT = latex +LATEX_CMD_NAME = latex +MAKEINDEX_CMD_NAME = makeindex +COMPACT_LATEX = NO +PAPER_TYPE = a4wide +EXTRA_PACKAGES = +LATEX_HEADER = +PDF_HYPERLINKS = NO +USE_PDFLATEX = NO +LATEX_BATCHMODE = NO +LATEX_HIDE_INDICES = NO +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- +GENERATE_RTF = NO +RTF_OUTPUT = rtf +COMPACT_RTF = NO +RTF_HYPERLINKS = NO +RTF_STYLESHEET_FILE = +RTF_EXTENSIONS_FILE = +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- +GENERATE_MAN = NO +MAN_OUTPUT = man +MAN_EXTENSION = .3 +MAN_LINKS = NO +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- +GENERATE_XML = NO +XML_OUTPUT = xml +XML_SCHEMA = +XML_DTD = +XML_PROGRAMLISTING = YES +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- +GENERATE_AUTOGEN_DEF = NO +#--------------------------------------------------------------------------- +# configuration options related to the Perl module output +#--------------------------------------------------------------------------- +GENERATE_PERLMOD = NO +PERLMOD_LATEX = NO +PERLMOD_PRETTY = YES +PERLMOD_MAKEVAR_PREFIX = +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- +ENABLE_PREPROCESSING = YES +MACRO_EXPANSION = NO +EXPAND_ONLY_PREDEF = NO +SEARCH_INCLUDES = YES +INCLUDE_PATH = +INCLUDE_FILE_PATTERNS = +PREDEFINED = +EXPAND_AS_DEFINED = +SKIP_FUNCTION_MACROS = YES +#--------------------------------------------------------------------------- +# Configuration::additions related to external references +#--------------------------------------------------------------------------- +TAGFILES = +GENERATE_TAGFILE = +ALLEXTERNALS = NO +EXTERNAL_GROUPS = YES +PERL_PATH = /usr/bin/perl +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- +CLASS_DIAGRAMS = YES +MSCGEN_PATH = +HIDE_UNDOC_RELATIONS = YES +HAVE_DOT = YES +CLASS_GRAPH = YES +COLLABORATION_GRAPH = YES +GROUP_GRAPHS = YES +UML_LOOK = NO +TEMPLATE_RELATIONS = YES +INCLUDE_GRAPH = YES +INCLUDED_BY_GRAPH = YES +CALL_GRAPH = YES +CALLER_GRAPH = NO +GRAPHICAL_HIERARCHY = YES +DIRECTORY_GRAPH = YES +DOT_IMAGE_FORMAT = png +DOT_PATH = +DOTFILE_DIRS = +DOT_GRAPH_MAX_NODES = 50 +MAX_DOT_GRAPH_DEPTH = 5 +DOT_TRANSPARENT = YES +DOT_MULTI_TARGETS = NO +GENERATE_LEGEND = YES +DOT_CLEANUP = YES +#--------------------------------------------------------------------------- +# Configuration::additions related to the search engine +#--------------------------------------------------------------------------- +SEARCHENGINE = NO diff --git a/jm_vc10.sln b/jm_vc10.sln new file mode 100644 index 0000000..e702b7b --- /dev/null +++ b/jm_vc10.sln @@ -0,0 +1,59 @@ +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual Studio 2010 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ldecod", "ldecod_vc10.vcxproj", "{5499B067-CF32-4141-A757-E0A29866994A}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "outside_debug", "outside_debug", "{47CBCC62-2112-4673-83A3-7DA41A90E32E}" + ProjectSection(SolutionItems) = preProject + ..\jm_debug_mvc\decoder_config_jmvc_decoder.cfg = ..\jm_debug_mvc\decoder_config_jmvc_decoder.cfg + ..\jm_debug\zdecoder_config.cfg = ..\jm_debug\zdecoder_config.cfg + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "lencod", "lencod_vc10.vcxproj", "{9A39F1B7-3DFE-4263-8C02-8070F7D99AFA}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "rtpdump", "rtpdump\rtpdump_vc10.vcxproj", "{BE2D5D97-BD3F-489B-A346-123834DDDBBD}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "rtp_loss", "rtp_loss\rtp_loss_vc10.vcxproj", "{5160AFF8-033C-4944-8AD7-6E960344640F}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Debug|x64 = Debug|x64 + Release|Win32 = Release|Win32 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {5499B067-CF32-4141-A757-E0A29866994A}.Debug|Win32.ActiveCfg = Debug|Win32 + {5499B067-CF32-4141-A757-E0A29866994A}.Debug|Win32.Build.0 = Debug|Win32 + {5499B067-CF32-4141-A757-E0A29866994A}.Debug|x64.ActiveCfg = Debug|x64 + {5499B067-CF32-4141-A757-E0A29866994A}.Debug|x64.Build.0 = Debug|x64 + {5499B067-CF32-4141-A757-E0A29866994A}.Release|Win32.ActiveCfg = Release|Win32 + {5499B067-CF32-4141-A757-E0A29866994A}.Release|Win32.Build.0 = Release|Win32 + {5499B067-CF32-4141-A757-E0A29866994A}.Release|x64.ActiveCfg = Release|x64 + {5499B067-CF32-4141-A757-E0A29866994A}.Release|x64.Build.0 = Release|x64 + {9A39F1B7-3DFE-4263-8C02-8070F7D99AFA}.Debug|Win32.ActiveCfg = Debug|Win32 + {9A39F1B7-3DFE-4263-8C02-8070F7D99AFA}.Debug|Win32.Build.0 = Debug|Win32 + {9A39F1B7-3DFE-4263-8C02-8070F7D99AFA}.Debug|x64.ActiveCfg = Debug|x64 + {9A39F1B7-3DFE-4263-8C02-8070F7D99AFA}.Debug|x64.Build.0 = Debug|x64 + {9A39F1B7-3DFE-4263-8C02-8070F7D99AFA}.Release|Win32.ActiveCfg = Release|Win32 + {9A39F1B7-3DFE-4263-8C02-8070F7D99AFA}.Release|Win32.Build.0 = Release|Win32 + {9A39F1B7-3DFE-4263-8C02-8070F7D99AFA}.Release|x64.ActiveCfg = Release|x64 + {9A39F1B7-3DFE-4263-8C02-8070F7D99AFA}.Release|x64.Build.0 = Release|x64 + {BE2D5D97-BD3F-489B-A346-123834DDDBBD}.Debug|Win32.ActiveCfg = Debug|Win32 + {BE2D5D97-BD3F-489B-A346-123834DDDBBD}.Debug|Win32.Build.0 = Debug|Win32 + {BE2D5D97-BD3F-489B-A346-123834DDDBBD}.Debug|x64.ActiveCfg = Debug|Win32 + {BE2D5D97-BD3F-489B-A346-123834DDDBBD}.Release|Win32.ActiveCfg = Release|Win32 + {BE2D5D97-BD3F-489B-A346-123834DDDBBD}.Release|Win32.Build.0 = Release|Win32 + {BE2D5D97-BD3F-489B-A346-123834DDDBBD}.Release|x64.ActiveCfg = Release|x64 + {BE2D5D97-BD3F-489B-A346-123834DDDBBD}.Release|x64.Build.0 = Release|x64 + {5160AFF8-033C-4944-8AD7-6E960344640F}.Debug|Win32.ActiveCfg = Debug|Win32 + {5160AFF8-033C-4944-8AD7-6E960344640F}.Debug|Win32.Build.0 = Debug|Win32 + {5160AFF8-033C-4944-8AD7-6E960344640F}.Debug|x64.ActiveCfg = Debug|Win32 + {5160AFF8-033C-4944-8AD7-6E960344640F}.Release|Win32.ActiveCfg = Release|Win32 + {5160AFF8-033C-4944-8AD7-6E960344640F}.Release|Win32.Build.0 = Release|Win32 + {5160AFF8-033C-4944-8AD7-6E960344640F}.Release|x64.ActiveCfg = Release|Win32 + {5160AFF8-033C-4944-8AD7-6E960344640F}.Release|x64.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/jm_vc7.sln b/jm_vc7.sln new file mode 100644 index 0000000..2815fd1 --- /dev/null +++ b/jm_vc7.sln @@ -0,0 +1,47 @@ +Microsoft Visual Studio Solution File, Format Version 8.00 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ldecod", "ldecod_vc7.vcproj", "{5499B067-CF32-4141-A757-E0A29866994A}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "lencod", "lencod_vc7.vcproj", "{9A39F1B7-3DFE-4263-8C02-8070F7D99AFA}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "rtpdump", "rtpdump\rtpdump_vc7.vcproj", "{BE2D5D97-BD3F-489B-A346-123834DDDBBD}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "rtp_loss", "rtp_loss\rtp_loss_vc7.vcproj", "{BE2D5D97-BD3F-489B-A346-123834DDDBBD}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfiguration) = preSolution + Debug = Debug + Release = Release + EndGlobalSection + GlobalSection(ProjectDependencies) = postSolution + EndGlobalSection + GlobalSection(ProjectConfiguration) = postSolution + {5499B067-CF32-4141-A757-E0A29866994A}.Debug.ActiveCfg = Debug|Win32 + {5499B067-CF32-4141-A757-E0A29866994A}.Debug.Build.0 = Debug|Win32 + {5499B067-CF32-4141-A757-E0A29866994A}.Release.ActiveCfg = Release|Win32 + {5499B067-CF32-4141-A757-E0A29866994A}.Release.Build.0 = Release|Win32 + {9A39F1B7-3DFE-4263-8C02-8070F7D99AFA}.Debug.ActiveCfg = Debug|Win32 + {9A39F1B7-3DFE-4263-8C02-8070F7D99AFA}.Debug.Build.0 = Debug|Win32 + {9A39F1B7-3DFE-4263-8C02-8070F7D99AFA}.Release.ActiveCfg = Release|Win32 + {9A39F1B7-3DFE-4263-8C02-8070F7D99AFA}.Release.Build.0 = Release|Win32 + {BE2D5D97-BD3F-489B-A346-123834DDDBBD}.Debug.ActiveCfg = Debug|Win32 + {BE2D5D97-BD3F-489B-A346-123834DDDBBD}.Debug.Build.0 = Debug|Win32 + {BE2D5D97-BD3F-489B-A346-123834DDDBBD}.Release.ActiveCfg = Release|Win32 + {BE2D5D97-BD3F-489B-A346-123834DDDBBD}.Release.Build.0 = Release|Win32 + {BE2D5D97-BD3F-489B-A346-123834DDDBBD}.Debug.ActiveCfg = Debug|Win32 + {BE2D5D97-BD3F-489B-A346-123834DDDBBD}.Debug.Build.0 = Debug|Win32 + {BE2D5D97-BD3F-489B-A346-123834DDDBBD}.Release.ActiveCfg = Release|Win32 + {BE2D5D97-BD3F-489B-A346-123834DDDBBD}.Release.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + EndGlobalSection + GlobalSection(ExtensibilityAddIns) = postSolution + EndGlobalSection +EndGlobal diff --git a/jm_vc8.sln b/jm_vc8.sln new file mode 100644 index 0000000..799da8e --- /dev/null +++ b/jm_vc8.sln @@ -0,0 +1,57 @@ +Microsoft Visual Studio Solution File, Format Version 9.00 +# Visual Studio 2005 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ldecod", "ldecod_vc8.vcproj", "{5499B067-CF32-4141-A757-E0A29866994A}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "lencod", "lencod_vc8.vcproj", "{9A39F1B7-3DFE-4263-8C02-8070F7D99AFA}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "rtpdump", "rtpdump\rtpdump_vc8.vcproj", "{BE2D5D97-BD3F-489B-A346-123834DDDBBD}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "rtp_loss", "rtp_loss\rtp_loss_vc8.vcproj", "{50C36073-1189-4F75-B0BE-45186CF200A6}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Debug|x64 = Debug|x64 + Release|Win32 = Release|Win32 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {5499B067-CF32-4141-A757-E0A29866994A}.Debug|Win32.ActiveCfg = Debug|Win32 + {5499B067-CF32-4141-A757-E0A29866994A}.Debug|Win32.Build.0 = Debug|Win32 + {5499B067-CF32-4141-A757-E0A29866994A}.Debug|x64.ActiveCfg = Debug|x64 + {5499B067-CF32-4141-A757-E0A29866994A}.Debug|x64.Build.0 = Debug|x64 + {5499B067-CF32-4141-A757-E0A29866994A}.Debug|x64.Build.2 = ReleaseMMX|x64 + {5499B067-CF32-4141-A757-E0A29866994A}.Release|Win32.ActiveCfg = Release|Win32 + {5499B067-CF32-4141-A757-E0A29866994A}.Release|Win32.Build.0 = Release|Win32 + {5499B067-CF32-4141-A757-E0A29866994A}.Release|x64.ActiveCfg = Release|x64 + {5499B067-CF32-4141-A757-E0A29866994A}.Release|x64.Build.0 = Release|x64 + {9A39F1B7-3DFE-4263-8C02-8070F7D99AFA}.Debug|Win32.ActiveCfg = Debug|Win32 + {9A39F1B7-3DFE-4263-8C02-8070F7D99AFA}.Debug|Win32.Build.0 = Debug|Win32 + {9A39F1B7-3DFE-4263-8C02-8070F7D99AFA}.Debug|x64.ActiveCfg = Debug|x64 + {9A39F1B7-3DFE-4263-8C02-8070F7D99AFA}.Debug|x64.Build.0 = Debug|x64 + {9A39F1B7-3DFE-4263-8C02-8070F7D99AFA}.Debug|x64.Build.2 = Matlab|x64 + {9A39F1B7-3DFE-4263-8C02-8070F7D99AFA}.Release|Win32.ActiveCfg = Release|Win32 + {9A39F1B7-3DFE-4263-8C02-8070F7D99AFA}.Release|Win32.Build.0 = Release|Win32 + {9A39F1B7-3DFE-4263-8C02-8070F7D99AFA}.Release|x64.ActiveCfg = Release|x64 + {9A39F1B7-3DFE-4263-8C02-8070F7D99AFA}.Release|x64.Build.0 = Release|x64 + {BE2D5D97-BD3F-489B-A346-123834DDDBBD}.Debug|Win32.ActiveCfg = Debug|Win32 + {BE2D5D97-BD3F-489B-A346-123834DDDBBD}.Debug|Win32.Build.0 = Debug|Win32 + {BE2D5D97-BD3F-489B-A346-123834DDDBBD}.Debug|x64.ActiveCfg = Debug|x64 + {BE2D5D97-BD3F-489B-A346-123834DDDBBD}.Debug|x64.Build.0 = Debug|x64 + {BE2D5D97-BD3F-489B-A346-123834DDDBBD}.Release|Win32.ActiveCfg = Release|Win32 + {BE2D5D97-BD3F-489B-A346-123834DDDBBD}.Release|Win32.Build.0 = Release|Win32 + {BE2D5D97-BD3F-489B-A346-123834DDDBBD}.Release|x64.ActiveCfg = Release|x64 + {BE2D5D97-BD3F-489B-A346-123834DDDBBD}.Release|x64.Build.0 = Release|x64 + {50C36073-1189-4F75-B0BE-45186CF200A6}.Debug|Win32.ActiveCfg = Debug|Win32 + {50C36073-1189-4F75-B0BE-45186CF200A6}.Debug|Win32.Build.0 = Debug|Win32 + {50C36073-1189-4F75-B0BE-45186CF200A6}.Debug|x64.ActiveCfg = Debug|x64 + {50C36073-1189-4F75-B0BE-45186CF200A6}.Debug|x64.Build.0 = Debug|x64 + {50C36073-1189-4F75-B0BE-45186CF200A6}.Release|Win32.ActiveCfg = Release|Win32 + {50C36073-1189-4F75-B0BE-45186CF200A6}.Release|Win32.Build.0 = Release|Win32 + {50C36073-1189-4F75-B0BE-45186CF200A6}.Release|x64.ActiveCfg = Release|x64 + {50C36073-1189-4F75-B0BE-45186CF200A6}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/jm_vc9.sln b/jm_vc9.sln new file mode 100644 index 0000000..badf233 --- /dev/null +++ b/jm_vc9.sln @@ -0,0 +1,61 @@ +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual Studio 2008 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "lencod", "lencod_vc9.vcproj", "{9A39F1B7-3DFE-4263-8C02-8070F7D99AFA}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "rtpdump", "rtpdump\rtpdump_vc9.vcproj", "{BE2D5D97-BD3F-489B-A346-123834DDDBBD}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "rtp_loss", "rtp_loss\rtp_loss_vc9.vcproj", "{50C36073-1189-4F75-B0BE-45186CF200A6}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ldecod", "ldecod_vc9.vcproj", "{5499B067-CF32-4141-A757-E0A29866994A}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Debug|x64 = Debug|x64 + Release|Win32 = Release|Win32 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {9A39F1B7-3DFE-4263-8C02-8070F7D99AFA}.Debug|Win32.ActiveCfg = Debug|Win32 + {9A39F1B7-3DFE-4263-8C02-8070F7D99AFA}.Debug|Win32.Build.0 = Debug|Win32 + {9A39F1B7-3DFE-4263-8C02-8070F7D99AFA}.Debug|x64.ActiveCfg = Debug|x64 + {9A39F1B7-3DFE-4263-8C02-8070F7D99AFA}.Debug|x64.Build.0 = Debug|x64 + {9A39F1B7-3DFE-4263-8C02-8070F7D99AFA}.Release|Win32.ActiveCfg = Release|Win32 + {9A39F1B7-3DFE-4263-8C02-8070F7D99AFA}.Release|Win32.Build.0 = Release|Win32 + {9A39F1B7-3DFE-4263-8C02-8070F7D99AFA}.Release|x64.ActiveCfg = Release|x64 + {9A39F1B7-3DFE-4263-8C02-8070F7D99AFA}.Release|x64.Build.0 = Release|x64 + {BE2D5D97-BD3F-489B-A346-123834DDDBBD}.Debug|Win32.ActiveCfg = Debug|Win32 + {BE2D5D97-BD3F-489B-A346-123834DDDBBD}.Debug|Win32.Build.0 = Debug|Win32 + {BE2D5D97-BD3F-489B-A346-123834DDDBBD}.Debug|x64.ActiveCfg = Debug|x64 + {BE2D5D97-BD3F-489B-A346-123834DDDBBD}.Debug|x64.Build.0 = Debug|x64 + {BE2D5D97-BD3F-489B-A346-123834DDDBBD}.Release|Win32.ActiveCfg = Release|Win32 + {BE2D5D97-BD3F-489B-A346-123834DDDBBD}.Release|Win32.Build.0 = Release|Win32 + {BE2D5D97-BD3F-489B-A346-123834DDDBBD}.Release|x64.ActiveCfg = Release|x64 + {BE2D5D97-BD3F-489B-A346-123834DDDBBD}.Release|x64.Build.0 = Release|x64 + {50C36073-1189-4F75-B0BE-45186CF200A6}.Debug|Win32.ActiveCfg = Debug|Win32 + {50C36073-1189-4F75-B0BE-45186CF200A6}.Debug|Win32.Build.0 = Debug|Win32 + {50C36073-1189-4F75-B0BE-45186CF200A6}.Debug|x64.ActiveCfg = Debug|x64 + {50C36073-1189-4F75-B0BE-45186CF200A6}.Debug|x64.Build.0 = Debug|x64 + {50C36073-1189-4F75-B0BE-45186CF200A6}.Release|Win32.ActiveCfg = Release|Win32 + {50C36073-1189-4F75-B0BE-45186CF200A6}.Release|Win32.Build.0 = Release|Win32 + {50C36073-1189-4F75-B0BE-45186CF200A6}.Release|x64.ActiveCfg = Release|x64 + {50C36073-1189-4F75-B0BE-45186CF200A6}.Release|x64.Build.0 = Release|x64 + {5499B067-CF32-4141-A757-E0A29866994A}.Debug|Win32.ActiveCfg = Debug|Win32 + {5499B067-CF32-4141-A757-E0A29866994A}.Debug|Win32.Build.0 = Debug|Win32 + {5499B067-CF32-4141-A757-E0A29866994A}.Debug|x64.ActiveCfg = Debug|x64 + {5499B067-CF32-4141-A757-E0A29866994A}.Debug|x64.Build.0 = Debug|x64 + {5499B067-CF32-4141-A757-E0A29866994A}.Release|Win32.ActiveCfg = Release|Win32 + {5499B067-CF32-4141-A757-E0A29866994A}.Release|Win32.Build.0 = Release|Win32 + {5499B067-CF32-4141-A757-E0A29866994A}.Release|x64.ActiveCfg = Release|x64 + {5499B067-CF32-4141-A757-E0A29866994A}.Release|x64.Build.0 = Release|x64 + {F0A0C825-4281-41C9-8737-1163C5FEFD6E}.Debug|Win32.ActiveCfg = Debug|Win32 + {F0A0C825-4281-41C9-8737-1163C5FEFD6E}.Debug|Win32.Build.0 = Debug|Win32 + {F0A0C825-4281-41C9-8737-1163C5FEFD6E}.Debug|x64.ActiveCfg = Debug|Win32 + {F0A0C825-4281-41C9-8737-1163C5FEFD6E}.Release|Win32.ActiveCfg = Release|Win32 + {F0A0C825-4281-41C9-8737-1163C5FEFD6E}.Release|Win32.Build.0 = Release|Win32 + {F0A0C825-4281-41C9-8737-1163C5FEFD6E}.Release|x64.ActiveCfg = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/lcommon/inc/blk_prediction.h b/lcommon/inc/blk_prediction.h new file mode 100644 index 0000000..288584b --- /dev/null +++ b/lcommon/inc/blk_prediction.h @@ -0,0 +1,25 @@ + +/*! + ************************************************************************ + * \file + * blk_prediction.h + * + * \brief + * block prediction header + * + * \author + * Main contributors (see contributors.h for copyright, + * address and affiliation details) + * - Alexis Michael Tourapis + * + ************************************************************************************* + */ + +#ifndef _BLK_PREDICTION_H_ +#define _BLK_PREDICTION_H_ +#include "mbuffer.h" + +extern void compute_residue (imgpel **curImg, imgpel **mb_pred, int **mb_rres, int mb_x, int opix_x, int width, int height); +extern void sample_reconstruct (imgpel **curImg, imgpel **mb_pred, int **mb_rres, int mb_x, int opix_x, int width, int height, int max_imgpel_value, int dq_bits); +#endif + diff --git a/lcommon/inc/config_common.h b/lcommon/inc/config_common.h new file mode 100644 index 0000000..3b03aef --- /dev/null +++ b/lcommon/inc/config_common.h @@ -0,0 +1,35 @@ + +/*! + ************************************************************************ + * \file config_common.h + * + * \brief + * Common Config parsing functions + * + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * - Alexis Tourapis + * + ************************************************************************ + */ +#ifndef _CONFIG_COMMON_H_ +#define _CONFIG_COMMON_H_ + +//! Maps parameter name to its address, type etc. +typedef struct { + char *TokenName; //!< name + void *Place; //!< address + int Type; //!< type: 0-int, 1-char[], 2-double + double Default; //!< default value + int param_limits; //!< 0: no limits, 1: both min and max, 2: only min (i.e. no negatives), 3: special case for QPs since min needs bitdepth_qp_scale + double min_limit; + double max_limit; + int char_size; //!< Dimension of type char[] +} Mapping; + +extern char *GetConfigFileContent (char *Filename); +extern int InitParams (Mapping *Map); +extern int TestParams(Mapping *Map, int bitdepth_qp_scale[3]); +extern int DisplayParams(Mapping *Map, char *message); +extern void ParseContent (InputParameters *p_Inp, Mapping *Map, char *buf, int bufsize); +#endif \ No newline at end of file diff --git a/lcommon/inc/ctx_tables.h b/lcommon/inc/ctx_tables.h new file mode 100644 index 0000000..511e9b1 --- /dev/null +++ b/lcommon/inc/ctx_tables.h @@ -0,0 +1,994 @@ + +/*! + ************************************************************************************* + * \file ctx_tables.h + * + * \brief + * CABAC context initialization tables + * + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * - Detlev Marpe + * - Heiko Schwarz + ************************************************************************************** + */ + +#define CTX_UNUSED {0,64} +#define CTX_UNDEF {0,63} + +#ifdef CONTEXT_INI_C + + +#define NUM_CTX_MODELS_I 1 +#define NUM_CTX_MODELS_P 3 + + +static const char INIT_MB_TYPE_I[1][3][11][2] = +{ + //----- model 0 ----- + { + { { 20, -15} , { 2, 54} , { 3, 74} , CTX_UNUSED , { -28, 127} , { -23, 104} , { -6, 53} , { -1, 54} , { 7, 51} , CTX_UNUSED , CTX_UNUSED }, + { { 20, -15} , { 2, 54} , { 3, 74} , { 20, -15} , { 2, 54} , { 3, 74} , { -28, 127} , { -23, 104} , { -6, 53} , { -1, 54} , { 7, 51} }, // SI (unused at the moment) + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED } + } +}; +static const char INIT_MB_TYPE_P[3][3][11][2] = +{ + //----- model 0 ----- + { + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { { 23, 33} , { 23, 2} , { 21, 0} , CTX_UNUSED , { 1, 9} , { 0, 49} , { -37, 118} , { 5, 57} , { -13, 78} , { -11, 65} , { 1, 62} }, + { { 26, 67} , { 16, 90} , { 9, 104} , CTX_UNUSED , { -46, 127} , { -20, 104} , { 1, 67} , { 18, 64} , { 9, 43} , { 29, 0} , CTX_UNUSED } + }, + //----- model 1 ----- + { + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { { 22, 25} , { 34, 0} , { 16, 0} , CTX_UNUSED , { -2, 9} , { 4, 41} , { -29, 118} , { 2, 65} , { -6, 71} , { -13, 79} , { 5, 52} }, + { { 57, 2} , { 41, 36} , { 26, 69} , CTX_UNUSED , { -45, 127} , { -15, 101} , { -4, 76} , { 26, 34} , { 19, 22} , { 40, 0} , CTX_UNUSED } + }, + //----- model 2 ----- + { + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { { 29, 16} , { 25, 0} , { 14, 0} , CTX_UNUSED , { -10, 51} , { -3, 62} , { -27, 99} , { 26, 16} , { -4, 85} , { -24, 102} , { 5, 57} }, + { { 54, 0} , { 37, 42} , { 12, 97} , CTX_UNUSED , { -32, 127} , { -22, 117} , { -2, 74} , { 20, 40} , { 20, 10} , { 29, 0} , CTX_UNUSED } + } +}; + +static const char INIT_B8_TYPE_I[1][2][9][2] = +{ + //----- model 0 ----- + { + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED } + } +}; + +static const char INIT_B8_TYPE_P[3][2][9][2] = +{ + //----- model 0 ----- + { + { CTX_UNUSED , { 12, 49} , CTX_UNUSED , { -4, 73} , { 17, 50} , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { { -6, 86} , { -17, 95} , { -6, 61} , { 9, 45} , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED } + }, + //----- model 1 ----- + { + { CTX_UNUSED , { 9, 50} , CTX_UNUSED , { -3, 70} , { 10, 54} , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { { 6, 69} , { -13, 90} , { 0, 52} , { 8, 43} , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED } + }, + //----- model 2 ----- + { + { CTX_UNUSED , { 6, 57} , CTX_UNUSED , { -17, 73} , { 14, 57} , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { { -6, 93} , { -14, 88} , { -6, 44} , { 4, 55} , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED } + } +}; + +static const char INIT_MV_RES_I[1][2][10][2] = +{ + //----- model 0 ----- + { + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED } + } +}; + +static const char INIT_MV_RES_P[3][2][10][2] = +{ + //----- model 0 ----- + { + { { -3, 69} , CTX_UNUSED , { -6, 81} , { -11, 96} , CTX_UNUSED , { 0, 58} , CTX_UNUSED , { -3, 76} , { -10, 94} , CTX_UNUSED }, + { { 6, 55} , { 7, 67} , { -5, 86} , { 2, 88} , CTX_UNUSED , { 5, 54} , { 4, 69} , { -3, 81} , { 0, 88} , CTX_UNUSED } + }, + //----- model 1 ----- + { + { { -2, 69} , CTX_UNUSED , { -5, 82} , { -10, 96} , CTX_UNUSED , { 1, 56} , CTX_UNUSED , { -3, 74} , { -6, 85} , CTX_UNUSED }, + { { 2, 59} , { 2, 75} , { -3, 87} , { -3, 100} , CTX_UNUSED , { 0, 59} , { -3, 81} , { -7, 86} , { -5, 95} , CTX_UNUSED } + }, + //----- model 2 ----- + { + { { -11, 89} , CTX_UNUSED , { -15, 103} , { -21, 116} , CTX_UNUSED , { 1, 63} , CTX_UNUSED , { -5, 85} , { -13, 106} , CTX_UNUSED }, + { { 19, 57} , { 20, 58} , { 4, 84} , { 6, 96} , CTX_UNUSED , { 5, 63} , { 6, 75} , { -3, 90} , { -1, 101} , CTX_UNUSED } + } +}; + +static const char INIT_REF_NO_I[1][2][6][2] = +{ + //----- model 0 ----- + { + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED } + } +}; + +static const char INIT_REF_NO_P[3][2][6][2] = +{ + //----- model 0 ----- + { + { { -7, 67} , { -5, 74} , { -4, 74} , { -5, 80} , { -7, 72} , { 1, 58} }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED } + }, + //----- model 1 ----- + { + { { -1, 66} , { -1, 77} , { 1, 70} , { -2, 86} , { -5, 72} , { 0, 61} }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED } + }, + //----- model 2 ----- + { + { { 3, 55} , { -4, 79} , { -2, 75} , { -12, 97} , { -7, 50} , { 1, 60} }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED } + } +}; + + +static const char INIT_TRANSFORM_SIZE_I[1][1][3][2]= +{ + //----- model 0 ----- + { + { { 31, 21} , { 31, 31} , { 25, 50} }, +// { { 0, 41} , { 0, 63} , { 0, 63} }, + } +}; + +static const char INIT_TRANSFORM_SIZE_P[3][1][3][2]= +{ + //----- model 0 ----- + { + { { 12, 40} , { 11, 51} , { 14, 59} }, +// { { 0, 41} , { 0, 63} , { 0, 63} }, + }, + //----- model 1 ----- + { + { { 25, 32} , { 21, 49} , { 21, 54} }, +// { { 0, 41} , { 0, 63} , { 0, 63} }, + }, + //----- model 2 ----- + { + { { 21, 33} , { 19, 50} , { 17, 61} }, +// { { 0, 41} , { 0, 63} , { 0, 63} }, + } +}; + +static const char INIT_DELTA_QP_I[1][1][4][2]= +{ + //----- model 0 ----- + { + { { 0, 41} , { 0, 63} , { 0, 63} , { 0, 63} }, + } +}; +static const char INIT_DELTA_QP_P[3][1][4][2]= +{ + //----- model 0 ----- + { + { { 0, 41} , { 0, 63} , { 0, 63} , { 0, 63} }, + }, + //----- model 1 ----- + { + { { 0, 41} , { 0, 63} , { 0, 63} , { 0, 63} }, + }, + //----- model 2 ----- + { + { { 0, 41} , { 0, 63} , { 0, 63} , { 0, 63} }, + } +}; + +static const char INIT_MB_AFF_I[1][1][4][2] = +{ + //----- model 0 ----- + { + { { 0, 11} , { 1, 55} , { 0, 69} , CTX_UNUSED } + } +}; +static const char INIT_MB_AFF_P[3][1][4][2] = +{ + //----- model 0 ----- + { + { { 0, 45} , { -4, 78} , { -3, 96} , CTX_UNUSED } + }, + //----- model 1 ----- + { + { { 13, 15} , { 7, 51} , { 2, 80} , CTX_UNUSED } + }, + //----- model 2 ----- + { + { { 7, 34} , { -9, 88} , { -20, 127} , CTX_UNUSED } + } +}; + +static const char INIT_IPR_I[1][1][2][2] = +{ + //----- model 0 ----- + { + { { 13, 41} , { 3, 62} } + } +}; + +static const char INIT_IPR_P[3][1][2][2] = +{ + //----- model 0 ----- + { + { { 13, 41} , { 3, 62} } + }, + //----- model 1 ----- + { + { { 13, 41} , { 3, 62} } + }, + //----- model 2 ----- + { + { { 13, 41} , { 3, 62} } + } +}; + +static const char INIT_CIPR_I[1][1][4][2] = +{ + //----- model 0 ----- + { + { { -9, 83} , { 4, 86} , { 0, 97} , { -7, 72} } + } +}; + +static const char INIT_CIPR_P[3][1][4][2] = +{ + //----- model 0 ----- + { + { { -9, 83} , { 4, 86} , { 0, 97} , { -7, 72} } + }, + //----- model 1 ----- + { + { { -9, 83} , { 4, 86} , { 0, 97} , { -7, 72} } + }, + //----- model 2 ----- + { + { { -9, 83} , { 4, 86} , { 0, 97} , { -7, 72} } + } +}; + +static const char INIT_CBP_I[1][3][4][2] = +{ + //----- model 0 ----- + { + { { -17, 127} , { -13, 102} , { 0, 82} , { -7, 74} }, + { { -21, 107} , { -27, 127} , { -31, 127} , { -24, 127} }, + { { -18, 95} , { -27, 127} , { -21, 114} , { -30, 127} } + } +}; + +static const char INIT_CBP_P[3][3][4][2] = +{ + //----- model 0 ----- + { + { { -27, 126} , { -28, 98} , { -25, 101} , { -23, 67} }, + { { -28, 82} , { -20, 94} , { -16, 83} , { -22, 110} }, + { { -21, 91} , { -18, 102} , { -13, 93} , { -29, 127} } + }, + //----- model 1 ----- + { + { { -39, 127} , { -18, 91} , { -17, 96} , { -26, 81} }, + { { -35, 98} , { -24, 102} , { -23, 97} , { -27, 119} }, + { { -24, 99} , { -21, 110} , { -18, 102} , { -36, 127} } + }, + //----- model 2 ----- + { + { { -36, 127} , { -17, 91} , { -14, 95} , { -25, 84} }, + { { -25, 86} , { -12, 89} , { -17, 91} , { -31, 127} }, + { { -14, 76} , { -18, 103} , { -13, 90} , { -37, 127} } + } +}; + +static const char INIT_BCBP_I[1][22][4][2] = +{ + //----- model 0 ----- + { + { { -17, 123} , { -12, 115} , { -16, 122} , { -11, 115} }, + { { -12, 63} , { -2, 68} , { -15, 84} , { -13, 104} }, + { { -3, 70} , { -8, 93} , { -10, 90} , { -30, 127} }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { { -3, 70} , { -8, 93} , { -10, 90} , { -30, 127} }, + { { -1, 74} , { -6, 97} , { -7, 91} , { -20, 127} }, + { { -4, 56} , { -5, 82} , { -7, 76} , { -22, 125} }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + // Cb in the 4:4:4 common mode + { { -17, 123} , { -12, 115} , { -16, 122} , { -11, 115} }, + { { -12, 63} , { -2, 68} , { -15, 84} , { -13, 104} }, + { { -3, 70} , { -8, 93} , { -10, 90} , { -30, 127} }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { { -3, 70} , { -8, 93} , { -10, 90} , { -30, 127} }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + // Cr in the 4:4:4 common mode + { { -17, 123} , { -12, 115} , { -16, 122} , { -11, 115} }, + { { -12, 63} , { -2, 68} , { -15, 84} , { -13, 104} }, + { { -3, 70} , { -8, 93} , { -10, 90} , { -30, 127} }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { { -3, 70} , { -8, 93} , { -10, 90} , { -30, 127} }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED } + } +}; + +static const char INIT_BCBP_P[3][22][4][2] = +{ + //----- model 0 ----- + { + { { -7, 92} , { -5, 89} , { -7, 96} , { -13, 108} }, + { { -3, 46} , { -1, 65} , { -1, 57} , { -9, 93} }, + { { -3, 74} , { -9, 92} , { -8, 87} , { -23, 126} }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { { -3, 74} , { -9, 92} , { -8, 87} , { -23, 126} }, + { { 5, 54} , { 6, 60} , { 6, 59} , { 6, 69} }, + { { -1, 48} , { 0, 68} , { -4, 69} , { -8, 88} }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + // Cb in the 4:4:4 common mode + { { -7, 92} , { -5, 89} , { -7, 96} , { -13, 108} }, + { { -3, 46} , { -1, 65} , { -1, 57} , { -9, 93} }, + { { -3, 74} , { -9, 92} , { -8, 87} , { -23, 126} }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { { -3, 74} , { -9, 92} , { -8, 87} , { -23, 126} }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + // Cr in the 4:4:4 common mode + { { -7, 92} , { -5, 89} , { -7, 96} , { -13, 108} }, + { { -3, 46} , { -1, 65} , { -1, 57} , { -9, 93} }, + { { -3, 74} , { -9, 92} , { -8, 87} , { -23, 126} }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { { -3, 74} , { -9, 92} , { -8, 87} , { -23, 126} }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED } + }, + //----- model 1 ----- + { + { { 0, 80} , { -5, 89} , { -7, 94} , { -4, 92} }, + { { 0, 39} , { 0, 65} , { -15, 84} , { -35, 127} }, + { { -2, 73} , { -12, 104} , { -9, 91} , { -31, 127} }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { { -2, 73} , { -12, 104} , { -9, 91} , { -31, 127} }, + { { 3, 55} , { 7, 56} , { 7, 55} , { 8, 61} }, + { { -3, 53} , { 0, 68} , { -7, 74} , { -9, 88} }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + // Cb in the 4:4:4 common mode + { { 0, 80} , { -5, 89} , { -7, 94} , { -4, 92} }, + { { 0, 39} , { 0, 65} , { -15, 84} , { -35, 127} }, + { { -2, 73} , { -12, 104} , { -9, 91} , { -31, 127} }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { { -2, 73} , { -12, 104} , { -9, 91} , { -31, 127} }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + // Cr in the 4:4:4 common mode + { { 0, 80} , { -5, 89} , { -7, 94} , { -4, 92} }, + { { 0, 39} , { 0, 65} , { -15, 84} , { -35, 127} }, + { { -2, 73} , { -12, 104} , { -9, 91} , { -31, 127} }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { { -2, 73} , { -12, 104} , { -9, 91} , { -31, 127} }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED } + }, + //----- model 2 ----- + { + { { 11, 80} , { 5, 76} , { 2, 84} , { 5, 78} }, + { { -6, 55} , { 4, 61} , { -14, 83} , { -37, 127} }, + { { -5, 79} , { -11, 104} , { -11, 91} , { -30, 127} }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { { -5, 79} , { -11, 104} , { -11, 91} , { -30, 127} }, + { { 0, 65} , { -2, 79} , { 0, 72} , { -4, 92} }, + { { -6, 56} , { 3, 68} , { -8, 71} , { -13, 98} }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + // Cb in the 4:4:4 common mode + { { 11, 80} , { 5, 76} , { 2, 84} , { 5, 78} }, + { { -6, 55} , { 4, 61} , { -14, 83} , { -37, 127} }, + { { -5, 79} , { -11, 104} , { -11, 91} , { -30, 127} }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { { -5, 79} , { -11, 104} , { -11, 91} , { -30, 127} }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + // Cr in the 4:4:4 common mode + { { 11, 80} , { 5, 76} , { 2, 84} , { 5, 78} }, + { { -6, 55} , { 4, 61} , { -14, 83} , { -37, 127} }, + { { -5, 79} , { -11, 104} , { -11, 91} , { -30, 127} }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { { -5, 79} , { -11, 104} , { -11, 91} , { -30, 127} }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED } + } +}; + +static const char INIT_MAP_I[1][22][15][2] = +{ + //----- model 0 ----- + { + { { -7, 93} , { -11, 87} , { -3, 77} , { -5, 71} , { -4, 63} , { -4, 68} , { -12, 84} , { -7, 62} , { -7, 65} , { 8, 61} , { 5, 56} , { -2, 66} , { 1, 64} , { 0, 61} , { -2, 78} }, + { CTX_UNUSED , { 1, 50} , { 7, 52} , { 10, 35} , { 0, 44} , { 11, 38} , { 1, 45} , { 0, 46} , { 5, 44} , { 31, 17} , { 1, 51} , { 7, 50} , { 28, 19} , { 16, 33} , { 14, 62} }, + { { -17, 120} , { -20, 112} , { -18, 114} , { -11, 85} , { -15, 92} , { -14, 89} , { -26, 71} , { -15, 81} , { -14, 80} , { 0, 68} , { -14, 70} , { -24, 56} , { -23, 68} , { -24, 50} , { -11, 74} }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { { -13, 108} , { -15, 100} , { -13, 101} , { -13, 91} , { -12, 94} , { -10, 88} , { -16, 84} , { -10, 86} , { -7, 83} , { -13, 87} , { -19, 94} , { 1, 70} , { 0, 72} , { -5, 74} , { 18, 59} }, + { { -8, 102} , { -15, 100} , { 0, 95} , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { CTX_UNUSED , { -4, 75} , { 2, 72} , { -11, 75} , { -3, 71} , { 15, 46} , { -13, 69} , { 0, 62} , { 0, 65} , { 21, 37} , { -15, 72} , { 9, 57} , { 16, 54} , { 0, 62} , { 12, 72} }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + //Cb in the 4:4:4 common mode + { { -7, 93} , { -11, 87} , { -3, 77} , { -5, 71} , { -4, 63} , { -4, 68} , { -12, 84} , { -7, 62} , { -7, 65} , { 8, 61} , { 5, 56} , { -2, 66} , { 1, 64} , { 0, 61} , { -2, 78} }, + { CTX_UNUSED , { 1, 50} , { 7, 52} , { 10, 35} , { 0, 44} , { 11, 38} , { 1, 45} , { 0, 46} , { 5, 44} , { 31, 17} , { 1, 51} , { 7, 50} , { 28, 19} , { 16, 33} , { 14, 62} }, + { { -17, 120} , { -20, 112} , { -18, 114} , { -11, 85} , { -15, 92} , { -14, 89} , { -26, 71} , { -15, 81} , { -14, 80} , { 0, 68} , { -14, 70} , { -24, 56} , { -23, 68} , { -24, 50} , { -11, 74} }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { { -13, 108} , { -15, 100} , { -13, 101} , { -13, 91} , { -12, 94} , { -10, 88} , { -16, 84} , { -10, 86} , { -7, 83} , { -13, 87} , { -19, 94} , { 1, 70} , { 0, 72} , { -5, 74} , { 18, 59} }, + //Cr in the 4:4:4 common mode + { { -7, 93} , { -11, 87} , { -3, 77} , { -5, 71} , { -4, 63} , { -4, 68} , { -12, 84} , { -7, 62} , { -7, 65} , { 8, 61} , { 5, 56} , { -2, 66} , { 1, 64} , { 0, 61} , { -2, 78} }, + { CTX_UNUSED , { 1, 50} , { 7, 52} , { 10, 35} , { 0, 44} , { 11, 38} , { 1, 45} , { 0, 46} , { 5, 44} , { 31, 17} , { 1, 51} , { 7, 50} , { 28, 19} , { 16, 33} , { 14, 62} }, + { { -17, 120} , { -20, 112} , { -18, 114} , { -11, 85} , { -15, 92} , { -14, 89} , { -26, 71} , { -15, 81} , { -14, 80} , { 0, 68} , { -14, 70} , { -24, 56} , { -23, 68} , { -24, 50} , { -11, 74} }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { { -13, 108} , { -15, 100} , { -13, 101} , { -13, 91} , { -12, 94} , { -10, 88} , { -16, 84} , { -10, 86} , { -7, 83} , { -13, 87} , { -19, 94} , { 1, 70} , { 0, 72} , { -5, 74} , { 18, 59} } + } +}; + +static const char INIT_MAP_P[3][22][15][2] = +{ + //----- model 0 ----- + { + { { -2, 85} , { -6, 78} , { -1, 75} , { -7, 77} , { 2, 54} , { 5, 50} , { -3, 68} , { 1, 50} , { 6, 42} , { -4, 81} , { 1, 63} , { -4, 70} , { 0, 67} , { 2, 57} , { -2, 76} }, + { CTX_UNUSED , { 11, 35} , { 4, 64} , { 1, 61} , { 11, 35} , { 18, 25} , { 12, 24} , { 13, 29} , { 13, 36} , { -10, 93} , { -7, 73} , { -2, 73} , { 13, 46} , { 9, 49} , { -7, 100} }, + { { -4, 79} , { -7, 71} , { -5, 69} , { -9, 70} , { -8, 66} , { -10, 68} , { -19, 73} , { -12, 69} , { -16, 70} , { -15, 67} , { -20, 62} , { -19, 70} , { -16, 66} , { -22, 65} , { -20, 63} }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { { 9, 53} , { 2, 53} , { 5, 53} , { -2, 61} , { 0, 56} , { 0, 56} , { -13, 63} , { -5, 60} , { -1, 62} , { 4, 57} , { -6, 69} , { 4, 57} , { 14, 39} , { 4, 51} , { 13, 68} }, + { { 3, 64} , { 1, 61} , { 9, 63} , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { CTX_UNUSED , { 7, 50} , { 16, 39} , { 5, 44} , { 4, 52} , { 11, 48} , { -5, 60} , { -1, 59} , { 0, 59} , { 22, 33} , { 5, 44} , { 14, 43} , { -1, 78} , { 0, 60} , { 9, 69} }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + //Cb in the 4:4:4 common mode + { { -2, 85} , { -6, 78} , { -1, 75} , { -7, 77} , { 2, 54} , { 5, 50} , { -3, 68} , { 1, 50} , { 6, 42} , { -4, 81} , { 1, 63} , { -4, 70} , { 0, 67} , { 2, 57} , { -2, 76} }, + { CTX_UNUSED , { 11, 35} , { 4, 64} , { 1, 61} , { 11, 35} , { 18, 25} , { 12, 24} , { 13, 29} , { 13, 36} , { -10, 93} , { -7, 73} , { -2, 73} , { 13, 46} , { 9, 49} , { -7, 100} }, + { { -4, 79} , { -7, 71} , { -5, 69} , { -9, 70} , { -8, 66} , { -10, 68} , { -19, 73} , { -12, 69} , { -16, 70} , { -15, 67} , { -20, 62} , { -19, 70} , { -16, 66} , { -22, 65} , { -20, 63} }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { { 9, 53} , { 2, 53} , { 5, 53} , { -2, 61} , { 0, 56} , { 0, 56} , { -13, 63} , { -5, 60} , { -1, 62} , { 4, 57} , { -6, 69} , { 4, 57} , { 14, 39} , { 4, 51} , { 13, 68} }, + //Cr in the 4:4:4 common mode + { { -2, 85} , { -6, 78} , { -1, 75} , { -7, 77} , { 2, 54} , { 5, 50} , { -3, 68} , { 1, 50} , { 6, 42} , { -4, 81} , { 1, 63} , { -4, 70} , { 0, 67} , { 2, 57} , { -2, 76} }, + { CTX_UNUSED , { 11, 35} , { 4, 64} , { 1, 61} , { 11, 35} , { 18, 25} , { 12, 24} , { 13, 29} , { 13, 36} , { -10, 93} , { -7, 73} , { -2, 73} , { 13, 46} , { 9, 49} , { -7, 100} }, + { { -4, 79} , { -7, 71} , { -5, 69} , { -9, 70} , { -8, 66} , { -10, 68} , { -19, 73} , { -12, 69} , { -16, 70} , { -15, 67} , { -20, 62} , { -19, 70} , { -16, 66} , { -22, 65} , { -20, 63} }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { { 9, 53} , { 2, 53} , { 5, 53} , { -2, 61} , { 0, 56} , { 0, 56} , { -13, 63} , { -5, 60} , { -1, 62} , { 4, 57} , { -6, 69} , { 4, 57} , { 14, 39} , { 4, 51} , { 13, 68} } + }, + //----- model 1 ----- + { + { { -13, 103} , { -13, 91} , { -9, 89} , { -14, 92} , { -8, 76} , { -12, 87} , { -23, 110} , { -24, 105} , { -10, 78} , { -20, 112} , { -17, 99} , { -78, 127} , { -70, 127} , { -50, 127} , { -46, 127} }, + { CTX_UNUSED , { -4, 66} , { -5, 78} , { -4, 71} , { -8, 72} , { 2, 59} , { -1, 55} , { -7, 70} , { -6, 75} , { -8, 89} , { -34, 119} , { -3, 75} , { 32, 20} , { 30, 22} , { -44, 127} }, + { { -5, 85} , { -6, 81} , { -10, 77} , { -7, 81} , { -17, 80} , { -18, 73} , { -4, 74} , { -10, 83} , { -9, 71} , { -9, 67} , { -1, 61} , { -8, 66} , { -14, 66} , { 0, 59} , { 2, 59} }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { { 0, 54} , { -5, 61} , { 0, 58} , { -1, 60} , { -3, 61} , { -8, 67} , { -25, 84} , { -14, 74} , { -5, 65} , { 5, 52} , { 2, 57} , { 0, 61} , { -9, 69} , { -11, 70} , { 18, 55} }, + { { -4, 71} , { 0, 58} , { 7, 61} , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { CTX_UNUSED , { 9, 41} , { 18, 25} , { 9, 32} , { 5, 43} , { 9, 47} , { 0, 44} , { 0, 51} , { 2, 46} , { 19, 38} , { -4, 66} , { 15, 38} , { 12, 42} , { 9, 34} , { 0, 89} }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + //Cb in the 4:4:4 common mode + { { -13, 103} , { -13, 91} , { -9, 89} , { -14, 92} , { -8, 76} , { -12, 87} , { -23, 110} , { -24, 105} , { -10, 78} , { -20, 112} , { -17, 99} , { -78, 127} , { -70, 127} , { -50, 127} , { -46, 127} }, + { CTX_UNUSED , { -4, 66} , { -5, 78} , { -4, 71} , { -8, 72} , { 2, 59} , { -1, 55} , { -7, 70} , { -6, 75} , { -8, 89} , { -34, 119} , { -3, 75} , { 32, 20} , { 30, 22} , { -44, 127} }, + { { -5, 85} , { -6, 81} , { -10, 77} , { -7, 81} , { -17, 80} , { -18, 73} , { -4, 74} , { -10, 83} , { -9, 71} , { -9, 67} , { -1, 61} , { -8, 66} , { -14, 66} , { 0, 59} , { 2, 59} }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { { 0, 54} , { -5, 61} , { 0, 58} , { -1, 60} , { -3, 61} , { -8, 67} , { -25, 84} , { -14, 74} , { -5, 65} , { 5, 52} , { 2, 57} , { 0, 61} , { -9, 69} , { -11, 70} , { 18, 55} }, + //Cr in the 4:4:4 common mode + { { -13, 103} , { -13, 91} , { -9, 89} , { -14, 92} , { -8, 76} , { -12, 87} , { -23, 110} , { -24, 105} , { -10, 78} , { -20, 112} , { -17, 99} , { -78, 127} , { -70, 127} , { -50, 127} , { -46, 127} }, + { CTX_UNUSED , { -4, 66} , { -5, 78} , { -4, 71} , { -8, 72} , { 2, 59} , { -1, 55} , { -7, 70} , { -6, 75} , { -8, 89} , { -34, 119} , { -3, 75} , { 32, 20} , { 30, 22} , { -44, 127} }, + { { -5, 85} , { -6, 81} , { -10, 77} , { -7, 81} , { -17, 80} , { -18, 73} , { -4, 74} , { -10, 83} , { -9, 71} , { -9, 67} , { -1, 61} , { -8, 66} , { -14, 66} , { 0, 59} , { 2, 59} }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { { 0, 54} , { -5, 61} , { 0, 58} , { -1, 60} , { -3, 61} , { -8, 67} , { -25, 84} , { -14, 74} , { -5, 65} , { 5, 52} , { 2, 57} , { 0, 61} , { -9, 69} , { -11, 70} , { 18, 55} } + }, + //----- model 2 ----- + { + { { -4, 86} , { -12, 88} , { -5, 82} , { -3, 72} , { -4, 67} , { -8, 72} , { -16, 89} , { -9, 69} , { -1, 59} , { 5, 66} , { 4, 57} , { -4, 71} , { -2, 71} , { 2, 58} , { -1, 74} }, + { CTX_UNUSED , { -4, 44} , { -1, 69} , { 0, 62} , { -7, 51} , { -4, 47} , { -6, 42} , { -3, 41} , { -6, 53} , { 8, 76} , { -9, 78} , { -11, 83} , { 9, 52} , { 0, 67} , { -5, 90} }, + { { -3, 78} , { -8, 74} , { -9, 72} , { -10, 72} , { -18, 75} , { -12, 71} , { -11, 63} , { -5, 70} , { -17, 75} , { -14, 72} , { -16, 67} , { -8, 53} , { -14, 59} , { -9, 52} , { -11, 68} }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { { 1, 67} , { -15, 72} , { -5, 75} , { -8, 80} , { -21, 83} , { -21, 64} , { -13, 31} , { -25, 64} , { -29, 94} , { 9, 75} , { 17, 63} , { -8, 74} , { -5, 35} , { -2, 27} , { 13, 91} }, + { { 3, 65} , { -7, 69} , { 8, 77} , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { CTX_UNUSED , { -10, 66} , { 3, 62} , { -3, 68} , { -20, 81} , { 0, 30} , { 1, 7} , { -3, 23} , { -21, 74} , { 16, 66} , { -23, 124} , { 17, 37} , { 44, -18} , { 50, -34} , { -22, 127} }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + //Cb in the 4:4:4 common mode + { { -4, 86} , { -12, 88} , { -5, 82} , { -3, 72} , { -4, 67} , { -8, 72} , { -16, 89} , { -9, 69} , { -1, 59} , { 5, 66} , { 4, 57} , { -4, 71} , { -2, 71} , { 2, 58} , { -1, 74} }, + { CTX_UNUSED , { -4, 44} , { -1, 69} , { 0, 62} , { -7, 51} , { -4, 47} , { -6, 42} , { -3, 41} , { -6, 53} , { 8, 76} , { -9, 78} , { -11, 83} , { 9, 52} , { 0, 67} , { -5, 90} }, + { { -3, 78} , { -8, 74} , { -9, 72} , { -10, 72} , { -18, 75} , { -12, 71} , { -11, 63} , { -5, 70} , { -17, 75} , { -14, 72} , { -16, 67} , { -8, 53} , { -14, 59} , { -9, 52} , { -11, 68} }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { { 1, 67} , { -15, 72} , { -5, 75} , { -8, 80} , { -21, 83} , { -21, 64} , { -13, 31} , { -25, 64} , { -29, 94} , { 9, 75} , { 17, 63} , { -8, 74} , { -5, 35} , { -2, 27} , { 13, 91} }, + //Cr in the 4:4:4 common mode + { { -4, 86} , { -12, 88} , { -5, 82} , { -3, 72} , { -4, 67} , { -8, 72} , { -16, 89} , { -9, 69} , { -1, 59} , { 5, 66} , { 4, 57} , { -4, 71} , { -2, 71} , { 2, 58} , { -1, 74} }, + { CTX_UNUSED , { -4, 44} , { -1, 69} , { 0, 62} , { -7, 51} , { -4, 47} , { -6, 42} , { -3, 41} , { -6, 53} , { 8, 76} , { -9, 78} , { -11, 83} , { 9, 52} , { 0, 67} , { -5, 90} }, + { { -3, 78} , { -8, 74} , { -9, 72} , { -10, 72} , { -18, 75} , { -12, 71} , { -11, 63} , { -5, 70} , { -17, 75} , { -14, 72} , { -16, 67} , { -8, 53} , { -14, 59} , { -9, 52} , { -11, 68} }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { { 1, 67} , { -15, 72} , { -5, 75} , { -8, 80} , { -21, 83} , { -21, 64} , { -13, 31} , { -25, 64} , { -29, 94} , { 9, 75} , { 17, 63} , { -8, 74} , { -5, 35} , { -2, 27} , { 13, 91} } + } +}; + +static const char INIT_LAST_I[1][22][15][2] = +{ + //----- model 0 ----- + { + { { 24, 0} , { 15, 9} , { 8, 25} , { 13, 18} , { 15, 9} , { 13, 19} , { 10, 37} , { 12, 18} , { 6, 29} , { 20, 33} , { 15, 30} , { 4, 45} , { 1, 58} , { 0, 62} , { 7, 61} }, + { CTX_UNUSED , { 12, 38} , { 11, 45} , { 15, 39} , { 11, 42} , { 13, 44} , { 16, 45} , { 12, 41} , { 10, 49} , { 30, 34} , { 18, 42} , { 10, 55} , { 17, 51} , { 17, 46} , { 0, 89} }, + { { 23, -13} , { 26, -13} , { 40, -15} , { 49, -14} , { 44, 3} , { 45, 6} , { 44, 34} , { 33, 54} , { 19, 82} , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { { 26, -19} , { 22, -17} , { 26, -17} , { 30, -25} , { 28, -20} , { 33, -23} , { 37, -27} , { 33, -23} , { 40, -28} , { 38, -17} , { 33, -11} , { 40, -15} , { 41, -6} , { 38, 1} , { 41, 17} }, + { { 30, -6} , { 27, 3} , { 26, 22} , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { CTX_UNUSED , { 37, -16} , { 35, -4} , { 38, -8} , { 38, -3} , { 37, 3} , { 38, 5} , { 42, 0} , { 35, 16} , { 39, 22} , { 14, 48} , { 27, 37} , { 21, 60} , { 12, 68} , { 2, 97} }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + //Cb in the 4:4:4 common mode + { { 24, 0} , { 15, 9} , { 8, 25} , { 13, 18} , { 15, 9} , { 13, 19} , { 10, 37} , { 12, 18} , { 6, 29} , { 20, 33} , { 15, 30} , { 4, 45} , { 1, 58} , { 0, 62} , { 7, 61} }, + { CTX_UNUSED , { 12, 38} , { 11, 45} , { 15, 39} , { 11, 42} , { 13, 44} , { 16, 45} , { 12, 41} , { 10, 49} , { 30, 34} , { 18, 42} , { 10, 55} , { 17, 51} , { 17, 46} , { 0, 89} }, + { { 23, -13} , { 26, -13} , { 40, -15} , { 49, -14} , { 44, 3} , { 45, 6} , { 44, 34} , { 33, 54} , { 19, 82} , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { { 26, -19} , { 22, -17} , { 26, -17} , { 30, -25} , { 28, -20} , { 33, -23} , { 37, -27} , { 33, -23} , { 40, -28} , { 38, -17} , { 33, -11} , { 40, -15} , { 41, -6} , { 38, 1} , { 41, 17} }, + //Cr in the 4:4:4 common mode + { { 24, 0} , { 15, 9} , { 8, 25} , { 13, 18} , { 15, 9} , { 13, 19} , { 10, 37} , { 12, 18} , { 6, 29} , { 20, 33} , { 15, 30} , { 4, 45} , { 1, 58} , { 0, 62} , { 7, 61} }, + { CTX_UNUSED , { 12, 38} , { 11, 45} , { 15, 39} , { 11, 42} , { 13, 44} , { 16, 45} , { 12, 41} , { 10, 49} , { 30, 34} , { 18, 42} , { 10, 55} , { 17, 51} , { 17, 46} , { 0, 89} }, + { { 23, -13} , { 26, -13} , { 40, -15} , { 49, -14} , { 44, 3} , { 45, 6} , { 44, 34} , { 33, 54} , { 19, 82} , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { { 26, -19} , { 22, -17} , { 26, -17} , { 30, -25} , { 28, -20} , { 33, -23} , { 37, -27} , { 33, -23} , { 40, -28} , { 38, -17} , { 33, -11} , { 40, -15} , { 41, -6} , { 38, 1} , { 41, 17} } + } +}; + +static const char INIT_LAST_P[3][22][15][2] = +{ + //----- model 0 ----- + { + { { 11, 28} , { 2, 40} , { 3, 44} , { 0, 49} , { 0, 46} , { 2, 44} , { 2, 51} , { 0, 47} , { 4, 39} , { 2, 62} , { 6, 46} , { 0, 54} , { 3, 54} , { 2, 58} , { 4, 63} }, + { CTX_UNUSED , { 6, 51} , { 6, 57} , { 7, 53} , { 6, 52} , { 6, 55} , { 11, 45} , { 14, 36} , { 8, 53} , { -1, 82} , { 7, 55} , { -3, 78} , { 15, 46} , { 22, 31} , { -1, 84} }, + { { 9, -2} , { 26, -9} , { 33, -9} , { 39, -7} , { 41, -2} , { 45, 3} , { 49, 9} , { 45, 27} , { 36, 59} , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { { 25, 7} , { 30, -7} , { 28, 3} , { 28, 4} , { 32, 0} , { 34, -1} , { 30, 6} , { 30, 6} , { 32, 9} , { 31, 19} , { 26, 27} , { 26, 30} , { 37, 20} , { 28, 34} , { 17, 70} }, + { { 1, 67} , { 5, 59} , { 9, 67} , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { CTX_UNUSED , { 16, 30} , { 18, 32} , { 18, 35} , { 22, 29} , { 24, 31} , { 23, 38} , { 18, 43} , { 20, 41} , { 11, 63} , { 9, 59} , { 9, 64} , { -1, 94} , { -2, 89} , { -9, 108} }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + //Cb in the 4:4:4 common mode + { { 11, 28} , { 2, 40} , { 3, 44} , { 0, 49} , { 0, 46} , { 2, 44} , { 2, 51} , { 0, 47} , { 4, 39} , { 2, 62} , { 6, 46} , { 0, 54} , { 3, 54} , { 2, 58} , { 4, 63} }, + { CTX_UNUSED , { 6, 51} , { 6, 57} , { 7, 53} , { 6, 52} , { 6, 55} , { 11, 45} , { 14, 36} , { 8, 53} , { -1, 82} , { 7, 55} , { -3, 78} , { 15, 46} , { 22, 31} , { -1, 84} }, + { { 9, -2} , { 26, -9} , { 33, -9} , { 39, -7} , { 41, -2} , { 45, 3} , { 49, 9} , { 45, 27} , { 36, 59} , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { { 25, 7} , { 30, -7} , { 28, 3} , { 28, 4} , { 32, 0} , { 34, -1} , { 30, 6} , { 30, 6} , { 32, 9} , { 31, 19} , { 26, 27} , { 26, 30} , { 37, 20} , { 28, 34} , { 17, 70} }, + //Cr in the 4:4:4 common mode + { { 11, 28} , { 2, 40} , { 3, 44} , { 0, 49} , { 0, 46} , { 2, 44} , { 2, 51} , { 0, 47} , { 4, 39} , { 2, 62} , { 6, 46} , { 0, 54} , { 3, 54} , { 2, 58} , { 4, 63} }, + { CTX_UNUSED , { 6, 51} , { 6, 57} , { 7, 53} , { 6, 52} , { 6, 55} , { 11, 45} , { 14, 36} , { 8, 53} , { -1, 82} , { 7, 55} , { -3, 78} , { 15, 46} , { 22, 31} , { -1, 84} }, + { { 9, -2} , { 26, -9} , { 33, -9} , { 39, -7} , { 41, -2} , { 45, 3} , { 49, 9} , { 45, 27} , { 36, 59} , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { { 25, 7} , { 30, -7} , { 28, 3} , { 28, 4} , { 32, 0} , { 34, -1} , { 30, 6} , { 30, 6} , { 32, 9} , { 31, 19} , { 26, 27} , { 26, 30} , { 37, 20} , { 28, 34} , { 17, 70} } + }, + //----- model 1 ----- + { + { { 4, 45} , { 10, 28} , { 10, 31} , { 33, -11} , { 52, -43} , { 18, 15} , { 28, 0} , { 35, -22} , { 38, -25} , { 34, 0} , { 39, -18} , { 32, -12} , { 102, -94} , { 0, 0} , { 56, -15} }, + { CTX_UNUSED , { 33, -4} , { 29, 10} , { 37, -5} , { 51, -29} , { 39, -9} , { 52, -34} , { 69, -58} , { 67, -63} , { 44, -5} , { 32, 7} , { 55, -29} , { 32, 1} , { 0, 0} , { 27, 36} }, + { { 17, -10} , { 32, -13} , { 42, -9} , { 49, -5} , { 53, 0} , { 64, 3} , { 68, 10} , { 66, 27} , { 47, 57} , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { { 33, -25} , { 34, -30} , { 36, -28} , { 38, -28} , { 38, -27} , { 34, -18} , { 35, -16} , { 34, -14} , { 32, -8} , { 37, -6} , { 35, 0} , { 30, 10} , { 28, 18} , { 26, 25} , { 29, 41} }, + { { 0, 75} , { 2, 72} , { 8, 77} , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { CTX_UNUSED , { 14, 35} , { 18, 31} , { 17, 35} , { 21, 30} , { 17, 45} , { 20, 42} , { 18, 45} , { 27, 26} , { 16, 54} , { 7, 66} , { 16, 56} , { 11, 73} , { 10, 67} , { -10, 116} }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + //Cb in the 4:4:4 common mode + { { 4, 45} , { 10, 28} , { 10, 31} , { 33, -11} , { 52, -43} , { 18, 15} , { 28, 0} , { 35, -22} , { 38, -25} , { 34, 0} , { 39, -18} , { 32, -12} , { 102, -94} , { 0, 0} , { 56, -15} }, + { CTX_UNUSED , { 33, -4} , { 29, 10} , { 37, -5} , { 51, -29} , { 39, -9} , { 52, -34} , { 69, -58} , { 67, -63} , { 44, -5} , { 32, 7} , { 55, -29} , { 32, 1} , { 0, 0} , { 27, 36} }, + { { 17, -10} , { 32, -13} , { 42, -9} , { 49, -5} , { 53, 0} , { 64, 3} , { 68, 10} , { 66, 27} , { 47, 57} , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { { 33, -25} , { 34, -30} , { 36, -28} , { 38, -28} , { 38, -27} , { 34, -18} , { 35, -16} , { 34, -14} , { 32, -8} , { 37, -6} , { 35, 0} , { 30, 10} , { 28, 18} , { 26, 25} , { 29, 41} }, + //Cr in the 4:4:4 common mode + { { 4, 45} , { 10, 28} , { 10, 31} , { 33, -11} , { 52, -43} , { 18, 15} , { 28, 0} , { 35, -22} , { 38, -25} , { 34, 0} , { 39, -18} , { 32, -12} , { 102, -94} , { 0, 0} , { 56, -15} }, + { CTX_UNUSED , { 33, -4} , { 29, 10} , { 37, -5} , { 51, -29} , { 39, -9} , { 52, -34} , { 69, -58} , { 67, -63} , { 44, -5} , { 32, 7} , { 55, -29} , { 32, 1} , { 0, 0} , { 27, 36} }, + { { 17, -10} , { 32, -13} , { 42, -9} , { 49, -5} , { 53, 0} , { 64, 3} , { 68, 10} , { 66, 27} , { 47, 57} , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { { 33, -25} , { 34, -30} , { 36, -28} , { 38, -28} , { 38, -27} , { 34, -18} , { 35, -16} , { 34, -14} , { 32, -8} , { 37, -6} , { 35, 0} , { 30, 10} , { 28, 18} , { 26, 25} , { 29, 41} } + }, + //----- model 2 ----- + { + { { 4, 39} , { 0, 42} , { 7, 34} , { 11, 29} , { 8, 31} , { 6, 37} , { 7, 42} , { 3, 40} , { 8, 33} , { 13, 43} , { 13, 36} , { 4, 47} , { 3, 55} , { 2, 58} , { 6, 60} }, + { CTX_UNUSED , { 8, 44} , { 11, 44} , { 14, 42} , { 7, 48} , { 4, 56} , { 4, 52} , { 13, 37} , { 9, 49} , { 19, 58} , { 10, 48} , { 12, 45} , { 0, 69} , { 20, 33} , { 8, 63} }, + { { 9, -2} , { 30, -10} , { 31, -4} , { 33, -1} , { 33, 7} , { 31, 12} , { 37, 23} , { 31, 38} , { 20, 64} , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { { 35, -18} , { 33, -25} , { 28, -3} , { 24, 10} , { 27, 0} , { 34, -14} , { 52, -44} , { 39, -24} , { 19, 17} , { 31, 25} , { 36, 29} , { 24, 33} , { 34, 15} , { 30, 20} , { 22, 73} }, + { { 20, 34} , { 19, 31} , { 27, 44} , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { CTX_UNUSED , { 19, 16} , { 15, 36} , { 15, 36} , { 21, 28} , { 25, 21} , { 30, 20} , { 31, 12} , { 27, 16} , { 24, 42} , { 0, 93} , { 14, 56} , { 15, 57} , { 26, 38} , { -24, 127} }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + //Cb in the 4:4:4 common mode + { { 4, 39} , { 0, 42} , { 7, 34} , { 11, 29} , { 8, 31} , { 6, 37} , { 7, 42} , { 3, 40} , { 8, 33} , { 13, 43} , { 13, 36} , { 4, 47} , { 3, 55} , { 2, 58} , { 6, 60} }, + { CTX_UNUSED , { 8, 44} , { 11, 44} , { 14, 42} , { 7, 48} , { 4, 56} , { 4, 52} , { 13, 37} , { 9, 49} , { 19, 58} , { 10, 48} , { 12, 45} , { 0, 69} , { 20, 33} , { 8, 63} }, + { { 9, -2} , { 30, -10} , { 31, -4} , { 33, -1} , { 33, 7} , { 31, 12} , { 37, 23} , { 31, 38} , { 20, 64} , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { { 35, -18} , { 33, -25} , { 28, -3} , { 24, 10} , { 27, 0} , { 34, -14} , { 52, -44} , { 39, -24} , { 19, 17} , { 31, 25} , { 36, 29} , { 24, 33} , { 34, 15} , { 30, 20} , { 22, 73} }, + //Cr in the 4:4:4 common mode + { { 4, 39} , { 0, 42} , { 7, 34} , { 11, 29} , { 8, 31} , { 6, 37} , { 7, 42} , { 3, 40} , { 8, 33} , { 13, 43} , { 13, 36} , { 4, 47} , { 3, 55} , { 2, 58} , { 6, 60} }, + { CTX_UNUSED , { 8, 44} , { 11, 44} , { 14, 42} , { 7, 48} , { 4, 56} , { 4, 52} , { 13, 37} , { 9, 49} , { 19, 58} , { 10, 48} , { 12, 45} , { 0, 69} , { 20, 33} , { 8, 63} }, + { { 9, -2} , { 30, -10} , { 31, -4} , { 33, -1} , { 33, 7} , { 31, 12} , { 37, 23} , { 31, 38} , { 20, 64} , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { { 35, -18} , { 33, -25} , { 28, -3} , { 24, 10} , { 27, 0} , { 34, -14} , { 52, -44} , { 39, -24} , { 19, 17} , { 31, 25} , { 36, 29} , { 24, 33} , { 34, 15} , { 30, 20} , { 22, 73} } + } +}; + +static const char INIT_ONE_I[1][22][5][2] = +{ + //----- model 0 ----- + { + { { -3, 71} , { -6, 42} , { -5, 50} , { -3, 54} , { -2, 62} }, + { { -5, 67} , { -5, 27} , { -3, 39} , { -2, 44} , { 0, 46} }, + { { -3, 75} , { -1, 23} , { 1, 34} , { 1, 43} , { 0, 54} }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { { -12, 92} , { -15, 55} , { -10, 60} , { -6, 62} , { -4, 65} }, + { { -11, 97} , { -20, 84} , { -11, 79} , { -6, 73} , { -4, 74} }, + { { -8, 78} , { -5, 33} , { -4, 48} , { -2, 53} , { -3, 62} }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + //Cb in the 4:4:4 common mode + { { -3, 71} , { -6, 42} , { -5, 50} , { -3, 54} , { -2, 62} }, + { { -5, 67} , { -5, 27} , { -3, 39} , { -2, 44} , { 0, 46} }, + { { -3, 75} , { -1, 23} , { 1, 34} , { 1, 43} , { 0, 54} }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { { -12, 92} , { -15, 55} , { -10, 60} , { -6, 62} , { -4, 65} }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + //Cr in the 4:4:4 common mode + { { -3, 71} , { -6, 42} , { -5, 50} , { -3, 54} , { -2, 62} }, + { { -5, 67} , { -5, 27} , { -3, 39} , { -2, 44} , { 0, 46} }, + { { -3, 75} , { -1, 23} , { 1, 34} , { 1, 43} , { 0, 54} }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { { -12, 92} , { -15, 55} , { -10, 60} , { -6, 62} , { -4, 65} }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED } + } +}; + +static const char INIT_ONE_P[3][22][5][2] = +{ + //----- model 0 ----- + { + { { -6, 76} , { -2, 44} , { 0, 45} , { 0, 52} , { -3, 64} }, + { { -9, 77} , { 3, 24} , { 0, 42} , { 0, 48} , { 0, 55} }, + { { -6, 66} , { -7, 35} , { -7, 42} , { -8, 45} , { -5, 48} }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { { 1, 58} , { -3, 29} , { -1, 36} , { 1, 38} , { 2, 43} }, + { { 0, 70} , { -4, 29} , { 5, 31} , { 7, 42} , { 1, 59} }, + { { 0, 58} , { 8, 5} , { 10, 14} , { 14, 18} , { 13, 27} }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + //Cb in the 4:4:4 common mode + { { -6, 76} , { -2, 44} , { 0, 45} , { 0, 52} , { -3, 64} }, + { { -9, 77} , { 3, 24} , { 0, 42} , { 0, 48} , { 0, 55} }, + { { -6, 66} , { -7, 35} , { -7, 42} , { -8, 45} , { -5, 48} }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { { 1, 58} , { -3, 29} , { -1, 36} , { 1, 38} , { 2, 43} }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + //Cr in the 4:4:4 common mode + { { -6, 76} , { -2, 44} , { 0, 45} , { 0, 52} , { -3, 64} }, + { { -9, 77} , { 3, 24} , { 0, 42} , { 0, 48} , { 0, 55} }, + { { -6, 66} , { -7, 35} , { -7, 42} , { -8, 45} , { -5, 48} }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { { 1, 58} , { -3, 29} , { -1, 36} , { 1, 38} , { 2, 43} }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED } + }, + //----- model 1 ----- + { + { { -23, 112} , { -15, 71} , { -7, 61} , { 0, 53} , { -5, 66} }, + { { -21, 101} , { -3, 39} , { -5, 53} , { -7, 61} , { -11, 75} }, + { { -5, 71} , { 0, 24} , { -1, 36} , { -2, 42} , { -2, 52} }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { { -11, 76} , { -10, 44} , { -10, 52} , { -10, 57} , { -9, 58} }, + { { 2, 66} , { -9, 34} , { 1, 32} , { 11, 31} , { 5, 52} }, + { { 3, 52} , { 7, 4} , { 10, 8} , { 17, 8} , { 16, 19} }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + //Cb in the 4:4:4 common mode + { { -23, 112} , { -15, 71} , { -7, 61} , { 0, 53} , { -5, 66} }, + { { -21, 101} , { -3, 39} , { -5, 53} , { -7, 61} , { -11, 75} }, + { { -5, 71} , { 0, 24} , { -1, 36} , { -2, 42} , { -2, 52} }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { { -11, 76} , { -10, 44} , { -10, 52} , { -10, 57} , { -9, 58} }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + //Cr in the 4:4:4 common mode + { { -23, 112} , { -15, 71} , { -7, 61} , { 0, 53} , { -5, 66} }, + { { -21, 101} , { -3, 39} , { -5, 53} , { -7, 61} , { -11, 75} }, + { { -5, 71} , { 0, 24} , { -1, 36} , { -2, 42} , { -2, 52} }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { { -11, 76} , { -10, 44} , { -10, 52} , { -10, 57} , { -9, 58} }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED } + }, + //----- model 2 ----- + { + { { -24, 115} , { -22, 82} , { -9, 62} , { 0, 53} , { 0, 59} }, + { { -21, 100} , { -14, 57} , { -12, 67} , { -11, 71} , { -10, 77} }, + { { -9, 71} , { -7, 37} , { -8, 44} , { -11, 49} , { -10, 56} }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { { -10, 82} , { -8, 48} , { -8, 61} , { -8, 66} , { -7, 70} }, + { { -4, 79} , { -22, 69} , { -16, 75} , { -2, 58} , { 1, 58} }, + { { -13, 81} , { -6, 38} , { -13, 62} , { -6, 58} , { -2, 59} }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + //Cb in the 4:4:4 common mode + { { -24, 115} , { -22, 82} , { -9, 62} , { 0, 53} , { 0, 59} }, + { { -21, 100} , { -14, 57} , { -12, 67} , { -11, 71} , { -10, 77} }, + { { -9, 71} , { -7, 37} , { -8, 44} , { -11, 49} , { -10, 56} }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { { -10, 82} , { -8, 48} , { -8, 61} , { -8, 66} , { -7, 70} }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + //Cr in the 4:4:4 common mode + { { -24, 115} , { -22, 82} , { -9, 62} , { 0, 53} , { 0, 59} }, + { { -21, 100} , { -14, 57} , { -12, 67} , { -11, 71} , { -10, 77} }, + { { -9, 71} , { -7, 37} , { -8, 44} , { -11, 49} , { -10, 56} }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { { -10, 82} , { -8, 48} , { -8, 61} , { -8, 66} , { -7, 70} }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED } + } +}; + +static const char INIT_ABS_I[1][22][5][2] = +{ + //----- model 0 ----- + { + { { 0, 58} , { 1, 63} , { -2, 72} , { -1, 74} , { -9, 91} }, + { { -16, 64} , { -8, 68} , { -10, 78} , { -6, 77} , { -10, 86} }, + { { -2, 55} , { 0, 61} , { 1, 64} , { 0, 68} , { -9, 92} }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { { -12, 73} , { -8, 76} , { -7, 80} , { -9, 88} , { -17, 110} }, + { { -13, 86} , { -13, 96} , { -11, 97} , { -19, 117} , CTX_UNUSED }, + { { -13, 71} , { -10, 79} , { -12, 86} , { -13, 90} , { -14, 97} }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + //Cb in the 4:4:4 common mode + { { 0, 58} , { 1, 63} , { -2, 72} , { -1, 74} , { -9, 91} }, + { { -16, 64} , { -8, 68} , { -10, 78} , { -6, 77} , { -10, 86} }, + { { -2, 55} , { 0, 61} , { 1, 64} , { 0, 68} , { -9, 92} }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { { -12, 73} , { -8, 76} , { -7, 80} , { -9, 88} , { -17, 110} }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + //Cr in the 4:4:4 common mode + { { 0, 58} , { 1, 63} , { -2, 72} , { -1, 74} , { -9, 91} }, + { { -16, 64} , { -8, 68} , { -10, 78} , { -6, 77} , { -10, 86} }, + { { -2, 55} , { 0, 61} , { 1, 64} , { 0, 68} , { -9, 92} }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { { -12, 73} , { -8, 76} , { -7, 80} , { -9, 88} , { -17, 110} }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED } + } +}; + +static const char INIT_ABS_P[3][22][5][2] = +{ + //----- model 0 ----- + { + { { -2, 59} , { -4, 70} , { -4, 75} , { -8, 82} , { -17, 102} }, + { { -6, 59} , { -7, 71} , { -12, 83} , { -11, 87} , { -30, 119} }, + { { -12, 56} , { -6, 60} , { -5, 62} , { -8, 66} , { -8, 76} }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { { -6, 55} , { 0, 58} , { 0, 64} , { -3, 74} , { -10, 90} }, + { { -2, 58} , { -3, 72} , { -3, 81} , { -11, 97} , CTX_UNUSED }, + { { 2, 40} , { 0, 58} , { -3, 70} , { -6, 79} , { -8, 85} }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + //Cb in the 4:4:4 common mode + { { -2, 59} , { -4, 70} , { -4, 75} , { -8, 82} , { -17, 102} }, + { { -6, 59} , { -7, 71} , { -12, 83} , { -11, 87} , { -30, 119} }, + { { -12, 56} , { -6, 60} , { -5, 62} , { -8, 66} , { -8, 76} }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { { -6, 55} , { 0, 58} , { 0, 64} , { -3, 74} , { -10, 90} }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + //Cr in the 4:4:4 common mode + { { -2, 59} , { -4, 70} , { -4, 75} , { -8, 82} , { -17, 102} }, + { { -6, 59} , { -7, 71} , { -12, 83} , { -11, 87} , { -30, 119} }, + { { -12, 56} , { -6, 60} , { -5, 62} , { -8, 66} , { -8, 76} }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { { -6, 55} , { 0, 58} , { 0, 64} , { -3, 74} , { -10, 90} }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + }, + //----- model 1 ----- + { + { { -11, 77} , { -9, 80} , { -9, 84} , { -10, 87} , { -34, 127} }, + { { -15, 77} , { -17, 91} , { -25, 107} , { -25, 111} , { -28, 122} }, + { { -9, 57} , { -6, 63} , { -4, 65} , { -4, 67} , { -7, 82} }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { { -16, 72} , { -7, 69} , { -4, 69} , { -5, 74} , { -9, 86} }, + { { -2, 55} , { -2, 67} , { 0, 73} , { -8, 89} , CTX_UNUSED }, + { { 3, 37} , { -1, 61} , { -5, 73} , { -1, 70} , { -4, 78} }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + //Cb in the 4:4:4 common mode + { { -11, 77} , { -9, 80} , { -9, 84} , { -10, 87} , { -34, 127} }, + { { -15, 77} , { -17, 91} , { -25, 107} , { -25, 111} , { -28, 122} }, + { { -9, 57} , { -6, 63} , { -4, 65} , { -4, 67} , { -7, 82} }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { { -16, 72} , { -7, 69} , { -4, 69} , { -5, 74} , { -9, 86} }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + //Cr in the 4:4:4 common mode + { { -11, 77} , { -9, 80} , { -9, 84} , { -10, 87} , { -34, 127} }, + { { -15, 77} , { -17, 91} , { -25, 107} , { -25, 111} , { -28, 122} }, + { { -9, 57} , { -6, 63} , { -4, 65} , { -4, 67} , { -7, 82} }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { { -16, 72} , { -7, 69} , { -4, 69} , { -5, 74} , { -9, 86} }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + }, + //----- model 2 ----- + { + { { -14, 85} , { -13, 89} , { -13, 94} , { -11, 92} , { -29, 127} }, + { { -21, 85} , { -16, 88} , { -23, 104} , { -15, 98} , { -37, 127} }, + { { -12, 59} , { -8, 63} , { -9, 67} , { -6, 68} , { -10, 79} }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { { -14, 75} , { -10, 79} , { -9, 83} , { -12, 92} , { -18, 108} }, + { { -13, 78} , { -9, 83} , { -4, 81} , { -13, 99} , CTX_UNUSED }, + { { -16, 73} , { -10, 76} , { -13, 86} , { -9, 83} , { -10, 87} }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + //Cb in the 4:4:4 common mode + { { -14, 85} , { -13, 89} , { -13, 94} , { -11, 92} , { -29, 127} }, + { { -21, 85} , { -16, 88} , { -23, 104} , { -15, 98} , { -37, 127} }, + { { -12, 59} , { -8, 63} , { -9, 67} , { -6, 68} , { -10, 79} }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { { -14, 75} , { -10, 79} , { -9, 83} , { -12, 92} , { -18, 108} }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + //Cr in the 4:4:4 common mode + { { -14, 85} , { -13, 89} , { -13, 94} , { -11, 92} , { -29, 127} }, + { { -21, 85} , { -16, 88} , { -23, 104} , { -15, 98} , { -37, 127} }, + { { -12, 59} , { -8, 63} , { -9, 67} , { -6, 68} , { -10, 79} }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { { -14, 75} , { -10, 79} , { -9, 83} , { -12, 92} , { -18, 108} }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED } + } +}; + + + +#if ENABLE_FIELD_CTX +static const char INIT_FLD_MAP_I[1][8][15][2] = +{ + //----- model 0 ----- + { + { { -6, 93} , { -6, 84} , { -8, 79} , { 0, 66} , { -1, 71} , { 0, 62} , { -2, 60} , { -2, 59} , { -5, 75} , { -3, 62} , { -4, 58} , { -9, 66} , { -1, 79} , { 0, 71} , { 3, 68} }, + { CTX_UNUSED , { 10, 44} , { -7, 62} , { 15, 36} , { 14, 40} , { 16, 27} , { 12, 29} , { 1, 44} , { 20, 36} , { 18, 32} , { 5, 42} , { 1, 48} , { 10, 62} , { 17, 46} , { 9, 64} }, + { { -14, 106} , { -13, 97} , { -15, 90} , { -12, 90} , { -18, 88} , { -10, 73} , { -9, 79} , { -14, 86} , { -10, 73} , { -10, 70} , { -10, 69} , { -5, 66} , { -9, 64} , { -5, 58} , { 2, 59} }, +// { { -1, 73} , { -7, 73} , { -6, 76} , { -7, 71} , { -9, 72} , { -5, 65} , { -14, 83} , { -8, 72} , { -10, 75} , { -5, 64} , { -4, 59} , { -13, 79} , { -9, 69} , { -8, 66} , { 3, 55} }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { { -12, 104} , { -11, 97} , { -16, 96} , { -7, 88} , { -8, 85} , { -7, 85} , { -9, 85} , { -13, 88} , { 4, 66} , { -3, 77} , { -3, 76} , { -6, 76} , { 10, 58} , { -1, 76} , { -1, 83} }, + { { -7, 99} , { -14, 95} , { 2, 95} , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { CTX_UNUSED , { 0, 76} , { -5, 74} , { 0, 70} , { -11, 75} , { 1, 68} , { 0, 65} , { -14, 73} , { 3, 62} , { 4, 62} , { -1, 68} , { -13, 75} , { 11, 55} , { 5, 64} , { 12, 70} } + } +}; + +static const char INIT_FLD_MAP_P[3][8][15][2] = +{ + //----- model 0 ----- + { + { { -13, 106} , { -16, 106} , { -10, 87} , { -21, 114} , { -18, 110} , { -14, 98} , { -22, 110} , { -21, 106} , { -18, 103} , { -21, 107} , { -23, 108} , { -26, 112} , { -10, 96} , { -12, 95} , { -5, 91} }, + { CTX_UNUSED , { -9, 93} , { -22, 94} , { -5, 86} , { 9, 67} , { -4, 80} , { -10, 85} , { -1, 70} , { 7, 60} , { 9, 58} , { 5, 61} , { 12, 50} , { 15, 50} , { 18, 49} , { 17, 54} }, + { { -5, 85} , { -6, 81} , { -10, 77} , { -7, 81} , { -17, 80} , { -18, 73} , { -4, 74} , { -10, 83} , { -9, 71} , { -9, 67} , { -1, 61} , { -8, 66} , { -14, 66} , { 0, 59} , { 2, 59} }, +// { { -4, 60} , { -3, 49} , { -2, 50} , { -4, 49} , { -5, 48} , { -2, 46} , { -7, 54} , { -1, 45} , { -4, 49} , { 4, 39} , { 0, 42} , { 2, 43} , { 0, 44} , { 5, 32} , { 15, 30} }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { { 10, 41} , { 7, 46} , { -1, 51} , { 7, 49} , { 8, 52} , { 9, 41} , { 6, 47} , { 2, 55} , { 13, 41} , { 10, 44} , { 6, 50} , { 5, 53} , { 13, 49} , { 4, 63} , { 6, 64} }, + { { -2, 69} , { -2, 59} , { 6, 70} , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { CTX_UNUSED , { 10, 44} , { 9, 31} , { 12, 43} , { 3, 53} , { 14, 34} , { 10, 38} , { -3, 52} , { 13, 40} , { 17, 32} , { 7, 44} , { 7, 38} , { 13, 50} , { 10, 57} , { 26, 43} } + }, + //----- model 1 ----- + { + { { -21, 126} , { -23, 124} , { -20, 110} , { -26, 126} , { -25, 124} , { -17, 105} , { -27, 121} , { -27, 117} , { -17, 102} , { -26, 117} , { -27, 116} , { -33, 122} , { -10, 95} , { -14, 100} , { -8, 95} }, + { CTX_UNUSED , { -17, 111} , { -28, 114} , { -6, 89} , { -2, 80} , { -4, 82} , { -9, 85} , { -8, 81} , { -1, 72} , { 5, 64} , { 1, 67} , { 9, 56} , { 0, 69} , { 1, 69} , { 7, 69} }, + { { -3, 81} , { -3, 76} , { -7, 72} , { -6, 78} , { -12, 72} , { -14, 68} , { -3, 70} , { -6, 76} , { -5, 66} , { -5, 62} , { 0, 57} , { -4, 61} , { -9, 60} , { 1, 54} , { 2, 58} }, +// { { -4, 60} , { -3, 49} , { -2, 50} , { -4, 49} , { -5, 48} , { -2, 46} , { -7, 54} , { -1, 45} , { -4, 49} , { 4, 39} , { 0, 42} , { 2, 43} , { 0, 44} , { 5, 32} , { 15, 30} }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { { -7, 69} , { -6, 67} , { -16, 77} , { -2, 64} , { 2, 61} , { -6, 67} , { -3, 64} , { 2, 57} , { -3, 65} , { -3, 66} , { 0, 62} , { 9, 51} , { -1, 66} , { -2, 71} , { -2, 75} }, + { { -1, 70} , { -9, 72} , { 14, 60} , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { CTX_UNUSED , { 16, 37} , { 0, 47} , { 18, 35} , { 11, 37} , { 12, 41} , { 10, 41} , { 2, 48} , { 12, 41} , { 13, 41} , { 0, 59} , { 3, 50} , { 19, 40} , { 3, 66} , { 18, 50} } + }, + //----- model 2 ----- + { + { { -22, 127} , { -25, 127} , { -25, 120} , { -27, 127} , { -19, 114} , { -23, 117} , { -25, 118} , { -26, 117} , { -24, 113} , { -28, 118} , { -31, 120} , { -37, 124} , { -10, 94} , { -15, 102} , { -10, 99} }, + { CTX_UNUSED , { -13, 106} , { -50, 127} , { -5, 92} , { 17, 57} , { -5, 86} , { -13, 94} , { -12, 91} , { -2, 77} , { 0, 71} , { -1, 73} , { 4, 64} , { -7, 81} , { 5, 64} , { 15, 57} }, + { { -3, 78} , { -8, 74} , { -9, 72} , { -10, 72} , { -18, 75} , { -12, 71} , { -11, 63} , { -5, 70} , { -17, 75} , { -14, 72} , { -16, 67} , { -8, 53} , { -14, 59} , { -9, 52} , { -11, 68} }, +// { { -4, 60} , { -3, 49} , { -2, 50} , { -4, 49} , { -5, 48} , { -2, 46} , { -7, 54} , { -1, 45} , { -4, 49} , { 4, 39} , { 0, 42} , { 2, 43} , { 0, 44} , { 5, 32} , { 15, 30} }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { { 1, 67} , { 0, 68} , { -10, 67} , { 1, 68} , { 0, 77} , { 2, 64} , { 0, 68} , { -5, 78} , { 7, 55} , { 5, 59} , { 2, 65} , { 14, 54} , { 15, 44} , { 5, 60} , { 2, 70} }, + { { -2, 76} , { -18, 86} , { 12, 70} , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { CTX_UNUSED , { 5, 64} , { -12, 70} , { 11, 55} , { 5, 56} , { 0, 69} , { 2, 65} , { -6, 74} , { 5, 54} , { 7, 54} , { -6, 76} , { -11, 82} , { -2, 77} , { -2, 77} , { 25, 42} } + } +}; + +static const char INIT_FLD_LAST_I[1][8][15][2] = +{ + //----- model 0 ----- + { + { { 15, 6} , { 6, 19} , { 7, 16} , { 12, 14} , { 18, 13} , { 13, 11} , { 13, 15} , { 15, 16} , { 12, 23} , { 13, 23} , { 15, 20} , { 14, 26} , { 14, 44} , { 17, 40} , { 17, 47} }, + { CTX_UNUSED , { 24, 17} , { 21, 21} , { 25, 22} , { 31, 27} , { 22, 29} , { 19, 35} , { 14, 50} , { 10, 57} , { 7, 63} , { -2, 77} , { -4, 82} , { -3, 94} , { 9, 69} , { -12, 109} }, + { { 21, -10} , { 24, -11} , { 28, -8} , { 28, -1} , { 29, 3} , { 29, 9} , { 35, 20} , { 29, 36} , { 14, 67} , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, +// { { 12, 33} , { 5, 38} , { 9, 34} , { 18, 22} , { 19, 22} , { 23, 19} , { 26, 16} , { 14, 44} , { 40, 14} , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { { 36, -35} , { 36, -34} , { 32, -26} , { 37, -30} , { 44, -32} , { 34, -18} , { 34, -15} , { 40, -15} , { 33, -7} , { 35, -5} , { 33, 0} , { 38, 2} , { 33, 13} , { 23, 35} , { 13, 58} }, + { { 29, -3} , { 26, 0} , { 22, 30} , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { CTX_UNUSED , { 31, -7} , { 35, -15} , { 34, -3} , { 34, 3} , { 36, -1} , { 34, 5} , { 32, 11} , { 35, 5} , { 34, 12} , { 39, 11} , { 30, 29} , { 34, 26} , { 29, 39} , { 19, 66} } + } +}; + +static const char INIT_FLD_LAST_P[3][8][15][2] = +{ + //----- model 0 ----- + { + { { 14, 11} , { 11, 14} , { 9, 11} , { 18, 11} , { 21, 9} , { 23, -2} , { 32, -15} , { 32, -15} , { 34, -21} , { 39, -23} , { 42, -33} , { 41, -31} , { 46, -28} , { 38, -12} , { 21, 29} }, + { CTX_UNUSED , { 45, -24} , { 53, -45} , { 48, -26} , { 65, -43} , { 43, -19} , { 39, -10} , { 30, 9} , { 18, 26} , { 20, 27} , { 0, 57} , { -14, 82} , { -5, 75} , { -19, 97} , { -35, 125} }, + { { 21, -13} , { 33, -14} , { 39, -7} , { 46, -2} , { 51, 2} , { 60, 6} , { 61, 17} , { 55, 34} , { 42, 62} , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, +// { { 17, 27} , { 23, 13} , { 24, 16} , { 22, 25} , { 23, 27} , { 23, 32} , { 17, 43} , { 17, 49} , { 2, 70} , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { { 27, 0} , { 28, 0} , { 31, -4} , { 27, 6} , { 34, 8} , { 30, 10} , { 24, 22} , { 33, 19} , { 22, 32} , { 26, 31} , { 21, 41} , { 26, 44} , { 23, 47} , { 16, 65} , { 14, 71} }, + { { 8, 60} , { 6, 63} , { 17, 65} , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { CTX_UNUSED , { 21, 24} , { 23, 20} , { 26, 23} , { 27, 32} , { 28, 23} , { 28, 24} , { 23, 40} , { 24, 32} , { 28, 29} , { 23, 42} , { 19, 57} , { 22, 53} , { 22, 61} , { 11, 86} } + }, + //----- model 1 ----- + { + { { 19, -6} , { 18, -6} , { 14, 0} , { 26, -12} , { 31, -16} , { 33, -25} , { 33, -22} , { 37, -28} , { 39, -30} , { 42, -30} , { 47, -42} , { 45, -36} , { 49, -34} , { 41, -17} , { 32, 9} }, + { CTX_UNUSED , { 69, -71} , { 63, -63} , { 66, -64} , { 77, -74} , { 54, -39} , { 52, -35} , { 41, -10} , { 36, 0} , { 40, -1} , { 30, 14} , { 28, 26} , { 23, 37} , { 12, 55} , { 11, 65} }, + { { 17, -10} , { 32, -13} , { 42, -9} , { 49, -5} , { 53, 0} , { 64, 3} , { 68, 10} , { 66, 27} , { 47, 57} , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, +// { { 17, 27} , { 23, 13} , { 24, 16} , { 22, 25} , { 23, 27} , { 23, 32} , { 17, 43} , { 17, 49} , { 2, 70} , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { { 37, -33} , { 39, -36} , { 40, -37} , { 38, -30} , { 46, -33} , { 42, -30} , { 40, -24} , { 49, -29} , { 38, -12} , { 40, -10} , { 38, -3} , { 46, -5} , { 31, 20} , { 29, 30} , { 25, 44} }, + { { 12, 48} , { 11, 49} , { 26, 45} , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { CTX_UNUSED , { 22, 22} , { 23, 22} , { 27, 21} , { 33, 20} , { 26, 28} , { 30, 24} , { 27, 34} , { 18, 42} , { 25, 39} , { 18, 50} , { 12, 70} , { 21, 54} , { 14, 71} , { 11, 83} } + }, + //----- model 2 ----- + { + { { 17, -13} , { 16, -9} , { 17, -12} , { 27, -21} , { 37, -30} , { 41, -40} , { 42, -41} , { 48, -47} , { 39, -32} , { 46, -40} , { 52, -51} , { 46, -41} , { 52, -39} , { 43, -19} , { 32, 11} }, + { CTX_UNUSED , { 61, -55} , { 56, -46} , { 62, -50} , { 81, -67} , { 45, -20} , { 35, -2} , { 28, 15} , { 34, 1} , { 39, 1} , { 30, 17} , { 20, 38} , { 18, 45} , { 15, 54} , { 0, 79} }, + { { 9, -2} , { 30, -10} , { 31, -4} , { 33, -1} , { 33, 7} , { 31, 12} , { 37, 23} , { 31, 38} , { 20, 64} , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, +// { { 17, 27} , { 23, 13} , { 24, 16} , { 22, 25} , { 23, 27} , { 23, 32} , { 17, 43} , { 17, 49} , { 2, 70} , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { { 36, -16} , { 37, -14} , { 37, -17} , { 32, 1} , { 34, 15} , { 29, 15} , { 24, 25} , { 34, 22} , { 31, 16} , { 35, 18} , { 31, 28} , { 33, 41} , { 36, 28} , { 27, 47} , { 21, 62} }, + { { 18, 31} , { 19, 26} , { 36, 24} , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED , CTX_UNUSED }, + { CTX_UNUSED , { 24, 23} , { 27, 16} , { 24, 30} , { 31, 29} , { 22, 41} , { 22, 42} , { 16, 60} , { 15, 52} , { 14, 60} , { 3, 78} , { -16, 123} , { 21, 53} , { 22, 56} , { 25, 61} } + } +}; +#endif + + +#endif + diff --git a/lcommon/inc/distortion.h b/lcommon/inc/distortion.h new file mode 100644 index 0000000..dccf196 --- /dev/null +++ b/lcommon/inc/distortion.h @@ -0,0 +1,31 @@ +/*! + ************************************************************************** + * \file distortion.h + * \brief + * Distortion data header file + * \date 2.23.2009, + * + * \author + * Alexis Michael Tourapis + * + ************************************************************************** + */ + +#ifndef _DISTORTION_H_ +#define _DISTORTION_H_ + +// Distortion data structure. Could be extended in the future to support +// other data +typedef struct distortion_data +{ + int i4x4rd[4][4]; //! i4x4 rd cost + distblk i4x4 [4][4]; //! i4x4 cost + distblk i8x8 [2][2]; //! i8x8 cost + int i8x8rd[2][2]; //! i8x8 rd cost + int i16x16; + int i16x16rd; + double rd_cost; +} DistortionData; + +#endif + diff --git a/lcommon/inc/enc_statistics.h b/lcommon/inc/enc_statistics.h new file mode 100644 index 0000000..40befe6 --- /dev/null +++ b/lcommon/inc/enc_statistics.h @@ -0,0 +1,70 @@ +/*! + ************************************************************************** + * \file enc_statistics.h + * + * \brief + * statistics reports for the encoding process. + * + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * - Alexis Tourapis + * - Karsten Suehring + * + ************************************************************************** + */ + +#ifndef _ENC_STATISTICS_H_ +#define _ENC_STATISTICS_H_ +#include "global.h" + +struct stat_parameters +{ + float bitrate; //!< average bit rate for the sequence except first frame + int64 bit_ctr; //!< counter for bit usage + int64 bit_ctr_n; //!< bit usage for the current frame + int64 bit_ctr_emulation_prevention; //!< stored bits needed to prevent start code emulation + int bit_slice; //!< number of bits in current slice + int stored_bit_slice; //!< keep number of bits in current slice (to restore status in case of MB re-encoding) + int b8_mode_0_use [NUM_SLICE_TYPES][2]; + int64 mode_use_transform[NUM_SLICE_TYPES][MAXMODE][2]; + int64 intra_chroma_mode[4]; + + // B pictures + int NumberBFrames; + + int frame_counter; + int64 quant [NUM_SLICE_TYPES]; + int64 num_macroblocks [NUM_SLICE_TYPES]; + int frame_ctr [NUM_SLICE_TYPES]; + int64 bit_counter [NUM_SLICE_TYPES]; + float bitrate_st [NUM_SLICE_TYPES]; + int64 mode_use [NUM_SLICE_TYPES][MAXMODE]; //!< Macroblock mode usage for Intra frames + int64 bit_use_mode [NUM_SLICE_TYPES][MAXMODE]; //!< statistics of bit usage + int64 bit_use_mb_type [NUM_SLICE_TYPES]; + int64 bit_use_header [NUM_SLICE_TYPES]; + int64 tmp_bit_use_cbp [NUM_SLICE_TYPES]; + int64 bit_use_coeffC [NUM_SLICE_TYPES]; + int64 bit_use_coeff [3][NUM_SLICE_TYPES]; + int64 bit_use_delta_quant [NUM_SLICE_TYPES]; + int64 bit_use_stuffing_bits[NUM_SLICE_TYPES]; + + int bit_ctr_parametersets; + int bit_ctr_parametersets_n; + int64 bit_ctr_filler_data; + int64 bit_ctr_filler_data_n; + +#if (MVC_EXTENSION_ENABLE) + float bitrate_v[2]; //!< average bit rate for the sequence except first frame + int64 bit_ctr_v[2]; //!< counter for bit usage + int64 bit_ctr_n_v[2]; //!< bit usage for the current frame + int64 bit_ctr_emulationprevention_v[2]; //!< stored bits needed to prevent start code emulation + int64 bit_counter_v[2][NUM_SLICE_TYPES]; + int bit_ctr_parametersets_v[2]; + int bit_ctr_parametersets_n_v[2]; + int64 bit_ctr_filler_data_v[2]; + int64 bit_ctr_filler_data_n_v[2]; +#endif +}; +typedef struct stat_parameters StatParameters; + +#endif diff --git a/lcommon/inc/fast_memory.h b/lcommon/inc/fast_memory.h new file mode 100644 index 0000000..f2b921e --- /dev/null +++ b/lcommon/inc/fast_memory.h @@ -0,0 +1,37 @@ +/*! + ************************************************************************** + * \file fast_memory.h + * + * \brief + * Memory handling operations + * + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * - Chris Vogt + * + ************************************************************************** + */ + +#ifndef _FAST_MEMORY_H_ +#define _FAST_MEMORY_H_ + +#include "typedefs.h" + + +static inline void fast_memset(void *dst,int value,int width) +{ + memset(dst,value,width); +} + +static inline void fast_memcpy(void *dst,void *src,int width) +{ + memcpy(dst,src,width); +} + +static inline void fast_memset_zero(void *dst, int width) +{ + memset(dst,0,width); +} + +#endif + diff --git a/lcommon/inc/frame.h b/lcommon/inc/frame.h new file mode 100644 index 0000000..9a48bf6 --- /dev/null +++ b/lcommon/inc/frame.h @@ -0,0 +1,66 @@ +/*! + ************************************************************************ + * \file frame.h + * + * \brief + * headers for frame format related information + * + * \author + * + ************************************************************************ + */ +#ifndef _FRAME_H_ +#define _FRAME_H_ + +typedef enum { + CM_UNKNOWN = -1, + CM_YUV = 0, + CM_RGB = 1, + CM_XYZ = 2 +} ColorModel; + +typedef enum { + CF_UNKNOWN = -1, //!< Unknown color format + YUV400 = 0, //!< Monochrome + YUV420 = 1, //!< 4:2:0 + YUV422 = 2, //!< 4:2:2 + YUV444 = 3 //!< 4:4:4 +} ColorFormat; + +typedef enum { + PF_UNKNOWN = -1, //!< Unknown color ordering + UYVY = 0, //!< UYVY + YUY2 = 1, //!< YUY2 + YUYV = 1, //!< YUYV + YVYU = 2, //!< YVYU + BGR = 3, //!< BGR + V210 = 4 //!< Video Clarity 422 format (10 bits) +} PixelFormat; + + +typedef struct frame_format +{ + ColorFormat yuv_format; //!< YUV format (0=4:0:0, 1=4:2:0, 2=4:2:2, 3=4:4:4) + ColorModel color_model; //!< 4:4:4 format (0: YUV, 1: RGB, 2: XYZ) + PixelFormat pixel_format; //!< pixel format support for certain interleaved yuv sources + double frame_rate; //!< frame rate + int width[3]; //!< component frame width + int height[3]; //!< component frame height + int auto_crop_right; //!< luma component auto crop right + int auto_crop_bottom; //!< luma component auto crop bottom + int auto_crop_right_cr; //!< chroma component auto crop right + int auto_crop_bottom_cr; //!< chroma component auto crop bottom + int width_crop; //!< width after cropping consideration + int height_crop; //!< height after cropping consideration + int mb_width; //!< luma component frame width + int mb_height; //!< luma component frame height + int size_cmp[3]; //!< component sizes (width * height) + int size; //!< total image size (sum of size_cmp) + int bit_depth[3]; //!< component bit depth + int max_value[3]; //!< component max value + int max_value_sq[3]; //!< component max value squared + int pic_unit_size_on_disk; //!< picture sample unit size on storage medium + int pic_unit_size_shift3; //!< pic_unit_size_on_disk >> 3 +} FrameFormat; + +#endif diff --git a/lcommon/inc/ifunctions.h b/lcommon/inc/ifunctions.h new file mode 100644 index 0000000..74e7c36 --- /dev/null +++ b/lcommon/inc/ifunctions.h @@ -0,0 +1,448 @@ + +/*! + ************************************************************************ + * \file + * ifunctions.h + * + * \brief + * define some inline functions that are used within the encoder. + * + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * - Karsten Suehring + * - Alexis Tourapis + * + ************************************************************************ + */ + +#ifndef _IFUNCTIONS_H_ +#define _IFUNCTIONS_H_ + +# if !(defined(WIN32) || defined(WIN64)) && (__STDC_VERSION__ < 199901L) +#define static +#define inline +#endif +#include +#include + + +static inline short smin(short a, short b) +{ + return (short) (((a) < (b)) ? (a) : (b)); +} + +static inline short smax(short a, short b) +{ + return (short) (((a) > (b)) ? (a) : (b)); +} + +static inline int imin(int a, int b) +{ + return ((a) < (b)) ? (a) : (b); +} + +static inline int imin3(int a, int b, int c) +{ + return ((a) < (b)) ? imin(a, c) : imin(b, c); +} + +static inline int imax(int a, int b) +{ + return ((a) > (b)) ? (a) : (b); +} + +static inline int imedian(int a,int b,int c) +{ + if (a > b) // a > b + { + if (b > c) + return(b); // a > b > c + else if (a > c) + return(c); // a > c > b + else + return(a); // c > a > b + } + else // b > a + { + if (a > c) + return(a); // b > a > c + else if (b > c) + return(c); // b > c > a + else + return(b); // c > b > a + } +} + +static inline int imedian_old(int a, int b, int c) +{ + return (a + b + c - imin(a, imin(b, c)) - imax(a, imax(b ,c))); +} + +static inline double dmin(double a, double b) +{ + return ((a) < (b)) ? (a) : (b); +} + +static inline double dmax(double a, double b) +{ + return ((a) > (b)) ? (a) : (b); +} + +static inline int64 i64min(int64 a, int64 b) +{ + return ((a) < (b)) ? (a) : (b); +} + +static inline int64 i64max(int64 a, int64 b) +{ + return ((a) > (b)) ? (a) : (b); +} + +static inline distblk distblkmin(distblk a, distblk b) +{ + return ((a) < (b)) ? (a) : (b); +} + +static inline distblk distblkmax(distblk a, distblk b) +{ + return ((a) > (b)) ? (a) : (b); +} + +static inline short sabs(short x) +{ + static const short SHORT_BITS = (sizeof(short) * CHAR_BIT) - 1; + short y = (short) (x >> SHORT_BITS); + return (short) ((x ^ y) - y); +} + +static inline int iabs(int x) +{ + static const int INT_BITS = (sizeof(int) * CHAR_BIT) - 1; + int y = x >> INT_BITS; + return (x ^ y) - y; +} + +static inline double dabs(double x) +{ + return ((x) < 0) ? -(x) : (x); +} + +static inline int64 i64abs(int64 x) +{ + static const int64 INT64_BITS = (sizeof(int64) * CHAR_BIT) - 1; + int64 y = x >> INT64_BITS; + return (x ^ y) - y; +} + +static inline double dabs2(double x) +{ + return (x) * (x); +} + +static inline int iabs2(int x) +{ + return (x) * (x); +} + +static inline int64 i64abs2(int64 x) +{ + return (x) * (x); +} + +static inline int isign(int x) +{ + return ( (x > 0) - (x < 0)); +} + +static inline int isignab(int a, int b) +{ + return ((b) < 0) ? -iabs(a) : iabs(a); +} + +static inline int rshift_rnd(int x, int a) +{ + return (a > 0) ? ((x + (1 << (a-1) )) >> a) : (x << (-a)); +} + +static inline int rshift_rnd_sign(int x, int a) +{ + return (x > 0) ? ( ( x + (1 << (a-1)) ) >> a ) : (-( ( iabs(x) + (1 << (a-1)) ) >> a )); +} + +static inline unsigned int rshift_rnd_us(unsigned int x, unsigned int a) +{ + return (a > 0) ? ((x + (1 << (a-1))) >> a) : x; +} + +static inline int rshift_rnd_sf(int x, int a) +{ + return ((x + (1 << (a-1) )) >> a); +} + +static inline int shift_off_sf(int x, int o, int a) +{ + return ((x + o) >> a); +} + +static inline unsigned int rshift_rnd_us_sf(unsigned int x, unsigned int a) +{ + return ((x + (1 << (a-1))) >> a); +} + +static inline int iClip1(int high, int x) +{ + x = imax(x, 0); + x = imin(x, high); + + return x; +} + +static inline int iClip3(int low, int high, int x) +{ + x = imax(x, low); + x = imin(x, high); + + return x; +} + +static inline short sClip3(short low, short high, short x) +{ + x = smax(x, low); + x = smin(x, high); + + return x; +} + +static inline double dClip3(double low, double high, double x) +{ + x = dmax(x, low); + x = dmin(x, high); + + return x; +} + + +//! Edited by Joao Carreira +static inline double abs_ivec( int x, int y) +{ + return sqrt( iabs2(x) + iabs2(y) ); +} +static inline double abs_dvec( double x, double y) +{ + return sqrt( dabs2(x) + dabs2(y) ); +} +static inline int i_min_max( int n, int min, int max) +{ + return imin( imax( min, n ) , max); +} + +static inline unsigned int ui_min_position( unsigned int *vector, unsigned int n_elem, int *min_value, int *min_pos) +{ + unsigned int i, aux_min_value = vector[0], aux_min_pos = 0; + if(min_pos) *min_pos = 0; + for(i=1;i aux_value )//MAX + { + aux_value = vector[i]; + aux_pos = i; + if( aux_value == 0 ) + break; + } + } + if(pos) *pos = aux_pos; + if(value) *value = aux_value; + return aux_pos; +} + + + + + + +static inline distblk weighted_cost(int factor, int bits) +{ +#if JCOST_CALC_SCALEUP + return (((distblk)(factor))*((distblk)(bits))); +#else +#if (USE_RND_COST) + return (rshift_rnd_sf((lambda) * (bits), LAMBDA_ACCURACY_BITS)); +#else + return (((factor)*(bits))>>LAMBDA_ACCURACY_BITS); +#endif +#endif +} + +static inline int RSD(int x) +{ + return ((x&2)?(x|1):(x&(~1))); +} + +static inline int power2(int x) +{ + return 1 << (x); +} + + +static const int64 po2[64] = {0x1,0x2,0x4,0x8,0x10,0x20,0x40,0x80,0x100,0x200,0x400,0x800,0x1000,0x2000,0x4000,0x8000, + 0x10000,0x20000,0x40000,0x80000,0x100000,0x200000,0x400000,0x800000,0x1000000,0x2000000,0x4000000,0x8000000, + 0x10000000,0x20000000,0x40000000,0x80000000,0x100000000,0x200000000,0x400000000,0x800000000, + 0x1000000000,0x2000000000,0x4000000000,0x8000000000,0x10000000000,0x20000000000,0x40000000000,0x80000000000, + 0x100000000000,0x200000000000,0x400000000000,0x800000000000, + 0x1000000000000,0x2000000000000,0x4000000000000,0x8000000000000, + 0x10000000000000,0x20000000000000,0x40000000000000,0x80000000000000, + 0x100000000000000,0x200000000000000,0x400000000000000,0x800000000000000, + 0x1000000000000000,0x2000000000000000,0x4000000000000000,0x8000000000000000}; + +static inline int64 i64_power2(int x) +{ + return((x > 63) ? 0 : po2[x]); +} + +static inline int float2int (float x) +{ + return (int)((x < 0) ? (x - 0.5f) : (x + 0.5f)); +} + +static inline int get_bit(int64 x,int n) +{ + return (int)(((x >> n) & 1)); +} + +#if ZEROSNR +static inline float psnr(int max_sample_sq, int samples, float sse_distortion ) +{ + return (float) (10.0 * log10(max_sample_sq * (double) ((double) samples / (sse_distortion < 1.0 ? 1.0 : sse_distortion)))); +} +#else +static inline float psnr(int max_sample_sq, int samples, float sse_distortion ) +{ + return (float) (sse_distortion == 0.0 ? 0.0 : (10.0 * log10(max_sample_sq * (double) ((double) samples / sse_distortion)))); +} +#endif + +static inline int CheckCost_Shift(int64 mcost, int64 min_mcost) +{ + if((mcost<= min_mcost) + return 1; + else + return 0; +} + +static inline int CheckCost(int64 mcost, int64 min_mcost) +{ + return ((mcost) >= (min_mcost)); +} + +static inline void down_scale(distblk *pblkdistCost) +{ +#if JCOST_CALC_SCALEUP + *pblkdistCost = (*pblkdistCost)>>LAMBDA_ACCURACY_BITS; +#endif +} + +static inline void up_scale(distblk *pblkdistCost) +{ +#if JCOST_CALC_SCALEUP + *pblkdistCost = (*pblkdistCost)<>LAMBDA_ACCURACY_BITS)); +#else + return ((int)blkdistCost); +#endif +} + +/*! + ************************************************************************ + * \brief + * calculate RoundLog2(uiVal) + ************************************************************************ + */ +static inline int RoundLog2 (int iValue) +{ + int iRet = 0; + int iValue_square = iValue * iValue; + while ((1 << (iRet + 1)) <= iValue_square) + ++iRet; + + iRet = (iRet + 1) >> 1; + return iRet; +} + +static inline void free_pointer(void *pointer) +{ + if (pointer != NULL) + { + free(pointer); + pointer = NULL; + } +} + +static inline void i32_swap(int x, int y) +{ + int temp = x; + x = y; + y = temp; +} + +# if !(defined(WIN32) || defined(WIN64)) && (__STDC_VERSION__ < 199901L) +#undef static +#undef inline +#endif + +#endif + diff --git a/lcommon/inc/img_io.h b/lcommon/inc/img_io.h new file mode 100644 index 0000000..2c910f0 --- /dev/null +++ b/lcommon/inc/img_io.h @@ -0,0 +1,30 @@ +/*! + ************************************************************************************* + * \file img_io.h + * + * \brief + * image I/O related functions + * + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * - Alexis Michael Tourapis + ************************************************************************************* + */ +#include "global.h" + +#ifndef _IMG_IO_H_ +#define _IMG_IO_H_ + +#include "io_video.h" +#include "io_raw.h" +#include "io_tiff.h" + +extern int ParseSizeFromString (VideoDataFile *input_file, int *xlen, int *ylen, double *fps); +extern void ParseFrameNoFormatFromString (VideoDataFile *input_file); +extern void OpenFrameFile (VideoDataFile *input_file, int FrameNumberInFile); +extern void OpenFiles (VideoDataFile *input_file); +extern void CloseFiles (VideoDataFile *input_file); +extern VideoFileType ParseVideoType (VideoDataFile *input_file); + +#endif + diff --git a/lcommon/inc/img_process.h b/lcommon/inc/img_process.h new file mode 100644 index 0000000..e351e03 --- /dev/null +++ b/lcommon/inc/img_process.h @@ -0,0 +1,25 @@ +/*! + ************************************************************************ + * \file img_process.h + * + * \brief + * Input data Image Processing functions + * + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * - Alexis Michael Tourapis + * + ************************************************************************ + */ + +#ifndef _IMG_PROCESS_H_ +#define _IMG_PROCESS_H_ + + +extern int init_process_image ( VideoParameters *p_Vid, InputParameters *p_Inp ); +extern void clear_process_image( VideoParameters *p_Vid, InputParameters *p_Inp); +extern void process_image ( VideoParameters *p_Vid, InputParameters *p_Inp ); + + + +#endif diff --git a/lcommon/inc/img_process_types.h b/lcommon/inc/img_process_types.h new file mode 100644 index 0000000..431aa7c --- /dev/null +++ b/lcommon/inc/img_process_types.h @@ -0,0 +1,24 @@ +/*! + ************************************************************************ + * \file img_process_types.h + * + * \brief + * Input data Image Processing Types + * + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * - Alexis Michael Tourapis + * - Athanasios Leontaris + * - Yuwen He + ************************************************************************ + */ + +#ifndef _IMG_PROCESS_TYPES_H_ +#define _IMG_PROCESS_TYPES_H_ + +#define DEMUX_META_DEBUG 1 +#define MAX_NUM_PARTITIONS 512 +#define NUM_VIEWS 2 +#define NUM_COMPONENTS 3 + +#endif diff --git a/lcommon/inc/input.h b/lcommon/inc/input.h new file mode 100644 index 0000000..3ad6e6e --- /dev/null +++ b/lcommon/inc/input.h @@ -0,0 +1,25 @@ +/*! + ************************************************************************ + * \file input.h + * + * \brief + * Input related definitions + * + * \author + * + ************************************************************************ + */ + +#ifndef _INPUT_H_ +#define _INPUT_H_ + +extern int testEndian(void); +extern void initInput(VideoParameters *p_Vid, FrameFormat *source, FrameFormat *output); +extern void AllocateFrameMemory (VideoParameters *p_Vid, InputParameters *p_Inp, FrameFormat *source); +extern void DeleteFrameMemory (VideoParameters *p_Vid); + +extern int read_one_frame (VideoParameters *p_Vid, VideoDataFile *input_file, int FrameNoInFile, int HeaderSize, FrameFormat *source, FrameFormat *output, imgpel **pImage[3]); +extern void pad_borders ( FrameFormat output, int img_size_x, int img_size_y, int img_size_x_cr, int img_size_y_cr, imgpel **pImage[3]); + +#endif + diff --git a/lcommon/inc/io_image.h b/lcommon/inc/io_image.h new file mode 100644 index 0000000..910fd68 --- /dev/null +++ b/lcommon/inc/io_image.h @@ -0,0 +1,45 @@ +/*! + ************************************************************************ + * \file io_image.h + * + * \brief + * Image I/O + * + * \author + * - Alexis Michael Tourapis + * + ************************************************************************ + */ + +#ifndef _IO_IMAGE_H_ +#define _IO_IMAGE_H_ + +#include "defines.h" +#include "frame.h" + +typedef struct image_data +{ + FrameFormat format; //!< image format + // Standard data + imgpel **frm_data[MAX_PLANE]; //!< Frame Data + imgpel **top_data[MAX_PLANE]; //!< pointers to top field data + imgpel **bot_data[MAX_PLANE]; //!< pointers to bottom field data + + imgpel **frm_data_buf[2][MAX_PLANE]; //!< Frame Data + imgpel **top_data_buf[2][MAX_PLANE]; //!< pointers to top field data + imgpel **bot_data_buf[2][MAX_PLANE]; //!< pointers to bottom field data + + //! Optional data (could also add uint8 data in case imgpel is of type uint16) + //! These can be useful for enabling input/conversion of content of different types + //! while keeping optimal processing size. + uint16 **frm_uint16[MAX_PLANE]; //!< optional frame Data for uint16 + uint16 **top_uint16[MAX_PLANE]; //!< optional pointers to top field data + uint16 **bot_uint16[MAX_PLANE]; //!< optional pointers to bottom field data + + int frm_stride[MAX_PLANE]; + int top_stride[MAX_PLANE]; + int bot_stride[MAX_PLANE]; +} ImageData; + +#endif + diff --git a/lcommon/inc/io_raw.h b/lcommon/inc/io_raw.h new file mode 100644 index 0000000..91ffa0e --- /dev/null +++ b/lcommon/inc/io_raw.h @@ -0,0 +1,22 @@ + +/*! + ************************************************************************ + * \file io_raw.h + * + * \brief + * I/O functions related to raw images + * + * \author + * - Alexis Michael Tourapis + * + ************************************************************************ + */ + +#ifndef _IO_RAW_H_ +#define _IO_RAW_H_ + +extern int ReadFrameConcatenated (InputParameters *p_Inp, VideoDataFile *input_file, int FrameNoInFile, int HeaderSize, FrameFormat *source, unsigned char *buf); +extern int ReadFrameSeparate (InputParameters *p_Inp, VideoDataFile *input_file, int FrameNoInFile, int HeaderSize, FrameFormat *source, unsigned char *buf); + +#endif + diff --git a/lcommon/inc/io_tiff.h b/lcommon/inc/io_tiff.h new file mode 100644 index 0000000..c6cd9b0 --- /dev/null +++ b/lcommon/inc/io_tiff.h @@ -0,0 +1,24 @@ +/*! +************************************************************************ +* \file io_tiff.h +* +* \brief +* I/O functions related to tiff images +* Part of code was based on libtiff (see http://www.libtiff.org/) +* +* \author +* - Larry Luther +* - Alexis Michael Tourapis +* +************************************************************************ +*/ + +#ifndef _IO_TIFF_H_ +#define _IO_TIFF_H_ +// See TIFF 6.0 Specification +// http://partners.adobe.com/public/developer/tiff/index.html + + +extern int ReadTIFFImage (InputParameters *p_Inp, VideoDataFile *input_file, int FrameNoInFile, FrameFormat *source, unsigned char *buf); + +#endif diff --git a/lcommon/inc/io_video.h b/lcommon/inc/io_video.h new file mode 100644 index 0000000..4275944 --- /dev/null +++ b/lcommon/inc/io_video.h @@ -0,0 +1,63 @@ +/*! + ************************************************************************ + * \file io_video.h + * + * \brief + * Video I/O + * + * \author + * - Alexis Michael Tourapis + * + ************************************************************************ + */ + +#ifndef _IO_VIDEO_H_ +#define _IO_VIDEO_H_ + +#include "frame.h" + +typedef struct video_size { + char* name; + int x_size; + int y_size; +} VIDEO_SIZE; + +typedef enum { + VIDEO_UNKNOWN = -1, + VIDEO_YUV = 0, + VIDEO_RGB = 1, + VIDEO_XYZ = 2, + VIDEO_TIFF = 3, + VIDEO_AVI = 4 +} VideoFileType; + +typedef struct video_data_file +{ + //char* fname; //!< video file name + char fname[FILE_NAME_SIZE]; //!< video file name + char fhead[FILE_NAME_SIZE]; //!< header of video file + char ftail[FILE_NAME_SIZE]; //!< tail of video file + int f_num; //!< video file number + VideoFileType vdtype; //!< File format + FrameFormat format; //!< video format information + int is_concatenated; //!< Single or multifile input? + int is_interleaved; //!< Support for interleaved and non-interleaved input sources + int zero_pad; //!< Used when separate image files are used as input. Enables zero padding for file numbering + int num_digits; //!< Number of digits for file numbering + int start_frame; //!< start frame + int end_frame; //!< end frame + int nframes; //!< number of frames + int crop_x_size; //!< crop information (x component) + int crop_y_size; //!< crop information (y component) + int crop_x_offset; //!< crop offset (x component); + int crop_y_offset; //!< crop offset (y component); + + // AVI related information to be added here + int* avi; + //avi_t* avi; + //int header; + //char compressor[8]; +} VideoDataFile; + +#endif + diff --git a/lcommon/inc/lagrangian.h b/lcommon/inc/lagrangian.h new file mode 100644 index 0000000..abd742e --- /dev/null +++ b/lcommon/inc/lagrangian.h @@ -0,0 +1,25 @@ +/*! + ************************************************************************** + * \file lagrangian.h + * \brief + * Distortion data header file + * \date 2.23.2009, + * + * \author + * Alexis Michael Tourapis + * + ************************************************************************** + */ + +#ifndef _LAGRANGIAN_H_ +#define _LAGRANGIAN_H_ + +typedef struct lambda_params +{ + double md; //!< Mode decision Lambda + double me[3]; //!< Motion Estimation Lambda + int mf[3]; //!< Integer formatted Motion Estimation Lambda +} LambdaParams; + +#endif + diff --git a/lcommon/inc/mb_access.h b/lcommon/inc/mb_access.h new file mode 100644 index 0000000..8cc7601 --- /dev/null +++ b/lcommon/inc/mb_access.h @@ -0,0 +1,33 @@ + +/*! + ************************************************************************************* + * \file mb_access.h + * + * \brief + * Functions for macroblock neighborhoods + * + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * - Karsten Suehring + * - Alexis Michael Tourapis + ************************************************************************************* + */ + +#ifndef _MB_ACCESS_H_ +#define _MB_ACCESS_H_ + +extern void CheckAvailabilityOfNeighbors(Macroblock *currMB); +extern void CheckAvailabilityOfNeighborsMBAFF(Macroblock *currMB); +extern void CheckAvailabilityOfNeighborsNormal(Macroblock *currMB); + +extern void getAffNeighbour (Macroblock *currMB, int xN, int yN, int mb_size[2], PixelPos *pix); +extern void getNonAffNeighbour (Macroblock *currMB, int xN, int yN, int mb_size[2], PixelPos *pix); +extern void get4x4Neighbour (Macroblock *currMB, int xN, int yN, int mb_size[2], PixelPos *pix); +extern void get4x4NeighbourBase (Macroblock *currMB, int block_x, int block_y, int mb_size[2], PixelPos *pix); +extern Boolean mb_is_available (int mbAddr, Macroblock *currMB); +extern void get_mb_pos (VideoParameters *p_Vid, int mb_addr, int mb_size[2], short *x, short *y); +extern void get_mb_block_pos_normal (BlockPos *PicPos, int mb_addr, short *x, short *y); +extern void get_mb_block_pos_mbaff (BlockPos *PicPos, int mb_addr, short *x, short *y); + + +#endif diff --git a/lcommon/inc/mbuffer_common.h b/lcommon/inc/mbuffer_common.h new file mode 100644 index 0000000..5c546cf --- /dev/null +++ b/lcommon/inc/mbuffer_common.h @@ -0,0 +1,225 @@ +/*! +*********************************************************************** +* \file +* mbuffer_common.h +* +* \brief +* Frame buffer functions +* +* \author +* Main contributors (see contributors.h for copyright, address and affiliation details) +* - Karsten Suehring +* - Alexis Michael Tourapis +* - Yuwen He +*********************************************************************** +*/ +#ifndef _MBUFFER_COMMON_H_ +#define _MBUFFER_COMMON_H_ + +/*! + ************************************************************************ + * \brief + * compares two stored pictures by picture number for qsort in descending order + * + ************************************************************************ + */ +static inline int compare_pic_by_pic_num_desc( const void *arg1, const void *arg2 ) +{ + int pic_num1 = (*(StorablePicture**)arg1)->pic_num; + int pic_num2 = (*(StorablePicture**)arg2)->pic_num; + + if (pic_num1 < pic_num2) + return 1; + if (pic_num1 > pic_num2) + return -1; + else + return 0; +} + +/*! + ************************************************************************ + * \brief + * compares two stored pictures by picture number for qsort in descending order + * + ************************************************************************ + */ +static inline int compare_pic_by_lt_pic_num_asc( const void *arg1, const void *arg2 ) +{ + int long_term_pic_num1 = (*(StorablePicture**)arg1)->long_term_pic_num; + int long_term_pic_num2 = (*(StorablePicture**)arg2)->long_term_pic_num; + + if ( long_term_pic_num1 < long_term_pic_num2) + return -1; + if ( long_term_pic_num1 > long_term_pic_num2) + return 1; + else + return 0; +} + +/*! + ************************************************************************ + * \brief + * compares two frame stores by pic_num for qsort in descending order + * + ************************************************************************ + */ +static inline int compare_fs_by_frame_num_desc( const void *arg1, const void *arg2 ) +{ + int frame_num_wrap1 = (*(FrameStore**)arg1)->frame_num_wrap; + int frame_num_wrap2 = (*(FrameStore**)arg2)->frame_num_wrap; + if ( frame_num_wrap1 < frame_num_wrap2) + return 1; + if ( frame_num_wrap1 > frame_num_wrap2) + return -1; + else + return 0; +} + + +/*! + ************************************************************************ + * \brief + * compares two frame stores by lt_pic_num for qsort in descending order + * + ************************************************************************ + */ +static inline int compare_fs_by_lt_pic_idx_asc( const void *arg1, const void *arg2 ) +{ + int long_term_frame_idx1 = (*(FrameStore**)arg1)->long_term_frame_idx; + int long_term_frame_idx2 = (*(FrameStore**)arg2)->long_term_frame_idx; + + if ( long_term_frame_idx1 < long_term_frame_idx2) + return -1; + else if ( long_term_frame_idx1 > long_term_frame_idx2) + return 1; + else + return 0; +} + + +/*! + ************************************************************************ + * \brief + * compares two stored pictures by poc for qsort in ascending order + * + ************************************************************************ + */ +static inline int compare_pic_by_poc_asc( const void *arg1, const void *arg2 ) +{ + int poc1 = (*(StorablePicture**)arg1)->poc; + int poc2 = (*(StorablePicture**)arg2)->poc; + + if ( poc1 < poc2) + return -1; + else if ( poc1 > poc2) + return 1; + else + return 0; +} + + +/*! + ************************************************************************ + * \brief + * compares two stored pictures by poc for qsort in descending order + * + ************************************************************************ + */ +static inline int compare_pic_by_poc_desc( const void *arg1, const void *arg2 ) +{ + int poc1 = (*(StorablePicture**)arg1)->poc; + int poc2 = (*(StorablePicture**)arg2)->poc; + + if (poc1 < poc2) + return 1; + else if (poc1 > poc2) + return -1; + else + return 0; +} + + +/*! + ************************************************************************ + * \brief + * compares two frame stores by poc for qsort in ascending order + * + ************************************************************************ + */ +static inline int compare_fs_by_poc_asc( const void *arg1, const void *arg2 ) +{ + int poc1 = (*(FrameStore**)arg1)->poc; + int poc2 = (*(FrameStore**)arg2)->poc; + + if (poc1 < poc2) + return -1; + else if (poc1 > poc2) + return 1; + else + return 0; +} + + +/*! + ************************************************************************ + * \brief + * compares two frame stores by poc for qsort in descending order + * + ************************************************************************ + */ +static inline int compare_fs_by_poc_desc( const void *arg1, const void *arg2 ) +{ + int poc1 = (*(FrameStore**)arg1)->poc; + int poc2 = (*(FrameStore**)arg2)->poc; + + if (poc1 < poc2) + return 1; + else if (poc1 > poc2) + return -1; + else + return 0; +} + +/*! + ************************************************************************ + * \brief + * returns true, if picture is short term reference picture + * + ************************************************************************ + */ +static inline int is_short_ref(StorablePicture *s) +{ + return ((s->used_for_reference) && (!(s->is_long_term))); +} + + +/*! + ************************************************************************ + * \brief + * returns true, if picture is long term reference picture + * + ************************************************************************ + */ +static inline int is_long_ref(StorablePicture *s) +{ + return ((s->used_for_reference) && (s->is_long_term)); +} + + +extern void gen_pic_list_from_frame_list(PictureStructure currStructure, FrameStore **fs_list, int list_idx, StorablePicture **list, char *list_size, int long_term); +extern StorablePicture* get_long_term_pic(Slice *currSlice, DecodedPictureBuffer *p_Dpb, int LongtermPicNum); +extern void update_ref_list(DecodedPictureBuffer *p_Dpb); +extern void update_ltref_list(DecodedPictureBuffer *p_Dpb); +extern void mm_mark_current_picture_long_term(DecodedPictureBuffer *p_Dpb, StorablePicture *p, int long_term_frame_idx); +extern void mm_unmark_short_term_for_reference(DecodedPictureBuffer *p_Dpb, StorablePicture *p, int difference_of_pic_nums_minus1); +extern void mm_unmark_long_term_for_reference(DecodedPictureBuffer *p_Dpb, StorablePicture *p, int long_term_pic_num); +extern void mm_assign_long_term_frame_idx(DecodedPictureBuffer *p_Dpb, StorablePicture* p, int difference_of_pic_nums_minus1, int long_term_frame_idx); +extern void mm_update_max_long_term_frame_idx(DecodedPictureBuffer *p_Dpb, int max_long_term_frame_idx_plus1); +extern void mm_unmark_all_short_term_for_reference (DecodedPictureBuffer *p_Dpb); +extern void mm_unmark_all_long_term_for_reference (DecodedPictureBuffer *p_Dpb); +extern int is_used_for_reference (FrameStore* fs); +extern void get_smallest_poc(DecodedPictureBuffer *p_Dpb, int *poc,int * pos); +extern int remove_unused_frame_from_dpb(DecodedPictureBuffer *p_Dpb); + +#endif + diff --git a/lcommon/inc/memalloc.h b/lcommon/inc/memalloc.h new file mode 100644 index 0000000..f2ce1c7 --- /dev/null +++ b/lcommon/inc/memalloc.h @@ -0,0 +1,217 @@ +/*! + ************************************************************************ + * \file memalloc.h + * + * \brief + * Memory allocation and free helper funtions + * + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * - Karsten Suehring + * - Alexis Michael Tourapis + * - Yuwen He + * + ************************************************************************ + */ + +#ifndef _MEMALLOC_H_ +#define _MEMALLOC_H_ + +#include "global.h" +#include "mbuffer.h" +#include "distortion.h" +#include "lagrangian.h" +#include "quant_params.h" + +extern int get_mem2Ddist(DistortionData ***array2D, int dim0, int dim1); + +extern int get_mem2Dlm (LambdaParams ***array2D, int dim0, int dim1); +extern int get_mem2Dolm (LambdaParams ***array2D, int dim0, int dim1, int offset); + +extern int get_mem2Dmp (PicMotionParams ***array2D, int dim0, int dim1); +extern int get_mem3Dmp (PicMotionParams ****array3D, int dim0, int dim1, int dim2); + +extern int get_mem2Dquant(LevelQuantParams ***array2D, int dim0, int dim1); +extern int get_mem3Dquant(LevelQuantParams ****array3D, int dim0, int dim1, int dim2); +extern int get_mem4Dquant(LevelQuantParams *****array4D, int dim0, int dim1, int dim2, int dim3); +extern int get_mem5Dquant(LevelQuantParams ******array5D, int dim0, int dim1, int dim2, int dim3, int dim4); + +extern int get_mem2Dmv (MotionVector ***array2D, int dim0, int dim1); +extern int get_mem3Dmv (MotionVector ****array3D, int dim0, int dim1, int dim2); +extern int get_mem4Dmv (MotionVector *****array4D, int dim0, int dim1, int dim2, int dim3); +extern int get_mem5Dmv (MotionVector ******array5D, int dim0, int dim1, int dim2, int dim3, int dim4); +extern int get_mem6Dmv (MotionVector *******array6D, int dim0, int dim1, int dim2, int dim3, int dim4, int dim5); +extern int get_mem7Dmv (MotionVector ********array7D, int dim0, int dim1, int dim2, int dim3, int dim4, int dim5, int dim6); + +extern byte** new_mem2D(int dim0, int dim1); +extern int get_mem2D(byte ***array2D, int dim0, int dim1); +extern int get_mem3D(byte ****array3D, int dim0, int dim1, int dim2); +extern int get_mem4D(byte *****array4D, int dim0, int dim1, int dim2, int dim3); + +extern int** new_mem2Dint(int dim0, int dim1); +extern int get_mem2Dint(int ***array2D, int dim0, int dim1); +extern int get_mem2Dint_pad(int ***array2D, int dim0, int dim1, int iPadY, int iPadX); +extern int get_mem2Dint64(int64 ***array2D, int dim0, int dim1); +extern int get_mem3Dint(int ****array3D, int dim0, int dim1, int dim2); +extern int get_mem3Dint64(int64 ****array3D, int dim0, int dim1, int dim2); +extern int get_mem4Dint(int *****array4D, int dim0, int dim1, int dim2, int dim3); +extern int get_mem4Dint64(int64 *****array4D, int dim0, int dim1, int dim2, int dim3); +extern int get_mem5Dint(int ******array5D, int dim0, int dim1, int dim2, int dim3, int dim4); + +extern uint16** new_mem2Duint16(int dim0, int dim1); +extern int get_mem2Duint16(uint16 ***array2D, int dim0, int dim1); +extern int get_mem3Duint16(uint16 ****array3D,int dim0, int dim1, int dim2); + +extern int get_mem2Ddistblk(distblk ***array2D, int dim0, int dim1); +extern int get_mem3Ddistblk(distblk ****array3D, int dim0, int dim1, int dim2); +extern int get_mem4Ddistblk(distblk *****array4D, int dim0, int dim1, int dim2, int dim3); + +extern int get_mem2Dshort(short ***array2D, int dim0, int dim1); +extern int get_mem3Dshort(short ****array3D, int dim0, int dim1, int dim2); +extern int get_mem4Dshort(short *****array4D, int dim0, int dim1, int dim2, int dim3); +extern int get_mem5Dshort(short ******array5D, int dim0, int dim1, int dim2, int dim3, int dim4); +extern int get_mem6Dshort(short *******array6D, int dim0, int dim1, int dim2, int dim3, int dim4, int dim5); +extern int get_mem7Dshort(short ********array7D, int dim0, int dim1, int dim2, int dim3, int dim4, int dim5, int dim6); + +extern int get_mem1Dpel(imgpel **array2D, int dim0); +extern int get_mem2Dpel(imgpel ***array2D, int dim0, int dim1); +extern int get_mem2Dpel_pad(imgpel ***array2D, int dim0, int dim1, int iPadY, int iPadX); + +extern int get_mem3Dpel (imgpel ****array3D, int dim0, int dim1, int dim2); +extern int get_mem3Dpel_pad(imgpel ****array3D, int dim0, int dim1, int dim2, int iPadY, int iPadX); +extern int get_mem4Dpel (imgpel *****array4D, int dim0, int dim1, int dim2, int dim3); +extern int get_mem4Dpel_pad(imgpel *****array4D, int dim0, int dim1, int dim2, int dim3, int iPadY, int iPadX); +extern int get_mem5Dpel (imgpel ******array5D, int dim0, int dim1, int dim2, int dim3, int dim4); +extern int get_mem5Dpel_pad(imgpel ******array5D, int dim0, int dim1, int dim2, int dim3, int dim4, int iPadY, int iPadX); +extern int get_mem2Ddouble (double ***array2D, int dim0, int dim1); + +extern int get_mem1Dodouble(double **array1D, int dim0, int offset); +extern int get_mem2Dodouble(double ***array2D, int dim0, int dim1, int offset); +extern int get_mem3Dodouble(double ****array2D, int dim0, int dim1, int dim2, int offset); + +extern int get_mem2Doint (int ***array2D, int dim0, int dim1, int offset); +extern int get_mem3Doint (int ****array3D, int dim0, int dim1, int dim2, int offset); + +extern int get_mem2Dwp (WPParams ***array2D, int dim0, int dim1); + +extern int get_offset_mem2Dshort(short ***array2D, int rows, int columns, int offset_y, int offset_x); + +extern void free_offset_mem2Dshort(short **array2D, int columns, int offset_x, int offset_y); + +extern void free_mem2Ddist (DistortionData **array2D); + +extern void free_mem2Dlm (LambdaParams **array2D); +extern void free_mem2Dolm (LambdaParams **array2D, int offset); + +extern void free_mem2Dmp (PicMotionParams **array2D); +extern void free_mem3Dmp (PicMotionParams ***array2D); + +extern void free_mem2Dquant(LevelQuantParams **array2D); +extern void free_mem3Dquant(LevelQuantParams ***array2D); +extern void free_mem4Dquant(LevelQuantParams ****array2D); +extern void free_mem5Dquant(LevelQuantParams *****array2D); + +extern void free_mem2Dmv (MotionVector **array2D); +extern void free_mem3Dmv (MotionVector ***array2D); +extern void free_mem4Dmv (MotionVector ****array2D); +extern void free_mem5Dmv (MotionVector *****array2D); +extern void free_mem6Dmv (MotionVector ******array2D); +extern void free_mem7Dmv (MotionVector *******array7D); + +extern int get_mem2D_spp(StorablePicturePtr ***array3D, int dim0, int dim1); +extern int get_mem3D_spp(StorablePicturePtr ****array3D, int dim0, int dim1, int dim2); + +extern void free_mem2D_spp (StorablePicturePtr **array2D); +extern void free_mem3D_spp (StorablePicturePtr ***array2D); + +extern void free_mem2D (byte **array2D); +extern void free_mem3D (byte ***array3D); +extern void free_mem4D (byte ****array4D); + +extern void free_mem2Dint (int **array2D); +extern void free_mem2Dint_pad(int **array2D, int iPadY, int iPadX); +extern void free_mem3Dint (int ***array3D); +extern void free_mem4Dint (int ****array4D); +extern void free_mem5Dint (int *****array5D); + +extern void free_mem2Duint16(uint16 **array2D); +extern void free_mem3Duint16(uint16 ***array3D); + +extern void free_mem2Dint64(int64 **array2D); +extern void free_mem3Dint64(int64 ***array3D); +extern void free_mem4Dint64(int64 ****array4D); + +extern void free_mem2Ddistblk(distblk **array2D); +extern void free_mem3Ddistblk(distblk ***array3D); +extern void free_mem4Ddistblk(distblk ****array4D); + +extern void free_mem2Dshort(short **array2D); +extern void free_mem3Dshort(short ***array3D); +extern void free_mem4Dshort(short ****array4D); +extern void free_mem5Dshort(short *****array5D); +extern void free_mem6Dshort(short ******array6D); +extern void free_mem7Dshort(short *******array7D); + +extern void free_mem1Dpel (imgpel *array1D); +extern void free_mem2Dpel (imgpel **array2D); +extern void free_mem2Dpel_pad(imgpel **array2D, int iPadY, int iPadX); +extern void free_mem3Dpel (imgpel ***array3D); +extern void free_mem3Dpel_pad(imgpel ***array3D, int iDim12, int iPadY, int iPadX); +extern void free_mem4Dpel (imgpel ****array4D); +extern void free_mem4Dpel_pad(imgpel ****array4D, int iFrames, int iPadY, int iPadX); +extern void free_mem5Dpel (imgpel *****array5D); +extern void free_mem5Dpel_pad(imgpel *****array5D, int iFrames, int iPadY, int iPadX); +extern void free_mem2Ddouble (double **array2D); +extern void free_mem3Ddouble (double ***array3D); + +extern void free_mem1Dodouble(double *array1D, int offset); +extern void free_mem2Dodouble(double **array2D, int offset); +extern void free_mem3Dodouble(double ***array3D, int rows, int columns, int offset); +extern void free_mem2Doint (int **array2D, int offset); +extern void free_mem3Doint (int ***array3D, int rows, int columns, int offset); + +extern int init_top_bot_planes(imgpel **imgFrame, int height, imgpel ***imgTopField, imgpel ***imgBotField); +extern void free_top_bot_planes(imgpel **imgTopField, imgpel **imgBotField); + +extern void free_mem2Dwp (WPParams **array2D); + +extern void copy2DImage(imgpel **dst_img, imgpel **src_img, int size_x, int size_y); +extern void no_mem_exit(char *where); +extern int malloc_mem2Dpel_2SLayers(imgpel ***buf0, int imgtype0, imgpel ***buf1, int imgtype1, int height, int width); +extern int malloc_mem3Dpel_2SLayers(imgpel ****buf0, int imgtype0, imgpel ****buf1, int imgtype1, int frames, int height, int width); + +extern void free_mem2Dpel_2SLayers(imgpel ***buf0, imgpel ***buf1); +extern void free_mem3Dpel_2SLayers(imgpel ****buf0, imgpel ****buf1); + + +static inline void* mem_malloc(size_t nitems) +{ + void *d; + if((d = malloc(nitems)) == NULL) + { + no_mem_exit("malloc failed.\n"); + return NULL; + } + return d; +} + +/*! + ************************************************************************ + * \brief + * allocate and set memory aligned at SSE_MEMORY_ALIGNMENT + * + ************************************************************************/ +static inline void* mem_calloc(size_t nitems, size_t size) +{ + size_t padded_size = nitems * size; + void *d = mem_malloc(padded_size); + memset(d, 0, (int)padded_size); + return d; +} + +static inline void mem_free(void *a) +{ + free_pointer(a); +} + +#endif \ No newline at end of file diff --git a/lcommon/inc/mv_prediction.h b/lcommon/inc/mv_prediction.h new file mode 100644 index 0000000..f57ad57 --- /dev/null +++ b/lcommon/inc/mv_prediction.h @@ -0,0 +1,19 @@ +/*! + ************************************************************************************* + * \file mv_prediction.h + * + * \brief + * Declarations for Motion Vector Prediction + * + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * - Alexis Michael Tourapis + ************************************************************************************* + */ + +#ifndef _MV_PREDICTION_H_ +#define _MV_PREDICTION_H_ + +extern void init_motion_vector_prediction(Macroblock *currMB, int mb_aff_frame_flag); + +#endif diff --git a/lcommon/inc/nalucommon.h b/lcommon/inc/nalucommon.h new file mode 100644 index 0000000..9585b93 --- /dev/null +++ b/lcommon/inc/nalucommon.h @@ -0,0 +1,91 @@ + +/*! + ************************************************************************************** + * \file + * nalucommon.h + * \brief + * NALU handling common to encoder and decoder + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * - Stephan Wenger + * - Karsten Suehring + *************************************************************************************** + */ + +#ifndef _NALUCOMMON_H_ +#define _NALUCOMMON_H_ + +#include "defines.h" + +#define MAXRBSPSIZE 64000 +#define MAXNALUSIZE 64000 + +//! values for nal_unit_type +typedef enum { + NALU_TYPE_SLICE = 1, + NALU_TYPE_DPA = 2, + NALU_TYPE_DPB = 3, + NALU_TYPE_DPC = 4, + NALU_TYPE_IDR = 5, + NALU_TYPE_SEI = 6, + NALU_TYPE_SPS = 7, + NALU_TYPE_PPS = 8, + NALU_TYPE_AUD = 9, + NALU_TYPE_EOSEQ = 10, + NALU_TYPE_EOSTREAM = 11, + NALU_TYPE_FILL = 12, +#if (MVC_EXTENSION_ENABLE) + NALU_TYPE_PREFIX = 14, + NALU_TYPE_SUB_SPS = 15, + NALU_TYPE_SLC_EXT = 20, + NALU_TYPE_VDRD = 24 // View and Dependency Representation Delimiter NAL Unit +#endif +} NaluType; + +//! values for nal_ref_idc +typedef enum { + NALU_PRIORITY_HIGHEST = 3, + NALU_PRIORITY_HIGH = 2, + NALU_PRIORITY_LOW = 1, + NALU_PRIORITY_DISPOSABLE = 0 +} NalRefIdc; + +//! NAL unit structure +typedef struct nalu_t +{ + int startcodeprefix_len; //!< 4 for parameter sets and first slice in picture, 3 for everything else (suggested) + unsigned len; //!< Length of the NAL unit (Excluding the start code, which does not belong to the NALU) + unsigned max_size; //!< NAL Unit Buffer size + int forbidden_bit; //!< should be always FALSE + NaluType nal_unit_type; //!< NALU_TYPE_xxxx + NalRefIdc nal_reference_idc; //!< NALU_PRIORITY_xxxx + byte *buf; //!< contains the first byte followed by the EBSP + uint16 lost_packets; //!< true, if packet loss is detected +#if (MVC_EXTENSION_ENABLE) + int svc_extension_flag; //!< should be always 0, for MVC + int non_idr_flag; //!< 0 = current is IDR + int priority_id; //!< a lower value of priority_id specifies a higher priority + int view_id; //!< view identifier for the NAL unit + int temporal_id; //!< temporal identifier for the NAL unit + int anchor_pic_flag; //!< anchor access unit + int inter_view_flag; //!< inter-view prediction enable + int reserved_one_bit; //!< shall be equal to 1 +#endif + + //! Edited by Joao Carreira + int position; + +} NALU_t; + +//! allocate one NAL Unit +extern NALU_t *AllocNALU(int); + +//! free one NAL Unit +extern void FreeNALU(NALU_t *n); + +#if (MVC_EXTENSION_ENABLE) +extern void nal_unit_header_svc_extension(); +extern void prefix_nal_unit_svc(); +#endif + +#endif diff --git a/lcommon/inc/parsetcommon.h b/lcommon/inc/parsetcommon.h new file mode 100644 index 0000000..2f4a592 --- /dev/null +++ b/lcommon/inc/parsetcommon.h @@ -0,0 +1,274 @@ + +/*! + ************************************************************************************** + * \file + * parsetcommon.h + * \brief + * Picture and Sequence Parameter Sets, structures common to encoder and decoder + * + * \date 25 November 2002 + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * - Stephan Wenger + *************************************************************************************** + */ + + + +// In the MPEG-4 AVC/H.264 syntax, frequently flags are used that indicate the presence of +// certain pieces of information in the NALU. Here, these flags are also +// present. In the encoder, those bits indicate that the values signaled to +// be present are meaningful and that this part of the syntax should be +// written to the NALU. In the decoder, the flag indicates that information +// was received from the decoded NALU and should be used henceforth. +// The structure names were chosen as indicated in the MPEG-4 AVC/H.264 syntax + +#ifndef _PARSETCOMMON_H_ +#define _PARSETCOMMON_H_ + +#include "defines.h" + +#define MAXIMUMPARSETRBSPSIZE 1500 +#define MAXIMUMPARSETNALUSIZE 1500 + +#define MAXSPS 32 +#define MAXPPS 256 + +#define MAXIMUMVALUEOFcpb_cnt 32 +typedef struct +{ + unsigned int cpb_cnt_minus1; // ue(v) + unsigned int bit_rate_scale; // u(4) + unsigned int cpb_size_scale; // u(4) + unsigned int bit_rate_value_minus1 [MAXIMUMVALUEOFcpb_cnt]; // ue(v) + unsigned int cpb_size_value_minus1 [MAXIMUMVALUEOFcpb_cnt]; // ue(v) + unsigned int cbr_flag [MAXIMUMVALUEOFcpb_cnt]; // u(1) + unsigned int initial_cpb_removal_delay_length_minus1; // u(5) + unsigned int cpb_removal_delay_length_minus1; // u(5) + unsigned int dpb_output_delay_length_minus1; // u(5) + unsigned int time_offset_length; // u(5) +} hrd_parameters_t; + + +typedef struct +{ + Boolean aspect_ratio_info_present_flag; // u(1) + unsigned int aspect_ratio_idc; // u(8) + unsigned short sar_width; // u(16) + unsigned short sar_height; // u(16) + Boolean overscan_info_present_flag; // u(1) + Boolean overscan_appropriate_flag; // u(1) + Boolean video_signal_type_present_flag; // u(1) + unsigned int video_format; // u(3) + Boolean video_full_range_flag; // u(1) + Boolean colour_description_present_flag; // u(1) + unsigned int colour_primaries; // u(8) + unsigned int transfer_characteristics; // u(8) + unsigned int matrix_coefficients; // u(8) + Boolean chroma_location_info_present_flag; // u(1) + unsigned int chroma_sample_loc_type_top_field; // ue(v) + unsigned int chroma_sample_loc_type_bottom_field; // ue(v) + Boolean timing_info_present_flag; // u(1) + unsigned int num_units_in_tick; // u(32) + unsigned int time_scale; // u(32) + Boolean fixed_frame_rate_flag; // u(1) + Boolean nal_hrd_parameters_present_flag; // u(1) + hrd_parameters_t nal_hrd_parameters; // hrd_paramters_t + Boolean vcl_hrd_parameters_present_flag; // u(1) + hrd_parameters_t vcl_hrd_parameters; // hrd_paramters_t + // if ((nal_hrd_parameters_present_flag || (vcl_hrd_parameters_present_flag)) + Boolean low_delay_hrd_flag; // u(1) + Boolean pic_struct_present_flag; // u(1) + Boolean bitstream_restriction_flag; // u(1) + Boolean motion_vectors_over_pic_boundaries_flag; // u(1) + unsigned int max_bytes_per_pic_denom; // ue(v) + unsigned int max_bits_per_mb_denom; // ue(v) + unsigned int log2_max_mv_length_vertical; // ue(v) + unsigned int log2_max_mv_length_horizontal; // ue(v) + unsigned int num_reorder_frames; // ue(v) + unsigned int max_dec_frame_buffering; // ue(v) +} vui_seq_parameters_t; + + +#define MAXnum_slice_groups_minus1 8 +typedef struct +{ + Boolean Valid; // indicates the parameter set is valid + unsigned int pic_parameter_set_id; // ue(v) + unsigned int seq_parameter_set_id; // ue(v) + Boolean entropy_coding_mode_flag; // u(1) + Boolean transform_8x8_mode_flag; // u(1) + + Boolean pic_scaling_matrix_present_flag; // u(1) + int pic_scaling_list_present_flag[12]; // u(1) + int ScalingList4x4[6][16]; // se(v) + int ScalingList8x8[6][64]; // se(v) + Boolean UseDefaultScalingMatrix4x4Flag[6]; + Boolean UseDefaultScalingMatrix8x8Flag[6]; + + // if( pic_order_cnt_type < 2 ) in the sequence parameter set + Boolean bottom_field_pic_order_in_frame_present_flag; // u(1) + unsigned int num_slice_groups_minus1; // ue(v) + unsigned int slice_group_map_type; // ue(v) + // if( slice_group_map_type = = 0 ) + unsigned int run_length_minus1[MAXnum_slice_groups_minus1]; // ue(v) + // else if( slice_group_map_type = = 2 ) + unsigned int top_left[MAXnum_slice_groups_minus1]; // ue(v) + unsigned int bottom_right[MAXnum_slice_groups_minus1]; // ue(v) + // else if( slice_group_map_type = = 3 || 4 || 5 + Boolean slice_group_change_direction_flag; // u(1) + unsigned int slice_group_change_rate_minus1; // ue(v) + // else if( slice_group_map_type = = 6 ) + unsigned int pic_size_in_map_units_minus1; // ue(v) + byte *slice_group_id; // complete MBAmap u(v) + + int num_ref_idx_l0_default_active_minus1; // ue(v) + int num_ref_idx_l1_default_active_minus1; // ue(v) + Boolean weighted_pred_flag; // u(1) + unsigned int weighted_bipred_idc; // u(2) + int pic_init_qp_minus26; // se(v) + int pic_init_qs_minus26; // se(v) + int chroma_qp_index_offset; // se(v) + + int cb_qp_index_offset; // se(v) + int cr_qp_index_offset; // se(v) + int second_chroma_qp_index_offset; // se(v) + + Boolean deblocking_filter_control_present_flag; // u(1) + Boolean constrained_intra_pred_flag; // u(1) + Boolean redundant_pic_cnt_present_flag; // u(1) + Boolean vui_pic_parameters_flag; // u(1) +} pic_parameter_set_rbsp_t; + + +#define MAXnum_ref_frames_in_pic_order_cnt_cycle 256 +typedef struct +{ + Boolean Valid; // indicates the parameter set is valid + + unsigned int profile_idc; // u(8) + Boolean constrained_set0_flag; // u(1) + Boolean constrained_set1_flag; // u(1) + Boolean constrained_set2_flag; // u(1) + Boolean constrained_set3_flag; // u(1) +#if (MVC_EXTENSION_ENABLE) + Boolean constrained_set4_flag; // u(1) + Boolean constrained_set5_flag; // u(2) +#endif + unsigned int level_idc; // u(8) + unsigned int seq_parameter_set_id; // ue(v) + unsigned int chroma_format_idc; // ue(v) + + Boolean seq_scaling_matrix_present_flag; // u(1) + int seq_scaling_list_present_flag[12]; // u(1) + int ScalingList4x4[6][16]; // se(v) + int ScalingList8x8[6][64]; // se(v) + Boolean UseDefaultScalingMatrix4x4Flag[6]; + Boolean UseDefaultScalingMatrix8x8Flag[6]; + + unsigned int bit_depth_luma_minus8; // ue(v) + unsigned int bit_depth_chroma_minus8; // ue(v) + unsigned int log2_max_frame_num_minus4; // ue(v) + unsigned int pic_order_cnt_type; + // if( pic_order_cnt_type == 0 ) + unsigned int log2_max_pic_order_cnt_lsb_minus4; // ue(v) + // else if( pic_order_cnt_type == 1 ) + Boolean delta_pic_order_always_zero_flag; // u(1) + int offset_for_non_ref_pic; // se(v) + int offset_for_top_to_bottom_field; // se(v) + unsigned int num_ref_frames_in_pic_order_cnt_cycle; // ue(v) + // for( i = 0; i < num_ref_frames_in_pic_order_cnt_cycle; i++ ) + int offset_for_ref_frame[MAXnum_ref_frames_in_pic_order_cnt_cycle]; // se(v) + unsigned int num_ref_frames; // ue(v) + Boolean gaps_in_frame_num_value_allowed_flag; // u(1) + unsigned int pic_width_in_mbs_minus1; // ue(v) + unsigned int pic_height_in_map_units_minus1; // ue(v) + Boolean frame_mbs_only_flag; // u(1) + // if( !frame_mbs_only_flag ) + Boolean mb_adaptive_frame_field_flag; // u(1) + Boolean direct_8x8_inference_flag; // u(1) + Boolean frame_cropping_flag; // u(1) + unsigned int frame_crop_left_offset; // ue(v) + unsigned int frame_crop_right_offset; // ue(v) + unsigned int frame_crop_top_offset; // ue(v) + unsigned int frame_crop_bottom_offset; // ue(v) + Boolean vui_parameters_present_flag; // u(1) + vui_seq_parameters_t vui_seq_parameters; // vui_seq_parameters_t + unsigned separate_colour_plane_flag; // u(1) +#if (MVC_EXTENSION_ENABLE) + int max_dec_frame_buffering; +#endif + int lossless_qpprime_flag; +} seq_parameter_set_rbsp_t; + +#if (MVC_EXTENSION_ENABLE) +typedef struct mvcvui_tag +{ + int num_ops_minus1; + char *temporal_id; + int *num_target_output_views_minus1; + int **view_id; + char *timing_info_present_flag; + int *num_units_in_tick; + int *time_scale; + char *fixed_frame_rate_flag; + char *nal_hrd_parameters_present_flag; + char *vcl_hrd_parameters_present_flag; + char *low_delay_hrd_flag; + char *pic_struct_present_flag; + + //hrd parameters; + char cpb_cnt_minus1; + char bit_rate_scale; + char cpb_size_scale; + int bit_rate_value_minus1[32]; + int cpb_size_value_minus1[32]; + char cbr_flag[32]; + char initial_cpb_removal_delay_length_minus1; + char cpb_removal_delay_length_minus1; + char dpb_output_delay_length_minus1; + char time_offset_length; +}MVCVUI_t; + +typedef struct +{ + seq_parameter_set_rbsp_t sps; + + unsigned int bit_equal_to_one; + int num_views_minus1; + int *view_id; + int *num_anchor_refs_l0; + int **anchor_ref_l0; + int *num_anchor_refs_l1; + int **anchor_ref_l1; + + int *num_non_anchor_refs_l0; + int **non_anchor_ref_l0; + int *num_non_anchor_refs_l1; + int **non_anchor_ref_l1; + + int num_level_values_signalled_minus1; + int *level_idc; + int *num_applicable_ops_minus1; + int **applicable_op_temporal_id; + int **applicable_op_num_target_views_minus1; + int ***applicable_op_target_view_id; + int **applicable_op_num_views_minus1; + + unsigned int mvc_vui_parameters_present_flag; + Boolean Valid; // indicates the parameter set is valid + MVCVUI_t MVCVUIParams; +} subset_seq_parameter_set_rbsp_t; + +#endif + +pic_parameter_set_rbsp_t *AllocPPS (void); +seq_parameter_set_rbsp_t *AllocSPS (void); + +void FreePPS (pic_parameter_set_rbsp_t *pps); +void FreeSPS (seq_parameter_set_rbsp_t *sps); + +int sps_is_equal(seq_parameter_set_rbsp_t *sps1, seq_parameter_set_rbsp_t *sps2); +int pps_is_equal(pic_parameter_set_rbsp_t *pps1, pic_parameter_set_rbsp_t *pps2); + +#endif diff --git a/lcommon/inc/quant_params.h b/lcommon/inc/quant_params.h new file mode 100644 index 0000000..10fda89 --- /dev/null +++ b/lcommon/inc/quant_params.h @@ -0,0 +1,54 @@ +/*! + *************************************************************************** + * \file + * quant_params.h + * + * \author + * Alexis Michael Tourapis + * + * \brief + * Headerfile for Quantization parameters + ************************************************************************** + */ + +#ifndef _QUANT_PARAMS_H_ +#define _QUANT_PARAMS_H_ + +typedef struct level_quant_params { + int OffsetComp; + int ScaleComp; + int InvScaleComp; +} LevelQuantParams; + +typedef struct quant_params { + int AdaptRndWeight; + int AdaptRndCrWeight; + + LevelQuantParams *****q_params_4x4; + LevelQuantParams *****q_params_8x8; + + int *qp_per_matrix; + int *qp_rem_matrix; + + short **OffsetList4x4input; + short **OffsetList8x8input; + short ***OffsetList4x4; + short ***OffsetList8x8; +} QuantParameters; + +typedef struct quant_methods { + int block_y; + int block_x; + int qp; + int* ACLevel; + int* ACRun; + int **fadjust; + LevelQuantParams **q_params; + int *coeff_cost; + const byte (*pos_scan)[2]; + const byte *c_cost; + char type; +} QuantMethods; + +#endif + diff --git a/lcommon/inc/report.h b/lcommon/inc/report.h new file mode 100644 index 0000000..fb61e09 --- /dev/null +++ b/lcommon/inc/report.h @@ -0,0 +1,25 @@ + +/*! + ************************************************************************ + * \file report.h + * + * \brief + * headers for frame format related information + * + * \author + * + ************************************************************************ + */ +#ifndef _REPORT_H_ +#define _REPORT_H_ +#include "contributors.h" +#include "global.h" +#include "enc_statistics.h" + +extern void report ( VideoParameters *p_Vid, InputParameters *p_Inp, StatParameters *p_Stats ); +extern void information_init ( VideoParameters *p_Vid, InputParameters *p_Inp, StatParameters *p_Stats ); +extern void report_frame_statistic( VideoParameters *p_Vid, InputParameters *p_Inp ); +extern void report_stats_on_error (void); + +#endif + diff --git a/lcommon/inc/transcode_common.h b/lcommon/inc/transcode_common.h new file mode 100644 index 0000000..294e1ad --- /dev/null +++ b/lcommon/inc/transcode_common.h @@ -0,0 +1,33 @@ +/*! +************************************************************************ +* \file transcode_common.h +* +* \brief +* Common Structs to decoder and encoder to transcode a MVC Stream +* +* \author +* - João Carreira +* +************************************************************************ +*/ + +#ifndef _TRANSCODE_COMMON_H +#define _TRANSCODE_COMMON_H + +#include "mbuffer.h" + +typedef struct trans_common_t +{ + int poc; + int view_id; + int frame_width; + int frame_height; + PicMotionParams **pp_mv_info; + +} TRANS_FRAME_NODE_T; + + +extern int write_trans_node( int pfile, TRANS_FRAME_NODE_T node); +extern int read_trans_node( int pfile, TRANS_FRAME_NODE_T *node); + +#endif \ No newline at end of file diff --git a/lcommon/inc/transform.h b/lcommon/inc/transform.h new file mode 100644 index 0000000..13d8c78 --- /dev/null +++ b/lcommon/inc/transform.h @@ -0,0 +1,32 @@ + +/*! + *************************************************************************** + * + * \file transform.h + * + * \brief + * prototypes of transform functions + * + * \date + * 10 July 2007 + * + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * Alexis Michael Tourapis + **************************************************************************/ + +#ifndef _TRANSFORM_H_ +#define _TRANSFORM_H_ + +extern void forward4x4 (int **block , int **tblock, int pos_y, int pos_x); +extern void inverse4x4 (int **tblock, int **block , int pos_y, int pos_x); +extern void forward8x8 (int **block , int **tblock, int pos_y, int pos_x); +extern void inverse8x8 (int **tblock, int **block , int pos_x); +extern void hadamard4x4 (int **block , int **tblock); +extern void ihadamard4x4 (int **tblock, int **block); +extern void hadamard4x2 (int **block , int **tblock); +extern void ihadamard4x2 (int **tblock, int **block); +extern void hadamard2x2 (int **block , int tblock[4]); +extern void ihadamard2x2 (int block[4], int tblock[4]); + +#endif //_TRANSFORM_H_ diff --git a/lcommon/inc/typedefs.h b/lcommon/inc/typedefs.h new file mode 100644 index 0000000..e57bf1a --- /dev/null +++ b/lcommon/inc/typedefs.h @@ -0,0 +1,75 @@ +/*! + ************************************************************************************* + * \file typedefs.h + * + * \brief + * Common type definitions + * Currently only supports Windows and Linux operating systems. + * Need to add support for other "older systems such as VAX, DECC, Unix Alpha etc + * + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * - Alexis Michael Tourapis + ************************************************************************************* + */ + +#ifndef _TYPEDEFS_H_ +#define _TYPEDEFS_H_ + +#include "win32.h" + +typedef unsigned char byte; //!< byte type definition +typedef unsigned char uint8; //!< type definition for unsigned char (same as byte, 8 bits) +typedef unsigned short uint16; //!< type definition for unsigned short (16 bits) +typedef unsigned int uint32; //!< type definition for unsigned int (32 bits) + +typedef char int8; +typedef short int16; +typedef int int32; + +#if IMGTYPE == 0 +typedef byte imgpel; //!< pixel type +typedef uint16 distpel; //!< distortion type (for pixels) +typedef int32 distblk; //!< distortion type (for Macroblock) +typedef int32 transpel; //!< transformed coefficient type +#else +typedef uint16 imgpel; +typedef uint32 distpel; +typedef int64 distblk; +typedef int32 transpel; +#endif + +#define MAX_IMG_PEL ( (imgpel)~((imgpel)0) ) + +//! Boolean Type +#ifdef FALSE +# define Boolean int +#else +typedef enum { + FALSE, + TRUE +} Boolean; +#endif + +#ifndef MAXINT64 +#define MAXINT64 0x7fffffffffffffff +#endif + +/* +#define MAXUINT8 0xff +#define MAXUINT16 0xffff +#define MAXUINT32 0xffffffff +#define MAXUINT64 0xffffffffffffffff + +#define MAXINT8 0x7f +#define MININT8 (-MAXINT8) +#define MAXINT16 0x7fff +#define MININT16 (-MAXINT16) +#define MAXINT32 0x7fffffff +#define MININT32 (-MAXINT32) +#define MAXINT64 0x7fffffffffffffff +#define MININT64 (-MAXINT64) +*/ + +#endif + diff --git a/lcommon/inc/types.h b/lcommon/inc/types.h new file mode 100644 index 0000000..e22841a --- /dev/null +++ b/lcommon/inc/types.h @@ -0,0 +1,203 @@ +/*! + ************************************************************************ + * \file + * types.h + * + * \brief + * type definitions. + * + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * + ************************************************************************ + */ +#ifndef _TYPES_H_ +#define _TYPES_H_ + +/*********************************************************************** + * T y p e d e f i n i t i o n s f o r T M L + *********************************************************************** + */ + +typedef enum +{ + // YUV + PLANE_Y = 0, // PLANE_Y + PLANE_U = 1, // PLANE_Cb + PLANE_V = 2, // PLANE_Cr + // RGB + PLANE_G = 0, + PLANE_B = 1, + PLANE_R = 2 +} ColorPlane; + +enum { + LIST_0 = 0, + LIST_1 = 1, + BI_PRED = 2, + BI_PRED_L0 = 3, + BI_PRED_L1 = 4 +}; + +enum { + ERROR_SAD = 0, + ERROR_SSE = 1, + ERROR_SATD = 2, + ERROR_PSATD = 3 +}; + +enum { + ME_Y_ONLY = 0, + ME_YUV_FP = 1, + ME_YUV_FP_SP = 2 +}; + + +enum { + DISTORTION_MSE = 0 +}; + + +//! Data Partitioning Modes +typedef enum +{ + PAR_DP_1, //!< no data partitioning is supported + PAR_DP_3 //!< data partitioning with 3 partitions +} PAR_DP_TYPE; + + +//! Output File Types +typedef enum +{ + PAR_OF_ANNEXB, //!< Annex B byte stream format + PAR_OF_RTP //!< RTP packets in outfile +} PAR_OF_TYPE; + +//! Field Coding Types +typedef enum +{ + FRAME_CODING = 0, + FIELD_CODING = 1, + ADAPTIVE_CODING = 2, + FRAME_MB_PAIR_CODING = 3 +} CodingType; + + +//! definition of H.264 syntax elements +typedef enum +{ + SE_HEADER, + SE_PTYPE, + SE_MBTYPE, + SE_REFFRAME, + SE_INTRAPREDMODE, + SE_MVD, + SE_CBP, + SE_LUM_DC_INTRA, + SE_CHR_DC_INTRA, + SE_LUM_AC_INTRA, + SE_CHR_AC_INTRA, + SE_LUM_DC_INTER, + SE_CHR_DC_INTER, + SE_LUM_AC_INTER, + SE_CHR_AC_INTER, + SE_DELTA_QUANT, + SE_BFRAME, + SE_EOS, + SE_MAX_ELEMENTS = 20 //!< number of maximum syntax elements +} SE_type; // substituting the definitions in elements.h + + +typedef enum +{ + NO_SLICES, + FIXED_MB, + FIXED_RATE, + CALL_BACK +} SliceMode; + + +typedef enum +{ + CAVLC, + CABAC +} SymbolMode; + +typedef enum +{ + FULL_SEARCH = -1, + FAST_FULL_SEARCH = 0, + UM_HEX = 1, + UM_HEX_SIMPLE = 2, + EPZS = 3 +} SearchType; + + +typedef enum +{ + FRAME, + TOP_FIELD, + BOTTOM_FIELD +} PictureStructure; //!< New enum for field processing + +typedef enum +{ + P_SLICE = 0, + B_SLICE = 1, + I_SLICE = 2, + SP_SLICE = 3, + SI_SLICE = 4, + NUM_SLICE_TYPES = 5 +} SliceType; + +//Motion Estimation levels +typedef enum +{ + F_PEL, //!< Full Pel refinement + H_PEL, //!< Half Pel refinement + Q_PEL //!< Quarter Pel refinement +} MELevel; + +typedef enum +{ + FAST_ACCESS = 0, //!< Fast/safe reference access + UMV_ACCESS = 1 //!< unconstrained reference access +} REF_ACCESS_TYPE; + +typedef enum +{ + IS_LUMA = 0, + IS_CHROMA = 1 +} Component_Type; + +typedef enum +{ + RC_MODE_0 = 0, + RC_MODE_1 = 1, + RC_MODE_2 = 2, + RC_MODE_3 = 3 +} RCModeType; + + +typedef enum { + SSE = 0, + SSE_RGB = 1, + PSNR = 2, + PSNR_RGB = 3, + SSIM = 4, + SSIM_RGB = 5, + MS_SSIM = 6, + MS_SSIM_RGB = 7, + TOTAL_DIST_TYPES = 8 +} distortion_types; + +typedef enum { + WP_MCPREC_PLUS0 = 4, + WP_MCPREC_PLUS1 = 5, + WP_MCPREC_MINUS0 = 6, + WP_MCPREC_MINUS1 = 7, + WP_MCPREC_MINUS_PLUS0 = 8, + WP_REGULAR = 9 +} weighted_prediction_types; + +#endif diff --git a/lcommon/inc/vui_params.h b/lcommon/inc/vui_params.h new file mode 100644 index 0000000..366ade1 --- /dev/null +++ b/lcommon/inc/vui_params.h @@ -0,0 +1,74 @@ +/*! + ************************************************************************ + * \file vui_params.h + * + * \brief + * Input parameters related definitions + * + * \author + * + ************************************************************************ + */ + +#ifndef _VUI_PARAMS_H_ +#define _VUI_PARAMS_H_ + +// VUI Parameters +typedef struct vui_parameters +{ + int aspect_ratio_info_present_flag; + int aspect_ratio_idc; + int sar_width; + int sar_height; + int overscan_info_present_flag; + int overscan_appropriate_flag; + int video_signal_type_present_flag; + int video_format; + int video_full_range_flag; + int colour_description_present_flag; + int colour_primaries; + int transfer_characteristics; + int matrix_coefficients; + int chroma_location_info_present_flag; + int chroma_sample_loc_type_top_field; + int chroma_sample_loc_type_bottom_field; + int timing_info_present_flag; + int num_units_in_tick; + int time_scale; + int fixed_frame_rate_flag; + int nal_hrd_parameters_present_flag; + int nal_cpb_cnt_minus1; + int nal_bit_rate_scale; + int nal_cpb_size_scale; + int nal_bit_rate_value_minus1; + int nal_cpb_size_value_minus1; + int nal_vbr_cbr_flag; + int nal_initial_cpb_removal_delay_length_minus1; + int nal_cpb_removal_delay_length_minus1; + int nal_dpb_output_delay_length_minus1; + int nal_time_offset_length; + int vcl_hrd_parameters_present_flag; + int vcl_cpb_cnt_minus1; + int vcl_bit_rate_scale; + int vcl_cpb_size_scale; + int vcl_bit_rate_value_minus1; + int vcl_cpb_size_value_minus1; + int vcl_vbr_cbr_flag; + int vcl_initial_cpb_removal_delay_length_minus1; + int vcl_cpb_removal_delay_length_minus1; + int vcl_dpb_output_delay_length_minus1; + int vcl_time_offset_length; + int low_delay_hrd_flag; + int pic_struct_present_flag; + int bitstream_restriction_flag; + int motion_vectors_over_pic_boundaries_flag; + int max_bytes_per_pic_denom; + int max_bits_per_mb_denom; + int log2_max_mv_length_vertical; + int log2_max_mv_length_horizontal; + int num_reorder_frames; + int max_dec_frame_buffering; +} VUIParameters; + +#endif + diff --git a/lcommon/inc/win32.h b/lcommon/inc/win32.h new file mode 100644 index 0000000..9fc71ab --- /dev/null +++ b/lcommon/inc/win32.h @@ -0,0 +1,122 @@ + +/*! + ************************************************************************ + * \file + * win32.h + * + * \brief + * win32 definitions for H.264 codec. + * + * \author + * + ************************************************************************ + */ +#ifndef _WIN32_H_ +#define _WIN32_H_ + +# include +# include +# include +# include +# include +#include + +#if (_MSC_VER >= 1400) || defined(__INTEL_COMPILER) || (__GNUC__ >= 5) // Check OPENMP compatibility +//# define OPENMP +# define NUM_THREADS 8 +#endif + +#if defined(WIN32) || defined (WIN64) +# include +# include +# include +# include +#if (_MSC_VER < 1400) +typedef int intptr_t; +#else +# include +#endif +#if defined(OPENMP) +# include +#endif + +# define strcasecmp _strcmpi +# define strncasecmp _strnicmp + +# define snprintf _snprintf +# define open _open +# define close _close +# define read _read +# define write _write +# define lseek _lseeki64 +# define fsync _commit +# define tell _telli64 +# define TIMEB _timeb +# define TIME_T LARGE_INTEGER +# define OPENFLAGS_WRITE _O_WRONLY|_O_CREAT|_O_BINARY|_O_TRUNC +# define OPEN_PERMISSIONS _S_IREAD | _S_IWRITE +# define OPENFLAGS_READ _O_RDONLY|_O_BINARY +# define inline _inline +# define forceinline __forceinline +#else +# include +# include +# include +# include +# include +#if defined(OPENMP) +# include +#endif + +#include + +# define TIMEB timeb +# define TIME_T struct timeval +# define tell(fd) lseek(fd, 0, SEEK_CUR) +# define OPENFLAGS_WRITE O_WRONLY|O_CREAT|O_TRUNC +# define OPENFLAGS_READ O_RDONLY +# define OPEN_PERMISSIONS S_IRUSR | S_IWUSR + +# if __STDC_VERSION__ >= 199901L + /* "inline" is a keyword */ +# else +# define inline /* nothing */ +# endif +# define forceinline inline +#endif + +#if (defined(WIN32) || defined(WIN64)) && !defined(__GNUC__) +typedef __int64 int64; +typedef unsigned __int64 uint64; +# define FORMAT_OFF_T "I64d" +# ifndef INT64_MIN +# define INT64_MIN (-9223372036854775807i64 - 1i64) +# endif +#else + +#ifndef __OPENCV_CORE_TYPES_H__ +typedef int64_t int64; +//typedef long long int64; +//typedef unsigned long long uint64; +typedef uint64_t uint64; + +#endif + + +# define FORMAT_OFF_T "lld" +# ifndef INT64_MIN +# define INT64_MIN (-9223372036854775807LL - 1LL) +# endif +#endif + +//! Edited by Joao Carreira +#ifndef NULL +#define NULL 0L +#endif + +extern void gettime(TIME_T* time); +extern void init_time(void); +extern int64 timediff(TIME_T* start, TIME_T* end); +extern int64 timenorm(int64 cur_time); + +#endif diff --git a/lcommon/src/blk_prediction.c b/lcommon/src/blk_prediction.c new file mode 100644 index 0000000..340ab40 --- /dev/null +++ b/lcommon/src/blk_prediction.c @@ -0,0 +1,63 @@ +/*! + ************************************************************************************* + * \file blk_prediction.c + * + * \brief + * Block Prediction related functions + * + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * - Alexis Michael Tourapis + * + ************************************************************************************* + */ + +#include "contributors.h" + +#include +#include +#include +#include + +#include "block.h" +#include "global.h" + +#include "macroblock.h" +#include "mc_prediction.h" +#include "image.h" +#include "mb_access.h" + +void compute_residue (imgpel **curImg, imgpel **mpr, int **mb_rres, int mb_x, int opix_x, int width, int height) +{ + imgpel *imgOrg, *imgPred; + int *m7; + int i, j; + + for (j = 0; j < height; j++) + { + imgOrg = &curImg[j][opix_x]; + imgPred = &mpr[j][mb_x]; + m7 = &mb_rres[j][mb_x]; + for (i = 0; i < width; i++) + { + *m7++ = *imgOrg++ - *imgPred++; + } + } +} + +void sample_reconstruct (imgpel **curImg, imgpel **mpr, int **mb_rres, int mb_x, int opix_x, int width, int height, int max_imgpel_value, int dq_bits) +{ + imgpel *imgOrg, *imgPred; + int *m7; + int i, j; + + for (j = 0; j < height; j++) + { + imgOrg = &curImg[j][opix_x]; + imgPred = &mpr[j][mb_x]; + m7 = &mb_rres[j][mb_x]; + for (i=0;i + * \note + * In the future this module should hide the Parameters and offer only + * Functions for their access. Modules which make frequent use of some parameters + * (e.g. picture size in macroblocks) are free to buffer them on local variables. + * This will not only avoid global variable and make the code more readable, but also + * speed it up. It will also greatly facilitate future enhancements such as the + * handling of different picture sizes in the same sequence. \n + * \n + * For now, everything is just copied to the inp_par structure (gulp) + * + ************************************************************************************** + * \par Configuration File Format + ************************************************************************************** + * Format is line oriented, maximum of one parameter per line \n + * \n + * Lines have the following format: \n + * \ = \ # Comments \\n \n + * Whitespace is space and \\t + * \par + * \ are the predefined names for Parameters and are case sensitive. + * See configfile.h for the definition of those names and their mapping to + * cfgparams->values. + * \par + * \ are either integers [0..9]* or strings. + * Integers must fit into the wordlengths, signed values are generally assumed. + * Strings containing no whitespace characters can be used directly. Strings containing + * whitespace characters are to be inclosed in double quotes ("string with whitespace") + * The double quote character is forbidden (may want to implement something smarter here). + * \par + * Any Parameters whose ParameterName is undefined lead to the termination of the program + * with an error message. + * + * \par Known bug/Shortcoming: + * zero-length strings (i.e. to signal an non-existing file + * have to be coded as "". + * + * \par Rules for using command files + * \n + * All Parameters are initially taken from DEFAULTCONFIGFILENAME, defined in configfile.h. + * If an -f \ parameter is present in the command line then this file is used to + * update the defaults of DEFAULTCONFIGFILENAME. There can be more than one -f parameters + * present. If -p parameters are present then these + * override the default and the additional config file's settings, and are themselves + * overridden by future -p parameters. There must be whitespace between -f and -p commands + * and their respective parameters + *********************************************************************** + */ + +#include + +#include "global.h" +#include "configfile.h" +#include "memalloc.h" + +static int ParameterNameToMapIndex (Mapping *Map, char *s); + + +#define MAX_ITEMS_TO_PARSE 10000 + +/*! + *********************************************************************** + * \brief + * allocates memory buf, opens file Filename in f, reads contents into + * buf and returns buf + * \param Filename + * name of config file + * \return + * if successfull, content of config file + * NULL in case of error. Error message will be set in errortext + *********************************************************************** + */ +char *GetConfigFileContent (char *Filename) +{ + long FileSize; + FILE *f; + char *buf; + + if (NULL == (f = fopen (Filename, "r"))) + { + snprintf (errortext, ET_SIZE, "Cannot open configuration file %s.", Filename); + return NULL; + } + + if (0 != fseek (f, 0, SEEK_END)) + { + snprintf (errortext, ET_SIZE, "Cannot fseek in configuration file %s.", Filename); + return NULL; + } + + FileSize = ftell (f); + + if (FileSize < 0 || FileSize > 150000) + { + snprintf (errortext, ET_SIZE, "\nUnreasonable Filesize %ld reported by ftell for configuration file %s.", FileSize, Filename); + return NULL; + } + if (0 != fseek (f, 0, SEEK_SET)) + { + snprintf (errortext, ET_SIZE, "Cannot fseek in configuration file %s.", Filename); + return NULL; + } + + if ((buf = malloc (FileSize + 1))==NULL) no_mem_exit("GetConfigFileContent: buf"); + + // Note that ftell() gives us the file size as the file system sees it. The actual file size, + // as reported by fread() below will be often smaller due to CR/LF to CR conversion and/or + // control characters after the dos EOF marker in the file. + + FileSize = (long) fread (buf, 1, FileSize, f); + buf[FileSize] = '\0'; + + + fclose (f); + return buf; +} + + +/*! + *********************************************************************** + * \brief + * Parses the character array buf and writes global variable input, which is defined in + * configfile.h. This hack will continue to be necessary to facilitate the addition of + * new parameters through the Map[] mechanism (Need compiler-generated addresses in map[]). + * \param p_Inp + * InputParameters of configuration + * \param Map + * Mapping structure to specify the name and value mapping relation + * \param buf + * buffer to be parsed + * \param bufsize + * buffer size of buffer + *********************************************************************** + */ +void ParseContent (InputParameters *p_Inp, Mapping *Map, char *buf, int bufsize) +{ + char *items[MAX_ITEMS_TO_PARSE] = {NULL}; + int MapIdx; + int item = 0; + int InString = 0, InItem = 0; + char *p = buf; + char *bufend = &buf[bufsize]; + int IntContent; + double DoubleContent; + int i; + + // Stage one: Generate an argc/argv-type list in items[], without comments and whitespace. + // This is context insensitive and could be done most easily with lex(1). + + while (p < bufend) + { + switch (*p) + { + case 13: + ++p; + break; + case '#': // Found comment + *p = '\0'; // Replace '#' with '\0' in case of comment immediately following integer or string + while (*p != '\n' && p < bufend) // Skip till EOL or EOF, whichever comes first + ++p; + InString = 0; + InItem = 0; + break; + case '\n': + InItem = 0; + InString = 0; + *p++='\0'; + break; + case ' ': + case '\t': // Skip whitespace, leave state unchanged + if (InString) + p++; + else + { // Terminate non-strings once whitespace is found + *p++ = '\0'; + InItem = 0; + } + break; + + case '"': // Begin/End of String + *p++ = '\0'; + if (!InString) + { + items[item++] = p; + InItem = ~InItem; + } + else + InItem = 0; + InString = ~InString; // Toggle + break; + + default: + if (!InItem) + { + items[item++] = p; + InItem = ~InItem; + } + p++; + } + } + + item--; + + for (i=0; i (MapIdx = ParameterNameToMapIndex (Map, items[i]))) + { + //snprintf (errortext, ET_SIZE, " Parsing error in config file: Parameter Name '%s' not recognized.", items[i]); + //error (errortext, 300); + printf ("\n\tParsing error in config file: Parameter Name '%s' not recognized.", items[i]); + i -= 2 ; + continue; + } + if (strcasecmp ("=", items[i+1])) + { + snprintf (errortext, ET_SIZE, " Parsing error in config file: '=' expected as the second token in each line."); + error (errortext, 300); + } + + // Now interpret the Value, context sensitive... + + switch (Map[MapIdx].Type) + { + case 0: // Numerical + if (1 != sscanf (items[i+2], "%d", &IntContent)) + { + snprintf (errortext, ET_SIZE, " Parsing error: Expected numerical value for Parameter of %s, found '%s'.", items[i], items[i+2]); + error (errortext, 300); + } + * (int *) (Map[MapIdx].Place) = IntContent; + printf ("."); + break; + case 1: + if (items[i + 2] == NULL) + memset((char *) Map[MapIdx].Place, 0, Map[MapIdx].char_size); + else + strncpy ((char *) Map[MapIdx].Place, items [i+2], Map[MapIdx].char_size); + printf ("."); + break; + case 2: // Numerical double + if (1 != sscanf (items[i+2], "%lf", &DoubleContent)) + { + snprintf (errortext, ET_SIZE, " Parsing error: Expected numerical value for Parameter of %s, found '%s'.", items[i], items[i+2]); + error (errortext, 300); + } + * (double *) (Map[MapIdx].Place) = DoubleContent; + printf ("."); + break; + default: + error ("Unknown value type in the map definition of configfile.h",-1); + } + } + *p_Inp = cfgparams; +} + +/*! + *********************************************************************** + * \brief + * Returns the index number from Map[] for a given parameter name. + * \param Map + * Mapping structure + * \param s + * parameter name string + * \return + * the index number if the string is a valid parameter name, \n + * -1 for error + *********************************************************************** + */ +static int ParameterNameToMapIndex (Mapping *Map, char *s) +{ + int i = 0; + + while (Map[i].TokenName != NULL) + if (0==strcasecmp (Map[i].TokenName, s)) + return i; + else + i++; + return -1; +} + +/*! + *********************************************************************** + * \brief + * Sets initial values for encoding parameters. + * \return + * -1 for error + *********************************************************************** + */ +int InitParams(Mapping *Map) +{ + int i = 0; + + while (Map[i].TokenName != NULL) + { + if (Map[i].Type == 0) + * (int *) (Map[i].Place) = (int) Map[i].Default; + else if (Map[i].Type == 2) + * (double *) (Map[i].Place) = Map[i].Default; + i++; + } + return -1; +} + +/*! + *********************************************************************** + * \brief + * Validates encoding parameters. + * \return + * -1 for error + *********************************************************************** + */ +int TestParams(Mapping *Map, int bitdepth_qp_scale[3]) +{ + int i = 0; + + while (Map[i].TokenName != NULL) + { + if (Map[i].param_limits == 1) + { + if (Map[i].Type == 0) + { + if ( * (int *) (Map[i].Place) < (int) Map[i].min_limit || * (int *) (Map[i].Place) > (int) Map[i].max_limit ) + { + snprintf(errortext, ET_SIZE, "Error in input parameter %s. Check configuration file. Value should be in [%d, %d] range.", Map[i].TokenName, (int) Map[i].min_limit,(int)Map[i].max_limit ); + error (errortext, 400); + } + + } + else if (Map[i].Type == 2) + { + if ( * (double *) (Map[i].Place) < Map[i].min_limit || * (double *) (Map[i].Place) > Map[i].max_limit ) + { + snprintf(errortext, ET_SIZE, "Error in input parameter %s. Check configuration file. Value should be in [%.2f, %.2f] range.", Map[i].TokenName,Map[i].min_limit ,Map[i].max_limit ); + error (errortext, 400); + } + } + } + else if (Map[i].param_limits == 2) + { + if (Map[i].Type == 0) + { + if ( * (int *) (Map[i].Place) < (int) Map[i].min_limit ) + { + snprintf(errortext, ET_SIZE, "Error in input parameter %s. Check configuration file. Value should not be smaller than %d.", Map[i].TokenName, (int) Map[i].min_limit); + error (errortext, 400); + } + } + else if (Map[i].Type == 2) + { + if ( * (double *) (Map[i].Place) < Map[i].min_limit ) + { + snprintf(errortext, ET_SIZE, "Error in input parameter %s. Check configuration file. Value should not be smaller than %2.f.", Map[i].TokenName,Map[i].min_limit); + error (errortext, 400); + } + } + } + else if (Map[i].param_limits == 3) // Only used for QPs + { + + if (Map[i].Type == 0) + { + int cur_qp = * (int *) (Map[i].Place); + int min_qp = (int) (Map[i].min_limit - (bitdepth_qp_scale? bitdepth_qp_scale[0]: 0)); + int max_qp = (int) Map[i].max_limit; + + if (( cur_qp < min_qp ) || ( cur_qp > max_qp )) + { + snprintf(errortext, ET_SIZE, "Error in input parameter %s. Check configuration file. Value should be in [%d, %d] range.", Map[i].TokenName, min_qp, max_qp ); + error (errortext, 400); + } + } + } + + i++; + } + return -1; +} + + + +/*! + *********************************************************************** + * \brief + * Outputs encoding parameters. + * \return + * -1 for error + *********************************************************************** + */ +int DisplayParams(Mapping *Map, char *message) +{ + int i = 0; + + printf("******************************************************\n"); + printf("* %s *\n", message); + printf("******************************************************\n"); + while (Map[i].TokenName != NULL) + { + if (Map[i].Type == 0) + printf("Parameter %s = %d\n",Map[i].TokenName,* (int *) (Map[i].Place)); + else if (Map[i].Type == 1) + printf("Parameter %s = ""%s""\n",Map[i].TokenName,(char *) (Map[i].Place)); + else if (Map[i].Type == 2) + printf("Parameter %s = %.2f\n",Map[i].TokenName,* (double *) (Map[i].Place)); + i++; + } + printf("******************************************************\n"); + return i; +} + diff --git a/lcommon/src/img_io.c b/lcommon/src/img_io.c new file mode 100644 index 0000000..e021910 --- /dev/null +++ b/lcommon/src/img_io.c @@ -0,0 +1,327 @@ + +/*! + ************************************************************************************* + * \file img_io.c + * + * \brief + * image I/O related functions + * + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * - Alexis Michael Tourapis + ************************************************************************************* + */ +#include "contributors.h" +#include "global.h" +#include "img_io.h" +#include "report.h" + +static const VIDEO_SIZE VideoRes[] = { + { "qcif" , 176, 144}, + { "qqvga" , 160, 128}, + { "qvga" , 320, 240}, + { "sif" , 352, 240}, + { "cif" , 352, 288}, + { "vga" , 640, 480}, + { "sd1" , 720, 480}, + { "sd2" , 704, 576}, + { "sd3" , 720, 576}, + { "720p" , 1280, 720}, + { "1080p" , 1920, 1080}, + { NULL, 0, 0} +}; + +/*! + ************************************************************************ + * \brief + * Parse Size from from file name + * + ************************************************************************ + */ +int ParseSizeFromString (VideoDataFile *input_file, int *x_size, int *y_size, double *fps) +{ + char *p1, *p2, *tail; + char *fn = input_file->fname; + char c; + int i = 0; + + *x_size = *y_size = -1; + p1 = p2 = fn; + while (p1 != NULL && p2 != NULL) + { + // Search for first '_' + p1 = strstr( p1, "_"); + if (p1 == NULL) + break; + + // Search for end character of x_size (first 'x' after last '_') + p2 = strstr( p1, "x"); + + // If no 'x' is found, exit + if (p2 == NULL) + break; + + // Try conversion of number + *p2 = 0; + *x_size = strtol( p1 + 1, &tail, 10); + + // If there are characters left in the string, or the string is null, discard conversion + if (*tail != '\0' || *(p1 + 1) == '\0') + { + *p2 = 'x'; + p1 = tail; + continue; + } + + // Conversion was correct. Restore string + *p2 = 'x'; + + // Search for end character of y_size (first '_' or '.' after last 'x') + p1 = strpbrk( p2 + 1, "_."); + // If no '_' or '.' is found, try again from current position + if (p1 == NULL) + { + p1 = p2 + 1; + continue; + } + + // Try conversion of number + c = *p1; + *p1 = 0; + *y_size = strtol( p2 + 1, &tail, 10); + + // If there are characters left in the string, or the string is null, discard conversion + if (*tail != '\0' || *(p2 + 1) == '\0') + { + *p1 = c; + p1 = tail; + continue; + } + + // Conversion was correct. Restore string + *p1 = c; + + // Search for end character of y_size (first 'i' or 'p' after last '_') + p2 = strstr( p1 + 1, "ip"); + + // If no 'i' or 'p' is found, exit + if (p2 == NULL) + break; + + // Try conversion of number + c = *p2; + *p2 = 0; + *fps = strtod( p1 + 1, &tail); + + // If there are characters left in the string, or the string is null, discard conversion + if (*tail != '\0' || *(p1 + 1) == '\0') + { + *p2 = c; + p1 = tail; + continue; + } + + // Conversion was correct. Restore string + *p2 = c; + break; + } + + // Now lets test some common video file formats + if (p1 == NULL || p2 == NULL) + { + for (i = 0; VideoRes[i].name != NULL; i++) + { + if (strcasecmp (fn, VideoRes[i].name)) + { + *x_size = VideoRes[i].x_size; + *y_size = VideoRes[i].y_size; + // Should add frame rate support as well + break; + } + } + } + + return (*x_size == -1 || *y_size == -1) ? 0 : 1; +} + +/*! + ************************************************************************ + * \brief + * Parse Size from from file name + * + ************************************************************************ + */ +void ParseFrameNoFormatFromString (VideoDataFile *input_file) +{ + char *p1, *p2, *tail; + char *fn = input_file->fname; + char *fhead = input_file->fhead; + char *ftail = input_file->ftail; + int *zero_pad = &input_file->zero_pad; + int *num_digits = &input_file->num_digits; + + *zero_pad = 0; + *num_digits = -1; + p1 = p2 = fn; + while (p1 != NULL && p2 != NULL) + { + // Search for first '_' + p1 = strstr( p1, "%"); + if (p1 == NULL) + break; + + strncpy(fhead, fn, p1 - fn); + + // Search for end character of x_size (first 'x' after last '_') + p2 = strstr( p1, "d"); + + // If no 'x' is found, exit + if (p2 == NULL) + break; + + // Try conversion of number + *p2 = 0; + + if (*(p1 + 1) == '0') + *zero_pad = 1; + + *num_digits = strtol( p1 + 1, &tail, 10); + + // If there are characters left in the string, or the string is null, discard conversion + if (*tail != '\0' || *(p1 + 1) == '\0') + { + *p2 = 'd'; + p1 = tail; + continue; + } + + // Conversion was correct. Restore string + *p2 = 'd'; + + tail++; + strncpy(ftail, tail, (int) strlen(tail)); + break; + } + + if (input_file->vdtype == VIDEO_TIFF) + { + input_file->is_concatenated = 0; + } + else + input_file->is_concatenated = (*num_digits == -1) ? 1 : 0; +} + +/*! + ************************************************************************ + * \brief + * Open file containing a single frame + ************************************************************************ + */ +void OpenFrameFile( VideoDataFile *input_file, int FrameNumberInFile) +{ + char infile [FILE_NAME_SIZE], in_number[16]; + int length = 0; + in_number[length]='\0'; + length = (int) strlen(input_file->fhead); + strncpy(infile, input_file->fhead, length); + infile[length]='\0'; + if (input_file->zero_pad) + snprintf(in_number, 16, "%0*d", input_file->num_digits, FrameNumberInFile); + else + snprintf(in_number, 16, "%*d", input_file->num_digits, FrameNumberInFile); + + strncat(infile, in_number, sizeof(in_number)); + length += sizeof(in_number); + infile[length]='\0'; + strncat(infile, input_file->ftail, strlen(input_file->ftail)); + length += (int) strlen(input_file->ftail); + infile[length]='\0'; + + if ((input_file->f_num = open(infile, OPENFLAGS_READ)) == -1) + { + printf ("OpenFrameFile: cannot open file %s\n", infile); + report_stats_on_error(); + } +} + +/*! + ************************************************************************ + * \brief + * Open file(s) containing the entire frame sequence + ************************************************************************ + */ +void OpenFiles( VideoDataFile *input_file) +{ + if (input_file->is_concatenated == 1) + { + if ((int) strlen(input_file->fname) == 0) + { + snprintf(errortext, ET_SIZE, "No input sequence name was provided. Please check settings."); + error (errortext, 500); + } + + if ((input_file->f_num = open(input_file->fname, OPENFLAGS_READ)) == -1) + { + snprintf(errortext, ET_SIZE, "Input file %s does not exist",input_file->fname); + error (errortext, 500); + } + } +} + +/*! + ************************************************************************ + * \brief + * Close input file + ************************************************************************ + */ +void CloseFiles(VideoDataFile *input_file) +{ + if (input_file->f_num != -1) + close(input_file->f_num); + input_file->f_num = -1; +} + +/* ========================================================================== + * + * ParseVideoType + * + * ========================================================================== +*/ +VideoFileType ParseVideoType (VideoDataFile *input_file) +{ + char *format; + + format = input_file->fname + (int) strlen(input_file->fname) - 3; + + if (strcasecmp (format, "yuv") == 0) + { + input_file->vdtype = VIDEO_YUV; + input_file->format.yuv_format = YUV420; + input_file->avi = NULL; + } + else if (strcasecmp (format, "rgb") == 0) + { + input_file->vdtype = VIDEO_RGB; + input_file->format.yuv_format = YUV444; + input_file->avi = NULL; + } + else if (strcasecmp (format, "tif") == 0) + { + input_file->vdtype = VIDEO_TIFF; + input_file->avi = NULL; + } + else if (strcasecmp (format, "avi") == 0) + { + input_file->vdtype = VIDEO_AVI; + } + else + { + //snprintf(errortext, ET_SIZE, "ERROR: video file format not supported"); + //error (errortext, 500); + input_file->vdtype = VIDEO_YUV; + input_file->format.yuv_format = YUV420; + input_file->avi = NULL; + } + + return input_file->vdtype; +} diff --git a/lcommon/src/img_process.c b/lcommon/src/img_process.c new file mode 100644 index 0000000..1e7cad1 --- /dev/null +++ b/lcommon/src/img_process.c @@ -0,0 +1,272 @@ +/*! +************************************************************************************* +* \file img_process.c +* +* \brief +* Input data Image Processing functions +* +* \author +* Main contributors (see contributors.h for copyright, address and affiliation details) +* - Alexis Michael Tourapis +* +************************************************************************************* +*/ + +#include "contributors.h" +#include "global.h" +#include "img_process.h" +#include "io_image.h" +#include "memalloc.h" +#include "fast_memory.h" + +static inline void ResetImage(ImageData *imgOut) +{ + fast_memset(imgOut->frm_data[0][0], 0, imgOut->format.height[0] * imgOut->format.width[0] * sizeof (imgpel)); + + if (imgOut->format.yuv_format != YUV400) + { + if (sizeof(imgpel) == sizeof(char)) + { + fast_memset(imgOut->frm_data[1][0], 128, imgOut->format.height[1] * imgOut->format.width[1] * sizeof (imgpel)); + fast_memset(imgOut->frm_data[2][0], 128, imgOut->format.height[2] * imgOut->format.width[2] * sizeof (imgpel)); + } + else + { + int i, j, k; + imgpel med_value; + for (k = 1; k <=2; k++) + { + med_value = (imgpel) (imgOut->format.max_value[k] + 1) >> 1; + for (j = 0; j < imgOut->format.height[k]; j++) + { + for (i = 0; i < imgOut->format.width[k]; i++) + { + imgOut->frm_data[k][j][i] = med_value; + } + } + } + } + } +} + + +static inline void CPImage(ImageData *imgOut, ImageData *imgIn) +{ + memcpy(imgOut->frm_data[0][0], imgIn->frm_data[0][0], imgIn->format.height[0] * imgIn->format.width[0] * sizeof (imgpel)); + + if (imgIn->format.yuv_format != YUV400) + { + memcpy(imgOut->frm_data[1][0], imgIn->frm_data[1][0], imgIn->format.height[1] * imgIn->format.width[1] * sizeof (imgpel)); + memcpy(imgOut->frm_data[2][0], imgIn->frm_data[2][0], imgIn->format.height[2] * imgIn->format.width[2] * sizeof (imgpel)); + } +} + +// to be modified +static inline void FilterImage(ImageData *imgOut, ImageData *imgIn) +{ + memcpy(imgOut->frm_data[0][0], imgIn->frm_data[0][0], imgIn->format.height[0] * imgIn->format.width[0] * sizeof (imgpel)); + + if (imgIn->format.yuv_format != YUV400) + { + memcpy(imgOut->frm_data[1][0], imgIn->frm_data[1][0], imgIn->format.height[1] * imgIn->format.width[1] * sizeof (imgpel)); + memcpy(imgOut->frm_data[2][0], imgIn->frm_data[2][0], imgIn->format.height[2] * imgIn->format.width[2] * sizeof (imgpel)); + } +} + +// Line interleaving for 3:2 pulldown +static inline void BlendImageLines(ImageData *imgIn0, ImageData *imgIn1) +{ + int j; + for (j = 1; j < imgIn1->format.height[0]; j += 2) + memcpy(imgIn0->frm_data[0][j], imgIn1->frm_data[0][j], imgIn1->format.width[0] * sizeof (imgpel)); + + if (imgIn1->format.yuv_format != YUV400) + { + for (j = 1; j < imgIn1->format.height[1]; j += 2) + { + memcpy(imgIn0->frm_data[1][j], imgIn1->frm_data[1][j], imgIn1->format.width[1] * sizeof (imgpel)); + } + for (j = 1; j < imgIn1->format.height[2]; j += 2) + { + memcpy(imgIn0->frm_data[2][j], imgIn1->frm_data[2][j], imgIn1->format.width[2] * sizeof (imgpel)); + } + } +} + +// to be modified +static inline void FilterImageSep(ImageData *imgOut, ImageData *imgIn) +{ + int i, j; + static const int SepFilter[6] = {1, -5, 20, 20, -5, 1}; + int max_width = imgOut->format.width[0] - 1; + int max_height = imgOut->format.height[0] - 1; + + int **temp_data = new_mem2Dint(imgIn->format.height[0], imgIn->format.width[0]); // temp memory for filtering. Could be allocated once to speed up code + imgpel *frm_data = NULL; + + // implementation was not optimized. only just implemented as proof of concept + // horizontal filtering + for (j = 0; j < imgOut->format.height[0]; j++) + { + frm_data = imgIn->frm_data[0][j]; + for (i = 0; i < imgOut->format.width[0]; i++) + { + temp_data[j][i] = + SepFilter[0] * frm_data[iClip3(0, max_width, i - 2)] + + SepFilter[1] * frm_data[iClip3(0, max_width, i - 1)] + + SepFilter[2] * frm_data[iClip3(0, max_width, i )] + + SepFilter[3] * frm_data[iClip3(0, max_width, i + 1)] + + SepFilter[4] * frm_data[iClip3(0, max_width, i + 2)] + + SepFilter[5] * frm_data[iClip3(0, max_width, i + 3)]; + } + } + + for (j = 0; j < imgOut->format.height[0]; j++) + { + frm_data = imgOut->frm_data[0][j]; + for (i = 0; i < imgOut->format.width[0]; i++) + { + frm_data[i] = (imgpel) iClip3(0, imgOut->format.max_value[0], rshift_rnd_sign( + SepFilter[0] * temp_data[iClip3(0, max_height, j - 2)][i] + + SepFilter[1] * temp_data[iClip3(0, max_height, j - 1)][i] + + SepFilter[2] * temp_data[iClip3(0, max_height, j )][i] + + SepFilter[3] * temp_data[iClip3(0, max_height, j + 1)][i] + + SepFilter[4] * temp_data[iClip3(0, max_height, j + 2)][i] + + SepFilter[5] * temp_data[iClip3(0, max_height, j + 3)][i], 10)); + } + } + + if (imgOut->format.yuv_format != YUV400) + { + int k; + + for (k = 1; k <=2; k++) + { + max_width = imgOut->format.width[k] - 1; + max_height = imgOut->format.height[k] - 1; + + // horizontal filtering + for (j = 0; j < imgOut->format.height[k]; j++) + { + frm_data = imgIn->frm_data[k][j]; + for (i = 0; i < imgOut->format.width[k]; i++) + { + temp_data[j][i] = + SepFilter[0] * frm_data[iClip3(0, max_width, i - 2)] + + SepFilter[1] * frm_data[iClip3(0, max_width, i - 1)] + + SepFilter[2] * frm_data[iClip3(0, max_width, i )] + + SepFilter[3] * frm_data[iClip3(0, max_width, i + 1)] + + SepFilter[4] * frm_data[iClip3(0, max_width, i + 2)] + + SepFilter[5] * frm_data[iClip3(0, max_width, i + 3)]; + } + } + + for (j = 0; j < imgOut->format.height[k]; j++) + { + frm_data = imgOut->frm_data[k][j]; + for (i = 0; i < imgOut->format.width[k]; i++) + { + frm_data[i] = (imgpel) iClip3(0, imgOut->format.max_value[k], rshift_rnd_sign( + SepFilter[0] * temp_data[iClip3(0, max_height, j - 2)][i] + + SepFilter[1] * temp_data[iClip3(0, max_height, j - 1)][i] + + SepFilter[2] * temp_data[iClip3(0, max_height, j )][i] + + SepFilter[3] * temp_data[iClip3(0, max_height, j + 1)][i] + + SepFilter[4] * temp_data[iClip3(0, max_height, j + 2)][i] + + SepFilter[5] * temp_data[iClip3(0, max_height, j + 3)][i], 10)); + } + } + } + } + + free_mem2Dint(temp_data); +} + + +// to be modified +static inline void MuxImages(ImageData *imgOut, ImageData *imgIn0, ImageData *imgIn1, ImageData *Map) +{ + int i, j; + for (j = 0; j < imgOut->format.height[0]; j++) + { + for (i = 0; i < imgOut->format.width[0]; i++) + { + imgOut->frm_data[0][j][i] = (imgpel) rshift_rnd_sf(imgIn0->frm_data[0][j][i] * (Map->format.max_value[0] - Map->frm_data[0][j][i]) + imgIn1->frm_data[0][j][i] * Map->frm_data[0][j][i], Map->format.bit_depth[0]); + } + } + + if (imgOut->format.yuv_format != YUV400) + { + int k; + for (k = 1; k <=2; k++) + { + for (j = 0; j < imgOut->format.height[k]; j++) + { + for (i = 0; i < imgOut->format.width[k]; i++) + { + imgOut->frm_data[k][j][i] = (imgpel) rshift_rnd_sf(imgIn0->frm_data[k][j][i] * (Map->format.max_value[k] - Map->frm_data[k][j][i]) + imgIn1->frm_data[k][j][i] * Map->frm_data[k][j][i], Map->format.bit_depth[k]); + } + } + } + } +} + +static inline void YV12toYUV(ImageData *imgOut, ImageData *imgIn) +{ + memcpy(imgOut->frm_data[0][0], imgIn->frm_data[0][0], imgIn->format.height[0] * imgIn->format.width[0] * sizeof (imgpel)); + + if (imgIn->format.yuv_format != YUV400) + { + memcpy(imgOut->frm_data[1][0], imgIn->frm_data[2][0], imgIn->format.height[1] * imgIn->format.width[1] * sizeof (imgpel)); + memcpy(imgOut->frm_data[2][0], imgIn->frm_data[1][0], imgIn->format.height[2] * imgIn->format.width[2] * sizeof (imgpel)); + } +} + +void process_image( VideoParameters *p_Vid, InputParameters *p_Inp ) +{ + switch( p_Inp->ProcessInput ) + { + default: + case 0: + CPImage(&p_Vid->imgData, &p_Vid->imgData0); + if (p_Inp->enable_32_pulldown) + BlendImageLines(&p_Vid->imgData, &p_Vid->imgData4); + break; + case 1: + FilterImage(&p_Vid->imgData, &p_Vid->imgData0); + if (p_Inp->enable_32_pulldown) + { + FilterImage(&p_Vid->imgData32, &p_Vid->imgData4); + BlendImageLines(&p_Vid->imgData, &p_Vid->imgData32); + } + break; + case 2: + YV12toYUV(&p_Vid->imgData, &p_Vid->imgData0); + if (p_Inp->enable_32_pulldown) + { + YV12toYUV(&p_Vid->imgData32, &p_Vid->imgData4); + BlendImageLines(&p_Vid->imgData, &p_Vid->imgData32); + } + break; + case 3: + MuxImages(&p_Vid->imgData, &p_Vid->imgData0, &p_Vid->imgData1, &p_Vid->imgData2); + if (p_Inp->enable_32_pulldown) + { + MuxImages(&p_Vid->imgData32, &p_Vid->imgData4, &p_Vid->imgData5, &p_Vid->imgData6); + BlendImageLines(&p_Vid->imgData, &p_Vid->imgData32); + } + + break; + case 4: + FilterImageSep(&p_Vid->imgData, &p_Vid->imgData0); + if (p_Inp->enable_32_pulldown) + { + FilterImageSep(&p_Vid->imgData, &p_Vid->imgData4); + BlendImageLines(&p_Vid->imgData, &p_Vid->imgData32); + } + + break; + } +} + + + diff --git a/lcommon/src/input.c b/lcommon/src/input.c new file mode 100644 index 0000000..b805589 --- /dev/null +++ b/lcommon/src/input.c @@ -0,0 +1,924 @@ + +/*! + ************************************************************************************* + * \file input.c + * + * \brief + * Input related functions + * + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * - Karsten Suehring + * - Alexis Michael Tourapis + * + ************************************************************************************* + */ +#include "contributors.h" + +#include +#include + +#include "global.h" +#include "defines.h" +#include "input.h" +#include "img_io.h" +#include "memalloc.h" +#include "fast_memory.h" + +void buf2img_basic ( imgpel** imgX, unsigned char* buf, int size_x, int size_y, int o_size_x, int o_size_y, int symbol_size_in_bytes, int bitshift); +void buf2img_endian ( imgpel** imgX, unsigned char* buf, int size_x, int size_y, int o_size_x, int o_size_y, int symbol_size_in_bytes, int bitshift); +void buf2img_bitshift ( imgpel** imgX, unsigned char* buf, int size_x, int size_y, int o_size_x, int o_size_y, int symbol_size_in_bytes, int bitshift); +void fillPlane ( imgpel** imgX, int nVal, int size_x, int size_y); + +/*! + ************************************************************************ + * \brief + * checks if the System is big- or little-endian + * \return + * 0, little-endian (e.g. Intel architectures) + * 1, big-endian (e.g. SPARC, MIPS, PowerPC) + ************************************************************************ + */ +void initInput(VideoParameters *p_Vid, FrameFormat *source, FrameFormat *output) +{ + if (source->bit_depth[0] == output->bit_depth[0] && source->bit_depth[1] == output->bit_depth[1]) + { + if (( sizeof(char) != sizeof (imgpel)) && testEndian()) + p_Vid->buf2img = buf2img_endian; + else + p_Vid->buf2img = buf2img_basic; + } + else + p_Vid->buf2img = buf2img_bitshift; +} + + +/*! + ************************************************************************ + * \brief + * checks if the System is big- or little-endian + * \return + * 0, little-endian (e.g. Intel architectures) + * 1, big-endian (e.g. SPARC, MIPS, PowerPC) + ************************************************************************ + */ +int testEndian(void) +{ + short s; + byte *p; + + p=(byte*)&s; + + s=1; + + return (*p==0); +} + +#if (DEBUG_BITDEPTH) +/*! + ************************************************************************ + * \brief + * Masking to ensure data within appropriate range + ************************************************************************ + */ +static void MaskMSBs (imgpel** imgX, int mask, int width, int height) +{ + int i,j; + + for (j=0; j < height; j++) + { + for (i=0; i < width; i++) + { + imgX[j][i]=(imgpel) (imgX[j][i] & mask); + } + } +} +#endif + +/*! + ************************************************************************ + * \brief + * Fill plane with constant value + ************************************************************************ + */ +void fillPlane ( imgpel** imgX, //!< Pointer to image plane + int nVal, //!< Fill value (currently 0 <= nVal < 256) + int size_x, //!< horizontal size of picture + int size_y //!< vertical size of picture + ) +{ + int j, i; + + if (sizeof(imgpel) == sizeof(char)) + { + fast_memset(imgX[0], nVal, size_y * size_x); + } + else + { + for (j = 0; j < size_y; j++) + { + for (i = 0; i < size_x; i++) + { + imgX[j][i] = (imgpel) nVal; + } + } + } +} + +static void deinterleave_yuv420( unsigned char** input, //!< input buffer + unsigned char** output, //!< output buffer + FrameFormat *source, //!< format of source buffer + int symbol_size_in_bytes //!< number of bytes per symbol + ) +{ + int i; + // original buffer + unsigned char *icmp = *input; + // final buffer + unsigned char *ocmp0 = *output; + + unsigned char *ocmp1 = ocmp0 + symbol_size_in_bytes * source->size_cmp[Y_COMP]; + unsigned char *ocmp2 = ocmp1 + symbol_size_in_bytes * source->size_cmp[U_COMP]; + + for (i = 0; i < source->size_cmp[U_COMP]; i++) + { + memcpy(ocmp1, icmp, symbol_size_in_bytes); + ocmp1 += symbol_size_in_bytes; + icmp += symbol_size_in_bytes; + memcpy(ocmp0, icmp, 2 * symbol_size_in_bytes); + ocmp0 += 2 * symbol_size_in_bytes; + icmp += 2 * symbol_size_in_bytes; + memcpy(ocmp2, icmp, symbol_size_in_bytes); + ocmp2 += symbol_size_in_bytes; + icmp += symbol_size_in_bytes; + memcpy(ocmp0, icmp, 2 * symbol_size_in_bytes); + ocmp0 += 2 * symbol_size_in_bytes; + icmp += 2 * symbol_size_in_bytes; + } + + // flip buffers + icmp = *input; + *input = *output; + *output = icmp; +} + +static void deinterleave_yuv444( unsigned char** input, //!< input buffer + unsigned char** output, //!< output buffer + FrameFormat *source, //!< format of source buffer + int symbol_size_in_bytes //!< number of bytes per symbol + ) +{ + int i; + // original buffer + unsigned char *icmp = *input; + // final buffer + unsigned char *ocmp0 = *output; + + unsigned char *ocmp1 = ocmp0 + symbol_size_in_bytes * source->size_cmp[Y_COMP]; + unsigned char *ocmp2 = ocmp1 + symbol_size_in_bytes * source->size_cmp[U_COMP]; + + for (i = 0; i < source->size_cmp[Y_COMP]; i++) + { + memcpy(ocmp0, icmp, symbol_size_in_bytes); + ocmp0 += symbol_size_in_bytes; + icmp += symbol_size_in_bytes; + memcpy(ocmp1, icmp, symbol_size_in_bytes); + ocmp1 += symbol_size_in_bytes; + icmp += symbol_size_in_bytes; + memcpy(ocmp2, icmp, symbol_size_in_bytes); + ocmp2 += symbol_size_in_bytes; + icmp += symbol_size_in_bytes; + } + // flip buffers + icmp = *input; + *input = *output; + *output = icmp; +} + +static void deinterleave_yuyv ( unsigned char** input, //!< input buffer + unsigned char** output, //!< output buffer + FrameFormat *source, //!< format of source buffer + int symbol_size_in_bytes //!< number of bytes per symbol + ) +{ + int i; + // original buffer + unsigned char *icmp = *input; + // final buffer + unsigned char *ocmp0 = *output; + + unsigned char *ocmp1 = ocmp0 + symbol_size_in_bytes * source->size_cmp[Y_COMP]; + unsigned char *ocmp2 = ocmp1 + symbol_size_in_bytes * source->size_cmp[U_COMP]; + + for (i = 0; i < source->size_cmp[U_COMP]; i++) + { + // Y + memcpy(ocmp0, icmp, symbol_size_in_bytes); + ocmp0 += symbol_size_in_bytes; + icmp += symbol_size_in_bytes; + // U + memcpy(ocmp1, icmp, symbol_size_in_bytes); + ocmp1 += symbol_size_in_bytes; + icmp += symbol_size_in_bytes; + // Y + memcpy(ocmp0, icmp, symbol_size_in_bytes); + ocmp0 += symbol_size_in_bytes; + icmp += symbol_size_in_bytes; + // V + memcpy(ocmp2, icmp, symbol_size_in_bytes); + ocmp2 += symbol_size_in_bytes; + icmp += symbol_size_in_bytes; + } + // flip buffers + icmp = *input; + *input = *output; + *output = icmp; +} + +static void deinterleave_yvyu ( unsigned char** input, //!< input buffer + unsigned char** output, //!< output buffer + FrameFormat *source, //!< format of source buffer + int symbol_size_in_bytes //!< number of bytes per symbol + ) +{ + int i; + // original buffer + unsigned char *icmp = *input; + // final buffer + unsigned char *ocmp0 = *output; + + unsigned char *ocmp1 = ocmp0 + symbol_size_in_bytes * source->size_cmp[Y_COMP]; + unsigned char *ocmp2 = ocmp1 + symbol_size_in_bytes * source->size_cmp[U_COMP]; + + for (i = 0; i < source->size_cmp[U_COMP]; i++) + { + // Y + memcpy(ocmp0, icmp, symbol_size_in_bytes); + ocmp0 += symbol_size_in_bytes; + icmp += symbol_size_in_bytes; + // V + memcpy(ocmp2, icmp, symbol_size_in_bytes); + ocmp2 += symbol_size_in_bytes; + icmp += symbol_size_in_bytes; + // Y + memcpy(ocmp0, icmp, symbol_size_in_bytes); + ocmp0 += symbol_size_in_bytes; + icmp += symbol_size_in_bytes; + // U + memcpy(ocmp1, icmp, symbol_size_in_bytes); + ocmp1 += symbol_size_in_bytes; + icmp += symbol_size_in_bytes; + } + // flip buffers + icmp = *input; + *input = *output; + *output = icmp; +} + +static void deinterleave_uyvy ( unsigned char** input, //!< input buffer + unsigned char** output, //!< output buffer + FrameFormat *source, //!< format of source buffer + int symbol_size_in_bytes //!< number of bytes per symbol + ) +{ + int i; + // original buffer + unsigned char *icmp = *input; + // final buffer + unsigned char *ocmp0 = *output; + + unsigned char *ocmp1 = ocmp0 + symbol_size_in_bytes * source->size_cmp[Y_COMP]; + unsigned char *ocmp2 = ocmp1 + symbol_size_in_bytes * source->size_cmp[U_COMP]; + + for (i = 0; i < source->size_cmp[U_COMP]; i++) + { + // U + memcpy(ocmp1, icmp, symbol_size_in_bytes); + ocmp1 += symbol_size_in_bytes; + icmp += symbol_size_in_bytes; + // Y + memcpy(ocmp0, icmp, symbol_size_in_bytes); + ocmp0 += symbol_size_in_bytes; + icmp += symbol_size_in_bytes; + // V + memcpy(ocmp2, icmp, symbol_size_in_bytes); + ocmp2 += symbol_size_in_bytes; + icmp += symbol_size_in_bytes; + // Y + memcpy(ocmp0, icmp, symbol_size_in_bytes); + ocmp0 += symbol_size_in_bytes; + icmp += symbol_size_in_bytes; + } + // flip buffers + icmp = *input; + *input = *output; + *output = icmp; +} + +static void deinterleave_v210 ( unsigned char** input, //!< input buffer + unsigned char** output, //!< output buffer + FrameFormat *source, //!< format of source buffer + int symbol_size_in_bytes //!< number of bytes per symbol + ) +{ + int i; + // original buffer + unsigned char *icmp = *input; + + unsigned int *ui32cmp = (unsigned int *) *input; + unsigned short *ui16cmp0 = (unsigned short *) *output; + unsigned short *ui16cmp1 = ui16cmp0 + source->size_cmp[0]; + unsigned short *ui16cmp2 = ui16cmp1 + source->size_cmp[1]; + + for (i = 0; i < source->size_cmp[U_COMP] / 3; i++) + { + // Byte 3 Byte 2 Byte 1 Byte 0 + // Cr 0 Y 0 Cb 0 + // X X 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 + *ui16cmp2 = (*ui32cmp & 0x3FF00000)>>20; // Cr 0 + *ui16cmp0 = (*ui32cmp & 0xffc00)>>10; // Y 0 + *ui16cmp1 = (*(ui32cmp++) & 0x3FF); // Cb 0 + + // Byte 7 Byte 6 Byte 5 Byte 4 + // Y 2 Cb 1 Y 1 + // X X 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 + *(ui16cmp0 + 2) = (*ui32cmp & 0x3FF00000)>>20; // Y 2 + *(ui16cmp1 + 1) = (*ui32cmp & 0xffc00)>>10; // Cb 1 + *(ui16cmp0 + 1) = (*(ui32cmp++) & 0x3FF); // Y 1 + + // Byte 11 Byte 10 Byte 9 Byte 8 + // Cb 2 Y 3 Cr 1 + // X X 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 + *(ui16cmp1 + 2) = (*ui32cmp & 0x3FF00000)>>20; // Cb 2 + *(ui16cmp0 + 3) = (*ui32cmp & 0xffc00)>>10; // Y 3 + *(ui16cmp2 + 1) = (*(ui32cmp++) & 0x3FF); // Cr 1 + + // Byte 15 Byte 14 Byte 13 Byte 12 + // Y 5 Cr 2 Y 4 + // X X 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 + *(ui16cmp0 + 5) = (*ui32cmp & 0x3FF00000)>>20; // Y 2 + *(ui16cmp2 + 2) = (*ui32cmp & 0xffc00)>>10; // Cb 1 + *(ui16cmp0 + 4) = (*(ui32cmp++) & 0x3FF); // Y 1 + + //printf("luma: %d %d %d %d %d %d\n", *(ui16cmp0 + 0), *(ui16cmp0 + 1), *(ui16cmp0 + 2), *(ui16cmp0 + 3), *(ui16cmp0 + 4), *(ui16cmp0 + 5)); + + ui16cmp0 += 6; + ui16cmp1 += 3; + ui16cmp2 += 3; + } + // flip buffers + icmp = *input; + *input = *output; + *output = icmp; +} + +/*! + ************************************************************************ + * \brief + * Deinterleave file read buffer to source picture structure + ************************************************************************ + */ +static void deinterleave ( unsigned char** input, //!< input buffer + unsigned char** output, //!< output buffer + FrameFormat *source, //!< format of source buffer + int symbol_size_in_bytes //!< number of bytes per symbol + ) +{ + if (source->yuv_format == YUV420) + { // UYYVYY + deinterleave_yuv420(input, output, source, symbol_size_in_bytes); + } + else if (source->yuv_format == YUV422) + { + if (source->pixel_format == YUYV || source->pixel_format == YUY2) + { + deinterleave_yuyv(input, output, source, symbol_size_in_bytes); + } + else if (source->pixel_format == YVYU) + { + deinterleave_yvyu(input, output, source, symbol_size_in_bytes); + } + else if (source->pixel_format == UYVY) + { + deinterleave_uyvy(input, output, source, symbol_size_in_bytes); + } + else if (source->pixel_format == V210) + { + deinterleave_v210(input, output, source, symbol_size_in_bytes); + } + else { + printf( "Unsupported pixel format.\n"); + exit(EXIT_FAILURE); + } + } + else if (source->yuv_format == YUV444) + deinterleave_yuv444(input, output, source, symbol_size_in_bytes); +} + +/*! + ************************************************************************ + * \brief + * Convert file read buffer to source picture structure + ************************************************************************ + */ +void buf2img_bitshift ( imgpel** imgX, //!< Pointer to image plane + unsigned char* buf, //!< Buffer for file output + int size_x, //!< horizontal size of picture + int size_y, //!< vertical size of picture + int o_size_x, //!< horizontal size of picture + int o_size_y, //!< vertical size of picture + int symbol_size_in_bytes, //!< number of bytes in file used for one pixel + int bitshift //!< variable for bitdepth expansion + ) +{ + int i,j; + + uint16 tmp16, ui16; + unsigned long tmp32, ui32; + + // This test should be done once. + if (((symbol_size_in_bytes << 3) - bitshift) > (sizeof(imgpel)<< 3)) + { + error ("Source picture has higher bit depth than imgpel data type. \nPlease recompile with larger data type for imgpel.", 500); + } + + if (testEndian()) + { + if (size_x != o_size_x || size_y != o_size_y) + { + error ("Rescaling not supported in big endian architectures. ", 500); + } + + // big endian + switch (symbol_size_in_bytes) + { + case 1: + { + for(j = 0; j < o_size_y; j++) + for(i = 0; i < o_size_x; i++) + { + imgX[j][i]= (imgpel) rshift_rnd(buf[i + j*size_x], bitshift); + } + break; + } + case 2: + { + for(j = 0; j < o_size_y; j++) + for(i = 0; i < o_size_x; i++) + { + memcpy(&tmp16, buf + ((i + j * size_x) * 2), 2); + ui16 = (tmp16 >> 8) | ((tmp16 & 0xFF) << 8); + imgX[j][i] = (imgpel) rshift_rnd(ui16, bitshift); + } + break; + } + case 4: + { + for(j = 0; j < o_size_y; j++) + for(i = 0; i < o_size_x; i++) + { + memcpy(&tmp32, buf + ((i + j * size_x) * 4), 4); + ui32 = ((tmp32 & 0xFF00) << 8) | ((tmp32 & 0xFF)<<24) | ((tmp32 & 0xFF0000)>>8) | ((tmp32 & 0xFF000000)>>24); + imgX[j][i] = (imgpel) rshift_rnd(ui32, bitshift); + } + } + default: + { + error ("reading only from formats of 8, 16 or 32 bit allowed on big endian architecture", 500); + break; + } + } + } + else + { + // little endian + int j_pos; + if (size_x == o_size_x && size_y == o_size_y) + { + for (j = 0; j < o_size_y; j++) + { + j_pos = j*size_x; + for (i = 0; i < o_size_x; i++) + { + ui16=0; + memcpy(&(ui16), buf + ((i + j_pos) * symbol_size_in_bytes), symbol_size_in_bytes); + imgX[j][i] = (imgpel) rshift_rnd(ui16,bitshift); + } + } + } + else + { + int iminwidth = imin(size_x, o_size_x); + int iminheight = imin(size_y, o_size_y); + int dst_offset_x = 0, dst_offset_y = 0; + int offset_x = 0, offset_y = 0; // currently not used + + // determine whether we need to center the copied frame or crop it + if ( o_size_x >= size_x ) + dst_offset_x = ( o_size_x - size_x ) >> 1; + + if (o_size_y >= size_y) + dst_offset_y = ( o_size_y - size_y ) >> 1; + + // check copied area to avoid copying memory garbage + // source + iminwidth = ( (offset_x + iminwidth ) > size_x ) ? (size_x - offset_x) : iminwidth; + iminheight = ( (offset_y + iminheight) > size_y ) ? (size_y - offset_y) : iminheight; + // destination + iminwidth = ( (dst_offset_x + iminwidth ) > o_size_x ) ? (o_size_x - dst_offset_x) : iminwidth; + iminheight = ( (dst_offset_y + iminheight) > o_size_y ) ? (o_size_y - dst_offset_y) : iminheight; + + for (j=0; j < iminheight; j++) + { + j_pos = (j + offset_y) * size_x + offset_x; + for (i=0; i < iminwidth; i++) + { + ui16=0; + memcpy(&(ui16), buf + ((i + j_pos) * symbol_size_in_bytes), symbol_size_in_bytes); + imgX[j + dst_offset_y][i + dst_offset_x] = (imgpel) rshift_rnd(ui16,bitshift); + } + } + } + } +} + + +/*! + ************************************************************************ + * \brief + * Convert file read buffer to source picture structure + ************************************************************************ + */ +void buf2img_basic (imgpel** imgX, //!< Pointer to image plane + unsigned char* buf, //!< Buffer for file output + int size_x, //!< horizontal size of picture + int size_y, //!< vertical size of picture + int o_size_x, //!< horizontal size of picture + int o_size_y, //!< vertical size of picture + int symbol_size_in_bytes, //!< number of bytes in file used for one pixel + int dummy //!< dummy variable used for allowing function pointer use + ) +{ + int i,j; + unsigned char* temp_buf = buf; + + if (symbol_size_in_bytes> sizeof(imgpel)) + { + error ("Source picture has higher bit depth than imgpel data type. \nPlease recompile with larger data type for imgpel.", 500); + } + + if (( sizeof (imgpel) == symbol_size_in_bytes)) + { + // imgpel == pixel_in_file -> simple copy + if (size_x == o_size_x && size_y == o_size_y) + memcpy(&imgX[0][0], temp_buf, size_x * size_y * sizeof(imgpel)); + else + { + int iminwidth = imin(size_x, o_size_x); + int iminheight = imin(size_y, o_size_y); + int dst_offset_x = 0, dst_offset_y = 0; + int offset_x = 0, offset_y = 0; // currently not used + + // determine whether we need to center the copied frame or crop it + if ( o_size_x >= size_x ) + dst_offset_x = ( o_size_x - size_x ) >> 1; + + if (o_size_y >= size_y) + dst_offset_y = ( o_size_y - size_y ) >> 1; + + // check copied area to avoid copying memory garbage + // source + iminwidth = ( (offset_x + iminwidth ) > size_x ) ? (size_x - offset_x) : iminwidth; + iminheight = ( (offset_y + iminheight) > size_y ) ? (size_y - offset_y) : iminheight; + // destination + iminwidth = ( (dst_offset_x + iminwidth ) > o_size_x ) ? (o_size_x - dst_offset_x) : iminwidth; + iminheight = ( (dst_offset_y + iminheight) > o_size_y ) ? (o_size_y - dst_offset_y) : iminheight; + + for (i=0; i= size_x ) + dst_offset_x = ( o_size_x - size_x ) >> 1; + + if (o_size_y >= size_y) + dst_offset_y = ( o_size_y - size_y ) >> 1; + + // check copied area to avoid copying memory garbage + // source + iminwidth = ( (offset_x + iminwidth ) > size_x ) ? (size_x - offset_x) : iminwidth; + iminheight = ( (offset_y + iminheight) > size_y ) ? (size_y - offset_y) : iminheight; + // destination + iminwidth = ( (dst_offset_x + iminwidth ) > o_size_x ) ? (o_size_x - dst_offset_x) : iminwidth; + iminheight = ( (dst_offset_y + iminheight) > o_size_y ) ? (o_size_y - dst_offset_y) : iminheight; + + for (j = 0; j < iminheight; j++) + { + memcpy(&imgX[j + dst_offset_y][dst_offset_x], &(temp_buf[(j + offset_y) * size_x + offset_x]), iminwidth * symbol_size_in_bytes); + } + for (j=0; j < iminheight; j++) + { + j_pos = (j + offset_y) * size_x + offset_x; + for (i=0; i < iminwidth; i++) + { + ui16 = 0; + memcpy(&(ui16), buf + ((i + j_pos) * symbol_size_in_bytes), symbol_size_in_bytes); + imgX[j + dst_offset_y][i + dst_offset_x]= (imgpel) ui16; + } + } + } + } +} + +/*! + ************************************************************************ + * \brief + * Convert file read buffer to source picture structure + ************************************************************************ + */ +void buf2img_endian (imgpel** imgX, //!< Pointer to image plane + unsigned char* buf, //!< Buffer for file output + int size_x, //!< horizontal size of picture + int size_y, //!< vertical size of picture + int o_size_x, //!< horizontal size of picture + int o_size_y, //!< vertical size of picture + int symbol_size_in_bytes, //!< number of bytes in file used for one pixel + int dummy //!< dummy variable used for allowing function pointer use + ) +{ + int i,j; + + uint16 tmp16, ui16; + unsigned long tmp32, ui32; + + if (symbol_size_in_bytes > sizeof(imgpel)) + { + error ("Source picture has higher bit depth than imgpel data type. \nPlease recompile with larger data type for imgpel.", 500); + } + + if (size_x != o_size_x || size_y != o_size_y) + { + error ("Rescaling not supported in big endian architectures. ", 500); + } + + // big endian + switch (symbol_size_in_bytes) + { + case 1: + { + for(j=0;j> 8) | ((tmp16&0xFF)<<8); + imgX[j][i] = (imgpel) ui16; + } + } + break; + } + case 4: + { + for(j=0;j>8) | ((tmp32&0xFF000000)>>24); + imgX[j][i] = (imgpel) ui32; + } + } + break; + } + default: + { + error ("reading only from formats of 8, 16 or 32 bit allowed on big endian architecture", 500); + break; + } + } +} + +/*! + ************************************************************************ + * \brief + * Create Frame Memory buffer + * + ************************************************************************ + */ +void AllocateFrameMemory (VideoParameters *p_Vid, InputParameters *p_Inp, FrameFormat *source) +{ + // Note that size seems to be ok even for v210 formats (wasteful yes, but should not + // create any issues with how we manage that format. + if (NULL == (p_Vid->buf = malloc (source->size * source->pic_unit_size_shift3))) + no_mem_exit("AllocateFrameMemory: p_Vid->buf"); + if (p_Inp->input_file1.is_interleaved) + { + if (NULL == (p_Vid->ibuf = malloc (source->size * source->pic_unit_size_shift3))) + no_mem_exit("AllocateFrameMemory: p_Vid->ibuf"); + } +} + +/*! + ************************************************************************ + * \brief + * Delete Frame Memory buffer + * + ************************************************************************ + */ +void DeleteFrameMemory (VideoParameters *p_Vid) +{ + if (p_Vid->buf != NULL) + free (p_Vid->buf); + if (p_Vid->ibuf != NULL) + free (p_Vid->ibuf); +} + +/*! + ************************************************************************ + * \brief + * Reads one new frame from file + * + * \param input_file + * structure containing information (filename, format) about the source file + * \param FrameNoInFile + * Frame number in the source file + * \param HeaderSize + * Number of bytes in the source file to be skipped + * \param source + * source file (on disk) information + * \param output + * output file (for encoding) information + * \param pImage + * Image planes + ************************************************************************ + */ +int read_one_frame (VideoParameters *p_Vid, VideoDataFile *input_file, int FrameNoInFile, int HeaderSize, FrameFormat *source, FrameFormat *output, imgpel **pImage[3]) +{ + InputParameters *p_Inp = p_Vid->p_Inp; + int file_read = 0; + unsigned int symbol_size_in_bytes = source->pic_unit_size_shift3; + + const int bytes_y = source->size_cmp[0] * symbol_size_in_bytes; + const int bytes_uv = source->size_cmp[1] * symbol_size_in_bytes; + int bit_scale; + + Boolean rgb_input = (Boolean) (source->color_model == CM_RGB && source->yuv_format == YUV444); + + if (input_file->is_concatenated == 0) + { + if (input_file->vdtype == VIDEO_TIFF) + { + file_read = ReadTIFFImage (p_Inp, input_file, FrameNoInFile, source, p_Vid->buf); + } + else + { + file_read = ReadFrameSeparate (p_Inp, input_file, FrameNoInFile, HeaderSize, source, p_Vid->buf); + } + } + else + { + file_read = ReadFrameConcatenated (p_Inp, input_file, FrameNoInFile, HeaderSize, source, p_Vid->buf); + } + + if ( !file_read ) + { + return 0; + } + + // De-interleave input source + if (input_file->is_interleaved) + { + deinterleave ( &p_Vid->buf, &p_Vid->ibuf, source, symbol_size_in_bytes); + } + + bit_scale = source->bit_depth[0] - output->bit_depth[0]; + + if(rgb_input) + p_Vid->buf2img(pImage[0], p_Vid->buf + bytes_y, source->width[0], source->height[0], output->width[0], output->height[0], symbol_size_in_bytes, bit_scale); + else + p_Vid->buf2img(pImage[0], p_Vid->buf, source->width[0], source->height[0], output->width[0], output->height[0], symbol_size_in_bytes, bit_scale); + +#if (DEBUG_BITDEPTH) + MaskMSBs(pImage[0], ((1 << output->bit_depth[0]) - 1), output->width[0], output->height[0]); +#endif + + if (p_Vid->yuv_format != YUV400) + { + bit_scale = source->bit_depth[1] - output->bit_depth[1]; +#if (ALLOW_GRAYSCALE) + if (!p_Inp->grayscale) +#endif + { + if(rgb_input) + p_Vid->buf2img(pImage[1], p_Vid->buf + bytes_y + bytes_uv, source->width[1], source->height[1], output->width[1], output->height[1], symbol_size_in_bytes, bit_scale); + else + p_Vid->buf2img(pImage[1], p_Vid->buf + bytes_y, source->width[1], source->height[1], output->width[1], output->height[1], symbol_size_in_bytes, bit_scale); + + bit_scale = source->bit_depth[2] - output->bit_depth[2]; + if(rgb_input) + p_Vid->buf2img(pImage[2], p_Vid->buf, source->width[1], source->height[1], output->width[1], output->height[1], symbol_size_in_bytes, bit_scale); + else + p_Vid->buf2img(pImage[2], p_Vid->buf + bytes_y + bytes_uv, source->width[1], source->height[1], output->width[1], output->height[1], symbol_size_in_bytes, bit_scale); + } +#if (DEBUG_BITDEPTH) + MaskMSBs(pImage[1], ((1 << output->bit_depth[1]) - 1), output->width[1], output->height[1]); + MaskMSBs(pImage[2], ((1 << output->bit_depth[2]) - 1), output->width[1], output->height[1]); +#endif + } + + return file_read; +} + +/*! + ************************************************************************ + * \brief + * Padding of automatically added border for picture sizes that are not + * multiples of macroblock/macroblock pair size + * + * \param output + * Image dimensions + * \param img_size_x + * coded image horizontal size (luma) + * \param img_size_y + * code image vertical size (luma) + * \param img_size_x_cr + * coded image horizontal size (chroma) + * \param img_size_y_cr + * code image vertical size (chroma) + * \param pImage + * image planes + ************************************************************************ + */ +void pad_borders (FrameFormat output, int img_size_x, int img_size_y, int img_size_x_cr, int img_size_y_cr, imgpel **pImage[3]) +{ + int x, y; + + // Luma or 1st component + // padding right border + if (output.width[0] < img_size_x) + for (y = 0; y < output.height[0]; y++) + for (x = output.width[0]; x < img_size_x; x++) + pImage[0][y][x] = pImage[0][y][x-1]; + + // padding bottom border + if (output.height[0] < img_size_y) + for (y = output.height[0]; y + * + ************************************************************************************* + */ +#include "contributors.h" + +#include "global.h" +#include "img_io.h" + +#define FAST_READ 1 + +#if FAST_READ +static inline int ReadData (int vfile, FrameFormat *source, unsigned char *buf) +{ + unsigned char *cur_buf = buf; + int read_size = source->pic_unit_size_shift3 * source->width[0]; + int i, j; + for (i = 0; i < source->height[0]; i++) + { + if (read(vfile, cur_buf, read_size) != read_size) + { + printf ("read_one_frame: cannot read %d bytes from input file, unexpected EOF!\n", source->width[0]); + return 0; + } + cur_buf += read_size; + } + + if (source->yuv_format != YUV400) + { + read_size = source->pic_unit_size_shift3 * source->width[1]; + for (j = 0; j < 2; j++) + { + for (i = 0; i < source->height[1]; i++) + { + if (read(vfile, cur_buf, read_size) != read_size) + { + printf ("read_one_frame: cannot read %d bytes from input file, unexpected EOF!\n", source->width[1]); + return 0; + } + cur_buf += read_size; + } + } + } + return 1; +} +#else +static inline int ReadData (int vfile, int framesize_in_bytes, unsigned char *buf) +{ + if (read(vfile, buf, (int) framesize_in_bytes) != (int) framesize_in_bytes) + { + printf ("read_one_frame: cannot read %d bytes from input file, unexpected EOF!\n", (int) framesize_in_bytes); + return 0; + } + else + { + return 1; + } +} +#endif + +static inline int ReadDataV210 (int vfile, int framesize_in_bytes, unsigned char *buf) +{ + if (read(vfile, buf, (int) framesize_in_bytes) != (int) framesize_in_bytes) + { + printf ("read_one_frame: cannot read %d bytes from input file, unexpected EOF!\n", (int) framesize_in_bytes); + return 0; + } + else + { + return 1; + } +} + + +/*! + ************************************************************************ + * \brief + * Reads one new frame from concatenated raw file + * Code does not distinguish between planar and interleaved data + * + * \param input_file + * Input file to read from + * \param FrameNoInFile + * Frame number in the source file + * \param HeaderSize + * Number of bytes in the source file to be skipped + * \param source + * source file (on disk) information + * \param buf + * image buffer data + ************************************************************************ + */ +int ReadFrameConcatenated (InputParameters *p_Inp, VideoDataFile *input_file, int FrameNoInFile, int HeaderSize, FrameFormat *source, unsigned char *buf) +{ + int file_read = 0; + int vfile = input_file->f_num; + Boolean is_v210 = (input_file->is_interleaved && source->pixel_format == V210); + unsigned int symbol_size_in_bytes = source->pic_unit_size_shift3; + + const int bytes_y = source->size_cmp[0] * symbol_size_in_bytes; + const int bytes_uv = source->size_cmp[1] * symbol_size_in_bytes; + + const int64 framesize_in_bytes = (is_v210 == TRUE) ? 16 * (source->size_cmp[0] / 6) : bytes_y + 2 * bytes_uv; + + // Let us seek directly to the current frame + if (lseek (vfile, HeaderSize + framesize_in_bytes * (FrameNoInFile + p_Inp->start_frame), SEEK_SET) == -1) + { + snprintf(errortext, ET_SIZE, "read_one_frame: cannot advance file pointer in input file beyond frame %d\n", p_Inp->start_frame + FrameNoInFile); + error (errortext,-1); + } + + // Here we are at the correct position for the source frame in the file. + // Now read it. + if ((source->pic_unit_size_on_disk & 0x07) == 0) + { + if (is_v210 == TRUE) + { + file_read = ReadDataV210(vfile, (int) framesize_in_bytes, buf); + } + else + { +#if FAST_READ + file_read = ReadData (vfile, source, buf); +#else + file_read = ReadData (vfile, (int) framesize_in_bytes, buf); +#endif + } + } + else + { + printf ("read_one_frame (NOT IMPLEMENTED): pic unit size on disk must be divisible by 8"); + exit (-1); + } + return file_read; +} + +/*! + ************************************************************************ + * \brief + * Reads one new frame from separate data files + * Code does not distinguish between planar and interleaved data + * + * \param input_file + * Input file to read from + * \param FrameNoInFile + * Frame number in the source file + * \param HeaderSize + * Number of bytes in the source file to be skipped + * \param source + * source file (on disk) information + * \param buf + * taget buffer + ************************************************************************ + */ +int ReadFrameSeparate (InputParameters *p_Inp, VideoDataFile *input_file, int FrameNoInFile, int HeaderSize, FrameFormat *source, unsigned char *buf) +{ + int file_read = 0; + int vfile = input_file->f_num; + + OpenFrameFile( input_file, FrameNoInFile + p_Inp->start_frame); + + // skip Header + if (lseek (vfile, HeaderSize, SEEK_SET) != HeaderSize) + { + error ("read_one_frame: cannot fseek to (Header size) in input file", -1); + } + + // Read data + if ((source->pic_unit_size_on_disk & 0x07) == 0) + { +#if FAST_READ + file_read = ReadData (vfile, source, buf); +#else + unsigned int symbol_size_in_bytes = source->pic_unit_size_shift3; + + const int bytes_y = source->size_cmp[0] * symbol_size_in_bytes; + const int bytes_uv = source->size_cmp[1] * symbol_size_in_bytes; + const int64 framesize_in_bytes = bytes_y + 2*bytes_uv; + + file_read = ReadData (vfile, (int) framesize_in_bytes, buf); +#endif + } + else + { + printf ("read_one_frame (NOT IMPLEMENTED): pic unit size on disk must be divisible by 8"); + exit (-1); + } + + if (vfile != -1) + close(vfile); + + return file_read; +} diff --git a/lcommon/src/io_tiff.c b/lcommon/src/io_tiff.c new file mode 100644 index 0000000..72331e7 --- /dev/null +++ b/lcommon/src/io_tiff.c @@ -0,0 +1,1153 @@ +/*! + ************************************************************************************* + * \file io_tiff.c + * + * \brief + * I/O functions related to TIFF images + * + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * - Larry Luther + * - Alexis Michael Tourapis + * + ************************************************************************************* + */ +#include "contributors.h" +#include "report.h" +#include "io_tiff.h" + + +// Maximum number of rows in image +#define YRES 1080 + +// Order of member variables is critical + +//! TIFF Field Type Code +typedef enum TiffType { + T_BYTE = 1, //!< 8-bit unsigned integer. + T_ASCII = 2, //!< 8-bit byte that contains a 7-bit ASCII code; the last byte must be NUL (binary zero). + T_SHORT = 3, //!< 16-bit (2-byte) unsigned integer. + T_LONG = 4, //!< 32-bit (4-byte) unsigned integer. + T_RATIONAL = 5, //!< Two LONGs: the first represents the numerator of a fraction; the second, the denominator. + T_SBYTE = 6, //!< An 8-bit signed (twos-complement) integer. + T_UNDEFINED = 7, //!< An 8-bit byte that may contain anything, depending on the definition of the field. + T_SSHORT = 8, //!< A 16-bit (2-byte) signed (twos-complement) integer. + T_SLONG = 9, //!< A 32-bit (4-byte) signed (twos-complement) integer. + T_SRATIONAL = 10, //!< Two SLONG’s: the first represents the numerator of a fraction, the second the denominator. + T_FLOAT = 11, //!< Single precision (4-byte) IEEE format. + T_DOUBLE = 12 //!< Double precision (8-byte) IEEE format. +} TiffType; + + +typedef struct TiffDirectoryEntry { + uint16 tag; //!< The tag that identifies the field. + uint16 type; //!< The field type. + uint32 count; //!< Number of values of the indicated type. + uint32 offset; //!< Value or offset. +} TiffDirectoryEntry; + + +//! TIFF Image File Directory +typedef struct TiffIFD { + uint16 nEntries; + TiffDirectoryEntry *directoryEntry; +} TiffIFD; + + +typedef struct TiffImageFileHeader { + uint16 byteOrder; //!< "II" (4949H) or "MM" (4D4DH) + uint16 arbitraryNumber; //!< 42 + uint32 offset; //!< Offset of the 0th IFD +} TiffImageFileHeader; + + +//! TIFF file data +typedef struct Tiff { + uint16 *img; //!< Image data + uint8 *fileInMemory; //!< The file will be read into memory in one gulp here. + uint8 *mp; //!< Memory pointer. + int le; //!< Little endian - 0 false, 1 - true + int nStrips; + TiffImageFileHeader ifh; + // Information from TAGs + uint16 Orientation; + uint32 + BitsPerSample[3], + RowsPerStrip, + ImageLength, + ImageWidth, + StripByteCounts[YRES], + StripOffsets[YRES], + XResolution[2], + YResolution[2]; + + uint32 (*getU16) (struct Tiff *); + uint32 (*getU32) (struct Tiff *); +} Tiff; + +//! Video codes for RGB ==> YUV conversions +typedef enum VideoCode { + VC_NULL = 0, + VC_ITU_REC709 = 1, + VC_CCIR_601 = 3, + VC_FCC = 4, + VC_ITU_REC624BG = 5, + VC_SMPTE_170M = 6, + VC_SMPTE_240M = 7, + VC_SMPTE_260M = 7, + VC_ITU_REC709_EXACT = 8, + VC_MAX = 8 +} VideoCode; + + +static const double Coef[VC_MAX+1][3] = { + {0.299 , 0.587 , 0.114}, // 0 unspecified + {0.2126 , 0.7152 , 0.0722}, // 1 SMPTE RP-177 & 274M, ITU-R Rec. 709 + {0.299 , 0.587 , 0.114}, // 2 unspecified + {0.299 , 0.587 , 0.114}, // 3 CCIR 601 ITU-R BT.601 / SMPTE 125M + {0.30 , 0.59 , 0.11}, // 4 FCC + {0.299 , 0.587 , 0.114}, // 5 ITU-R Rec. 624-4 System B, G + {0.299 , 0.587 , 0.114}, // 6 SMPTE 170M + {0.212 , 0.701 , 0.087}, // 7 SMPTE 240M (1987) 260M + {0.212639, 0.715169, 0.072192} // 8 SMPTE RP-177 & 274M, ITU-R Rec. 709 exact +}; + +#define CR Coef[videoCode][0] +#define CG Coef[videoCode][1] +#define CB Coef[videoCode][2] +#define CU (0.5/(CB-1.0)) +#define CV (0.5/(CR-1.0)) + + +#define stdScaleY 219.0 // nominal range: 16..235 +#define stdScaleUV 224.0 // nominal range: 16..240 + +#define INTEGER_SCALE 16384 +#define INTEGER_SHIFT 14 + + +//! Parameters for RGB ==> YUV conversions. +typedef struct RGB_YUV { + uint16 + pixMax; //!< Maximum pixel value. + int + stdRange, //!< 0 = full range, 1 = std range + y, u, v, + r, g, b, + yr, yg, yb, + ur, ug, ub, + vr, vg, vb, + offy, offuv; + double + sy, suv; +} RGB_YUV; + +/*! + ************************************************************************ + * \brief + * Initialize pointer variables, since they are maintained across multiple calls to ReadTIFFImage + * + ************************************************************************ + */ +void constructTiff (Tiff * t) +{ + t->fileInMemory = 0; + t->img = 0; + t->mp = 0; +} + + +/*! + ************************************************************************ + * \brief + * free allocated memory + * + ************************************************************************ + */ +void destructTiff (Tiff * t) +{ + free_pointer( t->fileInMemory); + free_pointer( t->img); +} + + +// No swap versions + +/*! + ************************************************************************ + * \brief + * Get an unsigned short without swapping. + * + ************************************************************************ + */ +static uint32 getU16 (Tiff * t) +{ + union { + uint8 in[2]; + uint16 out; + } u; + u.in[0] = *t->mp++; + u.in[1] = *t->mp++; + return u.out; +} + + +/*! + ************************************************************************ + * \brief + * Get an unsigned long without swapping. + * + ************************************************************************ + */ +static uint32 getU32 (Tiff * t) +{ + union { + uint8 in[4]; + uint32 out; + } u; + u.in[0] = *t->mp++; + u.in[1] = *t->mp++; + u.in[2] = *t->mp++; + u.in[3] = *t->mp++; + return u.out; +} + + +// Swap versions + +/*! + ************************************************************************ + * \brief + * Get an unsigned short and swap. + * + ************************************************************************ + */ +static uint32 getSwappedU16 (Tiff * t) +{ + union { + uint8 in[2]; + uint16 out; + } u; + u.in[1] = *t->mp++; + u.in[0] = *t->mp++; + return u.out; +} + + +/*! + ************************************************************************ + * \brief + * Get an unsigned long and swap. + * + ************************************************************************ + */ +static uint32 getSwappedU32 (Tiff * t) +{ + union { + uint8 in[4]; + uint32 out; + } u; + u.in[3] = *t->mp++; + u.in[2] = *t->mp++; + u.in[1] = *t->mp++; + u.in[0] = *t->mp++; + return u.out; +} + + +/*! + ************************************************************************ + * \brief + * Get an array of (uint16|uint32|rational). + * + ************************************************************************ + */ +static void getIntArray (Tiff * t, uint32 offset, TiffType type, uint32 a[], int n) +{ + int i; + uint8 *mp = t->mp; // save memory pointer + + t->mp = t->fileInMemory + offset; + switch (type) + { + case T_SHORT: + for (i=0; i < n; ++i) + { + a[i] = getU16( t); + } + break; + case T_LONG: + for (i=0; i < n; ++i) + { + a[i] = getU32( t); + } + break; + case T_RATIONAL: + for (i=0; i < 2*n; ++i) + { + a[i] = getU32( t); + } + break; + default: + assert( type == T_SHORT || type == T_LONG || type == T_RATIONAL); + } + t->mp = mp; // restore memory pointer +} + + +/*! + ************************************************************************ + * \brief + * Read DirectoryEntry and store important results in 't'. + * + ************************************************************************ + */ +static int readDirectoryEntry (Tiff * t) +{ + uint32 tag = t->getU16( t); + TiffType type = (TiffType) t->getU16( t); + uint32 count = t->getU32( t); + uint32 offset = t->getU32( t); + + switch (tag) + { + case 256: // ImageWidth SHORT or LONG + assert( count == 1); + //printf( "256: ImageWidth = %u\n", offset); + t->ImageWidth = offset; + break; + case 257: // ImageLength SHORT or LONG + assert( count == 1); + //printf( "257: ImageLength = %u\n", offset); + t->ImageLength = offset; + if (offset > YRES) + { + fprintf( stdout, "readDirectoryEntry: ImageLength (%d) exceeds builtin maximum of %d\n", offset, YRES); + return 1; + } + break; + case 258: // BitsPerSample SHORT 8,8,8 + if (count != 3) + { + fprintf( stdout, "BitsPerSample (only [3] supported)\n"); + return 1; + } + getIntArray( t, offset, type, t->BitsPerSample, 3); + //printf( "258: BitsPerSample[%d] = %u,%u,%u\n", count, t->BitsPerSample[0], t->BitsPerSample[1], t->BitsPerSample[2]); + if (t->BitsPerSample[0] != t->BitsPerSample[1] || t->BitsPerSample[0] != t->BitsPerSample[2]) + { + fprintf( stdout, "BitsPerSample must be the same for all samples\n"); + return 1; + } + if (t->BitsPerSample[0] != 8 && t->BitsPerSample[0] != 16) + { + fprintf( stdout, "Only 8 or 16 BitsPerSample is supported\n"); + return 1; + } + break; + case 259: // Compression SHORT 1 or 32773 + assert( count == 1); + //printf( "259: Compression = %u\n", offset); + if (offset != 1) + { + fprintf( stdout, "Only uncompressed TIFF files supported\n"); + return 1; + } + break; + case 262: // PhotometricInterpretation SHORT 2 + assert( count == 1); + //printf( "262: PhotometricInterpretation = %u\n", offset); + assert( offset == 2); + break; + case 273: // StripOffsets SHORT or LONG + //printf( "273: StripOffsets[%d] = %u\n", count, offset); + getIntArray( t, offset, type, t->StripOffsets, count); + t->nStrips = count; + break; + case 274: // Orientation SHORT + assert( count == 1); + //printf( "274: Orientation = %u\n", offset); + t->Orientation = (uint16) offset; + if (t->Orientation != 1) + { + fprintf( stdout, "Only Orientation 1 is supported\n"); + return 1; + } + break; + case 277: // SamplesPerPixel SHORT 3 or more + assert( count == 1); + //printf( "277: SamplesPerPixel = %u\n", offset); + assert( offset == 3); + break; + case 278: // RowsPerStrip SHORT or LONG + assert( count == 1); + //printf( "278: RowsPerStrip = %u\n", offset); + t->RowsPerStrip = offset; + break; + case 279: // StripByteCounts LONG or SHORT + //printf( "279: StripByteCounts[%u] = %u\n", count, offset); + getIntArray( t, offset, type, t->StripByteCounts, count); + break; + case 282: // XResolution RATIONAL + assert( count == 1); + getIntArray( t, offset, type, t->XResolution, 1); + //printf( "282: XResolution = %u/%u\n", offset, t->XResolution[0], t->XResolution[1]); + break; + case 283: // YResolution RATIONAL + assert( count == 1); + getIntArray( t, offset, type, t->YResolution, 1); + //printf( "283: YResolution = %u/%u\n", offset, t->YResolution[0], t->YResolution[1]); + break; + case 284: // PlanarConfiguration SHORT + assert( count == 1); + //printf( "284: PlanarConfiguration = %u\n", offset); + assert( offset == 1); + break; + case 296: // ResolutionUnit SHORT 1, 2 or 3 + //printf( "296: ResolutionUnit = %u\n", offset); + assert( count == 1); + break; + case 305: // Software ASCII + //printf( "305: Software = %s\n", t->fileInMemory + offset); + break; + case 339: // SampleFormat SHORT 1 + default: + //printf( "%3d: Unforseen = %u\n", tag, offset); + ; + } + return 0; +} + + +/*! + ************************************************************************ + * \brief + * Read file named 'path' into memory buffer 't->fileInMemory'. + * + * \return + * 0 if successful + ************************************************************************ + */ +static int readFileIntoMemory (Tiff * t, const char * path) +{ + long + cnt, result; + uint16 + byteOrder; + int + endian = 1, + machineLittleEndian = (*( (char *)(&endian) ) == 1) ? 1 : 0, + fd; + + assert( t); + assert( path); + + fd = open( path, OPENFLAGS_READ); + if (fd == -1) + { + fprintf( stdout, "Couldn't open to read: %s\n", path); + return 1; + } + + cnt = (long) lseek( fd, 0, SEEK_END); // TIFF files by definition cannot exceed 2^32 + if (cnt == -1L) + return 1; + + if (lseek( fd, 0, SEEK_SET) == -1L) // reposition file at beginning + return 1; + + t->fileInMemory = (uint8 *) realloc( t->fileInMemory, cnt); + if (t->fileInMemory == 0) + { + close( fd); + return 1; + } + + result = (long) read( fd, t->fileInMemory, cnt); + if (result != cnt) + { + close( fd); + return 1; + } + + close( fd); + + byteOrder = (t->fileInMemory[0] << 8) | t->fileInMemory[1]; + switch (byteOrder) + { + case 0x4949: // little endian file + t->le = 1; + //printf( "Little endian file\n"); + break; + case 0x4D4D: // big endian file + t->le = 0; + //printf( "Big endian file\n"); + break; + default: + fprintf( stdout, "First two bytes indicate: Not a TIFF file\n"); + free_pointer( t->fileInMemory); + return 1; + } + if (t->le == machineLittleEndian) // endianness of machine matches file + { + t->getU16 = getU16; + t->getU32 = getU32; + } + else // endianness of machine does not match file + { + t->getU16 = getSwappedU16; + t->getU32 = getSwappedU32; + } + t->mp = t->fileInMemory; + return 0; +} + +/*! + ************************************************************************ + * \brief + * Read image data into 't->img'. + * + ************************************************************************ + */ +static int readImageData (Tiff * t) +{ + int i, j, n; + uint8 *mp, *s; + uint16 *p; + uint32 size; + + assert( t); + + size = t->ImageWidth * t->ImageLength * 3 * sizeof(uint16); + t->img = (uint16 *) realloc( t->img, size); + if (t->img == 0) + return 1; + + switch (t->BitsPerSample[0]) + { + case 8: + p = t->img; + for (i=0; i < t->nStrips; ++i) + { + n = t->StripByteCounts[i]; + s = t->fileInMemory + t->StripOffsets[i]; + for (j=0; j < n; ++j) + { + *p++ = *s++; + } + } + break; + case 16: + mp = t->mp; // save memory pointer + p = t->img; + for (i=0; i < t->nStrips; ++i) + { + n = t->StripByteCounts[i] / 2; + t->mp = t->fileInMemory + t->StripOffsets[i]; + for (j=0; j < n; ++j) + { + *p++ = (uint16) getU16( t); + } + } + t->mp = mp; // restore memory pointer + break; + } + return 0; +} + + +/*! + ***************************************************************************** + * \brief + * Read the ImageFileDirectory. + * + ***************************************************************************** +*/ +static int readImageFileDirectory (Tiff * t) +{ + uint32 i; + uint32 nEntries = t->getU16( t); + + for (i=0; i < nEntries; ++i) + { + readDirectoryEntry( t); + } + return 0; +} + + +/*! + ***************************************************************************** + * \brief + * Read the ImageFileHeader. + * + ***************************************************************************** +*/ +static int readImageFileHeader (Tiff * t) +{ + t->ifh.byteOrder = (uint16) getU16( t); + t->ifh.arbitraryNumber = (uint16) getU16( t); + t->ifh.offset = getU32( t); + if (t->ifh.arbitraryNumber != 42) + { + fprintf( stdout, "ImageFileHeader.arbitrary != 42\n"); + return 1; + } + t->mp = t->fileInMemory + t->ifh.offset; + return 0; +} + +/*! + ***************************************************************************** + * \brief + * Read the TIFF file named 'path' into 't'. + * + ***************************************************************************** +*/ +static int readTiff (Tiff * t, char * path) { + assert( t); + assert( path); + + if (readFileIntoMemory( t, path)) + goto Error; + if (readImageFileHeader( t)) + goto Error; + if (readImageFileDirectory( t)) + goto Error; + if (readImageData( t)) + goto Error; + return 0; + +Error: + free_pointer( t->fileInMemory); + free_pointer( t->img); + return 1; +} + +/*! + ***************************************************************************** + * \brief + * Initialize RGB ==> YUV conversion factors + * + ***************************************************************************** +*/ +static int RGB_YUV_initialize (RGB_YUV * T, + VideoCode videoCode, //!< <-- Describes color space. + int stdRange, //!< <-- 1 = Standard range, 0 = Full range + uint16 pixMax //!< <-- Maximum component value. + ) +{ + int i, pixScale; + + switch (videoCode) + { + case VC_ITU_REC709: + case VC_CCIR_601: + break; + default: + fprintf( stdout, "RGB_YUV_initialize: Unsupported videoCode (%d)\n", videoCode); + return 1; + } + + T->stdRange = stdRange; + T->pixMax = pixMax; + + pixScale = (int)pixMax + ((int)pixMax & 1); + + if (stdRange) + { + T->offy = (int)(INTEGER_SCALE * (pixScale * 16 / 256.0 + 0.5)); // setup + rounding + T->sy = INTEGER_SCALE * stdScaleY / 255.0; + T->suv = INTEGER_SCALE * stdScaleUV / 255.0; + } + else // full range + { + T->offy = (int)(INTEGER_SCALE * 0.5); // rounding + T->sy = INTEGER_SCALE; + T->suv = INTEGER_SCALE; + } + T->offuv = (int)(INTEGER_SCALE * (0.5 * pixScale + 0.5)); // offset + rounding + + // IMPORTANT NOTE: sign of 0.5 for rounding must agree with sign of CU & CV + T->yr = (int)(T-> sy*CR +0.5); T->yg = (int)(T-> sy*CG +0.5); T->yb = (int)(T-> sy*CB +0.5); + T->ur = (int)(T->suv*CR*CU-0.5); T->ug = (int)(T->suv*CG*CU-0.5); T->ub = (int)(T->suv*0.5 +0.5); + T->vr = (int)(T->suv*0.5 +0.5); T->vg = (int)(T->suv*CG*CV-0.5); T->vb = (int)(T->suv*CB*CV-0.5); + + i = (unsigned int)(T->sy + 0.5); + if (T->yr+T->yg+T->yb != i) + { + fprintf( stdout, "ERROR: RGB_YUV_initialize: yr+yg+yb=%d sy=%u\n", T->yr+T->yg+T->yb, i); + return 1; + } + if (T->ur+T->ug+T->ub) + { + fprintf( stdout, "ERROR: RGB_YUV_initialize: ur+ug+ub=%d\n", T->ur+T->ug+T->ub); + return 1; + } + if (T->vr+T->vg+T->vb) + { + fprintf( stdout, "ERROR: RGB_YUV_initialize: vr+vg+vb=%d\n", T->vr+T->vg+T->vb); + return 1; + } + + return 0; +} + +/*! + ***************************************************************************** + * \brief + * Convert interleaved/planar RGB components to interleaved/planar YUV components + * + ***************************************************************************** +*/ +static void RGB_YUV_rgb_to_yuv (RGB_YUV * T, + uint16 *rp, //!< <-- red components + uint16 *gp, //!< <-- green components + uint16 *bp, //!< <-- blue components + int xres, //!< <-- number of pixels in a row + int yres, //!< <-- number of rows + int rgb_stride, //!< <-- stride to next R,G,B component + uint16 *yp, //!< --> Y components + uint16 *up, //!< --> U components + uint16 *vp, //!< --> V components + int yuv_stride //!< <-- stride to next Y,U,V component + ) +{ + int i; + int count = xres * yres; + + if (T->stdRange) // clipping not needed + { + for (i=0; i < count; ++i) + { + T->r = (int) *rp; rp += rgb_stride; + T->g = (int) *gp; gp += rgb_stride; + T->b = (int) *bp; bp += rgb_stride; + // convert to YUV + *yp = (uint16)((T->yr*T->r + T->yg*T->g + T->yb*T->b + T-> offy) >> INTEGER_SHIFT); yp += yuv_stride; + *up = (uint16)((T->ur*T->r + T->ug*T->g + T->ub*T->b + T->offuv) >> INTEGER_SHIFT); up += yuv_stride; + *vp = (uint16)((T->vr*T->r + T->vg*T->g + T->vb*T->b + T->offuv) >> INTEGER_SHIFT); vp += yuv_stride; + } + } + else // full range -- need to clip + { + for (i=0; i < count; ++i) + { + T->r = (int) *rp; rp += rgb_stride; + T->g = (int) *gp; gp += rgb_stride; + T->b = (int) *bp; bp += rgb_stride; + + // convert to YUV + // right shift of neg value is wrong, but will get clipped so o.k. + T->y = (T->yr*T->r + T->yg*T->g + T->yb*T->b + T-> offy) >> INTEGER_SHIFT; + if (T->y < 0) + T->y = 0; + else if (T->y > T->pixMax) + T->y = (int) T->pixMax; + *yp = (uint16) T->y; yp += yuv_stride; + + T->u = (T->ur*T->r + T->ug*T->g + T->ub*T->b + T->offuv) >> INTEGER_SHIFT; + if (T->u < 0) + T->u = 0; + else if (T->u > T->pixMax) + T->u = (int) T->pixMax; + *up = (uint16) T->u; up += yuv_stride; + + T->v = (T->vr*T->r + T->vg*T->g + T->vb*T->b + T->offuv) >> INTEGER_SHIFT; + if (T->v < 0) + T->v = 0; + else if (T->v > T->pixMax) + T->v = (int) T->pixMax; + *vp = (uint16) T->v; vp += yuv_stride; + } + } // clipping +} + + +/*! + ***************************************************************************** + * \brief FIR horizontal filter and 2:1 subsampling (with no phase shift) + * + * \note + * FILTER SPECIFICATION FILE\n + * FILTER TYPE:HALF BAND 10H\n + * STOPBAND RIPPLE IN -dB -.4000E+02\n + * PASSBAND CUTOFF FREQUENCY 0.170000E+03 HERTZ\n + * SAMPLING FREQUENCY 0.100000E+04 HERTZ\n + * number of taps in decimal 17 (normalized & rounded in kevin_filter.xls)\n + * Window Kaiser *4096 *8192\n + * 0.000000000000000000E+00 coefficient of tap 0\n + * 0.274349475697109923E-02 coefficient of tap 1 11 22\n + * 0.000000000000000000E+00 coefficient of tap 2\n + * 0.116164603817880207E-01 coefficient of tap 3 47 94\n + * 0.000000000000000000E+00 coefficient of tap 4\n + * -.645752746253402865E-01 coefficient of tap 5 -262 -524\n + * 0.000000000000000000E+00 coefficient of tap 6\n + * 0.302322754093924128E+00 coefficient of tap 7 1228 2456\n + * 0.500000000000000000E+00 coefficient of tap 8 2048\n + * 0.302322754093924128E+00 coefficient of tap 9\n + * 0.000000000000000000E+00 coefficient of tap 10\n + * -.645752746253402865E-01 coefficient of tap 11\n + * 0.000000000000000000E+00 coefficient of tap 12\n + * 0.116164603817880207E-01 coefficient of tap 13\n + * 0.000000000000000000E+00 coefficient of tap 14\n + * 0.274349475697109923E-02 coefficient of tap 15\n + * 0.000000000000000000E+00 coefficient of tap 16\n + ***************************************************************************** +*/ +void horizontal_half_1chan_cosite (uint16 *srcPtr, + int srcXres, + int yres, + int srcZres, + uint16 *dstPtr, + int dstZres, + int pixMax //!< needed for clipping + ) +{ + int x, y, limit, n7, n5, n3, n1; + int result; + uint16 *src = srcPtr; + uint16 *dst = dstPtr; + + for (y=0; y < yres; y++) + { + for (x=0; x < 8; x+=2) + { + n1 = (x >= 1) ? 1 : x; + n3 = (x >= 3) ? 3 : x; + n5 = (x >= 5) ? 5 : x; + n7 = (x >= 7) ? 7 : x; + result = ( 2048* *(src) + + 1228*( *(src-n1*srcZres) + *(src+ srcZres) ) + - 262*( *(src-n3*srcZres) + *(src+3*srcZres) ) + + 47*( *(src-n5*srcZres) + *(src+5*srcZres) ) + + 11*( *(src-n7*srcZres) + *(src+7*srcZres) )+2048) / 4096; + if (result < 0) + result = 0; + else if (result > pixMax) + result = pixMax; + *dst = (uint16) result; + dst += dstZres; + src += 2*srcZres; + } + + limit = srcXres - 8; + for (x=8; x < limit; x+=2) + { + result = ( 2048* *(src) + + 1228*( *(src- srcZres) + *(src+ srcZres) ) + - 262*( *(src-3*srcZres) + *(src+3*srcZres) ) + + 47*( *(src-5*srcZres) + *(src+5*srcZres) ) + + 11*( *(src-7*srcZres) + *(src+7*srcZres) )+2048) / 4096; + if (result < 0) + result = 0; + else if (result > pixMax) + result = pixMax; + *dst = (uint16) result; + dst += dstZres; + src += 2*srcZres; + } + + limit = srcXres - (srcXres & 1); // must round down to not exceed dst + for (x=x; x < limit; x+=2) + { + n1 = (x < srcXres-1) ? 1 : 0; + n3 = (x < srcXres-3) ? 3 : (srcXres-1-x); + n5 = (x < srcXres-5) ? 5 : (srcXres-1-x); + n7 = (x < srcXres-7) ? 7 : (srcXres-1-x); + result = ( 2048* *(src) + + 1228*( *(src- srcZres) + *(src+n1*srcZres) ) + - 262*( *(src-3*srcZres) + *(src+n3*srcZres) ) + + 47*( *(src-5*srcZres) + *(src+n5*srcZres) ) + + 11*( *(src-7*srcZres) + *(src+n7*srcZres) )+2048) / 4096; + if (result < 0) + result = 0; + else if (result > pixMax) + result = pixMax; + *dst = (uint16) result; + dst += dstZres; + src += 2*srcZres; + } + } +} // horizontal_half_1chan_cosite + + +/*! + ***************************************************************************** + * \brief + * FIR vertical filter and 2:1 subsampling with 0.5 sample interval phase shift + * see vertical_half_1chan.pdf for response + ***************************************************************************** +*/ +void vertical_half_1chan (uint16 *srcPtr, + int xres, + int srcYres, + int srcZres, + uint16 *dstPtr, + int dstZres, + int pixMax //!< needed for clipping + ) +{ + int x, y, limit, n6, n5, n4, n3, n2, n1; + int result; + + int srcRowCount = xres * srcZres; + uint16 *src = srcPtr; + uint16 *dst = dstPtr; + + for (y=0; y < 6; y+=2) + { + n1 = (y >= 1) ? 1 : y; + n2 = (y >= 2) ? 2 : y; + n3 = (y >= 3) ? 3 : y; + n4 = (y >= 4) ? 4 : y; + /*n5 = (y >= 5) ? 5 : y;*/ + for (x=0; x < xres; x++) + { + result = (225*( *(src ) + *(src+ srcRowCount) ) + +69*( *(src-n1*srcRowCount) + *(src+2*srcRowCount) ) + -30*( *(src-n2*srcRowCount) + *(src+3*srcRowCount) ) + -16*( *(src-n3*srcRowCount) + *(src+4*srcRowCount) ) + + 6*( *(src-n4*srcRowCount) + *(src+5*srcRowCount) ) + + 2*( *(src- y*srcRowCount) + *(src+6*srcRowCount) )+256) / 512; + if (result < 0) + result = 0; + else if (result > pixMax) + result = pixMax; + *dst = (uint16) result; + dst += dstZres; + src += srcZres; + } + src += srcRowCount; + } + + limit = srcYres - 6; + for (y=6; y < limit; y+=2) + { + for (x=0; x < xres; x++) + { + result = (225*( *(src ) + *(src+ srcRowCount) ) + +69*( *(src- srcRowCount) + *(src+2*srcRowCount) ) + -30*( *(src-2*srcRowCount) + *(src+3*srcRowCount) ) + -16*( *(src-3*srcRowCount) + *(src+4*srcRowCount) ) + + 6*( *(src-4*srcRowCount) + *(src+5*srcRowCount) ) + + 2*( *(src-5*srcRowCount) + *(src+6*srcRowCount) )+256) / 512; + if (result < 0) + result = 0; + else if (result > pixMax) + result = pixMax; + *dst = (uint16) result; + dst += dstZres; + src += srcZres; + } + src += srcRowCount; + } + + limit = srcYres - (srcYres & 1); // must round down to not exceed dst + for (y=y; y < limit; y+=2) + { + n1 = (y < srcYres-1) ? 1 : 0; + n2 = (y < srcYres-2) ? 2 : (srcYres-1-y); + n3 = (y < srcYres-3) ? 3 : (srcYres-1-y); + n4 = (y < srcYres-4) ? 4 : (srcYres-1-y); + n5 = (y < srcYres-5) ? 5 : (srcYres-1-y); + n6 = (y < srcYres-6) ? 6 : (srcYres-1-y); + for (x=0; x < xres; x++) + { + result = (225*( *(src ) + *(src+n1*srcRowCount) ) + +69*( *(src- srcRowCount) + *(src+n2*srcRowCount) ) + -30*( *(src-2*srcRowCount) + *(src+n3*srcRowCount) ) + -16*( *(src-3*srcRowCount) + *(src+n4*srcRowCount) ) + + 6*( *(src-4*srcRowCount) + *(src+n5*srcRowCount) ) + + 2*( *(src-5*srcRowCount) + *(src+n6*srcRowCount) )+256) / 512; + if (result < 0) + result = 0; + else if (result > pixMax) + result = pixMax; + *dst = (uint16) result; + dst += dstZres; + src += srcZres; + } + src += srcRowCount; + } +} // vertical_half_1chan + + +/*! + ***************************************************************************** + * \brief + * Reads entire tiff file from harddrive. Any processing is done + * in memory, reducing I/O overhead + * \param input_file + * [in] Input file name etc. to read from + * \param FrameNoInFile + * [in] Frame number in the source file + * \param source + * [in] source file (on disk) information + * \param buf + * [out] memory buffer where image will be stored + * \return + * 1, success + * 0, failure + ***************************************************************************** + */ +int ReadTIFFImage (InputParameters *p_Inp, VideoDataFile *input_file, int FrameNoInFile, FrameFormat *source, unsigned char *buf) +{ + static Tiff t; // Declaring it static allows it to "remember" memory allocations; ATOUR: Not a good idea since this affects reentrancy + char path[FILE_NAME_SIZE]; + int frameNumberInFile, n, nComponents, height, i, width, y; + RGB_YUV rgb_yuv; + uint16 + *img, *temp = NULL, *temp2 = NULL, + *p, *yp, *up, *vp; + + assert( p_Inp); + assert( input_file); + assert( source); + assert( buf); + + width = source->width[0]; + height = source->height[0]; + assert( (width & 1) == 0 && (height & 1) == 0); // width & height must be even. + + if (source->color_model == CM_RGB && !input_file->is_interleaved) + { + fprintf( stdout, "ReadTIFFImage: RGB input file has not been declared as interleaved but only interleaved is supported\n"); + goto Error; + } + + frameNumberInFile = FrameNoInFile + p_Inp->start_frame; + if (input_file->num_digits > 0) + { + if (input_file->zero_pad) + { + n = snprintf( path, sizeof(path), "%s%0*d%s", input_file->fhead, input_file->num_digits, frameNumberInFile, input_file->ftail); + } + else + { + n = snprintf( path, sizeof(path), "%s%*d%s", input_file->fhead, input_file->num_digits, frameNumberInFile, input_file->ftail); + } + if (n == FILE_NAME_SIZE || n == -1) + { + fprintf( stdout, "ReadTIFFImage: file name is too large\n"); + return 0; + } + } + else + { + strcpy( path, input_file->fname); + } + + if (readTiff( &t, path)) + { + goto Error; + } + + if ((int) t.ImageLength != height) + { + fprintf( stdout, "ReadTIFFImage: Tiff height (%u) different from encoder input height (%d) . Exiting...\n", t.ImageLength, height); + goto Error; + } + if ((int) t.ImageWidth != width) + { + fprintf( stdout, "ReadTIFFImage: Tiff width (%u) different from encoder input width (%d) . Exiting...\n", t.ImageWidth, width); + goto Error; + } + + if (source->pic_unit_size_on_disk != 8 && source->pic_unit_size_on_disk != 16) { + fprintf( stdout, "ReadTIFFImage only supports pic_unit_size_on_disk of 8 or 16 not %d\n", source->pic_unit_size_on_disk); + goto Error; + } + + // Transfer image to 'buf' + + img = t.img; // default setting points at TIFF image data. + nComponents = width * height * 3; // for RGB, which will be overridden by YUV420 etc. + + if (source->color_model == CM_YUV) + { + if (RGB_YUV_initialize( &rgb_yuv, (VideoCode) p_Inp->videoCode, p_Inp->stdRange, 65535)) + goto Error; + + RGB_YUV_rgb_to_yuv( &rgb_yuv, img, img+1, img+2, width, height, 3, img, img+1, img+2, 3); + switch (source->yuv_format) + { + case YUV420: + // allocate planar buffer + nComponents = width * height * 3 / 2; + yp = temp = (uint16 *) malloc( nComponents * sizeof(uint16)); + up = yp + width * height; + vp = up + width * height / 4; + // Y + p = img; + for (i=0; i < width*height; ++i) + { + yp[i] = *p; p += 3; + } + // subsample into planar buffer + temp2 = (uint16 *) malloc( height * width * sizeof(uint16) / 2); + // U + horizontal_half_1chan_cosite( img+1, width, height, 3, temp2, 1, 65535); + vertical_half_1chan( temp2, width/2, height, 1, up, 1, 65535); + // V + horizontal_half_1chan_cosite( img+2, width, height, 3, temp2, 1, 65535); + vertical_half_1chan( temp2, width/2, height, 1, vp, 1, 65535); + free_pointer(temp2); + img = yp; // img points at result + break; + case YUV422: + // allocate planar buffer + nComponents = width * height * 2; + yp = temp = (uint16 *) malloc( nComponents * sizeof(uint16)); + up = yp + width*height; + vp = yp + width*height/2; + // Y + p = img; + for (i=0; i < width*height; ++i) + { + yp[i] = *p; p += 3; + } + // U & V + for (y=0; y < height; ++y) { + horizontal_half_1chan_cosite( img + 1 + y*width*3, width, 1, 3, up + y * width/2, 1, 65535); + horizontal_half_1chan_cosite( img + 2 + y*width*3, width, 1, 3, vp + y * width/2, 1, 65535); + } + img = yp; // img points at result + break; + case YUV444: + break; + default: + fprintf( stdout, "ReadTIFFImage: Unsupported ColorFormat (%d)\n", source->yuv_format); + goto Error; + } + } + + switch (source->pic_unit_size_shift3) + { + case 1: + for (i=0; i < nComponents; ++i) + { + buf[i] = (uint8)(img[i] >> 8); + } + break; + case 2: + for (i=0; i < nComponents; ++i) + { + ((uint16 *) buf)[i] = img[i]; + } + break; + default: + fprintf( stdout, "ReadTIFFImage only supports pic_unit_size_shift3 of 1 or 2 not %d\n", source->pic_unit_size_shift3); + goto Error; + } + + free_pointer(temp); + return 1; + +Error: + + free_pointer(temp); + report_stats_on_error(); + + return 0; +} diff --git a/lcommon/src/mbuffer_common.c b/lcommon/src/mbuffer_common.c new file mode 100644 index 0000000..cd3c2e3 --- /dev/null +++ b/lcommon/src/mbuffer_common.c @@ -0,0 +1,863 @@ + +/*! + *********************************************************************** + * \file + * mbuffer_common.c + * + * \brief + * Common (Encoder/Decoder) Frame buffer functions + * + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * - Karsten Suehring + * - Alexis Tourapis + * - Yuwen He + *********************************************************************** + */ + +#include + +#include "global.h" +#include "header.h" +#include "image.h" +#include "mbuffer_common.h" +#include "mbuffer.h" +#include "memalloc.h" +#include "output.h" +#include "fast_memory.h" +#include "input.h" + +static void unmark_long_term_field_for_reference_by_frame_idx(DecodedPictureBuffer *p_Dpb, PictureStructure structure, int long_term_frame_idx, int mark_current, unsigned curr_frame_num, int curr_pic_num); +/*! + ************************************************************************ + * \brief + * Check if one of the frames/fields in frame store is used for short-term reference + ************************************************************************ + */ +static int is_short_term_reference(FrameStore* fs) +{ + + if (fs->is_used==3) // frame + { + if ((fs->frame->used_for_reference)&&(!fs->frame->is_long_term)) + { + return 1; + } + } + + if (fs->is_used & 1) // top field + { + if (fs->top_field) + { + if ((fs->top_field->used_for_reference)&&(!fs->top_field->is_long_term)) + { + return 1; + } + } + } + + if (fs->is_used & 2) // bottom field + { + if (fs->bottom_field) + { + if ((fs->bottom_field->used_for_reference)&&(!fs->bottom_field->is_long_term)) + { + return 1; + } + } + } + return 0; +} + + +/*! + ************************************************************************ + * \brief + * Check if one of the frames/fields in frame store is used for short-term reference + ************************************************************************ + */ +static int is_long_term_reference(FrameStore* fs) +{ + + if (fs->is_used==3) // frame + { + if ((fs->frame->used_for_reference)&&(fs->frame->is_long_term)) + { + return 1; + } + } + + if (fs->is_used & 1) // top field + { + if (fs->top_field) + { + if ((fs->top_field->used_for_reference)&&(fs->top_field->is_long_term)) + { + return 1; + } + } + } + + if (fs->is_used & 2) // bottom field + { + if (fs->bottom_field) + { + if ((fs->bottom_field->used_for_reference)&&(fs->bottom_field->is_long_term)) + { + return 1; + } + } + } + return 0; +} + +/*! + ************************************************************************ + * \brief + * Generates a alternating field list from a given FrameStore list + * + ************************************************************************ + */ +void gen_pic_list_from_frame_list(PictureStructure currStructure, FrameStore **fs_list, int list_idx, StorablePicture **list, char *list_size, int long_term) +{ + int top_idx = 0; + int bot_idx = 0; + + int (*is_ref)(StorablePicture *s) = (long_term) ? is_long_ref : is_short_ref; + + + if (currStructure == TOP_FIELD) + { + while ((top_idxis_used & 1) + { + if(is_ref(fs_list[top_idx]->top_field)) + { + // short term ref pic + list[(short) *list_size] = fs_list[top_idx]->top_field; + (*list_size)++; + top_idx++; + break; + } + } + } + for ( ; bot_idxis_used & 2) + { + if(is_ref(fs_list[bot_idx]->bottom_field)) + { + // short term ref pic + list[(short) *list_size] = fs_list[bot_idx]->bottom_field; + (*list_size)++; + bot_idx++; + break; + } + } + } + } + } + if (currStructure == BOTTOM_FIELD) + { + while ((top_idxis_used & 2) + { + if(is_ref(fs_list[bot_idx]->bottom_field)) + { + // short term ref pic + list[(short) *list_size] = fs_list[bot_idx]->bottom_field; + (*list_size)++; + bot_idx++; + break; + } + } + } + for ( ; top_idxis_used & 1) + { + if(is_ref(fs_list[top_idx]->top_field)) + { + // short term ref pic + list[(short) *list_size] = fs_list[top_idx]->top_field; + (*list_size)++; + top_idx++; + break; + } + } + } + } + } +} + +/*! + ************************************************************************ + * \brief + * Returns long term pic with given LongtermPicNum + * + ************************************************************************ + */ +StorablePicture* get_long_term_pic(Slice *currSlice, DecodedPictureBuffer *p_Dpb, int LongtermPicNum) +{ + uint32 i; + + for (i=0; iltref_frames_in_buffer; i++) + { + if (currSlice->structure==FRAME) + { + if (p_Dpb->fs_ltref[i]->is_reference == 3) + if ((p_Dpb->fs_ltref[i]->frame->is_long_term)&&(p_Dpb->fs_ltref[i]->frame->long_term_pic_num == LongtermPicNum)) + return p_Dpb->fs_ltref[i]->frame; + } + else + { + if (p_Dpb->fs_ltref[i]->is_reference & 1) + if ((p_Dpb->fs_ltref[i]->top_field->is_long_term)&&(p_Dpb->fs_ltref[i]->top_field->long_term_pic_num == LongtermPicNum)) + return p_Dpb->fs_ltref[i]->top_field; + if (p_Dpb->fs_ltref[i]->is_reference & 2) + if ((p_Dpb->fs_ltref[i]->bottom_field->is_long_term)&&(p_Dpb->fs_ltref[i]->bottom_field->long_term_pic_num == LongtermPicNum)) + return p_Dpb->fs_ltref[i]->bottom_field; + } + } + return NULL; +} + +/*! + ************************************************************************ + * \brief + * Update the list of frame stores that contain reference frames/fields + * + ************************************************************************ + */ +void update_ref_list(DecodedPictureBuffer *p_Dpb) +{ + unsigned i, j; + for (i=0, j=0; iused_size; i++) + { + if (is_short_term_reference(p_Dpb->fs[i])) + { + p_Dpb->fs_ref[j++]=p_Dpb->fs[i]; + } + } + + p_Dpb->ref_frames_in_buffer = j; + + while (jsize) + { + p_Dpb->fs_ref[j++]=NULL; + } +} + + + +/*! + ************************************************************************ + * \brief + * Update the list of frame stores that contain long-term reference + * frames/fields + * + ************************************************************************ + */ +void update_ltref_list(DecodedPictureBuffer *p_Dpb) +{ + unsigned i, j; + for (i=0, j=0; iused_size; i++) + { + if (is_long_term_reference(p_Dpb->fs[i])) + { + p_Dpb->fs_ltref[j++] = p_Dpb->fs[i]; + } + } + + p_Dpb->ltref_frames_in_buffer = j; + + while (jsize) + { + p_Dpb->fs_ltref[j++]=NULL; + } +} + +/*! + ************************************************************************ + * \brief + * Calculate picNumX + ************************************************************************ + */ +static int get_pic_num_x (StorablePicture *p, int difference_of_pic_nums_minus1) +{ + int currPicNum; + + if (p->structure == FRAME) + currPicNum = p->frame_num; + else + currPicNum = 2 * p->frame_num + 1; + + return currPicNum - (difference_of_pic_nums_minus1 + 1); +} + +/*! + ************************************************************************ + * \brief + * Adaptive Memory Management: Mark short term picture unused + ************************************************************************ + */ +void mm_unmark_short_term_for_reference(DecodedPictureBuffer *p_Dpb, StorablePicture *p, int difference_of_pic_nums_minus1) +{ + int picNumX; + + uint32 i; + + picNumX = get_pic_num_x(p, difference_of_pic_nums_minus1); + + for (i=0; iref_frames_in_buffer; i++) + { + if (p->structure == FRAME) + { + if ((p_Dpb->fs_ref[i]->is_reference==3) && (p_Dpb->fs_ref[i]->is_long_term==0)) + { + if (p_Dpb->fs_ref[i]->frame->pic_num == picNumX) + { + unmark_for_reference(p_Dpb->fs_ref[i]); + return; + } + } + } + else + { + if ((p_Dpb->fs_ref[i]->is_reference & 1) && (!(p_Dpb->fs_ref[i]->is_long_term & 1))) + { + if (p_Dpb->fs_ref[i]->top_field->pic_num == picNumX) + { + p_Dpb->fs_ref[i]->top_field->used_for_reference = 0; + p_Dpb->fs_ref[i]->is_reference &= 2; + if (p_Dpb->fs_ref[i]->is_used == 3) + { + p_Dpb->fs_ref[i]->frame->used_for_reference = 0; + } + return; + } + } + if ((p_Dpb->fs_ref[i]->is_reference & 2) && (!(p_Dpb->fs_ref[i]->is_long_term & 2))) + { + if (p_Dpb->fs_ref[i]->bottom_field->pic_num == picNumX) + { + p_Dpb->fs_ref[i]->bottom_field->used_for_reference = 0; + p_Dpb->fs_ref[i]->is_reference &= 1; + if (p_Dpb->fs_ref[i]->is_used == 3) + { + p_Dpb->fs_ref[i]->frame->used_for_reference = 0; + } + return; + } + } + } + } +} + + +/*! + ************************************************************************ + * \brief + * Adaptive Memory Management: Mark long term picture unused + ************************************************************************ + */ +void mm_unmark_long_term_for_reference(DecodedPictureBuffer *p_Dpb, StorablePicture *p, int long_term_pic_num) +{ + uint32 i; + for (i=0; iltref_frames_in_buffer; i++) + { + if (p->structure == FRAME) + { + if ((p_Dpb->fs_ltref[i]->is_reference==3) && (p_Dpb->fs_ltref[i]->is_long_term==3)) + { + if (p_Dpb->fs_ltref[i]->frame->long_term_pic_num == long_term_pic_num) + { + unmark_for_long_term_reference(p_Dpb->fs_ltref[i]); + } + } + } + else + { + if ((p_Dpb->fs_ltref[i]->is_reference & 1) && ((p_Dpb->fs_ltref[i]->is_long_term & 1))) + { + if (p_Dpb->fs_ltref[i]->top_field->long_term_pic_num == long_term_pic_num) + { + p_Dpb->fs_ltref[i]->top_field->used_for_reference = 0; + p_Dpb->fs_ltref[i]->top_field->is_long_term = 0; + p_Dpb->fs_ltref[i]->is_reference &= 2; + p_Dpb->fs_ltref[i]->is_long_term &= 2; + if (p_Dpb->fs_ltref[i]->is_used == 3) + { + p_Dpb->fs_ltref[i]->frame->used_for_reference = 0; + p_Dpb->fs_ltref[i]->frame->is_long_term = 0; + } + return; + } + } + if ((p_Dpb->fs_ltref[i]->is_reference & 2) && ((p_Dpb->fs_ltref[i]->is_long_term & 2))) + { + if (p_Dpb->fs_ltref[i]->bottom_field->long_term_pic_num == long_term_pic_num) + { + p_Dpb->fs_ltref[i]->bottom_field->used_for_reference = 0; + p_Dpb->fs_ltref[i]->bottom_field->is_long_term = 0; + p_Dpb->fs_ltref[i]->is_reference &= 1; + p_Dpb->fs_ltref[i]->is_long_term &= 1; + if (p_Dpb->fs_ltref[i]->is_used == 3) + { + p_Dpb->fs_ltref[i]->frame->used_for_reference = 0; + p_Dpb->fs_ltref[i]->frame->is_long_term = 0; + } + return; + } + } + } + } +} + + +/*! + ************************************************************************ + * \brief + * Mark a long-term reference frame or complementary field pair unused for referemce + ************************************************************************ + */ +static void unmark_long_term_frame_for_reference_by_frame_idx(DecodedPictureBuffer *p_Dpb, int long_term_frame_idx) +{ + uint32 i; + for(i=0; iltref_frames_in_buffer; i++) + { + if (p_Dpb->fs_ltref[i]->long_term_frame_idx == long_term_frame_idx) + unmark_for_long_term_reference(p_Dpb->fs_ltref[i]); + } +} + + + +/*! + ************************************************************************ + * \brief + * mark a picture as long-term reference + ************************************************************************ + */ +static void mark_pic_long_term(DecodedPictureBuffer *p_Dpb, StorablePicture* p, int long_term_frame_idx, int picNumX) +{ + uint32 i; + int add_top, add_bottom; + + if (p->structure == FRAME) + { + for (i=0; iref_frames_in_buffer; i++) + { + if (p_Dpb->fs_ref[i]->is_reference == 3) + { + if ((!p_Dpb->fs_ref[i]->frame->is_long_term)&&(p_Dpb->fs_ref[i]->frame->pic_num == picNumX)) + { + p_Dpb->fs_ref[i]->long_term_frame_idx = p_Dpb->fs_ref[i]->frame->long_term_frame_idx + = long_term_frame_idx; + p_Dpb->fs_ref[i]->frame->long_term_pic_num = long_term_frame_idx; + p_Dpb->fs_ref[i]->frame->is_long_term = 1; + + if (p_Dpb->fs_ref[i]->top_field && p_Dpb->fs_ref[i]->bottom_field) + { + p_Dpb->fs_ref[i]->top_field->long_term_frame_idx = p_Dpb->fs_ref[i]->bottom_field->long_term_frame_idx + = long_term_frame_idx; + p_Dpb->fs_ref[i]->top_field->long_term_pic_num = long_term_frame_idx; + p_Dpb->fs_ref[i]->bottom_field->long_term_pic_num = long_term_frame_idx; + + p_Dpb->fs_ref[i]->top_field->is_long_term = p_Dpb->fs_ref[i]->bottom_field->is_long_term + = 1; + + } + p_Dpb->fs_ref[i]->is_long_term = 3; + return; + } + } + } + printf ("Warning: reference frame for long term marking not found\n"); + } + else + { + if (p->structure == TOP_FIELD) + { + add_top = 1; + add_bottom = 0; + } + else + { + add_top = 0; + add_bottom = 1; + } + for (i=0; iref_frames_in_buffer; i++) + { + if (p_Dpb->fs_ref[i]->is_reference & 1) + { + if ((!p_Dpb->fs_ref[i]->top_field->is_long_term)&&(p_Dpb->fs_ref[i]->top_field->pic_num == picNumX)) + { + if ((p_Dpb->fs_ref[i]->is_long_term) && (p_Dpb->fs_ref[i]->long_term_frame_idx != long_term_frame_idx)) + { + printf ("Warning: assigning long_term_frame_idx different from other field\n"); + } + + p_Dpb->fs_ref[i]->long_term_frame_idx = p_Dpb->fs_ref[i]->top_field->long_term_frame_idx + = long_term_frame_idx; + p_Dpb->fs_ref[i]->top_field->long_term_pic_num = 2 * long_term_frame_idx + add_top; + p_Dpb->fs_ref[i]->top_field->is_long_term = 1; + p_Dpb->fs_ref[i]->is_long_term |= 1; + if (p_Dpb->fs_ref[i]->is_long_term == 3) + { + p_Dpb->fs_ref[i]->frame->is_long_term = 1; + p_Dpb->fs_ref[i]->frame->long_term_frame_idx = p_Dpb->fs_ref[i]->frame->long_term_pic_num = long_term_frame_idx; + } + return; + } + } + if (p_Dpb->fs_ref[i]->is_reference & 2) + { + if ((!p_Dpb->fs_ref[i]->bottom_field->is_long_term)&&(p_Dpb->fs_ref[i]->bottom_field->pic_num == picNumX)) + { + if ((p_Dpb->fs_ref[i]->is_long_term) && (p_Dpb->fs_ref[i]->long_term_frame_idx != long_term_frame_idx)) + { + printf ("Warning: assigning long_term_frame_idx different from other field\n"); + } + + p_Dpb->fs_ref[i]->long_term_frame_idx = p_Dpb->fs_ref[i]->bottom_field->long_term_frame_idx + = long_term_frame_idx; + p_Dpb->fs_ref[i]->bottom_field->long_term_pic_num = 2 * long_term_frame_idx + add_bottom; + p_Dpb->fs_ref[i]->bottom_field->is_long_term = 1; + p_Dpb->fs_ref[i]->is_long_term |= 2; + if (p_Dpb->fs_ref[i]->is_long_term == 3) + { + p_Dpb->fs_ref[i]->frame->is_long_term = 1; + p_Dpb->fs_ref[i]->frame->long_term_frame_idx = p_Dpb->fs_ref[i]->frame->long_term_pic_num = long_term_frame_idx; + } + return; + } + } + } + printf ("Warning: reference field for long term marking not found\n"); + } +} + + +/*! + ************************************************************************ + * \brief + * Assign a long term frame index to a short term picture + ************************************************************************ + */ +void mm_assign_long_term_frame_idx(DecodedPictureBuffer *p_Dpb, StorablePicture* p, int difference_of_pic_nums_minus1, int long_term_frame_idx) +{ + int picNumX = get_pic_num_x(p, difference_of_pic_nums_minus1); + + // remove frames/fields with same long_term_frame_idx + if (p->structure == FRAME) + { + unmark_long_term_frame_for_reference_by_frame_idx(p_Dpb, long_term_frame_idx); + } + else + { + unsigned i; + PictureStructure structure = FRAME; + + for (i=0; iref_frames_in_buffer; i++) + { + if (p_Dpb->fs_ref[i]->is_reference & 1) + { + if (p_Dpb->fs_ref[i]->top_field->pic_num == picNumX) + { + structure = TOP_FIELD; + break; + } + } + if (p_Dpb->fs_ref[i]->is_reference & 2) + { + if (p_Dpb->fs_ref[i]->bottom_field->pic_num == picNumX) + { + structure = BOTTOM_FIELD; + break; + } + } + } + if (structure==FRAME) + { + error ("field for long term marking not found",200); + } + + unmark_long_term_field_for_reference_by_frame_idx(p_Dpb, structure, long_term_frame_idx, 0, 0, picNumX); + } + + mark_pic_long_term(p_Dpb, p, long_term_frame_idx, picNumX); +} + +/*! + ************************************************************************ + * \brief + * Set new max long_term_frame_idx + ************************************************************************ + */ +void mm_update_max_long_term_frame_idx(DecodedPictureBuffer *p_Dpb, int max_long_term_frame_idx_plus1) +{ + uint32 i; + + p_Dpb->max_long_term_pic_idx = max_long_term_frame_idx_plus1 - 1; + + // check for invalid frames + for (i=0; iltref_frames_in_buffer; i++) + { + if (p_Dpb->fs_ltref[i]->long_term_frame_idx > p_Dpb->max_long_term_pic_idx) + { + unmark_for_long_term_reference(p_Dpb->fs_ltref[i]); + } + } +} + + +/*! + ************************************************************************ + * \brief + * Mark all long term reference pictures unused for reference + ************************************************************************ + */ +void mm_unmark_all_long_term_for_reference (DecodedPictureBuffer *p_Dpb) +{ + mm_update_max_long_term_frame_idx(p_Dpb, 0); +} + +/*! + ************************************************************************ + * \brief + * Mark all short term reference pictures unused for reference + ************************************************************************ + */ +void mm_unmark_all_short_term_for_reference (DecodedPictureBuffer *p_Dpb) +{ + unsigned int i; + for (i=0; iref_frames_in_buffer; i++) + { + unmark_for_reference(p_Dpb->fs_ref[i]); + } + update_ref_list(p_Dpb); +} + + +/*! + ************************************************************************ + * \brief + * Mark the current picture used for long term reference + ************************************************************************ + */ +void mm_mark_current_picture_long_term(DecodedPictureBuffer *p_Dpb, StorablePicture *p, int long_term_frame_idx) +{ + // remove long term pictures with same long_term_frame_idx + if (p->structure == FRAME) + { + unmark_long_term_frame_for_reference_by_frame_idx(p_Dpb, long_term_frame_idx); + } + else + { + unmark_long_term_field_for_reference_by_frame_idx(p_Dpb, p->structure, long_term_frame_idx, 1, p->pic_num, 0); + } + + p->is_long_term = 1; + p->long_term_frame_idx = long_term_frame_idx; +} + + +/*! + ************************************************************************ + * \brief + * Check if one of the frames/fields in frame store is used for reference + ************************************************************************ + */ +int is_used_for_reference(FrameStore* fs) +{ + if (fs->is_reference) + { + return 1; + } + + if (fs->is_used == 3) // frame + { + if (fs->frame->used_for_reference) + { + return 1; + } + } + + if (fs->is_used & 1) // top field + { + if (fs->top_field) + { + if (fs->top_field->used_for_reference) + { + return 1; + } + } + } + + if (fs->is_used & 2) // bottom field + { + if (fs->bottom_field) + { + if (fs->bottom_field->used_for_reference) + { + return 1; + } + } + } + return 0; +} + +/*! + ************************************************************************ + * \brief + * find smallest POC in the DPB. + ************************************************************************ + */ +void get_smallest_poc(DecodedPictureBuffer *p_Dpb, int *poc,int * pos) +{ + uint32 i; + + if (p_Dpb->used_size<1) + { + error("Cannot determine smallest POC, DPB empty.",150); + } + + *pos=-1; + *poc = INT_MAX; + for (i = 0; i < p_Dpb->used_size; i++) + { + if ((*poc > p_Dpb->fs[i]->poc)&&(!p_Dpb->fs[i]->is_output)) + { + *poc = p_Dpb->fs[i]->poc; + *pos = i; + } + } +} + +/*! + ************************************************************************ + * \brief + * Remove a picture from DPB which is no longer needed. + ************************************************************************ + */ +int remove_unused_frame_from_dpb(DecodedPictureBuffer *p_Dpb) +{ + uint32 i; + + // check for frames that were already output and no longer used for reference + for (i = 0; i < p_Dpb->used_size; i++) + { + if (p_Dpb->fs[i]->is_output && (!is_used_for_reference(p_Dpb->fs[i]))) + { + remove_frame_from_dpb(p_Dpb, i); + return 1; + } + } + return 0; +} + +/*! + ************************************************************************ + * \brief + * Mark a long-term reference field unused for reference only if it's not + * the complementary field of the picture indicated by picNumX + ************************************************************************ + */ +static void unmark_long_term_field_for_reference_by_frame_idx(DecodedPictureBuffer *p_Dpb, PictureStructure structure, int long_term_frame_idx, int mark_current, unsigned curr_frame_num, int curr_pic_num) +{ + VideoParameters *p_Vid = p_Dpb->p_Vid; + unsigned i; + + assert(structure!=FRAME); + if (curr_pic_num<0) + curr_pic_num += (2 * p_Vid->max_frame_num); + + for(i=0; iltref_frames_in_buffer; i++) + { + if (p_Dpb->fs_ltref[i]->long_term_frame_idx == long_term_frame_idx) + { + if (structure == TOP_FIELD) + { + if ((p_Dpb->fs_ltref[i]->is_long_term == 3)) + { + unmark_for_long_term_reference(p_Dpb->fs_ltref[i]); + } + else + { + if ((p_Dpb->fs_ltref[i]->is_long_term == 1)) + { + unmark_for_long_term_reference(p_Dpb->fs_ltref[i]); + } + else + { + if (mark_current) + { + if (p_Dpb->last_picture) + { + if ( ( p_Dpb->last_picture != p_Dpb->fs_ltref[i] )|| p_Dpb->last_picture->frame_num != curr_frame_num) + unmark_for_long_term_reference(p_Dpb->fs_ltref[i]); + } + else + { + unmark_for_long_term_reference(p_Dpb->fs_ltref[i]); + } + } + else + { + if ((p_Dpb->fs_ltref[i]->frame_num) != (unsigned)(curr_pic_num >> 1)) + { + unmark_for_long_term_reference(p_Dpb->fs_ltref[i]); + } + } + } + } + } + if (structure == BOTTOM_FIELD) + { + if ((p_Dpb->fs_ltref[i]->is_long_term == 3)) + { + unmark_for_long_term_reference(p_Dpb->fs_ltref[i]); + } + else + { + if ((p_Dpb->fs_ltref[i]->is_long_term == 2)) + { + unmark_for_long_term_reference(p_Dpb->fs_ltref[i]); + } + else + { + if (mark_current) + { + if (p_Dpb->last_picture) + { + if ( ( p_Dpb->last_picture != p_Dpb->fs_ltref[i] )|| p_Dpb->last_picture->frame_num != curr_frame_num) + unmark_for_long_term_reference(p_Dpb->fs_ltref[i]); + } + else + { + unmark_for_long_term_reference(p_Dpb->fs_ltref[i]); + } + } + else + { + if ((p_Dpb->fs_ltref[i]->frame_num) != (unsigned)(curr_pic_num >> 1)) + { + unmark_for_long_term_reference(p_Dpb->fs_ltref[i]); + } + } + } + } + } + } + } +} + + + diff --git a/lcommon/src/memalloc.c b/lcommon/src/memalloc.c new file mode 100644 index 0000000..6e28331 --- /dev/null +++ b/lcommon/src/memalloc.c @@ -0,0 +1,2726 @@ + +/*! + ************************************************************************ + * \file memalloc.c + * + * \brief + * Memory allocation and free helper functions + * + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * - Alexis Michael Tourapis + * - Karsten Suehring + * - Yuwen He + * + ************************************************************************ + */ + +#include "global.h" +#include "memalloc.h" + +/*! + ************************************************************************ + * \brief + * Initialize 2-dimensional top and bottom field to point to the proper + * lines in frame + * + * \par Output: + * memory size in bytes + ************************************************************************/ +int init_top_bot_planes(imgpel **imgFrame, int dim0, imgpel ***imgTopField, imgpel ***imgBotField) +{ + int i; + + if((*imgTopField = (imgpel**)mem_malloc((dim0>>1) * sizeof(imgpel*))) == NULL) + no_mem_exit("init_top_bot_planes: imgTopField"); + + if((*imgBotField = (imgpel**)mem_malloc((dim0>>1) * sizeof(imgpel*))) == NULL) + no_mem_exit("init_top_bot_planes: imgBotField"); + + for(i = 0; i < (dim0>>1); i++) + { + (*imgTopField)[i] = imgFrame[2 * i ]; + (*imgBotField)[i] = imgFrame[2 * i + 1]; + } + + return dim0 * sizeof(imgpel*); +} + + /*! + ************************************************************************ + * \brief + * free 2-dimensional top and bottom fields without freeing target memory + * + * \par Output: + * memory size in bytes + ************************************************************************/ +void free_top_bot_planes(imgpel **imgTopField, imgpel **imgBotField) +{ + mem_free (imgTopField); + mem_free (imgBotField); +} + + +/*! + ************************************************************************ + * \brief + * Allocate 2D memory array -> DistortionData array2D[dim0][dim1] + * + * \par Output: + * memory size in bytes + ************************************************************************/ +int get_mem2Ddist(DistortionData ***array2D, int dim0, int dim1) +{ + int i; + + if((*array2D = (DistortionData**)mem_malloc(dim0 * sizeof(DistortionData*))) == NULL) + no_mem_exit("get_mem2Ddist: array2D"); + if((*(*array2D) = (DistortionData* )mem_calloc(dim0 * dim1,sizeof(DistortionData ))) == NULL) + no_mem_exit("get_mem2Ddist: array2D"); + + for(i = 1 ; i < dim0; i++) + (*array2D)[i] = (*array2D)[i-1] + dim1; + + return dim0 * (sizeof(DistortionData*) + dim1 * sizeof(DistortionData)); +} + +/*! + ************************************************************************ + * \brief + * Allocate 2D memory array -> LambdaParams array2D[dim0][dim1] + * + * \par Output: + * memory size in bytes + ************************************************************************/ +int get_mem2Dlm(LambdaParams ***array2D, int dim0, int dim1) +{ + int i; + + if((*array2D = (LambdaParams**)mem_malloc(dim0 * sizeof(LambdaParams*))) == NULL) + no_mem_exit("get_mem2Dlm: array2D"); + if((*(*array2D) = (LambdaParams* )mem_calloc(dim0 * dim1,sizeof(LambdaParams ))) == NULL) + no_mem_exit("get_mem2Dlm: array2D"); + + for(i = 1 ; i < dim0; i++) + (*array2D)[i] = (*array2D)[i-1] + dim1; + + return dim0 * (sizeof(LambdaParams*) + dim1 * sizeof(LambdaParams)); +} + +/*! + ************************************************************************ + * \brief + * free 2D memory array + * which was allocated with get_mem2Ddist() + ************************************************************************ + */ +void free_mem2Ddist(DistortionData **array2D) +{ + if (array2D) + { + if (*array2D) + mem_free (*array2D); + else + error ("free_mem2Ddist: trying to free unused memory",100); + + mem_free (array2D); + } + else + { + error ("free_mem2Ddist: trying to free unused memory",100); + } +} + +/*! + ************************************************************************ + * \brief + * free 2D memory array + * which was allocated with get_mem2Dlm() + ************************************************************************ + */ +void free_mem2Dlm(LambdaParams **array2D) +{ + if (array2D) + { + if (*array2D) + mem_free (*array2D); + else + error ("free_mem2Dlm: trying to free unused memory",100); + + mem_free (array2D); + } + else + { + error ("free_mem2Dlm: trying to free unused memory",100); + } +} + +/*! + ************************************************************************ + * \brief + * Allocate 2D memory array -> PicMotionParams array2D[dim0][dim1] + * + * \par Output: + * memory size in bytes + ************************************************************************/ +int get_mem2Dmp(PicMotionParams ***array2D, int dim0, int dim1) +{ + int i; + + if((*array2D = (PicMotionParams**)mem_malloc(dim0 * sizeof(PicMotionParams*))) == NULL) + no_mem_exit("get_mem2Dmp: array2D"); + if((*(*array2D) = (PicMotionParams* )mem_calloc(dim0 * dim1, sizeof(PicMotionParams ))) == NULL) + no_mem_exit("get_mem2Dmp: array2D"); + + for(i = 1 ; i < dim0; i++) + (*array2D)[i] = (*array2D)[i-1] + dim1; + + return dim0 * (sizeof(PicMotionParams*) + dim1 * sizeof(PicMotionParams)); +} + +/*! + ************************************************************************ + * \brief + * Allocate 3D memory array -> PicMotionParams array3D[frames][dim0][dim1] + * + * \par Output: + * memory size in bytes + ************************************************************************ + */ +int get_mem3Dmp(PicMotionParams ****array3D, int dim0, int dim1, int dim2) +{ + int i, mem_size = dim0 * sizeof(PicMotionParams**); + + if(((*array3D) = (PicMotionParams***)mem_malloc(dim0 * sizeof(PicMotionParams**))) == NULL) + no_mem_exit("get_mem3Dmp: array3D"); + + mem_size += get_mem2Dmp(*array3D, dim0 * dim1, dim2); + + for(i = 1; i < dim0; i++) + (*array3D)[i] = (*array3D)[i - 1] + dim1; + + return mem_size; +} + +/*! + ************************************************************************ + * \brief + * free 2D memory array + * which was allocated with get_mem2Dmp() + ************************************************************************ + */ +void free_mem2Dmp(PicMotionParams **array2D) +{ + if (array2D) + { + if (*array2D) + mem_free (*array2D); + else + error ("free_mem2Dmp: trying to free unused memory",100); + + mem_free (array2D); + } + else + { + error ("free_mem2Dmp: trying to free unused memory",100); + } +} + +/*! + ************************************************************************ + * \brief + * free 3D memory array + * which was allocated with get_mem3Dmp() + ************************************************************************ + */ +void free_mem3Dmp(PicMotionParams ***array3D) +{ + if (array3D) + { + free_mem2Dmp(*array3D); + mem_free (array3D); + } + else + { + error ("free_mem3Dmp: trying to free unused memory",100); + } +} + +/*! + ************************************************************************ + * \brief + * Allocate 2D memory array -> LevelQuantParams array2D[dim0][dim1] + * + * \par Output: + * memory size in bytes + ************************************************************************/ +int get_mem2Dquant(LevelQuantParams ***array2D, int dim0, int dim1) +{ + int i; + + if((*array2D = (LevelQuantParams**) mem_malloc(dim0 * sizeof(LevelQuantParams*))) == NULL) + no_mem_exit("get_mem2Dquant: array2D"); + if((*(*array2D) = (LevelQuantParams* ) mem_calloc(dim0 * dim1,sizeof(LevelQuantParams ))) == NULL) + no_mem_exit("get_mem2Dquant: array2D"); + + for(i = 1 ; i < dim0; i++) + (*array2D)[i] = (*array2D)[i-1] + dim1; + + return dim0 * (sizeof(LevelQuantParams*) + dim1 * sizeof(LevelQuantParams)); +} + +/*! + ************************************************************************ + * \brief + * Allocate 3D memory array -> LevelQuantParams array3D[dim0][dim1][dim2] + * + * \par Output: + * memory size in bytes + ************************************************************************ + */ +int get_mem3Dquant(LevelQuantParams ****array3D, int dim0, int dim1, int dim2) +{ + int i, mem_size = dim0 * sizeof(LevelQuantParams**); + + if(((*array3D) = (LevelQuantParams***)mem_malloc(dim0 * sizeof(LevelQuantParams**))) == NULL) + no_mem_exit("get_mem3Dquant: array3D"); + + mem_size += get_mem2Dquant(*array3D, dim0 * dim1, dim2); + + for(i = 1; i < dim0; i++) + (*array3D)[i] = (*array3D)[i - 1] + dim1; + + return mem_size; +} + +/*! + ************************************************************************ + * \brief + * Allocate 4D memory array -> LevelQuantParams array3D[dim0][dim1][dim2][dim3] + * + * \par Output: + * memory size in bytes + ************************************************************************ + */ +int get_mem4Dquant(LevelQuantParams *****array4D, int dim0, int dim1, int dim2, int dim3) +{ + int i, mem_size = dim0 * sizeof(LevelQuantParams***); + + if(((*array4D) = (LevelQuantParams****)mem_malloc(dim0 * sizeof(LevelQuantParams***))) == NULL) + no_mem_exit("get_mem4Dquant: array4D"); + + mem_size += get_mem3Dquant(*array4D, dim0 * dim1, dim2, dim3); + + for(i = 1; i < dim0; i++) + (*array4D)[i] = (*array4D)[i - 1] + dim1; + + return mem_size; +} + +/*! + ************************************************************************ + * \brief + * Allocate 5D memory array -> LevelQuantParams array3D[dim0][dim1][dim2][dim3][dim4] + * + * \par Output: + * memory size in bytes + ************************************************************************ + */ +int get_mem5Dquant(LevelQuantParams ******array5D, int dim0, int dim1, int dim2, int dim3, int dim4) +{ + int i, mem_size = dim0 * sizeof(LevelQuantParams***); + + if(((*array5D) = (LevelQuantParams*****)mem_malloc(dim0 * sizeof(LevelQuantParams****))) == NULL) + no_mem_exit("get_mem5Dquant: array5D"); + + mem_size += get_mem4Dquant(*array5D, dim0 * dim1, dim2, dim3, dim4); + + for(i = 1; i < dim0; i++) + (*array5D)[i] = (*array5D)[i - 1] + dim1; + + return mem_size; +} + + +/*! + ************************************************************************ + * \brief + * Allocate 2D memory array -> WPParams array2D[dim0][dim1] + * + * \par Output: + * memory size in bytes + ************************************************************************/ +int get_mem2Dwp(WPParams ***array2D, int dim0, int dim1) +{ + int i; + + if((*array2D = (WPParams**)mem_malloc(dim0 * sizeof(WPParams*))) == NULL) + no_mem_exit("get_mem2Dwp: array2D"); + if((*(*array2D) = (WPParams* )mem_calloc(dim0 * dim1,sizeof(WPParams ))) == NULL) + no_mem_exit("get_mem2Dwp: array2D"); + + for(i = 1 ; i < dim0; i++) + (*array2D)[i] = (*array2D)[i-1] + dim1; + + return dim0 * (sizeof(WPParams*) + dim1 * sizeof(WPParams)); +} + +/*! + ************************************************************************ + * \brief + * free 2D memory array + * which was allocated with get_mem2Dwp() + ************************************************************************ + */ +void free_mem2Dwp(WPParams **array2D) +{ + if (array2D) + { + if (*array2D) + mem_free (*array2D); + else + error ("free_mem2Dwp: trying to free unused memory",100); + + mem_free (array2D); + } + else + { + error ("free_mem2Dwp: trying to free unused memory",100); + } +} + +/*! + ************************************************************************ + * \brief + * free 2D memory array + * which was allocated with get_mem2Dquant() + ************************************************************************ + */ +void free_mem2Dquant(LevelQuantParams **array2D) +{ + if (array2D) + { + if (*array2D) + mem_free (*array2D); + else + error ("free_mem2Dquant: trying to free unused memory",100); + + mem_free (array2D); + } + else + { + error ("free_mem2Dquant: trying to free unused memory",100); + } +} + + +/*! + ************************************************************************ + * \brief + * free 3D memory array + * which was allocated with get_mem3Dquant() + ************************************************************************ + */ +void free_mem3Dquant(LevelQuantParams ***array3D) +{ + if (array3D) + { + free_mem2Dquant(*array3D); + mem_free (array3D); + } + else + { + error ("free_mem3Dquant: trying to free unused memory",100); + } +} + +/*! + ************************************************************************ + * \brief + * free 4D memory array + * which was allocated with get_mem4Dquant() + ************************************************************************ + */ +void free_mem4Dquant(LevelQuantParams ****array4D) +{ + if (array4D) + { + free_mem3Dquant(*array4D); + mem_free (array4D); + } + else + { + error ("free_mem4Dquant: trying to free unused memory",100); + } +} + +/*! + ************************************************************************ + * \brief + * free 5D memory array + * which was allocated with get_mem5Dquant() + ************************************************************************ + */ +void free_mem5Dquant(LevelQuantParams *****array5D) +{ + if (array5D) + { + free_mem4Dquant(*array5D); + mem_free (array5D); + } + else + { + error ("free_mem5Dquant: trying to free unused memory",100); + } +} + + +/*! + ************************************************************************ + * \brief + * Allocate 2D memory array -> StorablePicturePtr array2D[dim0][dim1] + * + * \par Output: + * memory size in bytes + ************************************************************************/ +int get_mem2D_spp(StorablePicturePtr ***array2D, int dim0, int dim1) +{ + int i; + + if((*array2D = (StorablePicturePtr**)mem_malloc(dim0 * sizeof(StorablePicturePtr*))) == NULL) + no_mem_exit("get_mem2D_spp: array2D"); + if((*(*array2D) = (StorablePicturePtr* )mem_calloc(dim0 * dim1,sizeof(StorablePicturePtr ))) == NULL) + no_mem_exit("get_mem2D_spp: array2D"); + + for(i = 1 ; i < dim0; i++) + (*array2D)[i] = (*array2D)[i-1] + dim1; + + return dim0 * (sizeof(StorablePicturePtr*) + dim1 * sizeof(StorablePicturePtr)); +} + +/*! + ************************************************************************ + * \brief + * Allocate 3D memory array -> StorablePicturePtr array3D[dim0][dim1][dim2] + * + * \par Output: + * memory size in bytes + ************************************************************************ + */ +int get_mem3D_spp(StorablePicturePtr ****array3D, int dim0, int dim1, int dim2) +{ + int i, mem_size = dim0 * sizeof(StorablePicturePtr**); + + if(((*array3D) = (StorablePicturePtr***)mem_malloc(dim0 * sizeof(StorablePicturePtr**))) == NULL) + no_mem_exit("get_mem3D_spp: array3D"); + + mem_size += get_mem2D_spp(*array3D, dim0 * dim1, dim2); + + for(i = 1; i < dim0; i++) + (*array3D)[i] = (*array3D)[i - 1] + dim1; + + return mem_size; +} + +/*! + ************************************************************************ + * \brief + * Allocate 2D memory array -> MotionVector array2D[dim0][dim1] + * + * \par Output: + * memory size in bytes + ************************************************************************/ +int get_mem2Dmv(MotionVector ***array2D, int dim0, int dim1) +{ + int i; + + if((*array2D = (MotionVector**)mem_malloc(dim0 * sizeof(MotionVector*))) == NULL) + no_mem_exit("get_mem2Dmv: array2D"); + if((*(*array2D) = (MotionVector* )mem_calloc(dim0 * dim1,sizeof(MotionVector ))) == NULL) + no_mem_exit("get_mem2Dmv: array2D"); + + memset(*(*array2D),0,dim0 * dim1 * sizeof(MotionVector ) ); + + for(i = 1 ; i < dim0; i++) + (*array2D)[i] = (*array2D)[i-1] + dim1; + + return dim0 * (sizeof(MotionVector*) + dim1 * sizeof(MotionVector)); +} + +/*! + ************************************************************************ + * \brief + * Allocate 3D memory array -> MotionVector array3D[dim0][dim1][dim2] + * + * \par Output: + * memory size in bytes + ************************************************************************ + */ +int get_mem3Dmv(MotionVector ****array3D, int dim0, int dim1, int dim2) +{ + int i, mem_size = dim0 * sizeof(MotionVector**); + + if(((*array3D) = (MotionVector***)mem_malloc(dim0 * sizeof(MotionVector**))) == NULL) + no_mem_exit("get_mem3Dmv: array3D"); + + mem_size += get_mem2Dmv(*array3D, dim0 * dim1, dim2); + + for(i = 1; i < dim0; i++) + (*array3D)[i] = (*array3D)[i - 1] + dim1; + + return mem_size; +} + +/*! + ************************************************************************ + * \brief + * Allocate 4D memory array -> MotionVector array3D[dim0][dim1][dim2][dim3] + * + * \par Output: + * memory size in bytes + ************************************************************************ + */ +int get_mem4Dmv(MotionVector *****array4D, int dim0, int dim1, int dim2, int dim3) +{ + int i, mem_size = dim0 * sizeof(MotionVector***); + + if(((*array4D) = (MotionVector****)mem_malloc(dim0 * sizeof(MotionVector***))) == NULL) + no_mem_exit("get_mem4Dpel: array4D"); + + mem_size += get_mem3Dmv(*array4D, dim0 * dim1, dim2, dim3); + + for(i = 1; i < dim0; i++) + (*array4D)[i] = (*array4D)[i - 1] + dim1; + + return mem_size; +} + +/*! + ************************************************************************ + * \brief + * Allocate 5D memory array -> MotionVector array3D[dim0][dim1][dim2][dim3][dim4] + * + * \par Output: + * memory size in bytes + ************************************************************************ + */ +int get_mem5Dmv(MotionVector ******array5D, int dim0, int dim1, int dim2, int dim3, int dim4) +{ + int i, mem_size = dim0 * sizeof(MotionVector***); + + if(((*array5D) = (MotionVector*****)mem_malloc(dim0 * sizeof(MotionVector****))) == NULL) + no_mem_exit("get_mem5Dmv: array5D"); + + mem_size += get_mem4Dmv(*array5D, dim0 * dim1, dim2, dim3, dim4); + + for(i = 1; i < dim0; i++) + (*array5D)[i] = (*array5D)[i - 1] + dim1; + + return mem_size; +} + +/*! + ************************************************************************ + * \brief + * Allocate 6D memory array -> MotionVector array6D[dim0][dim1][dim2][dim3][dim4][dim5] + * + * \par Output: + * memory size in bytes + ************************************************************************ + */ +int get_mem6Dmv(MotionVector *******array6D, int dim0, int dim1, int dim2, int dim3, int dim4, int dim5) +{ + int i, mem_size = dim0 * sizeof(MotionVector*****); + + if(((*array6D) = (MotionVector******)mem_malloc(dim0 * sizeof(MotionVector*****))) == NULL) + no_mem_exit("get_mem6Dmv: array6D"); + + mem_size += get_mem5Dmv(*array6D, dim0 * dim1, dim2, dim3, dim4, dim5); + + for(i = 1; i < dim0; i++) + (*array6D)[i] = (*array6D)[i - 1] + dim1; + + return mem_size; +} + +/*! + ************************************************************************ + * \brief + * Allocate 7D memory array -> MotionVector array6D[dim0][dim1][dim2][dim3][dim4][dim5][dim6] + * + * \par Output: + * memory size in bytes + ************************************************************************ + */ +int get_mem7Dmv(MotionVector ********array7D, int dim0, int dim1, int dim2, int dim3, int dim4, int dim5, int dim6) +{ + int i, mem_size = dim0 * sizeof(MotionVector*****); + + if(((*array7D) = (MotionVector*******)mem_malloc(dim0 * sizeof(MotionVector******))) == NULL) + no_mem_exit("get_mem7Dmv: array7D"); + + mem_size += get_mem6Dmv(*array7D, dim0 * dim1, dim2, dim3, dim4, dim5, dim6); + + for(i = 1; i < dim0; i++) + (*array7D)[i] = (*array7D)[i - 1] + dim1; + + return mem_size; +} + + +/*! + ************************************************************************ + * \brief + * free 2D memory array + * which was allocated with get_mem2D_spp() + ************************************************************************ + */ +void free_mem2D_spp(StorablePicturePtr **array2D) +{ + if (array2D) + { + if (*array2D) + mem_free (*array2D); + else + error ("free_mem2D_spp: trying to free unused memory",100); + + mem_free (array2D); + } + else + { + error ("free_mem2D_spp: trying to free unused memory",100); + } +} + + +/*! + ************************************************************************ + * \brief + * free 3D memory array + * which was allocated with get_mem3D_spp() + ************************************************************************ + */ +void free_mem3D_spp(StorablePicturePtr ***array3D) +{ + if (array3D) + { + free_mem2D_spp(*array3D); + mem_free (array3D); + } + else + { + error ("free_mem3D_spp: trying to free unused memory",100); + } +} + + +/*! + ************************************************************************ + * \brief + * free 2D memory array + * which was allocated with get_mem2Dmv() + ************************************************************************ + */ +void free_mem2Dmv(MotionVector **array2D) +{ + if (array2D) + { + if (*array2D) + mem_free (*array2D); + else + error ("free_mem2Dmv: trying to free unused memory",100); + + mem_free (array2D); + } + else + { + error ("free_mem2Dmv: trying to free unused memory",100); + } +} + + +/*! + ************************************************************************ + * \brief + * free 3D memory array + * which was allocated with get_mem3Dmv() + ************************************************************************ + */ +void free_mem3Dmv(MotionVector ***array3D) +{ + if (array3D) + { + free_mem2Dmv(*array3D); + mem_free (array3D); + } + else + { + error ("free_mem3Dmv: trying to free unused memory",100); + } +} + +/*! + ************************************************************************ + * \brief + * free 4D memory array + * which was allocated with get_mem4Dmv() + ************************************************************************ + */ +void free_mem4Dmv(MotionVector ****array4D) +{ + if (array4D) + { + free_mem3Dmv(*array4D); + mem_free (array4D); + } + else + { + error ("free_mem4Dmv: trying to free unused memory",100); + } +} + +/*! + ************************************************************************ + * \brief + * free 5D memory array + * which was allocated with get_mem5Dmv() + ************************************************************************ + */ +void free_mem5Dmv(MotionVector *****array5D) +{ + if (array5D) + { + free_mem4Dmv(*array5D); + mem_free (array5D); + } + else + { + error ("free_mem5Dmv: trying to free unused memory",100); + } +} + +/*! + ************************************************************************ + * \brief + * free 6D memory array + * which was allocated with get_mem6Dmv() + ************************************************************************ + */ +void free_mem6Dmv(MotionVector ******array6D) +{ + if (array6D) + { + free_mem5Dmv(*array6D); + mem_free (array6D); + } + else + { + error ("free_mem6Dmv: trying to free unused memory",100); + } +} + +/*! + ************************************************************************ + * \brief + * free 7D memory array + * which was allocated with get_mem7Dmv() + ************************************************************************ + */ +void free_mem7Dmv(MotionVector *******array7D) +{ + if (array7D) + { + free_mem6Dmv(*array7D); + mem_free (array7D); + } + else + { + error ("free_mem7Dmv: trying to free unused memory",100); + } +} + +/*! + ************************************************************************ + * \brief + * Allocate 1D memory array -> imgpel array1D[dim0 + * + * \par Output: + * memory size in bytes + ************************************************************************/ +int get_mem1Dpel(imgpel **array1D, int dim0) +{ + if((*array1D = (imgpel*)mem_calloc(dim0, sizeof(imgpel))) == NULL) + no_mem_exit("get_mem1Dpel: array1D"); + + return (sizeof(imgpel*) + dim0 * sizeof(imgpel)); +} + +/*! + ************************************************************************ + * \brief + * Allocate 2D memory array -> imgpel array2D[dim0][dim1] + * + * \par Output: + * memory size in bytes + ************************************************************************/ +int get_mem2Dpel(imgpel ***array2D, int dim0, int dim1) +{ + int i; + + if((*array2D = (imgpel**)mem_malloc(dim0 * sizeof(imgpel*))) == NULL) + no_mem_exit("get_mem2Dpel: array2D"); + if((*(*array2D) = (imgpel* )mem_malloc(dim0 * dim1 * sizeof(imgpel ))) == NULL) + no_mem_exit("get_mem2Dpel: array2D"); + + for(i = 1 ; i < dim0; i++) + { + (*array2D)[i] = (*array2D)[i-1] + dim1; + } + + return dim0 * (sizeof(imgpel*) + dim1 * sizeof(imgpel)); +} + +int get_mem2Dpel_pad(imgpel ***array2D, int dim0, int dim1, int iPadY, int iPadX) +{ + int i; + imgpel *curr = NULL; + int iHeight, iWidth; + + iHeight = dim0+2*iPadY; + iWidth = dim1+2*iPadX; + if((*array2D = (imgpel**)mem_malloc(iHeight*sizeof(imgpel*))) == NULL) + no_mem_exit("get_mem2Dpel_pad: array2D"); + if((*(*array2D) = (imgpel* )mem_calloc(iHeight * iWidth, sizeof(imgpel ))) == NULL) + no_mem_exit("get_mem2Dpel_pad: array2D"); + + (*array2D)[0] += iPadX; + curr = (*array2D)[0]; + for(i = 1 ; i < iHeight; i++) + { + curr += iWidth; + (*array2D)[i] = curr; + } + (*array2D) = &((*array2D)[iPadY]); + + return iHeight * (sizeof(imgpel*) + iWidth * sizeof(imgpel)); +} + + +/*! + ************************************************************************ + * \brief + * Allocate 3D memory array -> imgpel array3D[dim0][dim1][dim2] + * + * \par Output: + * memory size in bytes + ************************************************************************ + */ +int get_mem3Dpel(imgpel ****array3D, int dim0, int dim1, int dim2) +{ + int i, mem_size = dim0 * sizeof(imgpel**); + + if(((*array3D) = (imgpel***)malloc(dim0 * sizeof(imgpel**))) == NULL) + no_mem_exit("get_mem3Dpel: array3D"); + + mem_size += get_mem2Dpel(*array3D, dim0 * dim1, dim2); + + for(i = 1; i < dim0; i++) + (*array3D)[i] = (*array3D)[i - 1] + dim1; + + return mem_size; +} + +int get_mem3Dpel_pad(imgpel ****array3D, int dim0, int dim1, int dim2, int iPadY, int iPadX) +{ + int i, mem_size = dim0 * sizeof(imgpel**); + + if(((*array3D) = (imgpel***)mem_malloc(dim0*sizeof(imgpel**))) == NULL) + no_mem_exit("get_mem3Dpel_pad: array3D"); + + for(i = 0; i < dim0; i++) + mem_size += get_mem2Dpel_pad((*array3D)+i, dim1, dim2, iPadY, iPadX); + + return mem_size; +} + + +/*! + ************************************************************************ + * \brief + * Allocate 4D memory array -> imgpel array4D[dim0][dim1][dim2][dim3] + * + * \par Output: + * memory size in bytes + ************************************************************************ + */ +int get_mem4Dpel(imgpel *****array4D, int dim0, int dim1, int dim2, int dim3) +{ + int i, mem_size = dim0 * sizeof(imgpel***); + + if(((*array4D) = (imgpel****)mem_malloc(dim0 * sizeof(imgpel***))) == NULL) + no_mem_exit("get_mem4Dpel: array4D"); + + mem_size += get_mem3Dpel(*array4D, dim0 * dim1, dim2, dim3); + + for(i = 1; i < dim0; i++) + (*array4D)[i] = (*array4D)[i - 1] + dim1; + + return mem_size; +} + + +int get_mem4Dpel_pad(imgpel *****array4D, int dim0, int dim1, int dim2, int dim3, int iPadY, int iPadX) +{ + int i, mem_size = dim0 * sizeof(imgpel***); + + if(((*array4D) = (imgpel****)mem_malloc(dim0 * sizeof(imgpel***))) == NULL) + no_mem_exit("get_mem4Dpel_pad: array4D"); + + mem_size += get_mem3Dpel_pad(*array4D, dim0 * dim1, dim2, dim3, iPadY, iPadX); + + for(i = 1; i < dim0; i++) + (*array4D)[i] = (*array4D)[i - 1] + dim1; + + return mem_size; +} + +/*! + ************************************************************************ + * \brief + * Allocate 5D memory array -> imgpel array5D[dim0][dim1][dim2][dim3][dim4] + * + * \par Output: + * memory size in bytes + ************************************************************************ + */ +int get_mem5Dpel(imgpel ******array5D, int dim0, int dim1, int dim2, int dim3, int dim4) +{ + int i, mem_size = dim0 * sizeof(imgpel****); + + if(((*array5D) = (imgpel*****)mem_malloc(dim0 * sizeof(imgpel****))) == NULL) + no_mem_exit("get_mem5Dpel: array5D"); + + mem_size += get_mem4Dpel(*array5D, dim0 * dim1, dim2, dim3, dim4); + + for(i = 1; i < dim0; i++) + (*array5D)[i] = (*array5D)[i - 1] + dim1; + + return mem_size; +} + +int get_mem5Dpel_pad(imgpel ******array5D, int dim0, int dim1, int dim2, int dim3, int dim4, int iPadY, int iPadX) +{ + int i, mem_size = dim0 * sizeof(imgpel****); + + if(((*array5D) = (imgpel*****)mem_malloc(dim0 * sizeof(imgpel****))) == NULL) + no_mem_exit("get_mem5Dpel_pad: array5D"); + + mem_size += get_mem4Dpel_pad(*array5D, dim0 * dim1, dim2, dim3, dim4, iPadY, iPadX); + + for(i = 1; i < dim0; i++) + (*array5D)[i] = (*array5D)[i - 1] + dim1; + + return mem_size; +} + +/*! + ************************************************************************ + * \brief + * free 1D memory array + * which was allocated with get_mem1Dpel() + ************************************************************************ + */ +void free_mem1Dpel(imgpel *array1D) +{ + if (array1D) + { + mem_free (array1D); + } + else + { + error ("free_mem1Dpel: trying to free unused memory",100); + } +} + +/*! + ************************************************************************ + * \brief + * free 2D memory array + * which was allocated with get_mem2Dpel() + ************************************************************************ + */ +void free_mem2Dpel(imgpel **array2D) +{ + if (array2D) + { + if (*array2D) + mem_free (*array2D); + else + error ("free_mem2Dpel: trying to free unused memory",100); + + mem_free (array2D); + } + else + { + error ("free_mem2Dpel: trying to free unused memory",100); + } +} + +void free_mem2Dpel_pad(imgpel **array2D, int iPadY, int iPadX) +{ + if (array2D) + { + if (*array2D) + { + mem_free (array2D[-iPadY]-iPadX); + } + else + error ("free_mem2Dpel_pad: trying to free unused memory",100); + + mem_free (&array2D[-iPadY]); + } + else + { + error ("free_mem2Dpel_pad: trying to free unused memory",100); + } +} + +/*! + ************************************************************************ + * \brief + * free 3D memory array + * which was allocated with get_mem3Dpel() + ************************************************************************ + */ +void free_mem3Dpel(imgpel ***array3D) +{ + if (array3D) + { + free_mem2Dpel(*array3D); + mem_free (array3D); + } + else + { + error ("free_mem3Dpel: trying to free unused memory",100); + } +} + +void free_mem3Dpel_pad(imgpel ***array3D, int iDim12, int iPadY, int iPadX) +{ + if (array3D) + { + int i; + for(i=0; i byte array2D[dim0][dim1] + * + * \par Output: + * byte type array of size dim0 * dim1 + ************************************************************************ + */ +byte** new_mem2D(int dim0, int dim1) +{ + int i; + byte **array2D; + + if(( array2D = (byte**)mem_malloc(dim0 * sizeof(byte*))) == NULL) + no_mem_exit("get_mem2D: array2D"); + if((*(array2D) = (byte* )mem_calloc(dim0 * dim1,sizeof(byte ))) == NULL) + no_mem_exit("get_mem2D: array2D"); + + for(i = 1; i < dim0; i++) + array2D[i] = array2D[i-1] + dim1; + + return (array2D); +} + + + +/*! + ************************************************************************ + * \brief + * Allocate 2D memory array -> unsigned char array2D[dim0][dim1] + * + * \par Output: + * memory size in bytes + ************************************************************************/ +int get_mem2D(byte ***array2D, int dim0, int dim1) +{ + int i; + + if(( *array2D = (byte**)mem_malloc(dim0 * sizeof(byte*))) == NULL) + no_mem_exit("get_mem2D: array2D"); + if((*(*array2D) = (byte* )mem_calloc(dim0 * dim1,sizeof(byte ))) == NULL) + no_mem_exit("get_mem2D: array2D"); + + for(i = 1; i < dim0; i++) + (*array2D)[i] = (*array2D)[i-1] + dim1; + + return dim0 * (sizeof(byte*) + dim1 * sizeof(byte)); +} + + +/*! + ************************************************************************ + * \brief + * Create 2D memory array -> int array2D[dim0][dim1] + * + * \par Output: + * int type array of size dim0 * dim1 + ************************************************************************ + */ +int** new_mem2Dint(int dim0, int dim1) +{ + int i; + int **array2D; + + if((array2D = (int**)mem_malloc(dim0 * sizeof(int*))) == NULL) + no_mem_exit("get_mem2Dint: array2D"); + if((*(array2D) = (int* )mem_calloc(dim0 * dim1, sizeof(int ))) == NULL) + no_mem_exit("get_mem2Dint: array2D"); + + for(i = 1 ; i < dim0; i++) + (array2D)[i] = (array2D)[i-1] + dim1; + + return (array2D); +} + +/*! + ************************************************************************ + * \brief + * Allocate 2D memory array -> int array2D[dim0][dim1] + * + * \par Output: + * memory size in bytes + ************************************************************************ + */ +int get_mem2Dint(int ***array2D, int dim0, int dim1) +{ + int i; + + if((*array2D = (int**)mem_malloc(dim0 * sizeof(int*))) == NULL) + no_mem_exit("get_mem2Dint: array2D"); + if((*(*array2D) = (int* )mem_calloc(dim0 * dim1, sizeof(int ))) == NULL) + no_mem_exit("get_mem2Dint: array2D"); + + for(i = 1 ; i < dim0; i++) + (*array2D)[i] = (*array2D)[i-1] + dim1; + + return dim0 * (sizeof(int*) + dim1 * sizeof(int)); +} + +int get_mem2Dint_pad(int ***array2D, int dim0, int dim1, int iPadY, int iPadX) +{ + int i; + int *curr = NULL; + int iHeight, iWidth; + + iHeight = dim0+2*iPadY; + iWidth = dim1+2*iPadX; + if((*array2D = (int**)mem_malloc(iHeight*sizeof(int*))) == NULL) + no_mem_exit("get_mem2Dint_pad: array2D"); + if((*(*array2D) = (int* )mem_calloc(iHeight * iWidth, sizeof(int ))) == NULL) + no_mem_exit("get_mem2Dint_pad: array2D"); + + (*array2D)[0] += iPadX; + curr = (*array2D)[0]; + for(i = 1 ; i < iHeight; i++) + { + curr += iWidth; + (*array2D)[i] = curr; + } + (*array2D) = &((*array2D)[iPadY]); + + return iHeight * (sizeof(int*) + iWidth * sizeof(int)); +} + +/*! + ************************************************************************ + * \brief + * Allocate 2D memory array -> int64 array2D[dim0][dim1] + * + * \par Output: + * memory size in bytes + ************************************************************************ + */ +int get_mem2Dint64(int64 ***array2D, int dim0, int dim1) +{ + int i; + + if((*array2D = (int64**)mem_malloc(dim0 * sizeof(int64*))) == NULL) + no_mem_exit("get_mem2Dint64: array2D"); + if((*(*array2D) = (int64* )mem_calloc(dim0 * dim1,sizeof(int64 ))) == NULL) + no_mem_exit("get_mem2Dint64: array2D"); + + for(i = 1; i < dim0; i++) + (*array2D)[i] = (*array2D)[i-1] + dim1; + + return dim0 * (sizeof(int64*) + dim1 * sizeof(int64)); +} + +/*! + ************************************************************************ + * \brief + * Allocate 3D memory array -> unsigned char array3D[dim0][dim1][dim2] + * + * \par Output: + * memory size in bytes + ************************************************************************ + */ +int get_mem3D(byte ****array3D, int dim0, int dim1, int dim2) +{ + int i, mem_size = dim0 * sizeof(byte**); + + if(((*array3D) = (byte***)mem_malloc(dim0 * sizeof(byte**))) == NULL) + no_mem_exit("get_mem3D: array3D"); + + mem_size += get_mem2D(*array3D, dim0 * dim1, dim2); + + for(i = 1; i < dim0; i++) + (*array3D)[i] = (*array3D)[i-1] + dim1; + + return mem_size; +} + +/*! + ************************************************************************ + * \brief + * Allocate 4D memory array -> unsigned char array4D[dim0][dim1][dim2][dim3] + * + * \par Output: + * memory size in bytes + ************************************************************************ + */ +int get_mem4D(byte *****array4D, int dim0, int dim1, int dim2, int dim3) +{ + int i, mem_size = dim0 * sizeof(byte***); + + if(((*array4D) = (byte****)mem_malloc(dim0 * sizeof(byte***))) == NULL) + no_mem_exit("get_mem4D: array4D"); + + mem_size += get_mem3D(*array4D, dim0 * dim1, dim2, dim3); + + for(i = 1; i < dim0; i++) + (*array4D)[i] = (*array4D)[i-1] + dim1; + + return mem_size; +} + +/*! + ************************************************************************ + * \brief + * Allocate 3D memory array -> int array3D[dim0][dim1][dim2] + * + * \par Output: + * memory size in bytes + ************************************************************************ + */ +int get_mem3Dint(int ****array3D, int dim0, int dim1, int dim2) +{ + int i, mem_size = dim0 * sizeof(int**); + + if(((*array3D) = (int***)mem_malloc(dim0 * sizeof(int**))) == NULL) + no_mem_exit("get_mem3Dint: array3D"); + + mem_size += get_mem2Dint(*array3D, dim0 * dim1, dim2); + + for(i = 1; i < dim0; i++) + (*array3D)[i] = (*array3D)[i-1] + dim1; + + return mem_size; +} + +/*! + ************************************************************************ + * \brief + * Allocate 3D memory array -> int64 array3D[dim0][dim1][dim2] + * + * \par Output: + * memory size in bytes + ************************************************************************ + */ +int get_mem3Dint64(int64 ****array3D, int dim0, int dim1, int dim2) +{ + int i, mem_size = dim0 * sizeof(int64**); + + if(((*array3D) = (int64***)mem_malloc(dim0 * sizeof(int64**))) == NULL) + no_mem_exit("get_mem3Dint64: array3D"); + + mem_size += get_mem2Dint64(*array3D, dim0 * dim1, dim2); + + for(i = 1; i < dim0; i++) + (*array3D)[i] = (*array3D)[i-1] + dim1; + + return mem_size; +} + +int get_mem2Ddistblk(distblk ***array2D, int dim0, int dim1) +{ + int i; + + if((*array2D = (distblk**)mem_malloc(dim0 * sizeof(distblk*))) == NULL) + no_mem_exit("get_mem2Ddistblk: array2D"); + if((*(*array2D) = (distblk* )mem_calloc(dim0 * dim1,sizeof(distblk ))) == NULL) + no_mem_exit("get_mem2Ddistblk: array2D"); + + for(i = 1; i < dim0; i++) + (*array2D)[i] = (*array2D)[i-1] + dim1; + + return dim0 * (sizeof(distblk*) + dim1 * sizeof(distblk)); +} + +int get_mem3Ddistblk(distblk ****array3D, int dim0, int dim1, int dim2) +{ + int i, mem_size = dim0 * sizeof(distblk**); + + if(((*array3D) = (distblk***)mem_malloc(dim0 * sizeof(distblk**))) == NULL) + no_mem_exit("get_mem3Ddistblk: array3D"); + + mem_size += get_mem2Ddistblk(*array3D, dim0 * dim1, dim2); + + for(i = 1; i < dim0; i++) + (*array3D)[i] = (*array3D)[i-1] + dim1; + + return mem_size; +} + +int get_mem4Ddistblk(distblk *****array4D, int dim0, int dim1, int dim2, int dim3) +{ + int i, mem_size = dim0 * sizeof(distblk***); + + if(((*array4D) = (distblk****)mem_malloc(dim0 * sizeof(distblk***))) == NULL) + no_mem_exit("get_mem4Ddistblk: array4D"); + + mem_size += get_mem3Ddistblk(*array4D, dim0 * dim1, dim2, dim3); + + for(i = 1; i < dim0; i++) + (*array4D)[i] = (*array4D)[i-1] + dim1; + + return mem_size; +} +/*! + ************************************************************************ + * \brief + * Allocate 4D memory array -> int array4D[dim0][dim1][dim2][dim3] + * + * \par Output: + * memory size in bytes + ************************************************************************ + */ +int get_mem4Dint(int *****array4D, int dim0, int dim1, int dim2, int dim3) +{ + int i, mem_size = dim0 * sizeof(int***); + + if(((*array4D) = (int****)mem_malloc(dim0 * sizeof(int***))) == NULL) + no_mem_exit("get_mem4Dint: array4D"); + + mem_size += get_mem3Dint(*array4D, dim0 * dim1, dim2, dim3); + + for(i = 1; i < dim0; i++) + (*array4D)[i] = (*array4D)[i-1] + dim1; + + return mem_size; +} + +int get_mem4Dint64(int64 *****array4D, int dim0, int dim1, int dim2, int dim3) +{ + int i, mem_size = dim0 * sizeof(int64***); + + if(((*array4D) = (int64****)mem_malloc(dim0 * sizeof(int64***))) == NULL) + no_mem_exit("get_mem4Dint64: array4D"); + + mem_size += get_mem3Dint64(*array4D, dim0 * dim1, dim2, dim3); + + for(i = 1; i < dim0; i++) + (*array4D)[i] = (*array4D)[i-1] + dim1; + + return mem_size; +} + +/*! + ************************************************************************ + * \brief + * Allocate 5D memory array -> int array5D[dim0][dim1][dim2][dim3][dim4] + * + * \par Output: + * memory size in bytes + ************************************************************************ + */ +int get_mem5Dint(int ******array5D, int dim0, int dim1, int dim2, int dim3, int dim4) +{ + int i, mem_size = dim0 * sizeof(int****); + + if(((*array5D) = (int*****)mem_malloc(dim0 * sizeof(int****))) == NULL) + no_mem_exit("get_mem5Dint: array5D"); + + mem_size += get_mem4Dint(*array5D, dim0 * dim1, dim2, dim3, dim4); + + for(i = 1; i < dim0; i++) + (*array5D)[i] = (*array5D)[i-1] + dim1; + + return mem_size; +} + + +/*! + ************************************************************************ + * \brief + * free 2D memory array + * which was allocated with get_mem2D() + ************************************************************************ + */ +void free_mem2D(byte **array2D) +{ + if (array2D) + { + if (*array2D) + mem_free (*array2D); + else + error ("free_mem2D: trying to free unused memory",100); + + mem_free (array2D); + } + else + { + error ("free_mem2D: trying to free unused memory",100); + } +} + +/*! + ************************************************************************ + * \brief + * free 2D memory array + * which was allocated with get_mem2Dint() + ************************************************************************ + */ +void free_mem2Dint(int **array2D) +{ + if (array2D) + { + if (*array2D) + mem_free (*array2D); + else + error ("free_mem2Dint: trying to free unused memory",100); + + mem_free (array2D); + } + else + { + error ("free_mem2Dint: trying to free unused memory",100); + } +} + +void free_mem2Dint_pad(int **array2D, int iPadY, int iPadX) +{ + if (array2D) + { + if (*array2D) + mem_free (array2D[-iPadY]-iPadX); + else + error ("free_mem2Dint_pad: trying to free unused memory",100); + + mem_free (&array2D[-iPadY]); + } + else + { + error ("free_mem2Dint_pad: trying to free unused memory",100); + } +} + +/*! + ************************************************************************ + * \brief + * free 2D memory array + * which was allocated with get_mem2Dint64() + ************************************************************************ + */ +void free_mem2Dint64(int64 **array2D) +{ + if (array2D) + { + if (*array2D) + mem_free (*array2D); + else + error ("free_mem2Dint64: trying to free unused memory",100); + mem_free (array2D); + } + else + { + error ("free_mem2Dint64: trying to free unused memory",100); + } +} + + +/*! + ************************************************************************ + * \brief + * free 3D memory array + * which was allocated with get_mem3D() + ************************************************************************ + */ +void free_mem3D(byte ***array3D) +{ + if (array3D) + { + free_mem2D(*array3D); + mem_free (array3D); + } + else + { + error ("free_mem3D: trying to free unused memory",100); + } +} + +/*! + ************************************************************************ + * \brief + * free 4D memory array + * which was allocated with get_mem3D() + ************************************************************************ + */ +void free_mem4D(byte ****array4D) +{ + if (array4D) + { + free_mem3D(*array4D); + mem_free (array4D); + } + else + { + error ("free_mem4D: trying to free unused memory",100); + } +} + +/*! + ************************************************************************ + * \brief + * free 3D memory array + * which was allocated with get_mem3Dint() + ************************************************************************ + */ +void free_mem3Dint(int ***array3D) +{ + if (array3D) + { + free_mem2Dint(*array3D); + mem_free (array3D); + } + else + { + error ("free_mem3Dint: trying to free unused memory",100); + } +} + + +/*! + ************************************************************************ + * \brief + * free 3D memory array + * which was allocated with get_mem3Dint64() + ************************************************************************ + */ +void free_mem3Dint64(int64 ***array3D) +{ + if (array3D) + { + free_mem2Dint64(*array3D); + mem_free (array3D); + } + else + { + error ("free_mem3Dint64: trying to free unused memory",100); + } +} + +void free_mem3Ddistblk(distblk ***array3D) +{ + if (array3D) + { + free_mem2Ddistblk(*array3D); + mem_free (array3D); + } + else + { + error ("free_mem3Ddistblk: trying to free unused memory",100); + } +} + +/*! + ************************************************************************ + * \brief + * free 4D memory array + * which was allocated with get_mem4Dint() + ************************************************************************ + */ +void free_mem4Dint(int ****array4D) +{ + if (array4D) + { + free_mem3Dint( *array4D); + mem_free (array4D); + } else + { + error ("free_mem4Dint: trying to free unused memory",100); + } +} + +void free_mem4Dint64(int64 ****array4D) +{ + if (array4D) + { + free_mem3Dint64( *array4D); + mem_free (array4D); + } else + { + error ("free_mem4Dint64: trying to free unused memory",100); + } +} + +void free_mem4Ddistblk(distblk ****array4D) +{ + if (array4D) + { + free_mem3Ddistblk( *array4D); + mem_free (array4D); + } else + { + error ("free_mem4Ddistblk: trying to free unused memory",100); + } +} + +/*! + ************************************************************************ + * \brief + * free 5D int memory array + * which was allocated with get_mem5Dint() + ************************************************************************ + */ +void free_mem5Dint(int *****array5D) +{ + if (array5D) + { + free_mem4Dint( *array5D); + mem_free (array5D); + } else + { + error ("free_mem5Dint: trying to free unused memory",100); + } +} + +/*! + ************************************************************************ + * \brief + * Exit program if memory allocation failed (using error()) + * \param where + * string indicating which memory allocation failed + ************************************************************************ + */ +void no_mem_exit(char *where) +{ + snprintf(errortext, ET_SIZE, "Could not allocate memory: %s",where); + error (errortext, 100); +} + + +/*! + ************************************************************************ + * \brief + * Create 2D memory array -> uint16 array2D[dim0][dim1] + * + * \par Output: + * uint16 type array of size dim0 * dim1 + ************************************************************************ + */ +uint16** new_mem2Duint16(int dim0, int dim1) +{ + int i; + uint16 **array2D; + + if(( array2D = (uint16**)mem_malloc(dim0 * sizeof(uint16*))) == NULL) + no_mem_exit("get_mem2Duint16: array2D"); + if((*array2D = (uint16* )mem_calloc(dim0 * dim1,sizeof(uint16 ))) == NULL) + no_mem_exit("get_mem2Duint16: array2D"); + + for(i = 1; i < dim0; i++) + array2D[i] = array2D[i-1] + dim1; + + return (array2D); +} + +/*! + ************************************************************************ + * \brief + * Allocate 2D uint16 memory array -> uint16 array2D[dim0][dim1] + * + * \par Output: + * memory size in bytes + ************************************************************************ + */ +int get_mem2Duint16(uint16 ***array2D, int dim0, int dim1) +{ + int i; + + if(( *array2D = (uint16**)mem_malloc(dim0 * sizeof(uint16*))) == NULL) + no_mem_exit("get_mem2Duint16: array2D"); + + if((*(*array2D) = (uint16* )mem_calloc(dim0 * dim1,sizeof(uint16 ))) == NULL) + no_mem_exit("get_mem2Duint16: array2D"); + + for(i = 1; i < dim0; i++) + (*array2D)[i] = (*array2D)[i-1] + dim1; + + return dim0 * (sizeof(uint16*) + dim1 * sizeof(uint16)); +} + +/*! + ************************************************************************ + * \brief + * Allocate 3D memory uint16 array -> uint16 array3D[dim0][dim1][dim2] + * + * \par Output: + * memory size in bytes + ************************************************************************ + */ +int get_mem3Duint16(uint16 ****array3D,int dim0, int dim1, int dim2) +{ + int i, mem_size = dim0 * sizeof(uint16**); + + if(((*array3D) = (uint16***)mem_malloc(dim0 * sizeof(uint16**))) == NULL) + no_mem_exit("get_mem3Duint16: array3D"); + + mem_size += get_mem2Duint16(*array3D, dim0 * dim1, dim2); + + for(i = 1; i < dim0; i++) + (*array3D)[i] = (*array3D)[i-1] + dim1; + + return mem_size; +} + + +/*! + ************************************************************************ + * \brief + * Allocate 4D memory uint16 array -> uint16 array3D[dim0][dim1][dim2][dim3] + * + * \par Output: + * memory size in bytes + ************************************************************************ + */ +int get_mem4Duint16(uint16 *****array4D, int dim0, int dim1, int dim2, int dim3) +{ + int i, mem_size = dim0 * sizeof(uint16***); + + if(((*array4D) = (uint16****)mem_malloc(dim0 * sizeof(uint16***))) == NULL) + no_mem_exit("get_mem4Duint16: array4D"); + + mem_size += get_mem3Duint16(*array4D, dim0 * dim1, dim2, dim3); + + for(i = 1; i < dim0; i++) + (*array4D)[i] = (*array4D)[i-1] + dim1; + + return mem_size; +} + +/*! + ************************************************************************ + * \brief + * Allocate 2D short memory array -> short array2D[dim0][dim1] + * + * \par Output: + * memory size in bytes + ************************************************************************ + */ +int get_mem2Dshort(short ***array2D, int dim0, int dim1) +{ + int i; + short *curr = NULL; + + if(( *array2D = (short**)mem_malloc(dim0 * sizeof(short*))) == NULL) + no_mem_exit("get_mem2Dshort: array2D"); + if((*(*array2D) = (short* )mem_calloc(dim0 * dim1,sizeof(short ))) == NULL) + no_mem_exit("get_mem2Dshort: array2D"); + + curr = (*array2D)[0]; + for(i = 1; i < dim0; i++) + { + curr += dim1; + (*array2D)[i] = curr; + } + + return dim0 * (sizeof(short*) + dim1 * sizeof(short)); +} + +/*! + ************************************************************************ + * \brief + * Allocate 3D memory short array -> short array3D[dim0][dim1][dim2] + * + * \par Output: + * memory size in bytes + ************************************************************************ + */ +int get_mem3Dshort(short ****array3D,int dim0, int dim1, int dim2) +{ + int i, mem_size = dim0 * sizeof(short**); + short **curr = NULL; + + if(((*array3D) = (short***)mem_malloc(dim0 * sizeof(short**))) == NULL) + no_mem_exit("get_mem3Dshort: array3D"); + + mem_size += get_mem2Dshort(*array3D, dim0 * dim1, dim2); + + curr = (*array3D)[0]; + for(i = 1; i < dim0; i++) + { + curr += dim1; + (*array3D)[i] = curr; + } + + return mem_size; +} + + +/*! + ************************************************************************ + * \brief + * Allocate 4D memory short array -> short array3D[dim0][dim1][dim2][dim3] + * + * \par Output: + * memory size in bytes + ************************************************************************ + */ +int get_mem4Dshort(short *****array4D, int dim0, int dim1, int dim2, int dim3) +{ + int i, mem_size = dim0 * sizeof(short***); + + if(((*array4D) = (short****)mem_malloc(dim0 * sizeof(short***))) == NULL) + no_mem_exit("get_mem4Dshort: array4D"); + + mem_size += get_mem3Dshort(*array4D, dim0 * dim1, dim2, dim3); + + for(i = 1; i < dim0; i++) + (*array4D)[i] = (*array4D)[i-1] + dim1; + + return mem_size; +} + +/*! + ************************************************************************ + * \brief + * Allocate 5D memory array -> short array5D[dim0][dim1][dim2][dim3][dim4] + * + * \par Output: + * memory size in bytes + ************************************************************************ + */ +int get_mem5Dshort(short ******array5D, int dim0, int dim1, int dim2, int dim3, int dim4) +{ + int i, mem_size = dim0 * sizeof(short****); + + if(((*array5D) = (short*****)mem_malloc(dim0 * sizeof(short****))) == NULL) + no_mem_exit("get_mem5Dshort: array5D"); + + mem_size += get_mem4Dshort(*array5D, dim0 * dim1, dim2, dim3, dim4); + + for(i = 1; i < dim0; i++) + (*array5D)[i] = (*array5D)[i-1] + dim1; + + return mem_size; +} + +/*! + ************************************************************************ + * \brief + * Allocate 6D memory array -> short array6D[dim0][dim1][dim2][dim3][dim4][dim5] + * + * \par Output: + * memory size in bytes + ************************************************************************ + */ +int get_mem6Dshort(short *******array6D, int dim0, int dim1, int dim2, int dim3, int dim4, int dim5) +{ + int i, mem_size = dim0 * sizeof(short*****); + + if(((*array6D) = (short******)mem_malloc(dim0 * sizeof(short*****))) == NULL) + no_mem_exit("get_mem6Dshort: array6D"); + + mem_size += get_mem5Dshort(*array6D, dim0 * dim1, dim2, dim3, dim4, dim5); + + for(i = 1; i < dim0; i++) + (*array6D)[i] = (*array6D)[i-1] + dim1; + + return mem_size; +} + +/*! + ************************************************************************ + * \brief + * Allocate 7D memory array -> short array7D[dim0][dim1][dim2][dim3][dim4][dim5][dim6] + * + * \par Output: + * memory size in bytes + ************************************************************************ + */ +int get_mem7Dshort(short ********array7D, int dim0, int dim1, int dim2, int dim3, int dim4, int dim5, int dim6) +{ + int i, mem_size = dim0 * sizeof(short******); + + if(((*array7D) = (short*******)mem_malloc(dim0 * sizeof(short******))) == NULL) + no_mem_exit("get_mem7Dshort: array7D"); + + mem_size += get_mem6Dshort(*array7D, dim0 * dim1, dim2, dim3, dim4, dim5, dim6); + + for(i = 1; i < dim0; i++) + (*array7D)[i] = (*array7D)[i-1] + dim1; + + return mem_size; +} + +/*! + ************************************************************************ + * \brief + * free 2D uint16 memory array + * which was allocated with get_mem2Duint16() + ************************************************************************ + */ +void free_mem2Duint16(uint16 **array2D) +{ + if (array2D) + { + if (*array2D) + mem_free (*array2D); + else error ("free_mem2Duint16: trying to free unused memory",100); + + mem_free (array2D); + } + else + { + error ("free_mem2Duint16: trying to free unused memory",100); + } +} + +/*! + ************************************************************************ + * \brief + * free 3D uint16 memory array + * which was allocated with get_mem3Duint16() + ************************************************************************ + */ +void free_mem3Duint16(uint16 ***array3D) +{ + if (array3D) + { + free_mem2Duint16(*array3D); + mem_free (array3D); + } + else + { + error ("free_mem3Duint16: trying to free unused memory",100); + } +} + +/*! + ************************************************************************ + * \brief + * free 4D uint16 memory array + * which was allocated with get_mem4Duint16() + ************************************************************************ + */ +void free_mem4Duint16(uint16 ****array4D) +{ + if (array4D) + { + free_mem3Duint16( *array4D); + mem_free (array4D); + } + else + { + error ("free_mem4Duint16: trying to free unused memory",100); + } +} + +/*! + ************************************************************************ + * \brief + * free 2D short memory array + * which was allocated with get_mem2Dshort() + ************************************************************************ + */ +void free_mem2Dshort(short **array2D) +{ + if (array2D) + { + if (*array2D) + mem_free (*array2D); + else error ("free_mem2Dshort: trying to free unused memory",100); + + mem_free (array2D); + } + else + { + error ("free_mem2Dshort: trying to free unused memory",100); + } +} + +/*! + ************************************************************************ + * \brief + * free 3D short memory array + * which was allocated with get_mem3Dshort() + ************************************************************************ + */ +void free_mem3Dshort(short ***array3D) +{ + if (array3D) + { + free_mem2Dshort(*array3D); + mem_free (array3D); + } + else + { + error ("free_mem3Dshort: trying to free unused memory",100); + } +} + +/*! + ************************************************************************ + * \brief + * free 4D short memory array + * which was allocated with get_mem4Dshort() + ************************************************************************ + */ +void free_mem4Dshort(short ****array4D) +{ + if (array4D) + { + free_mem3Dshort( *array4D); + mem_free (array4D); + } + else + { + error ("free_mem4Dshort: trying to free unused memory",100); + } +} + +/*! + ************************************************************************ + * \brief + * free 5D short memory array + * which was allocated with get_mem5Dshort() + ************************************************************************ + */ +void free_mem5Dshort(short *****array5D) +{ + if (array5D) + { + free_mem4Dshort( *array5D) ; + mem_free (array5D); + } + else + { + error ("free_mem5Dshort: trying to free unused memory",100); + } +} + +/*! + ************************************************************************ + * \brief + * free 6D short memory array + * which was allocated with get_mem6Dshort() + ************************************************************************ + */ +void free_mem6Dshort(short ******array6D) +{ + if (array6D) + { + free_mem5Dshort( *array6D); + mem_free (array6D); + } + else + { + error ("free_mem6Dshort: trying to free unused memory",100); + } +} + +/*! + ************************************************************************ + * \brief + * free 7D short memory array + * which was allocated with get_mem7Dshort() + ************************************************************************ + */ +void free_mem7Dshort(short *******array7D) +{ + if (array7D) + { + free_mem6Dshort( *array7D); + mem_free (array7D); + } + else + { + error ("free_mem7Dshort: trying to free unused memory",100); + } +} + +/*! + ************************************************************************ + * \brief + * Allocate 2D memory array -> double array2D[dim0][dim1] + * + * \par Output: + * memory size in bytes + ************************************************************************ + */ +int get_mem2Ddouble(double ***array2D, int dim0, int dim1) +{ + int i; + + if((*array2D = (double**)mem_malloc(dim0 * sizeof(double*))) == NULL) + no_mem_exit("get_mem2Ddouble: array2D"); + + if(((*array2D)[0] = (double* )mem_calloc(dim0 * dim1, sizeof(double ))) == NULL) + no_mem_exit("get_mem2Ddouble: array2D"); + + for(i=1 ; i double array1D[dim0] + * Note that array is shifted towards offset allowing negative values + * + * \par Output: + * memory size in bytes + ************************************************************************ + */ +int get_mem1Dodouble(double **array1D, int dim0, int offset) +{ + if((*array1D = (double*)mem_calloc(dim0, sizeof(double))) == NULL) + no_mem_exit("get_mem1Dodouble: array2D"); + + *array1D += offset; + + return dim0 * sizeof(double); +} + +/*! + ************************************************************************ + * \brief + * Allocate 2D memory array -> double array2D[dim0][dim1] + * Note that array is shifted towards offset allowing negative values + * + * \par Output: + * memory size in bytes + ************************************************************************ + */ +int get_mem2Dodouble(double ***array2D, int dim0, int dim1, int offset) +{ + int i; + + if((*array2D = (double**)mem_malloc(dim0 * sizeof(double*))) == NULL) + no_mem_exit("get_mem2Dodouble: array2D"); + if(((*array2D)[0] = (double* )mem_calloc(dim0 * dim1,sizeof(double ))) == NULL) + no_mem_exit("get_mem2Dodouble: array2D"); + + (*array2D)[0] += offset; + + for(i=1 ; i double array3D[dim0][dim1][dim2] + * + * \par Output: + * memory size in bytes + ************************************************************************ + */ +int get_mem3Dodouble(double ****array3D, int dim0, int dim1, int dim2, int offset) +{ + int i,j; + + if(((*array3D) = (double***)mem_malloc(dim0 * sizeof(double**))) == NULL) + no_mem_exit("get_mem3Dodouble: array3D"); + + if(((*array3D)[0] = (double** )mem_calloc(dim0 * dim1,sizeof(double*))) == NULL) + no_mem_exit("get_mem3Dodouble: array3D"); + + (*array3D) [0] += offset; + + for(i=1 ; i int array2D[dim0][dim1] + * Note that array is shifted towards offset allowing negative values + * + * \par Output: + * memory size in bytes + ************************************************************************ + */ +int get_offset_mem2Dshort(short ***array2D, int dim0, int dim1, int offset_y, int offset_x) +{ + int i; + + if((*array2D = (short**)mem_malloc(dim0 * sizeof(short*))) == NULL) + no_mem_exit("get_offset_mem2Dshort: array2D"); + + if(((*array2D)[0] = (short* )mem_calloc(dim0 * dim1,sizeof(short))) == NULL) + no_mem_exit("get_offset_mem2Dshort: array2D"); + (*array2D)[0] += offset_x + offset_y * dim1; + + for(i=-1 ; i > -offset_y - 1; i--) + { + (*array2D)[i] = (*array2D)[i+1] - dim1; + } + + for(i=1 ; i < dim1 - offset_y; i++) + (*array2D)[i] = (*array2D)[i-1] + dim1; + + return dim0 * (sizeof(short*) + dim1 * sizeof(short)); +} + +/*! + ************************************************************************ + * \brief + * Allocate 3D memory int array -> int array3D[dim0][dim1][dim2] + * + * \par Output: + * memory size in bytes + ************************************************************************ + */ +int get_mem3Doint(int ****array3D, int dim0, int dim1, int dim2, int offset) +{ + int i,j; + + if(((*array3D) = (int***)mem_malloc(dim0 * sizeof(int**))) == NULL) + no_mem_exit("get_mem3Doint: array3D"); + + if(((*array3D)[0] = (int** )mem_calloc(dim0 * dim1,sizeof(int*))) == NULL) + no_mem_exit("get_mem3Doint: array3D"); + + (*array3D) [0] += offset; + + for(i=1 ; i int array2D[dim0][dim1] + * Note that array is shifted towards offset allowing negative values + * + * \par Output: + * memory size in bytes + ************************************************************************ + */ +int get_mem2Doint(int ***array2D, int dim0, int dim1, int offset) +{ + int i; + + if((*array2D = (int**)mem_malloc(dim0 * sizeof(int*))) == NULL) + no_mem_exit("get_mem2Dint: array2D"); + if(((*array2D)[0] = (int* )mem_calloc(dim0 * dim1,sizeof(int))) == NULL) + no_mem_exit("get_mem2Dint: array2D"); + + (*array2D)[0] += offset; + + for(i=1 ; i LambdaParams array2D[dim0][dim1] + * Note that array is shifted towards offset allowing negative values + * + * \par Output: + * memory size in bytes + ************************************************************************ + */ +int get_mem2Dolm(LambdaParams ***array2D, int dim0, int dim1, int offset) +{ + int i; + + if((*array2D = (LambdaParams**)mem_malloc(dim0 * sizeof(LambdaParams*))) == NULL) + no_mem_exit("get_mem2Dolm: array2D"); + if(((*array2D)[0] = (LambdaParams* )mem_calloc(dim0 * dim1, sizeof(LambdaParams))) == NULL) + no_mem_exit("get_mem2Dolm: array2D"); + + (*array2D)[0] += offset; + + for(i=1 ; i + * - Karsten Suehring + ************************************************************************************* + */ + +#include "global.h" +#include "mbuffer.h" + +/*! + ************************************************************************ + * \brief + * Get motion vector predictor + ************************************************************************ + */ +static void GetMotionVectorPredictorMBAFF (Macroblock *currMB, + PixelPos *block, // <--> block neighbors + MotionVector *pmv, + short ref_frame, + PicMotionParams **mv_info, + int list, + int mb_x, + int mb_y, + int blockshape_x, + int blockshape_y) +{ + int mv_a, mv_b, mv_c, pred_vec=0; + int mvPredType, rFrameL, rFrameU, rFrameUR; + int hv; + VideoParameters *p_Vid = currMB->p_Vid; + + mvPredType = MVPRED_MEDIAN; + + + if (currMB->mb_field) + { + rFrameL = block[0].available + ? (p_Vid->mb_data[block[0].mb_addr].mb_field + ? mv_info[block[0].pos_y][block[0].pos_x].ref_idx[list] + : mv_info[block[0].pos_y][block[0].pos_x].ref_idx[list] * 2) : -1; + rFrameU = block[1].available + ? (p_Vid->mb_data[block[1].mb_addr].mb_field + ? mv_info[block[1].pos_y][block[1].pos_x].ref_idx[list] + : mv_info[block[1].pos_y][block[1].pos_x].ref_idx[list] * 2) : -1; + rFrameUR = block[2].available + ? (p_Vid->mb_data[block[2].mb_addr].mb_field + ? mv_info[block[2].pos_y][block[2].pos_x].ref_idx[list] + : mv_info[block[2].pos_y][block[2].pos_x].ref_idx[list] * 2) : -1; + } + else + { + rFrameL = block[0].available + ? (p_Vid->mb_data[block[0].mb_addr].mb_field + ? mv_info[block[0].pos_y][block[0].pos_x].ref_idx[list] >>1 + : mv_info[block[0].pos_y][block[0].pos_x].ref_idx[list]) : -1; + rFrameU = block[1].available + ? (p_Vid->mb_data[block[1].mb_addr].mb_field + ? mv_info[block[1].pos_y][block[1].pos_x].ref_idx[list] >>1 + : mv_info[block[1].pos_y][block[1].pos_x].ref_idx[list]) : -1; + rFrameUR = block[2].available + ? (p_Vid->mb_data[block[2].mb_addr].mb_field + ? mv_info[block[2].pos_y][block[2].pos_x].ref_idx[list] >>1 + : mv_info[block[2].pos_y][block[2].pos_x].ref_idx[list]) : -1; + } + + + /* Prediction if only one of the neighbors uses the reference frame + * we are checking + */ + if(rFrameL == ref_frame && rFrameU != ref_frame && rFrameUR != ref_frame) + mvPredType = MVPRED_L; + else if(rFrameL != ref_frame && rFrameU == ref_frame && rFrameUR != ref_frame) + mvPredType = MVPRED_U; + else if(rFrameL != ref_frame && rFrameU != ref_frame && rFrameUR == ref_frame) + mvPredType = MVPRED_UR; + // Directional predictions + if(blockshape_x == 8 && blockshape_y == 16) + { + if(mb_x == 0) + { + if(rFrameL == ref_frame) + mvPredType = MVPRED_L; + } + else + { + if( rFrameUR == ref_frame) + mvPredType = MVPRED_UR; + } + } + else if(blockshape_x == 16 && blockshape_y == 8) + { + if(mb_y == 0) + { + if(rFrameU == ref_frame) + mvPredType = MVPRED_U; + } + else + { + if(rFrameL == ref_frame) + mvPredType = MVPRED_L; + } + } + + for (hv=0; hv < 2; hv++) + { + if (hv == 0) + { + mv_a = block[0].available ? mv_info[block[0].pos_y][block[0].pos_x].mv[list].mv_x : 0; + mv_b = block[1].available ? mv_info[block[1].pos_y][block[1].pos_x].mv[list].mv_x : 0; + mv_c = block[2].available ? mv_info[block[2].pos_y][block[2].pos_x].mv[list].mv_x : 0; + } + else + { + if (currMB->mb_field) + { + mv_a = block[0].available ? p_Vid->mb_data[block[0].mb_addr].mb_field + ? mv_info[block[0].pos_y][block[0].pos_x].mv[list].mv_y + : mv_info[block[0].pos_y][block[0].pos_x].mv[list].mv_y / 2 + : 0; + mv_b = block[1].available ? p_Vid->mb_data[block[1].mb_addr].mb_field + ? mv_info[block[1].pos_y][block[1].pos_x].mv[list].mv_y + : mv_info[block[1].pos_y][block[1].pos_x].mv[list].mv_y / 2 + : 0; + mv_c = block[2].available ? p_Vid->mb_data[block[2].mb_addr].mb_field + ? mv_info[block[2].pos_y][block[2].pos_x].mv[list].mv_y + : mv_info[block[2].pos_y][block[2].pos_x].mv[list].mv_y / 2 + : 0; + } + else + { + mv_a = block[0].available ? p_Vid->mb_data[block[0].mb_addr].mb_field + ? mv_info[block[0].pos_y][block[0].pos_x].mv[list].mv_y * 2 + : mv_info[block[0].pos_y][block[0].pos_x].mv[list].mv_y + : 0; + mv_b = block[1].available ? p_Vid->mb_data[block[1].mb_addr].mb_field + ? mv_info[block[1].pos_y][block[1].pos_x].mv[list].mv_y * 2 + : mv_info[block[1].pos_y][block[1].pos_x].mv[list].mv_y + : 0; + mv_c = block[2].available ? p_Vid->mb_data[block[2].mb_addr].mb_field + ? mv_info[block[2].pos_y][block[2].pos_x].mv[list].mv_y * 2 + : mv_info[block[2].pos_y][block[2].pos_x].mv[list].mv_y + : 0; + } + } + + switch (mvPredType) + { + case MVPRED_MEDIAN: + if(!(block[1].available || block[2].available)) + { + pred_vec = mv_a; + } + else + { + pred_vec = imedian(mv_a, mv_b, mv_c); + } + break; + case MVPRED_L: + pred_vec = mv_a; + break; + case MVPRED_U: + pred_vec = mv_b; + break; + case MVPRED_UR: + pred_vec = mv_c; + break; + default: + break; + } + + if (hv == 0) + pmv->mv_x = (short) pred_vec; + else + pmv->mv_y = (short) pred_vec; + } +} + +/*! + ************************************************************************ + * \brief + * Get motion vector predictor + ************************************************************************ + */ +static void GetMotionVectorPredictorNormal (Macroblock *currMB, + PixelPos *block, // <--> block neighbors + MotionVector *pmv, + short ref_frame, + PicMotionParams **mv_info, + int list, + int mb_x, + int mb_y, + int blockshape_x, + int blockshape_y) +{ + int mvPredType = MVPRED_MEDIAN; + + int rFrameL = block[0].available ? mv_info[block[0].pos_y][block[0].pos_x].ref_idx[list] : -1; + int rFrameU = block[1].available ? mv_info[block[1].pos_y][block[1].pos_x].ref_idx[list] : -1; + int rFrameUR = block[2].available ? mv_info[block[2].pos_y][block[2].pos_x].ref_idx[list] : -1; + + /* Prediction if only one of the neighbors uses the reference frame + * we are checking + */ + if(rFrameL == ref_frame && rFrameU != ref_frame && rFrameUR != ref_frame) + mvPredType = MVPRED_L; + else if(rFrameL != ref_frame && rFrameU == ref_frame && rFrameUR != ref_frame) + mvPredType = MVPRED_U; + else if(rFrameL != ref_frame && rFrameU != ref_frame && rFrameUR == ref_frame) + mvPredType = MVPRED_UR; + + // Directional predictions + if(blockshape_x == 8 && blockshape_y == 16) + { + if(mb_x == 0) + { + if(rFrameL == ref_frame) + mvPredType = MVPRED_L; + } + else + { + if(rFrameUR == ref_frame) + mvPredType = MVPRED_UR; + } + } + else if(blockshape_x == 16 && blockshape_y == 8) + { + if(mb_y == 0) + { + if(rFrameU == ref_frame) + mvPredType = MVPRED_U; + } + else + { + if(rFrameL == ref_frame) + mvPredType = MVPRED_L; + } + } + + switch (mvPredType) + { + case MVPRED_MEDIAN: + if(!(block[1].available || block[2].available)) + { + if (block[0].available) + { + *pmv = mv_info[block[0].pos_y][block[0].pos_x].mv[list]; + } + else + { + *pmv = zero_mv; + } + } + else + { + MotionVector *mv_a = block[0].available ? &mv_info[block[0].pos_y][block[0].pos_x].mv[list] : (MotionVector *) &zero_mv; + MotionVector *mv_b = block[1].available ? &mv_info[block[1].pos_y][block[1].pos_x].mv[list] : (MotionVector *) &zero_mv; + MotionVector *mv_c = block[2].available ? &mv_info[block[2].pos_y][block[2].pos_x].mv[list] : (MotionVector *) &zero_mv; + + pmv->mv_x = (short) imedian(mv_a->mv_x, mv_b->mv_x, mv_c->mv_x); + pmv->mv_y = (short) imedian(mv_a->mv_y, mv_b->mv_y, mv_c->mv_y); + } + break; + case MVPRED_L: + if (block[0].available) + { + *pmv = mv_info[block[0].pos_y][block[0].pos_x].mv[list]; + } + else + { + *pmv = zero_mv; + } + break; + case MVPRED_U: + if (block[1].available) + { + *pmv = mv_info[block[1].pos_y][block[1].pos_x].mv[list]; + } + else + { + *pmv = zero_mv; + } + break; + case MVPRED_UR: + if (block[2].available) + { + *pmv = mv_info[block[2].pos_y][block[2].pos_x].mv[list]; + } + else + { + *pmv = zero_mv; + } + break; + default: + break; + } +} + +void init_motion_vector_prediction(Macroblock *currMB, int mb_aff_frame_flag) +{ + if (mb_aff_frame_flag) + currMB->GetMVPredictor = GetMotionVectorPredictorMBAFF; + else + currMB->GetMVPredictor = GetMotionVectorPredictorNormal; +} diff --git a/lcommon/src/nalucommon.c b/lcommon/src/nalucommon.c new file mode 100644 index 0000000..57074a5 --- /dev/null +++ b/lcommon/src/nalucommon.c @@ -0,0 +1,71 @@ + +/*! + ************************************************************************ + * \file nalucommon.c + * + * \brief + * Common NALU support functions + * + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * - Stephan Wenger + ************************************************************************ + */ + +#include "global.h" +#include "nalucommon.h" +#include "memalloc.h" + +/*! + ************************************************************************************* + * \brief + * Allocates memory for a NALU + * + * \param buffersize + * size of NALU buffer + * + * \return + * pointer to a NALU + ************************************************************************************* + */ +NALU_t *AllocNALU(int buffersize) +{ + NALU_t *n; + + if ((n = (NALU_t*)calloc (1, sizeof (NALU_t))) == NULL) + no_mem_exit ("AllocNALU: n"); + + n->max_size=buffersize; + + if ((n->buf = (byte*)calloc (buffersize, sizeof (byte))) == NULL) + { + free (n); + no_mem_exit ("AllocNALU: n->buf"); + } + + return n; +} + + +/*! + ************************************************************************************* + * \brief + * Frees a NALU + * + * \param n + * NALU to be freed + * + ************************************************************************************* + */ +void FreeNALU(NALU_t *n) +{ + if (n != NULL) + { + if (n->buf != NULL) + { + free(n->buf); + n->buf=NULL; + } + free (n); + } +} diff --git a/lcommon/src/parsetcommon.c b/lcommon/src/parsetcommon.c new file mode 100644 index 0000000..2256e3d --- /dev/null +++ b/lcommon/src/parsetcommon.c @@ -0,0 +1,244 @@ + +/*! + ************************************************************************************** + * \file + * parsetcommon.c + * \brief + * Picture and Sequence Parameter set generation and handling + * \date 25 November 2002 + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * - Stephan Wenger + * + ************************************************************************************** + */ + +#include "global.h" +#include "parsetcommon.h" +#include "memalloc.h" +/*! + ************************************************************************************* + * \brief + * Allocates memory for a picture paramater set + * + * \return + * pointer to a pps + ************************************************************************************* + */ + +pic_parameter_set_rbsp_t *AllocPPS () + { + pic_parameter_set_rbsp_t *p; + + if ((p=calloc (1, sizeof (pic_parameter_set_rbsp_t))) == NULL) + no_mem_exit ("AllocPPS: PPS"); + p->slice_group_id = NULL; + return p; + } + + +/*! + ************************************************************************************* + * \brief + * Allocates memory for am sequence paramater set + * + * \return + * pointer to a sps + ************************************************************************************* + */ + +seq_parameter_set_rbsp_t *AllocSPS () + { + seq_parameter_set_rbsp_t *p; + + if ((p=calloc (1, sizeof (seq_parameter_set_rbsp_t))) == NULL) + no_mem_exit ("AllocSPS: SPS"); + return p; + } + + +/*! + ************************************************************************************* + * \brief + * Frees a picture parameter set + * + * \param pps to be freed + * Picture parameter set to be freed + ************************************************************************************* + */ + + void FreePPS (pic_parameter_set_rbsp_t *pps) + { + assert (pps != NULL); + if (pps->slice_group_id != NULL) + free (pps->slice_group_id); + free (pps); + } + + + /*! + ************************************************************************************* + * \brief + * Frees a sps + * + * \param sps + * Sequence parameter set to be freed + ************************************************************************************* + */ + + void FreeSPS (seq_parameter_set_rbsp_t *sps) + { + assert (sps != NULL); + free (sps); + } + + +int sps_is_equal(seq_parameter_set_rbsp_t *sps1, seq_parameter_set_rbsp_t *sps2) +{ + unsigned i; + int equal = 1; + + if ((!sps1->Valid) || (!sps2->Valid)) + return 0; + + equal &= (sps1->profile_idc == sps2->profile_idc); + equal &= (sps1->constrained_set0_flag == sps2->constrained_set0_flag); + equal &= (sps1->constrained_set1_flag == sps2->constrained_set1_flag); + equal &= (sps1->constrained_set2_flag == sps2->constrained_set2_flag); + equal &= (sps1->level_idc == sps2->level_idc); + equal &= (sps1->seq_parameter_set_id == sps2->seq_parameter_set_id); + equal &= (sps1->log2_max_frame_num_minus4 == sps2->log2_max_frame_num_minus4); + equal &= (sps1->pic_order_cnt_type == sps2->pic_order_cnt_type); + + if (!equal) return equal; + + if( sps1->pic_order_cnt_type == 0 ) + { + equal &= (sps1->log2_max_pic_order_cnt_lsb_minus4 == sps2->log2_max_pic_order_cnt_lsb_minus4); + } + + else if( sps1->pic_order_cnt_type == 1 ) + { + equal &= (sps1->delta_pic_order_always_zero_flag == sps2->delta_pic_order_always_zero_flag); + equal &= (sps1->offset_for_non_ref_pic == sps2->offset_for_non_ref_pic); + equal &= (sps1->offset_for_top_to_bottom_field == sps2->offset_for_top_to_bottom_field); + equal &= (sps1->num_ref_frames_in_pic_order_cnt_cycle == sps2->num_ref_frames_in_pic_order_cnt_cycle); + if (!equal) return equal; + + for ( i = 0 ; i< sps1->num_ref_frames_in_pic_order_cnt_cycle ;i ++) + equal &= (sps1->offset_for_ref_frame[i] == sps2->offset_for_ref_frame[i]); + } + + equal &= (sps1->num_ref_frames == sps2->num_ref_frames); + equal &= (sps1->gaps_in_frame_num_value_allowed_flag == sps2->gaps_in_frame_num_value_allowed_flag); + equal &= (sps1->pic_width_in_mbs_minus1 == sps2->pic_width_in_mbs_minus1); + equal &= (sps1->pic_height_in_map_units_minus1 == sps2->pic_height_in_map_units_minus1); + equal &= (sps1->frame_mbs_only_flag == sps2->frame_mbs_only_flag); + + if (!equal) return equal; + if( !sps1->frame_mbs_only_flag ) + equal &= (sps1->mb_adaptive_frame_field_flag == sps2->mb_adaptive_frame_field_flag); + + equal &= (sps1->direct_8x8_inference_flag == sps2->direct_8x8_inference_flag); + equal &= (sps1->frame_cropping_flag == sps2->frame_cropping_flag); + if (!equal) return equal; + if (sps1->frame_cropping_flag) + { + equal &= (sps1->frame_crop_left_offset == sps2->frame_crop_left_offset); + equal &= (sps1->frame_crop_right_offset == sps2->frame_crop_right_offset); + equal &= (sps1->frame_crop_top_offset == sps2->frame_crop_top_offset); + equal &= (sps1->frame_crop_bottom_offset == sps2->frame_crop_bottom_offset); + } + equal &= (sps1->vui_parameters_present_flag == sps2->vui_parameters_present_flag); + + return equal; +} + +int pps_is_equal(pic_parameter_set_rbsp_t *pps1, pic_parameter_set_rbsp_t *pps2) +{ + unsigned i, j; + int equal = 1; + + if ((!pps1->Valid) || (!pps2->Valid)) + return 0; + + equal &= (pps1->pic_parameter_set_id == pps2->pic_parameter_set_id); + equal &= (pps1->seq_parameter_set_id == pps2->seq_parameter_set_id); + equal &= (pps1->entropy_coding_mode_flag == pps2->entropy_coding_mode_flag); + equal &= (pps1->bottom_field_pic_order_in_frame_present_flag == pps2->bottom_field_pic_order_in_frame_present_flag); + equal &= (pps1->num_slice_groups_minus1 == pps2->num_slice_groups_minus1); + + if (!equal) return equal; + + if (pps1->num_slice_groups_minus1>0) + { + equal &= (pps1->slice_group_map_type == pps2->slice_group_map_type); + if (!equal) return equal; + if (pps1->slice_group_map_type == 0) + { + for (i=0; i<=pps1->num_slice_groups_minus1; i++) + equal &= (pps1->run_length_minus1[i] == pps2->run_length_minus1[i]); + } + else if( pps1->slice_group_map_type == 2 ) + { + for (i=0; inum_slice_groups_minus1; i++) + { + equal &= (pps1->top_left[i] == pps2->top_left[i]); + equal &= (pps1->bottom_right[i] == pps2->bottom_right[i]); + } + } + else if( pps1->slice_group_map_type == 3 || pps1->slice_group_map_type==4 || pps1->slice_group_map_type==5 ) + { + equal &= (pps1->slice_group_change_direction_flag == pps2->slice_group_change_direction_flag); + equal &= (pps1->slice_group_change_rate_minus1 == pps2->slice_group_change_rate_minus1); + } + else if( pps1->slice_group_map_type == 6 ) + { + equal &= (pps1->pic_size_in_map_units_minus1 == pps2->pic_size_in_map_units_minus1); + if (!equal) return equal; + for (i=0; i<=pps1->pic_size_in_map_units_minus1; i++) + equal &= (pps1->slice_group_id[i] == pps2->slice_group_id[i]); + } + } + + equal &= (pps1->num_ref_idx_l0_default_active_minus1 == pps2->num_ref_idx_l0_default_active_minus1); + equal &= (pps1->num_ref_idx_l1_default_active_minus1 == pps2->num_ref_idx_l1_default_active_minus1); + equal &= (pps1->weighted_pred_flag == pps2->weighted_pred_flag); + equal &= (pps1->weighted_bipred_idc == pps2->weighted_bipred_idc); + equal &= (pps1->pic_init_qp_minus26 == pps2->pic_init_qp_minus26); + equal &= (pps1->pic_init_qs_minus26 == pps2->pic_init_qs_minus26); + equal &= (pps1->chroma_qp_index_offset == pps2->chroma_qp_index_offset); + equal &= (pps1->deblocking_filter_control_present_flag == pps2->deblocking_filter_control_present_flag); + equal &= (pps1->constrained_intra_pred_flag == pps2->constrained_intra_pred_flag); + equal &= (pps1->redundant_pic_cnt_present_flag == pps2->redundant_pic_cnt_present_flag); + + if (!equal) return equal; + + //Fidelity Range Extensions Stuff + //It is initialized to zero, so should be ok to check all the time. + equal &= (pps1->transform_8x8_mode_flag == pps2->transform_8x8_mode_flag); + equal &= (pps1->pic_scaling_matrix_present_flag == pps2->pic_scaling_matrix_present_flag); + if(pps1->pic_scaling_matrix_present_flag) + { + for(i = 0; i < (6 + ((unsigned)pps1->transform_8x8_mode_flag << 1)); i++) + { + equal &= (pps1->pic_scaling_list_present_flag[i] == pps2->pic_scaling_list_present_flag[i]); + if(pps1->pic_scaling_list_present_flag[i]) + { + if(i < 6) + { + for (j = 0; j < 16; j++) + equal &= (pps1->ScalingList4x4[i][j] == pps2->ScalingList4x4[i][j]); + } + else + { + for (j = 0; j < 64; j++) + equal &= (pps1->ScalingList8x8[i-6][j] == pps2->ScalingList8x8[i-6][j]); + } + } + } + } + equal &= (pps1->second_chroma_qp_index_offset == pps2->second_chroma_qp_index_offset); + + return equal; +} diff --git a/lcommon/src/transcode_common.c b/lcommon/src/transcode_common.c new file mode 100644 index 0000000..56b8d47 --- /dev/null +++ b/lcommon/src/transcode_common.c @@ -0,0 +1,39 @@ +/*! +************************************************************************ +* \file transcode_common.h +* +* \brief +* Common Functions to write and read a frame node +* +* \author +* - João Carreira +* +************************************************************************ +*/ + +#include "transcode_common.h" +#include "memalloc.h" +//! this is not safe + +int write_trans_node( int pfile, TRANS_FRAME_NODE_T node) +{ + int i=0; + write(pfile,&node,sizeof(TRANS_FRAME_NODE_T)); + write(pfile, (*(node.pp_mv_info)), (node.frame_height/BLOCK_SIZE)*(node.frame_width/BLOCK_SIZE)*sizeof(PicMotionParams)); + return 0; +} + +int read_trans_node( int pfile, TRANS_FRAME_NODE_T *node) +{ + if( read(pfile,node,sizeof(TRANS_FRAME_NODE_T)) != sizeof(TRANS_FRAME_NODE_T) ) + { + return -1; + } + get_mem2Dmp(&(node->pp_mv_info), (node->frame_height/BLOCK_SIZE), (node->frame_width/BLOCK_SIZE) ); + if( read(pfile, (*(node->pp_mv_info)), (node->frame_height/BLOCK_SIZE)*(node->frame_width/BLOCK_SIZE)*sizeof(PicMotionParams)) != + (node->frame_height/BLOCK_SIZE)*(node->frame_width/BLOCK_SIZE)*sizeof(PicMotionParams) ) + { + return -1; + } + return 0; +} \ No newline at end of file diff --git a/lcommon/src/transform.c b/lcommon/src/transform.c new file mode 100644 index 0000000..883f2eb --- /dev/null +++ b/lcommon/src/transform.c @@ -0,0 +1,548 @@ +/*! + *************************************************************************** + * \file transform.c + * + * \brief + * Transform functions + * + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * - Alexis Michael Tourapis + * \date + * 01. July 2007 + ************************************************************************** + */ + +#include "global.h" +#include "transform.h" + + +void forward4x4(int **block, int **tblock, int pos_y, int pos_x) +{ + int i, ii; + int tmp[16]; + int *pTmp = tmp, *pblock; + int p0,p1,p2,p3; + int t0,t1,t2,t3; + + // Horizontal + for (i=pos_y; i < pos_y + BLOCK_SIZE; i++) + { + pblock = &block[i][pos_x]; + p0 = *(pblock++); + p1 = *(pblock++); + p2 = *(pblock++); + p3 = *(pblock ); + + t0 = p0 + p3; + t1 = p1 + p2; + t2 = p1 - p2; + t3 = p0 - p3; + + *(pTmp++) = t0 + t1; + *(pTmp++) = (t3 << 1) + t2; + *(pTmp++) = t0 - t1; + *(pTmp++) = t3 - (t2 << 1); + } + + // Vertical + for (i=0; i < BLOCK_SIZE; i++) + { + pTmp = tmp + i; + p0 = *pTmp; + p1 = *(pTmp += BLOCK_SIZE); + p2 = *(pTmp += BLOCK_SIZE); + p3 = *(pTmp += BLOCK_SIZE); + + t0 = p0 + p3; + t1 = p1 + p2; + t2 = p1 - p2; + t3 = p0 - p3; + + ii = pos_x + i; + tblock[pos_y ][ii] = t0 + t1; + tblock[pos_y + 1][ii] = t2 + (t3 << 1); + tblock[pos_y + 2][ii] = t0 - t1; + tblock[pos_y + 3][ii] = t3 - (t2 << 1); + } +} + +void inverse4x4(int **tblock, int **block, int pos_y, int pos_x) +{ + int i, ii; + int tmp[16]; + int *pTmp = tmp, *pblock; + int p0,p1,p2,p3; + int t0,t1,t2,t3; + + // Horizontal + for (i = pos_y; i < pos_y + BLOCK_SIZE; i++) + { + pblock = &tblock[i][pos_x]; + t0 = *(pblock++); + t1 = *(pblock++); + t2 = *(pblock++); + t3 = *(pblock ); + + p0 = t0 + t2; + p1 = t0 - t2; + p2 = (t1 >> 1) - t3; + p3 = t1 + (t3 >> 1); + + *(pTmp++) = p0 + p3; + *(pTmp++) = p1 + p2; + *(pTmp++) = p1 - p2; + *(pTmp++) = p0 - p3; + } + + // Vertical + for (i = 0; i < BLOCK_SIZE; i++) + { + pTmp = tmp + i; + t0 = *pTmp; + t1 = *(pTmp += BLOCK_SIZE); + t2 = *(pTmp += BLOCK_SIZE); + t3 = *(pTmp += BLOCK_SIZE); + + p0 = t0 + t2; + p1 = t0 - t2; + p2 =(t1 >> 1) - t3; + p3 = t1 + (t3 >> 1); + + ii = i + pos_x; + block[pos_y ][ii] = p0 + p3; + block[pos_y + 1][ii] = p1 + p2; + block[pos_y + 2][ii] = p1 - p2; + block[pos_y + 3][ii] = p0 - p3; + } +} + + +void hadamard4x4(int **block, int **tblock) +{ + int i; + int tmp[16]; + int *pTmp = tmp, *pblock; + int p0,p1,p2,p3; + int t0,t1,t2,t3; + + // Horizontal + for (i = 0; i < BLOCK_SIZE; i++) + { + pblock = block[i]; + p0 = *(pblock++); + p1 = *(pblock++); + p2 = *(pblock++); + p3 = *(pblock ); + + t0 = p0 + p3; + t1 = p1 + p2; + t2 = p1 - p2; + t3 = p0 - p3; + + *(pTmp++) = t0 + t1; + *(pTmp++) = t3 + t2; + *(pTmp++) = t0 - t1; + *(pTmp++) = t3 - t2; + } + + // Vertical + for (i = 0; i < BLOCK_SIZE; i++) + { + pTmp = tmp + i; + p0 = *pTmp; + p1 = *(pTmp += BLOCK_SIZE); + p2 = *(pTmp += BLOCK_SIZE); + p3 = *(pTmp += BLOCK_SIZE); + + t0 = p0 + p3; + t1 = p1 + p2; + t2 = p1 - p2; + t3 = p0 - p3; + + tblock[0][i] = (t0 + t1) >> 1; + tblock[1][i] = (t2 + t3) >> 1; + tblock[2][i] = (t0 - t1) >> 1; + tblock[3][i] = (t3 - t2) >> 1; + } +} + + +void ihadamard4x4(int **tblock, int **block) +{ + int i; + int tmp[16]; + int *pTmp = tmp, *pblock; + int p0,p1,p2,p3; + int t0,t1,t2,t3; + + // Horizontal + for (i = 0; i < BLOCK_SIZE; i++) + { + pblock = tblock[i]; + t0 = *(pblock++); + t1 = *(pblock++); + t2 = *(pblock++); + t3 = *(pblock ); + + p0 = t0 + t2; + p1 = t0 - t2; + p2 = t1 - t3; + p3 = t1 + t3; + + *(pTmp++) = p0 + p3; + *(pTmp++) = p1 + p2; + *(pTmp++) = p1 - p2; + *(pTmp++) = p0 - p3; + } + + // Vertical + for (i = 0; i < BLOCK_SIZE; i++) + { + pTmp = tmp + i; + t0 = *pTmp; + t1 = *(pTmp += BLOCK_SIZE); + t2 = *(pTmp += BLOCK_SIZE); + t3 = *(pTmp += BLOCK_SIZE); + + p0 = t0 + t2; + p1 = t0 - t2; + p2 = t1 - t3; + p3 = t1 + t3; + + block[0][i] = p0 + p3; + block[1][i] = p1 + p2; + block[2][i] = p1 - p2; + block[3][i] = p0 - p3; + } +} + +void hadamard4x2(int **block, int **tblock) +{ + int i; + int tmp[8]; + int *pTmp = tmp; + int p0,p1,p2,p3; + int t0,t1,t2,t3; + + // Horizontal + *(pTmp++) = block[0][0] + block[1][0]; + *(pTmp++) = block[0][1] + block[1][1]; + *(pTmp++) = block[0][2] + block[1][2]; + *(pTmp++) = block[0][3] + block[1][3]; + + *(pTmp++) = block[0][0] - block[1][0]; + *(pTmp++) = block[0][1] - block[1][1]; + *(pTmp++) = block[0][2] - block[1][2]; + *(pTmp ) = block[0][3] - block[1][3]; + + // Vertical + pTmp = tmp; + for (i=0;i<2;i++) + { + p0 = *(pTmp++); + p1 = *(pTmp++); + p2 = *(pTmp++); + p3 = *(pTmp++); + + t0 = p0 + p3; + t1 = p1 + p2; + t2 = p1 - p2; + t3 = p0 - p3; + + tblock[i][0] = (t0 + t1); + tblock[i][1] = (t3 + t2); + tblock[i][2] = (t0 - t1); + tblock[i][3] = (t3 - t2); + } +} + +void ihadamard4x2(int **tblock, int **block) +{ + int i; + int tmp[8]; + int *pTmp = tmp; + int p0,p1,p2,p3; + int t0,t1,t2,t3; + + // Horizontal + *(pTmp++) = tblock[0][0] + tblock[1][0]; + *(pTmp++) = tblock[0][1] + tblock[1][1]; + *(pTmp++) = tblock[0][2] + tblock[1][2]; + *(pTmp++) = tblock[0][3] + tblock[1][3]; + + *(pTmp++) = tblock[0][0] - tblock[1][0]; + *(pTmp++) = tblock[0][1] - tblock[1][1]; + *(pTmp++) = tblock[0][2] - tblock[1][2]; + *(pTmp ) = tblock[0][3] - tblock[1][3]; + + // Vertical + pTmp = tmp; + for (i = 0; i < 2; i++) + { + p0 = *(pTmp++); + p1 = *(pTmp++); + p2 = *(pTmp++); + p3 = *(pTmp++); + + t0 = p0 + p2; + t1 = p0 - p2; + t2 = p1 - p3; + t3 = p1 + p3; + + // coefficients (transposed) + block[0][i] = t0 + t3; + block[1][i] = t1 + t2; + block[2][i] = t1 - t2; + block[3][i] = t0 - t3; + } +} + +//following functions perform 8 additions, 8 assignments. Should be a bit faster +void hadamard2x2(int **block, int tblock[4]) +{ + int p0,p1,p2,p3; + + p0 = block[0][0] + block[0][4]; + p1 = block[0][0] - block[0][4]; + p2 = block[4][0] + block[4][4]; + p3 = block[4][0] - block[4][4]; + + tblock[0] = (p0 + p2); + tblock[1] = (p1 + p3); + tblock[2] = (p0 - p2); + tblock[3] = (p1 - p3); +} + +void ihadamard2x2(int tblock[4], int block[4]) +{ + int t0,t1,t2,t3; + + t0 = tblock[0] + tblock[1]; + t1 = tblock[0] - tblock[1]; + t2 = tblock[2] + tblock[3]; + t3 = tblock[2] - tblock[3]; + + block[0] = (t0 + t2); + block[1] = (t1 + t3); + block[2] = (t0 - t2); + block[3] = (t1 - t3); +} + +/* +void hadamard2x2(int **block, int tblock[4]) +{ + //12 additions, 4 assignments + tblock[0] = (block[0][0] + block[0][4] + block[4][0] + block[4][4]); + tblock[1] = (block[0][0] - block[0][4] + block[4][0] - block[4][4]); + tblock[2] = (block[0][0] + block[0][4] - block[4][0] - block[4][4]); + tblock[3] = (block[0][0] - block[0][4] - block[4][0] + block[4][4]); +} + +void ihadamard2x2(int tblock[4], int block[4]) +{ + block[0] = (tblock[0] + tblock[1] + tblock[2] + tblock[3]); + block[1] = (tblock[0] - tblock[1] + tblock[2] - tblock[3]); + block[2] = (tblock[0] + tblock[1] - tblock[2] - tblock[3]); + block[3] = (tblock[0] - tblock[1] - tblock[2] + tblock[3]); +} + +*/ + + +void forward8x8(int **block, int **tblock, int pos_y, int pos_x) +{ + int i, ii; + int tmp[64]; + int *pTmp = tmp, *pblock; + int a0, a1, a2, a3; + int p0, p1, p2, p3, p4, p5 ,p6, p7; + int b0, b1, b2, b3, b4, b5, b6, b7; + + // Horizontal + for (i=pos_y; i < pos_y + BLOCK_SIZE_8x8; i++) + { + pblock = &block[i][pos_x]; + p0 = *(pblock++); + p1 = *(pblock++); + p2 = *(pblock++); + p3 = *(pblock++); + p4 = *(pblock++); + p5 = *(pblock++); + p6 = *(pblock++); + p7 = *(pblock ); + + a0 = p0 + p7; + a1 = p1 + p6; + a2 = p2 + p5; + a3 = p3 + p4; + + b0 = a0 + a3; + b1 = a1 + a2; + b2 = a0 - a3; + b3 = a1 - a2; + + a0 = p0 - p7; + a1 = p1 - p6; + a2 = p2 - p5; + a3 = p3 - p4; + + b4 = a1 + a2 + ((a0 >> 1) + a0); + b5 = a0 - a3 - ((a2 >> 1) + a2); + b6 = a0 + a3 - ((a1 >> 1) + a1); + b7 = a1 - a2 + ((a3 >> 1) + a3); + + *(pTmp++) = b0 + b1; + *(pTmp++) = b4 + (b7 >> 2); + *(pTmp++) = b2 + (b3 >> 1); + *(pTmp++) = b5 + (b6 >> 2); + *(pTmp++) = b0 - b1; + *(pTmp++) = b6 - (b5 >> 2); + *(pTmp++) = (b2 >> 1) - b3; + *(pTmp++) = (b4 >> 2) - b7; + } + + // Vertical + for (i=0; i < BLOCK_SIZE_8x8; i++) + { + pTmp = tmp + i; + p0 = *pTmp; + p1 = *(pTmp += BLOCK_SIZE_8x8); + p2 = *(pTmp += BLOCK_SIZE_8x8); + p3 = *(pTmp += BLOCK_SIZE_8x8); + p4 = *(pTmp += BLOCK_SIZE_8x8); + p5 = *(pTmp += BLOCK_SIZE_8x8); + p6 = *(pTmp += BLOCK_SIZE_8x8); + p7 = *(pTmp += BLOCK_SIZE_8x8); + + a0 = p0 + p7; + a1 = p1 + p6; + a2 = p2 + p5; + a3 = p3 + p4; + + b0 = a0 + a3; + b1 = a1 + a2; + b2 = a0 - a3; + b3 = a1 - a2; + + a0 = p0 - p7; + a1 = p1 - p6; + a2 = p2 - p5; + a3 = p3 - p4; + + b4 = a1 + a2 + ((a0 >> 1) + a0); + b5 = a0 - a3 - ((a2 >> 1) + a2); + b6 = a0 + a3 - ((a1 >> 1) + a1); + b7 = a1 - a2 + ((a3 >> 1) + a3); + + ii = pos_x + i; + tblock[pos_y ][ii] = b0 + b1; + tblock[pos_y + 1][ii] = b4 + (b7 >> 2); + tblock[pos_y + 2][ii] = b2 + (b3 >> 1); + tblock[pos_y + 3][ii] = b5 + (b6 >> 2); + tblock[pos_y + 4][ii] = b0 - b1; + tblock[pos_y + 5][ii] = b6 - (b5 >> 2); + tblock[pos_y + 6][ii] = (b2 >> 1) - b3; + tblock[pos_y + 7][ii] = (b4 >> 2) - b7; + } +} + +void inverse8x8(int **tblock, int **block, int pos_x) +{ + int i, ii; + int tmp[64]; + int *pTmp = tmp, *pblock; + int a0, a1, a2, a3; + int p0, p1, p2, p3, p4, p5 ,p6, p7; + int b0, b1, b2, b3, b4, b5, b6, b7; + + // Horizontal + for (i=0; i < BLOCK_SIZE_8x8; i++) + { + pblock = &tblock[i][pos_x]; + p0 = *(pblock++); + p1 = *(pblock++); + p2 = *(pblock++); + p3 = *(pblock++); + p4 = *(pblock++); + p5 = *(pblock++); + p6 = *(pblock++); + p7 = *(pblock ); + + a0 = p0 + p4; + a1 = p0 - p4; + a2 = p6 - (p2 >> 1); + a3 = p2 + (p6 >> 1); + + b0 = a0 + a3; + b2 = a1 - a2; + b4 = a1 + a2; + b6 = a0 - a3; + + a0 = -p3 + p5 - p7 - (p7 >> 1); + a1 = p1 + p7 - p3 - (p3 >> 1); + a2 = -p1 + p7 + p5 + (p5 >> 1); + a3 = p3 + p5 + p1 + (p1 >> 1); + + + b1 = a0 + (a3>>2); + b3 = a1 + (a2>>2); + b5 = a2 - (a1>>2); + b7 = a3 - (a0>>2); + + *(pTmp++) = b0 + b7; + *(pTmp++) = b2 - b5; + *(pTmp++) = b4 + b3; + *(pTmp++) = b6 + b1; + *(pTmp++) = b6 - b1; + *(pTmp++) = b4 - b3; + *(pTmp++) = b2 + b5; + *(pTmp++) = b0 - b7; + } + + // Vertical + for (i=0; i < BLOCK_SIZE_8x8; i++) + { + pTmp = tmp + i; + p0 = *pTmp; + p1 = *(pTmp += BLOCK_SIZE_8x8); + p2 = *(pTmp += BLOCK_SIZE_8x8); + p3 = *(pTmp += BLOCK_SIZE_8x8); + p4 = *(pTmp += BLOCK_SIZE_8x8); + p5 = *(pTmp += BLOCK_SIZE_8x8); + p6 = *(pTmp += BLOCK_SIZE_8x8); + p7 = *(pTmp += BLOCK_SIZE_8x8); + + a0 = p0 + p4; + a1 = p0 - p4; + a2 = p6 - (p2>>1); + a3 = p2 + (p6>>1); + + b0 = a0 + a3; + b2 = a1 - a2; + b4 = a1 + a2; + b6 = a0 - a3; + + a0 = -p3 + p5 - p7 - (p7 >> 1); + a1 = p1 + p7 - p3 - (p3 >> 1); + a2 = -p1 + p7 + p5 + (p5 >> 1); + a3 = p3 + p5 + p1 + (p1 >> 1); + + + b1 = a0 + (a3 >> 2); + b7 = a3 - (a0 >> 2); + b3 = a1 + (a2 >> 2); + b5 = a2 - (a1 >> 2); + + ii = i + pos_x; + block[0][ii] = b0 + b7; + block[1][ii] = b2 - b5; + block[2][ii] = b4 + b3; + block[3][ii] = b6 + b1; + block[4][ii] = b6 - b1; + block[5][ii] = b4 - b3; + block[6][ii] = b2 + b5; + block[7][ii] = b0 - b7; + } +} + diff --git a/lcommon/src/win32.c b/lcommon/src/win32.c new file mode 100644 index 0000000..ac7343d --- /dev/null +++ b/lcommon/src/win32.c @@ -0,0 +1,78 @@ + +/*! + ************************************************************************************* + * \file win32.c + * + * \brief + * Platform dependent code + * + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * - Karsten Suehring + ************************************************************************************* + */ + +#include "global.h" + + +#ifdef _WIN32 + +static LARGE_INTEGER freq; + +void gettime(TIME_T* time) +{ +#ifndef TIMING_DISABLE + QueryPerformanceCounter(time); +#endif +} + +int64 timediff(TIME_T* start, TIME_T* end) +{ +#ifndef TIMING_DISABLE + return (int64)((end->QuadPart - start->QuadPart)); +#else + return 0; +#endif +} + +void init_time(void) +{ + QueryPerformanceFrequency(&freq); +} + +int64 timenorm(int64 cur_time) +{ +#ifndef TIMING_DISABLE + return (int64)(cur_time * 1000 /(freq.QuadPart)); +#else + return 1; +#endif +} + +#else + +static struct timezone tz; + +void gettime(TIME_T* time) +{ + gettimeofday(time, &tz); +} + +void init_time(void) +{ +} + +int64 timediff(TIME_T* start, TIME_T* end) +{ + int t1, t2; + + t1 = end->tv_sec - start->tv_sec; + t2 = end->tv_usec - start->tv_usec; + return (int64) t2 + (int64) t1 * (int64) 1000000; +} + +int64 timenorm(int64 cur_time) +{ + return (int64)(cur_time / (int64) 1000); +} +#endif diff --git a/ldecod/Makefile b/ldecod/Makefile new file mode 100644 index 0000000..96f2a65 --- /dev/null +++ b/ldecod/Makefile @@ -0,0 +1,152 @@ +### +### Makefile for H.264/AVC decoder +### +### generated for UNIX/LINUX environments +### by H. Schwarz, Limin Wang +### + + + +NAME=ldecod + +### include debug information: 1=yes, 0=no +DBG?= 0 +### Generate 32 bit executable : 1=yes, 0=no +M32?= 0 +### include O level optimization : 0-3 +OPT?= 3 +### Static Compilation +STC?= 0 +### OPENMP support : 1=yes, 0=no +OPENMP?= 1 +### OPENCV support : 1=yes, 0=no +OPENCV?= 0 + + +DEPEND= dependencies + +BINDIR= ../bin +INCDIR= inc +SRCDIR= src +OBJDIR= obj + +ADDSRCDIR= ../lcommon/src +ADDINCDIR= ../lcommon/inc + +FERCSRCDIR= src/ferc +FERCINCDIR= inc/ferc + +ifeq ($(STC),1) +ifeq ($(DBG),1) ### Do not use static compilation for Debug mode +STC=0 +STATIC= +else +STATIC= -static +endif +else +STATIC= +endif + +LIBS= -lm $(STATIC) +CFLAGS= -std=gnu99 -pedantic -ffloat-store -fno-strict-aliasing -fsigned-char $(STATIC) +FLAGS= $(CFLAGS) -Wall -I$(INCDIR) -I$(ADDINCDIR) -I$(FERCINCDIR) -D __USE_LARGEFILE64 -D _FILE_OFFSET_BITS=64 +ifeq ($(M32),1) +FLAGS+=-m32 +endif + +ifeq ($(OPENMP),1) +FLAGS+=-fopenmp +endif + +ifeq ($(OPENCV),1) +LIBS+= `pkg-config --libs opencv` +CFLAGS+= -D HAVE_OPENCV=1 `pkg-config --cflags opencv` +else +CFLAGS+= -D HAVE_OPENCV=0 +endif + +# + +OPT_FLAG = -O$(OPT) +ifeq ($(DBG),1) +SUFFIX= .dbg +FLAGS+= -g +CFLAGS+= -D _DEBUG=1 +else +SUFFIX= +FLAGS+= $(OPT_FLAG) +endif + +OBJSUF= .o$(SUFFIX) + +SRC= $(wildcard $(SRCDIR)/*.c) +ADDSRC= $(wildcard $(ADDSRCDIR)/*.c) +FERCSRC= $(wildcard $(FERCSRCDIR)/*.c) +OBJ= $(SRC:$(SRCDIR)/%.c=$(OBJDIR)/%.o$(SUFFIX)) $(ADDSRC:$(ADDSRCDIR)/%.c=$(OBJDIR)/%.o$(SUFFIX)) $(FERCSRC:$(FERCSRCDIR)/%.c=$(OBJDIR)/%.o$(SUFFIX)) +BIN= $(BINDIR)/$(NAME)$(SUFFIX).exe + + +.PHONY: default distclean clean tags depend + +default: messages objdir_mk depend bin + +messages: +ifeq ($(M32),1) + @echo 'Compiling with M32 support...' +endif +ifeq ($(DBG),1) + @echo 'Compiling with Debug support...' + @echo 'Note static compilation not supported in this mode.' +endif +ifeq ($(STC),1) + @echo 'Compiling with -static support...' +endif +ifeq ($(OPENMP),1) + @echo 'Compiling with -fopenmp support...' +endif + +clean: + @echo remove all objects + @rm -rf $(OBJDIR) + +distclean: clean + @rm -f $(DEPEND) tags + @rm -f $(BIN) + +tags: + @echo update tag table + @ctags inc/*.h src/*.c + +bin: $(OBJ) + @echo + @echo 'creating binary "$(BIN)"' + @$(CC) $(FLAGS) -o $(BIN) $(OBJ) $(LIBS) + @echo '... done' + @echo + +depend: + @echo + @echo 'checking dependencies' + @$(SHELL) -ec '$(CC) $(FLAGS) -MM $(CFLAGS) -I$(INCDIR) -I$(ADDINCDIR) $(SRC) $(ADDSRC) \ + | sed '\''s@\(.*\)\.o[ :]@$(OBJDIR)/\1.o$(SUFFIX):@g'\'' \ + >$(DEPEND)' + @echo + +$(OBJDIR)/%.o$(SUFFIX): $(SRCDIR)/%.c + @echo 'compiling object file "$@" ...' + @$(CC) -c -o $@ $(FLAGS) $< + +$(OBJDIR)/%.o$(SUFFIX): $(ADDSRCDIR)/%.c + @echo 'compiling object file "$@" ...' + @$(CC) -c -o $@ $(FLAGS) $< + +$(OBJDIR)/%.o$(SUFFIX): $(FERCSRCDIR)/%.c + @echo 'compiling object file "$@" ...' + @$(CC) -c -o $@ $(FLAGS) $< + +objdir_mk: + @echo 'Creating $(OBJDIR) ...' + @mkdir -p $(OBJDIR) + +-include $(DEPEND) + diff --git a/ldecod/decoder_default_config.cfg b/ldecod/decoder_default_config.cfg new file mode 100644 index 0000000..9a6d328 --- /dev/null +++ b/ldecod/decoder_default_config.cfg @@ -0,0 +1,86 @@ +# This is a file containing input parameters to the JVT H.264/AVC decoder. +# The text line following each parameter is discarded by the decoder. +# +# For bug reporting and known issues see: +# https://ipbt.hhi.fraunhofer.de +# +# New Input File Format is as follows +# = # Comment +# +########################################################################################## +# Files +########################################################################################## +InputFile = "BookArrival_V2_QP28.264" # H.264/AVC coded bitstream +OutputFile = "BookArrival.yuv" # Output file, YUV/RGB +RefFile = "..\_References_Inv\BookArrival.yuv" # Ref sequence (for SNR) + +FrameSkip = 0 +#StatsFile = "ola.csv" +WriteUV = 1 # Write 4:2:0 chroma components for monochrome streams +FileFormat = 0 # NAL mode (0=Annex B, 1: RTP packets) +RefOffset = 0 # SNR computation offset +POCScale = 2 # Poc Scale (1 or 2) +########################################################################################## +# HRD parameters +########################################################################################## +#R_decoder = 500000 # Rate_Decoder +#B_decoder = 104000 # B_decoder +#F_decoder = 73000 # F_decoder +#LeakyBucketParamFile = "leakybucketparam.cfg" # LeakyBucket Params +########################################################################################## +# decoder control parameters +########################################################################################## +DisplayDecParams = 1 # 1: Display parameters; +POCGap = 2 # POC gap (2: IPP /IbP/IpP (Default), 4: IPP with frame skip = 1 etc.) +IDRPeriod = 20 # IDR Period +Silent = 0 # Silent decode +IntraProfileDeblocking = 1 # Enable Deblocking filter in intra only profiles (0=disable, 1=filter according to SPS parameters) +DecFrmNum = 41 # Number of frames to be decoded (-n) +########################################################################################## +# Frame Loss Concealment Parameters +########################################################################################## +# None - 0 +# Frame Copy - FF: 1, BC: 2 +# Motion Copy - Temporal: 3, Spatial: 4, Both motion vectors: 5 +# SMART - MVE: 11, BASE_MC: 12, COMB/BASE_MC first: 13, INTERCOPY: 14, SAD_BASED: 15 +# DFDP - 21 +# ISMID - 22 +ConcealMode = 15 # Err Concealment +RefPOCGap = 4 # Reference POC gap (2: IPP (Default), 4: IbP / IpP) +MotionVectorCandidates = 2 +DisparityMethod = 0 # 0:Load, 1:VAR, 2:BM +DisparityWindow = 3 # Disparity search window corresponds to a 256/DisparityWindow +DispLoadFileName = "DisparityBookArrival.bmp" +DisparityProjection = 1 +Disparity4x4BlockSize = 4 +MVConcealmentMode = 3 # Conceal motion vector in the proposed mode + # 0: ZeroMV + # 1: Previous Vector + # 2: Average + # 3: Median +# Modification in frame next to a concealed frame +ErrorPropagation = 0 +# SearchMode Brief +# SearchMode = 0: Uses the previous reference pic for concealment of reference frames +# within the same view +# SearchMode = 1: Uses the closest frame to conceal (inc concealed frames) +# SearchMode = 2: Uses the closest frame to conceal (not concealed frames) +SearchModeRef = 2 +SearchModeNonRef = 1 +########################################################################################## +# MVC decoding parameters +########################################################################################## +DecodeAllLayers = 1 # Decode all views (-mpr) +########################################################################################## +# Multi Layer Decoder Parameters +########################################################################################## +DPBPLUS0 = 4 +DPBPLUS1 = 2 +########################################################################################## +# NAL Loss and stream parser Parameters +########################################################################################## +NALUParser = 1 +PredictionParser = 0 +# NAL Loss Patern. Indicate the losted NALU -- EX: 1,4,50 +DropNALU = 22 +#DropNALU = 31,49,64,140,149,176,182,223,291,303 \ No newline at end of file diff --git a/ldecod/inc/annexb.h b/ldecod/inc/annexb.h new file mode 100644 index 0000000..4ab78ce --- /dev/null +++ b/ldecod/inc/annexb.h @@ -0,0 +1,40 @@ + +/*! + ************************************************************************************* + * \file annexb.h + * + * \brief + * Annex B byte stream buffer handling. + * + ************************************************************************************* + */ + +#ifndef _ANNEXB_H_ +#define _ANNEXB_H_ + +#include "nalucommon.h" + +typedef struct annex_b_struct +{ + int BitStreamFile; //!< the bit stream file + byte *iobuffer; + byte *iobufferread; + int bytesinbuffer; + int is_eof; + int iIOBufferSize; + + int IsFirstByteStreamNALU; + int nextstartcodebytes; + byte *Buf; +} ANNEXB_t; + +extern int get_annex_b_NALU (VideoParameters *p_Vid, NALU_t *nalu, ANNEXB_t *annex_b); + +extern void open_annex_b (char *fn, ANNEXB_t *annex_b); +extern void close_annex_b (ANNEXB_t *annex_b); +extern void malloc_annex_b (VideoParameters *p_Vid, ANNEXB_t **p_annex_b); +extern void free_annex_b (ANNEXB_t **p_annex_b); +extern void init_annex_b (ANNEXB_t *annex_b); +extern void reset_annex_b (ANNEXB_t *annex_b); +#endif + diff --git a/ldecod/inc/biaridecod.h b/ldecod/inc/biaridecod.h new file mode 100644 index 0000000..062621b --- /dev/null +++ b/ldecod/inc/biaridecod.h @@ -0,0 +1,133 @@ + +/*! + *************************************************************************** + * \file + * biaridecod.h + * + * \brief + * Headerfile for binary arithmetic decoder routines + * + * \author + * Detlev Marpe, + * Gabi Blättermann + * Copyright (C) 2000 HEINRICH HERTZ INSTITUTE All Rights Reserved. + * + * \date + * 21. Oct 2000 + ************************************************************************** + */ + +#ifndef _BIARIDECOD_H_ +#define _BIARIDECOD_H_ + + +/************************************************************************ + * D e f i n i t i o n s + *********************************************************************** + */ + +/* Range table for LPS */ +static const byte rLPS_table_64x4[64][4]= +{ + { 128, 176, 208, 240}, + { 128, 167, 197, 227}, + { 128, 158, 187, 216}, + { 123, 150, 178, 205}, + { 116, 142, 169, 195}, + { 111, 135, 160, 185}, + { 105, 128, 152, 175}, + { 100, 122, 144, 166}, + { 95, 116, 137, 158}, + { 90, 110, 130, 150}, + { 85, 104, 123, 142}, + { 81, 99, 117, 135}, + { 77, 94, 111, 128}, + { 73, 89, 105, 122}, + { 69, 85, 100, 116}, + { 66, 80, 95, 110}, + { 62, 76, 90, 104}, + { 59, 72, 86, 99}, + { 56, 69, 81, 94}, + { 53, 65, 77, 89}, + { 51, 62, 73, 85}, + { 48, 59, 69, 80}, + { 46, 56, 66, 76}, + { 43, 53, 63, 72}, + { 41, 50, 59, 69}, + { 39, 48, 56, 65}, + { 37, 45, 54, 62}, + { 35, 43, 51, 59}, + { 33, 41, 48, 56}, + { 32, 39, 46, 53}, + { 30, 37, 43, 50}, + { 29, 35, 41, 48}, + { 27, 33, 39, 45}, + { 26, 31, 37, 43}, + { 24, 30, 35, 41}, + { 23, 28, 33, 39}, + { 22, 27, 32, 37}, + { 21, 26, 30, 35}, + { 20, 24, 29, 33}, + { 19, 23, 27, 31}, + { 18, 22, 26, 30}, + { 17, 21, 25, 28}, + { 16, 20, 23, 27}, + { 15, 19, 22, 25}, + { 14, 18, 21, 24}, + { 14, 17, 20, 23}, + { 13, 16, 19, 22}, + { 12, 15, 18, 21}, + { 12, 14, 17, 20}, + { 11, 14, 16, 19}, + { 11, 13, 15, 18}, + { 10, 12, 15, 17}, + { 10, 12, 14, 16}, + { 9, 11, 13, 15}, + { 9, 11, 12, 14}, + { 8, 10, 12, 14}, + { 8, 9, 11, 13}, + { 7, 9, 11, 12}, + { 7, 9, 10, 12}, + { 7, 8, 10, 11}, + { 6, 8, 9, 11}, + { 6, 7, 9, 10}, + { 6, 7, 8, 9}, + { 2, 2, 2, 2} +}; + + +static const byte AC_next_state_MPS_64[64] = +{ + 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,62,63 +}; + + +static const byte AC_next_state_LPS_64[64] = +{ + 0, 0, 1, 2, 2, 4, 4, 5, 6, 7, + 8, 9, 9,11,11,12,13,13,15,15, + 16,16,18,18,19,19,21,21,22,22, + 23,24,24,25,26,26,27,27,28,29, + 29,30,30,30,31,32,32,33,33,33, + 34,34,35,35,35,36,36,36,37,37, + 37,38,38,63 +}; + +static const byte renorm_table_32[32]={6,5,4,4,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,1,1}; + + +extern void arideco_start_decoding(DecodingEnvironmentPtr eep, unsigned char *code_buffer, int firstbyte, int *code_len); +extern int arideco_bits_read(DecodingEnvironmentPtr dep); +extern void arideco_done_decoding(DecodingEnvironmentPtr dep); +extern void biari_init_context (int qp, BiContextTypePtr ctx, const char* ini); +extern unsigned int biari_decode_symbol(DecodingEnvironment *dep, BiContextType *bi_ct ); +extern unsigned int biari_decode_symbol_eq_prob(DecodingEnvironmentPtr dep); +extern unsigned int biari_decode_final(DecodingEnvironmentPtr dep); +#endif // BIARIDECOD_H_ + diff --git a/ldecod/inc/block.h b/ldecod/inc/block.h new file mode 100644 index 0000000..3ca0865 --- /dev/null +++ b/ldecod/inc/block.h @@ -0,0 +1,127 @@ + +/*! + ************************************************************************ + * \file block.h + * + * \brief + * definitions for block decoding functions + * + * \author + * Inge Lille-Langoy \n + * Telenor Satellite Services \n + * P.O.Box 6914 St.Olavs plass \n + * N-0130 Oslo, Norway + * + ************************************************************************ + */ + +#ifndef _BLOCK_H_ +#define _BLOCK_H_ + +#include "global.h" +#include "transform8x8.h" + +static const byte QP_SCALE_CR[52]= +{ + 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,32,33,34,34,35,35,36,36,37,37, + 37,38,38,38,39,39,39,39 + +}; + +//! look up tables for FRExt_chroma support +static const unsigned char subblk_offset_x[3][8][4] = +{ + { + {0, 4, 0, 4}, + {0, 4, 0, 4}, + {0, 0, 0, 0}, + {0, 0, 0, 0}, + {0, 0, 0, 0}, + {0, 0, 0, 0}, + {0, 0, 0, 0}, + {0, 0, 0, 0}, + }, + { + {0, 4, 0, 4}, + {0, 4, 0, 4}, + {0, 4, 0, 4}, + {0, 4, 0, 4}, + {0, 0, 0, 0}, + {0, 0, 0, 0}, + {0, 0, 0, 0}, + {0, 0, 0, 0}, + }, + { + {0, 4, 0, 4}, + {8,12, 8,12}, + {0, 4, 0, 4}, + {8,12, 8,12}, + {0, 4, 0, 4}, + {8,12, 8,12}, + {0, 4, 0, 4}, + {8,12, 8,12} + } +}; + + +static const unsigned char subblk_offset_y[3][8][4] = +{ + { + {0, 0, 4, 4}, + {0, 0, 4, 4}, + {0, 0, 0, 0}, + {0, 0, 0, 0}, + {0, 0, 0, 0}, + {0, 0, 0, 0}, + {0, 0, 0, 0}, + {0, 0, 0, 0} + }, + { + {0, 0, 4, 4}, + {8, 8,12,12}, + {0, 0, 4, 4}, + {8, 8,12,12}, + {0, 0, 0, 0}, + {0, 0, 0, 0}, + {0, 0, 0, 0}, + {0, 0, 0, 0} + }, + { + {0, 0, 4, 4}, + {0, 0, 4, 4}, + {8, 8,12,12}, + {8, 8,12,12}, + {0, 0, 4, 4}, + {0, 0, 4, 4}, + {8, 8,12,12}, + {8, 8,12,12} + } +}; + +static const byte decode_block_scan[16] = {0, 1, 4, 5, 2, 3, 6, 7, 8, 9, 12, 13, 10, 11, 14, 15}; + +extern void iMBtrans4x4(Macroblock *currMB, ColorPlane pl, int smb); +extern void iMBtrans8x8(Macroblock *currMB, ColorPlane pl); + +extern void itrans_sp_cr(Macroblock *currMB, int uv); + +extern void Inv_Residual_trans_4x4(Macroblock *currMB, ColorPlane pl, int ioff, int joff); +extern void Inv_Residual_trans_8x8(Macroblock *currMB, ColorPlane pl, int ioff,int joff); +extern void Inv_Residual_trans_16x16 (Macroblock *currMB, ColorPlane pl); +extern void Inv_Residual_trans_Chroma(Macroblock *currMB, int uv); + +extern void itrans4x4 (Macroblock *currMB, ColorPlane pl, int ioff, int joff); +extern void itrans4x4_ls(Macroblock *currMB, ColorPlane pl, int ioff, int joff); +extern void itrans_sp (Macroblock *currMB, ColorPlane pl, int ioff, int joff); +extern void itrans_2 (Macroblock *currMB, ColorPlane pl); +extern void iTransform (Macroblock *currMB, ColorPlane pl, int smb); + +extern void copy_image_data (imgpel **imgBuf1, imgpel **imgBuf2, int off1, int off2, int width, int height); +extern void copy_image_data_16x16 (imgpel **imgBuf1, imgpel **imgBuf2, int off1, int off2); +extern void copy_image_data_8x8 (imgpel **imgBuf1, imgpel **imgBuf2, int off1, int off2); +extern void copy_image_data_4x4 (imgpel **imgBuf1, imgpel **imgBuf2, int off1, int off2); +extern int CheckVertMV(Macroblock *currMB, int vec1_y, int block_size_y); +#endif + diff --git a/ldecod/inc/cabac.h b/ldecod/inc/cabac.h new file mode 100644 index 0000000..c83f391 --- /dev/null +++ b/ldecod/inc/cabac.h @@ -0,0 +1,63 @@ + +/*! + *************************************************************************** + * \file + * cabac.h + * + * \brief + * Header file for entropy coding routines + * + * \author + * Detlev Marpe \n + * Copyright (C) 2000 HEINRICH HERTZ INSTITUTE All Rights Reserved. + * + * \date + * 21. Oct 2000 (Changes by Tobias Oelbaum 28.08.2001) + *************************************************************************** + */ + +#ifndef _CABAC_H_ +#define _CABAC_H_ + +#include "global.h" + +extern MotionInfoContexts* create_contexts_MotionInfo(void); +extern TextureInfoContexts* create_contexts_TextureInfo(void); +extern void delete_contexts_MotionInfo(MotionInfoContexts *enco_ctx); +extern void delete_contexts_TextureInfo(TextureInfoContexts *enco_ctx); + +extern void cabac_new_slice(Slice *currSlice); + +extern void readMB_typeInfo_CABAC_i_slice (Macroblock *currMB, SyntaxElement *se, DecodingEnvironmentPtr dep_dp); +extern void readMB_typeInfo_CABAC_p_slice (Macroblock *currMB, SyntaxElement *se, DecodingEnvironmentPtr dep_dp); +extern void readMB_typeInfo_CABAC_b_slice (Macroblock *currMB, SyntaxElement *se, DecodingEnvironmentPtr dep_dp); +extern void readB8_typeInfo_CABAC_p_slice (Macroblock *currMB, SyntaxElement *se, DecodingEnvironmentPtr dep_dp); +extern void readB8_typeInfo_CABAC_b_slice (Macroblock *currMB, SyntaxElement *se, DecodingEnvironmentPtr dep_dp); +extern void readIntraPredMode_CABAC (Macroblock *currMB, SyntaxElement *se, DecodingEnvironmentPtr dep_dp); +extern void readRefFrame_CABAC (Macroblock *currMB, SyntaxElement *se, DecodingEnvironmentPtr dep_dp); +extern void read_MVD_CABAC (Macroblock *currMB, SyntaxElement *se, DecodingEnvironmentPtr dep_dp); +extern void read_mvd_CABAC_mbaff (Macroblock *currMB, SyntaxElement *se, DecodingEnvironmentPtr dep_dp); +extern void read_CBP_CABAC (Macroblock *currMB, SyntaxElement *se, DecodingEnvironmentPtr dep_dp); +extern void readRunLevel_CABAC (Macroblock *currMB, SyntaxElement *se, DecodingEnvironmentPtr dep_dp); +extern void read_dQuant_CABAC (Macroblock *currMB, SyntaxElement *se, DecodingEnvironmentPtr dep_dp); +extern void readCIPredMode_CABAC (Macroblock *currMB, SyntaxElement *se, DecodingEnvironmentPtr dep_dp); +extern void read_skip_flag_CABAC_p_slice (Macroblock *currMB, SyntaxElement *se, DecodingEnvironmentPtr dep_dp); +extern void read_skip_flag_CABAC_b_slice (Macroblock *currMB, SyntaxElement *se, DecodingEnvironmentPtr dep_dp); +extern void readFieldModeInfo_CABAC (Macroblock *currMB, SyntaxElement *se, DecodingEnvironmentPtr dep_dp); +extern void readMB_transform_size_flag_CABAC(Macroblock *currMB, SyntaxElement *se, DecodingEnvironmentPtr dep_dp); + +extern void readIPCM_CABAC(Slice *currSlice, struct datapartition_dec *dP); + +extern int cabac_startcode_follows(Slice *currSlice, int eos_bit); + +extern int readSyntaxElement_CABAC (Macroblock *currMB, SyntaxElement *se, DataPartition *this_dataPart); + +extern int check_next_mb_and_get_field_mode_CABAC_p_slice( Slice *currSlice, SyntaxElement *se, DataPartition *act_dp); +extern int check_next_mb_and_get_field_mode_CABAC_b_slice( Slice *currSlice, SyntaxElement *se, DataPartition *act_dp); + +extern void CheckAvailabilityOfNeighborsCABAC(Macroblock *currMB); + +extern void set_read_and_store_CBP(Macroblock **currMB, int chroma_format_idc); + +#endif // _CABAC_H_ + diff --git a/ldecod/inc/configfile.h b/ldecod/inc/configfile.h new file mode 100644 index 0000000..a0604f9 --- /dev/null +++ b/ldecod/inc/configfile.h @@ -0,0 +1,107 @@ + +/*! + *********************************************************************** + * \file + * configfile.h + * \brief + * Prototypes for configfile.c and definitions of used structures. + *********************************************************************** + */ + +#ifndef _CONFIGFILE_H_ +#define _CONFIGFILE_H_ + +#define DEFAULTCONFIGFILENAME "decoder.cfg" + +#include "config_common.h" +//#define PROFILE_IDC 88 +//#define LEVEL_IDC 21 + + +InputParameters cfgparams; + +#ifdef INCLUDED_BY_CONFIGFILE_C +// Mapping_Map Syntax: +// {NAMEinConfigFile, &cfgparams.VariableName, Type, InitialValue, LimitType, MinLimit, MaxLimit, CharSize} +// Types : {0:int, 1:text, 2: double} +// LimitType: {0:none, 1:both, 2:minimum, 3: QP based} +// We could separate this based on types to make it more flexible and allow also defaults for text types. +Mapping Map[] = { + {"InputFile", &cfgparams.infile, 1, 0.0, 0, 0.0, 0.0, FILE_NAME_SIZE, }, + {"OutputFile", &cfgparams.outfile, 1, 0.0, 0, 0.0, 0.0, FILE_NAME_SIZE, }, + {"RefFile", &cfgparams.reffile, 1, 0.0, 0, 0.0, 0.0, FILE_NAME_SIZE, }, + {"WriteUV", &cfgparams.write_uv, 0, 1.0, 1, 0.0, 1.0, }, + {"FileFormat", &cfgparams.FileFormat, 0, 0.0, 1, 0.0, 1.0, }, + {"RefOffset", &cfgparams.ref_offset, 0, 0.0, 1, 0.0, 256.0, }, + {"POCScale", &cfgparams.poc_scale, 0, 2.0, 1, 1.0, 10.0, }, +#ifdef _LEAKYBUCKET_ + {"R_decoder", &cfgparams.R_decoder, 0, 500000.0, 2, 0.0, 0.0, }, + {"B_decoder", &cfgparams.B_decoder, 0, 104000.0, 2, 0.0, 0.0, }, + {"F_decoder", &cfgparams.F_decoder, 0, 73000.0, 2, 0.0, 0.0, }, + {"LeakyBucketParamFile", &cfgparams.LeakyBucketParamFile, 1, 0.0, 0, 0.0, 0.0, FILE_NAME_SIZE, }, +#endif + {"DisplayDecParams", &cfgparams.bDisplayDecParams, 0, 1.0, 1, 0.0, 1.0, }, + + + {"RefPOCGap", &cfgparams.ref_poc_gap, 0, 2.0, 1, 0.0, 4.0, }, + {"POCGap", &cfgparams.poc_gap, 0, 2.0, 1, 0.0, 4.0, }, + {"Silent", &cfgparams.silent, 0, 0.0, 1, 0.0, 1.0, }, + {"IntraProfileDeblocking", &cfgparams.intra_profile_deblocking, 0, 1.0, 1, 0.0, 1.0, }, + {"DecFrmNum", &cfgparams.iDecFrmNum, 0, 0.0, 2, 0.0, 0.0, }, +#if (MVC_EXTENSION_ENABLE) + {"DecodeAllLayers", &cfgparams.DecodeAllLayers, 0, 0.0, 1, 0.0, 1.0, }, +#endif + {"DPBPLUS0", &cfgparams.dpb_plus[0], 0, 2.0, 1, -16.0, 16.0, }, + {"DPBPLUS1", &cfgparams.dpb_plus[1], 0, 0.0, 1, -16.0, 16.0, }, + + //Error Concealment + {"ConcealMode", &cfgparams.conceal_mode, 0, 0.0, 1, 0.0, 22.0, }, + + + + // {NAMEinConfigFile, &cfgparams.VariableName, Type, InitialValue, LimitType, MinLimit, MaxLimit, CharSize} + + //! Following parameters were edited by Joao Carreira + //! Parser + {"FrameSkip", &cfgparams.FrameSkip, 0, 0.0, 0, 0.0, 0.0, }, + {"IDRPeriod", &cfgparams.IDR_Period, 0, 0.0, 0, 0.0, 0.0, }, + {"GOPSize", &cfgparams.GOP_Size, 0, 0.0, 0, 0.0, 0.0, }, + {"SearchModeRef", &cfgparams.search_mode_ref, 0, 1.0, 1, 0.0, 4.0, }, + {"SearchModeNonRef", &cfgparams.search_mode_nonref, 0, 1.0, 1, 0.0, 4.0, }, + {"ErrorPropagation", &cfgparams.error_prop, 0, 0.0, 1, 0.0, 1.0, }, + {"DropNALU", &cfgparams.drop_nalu, 1, 0.0, 0, 0.0, 0.0, MAX_LOST_NALU, }, + //! Stats + {"StatsFile", &cfgparams.statsfile, 1, 0.0, 0, 0.0, 0.0, FILE_NAME_SIZE, }, + + {"DisparityMethod", &cfgparams.DisparityMethod, 0, 0.0, 1, 0.0, 4.0, }, + {"DisparityDirection", &cfgparams.DisparityDirection, 0, 0.0, 1, -1.0, 1.0, }, + {"DisparityReference", &cfgparams.DisparityReference, 0, 0.0, 1, 0.0, 1.0, }, + {"DisparityWindow", &cfgparams.DisparityWindow, 0, 1.0, 1, 1.0, 16.0, }, + {"DisparityProjection", &cfgparams.DisparityProjection, 0, 0.0, 1, 0.0, 2.0, }, + {"DispLoadFileName", &cfgparams.DispLoadFileName, 1, 0.0, 0, 0.0, 0.0, FILE_NAME_SIZE, }, + {"Disparity4x4BlockSize", &cfgparams.Disparity4x4BlockSize, 0, 4.0, 1, 4.0, 256.0, }, + {"MotionVectorCandidates", &cfgparams.MotionVectorCandidates, 0, 0.0, 1, 0.0, 5, }, + {"MVConcealmentMode", &cfgparams.MVConcealmentMode, 0, 0.0, 1, 0.0, 4, }, + {"TemporalThreshold", &cfgparams.TemporalThreshold, 0, 0.0, 1, 0.0, 100, }, + {"SpatialThreshold", &cfgparams.SpatialThreshold, 0, 0.0, 1, 0.0, 100, }, + + //! NAL unit Parser parameters + {"NALUParser", &cfgparams.dump_nalu, 0, 0.0, 1, 0.0, 1.0, }, + {"PredictionParser", &cfgparams.PredictionParser, 0, 0.0, 1, 0.0, 1.0, }, + + //! Transcode parameters + {"TranscodeFile", &cfgparams.TranscodeFile, 1, 0.0, 0, 0.0, 0.0, FILE_NAME_SIZE, }, + + {NULL, NULL, -1, 0.0, 0, 0.0, 0.0, }, + +}; +#endif + +#ifndef INCLUDED_BY_CONFIGFILE_C +extern Mapping Map[]; +#endif +extern void JMDecHelpExit (); +extern void ParseCommand(InputParameters *p_Inp, int ac, char *av[]); + +#endif + diff --git a/ldecod/inc/context_ini.h b/ldecod/inc/context_ini.h new file mode 100644 index 0000000..d56be25 --- /dev/null +++ b/ldecod/inc/context_ini.h @@ -0,0 +1,23 @@ + +/*! + ************************************************************************************* + * \file context_ini.h + * + * \brief + * CABAC context initializations + * + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * - Detlev Marpe + * - Heiko Schwarz + ************************************************************************************** + */ + + +#ifndef _CONTEXT_INI_ +#define _CONTEXT_INI_ + +extern void init_contexts (Slice *currslice); + +#endif + diff --git a/ldecod/inc/contributors.h b/ldecod/inc/contributors.h new file mode 100644 index 0000000..3822e34 --- /dev/null +++ b/ldecod/inc/contributors.h @@ -0,0 +1,232 @@ + +/*! \file + * contributors.h + * \brief + * List of contributors and copyright information. + * + * \par Copyright statements + \verbatim + H.264 JM coder/decoder + + Copyright (C) 2000 by + Telenor Satellite Services, Norway + Ericsson Radio Systems, Sweden + TELES AG, Germany + Nokia Inc., USA + Nokia Corporation, Finland + Siemens AG, Germany + Fraunhofer-Institute for Telecommunications Heinrich-Hertz-Institut (HHI), Germany + University of Hannover, Institut of Communication Theory and Signal Processing, Germany + TICSP, Tampere University of Technology, Finland + Munich University of Technology, Institute for Communications Engineering, Germany + Videolocus, Canada + Motorola Inc., USA + Microsoft Corp., USA + Apple Computer, Inc. + RealNetworks, Inc., USA + Thomson, Inc., USA + Sejong Univ., Digital Media System Lab., Korea + \endverbatim + \par Full Contact Information + \verbatim + + Lowell Winger + Guy Côté + Michael Gallant + VideoLocus Inc. + 97 Randall Dr. + Waterloo, ON, Canada N2V1C5 + + Inge Lille-Langøy + Telenor Satellite Services + P.O.Box 6914 St.Olavs plass + N-0130 Oslo, Norway + + Rickard Sjoberg + Ericsson Radio Systems + KI/ERA/T/VV + 164 80 Stockholm, Sweden + + Stephan Wenger + TU Berlin / TELES AG + Sekr. FR 6-3 + Franklinstr. 28-29 + D-10587 Berlin, Germany + + Jani Lainema + Nokia Inc. / Nokia Research Center + 6000 Connection Drive + Irving, TX 75039, USA + + Sebastian Purreiter + Siemens AG + ICM MD MP RD MCH 83 + P.O.Box 80 17 07 + D-81617 Munich, Germany + + Thomas Wedi + University of Hannover + Institut of Communication Theory and Signal Processing + Appelstr. 9a + 30167 Hannover, Germany + + Guido Heising + Fraunhofer-Institute for Telecommunications + Heinrich-Hertz-Institut (HHI) + Einsteinufer 37 + 10587 Berlin + Germany + + Gabi Blaettermann + Fraunhofer-Institute for Telecommunications + Heinrich-Hertz-Institut (HHI) + Einsteinufer 37 + 10587 Berlin + Germany + + Detlev Marpe + Fraunhofer-Institute for Telecommunications + Heinrich-Hertz-Institut (HHI) + Einsteinufer 37 + 10587 Berlin + Germany + + Ragip Kurceren + Nokia Inc. / Nokia Research Center + 6000 Connection Drive + Irving, TX 75039, USA + + Viktor Varsa + Nokia Inc. / Nokia Research Center + 6000 Connection Drive + Irving, TX 75039, USA + + Ye-Kui Wang + Tampere University of Technology + Tampere International Center for Signal Processing + 33720 Tampere, Finland + + Ari Hourunranta + Nokia Corporation / Nokia Mobile Phones + P.O. Box 88 + 33721 Tampere, Finland + + Yann Le Maguet + Philips Research France + + Dong Tian + Tampere University of Technology + Tampere International Center for Signal Processing + 33720 Tampere, Finland + + Miska M. Hannuksela + Nokia Corporation / Nokia Mobile Phones + P.O. Box 88 + 33721 Tampere, Finland + + Karsten Suehring + Fraunhofer-Institute for Telecommunications + Heinrich-Hertz-Institut (HHI) + Einsteinufer 37 + 10587 Berlin + Germany + + Heiko Schwarz + Fraunhofer-Institute for Telecommunications + Heinrich-Hertz-Institut (HHI) + Einsteinufer 37 + 10587 Berlin + Germany + + Tobias Oelbaum + Institute for Communications Engineering + Munich University of Technology + Germany + + Limin Wang + Krit Panusopone + Rajeev Gandhi + Yue Yu + Motorola Inc. + 6450 Sequence Drive + San Diego, CA 92121 USA + + Feng Wu + Xiaoyan Sun + Microsoft Research Asia + 3/F, Beijing Sigma Center + No.49, Zhichun Road, Hai Dian District, + Beijing China 100080 + + Yoshihiro Kikuchi + Takeshi Chujoh + Toshiba Corporation + Research and Development Center + Kawasaki 212-8582, Japan + + Shinya Kadono + Matsushita Electric Industrial Co., Ltd. + 1006 Kadoma, Kadoma + Osaka 663-8113, Japan + + Dzung Hoang + 10533 Roy Butler Dr. + Austin, TX 78717 + + Eric Viscito + eV Consulting + 52 Tracy Ln + Shelburne, VT 05482 USA + + Barry Haskell + Apple Computer, Inc. + 2 Infinite Loop + Cupertino, California 95014 + + Greg Conklin + RealNetworks, Inc. + 2601 Elliott Ave + Seattle, WA 98101 + + Jill Boyce + Cristina Gomila + Thomson + 2 Independence Way + Princeton, NJ 08540 + + Alexis Michael Tourapis + Athanasios Leontaris + Chris Vogt + Peshala Pahalawatta + Yan Ye + Yuwen He + Dolby Laboratories Inc. + 3601 West Alameda Ave. + Burbank, CA 91505 + + Saurav K Bandyopadhyay + Purvin Pandit + Zhenyu Wu + Thomson Inc. + 2 Independence Way + Princeton, NJ 08540 + + Shun-ichi Sekiguchi + Information Technology R&D Center, + Mitsubishi Electric Corporation + 5-1-1, Ofuna, Kamakura, Japan + + Yung-Lyul Lee + Ki-Hun Han + Department of Computer Engineering, + Sejong University + 98 Kunja-Dong, Kwangjin-Gu, Seoul 143-747, Korea + + Jianhua Wu + Panasonic Singapore Laboratories Pte Ltd + Blk 1022 Tai Seng Ave #06-3530 Tai Seng Ind Est + Singapore 534415 + + \endverbatim +*/ + diff --git a/ldecod/inc/dec_statistics.h b/ldecod/inc/dec_statistics.h new file mode 100644 index 0000000..08ed871 --- /dev/null +++ b/ldecod/inc/dec_statistics.h @@ -0,0 +1,33 @@ +/*! + ************************************************************************** + * \file dec_statistics.h + * + * \brief + * Various decoding statistics + * + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * - Alexis Tourapis + * + ************************************************************************** + */ + +#ifndef _DEC_STATISTICS_H_ +#define _DEC_STATISTICS_H_ +#include "global.h" + +typedef struct dec_stat_parameters +{ + int frame_ctr [NUM_SLICE_TYPES]; //!< Counter for different frame coding types (assumes one slice type per frame) + int64 mode_use [NUM_SLICE_TYPES][MAXMODE]; //!< Macroblock mode usage per slice + int64 mode_use_transform [NUM_SLICE_TYPES][MAXMODE][2]; + + int64 *histogram_mv [2][2]; //!< mv histogram (per list and per direction) + int64 *histogram_refs[2]; //!< reference histogram (per list) +} DecStatParameters; + +extern void init_dec_stats (DecStatParameters *stats); +extern void delete_dec_stats(DecStatParameters *stats); + + +#endif diff --git a/ldecod/inc/defines.h b/ldecod/inc/defines.h new file mode 100644 index 0000000..f1cfb96 --- /dev/null +++ b/ldecod/inc/defines.h @@ -0,0 +1,310 @@ + +/*! + ************************************************************************** + * \file defines.h + * + * \brief + * Header file containing some useful global definitions + * + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * - Detlev Marpe + * - Karsten Suehring + * - Alexis Michael Tourapis + * + * + * \date + * 21. March 2001 + ************************************************************************** + */ + +#ifndef _DEFINES_H_ +#define _DEFINES_H_ +#ifdef TRACE +#undef TRACE +#endif +#if defined _DEBUG +# define TRACE 0 //!< 0:Trace off 1:Trace on 2:detailed CABAC context information +#else +# define TRACE 0 //!< 0:Trace off 1:Trace on 2:detailed CABAC context information +#endif + +#define JM "18 JC (FRExt)" +#define VERSION "18.2" +#define EXT_VERSION "(FRExt)" + +//! Edited by Joao Carreira +#define PRINTREFLIST_JC 0 //!< Print ref list info for debug purposes +#define FOUND_NALU 0 //!< printf Founded NAL Unit +#define PRINT_OUTPUT_DPB_POC 0 //!< Print outputed frames for debug purposes +#define DISPLAY_OUTPUTED_FRAMES 0 +#define ENABLE_PARSER 0 //!< Use Parser functions +//#define HAVE_OPENCV 0 + +//#ifndef _DEBUG +//#define _DEBUG 1 +//#endif + +//! Edited by Joao Carreira +#if( ENABLE_PARSER == 1 ) +#define PRINTREFLIST_JC 1 //!< Print ref list info for debug purposes +#define FOUND_NALU 1 //!< printf Founded NAL Unit +#define PRINT_OUTPUT_DPB_POC 1 //!< Print outputed frames for debug purposes +#define DUMP_DPB 1 //!< Dump DPB info for debug purposes +#endif + +//#define DISABLE_MB_DECODING //! disable MB deconding. Software work as a stream parser + +#if ( _RELEASE ) +#define LOG_LEVEL 0 //!< Status report only +#else +#define LOG_LEVEL 2 //!< Debug porpose +#endif + +//#define USE_STDOUT_STDERR 1 + +//#define printf printf + +#define DUMP_DPB 0 //!< Dump DPB info for debug purposes +#define PRINTREFLIST 0 //!< Print ref list info for debug purposes +#define PAIR_FIELDS_IN_OUTPUT 0 //!< Pair field pictures for output purposes +#define IMGTYPE 0 //!< Define imgpel size type. 0 implies byte (cannot handle >8 bit depths) and 1 implies unsigned short +#define ENABLE_FIELD_CTX 1 //!< Enables Field mode related context types for CABAC +#define ENABLE_HIGH444_CTX 1 //!< Enables High 444 profile context types for CABAC. +#define ZEROSNR 0 //!< PSNR computation method +#define ENABLE_OUTPUT_TONEMAPPING 1 //!< enable tone map the output if tone mapping SEI present +#define JCOST_CALC_SCALEUP 1 //!< 1: J = (D<>LAMBDA_ACCURACY_BITS) +#define DISABLE_ERC 1 //!< Disable any error concealment processes +#define JM_PARALLEL_DEBLOCK 0 //!< Enables Parallel Deblocking +#define SIMULCAST_ENABLE 0 //!< to test the decoder + +#define MVC_EXTENSION_ENABLE 1 //!< enable support for the Multiview High Profile +#define ENABLE_DEC_STATS 0 //!< enable decoder statistics collection + +#define MVC_INIT_VIEW_ID -1 +#define MAX_VIEW_NUM 5 +#define BASE_VIEW_IDX 0 + +#include "typedefs.h" + +#define SSE_MEMORY_ALIGNMENT 16 + +#define MAX_NUM_SLICES 500 +#define MAX_REFERENCE_PICTURES 32 //!< H.264 allows 32 fields +#define MAX_CODED_FRAME_SIZE 8000000 //!< bytes for one frame +#define MAX_NUM_DECSLICES 150 +#define MAX_DEC_THREADS 16 //16 core deocoding; +#define MCBUF_LUMA_PAD_X 32 +#define MCBUF_LUMA_PAD_Y 12 +#define MCBUF_CHROMA_PAD_X 16 +#define MCBUF_CHROMA_PAD_Y 8 +#define MAX_NUM_DPB_LAYERS MAX_VIEW_NUM + +//AVC Profile IDC definitions +typedef enum { + FREXT_CAVLC444 = 44, //!< YUV 4:4:4/14 "CAVLC 4:4:4" + BASELINE = 66, //!< YUV 4:2:0/8 "Baseline" + MAIN = 77, //!< YUV 4:2:0/8 "Main" + EXTENDED = 88, //!< YUV 4:2:0/8 "Extended" + FREXT_HP = 100, //!< YUV 4:2:0/8 "High" + FREXT_Hi10P = 110, //!< YUV 4:2:0/10 "High 10" + FREXT_Hi422 = 122, //!< YUV 4:2:2/10 "High 4:2:2" + FREXT_Hi444 = 244, //!< YUV 4:4:4/14 "High 4:4:4" + MVC_HIGH = 118, //!< YUV 4:2:0/8 "Multiview High" + STEREO_HIGH = 128 //!< YUV 4:2:0/8 "Stereo High" +} ProfileIDC; + +/* Edited by Joao Carreira */ +#define MAX_LOST_NALU 200 + +#define FILE_NAME_SIZE 255 +#define INPUT_TEXT_SIZE 1024 + + +#if (ENABLE_HIGH444_CTX == 1) +# define NUM_BLOCK_TYPES 22 +#else +# define NUM_BLOCK_TYPES 10 +#endif + + +//#define _LEAKYBUCKET_ + +#define BLOCK_SHIFT 2 +#define BLOCK_SIZE 4 +#define BLOCK_SIZE_8x8 8 +#define SMB_BLOCK_SIZE 8 +#define BLOCK_PIXELS 16 +#define MB_BLOCK_SIZE 16 +#define MB_PIXELS 256 // MB_BLOCK_SIZE * MB_BLOCK_SIZE +#define MB_PIXELS_SHIFT 8 // log2(MB_BLOCK_SIZE * MB_BLOCK_SIZE) +#define MB_BLOCK_SHIFT 4 +#define BLOCK_MULTIPLE 4 // (MB_BLOCK_SIZE/BLOCK_SIZE) +#define MB_BLOCK_PARTITIONS 16 // (BLOCK_MULTIPLE * BLOCK_MULTIPLE) +#define BLOCK_CONTEXT 64 // (4 * MB_BLOCK_PARTITIONS) + +// These variables relate to the subpel accuracy supported by the software (1/4) +#define BLOCK_SIZE_SP 16 // BLOCK_SIZE << 2 +#define BLOCK_SIZE_8x8_SP 32 // BLOCK_SIZE8x8 << 2 + +// Available MB modes +typedef enum { + PSKIP = 0, + BSKIP_DIRECT = 0, + P16x16 = 1, + P16x8 = 2, + P8x16 = 3, + SMB8x8 = 4, + SMB8x4 = 5, + SMB4x8 = 6, + SMB4x4 = 7, + P8x8 = 8, + I4MB = 9, + I16MB = 10, + IBLOCK = 11, + SI4MB = 12, + I8MB = 13, + IPCM = 14, + MAXMODE = 15 +} MBModeTypes; + +// number of intra prediction modes +#define NO_INTRA_PMODE 9 + +// Direct Mode types +typedef enum { + DIR_TEMPORAL = 0, //!< Temporal Direct Mode + DIR_SPATIAL = 1 //!< Spatial Direct Mode +} DirectModes; + +// CAVLC block types +typedef enum { + LUMA = 0, + LUMA_INTRA16x16DC = 1, + LUMA_INTRA16x16AC = 2, + CB = 3, + CB_INTRA16x16DC = 4, + CB_INTRA16x16AC = 5, + CR = 8, + CR_INTRA16x16DC = 9, + CR_INTRA16x16AC = 10 +} CAVLCBlockTypes; + +// CABAC block types +typedef enum { + LUMA_16DC = 0, + LUMA_16AC = 1, + LUMA_8x8 = 2, + LUMA_8x4 = 3, + LUMA_4x8 = 4, + LUMA_4x4 = 5, + CHROMA_DC = 6, + CHROMA_AC = 7, + CHROMA_DC_2x4 = 8, + CHROMA_DC_4x4 = 9, + CB_16DC = 10, + CB_16AC = 11, + CB_8x8 = 12, + CB_8x4 = 13, + CB_4x8 = 14, + CB_4x4 = 15, + CR_16DC = 16, + CR_16AC = 17, + CR_8x8 = 18, + CR_8x4 = 19, + CR_4x8 = 20, + CR_4x4 = 21 +} CABACBlockTypes; + +// Macro defines +#define Q_BITS 15 +#define DQ_BITS 6 +#define Q_BITS_8 16 +#define DQ_BITS_8 6 + + +#define IS_I16MB(MB) ((MB)->mb_type==I16MB || (MB)->mb_type==IPCM) +#define IS_DIRECT(MB) ((MB)->mb_type==0 && (currSlice->slice_type == B_SLICE )) + +#define TOTRUN_NUM 15 +#define RUNBEFORE_NUM 7 +#define RUNBEFORE_NUM_M1 6 + +// Quantization parameter range +#define MIN_QP 0 +#define MAX_QP 51 +// 4x4 intra prediction modes +typedef enum { + VERT_PRED = 0, + HOR_PRED = 1, + DC_PRED = 2, + DIAG_DOWN_LEFT_PRED = 3, + DIAG_DOWN_RIGHT_PRED = 4, + VERT_RIGHT_PRED = 5, + HOR_DOWN_PRED = 6, + VERT_LEFT_PRED = 7, + HOR_UP_PRED = 8 +} I4x4PredModes; + +// 16x16 intra prediction modes +typedef enum { + VERT_PRED_16 = 0, + HOR_PRED_16 = 1, + DC_PRED_16 = 2, + PLANE_16 = 3 +} I16x16PredModes; + +// 8x8 chroma intra prediction modes +typedef enum { + DC_PRED_8 = 0, + HOR_PRED_8 = 1, + VERT_PRED_8 = 2, + PLANE_8 = 3 +} I8x8PredModes; + +// Color components +enum { + Y_COMP = 0, // Y Component + U_COMP = 1, // U Component + V_COMP = 2, // V Component + R_COMP = 3, // R Component + G_COMP = 4, // G Component + B_COMP = 5, // B Component + T_COMP = 6 +} ColorComponent; + +enum { + EOS = 1, //!< End Of Sequence + SOP = 2, //!< Start Of Picture + SOS = 3, //!< Start Of Slice + SOS_CONT = 4 +}; + +// MV Prediction types +typedef enum { + MVPRED_MEDIAN = 0, + MVPRED_L = 1, + MVPRED_U = 2, + MVPRED_UR = 3 +} MVPredTypes; + +enum { + DECODING_OK = 0, + SEARCH_SYNC = 1, + PICTURE_DECODED = 2 +}; + +#define LAMBDA_ACCURACY_BITS 16 +#define INVALIDINDEX (-135792468) + +#define RC_MAX_TEMPORAL_LEVELS 5 + +//Start code and Emulation Prevention need this to be defined in identical manner at encoder and decoder +#define ZEROBYTES_SHORTSTARTCODE 2 //indicates the number of zero bytes in the short start-code prefix + +#define MAX_PLANE 3 +#define IS_FREXT_PROFILE(profile_idc) ( profile_idc>=FREXT_HP || profile_idc == FREXT_CAVLC444 ) +#define HI_INTRA_ONLY_PROFILE (((p_Vid->active_sps->profile_idc>=FREXT_Hi10P)&&(p_Vid->active_sps->constrained_set3_flag))||(p_Vid->active_sps->profile_idc==FREXT_CAVLC444)) + +#endif + diff --git a/ldecod/inc/elements.h b/ldecod/inc/elements.h new file mode 100644 index 0000000..f115bff --- /dev/null +++ b/ldecod/inc/elements.h @@ -0,0 +1,112 @@ + +/*! + ************************************************************************************* + * \file elements.h + * + * \brief + * Header file for elements in H.264 streams + * + * \date + * 6.10.2000 + * + * \version + * 1.0 + * + * \author + * Sebastian Purreiter \n + * Siemens AG, Information and Communication Mobile \n + * P.O.Box 80 17 07 \n + * D-81617 Munich, Germany \n + ************************************************************************************* + */ + +#ifndef _ELEMENTS_H_ +#define _ELEMENTS_H_ + +/*! + * definition of H.264 syntax elements + * order of elements follow dependencies for picture reconstruction + */ +/*! + * \brief Assignment of old TYPE partition elements to new + * elements + * + * old element | new elements + * ----------------+------------------------------------------------------------------- + * TYPE_HEADER | SE_HEADER, SE_PTYPE + * TYPE_MBHEADER | SE_MBTYPE, SE_REFFRAME, SE_INTRAPREDMODE + * TYPE_MVD | SE_MVD + * TYPE_CBP | SE_CBP_INTRA, SE_CBP_INTER + * SE_DELTA_QUANT_INTER + * SE_DELTA_QUANT_INTRA + * TYPE_COEFF_Y | SE_LUM_DC_INTRA, SE_LUM_AC_INTRA, SE_LUM_DC_INTER, SE_LUM_AC_INTER + * TYPE_2x2DC | SE_CHR_DC_INTRA, SE_CHR_DC_INTER + * TYPE_COEFF_C | SE_CHR_AC_INTRA, SE_CHR_AC_INTER + * TYPE_EOS | SE_EOS +*/ + +#define SE_HEADER 0 +#define SE_PTYPE 1 +#define SE_MBTYPE 2 +#define SE_REFFRAME 3 +#define SE_INTRAPREDMODE 4 +#define SE_MVD 5 +#define SE_CBP_INTRA 6 +#define SE_LUM_DC_INTRA 7 +#define SE_CHR_DC_INTRA 8 +#define SE_LUM_AC_INTRA 9 +#define SE_CHR_AC_INTRA 10 +#define SE_CBP_INTER 11 +#define SE_LUM_DC_INTER 12 +#define SE_CHR_DC_INTER 13 +#define SE_LUM_AC_INTER 14 +#define SE_CHR_AC_INTER 15 +#define SE_DELTA_QUANT_INTER 16 +#define SE_DELTA_QUANT_INTRA 17 +#define SE_BFRAME 18 +#define SE_EOS 19 +#define SE_MAX_ELEMENTS 20 + + +#define NO_EC 0 //!< no error concealment necessary +#define EC_REQ 1 //!< error concealment required +#define EC_SYNC 2 //!< search and sync on next header element + +#define MAXPARTITIONMODES 2 //!< maximum possible partition modes as defined in assignSE2partition[][] + +/*! + * \brief lookup-table to assign different elements to partition + * + * \note here we defined up to 6 different partitions similar to + * document Q15-k-18 described in the PROGFRAMEMODE. + * The Sliceheader contains the PSYNC information. \par + * + * Elements inside a partition are not ordered. They are + * ordered by occurence in the stream. + * Assumption: Only partitionlosses are considered. \par + * + * The texture elements luminance and chrominance are + * not ordered in the progressive form + * This may be changed in image.c \par + * + * We also defined the proposed internet partition mode + * of Stephan Wenger here. To select the desired mode + * uncomment one of the two following lines. \par + * + * -IMPORTANT: + * Picture- or Sliceheaders must be assigned to partition 0. \par + * Furthermore partitions must follow syntax dependencies as + * outlined in document Q15-J-23. + */ + + +static const byte assignSE2partition[][SE_MAX_ELEMENTS] = +{ + // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 // element number (do not uncomment) + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, //!< all elements in one partition no data partitioning + { 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 2, 2, 2, 2, 0, 0, 0, 0 } //!< three partitions per slice +}; + + +#endif + diff --git a/ldecod/inc/erc_api.h b/ldecod/inc/erc_api.h new file mode 100644 index 0000000..af91513 --- /dev/null +++ b/ldecod/inc/erc_api.h @@ -0,0 +1,164 @@ + +/*! + ************************************************************************ + * \file erc_api.h + * + * \brief + * External (still inside video decoder) interface for error concealment module + * + * \author + * - Ari Hourunranta + * - Ye-Kui Wang + * - Jill Boyce + * - Saurav K Bandyopadhyay + * - Zhenyu Wu + * + * ************************************************************************ + */ + + +#ifndef _ERC_API_H_ +#define _ERC_API_H_ + +#include "erc_globals.h" + +/* +* Defines +*/ + +/* If the average motion vector of the correctly received macroblocks is less than the +threshold, concealByCopy is used, otherwise concealByTrial is used. */ +#define MVPERMB_THR 8 + +/* used to determine the size of the allocated memory for a temporal Region (MB) */ +#define DEF_REGION_SIZE 384 /* 8*8*6 */ + +#define ERC_BLOCK_OK 3 +#define ERC_BLOCK_CONCEALED 2 +#define ERC_BLOCK_CORRUPTED 1 +#define ERC_BLOCK_EMPTY 0 + + +/* +* Functions to convert MBNum representation to blockNum +*/ + +#define xPosYBlock(currYBlockNum,picSizeX) \ +((currYBlockNum)%((picSizeX)>>3)) + +#define yPosYBlock(currYBlockNum,picSizeX) \ +((currYBlockNum)/((picSizeX)>>3)) + +#define xPosMB(currMBNum,picSizeX) \ +((currMBNum)%((picSizeX)>>4)) + +#define yPosMB(currMBNum,picSizeX) \ +((currMBNum)/((picSizeX)>>4)) + +#define MBxy2YBlock(currXPos,currYPos,comp,picSizeX) \ +((((currYPos)<<1)+((comp)>>1))*((picSizeX)>>3)+((currXPos)<<1)+((comp)&1)) + +#define MBNum2YBlock(currMBNum,comp,picSizeX) \ +MBxy2YBlock(xPosMB((currMBNum),(picSizeX)),yPosMB((currMBNum),(picSizeX)),(comp),(picSizeX)) + + +/* +* typedefs +*/ + +/* segment data structure */ +typedef struct ercSegment_s +{ + short startMBPos; + short endMBPos; + char fCorrupted; +} ercSegment_t; + +/* Error detector & concealment instance data structure */ +typedef struct ercVariables_s +{ + /* Number of macroblocks (size or size/4 of the arrays) */ + int nOfMBs; + /* Number of segments (slices) in frame */ + int nOfSegments; + + /* Array for conditions of Y blocks */ + char *yCondition; + /* Array for conditions of U blocks */ + char *uCondition; + /* Array for conditions of V blocks */ + char *vCondition; + + /* Array for Slice level information */ + ercSegment_t *segments; + int currSegment; + + /* Conditions of the MBs of the previous frame */ + char *prevFrameYCondition; + + /* Flag telling if the current segment was found to be corrupted */ + int currSegmentCorrupted; + /* Counter for corrupted segments per picture */ + int nOfCorruptedSegments; + + /* State variables for error detector and concealer */ + int concealment; + +} ercVariables_t; + +/* +* External function interface +*/ + +void ercWriteMBMODEandMV(Macroblock *currMB); + +void ercInit (VideoParameters *p_Vid, int pic_sizex, int pic_sizey, int flag); +ercVariables_t *ercOpen( void ); +void ercReset( ercVariables_t *errorVar, int nOfMBs, int numOfSegments, int picSizeX ); +void ercClose( VideoParameters *p_Vid, ercVariables_t *errorVar ); +void ercSetErrorConcealment( ercVariables_t *errorVar, int value ); + +void ercStartSegment( int currMBNum, int segment, unsigned int bitPos, ercVariables_t *errorVar ); +void ercStopSegment( int currMBNum, int segment, unsigned int bitPos, ercVariables_t *errorVar ); +void ercMarkCurrSegmentLost(int picSizeX, ercVariables_t *errorVar ); +void ercMarkCurrSegmentOK(int picSizeX, ercVariables_t *errorVar ); +void ercMarkCurrMBConcealed( int currMBNum, int comp, int picSizeX, ercVariables_t *errorVar ); + +int ercConcealIntraFrame( VideoParameters *p_Vid, frame *recfr, int picSizeX, int picSizeY, ercVariables_t *errorVar ); +int ercConcealInterFrame( frame *recfr, objectBuffer_t *object_list, + int picSizeX, int picSizeY, ercVariables_t *errorVar, int chroma_format_idc ); + + + +/* Thomson APIs for concealing entire frame loss */ + +#include "mbuffer.h" +#include "output.h" + +struct concealment_node { + StorablePicture* picture; + int missingpocs; + struct concealment_node *next; +}; + +/** Edited by João Carreira + * Exclued Previous Frame concealment functions +extern struct concealment_node * init_node(StorablePicture* , int ); +extern void print_node( struct concealment_node * ); +extern void print_list( struct concealment_node * ); +extern void init_lists_for_non_reference_loss(DecodedPictureBuffer *p_Dpb, int , PictureStructure ); + +extern void conceal_non_ref_pics(DecodedPictureBuffer *p_Dpb, int diff); +extern void conceal_lost_frames (DecodedPictureBuffer *p_Dpb, Slice *pSlice); + +extern void sliding_window_poc_management(DecodedPictureBuffer *p_Dpb, StorablePicture *p); +extern void write_lost_non_ref_pic (DecodedPictureBuffer *p_Dpb, int poc, int p_out); +extern void write_lost_ref_after_idr (DecodedPictureBuffer *p_Dpb, int pos); +**/ + +extern int comp(const void *, const void *); + + +#endif + diff --git a/ldecod/inc/erc_do.h b/ldecod/inc/erc_do.h new file mode 100644 index 0000000..9b7012f --- /dev/null +++ b/ldecod/inc/erc_do.h @@ -0,0 +1,44 @@ + +/*! + ************************************************************************ + * \file erc_do.h + * + * \brief + * Header for the I & P frame error concealment common functions + * + * \author + * - Viktor Varsa + * - Ye-Kui Wang + * + ************************************************************************ + */ + +#ifndef _ERC_DO_H_ +#define _ERC_DO_H_ + + +#include "erc_api.h" + +void ercPixConcealIMB (VideoParameters *p_Vid, imgpel *currFrame, int row, int column, int predBlocks[], int frameWidth, int mbWidthInBlocks); + +int ercCollect8PredBlocks( int predBlocks[], int currRow, int currColumn, char *condition, + int maxRow, int maxColumn, int step, byte fNoCornerNeigh ); +int ercCollectColumnBlocks( int predBlocks[], int currRow, int currColumn, char *condition, int maxRow, int maxColumn, int step ); + +#define isSplitted(object_list,currMBNum) \ + ((object_list+((currMBNum)<<2))->regionMode >= REGMODE_SPLITTED) + +/* this can be used as isBlock(...,INTRA) or isBlock(...,INTER_COPY) */ +#define isBlock(object_list,currMBNum,comp,regMode) \ + (isSplitted(object_list,currMBNum) ? \ + ((object_list+((currMBNum)<<2)+(comp))->regionMode == REGMODE_##regMode##_8x8) : \ + ((object_list+((currMBNum)<<2))->regionMode == REGMODE_##regMode)) + +/* this can be used as getParam(...,mv) or getParam(...,xMin) or getParam(...,yMin) */ +#define getParam(object_list,currMBNum,comp,param) \ + (isSplitted(object_list,currMBNum) ? \ + ((object_list+((currMBNum)<<2)+(comp))->param) : \ + ((object_list+((currMBNum)<<2))->param)) + +#endif + diff --git a/ldecod/inc/erc_globals.h b/ldecod/inc/erc_globals.h new file mode 100644 index 0000000..2722cd5 --- /dev/null +++ b/ldecod/inc/erc_globals.h @@ -0,0 +1,52 @@ + +/*! + ************************************************************************ + * \file erc_globals.h + * + * \brief + * global header file for error concealment module + * + * \author + * - Viktor Varsa + * - Ye-Kui Wang + ************************************************************************ + */ + +#ifndef _ERC_GLOBALS_H_ +#define _ERC_GLOBALS_H_ + +#include "defines.h" + +/* "block" means an 8x8 pixel area */ + +/* Region modes */ +#define REGMODE_INTER_COPY 0 //!< Copy region +#define REGMODE_INTER_PRED 1 //!< Inter region with motion vectors +#define REGMODE_INTRA 2 //!< Intra region +#define REGMODE_SPLITTED 3 //!< Any region mode higher than this indicates that the region + //!< is splitted which means 8x8 block +#define REGMODE_INTER_COPY_8x8 4 +#define REGMODE_INTER_PRED_8x8 5 +#define REGMODE_INTRA_8x8 6 + +//! YUV pixel domain image arrays for a video frame +typedef struct frame_s +{ + VideoParameters *p_Vid; + imgpel *yptr; + imgpel *uptr; + imgpel *vptr; +} frame; + +//! region structure stores information about a region that is needed for concealment +typedef struct object_buffer +{ + byte regionMode; //!< region mode as above + int xMin; //!< X coordinate of the pixel position of the top-left corner of the region + int yMin; //!< Y coordinate of the pixel position of the top-left corner of the region + int mv[3]; //!< motion vectors in 1/4 pixel units: mvx = mv[0], mvy = mv[1], + //!< and ref_frame = mv[2] +} objectBuffer_t; + +#endif + diff --git a/ldecod/inc/errorconcealment.h b/ldecod/inc/errorconcealment.h new file mode 100644 index 0000000..4d5620c --- /dev/null +++ b/ldecod/inc/errorconcealment.h @@ -0,0 +1,21 @@ + + +/*! + **************************************************************************** + * \file errorconcealment.h + * + * \brief + * Header file for errorconcealment.c + * + **************************************************************************** + */ + +#ifndef _ERRORCONCEALMENT_H_ +#define _ERRORCONCEALMENT_H_ + +extern int get_concealed_element(VideoParameters *p_Vid, SyntaxElement *sym); +extern int set_ec_flag (VideoParameters *p_Vid, int se); +extern void reset_ec_flags (VideoParameters *p_Vid); + +#endif + diff --git a/ldecod/inc/ferc/ferc_api.h b/ldecod/inc/ferc/ferc_api.h new file mode 100644 index 0000000..38fcaa1 --- /dev/null +++ b/ldecod/inc/ferc/ferc_api.h @@ -0,0 +1,106 @@ + +/*! +************************************************************************ +* \file ferc_api.h.h +* +* \brief +* Definitions for frame lost error concealment. +* +* Based on Thomson APIs for concealing entire frame loss. +* Modification to be compatible with frame loss in auxiliary view, +* and some improvements in error concealment strategies +* +* \author +* Previous work +* - Jill Boyce +* - Saurav K Bandyopadhyay +* - Zhenyu Wu +* - Purvin Pandit +* Actual Work +* - Jo���o Carreira +* +************************************************************************ +*/ + +#ifndef _FERC_API_H +#define _FERC_API_H + +#define CONCEAL_REMAINING_FRAMES 1 + + +#include "global.h" +#include "mbuffer.h" + +/* Concealment Modes */ +enum { + + NONE_CONCEAL = 0, + // References + FRAME_COPY_FF = 1, + FRAME_COPY_BC = 2, + MOTION_COPY_TEMPORAL = 3, + MOTION_COPY_SPATIAL = 4, + MOTION_COPY_TEMPORAL_SPATIAL = 5, + MOTION_COPY_ENHANCED = 6, + // Concealment strategies implemented + INTERVIEW_COPY = 10, + MOTION_INTRAVIEW = 11, + MOTION_INTERVIEW = 12, + MOTION_INTERVIEW_FIRST = 13, + MOTION_INTERVIEW_MVE_INTERCOPY = 14, + MOTION_SMART_MOTION_BASED = 15, + MOTION_ITERATIONS = 16, + + MOTION_INTERACTIVE_INTRA_INTER = 17, + MOTION_INTRA_INTER_SAD = 18, + ENHANCED_MVE = 19, + + // Auxiliary methods + CONCEALMV = 50, + MOTION_INTRAVIEW_DISP = 51, + ZERO_MV_CONC = 52, + + MSE_BASED = 60, + SPAT_TEMP_DIFF = 61, + MOTION_DISP_FRAME = 62, + NONE = 63, + + //Previous proposed + REF_DFDP = 21, + REF_ISMID = 22, +}; + + +extern void fercOpenFrameConcealment ( VideoParameters *p_Vid ); +extern void fercCloseFrameConcealment ( VideoParameters *p_Vid ); + +extern void fercFillCurrPicture(VideoParameters *p_Vid, StorablePicture *currPicture); + +extern void fercConcealLostRefFrames(DecodedPictureBuffer *p_Dpb, Slice *currSlice, int is_IDR); + +extern void fercSlindingWindowsOnceFrameRemoved( DecodedPictureBuffer *p_Dpb , int pos ); +extern void fercAddPOC2Dpb( DecodedPictureBuffer *p_Dpb , int poc ); +extern void fercResetPOCInDpb( DecodedPictureBuffer *p_Dpb ); +extern void fercConcealLostNonRefFrames(DecodedPictureBuffer *p_Dpb, unsigned int diff); + +extern void fercConcealLostInterviewRefFrames(DecodedPictureBuffer *p_Dpb, StorablePicture **LostPicture, Slice *currSlice, int MissingView); + +extern void fercConcealRemainingLostFrames(DecodedPictureBuffer *p_Dpb, FrameStore *fs); + +extern void fercWriteConcealedLostNonRefFrames(DecodedPictureBuffer *p_Dpb, int iPoc, int iView); + +extern StorablePicture *fercGetPictureForConcealFromDpb(DecodedPictureBuffer *p_Dpb, int poc, int search_view_id, int search_mode, int search_concealed, unsigned int *position); + +extern void fercSaveListsInfo( VideoParameters *p_Vid, Slice *currSlice ); + +/* Error Propagation Concealment Functions */ +extern void GetDisparityForCurrentSlice(Slice *currSlice); + +extern void fercCopyImg( VideoParameters *p_Vid, StorablePicture *Source, StorablePicture *Dest ); +extern void fercModLostRefFrames( VideoParameters *p_Vid, StorablePicture *DecPicture ); + +extern void ProjectMotionToCurrSlice (VideoParameters *p_Vid, StorablePicture *currPicture, Slice *currSlice); +extern void ChangeInterViewPrediction(Macroblock *currMB, StorablePicture *dec_picture, int pred_dir, int i, int j, int block_size_x, int block_size_y ); +extern void fercFreeMotionPropInfo( StorablePicture *p ); + +#endif diff --git a/ldecod/inc/ferc/ferc_internals.h b/ldecod/inc/ferc/ferc_internals.h new file mode 100644 index 0000000..b2c24fc --- /dev/null +++ b/ldecod/inc/ferc/ferc_internals.h @@ -0,0 +1,148 @@ +/*! +************************************************************************ +* \file ferc_internals.h +* +* \brief +* Internal function for ferc API +* +* +* \author +* - Jo���o Carreira +* +************************************************************************ +*/ + +#ifndef _FERC_INTERNALS_H_ +#define _FERC_INTERNALS_H_ + +#include "defines.h" +#include "global.h" +#include "mbuffer.h" + +#define MAX_PEL_VALUE 255 +#define THRESHOLD_DISP 62 + + +/*! Definition of search modes */ +static enum search_frame { + CLOSEST_ALL = 0, + CLOSEST_RECEIVED = 1, + CLOSEST_FUTURE = 2, + EXACT = 3, + WELL_RECEIVED_STEREO = 4, +}; + +/*! Motion vector concealment modes implemented */ +enum { + ZERO_MV = 0, + PREVIOUS = 1, + AVERAGE = 2, + MEDIAN = 3, +}; + +struct ConcealNode { + StorablePicture* picture; + int MissPoc; + // int MissView; + struct ConcealNode *next; +}; + + +typedef struct conceal_params +{ + + int use_DisparityProjection; + //int use_InterMotionCopy; + //int use_IntraMotionCopy; + + //! Concealment Mode + int ConcealmentMode; + //! Concealment Function + void (*ConcealFuction) (VideoParameters *p_Vid, + StorablePicture *Source, StorablePicture *Dest ); + + int conceal_slice_type; + int flag_bad_poc_calculation; + + // picture error concealment + // concealment_head points to first node in list, concealment_end points to + // last node in list. Initialize both to NULL, meaning no nodes in list yet +#if (MVC_EXTENSION_ENABLE) + struct ConcealNode *ConcealHead[MAX_VIEW_NUM]; + // struct concealment_node *concealment_end[MAX_VIEW_NUM]; +#else + struct ConcealNode *ConcealHead[1]; + // struct concealment_node *concealment_end; +#endif + + int last_out_frame_no[MAX_VIEW_NUM]; + struct frame_store *last_out_fs[MAX_VIEW_NUM]; + + int fercPOCinDpb[MAX_VIEW_NUM][100]; + +}ConcealParameters; + + +struct partial_mv_double +{ + double mv_x; + double mv_y; +}; +struct partial_mv +{ + struct partial_mv_double mv; + char ref_idx; +}; + + +typedef struct motion_prop_info +{ + PicMotionParams ***mv_info; + int **mv_info_count; + int ***mv_info_count_lists; + int Temporal_Ref_Idx; + int Spatial_Ref_Idx; +}MotionPropInfo; + +extern int sort_by_frame_num_up( const void *arg1, const void *arg2 ); +extern int sort_by_poc_up( const void *arg1, const void *arg2 ); +extern int sort_mv_abs_up( const void *arg1, const void *arg2 ); + +extern void MeanFilter( imgpel **initial_matrix, imgpel **filtrated_matrix, int size_x, int size_y, int kernel_size ); +extern void MedianFilter( imgpel **initial_matrix, imgpel **filtrated_matrix, int size_x, int size_y, int kernel_size ); +extern void ConcealMotionVector(VideoParameters *p_Vid, StorablePicture *Dest, Slice *Slice, int y_block4x4, int x_block4x4,int method, int list, int conc_mv[3], int temporal_idx); + +extern void buildPredblockRegionYUV(VideoParameters *p_Vid, StorablePicture *ref_picture, Macroblock *currMB, int *mv,int x, int y, imgpel *pMB, int Block_Size, imgpel **tmp_block, int have_temp); +extern void EnhancedbuildPredblock4x4YUV(VideoParameters *p_Vid, int *mv, int x, int y, imgpel *predMB, int list, int current_mb_nr, imgpel **tmp_block); +extern void EnhancedbuildPredblockRegionYUV(VideoParameters *p_Vid, int *mv, int x, int y, imgpel *predMB, int list, int current_mb_nr, int BlockSize, imgpel **tmp_block); +extern void ferc_bi_prediction(imgpel *mb_pred, imgpel *block_l0, uint8 value_l0, imgpel *block_l1, uint8 value_l1, int ver_block_size, int hor_block_size, int have_uv); +extern void copyPredblockRegionYUVDestination(StorablePicture *Destination, imgpel *predBlock, int x, int y, int BLockSize); + + +extern StorablePicture *fercGetPictureForConcealFromDpb(DecodedPictureBuffer *p_Dpb, int poc, int search_view_id, int search_mode, int search_concealed, unsigned int *position); + + +//! Motion Estimation function +extern int blockMatch(const imgpel* refFrame, int stepBytesRF /*stride*/, const imgpel* curBlock, + int stepBytesCB /*stride*/, int* matchBlock /*results*/, int frameWidth, int frameHeight, int block_size); + +extern int blockMatch16x16SSE4(const imgpel* refFrame, int stepBytesRF, const imgpel* curBlock, + int stepBytesCB, int* matchBlock, int frameWidth, int frameHeight, int block_size); + +extern int blockMatch4x4SSE2(const imgpel* refFrame, int stepBytesRF, const imgpel* curBlock, + int stepBytesCB, int* matchBlock, int frameWidth, int frameHeight, int BlockSize); + + +//External Concelament Functions +extern void Conceal_by_FrameCopy (VideoParameters *p_Vid, StorablePicture *Source, StorablePicture *Dest); +extern void Conceal_by_MotionCopy_Ref (VideoParameters *p_Vid, StorablePicture *Source, StorablePicture *Dest); +//extern void Conceal_by_MotionCopyEnhanced (VideoParameters *p_Vid, StorablePicture *Source, StorablePicture *Dest); +extern void Conceal_by_IntraInterMotionCopy (VideoParameters *p_Vid, StorablePicture *Source, StorablePicture *Dest); +extern void Conceal_by_IntraInterMotionCopyBaseViewBased (VideoParameters *p_Vid, StorablePicture *Source/*Ignored*/, StorablePicture *Dest); +extern void Conceal_by_InteractiveIntraInterMotionCopy (VideoParameters *p_Vid, StorablePicture *Source/*Ignored*/, StorablePicture *Dest); +extern void Conceal_by_IntraInterMotionCopy_V2 (VideoParameters *p_Vid, StorablePicture *Source/*Ignored*/, StorablePicture *Dest); + +extern void Conceal_by_DisparityBasedFrameDifferenceProjection (VideoParameters *p_Vid, StorablePicture *Source, StorablePicture *Dest); +extern void Conceal_by_InterViewSimilarityMotion (VideoParameters *p_Vid, StorablePicture *Source, StorablePicture *Dest); + +#endif diff --git a/ldecod/inc/ferc/ferc_opencv_api.h b/ldecod/inc/ferc/ferc_opencv_api.h new file mode 100644 index 0000000..a7f7966 --- /dev/null +++ b/ldecod/inc/ferc/ferc_opencv_api.h @@ -0,0 +1,68 @@ +/*! +************************************************************************************* +* \file fercOpenCVApi.h +* +* \brief +* Functions to use OpenCV library in JM Software +* +* \date +* 29.11.2011 +* +* \author +* Jo�o Carreira +* +************************************************************************************* +*/ + + +#ifndef _FERC_OPENCV_API_H +#define _FERC_OPENCV_API_H + + +#include "mbuffer.h" +#include +#include + +#include "global.h" +#include "mbuffer.h" +#include "memalloc.h" + + + + +typedef struct{ + unsigned char *memory; + unsigned char **pY; + unsigned char **pU; + unsigned char **pV; + + unsigned int width; + unsigned int heigh; +}T_FRAME; + +enum Disparity{ + LOAD = 0, + VAR = 1, + BM = 2, + SGBM = 3, +}; + +typedef enum filters{ + NONE_FILTER = 0, + MEAN_FILTER = 1, + MEDIAN_FILTER = 2, +}CVFILTERTYPES; + +extern int CheckOpenCVUsage(void); + +extern void showStorablePicture(VideoParameters *p_Vid ,StorablePicture *Picture, int SymbolSize, int show_uv, char * WindowName, int wait); +extern void saveStorablePicture( VideoParameters *p_Vid, char *filename , StorablePicture *Picture , int SymbolSize, int save_uv ); + +extern void OpenCVCloseWindows (char *WindowName); + +extern void cvResizeStorablePicture( VideoParameters *p_Vid , StorablePicture *p_Input, StorablePicture *p_Output ); +extern void cvDisparity( VideoParameters *p_Vid , StorablePicture *Ref, StorablePicture *NonRef, StorablePicture *Disparity, int Method, int WindowRatio, int Direction ); +extern void cvFindImageContours( VideoParameters *p_Vid , StorablePicture *InputImage, StorablePicture *OutputContours ); +extern void cvImageFilter( VideoParameters *p_Vid , StorablePicture *InputImage, StorablePicture *OutputImage, CVFILTERTYPES Filter, int KernelSize ); +extern void cvLoadStorablePicture( VideoParameters *p_Vid, char *filename , StorablePicture *Picture , int SymbolSize, int load_uv ); +#endif diff --git a/ldecod/inc/ferc/ferc_proposed.h b/ldecod/inc/ferc/ferc_proposed.h new file mode 100644 index 0000000..d7c2478 --- /dev/null +++ b/ldecod/inc/ferc/ferc_proposed.h @@ -0,0 +1,22 @@ +/*! +************************************************************************ +* \file ferc_proposed.h +* +* \brief +* Function used in the proposed method +* +* \author +* - Joao Carreira +* +************************************************************************ +*/ + +#ifndef _FERC_PROPOSED_H_ +#define _FERC_PROPOSED_H_ + + +#include "defines.h" +#include "global.h" +#include "mbuffer.h" + +#endif diff --git a/ldecod/inc/filehandle.h b/ldecod/inc/filehandle.h new file mode 100644 index 0000000..360173f --- /dev/null +++ b/ldecod/inc/filehandle.h @@ -0,0 +1,23 @@ + +/*! + ************************************************************************************** + * \file + * filehandle.h + * \brief + * Trace file handling and standard error handling function headers. + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * - Karsten Suehring + * - Alexis Michael Tourapis + *************************************************************************************** + */ + +#include "contributors.h" + +#if TRACE +extern void dectracebitcnt(int count); +extern void tracebits ( const char *trace_str, int len, int info, int value1); +extern void tracebits2 ( const char *trace_str, int len, int info); +extern void trace_info ( SyntaxElement *currSE, const char *description_str, int value1 ); +#endif + diff --git a/ldecod/inc/fmo.h b/ldecod/inc/fmo.h new file mode 100644 index 0000000..ba54f51 --- /dev/null +++ b/ldecod/inc/fmo.h @@ -0,0 +1,30 @@ + +/*! + *************************************************************************** + * + * \file fmo.h + * + * \brief + * Support for Flexilble Macroblock Ordering (FMO) + * + * \date + * 19 June, 2002 + * + * \author + * Stephan Wenger stewe@cs.tu-berlin.de + **************************************************************************/ + +#ifndef _FMO_H_ +#define _FMO_H_ + + +extern int fmo_init (VideoParameters *p_Vid, Slice *pSlice); +extern int FmoFinit (VideoParameters *p_Vid); + +extern int FmoGetNumberOfSliceGroup(VideoParameters *p_Vid); +extern int FmoGetLastMBOfPicture (VideoParameters *p_Vid); +extern int FmoGetLastMBInSliceGroup(VideoParameters *p_Vid, int SliceGroup); +extern int FmoGetSliceGroupId (VideoParameters *p_Vid, int mb); +extern int FmoGetNextMBNr (VideoParameters *p_Vid, int CurrentMbNr); + +#endif diff --git a/ldecod/inc/global.h b/ldecod/inc/global.h new file mode 100644 index 0000000..5abbf4e --- /dev/null +++ b/ldecod/inc/global.h @@ -0,0 +1,1242 @@ + +/*! +************************************************************************ +* \file +* global.h +* \brief +* global definitions for H.264 decoder. +* \author +* Copyright (C) 1999 Telenor Satellite Services,Norway +* Ericsson Radio Systems, Sweden +* +* Inge Lille-Langoy +* +* Telenor Satellite Services +* Keysers gt.13 tel.: +47 23 13 86 98 +* N-0130 Oslo,Norway fax.: +47 22 77 79 80 +* +* Rickard Sjoberg +* +* Ericsson Radio Systems +* KI/ERA/T/VV +* 164 80 Stockholm, Sweden +* +************************************************************************ +*/ +#ifndef _GLOBAL_H_ +#define _GLOBAL_H_ +#include +#include +#include +#include +#include +#include + +#include "win32.h" +#include "defines.h" +#include "ifunctions.h" +#include "parsetcommon.h" +#include "types.h" +#include "io_image.h" +#include "frame.h" +#include "distortion.h" +#include "io_video.h" + +typedef struct bit_stream_dec Bitstream; + +#define ET_SIZE 300 //!< size of error text buffer +extern char errortext[ET_SIZE]; //!< buffer for error message for exit with error() + +struct pic_motion_params_old; +struct pic_motion_params; + +/*********************************************************************** +* T y p e d e f i n i t i o n s f o r J M +*********************************************************************** +*/ +typedef enum +{ + DEC_OPENED = 0, + DEC_STOPPED, +}DecoderStatus_e; + +typedef enum +{ + LumaComp = 0, + CrComp = 1, + CbComp = 2 +} Color_Component; + +/*********************************************************************** +* D a t a t y p e s f o r C A B A C +*********************************************************************** +*/ + +typedef struct pix_pos +{ + int available; + int mb_addr; + short x; + short y; + short pos_x; + short pos_y; +} PixelPos; + +//! struct to characterize the state of the arithmetic coding engine +typedef struct +{ + unsigned int Drange; + unsigned int Dvalue; + int DbitsLeft; + byte *Dcodestrm; + int *Dcodestrm_len; +} DecodingEnvironment; + +typedef DecodingEnvironment *DecodingEnvironmentPtr; + +// Motion Vector structure +typedef struct +{ + short mv_x; + short mv_y; +} MotionVector; + +static const MotionVector zero_mv = {0, 0}; + +typedef struct +{ + short x; + short y; +} BlockPos; + +//! struct for context management +typedef struct +{ + uint16 state; // index into state-table CP + unsigned char MPS; // Least Probable Symbol 0/1 CP + unsigned char dummy; // for alignment +} BiContextType; + +typedef BiContextType *BiContextTypePtr; + + +/********************************************************************** +* C O N T E X T S F O R T M L S Y N T A X E L E M E N T S +********************************************************************** +*/ + +#define NUM_MB_TYPE_CTX 11 +#define NUM_B8_TYPE_CTX 9 +#define NUM_MV_RES_CTX 10 +#define NUM_REF_NO_CTX 6 +#define NUM_DELTA_QP_CTX 4 +#define NUM_MB_AFF_CTX 4 +#define NUM_TRANSFORM_SIZE_CTX 3 + +// structures that will be declared somewhere else +struct storable_picture; +struct datapartition_dec; +struct syntaxelement_dec; + +typedef struct +{ + BiContextType mb_type_contexts [3][NUM_MB_TYPE_CTX]; + BiContextType b8_type_contexts [2][NUM_B8_TYPE_CTX]; + BiContextType mv_res_contexts [2][NUM_MV_RES_CTX]; + BiContextType ref_no_contexts [2][NUM_REF_NO_CTX]; + BiContextType delta_qp_contexts[NUM_DELTA_QP_CTX]; + BiContextType mb_aff_contexts [NUM_MB_AFF_CTX]; +} MotionInfoContexts; + +#define NUM_IPR_CTX 2 +#define NUM_CIPR_CTX 4 +#define NUM_CBP_CTX 4 +#define NUM_BCBP_CTX 4 +#define NUM_MAP_CTX 15 +#define NUM_LAST_CTX 15 +#define NUM_ONE_CTX 5 +#define NUM_ABS_CTX 5 + +typedef struct +{ + BiContextType transform_size_contexts [NUM_TRANSFORM_SIZE_CTX]; + BiContextType ipr_contexts [NUM_IPR_CTX]; + BiContextType cipr_contexts[NUM_CIPR_CTX]; + BiContextType cbp_contexts [3][NUM_CBP_CTX]; + BiContextType bcbp_contexts[NUM_BLOCK_TYPES][NUM_BCBP_CTX]; + BiContextType map_contexts [2][NUM_BLOCK_TYPES][NUM_MAP_CTX]; + BiContextType last_contexts[2][NUM_BLOCK_TYPES][NUM_LAST_CTX]; + BiContextType one_contexts [NUM_BLOCK_TYPES][NUM_ONE_CTX]; + BiContextType abs_contexts [NUM_BLOCK_TYPES][NUM_ABS_CTX]; +} TextureInfoContexts; + + +//*********************** end of data type definition for CABAC ******************* + +/*********************************************************************** +* N e w D a t a t y p e s f o r T M L +*********************************************************************** +*/ + +/*! Buffer structure for decoded referenc picture marking commands */ +typedef struct DecRefPicMarking_s +{ + int memory_management_control_operation; + int difference_of_pic_nums_minus1; + int long_term_pic_num; + int long_term_frame_idx; + int max_long_term_frame_idx_plus1; + struct DecRefPicMarking_s *Next; +} DecRefPicMarking_t; + +//! cbp structure +typedef struct cbp_s +{ + int64 blk ; + int64 bits ; + int64 bits_8x8; +} CBPStructure; + +//! Macroblock +typedef struct macroblock_dec +{ + int is_decoded; + struct slice *p_Slice; //!< pointer to the current slice + struct video_par *p_Vid; //!< pointer to VideoParameters + struct inp_par *p_Inp; + int mbAddrX; //!< current MB address + int mbAddrA, mbAddrB, mbAddrC, mbAddrD; + Boolean mbAvailA, mbAvailB, mbAvailC, mbAvailD; + BlockPos mb; + int block_x; + int block_y; + int block_y_aff; + int pix_x; + int pix_y; + int pix_c_x; + int pix_c_y; + + int subblock_x; + int subblock_y; + + int qp; //!< QP luma + int qpc[2]; //!< QP chroma + int qp_scaled[MAX_PLANE]; //!< QP scaled for all comps. + Boolean is_lossless; + Boolean is_intra_block; + Boolean is_v_block; + Boolean DeblockCall; + + short slice_nr; + char ei_flag; //!< error indicator flag that enables concealment + char dpl_flag; //!< error indicator flag that signals a missing data partition + short delta_quant; //!< for rate control + short list_offset; + + struct macroblock_dec *mb_up; //!< pointer to neighboring MB (CABAC) + struct macroblock_dec *mb_left; //!< pointer to neighboring MB (CABAC) + + struct macroblock_dec *mbup; // neighbors for loopfilter + struct macroblock_dec *mbleft; // neighbors for loopfilter + + // some storage of macroblock syntax elements for global access + short mb_type; + short mvd[2][BLOCK_MULTIPLE][BLOCK_MULTIPLE][2]; //!< indices correspond to [forw,backw][block_y][block_x][x,y] + //short ****mvd; //!< indices correspond to [forw,backw][block_y][block_x][x,y] + int cbp; + CBPStructure s_cbp[3]; + + int i16mode; + char b8mode[4]; + char b8pdir[4]; + char ipmode_DPCM; + char c_ipred_mode; //!< chroma intra prediction mode + char skip_flag; + short DFDisableIdc; + short DFAlphaC0Offset; + short DFBetaOffset; + + Boolean mb_field; + //Flag for MBAFF deblocking; + byte mixedModeEdgeFlag; + + // deblocking strength indices + byte strength_ver[4][4]; + byte strength_hor[4][16]; + + + Boolean luma_transform_size_8x8_flag; + Boolean NoMbPartLessThan8x8Flag; + + void (*itrans_4x4)(struct macroblock_dec *currMB, ColorPlane pl, int ioff, int joff); + void (*itrans_8x8)(struct macroblock_dec *currMB, ColorPlane pl, int ioff, int joff); + + void (*GetMVPredictor) (struct macroblock_dec *currMB, PixelPos *block, + MotionVector *pmv, short ref_frame, struct pic_motion_params **mv_info, int list, int mb_x, int mb_y, int blockshape_x, int blockshape_y); + + int (*read_and_store_CBP_block_bit) (struct macroblock_dec *currMB, DecodingEnvironmentPtr dep_dp, int type); + char (*readRefPictureIdx) (struct macroblock_dec *currMB, struct syntaxelement_dec *currSE, struct datapartition_dec *dP, char b8mode, int list); + + void (*read_comp_coeff_4x4_CABAC) (struct macroblock_dec *currMB, struct syntaxelement_dec *currSE, ColorPlane pl, int (*InvLevelScale4x4)[4], int qp_per, int cbp); + void (*read_comp_coeff_8x8_CABAC) (struct macroblock_dec *currMB, struct syntaxelement_dec *currSE, ColorPlane pl); + + void (*read_comp_coeff_4x4_CAVLC) (struct macroblock_dec *currMB, ColorPlane pl, int (*InvLevelScale4x4)[4], int qp_per, int cbp, byte **nzcoeff); + void (*read_comp_coeff_8x8_CAVLC) (struct macroblock_dec *currMB, ColorPlane pl, int (*InvLevelScale8x8)[8], int qp_per, int cbp, byte **nzcoeff); +} Macroblock; + +//! Syntaxelement +typedef struct syntaxelement_dec +{ + int type; //!< type of syntax element for data part. + int value1; //!< numerical value of syntax element + int value2; //!< for blocked symbols, e.g. run/level + int len; //!< length of code + int inf; //!< info part of CAVLC code + unsigned int bitpattern; //!< CAVLC bitpattern + int context; //!< CABAC context + int k; //!< CABAC context for coeff_count,uv + +#if TRACE +#define TRACESTRING_SIZE 100 //!< size of trace string + char tracestring[TRACESTRING_SIZE]; //!< trace string +#endif + + //! for mapping of CAVLC to syntaxElement + void (*mapping)(int len, int info, int *value1, int *value2); + //! used for CABAC: refers to actual coding method of each individual syntax element type + void (*reading)(struct macroblock_dec *currMB, struct syntaxelement_dec *, DecodingEnvironmentPtr); +} SyntaxElement; + + +//! Bitstream +struct bit_stream_dec +{ + // CABAC Decoding + int read_len; //!< actual position in the codebuffer, CABAC only + int code_len; //!< overall codebuffer length, CABAC only + // CAVLC Decoding + int frame_bitoffset; //!< actual position in the codebuffer, bit-oriented, CAVLC only + int bitstream_length; //!< over codebuffer lnegth, byte oriented, CAVLC only + // ErrorConcealment + byte *streamBuffer; //!< actual codebuffer for read bytes + int ei_flag; //!< error indication, 0: no error, else unspecified error +}; + +//! DataPartition +typedef struct datapartition_dec +{ + + Bitstream *bitstream; + DecodingEnvironment de_cabac; + + int (*readSyntaxElement)(struct macroblock_dec *currMB, struct syntaxelement_dec *, struct datapartition_dec *); + /*!< virtual function; + actual method depends on chosen data partition and + entropy coding method */ +} DataPartition; + +typedef struct wp_params +{ + short weight[3]; + short offset[3]; +} WPParams; + +#if (MVC_EXTENSION_ENABLE) +typedef struct nalunitheadermvcext_tag +{ + unsigned int non_idr_flag; + unsigned int priority_id; + unsigned int view_id; + unsigned int temporal_id; + unsigned int anchor_pic_flag; + unsigned int inter_view_flag; + unsigned int reserved_one_bit; + unsigned int iPrefixNALU; +} NALUnitHeaderMVCExt_t; +#endif + +//! Slice +typedef struct slice +{ + struct video_par *p_Vid; + struct inp_par *p_Inp; + pic_parameter_set_rbsp_t *active_pps; + seq_parameter_set_rbsp_t *active_sps; + int svc_extension_flag; + + // dpb pointer + struct decoded_picture_buffer *p_Dpb; + + //slice property; + int idr_flag; + int idr_pic_id; + int nal_reference_idc; //!< nal_reference_idc from NAL unit + int Transform8x8Mode; + Boolean chroma444_not_separate; //!< indicates chroma 4:4:4 coding with separate_colour_plane_flag equal to zero + + int toppoc; //poc for this top field + int bottompoc; //poc of bottom field of frame + int framepoc; //poc of this frame + + //the following is for slice header syntax elements of poc + // for poc mode 0. + unsigned int pic_order_cnt_lsb; + int delta_pic_order_cnt_bottom; + // for poc mode 1. + int delta_pic_order_cnt[2]; + + // //////////////////////// + // for POC mode 0: + signed int PicOrderCntMsb; + + //signed int PrevPicOrderCntMsb; + //unsigned int PrevPicOrderCntLsb; + + // for POC mode 1: + unsigned int AbsFrameNum; + int ThisPOC; + //signed int ExpectedPicOrderCnt, PicOrderCntCycleCnt, FrameNumInPicOrderCntCycle; + //unsigned int PreviousFrameNum, FrameNumOffset; + //int ExpectedDeltaPerPicOrderCntCycle; + //int PreviousFrameNumOffset; + // ///////////////////////// + + //information need to move to slice; + unsigned int current_mb_nr; // bitstream order + unsigned int num_dec_mb; + short current_slice_nr; + //int mb_x; + //int mb_y; + //int block_x; + //int block_y; + //int pix_c_x; + //int pix_c_y; + int cod_counter; //!< Current count of number of skipped macroblocks in a row + int allrefzero; + //end; + + int mb_aff_frame_flag; + int direct_spatial_mv_pred_flag; //!< Indicator for direct mode type (1 for Spatial, 0 for Temporal) + int num_ref_idx_active[2]; //!< number of available list references + //int num_ref_idx_l0_active; //!< number of available list 0 references + //int num_ref_idx_l1_active; //!< number of available list 1 references + + int ei_flag; //!< 0 if the partArr[0] contains valid information + int qp; + int slice_qp_delta; + int qs; + int slice_qs_delta; + int slice_type; //!< slice type + int model_number; //!< cabac model number + unsigned int frame_num; //frame_num for this frame + unsigned int field_pic_flag; + byte bottom_field_flag; + PictureStructure structure; //!< Identify picture structure type + int start_mb_nr; //!< MUST be set by NAL even in case of ei_flag == 1 + int end_mb_nr_plus1; + int max_part_nr; + int dp_mode; //!< data partitioning mode + int current_header; + int next_header; + int last_dquant; + + //slice header information; + int colour_plane_id; //!< colour_plane_id of the current coded slice + int redundant_pic_cnt; + int sp_switch; //!< 1 for switching sp, 0 for normal sp + int slice_group_change_cycle; + int redundant_slice_ref_idx; //!< reference index of redundant slice + int no_output_of_prior_pics_flag; + int long_term_reference_flag; + int adaptive_ref_pic_buffering_flag; + DecRefPicMarking_t *dec_ref_pic_marking_buffer; //!< stores the memory management control operations + + char listXsize[6]; + struct storable_picture **listX[6]; + + // int last_mb_nr; //!< only valid when entropy coding == CABAC + DataPartition *partArr; //!< array of partitions + MotionInfoContexts *mot_ctx; //!< pointer to struct of context models for use in CABAC + TextureInfoContexts *tex_ctx; //!< pointer to struct of context models for use in CABAC + + int mvscale[6][MAX_REFERENCE_PICTURES]; + + int ref_pic_list_reordering_flag[2]; + int *modification_of_pic_nums_idc[2]; + int *abs_diff_pic_num_minus1[2]; + int *long_term_pic_idx[2]; + +#if (MVC_EXTENSION_ENABLE) + int *abs_diff_view_idx_minus1[2]; + + int view_id; + int inter_view_flag; + int anchor_pic_flag; + + NALUnitHeaderMVCExt_t NaluHeaderMVCExt; +#endif + int layer_id; + short DFDisableIdc; //!< Disable deblocking filter on slice + short DFAlphaC0Offset; //!< Alpha and C0 offset for filtering slice + short DFBetaOffset; //!< Beta offset for filtering slice + + int pic_parameter_set_id; //!=0 multiview[VIEW1|VIEW0]; + int iPOC; + int iYUVFormat; //0: 4:0:0, 1: 4:2:0, 2: 4:2:2, 3: 4:4:4 + int iYUVStorageFormat; //0: YUV seperate; 1: YUV interleaved; 2: 3D output; + int iBitDepth; + byte *pY; //if iPictureFormat is 1, [0]: top; [1] bottom; + byte *pU; + byte *pV; + int iWidth; //frame width; + int iHeight; //frame height; + int iYBufStride; //stride of pY[0/1] buffer in bytes; + int iUVBufStride; //stride of pU[0/1] and pV[0/1] buffer in bytes; + int iSkipPicNum; + int iBufSize; + struct decodedpic_t *pNext; +} DecodedPicList; + +//****************************** ~DM *********************************** +typedef struct coding_par +{ + int layer_id; + int profile_idc; + int width; + int height; + int width_cr; //!< width chroma + int height_cr; //!< height chroma + + int pic_unit_bitsize_on_disk; + short bitdepth_luma; + short bitdepth_chroma; + int bitdepth_scale[2]; + int bitdepth_luma_qp_scale; + int bitdepth_chroma_qp_scale; + unsigned int dc_pred_value_comp[MAX_PLANE]; //!< component value for DC prediction (depends on component pel bit depth) + int max_pel_value_comp[MAX_PLANE]; //!< max value that one picture element (pixel) can take (depends on pic_unit_bitdepth) + + int yuv_format; + int lossless_qpprime_flag; + int num_blk8x8_uv; + int num_uv_blocks; + int num_cdc_coeff; + int mb_cr_size_x; + int mb_cr_size_y; + int mb_cr_size_x_blk; + int mb_cr_size_y_blk; + int mb_cr_size; + int mb_size[3][2]; //!< component macroblock dimensions + int mb_size_blk[3][2]; //!< component macroblock dimensions + int mb_size_shift[3][2]; + + int max_vmv_r; //!< maximum vertical motion vector range in luma quarter frame pixel units for the current level_idc + int separate_colour_plane_flag; + int ChromaArrayType; + int max_frame_num; + unsigned int PicWidthInMbs; + unsigned int PicHeightInMapUnits; + unsigned int FrameHeightInMbs; + unsigned int FrameSizeInMbs; + int iLumaPadX; + int iLumaPadY; + int iChromaPadX; + int iChromaPadY; + + int subpel_x; + int subpel_y; + int shiftpel_x; + int shiftpel_y; + int total_scale; + unsigned int oldFrameSizeInMbs; + + //padding info; + void (*img2buf) (imgpel** imgX, unsigned char* buf, int size_x, int size_y, int symbol_size_in_bytes, int crop_left, int crop_right, int crop_top, int crop_bottom, int iOutStride); + int rgb_output; + + imgpel **imgY_ref; //!< reference frame find snr + imgpel ***imgUV_ref; + Macroblock *mb_data; //!< array containing all MBs of a whole frame + Macroblock *mb_data_JV[MAX_PLANE]; //!< mb_data to be used for 4:4:4 independent mode + char *intra_block; + char *intra_block_JV[MAX_PLANE]; + BlockPos *PicPos; + byte **ipredmode; //!< prediction type [90][74] + byte **ipredmode_JV[MAX_PLANE]; + byte ****nz_coeff; + int **siblock; + int **siblock_JV[MAX_PLANE]; + int *qp_per_matrix; + int *qp_rem_matrix; +}CodingParameters; + +typedef struct layer_par +{ + int layer_id; + struct video_par *p_Vid; + CodingParameters *p_Cps; + seq_parameter_set_rbsp_t *p_SPS; + struct decoded_picture_buffer *p_Dpb; +}LayerParameters; + + +//! Edited by Joao Carreira +typedef struct vector_stats_t { + int total_frames; + float burst_average; + float burst_variance; +}VectorStats; + + + +// video parameters +typedef struct video_par +{ + struct inp_par *p_Inp; + pic_parameter_set_rbsp_t *active_pps; + seq_parameter_set_rbsp_t *active_sps; + seq_parameter_set_rbsp_t SeqParSet[MAXSPS]; + pic_parameter_set_rbsp_t PicParSet[MAXPPS]; + struct decoded_picture_buffer *p_Dpb_layer[MAX_NUM_DPB_LAYERS]; + CodingParameters *p_EncodePar[MAX_NUM_DPB_LAYERS]; + LayerParameters *p_LayerPar[MAX_NUM_DPB_LAYERS]; + +#if (MVC_EXTENSION_ENABLE) + subset_seq_parameter_set_rbsp_t *active_subset_sps; + //int svc_extension_flag; + subset_seq_parameter_set_rbsp_t SubsetSeqParSet[MAXSPS]; + int last_pic_width_in_mbs_minus1; + int last_pic_height_in_map_units_minus1; + int last_max_dec_frame_buffering; + int last_profile_idc; +#endif + + struct sei_params *p_SEI; + + struct old_slice_par *old_slice; +#if( MVC_EXTENSION_ENABLE ) + struct snr_par *snr[MAX_VIEW_NUM]; +#else + struct snr_par *snr; +#endif + int number; //!< frame number + + //current picture property; + unsigned int num_dec_mb; + int iSliceNumOfCurrPic; + int iNumOfSlicesAllocated; + int iNumOfSlicesDecoded; + Slice **ppSliceList; + char *intra_block; + char *intra_block_JV[MAX_PLANE]; + //int qp; //!< quant for the current frame + + //int sp_switch; //!< 1 for switching sp, 0 for normal sp + int type; //!< image type INTER/INTRA + + byte **ipredmode; //!< prediction type [90][74] + byte **ipredmode_JV[MAX_PLANE]; + byte ****nz_coeff; + int **siblock; + int **siblock_JV[MAX_PLANE]; + BlockPos *PicPos; + + int newframe; + int structure; //!< Identify picture structure type + + //Slice *currentSlice; //!< pointer to current Slice data struct + Slice *pNextSlice; //!< pointer to first Slice of next picture; + Macroblock *mb_data; //!< array containing all MBs of a whole frame + Macroblock *mb_data_JV[MAX_PLANE]; //!< mb_data to be used for 4:4:4 independent mode + //int colour_plane_id; //!< colour_plane_id of the current coded slice + int ChromaArrayType; + +#if (MVC_EXTENSION_ENABLE) + int pre_frame_num[MAX_VIEW_NUM]; //!< store the frame_num in the last decoded slice. For detecting gap in frame_num. +#else + int pre_frame_num; //!< store the frame_num in the last decoded slice. For detecting gap in frame_num. +#endif + int non_conforming_stream; + + // //////////////////////// + // for POC mode 0: + signed int PrevPicOrderCntMsb; + unsigned int PrevPicOrderCntLsb; + + // for POC mode 1: + signed int ExpectedPicOrderCnt, PicOrderCntCycleCnt, FrameNumInPicOrderCntCycle; + unsigned int PreviousFrameNum, FrameNumOffset; + int ExpectedDeltaPerPicOrderCntCycle; + int ThisPOC; + int PreviousFrameNumOffset; + // ///////////////////////// + + unsigned int PicHeightInMbs; + unsigned int PicSizeInMbs; + + int no_output_of_prior_pics_flag; + + int last_has_mmco_5; + int last_pic_bottom_field; + + int idr_psnr_number; + int psnr_number; + + // Timing related variables + TIME_T start_time; + TIME_T end_time; + + /* + * Edited by Joao Carreira + * Function Related to Frame error concealment + */ + // picture error concealment +#if (MVC_EXTENSION_ENABLE) + int last_ref_pic_poc[MAX_VIEW_NUM]; + int last_ref_hier0_poc[MAX_VIEW_NUM]; +#else + int last_ref_pic_poc; + int last_ref_hier0_poc; +#endif + + struct conceal_params *p_FrameConc; + + int ref_poc_gap; + int poc_gap; + int conceal_mode; + int earlier_missing_poc; + unsigned int frame_to_conceal; + double MotionAverage; + unsigned int NoZeroMVsCount; + unsigned int IntraBlocks; + + + Boolean first_sps; + // random access point decoding + int recovery_point; + int recovery_point_found; + int recovery_frame_cnt; + int recovery_frame_num; + int recovery_poc; + + byte *buf; + byte *ibuf; + + ImageData imgData; //!< Image data to be encoded (dummy variable for now) + ImageData imgData0; //!< base layer input + ImageData imgData1; //!< temp buffer for left de-muxed view + ImageData imgData2; //!< temp buffer for right de-muxed view + + // Data needed for 3:2 pulldown or temporal interleaving + ImageData imgData32; //!< Image data to be encoded + ImageData imgData4; + ImageData imgData5; + ImageData imgData6; + + + // Redundant slices. Should be moved to another structure and allocated only if extended profile + unsigned int previous_frame_num; //!< frame number of previous slice + //!< non-zero: i-th previous frame is correct + int Is_primary_correct; //!< if primary frame is correct, 0: incorrect + int Is_redundant_correct; //!< if redundant frame is correct, 0:incorrect + + // Time + int64 tot_time; + + // files + int p_out; //!< file descriptor to output YUV file +#if (MVC_EXTENSION_ENABLE) + int p_out_mvc[MAX_VIEW_NUM]; //!< file descriptor to output YUV file for MVC + int p_ref_mvc[MAX_VIEW_NUM]; //!< pointer to input original reference YUV file file +#else + int p_ref; //!< pointer to input original reference YUV file file +#endif + + + + //FILE *p_log; //!< SNR file + int LastAccessUnitExists; + int NALUCount; + + // B pictures + int Bframe_ctr; + int frame_no; + int frame_no_idr; + + int IDRframe_ctr; + + int g_nFrame; + Boolean global_init_done[MAX_VIEW_NUM]; + + // global picture format dependent buffers, memory allocation in decod.c + imgpel **imgY_ref; //!< reference frame find snr + imgpel ***imgUV_ref; + + int *qp_per_matrix; + int *qp_rem_matrix; + + /* Edited by Joao Carreira */ +#if ( MVC_EXTENSION_ENABLE ) + int last_output_frame_no[MAX_VIEW_NUM]; +#else + int last_output_frame_no; +#endif + struct storable_picture *dec_picture; + struct storable_picture *dec_picture_JV[MAX_PLANE]; //!< dec_picture to be used during 4:4:4 independent mode decoding + struct storable_picture *no_reference_picture; //!< dummy storable picture for recovery point + + // Error parameters + struct object_buffer *erc_object_list; + struct ercVariables_s *erc_errorVar; + + int erc_mvperMB; + struct video_par *erc_img; + int ec_flag[SE_MAX_ELEMENTS]; //!< array to set errorconcealment + + struct annex_b_struct *annex_b; + + struct frame_store *out_buffer; + + struct storable_picture *pending_output; + int pending_output_state; + int recovery_flag; + + int BitStreamFile; + + // report + char cslice_type[9]; + // FMO + int *MbToSliceGroupMap; + int *MapUnitToSliceGroupMap; + int NumberOfSliceGroups; // the number of slice groups -1 (0 == scan order, 7 == maximum) + +#if (ENABLE_OUTPUT_TONEMAPPING) + struct tone_mapping_struct_s *seiToneMapping; +#endif + + void (*buf2img) (imgpel** imgX, unsigned char* buf, int size_x, int size_y, int o_size_x, int o_size_y, int symbol_size_in_bytes, int bitshift); + void (*getNeighbour) (Macroblock *currMB, int xN, int yN, int mb_size[2], PixelPos *pix); + void (*get_mb_block_pos) (BlockPos *PicPos, int mb_addr, short *x, short *y); + void (*GetStrengthVer) (Macroblock *MbQ, int edge, int mvlimit, struct storable_picture *p); + void (*GetStrengthHor) (Macroblock *MbQ, int edge, int mvlimit, struct storable_picture *p); + void (*EdgeLoopLumaVer) (ColorPlane pl, imgpel** Img, byte *Strength, Macroblock *MbQ, int edge); + void (*EdgeLoopLumaHor) (ColorPlane pl, imgpel** Img, byte *Strength, Macroblock *MbQ, int edge, struct storable_picture *p); + void (*EdgeLoopChromaVer)(imgpel** Img, byte *Strength, Macroblock *MbQ, int edge, int uv, struct storable_picture *p); + void (*EdgeLoopChromaHor)(imgpel** Img, byte *Strength, Macroblock *MbQ, int edge, int uv, struct storable_picture *p); + void (*img2buf) (imgpel** imgX, unsigned char* buf, int size_x, int size_y, int symbol_size_in_bytes, int crop_left, int crop_right, int crop_top, int crop_bottom, int iOutStride); + + ImageData tempData3; + DecodedPicList *pDecOuputPic; + int iDeblockMode; //0: deblock in picture, 1: deblock in slice; + struct nalu_t *nalu; + int iLumaPadX; + int iLumaPadY; + int iChromaPadX; + int iChromaPadY; + //control; + int bDeblockEnable; + int iPostProcess; + int bFrameInit; +#if _FLTDBG_ + FILE *fpDbg; +#endif + pic_parameter_set_rbsp_t *pNextPPS; + int last_dec_poc; + int last_dec_view_id; + int last_dec_layer_id; + int dpb_layer_id; + + /******************* deprecative variables; ***************************************/ + int width; + int height; + int width_cr; //!< width chroma + int height_cr; //!< height chroma + // Fidelity Range Extensions Stuff + int pic_unit_bitsize_on_disk; + short bitdepth_luma; + short bitdepth_chroma; + int bitdepth_scale[2]; + int bitdepth_luma_qp_scale; + int bitdepth_chroma_qp_scale; + unsigned int dc_pred_value_comp[MAX_PLANE]; //!< component value for DC prediction (depends on component pel bit depth) + int max_pel_value_comp[MAX_PLANE]; //!< max value that one picture element (pixel) can take (depends on pic_unit_bitdepth) + + int separate_colour_plane_flag; + int pic_unit_size_on_disk; + + int profile_idc; + int yuv_format; + int lossless_qpprime_flag; + int num_blk8x8_uv; + int num_uv_blocks; + int num_cdc_coeff; + int mb_cr_size_x; + int mb_cr_size_y; + int mb_cr_size_x_blk; + int mb_cr_size_y_blk; + int mb_cr_size; + int mb_size[3][2]; //!< component macroblock dimensions + int mb_size_blk[3][2]; //!< component macroblock dimensions + int mb_size_shift[3][2]; + int subpel_x; + int subpel_y; + int shiftpel_x; + int shiftpel_y; + int total_scale; + int max_frame_num; + + unsigned int PicWidthInMbs; + unsigned int PicHeightInMapUnits; + unsigned int FrameHeightInMbs; + unsigned int FrameSizeInMbs; + unsigned int oldFrameSizeInMbs; + int max_vmv_r; //!< maximum vertical motion vector range in luma quarter frame pixel units for the current level_idc + //int max_mb_vmv_r; //!< maximum vertical motion vector range in luma quarter pixel units for the current level_idc + /******************* end deprecative variables; ***************************************/ + + struct dec_stat_parameters *dec_stats; + + //! Control the number of views in stream + int n_views; + + /* + * Edited by Jo�o Carreira + * NAL Units parser + */ + void *p_ParserNAL; + void *p_PredictionParser; + /* + * Edited by Jo�o Carreira + * Transcode Module Struct pointer + */ + void *p_Transcode; + + /* + * Edited by Joao Carreira + * Report Variables + */ +#if ( MVC_EXTENSION_ENABLE ) + int nFramesConceal[MAX_VIEW_NUM]; + int nFramesDecoded[MAX_VIEW_NUM]; + int nFramesOut[MAX_VIEW_NUM]; +#else + int nFramesConceal; + int nFramesDecoded; + int nFramesOut; +#endif + /**** Stats ****/ +#define INITAL_PFRAMES_LENGTH 10000 + int *p_PFrames[MAX_VIEW_NUM]; + unsigned int n_PFrames[MAX_VIEW_NUM]; + VectorStats LostFrames[MAX_VIEW_NUM]; + VectorStats AfectedFrames[MAX_VIEW_NUM]; + int n_Loops; + int n_BiPred; + int n_SKIP; +} VideoParameters; + +// signal to noise ratio parameters +typedef struct snr_par +{ + int frame_ctr; + float snr[3]; //!< current SNR (component) + float snr1[3]; //!< SNR (dB) first frame (component) + float snra[3]; //!< Average component SNR (dB) remaining frames + float sse[3]; //!< component SSE + float msse[3]; //!< Average component SSE +} SNRParameters; + +// input parameters from configuration file +typedef struct inp_par +{ + char infile[FILE_NAME_SIZE]; //!< H.264 inputfile + char outfile[FILE_NAME_SIZE]; //!< Decoded YUV 4:2:0 output + char reffile[FILE_NAME_SIZE]; //!< Optional YUV 4:2:0 reference file for SNR measurement + //! Edited by Joao Carreira + char statsfile[FILE_NAME_SIZE]; //!< Optional YUV 4:2:0 reference file for SNR measurement + + int FileFormat; //!< File format of the Input file, PAR_OF_ANNEXB or PAR_OF_RTP + int ref_offset; + int poc_scale; + int write_uv; + int silent; + int intra_profile_deblocking; //!< Loop filter usage determined by flags and parameters in bitstream + + // Input/output sequence format related variables + FrameFormat source; //!< source related information + FrameFormat output; //!< output related information + + int ProcessInput; + int enable_32_pulldown; + VideoDataFile input_file1; //!< Input video file1 + VideoDataFile input_file2; //!< Input video file2 + VideoDataFile input_file3; //!< Input video file3 +#if (MVC_EXTENSION_ENABLE) + int DecodeAllLayers; +#endif + +#ifdef _LEAKYBUCKET_ + unsigned long R_decoder; //!< Decoder Rate in HRD Model + unsigned long B_decoder; //!< Decoder Buffer size in HRD model + unsigned long F_decoder; //!< Decoder Initial buffer fullness in HRD model + char LeakyBucketParamFile[FILE_NAME_SIZE]; //!< LeakyBucketParamFile +#endif + + // picture error concealment + int conceal_mode; + int DisparityMethod; + int DisparityDirection; + int DisparityReference; + int DisparityWindow; + char DispLoadFileName[FILE_NAME_SIZE]; + int DisparityProjection; + int Disparity4x4BlockSize; + int MotionVectorCandidates; + int MVConcealmentMode; + int TemporalThreshold; + int SpatialThreshold; + char TranscodeFile[FILE_NAME_SIZE]; + + int error_prop; + int ref_poc_gap; + int poc_gap; + int IDR_Period; + int GOP_Size; + int FrameSkip; + int search_mode_ref; + int search_mode_nonref; + + /* Edited by Jo�o Carreira */ +#define MAX_LOST_NALU 200 + char drop_nalu[MAX_LOST_NALU]; + + //! Edited by Joao Carreira + int dump_nalu; //!< if 1 write to a file the nal units found in bitstream + int PredictionParser; + + + // dummy for encoder + int start_frame; + + // Needed to allow compilation for decoder. May be used later for distortion computation operations + int stdRange; //!< 1 - standard range, 0 - full range + int videoCode; //!< 1 - 709, 3 - 601: See VideoCode in io_tiff. + int export_views; + + int iDecFrmNum; + + int bDisplayDecParams; + int dpb_plus[2]; +} InputParameters; + +typedef struct old_slice_par +{ + unsigned field_pic_flag; + unsigned frame_num; + int nal_ref_idc; + unsigned pic_oder_cnt_lsb; + int delta_pic_oder_cnt_bottom; + int delta_pic_order_cnt[2]; + byte bottom_field_flag; + byte idr_flag; + int idr_pic_id; + int pps_id; +#if (MVC_EXTENSION_ENABLE) + int view_id; + int inter_view_flag; + int anchor_pic_flag; +#endif + int layer_id; +} OldSliceParams; + +typedef struct decoder_params +{ + InputParameters *p_Inp; //!< Input Parameters + VideoParameters *p_Vid; //!< Image Parameters + int64 bufferSize; //!< buffersize for tiff reads (not currently supported) + int UsedBits; // for internal statistics, is adjusted by read_se_v, read_ue_v, read_u_1 + FILE *p_trace; //!< Trace file + int bitcounter; +} DecoderParams; + +extern DecoderParams *p_Dec; + +// prototypes +extern void error(char *text, int code); + +// dynamic mem allocation +extern int init_global_buffers( VideoParameters *p_Vid, int layer_id ); +extern void free_global_buffers( VideoParameters *p_Vid); +extern void free_layer_buffers( VideoParameters *p_Vid, int layer_id ); + +extern int RBSPtoSODB(byte *streamBuffer, int last_byte_pos); +extern int EBSPtoRBSP(byte *streamBuffer, int end_bytepos, int begin_bytepos); + +extern void FreePartition (DataPartition *dp, int n); +extern DataPartition *AllocPartition(int n); + +extern void tracebits (const char *trace_str, int len, int info, int value1); +extern void tracebits2(const char *trace_str, int len, int info); + +extern unsigned CeilLog2 ( unsigned uiVal); +extern unsigned CeilLog2_sf( unsigned uiVal); + +// For 4:4:4 independent mode +extern void change_plane_JV ( VideoParameters *p_Vid, int nplane, Slice *pSlice); +extern void make_frame_picture_JV( VideoParameters *p_Vid ); + +#if (MVC_EXTENSION_ENABLE) +extern void nal_unit_header_mvc_extension(NALUnitHeaderMVCExt_t *NaluHeaderMVCExt, struct bit_stream_dec *bitstream); +#endif + +extern void FreeDecPicList ( DecodedPicList *pDecPicList ); +extern void ClearDecPicList( VideoParameters *p_Vid ); +extern DecodedPicList *get_one_avail_dec_pic_from_list(DecodedPicList *pDecPicList, int b3D, int view_id); +extern Slice *malloc_slice( InputParameters *p_Inp, VideoParameters *p_Vid ); +extern void free_slice (Slice *currSlice); +extern void copy_slice_info ( Slice *currSlice, OldSliceParams *p_old_slice ); +extern void OpenOutputFiles(VideoParameters *p_Vid, int view0_id, int view1_id); +extern void OpenRefFiles(VideoParameters *p_Vid, int view0_id, int view1_id); + +//! Edit by Joao Carreira +extern void OpenOutputFiles_per_layer(VideoParameters *p_Vid, int view_id); +extern void OpenRefFiles_per_layer(VideoParameters *p_Vid, int view_id); + +extern void set_global_coding_par(VideoParameters *p_Vid, CodingParameters *cps); + +static inline int is_FREXT_profile(unsigned int profile_idc) +{ + return ( profile_idc >= FREXT_HP || profile_idc == FREXT_CAVLC444 ); +} +static inline int HI_intra_only_profile(unsigned int profile_idc, Boolean constrained_set3_flag) +{ + return ( ((profile_idc >= FREXT_Hi10P) && constrained_set3_flag) || (profile_idc == FREXT_CAVLC444) ); +} +static inline int is_BL_profile(unsigned int profile_idc) +{ + return ( profile_idc == FREXT_CAVLC444 || profile_idc == BASELINE || profile_idc == MAIN || profile_idc == EXTENDED || + profile_idc == FREXT_HP || profile_idc == FREXT_Hi10P || profile_idc == FREXT_Hi422 || profile_idc == FREXT_Hi444); +} +static inline int is_EL_profile(unsigned int profile_idc) +{ + return ( (profile_idc == MVC_HIGH) || (profile_idc == STEREO_HIGH) + ); +} + +static inline int is_MVC_profile(unsigned int profile_idc) +{ + return ( (0) +#if (MVC_EXTENSION_ENABLE) + || (profile_idc == MVC_HIGH) || (profile_idc == STEREO_HIGH) +#endif + ); +} + + +//! Report Function +enum{ + STATUS = 0, + REPORT = 1, + DEBUG = 2, +}; +extern void av_log(int level, const char *fmt, ...); + + +#endif + diff --git a/ldecod/inc/h264decoder.h b/ldecod/inc/h264decoder.h new file mode 100644 index 0000000..7b8e068 --- /dev/null +++ b/ldecod/inc/h264decoder.h @@ -0,0 +1,54 @@ + +/*! + ************************************************************************ + * \file + * h264decoder.h + * \brief + * interface for H.264 decoder. + * \author + * Copyright (C) 2009 Dolby + * Yuwen He (yhe@dolby.com) + * + ************************************************************************ + */ +#ifndef _H264DECODER_H_ +#define _H264DECODER_H_ + +#include "global.h" + +typedef enum +{ + DEC_GEN_NOERR = 0, + DEC_OPEN_NOERR = 0, + DEC_CLOSE_NOERR = 0, + DEC_SUCCEED = 0, + DEC_EOS =1, + DEC_NEED_DATA = 2, + DEC_INVALID_PARAM = 3, + DEC_ERRMASK = 0x8000 +// DEC_ERRMASK = 0x80000000 +}DecErrCode; + +typedef struct dec_set_t +{ + int iPostprocLevel; // valid interval are [0..100] + int bDBEnable; + int bAllLayers; + int time_incr; + int bDecCompAdapt; +} DecSet_t; + +#ifdef __cplusplus +extern "C" { +#endif + +int OpenDecoder(InputParameters *p_Inp); +int DecodeOneFrame(DecodedPicList **ppDecPic); +int FinitDecoder(DecodedPicList **ppDecPicList); +int CloseDecoder(); +int SetOptsDecoder(DecSet_t *pDecOpts); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/ldecod/inc/header.h b/ldecod/inc/header.h new file mode 100644 index 0000000..d7269b1 --- /dev/null +++ b/ldecod/inc/header.h @@ -0,0 +1,22 @@ +/*! + ************************************************************************************* + * \file header.h + * + * \brief + * Prototypes for header.c + ************************************************************************************* + */ + +#ifndef _HEADER_H_ +#define _HEADER_H_ + +extern int FirstPartOfSliceHeader(Slice *currSlice); +extern int RestOfSliceHeader (Slice *currSlice); + +extern void dec_ref_pic_marking(VideoParameters *p_Vid, Bitstream *currStream, Slice *pSlice); + +extern void decode_poc(VideoParameters *p_Vid, Slice *pSlice); +extern int dumppoc (VideoParameters *p_Vid); + +#endif + diff --git a/ldecod/inc/image.h b/ldecod/inc/image.h new file mode 100644 index 0000000..aacfaeb --- /dev/null +++ b/ldecod/inc/image.h @@ -0,0 +1,47 @@ + +/*! + ************************************************************************ + * \file image.h + * + * \brief + * prototypes for image.c + * + ************************************************************************ + */ + +#ifndef _IMAGE_H_ +#define _IMAGE_H_ + +#include "mbuffer.h" + + + +extern void calculate_frame_no(VideoParameters *p_Vid, StorablePicture *p); +extern void find_snr (VideoParameters *p_Vid, StorablePicture *p, int *p_ref); +extern int picture_order ( Slice *pSlice ); + +extern void decode_one_slice (Slice *currSlice); +extern int read_new_slice (Slice *currSlice); +extern void exit_picture (VideoParameters *p_Vid, StorablePicture **dec_picture); +extern int decode_one_frame (DecoderParams *pDecoder); + +extern int is_new_picture(StorablePicture *dec_picture, Slice *currSlice, OldSliceParams *p_old_slice); +extern void init_old_slice(OldSliceParams *p_old_slice); +// For 4:4:4 independent mode +extern void copy_dec_picture_JV (VideoParameters *p_Vid, StorablePicture *dst, StorablePicture *src ); + +extern void frame_postprocessing(VideoParameters *p_Vid); +extern void field_postprocessing(VideoParameters *p_Vid); + +#if (MVC_EXTENSION_ENABLE) +extern int GetViewIdx(VideoParameters *p_Vid, int iVOIdx); +extern int GetVOIdx(VideoParameters *p_Vid, int iViewId); +extern int get_maxViewIdx(VideoParameters *p_Vid, int view_id, int anchor_pic_flag, int listidx); +extern void init_mvc_picture(Slice *currSlice); +#endif + +extern void init_slice(VideoParameters *p_Vid, Slice *currSlice); +extern void decode_slice(Slice *currSlice, int current_header); + +#endif + diff --git a/ldecod/inc/intra16x16_pred.h b/ldecod/inc/intra16x16_pred.h new file mode 100644 index 0000000..86ee97a --- /dev/null +++ b/ldecod/inc/intra16x16_pred.h @@ -0,0 +1,26 @@ +/*! + ************************************************************************************* + * \file intra16x16_pred.h + * + * \brief + * definitions for intra 16x16 prediction + * + * \author + * Main contributors (see contributors.h for copyright, + * address and affiliation details) + * - Alexis Michael Tourapis + * + ************************************************************************************* + */ + +#ifndef _INTRA16x16_PRED_H_ +#define _INTRA16x16_PRED_H_ + +#include "global.h" +#include "mbuffer.h" + +extern int intra_pred_16x16_mbaff (Macroblock *currMB, ColorPlane pl, int predmode); +extern int intra_pred_16x16_normal(Macroblock *currMB, ColorPlane pl, int predmode); + +#endif + diff --git a/ldecod/inc/intra4x4_pred.h b/ldecod/inc/intra4x4_pred.h new file mode 100644 index 0000000..67a5e14 --- /dev/null +++ b/ldecod/inc/intra4x4_pred.h @@ -0,0 +1,26 @@ +/*! + ************************************************************************************* + * \file intra4x4_pred.h + * + * \brief + * definitions for intra 4x4 prediction + * + * \author + * Main contributors (see contributors.h for copyright, + * address and affiliation details) + * - Alexis Michael Tourapis + * + ************************************************************************************* + */ + +#ifndef _INTRA4x4_PRED_H_ +#define _INTRA4x4_PRED_H_ + +#include "global.h" +#include "mbuffer.h" + +extern int intra_pred_4x4_mbaff (Macroblock *currMB, ColorPlane pl, int ioff, int joff, int img_block_x, int img_block_y); +extern int intra_pred_4x4_normal(Macroblock *currMB, ColorPlane pl, int ioff, int joff, int img_block_x, int img_block_y); + +#endif + diff --git a/ldecod/inc/intra8x8_pred.h b/ldecod/inc/intra8x8_pred.h new file mode 100644 index 0000000..664ce7e --- /dev/null +++ b/ldecod/inc/intra8x8_pred.h @@ -0,0 +1,26 @@ +/*! + ************************************************************************************* + * \file intra8x8_pred.h + * + * \brief + * definitions for intra 8x8 prediction + * + * \author + * Main contributors (see contributors.h for copyright, + * address and affiliation details) + * - Alexis Michael Tourapis + * + ************************************************************************************* + */ + +#ifndef _INTRA8x8_PRED_H_ +#define _INTRA8x8_PRED_H_ + +#include "global.h" +#include "mbuffer.h" + +extern int intra_pred_8x8_normal(Macroblock *currMB, ColorPlane pl, int ioff, int joff); +extern int intra_pred_8x8_mbaff(Macroblock *currMB, ColorPlane pl, int ioff, int joff); + +#endif + diff --git a/ldecod/inc/leaky_bucket.h b/ldecod/inc/leaky_bucket.h new file mode 100644 index 0000000..ad605a6 --- /dev/null +++ b/ldecod/inc/leaky_bucket.h @@ -0,0 +1,26 @@ + +/*! + ************************************************************************************* + * \file leaky_bucket.h + * + * \brief + * Header for Leaky Buffer parameters + * + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * - Shankar Regunathan + ************************************************************************************* + */ +#ifndef _LEAKY_BUCKET_H_ +#define _LEAKY_BUCKET_H_ + +#include "global.h" + +#ifdef _LEAKYBUCKET_ +// Leaky Bucket functions +unsigned long GetBigDoubleWord(FILE *fp); +void calc_buffer(InputParameters *p_Inp); +#endif + +#endif + diff --git a/ldecod/inc/loop_filter.h b/ldecod/inc/loop_filter.h new file mode 100644 index 0000000..c5dd912 --- /dev/null +++ b/ldecod/inc/loop_filter.h @@ -0,0 +1,68 @@ +/*! + *************************************************************************** + * \file + * loop_filter.h + * + * \date + * 25 April 2010 + * + * \brief + * Headerfile for loopfilter processing + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * - Alexis Michael Tourapis + + ************************************************************************** + */ + +#ifndef _LOOP_FILTER_H_ +#define _LOOP_FILTER_H_ + +#include "global.h" + + +#define GROUP_SIZE 1 + +/*********************************************************************************************************/ + +// NOTE: In principle, the alpha and beta tables are calculated with the formulas below +// Alpha( qp ) = 0.8 * (2^(qp/6) - 1) +// Beta ( qp ) = 0.5 * qp - 7 + +// The tables actually used have been "hand optimized" though (by Anthony Joch). So, the +// table values might be a little different to formula-generated values. Also, the first +// few values of both tables is set to zero to force the filter off at low qp’s + +static const byte ALPHA_TABLE[52] = {0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,4,4,5,6, 7,8,9,10,12,13,15,17, 20,22,25,28,32,36,40,45, 50,56,63,71,80,90,101,113, 127,144,162,182,203,226,255,255} ; +static const byte BETA_TABLE[52] = {0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,2,2,2,3, 3,3,3, 4, 4, 4, 6, 6, 7, 7, 8, 8, 9, 9,10,10, 11,11,12,12,13,13, 14, 14, 15, 15, 16, 16, 17, 17, 18, 18} ; +static const byte CLIP_TAB[52][5] = +{ + { 0, 0, 0, 0, 0},{ 0, 0, 0, 0, 0},{ 0, 0, 0, 0, 0},{ 0, 0, 0, 0, 0},{ 0, 0, 0, 0, 0},{ 0, 0, 0, 0, 0},{ 0, 0, 0, 0, 0},{ 0, 0, 0, 0, 0}, + { 0, 0, 0, 0, 0},{ 0, 0, 0, 0, 0},{ 0, 0, 0, 0, 0},{ 0, 0, 0, 0, 0},{ 0, 0, 0, 0, 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},{ 0, 0, 0, 1, 1},{ 0, 0, 0, 1, 1},{ 0, 0, 0, 1, 1},{ 0, 0, 1, 1, 1},{ 0, 0, 1, 1, 1},{ 0, 1, 1, 1, 1}, + { 0, 1, 1, 1, 1},{ 0, 1, 1, 1, 1},{ 0, 1, 1, 1, 1},{ 0, 1, 1, 2, 2},{ 0, 1, 1, 2, 2},{ 0, 1, 1, 2, 2},{ 0, 1, 1, 2, 2},{ 0, 1, 2, 3, 3}, + { 0, 1, 2, 3, 3},{ 0, 2, 2, 3, 3},{ 0, 2, 2, 4, 4},{ 0, 2, 3, 4, 4},{ 0, 2, 3, 4, 4},{ 0, 3, 3, 5, 5},{ 0, 3, 4, 6, 6},{ 0, 3, 4, 6, 6}, + { 0, 4, 5, 7, 7},{ 0, 4, 5, 8, 8},{ 0, 4, 6, 9, 9},{ 0, 5, 7,10,10},{ 0, 6, 8,11,11},{ 0, 6, 8,13,13},{ 0, 7,10,14,14},{ 0, 8,11,16,16}, + { 0, 9,12,18,18},{ 0,10,13,20,20},{ 0,11,15,23,23},{ 0,13,17,25,25} +} ; + +static const char chroma_edge[2][4][4] = //[dir][edge][yuv_format] +{ { {-4, 0, 0, 0}, + {-4,-4,-4, 4}, + {-4, 4, 4, 8}, + {-4,-4,-4, 12}}, + + { {-4, 0, 0, 0}, + {-4,-4, 4, 4}, + {-4, 4, 8, 8}, + {-4,-4, 12, 12}}}; + +static const int pelnum_cr[2][4] = {{0,8,16,16}, {0,8, 8,16}}; //[dir:0=vert, 1=hor.][yuv_format] + + +static inline int compare_mvs(const MotionVector *mv0, const MotionVector *mv1, int mvlimit) +{ + return ((iabs( mv0->mv_x - mv1->mv_x) >= 4) | (iabs( mv0->mv_y - mv1->mv_y) >= mvlimit)); +} + +#endif diff --git a/ldecod/inc/loopfilter.h b/ldecod/inc/loopfilter.h new file mode 100644 index 0000000..781feb5 --- /dev/null +++ b/ldecod/inc/loopfilter.h @@ -0,0 +1,19 @@ +/*! + ************************************************************************ + * \file + * loopfilter.h + * \brief + * external deblocking filter interface + ************************************************************************ + */ + +#ifndef _LOOPFILTER_H_ +#define _LOOPFILTER_H_ + +#include "global.h" +#include "mbuffer.h" + +extern void DeblockPicture(VideoParameters *p_Vid, StorablePicture *p) ; + +void init_Deblock(VideoParameters *p_Vid, int mb_aff_frame_flag); +#endif //_LOOPFILTER_H_ diff --git a/ldecod/inc/macroblock.h b/ldecod/inc/macroblock.h new file mode 100644 index 0000000..b50901e --- /dev/null +++ b/ldecod/inc/macroblock.h @@ -0,0 +1,152 @@ +/*! + ************************************************************************ + * \file macroblock.h + * + * \brief + * Arrays for macroblock encoding + * + * \author + * Inge Lille-Langoy + * Copyright (C) 1999 Telenor Satellite Services, Norway + ************************************************************************ + */ + +#ifndef _MACROBLOCK_H_ +#define _MACROBLOCK_H_ + +#include "global.h" +#include "mbuffer.h" +#include "block.h" + +//! single scan pattern +static const byte SNGL_SCAN[16][2] = +{ + {0,0},{1,0},{0,1},{0,2}, + {1,1},{2,0},{3,0},{2,1}, + {1,2},{0,3},{1,3},{2,2}, + {3,1},{3,2},{2,3},{3,3} +}; + +//! field scan pattern +static const byte FIELD_SCAN[16][2] = +{ + {0,0},{0,1},{1,0},{0,2}, + {0,3},{1,1},{1,2},{1,3}, + {2,0},{2,1},{2,2},{2,3}, + {3,0},{3,1},{3,2},{3,3} +}; + +//! used to control block sizes : Not used/16x16/16x8/8x16/8x8/8x4/4x8/4x4 +static const int BLOCK_STEP[8][2]= +{ + {0,0},{4,4},{4,2},{2,4},{2,2},{2,1},{1,2},{1,1} +}; + +//! single scan pattern +static const byte SNGL_SCAN8x8[64][2] = { + {0,0}, {1,0}, {0,1}, {0,2}, {1,1}, {2,0}, {3,0}, {2,1}, {1,2}, {0,3}, {0,4}, {1,3}, {2,2}, {3,1}, {4,0}, {5,0}, + {4,1}, {3,2}, {2,3}, {1,4}, {0,5}, {0,6}, {1,5}, {2,4}, {3,3}, {4,2}, {5,1}, {6,0}, {7,0}, {6,1}, {5,2}, {4,3}, + {3,4}, {2,5}, {1,6}, {0,7}, {1,7}, {2,6}, {3,5}, {4,4}, {5,3}, {6,2}, {7,1}, {7,2}, {6,3}, {5,4}, {4,5}, {3,6}, + {2,7}, {3,7}, {4,6}, {5,5}, {6,4}, {7,3}, {7,4}, {6,5}, {5,6}, {4,7}, {5,7}, {6,6}, {7,5}, {7,6}, {6,7}, {7,7} +}; + + +//! field scan pattern +static const byte FIELD_SCAN8x8[64][2] = { // 8x8 + {0,0}, {0,1}, {0,2}, {1,0}, {1,1}, {0,3}, {0,4}, {1,2}, {2,0}, {1,3}, {0,5}, {0,6}, {0,7}, {1,4}, {2,1}, {3,0}, + {2,2}, {1,5}, {1,6}, {1,7}, {2,3}, {3,1}, {4,0}, {3,2}, {2,4}, {2,5}, {2,6}, {2,7}, {3,3}, {4,1}, {5,0}, {4,2}, + {3,4}, {3,5}, {3,6}, {3,7}, {4,3}, {5,1}, {6,0}, {5,2}, {4,4}, {4,5}, {4,6}, {4,7}, {5,3}, {6,1}, {6,2}, {5,4}, + {5,5}, {5,6}, {5,7}, {6,3}, {7,0}, {7,1}, {6,4}, {6,5}, {6,6}, {6,7}, {7,2}, {7,3}, {7,4}, {7,5}, {7,6}, {7,7} +}; + +//! single scan pattern +static const byte SCAN_YUV422[8][2] = +{ + {0,0},{0,1}, + {1,0},{0,2}, + {0,3},{1,1}, + {1,2},{1,3} +}; + +static const unsigned char cbp_blk_chroma[8][4] = +{ {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} +}; + +static const unsigned char cofuv_blk_x[3][8][4] = +{ { {0, 1, 0, 1}, + {0, 1, 0, 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, 0, 1}, + {0, 1, 0, 1}, + {0, 1, 0, 1}, + {0, 1, 0, 1}, + {0, 0, 0, 0}, + {0, 0, 0, 0}, + {0, 0, 0, 0}, + {0, 0, 0, 0} }, + + { {0, 1, 0, 1}, + {2, 3, 2, 3}, + {0, 1, 0, 1}, + {2, 3, 2, 3}, + {0, 1, 0, 1}, + {2, 3, 2, 3}, + {0, 1, 0, 1}, + {2, 3, 2, 3} } +}; + +static const unsigned char cofuv_blk_y[3][8][4] = +{ + { { 0, 0, 1, 1}, + { 0, 0, 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, 1}, + { 2, 2, 3, 3}, + { 0, 0, 1, 1}, + { 2, 2, 3, 3}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0} }, + + { { 0, 0, 1, 1}, + { 0, 0, 1, 1}, + { 2, 2, 3, 3}, + { 2, 2, 3, 3}, + { 0, 0, 1, 1}, + { 0, 0, 1, 1}, + { 2, 2, 3, 3}, + { 2, 2, 3, 3}} +}; + +extern void setup_slice_methods_mbaff(Slice *currSlice); +extern void setup_slice_methods (Slice *currSlice); +extern void get_neighbors(Macroblock *currMB, PixelPos *block, int mb_x, int mb_y, int blockshape_x); + +extern void start_macroblock (Slice *currSlice, Macroblock **currMB); +extern int decode_one_macroblock(Macroblock *currMB, StorablePicture *dec_picture); +extern Boolean exit_macroblock (Slice *currSlice, int eos_bit); +extern void update_qp (Macroblock *currMB, int qp); + + +#endif + diff --git a/ldecod/inc/mb_prediction.h b/ldecod/inc/mb_prediction.h new file mode 100644 index 0000000..b77048c --- /dev/null +++ b/ldecod/inc/mb_prediction.h @@ -0,0 +1,35 @@ + +/*! + ************************************************************************************* + * \file mb_prediction.h + * + * \brief + * Functions for macroblock prediction + * + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * - Alexis Michael Tourapis + ************************************************************************************* + */ + +#ifndef _MB_PREDICTION_H_ +#define _MB_PREDICTION_H_ + +extern int mb_pred_intra4x4 (Macroblock *currMB, ColorPlane curr_plane, imgpel **currImg, StorablePicture *dec_picture); +extern int mb_pred_intra16x16 (Macroblock *currMB, ColorPlane curr_plane, StorablePicture *dec_picture); +extern int mb_pred_intra8x8 (Macroblock *currMB, ColorPlane curr_plane, imgpel **currImg, StorablePicture *dec_picture); + +extern int mb_pred_skip (Macroblock *currMB, ColorPlane curr_plane, imgpel **currImg, StorablePicture *dec_picture); +extern int mb_pred_sp_skip (Macroblock *currMB, ColorPlane curr_plane, StorablePicture *dec_picture); +extern int mb_pred_p_inter8x8 (Macroblock *currMB, ColorPlane curr_plane, StorablePicture *dec_picture); +extern int mb_pred_p_inter16x16 (Macroblock *currMB, ColorPlane curr_plane, StorablePicture *dec_picture); +extern int mb_pred_p_inter16x8 (Macroblock *currMB, ColorPlane curr_plane, StorablePicture *dec_picture); +extern int mb_pred_p_inter8x16 (Macroblock *currMB, ColorPlane curr_plane, StorablePicture *dec_picture); +extern int mb_pred_b_d4x4spatial (Macroblock *currMB, ColorPlane curr_plane, imgpel **currImg, StorablePicture *dec_picture); +extern int mb_pred_b_d8x8spatial (Macroblock *currMB, ColorPlane curr_plane, imgpel **currImg, StorablePicture *dec_picture); +extern int mb_pred_b_d4x4temporal(Macroblock *currMB, ColorPlane curr_plane, imgpel **currImg, StorablePicture *dec_picture); +extern int mb_pred_b_d8x8temporal(Macroblock *currMB, ColorPlane curr_plane, imgpel **currImg, StorablePicture *dec_picture); +extern int mb_pred_b_inter8x8 (Macroblock *currMB, ColorPlane curr_plane, StorablePicture *dec_picture); +extern int mb_pred_ipcm (Macroblock *currMB); + +#endif diff --git a/ldecod/inc/mbuffer.h b/ldecod/inc/mbuffer.h new file mode 100644 index 0000000..5c14b4d --- /dev/null +++ b/ldecod/inc/mbuffer.h @@ -0,0 +1,291 @@ +/*! +*********************************************************************** +* \file +* mbuffer.h +* +* \brief +* Frame buffer functions +* +* \author +* Main contributors (see contributors.h for copyright, address and affiliation details) +* - Karsten Suehring +* - Alexis Michael Tourapis + +* - Jill Boyce +* - Saurav K Bandyopadhyay +* - Zhenyu Wu +* +*********************************************************************** +*/ +#ifndef _MBUFFERDEC_H_ +#define _MBUFFERDEC_H_ + +#include "global.h" + +#define MAX_LIST_SIZE 33 +//! definition of pic motion parameters +typedef struct pic_motion_params_old +{ + byte * mb_field; //!< field macroblock indicator +} PicMotionParamsOld; + +//! definition of pic motion parameters +typedef struct pic_motion_params +{ + struct storable_picture *ref_pic[2]; //!< referrence picture pointer + MotionVector mv[2]; //!< motion vector + char ref_idx[2]; //!< reference picture [list][subblock_y][subblock_x] + //byte mb_field; //!< field macroblock indicator + byte slice_no; +} PicMotionParams; + +//! definition a picture (field or frame) +typedef struct storable_picture +{ + PictureStructure structure; + + int frame_no; + +#if (MVC_EXTENSION_ENABLE) + int view_id; +#endif + + int poc; + int top_poc; + int bottom_poc; + int frame_poc; + unsigned int frame_num; + unsigned int recovery_frame; + + int pic_num; + int long_term_pic_num; + int long_term_frame_idx; + + byte is_long_term; + + int used_for_reference; + int temp_used_for_reference; //!< Error concealment temp variable + + int is_output; + int non_existing; + int separate_colour_plane_flag; + + short max_slice_id; + + int size_x, size_y, size_x_cr, size_y_cr; + int size_x_m1, size_y_m1, size_x_cr_m1, size_y_cr_m1; + int coded_frame; + int mb_aff_frame_flag; + unsigned PicWidthInMbs; + unsigned PicSizeInMbs; + int iLumaPadY, iLumaPadX; + int iChromaPadY, iChromaPadX; + + + imgpel ** imgY; //!< Y picture component + imgpel *** imgUV; //!< U and V picture components + + struct pic_motion_params **mv_info; //!< Motion info + struct pic_motion_params **JVmv_info[MAX_PLANE]; //!< Motion info + + struct pic_motion_params_old motion; //!< Motion info + struct pic_motion_params_old JVmotion[MAX_PLANE]; //!< Motion info for 4:4:4 independent mode decoding + + struct storable_picture *top_field; // for mb aff, if frame for referencing the top field + struct storable_picture *bottom_field; // for mb aff, if frame for referencing the bottom field + struct storable_picture *frame; // for mb aff, if field for referencing the combined frame + + int slice_type; + int idr_flag; + int no_output_of_prior_pics_flag; + int long_term_reference_flag; + int adaptive_ref_pic_buffering_flag; + + int chroma_format_idc; + int frame_mbs_only_flag; + int frame_cropping_flag; + int frame_crop_left_offset; + int frame_crop_right_offset; + int frame_crop_top_offset; + int frame_crop_bottom_offset; + int qp; + int chroma_qp_offset[2]; + int slice_qp_delta; + DecRefPicMarking_t *dec_ref_pic_marking_buffer; //!< stores the memory management control operations + + //! Picture error concealment + int concealed_pic; //indicates if this is a concealed picture + char Conc_listXsize[2]; + int Conc_listXInterviewPos[2]; + struct storable_picture *Conc_listX[2][MAX_LIST_SIZE]; + struct storable_picture *disparity; + + // variables for tone mapping + int seiHasTone_mapping; + int tone_mapping_model_id; + int tonemapped_bit_depth; + imgpel* tone_mapping_lut; //!< tone mapping look up table + + int proc_flag; +#if (MVC_EXTENSION_ENABLE) + int inter_view_flag; + int anchor_pic_flag; +#endif + + int iLumaStride; + int iChromaStride; + int iLumaExpandedHeight; + int iChromaExpandedHeight; + imgpel **cur_imgY; // for more efficient get_block_luma + int no_ref; + int iCodingType; + // + char listXsize[MAX_NUM_SLICES][2]; + struct storable_picture **listX[MAX_NUM_SLICES][2]; + int layer_id; + + //! Edited by Joao Carreira + Slice *PicSlice; + struct motion_prop_info *Matrix_MV_Info; + int have_concealed_reference; //!< Control if a frame have concealed frames as references + int ref_pic_list_reordering_flag[2]; + int pixel_factor; //!< factor used to obtain the pixel values real = pixel / pixel_factor; + double mean_value; + + int have_pic; + +} StorablePicture; + +typedef StorablePicture *StorablePicturePtr; + +//! Frame Stores for Decoded Picture Buffer +typedef struct frame_store +{ + int is_used; //!< 0=empty; 1=top; 2=bottom; 3=both fields (or frame) + int is_reference; //!< 0=not used for ref; 1=top used; 2=bottom used; 3=both fields (or frame) used + int temp_is_reference; //!< temp variable of is_reference for motion copy erro concealment + + int is_long_term; //!< 0=not used for ref; 1=top used; 2=bottom used; 3=both fields (or frame) used + int is_orig_reference; //!< original marking by nal_ref_idc: 0=not used for ref; 1=top used; 2=bottom used; 3=both fields (or frame) used + + int is_non_existent; + + unsigned frame_num; + unsigned recovery_frame; + + int frame_num_wrap; + int long_term_frame_idx; + int is_output; + int poc; + + int frame_no; + + int unused_concealment; //!< picture error concealment + + StorablePicture *frame; + StorablePicture *top_field; + StorablePicture *bottom_field; + +#if (MVC_EXTENSION_ENABLE) + int view_id; + int inter_view_flag[2]; + int anchor_pic_flag[2]; +#endif + int layer_id; +} FrameStore; + + +//! Decoded Picture Buffer +typedef struct decoded_picture_buffer +{ + VideoParameters *p_Vid; + InputParameters *p_Inp; + FrameStore **fs; + FrameStore **fs_ref; + FrameStore **fs_ltref; + FrameStore **fs_ilref; // inter-layer reference (for multi-layered codecs) + unsigned size_ilref; + unsigned size; + unsigned used_size; + unsigned used_size_concealment; //! Dpb used size for error concealment strategies + unsigned ref_frames_in_buffer; + unsigned ltref_frames_in_buffer; + int last_output_poc; +#if (MVC_EXTENSION_ENABLE) + int last_output_view_id; +#endif + int max_long_term_pic_idx; + + + int init_done; + int num_ref_frames; + + FrameStore *last_picture; + unsigned used_size_il; + int layer_id; + + //DPB related function; + +} DecodedPictureBuffer; + + +extern void init_dpb(VideoParameters *p_Vid, DecodedPictureBuffer *p_Dpb, int type); +extern void re_init_dpb(VideoParameters *p_Vid, DecodedPictureBuffer *p_Dpb, int type); +extern void free_dpb(DecodedPictureBuffer *p_Dpb); +extern FrameStore* alloc_frame_store(void); +extern void free_frame_store (FrameStore* f); +extern StorablePicture* alloc_storable_picture(VideoParameters *p_Vid, PictureStructure type, int size_x, int size_y, int size_x_cr, int size_y_cr, int is_output); +extern void free_storable_picture (StorablePicture* p); +extern void store_picture_in_dpb(DecodedPictureBuffer *p_Dpb, StorablePicture* p); +extern StorablePicture* get_short_term_pic (Slice *currSlice, DecodedPictureBuffer *p_Dpb, int picNum); + +#if (MVC_EXTENSION_ENABLE) +extern void idr_memory_management(DecodedPictureBuffer *p_Dpb, StorablePicture* p); +extern void flush_dpbs(DecodedPictureBuffer **p_Dpb, int nLayers); +extern int GetMaxDecFrameBuffering(VideoParameters *p_Vid); +extern void append_interview_list(DecodedPictureBuffer *p_Dpb, + PictureStructure currPicStructure, int list_idx, + FrameStore **list, int *listXsize, int currPOC, + int curr_view_id, int anchor_pic_flag); +#endif + +extern void unmark_for_reference(FrameStore* fs); +extern void unmark_for_long_term_reference(FrameStore* fs); +extern void remove_frame_from_dpb(DecodedPictureBuffer *p_Dpb, int pos); + +extern void flush_dpb(DecodedPictureBuffer *p_Dpb); +extern void init_lists_p_slice (Slice *currSlice); +extern void init_lists_b_slice (Slice *currSlice); +extern void init_lists_i_slice (Slice *currSlice); +extern void update_pic_num (Slice *currSlice); + +extern void dpb_split_field (VideoParameters *p_Vid, FrameStore *fs); +extern void dpb_combine_field (VideoParameters *p_Vid, FrameStore *fs); +extern void dpb_combine_field_yuv(VideoParameters *p_Vid, FrameStore *fs); + +extern void reorder_ref_pic_list(Slice *currSlice, int cur_list); + +extern void init_mbaff_lists (VideoParameters *p_Vid, Slice *currSlice); +extern void alloc_ref_pic_list_reordering_buffer(Slice *currSlice); +extern void free_ref_pic_list_reordering_buffer(Slice *currSlice); + +extern void fill_frame_num_gap(VideoParameters *p_Vid, Slice *pSlice); + +extern void compute_colocated (Slice *currSlice, StorablePicture **listX[6]); + + +extern int init_img_data(VideoParameters *p_Vid, ImageData *p_ImgData, seq_parameter_set_rbsp_t *sps); +extern void free_img_data(VideoParameters *p_Vid, ImageData *p_ImgData); +extern void pad_dec_picture(VideoParameters *p_Vid, StorablePicture *dec_picture); +extern void pad_buf(imgpel *pImgBuf, int iWidth, int iHeight, int iStride, int iPadX, int iPadY); +extern void process_picture_in_dpb_s(VideoParameters *p_Vid, StorablePicture *p_pic); +extern StorablePicture * clone_storable_picture( VideoParameters *p_Vid, StorablePicture *p_pic ); +extern void store_proc_picture_in_dpb(DecodedPictureBuffer *p_Dpb, StorablePicture* p); +extern void check_for_previous_interview_frames(DecodedPictureBuffer *p_Dpb, StorablePicture* p); + +//! Debug porpose +extern void dump_ref_lists(Slice *currSlice); +extern void dump_dpb(DecodedPictureBuffer *p_Dpb); +#endif + diff --git a/ldecod/inc/mbuffer_mvc.h b/ldecod/inc/mbuffer_mvc.h new file mode 100644 index 0000000..5cc17cb --- /dev/null +++ b/ldecod/inc/mbuffer_mvc.h @@ -0,0 +1,37 @@ + +/*! + *********************************************************************** + * \file + * mbuffer_mvc.h + * + * \brief + * MVC Frame buffer functions + * + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * - Athanasios Leontaris + * - Karsten Suehring + * - Alexis Michael Tourapis + * + *********************************************************************** + */ +#ifndef _MBUFFER_MVC_H_ +#define _MBUFFER_MVC_H_ + +#include "global.h" + +#if (MVC_EXTENSION_ENABLE) +extern void reorder_lists_mvc (Slice * currSlice, int currPOC); +extern void init_lists_p_slice_mvc(Slice *currSlice); +extern void init_lists_b_slice_mvc(Slice *currSlice); +extern void init_lists_i_slice_mvc(Slice *currSlice); + +extern void reorder_ref_pic_list_mvc(Slice *currSlice, int cur_list, int **anchor_ref, int **non_anchor_ref, + int view_id, int anchor_pic_flag, int currPOC, int listidx); + +extern void reorder_short_term(Slice *currSlice, int cur_list, int num_ref_idx_lX_active_minus1, int picNumLX, int *refIdxLX, int currViewID); +extern void reorder_long_term(Slice *currSlice, StorablePicture **RefPicListX, int num_ref_idx_lX_active_minus1, int LongTermPicNum, int *refIdxLX, int currViewID); +#endif + +#endif + diff --git a/ldecod/inc/mc_prediction.h b/ldecod/inc/mc_prediction.h new file mode 100644 index 0000000..f7d0f89 --- /dev/null +++ b/ldecod/inc/mc_prediction.h @@ -0,0 +1,33 @@ + +/*! + ************************************************************************************* + * \file mc_prediction.h + * + * \brief + * definitions for motion compensated prediction + * + * \author + * Main contributors (see contributors.h for copyright, + * address and affiliation details) + * - Alexis Michael Tourapis + * + ************************************************************************************* + */ + +#ifndef _MC_PREDICTION_H_ +#define _MC_PREDICTION_H_ + +#include "global.h" +#include "mbuffer.h" + +extern int allocate_pred_mem(Slice *currSlice); +extern void free_pred_mem (Slice *currSlice); + +extern void get_block_luma(StorablePicture *curr_ref, int x_pos, int y_pos, int block_size_x, int block_size_y, imgpel **block, + int shift_x,int maxold_x,int maxold_y,int **tmp_res,int max_imgpel_value,imgpel no_ref_value,Macroblock *currMB); + +extern void intra_cr_decoding (Macroblock *currMB, int yuv); +extern void prepare_direct_params(Macroblock *currMB, StorablePicture *dec_picture, MotionVector *pmvl0, MotionVector *pmvl1,char *l0_rFrame, char *l1_rFrame); +extern void perform_mc (Macroblock *currMB, ColorPlane pl, StorablePicture *dec_picture, int pred_dir, int i, int j, int block_size_x, int block_size_y); +#endif + diff --git a/ldecod/inc/nalu.h b/ldecod/inc/nalu.h new file mode 100644 index 0000000..545f592 --- /dev/null +++ b/ldecod/inc/nalu.h @@ -0,0 +1,27 @@ + +/*! + ************************************************************************************** + * \file + * nalu.h + * \brief + * Common NALU support functions + * + * \date 25 November 2002 + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * - Stephan Wenger + *************************************************************************************** + */ + + +#ifndef _NALU_H_ +#define _NALU_H_ + +#include "nalucommon.h" + +extern void CheckZeroByteNonVCL(VideoParameters *p_Vid, NALU_t *nalu); +extern void CheckZeroByteVCL (VideoParameters *p_Vid, NALU_t *nalu); + +extern int read_next_nalu(VideoParameters *p_Vid, NALU_t *nalu); + +#endif diff --git a/ldecod/inc/output.h b/ldecod/inc/output.h new file mode 100644 index 0000000..6439995 --- /dev/null +++ b/ldecod/inc/output.h @@ -0,0 +1,26 @@ + +/*! + ************************************************************************************** + * \file + * output.h + * \brief + * Picture writing routine headers + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * - Karsten Suehring + *************************************************************************************** + */ + +#ifndef _OUTPUT_H_ +#define _OUTPUT_H_ + + +extern void write_stored_frame(VideoParameters *p_Vid, FrameStore *fs, int p_out); +extern void direct_output (VideoParameters *p_Vid, StorablePicture *p, int p_out); +extern void init_out_buffer (VideoParameters *p_Vid); +extern void uninit_out_buffer (VideoParameters *p_Vid); +#if (PAIR_FIELDS_IN_OUTPUT) +extern void flush_pending_output(VideoParameters *p_Vid, int p_out); +#endif +extern void init_output(CodingParameters *p_CodingParams, int symbol_size_in_bytes); +#endif //_OUTPUT_H_ diff --git a/ldecod/inc/parset.h b/ldecod/inc/parset.h new file mode 100644 index 0000000..7dc3b8a --- /dev/null +++ b/ldecod/inc/parset.h @@ -0,0 +1,68 @@ + +/*! + ************************************************************************************** + * \file + * parset.h + * \brief + * Picture and Sequence Parameter Sets, decoder operations + * + * \date 25 November 2002 + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * - Stephan Wenger + *************************************************************************************** + */ + + +#ifndef _PARSET_H_ +#define _PARSET_H_ + +#include "parsetcommon.h" +#include "nalucommon.h" + +static const byte ZZ_SCAN[16] = +{ 0, 1, 4, 8, 5, 2, 3, 6, 9, 12, 13, 10, 7, 11, 14, 15 +}; + +static const byte ZZ_SCAN8[64] = +{ 0, 1, 8, 16, 9, 2, 3, 10, 17, 24, 32, 25, 18, 11, 4, 5, + 12, 19, 26, 33, 40, 48, 41, 34, 27, 20, 13, 6, 7, 14, 21, 28, + 35, 42, 49, 56, 57, 50, 43, 36, 29, 22, 15, 23, 30, 37, 44, 51, + 58, 59, 52, 45, 38, 31, 39, 46, 53, 60, 61, 54, 47, 55, 62, 63 +}; + +extern void Scaling_List(int *scalingList, int sizeOfScalingList, Boolean *UseDefaultScalingMatrix, Bitstream *s); + +extern void InitVUI(seq_parameter_set_rbsp_t *sps); +extern int ReadVUI(DataPartition *p, seq_parameter_set_rbsp_t *sps); +extern int ReadHRDParameters(DataPartition *p, hrd_parameters_t *hrd); + +extern void PPSConsistencyCheck (pic_parameter_set_rbsp_t *pps); +extern void SPSConsistencyCheck (seq_parameter_set_rbsp_t *sps); + +extern void MakePPSavailable (VideoParameters *p_Vid, int id, pic_parameter_set_rbsp_t *pps); +extern void MakeSPSavailable (VideoParameters *p_Vid, int id, seq_parameter_set_rbsp_t *sps); + +extern void ProcessSPS (VideoParameters *p_Vid, NALU_t *nalu); +extern void ProcessPPS (VideoParameters *p_Vid, NALU_t *nalu); + +extern void CleanUpPPS(VideoParameters *p_Vid); + +extern void activate_sps (VideoParameters *p_Vid, seq_parameter_set_rbsp_t *sps); +extern void activate_pps (VideoParameters *p_Vid, pic_parameter_set_rbsp_t *pps); + +extern void UseParameterSet (Slice *currSlice); + +#if (MVC_EXTENSION_ENABLE) +extern void SubsetSPSConsistencyCheck (subset_seq_parameter_set_rbsp_t *subset_sps); +extern void ProcessSubsetSPS (VideoParameters *p_Vid, NALU_t *nalu); + +extern void mvc_vui_parameters_extension(MVCVUI_t *pMVCVUI, Bitstream *s); +extern void seq_parameter_set_mvc_extension(subset_seq_parameter_set_rbsp_t *subset_sps, Bitstream *s); +extern void init_subset_sps_list(subset_seq_parameter_set_rbsp_t *subset_sps_list, int iSize); +extern void reset_subset_sps(subset_seq_parameter_set_rbsp_t *subset_sps); +extern int GetBaseViewId(VideoParameters *p_Vid, subset_seq_parameter_set_rbsp_t **subset_sps); +extern void get_max_dec_frame_buf_size(seq_parameter_set_rbsp_t *sps); +#endif + +#endif diff --git a/ldecod/inc/quant.h b/ldecod/inc/quant.h new file mode 100644 index 0000000..64dcc0c --- /dev/null +++ b/ldecod/inc/quant.h @@ -0,0 +1,169 @@ + +/*! + ************************************************************************ + * \file quant.h + * + * \brief + * definitions for quantization functions + * + * \author + * + ************************************************************************ + */ + +#ifndef _QUANT_H_ +#define _QUANT_H_ + +// exported variables +static const int dequant_coef8[6][8][8] = +{ + { + {20, 19, 25, 19, 20, 19, 25, 19}, + {19, 18, 24, 18, 19, 18, 24, 18}, + {25, 24, 32, 24, 25, 24, 32, 24}, + {19, 18, 24, 18, 19, 18, 24, 18}, + {20, 19, 25, 19, 20, 19, 25, 19}, + {19, 18, 24, 18, 19, 18, 24, 18}, + {25, 24, 32, 24, 25, 24, 32, 24}, + {19, 18, 24, 18, 19, 18, 24, 18} + }, + { + {22, 21, 28, 21, 22, 21, 28, 21}, + {21, 19, 26, 19, 21, 19, 26, 19}, + {28, 26, 35, 26, 28, 26, 35, 26}, + {21, 19, 26, 19, 21, 19, 26, 19}, + {22, 21, 28, 21, 22, 21, 28, 21}, + {21, 19, 26, 19, 21, 19, 26, 19}, + {28, 26, 35, 26, 28, 26, 35, 26}, + {21, 19, 26, 19, 21, 19, 26, 19} + }, + { + {26, 24, 33, 24, 26, 24, 33, 24}, + {24, 23, 31, 23, 24, 23, 31, 23}, + {33, 31, 42, 31, 33, 31, 42, 31}, + {24, 23, 31, 23, 24, 23, 31, 23}, + {26, 24, 33, 24, 26, 24, 33, 24}, + {24, 23, 31, 23, 24, 23, 31, 23}, + {33, 31, 42, 31, 33, 31, 42, 31}, + {24, 23, 31, 23, 24, 23, 31, 23} + }, + { + {28, 26, 35, 26, 28, 26, 35, 26}, + {26, 25, 33, 25, 26, 25, 33, 25}, + {35, 33, 45, 33, 35, 33, 45, 33}, + {26, 25, 33, 25, 26, 25, 33, 25}, + {28, 26, 35, 26, 28, 26, 35, 26}, + {26, 25, 33, 25, 26, 25, 33, 25}, + {35, 33, 45, 33, 35, 33, 45, 33}, + {26, 25, 33, 25, 26, 25, 33, 25} + }, + { + {32, 30, 40, 30, 32, 30, 40, 30}, + {30, 28, 38, 28, 30, 28, 38, 28}, + {40, 38, 51, 38, 40, 38, 51, 38}, + {30, 28, 38, 28, 30, 28, 38, 28}, + {32, 30, 40, 30, 32, 30, 40, 30}, + {30, 28, 38, 28, 30, 28, 38, 28}, + {40, 38, 51, 38, 40, 38, 51, 38}, + {30, 28, 38, 28, 30, 28, 38, 28} + }, + { + {36, 34, 46, 34, 36, 34, 46, 34}, + {34, 32, 43, 32, 34, 32, 43, 32}, + {46, 43, 58, 43, 46, 43, 58, 43}, + {34, 32, 43, 32, 34, 32, 43, 32}, + {36, 34, 46, 34, 36, 34, 46, 34}, + {34, 32, 43, 32, 34, 32, 43, 32}, + {46, 43, 58, 43, 46, 43, 58, 43}, + {34, 32, 43, 32, 34, 32, 43, 32} + } +}; + + +//! Dequantization coefficients +static const int dequant_coef[6][4][4] = { + { + { 10, 13, 10, 13}, + { 13, 16, 13, 16}, + { 10, 13, 10, 13}, + { 13, 16, 13, 16}}, + { + { 11, 14, 11, 14}, + { 14, 18, 14, 18}, + { 11, 14, 11, 14}, + { 14, 18, 14, 18}}, + { + { 13, 16, 13, 16}, + { 16, 20, 16, 20}, + { 13, 16, 13, 16}, + { 16, 20, 16, 20}}, + { + { 14, 18, 14, 18}, + { 18, 23, 18, 23}, + { 14, 18, 14, 18}, + { 18, 23, 18, 23}}, + { + { 16, 20, 16, 20}, + { 20, 25, 20, 25}, + { 16, 20, 16, 20}, + { 20, 25, 20, 25}}, + { + { 18, 23, 18, 23}, + { 23, 29, 23, 29}, + { 18, 23, 18, 23}, + { 23, 29, 23, 29}} +}; + +static const int quant_coef[6][4][4] = { + { + { 13107, 8066, 13107, 8066}, + { 8066, 5243, 8066, 5243}, + { 13107, 8066, 13107, 8066}, + { 8066, 5243, 8066, 5243}}, + { + { 11916, 7490, 11916, 7490}, + { 7490, 4660, 7490, 4660}, + { 11916, 7490, 11916, 7490}, + { 7490, 4660, 7490, 4660}}, + { + { 10082, 6554, 10082, 6554}, + { 6554, 4194, 6554, 4194}, + { 10082, 6554, 10082, 6554}, + { 6554, 4194, 6554, 4194}}, + { + { 9362, 5825, 9362, 5825}, + { 5825, 3647, 5825, 3647}, + { 9362, 5825, 9362, 5825}, + { 5825, 3647, 5825, 3647}}, + { + { 8192, 5243, 8192, 5243}, + { 5243, 3355, 5243, 3355}, + { 8192, 5243, 8192, 5243}, + { 5243, 3355, 5243, 3355}}, + { + { 7282, 4559, 7282, 4559}, + { 4559, 2893, 4559, 2893}, + { 7282, 4559, 7282, 4559}, + { 4559, 2893, 4559, 2893}} +}; + +// SP decoding parameter (EQ. 8-425) +static const int A[4][4] = { + { 16, 20, 16, 20}, + { 20, 25, 20, 25}, + { 16, 20, 16, 20}, + { 20, 25, 20, 25} +}; + +// exported functions +// quantization initialization +extern void init_qp_process (CodingParameters *cps); +extern void free_qp_matrices(CodingParameters *cps); + +// For Q-matrix +extern void assign_quant_params (Slice *currslice); +extern void CalculateQuant4x4Param(Slice *currslice); +extern void CalculateQuant8x8Param(Slice *currslice); + +#endif + diff --git a/ldecod/inc/report_dec.h b/ldecod/inc/report_dec.h new file mode 100644 index 0000000..ac027dc --- /dev/null +++ b/ldecod/inc/report_dec.h @@ -0,0 +1,23 @@ + +/*! + ************************************************************************ + * \file report_dec.h + * + * \brief + * headers for decoder reports + * + * \author + * João Carreira + ************************************************************************ + */ + +#ifndef _REPORT_DEC_H_ +#define _REPORT_DEC_H_ + + +extern void init_report (InputParameters *p_Inp); +extern void FinalReport(VideoParameters *p_Vid); +extern void report_to_logfile(VideoParameters *p_Vid); +extern void report_stats (VideoParameters *p_Vid); + +#endif \ No newline at end of file diff --git a/ldecod/inc/rtp.h b/ldecod/inc/rtp.h new file mode 100644 index 0000000..2171a95 --- /dev/null +++ b/ldecod/inc/rtp.h @@ -0,0 +1,46 @@ + +/*! + ************************************************************************************* + * \file rtp.h + * + * \brief + * Prototypes for rtp.c + ************************************************************************************* + */ + +#ifndef _RTP_H_ +#define _RTP_H_ + +#include "nalucommon.h" + +#define MAXRTPPAYLOADLEN (65536 - 40) //!< Maximum payload size of an RTP packet */ +#define MAXRTPPACKETSIZE (65536 - 28) //!< Maximum size of an RTP packet incl. header */ +#define H264PAYLOADTYPE 105 //!< RTP paylaod type fixed here for simplicity*/ +#define H264SSRC 0x12345678 //!< SSRC, chosen to simplify debugging */ +#define RTP_TR_TIMESTAMP_MULT 1000 //!< should be something like 27 Mhz / 29.97 Hz */ + +typedef struct +{ + unsigned int v; //!< Version, 2 bits, MUST be 0x2 + unsigned int p; //!< Padding bit, Padding MUST NOT be used + unsigned int x; //!< Extension, MUST be zero + unsigned int cc; /*!< CSRC count, normally 0 in the absence + of RTP mixers */ + unsigned int m; //!< Marker bit + unsigned int pt; //!< 7 bits, Payload Type, dynamically established + uint16 seq; /*!< RTP sequence number, incremented by one for + each sent packet */ + unsigned int timestamp; //!< timestamp, 27 MHz for H.264 + unsigned int ssrc; //!< Synchronization Source, chosen randomly + byte * payload; //!< the payload including payload headers + unsigned int paylen; //!< length of payload in bytes + byte * packet; //!< complete packet including header and payload + unsigned int packlen; //!< length of packet, typically paylen+12 +} RTPpacket_t; + +void DumpRTPHeader (RTPpacket_t *p); +int GetRTPNALU (VideoParameters *p_Vid, NALU_t *nalu, int BitStreamFile); +void OpenRTPFile (char *fn, int *p_BitStreamFile); +void CloseRTPFile(int *p_BitStreamFile); + +#endif diff --git a/ldecod/inc/sei.h b/ldecod/inc/sei.h new file mode 100644 index 0000000..9145c4f --- /dev/null +++ b/ldecod/inc/sei.h @@ -0,0 +1,144 @@ + +/*! + ************************************************************************************* + * \file sei.h + * + * \brief + * Prototypes for sei.c + ************************************************************************************* + */ + +#ifndef SEI_H +#define SEI_H + +typedef enum { + SEI_BUFFERING_PERIOD = 0, + SEI_PIC_TIMING, + SEI_PAN_SCAN_RECT, + SEI_FILLER_PAYLOAD, + SEI_USER_DATA_REGISTERED_ITU_T_T35, + SEI_USER_DATA_UNREGISTERED, + SEI_RECOVERY_POINT, + SEI_DEC_REF_PIC_MARKING_REPETITION, + SEI_SPARE_PIC, + SEI_SCENE_INFO, + SEI_SUB_SEQ_INFO, + SEI_SUB_SEQ_LAYER_CHARACTERISTICS, + SEI_SUB_SEQ_CHARACTERISTICS, + SEI_FULL_FRAME_FREEZE, + SEI_FULL_FRAME_FREEZE_RELEASE, + SEI_FULL_FRAME_SNAPSHOT, + SEI_PROGRESSIVE_REFINEMENT_SEGMENT_START, + SEI_PROGRESSIVE_REFINEMENT_SEGMENT_END, + SEI_MOTION_CONSTRAINED_SLICE_GROUP_SET, + SEI_FILM_GRAIN_CHARACTERISTICS, + SEI_DEBLOCKING_FILTER_DISPLAY_PREFERENCE, + SEI_STEREO_VIDEO_INFO, + SEI_POST_FILTER_HINTS, + SEI_TONE_MAPPING, + SEI_SCALABILITY_INFO, + SEI_SUB_PIC_SCALABLE_LAYER, + SEI_NON_REQUIRED_LAYER_REP, + SEI_PRIORITY_LAYER_INFO, + SEI_LAYERS_NOT_PRESENT, + SEI_LAYER_DEPENDENCY_CHANGE, + SEI_SCALABLE_NESTING, + SEI_BASE_LAYER_TEMPORAL_HRD, + SEI_QUALITY_LAYER_INTEGRITY_CHECK, + SEI_REDUNDANT_PIC_PROPERTY, + SEI_TL0_DEP_REP_INDEX, + SEI_TL_SWITCHING_POINT, + SEI_PARALLEL_DECODING_INFO, + SEI_MVC_SCALABLE_NESTING, + SEI_VIEW_SCALABILITY_INFO, + SEI_MULTIVIEW_SCENE_INFO, + SEI_MULTIVIEW_ACQUISITION_INFO, + SEI_NON_REQUIRED_VIEW_COMPONENT, + SEI_VIEW_DEPENDENCY_CHANGE, + SEI_OPERATION_POINTS_NOT_PRESENT, + SEI_BASE_VIEW_TEMPORAL_HRD, + SEI_FRAME_PACKING_ARRANGEMENT, + + SEI_MAX_ELEMENTS //!< number of maximum syntax elements +} SEI_type; + +#define MAX_FN 256 +// tone mapping information +#define MAX_CODED_BIT_DEPTH 12 +#define MAX_SEI_BIT_DEPTH 12 +#define MAX_NUM_PIVOTS (1<> %file% +) +pause \ No newline at end of file diff --git a/ldecod/ldecod.props b/ldecod/ldecod.props new file mode 100644 index 0000000..49f1286 --- /dev/null +++ b/ldecod/ldecod.props @@ -0,0 +1,17 @@ + + + + + + + + Ws2_32.lib;%(AdditionalDependencies) + + + + + ldecod\inc;lcommon\inc;ldecod\inc\ferc + + + + \ No newline at end of file diff --git a/ldecod/src/annexb.c b/ldecod/src/annexb.c new file mode 100644 index 0000000..628a0c5 --- /dev/null +++ b/ldecod/src/annexb.c @@ -0,0 +1,353 @@ + +/*! + ************************************************************************************* + * \file annexb.c + * + * \brief + * Annex B Byte Stream format + * + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * - Stephan Wenger + ************************************************************************************* + */ + +#include "global.h" +#include "annexb.h" +#include "memalloc.h" +#include "fast_memory.h" + +static const int IOBUFFERSIZE = 512*1024; //65536; + +void malloc_annex_b(VideoParameters *p_Vid, ANNEXB_t **p_annex_b) +{ + if ( ((*p_annex_b) = (ANNEXB_t *) calloc(1, sizeof(ANNEXB_t))) == NULL) + { + snprintf(errortext, ET_SIZE, "Memory allocation for Annex_B file failed"); + error(errortext,100); + } + if (((*p_annex_b)->Buf = (byte*) malloc(p_Vid->nalu->max_size)) == NULL) + { + error("malloc_annex_b: Buf", 101); + } +} + + +void init_annex_b(ANNEXB_t *annex_b) +{ + annex_b->BitStreamFile = -1; + annex_b->iobuffer = NULL; + annex_b->iobufferread = NULL; + annex_b->bytesinbuffer = 0; + annex_b->is_eof = FALSE; + annex_b->IsFirstByteStreamNALU = 1; + annex_b->nextstartcodebytes = 0; +} + +void free_annex_b(ANNEXB_t **p_annex_b) +{ + free((*p_annex_b)->Buf); + (*p_annex_b)->Buf = NULL; + free(*p_annex_b); + *p_annex_b = NULL; +} + +/*! +************************************************************************ +* \brief +* fill IO buffer +************************************************************************ +*/ +static inline int getChunk(ANNEXB_t *annex_b) +{ + unsigned int readbytes = read (annex_b->BitStreamFile, annex_b->iobuffer, annex_b->iIOBufferSize); + if (0==readbytes) + { + annex_b->is_eof = TRUE; + return 0; + } + + annex_b->bytesinbuffer = readbytes; + annex_b->iobufferread = annex_b->iobuffer; + return readbytes; +} + +/*! +************************************************************************ +* \brief +* returns a byte from IO buffer +************************************************************************ +*/ +static inline byte getfbyte(ANNEXB_t *annex_b) +{ + if (0 == annex_b->bytesinbuffer) + { + if (0 == getChunk(annex_b)) + return 0; + } + annex_b->bytesinbuffer--; + return (*annex_b->iobufferread++); +} + +/*! + ************************************************************************ + * \brief + * returns if new start code is found at byte aligned position buf. + * new-startcode is of form N 0x00 bytes, followed by a 0x01 byte. + * + * \return + * 1 if start-code is found or \n + * 0, indicating that there is no start code + * + * \param Buf + * pointer to byte-stream + * \param zeros_in_startcode + * indicates number of 0x00 bytes in start-code. + ************************************************************************ + */ +static inline int FindStartCode (unsigned char *Buf, int zeros_in_startcode) +{ + int i; + + for (i = 0; i < zeros_in_startcode; i++) + { + if(*(Buf++) != 0) + { + return 0; + } + } + + if(*Buf != 1) + return 0; + + return 1; +} + + +/*! + ************************************************************************ + * \brief + * Returns the size of the NALU (bits between start codes in case of + * Annex B. nalu->buf and nalu->len are filled. Other field in + * nalu-> remain uninitialized (will be taken care of by NALUtoRBSP. + * + * \return + * 0 if there is nothing any more to read (EOF) + * -1 in case of any error + * + * \note Side-effect: Returns length of start-code in bytes. + * + * \note + * get_annex_b_NALU expects start codes at byte aligned positions in the file + * + ************************************************************************ + */ + +int get_annex_b_NALU (VideoParameters *p_Vid, NALU_t *nalu, ANNEXB_t *annex_b) +{ + int i; + int info2 = 0, info3 = 0, pos = 0; + int StartCodeFound = 0; + int LeadingZero8BitsCount = 0; + byte *pBuf = annex_b->Buf; + + if (annex_b->nextstartcodebytes != 0) + { + for (i=0; inextstartcodebytes-1; i++) + { + (*pBuf++) = 0; + pos++; + } + (*pBuf++) = 1; + pos++; + } + else + { + while(!annex_b->is_eof) + { + pos++; + if ((*(pBuf++)= getfbyte(annex_b))!= 0) + break; + } + } + if(annex_b->is_eof == TRUE) + { + if(pos==0) + { + return 0; + } + else + { + printf( "get_annex_b_NALU can't read start code\n"); + return -1; + } + } + + if(*(pBuf - 1) != 1 || pos < 3) + { + printf ("get_annex_b_NALU: no Start Code at the beginning of the NALU, return -1\n"); + return -1; + } + + if (pos == 3) + { + nalu->startcodeprefix_len = 3; + } + else + { + LeadingZero8BitsCount = pos - 4; + nalu->startcodeprefix_len = 4; + } + + //the 1st byte stream NAL unit can has leading_zero_8bits, but subsequent ones are not + //allowed to contain it since these zeros(if any) are considered trailing_zero_8bits + //of the previous byte stream NAL unit. + if(!annex_b->IsFirstByteStreamNALU && LeadingZero8BitsCount > 0) + { + printf ("get_annex_b_NALU: The leading_zero_8bits syntax can only be present in the first byte stream NAL unit, return -1\n"); + return -1; + } + + LeadingZero8BitsCount = pos; + annex_b->IsFirstByteStreamNALU = 0; + + while (!StartCodeFound) + { + if (annex_b->is_eof == TRUE) + { + pBuf -= 2; + while(*(pBuf--)==0) + pos--; + + + nalu->len = (pos - 1) - LeadingZero8BitsCount; + memcpy (nalu->buf, annex_b->Buf + LeadingZero8BitsCount, nalu->len); + nalu->forbidden_bit = (*(nalu->buf) >> 7) & 1; + nalu->nal_reference_idc = (NalRefIdc) ((*(nalu->buf) >> 5) & 3); + nalu->nal_unit_type = (NaluType) ((*(nalu->buf)) & 0x1f); + annex_b->nextstartcodebytes = 0; + + // printf ("get_annex_b_NALU, eof case: pos %d nalu->len %d, nalu->reference_idc %d, nal_unit_type %d \n", pos, nalu->len, nalu->nal_reference_idc, nalu->nal_unit_type); + +#if TRACE + fprintf (p_Dec->p_trace, "\n\nLast NALU in File\n\n"); + fprintf (p_Dec->p_trace, "Annex B NALU w/ %s startcode, len %d, forbidden_bit %d, nal_reference_idc %d, nal_unit_type %d\n\n", + nalu->startcodeprefix_len == 4?"long":"short", nalu->len, nalu->forbidden_bit, nalu->nal_reference_idc, nalu->nal_unit_type); + fflush (p_Dec->p_trace); +#endif + return (pos - 1); + } + + pos++; + *(pBuf ++) = getfbyte(annex_b); + info3 = FindStartCode(pBuf - 4, 3); + if(info3 != 1) + { + info2 = FindStartCode(pBuf - 3, 2); + StartCodeFound = info2 & 0x01; + } + else + StartCodeFound = 1; + } + + // Here, we have found another start code (and read length of startcode bytes more than we should + // have. Hence, go back in the file + if(info3 == 1) //if the detected start code is 00 00 01, trailing_zero_8bits is sure not to be present + { + pBuf -= 5; + while(*(pBuf--) == 0) + pos--; + annex_b->nextstartcodebytes = 4; + } + else if (info2 == 1) + annex_b->nextstartcodebytes = 3; + else + { + printf(" Panic: Error in next start code search \n"); + return -1; + } + + pos -= annex_b->nextstartcodebytes; + + // Here the leading zeros(if any), Start code, the complete NALU, trailing zeros(if any) + // and the next start code is in the Buf. + // The size of Buf is pos - rewind, pos are the number of bytes excluding the next + // start code, and (pos) - LeadingZero8BitsCount + // is the size of the NALU. + + nalu->len = pos - LeadingZero8BitsCount; + fast_memcpy (nalu->buf, annex_b->Buf + LeadingZero8BitsCount, nalu->len); + nalu->forbidden_bit = (*(nalu->buf) >> 7) & 1; + nalu->nal_reference_idc = (NalRefIdc) ((*(nalu->buf) >> 5) & 3); + nalu->nal_unit_type = (NaluType) ((*(nalu->buf)) & 0x1f); + nalu->lost_packets = 0; + + + //printf ("get_annex_b_NALU, regular case: pos %d nalu->len %d, nalu->reference_idc %d, nal_unit_type %d \n", pos, nalu->len, nalu->nal_reference_idc, nalu->nal_unit_type); +#if TRACE + fprintf (p_Dec->p_trace, "\n\nAnnex B NALU w/ %s startcode, len %d, forbidden_bit %d, nal_reference_idc %d, nal_unit_type %d\n\n", + nalu->startcodeprefix_len == 4?"long":"short", nalu->len, nalu->forbidden_bit, nalu->nal_reference_idc, nalu->nal_unit_type); + fflush (p_Dec->p_trace); +#endif + + return (pos); + +} + + + +/*! + ************************************************************************ + * \brief + * Opens the bit stream file named fn + * \return + * none + ************************************************************************ + */ +void open_annex_b (char *fn, ANNEXB_t *annex_b) +{ + if (NULL != annex_b->iobuffer) + { + error ("open_annex_b: tried to open Annex B file twice",500); + } + if ((annex_b->BitStreamFile = open(fn, OPENFLAGS_READ)) == -1) + { + snprintf (errortext, ET_SIZE, "Cannot open Annex B ByteStream file '%s'", fn); + error(errortext,500); + } + + annex_b->iIOBufferSize = IOBUFFERSIZE * sizeof (byte); + annex_b->iobuffer = malloc (annex_b->iIOBufferSize); + if (NULL == annex_b->iobuffer) + { + error ("open_annex_b: cannot allocate IO buffer",500); + } + annex_b->is_eof = FALSE; + getChunk(annex_b); +} + + +/*! + ************************************************************************ + * \brief + * Closes the bit stream file + ************************************************************************ + */ +void close_annex_b(ANNEXB_t *annex_b) +{ + if (annex_b->BitStreamFile != -1) + { + close(annex_b->BitStreamFile); + annex_b->BitStreamFile = - 1; + } + free (annex_b->iobuffer); + annex_b->iobuffer = NULL; +} + + +void reset_annex_b(ANNEXB_t *annex_b) +{ + annex_b->is_eof = FALSE; + annex_b->bytesinbuffer = 0; + annex_b->iobufferread = annex_b->iobuffer; +} diff --git a/ldecod/src/biaridecod.c b/ldecod/src/biaridecod.c new file mode 100644 index 0000000..b900495 --- /dev/null +++ b/ldecod/src/biaridecod.c @@ -0,0 +1,314 @@ +/*! + ************************************************************************************* + * \file biaridecod.c + * + * \brief + * Binary arithmetic decoder routines. + * + * This modified implementation of the M Coder is based on JVT-U084 + * with the choice of M_BITS = 16. + * + * \date + * 21. Oct 2000 + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * - Detlev Marpe + * - Gabi Blaettermann + * - Gunnar Marten + ************************************************************************************* + */ + +#include "global.h" +#include "memalloc.h" +#include "biaridecod.h" + + +#define B_BITS 10 // Number of bits to represent the whole coding interval +#define HALF 0x01FE //(1 << (B_BITS-1)) - 2 +#define QUARTER 0x0100 //(1 << (B_BITS-2)) + +/*! + ************************************************************************ + * \brief + * Allocates memory for the DecodingEnvironment struct + * \return DecodingContextPtr + * allocates memory + ************************************************************************ + */ +DecodingEnvironmentPtr arideco_create_decoding_environment() +{ + DecodingEnvironmentPtr dep; + + if ((dep = calloc(1,sizeof(DecodingEnvironment))) == NULL) + no_mem_exit("arideco_create_decoding_environment: dep"); + return dep; +} + + +/*! + *********************************************************************** + * \brief + * Frees memory of the DecodingEnvironment struct + *********************************************************************** + */ +void arideco_delete_decoding_environment(DecodingEnvironmentPtr dep) +{ + if (dep == NULL) + { + snprintf(errortext, ET_SIZE, "Error freeing dep (NULL pointer)"); + error (errortext, 200); + } + else + free(dep); +} + +/*! + ************************************************************************ + * \brief + * finalize arithetic decoding(): + ************************************************************************ + */ +void arideco_done_decoding(DecodingEnvironmentPtr dep) +{ + (*dep->Dcodestrm_len)++; +#if(TRACE==2) + fprintf(p_trace, "done_decoding: %d\n", *dep->Dcodestrm_len); +#endif +} + +/*! + ************************************************************************ + * \brief + * read one byte from the bitstream + ************************************************************************ + */ +static inline unsigned int getbyte(DecodingEnvironmentPtr dep) +{ +#if(TRACE==2) + fprintf(p_trace, "get_byte: %d\n", (*dep->Dcodestrm_len)); +#endif + return dep->Dcodestrm[(*dep->Dcodestrm_len)++]; +} + +/*! + ************************************************************************ + * \brief + * read two bytes from the bitstream + ************************************************************************ + */ +static inline unsigned int getword(DecodingEnvironmentPtr dep) +{ + int *len = dep->Dcodestrm_len; + byte *p_code_strm = &dep->Dcodestrm[*len]; +#if(TRACE==2) + fprintf(p_trace, "get_byte: %d\n", *len); + fprintf(p_trace, "get_byte: %d\n", *len + 1); +#endif + *len += 2; + return ((*p_code_strm<<8) | *(p_code_strm + 1)); +} +/*! + ************************************************************************ + * \brief + * Initializes the DecodingEnvironment for the arithmetic coder + ************************************************************************ + */ +void arideco_start_decoding(DecodingEnvironmentPtr dep, unsigned char *code_buffer, + int firstbyte, int *code_len) +{ + + dep->Dcodestrm = code_buffer; + dep->Dcodestrm_len = code_len; + *dep->Dcodestrm_len = firstbyte; + + dep->Dvalue = getbyte(dep); + dep->Dvalue = (dep->Dvalue << 16) | getword(dep); // lookahead of 2 bytes: always make sure that bitstream buffer + // contains 2 more bytes than actual bitstream + dep->DbitsLeft = 15; + dep->Drange = HALF; + +#if (2==TRACE) + fprintf(p_trace, "value: %d firstbyte: %d code_len: %d\n", dep->Dvalue >> dep->DbitsLeft, firstbyte, *code_len); +#endif +} + + +/*! + ************************************************************************ + * \brief + * arideco_bits_read + ************************************************************************ + */ +int arideco_bits_read(DecodingEnvironmentPtr dep) +{ +#if (2==TRACE) + int tmp = ((*dep->Dcodestrm_len) << 3) - dep->DbitsLeft; + fprintf(p_trace, "tmp: %d\n", tmp); + return tmp; +#else + return (((*dep->Dcodestrm_len) << 3) - dep->DbitsLeft); +#endif +} + + +/*! +************************************************************************ +* \brief +* biari_decode_symbol(): +* \return +* the decoded symbol +************************************************************************ +*/ +unsigned int biari_decode_symbol(DecodingEnvironment *dep, BiContextType *bi_ct ) +{ + unsigned int bit = bi_ct->MPS; + unsigned int *value = &dep->Dvalue; + unsigned int *range = &dep->Drange; + uint16 *state = &bi_ct->state; + unsigned int rLPS = rLPS_table_64x4[*state][(*range>>6) & 0x03]; + int *DbitsLeft = &dep->DbitsLeft; + + *range -= rLPS; + + if(*value < (*range << *DbitsLeft)) //MPS + { + *state = AC_next_state_MPS_64[*state]; // next state + if( *range >= QUARTER ) + { + return (bit); + } + else + { + *range <<= 1; + (*DbitsLeft)--; + } + } + else // LPS + { + int renorm = renorm_table_32[(rLPS>>3) & 0x1F]; + *value -= (*range << *DbitsLeft); + + *range = (rLPS << renorm); + (*DbitsLeft) -= renorm; + + bit ^= 0x01; + if (!(*state)) // switch meaning of MPS if necessary + bi_ct->MPS ^= 0x01; + + *state = AC_next_state_LPS_64[*state]; // next state + } + + if( *DbitsLeft > 0 ) + { + return (bit); + } + else + { + *value <<= 16; + *value |= getword(dep); // lookahead of 2 bytes: always make sure that bitstream buffer + // contains 2 more bytes than actual bitstream + (*DbitsLeft) += 16; + + return (bit); + } +} + + +/*! + ************************************************************************ + * \brief + * biari_decode_symbol_eq_prob(): + * \return + * the decoded symbol + ************************************************************************ + */ +unsigned int biari_decode_symbol_eq_prob(DecodingEnvironmentPtr dep) +{ + int tmp_value; + unsigned int *value = &dep->Dvalue; + int *DbitsLeft = &dep->DbitsLeft; + + if(--(*DbitsLeft) == 0) + { + *value = (*value << 16) | getword( dep ); // lookahead of 2 bytes: always make sure that bitstream buffer + // contains 2 more bytes than actual bitstream + *DbitsLeft = 16; + } + tmp_value = *value - (dep->Drange << *DbitsLeft); + + if (tmp_value < 0) + { + return 0; + } + else + { + *value = tmp_value; + return 1; + } +} + +/*! + ************************************************************************ + * \brief + * biari_decode_symbol_final(): + * \return + * the decoded symbol + ************************************************************************ + */ +unsigned int biari_decode_final(DecodingEnvironmentPtr dep) +{ + unsigned int range = dep->Drange - 2; + int value = dep->Dvalue; + value -= (range << dep->DbitsLeft); + + if (value < 0) + { + if( range >= QUARTER ) + { + dep->Drange = range; + return 0; + } + else + { + dep->Drange = (range << 1); + if( --(dep->DbitsLeft) > 0 ) + return 0; + else + { + dep->Dvalue = (dep->Dvalue << 16) | getword( dep ); // lookahead of 2 bytes: always make sure that bitstream buffer + // contains 2 more bytes than actual bitstream + dep->DbitsLeft = 16; + return 0; + } + } + } + else + { + return 1; + } +} + +/*! + ************************************************************************ + * \brief + * Initializes a given context with some pre-defined probability state + ************************************************************************ + */ +void biari_init_context (int qp, BiContextTypePtr ctx, const char* ini) +{ + int pstate = ((ini[0]* qp )>>4) + ini[1]; + + if ( pstate >= 64 ) + { + pstate = imin(126, pstate); + ctx->state = (uint16) (pstate - 64); + ctx->MPS = 1; + } + else + { + pstate = imax(1, pstate); + ctx->state = (uint16) (63 - pstate); + ctx->MPS = 0; + } +} + diff --git a/ldecod/src/block.c b/ldecod/src/block.c new file mode 100644 index 0000000..64d16d8 --- /dev/null +++ b/ldecod/src/block.c @@ -0,0 +1,907 @@ + +/*! + *********************************************************************** + * \file + * block.c + * + * \brief + * Block functions + * + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * - Inge Lille-Langoy + * - Rickard Sjoberg + *********************************************************************** + */ + +#include "contributors.h" + +#include "global.h" +#include "block.h" +#include "blk_prediction.h" +#include "image.h" +#include "mb_access.h" +#include "transform.h" +#include "quant.h" +#include "memalloc.h" + +/*! + *********************************************************************** + * \brief + * Inverse 4x4 transformation, transforms cof to mb_rres + *********************************************************************** + */ +void itrans4x4(Macroblock *currMB, //!< current macroblock + ColorPlane pl, //!< used color plane + int ioff, //!< index to 4x4 block + int joff) //!< index to 4x4 block +{ + Slice *currSlice = currMB->p_Slice; + int **mb_rres = currSlice->mb_rres[pl]; + + inverse4x4(currSlice->cof[pl],mb_rres,joff,ioff); + + sample_reconstruct (&currSlice->mb_rec[pl][joff], &currSlice->mb_pred[pl][joff], &mb_rres[joff], ioff, ioff, BLOCK_SIZE, BLOCK_SIZE, currMB->p_Vid->max_pel_value_comp[pl], DQ_BITS); +} + +/*! + **************************************************************************** + * \brief + * Inverse 4x4 lossless_qpprime transformation, transforms cof to mb_rres + **************************************************************************** + */ +void itrans4x4_ls(Macroblock *currMB, //!< current macroblock + ColorPlane pl, //!< Color plane (for 4:4:4) + int ioff, //!< index to 4x4 block + int joff) //!< index to 4x4 block +{ + int i,j; + + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + int max_imgpel_value = p_Vid->max_pel_value_comp[pl]; + + imgpel **mb_pred = currSlice->mb_pred[pl]; + imgpel **mb_rec = currSlice->mb_rec[pl]; + int **mb_rres = currSlice->mb_rres [pl]; + + for (j = joff; j < joff + BLOCK_SIZE; ++j) + { + for (i = ioff; i < ioff + BLOCK_SIZE; ++i) + { + mb_rec[j][i] = (imgpel) iClip1(max_imgpel_value, mb_pred[j][i] + mb_rres[j][i]); + } + } +} + +/*! +************************************************************************ +* \brief +* Inverse residual DPCM for Intra lossless coding +* +************************************************************************ +*/ +void Inv_Residual_trans_4x4(Macroblock *currMB, //!< current macroblock + ColorPlane pl, //!< used color plane + int ioff, //!< index to 4x4 block + int joff) //!< index to 4x4 block +{ + int i,j; + int temp[4][4]; + Slice *currSlice = currMB->p_Slice; + imgpel **mb_pred = currSlice->mb_pred[pl]; + imgpel **mb_rec = currSlice->mb_rec[pl]; + int **mb_rres = currSlice->mb_rres[pl]; + int **cof = currSlice->cof[pl]; + + if(currMB->ipmode_DPCM == VERT_PRED) + { + for(i=0; i<4; ++i) + { + temp[0][i] = cof[joff + 0][ioff + i]; + temp[1][i] = cof[joff + 1][ioff + i] + temp[0][i]; + temp[2][i] = cof[joff + 2][ioff + i] + temp[1][i]; + temp[3][i] = cof[joff + 3][ioff + i] + temp[2][i]; + } + + for(i=0; i<4; ++i) + { + mb_rres[joff ][ioff + i]=temp[0][i]; + mb_rres[joff + 1][ioff + i]=temp[1][i]; + mb_rres[joff + 2][ioff + i]=temp[2][i]; + mb_rres[joff + 3][ioff + i]=temp[3][i]; + } + } + else if(currMB->ipmode_DPCM == HOR_PRED) + { + for(j=0; j<4; ++j) + { + temp[j][0] = cof[joff + j][ioff ]; + temp[j][1] = cof[joff + j][ioff + 1] + temp[j][0]; + temp[j][2] = cof[joff + j][ioff + 2] + temp[j][1]; + temp[j][3] = cof[joff + j][ioff + 3] + temp[j][2]; + } + + for(j=0; j<4; ++j) + { + mb_rres[joff + j][ioff ]=temp[j][0]; + mb_rres[joff + j][ioff + 1]=temp[j][1]; + mb_rres[joff + j][ioff + 2]=temp[j][2]; + mb_rres[joff + j][ioff + 3]=temp[j][3]; + } + } + else + { + for (j = joff; j < joff + BLOCK_SIZE; ++j) + for (i = ioff; i < ioff + BLOCK_SIZE; ++i) + mb_rres[j][i] = cof[j][i]; + } + + for (j = joff; j < joff + BLOCK_SIZE; ++j) + { + for (i = ioff; i < ioff + BLOCK_SIZE; ++i) + { + mb_rec[j][i] = (imgpel) (mb_rres[j][i] + mb_pred[j][i]); + } + } +} + +/*! +************************************************************************ +* \brief +* Inverse residual DPCM for Intra lossless coding +* +* \par Input: +* ioff_x,joff_y: Block position inside a macro block (0,8). +************************************************************************ +*/ +//For residual DPCM +void Inv_Residual_trans_8x8(Macroblock *currMB, ColorPlane pl, int ioff,int joff) +{ + Slice *currSlice = currMB->p_Slice; + int i, j; + int temp[8][8]; + imgpel **mb_pred = currSlice->mb_pred[pl]; + imgpel **mb_rec = currSlice->mb_rec[pl]; + int **mb_rres = currSlice->mb_rres[pl]; + + if(currMB->ipmode_DPCM == VERT_PRED) + { + for(i=0; i<8; ++i) + { + temp[0][i] = mb_rres[joff + 0][ioff + i]; + temp[1][i] = mb_rres[joff + 1][ioff + i] + temp[0][i]; + temp[2][i] = mb_rres[joff + 2][ioff + i] + temp[1][i]; + temp[3][i] = mb_rres[joff + 3][ioff + i] + temp[2][i]; + temp[4][i] = mb_rres[joff + 4][ioff + i] + temp[3][i]; + temp[5][i] = mb_rres[joff + 5][ioff + i] + temp[4][i]; + temp[6][i] = mb_rres[joff + 6][ioff + i] + temp[5][i]; + temp[7][i] = mb_rres[joff + 7][ioff + i] + temp[6][i]; + } + for(i=0; i<8; ++i) + { + mb_rres[joff ][ioff+i]=temp[0][i]; + mb_rres[joff+1][ioff+i]=temp[1][i]; + mb_rres[joff+2][ioff+i]=temp[2][i]; + mb_rres[joff+3][ioff+i]=temp[3][i]; + mb_rres[joff+4][ioff+i]=temp[4][i]; + mb_rres[joff+5][ioff+i]=temp[5][i]; + mb_rres[joff+6][ioff+i]=temp[6][i]; + mb_rres[joff+7][ioff+i]=temp[7][i]; + } + } + else if(currMB->ipmode_DPCM == HOR_PRED)//HOR_PRED + { + for(i=0; i<8; ++i) + { + temp[i][0] = mb_rres[joff + i][ioff + 0]; + temp[i][1] = mb_rres[joff + i][ioff + 1] + temp[i][0]; + temp[i][2] = mb_rres[joff + i][ioff + 2] + temp[i][1]; + temp[i][3] = mb_rres[joff + i][ioff + 3] + temp[i][2]; + temp[i][4] = mb_rres[joff + i][ioff + 4] + temp[i][3]; + temp[i][5] = mb_rres[joff + i][ioff + 5] + temp[i][4]; + temp[i][6] = mb_rres[joff + i][ioff + 6] + temp[i][5]; + temp[i][7] = mb_rres[joff + i][ioff + 7] + temp[i][6]; + } + for(i=0; i<8; ++i) + { + mb_rres[joff+i][ioff+0]=temp[i][0]; + mb_rres[joff+i][ioff+1]=temp[i][1]; + mb_rres[joff+i][ioff+2]=temp[i][2]; + mb_rres[joff+i][ioff+3]=temp[i][3]; + mb_rres[joff+i][ioff+4]=temp[i][4]; + mb_rres[joff+i][ioff+5]=temp[i][5]; + mb_rres[joff+i][ioff+6]=temp[i][6]; + mb_rres[joff+i][ioff+7]=temp[i][7]; + } + } + + for (j = joff; j < joff + BLOCK_SIZE*2; ++j) + { + for (i = ioff; i < ioff + BLOCK_SIZE*2; ++i) + { + mb_rec [j][i] = (imgpel) (mb_rres[j][i] + mb_pred[j][i]); + } + } +} + + + +/*! +************************************************************************ +* \brief +* Inverse residual DPCM for Intra lossless coding +* +************************************************************************ +*/ +void Inv_Residual_trans_16x16(Macroblock *currMB, //!< current macroblock + ColorPlane pl) //!< used color plane +{ + int i,j; + int temp[16][16]; + Slice *currSlice = currMB->p_Slice; + imgpel **mb_pred = currSlice->mb_pred[pl]; + imgpel **mb_rec = currSlice->mb_rec[pl]; + int **mb_rres = currSlice->mb_rres[pl]; + int **cof = currSlice->cof[pl]; + + if(currMB->ipmode_DPCM == VERT_PRED_16) + { + for(i=0; iipmode_DPCM == HOR_PRED_16) + { + for(j=0; jp_Slice; + //imgpel **mb_pred = currSlice->mb_pred[uv+1]; + //imgpel **mb_rec = currSlice->mb_rec[uv+1]; + int **mb_rres = currSlice->mb_rres[uv+1]; + int **cof = currSlice->cof[uv+1]; + int width, height; + + width = currMB->p_Vid->mb_cr_size_x; + height = currMB->p_Vid->mb_cr_size_y; + + if(currMB->c_ipred_mode == VERT_PRED_8) + { + for(i=0; ip_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + int j; + + int transform_pl = (p_Vid->separate_colour_plane_flag != 0) ? PLANE_Y : pl; + int **cof = currSlice->cof[transform_pl]; + int qp_scaled = currMB->qp_scaled[transform_pl]; + + int qp_per = p_Vid->qp_per_matrix[ qp_scaled ]; + int qp_rem = p_Vid->qp_rem_matrix[ qp_scaled ]; + + int invLevelScale = currSlice->InvLevelScale4x4_Intra[pl][qp_rem][0][0]; + int **M4; + get_mem2Dint(&M4, BLOCK_SIZE, BLOCK_SIZE); + + // horizontal + for (j=0; j < 4;++j) + { + M4[j][0]=cof[j<<2][0]; + M4[j][1]=cof[j<<2][4]; + M4[j][2]=cof[j<<2][8]; + M4[j][3]=cof[j<<2][12]; + } + + ihadamard4x4(M4, M4); + + // vertical + for (j=0; j < 4;++j) + { + cof[j<<2][0] = rshift_rnd((( M4[j][0] * invLevelScale) << qp_per), 6); + cof[j<<2][4] = rshift_rnd((( M4[j][1] * invLevelScale) << qp_per), 6); + cof[j<<2][8] = rshift_rnd((( M4[j][2] * invLevelScale) << qp_per), 6); + cof[j<<2][12] = rshift_rnd((( M4[j][3] * invLevelScale) << qp_per), 6); + } + + free_mem2Dint(M4); +} + + +void itrans_sp(Macroblock *currMB, //!< current macroblock + ColorPlane pl, //!< used color plane + int ioff, //!< index to 4x4 block + int joff) //!< index to 4x4 block +{ + VideoParameters *p_Vid = currMB->p_Vid; + Slice *currSlice = currMB->p_Slice; + int i,j; + int ilev, icof; + + int qp = (currSlice->slice_type == SI_SLICE) ? currSlice->qs : currSlice->qp; + int qp_per = p_Vid->qp_per_matrix[ qp ]; + int qp_rem = p_Vid->qp_rem_matrix[ qp ]; + + int qp_per_sp = p_Vid->qp_per_matrix[ currSlice->qs ]; + int qp_rem_sp = p_Vid->qp_rem_matrix[ currSlice->qs ]; + int q_bits_sp = Q_BITS + qp_per_sp; + + imgpel **mb_pred = currSlice->mb_pred[pl]; + imgpel **mb_rec = currSlice->mb_rec[pl]; + int **mb_rres = currSlice->mb_rres[pl]; + int **cof = currSlice->cof[pl]; + int max_imgpel_value = p_Vid->max_pel_value_comp[pl]; + + const int (*InvLevelScale4x4) [4] = dequant_coef[qp_rem]; + const int (*InvLevelScale4x4SP)[4] = dequant_coef[qp_rem_sp]; + int **PBlock; + + get_mem2Dint(&PBlock, MB_BLOCK_SIZE, MB_BLOCK_SIZE); + + for (j=0; j< BLOCK_SIZE; ++j) + { + PBlock[j][0] = mb_pred[j+joff][ioff ]; + PBlock[j][1] = mb_pred[j+joff][ioff + 1]; + PBlock[j][2] = mb_pred[j+joff][ioff + 2]; + PBlock[j][3] = mb_pred[j+joff][ioff + 3]; + } + + forward4x4(PBlock, PBlock, 0, 0); + + if(currSlice->sp_switch || currSlice->slice_type==SI_SLICE) + { + for (j=0;j> qp_per) / InvLevelScale4x4[j][i]; + //icof = ((cof[joff + j][ioff + i] * quant_coef[qp_rem][j][i])>> (qp_per + 15)) ; + // icof = rshift_rnd_sf(cof[joff + j][ioff + i] * quant_coef[qp_rem][j][i], qp_per + 15); + ilev = rshift_rnd_sf(iabs(PBlock[j][i]) * quant_coef[qp_rem_sp][j][i], q_bits_sp); + ilev = isignab(ilev, PBlock[j][i]) + icof; + cof[joff + j][ioff + i] = ilev * InvLevelScale4x4SP[j][i] << qp_per_sp; + } + } + } + else + { + for (j=0;j> qp_per) / InvLevelScale4x4[j][i]; + //icof = cof[joff + j][ioff + i]; + //icof = rshift_rnd_sf(cof[joff + j][ioff + i] * quant_coef[qp_rem][j][i], qp_per + 15); + ilev = PBlock[j][i] + ((icof * InvLevelScale4x4[j][i] * A[j][i] << qp_per) >> 6); + ilev = isign(ilev) * rshift_rnd_sf(iabs(ilev) * quant_coef[qp_rem_sp][j][i], q_bits_sp); + //cof[joff + j][ioff + i] = ilev * InvLevelScale4x4SP[j][i] << qp_per_sp; + cof[joff + j][ioff + i] = ilev * InvLevelScale4x4SP[j][i] << qp_per_sp; + } + } + } + + inverse4x4(cof, mb_rres, joff, ioff); + + for (j=joff; jp_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + int i,j,ilev, icof, n2,n1; + int mp1[BLOCK_SIZE]; + int qp_per,qp_rem; + int qp_per_sp,qp_rem_sp,q_bits_sp; + imgpel **mb_pred = currSlice->mb_pred[uv + 1]; + int **cof = currSlice->cof[uv + 1]; + int **PBlock = new_mem2Dint(MB_BLOCK_SIZE, MB_BLOCK_SIZE); + + qp_per = p_Vid->qp_per_matrix[ ((currSlice->qp < 0 ? currSlice->qp : QP_SCALE_CR[currSlice->qp]))]; + qp_rem = p_Vid->qp_rem_matrix[ ((currSlice->qp < 0 ? currSlice->qp : QP_SCALE_CR[currSlice->qp]))]; + + qp_per_sp = p_Vid->qp_per_matrix[ ((currSlice->qs < 0 ? currSlice->qs : QP_SCALE_CR[currSlice->qs]))]; + qp_rem_sp = p_Vid->qp_rem_matrix[ ((currSlice->qs < 0 ? currSlice->qs : QP_SCALE_CR[currSlice->qs]))]; + q_bits_sp = Q_BITS + qp_per_sp; + + if (currSlice->slice_type == SI_SLICE) + { + qp_per = qp_per_sp; + qp_rem = qp_rem_sp; + } + + for (j=0; j < p_Vid->mb_cr_size_y; ++j) + { + for (i=0; i < p_Vid->mb_cr_size_x; ++i) + { + PBlock[j][i] = mb_pred[j][i]; + mb_pred[j][i] = 0; + } + } + + for (n2=0; n2 < p_Vid->mb_cr_size_y; n2 += BLOCK_SIZE) + { + for (n1=0; n1 < p_Vid->mb_cr_size_x; n1 += BLOCK_SIZE) + { + forward4x4(PBlock, PBlock, n2, n1); + } + } + + // 2X2 transform of DC coeffs. + mp1[0] = (PBlock[0][0] + PBlock[4][0] + PBlock[0][4] + PBlock[4][4]); + mp1[1] = (PBlock[0][0] - PBlock[4][0] + PBlock[0][4] - PBlock[4][4]); + mp1[2] = (PBlock[0][0] + PBlock[4][0] - PBlock[0][4] - PBlock[4][4]); + mp1[3] = (PBlock[0][0] - PBlock[4][0] - PBlock[0][4] + PBlock[4][4]); + + if (currSlice->sp_switch || currSlice->slice_type == SI_SLICE) + { + for (n2=0; n2 < 2; ++n2 ) + { + for (n1=0; n1 < 2; ++n1 ) + { + //quantization fo predicted block + ilev = rshift_rnd_sf(iabs (mp1[n1+n2*2]) * quant_coef[qp_rem_sp][0][0], q_bits_sp + 1); + //addition + ilev = isignab(ilev, mp1[n1+n2*2]) + cof[n2<<2][n1<<2]; + //dequantization + mp1[n1+n2*2] =ilev * dequant_coef[qp_rem_sp][0][0] << qp_per_sp; + } + } + + for (n2 = 0; n2 < p_Vid->mb_cr_size_y; n2 += BLOCK_SIZE) + { + for (n1 = 0; n1 < p_Vid->mb_cr_size_x; n1 += BLOCK_SIZE) + { + for (j = 0; j < BLOCK_SIZE; ++j) + { + for (i = 0; i < BLOCK_SIZE; ++i) + { + // recovering coefficient since they are already dequantized earlier + cof[n2 + j][n1 + i] = (cof[n2 + j][n1 + i] >> qp_per) / dequant_coef[qp_rem][j][i]; + + //quantization of the predicted block + ilev = rshift_rnd_sf(iabs(PBlock[n2 + j][n1 + i]) * quant_coef[qp_rem_sp][j][i], q_bits_sp); + //addition of the residual + ilev = isignab(ilev,PBlock[n2 + j][n1 + i]) + cof[n2 + j][n1 + i]; + // Inverse quantization + cof[n2 + j][n1 + i] = ilev * dequant_coef[qp_rem_sp][j][i] << qp_per_sp; + } + } + } + } + } + else + { + for (n2=0; n2 < 2; ++n2 ) + { + for (n1=0; n1 < 2; ++n1 ) + { + ilev = mp1[n1+n2*2] + (((cof[n2<<2][n1<<2] * dequant_coef[qp_rem][0][0] * A[0][0]) << qp_per) >> 5); + ilev = isign(ilev) * rshift_rnd_sf(iabs(ilev) * quant_coef[qp_rem_sp][0][0], q_bits_sp + 1); + //ilev = isignab(rshift_rnd_sf(iabs(ilev)* quant_coef[qp_rem_sp][0][0], q_bits_sp + 1), ilev); + mp1[n1+n2*2] = ilev * dequant_coef[qp_rem_sp][0][0] << qp_per_sp; + } + } + + for (n2 = 0; n2 < p_Vid->mb_cr_size_y; n2 += BLOCK_SIZE) + { + for (n1 = 0; n1 < p_Vid->mb_cr_size_x; n1 += BLOCK_SIZE) + { + for (j = 0; j< BLOCK_SIZE; ++j) + { + for (i = 0; i< BLOCK_SIZE; ++i) + { + // recovering coefficient since they are already dequantized earlier + //icof = ((((cof[n2 + j][n1 + i] << 4) + qp_per/2)>> qp_per) + dequant_coef[qp_rem][j][i]/2) / dequant_coef[qp_rem][j][i]; + icof = (cof[n2 + j][n1 + i] >> qp_per) / dequant_coef[qp_rem][j][i]; + //dequantization and addition of the predicted block + ilev = PBlock[n2 + j][n1 + i] + ((icof * dequant_coef[qp_rem][j][i] * A[j][i] << qp_per) >> 6); + //quantization and dequantization + ilev = isign(ilev) * rshift_rnd_sf(iabs(ilev) * quant_coef[qp_rem_sp][j][i], q_bits_sp); + cof[n2 + j][n1 + i] = ilev * dequant_coef[qp_rem_sp][j][i] << qp_per_sp; + //printf( " %d %d %d\n", j, i, quant_coef[qp_rem_sp][j][i]); + } + } + } + } + } + + cof[0][0] = (mp1[0] + mp1[1] + mp1[2] + mp1[3]) >> 1; + cof[0][4] = (mp1[0] + mp1[1] - mp1[2] - mp1[3]) >> 1; + cof[4][0] = (mp1[0] - mp1[1] + mp1[2] - mp1[3]) >> 1; + cof[4][4] = (mp1[0] - mp1[1] - mp1[2] + mp1[3]) >> 1; + + free_mem2Dint(PBlock); +} + +void iMBtrans4x4(Macroblock *currMB, ColorPlane pl, int smb) +{ + Slice *currSlice = currMB->p_Slice; + //VideoParameters *p_Vid = currMB->p_Vid; + + StorablePicture *dec_picture = currMB->p_Slice->dec_picture; + int jj, ii; + int block8x8; + int k; + + imgpel **curr_img = pl ? dec_picture->imgUV[pl - 1]: dec_picture->imgY; + + // =============== 4x4 itrans ================ + // ------------------------------------------- + if (currMB->is_lossless && currMB->mb_type == I16MB) + { + Inv_Residual_trans_16x16(currMB, pl) ; + } + else if (smb || currMB->is_lossless == TRUE) + { + currMB->itrans_4x4 = (smb) ? itrans_sp : ((currMB->is_lossless == FALSE) ? itrans4x4 : Inv_Residual_trans_4x4); + for (block8x8=0; block8x8 < MB_BLOCK_SIZE; block8x8 += 4) + { + for (k = block8x8; k < block8x8 + 4; ++k ) + { + jj = ((decode_block_scan[k] >> 2) & 3) << BLOCK_SHIFT; + ii = (decode_block_scan[k] & 3) << BLOCK_SHIFT; + + currMB->itrans_4x4(currMB, pl, ii, jj); // use integer transform and make 4x4 block mb_rres from prediction block mb_pred + } + } + } + else + { + int **cof = currSlice->cof[pl]; + int **mb_rres = currSlice->mb_rres[pl]; + + if (currMB->is_intra_block == FALSE) + { + if (currMB->cbp & 0x01) + { + inverse4x4(cof, mb_rres, 0, 0); + inverse4x4(cof, mb_rres, 0, 4); + inverse4x4(cof, mb_rres, 4, 0); + inverse4x4(cof, mb_rres, 4, 4); + } + if (currMB->cbp & 0x02) + { + inverse4x4(cof, mb_rres, 0, 8); + inverse4x4(cof, mb_rres, 0, 12); + inverse4x4(cof, mb_rres, 4, 8); + inverse4x4(cof, mb_rres, 4, 12); + } + if (currMB->cbp & 0x04) + { + inverse4x4(cof, mb_rres, 8, 0); + inverse4x4(cof, mb_rres, 8, 4); + inverse4x4(cof, mb_rres, 12, 0); + inverse4x4(cof, mb_rres, 12, 4); + } + if (currMB->cbp & 0x08) + { + inverse4x4(cof, mb_rres, 8, 8); + inverse4x4(cof, mb_rres, 8, 12); + inverse4x4(cof, mb_rres, 12, 8); + inverse4x4(cof, mb_rres, 12, 12); + } + } + else + { + for (jj = 0; jj < MB_BLOCK_SIZE; jj += BLOCK_SIZE) + { + inverse4x4(cof, mb_rres, jj, 0); + inverse4x4(cof, mb_rres, jj, 4); + inverse4x4(cof, mb_rres, jj, 8); + inverse4x4(cof, mb_rres, jj, 12); + } + } + sample_reconstruct (currSlice->mb_rec[pl], currSlice->mb_pred[pl], mb_rres, 0, 0, MB_BLOCK_SIZE, MB_BLOCK_SIZE, currMB->p_Vid->max_pel_value_comp[pl], DQ_BITS); + } + + // construct picture from 4x4 blocks + copy_image_data_16x16(&curr_img[currMB->pix_y], currSlice->mb_rec[pl], currMB->pix_x, 0); +} + +void iMBtrans8x8(Macroblock *currMB, ColorPlane pl) +{ + //VideoParameters *p_Vid = currMB->p_Vid; + StorablePicture *dec_picture = currMB->p_Slice->dec_picture; + imgpel **curr_img = pl ? dec_picture->imgUV[pl - 1]: dec_picture->imgY; + + // Perform 8x8 idct + if (currMB->cbp & 0x01) + itrans8x8(currMB, pl, 0, 0); + else + icopy8x8(currMB, pl, 0, 0); + + if (currMB->cbp & 0x02) + itrans8x8(currMB, pl, 8, 0); + else + icopy8x8(currMB, pl, 8, 0); + + if (currMB->cbp & 0x04) + itrans8x8(currMB, pl, 0, 8); + else + icopy8x8(currMB, pl, 0, 8); + + if (currMB->cbp & 0x08) + itrans8x8(currMB, pl, 8, 8); + else + icopy8x8(currMB, pl, 8, 8); + + copy_image_data_16x16(&curr_img[currMB->pix_y], currMB->p_Slice->mb_rec[pl], currMB->pix_x, 0); +} + +void iTransform(Macroblock *currMB, ColorPlane pl, int smb) +{ + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + StorablePicture *dec_picture = currSlice->dec_picture; + imgpel **curr_img; + int uv = pl-1; + + if ((currMB->cbp & 15) != 0 || smb) + { + if(currMB->luma_transform_size_8x8_flag == 0) // 4x4 inverse transform + { + iMBtrans4x4(currMB, pl, smb); + } + else // 8x8 inverse transform + { + iMBtrans8x8(currMB, pl); + } + } + else + { + curr_img = pl ? dec_picture->imgUV[uv] : dec_picture->imgY; + copy_image_data_16x16(&curr_img[currMB->pix_y], currSlice->mb_pred[pl], currMB->pix_x, 0); + } + if(smb) + currSlice->is_reset_coeff = FALSE; + + if ((dec_picture->chroma_format_idc != YUV400) && (dec_picture->chroma_format_idc != YUV444)) + { + imgpel **curUV; + int b8; + int ioff, joff; + imgpel **mb_rec; + + for(uv = PLANE_U; uv <= PLANE_V; ++uv) + { + // =============== 4x4 itrans ================ + // ------------------------------------------- + curUV = &dec_picture->imgUV[uv - 1][currMB->pix_c_y]; + mb_rec = currSlice->mb_rec[uv]; + + if (!smb && (currMB->cbp >> 4)) + { + if (currMB->is_lossless == FALSE) + { + const unsigned char *x_pos, *y_pos; + + for (b8 = 0; b8 < (p_Vid->num_uv_blocks); ++b8) + { + x_pos = subblk_offset_x[1][b8]; + y_pos = subblk_offset_y[1][b8]; + + itrans4x4(currMB, uv, *x_pos++, *y_pos++); + itrans4x4(currMB, uv, *x_pos++, *y_pos++); + itrans4x4(currMB, uv, *x_pos++, *y_pos++); + itrans4x4(currMB, uv, *x_pos , *y_pos ); + } + sample_reconstruct (mb_rec, currSlice->mb_pred[uv], currSlice->mb_rres[uv], 0, 0, + p_Vid->mb_size[1][0], p_Vid->mb_size[1][1], currMB->p_Vid->max_pel_value_comp[uv], DQ_BITS); + } + else + { + const unsigned char *x_pos, *y_pos; + for (b8 = 0; b8 < (p_Vid->num_uv_blocks); ++b8) + { + x_pos = subblk_offset_x[1][b8]; + y_pos = subblk_offset_y[1][b8]; + + itrans4x4_ls(currMB, uv, *x_pos++, *y_pos++); + itrans4x4_ls(currMB, uv, *x_pos++, *y_pos++); + itrans4x4_ls(currMB, uv, *x_pos++, *y_pos++); + itrans4x4_ls(currMB, uv, *x_pos , *y_pos ); + } + } + copy_image_data(curUV, mb_rec, currMB->pix_c_x, 0, p_Vid->mb_size[1][0], p_Vid->mb_size[1][1]); + + currSlice->is_reset_coeff_cr = FALSE; + } + else if (smb) + { + currMB->itrans_4x4 = (currMB->is_lossless == FALSE) ? itrans4x4 : itrans4x4_ls; + itrans_sp_cr(currMB, uv - 1); + + for (joff = 0; joff < p_Vid->mb_cr_size_y; joff += BLOCK_SIZE) + { + for(ioff = 0; ioff < p_Vid->mb_cr_size_x ;ioff += BLOCK_SIZE) + { + currMB->itrans_4x4(currMB, uv, ioff, joff); + } + } + + copy_image_data(curUV, mb_rec, currMB->pix_c_x, 0, p_Vid->mb_size[1][0], p_Vid->mb_size[1][1]); + currSlice->is_reset_coeff_cr = FALSE; + } + else + { + copy_image_data(curUV, currSlice->mb_pred[uv], currMB->pix_c_x, 0, p_Vid->mb_size[1][0], p_Vid->mb_size[1][1]); + } + } + } +} + +/*! + ************************************************************************************* + * \brief + * Copy ImgPel Data from one structure to another (16x16) + ************************************************************************************* + */ +void copy_image_data_16x16(imgpel **imgBuf1, imgpel **imgBuf2, int off1, int off2) +{ + int j; + for(j = 0; j < MB_BLOCK_SIZE; j += 4) + { + memcpy((*imgBuf1++ + off1), (*imgBuf2++ + off2), MB_BLOCK_SIZE * sizeof (imgpel)); + memcpy((*imgBuf1++ + off1), (*imgBuf2++ + off2), MB_BLOCK_SIZE * sizeof (imgpel)); + memcpy((*imgBuf1++ + off1), (*imgBuf2++ + off2), MB_BLOCK_SIZE * sizeof (imgpel)); + memcpy((*imgBuf1++ + off1), (*imgBuf2++ + off2), MB_BLOCK_SIZE * sizeof (imgpel)); + } +} + +/*! + ************************************************************************************* + * \brief + * Copy ImgPel Data from one structure to another (8x8) + ************************************************************************************* + */ +void copy_image_data_8x8(imgpel **imgBuf1, imgpel **imgBuf2, int off1, int off2) +{ + int j; + for(j = 0; j < BLOCK_SIZE_8x8; j+=4) + { + memcpy((*imgBuf1++ + off1), (*imgBuf2++ + off2), BLOCK_SIZE_8x8 * sizeof (imgpel)); + memcpy((*imgBuf1++ + off1), (*imgBuf2++ + off2), BLOCK_SIZE_8x8 * sizeof (imgpel)); + memcpy((*imgBuf1++ + off1), (*imgBuf2++ + off2), BLOCK_SIZE_8x8 * sizeof (imgpel)); + memcpy((*imgBuf1++ + off1), (*imgBuf2++ + off2), BLOCK_SIZE_8x8 * sizeof (imgpel)); + } +} + + +/*! + ************************************************************************************* + * \brief + * Copy ImgPel Data from one structure to another (4x4) + ************************************************************************************* + */ +void copy_image_data_4x4(imgpel **imgBuf1, imgpel **imgBuf2, int off1, int off2) +{ + memcpy((*imgBuf1++ + off1), (*imgBuf2++ + off2), BLOCK_SIZE * sizeof (imgpel)); + memcpy((*imgBuf1++ + off1), (*imgBuf2++ + off2), BLOCK_SIZE * sizeof (imgpel)); + memcpy((*imgBuf1++ + off1), (*imgBuf2++ + off2), BLOCK_SIZE * sizeof (imgpel)); + memcpy((*imgBuf1 + off1), (*imgBuf2 + off2), BLOCK_SIZE * sizeof (imgpel)); +} + +int CheckVertMV(Macroblock *currMB, int vec1_y, int block_size_y) +{ + VideoParameters *p_Vid = currMB->p_Vid; + StorablePicture *dec_picture = currMB->p_Slice->dec_picture; + int y_pos = vec1_y>>2; + int maxold_y = (currMB->mb_field) ? (dec_picture->size_y >> 1) - 1 : dec_picture->size_y_m1; + + if(y_pos < (-p_Vid->iLumaPadY + 2) || y_pos > (maxold_y + p_Vid->iLumaPadY - block_size_y - 2)) + return 1; + else + return 0; +} + + +/*! + ************************************************************************************* + * \brief + * Copy ImgPel Data from one structure to another (8x8) + ************************************************************************************* + */ +void copy_image_data(imgpel **imgBuf1, imgpel **imgBuf2, int off1, int off2, int width, int height) +{ + int j; + for(j = 0; j < height; ++j) + { + memcpy((*imgBuf1++ + off1), (*imgBuf2++ + off2), width * sizeof (imgpel)); + } +} diff --git a/ldecod/src/cabac.c b/ldecod/src/cabac.c new file mode 100644 index 0000000..96b61b8 --- /dev/null +++ b/ldecod/src/cabac.c @@ -0,0 +1,2448 @@ +/*! + ************************************************************************************* + * \file cabac.c + * + * \brief + * CABAC entropy coding routines + * + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * - Detlev Marpe + ************************************************************************************** + */ + +#include "global.h" +#include "cabac.h" +#include "memalloc.h" +#include "elements.h" +#include "image.h" +#include "biaridecod.h" +#include "mb_access.h" +#include "vlc.h" + +#if TRACE +int symbolCount = 0; +#endif + +static const short maxpos [] = {15, 14, 63, 31, 31, 15, 3, 14, 7, 15, 15, 14, 63, 31, 31, 15, 15, 14, 63, 31, 31, 15}; +static const short c1isdc [] = { 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1}; +static const short type2ctx_bcbp[] = { 0, 1, 2, 3, 3, 4, 5, 6, 5, 5, 10, 11, 12, 13, 13, 14, 16, 17, 18, 19, 19, 20}; +static const short type2ctx_map [] = { 0, 1, 2, 3, 4, 5, 6, 7, 6, 6, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21}; // 8 +static const short type2ctx_last[] = { 0, 1, 2, 3, 4, 5, 6, 7, 6, 6, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21}; // 8 +static const short type2ctx_one [] = { 0, 1, 2, 3, 3, 4, 5, 6, 5, 5, 10, 11, 12, 13, 13, 14, 16, 17, 18, 19, 19, 20}; // 7 +static const short type2ctx_abs [] = { 0, 1, 2, 3, 3, 4, 5, 6, 5, 5, 10, 11, 12, 13, 13, 14, 16, 17, 18, 19, 19, 20}; // 7 +static const short max_c2 [] = { 4, 4, 4, 4, 4, 4, 3, 4, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4}; // 9 + +/*********************************************************************** + * L O C A L L Y D E F I N E D F U N C T I O N P R O T O T Y P E S + *********************************************************************** + */ +static unsigned int unary_bin_decode ( DecodingEnvironmentPtr dep_dp, BiContextTypePtr ctx, int ctx_offset); +static unsigned int unary_bin_max_decode ( DecodingEnvironmentPtr dep_dp, BiContextTypePtr ctx, int ctx_offset, unsigned int max_symbol); +static unsigned int unary_exp_golomb_level_decode( DecodingEnvironmentPtr dep_dp, BiContextTypePtr ctx); +static unsigned int unary_exp_golomb_mv_decode ( DecodingEnvironmentPtr dep_dp, BiContextTypePtr ctx, unsigned int max_bin); + +void CheckAvailabilityOfNeighborsCABAC(Macroblock *currMB) +{ + VideoParameters *p_Vid = currMB->p_Vid; + PixelPos up, left; + int *mb_size = p_Vid->mb_size[IS_LUMA]; + + p_Vid->getNeighbour(currMB, -1, 0, mb_size, &left); + p_Vid->getNeighbour(currMB, 0, -1, mb_size, &up); + + if (up.available) + currMB->mb_up = &currMB->p_Slice->mb_data[up.mb_addr]; //&p_Vid->mb_data[up.mb_addr]; + else + currMB->mb_up = NULL; + + if (left.available) + currMB->mb_left = &currMB->p_Slice->mb_data[left.mb_addr]; //&p_Vid->mb_data[left.mb_addr]; + else + currMB->mb_left = NULL; +} + +void cabac_new_slice(Slice *currSlice) +{ + currSlice->last_dquant = 0; +} + +/*! + ************************************************************************ + * \brief + * Allocation of contexts models for the motion info + * used for arithmetic decoding + * + ************************************************************************ + */ +MotionInfoContexts* create_contexts_MotionInfo(void) +{ + MotionInfoContexts *deco_ctx; + + deco_ctx = (MotionInfoContexts*) calloc(1, sizeof(MotionInfoContexts) ); + if( deco_ctx == NULL ) + no_mem_exit("create_contexts_MotionInfo: deco_ctx"); + + return deco_ctx; +} + + +/*! + ************************************************************************ + * \brief + * Allocates of contexts models for the texture info + * used for arithmetic decoding + ************************************************************************ + */ +TextureInfoContexts* create_contexts_TextureInfo(void) +{ + TextureInfoContexts *deco_ctx; + + deco_ctx = (TextureInfoContexts*) calloc(1, sizeof(TextureInfoContexts) ); + if( deco_ctx == NULL ) + no_mem_exit("create_contexts_TextureInfo: deco_ctx"); + + return deco_ctx; +} + + +/*! + ************************************************************************ + * \brief + * Frees the memory of the contexts models + * used for arithmetic decoding of the motion info. + ************************************************************************ + */ +void delete_contexts_MotionInfo(MotionInfoContexts *deco_ctx) +{ + if( deco_ctx == NULL ) + return; + + free( deco_ctx ); +} + + +/*! + ************************************************************************ + * \brief + * Frees the memory of the contexts models + * used for arithmetic decoding of the texture info. + ************************************************************************ + */ +void delete_contexts_TextureInfo(TextureInfoContexts *deco_ctx) +{ + if( deco_ctx == NULL ) + return; + + free( deco_ctx ); +} + +void readFieldModeInfo_CABAC(Macroblock *currMB, + SyntaxElement *se, + DecodingEnvironmentPtr dep_dp) +{ + Slice *currSlice = currMB->p_Slice; + //VideoParameters *p_Vid = currMB->p_Vid; + MotionInfoContexts *ctx = currSlice->mot_ctx; + int a = currMB->mbAvailA ? currSlice->mb_data[currMB->mbAddrA].mb_field : 0; + int b = currMB->mbAvailB ? currSlice->mb_data[currMB->mbAddrB].mb_field : 0; + int act_ctx = a + b; + + se->value1 = biari_decode_symbol (dep_dp, &ctx->mb_aff_contexts[act_ctx]); + +#if TRACE + fprintf(p_Dec->p_trace, "@%-6d %-63s (%3d)\n",symbolCount++, se->tracestring, se->value1); + fflush(p_Dec->p_trace); +#endif +} + + +int check_next_mb_and_get_field_mode_CABAC_p_slice( Slice *currSlice, + SyntaxElement *se, + DataPartition *act_dp) +{ + VideoParameters *p_Vid = currSlice->p_Vid; + BiContextTypePtr mb_type_ctx_copy[3]; + BiContextTypePtr mb_aff_ctx_copy; + DecodingEnvironmentPtr dep_dp_copy; + MotionInfoContexts *mot_ctx = currSlice->mot_ctx; + + int length; + DecodingEnvironmentPtr dep_dp = &(act_dp->de_cabac); + + int skip = 0; + int field = 0; + int i; + + Macroblock *currMB; + + //get next MB + ++currSlice->current_mb_nr; // ++p_Vid->current_mb_nr; + + currMB = &currSlice->mb_data[currSlice->current_mb_nr]; + currMB->p_Vid = p_Vid; + currMB->p_Slice = currSlice; + currMB->slice_nr = currSlice->current_slice_nr; + currMB->mb_field = currSlice->mb_data[currSlice->current_mb_nr-1].mb_field; + currMB->mbAddrX = currSlice->current_mb_nr; + currMB->list_offset = ((currSlice->mb_aff_frame_flag)&&(currMB->mb_field))? (currMB->mbAddrX&0x01) ? 4 : 2 : 0; + + CheckAvailabilityOfNeighborsMBAFF(currMB); + CheckAvailabilityOfNeighborsCABAC(currMB); + + //create + dep_dp_copy = (DecodingEnvironmentPtr) calloc(1, sizeof(DecodingEnvironment) ); + for (i=0;i<3;++i) + mb_type_ctx_copy[i] = (BiContextTypePtr) calloc(NUM_MB_TYPE_CTX, sizeof(BiContextType) ); + mb_aff_ctx_copy = (BiContextTypePtr) calloc(NUM_MB_AFF_CTX, sizeof(BiContextType) ); + + //copy + memcpy(dep_dp_copy,dep_dp,sizeof(DecodingEnvironment)); + length = *(dep_dp_copy->Dcodestrm_len) = *(dep_dp->Dcodestrm_len); + for (i=0;i<3;++i) + memcpy(mb_type_ctx_copy[i], mot_ctx->mb_type_contexts[i],NUM_MB_TYPE_CTX*sizeof(BiContextType) ); + memcpy(mb_aff_ctx_copy, mot_ctx->mb_aff_contexts,NUM_MB_AFF_CTX*sizeof(BiContextType) ); + + //check_next_mb +#if TRACE + strncpy(se->tracestring, "mb_skip_flag (of following bottom MB)", TRACESTRING_SIZE); +#endif + currSlice->last_dquant = 0; + read_skip_flag_CABAC_p_slice(currMB, se, dep_dp); + + skip = (se->value1==0); + + if (!skip) + { +#if TRACE + strncpy(se->tracestring, "mb_field_decoding_flag (of following bottom MB)", TRACESTRING_SIZE); +#endif + readFieldModeInfo_CABAC( currMB, se,dep_dp); + field = se->value1; + currSlice->mb_data[currSlice->current_mb_nr-1].mb_field = field; + } + + //reset + currSlice->current_mb_nr--; + + memcpy(dep_dp,dep_dp_copy,sizeof(DecodingEnvironment)); + *(dep_dp->Dcodestrm_len) = length; + for (i=0;i<3;++i) + memcpy(mot_ctx->mb_type_contexts[i],mb_type_ctx_copy[i], NUM_MB_TYPE_CTX*sizeof(BiContextType) ); + memcpy( mot_ctx->mb_aff_contexts,mb_aff_ctx_copy,NUM_MB_AFF_CTX*sizeof(BiContextType) ); + + CheckAvailabilityOfNeighborsCABAC(currMB); + + //delete + free(dep_dp_copy); + for (i=0;i<3;++i) + free(mb_type_ctx_copy[i]); + free(mb_aff_ctx_copy); + + return skip; +} + +int check_next_mb_and_get_field_mode_CABAC_b_slice( Slice *currSlice, + SyntaxElement *se, + DataPartition *act_dp) +{ + VideoParameters *p_Vid = currSlice->p_Vid; + BiContextTypePtr mb_type_ctx_copy[3]; + BiContextTypePtr mb_aff_ctx_copy; + DecodingEnvironmentPtr dep_dp_copy; + + int length; + DecodingEnvironmentPtr dep_dp = &(act_dp->de_cabac); + MotionInfoContexts *mot_ctx = currSlice->mot_ctx; + + int skip = 0; + int field = 0; + int i; + + Macroblock *currMB; + + //get next MB + ++currSlice->current_mb_nr; // ++p_Vid->current_mb_nr; + + currMB = &currSlice->mb_data[currSlice->current_mb_nr]; + currMB->p_Vid = p_Vid; + currMB->p_Slice = currSlice; + currMB->slice_nr = currSlice->current_slice_nr; + currMB->mb_field = currSlice->mb_data[currSlice->current_mb_nr-1].mb_field; + currMB->mbAddrX = currSlice->current_mb_nr; + currMB->list_offset = ((currSlice->mb_aff_frame_flag)&&(currMB->mb_field))? (currMB->mbAddrX & 0x01) ? 4 : 2 : 0; + + CheckAvailabilityOfNeighborsMBAFF(currMB); + CheckAvailabilityOfNeighborsCABAC(currMB); + + //create + dep_dp_copy = (DecodingEnvironmentPtr) calloc(1, sizeof(DecodingEnvironment) ); + for (i=0;i<3;++i) + mb_type_ctx_copy[i] = (BiContextTypePtr) calloc(NUM_MB_TYPE_CTX, sizeof(BiContextType) ); + mb_aff_ctx_copy = (BiContextTypePtr) calloc(NUM_MB_AFF_CTX, sizeof(BiContextType) ); + + //copy + memcpy(dep_dp_copy,dep_dp,sizeof(DecodingEnvironment)); + length = *(dep_dp_copy->Dcodestrm_len) = *(dep_dp->Dcodestrm_len); + + for (i=0;i<3;++i) + memcpy(mb_type_ctx_copy[i], mot_ctx->mb_type_contexts[i],NUM_MB_TYPE_CTX*sizeof(BiContextType) ); + + memcpy(mb_aff_ctx_copy, mot_ctx->mb_aff_contexts,NUM_MB_AFF_CTX*sizeof(BiContextType) ); + + //check_next_mb +#if TRACE + strncpy(se->tracestring, "mb_skip_flag (of following bottom MB)", TRACESTRING_SIZE); +#endif + currSlice->last_dquant = 0; + read_skip_flag_CABAC_b_slice(currMB, se, dep_dp); + + skip = (se->value1==0 && se->value2==0); + if (!skip) + { +#if TRACE + strncpy(se->tracestring, "mb_field_decoding_flag (of following bottom MB)", TRACESTRING_SIZE); +#endif + readFieldModeInfo_CABAC( currMB, se,dep_dp); + field = se->value1; + currSlice->mb_data[currSlice->current_mb_nr-1].mb_field = field; + } + + //reset + currSlice->current_mb_nr--; + + memcpy(dep_dp,dep_dp_copy,sizeof(DecodingEnvironment)); + *(dep_dp->Dcodestrm_len) = length; + + for (i=0;i<3;++i) + memcpy(mot_ctx->mb_type_contexts[i],mb_type_ctx_copy[i], NUM_MB_TYPE_CTX * sizeof(BiContextType) ); + + memcpy( mot_ctx->mb_aff_contexts, mb_aff_ctx_copy, NUM_MB_AFF_CTX * sizeof(BiContextType) ); + + CheckAvailabilityOfNeighborsCABAC(currMB); + + //delete + free(dep_dp_copy); + for (i=0;i<3;++i) + free(mb_type_ctx_copy[i]); + free(mb_aff_ctx_copy); + + return skip; +} + +/*! + ************************************************************************ + * \brief + * This function is used to arithmetically decode the motion + * vector data of a B-frame MB. + ************************************************************************ + */ +void read_MVD_CABAC( Macroblock *currMB, + SyntaxElement *se, + DecodingEnvironmentPtr dep_dp) +{ + int *mb_size = currMB->p_Vid->mb_size[IS_LUMA]; + Slice *currSlice = currMB->p_Slice; + MotionInfoContexts *ctx = currSlice->mot_ctx; + int i = currMB->subblock_x; + int j = currMB->subblock_y; + int a = 0; + //int act_ctx; + int act_sym; + int list_idx = se->value2 & 0x01; + int k = (se->value2 >> 1); // MVD component + + PixelPos block_a, block_b; + + get4x4NeighbourBase(currMB, i - 1, j , mb_size, &block_a); + get4x4NeighbourBase(currMB, i , j - 1, mb_size, &block_b); + if (block_a.available) + { + a = iabs(currSlice->mb_data[block_a.mb_addr].mvd[list_idx][block_a.y][block_a.x][k]); + } + if (block_b.available) + { + a += iabs(currSlice->mb_data[block_b.mb_addr].mvd[list_idx][block_b.y][block_b.x][k]); + } + + //a += b; + + if (a < 3) + a = 5 * k; + else if (a > 32) + a = 5 * k + 3; + else + a = 5 * k + 2; + + se->context = a; + + act_sym = biari_decode_symbol(dep_dp, ctx->mv_res_contexts[0] + a ); + + if (act_sym != 0) + { + a = 5 * k; + act_sym = unary_exp_golomb_mv_decode(dep_dp, ctx->mv_res_contexts[1] + a, 3) + 1; + + if(biari_decode_symbol_eq_prob(dep_dp)) + act_sym = -act_sym; + } + se->value1 = act_sym; + +#if TRACE + fprintf(p_Dec->p_trace, "@%-6d %-63s (%3d)\n",symbolCount++, se->tracestring, se->value1); + fflush(p_Dec->p_trace); +#endif +} + + +/*! + ************************************************************************ + * \brief + * This function is used to arithmetically decode the motion + * vector data of a B-frame MB. + ************************************************************************ + */ +void read_mvd_CABAC_mbaff( Macroblock *currMB, + SyntaxElement *se, + DecodingEnvironmentPtr dep_dp) +{ + VideoParameters *p_Vid = currMB->p_Vid; + Slice *currSlice = currMB->p_Slice; + MotionInfoContexts *ctx = currSlice->mot_ctx; + int i = currMB->subblock_x; + int j = currMB->subblock_y; + int a = 0, b = 0; + int act_ctx; + int act_sym; + int list_idx = se->value2 & 0x01; + int k = (se->value2 >> 1); // MVD component + + PixelPos block_a, block_b; + + get4x4NeighbourBase(currMB, i - 1, j , p_Vid->mb_size[IS_LUMA], &block_a); + if (block_a.available) + { + a = iabs(currSlice->mb_data[block_a.mb_addr].mvd[list_idx][block_a.y][block_a.x][k]); + if (currSlice->mb_aff_frame_flag && (k==1)) + { + if ((currMB->mb_field==0) && (currSlice->mb_data[block_a.mb_addr].mb_field==1)) + a *= 2; + else if ((currMB->mb_field==1) && (currSlice->mb_data[block_a.mb_addr].mb_field==0)) + a /= 2; + } + } + + get4x4NeighbourBase(currMB, i , j - 1, p_Vid->mb_size[IS_LUMA], &block_b); + if (block_b.available) + { + b = iabs(currSlice->mb_data[block_b.mb_addr].mvd[list_idx][block_b.y][block_b.x][k]); + if (currSlice->mb_aff_frame_flag && (k==1)) + { + if ((currMB->mb_field==0) && (currSlice->mb_data[block_b.mb_addr].mb_field==1)) + b *= 2; + else if ((currMB->mb_field==1) && (currSlice->mb_data[block_b.mb_addr].mb_field==0)) + b /= 2; + } + } + a += b; + + if (a < 3) + act_ctx = 5 * k; + else if (a > 32) + act_ctx = 5 * k + 3; + else + act_ctx = 5 * k + 2; + + se->context = act_ctx; + + act_sym = biari_decode_symbol(dep_dp,&ctx->mv_res_contexts[0][act_ctx] ); + + if (act_sym != 0) + { + act_ctx = 5 * k; + act_sym = unary_exp_golomb_mv_decode(dep_dp, ctx->mv_res_contexts[1] + act_ctx, 3) + 1; + + if(biari_decode_symbol_eq_prob(dep_dp)) + act_sym = -act_sym; + } + se->value1 = act_sym; + +#if TRACE + fprintf(p_Dec->p_trace, "@%-6d %-63s (%3d)\n",symbolCount++, se->tracestring, se->value1); + fflush(p_Dec->p_trace); +#endif +} + + +/*! + ************************************************************************ + * \brief + * This function is used to arithmetically decode the 8x8 block type. + ************************************************************************ + */ +void readB8_typeInfo_CABAC_p_slice (Macroblock *currMB, + SyntaxElement *se, + DecodingEnvironmentPtr dep_dp) +{ + Slice *currSlice = currMB->p_Slice; + int act_sym = 0; + + MotionInfoContexts *ctx = currSlice->mot_ctx; + BiContextType *b8_type_contexts = &ctx->b8_type_contexts[0][1]; + + if (biari_decode_symbol (dep_dp, b8_type_contexts++)) + act_sym = 0; + else + { + if (biari_decode_symbol (dep_dp, ++b8_type_contexts)) + { + act_sym = (biari_decode_symbol (dep_dp, ++b8_type_contexts))? 2: 3; + } + else + { + act_sym = 1; + } + } + + se->value1 = act_sym; + +#if TRACE + fprintf(p_Dec->p_trace, "@%-6d %-63s (%3d)\n",symbolCount++, se->tracestring, se->value1); + fflush(p_Dec->p_trace); +#endif +} + + +/*! + ************************************************************************ + * \brief + * This function is used to arithmetically decode the 8x8 block type. + ************************************************************************ + */ +void readB8_typeInfo_CABAC_b_slice (Macroblock *currMB, + SyntaxElement *se, + DecodingEnvironmentPtr dep_dp) +{ + Slice *currSlice = currMB->p_Slice; + int act_sym = 0; + + MotionInfoContexts *ctx = currSlice->mot_ctx; + BiContextType *b8_type_contexts = ctx->b8_type_contexts[1]; + + if (biari_decode_symbol (dep_dp, b8_type_contexts++)) + { + if (biari_decode_symbol (dep_dp, b8_type_contexts++)) + { + if (biari_decode_symbol (dep_dp, b8_type_contexts++)) + { + if (biari_decode_symbol (dep_dp, b8_type_contexts)) + { + act_sym = 10; + if (biari_decode_symbol (dep_dp, b8_type_contexts)) + act_sym++; + } + else + { + act_sym = 6; + if (biari_decode_symbol (dep_dp, b8_type_contexts)) + act_sym += 2; + if (biari_decode_symbol (dep_dp, b8_type_contexts)) + act_sym++; + } + } + else + { + act_sym = 2; + if (biari_decode_symbol (dep_dp, b8_type_contexts)) + act_sym += 2; + if (biari_decode_symbol (dep_dp, b8_type_contexts)) + act_sym ++; + } + } + else + { + act_sym = (biari_decode_symbol (dep_dp, ++b8_type_contexts)) ? 1: 0; + } + ++act_sym; + } + else + { + act_sym = 0; + } + + se->value1 = act_sym; + +#if TRACE + fprintf(p_Dec->p_trace, "@%-6d %-63s (%3d)\n",symbolCount++, se->tracestring, se->value1); + fflush(p_Dec->p_trace); +#endif +} + +/*! + ************************************************************************ + * \brief + * This function is used to arithmetically decode the macroblock + * type info of a given MB. + ************************************************************************ + */ +void read_skip_flag_CABAC_p_slice( Macroblock *currMB, + SyntaxElement *se, + DecodingEnvironmentPtr dep_dp) +{ + int a = (currMB->mb_left != NULL) ? (currMB->mb_left->skip_flag == 0) : 0; + int b = (currMB->mb_up != NULL) ? (currMB->mb_up ->skip_flag == 0) : 0; + BiContextType *mb_type_contexts = &currMB->p_Slice->mot_ctx->mb_type_contexts[1][a + b]; + + se->value1 = (biari_decode_symbol(dep_dp, mb_type_contexts) != 1); + +#if TRACE + fprintf(p_Dec->p_trace, "@%-6d %-63s (%3d)\n",symbolCount++, se->tracestring, se->value1); + fflush(p_Dec->p_trace); +#endif + if (!se->value1) + { + currMB->p_Slice->last_dquant = 0; + } +} + +/*! + ************************************************************************ + * \brief + * This function is used to arithmetically decode the macroblock + * type info of a given MB. + ************************************************************************ + */ +void read_skip_flag_CABAC_b_slice( Macroblock *currMB, + SyntaxElement *se, + DecodingEnvironmentPtr dep_dp) +{ + int a = (currMB->mb_left != NULL) ? (currMB->mb_left->skip_flag == 0) : 0; + int b = (currMB->mb_up != NULL) ? (currMB->mb_up ->skip_flag == 0) : 0; + BiContextType *mb_type_contexts = &currMB->p_Slice->mot_ctx->mb_type_contexts[2][7 + a + b]; + + se->value1 = se->value2 = (biari_decode_symbol (dep_dp, mb_type_contexts) != 1); + +#if TRACE + fprintf(p_Dec->p_trace, "@%-6d %-63s (%3d)\n", symbolCount++, se->tracestring, se->value1); + fflush(p_Dec->p_trace); +#endif + if (!se->value1) + { + currMB->p_Slice->last_dquant = 0; + } +} + +/*! +*************************************************************************** +* \brief +* This function is used to arithmetically decode the macroblock +* intra_pred_size flag info of a given MB. +*************************************************************************** +*/ + +void readMB_transform_size_flag_CABAC( Macroblock *currMB, + SyntaxElement *se, + DecodingEnvironmentPtr dep_dp) +{ + Slice *currSlice = currMB->p_Slice; + TextureInfoContexts*ctx = currSlice->tex_ctx; + + int b = (currMB->mb_up == NULL) ? 0 : currMB->mb_up->luma_transform_size_8x8_flag; + int a = (currMB->mb_left == NULL) ? 0 : currMB->mb_left->luma_transform_size_8x8_flag; + + int act_sym = biari_decode_symbol(dep_dp, ctx->transform_size_contexts + a + b ); + + se->value1 = act_sym; + +#if TRACE + fprintf(p_Dec->p_trace, "@%-6d %-63s (%3d)\n",symbolCount++, se->tracestring, se->value1); + fflush(p_Dec->p_trace); +#endif + +} + +/*! + ************************************************************************ + * \brief + * This function is used to arithmetically decode the macroblock + * type info of a given MB. + ************************************************************************ + */ +void readMB_typeInfo_CABAC_i_slice(Macroblock *currMB, + SyntaxElement *se, + DecodingEnvironmentPtr dep_dp) +{ + Slice *currSlice = currMB->p_Slice; + MotionInfoContexts *ctx = currSlice->mot_ctx; + + int a = 0, b = 0; + int act_ctx; + int act_sym; + int mode_sym; + int curr_mb_type = 0; + + if(currSlice->slice_type == I_SLICE) // INTRA-frame + { + if (currMB->mb_up != NULL) + b = (((currMB->mb_up)->mb_type != I4MB && currMB->mb_up->mb_type != I8MB) ? 1 : 0 ); + + if (currMB->mb_left != NULL) + a = (((currMB->mb_left)->mb_type != I4MB && currMB->mb_left->mb_type != I8MB) ? 1 : 0 ); + + act_ctx = a + b; + act_sym = biari_decode_symbol(dep_dp, ctx->mb_type_contexts[0] + act_ctx); + se->context = act_ctx; // store context + + if (act_sym==0) // 4x4 Intra + { + curr_mb_type = act_sym; + } + else // 16x16 Intra + { + mode_sym = biari_decode_final(dep_dp); + if(mode_sym == 1) + { + curr_mb_type = 25; + } + else + { + act_sym = 1; + act_ctx = 4; + mode_sym = biari_decode_symbol(dep_dp, ctx->mb_type_contexts[0] + act_ctx ); // decoding of AC/no AC + act_sym += mode_sym*12; + act_ctx = 5; + // decoding of cbp: 0,1,2 + mode_sym = biari_decode_symbol(dep_dp, ctx->mb_type_contexts[0] + act_ctx ); + if (mode_sym!=0) + { + act_ctx=6; + mode_sym = biari_decode_symbol(dep_dp, ctx->mb_type_contexts[0] + act_ctx ); + act_sym+=4; + if (mode_sym!=0) + act_sym+=4; + } + // decoding of I pred-mode: 0,1,2,3 + act_ctx = 7; + mode_sym = biari_decode_symbol(dep_dp, ctx->mb_type_contexts[0] + act_ctx ); + act_sym += mode_sym*2; + act_ctx = 8; + mode_sym = biari_decode_symbol(dep_dp, ctx->mb_type_contexts[0] + act_ctx ); + act_sym += mode_sym; + curr_mb_type = act_sym; + } + } + } + else if(currSlice->slice_type == SI_SLICE) // SI-frame + { + // special ctx's for SI4MB + if (currMB->mb_up != NULL) + b = (( (currMB->mb_up)->mb_type != SI4MB) ? 1 : 0 ); + + if (currMB->mb_left != NULL) + a = (( (currMB->mb_left)->mb_type != SI4MB) ? 1 : 0 ); + + act_ctx = a + b; + act_sym = biari_decode_symbol(dep_dp, ctx->mb_type_contexts[1] + act_ctx); + se->context = act_ctx; // store context + + if (act_sym==0) // SI 4x4 Intra + { + curr_mb_type = 0; + } + else // analog INTRA_IMG + { + if (currMB->mb_up != NULL) + b = (( (currMB->mb_up)->mb_type != I4MB) ? 1 : 0 ); + + if (currMB->mb_left != NULL) + a = (( (currMB->mb_left)->mb_type != I4MB) ? 1 : 0 ); + + act_ctx = a + b; + act_sym = biari_decode_symbol(dep_dp, ctx->mb_type_contexts[0] + act_ctx); + se->context = act_ctx; // store context + + if (act_sym==0) // 4x4 Intra + { + curr_mb_type = 1; + } + else // 16x16 Intra + { + mode_sym = biari_decode_final(dep_dp); + if( mode_sym==1 ) + { + curr_mb_type = 26; + } + else + { + act_sym = 2; + act_ctx = 4; + mode_sym = biari_decode_symbol(dep_dp, ctx->mb_type_contexts[0] + act_ctx ); // decoding of AC/no AC + act_sym += mode_sym*12; + act_ctx = 5; + // decoding of cbp: 0,1,2 + mode_sym = biari_decode_symbol(dep_dp, ctx->mb_type_contexts[0] + act_ctx ); + if (mode_sym!=0) + { + act_ctx=6; + mode_sym = biari_decode_symbol(dep_dp, ctx->mb_type_contexts[0] + act_ctx ); + act_sym+=4; + if (mode_sym!=0) + act_sym+=4; + } + // decoding of I pred-mode: 0,1,2,3 + act_ctx = 7; + mode_sym = biari_decode_symbol(dep_dp, ctx->mb_type_contexts[0] + act_ctx ); + act_sym += mode_sym*2; + act_ctx = 8; + mode_sym = biari_decode_symbol(dep_dp, ctx->mb_type_contexts[0] + act_ctx ); + act_sym += mode_sym; + curr_mb_type = act_sym; + } + } + } + } + + se->value1 = curr_mb_type; + +#if TRACE + fprintf(p_Dec->p_trace, "@%-6d %-63s (%3d)\n",symbolCount++, se->tracestring, se->value1); + fflush(p_Dec->p_trace); +#endif +} + + +/*! + ************************************************************************ + * \brief + * This function is used to arithmetically decode the macroblock + * type info of a given MB. + ************************************************************************ + */ +void readMB_typeInfo_CABAC_p_slice(Macroblock *currMB, + SyntaxElement *se, + DecodingEnvironmentPtr dep_dp) +{ + Slice *currSlice = currMB->p_Slice; + MotionInfoContexts *ctx = currSlice->mot_ctx; + + int act_ctx; + int act_sym; + int mode_sym; + int curr_mb_type; + BiContextType *mb_type_contexts = ctx->mb_type_contexts[1]; + + if (biari_decode_symbol(dep_dp, &mb_type_contexts[4] )) + { + if (biari_decode_symbol(dep_dp, &mb_type_contexts[7] )) + act_sym = 7; + else + act_sym = 6; + } + else + { + if (biari_decode_symbol(dep_dp, &mb_type_contexts[5] )) + { + if (biari_decode_symbol(dep_dp, &mb_type_contexts[7] )) + act_sym = 2; + else + act_sym = 3; + } + else + { + if (biari_decode_symbol(dep_dp, &mb_type_contexts[6] )) + act_sym = 4; + else + act_sym = 1; + } + } + + if (act_sym <= 6) + { + curr_mb_type = act_sym; + } + else // additional info for 16x16 Intra-mode + { + mode_sym = biari_decode_final(dep_dp); + if( mode_sym==1 ) + { + curr_mb_type = 31; + } + else + { + act_ctx = 8; + mode_sym = biari_decode_symbol(dep_dp, mb_type_contexts + act_ctx ); // decoding of AC/no AC + act_sym += mode_sym*12; + + // decoding of cbp: 0,1,2 + act_ctx = 9; + mode_sym = biari_decode_symbol(dep_dp, mb_type_contexts + act_ctx ); + if (mode_sym != 0) + { + act_sym+=4; + mode_sym = biari_decode_symbol(dep_dp, mb_type_contexts + act_ctx ); + if (mode_sym != 0) + act_sym+=4; + } + + // decoding of I pred-mode: 0,1,2,3 + act_ctx = 10; + mode_sym = biari_decode_symbol(dep_dp, mb_type_contexts + act_ctx ); + act_sym += mode_sym*2; + mode_sym = biari_decode_symbol(dep_dp, mb_type_contexts + act_ctx ); + act_sym += mode_sym; + curr_mb_type = act_sym; + } + } + + se->value1 = curr_mb_type; + +#if TRACE + fprintf(p_Dec->p_trace, "@%-6d %-63s (%3d)\n",symbolCount++, se->tracestring, se->value1); + fflush(p_Dec->p_trace); +#endif +} + + +/*! + ************************************************************************ + * \brief + * This function is used to arithmetically decode the macroblock + * type info of a given MB. + ************************************************************************ + */ +void readMB_typeInfo_CABAC_b_slice(Macroblock *currMB, + SyntaxElement *se, + DecodingEnvironmentPtr dep_dp) +{ + Slice *currSlice = currMB->p_Slice; + MotionInfoContexts *ctx = currSlice->mot_ctx; + + int a = 0, b = 0; + int act_ctx; + int act_sym; + int mode_sym; + int curr_mb_type; + BiContextType *mb_type_contexts = ctx->mb_type_contexts[2]; + + if (currMB->mb_up != NULL) + b = (( (currMB->mb_up)->mb_type != 0) ? 1 : 0 ); + + if (currMB->mb_left != NULL) + a = (( (currMB->mb_left)->mb_type != 0) ? 1 : 0 ); + + act_ctx = a + b; + + if (biari_decode_symbol (dep_dp, &mb_type_contexts[act_ctx])) + { + if (biari_decode_symbol (dep_dp, &mb_type_contexts[4])) + { + if (biari_decode_symbol (dep_dp, &mb_type_contexts[5])) + { + act_sym = 12; + if (biari_decode_symbol (dep_dp, &mb_type_contexts[6])) + act_sym += 8; + if (biari_decode_symbol (dep_dp, &mb_type_contexts[6])) + act_sym += 4; + if (biari_decode_symbol (dep_dp, &mb_type_contexts[6])) + act_sym += 2; + + if (act_sym == 24) + act_sym=11; + else if (act_sym == 26) + act_sym = 22; + else + { + if (act_sym == 22) + act_sym = 23; + if (biari_decode_symbol (dep_dp, &mb_type_contexts[6])) + act_sym += 1; + } + } + else + { + act_sym = 3; + if (biari_decode_symbol (dep_dp, &mb_type_contexts[6])) + act_sym += 4; + if (biari_decode_symbol (dep_dp, &mb_type_contexts[6])) + act_sym += 2; + if (biari_decode_symbol (dep_dp, &mb_type_contexts[6])) + act_sym += 1; + } + } + else + { + if (biari_decode_symbol (dep_dp, &mb_type_contexts[6])) + act_sym=2; + else + act_sym=1; + } + } + else + { + act_sym = 0; + } + + + if (act_sym <= 23) + { + curr_mb_type = act_sym; + } + else // additional info for 16x16 Intra-mode + { + mode_sym = biari_decode_final(dep_dp); + if( mode_sym == 1 ) + { + curr_mb_type = 48; + } + else + { + mb_type_contexts = ctx->mb_type_contexts[1]; + act_ctx = 8; + mode_sym = biari_decode_symbol(dep_dp, mb_type_contexts + act_ctx ); // decoding of AC/no AC + act_sym += mode_sym*12; + + // decoding of cbp: 0,1,2 + act_ctx = 9; + mode_sym = biari_decode_symbol(dep_dp, mb_type_contexts + act_ctx ); + if (mode_sym != 0) + { + act_sym+=4; + mode_sym = biari_decode_symbol(dep_dp, mb_type_contexts + act_ctx ); + if (mode_sym != 0) + act_sym+=4; + } + + // decoding of I pred-mode: 0,1,2,3 + act_ctx = 10; + mode_sym = biari_decode_symbol(dep_dp, mb_type_contexts + act_ctx ); + act_sym += mode_sym*2; + mode_sym = biari_decode_symbol(dep_dp, mb_type_contexts + act_ctx ); + act_sym += mode_sym; + curr_mb_type = act_sym; + } + } + + se->value1 = curr_mb_type; + +#if TRACE + fprintf(p_Dec->p_trace, "@%-6d %-63s (%3d)\n",symbolCount++, se->tracestring, se->value1); + fflush(p_Dec->p_trace); +#endif +} + +/*! + ************************************************************************ + * \brief + * This function is used to arithmetically decode a pair of + * intra prediction modes of a given MB. + ************************************************************************ + */ +void readIntraPredMode_CABAC( Macroblock *currMB, + SyntaxElement *se, + DecodingEnvironmentPtr dep_dp) +{ + Slice *currSlice = currMB->p_Slice; + TextureInfoContexts *ctx = currSlice->tex_ctx; + // use_most_probable_mode + int act_sym = biari_decode_symbol(dep_dp, ctx->ipr_contexts); + + // remaining_mode_selector + if (act_sym == 1) + se->value1 = -1; + else + { + se->value1 = (biari_decode_symbol(dep_dp, ctx->ipr_contexts + 1) ); + se->value1 |= (biari_decode_symbol(dep_dp, ctx->ipr_contexts + 1) << 1); + se->value1 |= (biari_decode_symbol(dep_dp, ctx->ipr_contexts + 1) << 2); + } + +#if TRACE + fprintf(p_Dec->p_trace, "@%-6d %-63s (%3d)\n",symbolCount++, se->tracestring, se->value1); + fflush(p_Dec->p_trace); +#endif +} +/*! + ************************************************************************ + * \brief + * This function is used to arithmetically decode the reference + * parameter of a given MB. + ************************************************************************ + */ +void readRefFrame_CABAC(Macroblock *currMB, + SyntaxElement *se, + DecodingEnvironmentPtr dep_dp) +{ + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + StorablePicture *dec_picture = currSlice->dec_picture; + MotionInfoContexts *ctx = currSlice->mot_ctx; + Macroblock *neighborMB = NULL; + + int addctx = 0; + int a = 0, b = 0; + int act_ctx; + int act_sym; + int list = se->value2; + + PixelPos block_a, block_b; + + get4x4Neighbour(currMB, currMB->subblock_x - 1, currMB->subblock_y , p_Vid->mb_size[IS_LUMA], &block_a); + get4x4Neighbour(currMB, currMB->subblock_x, currMB->subblock_y - 1, p_Vid->mb_size[IS_LUMA], &block_b); + + if (block_b.available) + { + int b8b=((block_b.x >> 1) & 0x01)+(block_b.y & 0x02); + neighborMB = &currSlice->mb_data[block_b.mb_addr]; + if (!( (neighborMB->mb_type==IPCM) || IS_DIRECT(neighborMB) || (neighborMB->b8mode[b8b]==0 && neighborMB->b8pdir[b8b]==2))) + { + if (currSlice->mb_aff_frame_flag && (currMB->mb_field == FALSE) && (neighborMB->mb_field == TRUE)) + b = (dec_picture->mv_info[block_b.pos_y][block_b.pos_x].ref_idx[list] > 1 ? 2 : 0); + else + b = (dec_picture->mv_info[block_b.pos_y][block_b.pos_x].ref_idx[list] > 0 ? 2 : 0); + } + } + + if (block_a.available) + { + int b8a=((block_a.x >> 1) & 0x01)+(block_a.y & 0x02); + neighborMB = &currSlice->mb_data[block_a.mb_addr]; + if (!((neighborMB->mb_type==IPCM) || IS_DIRECT(neighborMB) || (neighborMB->b8mode[b8a]==0 && neighborMB->b8pdir[b8a]==2))) + { + if (currSlice->mb_aff_frame_flag && (currMB->mb_field == FALSE) && (neighborMB->mb_field == 1)) + a = (dec_picture->mv_info[block_a.pos_y][block_a.pos_x].ref_idx[list] > 1 ? 1 : 0); + else + a = (dec_picture->mv_info[block_a.pos_y][block_a.pos_x].ref_idx[list] > 0 ? 1 : 0); + } + } + + act_ctx = a + b; + se->context = act_ctx; // store context + + act_sym = biari_decode_symbol(dep_dp,ctx->ref_no_contexts[addctx] + act_ctx ); + + if (act_sym != 0) + { + act_ctx = 4; + act_sym = unary_bin_decode(dep_dp,ctx->ref_no_contexts[addctx] + act_ctx,1); + ++act_sym; + } + se->value1 = act_sym; + +#if TRACE + fprintf(p_Dec->p_trace, "@%-6d %-63s (%3d)\n",symbolCount++, se->tracestring, se->value1); +// fprintf(p_Dec->p_trace," c: %d :%d \n",ctx->ref_no_contexts[addctx][act_ctx].cum_freq[0],ctx->ref_no_contexts[addctx][act_ctx].cum_freq[1]); + fflush(p_Dec->p_trace); +#endif +} + + +/*! + ************************************************************************ + * \brief + * This function is used to arithmetically decode the delta qp + * of a given MB. + ************************************************************************ + */ +void read_dQuant_CABAC( Macroblock *currMB, + SyntaxElement *se, + DecodingEnvironmentPtr dep_dp) +{ + Slice *currSlice = currMB->p_Slice; + MotionInfoContexts *ctx = currSlice->mot_ctx; + int *dquant = &se->value1; + int act_ctx = ((currSlice->last_dquant != 0) ? 1 : 0); + int act_sym = biari_decode_symbol(dep_dp,ctx->delta_qp_contexts + act_ctx ); + + if (act_sym != 0) + { + act_ctx = 2; + act_sym = unary_bin_decode(dep_dp,ctx->delta_qp_contexts + act_ctx,1); + ++act_sym; + *dquant = (act_sym + 1) >> 1; + if((act_sym & 0x01)==0) // lsb is signed bit + *dquant = -*dquant; + } + else + *dquant = 0; + + currSlice->last_dquant = *dquant; + +#if TRACE + fprintf(p_Dec->p_trace, "@%-6d %-63s (%3d)\n",symbolCount++, se->tracestring, se->value1); + fflush(p_Dec->p_trace); +#endif +} +/*! + ************************************************************************ + * \brief + * This function is used to arithmetically decode the coded + * block pattern of a given MB. + ************************************************************************ + */ +void read_CBP_CABAC(Macroblock *currMB, + SyntaxElement *se, + DecodingEnvironmentPtr dep_dp) +{ + VideoParameters *p_Vid = currMB->p_Vid; + StorablePicture *dec_picture = currMB->p_Slice->dec_picture; + Slice *currSlice = currMB->p_Slice; + TextureInfoContexts *ctx = currSlice->tex_ctx; + Macroblock *neighborMB = NULL; + + int mb_x, mb_y; + int a = 0, b = 0; + int curr_cbp_ctx; + int cbp = 0; + int cbp_bit; + int mask; + PixelPos block_a; + + // coding of luma part (bit by bit) + for (mb_y=0; mb_y < 4; mb_y += 2) + { + if (mb_y == 0) + { + neighborMB = currMB->mb_up; + b = 0; + } + + for (mb_x=0; mb_x < 4; mb_x += 2) + { + if (mb_y == 0) + { + if (neighborMB != NULL) + { + if(neighborMB->mb_type!=IPCM) + b = (( (neighborMB->cbp & (1<<(2 + (mb_x>>1)))) == 0) ? 2 : 0); + } + } + else + b = ( ((cbp & (1<<(mb_x/2))) == 0) ? 2: 0); + + if (mb_x == 0) + { + get4x4Neighbour(currMB, (mb_x<<2) - 1, (mb_y << 2), p_Vid->mb_size[IS_LUMA], &block_a); + if (block_a.available) + { + if(currSlice->mb_data[block_a.mb_addr].mb_type==IPCM) + a = 0; + else + a = (( (currSlice->mb_data[block_a.mb_addr].cbp & (1<<(2*(block_a.y/2)+1))) == 0) ? 1 : 0); + } + else + a=0; + } + else + a = ( ((cbp & (1<> 1))); + cbp_bit = biari_decode_symbol(dep_dp, ctx->cbp_contexts[0] + curr_cbp_ctx ); + if (cbp_bit) + cbp += mask; + } + } + + if ((dec_picture->chroma_format_idc != YUV400) && (dec_picture->chroma_format_idc != YUV444)) + { + // coding of chroma part + // CABAC decoding for BinIdx 0 + b = 0; + neighborMB = currMB->mb_up; + if (neighborMB != NULL) + { + if (neighborMB->mb_type==IPCM || (neighborMB->cbp > 15)) + b = 2; + } + + a = 0; + neighborMB = currMB->mb_left; + if (neighborMB != NULL) + { + if (neighborMB->mb_type==IPCM || (neighborMB->cbp > 15)) + a = 1; + } + + curr_cbp_ctx = a + b; + cbp_bit = biari_decode_symbol(dep_dp, ctx->cbp_contexts[1] + curr_cbp_ctx ); + + // CABAC decoding for BinIdx 1 + if (cbp_bit) // set the chroma bits + { + b = 0; + neighborMB = currMB->mb_up; + if (neighborMB != NULL) + { + //if ((neighborMB->mb_type == IPCM) || ((neighborMB->cbp > 15) && ((neighborMB->cbp >> 4) == 2))) + if ((neighborMB->mb_type == IPCM) || ((neighborMB->cbp >> 4) == 2)) + b = 2; + } + + + a = 0; + neighborMB = currMB->mb_left; + if (neighborMB != NULL) + { + if ((neighborMB->mb_type == IPCM) || ((neighborMB->cbp >> 4) == 2)) + a = 1; + } + + curr_cbp_ctx = a + b; + cbp_bit = biari_decode_symbol(dep_dp, ctx->cbp_contexts[2] + curr_cbp_ctx ); + cbp += (cbp_bit == 1) ? 32 : 16; + } + } + + se->value1 = cbp; + + if (!cbp) + { + currSlice->last_dquant = 0; + } + +#if TRACE + fprintf(p_Dec->p_trace, "@%-6d %-63s (%3d)\n",symbolCount++, se->tracestring, se->value1); + fflush(p_Dec->p_trace); +#endif +} + +/*! + ************************************************************************ + * \brief + * This function is used to arithmetically decode the chroma + * intra prediction mode of a given MB. + ************************************************************************ + */ +void readCIPredMode_CABAC(Macroblock *currMB, + SyntaxElement *se, + DecodingEnvironmentPtr dep_dp) +{ + Slice *currSlice = currMB->p_Slice; + TextureInfoContexts *ctx = currSlice->tex_ctx; + int *act_sym = &se->value1; + + Macroblock *MbUp = currMB->mb_up; + Macroblock *MbLeft = currMB->mb_left; + + int b = (MbUp != NULL) ? (((MbUp->c_ipred_mode != 0) && (MbUp->mb_type != IPCM)) ? 1 : 0) : 0; + int a = (MbLeft != NULL) ? (((MbLeft->c_ipred_mode != 0) && (MbLeft->mb_type != IPCM)) ? 1 : 0) : 0; + int act_ctx = a + b; + + *act_sym = biari_decode_symbol(dep_dp, ctx->cipr_contexts + act_ctx ); + + if (*act_sym != 0) + *act_sym = unary_bin_max_decode(dep_dp, ctx->cipr_contexts + 3, 0, 1) + 1; + +#if TRACE + fprintf(p_Dec->p_trace, "@%-6d %-63s (%3d)\n",symbolCount++, se->tracestring, se->value1); + fflush(p_Dec->p_trace); +#endif + +} + + +/*! + ************************************************************************ + * \brief + * Read CBP4-BIT + ************************************************************************ +*/ +static int read_and_store_CBP_block_bit_444 (Macroblock *currMB, + DecodingEnvironmentPtr dep_dp, + int type) +{ + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + StorablePicture *dec_picture = currSlice->dec_picture; + TextureInfoContexts *tex_ctx = currSlice->tex_ctx; + Macroblock *mb_data = currSlice->mb_data; + int y_ac = (type==LUMA_16AC || type==LUMA_8x8 || type==LUMA_8x4 || type==LUMA_4x8 || type==LUMA_4x4 + || type==CB_16AC || type==CB_8x8 || type==CB_8x4 || type==CB_4x8 || type==CB_4x4 + || type==CR_16AC || type==CR_8x8 || type==CR_8x4 || type==CR_4x8 || type==CR_4x4); + int y_dc = (type==LUMA_16DC || type==CB_16DC || type==CR_16DC); + int u_ac = (type==CHROMA_AC && !currMB->is_v_block); + int v_ac = (type==CHROMA_AC && currMB->is_v_block); + int chroma_dc = (type==CHROMA_DC || type==CHROMA_DC_2x4 || type==CHROMA_DC_4x4); + int u_dc = (chroma_dc && !currMB->is_v_block); + int v_dc = (chroma_dc && currMB->is_v_block); + int j = (y_ac || u_ac || v_ac ? currMB->subblock_y : 0); + int i = (y_ac || u_ac || v_ac ? currMB->subblock_x : 0); + int bit = (y_dc ? 0 : y_ac ? 1 : u_dc ? 17 : v_dc ? 18 : u_ac ? 19 : 35); + int default_bit = (currMB->is_intra_block ? 1 : 0); + int upper_bit = default_bit; + int left_bit = default_bit; + int cbp_bit = 1; // always one for 8x8 mode + int ctx; + int bit_pos_a = 0; + int bit_pos_b = 0; + + PixelPos block_a, block_b; + if (y_ac) + { + get4x4Neighbour(currMB, i - 1, j , p_Vid->mb_size[IS_LUMA], &block_a); + get4x4Neighbour(currMB, i , j - 1, p_Vid->mb_size[IS_LUMA], &block_b); + if (block_a.available) + bit_pos_a = 4*block_a.y + block_a.x; + if (block_b.available) + bit_pos_b = 4*block_b.y + block_b.x; + } + else if (y_dc) + { + get4x4Neighbour(currMB, i - 1, j , p_Vid->mb_size[IS_LUMA], &block_a); + get4x4Neighbour(currMB, i , j - 1, p_Vid->mb_size[IS_LUMA], &block_b); + } + else if (u_ac||v_ac) + { + get4x4Neighbour(currMB, i - 1, j , p_Vid->mb_size[IS_CHROMA], &block_a); + get4x4Neighbour(currMB, i , j - 1, p_Vid->mb_size[IS_CHROMA], &block_b); + if (block_a.available) + bit_pos_a = 4*block_a.y + block_a.x; + if (block_b.available) + bit_pos_b = 4*block_b.y + block_b.x; + } + else + { + get4x4Neighbour(currMB, i - 1, j , p_Vid->mb_size[IS_CHROMA], &block_a); + get4x4Neighbour(currMB, i , j - 1, p_Vid->mb_size[IS_CHROMA], &block_b); + } + + if (dec_picture->chroma_format_idc!=YUV444) + { + if (type!=LUMA_8x8) + { + //--- get bits from neighboring blocks --- + if (block_b.available) + { + if(mb_data[block_b.mb_addr].mb_type==IPCM) + upper_bit=1; + else + upper_bit = get_bit(mb_data[block_b.mb_addr].s_cbp[0].bits, bit + bit_pos_b); + } + + if (block_a.available) + { + if(mb_data[block_a.mb_addr].mb_type==IPCM) + left_bit=1; + else + left_bit = get_bit(mb_data[block_a.mb_addr].s_cbp[0].bits, bit + bit_pos_a); + } + + + ctx = 2 * upper_bit + left_bit; + //===== encode symbol ===== + cbp_bit = biari_decode_symbol (dep_dp, tex_ctx->bcbp_contexts[type2ctx_bcbp[type]] + ctx); + } + } + else if( (p_Vid->separate_colour_plane_flag != 0) ) + { + if (type!=LUMA_8x8) + { + //--- get bits from neighbouring blocks --- + if (block_b.available) + { + if(mb_data[block_b.mb_addr].mb_type==IPCM) + upper_bit = 1; + else + upper_bit = get_bit(mb_data[block_b.mb_addr].s_cbp[0].bits,bit+bit_pos_b); + } + + + if (block_a.available) + { + if(mb_data[block_a.mb_addr].mb_type==IPCM) + left_bit = 1; + else + left_bit = get_bit(mb_data[block_a.mb_addr].s_cbp[0].bits,bit+bit_pos_a); + } + + + ctx = 2 * upper_bit + left_bit; + //===== encode symbol ===== + cbp_bit = biari_decode_symbol (dep_dp, tex_ctx->bcbp_contexts[type2ctx_bcbp[type]] + ctx); + } + } + else + { + if (block_b.available) + { + if((type==LUMA_8x8 || type==CB_8x8 || type==CR_8x8) && + !mb_data[block_b.mb_addr].luma_transform_size_8x8_flag) + {} + else if(mb_data[block_b.mb_addr].mb_type==IPCM) + upper_bit=1; + else + { + if(type==LUMA_8x8) + upper_bit = get_bit(mb_data[block_b.mb_addr].s_cbp[0].bits_8x8, bit + bit_pos_b); + else if (type==CB_8x8) + upper_bit = get_bit(mb_data[block_b.mb_addr].s_cbp[1].bits_8x8, bit + bit_pos_b); + else if (type==CR_8x8) + upper_bit = get_bit(mb_data[block_b.mb_addr].s_cbp[2].bits_8x8, bit + bit_pos_b); + else if ((type==CB_4x4)||(type==CB_4x8)||(type==CB_8x4)||(type==CB_16AC)||(type==CB_16DC)) + upper_bit = get_bit(mb_data[block_b.mb_addr].s_cbp[1].bits,bit+bit_pos_b); + else if ((type==CR_4x4)||(type==CR_4x8)||(type==CR_8x4)||(type==CR_16AC)||(type==CR_16DC)) + upper_bit = get_bit(mb_data[block_b.mb_addr].s_cbp[2].bits,bit+bit_pos_b); + else + upper_bit = get_bit(mb_data[block_b.mb_addr].s_cbp[0].bits,bit+bit_pos_b); + } + } + + + if (block_a.available) + { + if((type==LUMA_8x8 || type==CB_8x8 || type==CR_8x8) && + !mb_data[block_a.mb_addr].luma_transform_size_8x8_flag) + {} + else if(mb_data[block_a.mb_addr].mb_type==IPCM) + left_bit=1; + else + { + if(type==LUMA_8x8) + left_bit = get_bit(mb_data[block_a.mb_addr].s_cbp[0].bits_8x8,bit+bit_pos_a); + else if (type==CB_8x8) + left_bit = get_bit(mb_data[block_a.mb_addr].s_cbp[1].bits_8x8,bit+bit_pos_a); + else if (type==CR_8x8) + left_bit = get_bit(mb_data[block_a.mb_addr].s_cbp[2].bits_8x8,bit+bit_pos_a); + else if ((type==CB_4x4)||(type==CB_4x8)||(type==CB_8x4)||(type==CB_16AC)||(type==CB_16DC)) + left_bit = get_bit(mb_data[block_a.mb_addr].s_cbp[1].bits,bit+bit_pos_a); + else if ((type==CR_4x4)||(type==CR_4x8)||(type==CR_8x4)||(type==CR_16AC)||(type==CR_16DC)) + left_bit = get_bit(mb_data[block_a.mb_addr].s_cbp[2].bits,bit+bit_pos_a); + else + left_bit = get_bit(mb_data[block_a.mb_addr].s_cbp[0].bits,bit+bit_pos_a); + } + } + + ctx = 2 * upper_bit + left_bit; + //===== encode symbol ===== + cbp_bit = biari_decode_symbol (dep_dp, tex_ctx->bcbp_contexts[type2ctx_bcbp[type]] + ctx); + } + + //--- set bits for current block --- + bit = (y_dc ? 0 : y_ac ? 1 + j + (i >> 2) : u_dc ? 17 : v_dc ? 18 : u_ac ? 19 + j + (i >> 2) : 35 + j + (i >> 2)); + + if (cbp_bit) + { + CBPStructure *s_cbp = currMB->s_cbp; + if (type==LUMA_8x8) + { + s_cbp[0].bits |= ((int64) 0x33 << bit ); + + if (dec_picture->chroma_format_idc==YUV444) + { + s_cbp[0].bits_8x8 |= ((int64) 0x33 << bit ); + } + } + else if (type==CB_8x8) + { + s_cbp[1].bits_8x8 |= ((int64) 0x33 << bit ); + s_cbp[1].bits |= ((int64) 0x33 << bit ); + } + else if (type==CR_8x8) + { + s_cbp[2].bits_8x8 |= ((int64) 0x33 << bit ); + s_cbp[2].bits |= ((int64) 0x33 << bit ); + } + else if (type==LUMA_8x4) + { + s_cbp[0].bits |= ((int64) 0x03 << bit ); + } + else if (type==CB_8x4) + { + s_cbp[1].bits |= ((int64) 0x03 << bit ); + } + else if (type==CR_8x4) + { + s_cbp[2].bits |= ((int64) 0x03 << bit ); + } + else if (type==LUMA_4x8) + { + s_cbp[0].bits |= ((int64) 0x11<< bit ); + } + else if (type==CB_4x8) + { + s_cbp[1].bits |= ((int64)0x11<< bit ); + } + else if (type==CR_4x8) + { + s_cbp[2].bits |= ((int64)0x11<< bit ); + } + else if ((type==CB_4x4)||(type==CB_16AC)||(type==CB_16DC)) + { + s_cbp[1].bits |= i64_power2(bit); + } + else if ((type==CR_4x4)||(type==CR_16AC)||(type==CR_16DC)) + { + s_cbp[2].bits |= i64_power2(bit); + } + else + { + s_cbp[0].bits |= i64_power2(bit); + } + } + return cbp_bit; +} + + +static inline int set_cbp_bit(Macroblock *neighbor_mb) +{ + if(neighbor_mb->mb_type == IPCM) + return 1; + else + return (int) (neighbor_mb->s_cbp[0].bits & 0x01); +} + +static inline int set_cbp_bit_ac(Macroblock *neighbor_mb, PixelPos *block) +{ + if (neighbor_mb->mb_type == IPCM) + return 1; + else + { + int bit_pos = 1 + (block->y << 2) + block->x; + return get_bit(neighbor_mb->s_cbp[0].bits, bit_pos); + } +} + +/*! + ************************************************************************ + * \brief + * Read CBP4-BIT + ************************************************************************ + */ +static int read_and_store_CBP_block_bit_normal (Macroblock *currMB, + DecodingEnvironmentPtr dep_dp, + int type) +{ + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + TextureInfoContexts *tex_ctx = currSlice->tex_ctx; + int cbp_bit = 1; // always one for 8x8 mode + Macroblock *mb_data = currSlice->mb_data; + + if (type==LUMA_16DC) + { + int upper_bit = 1; + int left_bit = 1; + int ctx; + + PixelPos block_a, block_b; + + get4x4NeighbourBase(currMB, -1, 0, p_Vid->mb_size[IS_LUMA], &block_a); + get4x4NeighbourBase(currMB, 0, -1, p_Vid->mb_size[IS_LUMA], &block_b); + + //--- get bits from neighboring blocks --- + if (block_b.available) + { + upper_bit = set_cbp_bit(&mb_data[block_b.mb_addr]); + } + + if (block_a.available) + { + left_bit = set_cbp_bit(&mb_data[block_a.mb_addr]); + } + + ctx = 2 * upper_bit + left_bit; + //===== encode symbol ===== + cbp_bit = biari_decode_symbol (dep_dp, tex_ctx->bcbp_contexts[type2ctx_bcbp[type]] + ctx); + + //--- set bits for current block --- + + if (cbp_bit) + { + currMB->s_cbp[0].bits |= 1; + } + } + else if (type==LUMA_16AC) + { + int j = currMB->subblock_y; + int i = currMB->subblock_x; + int bit = 1; + int default_bit = (currMB->is_intra_block ? 1 : 0); + int upper_bit = default_bit; + int left_bit = default_bit; + int ctx; + + PixelPos block_a, block_b; + + get4x4NeighbourBase(currMB, i - 1, j , p_Vid->mb_size[IS_LUMA], &block_a); + get4x4NeighbourBase(currMB, i , j - 1, p_Vid->mb_size[IS_LUMA], &block_b); + + //--- get bits from neighboring blocks --- + if (block_b.available) + { + upper_bit = set_cbp_bit_ac(&mb_data[block_b.mb_addr], &block_b); + } + + if (block_a.available) + { + left_bit = set_cbp_bit_ac(&mb_data[block_a.mb_addr], &block_a); + } + + ctx = 2 * upper_bit + left_bit; + //===== encode symbol ===== + cbp_bit = biari_decode_symbol (dep_dp, tex_ctx->bcbp_contexts[type2ctx_bcbp[type]] + ctx); + + if (cbp_bit) + { + //--- set bits for current block --- + bit = 1 + j + (i >> 2); + currMB->s_cbp[0].bits |= i64_power2(bit); + } + } + else if (type==LUMA_8x4) + { + int j = currMB->subblock_y; + int i = currMB->subblock_x; + int bit = 1; + int default_bit = (currMB->is_intra_block ? 1 : 0); + int upper_bit = default_bit; + int left_bit = default_bit; + int ctx; + + PixelPos block_a, block_b; + + get4x4NeighbourBase(currMB, i - 1, j , p_Vid->mb_size[IS_LUMA], &block_a); + get4x4NeighbourBase(currMB, i , j - 1, p_Vid->mb_size[IS_LUMA], &block_b); + + //--- get bits from neighboring blocks --- + if (block_b.available) + { + upper_bit = set_cbp_bit_ac(&mb_data[block_b.mb_addr], &block_b); + } + + if (block_a.available) + { + left_bit = set_cbp_bit_ac(&mb_data[block_a.mb_addr], &block_a); + } + + ctx = 2 * upper_bit + left_bit; + //===== encode symbol ===== + cbp_bit = biari_decode_symbol (dep_dp, tex_ctx->bcbp_contexts[type2ctx_bcbp[type]] + ctx); + + if (cbp_bit) + { + //--- set bits for current block --- + bit = 1 + j + (i >> 2); + currMB->s_cbp[0].bits |= ((int64) 0x03 << bit ); + } + } + else if (type==LUMA_4x8) + { + int j = currMB->subblock_y; + int i = currMB->subblock_x; + int bit = 1; + int default_bit = (currMB->is_intra_block ? 1 : 0); + int upper_bit = default_bit; + int left_bit = default_bit; + int ctx; + + PixelPos block_a, block_b; + + get4x4NeighbourBase(currMB, i - 1, j , p_Vid->mb_size[IS_LUMA], &block_a); + get4x4NeighbourBase(currMB, i , j - 1, p_Vid->mb_size[IS_LUMA], &block_b); + + //--- get bits from neighboring blocks --- + if (block_b.available) + { + upper_bit = set_cbp_bit_ac(&mb_data[block_b.mb_addr], &block_b); + } + + if (block_a.available) + { + left_bit = set_cbp_bit_ac(&mb_data[block_a.mb_addr], &block_a); + } + + ctx = 2 * upper_bit + left_bit; + //===== encode symbol ===== + cbp_bit = biari_decode_symbol (dep_dp, tex_ctx->bcbp_contexts[type2ctx_bcbp[type]] + ctx); + + if (cbp_bit) + { + //--- set bits for current block --- + bit = 1 + j + (i >> 2); + + currMB->s_cbp[0].bits |= ((int64) 0x11 << bit ); + } + } + else if (type==LUMA_4x4) + { + int j = currMB->subblock_y; + int i = currMB->subblock_x; + int bit = 1; + int default_bit = (currMB->is_intra_block ? 1 : 0); + int upper_bit = default_bit; + int left_bit = default_bit; + int ctx; + + PixelPos block_a, block_b; + + get4x4NeighbourBase(currMB, i - 1, j , p_Vid->mb_size[IS_LUMA], &block_a); + get4x4NeighbourBase(currMB, i , j - 1, p_Vid->mb_size[IS_LUMA], &block_b); + + //--- get bits from neighboring blocks --- + if (block_b.available) + { + upper_bit = set_cbp_bit_ac(&mb_data[block_b.mb_addr], &block_b); + } + + if (block_a.available) + { + left_bit = set_cbp_bit_ac(&mb_data[block_a.mb_addr], &block_a); + } + + ctx = 2 * upper_bit + left_bit; + //===== encode symbol ===== + cbp_bit = biari_decode_symbol (dep_dp, tex_ctx->bcbp_contexts[type2ctx_bcbp[type]] + ctx); + + if (cbp_bit) + { + //--- set bits for current block --- + bit = 1 + j + (i >> 2); + + currMB->s_cbp[0].bits |= i64_power2(bit); + } + } + else if (type == LUMA_8x8) + { + int j = currMB->subblock_y; + int i = currMB->subblock_x; + //--- set bits for current block --- + int bit = 1 + j + (i >> 2); + + currMB->s_cbp[0].bits |= ((int64) 0x33 << bit ); + } + else if (type==CHROMA_DC || type==CHROMA_DC_2x4 || type==CHROMA_DC_4x4) + { + int u_dc = (!currMB->is_v_block); + int j = 0; + int i = 0; + int bit = (u_dc ? 17 : 18); + int default_bit = (currMB->is_intra_block ? 1 : 0); + int upper_bit = default_bit; + int left_bit = default_bit; + int ctx; + + PixelPos block_a, block_b; + + get4x4NeighbourBase(currMB, i - 1, j , p_Vid->mb_size[IS_CHROMA], &block_a); + get4x4NeighbourBase(currMB, i , j - 1, p_Vid->mb_size[IS_CHROMA], &block_b); + + //--- get bits from neighboring blocks --- + if (block_b.available) + { + if(mb_data[block_b.mb_addr].mb_type==IPCM) + upper_bit = 1; + else + upper_bit = get_bit(mb_data[block_b.mb_addr].s_cbp[0].bits, bit); + } + + if (block_a.available) + { + if(mb_data[block_a.mb_addr].mb_type==IPCM) + left_bit = 1; + else + left_bit = get_bit(mb_data[block_a.mb_addr].s_cbp[0].bits, bit); + } + + ctx = 2 * upper_bit + left_bit; + //===== encode symbol ===== + cbp_bit = biari_decode_symbol (dep_dp, tex_ctx->bcbp_contexts[type2ctx_bcbp[type]] + ctx); + + if (cbp_bit) + { + //--- set bits for current block --- + bit = (u_dc ? 17 : 18); + currMB->s_cbp[0].bits |= i64_power2(bit); + } + } + else + { + int u_ac = (!currMB->is_v_block); + int j = currMB->subblock_y; + int i = currMB->subblock_x; + int bit = (u_ac ? 19 : 35); + int default_bit = (currMB->is_intra_block ? 1 : 0); + int upper_bit = default_bit; + int left_bit = default_bit; + int ctx; + + PixelPos block_a, block_b; + + get4x4NeighbourBase(currMB, i - 1, j , p_Vid->mb_size[IS_CHROMA], &block_a); + get4x4NeighbourBase(currMB, i , j - 1, p_Vid->mb_size[IS_CHROMA], &block_b); + + //--- get bits from neighboring blocks --- + if (block_b.available) + { + if(mb_data[block_b.mb_addr].mb_type==IPCM) + upper_bit=1; + else + { + int bit_pos_b = 4*block_b.y + block_b.x; + upper_bit = get_bit(mb_data[block_b.mb_addr].s_cbp[0].bits, bit + bit_pos_b); + } + } + + if (block_a.available) + { + if(mb_data[block_a.mb_addr].mb_type==IPCM) + left_bit=1; + else + { + int bit_pos_a = 4*block_a.y + block_a.x; + left_bit = get_bit(mb_data[block_a.mb_addr].s_cbp[0].bits,bit + bit_pos_a); + } + } + + ctx = 2 * upper_bit + left_bit; + //===== encode symbol ===== + cbp_bit = biari_decode_symbol (dep_dp, tex_ctx->bcbp_contexts[type2ctx_bcbp[type]] + ctx); + + if (cbp_bit) + { + //--- set bits for current block --- + bit = (u_ac ? 19 + j + (i >> 2) : 35 + j + (i >> 2)); + currMB->s_cbp[0].bits |= i64_power2(bit); + } + } + return cbp_bit; +} + + +void set_read_and_store_CBP(Macroblock **currMB, int chroma_format_idc) +{ + if (chroma_format_idc == YUV444) + (*currMB)->read_and_store_CBP_block_bit = read_and_store_CBP_block_bit_444; + else + (*currMB)->read_and_store_CBP_block_bit = read_and_store_CBP_block_bit_normal; +} + + + + +//===== position -> ctx for MAP ===== +//--- zig-zag scan ---- +static const byte pos2ctx_map8x8 [] = { 0, 1, 2, 3, 4, 5, 5, 4, 4, 3, 3, 4, 4, 4, 5, 5, + 4, 4, 4, 4, 3, 3, 6, 7, 7, 7, 8, 9, 10, 9, 8, 7, + 7, 6, 11, 12, 13, 11, 6, 7, 8, 9, 14, 10, 9, 8, 6, 11, + 12, 13, 11, 6, 9, 14, 10, 9, 11, 12, 13, 11 ,14, 10, 12, 14}; // 15 CTX +static const byte pos2ctx_map8x4 [] = { 0, 1, 2, 3, 4, 5, 7, 8, 9, 10, 11, 9, 8, 6, 7, 8, + 9, 10, 11, 9, 8, 6, 12, 8, 9, 10, 11, 9, 13, 13, 14, 14}; // 15 CTX +static const byte pos2ctx_map4x4 [] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 14}; // 15 CTX +static const byte pos2ctx_map2x4c[] = { 0, 0, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2}; // 15 CTX +static const byte pos2ctx_map4x4c[] = { 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2}; // 15 CTX +static const byte* pos2ctx_map [] = {pos2ctx_map4x4, pos2ctx_map4x4, pos2ctx_map8x8, pos2ctx_map8x4, + pos2ctx_map8x4, pos2ctx_map4x4, pos2ctx_map4x4, pos2ctx_map4x4, + pos2ctx_map2x4c, pos2ctx_map4x4c, + pos2ctx_map4x4, pos2ctx_map4x4, pos2ctx_map8x8,pos2ctx_map8x4, + pos2ctx_map8x4, pos2ctx_map4x4, + pos2ctx_map4x4, pos2ctx_map4x4, pos2ctx_map8x8,pos2ctx_map8x4, + pos2ctx_map8x4,pos2ctx_map4x4}; +//--- interlace scan ---- +//taken from ABT +static const byte pos2ctx_map8x8i[] = { 0, 1, 1, 2, 2, 3, 3, 4, 5, 6, 7, 7, 7, 8, 4, 5, + 6, 9, 10, 10, 8, 11, 12, 11, 9, 9, 10, 10, 8, 11, 12, 11, + 9, 9, 10, 10, 8, 11, 12, 11, 9, 9, 10, 10, 8, 13, 13, 9, + 9, 10, 10, 8, 13, 13, 9, 9, 10, 10, 14, 14, 14, 14, 14, 14}; // 15 CTX +static const byte pos2ctx_map8x4i[] = { 0, 1, 2, 3, 4, 5, 6, 3, 4, 5, 6, 3, 4, 7, 6, 8, + 9, 7, 6, 8, 9, 10, 11, 12, 12, 10, 11, 13, 13, 14, 14, 14}; // 15 CTX +static const byte pos2ctx_map4x8i[] = { 0, 1, 1, 1, 2, 3, 3, 4, 4, 4, 5, 6, 2, 7, 7, 8, + 8, 8, 5, 6, 9, 10, 10, 11, 11, 11, 12, 13, 13, 14, 14, 14}; // 15 CTX +static const byte* pos2ctx_map_int[] = {pos2ctx_map4x4, pos2ctx_map4x4, pos2ctx_map8x8i,pos2ctx_map8x4i, + pos2ctx_map4x8i,pos2ctx_map4x4, pos2ctx_map4x4, pos2ctx_map4x4, + pos2ctx_map2x4c, pos2ctx_map4x4c, + pos2ctx_map4x4, pos2ctx_map4x4, pos2ctx_map8x8i,pos2ctx_map8x4i, + pos2ctx_map8x4i,pos2ctx_map4x4, + pos2ctx_map4x4, pos2ctx_map4x4, pos2ctx_map8x8i,pos2ctx_map8x4i, + pos2ctx_map8x4i,pos2ctx_map4x4}; + +//===== position -> ctx for LAST ===== +static const byte pos2ctx_last8x8 [] = { 0, 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, + 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, + 5, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8}; // 9 CTX +static const byte pos2ctx_last8x4 [] = { 0, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, + 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8}; // 9 CTX + +static const byte pos2ctx_last4x4 [] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}; // 15 CTX +static const byte pos2ctx_last2x4c[] = { 0, 0, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2}; // 15 CTX +static const byte pos2ctx_last4x4c[] = { 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2}; // 15 CTX +static const byte* pos2ctx_last [] = {pos2ctx_last4x4, pos2ctx_last4x4, pos2ctx_last8x8, pos2ctx_last8x4, + pos2ctx_last8x4, pos2ctx_last4x4, pos2ctx_last4x4, pos2ctx_last4x4, + pos2ctx_last2x4c, pos2ctx_last4x4c, + pos2ctx_last4x4, pos2ctx_last4x4, pos2ctx_last8x8,pos2ctx_last8x4, + pos2ctx_last8x4, pos2ctx_last4x4, + pos2ctx_last4x4, pos2ctx_last4x4, pos2ctx_last8x8,pos2ctx_last8x4, + pos2ctx_last8x4, pos2ctx_last4x4}; + + + +/*! + ************************************************************************ + * \brief + * Read Significance MAP + ************************************************************************ + */ +static int read_significance_map (Macroblock *currMB, + DecodingEnvironmentPtr dep_dp, + int type, + int coeff[]) +{ + Slice *currSlice = currMB->p_Slice; + int fld = ( currSlice->structure!=FRAME || currMB->mb_field ); + const byte *pos2ctx_Map = (fld) ? pos2ctx_map_int[type] : pos2ctx_map[type]; + const byte *pos2ctx_Last = pos2ctx_last[type]; + + BiContextTypePtr map_ctx = currSlice->tex_ctx->map_contexts [fld][type2ctx_map [type]]; + BiContextTypePtr last_ctx = currSlice->tex_ctx->last_contexts[fld][type2ctx_last[type]]; + + int i; + int coeff_ctr = 0; + int i0 = 0; + int i1 = maxpos[type]; + + + if (!c1isdc[type]) + { + ++i0; + ++i1; + } + + for (i=i0; i < i1; ++i) // if last coeff is reached, it has to be significant + { + //--- read significance symbol --- + if (biari_decode_symbol (dep_dp, map_ctx + pos2ctx_Map[i])) + { + *(coeff++) = 1; + ++coeff_ctr; + //--- read last coefficient symbol --- + if (biari_decode_symbol (dep_dp, last_ctx + pos2ctx_Last[i])) + { + memset(coeff, 0, (i1 - i) * sizeof(int)); + return coeff_ctr; + } + } + else + { + *(coeff++) = 0; + } + } + //--- last coefficient must be significant if no last symbol was received --- + if (i < i1 + 1) + { + *coeff = 1; + ++coeff_ctr; + } + + return coeff_ctr; +} + + + +/*! + ************************************************************************ + * \brief + * Read Levels + ************************************************************************ + */ +static void read_significant_coefficients (DecodingEnvironmentPtr dep_dp, + TextureInfoContexts *tex_ctx, + int type, + int *coeff) +{ + BiContextType *one_contexts = tex_ctx->one_contexts[type2ctx_one[type]]; + BiContextType *abs_contexts = tex_ctx->abs_contexts[type2ctx_abs[type]]; + const short max_type = max_c2[type]; + int i = maxpos[type]; + int *cof = coeff + i; + int c1 = 1; + int c2 = 0; + + for (; i>=0; i--) + { + if (*cof != 0) + { + *cof += biari_decode_symbol (dep_dp, one_contexts + c1); + + if (*cof == 2) + { + *cof += unary_exp_golomb_level_decode (dep_dp, abs_contexts + c2); + c2 = imin (++c2, max_type); + c1 = 0; + } + else if (c1) + { + c1 = imin (++c1, 4); + } + + if (biari_decode_symbol_eq_prob(dep_dp)) + { + *cof = - *cof; + } + } + cof--; + } +} + + +/*! + ************************************************************************ + * \brief + * Read Block-Transform Coefficients + ************************************************************************ + */ +void readRunLevel_CABAC (Macroblock *currMB, + SyntaxElement *se, + DecodingEnvironmentPtr dep_dp) +{ + Slice *currSlice = currMB->p_Slice; + int *coeff_ctr = &currSlice->coeff_ctr; + int *coeff = currSlice->coeff; + + //--- read coefficients for whole block --- + if (*coeff_ctr < 0) + { + //===== decode CBP-BIT ===== + if ((*coeff_ctr = currMB->read_and_store_CBP_block_bit (currMB, dep_dp, se->context) ) != 0) + { + //===== decode significance map ===== + *coeff_ctr = read_significance_map (currMB, dep_dp, se->context, coeff); + + //===== decode significant coefficients ===== + read_significant_coefficients (dep_dp, currSlice->tex_ctx, se->context, coeff); + } + } + + //--- set run and level --- + if (*coeff_ctr) + { + //--- set run and level (coefficient) --- + for (se->value2 = 0; coeff[currSlice->pos] == 0; ++currSlice->pos, ++se->value2); + se->value1 = coeff[currSlice->pos++]; + } + else + { + //--- set run and level (EOB) --- + se->value1 = se->value2 = 0; + } + //--- decrement coefficient counter and re-set position --- + if ((*coeff_ctr)-- == 0) + currSlice->pos = 0; + +#if TRACE + fprintf(p_Dec->p_trace, "@%-6d %-53s %3d %3d\n",symbolCount++, se->tracestring, se->value1,se->value2); + fflush(p_Dec->p_trace); +#endif +} + +/*! + ************************************************************************ + * \brief + * arithmetic decoding + ************************************************************************ + */ +int readSyntaxElement_CABAC(Macroblock *currMB, SyntaxElement *se, DataPartition *this_dataPart) +{ + DecodingEnvironmentPtr dep_dp = &(this_dataPart->de_cabac); + int curr_len = arideco_bits_read(dep_dp); + + // perform the actual decoding by calling the appropriate method + se->reading(currMB, se, dep_dp); + //read again and minus curr_len = arideco_bits_read(dep_dp); from above + se->len = (arideco_bits_read(dep_dp) - curr_len); + +#if (TRACE==2) + fprintf(p_Dec->p_trace, "curr_len: %d\n",curr_len); + fprintf(p_Dec->p_trace, "se_len: %d\n",se->len); +#endif + + return (se->len); +} + + +/*! + ************************************************************************ + * \brief + * decoding of unary binarization using one or 2 distinct + * models for the first and all remaining bins; no terminating + * "0" for max_symbol + *********************************************************************** + */ +static unsigned int unary_bin_max_decode(DecodingEnvironmentPtr dep_dp, + BiContextTypePtr ctx, + int ctx_offset, + unsigned int max_symbol) +{ + unsigned int symbol = biari_decode_symbol(dep_dp, ctx ); + + if (symbol == 0 || (max_symbol == 0)) + return symbol; + else + { + unsigned int l; + ctx += ctx_offset; + symbol = 0; + do + { + l = biari_decode_symbol(dep_dp, ctx); + ++symbol; + } + while( (l != 0) && (symbol < max_symbol) ); + + if ((l != 0) && (symbol == max_symbol)) + ++symbol; + return symbol; + } +} + + +/*! + ************************************************************************ + * \brief + * decoding of unary binarization using one or 2 distinct + * models for the first and all remaining bins + *********************************************************************** + */ +static unsigned int unary_bin_decode(DecodingEnvironmentPtr dep_dp, + BiContextTypePtr ctx, + int ctx_offset) +{ + unsigned int symbol = biari_decode_symbol(dep_dp, ctx ); + + if (symbol == 0) + return 0; + else + { + unsigned int l; + ctx += ctx_offset;; + symbol = 0; + do + { + l = biari_decode_symbol(dep_dp, ctx); + ++symbol; + } + while( l != 0 ); + return symbol; + } +} + + +/*! + ************************************************************************ + * \brief + * finding end of a slice in case this is not the end of a frame + * + * Unsure whether the "correction" below actually solves an off-by-one + * problem or whether it introduces one in some cases :-( Anyway, + * with this change the bit stream format works with CABAC again. + * StW, 8.7.02 + ************************************************************************ + */ +int cabac_startcode_follows(Slice *currSlice, int eos_bit) +{ + unsigned int bit; + + if( eos_bit ) + { + const byte *partMap = assignSE2partition[currSlice->dp_mode]; + DataPartition *dP = &(currSlice->partArr[partMap[SE_MBTYPE]]); + DecodingEnvironmentPtr dep_dp = &(dP->de_cabac); + + bit = biari_decode_final (dep_dp); //GB + +#if TRACE + fprintf(p_Dec->p_trace, "@%-6d %-63s (%3d)\n",symbolCount++, "end_of_slice_flag", bit); + fflush(p_Dec->p_trace); +#endif + } + else + { + bit = 0; + } + + return (bit == 1 ? 1 : 0); +} + +/*! + ************************************************************************ + * \brief + * Exp Golomb binarization and decoding of a symbol + * with prob. of 0.5 + ************************************************************************ + */ +static unsigned int exp_golomb_decode_eq_prob( DecodingEnvironmentPtr dep_dp, + int k) +{ + unsigned int l; + int symbol = 0; + int binary_symbol = 0; + + do + { + l = biari_decode_symbol_eq_prob(dep_dp); + if (l == 1) + { + symbol += (1<p_Vid; + StorablePicture *dec_picture = currSlice->dec_picture; + Bitstream* currStream = dP->bitstream; + DecodingEnvironmentPtr dep = &(dP->de_cabac); + byte *buf = currStream->streamBuffer; + int BitstreamLengthInBits = (dP->bitstream->bitstream_length << 3) + 7; + + int val = 0; + + int bits_read = 0; + int bitoffset, bitdepth; + int uv, i, j; + + while (dep->DbitsLeft >= 8) + { + dep->Dvalue >>= 8; + dep->DbitsLeft -= 8; + (*dep->Dcodestrm_len)--; + } + + bitoffset = (*dep->Dcodestrm_len) << 3; + + // read luma values + bitdepth = p_Vid->bitdepth_luma; + for(i=0;icof[0][i][j] = val; + + bitoffset += bitdepth; + } + } + + // read chroma values + bitdepth = p_Vid->bitdepth_chroma; + if ((dec_picture->chroma_format_idc != YUV400) && (p_Vid->separate_colour_plane_flag == 0)) + { + for (uv = 1; uv < 3; ++uv) + { + for(i = 0; i < p_Vid->mb_cr_size_y; ++i) + { + for(j = 0; j < p_Vid->mb_cr_size_x; ++j) + { + bits_read += GetBits(buf, bitoffset, &val, BitstreamLengthInBits, bitdepth); +#if TRACE + tracebits2("pcm_byte chroma", bitdepth, val); +#endif + currSlice->cof[uv][i][j] = val; + + bitoffset += bitdepth; + } + } + } + } + + (*dep->Dcodestrm_len) += ( bits_read >> 3); + if (bits_read & 7) + { + ++(*dep->Dcodestrm_len); + } +} + diff --git a/ldecod/src/configfile.c b/ldecod/src/configfile.c new file mode 100644 index 0000000..09b1259 --- /dev/null +++ b/ldecod/src/configfile.c @@ -0,0 +1,312 @@ + +/*! + *********************************************************************** + * \file + * configfile.c + * \brief + * Configuration handling. + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * - Stephan Wenger + * \note + * In the future this module should hide the Parameters and offer only + * Functions for their access. Modules which make frequent use of some parameters + * (e.g. picture size in macroblocks) are free to buffer them on local variables. + * This will not only avoid global variable and make the code more readable, but also + * speed it up. It will also greatly facilitate future enhancements such as the + * handling of different picture sizes in the same sequence. \n + * \n + * For now, everything is just copied to the inp_par structure (gulp) + * + ************************************************************************************** + * \par Configuration File Format + ************************************************************************************** + * Format is line oriented, maximum of one parameter per line \n + * \n + * Lines have the following format: \n + * \ = \ # Comments \\n \n + * Whitespace is space and \\t + * \par + * \ are the predefined names for Parameters and are case sensitive. + * See configfile.h for the definition of those names and their mapping to + * cfgparams->values. + * \par + * \ are either integers [0..9]* or strings. + * Integers must fit into the wordlengths, signed values are generally assumed. + * Strings containing no whitespace characters can be used directly. Strings containing + * whitespace characters are to be inclosed in double quotes ("string with whitespace") + * The double quote character is forbidden (may want to implement something smarter here). + * \par + * Any Parameters whose ParameterName is undefined lead to the termination of the program + * with an error message. + * + * \par Known bug/Shortcoming: + * zero-length strings (i.e. to signal an non-existing file + * have to be coded as "". + * + * \par Rules for using command files + * \n + * All Parameters are initially taken from DEFAULTCONFIGFILENAME, defined in configfile.h. + * If an -f \ parameter is present in the command line then this file is used to + * update the defaults of DEFAULTCONFIGFILENAME. There can be more than one -f parameters + * present. If -p parameters are present then these + * override the default and the additional config file's settings, and are themselves + * overridden by future -p parameters. There must be whitespace between -f and -p commands + * and their respective parameters + *********************************************************************** + */ + +#define INCLUDED_BY_CONFIGFILE_C + +#include + +#include "global.h" +#include "memalloc.h" +#include "config_common.h" +#include "configfile.h" +#define MAX_ITEMS_TO_PARSE 10000 + +static void PatchInp (InputParameters *p_Inp); + +/*! + *********************************************************************** + * \brief + * print help message and exit + *********************************************************************** + */ +void JMDecHelpExit (void) +{ + fprintf( stdout, "\n ldecod [-h] [-d defdec.cfg] {[-f curenc1.cfg]...[-f curencN.cfg]}" + " {[-p EncParam1=EncValue1]..[-p EncParamM=EncValueM]}\n\n" + "## Parameters\n\n" + + "## Options\n" + " -h : prints function usage\n" + " -d : use as default file for parameter initializations.\n" + " If not used then file defaults to encoder.cfg in local directory.\n" + " -f : read for reseting selected encoder parameters.\n" + " Multiple files could be used that set different parameters\n" + " -p : Set parameter to .\n" + " See default decoder.cfg file for description of all parameters.\n\n" + + "## Examples of usage:\n" + " ldecod\n" + " ldecod -h\n" + " ldecod -d default.cfg\n" + " ldecod -f curenc1.cfg\n" + " ldecod -f curenc1.cfg -p InputFile=\"e:\\data\\container_qcif_30.264\" -p OutputFile=\"dec.yuv\" -p RefFile=\"Rec.yuv\"\n"); + + exit(-1); +} + + +/*! +************************************************************************ +* \brief +* exit with error message if reading from config file failed +************************************************************************ +*/ +static inline void conf_read_check (int val, int expected) +{ + if (val != expected) + { + error ("init_conf: error reading from config file", 500); + } +} + +/*! + *********************************************************************** + * \brief + * Parse the command line parameters and read the config files. + * \param p_Vid + * VideoParameters structure for encoding + * \param p_Inp + * InputParameters structure as input configuration + * \param ac + * number of command line parameters + * \param av + * command line parameters + *********************************************************************** + */ +void ParseCommand(InputParameters *p_Inp, int ac, char *av[]) +{ + char *content = NULL; + int CLcount, ContentLen, NumberParams; + char *filename=DEFAULTCONFIGFILENAME; + + if (ac==2) + { + if (0 == strncmp (av[1], "-v", 2)) + { + printf("JM " JM ": compiled " __DATE__ " " __TIME__ "\n"); + exit(-1); + } + + if (0 == strncmp (av[1], "-h", 2)) + { + JMDecHelpExit(); + } + } + + memcpy (&cfgparams, p_Inp, sizeof (InputParameters)); + //Set default parameters. + printf ("Setting Default Parameters...\n"); + InitParams(Map); + + *p_Inp = cfgparams; + // Process default config file + CLcount = 1; + + if (ac>=3) + { + if ((strlen(av[1])==2) && (0 == strncmp (av[1], "-d", 2))) + { + if(0 == strncmp (av[2], "null", 4)) + filename=NULL; + else + filename=av[2]; + CLcount = 3; + } + if (0 == strncmp (av[1], "-h", 2)) + { + JMDecHelpExit(); + } + } + if(filename) + { + printf ("Parsing Configfile %s\n", filename); + content = GetConfigFileContent (filename); + if (NULL != content) + { + //error (errortext, 300); + ParseContent (p_Inp, Map, content, (int) strlen(content)); + printf ("\n"); + free (content); + } + } + // Parse the command line + + while (CLcount < ac) + { + if (0 == strncmp (av[CLcount], "-h", 2)) + { + JMDecHelpExit(); + } + + if (0 == strncmp (av[CLcount], "-f", 2) || 0 == strncmp (av[CLcount], "-F", 2)) // A file parameter? + { + content = GetConfigFileContent (av[CLcount+1]); + if (NULL==content) + error (errortext, 300); + printf ("Parsing Configfile %s", av[CLcount+1]); + ParseContent (p_Inp, Map, content, (int) strlen (content)); + printf ("\n"); + free (content); + CLcount += 2; + } + else if (0 == strncmp (av[CLcount], "-i", 2) || 0 == strncmp (av[CLcount], "-I", 2)) // A file parameter? + { + strncpy(p_Inp->infile, av[CLcount+1], FILE_NAME_SIZE); + CLcount += 2; + } + else if (0 == strncmp (av[CLcount], "-r", 2) || 0 == strncmp (av[CLcount], "-R", 2)) // A file parameter? + { + strncpy(p_Inp->reffile, av[CLcount+1], FILE_NAME_SIZE); + CLcount += 2; + } + else if (0 == strncmp (av[CLcount], "-o", 2) || 0 == strncmp (av[CLcount], "-O", 2)) // A file parameter? + { + strncpy(p_Inp->outfile, av[CLcount+1], FILE_NAME_SIZE); + CLcount += 2; + } + else if (0 == strncmp (av[CLcount], "-s", 2) || 0 == strncmp (av[CLcount], "-S", 2)) // A file parameter? + { + p_Inp->silent = 1; + CLcount += 1; + } + else if (0 == strncmp (av[CLcount], "-n", 2) || 0 == strncmp (av[CLcount], "-N", 2)) // A file parameter? + { + conf_read_check (sscanf(av[CLcount+1],"%d", &p_Inp->iDecFrmNum), 1); + CLcount += 2; + } +#if (MVC_EXTENSION_ENABLE) + else if (0 == strncmp (av[CLcount], "-mpr", 4) || 0 == strncmp (av[CLcount], "-MPR", 4)) // A file parameter? + { + conf_read_check (sscanf(av[CLcount+1],"%d", &p_Inp->DecodeAllLayers), 1); + CLcount += 2; + } +#endif + else if (0 == strncmp (av[CLcount], "-p", 2) || 0 == strncmp (av[CLcount], "-P", 2)) // A config change? + { + // Collect all data until next parameter (starting with - (x is any character)), + // put it into content, and parse content. + + ++CLcount; + ContentLen = 0; + NumberParams = CLcount; + + // determine the necessary size for content + while (NumberParams < ac && av[NumberParams][0] != '-') + ContentLen += (int) strlen (av[NumberParams++]); // Space for all the strings + ContentLen += 1000; // Additional 1000 bytes for spaces and \0s + + + if ((content = malloc (ContentLen))==NULL) no_mem_exit("Configure: content");; + content[0] = '\0'; + + // concatenate all parameters identified before + + while (CLcount < NumberParams) + { + char *source = &av[CLcount][0]; + char *destin = &content[(int) strlen (content)]; + + while (*source != '\0') + { + if (*source == '=') // The Parser expects whitespace before and after '=' + { + *destin++=' '; *destin++='='; *destin++=' '; // Hence make sure we add it + } + else + *destin++=*source; + source++; + } + *destin = '\0'; + CLcount++; + } + printf ("Parsing command line string '%s'", content); + ParseContent (p_Inp, Map, content, (int) strlen(content)); + free (content); + printf ("\n"); + } + else + { + snprintf (errortext, ET_SIZE, "Error in command line, ac %d, around string '%s', missing -f or -p parameters?", CLcount, av[CLcount]); + error (errortext, 300); + } + } + printf ("\n"); + + PatchInp(p_Inp); + cfgparams = *p_Inp; + p_Inp->enable_32_pulldown = 0; + if (p_Inp->bDisplayDecParams) + DisplayParams(Map, "Decoder Parameters"); +} + + +/*! + *********************************************************************** + * \brief + * Checks the input parameters for consistency. + *********************************************************************** + */ +static void PatchInp (InputParameters *p_Inp) +{ + //int i; + //int storedBplus1; + TestParams(Map, NULL); + if(p_Inp->export_views == 1) + p_Inp->dpb_plus[1] = imax(1, p_Inp->dpb_plus[1]); +} + diff --git a/ldecod/src/context_ini.c b/ldecod/src/context_ini.c new file mode 100644 index 0000000..27f244c --- /dev/null +++ b/ldecod/src/context_ini.c @@ -0,0 +1,122 @@ + +/*! + ************************************************************************************* + * \file context_ini.c + * + * \brief + * CABAC context initializations + * + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * - Detlev Marpe + * - Heiko Schwarz + ************************************************************************************** + */ + +#define CONTEXT_INI_C + +#include "defines.h" +#include "global.h" +#include "biaridecod.h" +#include "ctx_tables.h" + + +#define IBIARI_CTX_INIT2(ii,jj,ctx,tab,num, qp) \ +{ \ + for (i=0; imot_ctx; + TextureInfoContexts* tc = currSlice->tex_ctx; + int i, j; + int qp = imax(0, currSlice->qp); //p_Vid->qp); + int model_number = currSlice->model_number; + + //printf("%d -", p_Vid->currentSlice->model_number); + + //--- motion coding contexts --- + if ((currSlice->slice_type == I_SLICE)||(currSlice->slice_type == SI_SLICE)) + { + IBIARI_CTX_INIT2 (3, NUM_MB_TYPE_CTX, mc->mb_type_contexts, INIT_MB_TYPE, model_number, qp); + IBIARI_CTX_INIT2 (2, NUM_B8_TYPE_CTX, mc->b8_type_contexts, INIT_B8_TYPE, model_number, qp); + IBIARI_CTX_INIT2 (2, NUM_MV_RES_CTX, mc->mv_res_contexts, INIT_MV_RES, model_number, qp); + IBIARI_CTX_INIT2 (2, NUM_REF_NO_CTX, mc->ref_no_contexts, INIT_REF_NO, model_number, qp); + IBIARI_CTX_INIT1 ( NUM_DELTA_QP_CTX, mc->delta_qp_contexts, INIT_DELTA_QP, model_number, qp); + IBIARI_CTX_INIT1 ( NUM_MB_AFF_CTX, mc->mb_aff_contexts, INIT_MB_AFF, model_number, qp); + + //--- texture coding contexts --- + IBIARI_CTX_INIT1 ( NUM_TRANSFORM_SIZE_CTX, tc->transform_size_contexts, INIT_TRANSFORM_SIZE, model_number, qp); + IBIARI_CTX_INIT1 ( NUM_IPR_CTX, tc->ipr_contexts, INIT_IPR, model_number, qp); + IBIARI_CTX_INIT1 ( NUM_CIPR_CTX, tc->cipr_contexts, INIT_CIPR, model_number, qp); + IBIARI_CTX_INIT2 (3, NUM_CBP_CTX, tc->cbp_contexts, INIT_CBP, model_number, qp); + IBIARI_CTX_INIT2 (NUM_BLOCK_TYPES, NUM_BCBP_CTX, tc->bcbp_contexts, INIT_BCBP, model_number, qp); + IBIARI_CTX_INIT2 (NUM_BLOCK_TYPES, NUM_MAP_CTX, tc->map_contexts[0], INIT_MAP, model_number, qp); +#if ENABLE_FIELD_CTX + IBIARI_CTX_INIT2 (NUM_BLOCK_TYPES, NUM_MAP_CTX, tc->map_contexts[1], INIT_FLD_MAP, model_number, qp); + IBIARI_CTX_INIT2 (NUM_BLOCK_TYPES, NUM_LAST_CTX, tc->last_contexts[1], INIT_FLD_LAST, model_number, qp); +#endif + IBIARI_CTX_INIT2 (NUM_BLOCK_TYPES, NUM_LAST_CTX, tc->last_contexts[0], INIT_LAST, model_number, qp); + IBIARI_CTX_INIT2 (NUM_BLOCK_TYPES, NUM_ONE_CTX, tc->one_contexts, INIT_ONE, model_number, qp); + IBIARI_CTX_INIT2 (NUM_BLOCK_TYPES, NUM_ABS_CTX, tc->abs_contexts, INIT_ABS, model_number, qp); + } + else + { + PBIARI_CTX_INIT2 (3, NUM_MB_TYPE_CTX, mc->mb_type_contexts, INIT_MB_TYPE, model_number, qp); + PBIARI_CTX_INIT2 (2, NUM_B8_TYPE_CTX, mc->b8_type_contexts, INIT_B8_TYPE, model_number, qp); + PBIARI_CTX_INIT2 (2, NUM_MV_RES_CTX, mc->mv_res_contexts, INIT_MV_RES, model_number, qp); + PBIARI_CTX_INIT2 (2, NUM_REF_NO_CTX, mc->ref_no_contexts, INIT_REF_NO, model_number, qp); + PBIARI_CTX_INIT1 ( NUM_DELTA_QP_CTX, mc->delta_qp_contexts, INIT_DELTA_QP, model_number, qp); + PBIARI_CTX_INIT1 ( NUM_MB_AFF_CTX, mc->mb_aff_contexts, INIT_MB_AFF, model_number, qp); + + //--- texture coding contexts --- + PBIARI_CTX_INIT1 ( NUM_TRANSFORM_SIZE_CTX, tc->transform_size_contexts, INIT_TRANSFORM_SIZE, model_number, qp); + PBIARI_CTX_INIT1 ( NUM_IPR_CTX, tc->ipr_contexts, INIT_IPR, model_number, qp); + PBIARI_CTX_INIT1 ( NUM_CIPR_CTX, tc->cipr_contexts, INIT_CIPR, model_number, qp); + PBIARI_CTX_INIT2 (3, NUM_CBP_CTX, tc->cbp_contexts, INIT_CBP, model_number, qp); + PBIARI_CTX_INIT2 (NUM_BLOCK_TYPES, NUM_BCBP_CTX, tc->bcbp_contexts, INIT_BCBP, model_number, qp); + PBIARI_CTX_INIT2 (NUM_BLOCK_TYPES, NUM_MAP_CTX, tc->map_contexts[0], INIT_MAP, model_number, qp); +#if ENABLE_FIELD_CTX + PBIARI_CTX_INIT2 (NUM_BLOCK_TYPES, NUM_MAP_CTX, tc->map_contexts[1], INIT_FLD_MAP, model_number, qp); + PBIARI_CTX_INIT2 (NUM_BLOCK_TYPES, NUM_LAST_CTX, tc->last_contexts[1], INIT_FLD_LAST, model_number, qp); +#endif + PBIARI_CTX_INIT2 (NUM_BLOCK_TYPES, NUM_LAST_CTX, tc->last_contexts[0], INIT_LAST, model_number, qp); + PBIARI_CTX_INIT2 (NUM_BLOCK_TYPES, NUM_ONE_CTX, tc->one_contexts, INIT_ONE, model_number, qp); + PBIARI_CTX_INIT2 (NUM_BLOCK_TYPES, NUM_ABS_CTX, tc->abs_contexts, INIT_ABS, model_number, qp); + } +} + diff --git a/ldecod/src/dec_statistics.c b/ldecod/src/dec_statistics.c new file mode 100644 index 0000000..81f8038 --- /dev/null +++ b/ldecod/src/dec_statistics.c @@ -0,0 +1,71 @@ +/*! + *********************************************************************** + * \file + * dec_statistics.c + * \brief + * Decoder statistics handling. + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * - Alexis Michael Tourapis + *********************************************************************** + */ + +#include "global.h" +#include "dec_statistics.h" +#include "memalloc.h" + +/*! + *********************************************************************** + * \brief + * allocates and initializes decoder statistics memory + * \param stats + * Decoder statistics + * \return + *********************************************************************** + */ +void init_dec_stats(DecStatParameters *stats) +{ + int i, j; + int64 *hist; + for (i = 0; i < NUM_SLICE_TYPES; i++) + { + stats->frame_ctr[i] = 0; + for (j = 0; j < MAXMODE; j++) + { + stats->mode_use [i][j] = 0; + stats->mode_use_transform[i][j][0] = 0; + stats->mode_use_transform[i][j][1] = 0; + } + } + + for (i = 0; i < 2; i++) + { + for (j = 0; j < 2; j++) + { + if ((hist = (int64 *) malloc (4096 * sizeof (int64)))== NULL) + no_mem_exit ("init_dec_stats: stats->histogram_mv"); + memset(hist, 0, 4096 * sizeof (int64)); + stats->histogram_mv[i][j] = hist + 2048; + } + if ((hist = (int64 *) malloc (17 * sizeof (int64)))== NULL) + no_mem_exit ("init_dec_stats: stats->histogram_refs"); + memset(hist, 0, 17 * sizeof (int64)); + stats->histogram_refs[i] = hist + 1; + } +} + +void delete_dec_stats(DecStatParameters *stats) +{ + int i, j; + + for (i = 0; i < 2; i++) + { + for (j = 0; j < 2; j++) + { + stats->histogram_mv[i][j] -= 2048; + free(stats->histogram_mv[i][j]); + } + stats->histogram_refs[i] -= 1; + free(stats->histogram_refs[i]); + } +} diff --git a/ldecod/src/decoder_test.c b/ldecod/src/decoder_test.c new file mode 100644 index 0000000..8c61c4a --- /dev/null +++ b/ldecod/src/decoder_test.c @@ -0,0 +1,305 @@ + +/*! +*********************************************************************** +* \file +* decoder_test.c +* \brief +* H.264/AVC decoder test +* \author +* Main contributors (see contributors.h for copyright, address and affiliation details) +* - Yuwen He +*********************************************************************** +*/ + +#include "contributors.h" +#include + +//#include "global.h" +#include "win32.h" +#include "h264decoder.h" +#include "configfile.h" + +#if( DISPLAY_OUTPUTED_FRAMES ) +#include "debug_functions.h" +#endif + +#define DECOUTPUT_TEST 0 + +#define PRINT_OUTPUT_POC 0 +#define BITSTREAM_FILENAME "test.264" +#define DECRECON_FILENAME "test_dec.yuv" +#define ENCRECON_FILENAME "test_rec.yuv" +#define FCFR_DEBUG_FILENAME "fcfr_dec_rpu_stats.txt" +#define DECOUTPUT_VIEW0_FILENAME "H264_Decoder_Output_View0.yuv" +#define DECOUTPUT_VIEW1_FILENAME "H264_Decoder_Output_View1.yuv" + +//#if (_DEBUG) +//#define _CRTDBG_MAP_ALLOC +//#include +//#include +//#endif + + +static void Configure(InputParameters *p_Inp, int ac, char *av[]) +{ + //char *config_filename=NULL; + //char errortext[ET_SIZE]; + memset(p_Inp, 0, sizeof(InputParameters)); + strcpy(p_Inp->infile, BITSTREAM_FILENAME); //! set default bitstream name + strcpy(p_Inp->outfile, DECRECON_FILENAME); //! set default output file name + strcpy(p_Inp->reffile, ENCRECON_FILENAME); //! set default reference file name + + + +#ifdef _LEAKYBUCKET_ + strcpy(p_Inp->LeakyBucketParamFile,"leakybucketparam.cfg"); // file where Leaky Bucket parameters (computed by encoder) are stored +#endif + + ParseCommand(p_Inp, ac, av); + + printf("----------------------------- JM %s %s -----------------------------\n", VERSION, EXT_VERSION); + //printf(" Decoder config file : %s \n",config_filename); + if(!p_Inp->bDisplayDecParams) + { + printf("--------------------------------------------------------------------------\n"); + printf(" Input H.264 bitstream : %s \n",p_Inp->infile); + printf(" Output decoded YUV : %s \n",p_Inp->outfile); + //printf(" Output status file : %s \n",LOGFILE); + printf(" Input reference file : %s \n",p_Inp->reffile); + + printf("--------------------------------------------------------------------------\n"); +#ifdef _LEAKYBUCKET_ + printf(" Rate_decoder : %8ld \n",p_Inp->R_decoder); + printf(" B_decoder : %8ld \n",p_Inp->B_decoder); + printf(" F_decoder : %8ld \n",p_Inp->F_decoder); + printf(" LeakyBucketParamFile: %s \n",p_Inp->LeakyBucketParamFile); // Leaky Bucket Param file + calc_buffer(p_Inp); + printf("--------------------------------------------------------------------------\n"); +#endif + } + +} + +/********************************************************* +if bOutputAllFrames is 1, then output all valid frames to file onetime; +else output the first valid frame and move the buffer to the end of list; +*********************************************************/ +static int WriteOneFrame(DecodedPicList *pDecPic, int hFileOutput0, int hFileOutput1, int bOutputAllFrames) +{ + int iOutputFrame=0; + DecodedPicList *pPic = pDecPic; + + if(pPic && (((pPic->iYUVStorageFormat==2) && pPic->bValid==3) || ((pPic->iYUVStorageFormat!=2) && pPic->bValid==1)) ) + { + int i, iWidth, iHeight, iStride, iWidthUV, iHeightUV, iStrideUV; + byte *pbBuf; + int hFileOutput; + int res; + + iWidth = pPic->iWidth*((pPic->iBitDepth+7)>>3); + iHeight = pPic->iHeight; + iStride = pPic->iYBufStride; + if(pPic->iYUVFormat != YUV444) + iWidthUV = pPic->iWidth>>1; + else + iWidthUV = pPic->iWidth; + if(pPic->iYUVFormat == YUV420) + iHeightUV = pPic->iHeight>>1; + else + iHeightUV = pPic->iHeight; + iWidthUV *= ((pPic->iBitDepth+7)>>3); + iStrideUV = pPic->iUVBufStride; + + do + { + if(pPic->iYUVStorageFormat==2) + hFileOutput = (pPic->iViewId&0xffff)? hFileOutput1 : hFileOutput0; + else + hFileOutput = hFileOutput0; + if(hFileOutput >=0) + { + //Y; + pbBuf = pPic->pY; + for(i=0; iiYUVFormat != YUV400) + { + //U; + pbBuf = pPic->pU; + for(i=0; ipV; + for(i=0; iiYUVStorageFormat==2)) + { + hFileOutput = ((pPic->iViewId>>16)&0xffff)? hFileOutput1 : hFileOutput0; + if(hFileOutput>=0) + { + int iPicSize =iHeight*iStride; + //Y; + pbBuf = pPic->pY+iPicSize; + for(i=0; iiYUVFormat != YUV400) + { + iPicSize = iHeightUV*iStrideUV; + //U; + pbBuf = pPic->pU+iPicSize; + for(i=0; ipV+iPicSize; + for(i=0; iiPOC, pPic->iViewId); +#endif + pPic->bValid = 0; + pPic = pPic->pNext; + }while(pPic != NULL && pPic->bValid && bOutputAllFrames); + } +#if PRINT_OUTPUT_POC + else + printf( "\nNone frame output\n"); +#endif + + return iOutputFrame; +} + +/*! +*********************************************************************** +* \brief +* main function for JM decoder +*********************************************************************** +*/ +int main(int argc, char **argv) +{ + int iRet; + DecodedPicList *pDecPicList; + int hFileDecOutput0=-1, hFileDecOutput1=-1; + int iFramesOutput=0, iFramesDecoded=0; + InputParameters InputParams; + +#if DECOUTPUT_TEST + hFileDecOutput0 = open(DECOUTPUT_VIEW0_FILENAME, OPENFLAGS_WRITE, OPEN_PERMISSIONS); + printf( "Decoder output view0: %s\n", DECOUTPUT_VIEW0_FILENAME); + hFileDecOutput1 = open(DECOUTPUT_VIEW1_FILENAME, OPENFLAGS_WRITE, OPEN_PERMISSIONS); + printf( "Decoder output view1: %s\n", DECOUTPUT_VIEW1_FILENAME); +#endif + +//#if (_DEBUG) +// _CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF ); +//#endif + + init_time(); + + //get input parameters; + Configure(&InputParams, argc, argv); + //open decoder; + iRet = OpenDecoder(&InputParams); + if(iRet != DEC_OPEN_NOERR) + { + printf( "Open encoder failed: 0x%x!\n", iRet); + return -1; //failed; + } + + //decoding; + do + { + iRet = DecodeOneFrame(&pDecPicList); + if(iRet==DEC_EOS || iRet==DEC_SUCCEED) + { + //process the decoded picture, output or display; + iFramesOutput += WriteOneFrame(pDecPicList, hFileDecOutput0, hFileDecOutput1, 0); + iFramesDecoded++; + } + else + { + //error handling; + printf( "Error in decoding process: 0x%x\n", iRet); + } + }while((iRet == DEC_SUCCEED) && ((p_Dec->p_Inp->iDecFrmNum==0) || (iFramesDecodedp_Inp->iDecFrmNum))); + + iRet = FinitDecoder(&pDecPicList); + iFramesOutput += WriteOneFrame(pDecPicList, hFileDecOutput0, hFileDecOutput1 , 1); + iRet = CloseDecoder(); + + //quit; + if(hFileDecOutput0>=0) + { + close(hFileDecOutput0); + } + if(hFileDecOutput1>=0) + { + close(hFileDecOutput1); + } + +// printf("%d frames are decoded.\n", iFramesDecoded); + +#if( DISPLAY_OUTPUTED_FRAMES ) + Debug_Close_Windows("Outputed Frame"); +#endif + +//#if (_DEBUG) +// _CrtDumpMemoryLeaks(); +// //_crtBreakAlloc=19044 +//#if( WIN32 ) +// system("pause"); +//#endif +//#endif + fflush(stdout); + return 0; +} + + diff --git a/ldecod/src/erc_api.c b/ldecod/src/erc_api.c new file mode 100644 index 0000000..c215255 --- /dev/null +++ b/ldecod/src/erc_api.c @@ -0,0 +1,482 @@ + +/*! + ************************************************************************************* + * \file erc_api.c + * + * \brief + * External (still inside video decoder) interface for error concealment module + * + * \author + * - Ari Hourunranta + * - Viktor Varsa + * - Ye-Kui Wang + * + ************************************************************************************* + */ + + +#include "global.h" +#include "memalloc.h" +#include "erc_api.h" +#include "fast_memory.h" + + +/*! +************************************************************************ +* \brief +* write the encoding mode and motion vectors of current +* MB to the buffer of the error concealment module. +************************************************************************ +*/ +void ercWriteMBMODEandMV(Macroblock *currMB) +{ + VideoParameters *p_Vid = currMB->p_Vid; + int i, ii, jj, currMBNum = currMB->mbAddrX; //p_Vid->currentSlice->current_mb_nr; + StorablePicture *dec_picture = p_Vid->dec_picture; + int mbx = xPosMB(currMBNum, dec_picture->size_x), mby = yPosMB(currMBNum, dec_picture->size_x); + objectBuffer_t *currRegion, *pRegion; + + currRegion = p_Vid->erc_object_list + (currMBNum<<2); + + if(p_Vid->type != B_SLICE) //non-B frame + { + for (i=0; i<4; ++i) + { + pRegion = currRegion + i; + pRegion->regionMode = (currMB->mb_type ==I16MB ? REGMODE_INTRA : + currMB->b8mode[i]==IBLOCK ? REGMODE_INTRA_8x8 : + currMB->b8mode[i]==0 ? REGMODE_INTER_COPY : + currMB->b8mode[i]==1 ? REGMODE_INTER_PRED : REGMODE_INTER_PRED_8x8); + if (currMB->b8mode[i]==0 || currMB->b8mode[i]==IBLOCK) // INTRA OR COPY + { + pRegion->mv[0] = 0; + pRegion->mv[1] = 0; + pRegion->mv[2] = 0; + } + else + { + ii = 4*mbx + (i & 0x01)*2;// + BLOCK_SIZE; + jj = 4*mby + (i >> 1 )*2; + if (currMB->b8mode[i]>=5 && currMB->b8mode[i]<=7) // SMALL BLOCKS + { + pRegion->mv[0] = (dec_picture->mv_info[jj][ii].mv[LIST_0].mv_x + dec_picture->mv_info[jj][ii + 1].mv[LIST_0].mv_x + dec_picture->mv_info[jj + 1][ii].mv[LIST_0].mv_x + dec_picture->mv_info[jj + 1][ii + 1].mv[LIST_0].mv_x + 2)/4; + pRegion->mv[1] = (dec_picture->mv_info[jj][ii].mv[LIST_0].mv_y + dec_picture->mv_info[jj][ii + 1].mv[LIST_0].mv_y + dec_picture->mv_info[jj + 1][ii].mv[LIST_0].mv_y + dec_picture->mv_info[jj + 1][ii + 1].mv[LIST_0].mv_y + 2)/4; + } + else // 16x16, 16x8, 8x16, 8x8 + { + pRegion->mv[0] = dec_picture->mv_info[jj][ii].mv[LIST_0].mv_x; + pRegion->mv[1] = dec_picture->mv_info[jj][ii].mv[LIST_0].mv_y; + // pRegion->mv[0] = dec_picture->motion.mv[LIST_0][4*mby+(i/2)*2][4*mbx+(i%2)*2+BLOCK_SIZE][0]; + // pRegion->mv[1] = dec_picture->motion.mv[LIST_0][4*mby+(i/2)*2][4*mbx+(i%2)*2+BLOCK_SIZE][1]; + } + currMB->p_Slice->erc_mvperMB += iabs(pRegion->mv[0]) + iabs(pRegion->mv[1]); + pRegion->mv[2] = dec_picture->mv_info[jj][ii].ref_idx[LIST_0]; + } + } + } + else //B-frame + { + for (i=0; i<4; ++i) + { + ii = 4*mbx + (i%2)*2;// + BLOCK_SIZE; + jj = 4*mby + (i/2)*2; + pRegion = currRegion + i; + pRegion->regionMode = (currMB->mb_type ==I16MB ? REGMODE_INTRA : + currMB->b8mode[i]==IBLOCK ? REGMODE_INTRA_8x8 : REGMODE_INTER_PRED_8x8); + if (currMB->mb_type==I16MB || currMB->b8mode[i]==IBLOCK) // INTRA + { + pRegion->mv[0] = 0; + pRegion->mv[1] = 0; + pRegion->mv[2] = 0; + } + else + { + int idx = (dec_picture->mv_info[jj][ii].ref_idx[0] < 0) ? 1 : 0; + // int idx = (currMB->b8mode[i]==0 && currMB->b8pdir[i]==2 ? LIST_0 : currMB->b8pdir[i]==1 ? LIST_1 : LIST_0); + // int idx = currMB->b8pdir[i]==0 ? LIST_0 : LIST_1; + pRegion->mv[0] = (dec_picture->mv_info[jj][ii].mv[idx].mv_x + + dec_picture->mv_info[jj][ii+1].mv[idx].mv_x + + dec_picture->mv_info[jj+1][ii].mv[idx].mv_x + + dec_picture->mv_info[jj+1][ii+1].mv[idx].mv_x + 2)/4; + pRegion->mv[1] = (dec_picture->mv_info[jj][ii].mv[idx].mv_y + + dec_picture->mv_info[jj][ii+1].mv[idx].mv_y + + dec_picture->mv_info[jj+1][ii].mv[idx].mv_y + + dec_picture->mv_info[jj+1][ii+1].mv[idx].mv_y + 2)/4; + currMB->p_Slice->erc_mvperMB += iabs(pRegion->mv[0]) + iabs(pRegion->mv[1]); + + pRegion->mv[2] = (dec_picture->mv_info[jj][ii].ref_idx[idx]); + /* + if (currMB->b8pdir[i]==0 || (currMB->b8pdir[i]==2 && currMB->b8mode[i]!=0)) // forward or bidirect + { + pRegion->mv[2] = (dec_picture->motion.ref_idx[LIST_0][jj][ii]); + ///???? is it right, not only "p_Vid->fw_refFrArr[jj][ii-4]" + } + else + { + pRegion->mv[2] = (dec_picture->motion.ref_idx[LIST_1][jj][ii]); + // pRegion->mv[2] = 0; + } + */ + } + } + } +} + +/*! + ************************************************************************ + * \brief + * Initinize the error concealment module + ************************************************************************ + */ +void ercInit(VideoParameters *p_Vid, int pic_sizex, int pic_sizey, int flag) +{ + ercClose(p_Vid, p_Vid->erc_errorVar); + p_Vid->erc_object_list = (objectBuffer_t *) calloc((pic_sizex * pic_sizey) >> 6, sizeof(objectBuffer_t)); + if (p_Vid->erc_object_list == NULL) no_mem_exit("ercInit: erc_object_list"); + + // the error concealment instance is allocated + p_Vid->erc_errorVar = ercOpen(); + + // set error concealment ON + ercSetErrorConcealment(p_Vid->erc_errorVar, flag); +} + +/*! + ************************************************************************ + * \brief + * Allocates data structures used in error concealment. + *\return + * The allocated ercVariables_t is returned. + ************************************************************************ + */ +ercVariables_t *ercOpen( void ) +{ + ercVariables_t *errorVar = NULL; + + errorVar = (ercVariables_t *)malloc( sizeof(ercVariables_t)); + if ( errorVar == NULL ) no_mem_exit("ercOpen: errorVar"); + + errorVar->nOfMBs = 0; + errorVar->segments = NULL; + errorVar->currSegment = 0; + errorVar->yCondition = NULL; + errorVar->uCondition = NULL; + errorVar->vCondition = NULL; + errorVar->prevFrameYCondition = NULL; + + errorVar->concealment = 1; + + return errorVar; +} + +/*! + ************************************************************************ + * \brief + * Resets the variables used in error detection. + * Should be called always when starting to decode a new frame. + * \param errorVar + * Variables for error concealment + * \param nOfMBs + * Number of macroblocks in a frame + * \param numOfSegments + * Estimated number of segments (memory reserved) + * \param picSizeX + * Width of the frame in pixels. + ************************************************************************ + */ +void ercReset( ercVariables_t *errorVar, int nOfMBs, int numOfSegments, int picSizeX ) +{ + char *tmp = NULL; + int i = 0; + + if ( errorVar && errorVar->concealment ) + { + ercSegment_t *segments = NULL; + // If frame size has been changed + if ( nOfMBs != errorVar->nOfMBs && errorVar->yCondition != NULL ) + { + free( errorVar->yCondition ); + errorVar->yCondition = NULL; + free( errorVar->prevFrameYCondition ); + errorVar->prevFrameYCondition = NULL; + free( errorVar->uCondition ); + errorVar->uCondition = NULL; + free( errorVar->vCondition ); + errorVar->vCondition = NULL; + free( errorVar->segments ); + errorVar->segments = NULL; + } + + // If the structures are uninitialized (first frame, or frame size is changed) + if ( errorVar->yCondition == NULL ) + { + errorVar->segments = (ercSegment_t *)malloc( numOfSegments*sizeof(ercSegment_t) ); + if ( errorVar->segments == NULL ) no_mem_exit("ercReset: errorVar->segments"); + fast_memset( errorVar->segments, 0, numOfSegments*sizeof(ercSegment_t)); + errorVar->nOfSegments = numOfSegments; + + errorVar->yCondition = (char *)malloc( 4*nOfMBs*sizeof(char) ); + if ( errorVar->yCondition == NULL ) no_mem_exit("ercReset: errorVar->yCondition"); + errorVar->prevFrameYCondition = (char *)malloc( 4*nOfMBs*sizeof(char) ); + if ( errorVar->prevFrameYCondition == NULL ) no_mem_exit("ercReset: errorVar->prevFrameYCondition"); + errorVar->uCondition = (char *)malloc( nOfMBs*sizeof(char) ); + if ( errorVar->uCondition == NULL ) no_mem_exit("ercReset: errorVar->uCondition"); + errorVar->vCondition = (char *)malloc( nOfMBs*sizeof(char) ); + if ( errorVar->vCondition == NULL ) no_mem_exit("ercReset: errorVar->vCondition"); + errorVar->nOfMBs = nOfMBs; + } + else + { + // Store the yCondition struct of the previous frame + tmp = errorVar->prevFrameYCondition; + errorVar->prevFrameYCondition = errorVar->yCondition; + errorVar->yCondition = tmp; + } + + // Reset tables and parameters + fast_memset( errorVar->yCondition, 0, 4*nOfMBs*sizeof(*errorVar->yCondition)); + fast_memset( errorVar->uCondition, 0, nOfMBs*sizeof(*errorVar->uCondition)); + fast_memset( errorVar->vCondition, 0, nOfMBs*sizeof(*errorVar->vCondition)); + + if (errorVar->nOfSegments != numOfSegments) + { + free( errorVar->segments ); + errorVar->segments = NULL; + errorVar->segments = (ercSegment_t *)malloc( numOfSegments*sizeof(ercSegment_t) ); + if ( errorVar->segments == NULL ) no_mem_exit("ercReset: errorVar->segments"); + errorVar->nOfSegments = numOfSegments; + } + + //memset( errorVar->segments, 0, errorVar->nOfSegments * sizeof(ercSegment_t)); + + segments = errorVar->segments; + for ( i = 0; i < errorVar->nOfSegments; i++ ) + { + segments->startMBPos = 0; + segments->endMBPos = (short) (nOfMBs - 1); + (segments++)->fCorrupted = 1; //! mark segments as corrupted + } + + errorVar->currSegment = 0; + errorVar->nOfCorruptedSegments = 0; + } +} + +/*! + ************************************************************************ + * \brief + * Resets the variables used in error detection. + * Should be called always when starting to decode a new frame. + * \param p_Vid + * VideoParameters variable + * \param errorVar + * Variables for error concealment + ************************************************************************ + */ +void ercClose(VideoParameters *p_Vid, ercVariables_t *errorVar ) +{ + if ( errorVar != NULL ) + { + if (errorVar->yCondition != NULL) + { + free( errorVar->segments ); + free( errorVar->yCondition ); + free( errorVar->uCondition ); + free( errorVar->vCondition ); + free( errorVar->prevFrameYCondition ); + } + free( errorVar ); + errorVar = NULL; + } + + if (p_Vid->erc_object_list) + { + free(p_Vid->erc_object_list); + p_Vid->erc_object_list=NULL; + } +} + +/*! + ************************************************************************ + * \brief + * Sets error concealment ON/OFF. Can be invoked only between frames, not during a frame + * \param errorVar + * Variables for error concealment + * \param value + * New value + ************************************************************************ + */ +void ercSetErrorConcealment( ercVariables_t *errorVar, int value ) +{ + if ( errorVar != NULL ) + errorVar->concealment = value; +} + +/*! + ************************************************************************ + * \brief + * Creates a new segment in the segment-list, and marks the start MB and bit position. + * If the end of the previous segment was not explicitly marked by "ercStopSegment", + * also marks the end of the previous segment. + * If needed, it reallocates the segment-list for a larger storage place. + * \param currMBNum + * The MB number where the new slice/segment starts + * \param segment + * Segment/Slice No. counted by the caller + * \param bitPos + * Bitstream pointer: number of bits read from the buffer. + * \param errorVar + * Variables for error detector + ************************************************************************ + */ +void ercStartSegment( int currMBNum, int segment, unsigned int bitPos, ercVariables_t *errorVar ) +{ + if ( errorVar && errorVar->concealment ) + { + errorVar->currSegmentCorrupted = 0; + + errorVar->segments[ segment ].fCorrupted = 0; + errorVar->segments[ segment ].startMBPos = (short) currMBNum; + + } +} + +/*! + ************************************************************************ + * \brief + * Marks the end position of a segment. + * \param currMBNum + * The last MB number of the previous segment + * \param segment + * Segment/Slice No. counted by the caller + * If (segment<0) the internal segment counter is used. + * \param bitPos + * Bitstream pointer: number of bits read from the buffer. + * \param errorVar + * Variables for error detector + ************************************************************************ + */ +void ercStopSegment( int currMBNum, int segment, unsigned int bitPos, ercVariables_t *errorVar ) +{ + if ( errorVar && errorVar->concealment ) + { + errorVar->segments[ segment ].endMBPos = (short) currMBNum; + errorVar->currSegment++; + } +} + +/*! + ************************************************************************ + * \brief + * Marks the current segment (the one which has the "currMBNum" MB in it) + * as lost: all the blocks of the MBs in the segment as corrupted. + * \param picSizeX + * Width of the frame in pixels. + * \param errorVar + * Variables for error detector + ************************************************************************ + */ +void ercMarkCurrSegmentLost(int picSizeX, ercVariables_t *errorVar ) +{ + int j = 0; + int current_segment; + + current_segment = errorVar->currSegment-1; + if ( errorVar && errorVar->concealment ) + { + if (errorVar->currSegmentCorrupted == 0) + { + errorVar->nOfCorruptedSegments++; + errorVar->currSegmentCorrupted = 1; + } + + for ( j = errorVar->segments[current_segment].startMBPos; j <= errorVar->segments[current_segment].endMBPos; j++ ) + { + errorVar->yCondition[MBNum2YBlock (j, 0, picSizeX)] = ERC_BLOCK_CORRUPTED; + errorVar->yCondition[MBNum2YBlock (j, 1, picSizeX)] = ERC_BLOCK_CORRUPTED; + errorVar->yCondition[MBNum2YBlock (j, 2, picSizeX)] = ERC_BLOCK_CORRUPTED; + errorVar->yCondition[MBNum2YBlock (j, 3, picSizeX)] = ERC_BLOCK_CORRUPTED; + errorVar->uCondition[j] = ERC_BLOCK_CORRUPTED; + errorVar->vCondition[j] = ERC_BLOCK_CORRUPTED; + } + errorVar->segments[current_segment].fCorrupted = 1; + } +} + +/*! + ************************************************************************ + * \brief + * Marks the current segment (the one which has the "currMBNum" MB in it) + * as OK: all the blocks of the MBs in the segment as OK. + * \param picSizeX + * Width of the frame in pixels. + * \param errorVar + * Variables for error detector + ************************************************************************ + */ +void ercMarkCurrSegmentOK(int picSizeX, ercVariables_t *errorVar ) +{ + int j = 0; + int current_segment; + + current_segment = errorVar->currSegment-1; + if ( errorVar && errorVar->concealment ) + { + // mark all the Blocks belonging to the segment as OK */ + for ( j = errorVar->segments[current_segment].startMBPos; j <= errorVar->segments[current_segment].endMBPos; j++ ) + { + errorVar->yCondition[MBNum2YBlock (j, 0, picSizeX)] = ERC_BLOCK_OK; + errorVar->yCondition[MBNum2YBlock (j, 1, picSizeX)] = ERC_BLOCK_OK; + errorVar->yCondition[MBNum2YBlock (j, 2, picSizeX)] = ERC_BLOCK_OK; + errorVar->yCondition[MBNum2YBlock (j, 3, picSizeX)] = ERC_BLOCK_OK; + errorVar->uCondition[j] = ERC_BLOCK_OK; + errorVar->vCondition[j] = ERC_BLOCK_OK; + } + errorVar->segments[current_segment].fCorrupted = 0; + } +} + +/*! + ************************************************************************ + * \brief + * Marks the Blocks of the given component (YUV) of the current MB as concealed. + * \param currMBNum + * Selects the segment where this MB number is in. + * \param comp + * Component to mark (0:Y, 1:U, 2:V, <0:All) + * \param picSizeX + * Width of the frame in pixels. + * \param errorVar + * Variables for error detector + ************************************************************************ + */ +void ercMarkCurrMBConcealed( int currMBNum, int comp, int picSizeX, ercVariables_t *errorVar ) +{ + int setAll = 0; + + if ( errorVar && errorVar->concealment ) + { + if (comp < 0) + { + setAll = 1; + comp = 0; + } + + switch (comp) + { + case 0: + errorVar->yCondition[MBNum2YBlock (currMBNum, 0, picSizeX)] = ERC_BLOCK_CONCEALED; + errorVar->yCondition[MBNum2YBlock (currMBNum, 1, picSizeX)] = ERC_BLOCK_CONCEALED; + errorVar->yCondition[MBNum2YBlock (currMBNum, 2, picSizeX)] = ERC_BLOCK_CONCEALED; + errorVar->yCondition[MBNum2YBlock (currMBNum, 3, picSizeX)] = ERC_BLOCK_CONCEALED; + if (!setAll) + break; + case 1: + errorVar->uCondition[currMBNum] = ERC_BLOCK_CONCEALED; + if (!setAll) + break; + case 2: + errorVar->vCondition[currMBNum] = ERC_BLOCK_CONCEALED; + } + } +} diff --git a/ldecod/src/erc_do_i.c b/ldecod/src/erc_do_i.c new file mode 100644 index 0000000..cb1c8ff --- /dev/null +++ b/ldecod/src/erc_do_i.c @@ -0,0 +1,544 @@ + +/*! + ************************************************************************************* + * \file + * erc_do_i.c + * + * \brief + * Intra (I) frame error concealment algorithms for decoder + * + * \author + * - Ari Hourunranta + * - Viktor Varsa + * - Ye-Kui Wang + * + ************************************************************************************* + */ + +#include "global.h" +#include "erc_do.h" + +static void concealBlocks ( VideoParameters *p_Vid, int lastColumn, int lastRow, int comp, frame *recfr, int picSizeX, char *condition ); +static void pixMeanInterpolateBlock( VideoParameters *p_Vid, imgpel *src[], imgpel *block, int blockSize, int frameWidth ); + +/*! + ************************************************************************ + * \brief + * The main function for Intra frame concealment. + * Calls "concealBlocks" for each color component (Y,U,V) separately + * \return + * 0, if the concealment was not successful and simple concealment should be used + * 1, otherwise (even if none of the blocks were concealed) + * \param p_Vid + * video encoding parameters for current picture + * \param recfr + * Reconstructed frame buffer + * \param picSizeX + * Width of the frame in pixels + * \param picSizeY + * Height of the frame in pixels + * \param errorVar + * Variables for error concealment + ************************************************************************ + */ +int ercConcealIntraFrame( VideoParameters *p_Vid, frame *recfr, int picSizeX, int picSizeY, ercVariables_t *errorVar ) +{ + int lastColumn = 0, lastRow = 0; + + // if concealment is on + if ( errorVar && errorVar->concealment ) + { + // if there are segments to be concealed + if ( errorVar->nOfCorruptedSegments ) + { + // Y + lastRow = (int) (picSizeY>>3); + lastColumn = (int) (picSizeX>>3); + concealBlocks( p_Vid, lastColumn, lastRow, 0, recfr, picSizeX, errorVar->yCondition ); + + // U (dimensions halved compared to Y) + lastRow = (int) (picSizeY>>4); + lastColumn = (int) (picSizeX>>4); + concealBlocks( p_Vid, lastColumn, lastRow, 1, recfr, picSizeX, errorVar->uCondition ); + + // V ( dimensions equal to U ) + concealBlocks( p_Vid, lastColumn, lastRow, 2, recfr, picSizeX, errorVar->vCondition ); + } + return 1; + } + else + return 0; +} + +/*! + ************************************************************************ + * \brief + * Conceals the MB at position (row, column) using pixels from predBlocks[] + * using pixMeanInterpolateBlock() + * \param p_Vid + * video encoding parameters for current picture + * \param currFrame + * current frame + * \param row + * y coordinate in blocks + * \param column + * x coordinate in blocks + * \param predBlocks[] + * list of neighboring source blocks (numbering 0 to 7, 1 means: use the neighbor) + * \param frameWidth + * width of frame in pixels + * \param mbWidthInBlocks + * 2 for Y, 1 for U/V components + ************************************************************************ + */ +void ercPixConcealIMB(VideoParameters *p_Vid, imgpel *currFrame, int row, int column, int predBlocks[], int frameWidth, int mbWidthInBlocks) +{ + imgpel *src[8]={NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}; + imgpel *currBlock = NULL; + + // collect the reliable neighboring blocks + if (predBlocks[0]) + src[0] = currFrame + (row-mbWidthInBlocks)*frameWidth*8 + (column+mbWidthInBlocks)*8; + if (predBlocks[1]) + src[1] = currFrame + (row-mbWidthInBlocks)*frameWidth*8 + (column-mbWidthInBlocks)*8; + if (predBlocks[2]) + src[2] = currFrame + (row+mbWidthInBlocks)*frameWidth*8 + (column-mbWidthInBlocks)*8; + if (predBlocks[3]) + src[3] = currFrame + (row+mbWidthInBlocks)*frameWidth*8 + (column+mbWidthInBlocks)*8; + if (predBlocks[4]) + src[4] = currFrame + (row-mbWidthInBlocks)*frameWidth*8 + column*8; + if (predBlocks[5]) + src[5] = currFrame + row*frameWidth*8 + (column-mbWidthInBlocks)*8; + if (predBlocks[6]) + src[6] = currFrame + (row+mbWidthInBlocks)*frameWidth*8 + column*8; + if (predBlocks[7]) + src[7] = currFrame + row*frameWidth*8 + (column+mbWidthInBlocks)*8; + + currBlock = currFrame + row*frameWidth*8 + column*8; + pixMeanInterpolateBlock( p_Vid, src, currBlock, mbWidthInBlocks*8, frameWidth ); +} + +/*! + ************************************************************************ + * \brief + * This function checks the neighbors of a Macroblock for usability in + * concealment. First the OK macroblocks are marked, and if there is not + * enough of them, then the CONCEALED ones as well. + * A "1" in the the output array means reliable, a "0" non reliable MB. + * The block order in "predBlocks": + * 1 4 0 + * 5 x 7 + * 2 6 3 + * i.e., corners first. + * \return + * Number of useable neighbor macroblocks for concealment. + * \param predBlocks[] + * Array for indicating the valid neighbor blocks + * \param currRow + * Current block row in the frame + * \param currColumn + * Current block column in the frame + * \param condition + * The block condition (ok, lost) table + * \param maxRow + * Number of block rows in the frame + * \param maxColumn + * Number of block columns in the frame + * \param step + * Number of blocks belonging to a MB, when counting + * in vertical/horizontal direction. (Y:2 U,V:1) + * \param fNoCornerNeigh + * No corner neighbors are considered + ************************************************************************ + */ +int ercCollect8PredBlocks( int predBlocks[], int currRow, int currColumn, char *condition, + int maxRow, int maxColumn, int step, byte fNoCornerNeigh ) +{ + int srcCounter = 0; + int srcCountMin = (fNoCornerNeigh ? 2 : 4); + int threshold = ERC_BLOCK_OK; + + memset( predBlocks, 0, 8*sizeof(int) ); + + // collect the reliable neighboring blocks + do + { + srcCounter = 0; + // top + if (currRow > 0 && condition[ (currRow-1)*maxColumn + currColumn ] >= threshold ) + { //ERC_BLOCK_OK (3) or ERC_BLOCK_CONCEALED (2) + predBlocks[4] = condition[ (currRow-1)*maxColumn + currColumn ]; + srcCounter++; + } + // bottom + if ( currRow < (maxRow-step) && condition[ (currRow+step)*maxColumn + currColumn ] >= threshold ) + { + predBlocks[6] = condition[ (currRow+step)*maxColumn + currColumn ]; + srcCounter++; + } + + if ( currColumn > 0 ) + { + // left + if ( condition[ currRow*maxColumn + currColumn - 1 ] >= threshold ) + { + predBlocks[5] = condition[ currRow*maxColumn + currColumn - 1 ]; + srcCounter++; + } + + if ( !fNoCornerNeigh ) + { + // top-left + if ( currRow > 0 && condition[ (currRow-1)*maxColumn + currColumn - 1 ] >= threshold ) + { + predBlocks[1] = condition[ (currRow-1)*maxColumn + currColumn - 1 ]; + srcCounter++; + } + // bottom-left + if ( currRow < (maxRow-step) && condition[ (currRow+step)*maxColumn + currColumn - 1 ] >= threshold ) + { + predBlocks[2] = condition[ (currRow+step)*maxColumn + currColumn - 1 ]; + srcCounter++; + } + } + } + + if ( currColumn < (maxColumn-step) ) + { + // right + if ( condition[ currRow*maxColumn+currColumn + step ] >= threshold ) + { + predBlocks[7] = condition[ currRow*maxColumn+currColumn + step ]; + srcCounter++; + } + + if ( !fNoCornerNeigh ) + { + // top-right + if ( currRow > 0 && condition[ (currRow-1)*maxColumn + currColumn + step ] >= threshold ) + { + predBlocks[0] = condition[ (currRow-1)*maxColumn + currColumn + step ]; + srcCounter++; + } + // bottom-right + if ( currRow < (maxRow-step) && condition[ (currRow+step)*maxColumn + currColumn + step ] >= threshold ) + { + predBlocks[3] = condition[ (currRow+step)*maxColumn + currColumn + step ]; + srcCounter++; + } + } + } + // prepare for the next round + threshold--; + if (threshold < ERC_BLOCK_CONCEALED) + break; + } while ( srcCounter < srcCountMin); + + return srcCounter; +} + +/*! + ************************************************************************ + * \brief + * collects prediction blocks only from the current column + * \return + * Number of usable neighbour Macroblocks for concealment. + * \param predBlocks[] + * Array for indicating the valid neighbor blocks + * \param currRow + * Current block row in the frame + * \param currColumn + * Current block column in the frame + * \param condition + * The block condition (ok, lost) table + * \param maxRow + * Number of block rows in the frame + * \param maxColumn + * Number of block columns in the frame + * \param step + * Number of blocks belonging to a MB, when counting + * in vertical/horizontal direction. (Y:2 U,V:1) + ************************************************************************ + */ +int ercCollectColumnBlocks( int predBlocks[], int currRow, int currColumn, char *condition, int maxRow, int maxColumn, int step ) +{ + int srcCounter = 0, threshold = ERC_BLOCK_CORRUPTED; + + memset( predBlocks, 0, 8*sizeof(int) ); + + // in this case, row > 0 and row < 17 + if ( condition[ (currRow-1)*maxColumn + currColumn ] > threshold ) + { + predBlocks[4] = 1; + srcCounter++; + } + if ( condition[ (currRow+step)*maxColumn + currColumn ] > threshold ) + { + predBlocks[6] = 1; + srcCounter++; + } + + return srcCounter; +} + +/*! + ************************************************************************ + * \brief + * Core for the Intra blocks concealment. + * It is called for each color component (Y,U,V) separately + * Finds the corrupted blocks and calls pixel interpolation functions + * to correct them, one block at a time. + * Scanning is done vertically and each corrupted column is corrected + * bi-directionally, i.e., first block, last block, first block+1, last block -1 ... + * \param p_Vid + * video encoding parameters for current picture + * \param lastColumn + * Number of block columns in the frame + * \param lastRow + * Number of block rows in the frame + * \param comp + * color component + * \param recfr + * Reconstructed frame buffer + * \param picSizeX + * Width of the frame in pixels + * \param condition + * The block condition (ok, lost) table + ************************************************************************ + */ +static void concealBlocks( VideoParameters *p_Vid, int lastColumn, int lastRow, int comp, frame *recfr, int picSizeX, char *condition ) +{ + int row, column, srcCounter = 0, thr = ERC_BLOCK_CORRUPTED, + lastCorruptedRow = -1, firstCorruptedRow = -1, currRow = 0, + areaHeight = 0, i = 0, smoothColumn = 0; + int predBlocks[8], step = 1; + + // in the Y component do the concealment MB-wise (not block-wise): + // this is useful if only whole MBs can be damaged or lost + if ( comp == 0 ) + step = 2; + else + step = 1; + + for ( column = 0; column < lastColumn; column += step ) + { + for ( row = 0; row < lastRow; row += step ) + { + if ( condition[row*lastColumn+column] <= thr ) + { + firstCorruptedRow = row; + // find the last row which has corrupted blocks (in same continuous area) + for ( lastCorruptedRow = row+step; lastCorruptedRow < lastRow; lastCorruptedRow += step ) + { + // check blocks in the current column + if ( condition[ lastCorruptedRow*lastColumn + column ] > thr ) + { + // current one is already OK, so the last was the previous one + lastCorruptedRow -= step; + break; + } + } + if ( lastCorruptedRow >= lastRow ) + { + // correct only from above + lastCorruptedRow = lastRow-step; + for ( currRow = firstCorruptedRow; currRow < lastRow; currRow += step ) + { + srcCounter = ercCollect8PredBlocks( predBlocks, currRow, column, condition, lastRow, lastColumn, step, 1 ); + + switch( comp ) + { + case 0 : + ercPixConcealIMB( p_Vid, recfr->yptr, currRow, column, predBlocks, picSizeX, 2 ); + break; + case 1 : + ercPixConcealIMB( p_Vid, recfr->uptr, currRow, column, predBlocks, (picSizeX>>1), 1 ); + break; + case 2 : + ercPixConcealIMB( p_Vid, recfr->vptr, currRow, column, predBlocks, (picSizeX>>1), 1 ); + break; + } + + if ( comp == 0 ) + { + condition[ currRow*lastColumn+column] = ERC_BLOCK_CONCEALED; + condition[ currRow*lastColumn+column + 1] = ERC_BLOCK_CONCEALED; + condition[ currRow*lastColumn+column + lastColumn] = ERC_BLOCK_CONCEALED; + condition[ currRow*lastColumn+column + lastColumn + 1] = ERC_BLOCK_CONCEALED; + } + else + { + condition[ currRow*lastColumn+column] = ERC_BLOCK_CONCEALED; + } + + } + row = lastRow; + } + else if ( firstCorruptedRow == 0 ) + { + // correct only from below + for ( currRow = lastCorruptedRow; currRow >= 0; currRow -= step ) + { + srcCounter = ercCollect8PredBlocks( predBlocks, currRow, column, condition, lastRow, lastColumn, step, 1 ); + + switch( comp ) + { + case 0 : + ercPixConcealIMB( p_Vid, recfr->yptr, currRow, column, predBlocks, picSizeX, 2 ); + break; + case 1 : + ercPixConcealIMB( p_Vid, recfr->uptr, currRow, column, predBlocks, (picSizeX>>1), 1 ); + break; + case 2 : + ercPixConcealIMB( p_Vid, recfr->vptr, currRow, column, predBlocks, (picSizeX>>1), 1 ); + break; + } + + if ( comp == 0 ) + { + condition[ currRow*lastColumn+column] = ERC_BLOCK_CONCEALED; + condition[ currRow*lastColumn+column + 1] = ERC_BLOCK_CONCEALED; + condition[ currRow*lastColumn+column + lastColumn] = ERC_BLOCK_CONCEALED; + condition[ currRow*lastColumn+column + lastColumn + 1] = ERC_BLOCK_CONCEALED; + } + else + { + condition[ currRow*lastColumn+column] = ERC_BLOCK_CONCEALED; + } + + } + + row = lastCorruptedRow+step; + } + else + { + // correct bi-directionally + + row = lastCorruptedRow+step; + areaHeight = lastCorruptedRow-firstCorruptedRow+step; + + // Conceal the corrupted area switching between the up and the bottom rows + for ( i = 0; i < areaHeight; i += step ) + { + if ( i % 2 ) + { + currRow = lastCorruptedRow; + lastCorruptedRow -= step; + } + else + { + currRow = firstCorruptedRow; + firstCorruptedRow += step; + } + + if (smoothColumn > 0) + { + srcCounter = ercCollectColumnBlocks( predBlocks, currRow, column, condition, lastRow, lastColumn, step ); + } + else + { + srcCounter = ercCollect8PredBlocks( predBlocks, currRow, column, condition, lastRow, lastColumn, step, 1 ); + } + + switch( comp ) + { + case 0 : + ercPixConcealIMB( p_Vid, recfr->yptr, currRow, column, predBlocks, picSizeX, 2 ); + break; + + case 1 : + ercPixConcealIMB( p_Vid, recfr->uptr, currRow, column, predBlocks, (picSizeX>>1), 1 ); + break; + + case 2 : + ercPixConcealIMB( p_Vid, recfr->vptr, currRow, column, predBlocks, (picSizeX>>1), 1 ); + break; + } + + if ( comp == 0 ) + { + condition[ currRow*lastColumn+column] = ERC_BLOCK_CONCEALED; + condition[ currRow*lastColumn+column + 1] = ERC_BLOCK_CONCEALED; + condition[ currRow*lastColumn+column + lastColumn] = ERC_BLOCK_CONCEALED; + condition[ currRow*lastColumn+column + lastColumn + 1] = ERC_BLOCK_CONCEALED; + } + else + { + condition[ currRow*lastColumn+column ] = ERC_BLOCK_CONCEALED; + } + } + } + + lastCorruptedRow = -1; + firstCorruptedRow = -1; + + } + } + } +} + +/*! + ************************************************************************ + * \brief + * Does the actual pixel based interpolation for block[] + * using weighted average + * \param p_Vid + * video encoding parameters for current picture + * \param src[] + * pointers to neighboring source blocks + * \param block + * destination block + * \param blockSize + * 16 for Y, 8 for U/V components + * \param frameWidth + * Width of the frame in pixels + ************************************************************************ + */ +static void pixMeanInterpolateBlock( VideoParameters *p_Vid, imgpel *src[], imgpel *block, int blockSize, int frameWidth ) +{ + int row, column, k, tmp, srcCounter = 0, weight = 0, bmax = blockSize - 1; + + k = 0; + for ( row = 0; row < blockSize; row++ ) + { + for ( column = 0; column < blockSize; column++ ) + { + tmp = 0; + srcCounter = 0; + // above + if ( src[4] != NULL ) + { + weight = blockSize-row; + tmp += weight * (*(src[4]+bmax*frameWidth+column)); + srcCounter += weight; + } + // left + if ( src[5] != NULL ) + { + weight = blockSize-column; + tmp += weight * (*(src[5]+row*frameWidth+bmax)); + srcCounter += weight; + } + // below + if ( src[6] != NULL ) + { + weight = row+1; + tmp += weight * (*(src[6]+column)); + srcCounter += weight; + } + // right + if ( src[7] != NULL ) + { + weight = column+1; + tmp += weight * (*(src[7]+row*frameWidth)); + srcCounter += weight; + } + + if ( srcCounter > 0 ) + block[ k + column ] = (imgpel)(tmp/srcCounter); + else + block[ k + column ] = (imgpel) (blockSize == 8 ? p_Vid->dc_pred_value_comp[1] : p_Vid->dc_pred_value_comp[0]); + } + k += frameWidth; + } +} diff --git a/ldecod/src/erc_do_p.c b/ldecod/src/erc_do_p.c new file mode 100644 index 0000000..8a59a7a --- /dev/null +++ b/ldecod/src/erc_do_p.c @@ -0,0 +1,1862 @@ + +/*! + ************************************************************************************* + * \file + * erc_do_p.c + * + * \brief + * Inter (P) frame error concealment algorithms for decoder + * + * \author + * - Viktor Varsa + * - Ye-Kui Wang + * - Jill Boyce + * - Saurav K Bandyopadhyay + * - Zhenyu Wu + * - Purvin Pandit + * + ************************************************************************************* + */ + +#include "global.h" +#include "mbuffer.h" +#include "memalloc.h" +#include "erc_do.h" +#include "image.h" +#include "mc_prediction.h" +#include "macroblock.h" + + +// static function declarations +static int concealByCopy(frame *recfr, int currMBNum, objectBuffer_t *object_list, int picSizeX); +static int concealByTrial(frame *recfr, imgpel *predMB, + int currMBNum, objectBuffer_t *object_list, int predBlocks[], + int picSizeX, int picSizeY, char *yCondition); +static int edgeDistortion (int predBlocks[], int currYBlockNum, imgpel *predMB, + imgpel *recY, int picSizeX, int regionSize); +static void copyBetweenFrames (frame *recfr, int currYBlockNum, int picSizeX, int regionSize); +static void buildPredRegionYUV(VideoParameters *p_Vid, int *mv, int x, int y, imgpel *predMB); + +// picture error concealment +static void buildPredblockRegionYUV(VideoParameters *p_Vid, int *mv, + int x, int y, imgpel *predMB, int list, int mb); +static void CopyImgData(imgpel **inputY, imgpel ***inputUV, imgpel **outputY, imgpel ***outputUV, + int img_width, int img_height, int img_width_cr, int img_height_cr); + +static void copyPredMB (int currYBlockNum, imgpel *predMB, frame *recfr, + int picSizeX, int regionSize); +static void add_node ( VideoParameters *p_Vid, struct concealment_node *ptr ); +static void delete_node( VideoParameters *p_Vid, struct concealment_node *ptr ); + +static const int uv_div[2][4] = {{0, 1, 1, 0}, {0, 1, 0, 0}}; //[x/y][yuv_format] + +/*! + ************************************************************************ + * \brief + * The main function for Inter (P) frame concealment. + * \return + * 0, if the concealment was not successful and simple concealment should be used + * 1, otherwise (even if none of the blocks were concealed) + * \param recfr + * Reconstructed frame buffer + * \param object_list + * Motion info for all MBs in the frame + * \param picSizeX + * Width of the frame in pixels + * \param picSizeY + * Height of the frame in pixels + * \param errorVar + * Variables for error concealment + * \param chroma_format_idc + * Chroma format IDC + ************************************************************************ + */ +int ercConcealInterFrame(frame *recfr, objectBuffer_t *object_list, + int picSizeX, int picSizeY, ercVariables_t *errorVar, int chroma_format_idc ) +{ + VideoParameters *p_Vid = recfr->p_Vid; + int lastColumn = 0, lastRow = 0, predBlocks[8]; + int lastCorruptedRow = -1, firstCorruptedRow = -1; + int currRow = 0, row, column, columnInd, areaHeight = 0, i = 0; + imgpel *predMB; + + /* if concealment is on */ + if ( errorVar && errorVar->concealment ) + { + /* if there are segments to be concealed */ + if ( errorVar->nOfCorruptedSegments ) + { + if (chroma_format_idc != YUV400) + predMB = (imgpel *) malloc ( (256 + (p_Vid->mb_cr_size)*2) * sizeof (imgpel)); + else + predMB = (imgpel *) malloc(256 * sizeof (imgpel)); + + if ( predMB == NULL ) no_mem_exit("ercConcealInterFrame: predMB"); + + lastRow = (int) (picSizeY>>4); + lastColumn = (int) (picSizeX>>4); + + for ( columnInd = 0; columnInd < lastColumn; columnInd ++) + { + + column = ((columnInd%2) ? (lastColumn - columnInd/2 -1) : (columnInd/2)); + + for ( row = 0; row < lastRow; row++) + { + + if ( errorVar->yCondition[MBxy2YBlock(column, row, 0, picSizeX)] <= ERC_BLOCK_CORRUPTED ) + { // ERC_BLOCK_CORRUPTED (1) or ERC_BLOCK_EMPTY (0) + firstCorruptedRow = row; + /* find the last row which has corrupted blocks (in same continuous area) */ + for ( lastCorruptedRow = row+1; lastCorruptedRow < lastRow; lastCorruptedRow++) + { + /* check blocks in the current column */ + if (errorVar->yCondition[MBxy2YBlock(column, lastCorruptedRow, 0, picSizeX)] > ERC_BLOCK_CORRUPTED) + { + /* current one is already OK, so the last was the previous one */ + lastCorruptedRow --; + break; + } + } + if ( lastCorruptedRow >= lastRow ) + { + /* correct only from above */ + lastCorruptedRow = lastRow-1; + for ( currRow = firstCorruptedRow; currRow < lastRow; currRow++ ) + { + + ercCollect8PredBlocks (predBlocks, (currRow<<1), (column<<1), + errorVar->yCondition, (lastRow<<1), (lastColumn<<1), 2, 0); + + if(p_Vid->erc_mvperMB >= MVPERMB_THR) + concealByTrial(recfr, predMB, + currRow*lastColumn+column, object_list, predBlocks, + picSizeX, picSizeY, + errorVar->yCondition); + else + concealByCopy(recfr, currRow*lastColumn+column, + object_list, picSizeX); + + ercMarkCurrMBConcealed (currRow*lastColumn+column, -1, picSizeX, errorVar); + } + row = lastRow; + } + else if ( firstCorruptedRow == 0 ) + { + /* correct only from below */ + for ( currRow = lastCorruptedRow; currRow >= 0; currRow-- ) + { + + ercCollect8PredBlocks (predBlocks, (currRow<<1), (column<<1), + errorVar->yCondition, (lastRow<<1), (lastColumn<<1), 2, 0); + + if(p_Vid->erc_mvperMB >= MVPERMB_THR) + concealByTrial(recfr, predMB, + currRow*lastColumn+column, object_list, predBlocks, + picSizeX, picSizeY, + errorVar->yCondition); + else + concealByCopy(recfr, currRow*lastColumn+column, + object_list, picSizeX); + + ercMarkCurrMBConcealed (currRow*lastColumn+column, -1, picSizeX, errorVar); + } + + row = lastCorruptedRow+1; + } + else + { + /* correct bi-directionally */ + + row = lastCorruptedRow+1; + + areaHeight = lastCorruptedRow-firstCorruptedRow+1; + + /* + * Conceal the corrupted area switching between the up and the bottom rows + */ + for ( i = 0; i < areaHeight; i++) + { + if ( i % 2 ) + { + currRow = lastCorruptedRow; + lastCorruptedRow --; + } + else + { + currRow = firstCorruptedRow; + firstCorruptedRow ++; + } + + ercCollect8PredBlocks (predBlocks, (currRow<<1), (column<<1), + errorVar->yCondition, (lastRow<<1), (lastColumn<<1), 2, 0); + + if(p_Vid->erc_mvperMB >= MVPERMB_THR) + concealByTrial(recfr, predMB, + currRow*lastColumn+column, object_list, predBlocks, + picSizeX, picSizeY, + errorVar->yCondition); + else + concealByCopy(recfr, currRow*lastColumn+column, + object_list, picSizeX); + + ercMarkCurrMBConcealed (currRow*lastColumn+column, -1, picSizeX, errorVar); + + } + } + lastCorruptedRow = -1; + firstCorruptedRow = -1; + } + } + } + + free(predMB); + } + return 1; + } + else + return 0; +} + +/*! + ************************************************************************ + * \brief + * It conceals a given MB by simply copying the pixel area from the reference image + * that is at the same location as the macroblock in the current image. This correcponds + * to COPY MBs. + * \return + * Always zero (0). + * \param recfr + * Reconstructed frame buffer + * \param currMBNum + * current MB index + * \param object_list + * Motion info for all MBs in the frame + * \param picSizeX + * Width of the frame in pixels + ************************************************************************ + */ +static int concealByCopy(frame *recfr, int currMBNum, + objectBuffer_t *object_list, int picSizeX) +{ + objectBuffer_t *currRegion; + + currRegion = object_list+(currMBNum<<2); + currRegion->regionMode = REGMODE_INTER_COPY; + + currRegion->xMin = (xPosMB(currMBNum,picSizeX)<<4); + currRegion->yMin = (yPosMB(currMBNum,picSizeX)<<4); + + copyBetweenFrames (recfr, MBNum2YBlock(currMBNum,0,picSizeX), picSizeX, 16); + + return 0; +} + +/*! + ************************************************************************ + * \brief + * Copies the co-located pixel values from the reference to the current frame. + * Used by concealByCopy + * \param recfr + * Reconstructed frame buffer + * \param currYBlockNum + * index of the block (8x8) in the Y plane + * \param picSizeX + * Width of the frame in pixels + * \param regionSize + * can be 16 or 8 to tell the dimension of the region to copy + ************************************************************************ + */ +static void copyBetweenFrames (frame *recfr, int currYBlockNum, int picSizeX, int regionSize) +{ + VideoParameters *p_Vid = recfr->p_Vid; + StorablePicture *dec_picture = p_Vid->dec_picture; + int j, k, location, xmin, ymin; + StorablePicture* refPic = p_Vid->ppSliceList[0]->listX[0][0]; + + /* set the position of the region to be copied */ + xmin = (xPosYBlock(currYBlockNum,picSizeX)<<3); + ymin = (yPosYBlock(currYBlockNum,picSizeX)<<3); + + for (j = ymin; j < ymin + regionSize; j++) + for (k = xmin; k < xmin + regionSize; k++) + { + location = j * picSizeX + k; +//th recfr->yptr[location] = dec_picture->imgY[j][k]; + recfr->yptr[location] = refPic->imgY[j][k]; + } + + for (j = ymin >> uv_div[1][dec_picture->chroma_format_idc]; j < (ymin + regionSize) >> uv_div[1][dec_picture->chroma_format_idc]; j++) + for (k = xmin >> uv_div[0][dec_picture->chroma_format_idc]; k < (xmin + regionSize) >> uv_div[0][dec_picture->chroma_format_idc]; k++) + { +// location = j * picSizeX / 2 + k; + location = ((j * picSizeX) >> uv_div[0][dec_picture->chroma_format_idc]) + k; + +//th recfr->uptr[location] = dec_picture->imgUV[0][j][k]; +//th recfr->vptr[location] = dec_picture->imgUV[1][j][k]; + recfr->uptr[location] = refPic->imgUV[0][j][k]; + recfr->vptr[location] = refPic->imgUV[1][j][k]; + } +} + +/*! + ************************************************************************ + * \brief + * It conceals a given MB by using the motion vectors of one reliable neighbor. That MV of a + * neighbor is selected wich gives the lowest pixel difference at the edges of the MB + * (see function edgeDistortion). This corresponds to a spatial smoothness criteria. + * \return + * Always zero (0). + * \param recfr + * Reconstructed frame buffer + * \param predMB + * memory area for storing temporary pixel values for a macroblock + * the Y,U,V planes are concatenated y = predMB, u = predMB+256, v = predMB+320 + * \param currMBNum + * current MB index + * \param object_list + * array of region structures storing region mode and mv for each region + * \param predBlocks + * status array of the neighboring blocks (if they are OK, concealed or lost) + * \param picSizeX + * Width of the frame in pixels + * \param picSizeY + * Height of the frame in pixels + * \param yCondition + * array for conditions of Y blocks from ercVariables_t + ************************************************************************ + */ +static int concealByTrial(frame *recfr, imgpel *predMB, + int currMBNum, objectBuffer_t *object_list, int predBlocks[], + int picSizeX, int picSizeY, char *yCondition) +{ + VideoParameters *p_Vid = recfr->p_Vid; + + int predMBNum = 0, numMBPerLine, + compSplit1 = 0, compSplit2 = 0, compLeft = 1, comp = 0, compPred, order = 1, + fInterNeighborExists, numIntraNeighbours, + fZeroMotionChecked, predSplitted = 0, + threshold = ERC_BLOCK_OK, + minDist, currDist, i, k; + int regionSize; + objectBuffer_t *currRegion; + int mvBest[3] = {0, 0, 0}, mvPred[3] = {0, 0, 0}, *mvptr; + + numMBPerLine = (int) (picSizeX>>4); + + comp = 0; + regionSize = 16; + + do + { /* 4 blocks loop */ + + currRegion = object_list+(currMBNum<<2)+comp; + + /* set the position of the region to be concealed */ + + currRegion->xMin = (xPosYBlock(MBNum2YBlock(currMBNum,comp,picSizeX),picSizeX)<<3); + currRegion->yMin = (yPosYBlock(MBNum2YBlock(currMBNum,comp,picSizeX),picSizeX)<<3); + + do + { /* reliability loop */ + + minDist = 0; + fInterNeighborExists = 0; + numIntraNeighbours = 0; + fZeroMotionChecked = 0; + + /* loop the 4 neighbours */ + for (i = 4; i < 8; i++) + { + + /* if reliable, try it */ + if (predBlocks[i] >= threshold) + { + switch (i) + { + case 4: + predMBNum = currMBNum-numMBPerLine; + compSplit1 = 2; + compSplit2 = 3; + break; + + case 5: + predMBNum = currMBNum-1; + compSplit1 = 1; + compSplit2 = 3; + break; + + case 6: + predMBNum = currMBNum+numMBPerLine; + compSplit1 = 0; + compSplit2 = 1; + break; + + case 7: + predMBNum = currMBNum+1; + compSplit1 = 0; + compSplit2 = 2; + break; + } + + /* try the concealment with the Motion Info of the current neighbour + only try if the neighbour is not Intra */ + if (isBlock(object_list,predMBNum,compSplit1,INTRA) || + isBlock(object_list,predMBNum,compSplit2,INTRA)) + { + numIntraNeighbours++; + } + else + { + /* if neighbour MB is splitted, try both neighbour blocks */ + for (predSplitted = isSplitted(object_list, predMBNum), + compPred = compSplit1; + predSplitted >= 0; + compPred = compSplit2, + predSplitted -= ((compSplit1 == compSplit2) ? 2 : 1)) + { + + /* if Zero Motion Block, do the copying. This option is tried only once */ + if (isBlock(object_list, predMBNum, compPred, INTER_COPY)) + { + + if (fZeroMotionChecked) + { + continue; + } + else + { + fZeroMotionChecked = 1; + + mvPred[0] = mvPred[1] = 0; + mvPred[2] = 0; + + buildPredRegionYUV(p_Vid->erc_img, mvPred, currRegion->xMin, currRegion->yMin, predMB); + } + } + /* build motion using the neighbour's Motion Parameters */ + else if (isBlock(object_list,predMBNum,compPred,INTRA)) + { + continue; + } + else + { + mvptr = getParam(object_list, predMBNum, compPred, mv); + + mvPred[0] = mvptr[0]; + mvPred[1] = mvptr[1]; + mvPred[2] = mvptr[2]; + + buildPredRegionYUV(p_Vid->erc_img, mvPred, currRegion->xMin, currRegion->yMin, predMB); + } + + /* measure absolute boundary pixel difference */ + currDist = edgeDistortion(predBlocks, + MBNum2YBlock(currMBNum,comp,picSizeX), + predMB, recfr->yptr, picSizeX, regionSize); + + /* if so far best -> store the pixels as the best concealment */ + if (currDist < minDist || !fInterNeighborExists) + { + + minDist = currDist; + + for (k=0;k<3;k++) + mvBest[k] = mvPred[k]; + + currRegion->regionMode = + (isBlock(object_list, predMBNum, compPred, INTER_COPY)) ? + ((regionSize == 16) ? REGMODE_INTER_COPY : REGMODE_INTER_COPY_8x8) : + ((regionSize == 16) ? REGMODE_INTER_PRED : REGMODE_INTER_PRED_8x8); + + copyPredMB(MBNum2YBlock(currMBNum,comp,picSizeX), predMB, recfr, + picSizeX, regionSize); + } + + fInterNeighborExists = 1; + } + } + } + } + + threshold--; + + } while ((threshold >= ERC_BLOCK_CONCEALED) && (fInterNeighborExists == 0)); + + /* always try zero motion */ + if (!fZeroMotionChecked) + { + mvPred[0] = mvPred[1] = 0; + mvPred[2] = 0; + + buildPredRegionYUV(p_Vid->erc_img, mvPred, currRegion->xMin, currRegion->yMin, predMB); + + currDist = edgeDistortion(predBlocks, + MBNum2YBlock(currMBNum,comp,picSizeX), + predMB, recfr->yptr, picSizeX, regionSize); + + if (currDist < minDist || !fInterNeighborExists) + { + + minDist = currDist; + for (k=0;k<3;k++) + mvBest[k] = mvPred[k]; + + currRegion->regionMode = + ((regionSize == 16) ? REGMODE_INTER_COPY : REGMODE_INTER_COPY_8x8); + + copyPredMB(MBNum2YBlock(currMBNum,comp,picSizeX), predMB, recfr, + picSizeX, regionSize); + } + } + + for (i=0; i<3; i++) + currRegion->mv[i] = mvBest[i]; + + yCondition[MBNum2YBlock(currMBNum,comp,picSizeX)] = ERC_BLOCK_CONCEALED; + comp = (comp+order+4)%4; + compLeft--; + + } while (compLeft); + + return 0; +} + +/*! +************************************************************************ +* \brief +* Builds the motion prediction pixels from the given location (in 1/4 pixel units) +* of the reference frame. It not only copies the pixel values but builds the interpolation +* when the pixel positions to be copied from is not full pixel (any 1/4 pixel position). +* It copies the resulting pixel vlaues into predMB. +* \param p_Vid +* The pointer of video_par structure of current frame +* \param mv +* The pointer of the predicted MV of the current (being concealed) MB +* \param x +* The x-coordinate of the above-left corner pixel of the current MB +* \param y +* The y-coordinate of the above-left corner pixel of the current MB +* \param predMB +* memory area for storing temporary pixel values for a macroblock +* the Y,U,V planes are concatenated y = predMB, u = predMB+256, v = predMB+320 +************************************************************************ +*/ +static void buildPredRegionYUV(VideoParameters *p_Vid, int *mv, int x, int y, imgpel *predMB) +{ + imgpel **tmp_block; + int i=0, j=0, ii=0, jj=0,i1=0,j1=0,j4=0,i4=0; + int uv; + int vec1_x=0,vec1_y=0; + int ioff,joff; + imgpel *pMB = predMB; + Slice *currSlice;// = p_Vid->currentSlice; + StorablePicture *dec_picture = p_Vid->dec_picture; + int ii0,jj0,ii1,jj1,if1,jf1,if0,jf0; + int mv_mul; + + //FRExt + int f1_x, f1_y, f2_x, f2_y, f3, f4; + int b8, b4; + int yuv = dec_picture->chroma_format_idc - 1; + + int ref_frame = imax (mv[2], 0); // !!KS: quick fix, we sometimes seem to get negative ref_pic here, so restrict to zero and above + int mb_nr = y/16*(p_Vid->width/16)+x/16; ///currSlice->current_mb_nr; + int **tmp_res = NULL; + + Macroblock *currMB = &p_Vid->mb_data[mb_nr]; // intialization code deleted, see below, StW + currSlice = currMB->p_Slice; + tmp_res = currSlice->tmp_res; + + // This should be allocated only once. + get_mem2Dpel(&tmp_block, MB_BLOCK_SIZE, MB_BLOCK_SIZE); + + /* Update coordinates of the current concealed macroblock */ + currMB->mb.x = (short) (x/MB_BLOCK_SIZE); + currMB->mb.y = (short) (y/MB_BLOCK_SIZE); + currMB->block_y = currMB->mb.y * BLOCK_SIZE; + currMB->pix_c_y = currMB->mb.y * p_Vid->mb_cr_size_y; + currMB->block_x = currMB->mb.x * BLOCK_SIZE; + currMB->pix_c_x = currMB->mb.x * p_Vid->mb_cr_size_x; + + mv_mul=4; + + // luma ******************************************************* + + for(j=0;jblock_y+j; + for(i=0;iblock_x+i; + + vec1_x = i4*4*mv_mul + mv[0]; + vec1_y = j4*4*mv_mul + mv[1]; + + get_block_luma(currSlice->listX[0][ref_frame], vec1_x, vec1_y, BLOCK_SIZE, BLOCK_SIZE, + tmp_block, + dec_picture->iLumaStride,dec_picture->size_x_m1, + (currMB->mb_field) ? (dec_picture->size_y >> 1) - 1 : dec_picture->size_y_m1,tmp_res, + p_Vid->max_pel_value_comp[PLANE_Y],(imgpel) p_Vid->dc_pred_value_comp[PLANE_Y], currMB); + + for(ii=0;iimb_pred[LumaComp][jj+joff][ii+ioff]=tmp_block[jj][ii]; + } + } + + + for (j = 0; j < 16; j++) + { + for (i = 0; i < 16; i++) + { + pMB[j*16+i] = currSlice->mb_pred[LumaComp][j][i]; + } + } + pMB += 256; + + if (dec_picture->chroma_format_idc != YUV400) + { + // chroma ******************************************************* + f1_x = 64/p_Vid->mb_cr_size_x; + f2_x=f1_x-1; + + f1_y = 64/p_Vid->mb_cr_size_y; + f2_y=f1_y-1; + + f3=f1_x*f1_y; + f4=f3>>1; + + for(uv=0;uv<2;uv++) + { + for (b8=0;b8<(p_Vid->num_uv_blocks);b8++) + { + for(b4=0;b4<4;b4++) + { + joff = subblk_offset_y[yuv][b8][b4]; + j4=currMB->pix_c_y+joff; + ioff = subblk_offset_x[yuv][b8][b4]; + i4=currMB->pix_c_x+ioff; + + for(jj=0;jj<4;jj++) + { + for(ii=0;ii<4;ii++) + { + i1=(i4+ii)*f1_x + mv[0]; + j1=(j4+jj)*f1_y + mv[1]; + + ii0=iClip3 (0, dec_picture->size_x_cr-1, i1/f1_x); + jj0=iClip3 (0, dec_picture->size_y_cr-1, j1/f1_y); + ii1=iClip3 (0, dec_picture->size_x_cr-1, ((i1+f2_x)/f1_x)); + jj1=iClip3 (0, dec_picture->size_y_cr-1, ((j1+f2_y)/f1_y)); + + if1=(i1 & f2_x); + jf1=(j1 & f2_y); + if0=f1_x-if1; + jf0=f1_y-jf1; + + currSlice->mb_pred[uv + 1][jj+joff][ii+ioff] = (imgpel) + ((if0*jf0*currSlice->listX[0][ref_frame]->imgUV[uv][jj0][ii0]+ + if1*jf0*currSlice->listX[0][ref_frame]->imgUV[uv][jj0][ii1]+ + if0*jf1*currSlice->listX[0][ref_frame]->imgUV[uv][jj1][ii0]+ + if1*jf1*currSlice->listX[0][ref_frame]->imgUV[uv][jj1][ii1]+f4)/f3); + } + } + } + } + + for (j = 0; j < 8; j++) + { + for (i = 0; i < 8; i++) + { + pMB[j*8+i] = currSlice->mb_pred[uv + 1][j][i]; + } + } + pMB += 64; + + } + } + // We should allocate this memory only once. + free_mem2Dpel(tmp_block); +} +/*! + ************************************************************************ + * \brief + * Copies pixel values between a YUV frame and the temporary pixel value storage place. This is + * used to save some pixel values temporarily before overwriting it, or to copy back to a given + * location in a frame the saved pixel values. + * \param currYBlockNum + * index of the block (8x8) in the Y plane + * \param predMB + * memory area where the temporary pixel values are stored + * the Y,U,V planes are concatenated y = predMB, u = predMB+256, v = predMB+320 + * \param recfr + * pointer to a YUV frame + * \param picSizeX + * picture width in pixels + * \param regionSize + * can be 16 or 8 to tell the dimension of the region to copy + ************************************************************************ + */ +static void copyPredMB (int currYBlockNum, imgpel *predMB, frame *recfr, + int picSizeX, int regionSize) +{ + VideoParameters *p_Vid = recfr->p_Vid; + StorablePicture *dec_picture = p_Vid->dec_picture; + int j, k, xmin, ymin, xmax, ymax; + int locationTmp; + int uv_x = uv_div[0][dec_picture->chroma_format_idc]; + int uv_y = uv_div[1][dec_picture->chroma_format_idc]; + + xmin = (xPosYBlock(currYBlockNum,picSizeX)<<3); + ymin = (yPosYBlock(currYBlockNum,picSizeX)<<3); + xmax = xmin + regionSize -1; + ymax = ymin + regionSize -1; + + for (j = ymin; j <= ymax; j++) + { + for (k = xmin; k <= xmax; k++) + { + locationTmp = (j-ymin) * 16 + (k-xmin); + dec_picture->imgY[j][k] = predMB[locationTmp]; + } + } + + if (dec_picture->chroma_format_idc != YUV400) + { + for (j = (ymin>>uv_y); j <= (ymax>>uv_y); j++) + { + for (k = (xmin>>uv_x); k <= (xmax>>uv_x); k++) + { + locationTmp = (j-(ymin>>uv_y)) * p_Vid->mb_cr_size_x + (k-(xmin>>1)) + 256; + dec_picture->imgUV[0][j][k] = predMB[locationTmp]; + + locationTmp += 64; + + dec_picture->imgUV[1][j][k] = predMB[locationTmp]; + } + } + } +} + +/*! + ************************************************************************ + * \brief + * Calculates a weighted pixel difference between edge Y pixels of the macroblock stored in predMB + * and the pixels in the given Y plane of a frame (recY) that would become neighbor pixels if + * predMB was placed at currYBlockNum block position into the frame. This "edge distortion" value + * is used to determine how well the given macroblock in predMB would fit into the frame when + * considering spatial smoothness. If there are correctly received neighbor blocks (status stored + * in predBlocks) only they are used in calculating the edge distorion; otherwise also the already + * concealed neighbor blocks can also be used. + * \return + * The calculated weighted pixel difference at the edges of the MB. + * \param predBlocks + * status array of the neighboring blocks (if they are OK, concealed or lost) + * \param currYBlockNum + * index of the block (8x8) in the Y plane + * \param predMB + * memory area where the temporary pixel values are stored + * the Y,U,V planes are concatenated y = predMB, u = predMB+256, v = predMB+320 + * \param recY + * pointer to a Y plane of a YUV frame + * \param picSizeX + * picture width in pixels + * \param regionSize + * can be 16 or 8 to tell the dimension of the region to copy + ************************************************************************ + */ +static int edgeDistortion (int predBlocks[], int currYBlockNum, imgpel *predMB, + imgpel *recY, int picSizeX, int regionSize) +{ + int i, j, distortion, numOfPredBlocks, threshold = ERC_BLOCK_OK; + imgpel *currBlock = NULL, *neighbor = NULL; + int currBlockOffset = 0; + + currBlock = recY + (yPosYBlock(currYBlockNum,picSizeX)<<3)*picSizeX + (xPosYBlock(currYBlockNum,picSizeX)<<3); + + do + { + + distortion = 0; numOfPredBlocks = 0; + + // loop the 4 neighbors + for (j = 4; j < 8; j++) + { + /* if reliable, count boundary pixel difference */ + if (predBlocks[j] >= threshold) + { + + switch (j) + { + case 4: + neighbor = currBlock - picSizeX; + for ( i = 0; i < regionSize; i++ ) + { + distortion += iabs((int)(predMB[i] - neighbor[i])); + } + break; + case 5: + neighbor = currBlock - 1; + for ( i = 0; i < regionSize; i++ ) + { + distortion += iabs((int)(predMB[i*16] - neighbor[i*picSizeX])); + } + break; + case 6: + neighbor = currBlock + regionSize*picSizeX; + currBlockOffset = (regionSize-1)*16; + for ( i = 0; i < regionSize; i++ ) + { + distortion += iabs((int)(predMB[i+currBlockOffset] - neighbor[i])); + } + break; + case 7: + neighbor = currBlock + regionSize; + currBlockOffset = regionSize-1; + for ( i = 0; i < regionSize; i++ ) + { + distortion += iabs((int)(predMB[i*16+currBlockOffset] - neighbor[i*picSizeX])); + } + break; + } + + numOfPredBlocks++; + } + } + + threshold--; + if (threshold < ERC_BLOCK_CONCEALED) + break; + } while (numOfPredBlocks == 0); + + if(numOfPredBlocks == 0) + { + return 0; + // assert (numOfPredBlocks != 0); !!!KS hmm, trying to continue... + } + return (distortion/numOfPredBlocks); +} + +// picture error concealment below + +/*! +************************************************************************ +* \brief +* The motion prediction pixels are calculated from the given location (in +* 1/4 pixel units) of the referenced frame. It copies the sub block from the +* corresponding reference to the frame to be concealed. +* +************************************************************************* +*/ +static void buildPredblockRegionYUV(VideoParameters *p_Vid, int *mv, + int x, int y, imgpel *predMB, int list, int current_mb_nr) +{ + imgpel **tmp_block; + int i=0,j=0,ii=0,jj=0,i1=0,j1=0,j4=0,i4=0; + int uv; + int vec1_x=0,vec1_y=0; + int ioff,joff; + + StorablePicture *dec_picture = p_Vid->dec_picture; + imgpel *pMB = predMB; + + int ii0,jj0,ii1,jj1,if1,jf1,if0,jf0; + int mv_mul; + + //FRExt + int f1_x, f1_y, f2_x, f2_y, f3, f4; + int yuv = dec_picture->chroma_format_idc - 1; + + int ref_frame = mv[2]; + int mb_nr = current_mb_nr; + + Macroblock *currMB = &p_Vid->mb_data[mb_nr]; // intialization code deleted, see below, StW + Slice *currSlice = currMB->p_Slice; + + get_mem2Dpel(&tmp_block, MB_BLOCK_SIZE, MB_BLOCK_SIZE); + + /* Update coordinates of the current concealed macroblock */ + + currMB->mb.x = (short) (x/BLOCK_SIZE); + currMB->mb.y = (short) (y/BLOCK_SIZE); + currMB->block_y = currMB->mb.y * BLOCK_SIZE; + currMB->pix_c_y = currMB->mb.y * p_Vid->mb_cr_size_y/4; + currMB->block_x = currMB->mb.x * BLOCK_SIZE; + currMB->pix_c_x = currMB->mb.x * p_Vid->mb_cr_size_x/4; + + mv_mul=4; + + // luma ******************************************************* + + vec1_x = x*mv_mul + mv[0]; + vec1_y = y*mv_mul + mv[1]; + get_block_luma(currSlice->listX[list][ref_frame], vec1_x, vec1_y, BLOCK_SIZE, BLOCK_SIZE, tmp_block, + dec_picture->iLumaStride,dec_picture->size_x_m1, (currMB->mb_field) ? (dec_picture->size_y >> 1) - 1 : dec_picture->size_y_m1,currSlice->tmp_res, + p_Vid->max_pel_value_comp[PLANE_Y],(imgpel) p_Vid->dc_pred_value_comp[PLANE_Y], currMB); + + for(jj=0;jjmb_pred[LumaComp][jj][ii]=tmp_block[jj][ii]; + + + for (j = 0; j < 4; j++) + { + for (i = 0; i < 4; i++) + { + pMB[j*4+i] = currSlice->mb_pred[LumaComp][j][i]; + } + } + pMB += 16; + + if (dec_picture->chroma_format_idc != YUV400) + { + // chroma ******************************************************* + f1_x = 64/(p_Vid->mb_cr_size_x); + f2_x=f1_x-1; + + f1_y = 64/(p_Vid->mb_cr_size_y); + f2_y=f1_y-1; + + f3=f1_x*f1_y; + f4=f3>>1; + + for(uv=0;uv<2;uv++) + { + joff = subblk_offset_y[yuv][0][0]; + j4=currMB->pix_c_y+joff; + ioff = subblk_offset_x[yuv][0][0]; + i4=currMB->pix_c_x+ioff; + + for(jj=0;jj<2;jj++) + { + for(ii=0;ii<2;ii++) + { + i1=(i4+ii)*f1_x + mv[0]; + j1=(j4+jj)*f1_y + mv[1]; + + ii0=iClip3 (0, dec_picture->size_x_cr-1, i1/f1_x); + jj0=iClip3 (0, dec_picture->size_y_cr-1, j1/f1_y); + ii1=iClip3 (0, dec_picture->size_x_cr-1, ((i1+f2_x)/f1_x)); + jj1=iClip3 (0, dec_picture->size_y_cr-1, ((j1+f2_y)/f1_y)); + + if1=(i1 & f2_x); + jf1=(j1 & f2_y); + if0=f1_x-if1; + jf0=f1_y-jf1; + + currSlice->mb_pred[uv + 1][jj][ii]=(imgpel) ((if0*jf0*currSlice->listX[list][ref_frame]->imgUV[uv][jj0][ii0]+ + if1*jf0*currSlice->listX[list][ref_frame]->imgUV[uv][jj0][ii1]+ + if0*jf1*currSlice->listX[list][ref_frame]->imgUV[uv][jj1][ii0]+ + if1*jf1*currSlice->listX[list][ref_frame]->imgUV[uv][jj1][ii1]+f4)/f3); + } + } + + for (j = 0; j < 2; j++) + { + for (i = 0; i < 2; i++) + { + pMB[j*2+i] = currSlice->mb_pred[uv + 1][j][i]; + } + } + pMB += 4; + + } + } + free_mem2Dpel(tmp_block); +} + +/*! +************************************************************************ +* \brief +* compares two stored pictures by picture number for qsort in descending order +* +************************************************************************ +*/ +static inline int compare_pic_by_pic_num_desc( const void *arg1, const void *arg2 ) +{ + int pic_num1 = (*(StorablePicture**)arg1)->pic_num; + int pic_num2 = (*(StorablePicture**)arg2)->pic_num; + + if (pic_num1 < pic_num2) + return 1; + if (pic_num1 > pic_num2) + return -1; + else + return 0; +} + +/*! +************************************************************************ +* \brief +* compares two stored pictures by picture number for qsort in descending order +* +************************************************************************ +*/ +static inline int compare_pic_by_lt_pic_num_asc( const void *arg1, const void *arg2 ) +{ + int long_term_pic_num1 = (*(StorablePicture**)arg1)->long_term_pic_num; + int long_term_pic_num2 = (*(StorablePicture**)arg2)->long_term_pic_num; + if ( long_term_pic_num1 < long_term_pic_num2) + return -1; + + if ( long_term_pic_num1 > long_term_pic_num2) + return 1; + else + return 0; +} + +/*! +************************************************************************ +* \brief +* compares two stored pictures by poc for qsort in ascending order +* +************************************************************************ +*/ +static inline int compare_pic_by_poc_asc( const void *arg1, const void *arg2 ) +{ + int poc1 = (*(StorablePicture**)arg1)->poc; + int poc2 = (*(StorablePicture**)arg2)->poc; + + if ( poc1 < poc2) + return -1; + if ( poc1 > poc2) + return 1; + else + return 0; +} + + +///*! +//************************************************************************ +//* \brief +//* compares two stored pictures by poc for qsort in descending order +//* +//************************************************************************ +//*/ +//static inline int compare_pic_by_poc_desc( const void *arg1, const void *arg2 ) +//{ +// int poc1 = (*(StorablePicture**)arg1)->poc; +// int poc2 = (*(StorablePicture**)arg2)->poc; +// +// if (poc1 < poc2) +// return 1; +// if (poc1 > poc2) +// return -1; +// else +// return 0; +//} +// +///*! +//************************************************************************ +//* \brief +//* Copy image data from one array to another array +//************************************************************************ +//*/ +// +//static void CopyImgData(imgpel **inputY, imgpel ***inputUV, imgpel **outputY, imgpel ***outputUV, +// int img_width, int img_height, int img_width_cr, int img_height_cr) +//{ +// int x, y; +// +// for (y=0; yused_size - 1; +// int i; +// +// for(i = used_size; i >= 0; i--) +// { +// if (p_Dpb->fs[i]->is_used==3) +// { +// if (((p_Dpb->fs[i]->frame->used_for_reference) && +// (!p_Dpb->fs[i]->frame->is_long_term)) /*|| ((p_Dpb->fs[i]->frame->used_for_reference==0) +// && (p_Dpb->fs[i]->frame->slice_type == P_SLICE))*/ ) +// { +// return p_Dpb->fs[i]->frame; +// } +// } +// } +// +// return NULL; +//} +// +///*! +//************************************************************************ +//* \brief +//* Conceals the lost reference or non reference frame by either frame copy +//* or motion vector copy concealment. +//* +//************************************************************************ +//*/ +// +//static void copy_to_conceal(StorablePicture *src, StorablePicture *dst, VideoParameters *p_Vid) +//{ +// int i=0; +// int mv[3]; +// int multiplier; +// imgpel *predMB, *storeYUV; +// int j, y, x, mb_height, mb_width, ii=0, jj=0; +// int uv; +// int mm, nn; +// int scale = 1; +// StorablePicture *dec_picture = p_Vid->dec_picture; +// // InputParameters *test; +// +// int current_mb_nr = 0; +// +// dst->PicSizeInMbs = src->PicSizeInMbs; +// +// dst->slice_type = src->slice_type = p_Vid->conceal_slice_type; +// +// dst->idr_flag = FALSE; //since we do not want to clears the ref list +// +// dst->no_output_of_prior_pics_flag = src->no_output_of_prior_pics_flag; +// dst->long_term_reference_flag = src->long_term_reference_flag; +// dst->adaptive_ref_pic_buffering_flag = src->adaptive_ref_pic_buffering_flag = 0; +// dst->chroma_format_idc = src->chroma_format_idc; +// dst->frame_mbs_only_flag = src->frame_mbs_only_flag; +// dst->frame_cropping_flag = src->frame_cropping_flag; +// dst->frame_crop_left_offset = src->frame_crop_left_offset; +// dst->frame_crop_right_offset = src->frame_crop_right_offset; +// dst->frame_crop_bottom_offset = src->frame_crop_bottom_offset; +// dst->frame_crop_top_offset = src->frame_crop_top_offset; +// +// dst->qp = src->qp; +// dst->slice_qp_delta = src->slice_qp_delta; +// +// dec_picture = src; +// +// // Conceals the missing frame by frame copy concealment +// if (p_Vid->conceal_mode==1) +// { +// // We need these initializations for using deblocking filter for frame copy +// // concealment as well. +// dst->PicWidthInMbs = src->PicWidthInMbs; +// dst->PicSizeInMbs = src->PicSizeInMbs; +// +// CopyImgData( src->imgY, src->imgUV, dst->imgY, dst->imgUV, p_Vid->width, p_Vid->height, p_Vid->width_cr, p_Vid->height_cr); +// } +// +// // Conceals the missing frame by motion vector copy concealment +// if (p_Vid->conceal_mode==2) +// { +// if (dec_picture->chroma_format_idc != YUV400) +// { +// storeYUV = (imgpel *) malloc ( (16 + (p_Vid->mb_cr_size_x*p_Vid->mb_cr_size_y)*2/16) * sizeof (imgpel)); +// } +// else +// { +// storeYUV = (imgpel *) malloc (16 * sizeof (imgpel)); +// } +// +// p_Vid->erc_img = p_Vid; +// +// dst->PicWidthInMbs = src->PicWidthInMbs; +// dst->PicSizeInMbs = src->PicSizeInMbs; +// mb_width = dst->PicWidthInMbs; +// mb_height = (dst->PicSizeInMbs)/(dst->PicWidthInMbs); +// scale = (p_Vid->conceal_slice_type == B_SLICE) ? 2 : 1; +// +// if(p_Vid->conceal_slice_type == B_SLICE) +// { +// init_lists_for_non_reference_loss( +// p_Vid->p_Dpb_layer[0], +// dst->slice_type, p_Vid->ppSliceList[0]->structure); +// } +// else +// p_Vid->ppSliceList[0]->init_lists(p_Vid->ppSliceList[0]); //p_Vid->currentSlice); +// +// multiplier = BLOCK_SIZE; +// +// for(i=0;imv_info[i][j].mv[LIST_0].mv_x / scale; +// mv[1] = src->mv_info[i][j].mv[LIST_0].mv_y / scale; +// mv[2] = src->mv_info[i][j].ref_idx[LIST_0]; +// +// if(mv[2]<0) +// mv[2]=0; +// +// dst->mv_info[i][j].mv[LIST_0].mv_x = (short) mv[0]; +// dst->mv_info[i][j].mv[LIST_0].mv_y = (short) mv[1]; +// dst->mv_info[i][j].ref_idx[LIST_0] = (char) mv[2]; +// +// x = (j) * multiplier; +// y = (i) * multiplier; +// +// if ((mm%16==0) && (nn%16==0)) +// current_mb_nr++; +// +// buildPredblockRegionYUV(p_Vid->erc_img, mv, x, y, storeYUV, LIST_0, current_mb_nr); +// +// predMB = storeYUV; +// +// for(ii=0;iiimgY[i*multiplier+ii][j*multiplier+jj] = predMB[ii*(multiplier)+jj]; +// } +// } +// +// predMB = predMB + (multiplier*multiplier); +// +// if (dec_picture->chroma_format_idc != YUV400) +// { +// +// for(uv=0;uv<2;uv++) +// { +// for(ii=0;ii< (multiplier/2);ii++) +// { +// for(jj=0;jj< (multiplier/2);jj++) +// { +// dst->imgUV[uv][i*multiplier/2 +ii][j*multiplier/2 +jj] = predMB[ii*(multiplier/2)+jj]; +// } +// } +// predMB = predMB + (multiplier*multiplier/4); +// } +// } +// } +// } +// free(storeYUV); +// } +//} +// +///*! +//************************************************************************ +//* \brief +//* Uses the previous reference pic for concealment of reference frames +//* +//************************************************************************ +//*/ +// +//static void +//copy_prev_pic_to_concealed_pic(StorablePicture *picture, DecodedPictureBuffer *p_Dpb) +//{ +// VideoParameters *p_Vid = p_Dpb->p_Vid; +// /* get the last ref pic in dpb */ +// StorablePicture *ref_pic = get_last_ref_pic_from_dpb(p_Dpb); +// +// assert(ref_pic != NULL); +// +// /* copy all the struc from this to current concealment pic */ +// p_Vid->conceal_slice_type = P_SLICE; +// copy_to_conceal(ref_pic, picture, p_Vid); +//} +// +// +///*! +//************************************************************************ +//* \brief +//* This function conceals a missing reference frame. The routine is called +//* based on the difference in frame number. It conceals an IDR frame loss +//* based on the sudden decrease in frame number. +//* +//************************************************************************ +//*/ +// +//void conceal_lost_frames(DecodedPictureBuffer *p_Dpb, Slice *pSlice) +//{ +// VideoParameters *p_Vid = p_Dpb->p_Vid; +// int CurrFrameNum; +// int UnusedShortTermFrameNum; +// StorablePicture *picture = NULL; +// int tmp1 = pSlice->delta_pic_order_cnt[0]; +// int tmp2 = pSlice->delta_pic_order_cnt[1]; +// int i; +// +// pSlice->delta_pic_order_cnt[0] = pSlice->delta_pic_order_cnt[1] = 0; +// +// // printf("A gap in frame number is found, try to fill it.\n"); +// +// if(p_Vid->IDR_concealment_flag == 1) +// { +// // Conceals an IDR frame loss. Uses the reference frame in the previous +// // GOP for concealment. +// UnusedShortTermFrameNum = 0; +// p_Vid->last_ref_pic_poc = -p_Vid->poc_gap; +// p_Vid->earlier_missing_poc = 0; +// } +// else +// UnusedShortTermFrameNum = (p_Vid->pre_frame_num + 1) % p_Vid->max_frame_num; +// +// CurrFrameNum = pSlice->frame_num; +// +// while (CurrFrameNum != UnusedShortTermFrameNum) +// { +// picture = alloc_storable_picture (p_Vid, FRAME, p_Vid->width, p_Vid->height, p_Vid->width_cr, p_Vid->height_cr, 1); +// +// picture->coded_frame = 1; +// picture->pic_num = UnusedShortTermFrameNum; +// picture->frame_num = UnusedShortTermFrameNum; +// picture->non_existing = 0; +// picture->is_output = 0; +// picture->used_for_reference = 1; +// picture->concealed_pic = 1; +// +// picture->adaptive_ref_pic_buffering_flag = 0; +// +// pSlice->frame_num = UnusedShortTermFrameNum; +// +// picture->top_poc=p_Vid->last_ref_pic_poc + p_Vid->ref_poc_gap; +// picture->bottom_poc=picture->top_poc; +// picture->frame_poc=picture->top_poc; +// picture->poc=picture->top_poc; +// p_Vid->last_ref_pic_poc = picture->poc; +// +// copy_prev_pic_to_concealed_pic(picture, p_Dpb); +// +// //if (UnusedShortTermFrameNum == 0) +// if(p_Vid->IDR_concealment_flag == 1) +// { +// picture->slice_type = I_SLICE; +// picture->idr_flag = TRUE; +// flush_dpb(p_Dpb); +// picture->top_poc= 0; +// picture->bottom_poc=picture->top_poc; +// picture->frame_poc=picture->top_poc; +// picture->poc=picture->top_poc; +// p_Vid->last_ref_pic_poc = picture->poc; +// } +// +// store_picture_in_dpb(p_Vid->p_Dpb_layer[0], picture); +// +// picture=NULL; +// +// p_Vid->pre_frame_num = UnusedShortTermFrameNum; +// UnusedShortTermFrameNum = (UnusedShortTermFrameNum + 1) % p_Vid->max_frame_num; +// +// // update reference flags and set current flag. +// for(i=16;i>0;i--) +// { +// pSlice->ref_flag[i] = pSlice->ref_flag[i-1]; +// } +// pSlice->ref_flag[0] = 0; +// } +// pSlice->delta_pic_order_cnt[0] = tmp1; +// pSlice->delta_pic_order_cnt[1] = tmp2; +// pSlice->frame_num = CurrFrameNum; +//} +// +///*! +//************************************************************************ +//* \brief +//* Updates the reference list for motion vector copy concealment for non- +//* reference frame loss. +//* +//************************************************************************ +//*/ +// +//void update_ref_list_for_concealment(DecodedPictureBuffer *p_Dpb) +//{ +// VideoParameters *p_Vid = p_Dpb->p_Vid; +// unsigned i, j= 0; +// +// for (i = 0; i < p_Dpb->used_size; i++) +// { +// if (p_Dpb->fs[i]->concealment_reference) +// { +// p_Dpb->fs_ref[j++] = p_Dpb->fs[i]; +// } +// } +// +// p_Dpb->ref_frames_in_buffer = p_Vid->active_pps->num_ref_idx_l0_default_active_minus1; +//} +// +///*! +//************************************************************************ +//* \brief +//* Initialize the list based on the B frame or non reference 'p' frame +//* to be concealed. The function initialize currSlice->listX[0] and list 1 depending +//* on current picture type +//* +//************************************************************************ +//*/ +//void init_lists_for_non_reference_loss(DecodedPictureBuffer *p_Dpb, int currSliceType, PictureStructure currPicStructure) +//{ +// VideoParameters *p_Vid = p_Dpb->p_Vid; +// seq_parameter_set_rbsp_t *active_sps = p_Vid->active_sps; +// +// unsigned i; +// int j; +// int max_frame_num = 1 << (active_sps->log2_max_frame_num_minus4 + 4); +// int diff; +// +// int list0idx = 0; +// int list0idx_1 = 0; +// +// StorablePicture *tmp_s; +// +// if (currPicStructure == FRAME) +// { +// for(i=0;iref_frames_in_buffer; i++) +// { +// if(p_Dpb->fs[i]->concealment_reference == 1) +// { +// if(p_Dpb->fs[i]->frame_num > p_Vid->frame_to_conceal) +// p_Dpb->fs_ref[i]->frame_num_wrap = p_Dpb->fs[i]->frame_num - max_frame_num; +// else +// p_Dpb->fs_ref[i]->frame_num_wrap = p_Dpb->fs[i]->frame_num; +// p_Dpb->fs_ref[i]->frame->pic_num = p_Dpb->fs_ref[i]->frame_num_wrap; +// } +// } +// } +// +// if (currSliceType == P_SLICE) +// { +// // Calculate FrameNumWrap and PicNum +// if (currPicStructure == FRAME) +// { +// for(i=0;iused_size; i++) +// { +// if(p_Dpb->fs[i]->concealment_reference == 1) +// { +// p_Vid->ppSliceList[0]->listX[0][list0idx++] = p_Dpb->fs[i]->frame; +// } +// } +// // order list 0 by PicNum +// qsort((void *)p_Vid->ppSliceList[0]->listX[0], list0idx, sizeof(StorablePicture*), compare_pic_by_pic_num_desc); +// p_Vid->ppSliceList[0]->listXsize[0] = (char) list0idx; +// } +// } +// +// if (currSliceType == B_SLICE) +// { +// if (currPicStructure == FRAME) +// { +// // for(i=0;iref_frames_in_buffer; i++) +// for(i=0;iused_size; i++) +// { +// if(p_Dpb->fs[i]->concealment_reference == 1) +// { +// if(p_Vid->earlier_missing_poc > p_Dpb->fs[i]->frame->poc) +// p_Vid->ppSliceList[0]->listX[0][list0idx++] = p_Dpb->fs[i]->frame; +// } +// } +// +// qsort((void *)p_Vid->ppSliceList[0]->listX[0], list0idx, sizeof(StorablePicture*), compare_pic_by_poc_desc); +// list0idx_1 = list0idx; +// +// // for(i=0;iref_frames_in_buffer; i++) +// for(i=0;iused_size; i++) +// { +// if(p_Dpb->fs[i]->concealment_reference == 1) +// { +// if(p_Vid->earlier_missing_poc < p_Dpb->fs[i]->frame->poc) +// p_Vid->ppSliceList[0]->listX[0][list0idx++] = p_Dpb->fs[i]->frame; +// } +// } +// +// qsort((void *)&p_Vid->ppSliceList[0]->listX[0][list0idx_1], list0idx-list0idx_1, sizeof(StorablePicture*), compare_pic_by_poc_asc); +// +// for (j=0; jppSliceList[0]->listX[1][list0idx-list0idx_1+j]=p_Vid->ppSliceList[0]->listX[0][j]; +// } +// for (j=list0idx_1; jppSliceList[0]->listX[1][j-list0idx_1]=p_Vid->ppSliceList[0]->listX[0][j]; +// } +// +// p_Vid->ppSliceList[0]->listXsize[0] = p_Vid->ppSliceList[0]->listXsize[1] = (char) list0idx; +// +// qsort((void *)&p_Vid->ppSliceList[0]->listX[0][(short) p_Vid->ppSliceList[0]->listXsize[0]], list0idx-p_Vid->ppSliceList[0]->listXsize[0], sizeof(StorablePicture*), compare_pic_by_lt_pic_num_asc); +// qsort((void *)&p_Vid->ppSliceList[0]->listX[1][(short) p_Vid->ppSliceList[0]->listXsize[0]], list0idx-p_Vid->ppSliceList[0]->listXsize[0], sizeof(StorablePicture*), compare_pic_by_lt_pic_num_asc); +// p_Vid->ppSliceList[0]->listXsize[0] = p_Vid->ppSliceList[0]->listXsize[1] = (char) list0idx; +// } +// } +// +// if ((p_Vid->ppSliceList[0]->listXsize[0] == p_Vid->ppSliceList[0]->listXsize[1]) && (p_Vid->ppSliceList[0]->listXsize[0] > 1)) +// { +// // check if lists are identical, if yes swap first two elements of listX[1] +// diff=0; +// for (j = 0; j< p_Vid->ppSliceList[0]->listXsize[0]; j++) +// { +// if (p_Vid->ppSliceList[0]->listX[0][j]!=p_Vid->ppSliceList[0]->listX[1][j]) +// diff=1; +// } +// if (!diff) +// { +// tmp_s = p_Vid->ppSliceList[0]->listX[1][0]; +// p_Vid->ppSliceList[0]->listX[1][0]=p_Vid->ppSliceList[0]->listX[1][1]; +// p_Vid->ppSliceList[0]->listX[1][1]=tmp_s; +// } +// } +// +// // set max size +// p_Vid->ppSliceList[0]->listXsize[0] = (char) imin (p_Vid->ppSliceList[0]->listXsize[0], (int)active_sps->num_ref_frames); +// p_Vid->ppSliceList[0]->listXsize[1] = (char) imin (p_Vid->ppSliceList[0]->listXsize[1], (int)active_sps->num_ref_frames); +// +// p_Vid->ppSliceList[0]->listXsize[1] = 0; +// // set the unused list entries to NULL +// for (i=p_Vid->ppSliceList[0]->listXsize[0]; i< (MAX_LIST_SIZE) ; i++) +// { +// p_Vid->ppSliceList[0]->listX[0][i] = NULL; +// } +// for (i=p_Vid->ppSliceList[0]->listXsize[1]; i< (MAX_LIST_SIZE) ; i++) +// { +// p_Vid->ppSliceList[0]->listX[1][i] = NULL; +// } +//} +// +// +///*! +//************************************************************************ +//* \brief +//* Get from the dpb the picture corresponding to a POC. The POC varies +//* depending on whether it is a frame copy or motion vector copy concealment. +//* The frame corresponding to the POC is returned. +//* +//************************************************************************ +//*/ +// +//static StorablePicture *get_pic_from_dpb(DecodedPictureBuffer *p_Dpb, int missingpoc, unsigned int *pos) +//{ +// VideoParameters *p_Vid = p_Dpb->p_Vid; +// int used_size = p_Dpb->used_size - 1; +// int i, concealfrom = 0; +// +// if(p_Vid->conceal_mode == 1) +// concealfrom = missingpoc - p_Vid->poc_gap; +// else if (p_Vid->conceal_mode == 2) +// concealfrom = missingpoc + p_Vid->poc_gap; +// +// for(i = used_size; i >= 0; i--) +// { +// if(p_Dpb->fs[i]->poc == concealfrom) +// { +// *pos = i; +// return p_Dpb->fs[i]->frame; +// } +// } +// +// return NULL; +//} +// +///*! +//************************************************************************ +//* \brief +//* Function to sort the POC and find the lowest number in the POC list +//* Compare the integers +//* +//************************************************************************ +//*/ +// +//int comp(const void *i, const void *j) +//{ +// return *(int *)i - *(int *)j; +//} +// +///*! +//************************************************************************ +//* \brief +//* Initialises a node, allocates memory for the node, and returns +//* a pointer to the new node. +//* +//************************************************************************ +//*/ +// +//struct concealment_node * init_node( StorablePicture* picture, int missingpoc ) +//{ +// struct concealment_node *ptr; +// +// ptr = (struct concealment_node *) calloc( 1, sizeof(struct concealment_node ) ); +// +// if( ptr == NULL ) +// return (struct concealment_node *) NULL; +// else { +// ptr->picture = picture; +// ptr->missingpocs = missingpoc; +// ptr->next = NULL; +// return ptr; +// } +//} +// +///*! +//************************************************************************ +//* \brief +//* Prints the details of a node +//* +//************************************************************************ +//*/ +// +//void print_node( struct concealment_node *ptr ) +//{ +// printf("Missing POC=%d\n", ptr->missingpocs ); +//} +// +// +///*! +//************************************************************************ +//* \brief +//* Prints all nodes from the current address passed to it. +//* +//************************************************************************ +//*/ +// +//void print_list( struct concealment_node *ptr ) +//{ +// while( ptr != NULL ) +// { +// print_node( ptr ); +// ptr = ptr->next; +// } +//} +// +///*! +//************************************************************************ +//* \brief +//* Adds a node to the end of the list. +//* +//************************************************************************ +//*/ +// +// +//static void add_node( VideoParameters *p_Vid, struct concealment_node *concealment_new ) +//{ + // if( p_Vid->concealment_head == NULL ) +// { +// p_Vid->concealment_end = p_Vid->concealment_head = concealment_new; +// return; +// } +// p_Vid->concealment_end->next = concealment_new; +// p_Vid->concealment_end = concealment_new; +//} +// +// +///*! +//************************************************************************ +//* \brief +//* Deletes the specified node pointed to by 'ptr' from the list +//* +//************************************************************************ +//*/ +// +// +//static void delete_node( VideoParameters *p_Vid, struct concealment_node *ptr ) +//{ +// // We only need to delete the first node in the linked list +// if( ptr == p_Vid->concealment_head ) +// { +// p_Vid->concealment_head = p_Vid->concealment_head->next; +// if( p_Vid->concealment_end == ptr ) +// p_Vid->concealment_end = p_Vid->concealment_end->next; +// free(ptr); +// } +//} +// +///*! +//************************************************************************ +//* \brief +//* Deletes all nodes from the place specified by ptr +//* +//************************************************************************ +//*/ +// +//void delete_list( VideoParameters *p_Vid, struct concealment_node *ptr ) +//{ +// struct concealment_node *temp; +// +// if( p_Vid->concealment_head == NULL ) return; +// +// if( ptr == p_Vid->concealment_head ) +// { +// p_Vid->concealment_head = NULL; +// p_Vid->concealment_end = NULL; +// } +// else +// { +// temp = p_Vid->concealment_head; +// +// while( temp->next != ptr ) +// temp = temp->next; +// p_Vid->concealment_end = temp; +// } +// +// while( ptr != NULL ) +// { +// temp = ptr->next; +// free( ptr ); +// ptr = temp; +// } +//} +// +///*! +//************************************************************************ +//* \brief +//* Stores the missing non reference frames in the concealment buffer. The +//* detection is based on the POC difference in the sorted POC array. A missing +//* non reference frame is detected when the dpb is full. A singly linked list +//* is maintained for storing the missing non reference frames. +//* +//************************************************************************ +//*/ +// +//void conceal_non_ref_pics(DecodedPictureBuffer *p_Dpb, int diff) +//{ +// VideoParameters *p_Vid = p_Dpb->p_Vid; +// int missingpoc = 0; +// unsigned int i, pos = 0; +// StorablePicture *conceal_from_picture = NULL; +// StorablePicture *conceal_to_picture = NULL; +// struct concealment_node *concealment_ptr = NULL; +// int temp_used_size = p_Dpb->used_size; +// +// if(p_Dpb->used_size == 0 ) +// return; +// +// qsort(p_Vid->pocs_in_dpb, p_Dpb->size, sizeof(int), comp); +// +// for(i=0;isize-diff;i++) +// { +// p_Dpb->used_size = p_Dpb->size; +// if((p_Vid->pocs_in_dpb[i+1] - p_Vid->pocs_in_dpb[i]) > p_Vid->poc_gap) +// { +// conceal_to_picture = alloc_storable_picture (p_Vid, FRAME, p_Vid->width, p_Vid->height, p_Vid->width_cr, p_Vid->height_cr, 1); +// +// missingpoc = p_Vid->pocs_in_dpb[i] + p_Vid->poc_gap; +// // Diagnostics +// // printf("\n missingpoc = %d\n",missingpoc); +// +// if(missingpoc > p_Vid->earlier_missing_poc) +// { +// p_Vid->earlier_missing_poc = missingpoc; +// conceal_to_picture->top_poc = missingpoc; +// conceal_to_picture->bottom_poc = missingpoc; +// conceal_to_picture->frame_poc = missingpoc; +// conceal_to_picture->poc = missingpoc; +// conceal_from_picture = get_pic_from_dpb(p_Dpb, missingpoc, &pos); +// +// assert(conceal_from_picture != NULL); +// +// p_Dpb->used_size = pos + 1; +// +// p_Vid->frame_to_conceal = conceal_from_picture->frame_num + 1; +// +// update_ref_list_for_concealment(p_Dpb); +// p_Vid->conceal_slice_type = B_SLICE; +// copy_to_conceal(conceal_from_picture, conceal_to_picture, p_Vid); +// concealment_ptr = init_node( conceal_to_picture, missingpoc ); +// add_node(p_Vid, concealment_ptr); +// // Diagnostics +// // print_node(concealment_ptr); +// } +// } +// } +// +// //restore the original value +// //p_Dpb->used_size = p_Dpb->size; +// p_Dpb->used_size = temp_used_size; +//} +// +///*! +//************************************************************************ +//* \brief +//* Perform Sliding window decoded reference picture marking process. It +//* maintains the POC s stored in the dpb at a specific instance. +//* +//************************************************************************ +//*/ +// +//void sliding_window_poc_management(DecodedPictureBuffer *p_Dpb, StorablePicture *p) +//{ +// if (p_Dpb->used_size == p_Dpb->size) +// { +// VideoParameters *p_Vid = p_Dpb->p_Vid; +// unsigned int i; +// +// for(i=0;isize-1; i++) +// p_Vid->pocs_in_dpb[i] = p_Vid->pocs_in_dpb[i+1]; +// } +//} +// +// +///*! +//************************************************************************ +//* \brief +//* Outputs the non reference frames. The POCs in the concealment buffer are +//* sorted in ascending order and outputted when the lowest POC in the +//* concealment buffer is lower than the lowest in the p_Dpb-> The linked list +//* entry corresponding to the outputted POC is immediately deleted. +//* +//************************************************************************ +//*/ +// +//void write_lost_non_ref_pic(DecodedPictureBuffer *p_Dpb, int poc, int p_out) +//{ +// VideoParameters *p_Vid = p_Dpb->p_Vid; +// FrameStore concealment_fs; +// if(poc > 0) +// { +// if((poc - p_Dpb->last_output_poc) > p_Vid->poc_gap) +// { +// +// concealment_fs.frame = p_Vid->concealment_head->picture; +// concealment_fs.is_output = 0; +// concealment_fs.is_reference = 0; +// concealment_fs.is_used = 3; +// +// write_stored_frame(p_Vid, &concealment_fs, p_out); +// delete_node(p_Vid, p_Vid->concealment_head); +// } +// } +//} +// +///*! +//************************************************************************ +//* \brief +//* Conceals frame loss immediately after the IDR. This special case produces +//* the same result for either frame copy or motion vector copy concealment. +//* +//************************************************************************ +//*/ +// +//void write_lost_ref_after_idr(DecodedPictureBuffer *p_Dpb, int pos) +//{ +// VideoParameters *p_Vid = p_Dpb->p_Vid; +// +// int temp = 1; +// +// if(p_Vid->last_out_fs->frame == NULL) +// { +// p_Vid->last_out_fs->frame = alloc_storable_picture (p_Vid, FRAME, p_Vid->width, p_Vid->height, +// p_Vid->width_cr, p_Vid->height_cr, 1); +// p_Vid->last_out_fs->is_used = 3; +// } +// +// if(p_Vid->conceal_mode == 2) +// { +// temp = 2; +// p_Vid->conceal_mode = 1; +// } +// copy_to_conceal(p_Dpb->fs[pos]->frame, p_Vid->last_out_fs->frame, p_Vid); +// +// p_Vid->conceal_mode = temp; +//} + diff --git a/ldecod/src/errorconcealment.c b/ldecod/src/errorconcealment.c new file mode 100644 index 0000000..06df1e1 --- /dev/null +++ b/ldecod/src/errorconcealment.c @@ -0,0 +1,220 @@ + +/*! + *********************************************************************** + * \file errorconcealment.c + * + * \brief + * Implements error concealment scheme for H.264 decoder + * + * \date + * 6.10.2000 + * + * \version + * 1.0 + * + * \note + * This simple error concealment implemented in this decoder uses + * the existing dependencies of syntax elements. + * In case that an element is detected as false this elements and all + * dependend elements are marked as elements to conceal in the p_Vid->ec_flag[] + * array. If the decoder requests a new element by the function + * readSyntaxElement_xxxx() this array is checked first if an error concealment has + * to be applied on this element. + * In case that an error occured a concealed element is given to the + * decoding function in macroblock(). + * + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * - Sebastian Purreiter + *********************************************************************** + */ + +#include "contributors.h" +#include "global.h" +#include "elements.h" + + + +/*! + *********************************************************************** + * \brief + * set concealment for all elements in same partition + * and dependend syntax elements + * \param p_Vid + * video encoding parameters for current picture + * \param se + * type of syntax element to conceal + * \return + * EC_REQ, elements of same type or depending type need error concealment. \n + * EX_SYNC sync on next header + *********************************************************************** + */ +int set_ec_flag(VideoParameters *p_Vid, int se) +{ + + /* + if (p_Vid->ec_flag[se] == NO_EC) + printf("Error concealment on element %s\n",SEtypes[se]); + */ + switch (se) + { + case SE_HEADER : + p_Vid->ec_flag[SE_HEADER] = EC_REQ; + case SE_PTYPE : + p_Vid->ec_flag[SE_PTYPE] = EC_REQ; + case SE_MBTYPE : + p_Vid->ec_flag[SE_MBTYPE] = EC_REQ; + + case SE_REFFRAME : + p_Vid->ec_flag[SE_REFFRAME] = EC_REQ; + p_Vid->ec_flag[SE_MVD] = EC_REQ; // set all motion vectors to zero length + se = SE_CBP_INTER; // conceal also Inter texture elements + break; + + case SE_INTRAPREDMODE : + p_Vid->ec_flag[SE_INTRAPREDMODE] = EC_REQ; + se = SE_CBP_INTRA; // conceal also Intra texture elements + break; + case SE_MVD : + p_Vid->ec_flag[SE_MVD] = EC_REQ; + se = SE_CBP_INTER; // conceal also Inter texture elements + break; + + default: + break; + } + + switch (se) + { + case SE_CBP_INTRA : + p_Vid->ec_flag[SE_CBP_INTRA] = EC_REQ; + case SE_LUM_DC_INTRA : + p_Vid->ec_flag[SE_LUM_DC_INTRA] = EC_REQ; + case SE_CHR_DC_INTRA : + p_Vid->ec_flag[SE_CHR_DC_INTRA] = EC_REQ; + case SE_LUM_AC_INTRA : + p_Vid->ec_flag[SE_LUM_AC_INTRA] = EC_REQ; + case SE_CHR_AC_INTRA : + p_Vid->ec_flag[SE_CHR_AC_INTRA] = EC_REQ; + break; + + case SE_CBP_INTER : + p_Vid->ec_flag[SE_CBP_INTER] = EC_REQ; + case SE_LUM_DC_INTER : + p_Vid->ec_flag[SE_LUM_DC_INTER] = EC_REQ; + case SE_CHR_DC_INTER : + p_Vid->ec_flag[SE_CHR_DC_INTER] = EC_REQ; + case SE_LUM_AC_INTER : + p_Vid->ec_flag[SE_LUM_AC_INTER] = EC_REQ; + case SE_CHR_AC_INTER : + p_Vid->ec_flag[SE_CHR_AC_INTER] = EC_REQ; + break; + case SE_DELTA_QUANT_INTER : + p_Vid->ec_flag[SE_DELTA_QUANT_INTER] = EC_REQ; + break; + case SE_DELTA_QUANT_INTRA : + p_Vid->ec_flag[SE_DELTA_QUANT_INTRA] = EC_REQ; + break; + default: + break; + + } + return EC_REQ; +} + +/*! + *********************************************************************** + * \brief + * resets EC_Flags called at the start of each slice + * + *********************************************************************** + */ +void reset_ec_flags(VideoParameters *p_Vid) +{ + int i; + for (i=0; iec_flag[i] = NO_EC; +} + + +/*! + *********************************************************************** + * \brief + * get error concealed element in dependence of syntax + * element se. \n + * This function implements the error concealment. + * \return + * NO_EC if no error concealment required \n + * EC_REQ if element requires error concealment + *********************************************************************** + */ +int get_concealed_element(VideoParameters *p_Vid, SyntaxElement *sym) +{ + if (p_Vid->ec_flag[sym->type] == NO_EC) + return NO_EC; + /* +#if TRACE + printf("TRACE: get concealed element for %s!!!\n", SEtypes[sym->type]); +#endif + */ + switch (sym->type) + { + case SE_HEADER : + sym->len = 31; + sym->inf = 0; // Picture Header + break; + + case SE_PTYPE : // inter_img_1 + case SE_MBTYPE : // set COPY_MB + case SE_REFFRAME : + sym->len = 1; + sym->inf = 0; + break; + + case SE_INTRAPREDMODE : + case SE_MVD : + sym->len = 1; + sym->inf = 0; // set vector to zero length + break; + + case SE_CBP_INTRA : + sym->len = 5; + sym->inf = 0; // codenumber 3 <=> no CBP information for INTRA images + break; + + case SE_LUM_DC_INTRA : + case SE_CHR_DC_INTRA : + case SE_LUM_AC_INTRA : + case SE_CHR_AC_INTRA : + sym->len = 1; + sym->inf = 0; // return EOB + break; + + case SE_CBP_INTER : + sym->len = 1; + sym->inf = 0; // codenumber 1 <=> no CBP information for INTER images + break; + + case SE_LUM_DC_INTER : + case SE_CHR_DC_INTER : + case SE_LUM_AC_INTER : + case SE_CHR_AC_INTER : + sym->len = 1; + sym->inf = 0; // return EOB + break; + + case SE_DELTA_QUANT_INTER: + sym->len = 1; + sym->inf = 0; + break; + case SE_DELTA_QUANT_INTRA: + sym->len = 1; + sym->inf = 0; + break; + default: + break; + } + + return EC_REQ; +} + diff --git a/ldecod/src/ferc/ferc_api.c b/ldecod/src/ferc/ferc_api.c new file mode 100644 index 0000000..87378ee --- /dev/null +++ b/ldecod/src/ferc/ferc_api.c @@ -0,0 +1,1419 @@ +/*! + ************************************************************************ + * \file ferc_api.c + * + * \brief + * Definitions for frame lost error concealment. + * + * Based on Thomson APIs for concealing entire frame loss. + * Modification to be compatible with frame loss in auxiliary view, + * and some improvements in error concealment strategies + * + * \author + * Previous work + * - Jill Boyce + * - Saurav K Bandyopadhyay + * - Zhenyu Wu + * - Purvin Pandit + * Actual Work + * - João Carreira + * + ************************************************************************ + */ + +#include "ferc_api.h" +#include "ferc_internals.h" +#include "image.h" +#include "stream_parser.h" +#include "mbuffer_common.h" +#include "output.h" +#include "loopfilter.h" +#include +#include + +//! OpenCV +#include "ferc_opencv_api.h" + +void sliding_window_memory_management( DecodedPictureBuffer *p_Dpb, StorablePicture* p ); + +static void Report_Frame_Info( VideoParameters *p_Vid, StorablePicture *Picture ) +{ + char ref_char = ' ', yuv_types[4][6] = + { "4:0:0", "4:2:0", "4:2:2", "4:4:4" }; + int64 tmp_time; + + gettime( &( p_Vid->end_time ) ); // end time + tmp_time = timediff( &( p_Vid->start_time ), &( p_Vid->end_time ) ); + //p_Vid->tot_time += tmp_time; + tmp_time = timenorm( tmp_time ); + + if( p_Vid->p_Inp->silent == FALSE ) + { + SNRParameters *snr = p_Vid->snr[Picture->view_id]; + +#if(MVC_EXTENSION_ENABLE) + if( p_Vid->p_ref_mvc[GetViewIdx( p_Vid, Picture->view_id )] != -1 ) + find_snr( p_Vid, Picture, &( p_Vid->p_ref_mvc[GetViewIdx( p_Vid, Picture->view_id )] ) ); +#else + if( p_Vid->p_ref != -1) + find_snr( p_Vid, (*dec_picture) , &p_Vid->p_ref); +#endif + + if( Picture->used_for_reference ) + { + ref_char = 'C'; + } + else + { + ref_char = 'c'; + } + +#if( MVC_EXTENSION_ENABLE) + if( p_Vid->p_ref_mvc[Picture->view_id] != -1 ) +#else + if (p_Vid->p_ref != -1) +#endif + { + printf( "%05d( %c ) %5d|%2d %5d %5d %8.4f %8.4f %8.4f %s %7d\n", p_Vid->frame_no, ref_char, Picture->poc, GetViewIdx( p_Vid, Picture->view_id ), + Picture->pic_num, Picture->qp, snr->snr[0], snr->snr[1], snr->snr[2], yuv_types[Picture->chroma_format_idc], ( int )tmp_time ); + } + else + { + printf( "%05d( %c ) %5d|%2d %5d %5d %s %7d\n", p_Vid->frame_no, ref_char, Picture->poc, + GetViewIdx( p_Vid, Picture->view_id ), Picture->pic_num, Picture->qp, yuv_types[Picture->chroma_format_idc], ( int )tmp_time ); + } + } + fflush( stdout ); +} + +/*! + ************************************************************************ + * \brief + * Updates the reference list for motion vector copy concealment for non- + * reference frame loss. + * + ************************************************************************ + */ +//static void mark_frame_as_used(DecodedPictureBuffer *p_Dpb) +//{ +// VideoParameters *p_Vid = p_Dpb->p_Vid; +// unsigned i, j= 0; +// +// for (i = 0; i < p_Dpb->used_size; i++) +// { +// p_Dpb->fs[i]->unused_concealment = 0; +// } +//} +/*! + ************************************************************************ + * \brief + * Mark frame as used as reference to create the correct reference list + ************************************************************************ + */ +//static void mark_frame_as_reference(DecodedPictureBuffer *p_Dpb) +//{ +// unsigned i; +// +// for (i = 0; i < p_Dpb->used_size; i++) +// { +// p_Dpb->fs[i]->temp_is_reference = p_Dpb->fs[i]->is_reference; +// p_Dpb->fs[i]->frame->temp_used_for_reference = p_Dpb->fs[i]->frame->used_for_reference; +// +// p_Dpb->fs[i]->is_reference = 3; +// p_Dpb->fs[i]->frame->used_for_reference = 1; +// } +//} +//static void restore_reference_flag(DecodedPictureBuffer *p_Dpb) +//{ +// unsigned i; +// for (i = 0; i < p_Dpb->used_size; i++) +// { +// p_Dpb->fs[i]->is_reference = p_Dpb->fs[i]->temp_is_reference; +// p_Dpb->fs[i]->frame->used_for_reference = p_Dpb->fs[i]->frame->temp_used_for_reference; +// } +//} +void FillImageBorder( VideoParameters *p_Vid, StorablePicture *Conceal_to_picture ) +{ + int x, y, pad; + + // Left / Right + for( y = 0; y < p_Vid->height; y++ ) + { + for( pad = 0; pad < p_Vid->iLumaPadX; pad++ ) + { + Conceal_to_picture->imgY[y][-pad] = Conceal_to_picture->imgY[y][+pad]; + Conceal_to_picture->imgY[y][p_Vid->width - 1 + pad] = Conceal_to_picture->imgY[y][p_Vid->width - 1 - pad]; + } + } + for( y = 0; y < p_Vid->height_cr; y++ ) + { + for( pad = 0; pad < p_Vid->iChromaPadX; pad++ ) + { + Conceal_to_picture->imgUV[0][y][-pad] = Conceal_to_picture->imgUV[0][y][+pad]; + Conceal_to_picture->imgUV[1][y][-pad] = Conceal_to_picture->imgUV[1][y][+pad]; + + Conceal_to_picture->imgUV[0][y][p_Vid->width_cr - 1 + pad] = Conceal_to_picture->imgUV[0][y][p_Vid->width_cr - 1 - pad]; + Conceal_to_picture->imgUV[1][y][p_Vid->width_cr - 1 + pad] = Conceal_to_picture->imgUV[1][y][p_Vid->width_cr - 1 - pad]; + + } + } + + // Top / Bottom + for( x = 0; x < p_Vid->width; x++ ) + { + for( pad = 0; pad < p_Vid->iLumaPadY; pad++ ) + { + Conceal_to_picture->imgY[-pad][x] = Conceal_to_picture->imgY[+pad][x]; + Conceal_to_picture->imgY[p_Vid->height - 1 + pad][x] = Conceal_to_picture->imgY[p_Vid->height - 1 - pad][x]; + } + } + for( x = 0; x < p_Vid->width_cr; x++ ) + { + for( pad = 0; pad < p_Vid->iChromaPadY; pad++ ) + { + Conceal_to_picture->imgUV[0][-pad][x] = Conceal_to_picture->imgUV[0][+pad][x]; + Conceal_to_picture->imgUV[1][-pad][x] = Conceal_to_picture->imgUV[1][+pad][x]; + + Conceal_to_picture->imgUV[0][p_Vid->height_cr - 1 + pad][x] = Conceal_to_picture->imgUV[0][p_Vid->height_cr - 1 - pad][x]; + Conceal_to_picture->imgUV[1][p_Vid->height_cr - 1 + pad][x] = Conceal_to_picture->imgUV[1][p_Vid->height_cr - 1 - pad][x]; + } + } + +} + +void fercCloseFrameConcealment( VideoParameters *p_Vid ) +{ + ConcealParameters *p_FrameConc = p_Vid->p_FrameConc; + int view; + + for( view = 0; view < MAX_VIEW_NUM; view++ ) + { + if( p_FrameConc->last_out_fs[view] ) + { + free_frame_store( p_FrameConc->last_out_fs[view] ); + } + } + + if( p_Vid->p_FrameConc ) + free( p_Vid->p_FrameConc ); + +} + +void fercOpenFrameConcealment( VideoParameters *p_Vid ) +{ + ConcealParameters *p_FrameConc; + + if( ( p_Vid->p_FrameConc = ( ConcealParameters * )calloc( 1, sizeof(ConcealParameters) ) ) == NULL ) + no_mem_exit( "alloc_video_params: p_Vid->p_FrameConc" ); + + p_FrameConc = p_Vid->p_FrameConc; + + p_FrameConc->use_DisparityProjection = 1; + + if( p_Vid->p_Inp->conceal_mode == MOTION_INTERVIEW || p_Vid->p_Inp->conceal_mode == MOTION_INTERVIEW_FIRST + || p_Vid->p_Inp->conceal_mode == MOTION_INTRA_INTER_SAD ) + { + if( !CheckOpenCVUsage() ) + { + printf( " Cannot use this concealment method without opencv!\n Disabling concealment methods!\n" ); + p_Vid->conceal_mode = MOTION_INTRAVIEW; + //fercCloseFrameConcealment( p_Vid ); + } + } +} + +/*! + ************************************************************************ + * \brief + * Save the current reference list of the last decoded frame + ************************************************************************ + */ +void fercSaveListsInfo( VideoParameters *p_Vid, Slice *currSlice ) +{ + StorablePicture *decodedPicture = p_Vid->dec_picture; + + int i; + + decodedPicture->Conc_listXInterviewPos[LIST_0] = -1; + decodedPicture->Conc_listXInterviewPos[LIST_1] = -1; + + for( i = 0; i < currSlice->listXsize[LIST_0]; i++ ) + { + decodedPicture->Conc_listX[LIST_0][i] = currSlice->listX[LIST_0][i]; + if( currSlice->listX[LIST_0][i]->poc == decodedPicture->poc ) + { + decodedPicture->Conc_listXInterviewPos[LIST_0] = i; + } + } + for( i = 0; i < currSlice->listXsize[LIST_1]; i++ ) + { + decodedPicture->Conc_listX[LIST_1][i] = currSlice->listX[LIST_1][i]; + if( currSlice->listX[LIST_1][i]->poc == decodedPicture->poc ) + { + decodedPicture->Conc_listXInterviewPos[LIST_1] = i; + } + } + decodedPicture->Conc_listXsize[LIST_0] = currSlice->listXsize[LIST_0]; + decodedPicture->Conc_listXsize[LIST_1] = currSlice->listXsize[LIST_1]; + +} + +void fercCopyImg( VideoParameters *p_Vid, StorablePicture *Source, StorablePicture *Dest ) +{ + int x, y; + //! Y + for( y = 0; y < p_Vid->height; y++ ) + for( x = 0; x < p_Vid->width; x++ ) + Dest->imgY[y][x] = Source->imgY[y][x]; + //!UV + for( y = 0; y < p_Vid->height_cr; y++ ) + for( x = 0; x < p_Vid->width_cr; x++ ) + { + Dest->imgUV[0][y][x] = Source->imgUV[0][y][x]; + Dest->imgUV[1][y][x] = Source->imgUV[1][y][x]; + } +} + +/*! + ************************************************************************ + * \brief + * This function conceals a lost frame. This is the commomn operations + * that need to be performed to conceal a lost frame + * + ************************************************************************ + */ +static void CopyCommonInformation( StorablePicture *Source, StorablePicture *Dest ) +{ + Dest->PicSizeInMbs = Source->PicSizeInMbs; + Dest->slice_type = Source->slice_type; + Dest->idr_flag = FALSE; //since we do not want to clears the ref list + Dest->no_output_of_prior_pics_flag = Source->no_output_of_prior_pics_flag; + Dest->long_term_reference_flag = Source->long_term_reference_flag; + Dest->adaptive_ref_pic_buffering_flag = Source->adaptive_ref_pic_buffering_flag = 0; + Dest->chroma_format_idc = Source->chroma_format_idc; + Dest->frame_mbs_only_flag = Source->frame_mbs_only_flag; + Dest->frame_cropping_flag = Source->frame_cropping_flag; + Dest->frame_crop_left_offset = Source->frame_crop_left_offset; + Dest->frame_crop_right_offset = Source->frame_crop_right_offset; + Dest->frame_crop_bottom_offset = Source->frame_crop_bottom_offset; + Dest->frame_crop_top_offset = Source->frame_crop_top_offset; + Dest->qp = Source->qp; + Dest->chroma_format_idc = Source->chroma_format_idc; + Dest->concealed_pic = Source->concealed_pic; + //! Inter view prediction flags + Dest->inter_view_flag = Source->inter_view_flag; + + Dest->layer_id = Source->layer_id; + + // We need these initializations for using deblocking filter for frame copy and motion copy concealment. + Dest->PicWidthInMbs = Source->PicWidthInMbs; + Dest->PicSizeInMbs = Source->PicSizeInMbs; +} +static int fercConcealOneLostFrame( VideoParameters *p_Vid, DecodedPictureBuffer *p_Dpb, StorablePicture *ConcealToPicture, int is_reference ) +{ + ConcealParameters *p_FrameConc = p_Vid->p_FrameConc; + StorablePicture *ConcealFromPicture = NULL; + unsigned int pos, temp_used_size; + int search_mode = 0, search_poc, search_view, tmp_slice_type; + StorablePicture *temp_dec_picture = p_Vid->dec_picture; + + p_FrameConc->ConcealmentMode = p_Vid->conceal_mode; + + p_Vid->dec_picture = ConcealToPicture; + + tmp_slice_type = p_Vid->type; + + // keep values of buffer used size + temp_used_size = p_Dpb->used_size; + p_Dpb->used_size_concealment = p_Dpb->used_size; + + // sort buffer frame_num up + qsort( p_Dpb->fs, p_Dpb->used_size, sizeof(FrameStore*), sort_by_frame_num_up ); + + //First time the concealment should be done using the closest frame in buffer + search_mode = 1; + search_poc = ConcealToPicture->poc; + search_view = ConcealToPicture->view_id; + do + { + + // Restore Buffer size + p_Dpb->used_size = temp_used_size; + + // Proctect from perfom a impossible concealment method + if( p_FrameConc->ConcealmentMode == FRAME_COPY_BC && ConcealToPicture->view_id == BASE_VIEW_IDX ) + p_FrameConc->ConcealmentMode = FRAME_COPY_FF; + + switch( p_FrameConc->ConcealmentMode ) + { + case FRAME_COPY_FF: + p_FrameConc->ConcealFuction = Conceal_by_FrameCopy; + if( ConcealToPicture->used_for_reference == 1 ) + { + search_mode = CLOSEST_ALL; + } + else + { + search_mode = CLOSEST_FUTURE; + } + break; + case FRAME_COPY_BC: + p_FrameConc->ConcealFuction = Conceal_by_FrameCopy; + search_mode = EXACT; + search_view = BASE_VIEW_IDX; + break; + case MOTION_COPY_TEMPORAL: + case MOTION_COPY_SPATIAL: + case MOTION_COPY_TEMPORAL_SPATIAL: + p_FrameConc->ConcealFuction = Conceal_by_MotionCopy_Ref; + search_mode = CLOSEST_FUTURE; + break; + /*case MOTION_COPY_ENHANCED: + p_FrameConc->ConcealmentMode = MOTION_COPY_TEMPORAL_SPATIAL; + p_FrameConc->ConcealFuction = Conceal_by_MotionCopyEnhanced; + search_mode = CLOSEST_FUTURE; + search_view = BASE_VIEW_IDX; + break;*/ + case REF_DFDP: + p_FrameConc->ConcealmentMode = REF_DFDP; + p_FrameConc->ConcealFuction = Conceal_by_DisparityBasedFrameDifferenceProjection; + search_mode = CLOSEST_FUTURE; + break; + case REF_ISMID: + p_FrameConc->ConcealmentMode = REF_ISMID; + p_FrameConc->ConcealFuction = Conceal_by_InterViewSimilarityMotion; + search_mode = CLOSEST_FUTURE; + break; +// case MOTION_INTRA_INTER_SAD: +// p_FrameConc->ConcealFuction = Conceal_by_IntraInterMotionCopy; +// search_mode = CLOSEST_FUTURE; +// break; + case INTERVIEW_COPY: + case MOTION_INTRAVIEW: + case MOTION_INTERVIEW: + case MOTION_INTERVIEW_FIRST: + case MOTION_INTERVIEW_MVE_INTERCOPY: + case MOTION_SMART_MOTION_BASED: + case ENHANCED_MVE: + case MOTION_ITERATIONS: + p_FrameConc->ConcealFuction = Conceal_by_IntraInterMotionCopy_V2; + search_mode = CLOSEST_FUTURE; + break; + default: + return 0; + } + + ConcealFromPicture = fercGetPictureForConcealFromDpb( p_Vid->p_Dpb_layer[search_view], ConcealToPicture->poc, search_view, search_mode, 0, &pos ); + + if( ConcealFromPicture == NULL ) + { + printf( "[FERC] An unintentional loss of pictures occurs! Exit\n" ); + return 0; // Cannot conceal this frame + } + + break; + } + while( 1 ); + + // copy all the struc from this to current concealment pic + p_Vid->frame_to_conceal = ConcealFromPicture->frame_num + 1; + p_Vid->earlier_missing_poc = ConcealToPicture->poc; + + p_Vid->type = ConcealFromPicture->slice_type; + + CopyCommonInformation( ConcealFromPicture, ConcealToPicture ); + + ConcealToPicture->concealed_pic = 1; + ConcealToPicture->PicSlice = malloc_slice( p_Vid->p_Inp, p_Vid ); + ConcealToPicture->PicSlice->chroma444_not_separate = 0; + + //! Report some debug information + printf( "[FERC] Concealing Frame %4d|%2d using frame %4d|%2d with Method %2d\n", ConcealToPicture->poc, ConcealToPicture->view_id, ConcealFromPicture->poc, + ConcealFromPicture->view_id, p_FrameConc->ConcealmentMode ); + + p_FrameConc->ConcealFuction( p_Vid, ConcealFromPicture, ConcealToPicture ); + + // Restore Buffer size + p_Dpb->used_size = temp_used_size; + + //update_ref_list(p_Vid->p_Dpb_layer[Conceal_from_picture->view_id]); + //restore_reference_flag(p_Dpb); + + DeblockPicture( p_Vid, ConcealToPicture ); + + FillImageBorder( p_Vid, ConcealToPicture ); + + if( ConcealToPicture->PicSlice ) + free_slice( ConcealToPicture->PicSlice ); + + calculate_frame_no( p_Vid, ConcealToPicture ); + + Report_Frame_Info( p_Vid, ConcealToPicture ); + +#if( _DEBUG ) + //showStorablePicture (p_Vid , Conceal_to_picture, 1,1, "[FERC] Concealed Frame", 0); +#endif + + //{ + // char FileName[FILE_NAME_SIZE], chBuf[FILE_NAME_SIZE], *pch; + // strcpy( chBuf , p_Vid->p_Inp->outfile ); + // pch = strrchr( chBuf , '.' ); + // if(pch) + // *pch = '\0'; + // if( strcmp("nul", chBuf) ) + // { + // sprintf( FileName , "%s_ViewId%02d_Frame%2d_CMethod%d.pgm" , chBuf, ConcealToPicture->view_id, ConcealToPicture->poc, p_FrameConc->ConcealmentMode); + // saveStorablePicture( p_Vid , FileName , ConcealToPicture , 0 , 0 ); + // } + //} + + p_Vid->dec_picture = temp_dec_picture; + p_Vid->type = tmp_slice_type; + return 1; + +} + +void fercModLostRefFrames( VideoParameters *p_Vid, StorablePicture *DecPicture ) +{ + StorablePicture *ConcealFromPicture; + unsigned int pos; +#if( MVC_EXTENSION_ENABLE ) + int ViewID = DecPicture->view_id; +#else + int ViewID = 0; +#endif + + ConcealFromPicture = fercGetPictureForConcealFromDpb( p_Vid->p_Dpb_layer[ViewID], + DecPicture->poc, ViewID, CLOSEST_ALL, 0, &pos ); + + if( ConcealFromPicture != NULL) + { + printf("[FERC] Conceal Picture %d using FC from picture %d", + DecPicture->poc, ConcealFromPicture->poc); + fercCopyImg( p_Vid, ConcealFromPicture, DecPicture ); + } +} + +/*! + ************************************************************************ + * \brief + * This function conceals a missing reference frame. The routine is called + * based on the difference in frame number. It conceals an IDR frame loss + * based on the sudden decrease in frame number. + * + * Edit by João Carreira + * Function edited + * + ************************************************************************ + */ +void fercConcealLostRefFrames( DecodedPictureBuffer *p_Dpb, Slice *currSlice, int is_IDR ) +{ + VideoParameters *p_Vid = p_Dpb->p_Vid; + ConcealParameters *p_FrameConc = p_Vid->p_FrameConc; + + StorablePicture *tempPicture, *conceal_to_picture = NULL; + int pos = 0; + int tmp1 = currSlice->delta_pic_order_cnt[0]; + int tmp2 = currSlice->delta_pic_order_cnt[1]; + int CurrFrameNum, UnusedShortTermFrameNum, i, tmp_concealmode = p_Vid->conceal_mode; + + if( currSlice->structure == FRAME || currSlice->structure == TOP_FIELD ) + { + gettime( &( p_Vid->start_time ) ); // start time + } + + currSlice->delta_pic_order_cnt[0] = currSlice->delta_pic_order_cnt[1] = 0; + + // printf("A gap in frame number is found, try to fill it.\n"); + + if( is_IDR ) + { + // Conceals an IDR frame loss. Uses the reference frame in the previous + // GOP for concealment. + UnusedShortTermFrameNum = 0; + p_Vid->last_ref_pic_poc[currSlice->view_id] = -p_Vid->poc_gap; + } + else + UnusedShortTermFrameNum = ( p_Vid->pre_frame_num[currSlice->view_id] + 1 ) % p_Vid->max_frame_num; + + CurrFrameNum = currSlice->frame_num; + + while( CurrFrameNum != UnusedShortTermFrameNum ) + { + int search_view = 0, search_mode, search_poc; + int find = 0; + + // check duplicate concealment + for( i = 0; i < p_Dpb->used_size; i++ ) + { + if( p_Dpb->fs[i]->frame->frame_num == UnusedShortTermFrameNum && p_Dpb->fs[i]->frame->concealed_pic != 0 ) + { + p_Dpb->fs[i]->frame->used_for_reference = 1; + // maybe this is not the best approach + //if (p_Dpb->fs[i]->frame->used_for_reference) + { + p_Dpb->fs[i]->is_reference = 3; + p_Dpb->fs[i]->is_orig_reference = 3; + if( p_Dpb->fs[i]->frame->is_long_term ) + { + p_Dpb->fs[i]->is_long_term = 3; + p_Dpb->fs[i]->long_term_frame_idx = p_Dpb->fs[i]->frame->long_term_frame_idx; + } + } + sliding_window_memory_management( p_Dpb, p_Dpb->fs[i]->frame ); + find = 1; + break; + } + } + if( !find ) + { + // alloc new Pic + conceal_to_picture = alloc_storable_picture( p_Vid, FRAME, p_Vid->width, p_Vid->height, p_Vid->width_cr, p_Vid->height_cr, 1 ); + + //init Pic params + conceal_to_picture->coded_frame = 1; + conceal_to_picture->pic_num = UnusedShortTermFrameNum; //??? + conceal_to_picture->frame_num = UnusedShortTermFrameNum; + conceal_to_picture->view_id = currSlice->view_id; + conceal_to_picture->non_existing = 0; + conceal_to_picture->is_output = 0; + conceal_to_picture->used_for_reference = 1; + /*! Mark Concealed picture -> Conceal reference */ + conceal_to_picture->concealed_pic = 1; + conceal_to_picture->adaptive_ref_pic_buffering_flag = 0; + + conceal_to_picture->layer_id = currSlice->layer_id; + + if( is_IDR ) + { + conceal_to_picture->poc = 0; + } + else if( p_Vid->p_Inp->GOP_Size == 1) + { + conceal_to_picture->poc = p_Vid->last_ref_pic_poc[currSlice->view_id] + p_Vid->p_Inp->ref_poc_gap; + } + else + { + int GOPSize = p_Vid->p_Inp->GOP_Size; + int i, hierarchical_layer; + int RefPeriod; + if( p_Vid->last_ref_pic_poc[currSlice->view_id] == 0 ) + { + hierarchical_layer = 0; + } + else + { + for( i = 1; i < RoundLog2( GOPSize ); i++ ) + { + if( ( ( p_Vid->last_ref_pic_poc[currSlice->view_id] / p_Vid->p_Inp->poc_scale ) % GOPSize / power2( i - 1 ) ) == 0 ) + break; + } + hierarchical_layer = i; + } + RefPeriod = GOPSize / power2( imin( hierarchical_layer + 1, RoundLog2( GOPSize ) ) - 1 ) * p_Vid->p_Inp->poc_scale; + + if( p_Vid->last_ref_hier0_poc[currSlice->view_id] == 0 ) + { + for( i = p_Vid->last_ref_hier0_poc[currSlice->view_id]; + i <= p_Vid->last_ref_hier0_poc[currSlice->view_id] + ( GOPSize ) * p_Vid->p_Inp->poc_scale; i += RefPeriod ) + { + if( fercGetPictureForConcealFromDpb( p_Vid->p_Dpb_layer[currSlice->view_id], i, currSlice->view_id, EXACT, 1, NULL ) == NULL ) + { + conceal_to_picture->poc = i; + break; + } + } + } + else + { + for( i = p_Vid->last_ref_hier0_poc[currSlice->view_id] - ( GOPSize ) * p_Vid->p_Inp->poc_scale + RefPeriod; + i < p_Vid->last_ref_hier0_poc[currSlice->view_id]; i += RefPeriod ) + { + if( fercGetPictureForConcealFromDpb( p_Vid->p_Dpb_layer[currSlice->view_id], i, currSlice->view_id, EXACT, 1, NULL ) == NULL ) + { + conceal_to_picture->poc = i; + break; + } + } + } + } + + // conceal_to_picture->poc = 2; + + conceal_to_picture->bottom_poc = conceal_to_picture->poc; + conceal_to_picture->frame_poc = conceal_to_picture->poc; + conceal_to_picture->top_poc = conceal_to_picture->poc; + + //refresh p_Vid params + p_Vid->last_ref_pic_poc[currSlice->view_id] = conceal_to_picture->poc; + + //get a prev pic to conceal lost pic + search_view = conceal_to_picture->view_id; + search_poc = conceal_to_picture->poc; + search_mode = p_Vid->p_Inp->search_mode_ref; + + if( UnusedShortTermFrameNum == 0 ) + { + // First frame can only be concealed by frame copy method + p_Vid->conceal_mode = FRAME_COPY_BC; + } + else if( UnusedShortTermFrameNum == 1 && p_Vid->conceal_mode > FRAME_COPY_BC ) + { + // Second frame of GOP dont have motion vector references so need to be frame copy + p_Vid->conceal_mode = FRAME_COPY_FF; + } + + currSlice->frame_num = UnusedShortTermFrameNum; + + //! This picture may be already concealed + if( ( tempPicture = fercGetPictureForConcealFromDpb( p_Dpb, conceal_to_picture->poc, conceal_to_picture->view_id, EXACT, 1, &pos ) ) != NULL ) + { + CopyCommonInformation( tempPicture, conceal_to_picture ); + fercCopyImg( p_Vid, tempPicture, conceal_to_picture ); + calculate_frame_no( p_Vid, conceal_to_picture ); + remove_frame_from_dpb( p_Dpb, pos ); + } + else + { + if( !fercConcealOneLostFrame( p_Vid, p_Dpb, conceal_to_picture, 1 ) ) + printf( "[FERC] Cannot conceal current loss picture\n" ); + //regist the concealed frame +#if( MVC_EXTENSION_ENABLE) + p_Vid->nFramesConceal[currSlice->view_id]++; +#endif + } + + //if (UnusedShortTermFrameNum == 0) + if( is_IDR ) + { + conceal_to_picture->slice_type = I_SLICE; + conceal_to_picture->idr_flag = TRUE; +#if (MVC_EXTENSION_ENABLE) + //for( i=currSlice->view_id; i< MAX_NUM_DPB_LAYERS;i++) + idr_memory_management( p_Vid->p_Dpb_layer[currSlice->layer_id], conceal_to_picture ); +#else + flush_dpb(p_Dpb); +#endif + p_Vid->last_ref_pic_poc[currSlice->view_id] = conceal_to_picture->poc; + } + + update_ref_list( p_Dpb ); + //dump_dpb(p_Dpb); + + store_picture_in_dpb( p_Dpb, conceal_to_picture ); + + } +#if (MVC_EXTENSION_ENABLE) + update_ref_list( p_Dpb ); + update_ltref_list( p_Dpb ); +#endif + + p_Vid->pre_frame_num[currSlice->view_id] = UnusedShortTermFrameNum; + UnusedShortTermFrameNum = ( UnusedShortTermFrameNum + 1 ) % p_Vid->max_frame_num; + // update reference flags and set current flag. + for( i = 16; i > 0; i-- ) + { + currSlice->ref_flag[i] = currSlice->ref_flag[i - 1]; + } + currSlice->ref_flag[0] = 0; + } + currSlice->delta_pic_order_cnt[0] = tmp1; + currSlice->delta_pic_order_cnt[1] = tmp2; + currSlice->frame_num = CurrFrameNum; + p_Vid->conceal_mode = tmp_concealmode; +} + +void fercFillCurrPicture( VideoParameters *p_Vid, StorablePicture *currPicture ) +{ + DecodedPictureBuffer *p_Dpb = p_Vid->p_Dpb_layer[currPicture->view_id]; + StorablePicture *auxPicture; + unsigned int pos; + auxPicture = fercGetPictureForConcealFromDpb( p_Dpb, currPicture->poc, currPicture->view_id, CLOSEST_ALL, 1, &pos ); + if( auxPicture != NULL ) + fercCopyImg( p_Vid, auxPicture, currPicture ); +} + +/*! + ************************************************************************ + * \brief + * + * + * + * + ************************************************************************ + */ +void fercConcealLostInterviewRefFrames( DecodedPictureBuffer *p_Dpb, StorablePicture **LostPicture, Slice *currSlice, int MissingView ) +{ + VideoParameters *p_Vid = p_Dpb->p_Vid; + StorablePicture *conceal_to_picture; + int MissingPoc = currSlice->framepoc; + + conceal_to_picture = alloc_storable_picture( p_Vid, FRAME, p_Vid->width, p_Vid->height, p_Vid->width_cr, p_Vid->height_cr, 1 ); + conceal_to_picture->poc = conceal_to_picture->frame_poc = conceal_to_picture->top_poc = conceal_to_picture->bottom_poc = MissingPoc; + conceal_to_picture->view_id = MissingView; + + conceal_to_picture->pic_num = currSlice->frame_num; + conceal_to_picture->frame_num = currSlice->frame_num; + + /* Mark Concealed picture -> Conceal reference */ + conceal_to_picture->concealed_pic = 1; + conceal_to_picture->used_for_reference = 0; + + /* Set InterView Flag */ + conceal_to_picture->inter_view_flag = 1; + + if( !fercConcealOneLostFrame( p_Vid, p_Dpb, conceal_to_picture, 0 ) ) + { + printf( "Cannot conceal current loss picture\n" ); + } + + store_picture_in_dpb( p_Dpb, conceal_to_picture ); +#if (MVC_EXTENSION_ENABLE) + update_ref_list( p_Dpb ); + update_ltref_list( p_Dpb ); +#endif + + *LostPicture = conceal_to_picture; + + p_Vid->nFramesConceal[MissingView]++; + + return; +} + +/*! + ************************************************************************ + * \brief + * Perform Sliding window decoded reference picture marking process. It + * maintains the POC s stored in the dpb at a specific instance. + * + ************************************************************************ + */ +void fercSlindingWindowsOnceFrameRemoved( DecodedPictureBuffer *p_Dpb, int poc ) +{ + VideoParameters *p_Vid = p_Dpb->p_Vid; + ConcealParameters *p_FrameConc = p_Vid->p_FrameConc; + unsigned int i, pos; + int layer_id = p_Dpb->layer_id; + + if( p_Vid->conceal_mode == NONE_CONCEAL ) + return; + for( i = 0, pos = 0; i < p_Dpb->used_size; i++ ) + { + if( p_FrameConc->fercPOCinDpb[layer_id][i] == poc ) + { + pos = i; + break; + } + } + for( i = pos; i < p_Dpb->used_size - 1; i++ ) + { + p_FrameConc->fercPOCinDpb[layer_id][i] = p_FrameConc->fercPOCinDpb[layer_id][i + 1]; + } + p_FrameConc->fercPOCinDpb[layer_id][i + 1] = -1; +} + +void fercAddPOC2Dpb( DecodedPictureBuffer *p_Dpb, int poc ) +{ + VideoParameters *p_Vid = p_Dpb->p_Vid; + ConcealParameters *p_FrameConc = p_Vid->p_FrameConc; + int layer_id = p_Dpb->layer_id; + + if( p_Vid->conceal_mode == NONE_CONCEAL ) + return; + + p_FrameConc->fercPOCinDpb[layer_id][p_Dpb->used_size] = poc; + +} +void fercResetPOCInDpb( DecodedPictureBuffer *p_Dpb ) +{ + VideoParameters *p_Vid = p_Dpb->p_Vid; + ConcealParameters *p_FrameConc = p_Vid->p_FrameConc; + int layer_id = p_Dpb->layer_id; + + if( p_Vid->conceal_mode == NONE_CONCEAL ) + return; + + memset( p_FrameConc->fercPOCinDpb[layer_id], -1, sizeof(int) * 100 ); +} + +/*! + ************************************************************************ + * \brief + * Organize the concealment frames list + ************************************************************************ + */ +static void Add_Concealment_Node( VideoParameters *p_Vid, StorablePicture* ConcealedPicture, int layer ) +{ + ConcealParameters *p_FrameConc = p_Vid->p_FrameConc; + struct ConcealNode **conceal_list = &( p_FrameConc->ConcealHead[layer] ); + + struct ConcealNode *new_node; + + new_node = ( struct ConcealNode * )calloc( 1, sizeof(struct ConcealNode) ); + + if( new_node == NULL ) + { + no_mem_exit( "alloc_new_concealment_node: p_Vid->p_FrameConc->ConcealHead" ); + } + else + { + new_node->picture = ConcealedPicture; + new_node->MissPoc = ConcealedPicture->poc; + new_node->next = NULL; + } + + if( p_FrameConc->ConcealHead[layer] == NULL ) + { + p_FrameConc->ConcealHead[layer] = new_node; + return; + } + while( *conceal_list != NULL && ( *conceal_list )->MissPoc < new_node->MissPoc ) + conceal_list = &( *conceal_list )->next; + + new_node->next = ( *conceal_list ); + *conceal_list = new_node; + +#if (PRINT_ECNODES) + printf("Added node: "); + printf("Conceal node Frame POC = %5d View = %3d \n", ptr->MissPoc, ptr->MissView ); +#endif + return; +} +static void Delete_Concealment_Node( VideoParameters *p_Vid, struct ConcealNode *ptr, int view_id ) +{ + ConcealParameters *p_FrameConc = p_Vid->p_FrameConc; + // We only need to delete the first node in the linked list + if( ptr == p_FrameConc->ConcealHead[view_id] ) + { + p_FrameConc->ConcealHead[view_id] = p_FrameConc->ConcealHead[view_id]->next; +#if (PRINT_ECNODES) + printf("deleted node: "); + printf("Conceal node Frame POC = %5d View = %3d \n", ptr->MissPoc, ptr->MissView ); +#endif + free_storable_picture( ptr->picture ); + free( ptr ); + } +} +//static void Print_List( struct ConcealNode *ptr ) +//{ +//#if (PRINT_ECNODES) +// while( ptr != NULL ) +// { +// printf("Conceal node Frame POC = %5d View = %3d \n", ptr->MissPoc, ptr->MissView ); +// ptr = ptr->next; +// } +//#endif +//} +//static void Delete_List( VideoParameters *p_Vid, struct ConcealNode *ptr, int view_id) +//{ +// ConcealParameters *p_FrameConc = p_Vid->p_FrameConc; +// struct ConcealNode *temp; +// +// if( p_FrameConc->ConcealHead[view_id] == NULL ) return; +// +// if( ptr == p_FrameConc->ConcealHead[view_id] ) +// { +// p_FrameConc->ConcealHead[view_id] = NULL; +// } +// else +// { +// temp = p_FrameConc->ConcealHead[view_id]; +// +// while( temp->next != ptr ) +// temp = temp->next; +// } +// +// while( ptr != NULL ) +// { +// temp = ptr->next; +// free( ptr ); +// ptr = temp; +// } +//} + +/*! + ************************************************************************ + * \brief + * Check if the current lost frame have already been concealed + ************************************************************************ + */ +static inline int check_duplicate_concealment( VideoParameters *p_Vid, int poc, int view_id ) +{ + ConcealParameters *p_FrameConc = p_Vid->p_FrameConc; + struct ConcealNode *temp = p_FrameConc->ConcealHead[view_id]; + if( temp == NULL ) + return 0; + for( temp = p_FrameConc->ConcealHead[view_id]; temp != NULL ; temp = temp->next ) + { + if( temp->MissPoc == poc ) //&& temp->MissView == view_id) + return 1; + } + return 0; +} + +/*! + ************************************************************************ + * \brief + * Stores the missing non reference frames in the concealment buffer. The + * detection is based on the POC difference in the sorted POC array. A missing + * non reference frame is detected when the dpb is full. A singly linked list + * is maintained for storing the missing non reference frames. + * + * Edit by Joao Carreira + * Function edited + * + ************************************************************************ + */ +void fercConcealLostNonRefFrames( DecodedPictureBuffer *p_Dpb, unsigned int diff ) +{ + VideoParameters *p_Vid = p_Dpb->p_Vid; + ConcealParameters *p_FrameConc = p_Vid->p_FrameConc; + StorablePicture *conceal_to_picture = NULL; + unsigned int i_pic, j_pic; + int temp_used_size = p_Dpb->used_size; + int missing_poc = 0; + int view_id = 0; + + if( p_Dpb->used_size <= diff + 2 ) + return; + + /* Sort Dpb to get all frames in ascendent order */ + qsort( p_Dpb->fs, p_Dpb->used_size, sizeof(FrameStore*), sort_by_poc_up ); + + for( i_pic = 0; i_pic < ( p_Dpb->used_size - diff - 1 ); i_pic++ ) + { + if( p_Dpb->fs[i_pic]->is_output ) + continue; + + //find the next frame in Dpb with same view_id + //for (j_pic=i_pic+1; (p_Dpb->fs[j_pic]->view_id != p_Dpb->fs[i_pic]->view_id) && (j_pic < p_Dpb->used_size-diff-1) ;j_pic++); + j_pic = i_pic + 1; + + missing_poc = p_Dpb->fs[i_pic]->poc; + +#if(MVC_EXTENSION_ENABLE) + view_id = p_Dpb->fs[i_pic]->view_id; +#else + view_id = 0; +#endif + + while( ( p_Dpb->fs[j_pic]->frame->poc - missing_poc ) > p_Vid->poc_gap ) + { + missing_poc = missing_poc + p_Vid->poc_gap; + + if( check_duplicate_concealment( p_Vid, missing_poc, view_id ) ) + continue; + + if( p_FrameConc->flag_bad_poc_calculation ) + { + unsigned int ii_pic = 0, find = 0; + + for( ; ii_pic < ( p_Dpb->used_size - diff ); ii_pic++ ) + if( p_Dpb->fs[ii_pic]->frame->poc == p_Dpb->fs[ii_pic + 1]->frame->poc ) + { + StorablePicture *conc_pic = p_Dpb->fs[ii_pic]->frame; + + conc_pic->poc = missing_poc; + conc_pic->poc = missing_poc; + + calculate_frame_no( p_Vid, conc_pic ); + // p_Dpb->fs[ii_pic]->frame_no = p_Dpb->fs[ii_pic]->frame->frame_no; + + p_FrameConc->flag_bad_poc_calculation = 0; + break; + } + if( find ) + continue; + } + + // Concealment of Non Reference picture starts here + conceal_to_picture = alloc_storable_picture( p_Vid, FRAME, p_Vid->width, p_Vid->height, p_Vid->width_cr, p_Vid->height_cr, 1 ); + conceal_to_picture->poc = conceal_to_picture->frame_poc = conceal_to_picture->top_poc = conceal_to_picture->bottom_poc = missing_poc; + conceal_to_picture->view_id = view_id; + /* Mark Concealed picture -> Conceal non reference */ + conceal_to_picture->concealed_pic = 2; + + if( !fercConcealOneLostFrame( p_Vid, p_Dpb, conceal_to_picture, 0 ) ) + { + printf( "Cannot conceal current loss picture\n" ); + } + + calculate_frame_no( p_Vid, conceal_to_picture ); + + // re-order picture buffer + qsort( p_Dpb->fs, p_Dpb->used_size, sizeof(FrameStore*), sort_by_poc_up ); + + Add_Concealment_Node( p_Vid, conceal_to_picture, view_id ); + + p_Vid->nFramesConceal[view_id]++; + } + } + p_Dpb->used_size = temp_used_size; + qsort( p_Dpb->fs, p_Dpb->used_size, sizeof(FrameStore*), sort_by_frame_num_up ); +} + +/*! + ************************************************************************ + * \brief + * Outputs the non reference frames. The POCs in the concealment buffer are + * sorted in ascending order and outputted when the lowest POC in the + * concealment buffer is lower than the lowest in the p_Dpb-> The linked list + * entry corresponding to the outputted POC is immediately deleted. + * + ************************************************************************ + */ +void fercWriteConcealedLostNonRefFrames( DecodedPictureBuffer *p_Dpb, int iPoc, int View_Id ) +{ + VideoParameters *p_Vid = p_Dpb->p_Vid; + ConcealParameters *p_FrameConc = p_Vid->p_FrameConc; + FrameStore concealment_fs; + StorablePicture *conceal_picture; + // int last_output_poc = p_Dpb->last_output_poc[iView]; + + while( p_FrameConc->ConcealHead[View_Id] != NULL && p_FrameConc->ConcealHead[View_Id]->MissPoc <= iPoc ) + { + if( p_FrameConc->ConcealHead[View_Id]->MissPoc == iPoc ) + { +#if (PRINT_ECNODES) + if (!p_Vid->p_Inp->silent) + printf( "Duplicate frame. Discart concealed frame!\n"); +#endif + Delete_Concealment_Node( p_Vid, p_FrameConc->ConcealHead[View_Id], View_Id ); + p_Vid->nFramesConceal[View_Id]--; + continue; + } + + concealment_fs.frame = conceal_picture = p_FrameConc->ConcealHead[View_Id]->picture; + concealment_fs.view_id = conceal_picture->view_id = View_Id; + concealment_fs.poc = conceal_picture->poc = p_FrameConc->ConcealHead[View_Id]->MissPoc; + concealment_fs.is_output = 0; + concealment_fs.is_reference = 0; + concealment_fs.is_used = 3; + + conceal_picture->concealed_pic = 2; + + p_FrameConc->last_out_frame_no[View_Id] = conceal_picture->frame_no; + +#if (MVC_EXTENSION_ENABLE) + write_stored_frame( p_Vid, &concealment_fs, p_Vid->p_out_mvc[p_Dpb->layer_id] ); +#else + write_stored_frame(p_Vid, &concealment_fs, p_Vid->p_out); +#endif + + Delete_Concealment_Node( p_Vid, p_FrameConc->ConcealHead[View_Id], View_Id ); + } +} + +/*! + ************************************************************************ + * \brief + * Conceals the remaining lost frames. this fuction normally conceal the last + * frames of a GOP + ************************************************************************ + */ +void fercConcealRemainingLostFrames( DecodedPictureBuffer *p_Dpb, FrameStore *fs ) +{ + VideoParameters *p_Vid = p_Dpb->p_Vid; + ConcealParameters *p_FrameConc = p_Vid->p_FrameConc; + + int view_id = fs->view_id; + int last_frame_no = p_FrameConc->last_out_frame_no[view_id]; + + if( p_FrameConc->last_out_fs[view_id] == NULL ) + { + p_FrameConc->last_out_fs[view_id] = alloc_frame_store(); + if( p_FrameConc->last_out_fs[view_id]->frame == NULL ) + { + p_FrameConc->last_out_fs[view_id]->frame = alloc_storable_picture( p_Vid, FRAME, p_Vid->width, p_Vid->height, p_Vid->width_cr, p_Vid->height_cr, 1 ); + p_FrameConc->last_out_fs[view_id]->is_used = 3; + } + } + + while( fs->frame->frame_no > last_frame_no + 1 ) + { + StorablePicture *Frame = alloc_storable_picture( p_Vid, FRAME, p_Vid->width, p_Vid->height, p_Vid->width_cr, p_Vid->height_cr, 1 ); + + //dump_dpb(p_Dpb); + + last_frame_no++; + + CopyCommonInformation( p_FrameConc->last_out_fs[view_id]->frame, Frame ); + fercCopyImg( p_Vid, p_FrameConc->last_out_fs[view_id]->frame, Frame ); + + p_Vid->frame_no = Frame->frame_no = last_frame_no; + Frame->view_id = view_id; + + /* Mark Concealed picture -> Conceal final picture */ + Frame->concealed_pic = 3; + + Report_Frame_Info( p_Vid, Frame ); + + direct_output( p_Vid, Frame, p_Vid->p_out_mvc[p_Dpb->layer_id] ); + //write_stored_frame(p_Vid, p_FrameConc->last_out_fs[view_id], p_Vid->p_out_mvc[p_Dpb->layer_id]); + + // free_storable_picture( Frame ); + + p_Vid->nFramesConceal[view_id]++; + } + + p_FrameConc->last_out_frame_no[view_id] = fs->frame->frame_no; + + CopyCommonInformation( fs->frame, p_FrameConc->last_out_fs[view_id]->frame ); + fercCopyImg( p_Vid, fs->frame, p_FrameConc->last_out_fs[view_id]->frame ); +} + +/*! + ************************************************************************ + * \brief + * Get disparity Map for the frame ready to de decoded + ************************************************************************ + */ +void GetDisparityForCurrentSlice( Slice *currSlice ) +{ + VideoParameters *p_Vid = currSlice->p_Vid; + DecodedPictureBuffer *p_Dpb = currSlice->p_Dpb; + StorablePicture *dec_picture = p_Vid->dec_picture; + StorablePicture *disparity, *new_disparity; + int cont, list, idx; + StorablePicture *Base_Reference, *AuxFrame_Disp, *BaseFrame_Disp; + + imgpel *storeYUV = ( imgpel * )malloc( ( 16 + ( p_Vid->mb_cr_size_x * p_Vid->mb_cr_size_y ) * 2 / 16 ) * sizeof(imgpel) ); + + dec_picture->disparity = NULL; + + return; + + if( currSlice->view_id == BASE_VIEW_IDX ) + return; + + for( list = LIST_0, cont = 0; list < LIST_1; list++ ) + { + for( idx = 0; idx < currSlice->listXsize[list]; idx++ ) + { + cont += currSlice->listX[list][idx]->concealed_pic; + } + } + if( !cont ) + return; + + AuxFrame_Disp = fercGetPictureForConcealFromDpb( p_Vid->p_Dpb_layer[1], currSlice->ThisPOC, 1, CLOSEST_ALL, 0, NULL ); + if( !AuxFrame_Disp ) + return; + + BaseFrame_Disp = fercGetPictureForConcealFromDpb( p_Vid->p_Dpb_layer[0], AuxFrame_Disp->poc, 0, EXACT, 0, NULL ); + if( !AuxFrame_Disp ) + return; + + disparity = alloc_storable_picture( p_Vid, FRAME, p_Vid->width, p_Vid->height, p_Vid->width_cr, p_Vid->height_cr, 1 ); + new_disparity = alloc_storable_picture( p_Vid, FRAME, p_Vid->width, p_Vid->height, p_Vid->width_cr, p_Vid->height_cr, 1 ); + if( !disparity ) + return; + + //cvDisparity( p_Vid , AuxFrame_Disp , BaseFrame_Disp , disparity, p_Vid->p_Inp->DisparityMethod, p_Vid->p_Inp->DisparityWindow); + + disparity->poc = BaseFrame_Disp->poc; + disparity->PicSizeInMbs = BaseFrame_Disp->PicSizeInMbs; + disparity->PicWidthInMbs = BaseFrame_Disp->PicWidthInMbs; + + Base_Reference = fercGetPictureForConcealFromDpb( p_Vid->p_Dpb_layer[0], dec_picture->poc, 0, EXACT, 0, NULL ); + + new_disparity = alloc_storable_picture( p_Vid, FRAME, p_Vid->width, p_Vid->height, p_Vid->width_cr, p_Vid->height_cr, 1 ); + new_disparity->PicSizeInMbs = disparity->PicSizeInMbs; + new_disparity->PicWidthInMbs = disparity->PicWidthInMbs; + + new_disparity->chroma_format_idc = 1; + new_disparity->poc = Base_Reference->poc; + + DisparityProjection( p_Vid, disparity, Base_Reference, currSlice, &new_disparity, BLOCK_SIZE, 1 ); + + free_storable_picture( disparity ); + + dec_picture->disparity = new_disparity; + + //showStorablePicture(p_Vid, new_disparity, 0, "Disparity", 0); +} + +/*! + ************************************************************************ + * \brief + * Try to avoid error propagation + ************************************************************************ + */ +void ChangeInterViewPrediction( Macroblock *currMB, StorablePicture *dec_picture, int pred_dir, int i, int j, int block_size_x, int block_size_y ) +{ + Slice *currSlice = currMB->p_Slice; + int i4 = currMB->block_x + i; + int j4 = currMB->block_y + j; + PicMotionParams *mv_info = &dec_picture->mv_info[j4][i4]; + StorablePicture *IntraRefFrame, *Ref; + char idx, ref_idx, TempIdx = 0; + MotionPropInfo *Matrix_MV_Info = dec_picture->Matrix_MV_Info; + int ii4, jj4, mv[2] = + { 0, 0 }, mv_count = 0; + double scale_factor; + + if( !Matrix_MV_Info ) + return; + + //! Get concealment references + for( idx = 0; idx < currSlice->listXsize[0]; idx++ ) + { + if( currSlice->listX[0][idx]->view_id == currSlice->view_id ) + { + IntraRefFrame = currSlice->listX[0][idx]; + TempIdx = idx; + break; + } + } + + if( ( pred_dir == 0 || pred_dir == 2 ) && ( ref_idx = mv_info->ref_idx[pred_dir] ) >= 0 ) + { // Changes for the LIST_0 + + pred_dir = 0; + + ref_idx = mv_info->ref_idx[pred_dir]; + Ref = currMB->p_Slice->listX[currMB->list_offset + pred_dir][ref_idx]; + if( Ref->concealed_pic && Ref->view_id != currSlice->view_id ) // mv modification + { + for( jj4 = 0; jj4 < block_size_y / 4; jj4++ ) + { + for( ii4 = 0; ii4 < block_size_x / 4; ii4++ ) + { + if( Matrix_MV_Info->mv_info_count[j4 + jj4][i4 + ii4] >= 0 ) + { + mv[0] += Matrix_MV_Info->mv_info[j4 + jj4][i4 + ii4][mv_count].mv[LIST_0].mv_x; + mv[1] += Matrix_MV_Info->mv_info[j4 + jj4][i4 + ii4][mv_count].mv[LIST_0].mv_y; + mv_count++; + } + } + } + mv[0] = ( int )( ( double )( mv[0] ) / mv_count ); + mv[1] = ( int )( ( double )( mv[1] ) / mv_count ); + + // scale_factor = (double)( currSlice->ThisPOC - IntraRefFrame->poc ) / (double)( currSlice->ThisPOC - Ref->poc ); + scale_factor = 1; + mv_info->mv->mv_x = ( short )( ( double )( mv[0] ) * scale_factor ); + mv_info->mv->mv_y = ( short )( ( double )( mv[1] ) * scale_factor ); + mv_info->ref_idx[currMB->list_offset + pred_dir] = TempIdx; + mv_info->ref_pic[currMB->list_offset + pred_dir] = IntraRefFrame; + + } + + } + +} +void ProjectMotionToCurrSlice( VideoParameters *p_Vid, StorablePicture *currPicture, Slice *currSlice ) +{ + DecodedPictureBuffer *p_Dpb = p_Vid->p_Dpb_layer[currSlice->view_id]; + ConcealParameters *p_FrameConc = p_Vid->p_FrameConc; + int use_interview_reference = 1; + char idx, flagNeedMod = 0; + StorablePicture *IntraRefFrame = NULL; + MotionPropInfo *Matrix_MV_Info; + // Reference Slices - Store the information about pseudo Slices + // corresponding to the reference Frames + Slice *pSliceIntraViewReference = NULL; + + if( currPicture->view_id != 1 ) + return; + + //! Ccheck concealed references + for( idx = 0; idx < currSlice->listXsize[LIST_0]; idx++ ) + { + if( currSlice->listX[LIST_0][idx]->view_id != currSlice->view_id && currSlice->listX[LIST_0][idx]->concealed_pic ) + flagNeedMod = 1; + } + if( flagNeedMod == 0 ) + { + return; + } + + //! Get concealment references + for( idx = 0; idx < currSlice->listXsize[0]; idx++ ) + { + if( currSlice->listX[0][idx]->view_id == currSlice->view_id ) + { + IntraRefFrame = currSlice->listX[0][idx]; + break; + } + } + + if( !IntraRefFrame ) + { + return; + } + + if( IntraRefFrame->Conc_listXsize[LIST_0] == 1 && IntraRefFrame->Conc_listXInterviewPos[LIST_0] == 0 ) + { + return; + } + + //! Initialization of reference list for Intra-View Reference + pSliceIntraViewReference = malloc_slice( p_Vid->p_Inp, p_Vid ); + + pSliceIntraViewReference->p_Vid = p_Vid; + pSliceIntraViewReference->p_Dpb = p_Vid->p_Dpb_layer[IntraRefFrame->view_id]; + + pSliceIntraViewReference->ThisPOC = IntraRefFrame->poc; + pSliceIntraViewReference->framepoc = IntraRefFrame->poc; + pSliceIntraViewReference->view_id = IntraRefFrame->view_id; + pSliceIntraViewReference->layer_id = IntraRefFrame->layer_id; + pSliceIntraViewReference->slice_type = IntraRefFrame->slice_type; + + pSliceIntraViewReference->listXsize[LIST_0] = IntraRefFrame->Conc_listXsize[LIST_0]; + pSliceIntraViewReference->listXsize[LIST_1] = IntraRefFrame->Conc_listXsize[LIST_1]; + for( idx = 0; idx < pSliceIntraViewReference->listXsize[LIST_0]; idx++ ) + { + pSliceIntraViewReference->listX[LIST_0][idx] = IntraRefFrame->Conc_listX[LIST_0][idx]; + if( IntraRefFrame->Conc_listXInterviewPos[LIST_0] == idx ) + { + if( use_interview_reference ) + pSliceIntraViewReference->listX[LIST_0][idx] = fercGetPictureForConcealFromDpb( p_Vid->p_Dpb_layer[0], IntraRefFrame->poc, 0, EXACT, 1, NULL ); + else + pSliceIntraViewReference->listX[LIST_0][idx] = p_Vid->no_reference_picture; + } + } + for( idx = 0; idx < pSliceIntraViewReference->listXsize[LIST_1]; idx++ ) + { + pSliceIntraViewReference->listX[LIST_1][idx] = IntraRefFrame->Conc_listX[LIST_1][idx]; + if( IntraRefFrame->Conc_listXInterviewPos[LIST_1] == idx ) + { + if( use_interview_reference ) + pSliceIntraViewReference->listX[LIST_1][idx] = p_Dpb->fs_ilref[0]->frame; + else + pSliceIntraViewReference->listX[LIST_1][idx] = p_Vid->no_reference_picture; + } + } + printf( "[FERC] Printing references list for source of vectors\n" ); + dump_ref_lists( pSliceIntraViewReference ); + + if( !currPicture->Matrix_MV_Info ) + currPicture->Matrix_MV_Info = ( MotionPropInfo * )malloc( sizeof(MotionPropInfo) ); + + Matrix_MV_Info = currPicture->Matrix_MV_Info; + + Matrix_MV_Info->mv_info = NULL; + Matrix_MV_Info->mv_info_count = NULL; + + //! TODO: Check this error + //getBestMotionVectorCandidates(p_Vid, IntraRefFrame, pSliceIntraViewReference, currPicture, Matrix_MV_Info, NULL, p_Vid->p_Inp->MotionVectorCandidates, 0 ); + + if( pSliceIntraViewReference ) + free_slice( pSliceIntraViewReference ); + + return; +} +void fercFreeMotionPropInfo( StorablePicture *p ) +{ + MotionPropInfo *Matrix_MV_Info = p->Matrix_MV_Info; + if( Matrix_MV_Info ) + { + if( Matrix_MV_Info->mv_info ) + free_mem3Dmp( Matrix_MV_Info->mv_info ); + + if( Matrix_MV_Info->mv_info ) + free_mem2Dint( Matrix_MV_Info->mv_info_count ); + + free( Matrix_MV_Info ); + } +} diff --git a/ldecod/src/ferc/ferc_dfdp.c b/ldecod/src/ferc/ferc_dfdp.c new file mode 100644 index 0000000..45ce185 --- /dev/null +++ b/ldecod/src/ferc/ferc_dfdp.c @@ -0,0 +1,589 @@ +/*! + ************************************************************************ + * \file ferc_dfdp.c + * + * \brief + * Internal function for ferc API + * Include the method proposed: + * Yibin Chen; Canhui Cai; Kai-Kuang Ma;,"Stereoscopic video error concealment for missing frame recovery using + * disparity-based frame difference projection," Image Processing (ICIP), + * 2009 16th IEEE International Conference on , vol., no., pp.4289-4292, 7-10 Nov. 2009 + * + * \author + * - Jo���o Carreira + * + ************************************************************************ + */ + +#include + +#include "ferc_api.h" +#include "ferc_internals.h" + +#include "global.h" +#include "mbuffer.h" +#include "mbuffer_mvc.h" +#include "memalloc.h" +#include "image.h" +#include "mc_prediction.h" +#include "macroblock.h" +#include "block.h" +#include "mbuffer_common.h" +#include "win32.h" +#include "ferc_opencv_api.h" + +#define NORMALIZATION_VALUE 255 +/*! + ************************************************************************ + * \brief + * Difference of imgpel matrix + * C = A - B + ************************************************************************ + */ +static int i_diff_abs( int a, int b) +{ + return( iabs(a-b) ); +} +static int i_diff( int a, int b) +{ + return( a - b ); +} +static void image_diff( imgpel **A, imgpel **B, int **C, int size_x, int size_y, int absolute ) +{ + int i, j; + int (*funcao_diff)( int a, int b); + + if( absolute ) + funcao_diff = i_diff_abs; + else + funcao_diff = i_diff; + + for(i=0; i Max) + { + Max = A[j][i]; + } + } + } + return Max; +} + +/*! + ************************************************************************ + * \brief + * Minimun value of imgpel matrix + * Min = min(A) + ************************************************************************ + */ +static int min_matrix( int **A, int size_x, int size_y ) +{ + int i, j, Min = INT_MAX; + for(i=0; i= prev_thresh ) + { + sum_g1 += diff[j][i]; + elem_g1 += 1; + } + //! group 2 + else + { + sum_g2 += diff[j][i]; + elem_g2 += 1; + } + } + } + average_g1 = (double)(sum_g1) / (double)(elem_g1); + average_g2 = (double)(sum_g2) / (double)(elem_g2); + + return ( ( average_g2 + average_g1) / 2 ); +} + +/*! + ************************************************************************ + * \brief + * Compute the global disparity for the whole image + ************************************************************************ + */ +static int get_Global_Disparity( StorablePicture *Ref, StorablePicture *NonRef, StorablePicture *CDM, int WindowSize ) +{ + int x, y, d, global_d = 0; + int size_x = Ref->size_x, size_y = Ref->size_y; + unsigned long long int sad, min_sad = ULLONG_MAX; + + for(d=-WindowSize; d<=WindowSize; d++) + { + sad = 0; + for(y=0; yimgY[y][x] > 0 ) + { + sad += iabs( Ref->imgY[y][x] - NonRef->imgY[y][x-d]); + } + } + } + if( sad <= min_sad ) + { + min_sad = sad; + global_d = d; + } + } + return global_d; +} + +/*! + ************************************************************************ + * \brief + * Compute the local disparity for each pixel using a square block + * for a symmetric window + * + * The LocalDisparity are normalized between + * [-GlobalWindowsSize-LocalWindowsSize, GlobalWindowsSize+LocalWindowsSize] + * Disp = Disp + GlobalWindowsSize + LocalWindowsSize; + ************************************************************************ + */ +static void get_GlobalplusLocal_Disparity( StorablePicture *Ref, StorablePicture *NonRef, StorablePicture *CDM, + int GlobalDisparity, int GlobalWindowsSize, int LocalWindowsSize, int BlockSize, + StorablePicture *LocalDisparity) +{ + int x, y, xx, yy, d, local_d; + int size_x = Ref->size_x, size_y = Ref->size_y; + unsigned long long int sad, min_sad; + + for(y=0; yimgY[y][x] > 0) + { + for(d=-LocalWindowsSize; d<=LocalWindowsSize; d++) + { + sad = 0; + for(yy=-(BlockSize/2); yy<=(BlockSize/2); yy++) + { + for(xx=-(BlockSize/2); xx<=(BlockSize/2); xx++) + { + sad += iabs( Ref->imgY[y+yy][x+xx] - NonRef->imgY[y+yy][x+xx-d-GlobalDisparity]); + } + } + if( sad < min_sad ) + { + min_sad = sad; + local_d = d; + } + } + if( GlobalWindowsSize + LocalWindowsSize + GlobalDisparity + local_d >= 0 ) + { + LocalDisparity->imgY[y][x] = ( GlobalWindowsSize + LocalWindowsSize ) + ( GlobalDisparity + local_d ); + } + else + { + LocalDisparity->imgY[y][x] = 0; + printf("Inaccurate disparity value for pixel (%d,%d)\n", x,y); + } + } + else + { + LocalDisparity->imgY[y][x] = 0; + } + } + } + return; +} + +/*! + ************************************************************************ + * \brief + * Conceal missing frame using a method already proposed + * Yibin Chen; Canhui Cai; Kai-Kuang Ma; , + * "Stereoscopic video error concealment for missing frame recovery using + * disparity-based frame difference projection," Image Processing (ICIP), + * 2009 16th IEEE International Conference on , vol., no., pp.4289-4292, 7-10 Nov. 2009 + ************************************************************************ + */ +void Conceal_by_DisparityBasedFrameDifferenceProjection (VideoParameters *p_Vid, StorablePicture *Source, StorablePicture *Dest) +{ + + //! Alloc a Slice associated with lost frame + Slice *currSlice = Dest->PicSlice; + DecodedPictureBuffer *p_Dpb = p_Vid->p_Dpb_layer[Dest->view_id]; + int idx, y, x, Temporal_Ref_idx[2] = {-1, -1}; + int y_block4x4, x_block4x4; + unsigned int idx_mb; + Macroblock *currMB; + int mb_width, mb_height; + //! Initialization of reference list + currSlice->p_Vid = p_Vid; + currSlice->p_Dpb = p_Vid->p_Dpb_layer[Dest->view_id]; + +#if MVC_EXTENSION_ENABLE + if( Dest->view_id != BASE_VIEW_IDX) + { + currSlice->ThisPOC = Dest->poc; + currSlice->framepoc = currSlice->ThisPOC; + currSlice->view_id = Dest->view_id; + currSlice->layer_id = Dest->layer_id; + + init_mvc_picture(currSlice); + } +#endif + + currSlice->ThisPOC = Source->poc; + currSlice->framepoc = Source->poc; + currSlice->view_id = Source->view_id; + currSlice->layer_id = Source->layer_id; + + currSlice->listXsize[LIST_0] = Source->Conc_listXsize[LIST_0]; + currSlice->listXsize[LIST_1] = Source->Conc_listXsize[LIST_1]; + for( idx=0; idxlistXsize[LIST_0]; idx++) + { + currSlice->listX[LIST_0][idx] = Source->Conc_listX[LIST_0][idx]; + if( Source->Conc_listXInterviewPos[LIST_0] == idx ) + { + currSlice->listX[LIST_0][idx] = p_Dpb->fs_ilref[0]->frame; + } + else if( Temporal_Ref_idx[LIST_0] == -1) + { + Temporal_Ref_idx[LIST_0] = idx; + } + } + + for( idx=0; idxlistXsize[LIST_1]; idx++) + { + currSlice->listX[LIST_1][idx] = Source->Conc_listX[LIST_1][idx]; + if( Source->Conc_listXInterviewPos[LIST_1] == idx ) + { + currSlice->listX[LIST_1][idx] = p_Dpb->fs_ilref[0]->frame; + } + else if( Temporal_Ref_idx[LIST_1] == -1) + { + Temporal_Ref_idx[LIST_1] = idx; + } + } + // End of intialization + + mb_width = Dest->PicWidthInMbs; + mb_height = (Dest->PicSizeInMbs)/(Dest->PicWidthInMbs); + + /* Loop though all motion vectors */ + for( y_block4x4=0; y_block4x4 < ((Dest->PicSizeInMbs)/(Dest->PicWidthInMbs)) * MB_BLOCK_SIZE/BLOCK_SIZE; y_block4x4++) + { + y = y_block4x4 * BLOCK_SIZE; //!< get y cordinates + for( x_block4x4=0; x_block4x4 < (Dest->PicWidthInMbs) * MB_BLOCK_SIZE/BLOCK_SIZE; x_block4x4++) + { + x = x_block4x4 * BLOCK_SIZE; + idx_mb = ( y / MB_BLOCK_SIZE ) * mb_width + ( x / MB_BLOCK_SIZE ); + currMB = &( p_Vid->mb_data[idx_mb] ); + + // Update coordinates of the current concealed macroblock + currMB->mb.x = (short) (x/BLOCK_SIZE); + currMB->mb.y = (short) (y/BLOCK_SIZE); + currMB->block_y = currMB->mb.y * BLOCK_SIZE; + currMB->pix_c_y = currMB->mb.y * p_Vid->mb_cr_size_y/4; + currMB->block_x = currMB->mb.x * BLOCK_SIZE; + currMB->pix_c_x = currMB->mb.x * p_Vid->mb_cr_size_x/4; + + // configure motion vector + Dest->mv_info[y_block4x4][x_block4x4].mv[LIST_0].mv_x = 0; + Dest->mv_info[y_block4x4][x_block4x4].mv[LIST_0].mv_y = 0; + Dest->mv_info[y_block4x4][x_block4x4].ref_idx[LIST_0] = Temporal_Ref_idx[LIST_0]; + + Dest->mv_info[y_block4x4][x_block4x4].mv[LIST_1].mv_x = 0; + Dest->mv_info[y_block4x4][x_block4x4].mv[LIST_1].mv_y = 0; + Dest->mv_info[y_block4x4][x_block4x4].ref_idx[LIST_1] = Temporal_Ref_idx[LIST_1]; + } + } + + fercCopyImg(p_Vid, Source, Dest); + + { + StorablePicture *Frame_Base_T, *Frame_Base_Tminus1 = NULL, *Frame_Aux_Tminus1 = NULL; + StorablePicture *ChangeDectectionMap_Base = NULL, *ChangeDectectionMap_Aux = NULL; + StorablePicture *Disparity = NULL; + + int ***TemporalChangeDetection_Base_I = NULL, ***TemporalChangeDetection_Base = NULL, ***TemporalChangeDetection_Aux = NULL; + int aux_d, i_max, i_min, y, x, GlobalDisparity; + double Average, Threshold, prevThreshold; + + int GlobalWindowSize = 20; + int LocalWindowSize = 20; + + int relatation_y_c = p_Vid->width / p_Vid->width_cr; + + ChangeDectectionMap_Base = alloc_storable_picture (p_Vid, FRAME, p_Vid->width, p_Vid->height, p_Vid->width_cr, p_Vid->height_cr, 1); + ChangeDectectionMap_Aux = alloc_storable_picture (p_Vid, FRAME, p_Vid->width, p_Vid->height, p_Vid->width_cr, p_Vid->height_cr, 1); + + + + //! Get pictures from the buffer + Frame_Base_T = fercGetPictureForConcealFromDpb(p_Vid->p_Dpb_layer[BASE_VIEW_IDX], Dest->poc, BASE_VIEW_IDX, EXACT, 0, NULL); + Frame_Aux_Tminus1 = fercGetPictureForConcealFromDpb(p_Vid->p_Dpb_layer[Dest->view_id], Dest->poc, Dest->view_id, CLOSEST_ALL, 0, NULL); + Frame_Base_Tminus1 = fercGetPictureForConcealFromDpb(p_Vid->p_Dpb_layer[BASE_VIEW_IDX], Frame_Aux_Tminus1->poc, BASE_VIEW_IDX, EXACT, 0, NULL); + + + + + + + get_mem3Dint( &TemporalChangeDetection_Base_I, 3, p_Vid->height, p_Vid->width ); + + //! Absolute temporal difference + image_diff( Frame_Base_T->imgY, Frame_Base_Tminus1->imgY, TemporalChangeDetection_Base_I[0], p_Vid->width, p_Vid->height, 1 ); + + get_mem3Dint( &TemporalChangeDetection_Base , 3, p_Vid->height, p_Vid->width ); + MeanFilterInt( TemporalChangeDetection_Base_I[0],TemporalChangeDetection_Base[0], p_Vid->width, p_Vid->height, 8 ); + + + //! Exploit a global threshold technique - Section II-A-1) + i_min = min_matrix( TemporalChangeDetection_Base[0], p_Vid->width, p_Vid->height ); + i_max = max_matrix( TemporalChangeDetection_Base[0], p_Vid->width, p_Vid->height ); + + Threshold = (double)( i_max + i_min ) / (double)(2); + + do{ + prevThreshold = Threshold; + Threshold = find_new_threshold( TemporalChangeDetection_Base[0], p_Vid->width, p_Vid->height, prevThreshold ); + }while( dabs( prevThreshold - Threshold ) > 1 ); + + //! Find the change detection map + for(y=0; yheight; y++) + { + for(x=0; xwidth; x++) + { + if( TemporalChangeDetection_Base[0][y][x] >= Threshold ) + ChangeDectectionMap_Base->imgY[y][x] = 255; + else + ChangeDectectionMap_Base->imgY[y][x] = 0; + } + } + + showStorablePicture(p_Vid,ChangeDectectionMap_Base,0,0,"Map Base-view",0); + //saveStorablePicture(p_Vid,"cdm_base.bmp",ChangeDectectionMap_Base,0); + + //! Disparity Estimation using eq. (3,4,5,6) + Disparity = alloc_storable_picture (p_Vid, FRAME, p_Vid->width, p_Vid->height, p_Vid->width_cr, p_Vid->height_cr, 1); + GlobalDisparity = get_Global_Disparity( Frame_Base_Tminus1, Frame_Aux_Tminus1, ChangeDectectionMap_Base, GlobalWindowSize ); + get_GlobalplusLocal_Disparity( Frame_Base_Tminus1, Frame_Aux_Tminus1, ChangeDectectionMap_Base, GlobalDisparity, GlobalWindowSize, LocalWindowSize, 8, Disparity); + + //! get the real Temporal change for base view + image_diff( Frame_Base_T->imgY, Frame_Base_Tminus1->imgY, TemporalChangeDetection_Base_I[0], p_Vid->width, p_Vid->height, 0 ); + MeanFilterInt(TemporalChangeDetection_Base_I[0], TemporalChangeDetection_Base[0], p_Vid->width, p_Vid->height, 8 ); + + //! Project the Change dectection from one view to another using eq. (8) + //! Project the Temporal change from one view to another using eq. (9) + get_mem3Dint( &TemporalChangeDetection_Aux, 3, p_Vid->height, p_Vid->width ); + for(y=0; yheight; y++) + { + for(x=0; xwidth; x++) + { + if( ChangeDectectionMap_Base->imgY[y][x] > 0 ) + { + aux_d = (int)(Disparity->imgY[y][x]) - (GlobalWindowSize + LocalWindowSize); + ChangeDectectionMap_Aux->imgY[y][x-aux_d] = ChangeDectectionMap_Base->imgY[y][x]; + TemporalChangeDetection_Aux[0][y][x-aux_d] = TemporalChangeDetection_Base[0][y][x]; + } + } + } + + showStorablePicture(p_Vid,ChangeDectectionMap_Aux,0,0,"Map Aux",0); + //saveStorablePicture(p_Vid,"cdm_aux.bmp",ChangeDectectionMap_Aux,1,0); + + + //for(y=0; yheight; y++) + //{ + // for(x=0; xwidth; x++) + // { + // aux_d = (int)(Disparity->imgY[y][x]) - (GlobalWindowSize + LocalWindowSize); + // if( ChangeDectectionMap_Aux->imgY[y][x-aux_d] > 0 ) + // { + // TemporalChangeDetection_Aux->imgY[y][x-aux_d] = TemporalChangeDetection_Base->imgY[y][x]; + // } + // } + //} + + //for(y=0; yheight_cr; y++) + //{ + // for(x=0; xwidth_cr; x++) + // { + // aux_d = (int)(Disparity->imgUV[0][y][x]) - (GlobalWindowSize - LocalWindowSize); + // aux_d = aux_d / relatation_y_c; + // if( ChangeDectectionMap_Aux->imgY[y][x] > 0 ) + // { + // TemporalChangeDetection_Aux->imgUV[0][y][x-aux_d] = TemporalChangeDetection_Base->imgUV[0][y][x]; + // TemporalChangeDetection_Aux->imgUV[1][y][x-aux_d] = TemporalChangeDetection_Base->imgUV[1][y][x]; + // } + // } + //} + + //! Reconstruct the missing frame + for(y=0; ysize_y; y++) + { + for(x=0; xsize_x; x++) + { + Dest->imgY[y][x] = iClip3( 0, 255, (int)Frame_Aux_Tminus1->imgY[y][x] + TemporalChangeDetection_Aux[0][y][x] ); + } + } + //for(y=0; ysize_y_cr; y++) + //{ + // for(x=0; xsize_x_cr; x++) + // { + // Dest->imgUV[0][y][x] = Frame_Aux_Tminus1->imgUV[0][y][x] + TemporalChangeDetection_Aux->imgUV[0][y][x]; + // Dest->imgUV[1][y][x] = Frame_Aux_Tminus1->imgUV[1][y][x] + TemporalChangeDetection_Aux->imgUV[1][y][x]; + // } + //} + + + showStorablePicture(p_Vid,Dest,0,0,"Destination",0); + + free_storable_picture( Disparity ); + + free_storable_picture( ChangeDectectionMap_Base ); + free_storable_picture( ChangeDectectionMap_Aux ); + + free_mem3Dint( TemporalChangeDetection_Base ); + free_mem3Dint( TemporalChangeDetection_Aux ); + } +} + +//image_diff( Frame_Base_T->imgUV[0], Frame_Base_Tminus1->imgUV[0], TemporalChangeDetection_Base->imgUV[0], p_Vid->width_cr, p_Vid->height_cr ); +//image_diff( Frame_Base_T->imgUV[1], Frame_Base_Tminus1->imgUV[1], TemporalChangeDetection_Base->imgUV[1], p_Vid->width_cr, p_Vid->height_cr ); + +//{ +// char FileName[FILE_NAME_SIZE], chBuf[FILE_NAME_SIZE], *pch; +// strcpy( chBuf , p_Vid->p_Inp->outfile ); +// pch = strrchr( chBuf , '.' ); +// if(pch) +// *pch = '\0'; +// if( strcmp("nul", chBuf) ) +// { +// sprintf( FileName , "cdm.bmp" ); +// saveStorablePicture( p_Vid , FileName , ChangeDectectionMap_Base , 0 ); +// } +//} + +//{ +// char FileName[FILE_NAME_SIZE], chBuf[FILE_NAME_SIZE], *pch; +// strcpy( chBuf , p_Vid->p_Inp->outfile ); +// pch = strrchr( chBuf , '.' ); +// if(pch) +// *pch = '\0'; +// if( strcmp("nul", chBuf) ) +// { +// sprintf( FileName , "disp.bmp" ); +// saveStorablePicture( p_Vid , FileName , Disparity , 0 ); +// } +//} diff --git a/ldecod/src/ferc/ferc_internals.c b/ldecod/src/ferc/ferc_internals.c new file mode 100644 index 0000000..170dc5c --- /dev/null +++ b/ldecod/src/ferc/ferc_internals.c @@ -0,0 +1,946 @@ +/*! + ************************************************************************ + * \file ferc_internals.c + * + * \brief + * Internal function for ferc API + * + * \author + * - Jo���o Carreira + * + ************************************************************************ + */ + +#include "ferc_internals.h" +#include "ferc_api.h" +#include "global.h" +#include "mbuffer.h" +#include "mbuffer_mvc.h" +#include "memalloc.h" +#include "image.h" +#include "mc_prediction.h" +#include "macroblock.h" +#include "block.h" +#include "mbuffer_common.h" + +/*! + ************************************************************************ + * \brief + * Sortmotion vector abs up + ************************************************************************ + */ +int sort_mv_abs_up( const void *arg1, const void *arg2 ) +{ + struct partial_mv v_0 = *( ( struct partial_mv * )arg1 ); + struct partial_mv v_1 = *( ( struct partial_mv * )arg2 ); + double avg_v0 = abs_dvec( v_0.mv.mv_x, v_0.mv.mv_y ); + double avg_v1 = abs_dvec( v_1.mv.mv_x, v_1.mv.mv_y ); + + if( avg_v0 > avg_v1 ) + return 1; + else if( avg_v0 < avg_v1 ) + return -1; + else + return 0; +} + +/*! + ************************************************************************ + * \brief + * Sort Dpb by frame_num + * Edit by Joo Carreira + ************************************************************************ + */ +int sort_by_frame_num_up( const void *arg1, const void *arg2 ) +{ + FrameStore *f_0 = *( ( FrameStore ** )arg1 ); + FrameStore *f_1 = *( ( FrameStore ** )arg2 ); + int comp = f_0->frame_num - f_1->frame_num; + if( !comp ) + comp = f_0->poc - f_1->poc; + if( !comp ) + comp = f_0->view_id - f_1->view_id; + return comp; +} + +/*! + ************************************************************************ + * \brief + * Sort Dpb by poc + * Edit by Joao Carreira + ************************************************************************ + */ +int sort_by_poc_up( const void *arg1, const void *arg2 ) +{ + FrameStore *f_0 = *( ( FrameStore ** )arg1 ); + FrameStore *f_1 = *( ( FrameStore ** )arg2 ); + return ( ( int )( ( f_0->poc + f_0->view_id ) - ( f_1->poc + f_1->view_id ) ) ); +} + +/*! + ************************************************************************ + * \brief + * Conceal picture using frame copy method + * Copy image data from one array to another array + ************************************************************************ + */ +void Paint_Withe_Frame( VideoParameters *p_Vid, StorablePicture *Dest ) +{ + int x, y; + //! Y + for( y = 0; y < p_Vid->height; y++ ) + for( x = 0; x < p_Vid->width; x++ ) + Dest->imgY[y][x] = 0; + //!UV + for( y = 0; y < p_Vid->height_cr; y++ ) + for( x = 0; x < p_Vid->width_cr; x++ ) + { + Dest->imgUV[0][y][x] = 0; + Dest->imgUV[1][y][x] = 0; + } +} + +static int sort_imgpel_up( const void *arg1, const void *arg2 ) +{ + int int_arg1 = *( ( imgpel * )( arg1 ) ); + int int_arg2 = *( ( imgpel* )( arg2 ) ); + return int_arg1 - int_arg2; +} + +/*! + ************************************************************************ + * \brief + * Apply a mean filter + ************************************************************************ + */ +void MeanFilter( imgpel **initial_matrix, imgpel **filtrated_matrix, int size_x, int size_y, int kernel_size ) +{ + int x, y, xx, yy, x_start, x_end, y_start, y_end; + double sum; + int count; + + for( x = 0; x < size_x; x++ ) + { + for( y = 0; y < size_y; y++ ) + { + x_start = imax( 0, x - kernel_size / 2 ); + x_end = imin( size_x, x + kernel_size / 2 ); + y_start = imax( 0, y - kernel_size / 2 ); + y_end = imin( size_y, y + kernel_size / 2 ); + + for( xx = x_start, sum = 0, count = 0; xx <= x_end; xx++ ) + { + for( yy = y_start; yy <= y_end; yy++ ) + { + sum += initial_matrix[yy][xx]; + count++; + } + } + filtrated_matrix[y][x] = ( imgpel )( sum / ( double )( count ) ); + } + } + return; +} + +/*! + ************************************************************************ + * \brief + * Apply a median filter + ************************************************************************ + */ +void MedianFilter( imgpel **initial_matrix, imgpel **filtrated_matrix, int size_x, int size_y, int kernel_size ) +{ + imgpel *img_pel = ( imgpel * )malloc( ( kernel_size + 1 ) * ( kernel_size + 1 ) * sizeof(imgpel) ); + int x, y, xx, yy, x_start, x_end, y_start, y_end, elem; + + for( x = 0; x < size_x; x++ ) + { + for( y = 0; y < size_y; y++ ) + { + x_start = imax( 0, x - kernel_size / 2 ); + x_end = imin( size_x - 1, x + kernel_size / 2 ); + y_start = imax( 0, y - kernel_size / 2 ); + y_end = imin( size_y - 1, y + kernel_size / 2 ); + + for( xx = x_start, elem = 0; xx <= x_end; xx++ ) + { + for( yy = y_start; yy <= y_end; yy++ ) + { + img_pel[elem] = initial_matrix[yy][xx]; + elem++; + } + } + qsort( img_pel, elem, sizeof(imgpel), sort_imgpel_up ); + if( elem % 2 ) + filtrated_matrix[y][x] = ( img_pel[elem >> 1] + img_pel[( elem >> 1 ) + 1] + 1 ) >> 1; + else + filtrated_matrix[y][x] = img_pel[elem >> 1]; + } + } + free( img_pel ); + return; +} + + + +/*! + ************************************************************************ + * \brief + * Function to search in decoded picture buffer for a previous decoded frame + * based of input parameters: poc, view_id and search_mode + * + ************************************************************************ + */ +StorablePicture *fercGetPictureForConcealFromDpb( DecodedPictureBuffer *p_Dpb, int poc, int search_view_id, int search_mode, int search_concealed, + unsigned int *position ) +{ + VideoParameters *p_Vid = p_Dpb->p_Vid; + int BestScore, idxBestScore; + int i; + int j; + + switch( search_mode ) + { + case CLOSEST_ALL: + //Uses the closest frame to conceal (inc concealed frames) + BestScore = 99; + idxBestScore = 0; + for( i = 0; i < p_Dpb->used_size; i++ ) + { + if( p_Dpb->fs[i]->is_used == 3 && p_Dpb->fs[i]->unused_concealment == 0 ) + { + if( p_Dpb->fs[i]->view_id == search_view_id && ( !p_Dpb->fs[i]->frame->concealed_pic || search_concealed ) + && ( p_Dpb->fs[i]->frame->idr_flag != 1 || p_Vid->conceal_mode <= FRAME_COPY_BC ) //|!< Protection for using IDR picture as reference for motion copy + && ( p_Dpb->fs[i]->frame->frame_num != 0 || p_Vid->conceal_mode < 2 ) ) //!< Protection for using first P picture in auxiliary view for motion copy + { + if( abs( p_Dpb->fs[i]->frame->poc - poc ) < BestScore ) + { + BestScore = abs( p_Dpb->fs[i]->frame->poc - poc ); + idxBestScore = i; + } + } + } + } + if( position != NULL ) + *position = i; + return p_Dpb->fs[idxBestScore]->frame; + break; + case CLOSEST_FUTURE: + //Uses the closest future frame to conceal (inc concealed frames) + BestScore = 99; + idxBestScore = 0; + for( i = 0; i < p_Dpb->used_size; i++ ) + { + if( p_Dpb->fs[i]->is_used == 3 && p_Dpb->fs[i]->unused_concealment == 0 ) + { + if( p_Dpb->fs[i]->view_id == search_view_id && ( !p_Dpb->fs[i]->frame->concealed_pic || search_concealed ) + && ( p_Dpb->fs[i]->frame->idr_flag != 1 || p_Vid->conceal_mode <= FRAME_COPY_BC ) //|!< Protection for using IDR picture as reference for motion copy + && ( p_Dpb->fs[i]->frame->frame_num != 0 || p_Vid->conceal_mode <= FRAME_COPY_BC ) ) //!< Protection for using first P picture in auxiliary view for motion copy + { + if( abs( p_Dpb->fs[i]->frame->poc - poc ) <= BestScore ) + { + BestScore = abs( p_Dpb->fs[i]->frame->poc - poc ); + idxBestScore = i; + } + } + } + } + if( position != NULL ) + *position = i; + return p_Dpb->fs[idxBestScore]->frame; + break; + case WELL_RECEIVED_STEREO: + /* + * Return the last frame decoded of view = view_id, corresponding to + * corresponding to a well received stereo pair + */ + BestScore = 99; + idxBestScore = 0; + for( i = 0; i < p_Dpb->used_size; i++ ) + { + if( p_Dpb->fs[i]->is_used == 3 && p_Dpb->fs[i]->unused_concealment == 0 ) + { + if( p_Dpb->fs[i]->is_used == 3 && p_Dpb->fs[i]->view_id == search_view_id && !p_Dpb->fs[i]->frame->concealed_pic + && ( p_Dpb->fs[i]->frame->idr_flag != 1 || p_Vid->conceal_mode <= FRAME_COPY_BC ) ) //|!< Protection for using IDR picture as reference for motion copy + { + for( j = ( p_Dpb->used_size - 1 ); j >= 0; j-- ) + { + if( p_Dpb->fs[j]->frame->poc == p_Dpb->fs[i]->frame->poc && p_Dpb->fs[j]->frame->view_id == ( ( p_Dpb->fs[i]->frame->view_id + 1 ) & 0x01 ) ) + { + if( abs( p_Dpb->fs[i]->frame->poc - poc ) < BestScore ) + { + BestScore = abs( p_Dpb->fs[i]->frame->poc - poc ); + idxBestScore = i; + } + + } + } + } + } + } + if( position != NULL ) + *position = idxBestScore; + return p_Dpb->fs[idxBestScore]->frame; + break; + case EXACT: + /* Return the exact picture or NULL */ + for( i = ( p_Dpb->used_size - 1 ); i >= 0; i-- ) + { + if( p_Dpb->fs[i]->is_used == 3 && p_Dpb->fs[i]->unused_concealment == 0 ) + { + if( p_Dpb->fs[i]->view_id == search_view_id && p_Dpb->fs[i]->frame->poc == poc ) + { + if( position != NULL ) + *position = i; + return p_Dpb->fs[i]->frame; + } + } + } + break; + default: + return NULL ; + } + return NULL ; +} + +/*! + ************************************************************************ + * \brief + * Copy a block to an image + * + ************************************************************************ + */ +void copyPredblockRegionYUVDestination( StorablePicture *Destination, imgpel *predBlock, int x, int y, int BlockSize ) +{ + int ii, jj; + + //! Y Matrix + for( ii = 0; ii < BLOCK_SIZE; ii++ ) + for( jj = 0; jj < BLOCK_SIZE; jj++ ) + Destination->imgY[y + ii][x + jj] = predBlock[ii * ( BlockSize ) + jj]; + + predBlock = predBlock + ( BlockSize * BlockSize ); + + //! UV Matrix + if( Destination->chroma_format_idc != YUV400 ) + { + int uv; + for( uv = 0; uv < 2; uv++ ) + { + for( ii = 0; ii < ( BlockSize / 2 ); ii++ ) + for( jj = 0; jj < ( BlockSize / 2 ); jj++ ) + Destination->imgUV[uv][y / 2 + ii][x / 2 + jj] = predBlock[ii * ( BlockSize / 2 ) + jj]; + predBlock = predBlock + ( BlockSize * BlockSize / 4 ); + } + } +} + +/*! + ************************************************************************ + * \brief + * block bi-prediction + ************************************************************************ + */ +void ferc_bi_prediction( imgpel *mb_pred, imgpel *block_l0, uint8 value_l0, imgpel *block_l1, uint8 value_l1, int ver_block_size, int hor_block_size, + int have_uv ) +{ + int ii; + for( ii = 0; ii < ( int )( hor_block_size * hor_block_size * ( 1 + 0.5 * have_uv ) ); ii++ ) + { + // *(mb_pred++) = (imgpel)((( *(block_l0++) + *(block_l1++) ) + 1) >> 1); + // "+1" garante que nunca se desloca o valor zero -> + *( mb_pred++ ) = ( imgpel )( ( value_l1 * ( *( block_l0++ ) ) + value_l0 * ( *( block_l1++ ) ) + 1 ) / ( value_l0 + value_l1 ) ); + } + return; +} + +/*! + ************************************************************************ + * \brief + * The motion prediction pixels are calculated from the given location (in + * 1/4 pixel units) of the referenced frame. It copies the sub block from the + * corresponding reference to the frame to be concealed. + * + ************************************************************************* + */ +void EnhancedbuildPredblock4x4YUV( VideoParameters *p_Vid, int *mv, int x, int y, imgpel *predMB, int list, int current_mb_nr, imgpel **tmp_block ) +{ + int i = 0, j = 0, ii = 0, jj = 0, i1 = 0, j1 = 0, j4 = 0, i4 = 0; + int uv; + int vec1_x = 0, vec1_y = 0; + int ioff, joff; + int have_tmp; + StorablePicture *dec_picture = p_Vid->dec_picture; + imgpel *pMB = predMB; + + int ii0, jj0, ii1, jj1, if1, jf1, if0, jf0; + int mv_mul; + + //FRExt + int f1_x, f1_y, f2_x, f2_y, f3, f4; + int yuv = dec_picture->chroma_format_idc - 1; + + int ref_frame = mv[2]; + int mb_nr = current_mb_nr; + + Macroblock *currMB = &p_Vid->mb_data[mb_nr]; // intialization code deleted, see below, StW + Slice *currSlice = currMB->p_Slice; + + StorablePicture *ref_picture = currSlice->listX[list][ref_frame]; + + have_tmp = tmp_block ? 1 : 0; + //have_tmp = 0; + + if( !have_tmp ) + get_mem2Dpel( &tmp_block, MB_BLOCK_SIZE, MB_BLOCK_SIZE ); + + /* Update coordinates of the current concealed macroblock */ + + currMB->mb.x = ( short )( x / BLOCK_SIZE ); + currMB->mb.y = ( short )( y / BLOCK_SIZE ); + currMB->block_y = currMB->mb.y * BLOCK_SIZE; + currMB->pix_c_y = currMB->mb.y * p_Vid->mb_cr_size_y / 4; + currMB->block_x = currMB->mb.x * BLOCK_SIZE; + currMB->pix_c_x = currMB->mb.x * p_Vid->mb_cr_size_x / 4; + + mv_mul = 4; + + //! redundant information. Why cur_imgY if both point to the same memory block + ref_picture->cur_imgY = ref_picture->imgY; + + // luma ******************************************************* + + vec1_x = x * mv_mul + mv[0]; + vec1_y = y * mv_mul + mv[1]; + get_block_luma( ref_picture, vec1_x, vec1_y, BLOCK_SIZE, BLOCK_SIZE, tmp_block, dec_picture->iLumaStride, dec_picture->size_x_m1, + ( currMB->mb_field ) ? ( dec_picture->size_y >> 1 ) - 1 : dec_picture->size_y_m1, currSlice->tmp_res, p_Vid->max_pel_value_comp[PLANE_Y], + ( imgpel )p_Vid->dc_pred_value_comp[PLANE_Y], currMB ); + + for( jj = 0; jj < MB_BLOCK_SIZE / BLOCK_SIZE; jj++ ) + for( ii = 0; ii < BLOCK_SIZE; ii++ ) + currSlice->mb_pred[LumaComp][jj][ii] = tmp_block[jj][ii]; + + for( j = 0; j < 4; j++ ) + { + for( i = 0; i < 4; i++ ) + { + pMB[j * 4 + i] = currSlice->mb_pred[LumaComp][j][i]; + } + } + pMB += 16; + + if( dec_picture->chroma_format_idc != YUV400 ) + { + // chroma ******************************************************* + f1_x = 64 / ( p_Vid->mb_cr_size_x ); + f2_x = f1_x - 1; + + f1_y = 64 / ( p_Vid->mb_cr_size_y ); + f2_y = f1_y - 1; + + f3 = f1_x * f1_y; + f4 = f3 >> 1; + + for( uv = 0; uv < 2; uv++ ) + { + joff = subblk_offset_y[yuv][0][0]; + j4 = currMB->pix_c_y + joff; + ioff = subblk_offset_x[yuv][0][0]; + i4 = currMB->pix_c_x + ioff; + + for( jj = 0; jj < 2; jj++ ) + { + for( ii = 0; ii < 2; ii++ ) + { + i1 = ( i4 + ii ) * f1_x + mv[0]; + j1 = ( j4 + jj ) * f1_y + mv[1]; + + ii0 = iClip3( 0, dec_picture->size_x_cr - 1, i1 / f1_x ); + jj0 = iClip3( 0, dec_picture->size_y_cr - 1, j1 / f1_y ); + ii1 = iClip3( 0, dec_picture->size_x_cr - 1, ( ( i1 + f2_x ) / f1_x ) ); + jj1 = iClip3( 0, dec_picture->size_y_cr - 1, ( ( j1 + f2_y ) / f1_y ) ); + + if1 = ( i1 & f2_x ); + jf1 = ( j1 & f2_y ); + if0 = f1_x - if1; + jf0 = f1_y - jf1; + + currSlice->mb_pred[uv + 1][jj][ii] = ( imgpel )( ( if0 * jf0 * currSlice->listX[list][ref_frame]->imgUV[uv][jj0][ii0] + + if1 * jf0 * currSlice->listX[list][ref_frame]->imgUV[uv][jj0][ii1] + if0 * jf1 * currSlice->listX[list][ref_frame]->imgUV[uv][jj1][ii0] + + if1 * jf1 * currSlice->listX[list][ref_frame]->imgUV[uv][jj1][ii1] + f4 ) / f3 ); + } + } + + for( j = 0; j < 2; j++ ) + { + for( i = 0; i < 2; i++ ) + { + pMB[j * 2 + i] = currSlice->mb_pred[uv + 1][j][i]; + } + } + pMB += 4; + + } + } + if( !have_tmp ) + free_mem2Dpel( tmp_block ); +} +void EnhancedbuildPredblockRegionYUV( VideoParameters *p_Vid, int *mv, int x, int y, imgpel *predMB, int list, int current_mb_nr, int BlockSize, + imgpel **tmp_block ) +{ + int i, j; + + for( i = 0; i < BlockSize / BLOCK_SIZE; i++ ) + { + for( j = 0; j < BlockSize / BLOCK_SIZE; j++ ) + { + EnhancedbuildPredblock4x4YUV( p_Vid, mv, x, y, &( predMB[i * BlockSize + j * BLOCK_SIZE] ), LIST_0, current_mb_nr, tmp_block ); + } + } +} + +/*! + ************************************************************************ + * \brief + * The motion prediction pixels are calculated from the given location (in + * 1/4 pixel units) of the referenced frame. It copies the sub block from the + * corresponding reference to the frame to be concealed. + * + ************************************************************************* + */ +void buildPredblockRegionYUV( VideoParameters *p_Vid, StorablePicture *ref_picture, Macroblock *currMB, int *mv, int x, int y, imgpel *pMB, int Block_Size, + imgpel **tmp_block, int have_temp ) +{ + int i = 0, j = 0, ii = 0, jj = 0, i1 = 0, j1 = 0, j4 = 0, i4 = 0; + int uv; + int vec1_x = 0, vec1_y = 0; + int ioff, joff; + + int ii0, jj0, ii1, jj1, if1, jf1, if0, jf0, ifx, jf; + + // Interpolation of 1/4 subpixel + int mv_mul = 4; + + //FRExt + int f1_x, f1_y, f2_x, f2_y, f3, f4; + int yuv = ref_picture->chroma_format_idc - 1; + + // int mb_nr = current_mb_nr; + // Macroblock *currMB = &ref_picture->MB_pic_data[current_mb_nr]; + //Macroblock *currMB = &p_Vid->mb_data[current_mb_nr]; // intialization code deleted, see below, StW + Slice *currSlice = currMB->p_Slice; + + // alloc memory for temp blocks + if( !have_temp ) + get_mem2Dpel( &tmp_block, MB_BLOCK_SIZE, MB_BLOCK_SIZE ); + + // luma ******************************************************* + + vec1_x = x * mv_mul + mv[0]; + vec1_y = y * mv_mul + mv[1]; + + //! redundant information. Why cur_imgY if both point to the same memory block + ref_picture->cur_imgY = ref_picture->imgY; + + //void get_block_luma(StorablePicture *curr_ref, int x_pos, int y_pos, int block_size_x, int block_size_y, imgpel **block, + //int shift_x, int maxold_x, + //int maxold_y, int **tmp_res, + //int max_imgpel_value, imgpel no_ref_value, Macroblock *currMB) + get_block_luma( ref_picture, vec1_x, vec1_y, Block_Size, Block_Size, tmp_block, ref_picture->iLumaStride, ref_picture->size_x_m1 - 1, + ( currMB->mb_field ) ? ( ref_picture->size_y >> 1 ) - 1 : ref_picture->size_y_m1, currSlice->tmp_res, p_Vid->max_pel_value_comp[PLANE_Y], + ( imgpel )p_Vid->dc_pred_value_comp[PLANE_Y], currMB ); + + for( jj = 0; jj < MB_BLOCK_SIZE / Block_Size; jj++ ) + for( ii = 0; ii < Block_Size; ii++ ) + currSlice->mb_pred[LumaComp][jj][ii] = tmp_block[jj][ii]; + + for( j = 0; j < 4; j++ ) + { + for( i = 0; i < 4; i++ ) + { + pMB[j * 4 + i] = currSlice->mb_pred[LumaComp][j][i]; + } + } + pMB += 16; + + if( ref_picture->chroma_format_idc != YUV400 ) + { + // chroma ******************************************************* + f1_x = 64 / ( p_Vid->mb_cr_size_x ); + f2_x = f1_x - 1; + + f1_y = 64 / ( p_Vid->mb_cr_size_y ); + f2_y = f1_y - 1; + + f3 = f1_x * f1_y; + f4 = f3 >> 1; + + for( uv = 0; uv < 2; uv++ ) + { + joff = subblk_offset_y[yuv][0][0]; + j4 = currMB->pix_c_y + joff; + ioff = subblk_offset_x[yuv][0][0]; + i4 = currMB->pix_c_x + ioff; + + for( jj = 0; jj < 2; jj++ ) + { + jf = ( j4 + jj ) / ( p_Vid->mb_cr_size_y / Block_Size ); // jf = Subblock_y-coordinate + for( ii = 0; ii < 2; ii++ ) + { + ifx = ( i4 + ii ) / ( p_Vid->mb_cr_size_x / Block_Size ); // ifx = Subblock_x-coordinate + + i1 = ( i4 + ii ) * f1_x + mv[0]; + j1 = ( j4 + jj ) * f1_y + mv[1]; + + ii0 = iClip3( 0, ref_picture->size_x_cr - 1, i1 / f1_x ); + jj0 = iClip3( 0, ref_picture->size_y_cr - 1, j1 / f1_y ); + ii1 = iClip3( 0, ref_picture->size_x_cr - 1, ( ( i1 + f2_x ) / f1_x ) ); + jj1 = iClip3( 0, ref_picture->size_y_cr - 1, ( ( j1 + f2_y ) / f1_y ) ); + + if1 = ( i1 & f2_x ); + jf1 = ( j1 & f2_y ); + if0 = f1_x - if1; + jf0 = f1_y - jf1; + + currSlice->mb_pred[uv + 1][jj][ii] = ( imgpel )( ( if0 * jf0 * ref_picture->imgUV[uv][jj0][ii0] + if1 * jf0 * ref_picture->imgUV[uv][jj0][ii1] + + if0 * jf1 * ref_picture->imgUV[uv][jj1][ii0] + if1 * jf1 * ref_picture->imgUV[uv][jj1][ii1] + f4 ) / f3 ); + } + } + for( j = 0; j < 2; j++ ) + { + for( i = 0; i < 2; i++ ) + { + pMB[j * 2 + i] = currSlice->mb_pred[uv + 1][j][i]; + } + } + pMB += 4; + } + } + if( !have_temp ) + free_mem2Dpel( tmp_block ); +} + +/*! + ************************************************************************ + * \brief + * Predict the bad motion vector with previous know information of + * concealment reference frame + * + ************************************************************************ + */ +void ConcealMotionVector( VideoParameters *p_Vid, StorablePicture *Dest, Slice *Slice, int y_block4x4, int x_block4x4, int method, int list, int conc_mv[3], + int temporal_idx ) +{ + static int MV_PRED_POS[][2] = + {/*zona 1*/ + { 0, -1 }, + { -1, -1 }, + { -1, 0 }, + { -1, 1 },/*zona 2*/ + { 0, -2 }, + { -1, -2 }, + { -2, -2 }, + { -2, -1 }, + { -2, 0 }, + { -2, 1 } }; + //static int MV_PRED_POS[][2]={{-1,0},{0,-1},{1,-1}}; +#define N_MV_PRED_POS 4 + + int i, j; + int use_Disp = 0; + double sum_x = 0, sum_y = 0, scale; + int size_x = Dest->size_x, size_y = Dest->size_y; + int ref_idx, ref_idx_disp = 99, ref_idx_motion = 99, ref_poc; + struct partial_mv par_mv[N_MV_PRED_POS]; + + for( i = 0, j = 0; i < N_MV_PRED_POS; i++ ) + { + if( y_block4x4 + MV_PRED_POS[i][0] >= 0 && x_block4x4 + MV_PRED_POS[i][1] >= 0 && y_block4x4 + MV_PRED_POS[i][0] < size_y / 4 + && x_block4x4 + MV_PRED_POS[i][1] < size_x / 4 ) + { + ref_idx = Dest->mv_info[y_block4x4 + MV_PRED_POS[i][0]][x_block4x4 + MV_PRED_POS[i][1]].ref_idx[list]; + if( ref_idx < 0 ) + continue; + ref_poc = Slice->listX[list][ref_idx]->poc; + if( ref_poc == Dest->poc ) + { + ref_idx_disp = ref_idx_disp > ref_idx ? ref_idx : ref_idx_disp; + use_Disp++; + } + else + { + ref_idx_motion = ref_idx_motion > ref_idx ? ref_idx : ref_idx_motion; + use_Disp--; + } + } + } + use_Disp = use_Disp > 0 ? 1 : 0; + + switch( method ) + { + case PREVIOUS: + if( x_block4x4 ) + { + x_block4x4--; + } + else if( y_block4x4 ) + { + y_block4x4--; + } + else + { //impossible to copy previous vector -> use ZERO_MV + method = ZERO_MV; + break; + } + conc_mv[0] = Dest->mv_info[y_block4x4][x_block4x4].mv[list].mv_x; + conc_mv[1] = Dest->mv_info[y_block4x4][x_block4x4].mv[list].mv_y; + conc_mv[2] = Dest->mv_info[y_block4x4][x_block4x4].ref_idx[list]; + return; + case AVERAGE: + for( i = 0, j = 0; i < N_MV_PRED_POS; i++ ) + { + if( y_block4x4 + MV_PRED_POS[i][0] >= 0 && x_block4x4 + MV_PRED_POS[i][1] >= 0 && y_block4x4 + MV_PRED_POS[i][0] < size_y / 4 + && x_block4x4 + MV_PRED_POS[i][1] < size_x / 4 ) + { + ref_idx = Dest->mv_info[y_block4x4 + MV_PRED_POS[i][0]][x_block4x4 + MV_PRED_POS[i][1]].ref_idx[list]; + if( ref_idx < 0 ) + continue; + ref_poc = Slice->listX[LIST_0][ref_idx]->poc; + + if( ( use_Disp && Dest->poc != ref_poc ) || ( !use_Disp && Dest->poc == ref_poc ) ) + { + continue; + } + scale = Dest->poc - ref_poc; + scale = scale ? scale : 1; + + sum_x += ( ( double )Dest->mv_info[y_block4x4 + MV_PRED_POS[i][0]][x_block4x4 + MV_PRED_POS[i][1]].mv[list].mv_x ) / ( ( double )scale ); + sum_y += ( ( double )Dest->mv_info[y_block4x4 + MV_PRED_POS[i][0]][x_block4x4 + MV_PRED_POS[i][1]].mv[list].mv_y ) / ( ( double )scale ); + j++; + } + } + if( j == 0 ) + { + //impossible to use this method -> use ZERO_MV + method = ZERO_MV; + break; + } + if( use_Disp ) + ref_idx = ref_idx_disp; + else + ref_idx = ref_idx_motion; + + ref_poc = Slice->listX[list][ref_idx]->poc; + scale = Dest->poc - ref_poc; + scale = scale ? scale : 1; + conc_mv[0] = ( int )( ( sum_x / ( ( double )j ) ) * ( scale ) ); + conc_mv[1] = ( int )( ( sum_y / ( ( double )j ) ) * ( scale ) ); + conc_mv[2] = ref_idx; + + return; + case MEDIAN: + for( i = 0, j = 0; i < N_MV_PRED_POS; i++ ) + { + if( y_block4x4 + MV_PRED_POS[i][0] >= 0 && x_block4x4 + MV_PRED_POS[i][1] >= 0 && y_block4x4 + MV_PRED_POS[i][0] < size_y / 4 + && x_block4x4 + MV_PRED_POS[i][1] < size_x / 4 ) + { + ref_idx = Dest->mv_info[y_block4x4 + MV_PRED_POS[i][0]][x_block4x4 + MV_PRED_POS[i][1]].ref_idx[list]; + if( ref_idx < 0 ) + continue; + ref_poc = Slice->listX[LIST_0][ref_idx]->poc; + + if( ( use_Disp && Dest->poc != ref_poc ) || ( !use_Disp && Dest->poc == ref_poc ) ) + { + continue; + } + scale = Dest->poc - ref_poc; + scale = scale ? scale : 1; + par_mv[j].mv.mv_x = Dest->mv_info[y_block4x4 + MV_PRED_POS[i][0]][x_block4x4 + MV_PRED_POS[i][1]].mv[list].mv_x / scale; + par_mv[j].mv.mv_y = Dest->mv_info[y_block4x4 + MV_PRED_POS[i][0]][x_block4x4 + MV_PRED_POS[i][1]].mv[list].mv_y / scale; + + par_mv[j].ref_idx = Dest->mv_info[y_block4x4 + MV_PRED_POS[i][0]][x_block4x4 + MV_PRED_POS[i][1]].ref_idx[list]; + j++; + } + } + if( j == 0 ) + { + //impossible to use this method -> use ZERO_MV + method = ZERO_MV; + break; + } + if( use_Disp ) + ref_idx = ref_idx_disp; + else + ref_idx = ref_idx_motion; + ref_poc = Slice->listX[list][ref_idx]->poc; + scale = Dest->poc - ref_poc; + scale = scale ? scale : 1; + + qsort( par_mv, j, sizeof(struct partial_mv), sort_mv_abs_up ); + if( j % 2 == 0 ) + { + conc_mv[0] = ( int )( ( ( par_mv[j >> 1].mv.mv_x + par_mv[( j >> 1 ) - 1].mv.mv_x ) / 2 ) * scale ); + conc_mv[1] = ( int )( ( ( par_mv[j >> 1].mv.mv_y + par_mv[( j >> 1 ) - 1].mv.mv_y ) / 2 ) * scale ); + } + else + { + conc_mv[0] = ( int )( par_mv[j >> 1].mv.mv_x * scale ); + conc_mv[1] = ( int )( par_mv[j >> 1].mv.mv_y * scale ); + } + conc_mv[2] = ref_idx; + return; + } + + if( method == ZERO_MV ) + { // direct copy the colocated block of first ref frame of list + conc_mv[0] = 0; + conc_mv[1] = 0; + conc_mv[2] = temporal_idx; + return; + } +} + +/*! + ************************************************************************ + * \brief + * Conceal picture using frame copy method + * Copy image data from one array to another array + ************************************************************************ + */ +void Conceal_by_FrameCopy( VideoParameters *p_Vid, StorablePicture *Source, StorablePicture *Dest ) +{ + + //! Alloc a Slice associated with lost frame + Slice *currSlice = Dest->PicSlice; + DecodedPictureBuffer *p_Dpb = p_Vid->p_Dpb_layer[Dest->view_id]; + int idx, y, x, Temporal_Ref_idx[2] = + { -1, -1 }; + int y_block4x4, x_block4x4; + unsigned int idx_mb; + Macroblock *currMB; + int mb_width; + + //! Initialization of reference list + currSlice->p_Vid = p_Vid; + currSlice->p_Dpb = p_Vid->p_Dpb_layer[Dest->view_id]; + +#if MVC_EXTENSION_ENABLE + if( Dest->view_id > BASE_VIEW_IDX ) + { + currSlice->ThisPOC = Dest->poc; + currSlice->framepoc = currSlice->ThisPOC; + currSlice->view_id = Dest->view_id; + currSlice->layer_id = Dest->layer_id; + + init_mvc_picture( currSlice ); + } +#endif + + currSlice->ThisPOC = Source->poc; + currSlice->framepoc = Source->poc; + currSlice->view_id = Source->view_id; + currSlice->layer_id = Source->layer_id; + + currSlice->listXsize[LIST_0] = Source->Conc_listXsize[LIST_0]; + currSlice->listXsize[LIST_1] = Source->Conc_listXsize[LIST_1]; + if( Dest->view_id > BASE_VIEW_IDX ) + { + for( idx = 0; idx < currSlice->listXsize[LIST_0]; idx++ ) + { + currSlice->listX[LIST_0][idx] = Source->Conc_listX[LIST_0][idx]; + if( Source->Conc_listXInterviewPos[LIST_0] == idx ) + { + currSlice->listX[LIST_0][idx] = p_Dpb->fs_ilref[0]->frame; + } + else if( Temporal_Ref_idx[LIST_0] == -1 ) + { + Temporal_Ref_idx[LIST_0] = idx; + } + } + for( idx = 0; idx < currSlice->listXsize[LIST_1]; idx++ ) + { + currSlice->listX[LIST_1][idx] = Source->Conc_listX[LIST_1][idx]; + if( Source->Conc_listXInterviewPos[LIST_1] == idx ) + { + currSlice->listX[LIST_1][idx] = p_Dpb->fs_ilref[0]->frame; + } + else if( Temporal_Ref_idx[LIST_1] == -1 ) + { + Temporal_Ref_idx[LIST_1] = idx; + } + } + } + else + { + for( idx = 0; idx < currSlice->listXsize[LIST_0]; idx++ ) + { + currSlice->listX[LIST_0][idx] = Source->Conc_listX[LIST_0][idx]; + } + for( idx = 0; idx < currSlice->listXsize[LIST_1]; idx++ ) + { + currSlice->listX[LIST_1][idx] = Source->Conc_listX[LIST_1][idx]; + } + } + // End of intialization + mb_width = Dest->PicWidthInMbs; + + /* Loop though all motion vectors */ + for( y_block4x4 = 0; y_block4x4 < ( ( Dest->PicSizeInMbs ) / ( Dest->PicWidthInMbs ) ) * MB_BLOCK_SIZE / BLOCK_SIZE; y_block4x4++ ) + { + y = y_block4x4 * BLOCK_SIZE; //!< get y cordinates + for( x_block4x4 = 0; x_block4x4 < ( Dest->PicWidthInMbs ) * MB_BLOCK_SIZE / BLOCK_SIZE; x_block4x4++ ) + { + x = x_block4x4 * BLOCK_SIZE; + idx_mb = ( y / MB_BLOCK_SIZE ) * mb_width + ( x / MB_BLOCK_SIZE ); + currMB = &( p_Vid->mb_data[idx_mb] ); + + // Update coordinates of the current concealed macroblock + currMB->mb.x = ( short )( x / BLOCK_SIZE ); + currMB->mb.y = ( short )( y / BLOCK_SIZE ); + currMB->block_y = currMB->mb.y * BLOCK_SIZE; + currMB->pix_c_y = currMB->mb.y * p_Vid->mb_cr_size_y / 4; + currMB->block_x = currMB->mb.x * BLOCK_SIZE; + currMB->pix_c_x = currMB->mb.x * p_Vid->mb_cr_size_x / 4; + + // configure motion vector + Dest->mv_info[y_block4x4][x_block4x4].mv[LIST_0].mv_x = 0; + Dest->mv_info[y_block4x4][x_block4x4].mv[LIST_0].mv_y = 0; + Dest->mv_info[y_block4x4][x_block4x4].ref_idx[LIST_0] = Temporal_Ref_idx[LIST_0]; + + Dest->mv_info[y_block4x4][x_block4x4].mv[LIST_1].mv_x = 0; + Dest->mv_info[y_block4x4][x_block4x4].mv[LIST_1].mv_y = 0; + Dest->mv_info[y_block4x4][x_block4x4].ref_idx[LIST_1] = Temporal_Ref_idx[LIST_1]; + } + } + fercCopyImg( p_Vid, Source, Dest ); +} + +//static MotionVector MotionVectorPrediction(VideoParameters *p_Vid, StorablePicture *Dest, +// StorablePicture *SourceMvReference, Slice *RefSlice, +// int y_block4x4, int x_block4x4) +//{ +// int flag_ViewID = SourceMvReference->view_id; +// PicMotionParams PseudoMotion[5]; +// MotionVector returnVector; +// double scale; +// +// PseudoMotion[0] = SourceMvReference->mv_info[y_block4x4][x_block4x4]; +// PseudoMotion[1] = RefSlice->listX[LIST_0][0]->mv_info[y_block4x4+PseudoMotion[0].mv[LIST_0].mv_y/BLOCK_SIZE/4][x_block4x4+PseudoMotion[0].mv[LIST_0].mv_x/BLOCK_SIZE/4]; +// +// scale = (double)(PseudoMotion[0].mv[LIST_0].mv_y) / (double)(PseudoMotion[1].mv[LIST_0].mv_y); +// returnVector.mv_y = (double)(PseudoMotion[0].mv[LIST_0].mv_y) * scale; +// +// scale = (double)(PseudoMotion[0].mv[LIST_0].mv_x) / (double)(PseudoMotion[1].mv[LIST_0].mv_x); +// returnVector.mv_x = (double)(PseudoMotion[0].mv[LIST_0].mv_x) * scale; +// +// return returnVector; +//} +// +// +//static int ConcealmentOptimization( int ConcealmentMethod, StorablePicture *IntraRefFrame, StorablePicture *InterRefFrame, +// StorablePicture *Disparity, MotionVector **Disparity2D_mv, +// int y_block4x4, int x_block4x4) +//{ +// +// +//} diff --git a/ldecod/src/ferc/ferc_ismid.c b/ldecod/src/ferc/ferc_ismid.c new file mode 100644 index 0000000..87cce9f --- /dev/null +++ b/ldecod/src/ferc/ferc_ismid.c @@ -0,0 +1,657 @@ +/*! +************************************************************************ +* \file ferc_ismid.c +* +* \brief +* Internal function for ferc API +* Include a reference method based on disparity information +* +* \author +* - Joao Carreira +* +************************************************************************ +*/ + +#include + +#include "ferc_api.h" +#include "ferc_internals.h" + +#include "global.h" +#include "mbuffer.h" +#include "mbuffer_mvc.h" +#include "memalloc.h" +#include "image.h" +#include "mc_prediction.h" +#include "macroblock.h" +#include "block.h" +#include "mbuffer_common.h" +#include "ifunctions.h" +#include "ferc_opencv_api.h" + +#define DISP_SHIFT 128 + +int CLOSER_NEIHGHBORS[][2] = {{0,-1},{-1,0},{0,1},{1,0}}; + +static double AverageIntensityDifference( int ***Intensity, int BlockSize, int size_x, int size_y, int x, int y) +{ + int xx, yy, x_i, x_f, y_i, y_f, count = 0; + + unsigned long int sum = 0; + + x_i = imax(0,x-BlockSize/2); + x_f = imin(size_x-1, x+BlockSize/2); + + y_i = imax(0,y-BlockSize/2); + y_f = imin(size_y-1, y+BlockSize/2); + + for(yy=y_i; yy<=y_f; yy++) + { + for(xx=x_i; xx<=x_f; xx++) + { + if( Intensity[0][yy][xx] != INT_MAX ) + { + sum += iabs( Intensity[0][yy][xx] ); + count++; + } + } + } + if( count ) + return( sum / (double)(count)); + else + return -1; +} + +/*! +************************************************************************ +* \brief +* Compute the block based disparity for each pixel using a square block +* for a symmetric window +* +* The Disparity are normalized between +* [-WindowsSize, WindowsSize] +* Disp = Disp + GlobalWindowsSize + LocalWindowsSize; +************************************************************************ +*/ +static void BlockBasedDisparityEstimation( StorablePicture *Ref, StorablePicture *NonRef, + int WindowsSize, int BlockSize, StorablePicture *Disparity) +{ + + int x, y, xx, yy, d, disp, count; + int size_x = Ref->size_x, size_y = Ref->size_y; + double sad, min_sad; + int LowWindow, HighWindow; + + for(y=0; yRef->size_x ? Ref->size_x-x-1 : +WindowsSize; + + for(d=-LowWindow; d>=-HighWindow; d--) + { + sad = 0; + count = 0; + if( y-BlockSize/2<0 || y+BlockSize/2>=size_y || x-BlockSize/2-d<0 || x+BlockSize/2-d>=size_x ) + { + for(yy=-(BlockSize/2); yy<=(BlockSize/2); yy++) + { + for(xx=-(BlockSize/2); xx<=(BlockSize/2); xx++) + { + if( y+yy<0 || y+yy>=size_y || x+xx-d<0 || x+xx-d>=size_x ) + continue; + + sad += iabs( Ref->imgY[y+yy][x+xx] - NonRef->imgY[y+yy][x+xx-d]); + count++; + } + } + } + else + { + for(yy=-(BlockSize/2); yy<=(BlockSize/2); yy++) + { + for(xx=-(BlockSize/2); xx<=(BlockSize/2); xx+=1) + { + sad += iabs( Ref->imgY[y+yy][x+xx] - NonRef->imgY[y+yy][x+xx-d]); + count+=1; + } + } + } + sad = sad / (double)count; + if( sad < min_sad ) + { + min_sad = sad; + disp = d; + } + } + Disparity->imgY[y][x] = DISP_SHIFT + disp; + } + } + return; +} + +//static void MarkOclusionDectection( StorablePicture *Disparity, +// StorablePicture *OcclusionMap, int WindowSize ) +//{ +// int size_x = Disparity->size_x, size_y = Disparity->size_y; +// int x, y, w; +// +// for(y=0; yimgY[y][x] = 0; +// for(w=-WindowSize;w<=WindowSize;w++) +// { +// if( w != 0 && +// ( (short)(Disparity->imgY[y][x]) - DISP_SHIFT ) == +// ( ( (short)(Disparity->imgY[y][x+w]) - DISP_SHIFT) - w ) ) +// { +// OcclusionMap->imgY[y][x] = 255; +// } +// +// } +// } +// } +//} + +/*! +************************************************************************ +* \brief +* Diff to block and save to an image +* +************************************************************************ +*/ +void diffPredblockRegionYUV(int ***Dest_Diff, imgpel *predBlock_0, + imgpel *predBlock_1, int x, int y, int sum) +{ + int ii, jj; + int uv; + //! Y Matrix + for(ii=0;iiPicSlice; + DecodedPictureBuffer *p_Dpb = p_Vid->p_Dpb_layer[Dest->view_id]; + Macroblock *currMB; + MotionVector **pelbasedMotionVectors; + imgpel *storeYUV[2], **tmp_block_pred; + int i,j, idx, y, x, yy, xx, x_base, x_new, y_new, y_pel, x_pel, mv[3], ii, jj, + y_block4x4, x_block4x4, mb_width, mb_height, pel_value, pel_value_u, pel_value_v; + int relation_y_c = p_Vid->width / p_Vid->width_cr, Temporal_Ref_idx[2] = {-1, -1}; + unsigned int idx_mb; + char ref_idx; + double average, average_average, count_average; + int loop, count; + double scale_factor, max_diff; + + StorablePicture *Frame_Base_T, *Frame_Base_Tminus1 = NULL, *Frame_Aux_Tminus1 = NULL; + StorablePicture *Disparity = NULL, *Disparity_Filtered; + StorablePicture *currReference = NULL; + StorablePicture *OcclusionMap = NULL; + + int ***IntensityDifference = NULL, ***IntensityDifference_Rec = NULL; + int **FlagConcealed = NULL; + fercCopyImg(p_Vid, Source, Dest); + + get_mem2Dpel(&tmp_block_pred, MB_BLOCK_SIZE, MB_BLOCK_SIZE); + if (Source->chroma_format_idc != YUV400) + { + storeYUV[0] = (imgpel *) malloc ( (16 + (p_Vid->mb_cr_size_x*p_Vid->mb_cr_size_y)*2/16) * sizeof (imgpel)); + storeYUV[1] = (imgpel *) malloc ( (16 + (p_Vid->mb_cr_size_x*p_Vid->mb_cr_size_y)*2/16) * sizeof (imgpel)); + } + else + { + storeYUV[0] = (imgpel *) malloc (16 * sizeof (imgpel)); + storeYUV[1] = (imgpel *) malloc (16 * sizeof (imgpel)); + } + + //! Get pictures from the buffer + Frame_Base_T = fercGetPictureForConcealFromDpb(p_Vid->p_Dpb_layer[BASE_VIEW_IDX], Dest->poc, BASE_VIEW_IDX, EXACT, 0, NULL); + Frame_Aux_Tminus1 = fercGetPictureForConcealFromDpb(p_Vid->p_Dpb_layer[Dest->view_id], Dest->poc, Dest->view_id, CLOSEST_ALL, 0, NULL); + Frame_Base_Tminus1 = fercGetPictureForConcealFromDpb(p_Vid->p_Dpb_layer[BASE_VIEW_IDX], Frame_Aux_Tminus1->poc, BASE_VIEW_IDX, EXACT, 0, NULL); + + Disparity = alloc_storable_picture (p_Vid, FRAME, p_Vid->width, p_Vid->height, p_Vid->width_cr, p_Vid->height_cr, 1); + + +#if ( 0 ) + strcpy(p_Vid->p_Inp->DispLoadFileName,"Disparity.pgm"); + cvDisparity( p_Vid , Frame_Base_Tminus1, Frame_Aux_Tminus1, Disparity, LOAD, p_Vid->p_Inp->DisparityWindow, 0 ); +#else + BlockBasedDisparityEstimation( Frame_Aux_Tminus1, Frame_Base_Tminus1, 20, 17, Disparity); + saveStorablePicture(p_Vid,"Disparity.pgm",Disparity,0,0); +#endif + showStorablePicture(p_Vid, Disparity, 1, 0, "Disparity", 0); + + Disparity_Filtered = alloc_storable_picture (p_Vid, FRAME, p_Vid->width, p_Vid->height, p_Vid->width_cr, p_Vid->height_cr, 1); + MedianFilter( Disparity->imgY, Disparity_Filtered->imgY, p_Vid->width, p_Vid->height, 7 ); + free_storable_picture( Disparity ); + Disparity = Disparity_Filtered; + + showStorablePicture(p_Vid, Disparity, 1, 0, "Disparity", 0); + + //! Initialization of reference list for base slice + baseSlice = malloc_slice(p_Vid->p_Inp, p_Vid); + baseSlice->p_Vid = p_Vid; + baseSlice->p_Dpb = p_Vid->p_Dpb_layer[Frame_Base_T->view_id]; + baseSlice->ThisPOC = Frame_Base_T->poc; + baseSlice->framepoc = Frame_Base_T->poc; + baseSlice->view_id = Frame_Base_T->view_id; + baseSlice->layer_id = Frame_Base_T->layer_id; + baseSlice->slice_type = Frame_Base_T->slice_type; + + //! LIST_0 + baseSlice->listXsize[LIST_0] = Frame_Base_T->Conc_listXsize[LIST_0]; + for( idx=0; idxlistXsize[LIST_0]; idx++) + { + baseSlice->listX[LIST_0][idx] = Frame_Base_T->Conc_listX[LIST_0][idx]; + if( !baseSlice->listX[LIST_0][idx] ) + { + printf("Cannot conceal missing frame! Exit \n"); + Dest->have_pic = 0; + return; + } + } + //! LIST_1 + baseSlice->listXsize[LIST_1] = Frame_Base_T->Conc_listXsize[LIST_1]; + for( idx=0; idxlistXsize[LIST_1]; idx++) + { + baseSlice->listX[LIST_1][idx] = Frame_Base_T->Conc_listX[LIST_1][idx]; + if( !baseSlice->listX[LIST_1][idx] ) + { + printf("Cant conceal!\n"); + Dest->have_pic = 0; + return; + } + } + dump_ref_lists(baseSlice); + + //! Initialization of reference list for lost slice + currSlice->p_Vid = p_Vid; + currSlice->p_Dpb = p_Vid->p_Dpb_layer[Dest->view_id]; + currSlice->ThisPOC = Dest->poc; + currSlice->framepoc = Dest->poc; + currSlice->view_id = Dest->view_id; + currSlice->layer_id = Dest->layer_id; + currSlice->slice_type = Dest->slice_type; + + //! LIST_0 + currSlice->listXsize[LIST_0] = Frame_Base_T->Conc_listXsize[LIST_0]; + for( idx=0; idxlistXsize[LIST_0]; idx++) + { + currSlice->listX[LIST_0][idx] = fercGetPictureForConcealFromDpb(p_Vid->p_Dpb_layer[Dest->layer_id], Frame_Base_T->Conc_listX[LIST_0][idx]->poc, 1, EXACT, 1, NULL); + if( !currSlice->listX[LIST_0][idx] ) + { + printf("Cannot conceal missing frame! Exit \n"); + Dest->have_pic = 0; + return; + } + } + //! LIST_1 + currSlice->listXsize[LIST_1] = Frame_Base_T->Conc_listXsize[LIST_1]; + for( idx=0; idxlistXsize[LIST_1]; idx++) + { + currSlice->listX[LIST_1][idx] = fercGetPictureForConcealFromDpb(p_Vid->p_Dpb_layer[Dest->layer_id], Frame_Base_T->Conc_listX[LIST_1][idx]->poc, 1, EXACT, 1, NULL); + if( !currSlice->listX[LIST_1][idx] ) + { + printf("Cant conceal!\n"); + Dest->have_pic = 0; + return; + } + } + //dump_ref_lists(currSlice); + + + + //! Alloc auxiliary information to conceal + get_mem2Dint( &FlagConcealed, p_Vid->height, p_Vid->width); + get_mem3Dint( &IntensityDifference , 3, p_Vid->height, p_Vid->width); + get_mem3Dint( &IntensityDifference_Rec , 3, p_Vid->height, p_Vid->width); + get_mem2Dmv( &pelbasedMotionVectors, p_Vid->height, p_Vid->width); + + OcclusionMap = alloc_storable_picture (p_Vid, FRAME, p_Vid->width, p_Vid->height, p_Vid->width_cr, p_Vid->height_cr, 1); + + //!! Initialize the value of pixels + for(y=0;yheight;y++) + { + for(x=0;xwidth;x++) + { + FlagConcealed[y][x] = 0; + IntensityDifference_Rec[0][y][x] = IntensityDifference[0][y][x] = INT_MAX; + pelbasedMotionVectors[y][x].mv_x = pelbasedMotionVectors[y][x].mv_y = SHRT_MAX; + Dest->imgY[y][x] = 255; + OcclusionMap->imgY[y][x] = 0; + } + } + + //MarkOclusionDectection( Disparity, OcclusionMap, 20 ); + // saveStorablePicture(p_Vid,"OcclusionMap.bmp",OcclusionMap, 1, 0); + showStorablePicture(p_Vid, OcclusionMap, 0, 0, "Occlusion", 0); + + mb_width = Dest->PicWidthInMbs; + mb_height = (Dest->PicSizeInMbs)/(Dest->PicWidthInMbs); + + //! Loop though all motion vectors in the Base view frame + for(loop=0;loop<2;loop++) + { + for( y_block4x4=0; y_block4x4 < mb_height * MB_BLOCK_SIZE/BLOCK_SIZE; y_block4x4++) + { + y = y_block4x4 * BLOCK_SIZE; //!< get y cordinates + for( x_block4x4=0; x_block4x4 < mb_width * MB_BLOCK_SIZE/BLOCK_SIZE; x_block4x4++) + { + x = x_block4x4 * BLOCK_SIZE; + idx_mb = ( y / MB_BLOCK_SIZE ) * mb_width + ( x / MB_BLOCK_SIZE ); + currMB = &( p_Vid->mb_data[idx_mb] ); + // Update coordinates of the current concealed macroblock + currMB->mb.x = (short) (x/BLOCK_SIZE); + currMB->mb.y = (short) (y/BLOCK_SIZE); + currMB->block_y = currMB->mb.y * BLOCK_SIZE; + currMB->pix_c_y = currMB->mb.y * p_Vid->mb_cr_size_y/4; + currMB->block_x = currMB->mb.x * BLOCK_SIZE; + currMB->pix_c_x = currMB->mb.x * p_Vid->mb_cr_size_x/4; + + mv[2] = ref_idx = Frame_Base_T->mv_info[y_block4x4][x_block4x4].ref_idx[LIST_0]; + + if( loop == 0 )//! Intra blocks + { + + if( ref_idx >= 0 ) + continue; + + mv[0] = 0; + mv[1] = 0; + mv[2] = 0; + + x_new = x; + y_new = y; + } + else//! Other ones + { + + if( ref_idx < 0 ) + continue; + + mv[0] = Frame_Base_T->mv_info[y_block4x4][x_block4x4].mv[LIST_0].mv_x; + mv[1] = Frame_Base_T->mv_info[y_block4x4][x_block4x4].mv[LIST_0].mv_y; + + currReference = currSlice->listX[LIST_0][mv[2]]; + + scale_factor = (double)( Frame_Base_T->poc - Frame_Base_Tminus1->poc ) / (double)( Frame_Base_T->poc - currReference->poc ); + + mv[0] = mv[0] * scale_factor; + mv[1] = mv[1] * scale_factor; + + //! pel based vector + //! look at bit in position 2 - if it is one the final vector will have one more position + mv[0] = (mv[0]>>2) + ((mv[0]&0x02)>>1); + mv[1] = (mv[1]>>2) + ((mv[1]&0x02)>>1); + + x_new = x + mv[0]; + y_new = y + mv[1]; + + //! Goes outside of the image + if( x_new < 0 || x_new > p_Vid->width-4 || y_new < 0 || y_new > p_Vid->height-4) + continue; + } + + for(i=0;iheight;y++) + { + for(x=0;xwidth;x++) + { + if( pelbasedMotionVectors[y][x].mv_y != SHRT_MAX ) + { + y_new = y + pelbasedMotionVectors[y][x].mv_y; + x_new = x + pelbasedMotionVectors[y][x].mv_x; + + IntensityDifference[0][y][x] = (int)(Frame_Base_T->imgY[y_new][x_new]) - (int)(Frame_Base_Tminus1->imgY[y][x]); + IntensityDifference[1][y/2][x/2] = (int)(Frame_Base_T->imgY[y_new/2][x_new/2]) - (int)(Frame_Base_Tminus1->imgY[y/2][x/2]); + IntensityDifference[2][y/2][x/2] = (int)(Frame_Base_T->imgY[y_new/2][x_new/2]) - (int)(Frame_Base_Tminus1->imgY[y/2][x/2]); + } + else + { + IntensityDifference[0][y][x] = INT_MAX; + IntensityDifference[1][y>>1][x>>1] = INT_MAX; + IntensityDifference[2][y>>1][x>>1] = INT_MAX; + } + } + } + + //! Loop though all pixels in Frame of the view 1 in the instant t-1* + for(y=0;yheight;y++) + { + for(x=0;xwidth;x++) + { + //! Check oclude pixels + if( OcclusionMap->imgY[y][x] > 0) + continue; + + x_base = x - ( (int)(Disparity->imgY[y][x]) - DISP_SHIFT ); + //x_base = iClip3( 0, p_Vid->width - 1, x_base ); + if( x_base < 0 || x_base >= p_Vid->width ) + continue; + + //! This pixel were not reached in previous loop + if( IntensityDifference[0][y][x_base] == INT_MAX ) + continue; + + y_new = y + pelbasedMotionVectors[y][x_base].mv_y; + x_new = x + pelbasedMotionVectors[y][x_base].mv_x; + + if( y_new < 0 || y_new >= p_Vid->height || x_new < 0 || x_new >= p_Vid->width ) + { + continue; + } + + pel_value = (int)(Frame_Aux_Tminus1->imgY[y][x]) + IntensityDifference[0][y][x_base]; + pel_value_u = (int)(Frame_Aux_Tminus1->imgUV[0][y>>1][x>>1]) + IntensityDifference[1][y>>1][x_base>>1]; + pel_value_v = (int)(Frame_Aux_Tminus1->imgUV[1][y>>1][x>>1]) + IntensityDifference[2][y>>1][x_base>>1]; + //if( pel_value > 255 ) + //{ + // continue; + //} + //else if( pel_value < 0 ) + //{ + // continue; + //} + Dest->imgY[y_new][x_new] = (imgpel)iClip3(0,255,pel_value); + Dest->imgUV[0][y_new>>1][x_new>>1] = (imgpel)iClip3(0,255,pel_value_u); + Dest->imgUV[1][y_new>>1][x_new>>1] = (imgpel)iClip3(0,255,pel_value_v); + + IntensityDifference_Rec[0][y_new][x_new] = IntensityDifference[0][y][x_base]; + FlagConcealed[y_new][x_new] = 1; + } + } + + showStorablePicture(p_Vid,Dest,0,0,"Concealed frame",0); + + { + int count_1 = 0, count_2 = 0; + average_average = 0; + count_average=0; + + //! Hole Filling in the lost picture + for(y=0;yheight;y++) + { + for(x=0;xwidth;x++) + { + if( FlagConcealed[y][x] ) + continue; + + average = AverageIntensityDifference( IntensityDifference_Rec, 5, p_Vid->width, p_Vid->height, x, y); + + if( average < 0 ) + continue; + + count_average += 1; + average_average = ( average_average * (count_average-1) + average ) / ( count_average); + + if( average < 5 ) + { + Dest->imgY[y][x] = Frame_Aux_Tminus1->imgY[y][x]; + count_1++; + FlagConcealed[y][x] = 1; + } + } + } + showStorablePicture(p_Vid,Dest,0,0,"Concealed frame",0); + + //! Hole Filling + for(y=0;yheight;y++) + { + for(x=0;xwidth;x++) + { + if( FlagConcealed[y][x] ) + continue; + + for(idx=0, count = 0, average = 0; idx<4; idx++) + { + if( y+CLOSER_NEIHGHBORS[idx][0] >= 0 && y+CLOSER_NEIHGHBORS[idx][0] < Dest->size_y ) + { + if( x+CLOSER_NEIHGHBORS[idx][1] >= 0 && x+CLOSER_NEIHGHBORS[idx][1] < Dest->size_x ) + { + if( FlagConcealed[y+CLOSER_NEIHGHBORS[idx][0]][x+CLOSER_NEIHGHBORS[idx][1]] ) + { + average += Dest->imgY[y+CLOSER_NEIHGHBORS[idx][0]][x+CLOSER_NEIHGHBORS[idx][1]]; + count++; + } + } + } + } + if( count == 0 ) + { + Dest->imgY[y][x] = Frame_Aux_Tminus1->imgY[y][x]; + FlagConcealed[y][x] = 1; + count_1++; + continue; + } + average = average / count; + + Dest->imgY[y][x] = average; + FlagConcealed[y][x] = 1; + count_2++; + } + } + + printf("1: %d || 2: %d\n", count_1, count_2 ); + printf("%5.5f\n", average_average ); + } + + //! Hole Filling + for(y=0;yheight;y++) + { + for(x=0;xwidth;x++) + { + if( FlagConcealed[y][x] == 0) + { + int a = 0; + } + } + } + + showStorablePicture(p_Vid,Dest,0,0,"Concealed frame",0); + + + + /* Loop though all motion vectors */ + for( y_block4x4=0; y_block4x4 < ((Dest->PicSizeInMbs)/(Dest->PicWidthInMbs)) * MB_BLOCK_SIZE/BLOCK_SIZE; y_block4x4++) + { + y = y_block4x4 * BLOCK_SIZE; //!< get y cordinates + for( x_block4x4=0; x_block4x4 < (Dest->PicWidthInMbs) * MB_BLOCK_SIZE/BLOCK_SIZE; x_block4x4++) + { + x = x_block4x4 * BLOCK_SIZE; + idx_mb = ( y / MB_BLOCK_SIZE ) * mb_width + ( x / MB_BLOCK_SIZE ); + currMB = &( p_Vid->mb_data[idx_mb] ); + + // Update coordinates of the current concealed macroblock + currMB->mb.x = (short) (x/BLOCK_SIZE); + currMB->mb.y = (short) (y/BLOCK_SIZE); + currMB->block_y = currMB->mb.y * BLOCK_SIZE; + currMB->pix_c_y = currMB->mb.y * p_Vid->mb_cr_size_y/4; + currMB->block_x = currMB->mb.x * BLOCK_SIZE; + currMB->pix_c_x = currMB->mb.x * p_Vid->mb_cr_size_x/4; + + // configure motion vector + Dest->mv_info[y_block4x4][x_block4x4].mv[LIST_0].mv_x = 0; + Dest->mv_info[y_block4x4][x_block4x4].mv[LIST_0].mv_y = 0; + Dest->mv_info[y_block4x4][x_block4x4].ref_idx[LIST_0] = Temporal_Ref_idx[LIST_0]; + + Dest->mv_info[y_block4x4][x_block4x4].mv[LIST_1].mv_x = 0; + Dest->mv_info[y_block4x4][x_block4x4].mv[LIST_1].mv_y = 0; + Dest->mv_info[y_block4x4][x_block4x4].ref_idx[LIST_1] = Temporal_Ref_idx[LIST_1]; + } + } + + free_mem2Dpel( tmp_block_pred ); + free( storeYUV[0] ); + free( storeYUV[1] ); + free_mem2Dmv( pelbasedMotionVectors ); + + free_mem3Dint( IntensityDifference ); + free_mem3Dint( IntensityDifference_Rec ); + + + free_storable_picture( Disparity ); + + if( baseSlice ) + free_slice(baseSlice); +} diff --git a/ldecod/src/ferc/ferc_motion.c b/ldecod/src/ferc/ferc_motion.c new file mode 100644 index 0000000..20fb0d4 --- /dev/null +++ b/ldecod/src/ferc/ferc_motion.c @@ -0,0 +1,262 @@ +/*! + ************************************************************************ + * \file ferc_internals.c + * + * \brief + * Internal function for ferc API + * Include the concealment function using motion compensated concelament only + * + * \author + * - Jo�o Carreira + * + ************************************************************************ + */ + +#include "ferc_api.h" +#include "ferc_internals.h" + +#include "memalloc.h" +//#include "stream_parser.h" +//#include "image.h" +/*! + ************************************************************************ + * \brief + * Conceal using source reference motion vectors and references + ************************************************************************ + */ +void Conceal_by_MotionCopy_Ref (VideoParameters *p_Vid, StorablePicture *Source, StorablePicture *Dest) +{ + DecodedPictureBuffer *p_Dpb = p_Vid->p_Dpb_layer[Dest->view_id]; + ConcealParameters *p_FrameConc = p_Vid->p_FrameConc; + int ConcealmentMode = ZERO_MV; + /* Motion copy loop variables */ + int mb_height, mb_width; + //! Alloc a Slice associated with lost frame + Slice *currSlice = Dest->PicSlice; + StorablePicture *currRef; + Macroblock *currMB; + float scale_factor; + int List, x, y, y_block4x4, x_block4x4, idx_mb, iy_8x8, ix_8x8, mv[3]; + char used_list[2] = {0,0}; + int idx; + imgpel *predMB, *storeYUV_list[2], **tmp_block_pred; + + int n_concealed_mv = 0; + //int err = 0; + //T_FRAME *debugFrame; //= createFrame(p_Vid->width, p_Vid->height, &err); + + get_mem2Dpel(&tmp_block_pred, MB_BLOCK_SIZE, MB_BLOCK_SIZE); + if (Source->chroma_format_idc != YUV400) + { + storeYUV_list[LIST_0] = (imgpel *) malloc ( (16 + (p_Vid->mb_cr_size_x*p_Vid->mb_cr_size_y)*2/16) * sizeof (imgpel)); + storeYUV_list[LIST_1] = (imgpel *) malloc ( (16 + (p_Vid->mb_cr_size_x*p_Vid->mb_cr_size_y)*2/16) * sizeof (imgpel)); + } + else + { + storeYUV_list[LIST_0] = (imgpel *) malloc (16 * sizeof (imgpel)); + storeYUV_list[LIST_1] = (imgpel *) malloc (16 * sizeof (imgpel)); + } + //remove this + p_Vid->erc_img = p_Vid; + + //! Initialization of reference list + currSlice->p_Vid = p_Vid; + currSlice->p_Dpb = p_Vid->p_Dpb_layer[Dest->view_id]; + +#if MVC_EXTENSION_ENABLE + if( Dest->view_id != BASE_VIEW_IDX) + { + currSlice->ThisPOC = Dest->poc; + currSlice->framepoc = currSlice->ThisPOC; + currSlice->view_id = Dest->view_id; + currSlice->layer_id = Dest->layer_id; + + init_mvc_picture(currSlice); + } +#endif + + /* Initialization of references pic list */ + currSlice->ThisPOC = Source->poc; + currSlice->framepoc = Source->poc; + currSlice->view_id = Source->view_id; + currSlice->layer_id = Source->layer_id; + + currSlice->listXsize[LIST_0] = Source->Conc_listXsize[LIST_0]; + currSlice ->listXsize[LIST_1] = Source->Conc_listXsize[LIST_1]; + for( idx=0; idxlistXsize[LIST_0]; idx++) + { + currSlice->listX[LIST_0][idx] = Source->Conc_listX[LIST_0][idx]; + if( Source->Conc_listXInterviewPos[LIST_0] == idx ) + { + currSlice->listX[LIST_0][idx] = p_Dpb->fs_ilref[0]->frame; + } + } + + for( idx=0; idxlistXsize[LIST_1]; idx++) + { + currSlice->listX[LIST_1][idx] = Source->Conc_listX[LIST_1][idx]; + if( Source->Conc_listXInterviewPos[LIST_1] == idx ) + { + currSlice->listX[LIST_1][idx] = p_Dpb->fs_ilref[0]->frame; + } + } + // End of intialization + + dump_ref_lists( currSlice ); + + mb_width = Dest->PicWidthInMbs; + mb_height = (Dest->PicSizeInMbs)/(Dest->PicWidthInMbs); + + // dump_dpb(p_Vid->p_Dpb_layer[1]); + + //============================================================================ + //===================== Motion Concealment Loop ============================== + //============================================================================ + + /* Loop though all motion vectors */ + for( y_block4x4=0; y_block4x4 < mb_height * MB_BLOCK_SIZE/BLOCK_SIZE; y_block4x4++) + { + y = y_block4x4 * BLOCK_SIZE; + for( x_block4x4=0; x_block4x4 < mb_width * MB_BLOCK_SIZE/BLOCK_SIZE; x_block4x4++) + { + // Init variables + currRef = NULL; + + x = x_block4x4 * BLOCK_SIZE; + + // get block 8x8 cordinates within a MB + iy_8x8 = ( y % MB_BLOCK_SIZE ) / SMB_BLOCK_SIZE; + ix_8x8 = ( x % MB_BLOCK_SIZE ) / SMB_BLOCK_SIZE; + + ///* not exact - dont work for currSlice->mb_aff_frame_flag = 1 */ + idx_mb = ( y / MB_BLOCK_SIZE ) * mb_width + ( x / MB_BLOCK_SIZE ); + + currMB = &( p_Vid->mb_data[idx_mb] ); + currMB->p_Slice = currSlice; + + // Update coordinates of the current concealed macroblock + currMB->mb.x = (short) (x/BLOCK_SIZE); + currMB->mb.y = (short) (y/BLOCK_SIZE); + currMB->block_y = currMB->mb.y * BLOCK_SIZE; + currMB->pix_c_y = currMB->mb.y * p_Vid->mb_cr_size_y/4; + currMB->block_x = currMB->mb.x * BLOCK_SIZE; + currMB->pix_c_x = currMB->mb.x * p_Vid->mb_cr_size_x/4; + + List = Source->slice_type == B_SLICE ? LIST_1 : LIST_0; + + for(; List >= LIST_0; List--) + { + /* Update mv information - Read Reference Values */ + mv[0] = Source->mv_info[y_block4x4][x_block4x4].mv[List].mv_x; + mv[1] = Source->mv_info[y_block4x4][x_block4x4].mv[List].mv_y; + mv[2] = Source->mv_info[y_block4x4][x_block4x4].ref_idx[List]; //Reference Frame + + /* INTRA Blocks */ + if( Source->mv_info[y_block4x4][x_block4x4].ref_idx[LIST_0] < 0 && Source->mv_info[y_block4x4][x_block4x4].ref_idx[LIST_1] < 0) + { + break; + } + else if( Source->mv_info[y_block4x4][x_block4x4].ref_idx[List] >= 0) + { + /* Disparity Vectors */ + if( currSlice->view_id != 0 && currSlice->listX[List][mv[2]]->poc == Dest->poc) + { + if( !( p_FrameConc->ConcealmentMode == MOTION_COPY_SPATIAL || p_FrameConc->ConcealmentMode == MOTION_COPY_TEMPORAL_SPATIAL) ) + { + continue; + } + } + /* Temporal vectors */ + else + { + /* Unuse Temporal motion vectors */ + if( !( p_FrameConc->ConcealmentMode == MOTION_COPY_TEMPORAL || p_FrameConc->ConcealmentMode == MOTION_COPY_TEMPORAL_SPATIAL) ) + { + continue; + } + } + + currRef = currSlice->listX[List][mv[2]]; + // Scale mv + scale_factor = (float)( Dest->poc - currRef->poc ) / (float)( Source->poc - currRef->poc ); + scale_factor = scale_factor ? scale_factor : 1; + mv[0] = (int)( (float)( mv[0] ) * scale_factor ); + mv[1] = (int)( (float)( mv[1] ) * scale_factor ); + + // copy motion vector + Dest->mv_info[y_block4x4][x_block4x4].mv[List].mv_x = mv[0]; + Dest->mv_info[y_block4x4][x_block4x4].mv[List].mv_y = mv[1]; + Dest->mv_info[y_block4x4][x_block4x4].ref_idx[List] = mv[2]; + + if( currRef == p_Vid->no_reference_picture ) + { + printf("Bad reference frame!!\n"); + continue; + } + used_list[List] = 1; + buildPredblockRegionYUV(p_Vid, currRef, currMB, mv, x, y, storeYUV_list[List], BLOCK_SIZE, tmp_block_pred, 1); + } + else + { + //! current List not chosen + } + } + List = 0; + switch( used_list[LIST_0] + (used_list[LIST_1]<<1) ) + { + case 0: + ConcealmentMode = ZERO_MV; + //None -> Need concealment + // Update mv information - Read Reference Values + //conceal motion vector + //ConcealMotionVector(p_Vid, Dest, currSlice, y_block4x4, x_block4x4, AVERAGE, LIST_0, mv, currSlice->listX[LIST_0][0]->poc == Dest->poc ? 1 : 0); + ConcealMotionVector(p_Vid, Dest, currSlice, y_block4x4, x_block4x4, ConcealmentMode, LIST_0, mv, currSlice->listX[LIST_0][0]->poc == Dest->poc ? 1 : 0); // r80 + + if( ConcealmentMode == ZERO_MV) + { + mv[2] = imin( mv[2], currSlice->listXsize[LIST_0] - 1 ); + currRef = currSlice->listX[LIST_0][mv[2]]; + } + // Scale mv + //Do not need scaling because the vectors have already been modified + //scale_factor = (float)( Source->poc - currRef->poc ) / (float)( Dest->poc - currRef->poc ); + scale_factor = 1; + mv[0] = (int)( (float)( mv[0] ) / scale_factor ); + mv[1] = (int)( (float)( mv[1] ) / scale_factor ); + // copy mv + Dest->mv_info[y_block4x4][x_block4x4].mv[LIST_0].mv_x = mv[0]; + Dest->mv_info[y_block4x4][x_block4x4].mv[LIST_0].mv_y = mv[1]; + Dest->mv_info[y_block4x4][x_block4x4].ref_idx[LIST_0] = mv[2]; + + buildPredblockRegionYUV(p_Vid, currRef, currMB, mv, x, y, storeYUV_list[LIST_0], BLOCK_SIZE, tmp_block_pred, 1); + n_concealed_mv++; + predMB = storeYUV_list[LIST_0]; + break; + case 1: + // List 0 + predMB = storeYUV_list[LIST_0]; + break; + case 2: + // List 1 + predMB = storeYUV_list[LIST_1]; + break; + case 3: + // Both List 0 and 1 + ferc_bi_prediction(storeYUV_list[LIST_0], storeYUV_list[LIST_0], 1, storeYUV_list[LIST_1], 1, BLOCK_SIZE, BLOCK_SIZE, 1); + predMB = storeYUV_list[LIST_0]; + break; + } + used_list[LIST_0] = 0; + used_list[LIST_1] = 0; + + copyPredblockRegionYUVDestination( Dest, predMB, x, y, BLOCK_SIZE); + } + } + + //! Debug reports + printf("[FERC] Number of Concealed mv %d\n", n_concealed_mv); + + free_mem2Dpel( tmp_block_pred ); + free(storeYUV_list[LIST_0]); + free(storeYUV_list[LIST_1]); +} diff --git a/ldecod/src/ferc/ferc_motion_estimation.c b/ldecod/src/ferc/ferc_motion_estimation.c new file mode 100644 index 0000000..dae9bab --- /dev/null +++ b/ldecod/src/ferc/ferc_motion_estimation.c @@ -0,0 +1,288 @@ +/*! +************************************************************************ +* \file +* ferc_motion_estimation.c +* +* \brief +* Internal function for ferc API +* Motion estimation for intra blocks +* +* \author +* - Jo�o Carreira +* +************************************************************************ +*/ + +//#define OPTIMAZATIONS + + +#ifdef OPTIMAZATIONS + +//! System function and defines +#include +#include + + + +#include +#include +#include + +//! JM functions and defines +#include "typedefs.h" + + +int blockMatch(const imgpel* refFrame, int stepBytesRF /*stride*/, const imgpel* curBlock, + int stepBytesCB /*stride*/, int* matchBlock /*results*/, int frameWidth, int frameHeight, int block_size) +{ + int i, j, k, l; + int lowSum = INT_MAX; + int temSum = 0; + int blockHeight = block_size; + int blockWidth = block_size; + const imgpel *pRef, *pCur; + for (i=0; i<=frameHeight-blockHeight; i++) + { + for (j=0; j<=frameWidth-blockWidth; j++) + { + temSum = 0; + pCur = curBlock; + pRef = refFrame+i*stepBytesRF+j; + for (k=0; ksize_x; + // int size_y = InterRefFrame->size_y; + // int stride = InterRefFrame->size_x + 2 * InterRefFrame->iLumaPadX; + // int64 tmp_time; + + // Block_Size = 16; + + // gettime (&(p_Vid->start_time)); + + // get_mem3Dint( &BaseViewMV, mb_height * (MB_BLOCK_SIZE/Block_Size), mb_width * (MB_BLOCK_SIZE/Block_Size), 2); // size x + + // currSourceRef = pSliceInterViewReference->listX[LIST_0][0]; + + // //showStorablePicture( p_Vid , InterRefFrame , 0 , 0 , "Ref Disparity", 1); + // //showStorablePicture( p_Vid , currSourceRef , 0 , 0 , "New Disparity", 0); + // //OpenCVCloseWindows( NULL ); + + + // //===================== Motion Concealment Loop 1 ============================== + // /* Loop though all motion vectors */ + // for( y_block4x4=0; y_block4x4 < mb_height * (MB_BLOCK_SIZE/Block_Size); y_block4x4++) + // { + // y = y_block4x4 * Block_Size; //!< get y cordinates + // for( x_block4x4=0; x_block4x4 < mb_width * (MB_BLOCK_SIZE/Block_Size); x_block4x4+=1) + // { + // x = x_block4x4 * Block_Size; + + // //blockMatch( *(currSourceRef->imgY), stride /*stride*/, &(InterRefFrame->imgY[y][x]), stride /*stride*/, match_block /*results*/, size_x, size_y, Block_Size); + // blockMatch16x16SSE4( currSourceRef->imgY[0], xStride /*stride*/, &(InterRefFrame->imgY[y][x]), xStride /*stride*/, match_block /*results*/, SizeX, SizeY, Block_Size); + // //blockMatch4x4SSE2( *(currSourceRef->imgY), stride /*stride*/, &(InterRefFrame->imgY[y][x]), stride /*stride*/, match_block /*results*/, size_x, size_y, Block_Size); + + // BaseViewMV[y_block4x4][x_block4x4][0] = match_block[0] - x; + // BaseViewMV[y_block4x4][x_block4x4][1] = match_block[1] - y; + + // //BaseViewMV[y_block4x4][x_block4x4+1][0] = match_block[2] - x + 4; + // //BaseViewMV[y_block4x4][x_block4x4+1][1] = match_block[3] - y; + + // //BaseViewMV[y_block4x4][x_block4x4+1][0] = match_block[4] - x + 8; + // //BaseViewMV[y_block4x4][x_block4x4+1][1] = match_block[5] - y; + + // //BaseViewMV[y_block4x4][x_block4x4+1][0] = match_block[6] - x + 12; + // //BaseViewMV[y_block4x4][x_block4x4+1][1] = match_block[7] - y; + + // } + // } + + // gettime (&(p_Vid->end_time)); // end time + // tmp_time = timediff(&(p_Vid->start_time), &(p_Vid->end_time)); + // p_Vid->tot_time += tmp_time; + // tmp_time = timenorm(tmp_time); + + // printf("Motion estimation in %d\n", (int)tmp_time); + //} + +#endif diff --git a/ldecod/src/ferc/ferc_opencv_api.c b/ldecod/src/ferc/ferc_opencv_api.c new file mode 100644 index 0000000..ec1de46 --- /dev/null +++ b/ldecod/src/ferc/ferc_opencv_api.c @@ -0,0 +1,485 @@ +/*! +************************************************************************************* +* \file fercOpenCVApi.c +* +* \brief +* Functions to use OpenCV library in JM Software +* +* \date +* 29.11.2011 +* +* \author +* Joao Carreira +* +************************************************************************************* +*/ + +#include "ferc_opencv_api.h" + +#define HAVE_OPENCV 0 + +#if( HAVE_OPENCV == 1 ) + +#define ORIGINAL_OPENCV 1 + +//! OpenCV +#include "opencv2/highgui/highgui_c.h" +#include "opencv2/imgproc/imgproc_c.h" +#include "opencv2/calib3d/calib3d.hpp" +#include "opencv2/contrib/contrib.hpp" + +#if( _DEBUG ) +#define SHOW_IMG_DEBUG(WindowName,Img) cvShowImage( WindowName, Img ); cvWaitKey(0); cvDestroyWindow(WindowName); +#else +#define SHOW_IMG_DEBUG(WindowName,Img) ; +#endif + +static void convStorablePicture2IplImage( VideoParameters *p_Vid, StorablePicture *p_StorablePicture, IplImage **p_IplImageRGB, int SymbolSize ) +{ + static const int SubWidthC [4]= { 1, 2, 2, 1}; + static const int SubHeightC [4]= { 1, 2, 1, 1}; + int crop_left, crop_right, crop_top, crop_bottom; + IplImage *pU, *pV,*pY,*pU_big, *pV_big, *pYUV; + int depth; + + switch(SymbolSize) + { + case 1: + depth = IPL_DEPTH_8U; + break; + case 2: + depth = IPL_DEPTH_16U; + break; + default: + depth = IPL_DEPTH_8U; + break; + } + + pY = cvCreateImage ( cvSize( p_StorablePicture->size_x , p_StorablePicture->size_y ) , depth, 1 ); + pU_big = cvCreateImage ( cvSize( p_StorablePicture->size_x , p_StorablePicture->size_y ) , depth, 1 ); + pV_big = cvCreateImage ( cvSize( p_StorablePicture->size_x , p_StorablePicture->size_y ) , depth, 1 ); + pYUV = cvCreateImage ( cvSize( p_StorablePicture->size_x , p_StorablePicture->size_y ) , depth, 3 ); + + *p_IplImageRGB = cvCreateImage ( cvSize( p_StorablePicture->size_x , p_StorablePicture->size_y ) , depth, 3 ); + + if( p_StorablePicture->frame_cropping_flag ) + { + crop_left = SubWidthC [p_StorablePicture->chroma_format_idc] * p_StorablePicture->frame_crop_left_offset; + crop_right = SubWidthC [p_StorablePicture->chroma_format_idc] * p_StorablePicture->frame_crop_right_offset; + crop_top = SubHeightC[p_StorablePicture->chroma_format_idc] * ( 2 - p_StorablePicture->frame_mbs_only_flag ) * p_StorablePicture->frame_crop_top_offset; + crop_bottom = SubHeightC[p_StorablePicture->chroma_format_idc] * ( 2 - p_StorablePicture->frame_mbs_only_flag ) * p_StorablePicture->frame_crop_bottom_offset; + } + else + { + crop_left = crop_right = crop_top = crop_bottom = 0; + } + p_Vid->img2buf (p_StorablePicture->imgY, (unsigned char *)pY->imageData, p_StorablePicture->size_x, p_StorablePicture->size_y, ((p_Vid->pic_unit_bitsize_on_disk+7) >> 3), crop_left, crop_right, crop_top, crop_bottom, p_StorablePicture->size_x); + pY->imageDataOrigin = pY->imageData; + + pU = cvCreateImage ( cvSize( p_StorablePicture->size_x_cr , p_StorablePicture->size_y_cr ) , depth, 1 ); + pV = cvCreateImage ( cvSize( p_StorablePicture->size_x_cr , p_StorablePicture->size_y_cr ) , depth, 1 ); + + crop_left = p_StorablePicture->frame_crop_left_offset; + crop_right = p_StorablePicture->frame_crop_right_offset; + crop_top = ( 2 - p_StorablePicture->frame_mbs_only_flag ) * p_StorablePicture->frame_crop_top_offset; + crop_bottom = ( 2 - p_StorablePicture->frame_mbs_only_flag ) * p_StorablePicture->frame_crop_bottom_offset; + p_Vid->img2buf (p_StorablePicture->imgUV[0], (unsigned char *)pU->imageData, p_StorablePicture->size_x_cr, p_StorablePicture->size_y_cr, ((p_Vid->pic_unit_bitsize_on_disk+7) >> 3), crop_left, crop_right, crop_top, crop_bottom, p_StorablePicture->size_x_cr); + p_Vid->img2buf (p_StorablePicture->imgUV[1], (unsigned char *)pV->imageData, p_StorablePicture->size_x_cr, p_StorablePicture->size_y_cr, ((p_Vid->pic_unit_bitsize_on_disk+7) >> 3), crop_left, crop_right, crop_top, crop_bottom, p_StorablePicture->size_x_cr); + pU->imageDataOrigin = pU->imageData; + pV->imageDataOrigin = pV->imageData; + + cvResize(pU, pU_big, CV_INTER_LINEAR ); + cvResize(pV, pV_big, CV_INTER_LINEAR ); + + cvMerge(pY, pU_big, pV_big, NULL, pYUV); + + cvReleaseImage(&pU_big); + cvReleaseImage(&pV_big); + cvCvtColor(pYUV,*p_IplImageRGB,CV_YCrCb2RGB); + cvReleaseImage(&pYUV); +} + +static void convStorablePicture2IplImage_Y( VideoParameters *p_Vid, StorablePicture *p_StorablePicture, IplImage **p_IplImageY, int SymbolSize ) +{ + static const int SubWidthC [4]= { 1, 2, 2, 1}; + static const int SubHeightC [4]= { 1, 2, 1, 1}; + int crop_left, crop_right, crop_top, crop_bottom; + int depth; + + switch(SymbolSize) + { + case 1: + depth = IPL_DEPTH_8U; + break; + case 2: + depth = IPL_DEPTH_16U; + break; + default: + SymbolSize = 1; + depth = IPL_DEPTH_8U; + break; + } + + if( !(*p_IplImageY) ) + (*p_IplImageY) = cvCreateImage ( cvSize( p_StorablePicture->size_x , p_StorablePicture->size_y ) , depth, 1 ); + + if( p_StorablePicture->frame_cropping_flag ) + { + crop_left = SubWidthC [p_StorablePicture->chroma_format_idc] * p_StorablePicture->frame_crop_left_offset; + crop_right = SubWidthC [p_StorablePicture->chroma_format_idc] * p_StorablePicture->frame_crop_right_offset; + crop_top = SubHeightC[p_StorablePicture->chroma_format_idc] * ( 2 - p_StorablePicture->frame_mbs_only_flag ) * p_StorablePicture->frame_crop_top_offset; + crop_bottom = SubHeightC[p_StorablePicture->chroma_format_idc] * ( 2 - p_StorablePicture->frame_mbs_only_flag ) * p_StorablePicture->frame_crop_bottom_offset; + } + else + { + crop_left = crop_right = crop_top = crop_bottom = 0; + } + + p_Vid->img2buf (p_StorablePicture->imgY, (unsigned char *)(*p_IplImageY)->imageData, p_StorablePicture->size_x, p_StorablePicture->size_y, SymbolSize , crop_left, crop_right, crop_top, crop_bottom, p_StorablePicture->size_x*SymbolSize); + + (*p_IplImageY)->origin = 0; + (*p_IplImageY)->imageDataOrigin = (*p_IplImageY)->imageData; + +} + + +static void convIplImage_Y2StorablePicture( VideoParameters *p_Vid, IplImage *p_IplImage, StorablePicture *p_StorablePicture, int symbol_size_in_bytes ) +{ + int i, j; + int size_x = p_StorablePicture->size_x, size_y = p_StorablePicture->size_y; + unsigned char *buf; + imgpel *img_buf; + IplImage *p_IplImageY = cvCreateImage ( cvSize( p_StorablePicture->size_x , p_StorablePicture->size_y ) , IPL_DEPTH_8U, 1 ); + IplImage *p_IplImageU = cvCreateImage ( cvSize( p_StorablePicture->size_x , p_StorablePicture->size_y ) , IPL_DEPTH_8U, 1 ); + IplImage *p_IplImageV = cvCreateImage ( cvSize( p_StorablePicture->size_x , p_StorablePicture->size_y ) , IPL_DEPTH_8U, 1 ); + + IplImage *p_IplImageU_small = cvCreateImage ( cvSize( p_StorablePicture->size_x_cr , p_StorablePicture->size_y_cr ) , IPL_DEPTH_8U, 1 ); + IplImage *p_IplImageV_small = cvCreateImage ( cvSize( p_StorablePicture->size_x_cr , p_StorablePicture->size_y_cr ) , IPL_DEPTH_8U, 1 ); + + if( p_IplImage->nChannels == 1 ){ + cvSet(p_IplImageU_small , cvScalar( 0 , 0 , 0 , 0) , NULL ); + cvSet(p_IplImageV_small , cvScalar( 0 , 0 , 0 , 0) , NULL ); + p_IplImageY = p_IplImage; + } + else + { + cvSplit(p_IplImage,p_IplImageY,p_IplImageU,p_IplImageV,NULL); + cvResize( p_IplImageU_small , p_IplImageU , CV_INTER_NN ); + cvResize( p_IplImageV_small , p_IplImageV , CV_INTER_NN ); + } + + + buf = (unsigned char *)p_IplImage->imageData; + for (j=0; j < size_y; j++) + { + img_buf = p_StorablePicture->imgY[j]; + for (i=0; i < size_x; i++) + { + *(img_buf++)= (imgpel) *(buf++); + } + } + + if( p_IplImage->nChannels != 1 ) + cvReleaseImage(&p_IplImageY); + + cvReleaseImage(&p_IplImageU); + cvReleaseImage(&p_IplImageV); + cvReleaseImage(&p_IplImageU_small); + cvReleaseImage(&p_IplImageV_small); + +} + +#endif + +void OpenCVCloseWindows (char *WindowName) +{ +#if( HAVE_OPENCV == 1 ) + if( WindowName == NULL ) + cvDestroyAllWindows(); + else + cvDestroyWindow(WindowName); +#endif + return; +} + +void showStorablePicture(VideoParameters *p_Vid , StorablePicture *Pic, int SymbolSize, int show_uv, char * WindowName, int wait) +{ +#if( HAVE_OPENCV == 1 ) + IplImage *p_cvRGB = NULL; + +#if( _DEBUG == 0 ) + return; +#endif + + SymbolSize = SymbolSize ? SymbolSize : ((p_Vid->pic_unit_bitsize_on_disk+7) >> 3); + + if( show_uv ) + convStorablePicture2IplImage( p_Vid, Pic, &p_cvRGB, SymbolSize ); + else + convStorablePicture2IplImage_Y( p_Vid, Pic, &p_cvRGB, SymbolSize ); + + cvShowImage( WindowName, p_cvRGB); + cvWaitKey(wait); + + cvReleaseImage(&p_cvRGB); + + if( wait == 0) + cvDestroyWindow(WindowName); +#endif + return; +} + + +void cvResizeStorablePicture( VideoParameters *p_Vid , StorablePicture *p_Input, StorablePicture *p_Output ) +{ +#if( HAVE_OPENCV == 1 ) + IplImage *p_cvImgIn = NULL, *p_cvImgOut = NULL; + + convStorablePicture2IplImage_Y( p_Vid, p_Input , &p_cvImgIn, 0 ); + convStorablePicture2IplImage_Y( p_Vid, p_Output , &p_cvImgOut,0 ); + + cvResize( p_cvImgIn , p_cvImgOut , CV_INTER_NN ); + + convIplImage_Y2StorablePicture( p_Vid , p_cvImgOut , p_Output, 1 ); + + cvReleaseImage(&p_cvImgIn); + cvReleaseImage(&p_cvImgOut); +#endif + return; +} + +void cvDisparity( VideoParameters *p_Vid , StorablePicture *Ref, StorablePicture *NonRef, StorablePicture *Disparity, int Method, int WindowRatio, int Direction ) +{ +#if( HAVE_OPENCV == 1 ) + IplImage *p_cvImgLeft = NULL, *p_cvImgRight= NULL; + IplImage *p_cvImgDisp = NULL; + int numberOfDisparities; + + + + if( Method == LOAD ) + { + if( ( p_cvImgDisp = cvLoadImage(p_Vid->p_Inp->DispLoadFileName, CV_LOAD_IMAGE_UNCHANGED) ) != NULL ) + { + //SHOW_IMG_DEBUG( "Disp Debug ",p_cvImgDisp) + convIplImage_Y2StorablePicture( p_Vid , p_cvImgDisp , Disparity, 1 ); + cvReleaseImage(&p_cvImgDisp); + } + return; + } + + convStorablePicture2IplImage_Y( p_Vid, Ref , &p_cvImgLeft, 0 ); + convStorablePicture2IplImage_Y( p_Vid, NonRef , &p_cvImgRight, 0 ); + + //SHOW_IMG_DEBUG( "Left",p_cvImgLeft) + + //numberOfDisparities = ( ( cvGetSize(p_cvImgLeft).width/10 ) + 15 ) & -16; + numberOfDisparities = 256 / WindowRatio; + + if( Method == VAR) + { + p_cvImgDisp = cvCreateImage ( cvSize( Ref->size_x , Ref->size_y ) , IPL_DEPTH_8U, 1 ); +#if( ORIGINAL_OPENCV == 0 ) + CvStereoVARState *stateVAR = cvCreateStereoVARState( 0, numberOfDisparities, 1 ); + switch( Direction) + { + case 1: + stateVAR->minDisp = 0; + stateVAR->maxDisp = ( numberOfDisparities ); + break; + case -1: + stateVAR->minDisp = -( numberOfDisparities ); + stateVAR->maxDisp = 0; + break; + default: + return; + } + cvFindStereoCorrespondenceVAR( p_cvImgLeft , p_cvImgRight , &p_cvImgDisp, stateVAR ); + cvReleaseStereoVARState( &stateVAR ); +#endif + } + else if( Method == BM) + { + IplImage *p_cvImgDisp_16S = cvCreateImage ( cvSize( Ref->size_x , Ref->size_y ) , IPL_DEPTH_16S, 1 ); + CvStereoBMState *stateBM = cvCreateStereoBMState( CV_STEREO_BM_BASIC, numberOfDisparities ); + + stateBM->preFilterCap = 31; + stateBM->SADWindowSize = 17; + //stateBM->look_left = 1; //!< Not implemented + stateBM->minDisparity = 0; + stateBM->numberOfDisparities = numberOfDisparities; + stateBM->textureThreshold = 10; + stateBM->uniquenessRatio = 15; + stateBM->speckleWindowSize = 100; + stateBM->speckleRange = 32; + stateBM->disp12MaxDiff = 1; + + cvFindStereoCorrespondenceBM( p_cvImgLeft, p_cvImgRight, p_cvImgDisp_16S, stateBM ); + + p_cvImgDisp = cvCreateImage ( cvSize( Ref->size_x , Ref->size_y ) , IPL_DEPTH_8U, 1 ); + cvConvertScale( p_cvImgDisp_16S, p_cvImgDisp, 255/(numberOfDisparities*16.), 0); + + cvReleaseStereoBMState( &stateBM ); + cvReleaseImage(&p_cvImgDisp_16S); + } +// else if( Method == SGBM) +// { +// p_cvImgDisp = cvCreateImage ( cvSize( Ref->size_x , Ref->size_y ) , IPL_DEPTH_8U, 1 ); +// if( p_cvImgDisp == NULL ) +// { +// printf("Do not have disparity image\n"); +// } +// } + else + { + p_cvImgDisp = cvCreateImage ( cvSize( Ref->size_x , Ref->size_y ) , IPL_DEPTH_8U, 1 ); + printf("Do not have disparity image\n"); + } + + convIplImage_Y2StorablePicture( p_Vid , p_cvImgDisp , Disparity, 1 ); + + cvReleaseImage(&p_cvImgLeft); + cvReleaseImage(&p_cvImgRight); + cvReleaseImage(&p_cvImgDisp); + +#endif + + return; +} + +void saveStorablePicture( VideoParameters *p_Vid, char *filename , StorablePicture *Picture , int SymbolSize, int save_uv ) +{ +#if( HAVE_OPENCV == 1 ) + IplImage *p_cvRGB = NULL; + +#if( _DEBUG == 0 ) + //return; +#endif + + SymbolSize = SymbolSize ? SymbolSize : ((p_Vid->pic_unit_bitsize_on_disk+7) >> 3); + + if( save_uv ) + convStorablePicture2IplImage( p_Vid, Picture, &p_cvRGB, SymbolSize ); + else + convStorablePicture2IplImage_Y( p_Vid, Picture , &p_cvRGB, SymbolSize ); + + cvSaveImage( filename , p_cvRGB , 0 ); + + cvReleaseImage(&p_cvRGB); + + +#endif + return; +} + +void cvLoadStorablePicture( VideoParameters *p_Vid, char *filename , StorablePicture *Picture , int SymbolSize, int load_uv ) +{ +#if( HAVE_OPENCV == 1 ) + IplImage *cvInputImage = NULL; + + //#if( _DEBUG == 0 ) + // return; + //#endif + // + SymbolSize = SymbolSize ? SymbolSize : ((p_Vid->pic_unit_bitsize_on_disk+7) >> 3); + + if( ( cvInputImage = cvLoadImage(filename, CV_LOAD_IMAGE_UNCHANGED) ) != NULL ) + { + SHOW_IMG_DEBUG( "IMG DEBUG",cvInputImage) + convIplImage_Y2StorablePicture( p_Vid , cvInputImage , Picture, 1 ); + cvReleaseImage(&cvInputImage); + } + +#endif + return; +} + +void cvImageFilter( VideoParameters *p_Vid , StorablePicture *InputImage, StorablePicture *OutputImage, CVFILTERTYPES Filter, int KernelSize ) +{ +#if( HAVE_OPENCV == 1 ) + IplImage *cvInputImage = NULL, *cvOutputImage = NULL; + + /* + * Convert JM Storable picture to a OpenCV IplImage + * This function alloc the image struct so the pointer dont need to be allocated + */ + convStorablePicture2IplImage_Y( p_Vid, InputImage , &cvInputImage, 0 ); + + // Get image for contours with 8 bits depth and 1 channel (gray) + if( ( cvOutputImage = cvCreateImage ( cvSize( InputImage->size_x, InputImage->size_y) , IPL_DEPTH_8U, 1 ) ) == NULL) + { + printf("[OpenCV API] Cannot allocated memory for contours image - Returning a black image\n"); + cvReleaseImage(&cvInputImage); + return; + } + + SHOW_IMG_DEBUG( "Original Image", cvInputImage ); + + //! Apply the chosen filter + switch(Filter) + { + case MEAN_FILTER: + + cvSmooth( cvInputImage, cvOutputImage, CV_BLUR_NO_SCALE, KernelSize, KernelSize, 0, 0); + + break; + case MEDIAN_FILTER: + cvSmooth( cvInputImage, cvOutputImage, CV_MEDIAN , 5, 0, 0, 0); + break; + default: + return; + } + + SHOW_IMG_DEBUG( "Filtered Image", cvOutputImage ); + + //! Converts image + convIplImage_Y2StorablePicture( p_Vid , cvOutputImage , OutputImage, 1 ); + +#endif + return; +} + + +void cvFindImageContours( VideoParameters *p_Vid , StorablePicture *InputImage, StorablePicture *OutputContours ) +{ +#if( HAVE_OPENCV == 1 ) + IplImage *cvInputImage = NULL, *cvOutputContours = NULL; + int SobelOperatorKernel, lowerThresh, highTresh; + + /* + * Convert JM Storable picture to a OpenCV IplImage + * This function alloc the image struct so the pointer dont need to be allocated + */ + convStorablePicture2IplImage_Y( p_Vid, InputImage , &cvInputImage, 0 ); + + // Get image for contours with 8 bits depth and 1 channel (gray) + if( ( cvOutputContours = cvCreateImage ( cvSize( InputImage->size_x, InputImage->size_y) , IPL_DEPTH_8U, 1 ) ) == NULL) + { + printf("[OpenCV API] Cannot allocated memory for contours image - Returning a black image\n"); + cvReleaseImage(&cvInputImage); + return; + } + + //! Configure the edge detector variables + lowerThresh = 0; + highTresh = 9; + SobelOperatorKernel = 3; + + // Run the edge detector on grayscale + cvCanny( cvInputImage, cvOutputContours, lowerThresh, highTresh, SobelOperatorKernel); + + // Converts image + convIplImage_Y2StorablePicture( p_Vid , cvOutputContours , OutputContours, 1 ); +#endif + return; +} + + +int CheckOpenCVUsage(void) +{ +#if( HAVE_OPENCV == 1 ) + return 1; +#else + return 0; +#endif +} diff --git a/ldecod/src/ferc/ferc_proposed.c b/ldecod/src/ferc/ferc_proposed.c new file mode 100644 index 0000000..5b1096e --- /dev/null +++ b/ldecod/src/ferc/ferc_proposed.c @@ -0,0 +1,3693 @@ +/*! + ************************************************************************ + * \file ferc_proposed.c + * + * \brief + * Internal function for ferc API + * Include the proposed concealment method for the paper VCIP 2012 + * Proposed iterative concealment method + * + * \version + * 3.0 + * + * \author + * - Joao Carreira + * + ************************************************************************ + */ + +#include "ferc_api.h" +#include "ferc_internals.h" +#include "ferc_proposed.h" + +#include "global.h" +#include "mbuffer.h" +#include "mbuffer_mvc.h" +#include "memalloc.h" +#include "image.h" +#include "ferc_opencv_api.h" +#include "stream_parser.h" + +#undef BLOCK_SIZE +#define BLOCK_SIZE 4 + +enum direction +{ + LEFT = -1, RIGHT = 1, +}; + +enum mv_types +{ + MV_INTRA = 0, MV_INTER = 1, +}; + +enum diff_types +{ + TEMPORAL_DIFF = 0, SPATIAL_DIFF = 1, +}; + +enum motion_disparity +{ + MOTION = 0, DISPARITY = 1, +}; + +typedef struct inter_intra_concealment +{ + VideoParameters *p_Vid; + + StorablePicture *Destination; + StorablePicture *BlockConcealmentState; + + /*! Slices for the reference frames*/ + Slice *SliceDestiantion; + Slice *SliceIntraViewReference; + Slice *SliceInterViewReference; + Slice *SliceInterViewMinus1Reference; + + StorablePicture *IntraRefFrame; + StorablePicture *InterRefFrame; + StorablePicture *InterRefFrameMinus1; + + int IntraMapList[2][MAX_LIST_SIZE]; + MotionPropInfo IntraMVCandidates; + int MVExtrapolationRefIdx[2]; + + int InterMapList[2][MAX_LIST_SIZE]; + MotionPropInfo InterMVCandidates; + + MotionPropInfo InterIntraMVCandidates; + + /* Disparity Maps*/ + int DisparityMethod; + int DisparityDirection; + int DisparityWindow; + int DisparityReference; + StorablePicture *DisparityAuxMinus1; + StorablePicture *DisparityBaseMinus1; + StorablePicture *DisparityBase; + StorablePicture *DisparityAux; + + StorablePicture *DisparityBlockwiseBase; + StorablePicture *DisparityBlockwiseAux; + int MeanDisparityValue; + + StorablePicture *DisparityBlockwiseAuxMinus1; + + StorablePicture *TemporalDifference; + StorablePicture *SpatialDifference; + int DifferenceThresholds[2]; + + imgpel ***storeYUV, ***storeSeparatedYUV; + imgpel **tmp_storeYUV; + + int BlockSize; +} INTER_INTRA_CONC_T; + +const int BLOCK_IDX_MB[16][2] = { { 0, 0 }, { 1, 0 }, { 0, 1 }, { 1, 1 }, { 2, 0 }, { 3, 0 }, { 2, 1 }, { 3, 1 }, { 0, 2 }, { 1, 2 }, { 0, 3 }, { 1, 3 }, + { 2, 2 }, { 3, 2 }, { 2, 3 }, { 3, 3 } }; +const int CLUSTER[16] = { 16, 1, 1, 1, 4, 1, 1, 1, 4, 1, 1, 1, 4, 1, 1, 1 }; + +/*! + ************************************************************************ + * \brief + * Sort functions + ************************************************************************ + */ + +int sort_by_uint16_up( const void *arg1, const void *arg2 ) +{ + int32 n_0 = *( (uint16 *) arg1 ); + int32 n_1 = *( (uint16 *) arg2 ); + return ( n_0 - n_1 ); +} + +static int sort_imgpel_up( const void *arg1, const void *arg2 ) +{ + return (imgpel) ( *(imgpel *) arg1 ) - (imgpel) ( *(imgpel *) arg2 ); +} + +static void allocIntraInterConcData( VideoParameters *p_Vid, INTER_INTRA_CONC_T **pIntraInterConc, StorablePicture *Destination ) +{ + INTER_INTRA_CONC_T *IntraInterConc; + int i, j; + + IntraInterConc = ( *pIntraInterConc ) = (INTER_INTRA_CONC_T *) malloc( sizeof(INTER_INTRA_CONC_T) ); + + memset( IntraInterConc, 0, sizeof(INTER_INTRA_CONC_T) ); + + IntraInterConc->p_Vid = p_Vid; + IntraInterConc->Destination = Destination; + + IntraInterConc->BlockConcealmentState = alloc_storable_picture( p_Vid, FRAME, p_Vid->width / BLOCK_SIZE, p_Vid->height / BLOCK_SIZE, 0, 0, 1 ); + for( i = -p_Vid->iLumaPadX; i < p_Vid->width / BLOCK_SIZE + p_Vid->iLumaPadX; i++ ) + for( j = -p_Vid->iLumaPadY; j < ( p_Vid->height / BLOCK_SIZE + p_Vid->iLumaPadY ); j++ ) + IntraInterConc->BlockConcealmentState->imgY[j][i] = 0; + + IntraInterConc->TemporalDifference = alloc_storable_picture( p_Vid, FRAME, p_Vid->width, p_Vid->height, 0, 0, 1 ); + IntraInterConc->SpatialDifference = alloc_storable_picture( p_Vid, FRAME, p_Vid->width, p_Vid->height, 0, 0, 1 ); + + if( Destination->chroma_format_idc != YUV400 ) + get_mem3Dpel( &( IntraInterConc->storeYUV ), 2, 2, ( ( MB_BLOCK_SIZE * MB_BLOCK_SIZE ) + ( p_Vid->mb_cr_size_x * p_Vid->mb_cr_size_y ) * 2 / 16 ) ); + else + get_mem3Dpel( &( IntraInterConc->storeYUV ), 2, 2, MB_BLOCK_SIZE * MB_BLOCK_SIZE ); + + if( Destination->chroma_format_idc != YUV400 ) + get_mem3Dpel( &( IntraInterConc->storeSeparatedYUV ), 3, MB_BLOCK_SIZE, MB_BLOCK_SIZE ); + else + get_mem3Dpel( &( IntraInterConc->storeSeparatedYUV ), 1, MB_BLOCK_SIZE, MB_BLOCK_SIZE ); + + get_mem2Dpel( &( IntraInterConc->tmp_storeYUV ), MB_BLOCK_SIZE, MB_BLOCK_SIZE ); + + IntraInterConc->IntraMVCandidates.mv_info = NULL; + IntraInterConc->IntraMVCandidates.mv_info_count = NULL; + + IntraInterConc->DisparityMethod = p_Vid->p_Inp->DisparityMethod; + IntraInterConc->DisparityWindow = p_Vid->p_Inp->DisparityWindow; + IntraInterConc->DisparityDirection = p_Vid->p_Inp->DisparityDirection; + IntraInterConc->DisparityReference = p_Vid->p_Inp->DisparityReference; + +} + +static void freeIntraInterConcData( VideoParameters *p_Vid, INTER_INTRA_CONC_T *IntraInterConc ) +{ + free_storable_picture( IntraInterConc->BlockConcealmentState ); + free_storable_picture( IntraInterConc->TemporalDifference ); + free_storable_picture( IntraInterConc->SpatialDifference ); + free_storable_picture( IntraInterConc->DisparityBlockwiseBase ); + free_storable_picture( IntraInterConc->DisparityBlockwiseAux ); + + if( IntraInterConc->DisparityAuxMinus1 ) + free_storable_picture( IntraInterConc->DisparityAuxMinus1 ); + + if( IntraInterConc->DisparityBaseMinus1 ) + free_storable_picture( IntraInterConc->DisparityBaseMinus1 ); + + if( IntraInterConc->DisparityBase ) + free_storable_picture( IntraInterConc->DisparityBase ); + + if( IntraInterConc->DisparityAux ) + free_storable_picture( IntraInterConc->DisparityAux ); + + if( IntraInterConc->IntraMVCandidates.mv_info ) + free_mem3Dmp( IntraInterConc->IntraMVCandidates.mv_info ); + if( IntraInterConc->IntraMVCandidates.mv_info_count ) + free_mem2Dint( IntraInterConc->IntraMVCandidates.mv_info_count ); + + if( IntraInterConc->InterMVCandidates.mv_info ) + free_mem3Dmp( IntraInterConc->InterMVCandidates.mv_info ); + if( IntraInterConc->InterMVCandidates.mv_info_count_lists ) + free_mem3Dint( IntraInterConc->InterMVCandidates.mv_info_count_lists ); + + free_mem3Dpel( IntraInterConc->storeYUV ); + free_mem3Dpel( IntraInterConc->storeSeparatedYUV ); + + free_mem2Dpel( IntraInterConc->tmp_storeYUV ); + + free_slice( IntraInterConc->SliceInterViewReference ); + free_slice( IntraInterConc->SliceIntraViewReference ); + free_slice( IntraInterConc->SliceInterViewMinus1Reference ); + + free( IntraInterConc ); +} + +static void initInterIntraReferenceLists( VideoParameters *p_Vid, INTER_INTRA_CONC_T *IntraInterConc, StorablePicture *Destination ) +{ + DecodedPictureBuffer *p_Dpb = p_Vid->p_Dpb_layer[Destination->view_id]; + Slice *pSliceIntraViewReference, *pSliceInterViewReference, *pSliceInterViewMinus1Reference; + StorablePicture *IntraRefFrame, *InterRefFrame, *InterRefFrameMinus1; + Slice *pSliceDestiantion = Destination->PicSlice; + + StorablePicture *temp_frame; + int idx, idx1, dest_idx, list, find; + + IntraRefFrame = IntraInterConc->IntraRefFrame; + InterRefFrame = IntraInterConc->InterRefFrame; + InterRefFrameMinus1 = IntraInterConc->InterRefFrameMinus1; + + //! Initialization of reference list for Intra-View Reference + pSliceIntraViewReference = IntraInterConc->SliceIntraViewReference = malloc_slice( p_Vid->p_Inp, p_Vid ); + + pSliceIntraViewReference->p_Vid = p_Vid; + pSliceIntraViewReference->p_Dpb = p_Vid->p_Dpb_layer[IntraRefFrame->view_id]; + + pSliceIntraViewReference->ThisPOC = IntraRefFrame->poc; + pSliceIntraViewReference->framepoc = IntraRefFrame->poc; + pSliceIntraViewReference->view_id = IntraRefFrame->view_id; + pSliceIntraViewReference->layer_id = IntraRefFrame->layer_id; + pSliceIntraViewReference->slice_type = IntraRefFrame->slice_type; + + pSliceIntraViewReference->listXsize[LIST_0] = IntraRefFrame->Conc_listXsize[LIST_0]; + pSliceIntraViewReference->listXsize[LIST_1] = IntraRefFrame->Conc_listXsize[LIST_1]; + + for( idx = 0; idx < pSliceIntraViewReference->listXsize[LIST_0]; idx++ ) + { + pSliceIntraViewReference->listX[LIST_0][idx] = IntraRefFrame->Conc_listX[LIST_0][idx]; + if( IntraRefFrame->Conc_listXInterviewPos[LIST_0] == idx ) + { + pSliceIntraViewReference->listX[LIST_0][idx] = fercGetPictureForConcealFromDpb( p_Vid->p_Dpb_layer[0], IntraRefFrame->poc, 0, EXACT, 1, NULL ); + if( !pSliceIntraViewReference->listX[LIST_0][idx] ) + pSliceIntraViewReference->listX[LIST_0][idx] = p_Vid->no_reference_picture; + } + } + for( idx = 0; idx < pSliceIntraViewReference->listXsize[LIST_1]; idx++ ) + { + pSliceIntraViewReference->listX[LIST_1][idx] = IntraRefFrame->Conc_listX[LIST_1][idx]; + if( IntraRefFrame->Conc_listXInterviewPos[LIST_1] == idx ) + { + pSliceIntraViewReference->listX[LIST_1][idx] = fercGetPictureForConcealFromDpb( p_Vid->p_Dpb_layer[0], IntraRefFrame->poc, 0, EXACT, 1, NULL ); + if( !pSliceIntraViewReference->listX[LIST_0][idx] ) + pSliceIntraViewReference->listX[LIST_0][idx] = p_Vid->no_reference_picture; + } + } + printf( "[FERC] Reference list for the Intra-view reference frame" ); + dump_ref_lists( pSliceIntraViewReference ); +#if PRINTREFLIST_JC + printf( "[FERC] Reference list for the Intra-view reference frame"); + dump_ref_lists( pSliceIntraViewReference); +#endif + if( InterRefFrame ) + { + //! Initialization of reference list for Inter-View Reference + pSliceInterViewReference = IntraInterConc->SliceInterViewReference = malloc_slice( p_Vid->p_Inp, p_Vid ); + + pSliceInterViewReference->p_Vid = p_Vid; + pSliceInterViewReference->p_Dpb = p_Vid->p_Dpb_layer[Destination->view_id]; + pSliceInterViewReference->ThisPOC = InterRefFrame->poc; + pSliceInterViewReference->framepoc = InterRefFrame->poc; + pSliceInterViewReference->view_id = InterRefFrame->view_id; + pSliceInterViewReference->layer_id = InterRefFrame->layer_id; + pSliceInterViewReference->slice_type = InterRefFrame->slice_type; + + pSliceInterViewReference->listXsize[LIST_0] = InterRefFrame->Conc_listXsize[LIST_0]; + pSliceInterViewReference->listXsize[LIST_1] = InterRefFrame->Conc_listXsize[LIST_1]; + + for( idx = 0; idx < pSliceInterViewReference->listXsize[LIST_0]; idx++ ) + { + pSliceInterViewReference->listX[LIST_0][idx] = InterRefFrame->Conc_listX[LIST_0][idx]; + } + for( idx = 0; idx < pSliceInterViewReference->listXsize[LIST_1]; idx++ ) + { + pSliceInterViewReference->listX[LIST_1][idx] = InterRefFrame->Conc_listX[LIST_1][idx]; + } + printf( "[FERC] Reference list for the Inter-view reference frame" ); + dump_ref_lists( pSliceInterViewReference ); +#if PRINTREFLIST_JC + printf( "[FERC] Reference list for the Inter-view reference frame"); + dump_ref_lists( pSliceInterViewReference); +#endif + } + + //! Initialization of reference list for Destination + pSliceDestiantion = IntraInterConc->SliceDestiantion = Destination->PicSlice; + + pSliceDestiantion->p_Vid = p_Vid; + pSliceDestiantion->p_Dpb = p_Vid->p_Dpb_layer[Destination->view_id]; + pSliceDestiantion->ThisPOC = Destination->poc; + pSliceDestiantion->framepoc = Destination->poc; + pSliceDestiantion->view_id = Destination->view_id; + pSliceDestiantion->layer_id = Destination->layer_id; + pSliceDestiantion->slice_type = IntraRefFrame->slice_type; + + pSliceDestiantion->listXsize[LIST_0] = pSliceDestiantion->listXsize[LIST_1] = 0; + +#if MVC_EXTENSION_ENABLE + if( InterRefFrame && Destination->view_id != BASE_VIEW_IDX ) + { + init_mvc_picture( pSliceDestiantion ); + } +#endif + + IntraInterConc->MVExtrapolationRefIdx[0] = -1; + IntraInterConc->MVExtrapolationRefIdx[1] = -1; + + for( list = 0; list <= 1; list++ ) + { + //! Intra View Reference List + dest_idx = 0; + for( idx = 0; idx < pSliceIntraViewReference->listXsize[list]; dest_idx++ ) + { + pSliceDestiantion->listX[list][dest_idx] = pSliceIntraViewReference->listX[list][idx]; + if( IntraRefFrame->Conc_listXInterviewPos[list] == idx ) + { + if( InterRefFrame ) + pSliceDestiantion->listX[list][dest_idx] = p_Dpb->fs_ilref[0]->frame; + else + pSliceDestiantion->listX[list][dest_idx] = p_Vid->no_reference_picture; + IntraInterConc->MVExtrapolationRefIdx[1] = idx; + } + else if( IntraInterConc->MVExtrapolationRefIdx[0] == -1 ) + { + pSliceDestiantion->listX[list][dest_idx] = IntraRefFrame; + IntraInterConc->MVExtrapolationRefIdx[0] = dest_idx; + continue; + } + IntraInterConc->IntraMapList[list][idx] = dest_idx; + idx++; + } + pSliceDestiantion->listXsize[list] = dest_idx; + for( idx = 0; idx < pSliceInterViewReference->listXsize[list]; idx++ ) + { + // pSliceDestiantion->listX[LIST_0][idx] + temp_frame = fercGetPictureForConcealFromDpb( p_Vid->p_Dpb_layer[Destination->layer_id], InterRefFrame->Conc_listX[list][idx]->poc, Destination->layer_id, + EXACT, 1, NULL ); + for( idx1 = 0, find = 0; idx1 < pSliceDestiantion->listXsize[list]; idx1++ ) + { + if( pSliceDestiantion->listX[list][idx1] == temp_frame ) + { + IntraInterConc->InterMapList[list][idx] = idx1; + find = 1; + break; + } + } + if( !find ) + { + IntraInterConc->InterMapList[list][idx] = dest_idx; + pSliceDestiantion->listX[list][dest_idx++] = temp_frame; + } + } + pSliceDestiantion->listXsize[list] = dest_idx; + } + printf( "[FERC] Reference list for the Destination frame" ); + dump_ref_lists( pSliceDestiantion ); +#if PRINTREFLIST_JC + printf( "[FERC] Reference list for the Destination frame"); + dump_ref_lists( pSliceDestiantion); +#endif + + //! Initialization of reference list for Inter-View Minus 1 Reference + pSliceInterViewMinus1Reference = IntraInterConc->SliceInterViewMinus1Reference = malloc_slice( p_Vid->p_Inp, p_Vid ); + + pSliceInterViewMinus1Reference->p_Vid = p_Vid; + pSliceInterViewMinus1Reference->p_Dpb = p_Vid->p_Dpb_layer[IntraRefFrame->view_id]; + + pSliceInterViewMinus1Reference->ThisPOC = InterRefFrameMinus1->poc; + pSliceInterViewMinus1Reference->framepoc = InterRefFrameMinus1->poc; + pSliceInterViewMinus1Reference->view_id = InterRefFrameMinus1->view_id; + pSliceInterViewMinus1Reference->layer_id = InterRefFrameMinus1->layer_id; + pSliceInterViewMinus1Reference->slice_type = InterRefFrameMinus1->slice_type; + + pSliceInterViewMinus1Reference->listXsize[LIST_0] = InterRefFrameMinus1->Conc_listXsize[LIST_0]; + pSliceInterViewMinus1Reference->listXsize[LIST_1] = InterRefFrameMinus1->Conc_listXsize[LIST_1]; + + for( idx = 0; idx < pSliceInterViewMinus1Reference->listXsize[LIST_0]; idx++ ) + { + pSliceInterViewMinus1Reference->listX[LIST_0][idx] = InterRefFrameMinus1->Conc_listX[LIST_0][idx]; + } + for( idx = 0; idx < pSliceInterViewMinus1Reference->listXsize[LIST_1]; idx++ ) + { + pSliceInterViewMinus1Reference->listX[LIST_1][idx] = InterRefFrameMinus1->Conc_listX[LIST_1][idx]; + } + printf( "[FERC] Reference list for the Inter-view reference minus 1 frame" ); + dump_ref_lists( pSliceInterViewMinus1Reference ); +#if PRINTREFLIST_JC + printf( "[FERC] Reference list for the Inter-view reference minus 1 frame"); + dump_ref_lists( pSliceInterViewMinus1Reference); +#endif +} + +static int findBlock_Overlap( int x_pel, int y_pel, int x_block, int y_block, int block_size ) +{ + int x_block_start, y_block_start, x_block_end, y_block_end, i, j, overlap; + + x_block_start = x_block * block_size; + x_block_end = ( x_block + 1 ) * block_size - 1; + + y_block_start = y_block * block_size; + y_block_end = ( y_block + 1 ) * block_size - 1; + + for( overlap = 0, i = 0; i < block_size; i++ ) + { + for( j = 0; j < block_size; j++ ) + { + if( ( y_pel + i ) >= y_block_start && ( y_pel + i ) <= y_block_end ) + if( ( x_pel + j ) >= x_block_start && ( x_pel + j ) <= x_block_end ) + overlap++; + } + } + return overlap; +} + +static int findBlock_Quadrant( int x_pel, int y_pel, int x_block, int y_block, int block_size ) +{ + int x_block_start, y_block_start, x_block_end, y_block_end, quadrant; + + x_block_start = x_block * block_size; + x_block_end = ( x_block + 1 ) * block_size - 1; + + y_block_start = y_block * block_size; + y_block_end = ( y_block + 1 ) * block_size - 1; + + if( x_pel < x_block_start || x_pel > x_block_end || y_pel < y_block_start || y_pel > y_block_end ) + { + printf( "[FERC] Pixel (%d,%d) do not belong to the block (%d,%d)\n", x_pel, y_pel, x_block, y_block ); + return 0; + } + + quadrant = 1; + if( x_pel > x_block_end - ( block_size / 2 ) ) + quadrant += 1; + + if( y_pel > y_block_end - ( block_size / 2 ) ) + quadrant += 2; + + return quadrant; +} + +/*! + *********************************************************************** + * \brief + * Get the MV candidates for the different concealment strategies + *********************************************************************** + */ +void getINTERMotionVectorCandidates( VideoParameters *p_Vid, StorablePicture *Source, Slice *SourceSlice, StorablePicture *Destination, + StorablePicture *Disparity4x4, MotionPropInfo *Matrix_MV_Info, int Choose_Method ) +{ + const int Max_Candidates = 10; + + PicMotionParams ***mv_info; + int searched_mv[2], ***mv_info_count, ***mv_info_overlap; + int mv_info_count_i; + int Disparity; + StorablePicture *currRef; + int y_block, x_block, y, x, y_final_block, x_final_block, y_final, x_final; + int y_pos, x_pos; + int StartList, List, mv[3]; + float scale_factor; + StorablePicture *RefFrameMotionSearch; + + int DispDir = p_Vid->p_Inp->DisparityDirection; + + Matrix_MV_Info->mv_info_count = 0; + + get_mem3Dmp( &( mv_info ), ( Source->size_y >> BLOCK_SHIFT ), ( Source->size_x >> BLOCK_SHIFT ), Max_Candidates ); + get_mem3Dint( &mv_info_overlap, ( Source->size_y >> BLOCK_SHIFT ), ( Source->size_x >> BLOCK_SHIFT ), Max_Candidates ); + + get_mem3Dint( &mv_info_count, ( Source->size_y >> BLOCK_SHIFT ), ( Source->size_x >> BLOCK_SHIFT ), 2 ); + + memset( mv_info_count[0][0], -1, sizeof(int) * 2 * ( Source->size_y >> BLOCK_SHIFT ) * ( Source->size_x >> BLOCK_SHIFT ) ); + + StartList = SourceSlice->slice_type == B_SLICE ? LIST_1 : LIST_0; + + // StartList = LIST_0; + + //showStorablePicture(p_Vid, RefFrameMotionSearch, 0, 0, "Reference for search", 1); + //showStorablePicture(p_Vid, Source, 0, 0, "Source", 0); + + for( List = StartList; List >= LIST_0; List-- ) + { + memset( mv_info_overlap[0][0], 0, sizeof(int) * ( Source->size_y >> BLOCK_SHIFT ) * ( Source->size_x >> BLOCK_SHIFT ) * Max_Candidates ); + /* Loop though all block 4x4 in source */ + for( y_block = 0; y_block < ( Source->PicSizeInMbs / Source->PicWidthInMbs ) * ( MB_BLOCK_SIZE / BLOCK_SIZE ); y_block++ ) + { + y = y_block * BLOCK_SIZE; + for( x_block = 0; x_block < ( Source->PicWidthInMbs ) * ( MB_BLOCK_SIZE / BLOCK_SIZE ); x_block++ ) + { + x = x_block * BLOCK_SIZE; + + if( Source->mv_info[y_block][x_block].ref_idx[List] >= 0 ) + { + /* Update mv information - Read Reference Values */ + mv[0] = Source->mv_info[y_block][x_block].mv[List].mv_x; + mv[1] = Source->mv_info[y_block][x_block].mv[List].mv_y; + mv[2] = Source->mv_info[y_block][x_block].ref_idx[List]; + + currRef = SourceSlice->listX[List][mv[2]]; + + /* Project the Temporal vectors to the other view (auxiliary view) */ + //! Pixel destination in auxiliary view + Disparity = Disparity4x4->imgY[y_block][x_block]; + //x_final = iClip3(0, Source->size_x - 1, ( x + DispDir * Disparity ) ); + x_final = x + DispDir * Disparity; + if( x_final < 0 ) + continue; + + y_final = y; + + //! Ajustments to cope with negative values + x_final_block = x_final < 0 ? ( x_final + 1 ) / 4 : x_final / 4; + y_final_block = y_final < 0 ? ( y_final + 1 ) / 4 : y_final / 4; + + y_final_block = y_final < 0 ? y_final_block - 1 : y_final_block; + x_final_block = x_final < 0 ? x_final_block - 1 : x_final_block; + + //! Check the Quadrant + switch( findBlock_Quadrant( x_final, y_final, x_final_block, y_final_block, BLOCK_SIZE ) ) + { + case 0: + continue; + case 1: + break; + case 2: + x_final_block += 1; + break; + case 3: + y_final_block += 1; + break; + case 4: + y_final_block += 1; + x_final_block += 1; + break; + default: + continue; + } + + //! Check the image boundaries + if( y_final_block < 0 || y_final_block >= ( Source->PicSizeInMbs / Source->PicWidthInMbs ) * ( MB_BLOCK_SIZE / BLOCK_SIZE ) ) + continue; + if( x_final_block < 0 || x_final_block >= ( Source->PicWidthInMbs ) * ( MB_BLOCK_SIZE / BLOCK_SIZE ) ) + continue; + + //! Check for previous vectors associated with the current final block + if( mv_info_count[y_final_block][x_final_block][List] == -1 ) + mv_info_count[y_final_block][x_final_block][List] = 0; + + mv_info_count_i = mv_info_count[y_final_block][x_final_block][List]; + + if( mv_info_count_i >= 10 ) + continue; + + //! Associate the vector to the block + mv_info[y_final_block][x_final_block][mv_info_count_i].mv[List].mv_x = mv[0]; + mv_info[y_final_block][x_final_block][mv_info_count_i].mv[List].mv_y = mv[1]; + mv_info[y_final_block][x_final_block][mv_info_count_i].ref_idx[List] = mv[2]; + + //! Find block overlap + mv_info_overlap[y_final_block][x_final_block][mv_info_count_i] = findBlock_Overlap( x_final, y_final, x_final_block, y_final_block, BLOCK_SIZE ); + + mv_info_count[y_final_block][x_final_block][List]++; + } + } + } + + /* Loop though all block 4x4 in destination to choose the best */ + for( y_block = 0; y_block < ( Source->PicSizeInMbs / Source->PicWidthInMbs ) * ( MB_BLOCK_SIZE / BLOCK_SIZE ); y_block++ ) + { + y = y_block * BLOCK_SIZE; + for( x_block = 0; x_block < ( Source->PicWidthInMbs ) * ( MB_BLOCK_SIZE / BLOCK_SIZE ); x_block++ ) + { + x = x_block * BLOCK_SIZE; + + mv_info_count_i = mv_info_count[y_block][x_block][List]; + if( mv_info_count_i >= 0 ) + { + if( Choose_Method == 1 ) + { + //Most overlapped + mv_info_count_i = i_max_position( mv_info_overlap[y_block][x_block], mv_info_count_i, NULL, NULL ); + mv_info_count[y_block][x_block][List] = mv_info_count_i; + } + } + } + } + } + free_mem3Dint( mv_info_overlap ); + + ( *Matrix_MV_Info ).mv_info = mv_info; + ( *Matrix_MV_Info ).mv_info_count_lists = mv_info_count; + + return; +} + +void getINTRAMotionVectorCandidates( VideoParameters *p_Vid, StorablePicture *Source, Slice *SourceSlice, StorablePicture *Destination, + MotionPropInfo *Matrix_MV_Info, StorablePicture *MotionPicture, int Choose_Method, int UseDisparity ) +{ + const int Max_Candidates = 10; + + PicMotionParams ***mv_info; + int searched_mv[2], **mv_info_count, ***mv_info_overlap; + int mv_info_count_i, i; + + StorablePicture *currRef; + int y_block, x_block, y, x, y_final_block, x_final_block, y_final, x_final; + int y_pos, x_pos; + int StartList, List, mv[3]; + float scale_factor; + StorablePicture *RefFrameMotionSearch; + + Matrix_MV_Info->mv_info_count_lists = 0; + + get_mem3Dmp( &( mv_info ), ( Source->size_y >> BLOCK_SHIFT ), ( Source->size_x >> BLOCK_SHIFT ), Max_Candidates ); + get_mem3Dint( &mv_info_overlap, ( Source->size_y >> BLOCK_SHIFT ), ( Source->size_x >> BLOCK_SHIFT ), Max_Candidates ); + + mv_info_count = new_mem2Dint( ( Source->size_y >> BLOCK_SHIFT ), ( Source->size_x >> BLOCK_SHIFT ) ); + + memset( &( mv_info_count[0][0] ), -1, sizeof(int) * ( Source->size_y >> BLOCK_SHIFT ) * ( Source->size_x >> BLOCK_SHIFT ) ); + + StartList = SourceSlice->slice_type == B_SLICE ? LIST_1 : LIST_0; + + for( i = 0; i < SourceSlice->listXsize[LIST_0]; i++ ) + { + if( SourceSlice->listX[LIST_0][i]->view_id == Source->view_id ) + { + RefFrameMotionSearch = SourceSlice->listX[LIST_0][i]; + break; + } + } + + //showStorablePicture(p_Vid, RefFrameMotionSearch, 0, 0, "Reference for search", 1); + //showStorablePicture(p_Vid, Source, 0, 0, "Source", 0); + + /* Loop though all block 4x4 in source */ + for( y_block = 0; y_block < ( Source->PicSizeInMbs / Source->PicWidthInMbs ) * ( MB_BLOCK_SIZE / BLOCK_SIZE ); y_block++ ) + { + y = y_block * BLOCK_SIZE; + for( x_block = 0; x_block < ( Source->PicWidthInMbs ) * ( MB_BLOCK_SIZE / BLOCK_SIZE ); x_block++ ) + { + x = x_block * BLOCK_SIZE; + + for( List = StartList; List >= LIST_0; List-- ) + { + if( Source->mv_info[y_block][x_block].ref_idx[List] >= 0 ) + { + /* Update mv information - Read Reference Values */ + mv[0] = Source->mv_info[y_block][x_block].mv[List].mv_x; + mv[1] = Source->mv_info[y_block][x_block].mv[List].mv_y; + mv[2] = Source->mv_info[y_block][x_block].ref_idx[List]; + + currRef = SourceSlice->listX[List][mv[2]]; + /* Disparity Vectors */ + if( SourceSlice->view_id != 0 && currRef->poc == Source->poc ) + { + if( !UseDisparity ) + continue; + + //! Pixel destination + x_final = x; + y_final = y; + + //x_pos = imax( x - 6, 0 ); + //y_pos = imax( y - 6, 0 ); + + //MotionSearchDynamic( RefFrameMotionSearch->imgY[0], RefFrameMotionSearch->size_x, &(Source->imgY[y_pos][x_pos]), Source->size_x, searched_mv, x_pos, y_pos, Source->size_x, Source->size_y, 16, 32); + ////blockMatch16x16SSE4( RefFrameMotionSearch->imgY[0], RefFrameMotionSearch->size_x, &(Source->imgY[y_pos][x_pos]), Source->size_x, searched_mv, Source->size_x, Source->size_y, 16); + + ////! vector = pos_final - pos_inicial + //searched_mv[0] = searched_mv[0] - x_pos; + //searched_mv[1] = searched_mv[1] - y_pos; + + //if( searched_mv[0] == 16 || searched_mv[1] == 16 ) + //{ + // searched_mv[0] = searched_mv[1] = 0; + //} + + ////!Pixel destination + //x_final = ( x - searched_mv[0] ); + //y_final = ( y - searched_mv[1] ); + + mv[2] = 1; + + } + /* Temporal vectors */ + else + { + // Scale mv + scale_factor = (double) ( Destination->poc - Source->poc ) / (double) ( Source->poc - currRef->poc ); + mv[0] = (int) ( (float) ( mv[0] ) * scale_factor ); + mv[1] = (int) ( (float) ( mv[1] ) * scale_factor ); + + //! Quarter pixel destination + x_final = ( x * 4 - mv[0] ); + y_final = ( y * 4 - mv[1] ); + + //! Pixel destination +#define FINAL_PIXEL 2 +#if( FINAL_PIXEL == 1 ) + x_final = x_final/4 + ((x_final&0x03) && (x_final&0x01)); + y_final = y_final/4 + ((y_final&0x03) && (y_final&0x01)); +#endif +#if( FINAL_PIXEL == 2 ) + x_final = x_final / 4 + ( ( x_final & 0x02 ) >> 1 ); + y_final = y_final / 4 + ( ( y_final & 0x02 ) >> 1 ); +#endif +#if( FINAL_PIXEL == 3 ) + x_final = x_final/4; + y_final = y_final/4; +#endif + + mv[2] = 0; + } + + //! Ajustments to cope with negative values + x_final_block = x_final < 0 ? ( x_final + 1 ) / 4 : x_final / 4; + y_final_block = y_final < 0 ? ( y_final + 1 ) / 4 : y_final / 4; + + y_final_block = y_final < 0 ? y_final_block - 1 : y_final_block; + x_final_block = x_final < 0 ? x_final_block - 1 : x_final_block; + + switch( findBlock_Quadrant( x_final, y_final, x_final_block, y_final_block, BLOCK_SIZE ) ) + { + case 0: + continue; + case 1: + break; + case 2: + x_final_block += 1; + break; + case 3: + y_final_block += 1; + break; + case 4: + y_final_block += 1; + x_final_block += 1; + break; + default: + continue; + } + + if( y_final_block < 0 || y_final_block >= ( Source->PicSizeInMbs / Source->PicWidthInMbs ) * ( MB_BLOCK_SIZE / BLOCK_SIZE ) ) + continue; + if( x_final_block < 0 || x_final_block >= ( Source->PicWidthInMbs ) * ( MB_BLOCK_SIZE / BLOCK_SIZE ) ) + continue; + + if( mv_info_count[y_final_block][x_final_block] == -1 ) + mv_info_count[y_final_block][x_final_block] = 0; + + mv_info_count_i = mv_info_count[y_final_block][x_final_block]; + + if( mv_info_count_i >= 10 ) + continue; + + mv_info[y_final_block][x_final_block][mv_info_count_i].mv[LIST_0].mv_x = mv[0]; + mv_info[y_final_block][x_final_block][mv_info_count_i].mv[LIST_0].mv_y = mv[1]; + mv_info[y_final_block][x_final_block][mv_info_count_i].ref_idx[LIST_0] = mv[2]; + + mv_info_overlap[y_final_block][x_final_block][mv_info_count_i] = findBlock_Overlap( x_final, y_final, x_final_block, y_final_block, BLOCK_SIZE ); + + mv_info_count[y_final_block][x_final_block]++; + } + } + } + } + + /* Loop though all block 4x4 in destination to choose the best */ + for( y_block = 0; y_block < ( Source->PicSizeInMbs / Source->PicWidthInMbs ) * ( MB_BLOCK_SIZE / BLOCK_SIZE ); y_block++ ) + { + y = y_block * BLOCK_SIZE; + for( x_block = 0; x_block < ( Source->PicWidthInMbs ) * ( MB_BLOCK_SIZE / BLOCK_SIZE ); x_block++ ) + { + x = x_block * BLOCK_SIZE; + + mv_info_count_i = mv_info_count[y_block][x_block]; + + if( mv_info_count_i >= 0 ) + { + if( mv_info_count_i > 1 ) + { + if( Choose_Method == 1 ) + { + //Most overlaped + mv_info_count_i = i_max_position( mv_info_overlap[y_block][x_block], mv_info_count_i, NULL, NULL ); + mv_info_count[y_block][x_block] = mv_info_count_i; + } + else if( Choose_Method == 2 ) + { + double sum_x = 0, sum_y = 0, sum_over = 0; + int ref_flag; + if( mv_info_count_i == 1 ) + { + ref_flag = mv_info[y_block][x_block][0].ref_idx[LIST_0]; + sum_x += (double) mv_info[y_block][x_block][0].mv[LIST_0].mv_x; + sum_y += (double) mv_info[y_block][x_block][0].mv[LIST_0].mv_y; + } + else + { + ref_flag = 0; + for( i = 0; i < mv_info_count_i; i++ ) + { + if( ref_flag == mv_info[y_block][x_block][0].ref_idx[LIST_0] ) + { + //sum_x += (double)mv_info[y_block][x_block][i].mv[LIST_0].mv_x * (double)mv_info_overlap[y_block][x_block][i]; + //sum_y += (double)mv_info[y_block][x_block][i].mv[LIST_0].mv_y * (double)mv_info_overlap[y_block][x_block][i]; + //sum_over += (double)(mv_info_overlap[y_block][x_block][i]); + if( mv_info[y_block][x_block][i].mv[LIST_0].mv_x == 0 && mv_info[y_block][x_block][i].mv[LIST_0].mv_y == 0 ) + continue; + + sum_x += (double) mv_info[y_block][x_block][i].mv[LIST_0].mv_x; + sum_y += (double) mv_info[y_block][x_block][i].mv[LIST_0].mv_y; + sum_over += (double) ( 1 ); + + } + } + } + sum_over = sum_over ? sum_over : 1; + mv_info[y_block][x_block][0].mv[LIST_0].mv_x = (short) ( sum_x / sum_over ); + mv_info[y_block][x_block][0].mv[LIST_0].mv_y = (short) ( sum_y / sum_over ); + mv_info[y_block][x_block][0].ref_idx[LIST_0] = ref_flag; + + mv_info_count_i = 0; + mv_info_count[y_block][x_block] = mv_info_count_i; + } + // Motion vector average + else if( Choose_Method == 3 ) + { + double sum_x = 0, sum_y = 0, sum_over = 0; + int disp_flag = mv_info[y_block][x_block][0].ref_idx[LIST_0] == 1; + for( i = 0; i < mv_info_count_i; i++ ) + { + if( mv_info[y_block][x_block][0].ref_idx[LIST_0] == disp_flag ) + { + sum_x += (double) mv_info[y_block][x_block][i].mv[LIST_0].mv_x * (double) mv_info_overlap[y_block][x_block][i]; + sum_y += (double) mv_info[y_block][x_block][i].mv[LIST_0].mv_y * (double) mv_info_overlap[y_block][x_block][i]; + sum_over += (double) ( mv_info_overlap[y_block][x_block][i] ); + } + } + sum_over = sum_over ? sum_over : 1; + mv_info[y_block][x_block][0].mv[LIST_0].mv_x = (short) ( sum_x / sum_over ); + mv_info[y_block][x_block][0].mv[LIST_0].mv_y = (short) ( sum_y / sum_over ); + mv_info[y_block][x_block][0].ref_idx[LIST_0] = disp_flag; + + mv_info_count_i = 0; + } + // Motion vector weighted average using the predominant prediction + else if( Choose_Method == 4 ) + { + double sum_x = 0, sum_y = 0, sum_over = 0; + int disp_flag = 0; + for( i = 0; i < mv_info_count_i; i++ ) + { + if( mv_info[y_block][x_block][0].ref_idx[LIST_0] == 1 ) + { + disp_flag++; + } + else + { + disp_flag--; + } + } + disp_flag = disp_flag > 0 ? 1 : 0; + for( i = 0; i < mv_info_count_i; i++ ) + { + if( mv_info[y_block][x_block][0].ref_idx[LIST_0] == disp_flag ) + { + sum_x += (double) mv_info[y_block][x_block][i].mv[LIST_0].mv_x * (double) mv_info_overlap[y_block][x_block][i]; + sum_y += (double) mv_info[y_block][x_block][i].mv[LIST_0].mv_y * (double) mv_info_overlap[y_block][x_block][i]; + sum_over += (double) ( mv_info_overlap[y_block][x_block][i] ); + } + } + sum_over = sum_over ? sum_over : 1; + mv_info[y_block][x_block][0].mv[LIST_0].mv_x = (short) ( sum_x / sum_over ); + mv_info[y_block][x_block][0].mv[LIST_0].mv_y = (short) ( sum_y / sum_over ); + mv_info[y_block][x_block][0].ref_idx[LIST_0] = disp_flag; + + mv_info_count_i = 0; + mv_info_count[y_block][x_block] = mv_info_count_i; + } + //! 1. use the non-zero vectors; 2. Disparity vectors; 3. Zero vectors; + else if( Choose_Method == 5 ) + { + double sum_x = 0, sum_y = 0, sum_over = 0; + int disp_idx = 0; + + for( i = 0; i < mv_info_count_i; i++ ) + { + if( mv_info[y_block][x_block][i].ref_idx[LIST_0] == 0 ) + { + if( mv_info[y_block][x_block][i].mv[LIST_0].mv_x == 0 && mv_info[y_block][x_block][i].mv[LIST_0].mv_y == 0 ) + continue; + + sum_x += (double) mv_info[y_block][x_block][i].mv[LIST_0].mv_x; + sum_y += (double) mv_info[y_block][x_block][i].mv[LIST_0].mv_y; + sum_over += (double) ( 1 ); + } + } + if( sum_over ) + { + sum_x = sum_x / sum_over; + sum_y = sum_y / sum_over; + mv_info[y_block][x_block][0].mv[LIST_0].mv_x = (short) ( sum_x ); + mv_info[y_block][x_block][0].mv[LIST_0].mv_y = (short) ( sum_y ); + mv_info[y_block][x_block][0].ref_idx[LIST_0] = 0; + mv_info_count_i = 0; + continue; + } + + for( i = 0; i < mv_info_count_i; i++ ) + { + if( mv_info[y_block][x_block][0].ref_idx[LIST_0] == 1 ) + { + if( mv_info[y_block][x_block][i].mv[LIST_0].mv_x == 0 && mv_info[y_block][x_block][i].mv[LIST_0].mv_y == 0 ) + continue; + + sum_x += (double) mv_info[y_block][x_block][i].mv[LIST_0].mv_x; + sum_y += (double) mv_info[y_block][x_block][i].mv[LIST_0].mv_y; + sum_over += (double) ( 1 ); + } + } + if( sum_over ) + { + sum_x = sum_x / sum_over; + sum_y = sum_y / sum_over; + mv_info[y_block][x_block][0].mv[LIST_0].mv_x = (short) ( sum_x ); + mv_info[y_block][x_block][0].mv[LIST_0].mv_y = (short) ( sum_y ); + mv_info[y_block][x_block][0].ref_idx[LIST_0] = 1; + mv_info_count_i = 0; + continue; + } + mv_info[y_block][x_block][0].mv[LIST_0].mv_x = (short) ( 0 ); + mv_info[y_block][x_block][0].mv[LIST_0].mv_y = (short) ( 0 ); + mv_info[y_block][x_block][0].ref_idx[LIST_0] = 0; + mv_info_count_i = 0; + continue; + } + else + { + mv_info_count_i = 0; + } + } + else + { + mv_info_count_i = 0; + } + } + mv_info_count[y_block][x_block] = mv_info_count_i; + } + } + + free_mem3Dint( mv_info_overlap ); + + ( *Matrix_MV_Info ).mv_info = mv_info; + ( *Matrix_MV_Info ).mv_info_count = mv_info_count; + + return; +} + +void getINTERINTRAMotionVectorCandidates( VideoParameters *p_Vid, INTER_INTRA_CONC_T *IntraInterConc, MotionPropInfo *Matrix_MV_Info ) +{ + const int Max_Candidates = 10; + + PicMotionParams ***mv_info; + int searched_mv[2], **mv_info_count, ***mv_info_overlap; + int mv_info_count_i, i; + int UseDisparity = 1; + StorablePicture *currRef; + int y_block, x_block, y, x, y_final_block, x_final_block, y_final, x_final; + int y_pos, x_pos; + int StartList, List, mv[3]; + float scale_factor; + StorablePicture *RefFrameMotionSearch; + StorablePicture *Disparity4x4 = IntraInterConc->DisparityBlockwiseAuxMinus1; + int Disparity; + int Choose_Method = 1; + + StorablePicture *INTERViewReference = IntraInterConc->InterRefFrame; + StorablePicture *Destination = IntraInterConc->Destination; + Slice *SourceSliceAux = IntraInterConc->SliceIntraViewReference; + Slice *SourceSliceBase = IntraInterConc->SliceInterViewMinus1Reference; + StorablePicture *SourceAux = IntraInterConc->IntraRefFrame; + StorablePicture *SourceBase; + int DispDir = p_Vid->p_Inp->DisparityDirection; + + Matrix_MV_Info->mv_info_count_lists = 0; + + SourceBase = fercGetPictureForConcealFromDpb( p_Vid->p_Dpb_layer[INTERViewReference->view_id], SourceAux->poc, INTERViewReference->view_id, EXACT, 0, NULL ); + + get_mem3Dmp( &( mv_info ), ( SourceAux->size_y >> BLOCK_SHIFT ), ( SourceAux->size_x >> BLOCK_SHIFT ), Max_Candidates ); + get_mem3Dint( &mv_info_overlap, ( SourceAux->size_y >> BLOCK_SHIFT ), ( SourceAux->size_x >> BLOCK_SHIFT ), Max_Candidates ); + + mv_info_count = new_mem2Dint( ( SourceAux->size_y >> BLOCK_SHIFT ), ( SourceAux->size_x >> BLOCK_SHIFT ) ); + + memset( &( mv_info_count[0][0] ), -1, sizeof(int) * ( SourceAux->size_y >> BLOCK_SHIFT ) * ( SourceAux->size_x >> BLOCK_SHIFT ) ); + + StartList = SourceSliceAux->slice_type == B_SLICE ? LIST_1 : LIST_0; + + for( i = 0; i < SourceSliceAux->listXsize[LIST_0]; i++ ) + { + if( SourceSliceAux->listX[LIST_0][i]->view_id == SourceAux->view_id ) + { + RefFrameMotionSearch = SourceSliceAux->listX[LIST_0][i]; + break; + } + } + + // INTRA motion vector candidates + /* Loop though all block 4x4 in source */ + for( y_block = 0; y_block < ( SourceAux->PicSizeInMbs / SourceAux->PicWidthInMbs ) * ( MB_BLOCK_SIZE / BLOCK_SIZE ); y_block++ ) + { + y = y_block * BLOCK_SIZE; + for( x_block = 0; x_block < ( SourceAux->PicWidthInMbs ) * ( MB_BLOCK_SIZE / BLOCK_SIZE ); x_block++ ) + { + x = x_block * BLOCK_SIZE; + + for( List = StartList; List >= LIST_0; List-- ) + { + if( SourceAux->mv_info[y_block][x_block].ref_idx[List] >= 0 ) + { + /* Update mv information - Read Reference Values */ + mv[0] = SourceAux->mv_info[y_block][x_block].mv[List].mv_x; + mv[1] = SourceAux->mv_info[y_block][x_block].mv[List].mv_y; + mv[2] = SourceAux->mv_info[y_block][x_block].ref_idx[List]; + + currRef = SourceSliceAux->listX[List][mv[2]]; + /* Disparity Vectors */ + if( SourceSliceAux->view_id != 0 && currRef->poc == SourceAux->poc ) + { + if( !UseDisparity ) + continue; + + //! Pixel destination + x_final = x; + y_final = y; + + mv[2] = 1; + + } + /* Temporal vectors */ + else + { + // Scale mv + scale_factor = (double) ( Destination->poc - SourceAux->poc ) / (double) ( SourceAux->poc - currRef->poc ); + mv[0] = (int) ( (float) ( mv[0] ) * scale_factor ); + mv[1] = (int) ( (float) ( mv[1] ) * scale_factor ); + + //! Quarter pixel destination + x_final = ( x * 4 - mv[0] ); + y_final = ( y * 4 - mv[1] ); + + //! Pixel destination +#define FINAL_PIXEL 2 +#if( FINAL_PIXEL == 1 ) + x_final = x_final/4 + ((x_final&0x03) && (x_final&0x01)); + y_final = y_final/4 + ((y_final&0x03) && (y_final&0x01)); +#endif +#if( FINAL_PIXEL == 2 ) + x_final = x_final / 4 + ( ( x_final & 0x02 ) >> 1 ); + y_final = y_final / 4 + ( ( y_final & 0x02 ) >> 1 ); +#endif +#if( FINAL_PIXEL == 3 ) + x_final = x_final/4; + y_final = y_final/4; +#endif + + mv[2] = 0; + } + + //! Ajustments to cope with negative values + x_final_block = x_final < 0 ? ( x_final + 1 ) / 4 : x_final / 4; + y_final_block = y_final < 0 ? ( y_final + 1 ) / 4 : y_final / 4; + + y_final_block = y_final < 0 ? y_final_block - 1 : y_final_block; + x_final_block = x_final < 0 ? x_final_block - 1 : x_final_block; + + switch( findBlock_Quadrant( x_final, y_final, x_final_block, y_final_block, BLOCK_SIZE ) ) + { + case 0: + continue; + case 1: + break; + case 2: + x_final_block += 1; + break; + case 3: + y_final_block += 1; + break; + case 4: + y_final_block += 1; + x_final_block += 1; + break; + default: + continue; + } + + if( y_final_block < 0 || y_final_block >= ( SourceAux->PicSizeInMbs / SourceAux->PicWidthInMbs ) * ( MB_BLOCK_SIZE / BLOCK_SIZE ) ) + continue; + if( x_final_block < 0 || x_final_block >= ( SourceAux->PicWidthInMbs ) * ( MB_BLOCK_SIZE / BLOCK_SIZE ) ) + continue; + + if( mv_info_count[y_final_block][x_final_block] != -1 ) + continue; + + if( mv_info_count[y_final_block][x_final_block] == -1 ) + mv_info_count[y_final_block][x_final_block] = 0; + + mv_info_count_i = mv_info_count[y_final_block][x_final_block]; + + if( mv_info_count_i >= 10 ) + continue; + + mv_info[y_final_block][x_final_block][mv_info_count_i].mv[LIST_0].mv_x = mv[0]; + mv_info[y_final_block][x_final_block][mv_info_count_i].mv[LIST_0].mv_y = mv[1]; + mv_info[y_final_block][x_final_block][mv_info_count_i].ref_idx[LIST_0] = mv[2]; + + mv_info_overlap[y_final_block][x_final_block][mv_info_count_i] = findBlock_Overlap( x_final, y_final, x_final_block, y_final_block, BLOCK_SIZE ); + + mv_info_count[y_final_block][x_final_block]++; + } + } + } + } + + // Get INTER candidates + for( List = StartList; List >= LIST_0; List-- ) + { + /* Loop though all block 4x4 in source */ + for( y_block = 0; y_block < ( SourceBase->PicSizeInMbs / SourceBase->PicWidthInMbs ) * ( MB_BLOCK_SIZE / BLOCK_SIZE ); y_block++ ) + { + y = y_block * BLOCK_SIZE; + for( x_block = 0; x_block < ( SourceBase->PicWidthInMbs ) * ( MB_BLOCK_SIZE / BLOCK_SIZE ); x_block++ ) + { + x = x_block * BLOCK_SIZE; + + Disparity = Disparity4x4->imgY[y_block][x_block]; + + x_final_block = x_block * BLOCK_SIZE + -1 * DispDir * Disparity; + x_final_block = x_final_block / BLOCK_SIZE; + + if( x_final_block < 0 || x_final_block > SourceBase->size_x / BLOCK_SIZE ) + continue; + + if( SourceBase->mv_info[y_block][x_final_block].ref_idx[List] >= 0 ) + { + /* Update mv information - Read Reference Values */ + mv[0] = SourceBase->mv_info[y_block][x_final_block].mv[List].mv_x; + mv[1] = SourceBase->mv_info[y_block][x_final_block].mv[List].mv_y; + mv[2] = SourceBase->mv_info[y_block][x_final_block].ref_idx[List]; + + currRef = SourceSliceBase->listX[List][mv[2]]; + + // Scale mv + scale_factor = (double) ( Destination->poc - SourceBase->poc ) / (double) ( SourceBase->poc - currRef->poc ); + mv[0] = (int) ( (float) ( mv[0] ) * scale_factor ); + mv[1] = (int) ( (float) ( mv[1] ) * scale_factor ); + + //! Quarter pixel destination + x_final = ( x * 4 - mv[0] ); + y_final = ( y * 4 - mv[1] ); + + //! Pixel destination +#define FINAL_PIXEL 2 +#if( FINAL_PIXEL == 1 ) + x_final = x_final/4 + ((x_final&0x03) && (x_final&0x01)); + y_final = y_final/4 + ((y_final&0x03) && (y_final&0x01)); +#endif +#if( FINAL_PIXEL == 2 ) + x_final = x_final / 4 + ( ( x_final & 0x02 ) >> 1 ); + y_final = y_final / 4 + ( ( y_final & 0x02 ) >> 1 ); +#endif +#if( FINAL_PIXEL == 3 ) + x_final = x_final/4; + y_final = y_final/4; +#endif + + mv[2] = 0; + + //! Ajustments to cope with negative values + x_final_block = x_final < 0 ? ( x_final + 1 ) / 4 : x_final / 4; + y_final_block = y_final < 0 ? ( y_final + 1 ) / 4 : y_final / 4; + + y_final_block = y_final < 0 ? y_final_block - 1 : y_final_block; + x_final_block = x_final < 0 ? x_final_block - 1 : x_final_block; + + switch( findBlock_Quadrant( x_final, y_final, x_final_block, y_final_block, BLOCK_SIZE ) ) + { + case 0: + continue; + case 1: + break; + case 2: + x_final_block += 1; + break; + case 3: + y_final_block += 1; + break; + case 4: + y_final_block += 1; + x_final_block += 1; + break; + default: + continue; + } + + if( y_final_block < 0 || y_final_block >= ( SourceAux->PicSizeInMbs / SourceAux->PicWidthInMbs ) * ( MB_BLOCK_SIZE / BLOCK_SIZE ) ) + continue; + if( x_final_block < 0 || x_final_block >= ( SourceAux->PicWidthInMbs ) * ( MB_BLOCK_SIZE / BLOCK_SIZE ) ) + continue; + + if( mv_info_count[y_final_block][x_final_block] == -1 ) + mv_info_count[y_final_block][x_final_block] = 0; + + mv_info_count_i = mv_info_count[y_final_block][x_final_block]; + + if( mv_info_count_i >= Max_Candidates ) + continue; + + mv_info[y_final_block][x_final_block][mv_info_count_i].mv[LIST_0].mv_x = mv[0]; + mv_info[y_final_block][x_final_block][mv_info_count_i].mv[LIST_0].mv_y = mv[1]; + mv_info[y_final_block][x_final_block][mv_info_count_i].ref_idx[LIST_0] = mv[2]; + + mv_info_overlap[y_final_block][x_final_block][mv_info_count_i] = findBlock_Overlap( x_final, y_final, x_final_block, y_final_block, BLOCK_SIZE ); + + mv_info_count[y_final_block][x_final_block]++; + + } + } + } + } + + Choose_Method = 2; + /* Loop though all block 4x4 in destination to choose the best */ + for( y_block = 0; y_block < ( SourceAux->PicSizeInMbs / SourceAux->PicWidthInMbs ) * ( MB_BLOCK_SIZE / BLOCK_SIZE ); y_block++ ) + { + y = y_block * BLOCK_SIZE; + for( x_block = 0; x_block < ( SourceAux->PicWidthInMbs ) * ( MB_BLOCK_SIZE / BLOCK_SIZE ); x_block++ ) + { + x = x_block * BLOCK_SIZE; + + mv_info_count_i = mv_info_count[y_block][x_block]; + + if( mv_info_count_i >= 0 ) + { + if( mv_info_count_i > 1 ) + { + if( Choose_Method == 1 ) + { + //Most overlaped + mv_info_count_i = i_max_position( mv_info_overlap[y_block][x_block], mv_info_count_i, NULL, NULL ); + mv_info_count[y_block][x_block] = mv_info_count_i; + } + else if( Choose_Method == 2 ) + { + double sum_x = 0, sum_y = 0, sum_over = 0; + int ref_flag; + if( mv_info_count_i == 1 ) + { + ref_flag = mv_info[y_block][x_block][0].ref_idx[LIST_0]; + sum_x += (double) mv_info[y_block][x_block][0].mv[LIST_0].mv_x; + sum_y += (double) mv_info[y_block][x_block][0].mv[LIST_0].mv_y; + } + else + { + ref_flag = mv_info[y_block][x_block][0].ref_idx[LIST_0]; + for( i = 0; i < mv_info_count_i; i++ ) + { + if( ref_flag == mv_info[y_block][x_block][i].ref_idx[LIST_0] ) + { + //sum_x += (double)mv_info[y_block][x_block][i].mv[LIST_0].mv_x * (double)mv_info_overlap[y_block][x_block][i]; + //sum_y += (double)mv_info[y_block][x_block][i].mv[LIST_0].mv_y * (double)mv_info_overlap[y_block][x_block][i]; + //sum_over += (double)(mv_info_overlap[y_block][x_block][i]); + if( mv_info[y_block][x_block][i].mv[LIST_0].mv_x == 0 && mv_info[y_block][x_block][i].mv[LIST_0].mv_y == 0 ) + continue; + + sum_x += (double) mv_info[y_block][x_block][i].mv[LIST_0].mv_x; + sum_y += (double) mv_info[y_block][x_block][i].mv[LIST_0].mv_y; + sum_over += (double) ( 1 ); + + } + } + } + sum_over = sum_over ? sum_over : 1; + mv_info[y_block][x_block][0].mv[LIST_0].mv_x = (short) ( sum_x / sum_over ); + mv_info[y_block][x_block][0].mv[LIST_0].mv_y = (short) ( sum_y / sum_over ); + mv_info[y_block][x_block][0].ref_idx[LIST_0] = ref_flag; + + mv_info_count_i = 0; + mv_info_count[y_block][x_block] = mv_info_count_i; + } + } + else + { + mv_info_count_i = 0; + } + } + mv_info_count[y_block][x_block] = mv_info_count_i; + } + } + + free_mem3Dint( mv_info_overlap ); + + ( *Matrix_MV_Info ).mv_info = mv_info; + ( *Matrix_MV_Info ).mv_info_count = mv_info_count; + + return; +} + +static int GetBest4x4Disparity( VideoParameters *p_Vid, StorablePicture *p_Input, StorablePicture *p_Output, double MeanDisparity, unsigned int y_block4x4, + unsigned int x_block4x4, int out_block_size, int disp_block_size ) +{ + imgpel *img_pel = (imgpel *) malloc( disp_block_size * disp_block_size * sizeof(imgpel) ); + int i, j, x_start, y_start, x_end, y_end, ii, n_elem; + unsigned int x, y; + double average_n; + + int return_value = 0; + //int max_disparity = 255 / p_Vid->p_Inp->DisparityWindow; + int max_disparity = 2 * MeanDisparity; + char MODE = 2; + + if( p_Output != NULL ) + { + if( MODE == 1 ) + { + cvResizeStorablePicture( p_Vid, p_Input, p_Output ); + } + else + { + for( y_block4x4 = 0; y_block4x4 < ( p_Input->PicSizeInMbs / p_Input->PicWidthInMbs ) * ( MB_BLOCK_SIZE / out_block_size ); y_block4x4++ ) + { + y = y_block4x4 * out_block_size; + for( x_block4x4 = 0; x_block4x4 < p_Input->PicWidthInMbs * ( MB_BLOCK_SIZE / out_block_size ); x_block4x4++ ) + { + x = x_block4x4 * out_block_size; + + x_start = iClip3( 0, p_Input->size_x - disp_block_size, x - ( ( disp_block_size - out_block_size ) / 2 ) ); + y_start = iClip3( 0, p_Input->size_y - disp_block_size, y - ( ( disp_block_size - out_block_size ) / 2 ) ); + + x_end = x_start + disp_block_size; + y_end = y_start + disp_block_size; + + ii = 0; + for( i = y_start; i < y_end; i++ ) + { + for( j = x_start; j < x_end; j++ ) + { + img_pel[ii++] = p_Input->imgY[i][j]; + } + } + + if( MODE == 2 ) + { + //! Average + for( ii = 0, n_elem = 0, average_n = 0; ii < disp_block_size * disp_block_size; ii++ ) + { + if( img_pel[ii] == 0 ) + continue; + if( img_pel[ii] >= max_disparity ) + continue; + + n_elem++; + average_n += img_pel[ii]; + } + if( n_elem ) + { + average_n = average_n / (double) ( n_elem ); + p_Output->imgY[y_block4x4][x_block4x4] = (imgpel) ( average_n ); + } + else + { + double d_1, d_2, d_3, d_a, d_n; + d_1 = d_2 = d_3 = d_a = d_n = 0; + if( y_block4x4 != 0 ) + { + d_1 = p_Output->imgY[y_block4x4 - 1][x_block4x4]; + d_n++; + } + if( x_block4x4 != 0 ) + { + d_2 = p_Output->imgY[y_block4x4][x_block4x4 - 1]; + d_n++; + } + if( y_block4x4 != 0 && x_block4x4 != 0 ) + { + d_3 = p_Output->imgY[y_block4x4][x_block4x4 - 1]; + d_n++; + } + if( d_n == 0 ) + { + p_Output->imgY[y_block4x4][x_block4x4] = MeanDisparity; + } + else + { + d_a = ( d_1 + d_2 + d_3 ) / 3.0; + p_Output->imgY[y_block4x4][x_block4x4] = d_a; + } + + } + } + else + { + //! Median + qsort( img_pel, disp_block_size * disp_block_size, sizeof(imgpel), sort_imgpel_up ); + p_Output->imgY[y_block4x4][x_block4x4] = ( img_pel[disp_block_size / 2] + img_pel[disp_block_size / 2 + 1] + 1 ) >> 1; + } + } + } + } + } + else + { + for( y = y_block4x4 * out_block_size, i = 0; y < ( y_block4x4 * out_block_size + out_block_size ); y++ ) + for( x = x_block4x4 * out_block_size; x < ( x_block4x4 * out_block_size + out_block_size ); x++ ) + img_pel[i++] = p_Input->imgY[y][x]; + + if( MODE == 2 ) + { + //! Average + for( ii = 0, n_elem = 0, average_n = 0; ii < out_block_size * out_block_size; ii++ ) + { + if( img_pel[ii] > THRESHOLD_DISP ) + continue; + + n_elem++; + average_n = average_n + ( ( img_pel[ii] - average_n ) / n_elem ); + } + return_value = (imgpel) ( average_n ); + } + else + { + //! Median + qsort( img_pel, out_block_size * out_block_size, sizeof(imgpel), sort_imgpel_up ); + return_value = ( img_pel[7] + img_pel[8] + 1 ) >> 1; + } + } + + free( img_pel ); + return return_value; +} + +int DisparityProjection( VideoParameters *p_Vid, StorablePicture *Ref_Disparity, StorablePicture *Ref_Picture, Slice *Ref_Slice, + StorablePicture **New_Disparity, int Block_Size, int ProjectedPOC, int method ) +{ + int y_block4x4, x_block4x4; + double scale_factor; + int mv[3], need_concealment = 1, mb_width, mb_height; + // Init variables + unsigned int y, x, idx_mb; + Macroblock *currMB; + imgpel *storeYUV, **tmp_block_pred; + int FinalList, List, N_mv_Concealment = 0; + + if( ( *New_Disparity ) == NULL ) + { + ( *New_Disparity ) = alloc_storable_picture( p_Vid, FRAME, p_Vid->width, p_Vid->height, p_Vid->width_cr, p_Vid->height_cr, 1 ); + ( *New_Disparity )->PicSizeInMbs = Ref_Disparity->PicSizeInMbs; + ( *New_Disparity )->PicWidthInMbs = Ref_Disparity->PicWidthInMbs; + ( *New_Disparity )->poc = ProjectedPOC; + ( *New_Disparity )->chroma_format_idc = Ref_Disparity->chroma_format_idc; + ( *New_Disparity )->pixel_factor = Ref_Disparity->pixel_factor; + } + + //! Alloc memory for temp blocks + get_mem2Dpel( &tmp_block_pred, MB_BLOCK_SIZE, MB_BLOCK_SIZE ); + if( ( *New_Disparity )->chroma_format_idc != YUV400 ) + { + //int get_mem3Dpel(imgpel ****array3D, int dim0, int dim1, int dim2) + get_mem1Dpel( &storeYUV, ( ( Block_Size * Block_Size ) + ( p_Vid->mb_cr_size_x * p_Vid->mb_cr_size_y ) * 2 / 16 ) ); + } + else + { + get_mem1Dpel( &storeYUV, Block_Size * Block_Size ); + } + + mb_width = ( *New_Disparity )->PicWidthInMbs; + mb_height = ( ( *New_Disparity )->PicSizeInMbs ) / ( ( *New_Disparity )->PicWidthInMbs ); + + if( method == 1 ) + { + /* Loop though all motion vectors */ + for( y_block4x4 = 0; y_block4x4 < mb_height * MB_BLOCK_SIZE / Block_Size; y_block4x4++ ) + { + y = y_block4x4 * Block_Size; + for( x_block4x4 = 0; x_block4x4 < mb_width * MB_BLOCK_SIZE / Block_Size; x_block4x4++ ) + { + need_concealment = 1; + + x = x_block4x4 * Block_Size; + + //! get MB - not exact - dont work for currSlice->mb_aff_frame_flag = 1 + idx_mb = ( y / MB_BLOCK_SIZE ) * mb_width + ( x / MB_BLOCK_SIZE ); + currMB = &( p_Vid->mb_data[idx_mb] ); + + currMB->p_Slice = Ref_Slice; + + // Update coordinates of the current concealed macroblock + currMB->mb.x = (short) ( x / Block_Size ); + currMB->mb.y = (short) ( y / Block_Size ); + currMB->block_y = currMB->mb.y * Block_Size; + currMB->pix_c_y = currMB->mb.y * p_Vid->mb_cr_size_y / 4; + currMB->block_x = currMB->mb.x * Block_Size; + currMB->pix_c_x = currMB->mb.x * p_Vid->mb_cr_size_x / 4; + + FinalList = Ref_Picture->slice_type == B_SLICE ? LIST_1 : LIST_0; + + for( List = LIST_0; List <= FinalList; List++ ) + { + mv[0] = Ref_Picture->mv_info[y_block4x4][x_block4x4].mv[List].mv_x; + mv[1] = Ref_Picture->mv_info[y_block4x4][x_block4x4].mv[List].mv_y; + mv[2] = Ref_Picture->mv_info[y_block4x4][x_block4x4].ref_idx[List]; //Reference Frame + + if( Ref_Picture->mv_info[y_block4x4][x_block4x4].ref_idx[List] >= 0 ) + { + scale_factor = (float) ( Ref_Picture->poc - Ref_Disparity->poc ) / (float) ( Ref_Picture->poc - Ref_Slice->listX[List][mv[2]]->poc ); + scale_factor = scale_factor ? scale_factor : 1; + mv[0] = (int) ( (float) ( mv[0] ) * scale_factor ); + mv[1] = (int) ( (float) ( mv[1] ) * scale_factor ); + + buildPredblockRegionYUV( p_Vid, Ref_Disparity, currMB, mv, x, y, storeYUV, Block_Size, tmp_block_pred, 1 ); + + ( *New_Disparity )->mv_info[y_block4x4][x_block4x4].mv[LIST_0].mv_x = mv[0]; + ( *New_Disparity )->mv_info[y_block4x4][x_block4x4].mv[LIST_0].mv_y = mv[1]; + ( *New_Disparity )->mv_info[y_block4x4][x_block4x4].ref_idx[List] = mv[2]; + + need_concealment = 0; + break; + } + } + if( need_concealment ) + { + N_mv_Concealment++; + //conceal motion vector + ConcealMotionVector( p_Vid, ( *New_Disparity ), Ref_Slice, y_block4x4, x_block4x4, AVERAGE, LIST_0, mv, 0 ); + // Scale mv + //Dont need scalling because the vectors have already been modified + //scale_factor = (float)( Source->poc - currRef->poc ) / (float)( Dest->poc - currRef->poc ); + scale_factor = 1; + mv[0] = (int) ( (float) ( mv[0] ) * scale_factor ); + mv[1] = (int) ( (float) ( mv[1] ) * scale_factor ); + + buildPredblockRegionYUV( p_Vid, Ref_Disparity, currMB, mv, x, y, storeYUV, Block_Size, tmp_block_pred, 1 ); + } + copyPredblockRegionYUVDestination( ( *New_Disparity ), storeYUV, x, y, BLOCK_SIZE ); + } + } + } + else if( method == 2 ) + { + MotionPropInfo Matrix_MV_Info; + int mv_info_count_i; + int temporal_ref_idx = -1; + + getINTRAMotionVectorCandidates( p_Vid, Ref_Picture, Ref_Slice, ( *New_Disparity ), &Matrix_MV_Info, NULL, 2, 0 ); + + for( y_block4x4 = 0; y_block4x4 < Ref_Slice->listXsize[LIST_0]; y_block4x4++ ) + { + if( Ref_Slice->listX[LIST_0][y_block4x4]->poc == Ref_Picture->poc ) + { + temporal_ref_idx = y_block4x4; + break; + } + } + + /* Loop though all motion vectors */ + for( y_block4x4 = 0; y_block4x4 < mb_height * MB_BLOCK_SIZE / Block_Size; y_block4x4++ ) + { + y = y_block4x4 * Block_Size; + for( x_block4x4 = 0; x_block4x4 < mb_width * MB_BLOCK_SIZE / Block_Size; x_block4x4++ ) + { + x = x_block4x4 * Block_Size; + + //! get MB - not exact - dont work for currSlice->mb_aff_frame_flag = 1 + idx_mb = ( y / MB_BLOCK_SIZE ) * mb_width + ( x / MB_BLOCK_SIZE ); + currMB = &( p_Vid->mb_data[idx_mb] ); + + currMB->p_Slice = Ref_Slice; + + // Update coordinates of the current concealed macroblock + currMB->mb.x = (short) ( x / Block_Size ); + currMB->mb.y = (short) ( y / Block_Size ); + currMB->block_y = currMB->mb.y * Block_Size; + currMB->pix_c_y = currMB->mb.y * p_Vid->mb_cr_size_y / 4; + currMB->block_x = currMB->mb.x * Block_Size; + currMB->pix_c_x = currMB->mb.x * p_Vid->mb_cr_size_x / 4; + + mv_info_count_i = Matrix_MV_Info.mv_info_count[y_block4x4][x_block4x4]; + + //get motion vector + if( mv_info_count_i >= 0 ) + { + //! improve this + mv[0] = Matrix_MV_Info.mv_info[y_block4x4][x_block4x4][mv_info_count_i].mv[LIST_0].mv_x; + mv[1] = Matrix_MV_Info.mv_info[y_block4x4][x_block4x4][mv_info_count_i].mv[LIST_0].mv_y; + + mv[2] = temporal_ref_idx; + } + else + { + N_mv_Concealment++; + //conceal motion vector + ConcealMotionVector( p_Vid, ( *New_Disparity ), Ref_Slice, y_block4x4, x_block4x4, ZERO_MV, LIST_0, mv, temporal_ref_idx ); + //Dont need scalling because the vectors have already been modified + } + // memset(storeYUV,0,((Block_Size*Block_Size) + (Block_Size*Block_Size/4)) * sizeof(imgpel)); + buildPredblockRegionYUV( p_Vid, Ref_Disparity, currMB, mv, x, y, storeYUV, Block_Size, tmp_block_pred, 1 ); + + ( *New_Disparity )->mv_info[y_block4x4][x_block4x4].mv[LIST_0].mv_x = mv[0]; + ( *New_Disparity )->mv_info[y_block4x4][x_block4x4].mv[LIST_0].mv_y = mv[1]; + ( *New_Disparity )->mv_info[y_block4x4][x_block4x4].ref_idx[LIST_0] = mv[2]; + + copyPredblockRegionYUVDestination( ( *New_Disparity ), storeYUV, x, y, BLOCK_SIZE ); + } + } + + if( Matrix_MV_Info.mv_info ) + free_mem3Dmp( Matrix_MV_Info.mv_info ); + + if( Matrix_MV_Info.mv_info ) + free_mem2Dint( Matrix_MV_Info.mv_info_count ); + + } + else + { + return 0; + } + //! Debug reports + printf( "[FERC] Number of Concealed mv %d for Disparity Projection\n", N_mv_Concealment ); + + free_mem2Dpel( tmp_block_pred ); + free_mem1Dpel( storeYUV ); + return 1; +} + +void getDisparityMap( VideoParameters *p_Vid, INTER_INTRA_CONC_T *IntraInterConc, StorablePicture *Reference, int SearchView, StorablePicture **ppDisparity ) +{ + StorablePicture *Dest = IntraInterConc->Destination; + StorablePicture *NonRefFrame; + StorablePicture *Disparity; + int DisparityDirection = 0; + int i, j, nElem; + double MeanDisp; + + if( ( *ppDisparity ) ) + { + return; + } + + NonRefFrame = fercGetPictureForConcealFromDpb( p_Vid->p_Dpb_layer[SearchView], Reference->poc, SearchView, EXACT, 0, NULL ); + + printf( "[FERC] Disparity measurement using frame %2d|%2d as reference\n", Reference->poc, Reference->view_id ); + + if( NonRefFrame->view_id > Reference->view_id ) + { + DisparityDirection = IntraInterConc->DisparityDirection; + } + else + { + DisparityDirection = ( -1 ) * IntraInterConc->DisparityDirection; + } + +#if( _DEBUG ) + //showStorablePicture( p_Vid , Reference , 0 , 0 , "Disparity Reference Frame", 0); + //showStorablePicture( p_Vid , NonRefFrame , 0 , 0 , "Disparity Non Reference Frame", 0); +#endif + + Disparity = ( *ppDisparity ) = alloc_storable_picture( p_Vid, FRAME, p_Vid->width, p_Vid->height, p_Vid->width_cr, p_Vid->height_cr, 1 ); + + cvDisparity( p_Vid, Reference, NonRefFrame, Disparity, IntraInterConc->DisparityMethod, IntraInterConc->DisparityWindow, DisparityDirection ); + + // if (p_Vid->p_Inp->DisparityMethod != 0 && p_Vid->p_Inp->DispLoadFileName[0] != 0) + // { + // saveStorablePicture(p_Vid, p_Vid->p_Inp->DispLoadFileName, Disparity, 0, 0); + // } + + Disparity->PicSizeInMbs = Dest->PicSizeInMbs; + Disparity->PicWidthInMbs = Dest->PicWidthInMbs; + Disparity->poc = Reference->poc; + Disparity->chroma_format_idc = 0; + Disparity->pixel_factor = IntraInterConc->DisparityWindow; + + // Normalize the disparity map + nElem = 0; + MeanDisp = 0; + for( i = 0; i < Disparity->size_x; i++ ) + { + for( j = 0; j < Disparity->size_y; j++ ) + { + Disparity->imgY[j][i] = (imgpel) ( (double) ( Disparity->imgY[j][i] ) / (double) ( IntraInterConc->DisparityWindow ) ); + MeanDisp = (double) ( MeanDisp * nElem + (double) ( Disparity->imgY[j][i] ) ) / (double) ( nElem + 1 ); + nElem++; + } + } + Disparity->mean_value = MeanDisp; + return; +} + +void getDisparityINTERConcealment( VideoParameters *p_Vid, INTER_INTRA_CONC_T *IntraInterConc ) +{ + StorablePicture *Dest = IntraInterConc->Destination; + StorablePicture *Disparity = NULL, *FinalDisparity = NULL, *DisparityBlockWise = NULL; + StorablePicture *RefProjFrame = NULL; + Slice *pSliceRefProjFrame = NULL; + int DisparityReference = IntraInterConc->DisparityReference; + + DisparityBlockWise = alloc_storable_picture( p_Vid, FRAME, p_Vid->width / BLOCK_SIZE, p_Vid->height / BLOCK_SIZE, p_Vid->width_cr / BLOCK_SIZE, + p_Vid->height_cr / BLOCK_SIZE, 1 ); + + if( DisparityReference == 0 ) + { + getDisparityMap( p_Vid, IntraInterConc, IntraInterConc->InterRefFrameMinus1, Dest->view_id, &( IntraInterConc->DisparityBaseMinus1 ) ); + Disparity = IntraInterConc->DisparityBaseMinus1; + RefProjFrame = IntraInterConc->InterRefFrame; + pSliceRefProjFrame = IntraInterConc->SliceInterViewReference; + IntraInterConc->DisparityBlockwiseBase = DisparityBlockWise; + } + else + { + getDisparityMap( p_Vid, IntraInterConc, IntraInterConc->IntraRefFrame, IntraInterConc->InterRefFrame->view_id, &( IntraInterConc->DisparityAuxMinus1 ) ); + Disparity = IntraInterConc->DisparityAuxMinus1; + RefProjFrame = IntraInterConc->IntraRefFrame; + pSliceRefProjFrame = IntraInterConc->SliceIntraViewReference; + IntraInterConc->DisparityBlockwiseAux = DisparityBlockWise; + } + + DisparityProjection( p_Vid, Disparity, RefProjFrame, pSliceRefProjFrame, &FinalDisparity, BLOCK_SIZE, Dest->poc, DisparityReference == 0 ? 1 : 2 ); + //showStorablePicture(p_Vid, IntraInterConc->InterRefFrame, 0, 0, "InterRefFrame", 0); + //GetBest4x4Disparity(p_Vid, Disparity, DisparityBlockWise, Disparity->mean_value, 0, 0, BLOCK_SIZE, p_Vid->p_Inp->Disparity4x4BlockSize); + GetBest4x4Disparity( p_Vid, FinalDisparity, DisparityBlockWise, Disparity->mean_value, 0, 0, BLOCK_SIZE, p_Vid->p_Inp->Disparity4x4BlockSize ); + + //showStorablePicture(p_Vid, Disparity, 0, 0, "Disparity", 0); + + free_storable_picture( FinalDisparity ); + + //cvLoadStorablePicture(p_Vid, "DisparityBlock.pgm", IntraInterConc->DisparityBlockwiseBase, 0, 0); + //showStorablePicture(p_Vid,IntraInterConc->DisparityBlockwiseBase,0,0,"DisparityBlock",0); + +} + +void getMVIntra( INTER_INTRA_CONC_T *IntraInterConc, int list, int y_block, int x_block, int *MV ) +{ + int intra_mv_cnt; + //! check INTRA mv candidate + if( ( intra_mv_cnt = IntraInterConc->IntraMVCandidates.mv_info_count[y_block][x_block] ) >= 0 ) + { + MV[0] = IntraInterConc->IntraMVCandidates.mv_info[y_block][x_block][intra_mv_cnt].mv[LIST_0].mv_x; + MV[1] = IntraInterConc->IntraMVCandidates.mv_info[y_block][x_block][intra_mv_cnt].mv[LIST_0].mv_y; + MV[2] = IntraInterConc->IntraMVCandidates.mv_info[y_block][x_block][intra_mv_cnt].ref_idx[LIST_0]; + MV[2] = IntraInterConc->MVExtrapolationRefIdx[MV[2]]; + } + else + { + MV[0] = MV[1] = 0; + MV[2] = -1; + } +} + +void getMVInterview( INTER_INTRA_CONC_T *IntraInterConc, int list, int y_block, int x_block, int *MV ) +{ + StorablePicture *Dest = IntraInterConc->Destination; + StorablePicture *DisparityBlockwise = IntraInterConc->DisparityBlockwiseAux; + int DisparityDirection = IntraInterConc->DisparityDirection; + int x, y; + + MV[0] = MV[1] = 0; + MV[2] = -1; + + if( list == LIST_1 ) + return; + + x = x_block * BLOCK_SIZE; + y = y_block * BLOCK_SIZE; + + if( x + ( -1 * DisparityDirection ) * (int) ( DisparityBlockwise->imgY[y_block][x_block] ) + != iClip3( 0, Dest->size_x - 1, x + ( -1 * DisparityDirection ) * (int) ( DisparityBlockwise->imgY[y_block][x_block] ) ) ) + { + return; + } + else + { + MV[0] = ( ( -1 * DisparityDirection ) * (int) ( DisparityBlockwise->imgY[y_block][x_block] ) ) * 4; + MV[1] = 0; + } + MV[2] = IntraInterConc->MVExtrapolationRefIdx[1]; + +} + +void getMVENHANCEDMVE( INTER_INTRA_CONC_T *IntraInterConc, int list, int y_block, int x_block, int *MV ) +{ + int intra_mv_cnt; + //! check INTRA mv candidate + if( ( intra_mv_cnt = IntraInterConc->InterIntraMVCandidates.mv_info_count[y_block][x_block] ) >= 0 ) + { + MV[0] = IntraInterConc->InterIntraMVCandidates.mv_info[y_block][x_block][intra_mv_cnt].mv[LIST_0].mv_x; + MV[1] = IntraInterConc->InterIntraMVCandidates.mv_info[y_block][x_block][intra_mv_cnt].mv[LIST_0].mv_y; + MV[2] = IntraInterConc->InterIntraMVCandidates.mv_info[y_block][x_block][intra_mv_cnt].ref_idx[LIST_0]; + MV[2] = IntraInterConc->MVExtrapolationRefIdx[MV[2]]; + } + else + { + MV[0] = MV[1] = 0; + MV[2] = -1; + } +} + +void getMVInter( INTER_INTRA_CONC_T *IntraInterConc, int list, int y_block, int x_block, int *MV ) +{ + StorablePicture *Disp = NULL; + StorablePicture *InterRefFrame = IntraInterConc->InterRefFrame; + MotionPropInfo InterMVCandidates = IntraInterConc->InterMVCandidates; + int DispDir = -1 * IntraInterConc->DisparityDirection; + int Width = IntraInterConc->p_Vid->width; + int inter_src_block, inter_mv_cnt; + + MV[0] = MV[1] = 0; + MV[2] = -1; + + switch( IntraInterConc->DisparityReference ) + { + case 0: + Disp = IntraInterConc->DisparityBlockwiseBase; + if( ( inter_mv_cnt = InterMVCandidates.mv_info_count_lists[y_block][x_block][list] ) >= 0 ) + { + MV[0] = InterMVCandidates.mv_info[y_block][x_block][inter_mv_cnt].mv[list].mv_x; + MV[1] = InterMVCandidates.mv_info[y_block][x_block][inter_mv_cnt].mv[list].mv_y; + MV[2] = InterMVCandidates.mv_info[y_block][x_block][inter_mv_cnt].ref_idx[list]; + MV[2] = MV[2] < 0 ? MV[2] : IntraInterConc->InterMapList[list][MV[2]]; + } + break; + case 1: + Disp = IntraInterConc->DisparityBlockwiseAux; + //! Get source base block based on pixel position and then + inter_src_block = iClip3( 0, Width, x_block * BLOCK_SIZE + DispDir * (int) ( Disp->imgY[y_block][x_block] ) ); + inter_src_block = inter_src_block / BLOCK_SIZE; // + (((inter_src_block%BLOCK_SIZE)&0x2)>>1); + + MV[0] = InterRefFrame->mv_info[y_block][inter_src_block].mv[list].mv_x; + MV[1] = InterRefFrame->mv_info[y_block][inter_src_block].mv[list].mv_y; + MV[2] = InterRefFrame->mv_info[y_block][inter_src_block].ref_idx[list]; + MV[2] = MV[2] < 0 ? MV[2] : IntraInterConc->InterMapList[list][MV[2]]; + break; + } +} + +static inline int target_cluster( int n ) +{ + switch( n ) + { + case 0: + return 1; + case 4: + return 4; + case 8: + return 16; + default: + return 0; + } +} + +int MotionSearchDynamic( imgpel* refFrame, int stepBytesRF /*stride*/, imgpel* curBlock, int stepBytesCB /*stride*/, int* matchBlock /*results*/, int x_pos, + int y_pos, int frameWidth, int frameHeight, int block_size, int search_window ) +{ + int i, j, k, l; + int lowSum = INT_MAX; + int temSum = 0; + int blockHeight = block_size; + int blockWidth = block_size; + const imgpel *pRef, *pCur; + int x_start, y_start, x_limit, y_limit; + + x_start = imax( 0, x_pos - search_window ); + x_limit = imin( frameWidth - blockWidth, x_pos + search_window ); + + y_start = imax( 0, y_pos - search_window ); + y_limit = imin( frameHeight - blockHeight, y_pos + search_window ); + + for( i = y_start; i <= y_limit; i++ ) + { + for( j = x_start; j <= x_limit; j++ ) + { + temSum = 0; + pCur = curBlock; + pRef = refFrame + i * stepBytesRF + j; + for( k = 0; k < blockHeight; k++ ) + { + for( l = 0; l < blockWidth; l++ ) + { + temSum += abs( *pRef - *pCur ); + pCur++; + pRef++; + } + pCur += stepBytesCB - blockWidth; + pRef += stepBytesRF - blockWidth; + } + if( temSum < lowSum ) + { + lowSum = temSum; + *matchBlock = j; + *( matchBlock + 1 ) = i; + } + } + } + return 0; +} + +//static void DisparityCompensation( VideoParameters *p_Vid, StorablePicture *Ref, StorablePicture *Output, StorablePicture *Disparity, int Direction ) +//{ +// int i, j; +// int disp = 0; +// for( i = 0; i < p_Vid->width; i++ ) +// { +// for( j = 0; j < p_Vid->height; j++ ) +// { +// disp = Direction * (int) ( Disparity->imgY[j][i] / Disparity->pixel_factor ); +// Output->imgY[j][i] = Ref->imgY[j][i + disp]; +// } +// } +// return; +//} + +void getDisparityMapForLostFrame( VideoParameters *p_Vid, StorablePicture *Dest, StorablePicture *IntraRefFrame, StorablePicture *RefProjFrame, + Slice *pSliceRefProjFrame, StorablePicture **ppDisparity, StorablePicture **ppDisparity4x4, int DisparityReference, int DisparityDirection, + int DisparityProjectionFlag ) +{ + ConcealParameters *p_FrameConc = p_Vid->p_FrameConc; + StorablePicture *AuxRefFrame, *BaseRefFrame; + StorablePicture *Ref_Disparity, *Final_Disparity; + StorablePicture *Disparity4x4; + int iRet, i, j; + int nElem; + double MeanDisp; + AuxRefFrame = IntraRefFrame; + //fercGetPictureForConcealFromDpb(p_Vid->p_Dpb_layer[1], Dest->poc, 1, CLOSEST_FUTURE, 0, NULL); + BaseRefFrame = fercGetPictureForConcealFromDpb( p_Vid->p_Dpb_layer[0], AuxRefFrame->poc, 0, EXACT, 0, NULL ); + + Final_Disparity = ( *ppDisparity ) = alloc_storable_picture( p_Vid, FRAME, p_Vid->width, p_Vid->height, p_Vid->width_cr, p_Vid->height_cr, 1 ); + + //if( !AuxRefFrame->disparity ) + { +#if( _DEBUG ) + //showStorablePicture( p_Vid , BaseRefFrame , 0 , 0 , "Base", 0); + //showStorablePicture( p_Vid , AuxRefFrame , 0 , 0 , "Auxiliary", 0); +#endif + + if( DisparityReference == 0 ) + { + if( BaseRefFrame->disparity ) + { + Ref_Disparity = BaseRefFrame->disparity; + } + else + { + printf( "[FERC] Disparity measurement using frame %2d|%2d as reference\n", BaseRefFrame->poc, BaseRefFrame->view_id ); + + Ref_Disparity = BaseRefFrame->disparity = alloc_storable_picture( p_Vid, FRAME, p_Vid->width, p_Vid->height, p_Vid->width_cr, p_Vid->height_cr, 1 ); + cvDisparity( p_Vid, BaseRefFrame, AuxRefFrame, Ref_Disparity, p_Vid->p_Inp->DisparityMethod, p_Vid->p_Inp->DisparityWindow, + p_Vid->p_Inp->DisparityDirection ); + } + } + else if( DisparityReference == 1 ) + { + if( BaseRefFrame->disparity ) + { + Ref_Disparity = AuxRefFrame->disparity; + } + else + { + printf( "[FERC] Disparity measurement using frame %2d|%2d as reference\n", AuxRefFrame->poc, AuxRefFrame->view_id ); + Ref_Disparity = AuxRefFrame->disparity = alloc_storable_picture( p_Vid, FRAME, p_Vid->width, p_Vid->height, p_Vid->width_cr, p_Vid->height_cr, 1 ); + cvDisparity( p_Vid, AuxRefFrame, BaseRefFrame, Ref_Disparity, p_Vid->p_Inp->DisparityMethod, p_Vid->p_Inp->DisparityWindow, DisparityDirection ); + } + } + + if( p_Vid->p_Inp->DisparityMethod != 0 && p_Vid->p_Inp->DispLoadFileName[0] != 0 ) + { + saveStorablePicture( p_Vid, p_Vid->p_Inp->DispLoadFileName, Ref_Disparity, 0, 0 ); + } + //#if( _DEBUG ) + //showStorablePicture( p_Vid , Ref_Disparity , 0 , 0 , "Original Disparity", 0); + //saveStorablePicture( p_Vid , "Disparity_Original.pgm", Ref_Disparity , 0 , 0 ); + //#endif + Ref_Disparity->PicSizeInMbs = Dest->PicSizeInMbs; + Ref_Disparity->PicWidthInMbs = Dest->PicWidthInMbs; + Ref_Disparity->poc = AuxRefFrame->poc; + Ref_Disparity->chroma_format_idc = 0; + Ref_Disparity->pixel_factor = p_Vid->p_Inp->DisparityWindow; + + // Normalize the disparity map + nElem = 0; + MeanDisp = 0; + for( i = 0; i < Ref_Disparity->size_x; i++ ) + for( j = 0; j < Ref_Disparity->size_y; j++ ) + { + Ref_Disparity->imgY[j][i] = (imgpel) ( (double) ( Ref_Disparity->imgY[j][i] ) / (double) ( p_Vid->p_Inp->DisparityWindow ) ); + MeanDisp = (double) ( MeanDisp * nElem + (double) ( Ref_Disparity->imgY[j][i] ) ) / (double) ( nElem + 1 ); + nElem++; + } +#if( _DEBUG ) + // showStorablePicture( p_Vid , Ref_Disparity , 0 , 0 , "Normalized Disparity", 0); +#endif + } + + Final_Disparity->PicSizeInMbs = Ref_Disparity->PicSizeInMbs; + Final_Disparity->PicWidthInMbs = Ref_Disparity->PicWidthInMbs; + Final_Disparity->chroma_format_idc = Ref_Disparity->chroma_format_idc; + Final_Disparity->pixel_factor = Ref_Disparity->pixel_factor; + Final_Disparity->poc = Dest->poc; + + iRet = 1; + if( DisparityProjectionFlag ) // p_Vid->p_Inp->DisparityProjection + { + iRet = DisparityProjection( p_Vid, Ref_Disparity, RefProjFrame, pSliceRefProjFrame, &Final_Disparity, Final_Disparity->poc, BLOCK_SIZE, + DisparityReference == 0 ? 1 : 2 ); + } + if( DisparityProjectionFlag == 0 || iRet == 0 ) + { + fercCopyImg( p_Vid, Ref_Disparity, Final_Disparity ); + } + + //#if( _DEBUG ) + // showStorablePicture( p_Vid , Final_Disparity , 0 , 0 , "Projected Disparity", 0); + // saveStorablePicture( p_Vid , "Disparity_Projected.pgm", Final_Disparity , 0 , 0 ); + //#endif + + *( ppDisparity4x4 ) = alloc_storable_picture( p_Vid, FRAME, p_Vid->width / BLOCK_SIZE, p_Vid->height / BLOCK_SIZE, p_Vid->width_cr / BLOCK_SIZE, + p_Vid->height_cr / BLOCK_SIZE, 1 ); + Disparity4x4 = *( ppDisparity4x4 ); + GetBest4x4Disparity( p_Vid, Final_Disparity, Disparity4x4, MeanDisp, 0, 0, BLOCK_SIZE, p_Vid->p_Inp->Disparity4x4BlockSize ); + + //#if( _DEBUG ) + // showStorablePicture( p_Vid , Final_Disparity , 0 , 0 , "Projected Disparity", 0); + // saveStorablePicture( p_Vid , "Disparity_Projected.pgm", Final_Disparity , 0 , 0 ); + // showStorablePicture( p_Vid , Disparity4x4 , 0 , 0 , "Disparity4x4", 0); + // saveStorablePicture( p_Vid , "Disparity_4x4.pgm", Disparity4x4 , 0 , 0 ); + //#endif + + free_storable_picture( Ref_Disparity ); + Ref_Disparity = BaseRefFrame->disparity = AuxRefFrame->disparity = NULL; + + return; +} + +/*! + ************************************************************************ + * \brief + * Update Macroblock information + ************************************************************************ + */ +static inline void updateMBData( VideoParameters *p_Vid, Slice *slice, int mb_idx, int x, int y, int blocksize ) +{ + Macroblock *currMB = &( p_Vid->mb_data[mb_idx] ); + + currMB->mb.x = (short) ( x / blocksize ); + currMB->mb.y = (short) ( y / blocksize ); + currMB->block_y = currMB->mb.y * blocksize; + currMB->pix_c_y = currMB->mb.y * p_Vid->mb_cr_size_y / 4; + currMB->block_x = currMB->mb.x * blocksize; + currMB->pix_c_x = currMB->mb.x * p_Vid->mb_cr_size_x / 4; + currMB->p_Slice = slice; +} + +/*! + ************************************************************************ + * \brief + * Copy a block to a bigger block in separate components + ************************************************************************ + */ +void copyPredblockRegionYUVSeparated( imgpel ***Destination, imgpel *predBlock, int x, int y, int BlockSize ) +{ + int ii, jj; + int uv; + + //! Y Matrix + for( ii = 0; ii < BlockSize; ii++ ) + for( jj = 0; jj < BlockSize; jj++ ) + Destination[0][y + ii][x + jj] = predBlock[ii * ( BlockSize ) + jj]; + + predBlock = predBlock + ( BlockSize * BlockSize ); + + for( uv = 1; uv <= 2; uv++ ) + { + for( ii = 0; ii < ( BlockSize / 2 ); ii++ ) + for( jj = 0; jj < ( BlockSize / 2 ); jj++ ) + Destination[uv][y / 2 + ii][x / 2 + jj] = predBlock[ii * ( BlockSize / 2 ) + jj]; + + predBlock = predBlock + ( BlockSize * BlockSize / 4 ); + } +} + +void copyPredblockRegionYUVSeparatedDestination( StorablePicture *Destination, imgpel ***Source, int x, int y, int BlockSize ) +{ + int ii, jj; + + //! Y Matrix + for( ii = 0; ii < BlockSize; ii++ ) + for( jj = 0; jj < BlockSize; jj++ ) + Destination->imgY[y + ii][x + jj] = Source[0][ii][jj]; + + //! UV Matrix + if( Destination->chroma_format_idc != YUV400 ) + { + int uv; + for( uv = 0; uv < 2; uv++ ) + { + for( ii = 0; ii < ( BlockSize / 2 ); ii++ ) + for( jj = 0; jj < ( BlockSize / 2 ); jj++ ) + Destination->imgUV[uv][y / 2 + ii][x / 2 + jj] = Source[uv + 1][ii][jj]; + } + } +} + +double compareBlockMSE( imgpel **storeYUV, int Block_Size ) +{ + unsigned long int Error = 0; + double MSE = 0; + int uv, y, x; + imgpel *A = storeYUV[MV_INTER], *B = storeYUV[MV_INTRA]; + + for( y = 0; y < Block_Size; y++ ) + { + for( x = 0; x < Block_Size; x++ ) + { + Error += iabs2( (int) ( *A ) - (int) ( *B ) ); + A++; + B++; + } + } + //for(uv=0; uv<2; uv++) + //{ + // for( y=0; yBlockConcealmentState; + double SAD_Value; + unsigned int SAD_Count; + int xb, yb, x, y; + int x_block, y_block; + int x_limit = Destination->size_x; + int y_limit = Destination->size_y; + + dst_x_pos = dst_x_pos - ( TotalSize - BlockSize ) / 2; + dst_y_pos = dst_y_pos - ( TotalSize - BlockSize ) / 2; + + if( src_x_pos != iClip3( 0, x_limit - 1, src_x_pos ) || src_y_pos != iClip3( 0, y_limit - 1, src_y_pos ) ) + return INT_MAX; + + src_x_pos = src_x_pos - ( TotalSize - BlockSize ) / 2; + src_y_pos = src_y_pos - ( TotalSize - BlockSize ) / 2; + + y_block = dst_y_pos / BLOCK_SIZE; + x_block = dst_x_pos / BLOCK_SIZE; + + SAD_Value = 0; + SAD_Count = 0; + for( yb = 0; yb < TotalSize / BLOCK_SIZE; yb++ ) + { + for( xb = 0; xb < TotalSize / BLOCK_SIZE; xb++ ) + { + if( y_block + yb >= 0 && x_block + xb >= 0 && y_block + yb < y_limit / BLOCK_SIZE && x_block + xb < x_limit / BLOCK_SIZE + && BlockConcealmentState->imgY[y_block + yb][x_block + xb] > 0 ) + { + for( x = 0; x < BLOCK_SIZE; x++ ) + { + for( y = 0; y < BLOCK_SIZE; y++ ) + { + if( dst_y_pos + yb * BLOCK_SIZE + y < 0 || dst_x_pos + xb * BLOCK_SIZE + x < 0 ) + continue; + if( dst_y_pos + yb * BLOCK_SIZE + y >= x_limit || dst_x_pos + xb * BLOCK_SIZE + x >= x_limit ) + continue; + if( src_y_pos + yb * BLOCK_SIZE + y < 0 || src_x_pos + xb * BLOCK_SIZE + x < 0 ) + continue; + if( src_y_pos + yb * BLOCK_SIZE + y >= y_limit || src_x_pos + xb * BLOCK_SIZE + x >= x_limit ) + continue; + SAD_Value += iabs( + (int) ( Destination->imgY[dst_y_pos + yb * BLOCK_SIZE + y][dst_x_pos + xb * BLOCK_SIZE + x] ) + - (int) ( Source->imgY[src_y_pos + yb * BLOCK_SIZE + y][src_x_pos + xb * BLOCK_SIZE + x] ) ); + SAD_Count += 1; + } + } + } + } + } + SAD_Value = SAD_Value / ( (double) SAD_Count ); + return SAD_Value; + +} +static void PrintDebugPGM( VideoParameters *p_Vid, INTER_INTRA_CONC_T *IntraInterConc, StorablePicture *Image, char *Name ) +{ + StorablePicture *Dest = IntraInterConc->Destination; + char out_ViewFileName[FILE_NAME_SIZE], chBuf[FILE_NAME_SIZE], *pch; + + strcpy( chBuf, p_Vid->p_Inp->outfile ); + pch = strrchr( chBuf, '.' ); + if( pch ) + *pch = '\0'; + if( !strcmp( "nul", chBuf ) ) + return; + sprintf( out_ViewFileName, "%s_%s_F%d_V%1d.pgm", chBuf, Name, Dest->poc, Dest->view_id ); + saveStorablePicture( p_Vid, out_ViewFileName, Image, 0, 0 ); +} + +static int GetThresholdHistogramBased( StorablePicture *Image, double SamplesThresh ) +{ +#define N_BINS 256 + int Threshold = 0; + int nSamples, y, x; + double count, Samples[N_BINS]; + + memset( Samples, 0, sizeof( Samples ) ); + + nSamples = Image->size_y * Image->size_x; + + for( y = 0; y < Image->size_y; y++ ) + for( x = 0; x < Image->size_x; x++ ) + Samples[Image->imgY[y][x]]++; + + for( x = 0; x < N_BINS; x++ ) + Samples[x] = Samples[x] / (double) nSamples; + + for( x = 0, count = 0; x < N_BINS && count < SamplesThresh; x++ ) + count += Samples[x]; + + Threshold = x - 1; + + Threshold = Threshold ? Threshold : 1; + + return Threshold; +} + +static void GetTemporalDifferenceFrame( VideoParameters *p_Vid, INTER_INTRA_CONC_T *IntraInterConc, int ApplyFilter, double Threshold ) +{ + StorablePicture *TemporalDifference = IntraInterConc->TemporalDifference; + StorablePicture *TemporalDifferenceUnfiltred = NULL; + StorablePicture *IntraRefFrame = IntraInterConc->IntraRefFrame; + StorablePicture *IntraRefFrame_L0 = IntraInterConc->SliceIntraViewReference->listX[LIST_0][1]; + Slice *SliceIntraViewReference = IntraInterConc->SliceIntraViewReference; + StorablePicture *currRef; + int y, x, yf, xf, i; + int diff, mv[3], mvf[3] = { 0, 0, 0 }; + double scale_factor; + double mean_value = 0, n_samples = 0; + + if( ApplyFilter != NONE_FILTER ) + TemporalDifferenceUnfiltred = alloc_storable_picture( p_Vid, FRAME, p_Vid->width, p_Vid->height, 0, 0, 1 ); + else + TemporalDifferenceUnfiltred = TemporalDifference; + + for( i = 0; i < IntraInterConc->SliceIntraViewReference->listXsize[LIST_0]; i++ ) + { + if( IntraInterConc->SliceIntraViewReference->listX[LIST_0][i]->view_id == IntraRefFrame->view_id ) + { + IntraRefFrame_L0 = IntraInterConc->SliceIntraViewReference->listX[LIST_0][i]; + break; + } + } + + for( y = 0; y < p_Vid->height; y++ ) + { + mvf[0] = mvf[1] = 0; + for( x = 0; x < p_Vid->width; x++ ) + { + /* Update mv information - Read Reference Values */ + mv[0] = IntraRefFrame->mv_info[y >> 2][x >> 2].mv[LIST_0].mv_x; + mv[1] = IntraRefFrame->mv_info[y >> 2][x >> 2].mv[LIST_0].mv_y; + mv[2] = IntraRefFrame->mv_info[y >> 2][x >> 2].ref_idx[LIST_0]; + + if( mv[2] >= 0 ) + { + currRef = SliceIntraViewReference->listX[LIST_0][mv[2]]; + /* Disparity Vectors */ + if( !( SliceIntraViewReference->view_id != 0 && currRef->poc == IntraRefFrame->poc ) ) + { + // Scale mv + scale_factor = (double) ( IntraRefFrame->poc - IntraRefFrame_L0->poc ) / (double) ( IntraRefFrame->poc - currRef->poc ); + mv[0] = (int) ( (float) ( mv[0] ) * scale_factor ); + mv[1] = (int) ( (float) ( mv[1] ) * scale_factor ); + + mvf[0] = mv[0] >> 2; + mvf[1] = mv[1] >> 2; + } + + } + + xf = iClip3( 0, IntraRefFrame->size_x, x + mvf[0] ); + yf = iClip3( 0, IntraRefFrame->size_y, y + mvf[1] ); + + diff = iabs( (int) ( IntraRefFrame->imgY[y][x] ) - (int) ( IntraRefFrame_L0->imgY[yf][xf] ) ); + /*if( diff < -127 || diff > 127 ) + { + int a = 0; + }*/ + + diff = iClip3( -127, 127, diff ); + + TemporalDifferenceUnfiltred->imgY[y][x] = diff; + + mean_value = ( mean_value * n_samples + (double) ( diff ) ); + n_samples += 1; + mean_value /= n_samples; + + } + } + + TemporalDifferenceUnfiltred->mean_value = mean_value; + + if( ApplyFilter != NONE_FILTER ) + { + //! Filtering the temporal difference + cvImageFilter( p_Vid, TemporalDifferenceUnfiltred, TemporalDifference, MEDIAN_FILTER, 9 ); + free_storable_picture( TemporalDifferenceUnfiltred ); + } + + IntraInterConc->DifferenceThresholds[TEMPORAL_DIFF] = GetThresholdHistogramBased( TemporalDifference, Threshold ); + + printf( "[FERC] Temporal Diff Threshold: %d\n", IntraInterConc->DifferenceThresholds[TEMPORAL_DIFF] ); + + //showStorablePicture(p_Vid, TemporalDifference, 0, 0, "TemporalDifference", 0); + + PrintDebugPGM( p_Vid, IntraInterConc, TemporalDifference, "TemporalDifference" ); + +} + +static void GetSpatialDifferenceFrame( VideoParameters *p_Vid, INTER_INTRA_CONC_T *IntraInterConc, int ApplyFilter, double Threshold ) +{ + StorablePicture *IntraRefFrame = IntraInterConc->IntraRefFrame; + StorablePicture *IntraRefFrameDisp = NULL; + StorablePicture *SpatialDifference = IntraInterConc->SpatialDifference; + StorablePicture *IntraRefFrame_D0; + int DisparityDirection = IntraInterConc->DisparityDirection; + int y, x; + int n, disp, diff; + double mean_disp = 0; + double mean_value = 0, n_samples = 0; + + IntraRefFrame_D0 = fercGetPictureForConcealFromDpb( p_Vid->p_Dpb_layer[IntraInterConc->InterRefFrame->view_id], IntraRefFrame->poc, + IntraInterConc->InterRefFrame->view_id, EXACT, 0, NULL ); + + getDisparityMap( p_Vid, IntraInterConc, IntraRefFrame, IntraRefFrame_D0->view_id, &( IntraInterConc->DisparityAuxMinus1 ) ); + + IntraRefFrameDisp = IntraInterConc->DisparityAuxMinus1; + + //showStorablePicture(p_Vid, IntraRefFrameDisp, 1, 0, "[FERC] Test Dispartiy", 0); + + for( x = 0, n = 0; x < p_Vid->width; x++ ) + { + for( y = 0; y < p_Vid->height; y++ ) + { + disp = IntraRefFrameDisp->imgY[y][x]; + + disp = ( -1 * DisparityDirection ) * disp; + + //! In loop average value measurement + mean_disp = (double) ( mean_disp * n + disp ) / (double) ( n + 1 ); + n++; + + diff = iabs( (int) ( IntraRefFrame->imgY[y][x] ) - (int) ( IntraRefFrame_D0->imgY[y][x + disp] ) ); + /*if( diff < -127 || diff > 127 ) + { + int a = 0; + }*/ + + diff = iClip3( -127, 127, diff ); + + SpatialDifference->imgY[y][x] = diff; + + mean_value = ( mean_value * n_samples + (double) ( diff ) ); + n_samples += 1; + mean_value /= n_samples; + + } + } + + SpatialDifference->mean_value = mean_value; + + //! Lost of information, although not relevance for this studyMcDonalds loc: + IntraInterConc->MeanDisparityValue = mean_disp; + + IntraInterConc->DifferenceThresholds[SPATIAL_DIFF] = GetThresholdHistogramBased( SpatialDifference, Threshold ); + + printf( "[FERC] Spatial Diff Threshold: %d\n", IntraInterConc->DifferenceThresholds[SPATIAL_DIFF] ); + + //showStorablePicture(p_Vid, SpatialDifference, 0, 0, "Spatial Difference", 0); + + PrintDebugPGM( p_Vid, IntraInterConc, SpatialDifference, "SpatialDifference" ); +} + +static void ShowStageDebug( VideoParameters *p_Vid, StorablePicture *Dest, int Stage, char *StageName ) +{ + char Name[50]; + //return; + sprintf( Name, "Concealed Frame Stage %1d %s ", Stage, StageName ); + showStorablePicture( p_Vid, Dest, 0, 1, Name, 0 ); + sprintf( Name, "Concealed_Frame_Stage_%1d_%s.pgm", Stage, StageName ); + // saveStorablePicture(p_Vid, Name, Dest, 0, 0); +} + +/*! + ************************************************************************ + * \brief + * Definition of each Iteration and Main function + ************************************************************************ + */ + +/*! + ************************************************************************ + * \brief + * Conceal missing frame block MSE Based: + * Conceal if difference between predictors is lower than a specified threshold + ************************************************************************ + */ +static inline void ConcealCurrentClusterMSEBased( VideoParameters *p_Vid, INTER_INTRA_CONC_T *IntraInterConc, int x_MB, int y_MB, int block_idx, + int *block_size, double MSEDiffThreshold ) +{ + StorablePicture *Dest = IntraInterConc->Destination; + imgpel ***storeYUV = IntraInterConc->storeYUV; + double tmp_MSE; + int cur_idx, max_clust, x_block, y_block; + int mv_intra[3] = { 0, 0, 0 }, mv_inter[3] = { 0, 0, 0 }; + int idxMB = y_MB / MB_BLOCK_SIZE * Dest->PicWidthInMbs + x_MB / MB_BLOCK_SIZE; + int test_idx = 0; + + *block_size = 0; + cur_idx = block_idx; + + max_clust = CLUSTER[block_idx]; + max_clust += block_idx; + + cur_idx = block_idx; + while( cur_idx < max_clust ) + { + do + { + y_block = ( y_MB * MB_BLOCK_SIZE / BLOCK_SIZE ) + BLOCK_IDX_MB[cur_idx][1]; + x_block = ( x_MB * MB_BLOCK_SIZE / BLOCK_SIZE ) + BLOCK_IDX_MB[cur_idx][0]; + + if( IntraInterConc->BlockConcealmentState->imgY[y_block][x_block] > 0 ) + return; //!< Stop clustering + + getMVIntra( IntraInterConc, LIST_0, y_block, x_block, mv_intra ); + getMVInter( IntraInterConc, LIST_0, y_block, x_block, mv_inter ); + + if( mv_inter[2] < 0 || mv_intra[2] < 0 ) + return; //!< Stop clustering + + EnhancedbuildPredblock4x4YUV( p_Vid, mv_inter, x_block * BLOCK_SIZE, y_block * BLOCK_SIZE, storeYUV[LIST_0][MV_INTER], LIST_0, idxMB, + IntraInterConc->tmp_storeYUV ); + EnhancedbuildPredblock4x4YUV( p_Vid, mv_intra, x_block * BLOCK_SIZE, y_block * BLOCK_SIZE, storeYUV[LIST_0][MV_INTRA], LIST_0, idxMB, + IntraInterConc->tmp_storeYUV ); + + if( ( tmp_MSE = compareBlockMSE( storeYUV[LIST_0], BLOCK_SIZE ) ) <= MSEDiffThreshold ) + { + //ferc_bi_prediction( storeYUV[LIST_0][MV_INTER], storeYUV[LIST_0][MV_INTER], 1, storeYUV[LIST_0][MV_INTRA], 1, BLOCK_SIZE, BLOCK_SIZE, 1); + copyPredblockRegionYUVSeparated( IntraInterConc->storeSeparatedYUV, storeYUV[LIST_0][MV_INTER], BLOCK_IDX_MB[test_idx][0] * BLOCK_SIZE, + BLOCK_IDX_MB[test_idx][1] * BLOCK_SIZE, BLOCK_SIZE ); + + // set motion vector in the concealed picture + Dest->mv_info[y_block][x_block].mv[LIST_0].mv_x = mv_inter[0]; + Dest->mv_info[y_block][x_block].mv[LIST_0].mv_y = mv_inter[1]; + Dest->mv_info[y_block][x_block].ref_idx[LIST_0] = mv_inter[2]; + + test_idx++; + cur_idx++; + continue; + } + + return; + } while( cur_idx < block_idx + target_cluster( *block_size ) ); + ( *block_size ) = ( *block_size ) ? ( *block_size ) * 2 : BLOCK_SIZE; + } + return; +} +static int MSEBasedConcealmentStage( VideoParameters *p_Vid, INTER_INTRA_CONC_T *IntraInterConc ) +{ + Slice *SliceDestiantion = IntraInterConc->SliceDestiantion; + StorablePicture *BlockConcealmentState = IntraInterConc->BlockConcealmentState; + StorablePicture *Dest = IntraInterConc->Destination; + double BlockRatio, MSE_Threshold; + unsigned int MaxBlockNumber = ( p_Vid->width * p_Vid->height / iabs2( BLOCK_SIZE ) ); + int BlockSize, iBlock, y4x4, x4x4, y, x, iyMB, ixMB, idxMB, y_block4x4, x_block4x4, Iterations, BlockSizeThreshold, prev_nConcealed, nConcealed, mb_height, + mb_width; + + mb_width = Dest->PicWidthInMbs; + mb_height = ( Dest->PicSizeInMbs ) / ( Dest->PicWidthInMbs ); + nConcealed = 0; + BlockSizeThreshold = 16; + prev_nConcealed = 1; + BlockSize = 0; + + /* + * Loop through all Macroblocks and conceal the one based on the MSE value between the different prediction (INTRA and INTER) + */ + BlockRatio = 0.8; + while( BlockSizeThreshold >= 16 ) + { + for( Iterations = 0; /*((double) (nConcealed) < (double) (MaxBlockNumber) * BlockRatio ) && ( prev_nConcealed != nConcealed ) && */( Iterations < 5 ); + Iterations++ ) + { + MSE_Threshold = 1 * Iterations; + prev_nConcealed = nConcealed; + + for( iyMB = 0; iyMB < mb_height; iyMB++ ) + { + y = iyMB * MB_BLOCK_SIZE; + + for( ixMB = 0; ixMB < mb_width; ixMB++ ) + { + x = ixMB * MB_BLOCK_SIZE; + idxMB = iyMB * Dest->PicWidthInMbs + ixMB; + updateMBData( p_Vid, SliceDestiantion, idxMB, x, y, BLOCK_SIZE ); + + iBlock = 0; + while( iBlock < iabs2( MB_BLOCK_SIZE ) / iabs2( BLOCK_SIZE ) ) + { + x = ixMB * MB_BLOCK_SIZE + BLOCK_IDX_MB[iBlock][0] * BLOCK_SIZE; + y = iyMB * MB_BLOCK_SIZE + BLOCK_IDX_MB[iBlock][1] * BLOCK_SIZE; + + y_block4x4 = y / BLOCK_SIZE; + x_block4x4 = x / BLOCK_SIZE; + + ConcealCurrentClusterMSEBased( p_Vid, IntraInterConc, ixMB, iyMB, iBlock, &BlockSize, MSE_Threshold ); + + if( BlockSize >= BlockSizeThreshold ) + { + for( y4x4 = 0; y4x4 < BlockSize / BLOCK_SIZE; y4x4++ ) + for( x4x4 = 0; x4x4 < BlockSize / BLOCK_SIZE; x4x4++ ) + BlockConcealmentState->imgY[y_block4x4 + y4x4][x_block4x4 + x4x4] = 1; + + copyPredblockRegionYUVSeparatedDestination( Dest, IntraInterConc->storeSeparatedYUV, x, y, BlockSize ); + nConcealed += iabs2( BlockSize / BLOCK_SIZE ); + iBlock += iabs2( BlockSize / BLOCK_SIZE ); + } + else + { + iBlock += 1; + } + } + } + } //for (iyMB = 0; iyMB < mb_height; iyMB++) + + } + BlockSizeThreshold = BlockSizeThreshold / 2; + BlockRatio += 0.06; + } + BlockRatio = (double) nConcealed / (double) MaxBlockNumber; + + return nConcealed; +} + +static int ConcealBasedOnDifference( VideoParameters *p_Vid, INTER_INTRA_CONC_T *IntraInterConc, int DifferenceThreshold, int x_block4x4, int y_block4x4, + int BLockWindow, int DiffType ) +{ + StorablePicture *DifferenceMap; + StorablePicture *TemporalDifference = IntraInterConc->TemporalDifference; + StorablePicture *SpatialDifference = IntraInterConc->SpatialDifference; + imgpel *storeYUV = IntraInterConc->storeYUV[LIST_0][0]; + double CurrentDiff = 0; + int CountThreshHigher, MV[3], iRet = 0, curr_mb_idx, diff = 0, y, x, count = 0; + double currSAD, MinSAD = ULONG_MAX; + curr_mb_idx = y_block4x4 * BLOCK_SIZE / MB_BLOCK_SIZE * IntraInterConc->Destination->PicWidthInMbs + x_block4x4 * BLOCK_SIZE / MB_BLOCK_SIZE; + + MinSAD = INT_MAX; + //DiffType=SPATIAL_DIFF; + //DiffType = TEMPORAL_DIFF; + //for (; DiffType >= imin(TEMPORAL_DIFF, SPATIAL_DIFF) && DiffType <= imax(TEMPORAL_DIFF, SPATIAL_DIFF); DiffType++) + { + // if (DifferenceThreshold > IntraInterConc->DifferenceThresholds[DiffType]) + // return iRet; + + switch( DiffType ) + { + case TEMPORAL_DIFF: + //getMVIntra(IntraInterConc, LIST_0, y_block4x4, x_block4x4, MV); + getMVInter( IntraInterConc, LIST_0, y_block4x4, x_block4x4, MV ); + DifferenceMap = TemporalDifference; + // if( MV[2] < 0) + // { + // getMVIntra(IntraInterConc, LIST_0, y_block4x4, x_block4x4, MV); + // } + break; + case SPATIAL_DIFF: + getMVInterview( IntraInterConc, LIST_0, y_block4x4, x_block4x4, MV ); + DifferenceMap = SpatialDifference; + break; + default: + return iRet; + } + + if( MV[2] < 0 ) + return iRet; + + CountThreshHigher = 0; + for( y = imax( 0, y_block4x4 - BLockWindow ) * BLOCK_SIZE; y < imin( DifferenceMap->size_y / BLOCK_SIZE, y_block4x4 + BLockWindow + 1 ) * BLOCK_SIZE; y++ ) + { + for( x = imax( 0, x_block4x4 - BLockWindow ) * BLOCK_SIZE; x < imin( DifferenceMap->size_x / BLOCK_SIZE, x_block4x4 + BLockWindow + 1 ) * BLOCK_SIZE; + x++ ) + { + if( y >= 0 && y < DifferenceMap->size_y && x >= 0 && x < DifferenceMap->size_x ) + { + diff = (int) ( DifferenceMap->imgY[y][x] ); + CurrentDiff = ( (double) ( CurrentDiff * count + diff ) ) / ( (double) count + 1 ); + count++; + if( diff > DifferenceThreshold ) + CountThreshHigher++; + } + } + } + if( CountThreshHigher > MB_BLOCK_SIZE * MB_BLOCK_SIZE ) + return iRet; + + if( CurrentDiff <= (double) ( DifferenceThreshold ) ) + { + currSAD = computeSADOverlap( IntraInterConc, IntraInterConc->Destination, x_block4x4 * BLOCK_SIZE, y_block4x4 * BLOCK_SIZE, + IntraInterConc->SliceDestiantion->listX[LIST_0][MV[2]], x_block4x4 * BLOCK_SIZE + MV[0] / 4, y_block4x4 * BLOCK_SIZE + MV[1] / 4, BLOCK_SIZE, + BLOCK_SIZE + 2 * BLOCK_SIZE ); + + if( currSAD <= MinSAD ) + { + MinSAD = currSAD; + + EnhancedbuildPredblock4x4YUV( p_Vid, MV, x_block4x4 * BLOCK_SIZE, y_block4x4 * BLOCK_SIZE, storeYUV, LIST_0, curr_mb_idx, + IntraInterConc->tmp_storeYUV ); + copyPredblockRegionYUVDestination( IntraInterConc->Destination, storeYUV, x_block4x4 * BLOCK_SIZE, y_block4x4 * BLOCK_SIZE, BLOCK_SIZE ); + IntraInterConc->BlockConcealmentState->imgY[y_block4x4][x_block4x4] = 1; + + IntraInterConc->Destination->mv_info[y_block4x4][x_block4x4].mv[LIST_0].mv_x = MV[0]; + IntraInterConc->Destination->mv_info[y_block4x4][x_block4x4].mv[LIST_0].mv_y = MV[1]; + IntraInterConc->Destination->mv_info[y_block4x4][x_block4x4].ref_idx[LIST_0] = MV[2]; + iRet = 1; + } + } + } + return iRet; +} + +//static int ConcealBasedOnTemporalDifference(VideoParameters *p_Vid, INTER_INTRA_CONC_T *IntraInterConc, int DifferenceThreshold, int x_block4x4, int y_block4x4, +// int BLockWindow) +//{ +// void +// (*getMV)(INTER_INTRA_CONC_T *IntraInterConc, int list, int y_block, int x_block, int MV[3]); +// StorablePicture *DifferenceMap; +// StorablePicture *TemporalDifference = IntraInterConc->TemporalDifference; +// StorablePicture *SpatialDifference = IntraInterConc->SpatialDifference; +// imgpel *storeYUV = IntraInterConc->storeYUV[LIST_0][0]; +// double CurrentDiff = 0; +// int CountThreshHigher, MV[3], DiffType, iRet = 0, curr_mb_idx, diff = 0, y, x, count = 0; +// double currSAD, MinSAD = ULONG_MAX; +// curr_mb_idx = y_block4x4 * BLOCK_SIZE / MB_BLOCK_SIZE * IntraInterConc->Destination->PicWidthInMbs + x_block4x4 * BLOCK_SIZE / MB_BLOCK_SIZE; +// +// MinSAD = INT_MAX; +// //DiffType=SPATIAL_DIFF; +// DiffType = TEMPORAL_DIFF; +// +// if (DifferenceThreshold > IntraInterConc->DifferenceThresholds[DiffType]) +// return iRet; +// +// DifferenceMap = TemporalDifference; +// +// getMVInter(IntraInterConc, LIST_0, y_block4x4, x_block4x4, MV); +// +// // if (MV[2] < 0) +// // { +// // getMVIntra(IntraInterConc, LIST_0, y_block4x4, x_block4x4, MV); +// // +// // } +// +// if (MV[2] < 0) +// return iRet; +// +// CountThreshHigher = 0; +// for (y = imax(0, y_block4x4 - BLockWindow) * BLOCK_SIZE; y < imin(DifferenceMap->size_y / BLOCK_SIZE, y_block4x4 + BLockWindow + 1) * BLOCK_SIZE; y++) +// { +// for (x = imax(0, x_block4x4 - BLockWindow) * BLOCK_SIZE; x < imin(DifferenceMap->size_x / BLOCK_SIZE, x_block4x4 + BLockWindow + 1) * BLOCK_SIZE; x++) +// { +// if (y >= 0 && y < DifferenceMap->size_y && x >= 0 && x < DifferenceMap->size_x) +// { +// diff = (int) (DifferenceMap->imgY[y][x]); +// CurrentDiff = ((double) (CurrentDiff * count + diff)) / ((double) count + 1); +// count++; +// if (diff > DifferenceThreshold) +// CountThreshHigher++; +// } +// } +// } +// if (CountThreshHigher > MB_BLOCK_SIZE * MB_BLOCK_SIZE) +// return iRet; +// +// if (CurrentDiff <= (double) (DifferenceThreshold)) +// { +// currSAD = computeSADOverlap(IntraInterConc, IntraInterConc->Destination, x_block4x4 * BLOCK_SIZE, y_block4x4 * BLOCK_SIZE, +// IntraInterConc->SliceDestiantion->listX[LIST_0][MV[2]], x_block4x4 * BLOCK_SIZE + MV[0] / 4, y_block4x4 * BLOCK_SIZE + MV[1] / 4, BLOCK_SIZE, +// BLOCK_SIZE + 2 * BLOCK_SIZE); +// +// if (currSAD <= MinSAD) +// { +// MinSAD = currSAD; +// +// EnhancedbuildPredblock4x4YUV(p_Vid, MV, x_block4x4 * BLOCK_SIZE, y_block4x4 * BLOCK_SIZE, storeYUV, LIST_0, curr_mb_idx, IntraInterConc->tmp_storeYUV); +// copyPredblockRegionYUVDestination(IntraInterConc->Destination, storeYUV, x_block4x4 * BLOCK_SIZE, y_block4x4 * BLOCK_SIZE, BLOCK_SIZE); +// IntraInterConc->BlockConcealmentState->imgY[y_block4x4][x_block4x4] = 1; +// +// IntraInterConc->Destination->mv_info[y_block4x4][x_block4x4].mv[LIST_0].mv_x = MV[0]; +// IntraInterConc->Destination->mv_info[y_block4x4][x_block4x4].mv[LIST_0].mv_y = MV[1]; +// IntraInterConc->Destination->mv_info[y_block4x4][x_block4x4].ref_idx[LIST_0] = MV[2]; +// iRet = 1; +// } +// } +// return iRet; +//} + +//static int ConcealBasedOnINTRADisparity(VideoParameters *p_Vid, INTER_INTRA_CONC_T *IntraInterConc, int x_block4x4, int y_block4x4) +//{ +// void +// (*getMV)(INTER_INTRA_CONC_T *IntraInterConc, int list, int y_block, int x_block, int MV[3]); +// StorablePicture *DifferenceMap; +// StorablePicture *TemporalDifference = IntraInterConc->TemporalDifference; +// StorablePicture *DisparityMap = IntraInterConc->DisparityBlockwiseAux; +// imgpel *storeYUV = IntraInterConc->storeYUV[LIST_0][0]; +// double CurrentDiff = 0; +// int MV[3], DiffType, iRet = 0, curr_mb_idx, diff = 0, y, x, count = 0; +// unsigned long int currSAD, MinSAD; +// unsigned int Disparity; +// +// curr_mb_idx = y_block4x4 * BLOCK_SIZE / MB_BLOCK_SIZE * IntraInterConc->Destination->PicWidthInMbs + x_block4x4 * BLOCK_SIZE / MB_BLOCK_SIZE; +// +// getMVIntra(IntraInterConc, LIST_0, y_block4x4, x_block4x4, MV); +// if (MV[2] != IntraInterConc->MVExtrapolationRefIdx[1]) +// return iRet; +// +// Disparity = DisparityMap->imgY[y_block4x4][x_block4x4] * 4; +// +// EnhancedbuildPredblock4x4YUV(p_Vid, MV, x_block4x4 * BLOCK_SIZE, y_block4x4 * BLOCK_SIZE, storeYUV, LIST_0, curr_mb_idx, IntraInterConc->tmp_storeYUV); +// copyPredblockRegionYUVDestination(IntraInterConc->Destination, storeYUV, x_block4x4 * BLOCK_SIZE, y_block4x4 * BLOCK_SIZE, BLOCK_SIZE); +// IntraInterConc->BlockConcealmentState->imgY[y_block4x4][x_block4x4] = 1; +// +// IntraInterConc->Destination->mv_info[y_block4x4][x_block4x4].mv[LIST_0].mv_x = MV[0]; +// IntraInterConc->Destination->mv_info[y_block4x4][x_block4x4].mv[LIST_0].mv_y = MV[1]; +// IntraInterConc->Destination->mv_info[y_block4x4][x_block4x4].ref_idx[LIST_0] = MV[2]; +// iRet = 1; +// +// return iRet; +//} + +static int ConcealBasedOnINTRAandZeroMV( VideoParameters *p_Vid, INTER_INTRA_CONC_T *IntraInterConc, int x_block4x4, int y_block4x4 ) +{ + void + (*getMV)( INTER_INTRA_CONC_T *IntraInterConc, int list, int y_block, int x_block, int MV[3] ); + StorablePicture *DifferenceMap; + StorablePicture *TemporalDifference = IntraInterConc->TemporalDifference; + StorablePicture *DisparityMap = IntraInterConc->DisparityBlockwiseAux; + imgpel *storeYUV = IntraInterConc->storeYUV[LIST_0][0]; + double CurrentDiff = 0; + int MV[3], DiffType, iRet = 0, curr_mb_idx, diff = 0, y, x, count = 0; + unsigned long int currSAD, MinSAD; + unsigned int Disparity; + + curr_mb_idx = y_block4x4 * BLOCK_SIZE / MB_BLOCK_SIZE * IntraInterConc->Destination->PicWidthInMbs + x_block4x4 * BLOCK_SIZE / MB_BLOCK_SIZE; + + getMVIntra( IntraInterConc, LIST_0, y_block4x4, x_block4x4, MV ); + if( MV[2] < 0 ) + { + MV[0] = MV[1] = 0; + MV[2] = IntraInterConc->MVExtrapolationRefIdx[0]; + } + EnhancedbuildPredblock4x4YUV( p_Vid, MV, x_block4x4 * BLOCK_SIZE, y_block4x4 * BLOCK_SIZE, storeYUV, LIST_0, curr_mb_idx, IntraInterConc->tmp_storeYUV ); + copyPredblockRegionYUVDestination( IntraInterConc->Destination, storeYUV, x_block4x4 * BLOCK_SIZE, y_block4x4 * BLOCK_SIZE, BLOCK_SIZE ); + IntraInterConc->BlockConcealmentState->imgY[y_block4x4][x_block4x4] = 1; + + IntraInterConc->Destination->mv_info[y_block4x4][x_block4x4].mv[LIST_0].mv_x = MV[0]; + IntraInterConc->Destination->mv_info[y_block4x4][x_block4x4].mv[LIST_0].mv_y = MV[1]; + IntraInterConc->Destination->mv_info[y_block4x4][x_block4x4].ref_idx[LIST_0] = MV[2]; + iRet = 1; + + return iRet; +} + +static int ConcealBasedOnDisparityOrMotion( VideoParameters *p_Vid, INTER_INTRA_CONC_T *IntraInterConc, int x_block4x4, int y_block4x4, +//void (*getMV)(INTER_INTRA_CONC_T *, int, int, int, int *)) + int MotionDispFlag ) +{ + imgpel *storeYUV = IntraInterConc->storeYUV[LIST_0][0]; + int MV[3], iRet = 0, curr_mb_idx; + + switch( MotionDispFlag ) + { + case MOTION: + getMVInter( IntraInterConc, LIST_0, y_block4x4, x_block4x4, MV ); + if( MV[2] >= 0 ) + break; + getMVIntra( IntraInterConc, LIST_0, y_block4x4, x_block4x4, MV ); + break; + case DISPARITY: + getMVIntra( IntraInterConc, LIST_0, y_block4x4, x_block4x4, MV ); + if( MV[2] != IntraInterConc->MVExtrapolationRefIdx[1] ) + { + getMVInterview( IntraInterConc, LIST_0, y_block4x4, x_block4x4, MV ); + } + break; + } + + if( MV[2] < 0 ) + return iRet; + + curr_mb_idx = y_block4x4 * BLOCK_SIZE / MB_BLOCK_SIZE * IntraInterConc->Destination->PicWidthInMbs + x_block4x4 * BLOCK_SIZE / MB_BLOCK_SIZE; + + EnhancedbuildPredblock4x4YUV( p_Vid, MV, x_block4x4 * BLOCK_SIZE, y_block4x4 * BLOCK_SIZE, storeYUV, LIST_0, curr_mb_idx, IntraInterConc->tmp_storeYUV ); + copyPredblockRegionYUVDestination( IntraInterConc->Destination, storeYUV, x_block4x4 * BLOCK_SIZE, y_block4x4 * BLOCK_SIZE, BLOCK_SIZE ); + IntraInterConc->BlockConcealmentState->imgY[y_block4x4][x_block4x4] = 1; + + IntraInterConc->Destination->mv_info[y_block4x4][x_block4x4].mv[LIST_0].mv_x = MV[0]; + IntraInterConc->Destination->mv_info[y_block4x4][x_block4x4].mv[LIST_0].mv_y = MV[1]; + IntraInterConc->Destination->mv_info[y_block4x4][x_block4x4].ref_idx[LIST_0] = MV[2]; + iRet = 1; + + return iRet; +} + +/*! + ************************************************************************ + * \brief + * Definition of each Stage and Main function + ************************************************************************ + */ + +#define CONCEALMENT_STAGES 6 + +// MVE +const int MOTION_INTRAVIEW_STAGES[] = { MOTION_INTRAVIEW, CONCEALMV, CONCEALMV, CONCEALMV, CONCEALMV, CONCEALMV }; +// BASE_MC +const int MOTION_INTERVIEW_STAGES[] = { MOTION_INTERVIEW, CONCEALMV, CONCEALMV, CONCEALMV, CONCEALMV, CONCEALMV }; +// INTERVIEW_COPY +const int INTERVIEW_COPY_STAGES[] = { INTERVIEW_COPY, MOTION_INTRAVIEW, CONCEALMV, CONCEALMV, CONCEALMV, CONCEALMV }; +// COMB_MC +const int MOTION_INTERVIEW_FIRST_STAGES[] = { MOTION_INTERVIEW, MOTION_INTRAVIEW, CONCEALMV, CONCEALMV, CONCEALMV, CONCEALMV }; +// COMB_MC_DISP +const int MOTION_INTERVIEW_MVE_INTERCOPY_STAGES[] = { MOTION_INTERVIEW, INTERVIEW_COPY, MOTION_INTRAVIEW, CONCEALMV, CONCEALMV, CONCEALMV }; +// Enhanced MVE +const int ENHANCED_MVE_STAGES[] = { ENHANCED_MVE, CONCEALMV, CONCEALMV, CONCEALMV, CONCEALMV }; +//TEST +const int MOTION_INTERVIEW_DISP_INTERCOPY_MVE_STAGES[] = { MOTION_INTERVIEW, MOTION_INTRAVIEW_DISP, INTERVIEW_COPY, MOTION_INTRAVIEW, ZERO_MV_CONC, CONCEALMV }; + +const int MOTION_ITERATIONS_METHODS[] = { MOTION_INTERVIEW, MOTION_INTRAVIEW, INTERVIEW_COPY, CONCEALMV, CONCEALMV, CONCEALMV }; +const int MOTION_ITERATIONS_STAGES[] = { MSE_BASED, SPAT_TEMP_DIFF, MOTION_INTERVIEW, MOTION_DISP_FRAME, MOTION_INTRAVIEW, CONCEALMV }; + +static inline int GetCurrentConcealmentMethodFrame( INTER_INTRA_CONC_T *IntraInterConc, int ConcealmentMethod, int Stage ) +{ + int CurrentConcealmentMethod; + CurrentConcealmentMethod = CONCEALMV; + switch( ConcealmentMethod ) + { + case MOTION_INTRAVIEW: + CurrentConcealmentMethod = MOTION_INTRAVIEW_STAGES[Stage - 1]; + break; + case MOTION_INTERVIEW: + CurrentConcealmentMethod = MOTION_INTERVIEW_STAGES[Stage - 1]; + break; + case INTERVIEW_COPY: + CurrentConcealmentMethod = INTERVIEW_COPY_STAGES[Stage - 1]; + break; + case MOTION_INTERVIEW_FIRST: + CurrentConcealmentMethod = MOTION_INTERVIEW_FIRST_STAGES[Stage - 1]; + break; + case MOTION_INTERVIEW_MVE_INTERCOPY: + CurrentConcealmentMethod = MOTION_INTERVIEW_MVE_INTERCOPY_STAGES[Stage - 1]; + break; + case ENHANCED_MVE: + CurrentConcealmentMethod = ENHANCED_MVE_STAGES[Stage - 1]; + break; + case MOTION_ITERATIONS: + CurrentConcealmentMethod = MOTION_ITERATIONS_STAGES[Stage - 1]; + if( CurrentConcealmentMethod == MOTION_DISP_FRAME ) + { + if( IntraInterConc->SpatialDifference->mean_value > IntraInterConc->TemporalDifference->mean_value ) + { + printf( "[FERC] Using Motion in Stage %d\n", Stage ); + CurrentConcealmentMethod = MOTION_INTRAVIEW; + } + else + { + printf( "[FERC] Using Disparity in Stage %d\n", Stage ); + CurrentConcealmentMethod = INTERVIEW_COPY; + } + } + break; + } + return CurrentConcealmentMethod; +} + +static int SpatioTemporalDecision( INTER_INTRA_CONC_T *IntraInterConc, int x_block4x4, int y_block4x4, int BLockWindow ) +{ + + StorablePicture *DifferenceMap; + StorablePicture *TemporalDifference = IntraInterConc->TemporalDifference; + StorablePicture *SpatialDifference = IntraInterConc->SpatialDifference; + int Max_Count, count, diff, CountThreshHigher, x, y; + double CurrentDiffAverage; + int MV[3]; + int DiffType, DifferenceThreshold; + + Max_Count = ( BLockWindow + 1 ) * ( BLockWindow + 1 ) * BLOCK_SIZE * BLOCK_SIZE; + + for( DiffType = imin( TEMPORAL_DIFF, SPATIAL_DIFF ); DiffType <= imax( TEMPORAL_DIFF, SPATIAL_DIFF ); DiffType++ ) + { + switch( DiffType ) + { + case TEMPORAL_DIFF: + getMVInter( IntraInterConc, LIST_0, y_block4x4, x_block4x4, MV ); + DifferenceMap = TemporalDifference; + break; + case SPATIAL_DIFF: + getMVIntra( IntraInterConc, LIST_0, y_block4x4, x_block4x4, MV ); + if( MV[2] != IntraInterConc->MVExtrapolationRefIdx[1] ) + { + getMVInterview( IntraInterConc, LIST_0, y_block4x4, x_block4x4, MV ); + } + DifferenceMap = SpatialDifference; + break; + } + + if( MV[2] < 0 ) + return NONE_CONCEAL; + + CountThreshHigher = 0; + count = 0; + DifferenceThreshold = IntraInterConc->DifferenceThresholds[DiffType]; + //DifferenceThreshold = 256; + + for( y = imax( 0, y_block4x4 - BLockWindow ) * BLOCK_SIZE; y < imin( DifferenceMap->size_y / BLOCK_SIZE, y_block4x4 + BLockWindow + 1 ) * BLOCK_SIZE; y++ ) + { + for( x = imax( 0, x_block4x4 - BLockWindow ) * BLOCK_SIZE; x < imin( DifferenceMap->size_x / BLOCK_SIZE, x_block4x4 + BLockWindow + 1 ) * BLOCK_SIZE; + x++ ) + { + //if( y >= 0 && y < DifferenceMap->size_y && x >= 0 && x < DifferenceMap->size_x ) + //{ + diff = (int) ( DifferenceMap->imgY[y][x] ); + CurrentDiffAverage = ( (double) ( CurrentDiffAverage * count + diff ) ) / ( (double) count + 1 ); + count++; + if( diff > DifferenceThreshold ) + CountThreshHigher++; + //} + } + } + + if( CountThreshHigher > ( Max_Count / 2 ) ) + continue; + + switch( DiffType ) + { + case TEMPORAL_DIFF: + return MOTION_INTERVIEW; + break; + case SPATIAL_DIFF: + return INTERVIEW_COPY; + break; + } + + } + return NONE_CONCEAL; +} + +static inline int GetCurrentConcealmentMethodBlockBased( INTER_INTRA_CONC_T *IntraInterConc, int ConcealmentMethod, int y_block, int x_block ) +{ + int CurrentConcealmentMethod; + int mv_intra[3], mv_inter[3]; + CurrentConcealmentMethod = ConcealmentMethod; + + if( ConcealmentMethod != MSE_BASED && ConcealmentMethod != SPAT_TEMP_DIFF ) + return CurrentConcealmentMethod; + + if( ConcealmentMethod == MSE_BASED ) + { + CurrentConcealmentMethod = NONE_CONCEAL; + getMVIntra( IntraInterConc, LIST_0, y_block, x_block, mv_intra ); + getMVInter( IntraInterConc, LIST_0, y_block, x_block, mv_inter ); + if( mv_inter[2] >= 0 && mv_intra[2] >= 0 ) + if( mv_inter[0] == mv_intra[0] ) + if( mv_inter[1] == mv_intra[1] ) + if( mv_inter[2] == mv_intra[2] ) + CurrentConcealmentMethod = MOTION_INTERVIEW; + } + if( ConcealmentMethod == SPAT_TEMP_DIFF ) + { + CurrentConcealmentMethod = SpatioTemporalDecision( IntraInterConc, x_block, y_block, 2 ); + } + + return CurrentConcealmentMethod; +} + +static int UseThisMotion( int ConcealmentMethod, int MotionSource ) +{ + const int *MethodStages; + int i; + + switch( ConcealmentMethod ) + { + case MOTION_INTRAVIEW: + MethodStages = MOTION_INTRAVIEW_STAGES; + break; + case MOTION_INTERVIEW: + MethodStages = MOTION_INTERVIEW_STAGES; + break; + case INTERVIEW_COPY: + MethodStages = INTERVIEW_COPY_STAGES; + break; + case MOTION_INTERVIEW_FIRST: + MethodStages = MOTION_INTERVIEW_FIRST_STAGES; + break; + case MOTION_INTERVIEW_MVE_INTERCOPY: + MethodStages = MOTION_INTERVIEW_MVE_INTERCOPY_STAGES; + break; + case ENHANCED_MVE: + MethodStages = ENHANCED_MVE_STAGES; + break; + case MOTION_ITERATIONS: + MethodStages = MOTION_ITERATIONS_METHODS; + break; + } + for( i = 0; i < CONCEALMENT_STAGES; i++ ) + { + if( MethodStages[i] == MotionSource ) + return 1; + } + return 0; +} + +void Conceal_by_Stages( VideoParameters *p_Vid, INTER_INTRA_CONC_T *IntraInterConc, StorablePicture *Dest, int ConcealmentMethod ) +{ + + //! Reference frames + StorablePicture *InterRefFrame = IntraInterConc->InterRefFrame; + //! Storable picture to save the disparity maps + Slice *pSliceDestiantion = Dest->PicSlice; + + StorablePicture *BlockConcealmentState = IntraInterConc->BlockConcealmentState; //!< State of the lost image blocks + + //! Motion concealment loop variables +#define NCONCEALMENTMETHODS 3 + imgpel *predMB[NCONCEALMENTMETHODS]; + unsigned int SAD_Values[NCONCEALMENTMETHODS]; + Macroblock *currMB; + int usedList[2], idxMB, List; + int Stage = 0, mb_height, mb_width; + int x_block4x4, y_block4x4, y, x; + imgpel ***storeYUV = IntraInterConc->storeYUV; + int BlockSize, MV[3]; + StorablePicture *currRef; + int CurrentConcealmentMethodFrame, CurrentConcealmentMethod; + double PerStageConcealmentRatio = 0; + + //! Initialize the variables + BlockSize = 4; //!< Define the Block Size + mb_width = Dest->PicWidthInMbs; + mb_height = ( Dest->PicSizeInMbs ) / ( Dest->PicWidthInMbs ); + + for( Stage = 1; Stage <= CONCEALMENT_STAGES; Stage++ ) + { + PerStageConcealmentRatio = 0; + CurrentConcealmentMethodFrame = GetCurrentConcealmentMethodFrame( IntraInterConc, ConcealmentMethod, Stage ); + + for( y_block4x4 = 0; y_block4x4 < mb_height * ( MB_BLOCK_SIZE / BlockSize ); y_block4x4++ ) + { + y = y_block4x4 * BlockSize; //!< get y cordinates + for( x_block4x4 = 0; x_block4x4 < mb_width * ( MB_BLOCK_SIZE / BlockSize ); x_block4x4++ ) + { + x = x_block4x4 * BlockSize; + + // Init variables + memset( SAD_Values, 0, NCONCEALMENTMETHODS * sizeof(int) ); + memset( predMB, 0, NCONCEALMENTMETHODS * sizeof(imgpel *) ); + usedList[LIST_0] = 0; + usedList[LIST_1] = 0; + + // Update coordinates of the current concealed macroblock + idxMB = ( y / MB_BLOCK_SIZE ) * mb_width + ( x / MB_BLOCK_SIZE ); + updateMBData( p_Vid, IntraInterConc->SliceDestiantion, idxMB, x, y, BLOCK_SIZE ); + currMB = &( p_Vid->mb_data[idxMB] ); + currMB->mb.x = (short) ( x / BlockSize ); + currMB->mb.y = (short) ( y / BlockSize ); + currMB->block_y = currMB->mb.y * BlockSize; + currMB->pix_c_y = currMB->mb.y * p_Vid->mb_cr_size_y / 4; + currMB->block_x = currMB->mb.x * BlockSize; + currMB->pix_c_x = currMB->mb.x * p_Vid->mb_cr_size_x / 4; + currMB->p_Slice = pSliceDestiantion; + + //Check if the current block have been already concealed + if( BlockConcealmentState->imgY[y_block4x4][x_block4x4] != 0 ) + continue; + + CurrentConcealmentMethod = GetCurrentConcealmentMethodBlockBased( IntraInterConc, CurrentConcealmentMethodFrame, y_block4x4, x_block4x4 ); + + for( List = LIST_1; List >= LIST_0; List-- ) + { + if( CurrentConcealmentMethod == MOTION_INTRAVIEW ) + { + if( List == LIST_1 ) + continue; + getMVIntra( IntraInterConc, List, y_block4x4, x_block4x4, MV ); + if( MV[2] < 0 ) + continue; + } + else if( CurrentConcealmentMethod == MOTION_INTRAVIEW_DISP ) + { + if( List == LIST_1 ) + continue; + getMVIntra( IntraInterConc, List, y_block4x4, x_block4x4, MV ); + if( MV[2] < 0 ) + continue; + currRef = pSliceDestiantion->listX[List][MV[2]]; + if( currRef->view_id == Dest->view_id ) + continue; + } + else if( CurrentConcealmentMethod == MOTION_INTERVIEW ) + { + if( InterRefFrame->slice_type != B_SLICE && List == LIST_1 ) + continue; + getMVInter( IntraInterConc, List, y_block4x4, x_block4x4, MV ); + if( MV[2] < 0 ) + continue; + } + else if( CurrentConcealmentMethod == ENHANCED_MVE ) + { + if( List == LIST_1 ) + continue; + getMVENHANCEDMVE( IntraInterConc, List, y_block4x4, x_block4x4, MV ); + if( MV[2] < 0 ) + continue; + } + else if( CurrentConcealmentMethod == INTERVIEW_COPY ) + { + getMVInterview( IntraInterConc, List, y_block4x4, x_block4x4, MV ); + if( MV[2] < 0 ) + continue; + } + else if( CurrentConcealmentMethod == ZERO_MV_CONC ) + { + MV[0] = MV[1] = 0; + MV[2] = IntraInterConc->MVExtrapolationRefIdx[0]; + } + else if( CurrentConcealmentMethod == CONCEALMV ) + { + if( List == LIST_1 ) + continue; + + ConcealMotionVector( p_Vid, Dest, pSliceDestiantion, y_block4x4, x_block4x4, p_Vid->p_Inp->MVConcealmentMode, LIST_0, MV, + IntraInterConc->MVExtrapolationRefIdx[0] ); + //Dont need scalling because the vectors have already been modified + + BlockConcealmentState->imgY[y_block4x4][x_block4x4] = 1; + } + else if( CurrentConcealmentMethod == NONE_CONCEAL ) + { + continue; + } + else + { + continue; + } + + if( MV[2] >= 0 ) + { + usedList[List] = 1; + currRef = pSliceDestiantion->listX[List][MV[2]]; + + //copy motion vector + Dest->mv_info[y_block4x4][x_block4x4].mv[List].mv_x = MV[0]; + Dest->mv_info[y_block4x4][x_block4x4].mv[List].mv_y = MV[1]; + Dest->mv_info[y_block4x4][x_block4x4].ref_idx[List] = MV[2]; + + if( currRef == p_Vid->no_reference_picture ) + continue; + + usedList[List] = 1; + + buildPredblockRegionYUV( p_Vid, currRef, currMB, MV, x, y, storeYUV[0][List], BlockSize, IntraInterConc->tmp_storeYUV, 1 ); + + } + } + + switch( usedList[LIST_0] + usedList[LIST_1] ) + { + case 1: // List 0 or List 1 + predMB[0] = storeYUV[0][usedList[LIST_1]]; + break; + case 2: // Both List 0 and 1 + ferc_bi_prediction( storeYUV[0][LIST_0], storeYUV[0][LIST_0], 1, storeYUV[0][LIST_1], 1, BlockSize, BlockSize, 1 ); + predMB[0] = storeYUV[0][LIST_0]; + break; + default: + continue; + } + PerStageConcealmentRatio++; + //! Insert the block in the image + copyPredblockRegionYUVDestination( Dest, predMB[0], x, y, BLOCK_SIZE ); + BlockConcealmentState->imgY[y_block4x4][x_block4x4] = 1; + + } + } + PerStageConcealmentRatio = PerStageConcealmentRatio / (double) ( mb_height * ( MB_BLOCK_SIZE / BlockSize ) * mb_width * ( MB_BLOCK_SIZE / BlockSize ) ); + printf( "[FERC] Stage %d : %4.2f\n", Stage, PerStageConcealmentRatio ); + ShowStageDebug( p_Vid, Dest, Stage, "Debug" ); + fflush( stdout ); + + } + +} + +/***************************************************************************************************************** + * Function to conceal the missing frame using the motion vector from base and auxiliary view + * using multiple iterations + *****************************************************************************************************************/ +void Conceal_by_Iterations( VideoParameters *p_Vid, INTER_INTRA_CONC_T *IntraInterConc, StorablePicture *Dest, int ConcealmentMethod ) +{ + void (*getMV)( INTER_INTRA_CONC_T *, int, int, int, int * ); + //! Reference frames + //! Storable picture to save the disparity maps + StorablePicture *BlockConcealmentState = IntraInterConc->BlockConcealmentState; //!< State of the lost image blocks + + //! Motion concealment loop variables + int Stage = 0, mb_height, mb_width; + int x_block4x4, y_block4x4; + int nConcealed, DiffType, DifferenceThreshold; + unsigned int MaxBlockNumber = ( p_Vid->width * p_Vid->height / iabs2( BLOCK_SIZE ) ); + int MotionDispFlag = MOTION; + //! Initialize the variables + mb_width = Dest->PicWidthInMbs; + mb_height = ( Dest->PicSizeInMbs ) / ( Dest->PicWidthInMbs ); + + //! Get temporal and spatial difference + GetTemporalDifferenceFrame( p_Vid, IntraInterConc, 0, (double) ( p_Vid->p_Inp->TemporalThreshold ) / 100 ); + GetSpatialDifferenceFrame( p_Vid, IntraInterConc, 0, (double) ( p_Vid->p_Inp->SpatialThreshold ) / 100 ); + //IntraInterConc->DifferenceThresholds[TEMPORAL_DIFF] = 256; + // IntraInterConc->DifferenceThresholds[SPATIAL_DIFF] = 5; + + //! Alloc memory for temp blocks + BlockConcealmentState = IntraInterConc->BlockConcealmentState; + + //! Initialize the variables + mb_width = Dest->PicWidthInMbs; + mb_height = ( Dest->PicSizeInMbs ) / ( Dest->PicWidthInMbs ); + nConcealed = 0; + + nConcealed = MSEBasedConcealmentStage( p_Vid, IntraInterConc ); + ShowStageDebug( p_Vid, Dest, Stage, "MSEBased" ); + printf( "[FERC] Stage %d : %4.2f\n", Stage++, (double) ( nConcealed ) / (double) ( MaxBlockNumber ) ); + + // nConcealed = 0; + // //! Loop Through Spatial and Temporal Differences - Temporal First + // for (DiffType = imin(TEMPORAL_DIFF, SPATIAL_DIFF); DiffType <= imax(TEMPORAL_DIFF, SPATIAL_DIFF); DiffType++) + // { + // nConcealed = 0; + // DifferenceThreshold = IntraInterConc->DifferenceThresholds[DiffType]; + // DifferenceThreshold = 255; + // for (y_block4x4 = 0; y_block4x4 < mb_height * MB_BLOCK_SIZE / BLOCK_SIZE; y_block4x4++) + // { + // for (x_block4x4 = 0; x_block4x4 < mb_width * MB_BLOCK_SIZE / BLOCK_SIZE; x_block4x4++) + // { + // if (BlockConcealmentState->imgY[y_block4x4][x_block4x4] == 0) + // { + // nConcealed += ConcealBasedOnDifference(p_Vid, IntraInterConc, DifferenceThreshold, x_block4x4, y_block4x4, 3, DiffType); + // } + // } + // } + // if (DiffType == TEMPORAL_DIFF) + // { + // ShowStageDebug(p_Vid, Dest, Stage, "TemporalDiff"); + // PrintDebugPGM( p_Vid, IntraInterConc, Dest, "TemporalDiff"); + // printf("[FERC] Stage %d : %4.2f\n", Stage++, (double) (nConcealed) / (double) (MaxBlockNumber)); + // + // } + // if (DiffType == SPATIAL_DIFF) + // { + // ShowStageDebug(p_Vid, Dest, Stage, "SpatialDiff"); + // PrintDebugPGM( p_Vid, IntraInterConc, Dest, "SpatialDiff"); + // printf("[FERC] Stage %d : %4.2f\n", Stage++, (double) (nConcealed) / (double) (MaxBlockNumber)); + // } + // } + + nConcealed = 0; + DifferenceThreshold = 255; + for( y_block4x4 = 0; y_block4x4 < mb_height * MB_BLOCK_SIZE / BLOCK_SIZE; y_block4x4++ ) + { + for( x_block4x4 = 0; x_block4x4 < mb_width * MB_BLOCK_SIZE / BLOCK_SIZE; x_block4x4++ ) + { + if( BlockConcealmentState->imgY[y_block4x4][x_block4x4] == 0 ) + { + nConcealed += ConcealBasedOnDifference( p_Vid, IntraInterConc, DifferenceThreshold, x_block4x4, y_block4x4, 4, TEMPORAL_DIFF ); + } + } + } + ShowStageDebug( p_Vid, Dest, Stage, "TemporalDiff2" ); + printf( "[FERC] Stage %d : %4.2f\n", Stage++, (double) ( nConcealed ) / (double) ( MaxBlockNumber ) ); + + //nConcealed = 0; + // //! Only use the disparity vectors of the INTRA reference frame + // DifferenceThreshold = 20; + // for (y_block4x4 = 0; y_block4x4 < mb_height * MB_BLOCK_SIZE / BLOCK_SIZE; y_block4x4++) + // { + // for (x_block4x4 = 0; x_block4x4 < mb_width * MB_BLOCK_SIZE / BLOCK_SIZE; x_block4x4++) + // { + // if (BlockConcealmentState->imgY[y_block4x4][x_block4x4] == 0) + // { + // nConcealed += ConcealBasedOnINTRADisparity(p_Vid, IntraInterConc, x_block4x4, y_block4x4); + // } + // } + // } + // ShowStageDebug(p_Vid, Dest, Stage, "TemporalDiffDispOnly"); + // printf("[FERC] Stage %d : %4.2f\n", Stage++, (double) (nConcealed) / (double) (MaxBlockNumber)); + + nConcealed = 0; + if( IntraInterConc->SpatialDifference->mean_value > IntraInterConc->TemporalDifference->mean_value ) + { + printf( "[FERC] Using Motion in Stage %d\n", Stage ); + getMV = getMVIntra; + MotionDispFlag = MOTION; + } + else + { + printf( "[FERC] Using Disparity in Stage %d\n", Stage ); + getMV = getMVInterview; + MotionDispFlag = DISPARITY; + } + + for( y_block4x4 = 0; y_block4x4 < mb_height * MB_BLOCK_SIZE / BLOCK_SIZE; y_block4x4++ ) + { + for( x_block4x4 = 0; x_block4x4 < mb_width * MB_BLOCK_SIZE / BLOCK_SIZE; x_block4x4++ ) + { + if( BlockConcealmentState->imgY[y_block4x4][x_block4x4] == 0 ) + { + nConcealed += ConcealBasedOnDisparityOrMotion( p_Vid, IntraInterConc, x_block4x4, y_block4x4, MotionDispFlag ); + } + } + } + ShowStageDebug( p_Vid, Dest, Stage, "Disparity or Motion" ); + printf( "[FERC] Stage %d : %4.2f\n", Stage++, (double) ( nConcealed ) / (double) ( MaxBlockNumber ) ); + + nConcealed = 0; + for( y_block4x4 = 0; y_block4x4 < mb_height * MB_BLOCK_SIZE / BLOCK_SIZE; y_block4x4++ ) + { + for( x_block4x4 = 0; x_block4x4 < mb_width * MB_BLOCK_SIZE / BLOCK_SIZE; x_block4x4++ ) + { + if( BlockConcealmentState->imgY[y_block4x4][x_block4x4] == 0 ) + { + nConcealed += ConcealBasedOnINTRAandZeroMV( p_Vid, IntraInterConc, x_block4x4, y_block4x4 ); + } + } + } + ShowStageDebug( p_Vid, Dest, Stage, "INTRA or ZeroMV" ); + printf( "[FERC] Stage %d : %4.2f\n", Stage++, (double) ( nConcealed ) / (double) ( MaxBlockNumber ) ); + + // MeanDisparityValue = IntraInterConc->DisparityAux->mean_value; + + // for (Iterations = 0; nConcealed < MaxBlockNumber && Iterations <= 5; Iterations++) + // { + // BlockSizeThreshold = 128; + // for (BlockSizeThreshold = 32; BlockSizeThreshold >= 4; BlockSizeThreshold -= BLOCK_SIZE) + // { + // for (y_block4x4 = 0; y_block4x4 < mb_height * MB_BLOCK_SIZE / BLOCK_SIZE; y_block4x4++) + // { + // for (x_block4x4 = 0; x_block4x4 < mb_width * MB_BLOCK_SIZE / BLOCK_SIZE; x_block4x4++) + // { + // if (BlockConcealmentState->imgY[y_block4x4][x_block4x4] == 0 && x_block4x4 * BLOCK_SIZE > MeanDisparityValue) + // { + // getCurrentCluster(p_Vid, IntraInterConc,/*Same Condition=0*/ + // 0, x_block4x4, y_block4x4, &BlockSize); + // //if( BlockSize == 0 || BlockSize > BlockSizeThreshold) + // if (BlockSize != BlockSizeThreshold) + // continue; + // + // nConcealed += ConcealCurrentClusterDisparitySearch(p_Vid, IntraInterConc, x_block4x4 * BLOCK_SIZE, y_block4x4 * BLOCK_SIZE, BlockSize, 32, 1, 1); + // } + // } + // } + // } + // } + // ShowStageDebug(p_Vid, Dest, Stage++, "DisparitySearch"); + + // BlockSizeThreshold = 32; + // for (Iterations = 0; nConcealed < MaxBlockNumber && Iterations <= 5; Iterations++) + // { + // for (y_block4x4 = 0; y_block4x4 < mb_height * MB_BLOCK_SIZE / BLOCK_SIZE; y_block4x4++) + // { + // for (x_block4x4 = 0; x_block4x4 < mb_width * MB_BLOCK_SIZE / BLOCK_SIZE; x_block4x4++) + // { + // if (BlockConcealmentState->imgY[y_block4x4][x_block4x4] == 0) + // { + // getCurrentCluster(p_Vid, IntraInterConc,/*Same Condition=0*/ + // 0, x_block4x4, y_block4x4, &BlockSize); + // if (BlockSize == 0) + // continue; + // if (BlockSize > BlockSizeThreshold) + // BlockSize = BlockSizeThreshold; + // + // nConcealed += ConcealCurrentClusterMotionSearch(p_Vid, IntraInterConc, x_block4x4 * BLOCK_SIZE, y_block4x4 * BLOCK_SIZE, BlockSize, 64); + // } + // } + // } + // } + // ShowStageDebug(p_Vid, Dest, Stage++, "MotionSearch"); + +} + +void Conceal_by_IntraInterMotionCopy_V2( VideoParameters *p_Vid, StorablePicture *Source/*Ignored*/, StorablePicture *Dest ) +{ + //! Base concealment variables + DecodedPictureBuffer *p_Dpb = p_Vid->p_Dpb_layer[Dest->view_id]; + ConcealParameters *p_FrameConc = p_Vid->p_FrameConc; + INTER_INTRA_CONC_T *IntraInterConc = NULL; + StorablePicture *IntraRefFrame, *InterRefFrame, *InterRefFrameMinus1; + + int BlockSize, mb_height, mb_width, x_block4x4, y_block4x4; + + //! Temporally pointers to predicted blocks + double MotionRatio = 0; + int ConcealmentMethod = p_FrameConc->ConcealmentMode; + + // Reference Slices - Store the information about pseudo Slices + // corresponding to the reference Frames + Slice *pSliceInterViewReference = NULL, *pSliceIntraViewReference = NULL, *pSliceDestiantion = Dest->PicSlice; + + //! Alloc Data structures + allocIntraInterConcData( p_Vid, &IntraInterConc, Dest ); + + //! Get concealment references + IntraRefFrame = fercGetPictureForConcealFromDpb( p_Vid->p_Dpb_layer[Dest->view_id], Dest->poc, Dest->view_id, CLOSEST_FUTURE, 0, NULL ); + + if( p_Vid->active_subset_sps->num_views_minus1 > 1 ) + { + int iLayer, currView, bestRefLayer, BestDeltaView = 99; + currView = GetViewIdx( p_Vid, Dest->view_id ); + for( iLayer = Dest->view_id - 1; iLayer >= 0; iLayer-- ) + { + InterRefFrame = fercGetPictureForConcealFromDpb( p_Vid->p_Dpb_layer[iLayer], Dest->poc, iLayer, EXACT, 1, NULL ); + + if( !InterRefFrame->concealed_pic ) + { + if( iabs( currView - GetViewIdx( p_Vid, iLayer ) ) < BestDeltaView ) + { + BestDeltaView = iabs( currView - GetViewIdx( p_Vid, iLayer ) ); + bestRefLayer = iLayer; + } + } + } + InterRefFrame = fercGetPictureForConcealFromDpb( p_Vid->p_Dpb_layer[bestRefLayer], Dest->poc, bestRefLayer, EXACT, 1, NULL ); + } + else + { //! Stereo video + InterRefFrame = fercGetPictureForConcealFromDpb( p_Vid->p_Dpb_layer[BASE_VIEW_IDX], Dest->poc, BASE_VIEW_IDX, EXACT, 1, NULL ); + } + + InterRefFrameMinus1 = fercGetPictureForConcealFromDpb( p_Vid->p_Dpb_layer[InterRefFrame->view_id], IntraRefFrame->poc, InterRefFrame->view_id, EXACT, 1, + NULL ); + + IntraInterConc->IntraRefFrame = IntraRefFrame; + IntraInterConc->InterRefFrame = InterRefFrame; + IntraInterConc->InterRefFrameMinus1 = InterRefFrameMinus1; + + if( !IntraRefFrame && !InterRefFrame ) + { + printf( "Error: Cannot conceal missing frame! Exit\n" ); + Dest->have_pic = 0; + return; + } + + initInterIntraReferenceLists( p_Vid, IntraInterConc, Dest ); + + pSliceIntraViewReference = IntraInterConc->SliceIntraViewReference; + pSliceInterViewReference = IntraInterConc->SliceInterViewReference; + pSliceDestiantion = IntraInterConc->SliceDestiantion; + + if( ConcealmentMethod == MOTION_ITERATIONS ) + { + //! Get temporal and spatial difference + GetTemporalDifferenceFrame( p_Vid, IntraInterConc, 0, (double) ( p_Vid->p_Inp->TemporalThreshold ) / 100 ); + GetSpatialDifferenceFrame( p_Vid, IntraInterConc, 0, (double) ( p_Vid->p_Inp->SpatialThreshold ) / 100 ); + } + + //! Get motion information of the base view frame + if( ConcealmentMethod == MOTION_SMART_MOTION_BASED ) + { + printf( "[FERC] Parsing base view MV values \n" ); + parse_mv_values( p_Vid, pSliceInterViewReference, InterRefFrame, 0 ); + + MotionRatio = ( (double) ( p_Vid->IntraBlocks ) - 15.0 ) * 0.6 + ( (double) ( p_Vid->NoZeroMVsCount ) - 9 ) * 0.4; + + if( MotionRatio >= 0 ) + ConcealmentMethod = MOTION_INTERVIEW_MVE_INTERCOPY; + else + ConcealmentMethod = MOTION_INTERVIEW_FIRST; + + printf( "[FERC] MotionRatio: %2.1f Using method %d.\n", MotionRatio, ConcealmentMethod ); + } + + //! Project the Motion Vectors + IntraInterConc->IntraMVCandidates.mv_info = NULL; + IntraInterConc->IntraMVCandidates.mv_info_count = NULL; + + if( UseThisMotion( ConcealmentMethod, MOTION_INTRAVIEW ) ) + { + printf( "[FERC] Using frame %2d|%2d for Concealment Reference \n", IntraRefFrame->poc, IntraRefFrame->view_id ); + getINTRAMotionVectorCandidates( p_Vid, IntraRefFrame, pSliceIntraViewReference, Dest, &( IntraInterConc->IntraMVCandidates ), NULL, + p_Vid->p_Inp->MotionVectorCandidates, 1 ); + + } + + if( UseThisMotion( ConcealmentMethod, MOTION_INTERVIEW ) ) + { + printf( "[FERC] Using frame %2d|%2d for Concealment Reference \n", InterRefFrame->poc, InterRefFrame->view_id ); + getDisparityINTERConcealment( p_Vid, IntraInterConc ); + if( p_Vid->p_Inp->DisparityReference == 0 ) + { + getINTERMotionVectorCandidates( p_Vid, InterRefFrame, pSliceInterViewReference, Dest, IntraInterConc->DisparityBlockwiseBase, + &( IntraInterConc->InterMVCandidates ), 1 ); + } + } + + if( UseThisMotion( ConcealmentMethod, ENHANCED_MVE ) ) + { + getDisparityMap( p_Vid, IntraInterConc, IntraInterConc->IntraRefFrame, IntraInterConc->InterRefFrame->view_id, &( IntraInterConc->DisparityAuxMinus1 ) ); + + IntraInterConc->DisparityBlockwiseAuxMinus1 = alloc_storable_picture( p_Vid, FRAME, p_Vid->width / BLOCK_SIZE, p_Vid->height / BLOCK_SIZE, + p_Vid->width_cr / BLOCK_SIZE, p_Vid->height_cr / BLOCK_SIZE, 1 ); + + GetBest4x4Disparity( p_Vid, IntraInterConc->DisparityAuxMinus1, IntraInterConc->DisparityBlockwiseAuxMinus1, IntraInterConc->DisparityAuxMinus1->mean_value, + 0, 0, BLOCK_SIZE, p_Vid->p_Inp->Disparity4x4BlockSize ); + + //showStorablePicture(p_Vid, IntraInterConc->DisparityAuxMinus1, 0, 0, "Disparity Aux T-1", 0); + + getINTERINTRAMotionVectorCandidates( p_Vid, IntraInterConc, &( IntraInterConc->InterIntraMVCandidates ) ); + + } + + if( UseThisMotion( ConcealmentMethod, INTERVIEW_COPY ) ) + { + int aux = IntraInterConc->DisparityReference; + IntraInterConc->DisparityReference = 1; + getDisparityINTERConcealment( p_Vid, IntraInterConc ); + IntraInterConc->DisparityReference = aux; + } + + BlockSize = 4; + mb_width = Dest->PicWidthInMbs; + mb_height = ( Dest->PicSizeInMbs ) / ( Dest->PicWidthInMbs ); + for( y_block4x4 = 0; y_block4x4 < mb_height * ( MB_BLOCK_SIZE / BlockSize ); y_block4x4++ ) + { + for( x_block4x4 = 0; x_block4x4 < mb_width * ( MB_BLOCK_SIZE / BlockSize ); x_block4x4++ ) + { + Dest->mv_info[y_block4x4][x_block4x4].mv[LIST_0].mv_x = 0; + Dest->mv_info[y_block4x4][x_block4x4].mv[LIST_0].mv_y = 0; + Dest->mv_info[y_block4x4][x_block4x4].ref_idx[LIST_0] = -1; + Dest->mv_info[y_block4x4][x_block4x4].mv[LIST_1].mv_x = 0; + Dest->mv_info[y_block4x4][x_block4x4].mv[LIST_1].mv_y = 0; + Dest->mv_info[y_block4x4][x_block4x4].ref_idx[LIST_1] = -1; + } + } + + //if (ConcealmentMethod == MOTION_ITERATIONS) + //Conceal_by_Iterations(p_Vid, IntraInterConc, Dest, ConcealmentMethod); + //else + Conceal_by_Stages( p_Vid, IntraInterConc, Dest, ConcealmentMethod ); + + //parse_mv_prediction( p_Vid, pSliceDestiantion, Dest ); + parse_mv_values( p_Vid, pSliceDestiantion, Dest, 1 ); + + fercSaveListsInfo( p_Vid, pSliceDestiantion ); + free_storable_picture( IntraInterConc->DisparityBase ); + free_storable_picture( IntraInterConc->DisparityAux ); + + freeIntraInterConcData( p_Vid, IntraInterConc ); + + Dest->have_pic = 1; + + return; +} + +//for( Iterations=0; nConcealed < MaxBlockNumber && Iterations <= 5; Iterations++ ) +//{ +// for( BlockSizeThreshold=BLOCK_SIZE; BlockSizeThreshold<=8; BlockSizeThreshold+=BLOCK_SIZE ) +// { +// for(y_block4x4=0;y_block4x4imgY[y_block4x4][x_block4x4] == 0 && x_block4x4 * BLOCK_SIZE > MeanDisparityValue ) +// { +// BlockSize = BLOCK_SIZE; +// //getCurrentCluster ( p_Vid, IntraInterConc,/*Same Condition=0*/0, x_block4x4, y_block4x4, &BlockSize ); +// //if( BlockSize == 0 || BlockSize > BlockSizeThreshold) +// // continue; +// nConcealed += ConcealCurrentClusterDisparitySearch ( p_Vid, IntraInterConc, x_block4x4 * BLOCK_SIZE, y_block4x4 * BLOCK_SIZE, BlockSize, 16, 1 ); +// //showStorablePicture( p_Vid , Dest , 0 , 1 , "Concealed Frame", 0); +// } +// } +// } +// //showStorablePicture( p_Vid , Dest , 0 , 1 , "Concealed Frame", 0); +// } +//} +//ShowStageDebug( p_Vid, Dest, Stage++, "DisparitySearch2" ); + +//BlockSizeThreshold = MB_BLOCK_SIZE; +//while( BlockSizeThreshold <= MB_BLOCK_SIZE ) +//{ +// for(y_block4x4=0;y_block4x4 BlockSizeThreshold) +// continue; + +// nConcealed += ConcealCurrentClusterOnePredictor ( p_Vid, IntraInterConc, x_block4x4 * BLOCK_SIZE, y_block4x4 * BLOCK_SIZE, BlockSize ); +// } +// } +// BlockSizeThreshold *= 2; +//} + +//for( iyMB=0; iyMBPicWidthInMbs + ixMB; + +// updateMBData( p_Vid, pSliceDestiantion, idx_mb, x, y, BLOCK_SIZE ); + +// iBlock = 0; +// while( iBlock < iabs2(MB_BLOCK_SIZE)/iabs2(BLOCK_SIZE) ) +// { +// x = ixMB * MB_BLOCK_SIZE + BLOCK_IDX_MB[iBlock][0] * BLOCK_SIZE; +// y = iyMB * MB_BLOCK_SIZE + BLOCK_IDX_MB[iBlock][1] * BLOCK_SIZE; + +// y_block4x4 = y / BLOCK_SIZE; +// x_block4x4 = x / BLOCK_SIZE; + +// getCurrentClusterSafer( p_Vid, IntraInterConc, ixMB, iyMB, iBlock, &BlockSize, MSE_Threshold, MV); + +// currRef = pSliceDestiantion->listX[LIST_0][MV[2]]; + +// if( BlockSize >= BlockSizeThreshold ) +// { +// for(y4x4=0; y4x4imgY[y_block4x4+y4x4][x_block4x4+x4x4] == 1 ) +// { +// int a = 0; +// } +// EnhancedbuildPredblock4x4YUV( p_Vid, MV, x + x4x4*BLOCK_SIZE , y + y4x4*BLOCK_SIZE, storeYUV[LIST_0][0], LIST_0, idx_mb, IntraInterConc->tmp_storeYUV); +// copyPredblockRegionYUVDestination(Dest, storeYUV[LIST_0][0], x + x4x4*BLOCK_SIZE, y + y4x4*BLOCK_SIZE, BLOCK_SIZE); +// BlockConcealmentState->imgY[y_block4x4+y4x4][x_block4x4+x4x4] = 1; +// nConcealed++; +// } +// } +// iBlock += iabs2( BlockSize/BLOCK_SIZE ); +// } +// else +// { +// iBlock += 1; +// } +// } +// } +//} +//showStorablePicture( p_Vid , Dest , 0 , 1 , "Concealed Frame", 0); + diff --git a/ldecod/src/ferc/ferc_used_code.txt b/ldecod/src/ferc/ferc_used_code.txt new file mode 100644 index 0000000..ab128cf --- /dev/null +++ b/ldecod/src/ferc/ferc_used_code.txt @@ -0,0 +1,734 @@ + +/*! + ************************************************************************ + * \file ferc_proposed.c + * + * \brief + * Unused code + * + * \author + * - Joao Carreira + * + ************************************************************************ + */ + +static int computeSADNeighborsWithoutEdges(StorablePicture *Destination, StorablePicture *BlockState, int x, int y, int Block_Size) +{ + int ii, jj; + int sad = 0; + int yBlock4x4 = y / Block_Size, xBlock4x4 = x / Block_Size; + int discard; + + // ***** ***** + // ***** * * + // ***** * * + // ***** ***** + + //! Left Neighbor + if (xBlock4x4 != 0 && BlockState->imgY[yBlock4x4][xBlock4x4 - 1] != 0) + { + for (ii = 0; ii < Block_Size; ii++) + { + sad += iabs(Destination->imgY[y + ii][x - 1] - Destination->imgY[y + ii][x]); + } + } + //! Right Neighbor + if ((x + BLOCK_SIZE) != Destination->size_x && BlockState->imgY[yBlock4x4][xBlock4x4 + 1] != 0) + { + for (ii = 0; ii < BLOCK_SIZE; ii++) + { + sad += iabs(Destination->imgY[y + ii][x + BLOCK_SIZE - 1] - Destination->imgY[y + ii][x + BLOCK_SIZE]); + } + } + + // ***** + // ***** + // ***** + // ***** + // ***** + // * * + // * * + // ***** + + //! Top Neighbor + if (y != 0 && BlockState->imgY[yBlock4x4 - 1][xBlock4x4] != 0) + { + for (jj = 0; jj < Block_Size; jj++) + { + sad += iabs(Destination->imgY[y - 1][x + jj] - Destination->imgY[y][x + jj]); + } + } + //! Bottom Neighbor + if ((y + BLOCK_SIZE) != Destination->size_y && BlockState->imgY[yBlock4x4 + 1][xBlock4x4] != 0) + { + for (jj = 0; jj < BLOCK_SIZE; jj++) + { + sad += iabs(Destination->imgY[y + BLOCK_SIZE][x + jj] - Destination->imgY[y + BLOCK_SIZE - 1][x + jj]); + } + } + + if (sad == 0) + { + int a = 0; + } + return sad; +} + +static int computeSADNeighborsWithEdges(StorablePicture *Destination, StorablePicture *BlockState, int x, int y, int Block_Size, StorablePicture *Edges) +{ + int ii, jj; + int sad = 0; + int yBlock4x4 = y / Block_Size, xBlock4x4 = x / Block_Size; + int discard; + + // ***** ***** + // ***** * * + // ***** * * + // ***** ***** + + //! Left Neighbor + if (xBlock4x4 != 0 && BlockState->imgY[yBlock4x4][xBlock4x4 - 1] != 0) + { + for (ii = 0; ii < Block_Size; ii++) + { + //! Check the egdes + discard = 0; + for (jj = -Block_Size / 2; jj <= Block_Size / 2; jj++) + { + if (Edges->imgY[y + ii][x + jj] >= 128) + { + discard = 1; + break; + } + } + if (discard) + continue; + sad += iabs(Destination->imgY[y + ii][x - 1] - Destination->imgY[y + ii][x]); + } + } + //! Right Neighbor + if ((x + BLOCK_SIZE) != Destination->size_x && BlockState->imgY[yBlock4x4][xBlock4x4 + 1] != 0) + { + for (ii = 0; ii < BLOCK_SIZE; ii++) + { + //! Check the egdes + discard = 0; + for (jj = -Block_Size / 2; jj <= Block_Size / 2; jj++) + { + if (Edges->imgY[y + ii][x + BLOCK_SIZE + jj] >= 128) + { + discard = 1; + break; + } + } + if (discard) + continue; + sad += iabs(Destination->imgY[y + ii][x + BLOCK_SIZE - 1] - Destination->imgY[y + ii][x + BLOCK_SIZE]); + } + } + + // ***** + // ***** + // ***** + // ***** + // ***** + // * * + // * * + // ***** + + //! Top Neighbor + if (y != 0 && BlockState->imgY[yBlock4x4 - 1][xBlock4x4] != 0) + { + for (jj = 0; jj < Block_Size; jj++) + { + //! Check the egdes + discard = 0; + for (ii = -Block_Size / 2; ii <= Block_Size / 2; ii++) + { + if (Edges->imgY[y + ii][x + jj] >= 128) + { + discard = 1; + break; + } + } + if (discard) + continue; + sad += iabs(Destination->imgY[y - 1][x + jj] - Destination->imgY[y][x + jj]); + } + } + //! Bottom Neighbor + if ((y + BLOCK_SIZE) != Destination->size_y && BlockState->imgY[yBlock4x4 + 1][xBlock4x4] != 0) + { + for (jj = 0; jj < BLOCK_SIZE; jj++) + { + //! Check the egdes + discard = 0; + for (ii = -Block_Size / 2; ii <= Block_Size / 2; ii++) + { + if (Edges->imgY[y + BLOCK_SIZE + ii][x + jj] >= 128) + { + discard = 1; + break; + } + } + if (discard) + continue; + sad += iabs(Destination->imgY[y + BLOCK_SIZE][x + jj] - Destination->imgY[y + BLOCK_SIZE - 1][x + jj]); + } + } + return sad; +} + +static double BlockVariance(StorablePicture *Picture, int BlockSize, int xPel, int yPel) +{ + int i, j; + double n_elem, average_n, average_n_minus1, variance_n; + imgpel **ImgY = Picture->imgY; + + if (xPel + BlockSize > Picture->size_x || yPel + BlockSize > Picture->size_y) + return INT_MAX; + + n_elem = 0.0; + average_n = 0; + variance_n = 0; + + for (i = yPel; i < yPel + BlockSize; i++) + { + for (j = xPel; j < xPel + BlockSize; j++) + { + /* On-line average and population variation */ + n_elem++; + average_n_minus1 = average_n; + average_n = average_n_minus1 + ((ImgY[i][j] - average_n_minus1) / n_elem); + variance_n = (n_elem - 1) * variance_n + (ImgY[i][j] - average_n) * (ImgY[i][j] - average_n_minus1); + variance_n = variance_n / n_elem; + } + } + return variance_n; +} + +static int ConcealmentDecision(VideoParameters *p_Vid, int ConcealmentMethod, StorablePicture *IntraRefFrame, StorablePicture *InterRefFrame, + StorablePicture *Disparity, StorablePicture *Disparity_4x4, MotionPropInfo Matrix_MV_Info, int y_block4x4, int x_block4x4, int Block_Size) +{ + int use_IntraMotionCopy = 1, use_InterMotionCopy = 1; + int mv_info_count, src_mv_y, src_mv_x; + int DispDir = p_Vid->p_Inp->DisparityDirection; + + if (ConcealmentMethod != MOTION_INTERVIEW_FIRST && ConcealmentMethod != MOTION_INTRA_INTER_SAD) + return ConcealmentMethod; + + src_mv_y = y_block4x4; + src_mv_x = x_block4x4; + + //! Check wich concealment strategies are valid + if ((mv_info_count = Matrix_MV_Info.mv_info_count[y_block4x4][x_block4x4]) < 0) + { + use_IntraMotionCopy = 0; + } + + src_mv_y = y_block4x4; + src_mv_x = i_min_max(x_block4x4 * Block_Size + DispDir * (int) (Disparity_4x4->imgY[y_block4x4][x_block4x4]), 0, p_Vid->width - 1); + src_mv_x = src_mv_x / Block_Size + (((src_mv_x % Block_Size) & 0x2) >> 1); + + if (InterRefFrame->mv_info[src_mv_y][src_mv_x].ref_idx[LIST_0] < 0 + && (InterRefFrame->slice_type != B_SLICE || InterRefFrame->mv_info[src_mv_y][src_mv_x].ref_idx[LIST_1] < 0)) + use_InterMotionCopy = 0; + + if ((use_IntraMotionCopy + use_InterMotionCopy) == 0) + { + //return MOTION_INTERVIEW_COPY; + return CONCEALMV; + } + else if ((use_IntraMotionCopy + use_InterMotionCopy) == 1) + { + if (use_IntraMotionCopy) + return MOTION_INTRAVIEW; + else + return MOTION_INTERVIEW; + } + else + { + if (ConcealmentMethod == MOTION_INTERVIEW_FIRST) + return MOTION_INTERVIEW; + + if (InterRefFrame->mv_info[src_mv_y][src_mv_x].mv[LIST_0].mv_x == Matrix_MV_Info.mv_info[y_block4x4][x_block4x4][mv_info_count].mv[LIST_0].mv_x + && InterRefFrame->mv_info[src_mv_y][src_mv_x].mv[LIST_0].mv_y == Matrix_MV_Info.mv_info[y_block4x4][x_block4x4][mv_info_count].mv[LIST_0].mv_y + && InterRefFrame->mv_info[src_mv_y][src_mv_x].ref_idx[LIST_0]) + { + //Same vectors + return MOTION_INTERVIEW; + } + + return (MOTION_INTRAVIEW + MOTION_INTERVIEW); + + } +} + +static int ConcealCurrentClusterDisparitySearch(VideoParameters *p_Vid, INTER_INTRA_CONC_T *IntraInterConc, int x_pos, int y_pos, int BlockSize, + int SearchRange, int Inter_NotIntra, int BLockWindow) +{ + StorablePicture *Disparity = IntraInterConc->DisparityBlockwiseAux; + StorablePicture *BlockConcealmentState = IntraInterConc->BlockConcealmentState; + StorablePicture *InterRefFrame = IntraInterConc->InterRefFrame; + StorablePicture *Destination = IntraInterConc->Destination; + StorablePicture *DifferenceMap = IntraInterConc->SpatialDifference; + imgpel *storeYUV = IntraInterConc->storeYUV[LIST_0][0]; + int count, y, x, diff, curr_mb_idx, d, x_block, y_block, xx_b, yy_b, num, disp; + double SAD, MinSAD = 999; + int iRet = 0, MV[3], xFinalDisp; + double CurrentDiff; + int DifferenceThreshold; + + x_block = x_pos / BLOCK_SIZE; + y_block = y_pos / BLOCK_SIZE; + + disp = 0; + num = 0; + for (yy_b = y_block; yy_b < y_block + BlockSize / BLOCK_SIZE; yy_b++) + { + for (xx_b = x_block; xx_b < x_block + BlockSize / BLOCK_SIZE; xx_b++) + { + disp += Disparity->imgY[yy_b][xx_b]; + if (disp == 255) + return 0; + num++; + } + } + disp /= num; + + CurrentDiff = 0; + count = 0; + for (y = imax(0, y_block - BLockWindow) * BLOCK_SIZE; y < imin(DifferenceMap->size_y / BLOCK_SIZE, y_block + BLockWindow + 1) * BLOCK_SIZE; y++) + { + for (x = imax(0, x_block - BLockWindow) * BLOCK_SIZE; x < imin(DifferenceMap->size_x / BLOCK_SIZE, x_block + BLockWindow + 1) * BLOCK_SIZE; x++) + { + if (y >= 0 && y < DifferenceMap->size_y && x >= 0 && x < DifferenceMap->size_x) + { + diff = (int) (DifferenceMap->imgY[y][x]); + CurrentDiff = ((double) (CurrentDiff * count + diff)) / ((double) count + 1); + count++; + } + } + } + + xFinalDisp = disp; + //if( CurrentDiff > IntraInterConc->DifferenceThresholds[SPATIAL_DIFF] ) + { + MinSAD = INT_MAX; + for (d = -SearchRange; d <= SearchRange; d++) + { + if ((x_pos - (disp + d)) < 0 || (x_pos - (disp + d)) >= Destination->size_x) + continue; + SAD = computeSADOverlap(IntraInterConc, Destination, x_pos, y_pos, InterRefFrame, x_pos - (disp + d), y_pos, BlockSize, BlockSize + 4 * BLOCK_SIZE); + if (SAD <= MinSAD) + { + MinSAD = SAD; + xFinalDisp = disp + d; + } + } + } + + if (Inter_NotIntra) + { + MV[0] = (-xFinalDisp) * 4; + MV[1] = 0; + MV[2] = 0; + } + else + { + //xFinalDisp /= /BLOCK_SIZE; + //! For 4x4 blocks + xFinalDisp = xFinalDisp / BLOCK_SIZE + (((xFinalDisp % BLOCK_SIZE) & 0x2) >> 1); + } + + for (yy_b = y_block; yy_b < y_block + BlockSize / BLOCK_SIZE; yy_b++) + { + for (xx_b = x_block; xx_b < x_block + BlockSize / BLOCK_SIZE; xx_b++) + { + curr_mb_idx = yy_b * BLOCK_SIZE / MB_BLOCK_SIZE * IntraInterConc->Destination->PicWidthInMbs + xx_b * BLOCK_SIZE / MB_BLOCK_SIZE; + + if (!Inter_NotIntra) + { + getMVInter(IntraInterConc, LIST_0, y_block, x_block - xFinalDisp, MV); + if (MV[2] < 0) + continue; + } + + EnhancedbuildPredblock4x4YUV(p_Vid, MV, xx_b * BLOCK_SIZE, yy_b * BLOCK_SIZE, storeYUV, LIST_0, curr_mb_idx, IntraInterConc->tmp_storeYUV); + copyPredblockRegionYUVDestination(IntraInterConc->Destination, storeYUV, xx_b * BLOCK_SIZE, yy_b * BLOCK_SIZE, BLOCK_SIZE); + + // set motion vector in the concealed picture + IntraInterConc->Destination->mv_info[yy_b][xx_b].mv[LIST_0].mv_x = MV[0]; + IntraInterConc->Destination->mv_info[yy_b][xx_b].mv[LIST_0].mv_y = MV[1]; + IntraInterConc->Destination->mv_info[yy_b][xx_b].ref_idx[LIST_0] = MV[2]; + + IntraInterConc->BlockConcealmentState->imgY[yy_b][xx_b] = 1; + iRet++; + } + } + return iRet; +} + +static int ConcealCurrentClusterMotionSearch(VideoParameters *p_Vid, INTER_INTRA_CONC_T *IntraInterConc, int x_pos, int y_pos, int BlockSize, int SearchRange) +{ + StorablePicture *Disparity = IntraInterConc->DisparityBlockwiseAux; + StorablePicture *BlockConcealmentState = IntraInterConc->BlockConcealmentState; + StorablePicture *IntraRefFrame = IntraInterConc->IntraRefFrame; + StorablePicture *Destination = IntraInterConc->Destination; + imgpel *storeYUV = IntraInterConc->storeYUV[LIST_0][0]; + int curr_mb_idx, i, d, x_block, y_block, xx_b, yy_b, num, disp; + double SAD, MinSAD; + int y_final, x_final; + int iRet = 0, MVCandidate[2], MV[3], MVFinal[3]; + + x_block = x_pos / BLOCK_SIZE; + y_block = y_pos / BLOCK_SIZE; + + disp = 0; + num = 0; + for (yy_b = y_block; yy_b < y_block + BlockSize / BLOCK_SIZE; yy_b++) + { + for (xx_b = x_block; xx_b < x_block + BlockSize / BLOCK_SIZE; xx_b++) + { + disp += Disparity->imgY[yy_b][xx_b]; + num++; + } + } + disp /= num; + + MVCandidate[0] = 0; + MVCandidate[1] = 0; + num = 0; + getMVInter(IntraInterConc, LIST_0, y_block, x_block - disp / BLOCK_SIZE, MV); + if (MV[2] >= 0) + { + MVCandidate[0] += MV[0]; + MVCandidate[1] += MV[1]; + num++; + } + getMVIntra(IntraInterConc, LIST_0, y_block, x_block, MV); + if (MV[2] >= 0) + { + MVCandidate[0] += MV[0]; + MVCandidate[1] += MV[1]; + num++; + } + num = num ? num : 1; + MVCandidate[0] = MV[0] / num; + MVCandidate[1] = MV[1] / num; + + MinSAD = INT_MAX; + for (MV[1] = -SearchRange; MV[1] <= SearchRange; MV[1]++) + { + for (MV[0] = -SearchRange; MV[0] <= SearchRange; MV[0]++) + { + // if( x_pos + MVCandidate[0] + MV[0] ) + SAD = computeSADOverlap(IntraInterConc, Destination, x_pos, y_pos, IntraRefFrame, x_pos + MVCandidate[0] + MV[0], y_pos + MVCandidate[1] + MV[1], + BlockSize, BlockSize + 4 * BLOCK_SIZE); + if (SAD < MinSAD) + { + MinSAD = SAD; + MVFinal[0] = MVCandidate[0] + MV[0]; + MVFinal[1] = MVCandidate[1] + MV[1]; + } + } + } + MVFinal[0] *= 4; + MVFinal[1] *= 4; + MVFinal[2] = IntraInterConc->MVExtrapolationRefIdx[0]; + + for (yy_b = y_block; yy_b < y_block + BlockSize / BLOCK_SIZE; yy_b++) + { + for (xx_b = x_block; xx_b < x_block + BlockSize / BLOCK_SIZE; xx_b++) + { + curr_mb_idx = yy_b * BLOCK_SIZE / MB_BLOCK_SIZE * IntraInterConc->Destination->PicWidthInMbs + xx_b * BLOCK_SIZE / MB_BLOCK_SIZE; + + EnhancedbuildPredblock4x4YUV(p_Vid, MVFinal, xx_b * BLOCK_SIZE, yy_b * BLOCK_SIZE, storeYUV, LIST_0, curr_mb_idx, IntraInterConc->tmp_storeYUV); + copyPredblockRegionYUVDestination(IntraInterConc->Destination, storeYUV, xx_b * BLOCK_SIZE, yy_b * BLOCK_SIZE, BLOCK_SIZE); + + // set motion vector in the concealed picture + IntraInterConc->Destination->mv_info[yy_b][xx_b].mv[LIST_0].mv_x = MVFinal[0]; + IntraInterConc->Destination->mv_info[yy_b][xx_b].mv[LIST_0].mv_y = MVFinal[1]; + IntraInterConc->Destination->mv_info[yy_b][xx_b].ref_idx[LIST_0] = MVFinal[2]; + + IntraInterConc->BlockConcealmentState->imgY[yy_b][xx_b] = 1; + iRet++; + } + } + return iRet; +} + +static void getCurrentCluster(VideoParameters *p_Vid, INTER_INTRA_CONC_T *IntraInterConc, int SameCondition, int x_block, int y_block, int *BlockSize) +{ + StorablePicture *BlockConcealmentState = IntraInterConc->BlockConcealmentState; + int i, j; + int Stop, currBlockSize = 1; + int *curr_mv, mv_intra[3] = { 0, 0, 0 }, mv_inter[3] = { 0, 0, 0 }; + int prev_condition = -1, condition; + + *BlockSize = 0; + //! Find the first block to conceal + if (BlockConcealmentState->imgY[y_block][x_block] == 0 && (x_block == 0 || BlockConcealmentState->imgY[y_block][x_block - 1] != 0) + && (y_block == 0 || BlockConcealmentState->imgY[y_block - 1][x_block] != 0)) + { + Stop = 0; + currBlockSize = 0; + while (1) + { + //! Check Vertical side + for (i = 0; i < currBlockSize + 1; i++) + { + if (y_block + i >= BlockConcealmentState->size_y || x_block + currBlockSize >= BlockConcealmentState->size_x + || BlockConcealmentState->imgY[y_block + i][x_block + currBlockSize] == 1) + { + Stop = 1; + break; + } + + if (SameCondition) + { + getMVIntra(IntraInterConc, LIST_0, y_block + i, x_block + currBlockSize, mv_intra); + getMVInter(IntraInterConc, LIST_0, y_block + i, x_block + currBlockSize, mv_inter); + condition = (mv_intra[2] > 0) + (mv_inter[2] > 0) * 10; + if (prev_condition != -1 && condition != prev_condition) + { + Stop = 1; + break; + } + prev_condition = condition; + } + } + //! Check Horizontal side + for (i = 0; i < currBlockSize + 1; i++) + { + if (y_block + currBlockSize >= BlockConcealmentState->size_y || x_block + i >= BlockConcealmentState->size_x + || BlockConcealmentState->imgY[y_block + currBlockSize][x_block + i] == 1) + { + Stop = 1; + break; + } + + if (SameCondition) + { + getMVIntra(IntraInterConc, LIST_0, y_block + currBlockSize, x_block + i, mv_intra); + getMVInter(IntraInterConc, LIST_0, y_block + currBlockSize, x_block + i, mv_inter); + condition = (mv_intra[2] > 0) + (mv_inter[2] > 0) * 10; + if (prev_condition != -1 && condition != prev_condition) + { + Stop = 1; + break; + } + prev_condition = condition; + } + } + if (Stop) + break; + + currBlockSize++; + } + currBlockSize *= BLOCK_SIZE; + *BlockSize = currBlockSize; + return; + } +} + +static int ConcealCurrentClusterOnePredictor(VideoParameters *p_Vid, INTER_INTRA_CONC_T *IntraInterConc, int x_pos, int y_pos, int BlockSize) +{ + imgpel ***storeYUV = IntraInterConc->storeYUV; + int *curr_mv, mv_intra[3] = { 0, 0, 0 }, mv_inter[3] = { 0, 0, 0 }; + int curr_mb_idx, x_block, y_block, xx_b, yy_b; + int prev_case_metric = -1, case_metric; + int iRet = 0; + void + (*getMV)(INTER_INTRA_CONC_T *IntraInterConc, int list, int y_block, int x_block, int MV[3]); + + x_block = x_pos / BLOCK_SIZE; + y_block = y_pos / BLOCK_SIZE; + + for (xx_b = x_block; xx_b < x_block + BlockSize / BLOCK_SIZE; xx_b++) + { + for (yy_b = y_block; yy_b < y_block + BlockSize / BLOCK_SIZE; yy_b++) + { + getMVIntra(IntraInterConc, LIST_0, yy_b, xx_b, mv_intra); + getMVInter(IntraInterConc, LIST_0, yy_b, xx_b, mv_inter); + + case_metric = (mv_intra[2] > 0) + (mv_inter[2] > 0) * 10; + + if (prev_case_metric != -1 && case_metric != prev_case_metric) + { + return iRet; + } + prev_case_metric = case_metric; + } + } + + switch (case_metric) + { + case 0: // nothing + return iRet; + case 1: // only INTRA + getMV = getMVIntra; + break; + case 10: // only INTER + getMV = getMVInter; + break; + case 11: // both + //getMV = getMVInter; + return iRet; + break; + default: + case_metric = 0; + return iRet; + } + + for (xx_b = x_block; xx_b < x_block + BlockSize / BLOCK_SIZE; xx_b++) + { + for (yy_b = y_block; yy_b < y_block + BlockSize / BLOCK_SIZE; yy_b++) + { + getMV(IntraInterConc, LIST_0, yy_b, xx_b, mv_inter); + + curr_mb_idx = yy_b * BLOCK_SIZE / MB_BLOCK_SIZE * IntraInterConc->Destination->PicWidthInMbs + xx_b * BLOCK_SIZE / MB_BLOCK_SIZE; + + EnhancedbuildPredblock4x4YUV(p_Vid, mv_inter, xx_b * BLOCK_SIZE, yy_b * BLOCK_SIZE, storeYUV[LIST_0][0], LIST_0, curr_mb_idx, + IntraInterConc->tmp_storeYUV); + copyPredblockRegionYUVDestination(IntraInterConc->Destination, storeYUV[LIST_0][0], xx_b * BLOCK_SIZE, yy_b * BLOCK_SIZE, BLOCK_SIZE); + + // set motion vector in the concealed picture + IntraInterConc->Destination->mv_info[yy_b][xx_b].mv[LIST_0].mv_x = mv_inter[0]; + IntraInterConc->Destination->mv_info[yy_b][xx_b].mv[LIST_0].mv_y = mv_inter[1]; + IntraInterConc->Destination->mv_info[yy_b][xx_b].ref_idx[LIST_0] = mv_inter[2]; + + IntraInterConc->BlockConcealmentState->imgY[yy_b][xx_b] = 1; + iRet++; + } + } + return iRet; +} + +static void getImageEdges(VideoParameters *p_Vid, StorablePicture *BaseviewFrame, StorablePicture *Disparity, StorablePicture *EdgesAuxView) +{ + StorablePicture *EdgesBaseView = NULL; + + EdgesBaseView = alloc_storable_picture(p_Vid, FRAME, p_Vid->width, p_Vid->height, p_Vid->width_cr, p_Vid->height_cr, 1); + + cvFindImageContours(p_Vid, BaseviewFrame, EdgesBaseView); + + DisparityCompensation(p_Vid, EdgesBaseView, EdgesAuxView, Disparity, LEFT); + + //showStorablePicture( p_Vid, EdgesBaseView, 0, 0, "Edges Base", 0 ); + //saveStorablePicture( p_Vid , "EdgesBaseView.bmp" , EdgesBaseView , 0 , 0 ); + //showStorablePicture( p_Vid, EdgesAuxView, 0, 0, "Edges Aux", 0 ); + //saveStorablePicture( p_Vid , "EdgesAuxView.bmp" , EdgesAuxView , 0 , 0 ); + + free_storable_picture(EdgesBaseView); + + return; +} + + +static void getCurrentClusterSafer(VideoParameters *p_Vid, INTER_INTRA_CONC_T *IntraInterConc, int x_MB, int y_MB, int block_idx, int *block_size, + double MSEThreshold, int MV[3]) +{ + int cur_idx, max_clust, x_block, y_block, inter_src_block, intra_mv_cnt, tmp_block_size = BLOCK_SIZE; + int mv_intra[3] = { 0, 0, 0 }, mv_inter[3] = { 0, 0, 0 }; + + MV[0] = MV[1] = 0; + MV[2] = -1; + + *block_size = 0; + cur_idx = block_idx; + + max_clust = CLUSTER[block_idx]; + max_clust += block_idx; + + cur_idx = block_idx; + while (cur_idx < max_clust) + { + do + { + y_block = (y_MB * MB_BLOCK_SIZE / BLOCK_SIZE) + BLOCK_IDX_MB[cur_idx][1]; + x_block = (x_MB * MB_BLOCK_SIZE / BLOCK_SIZE) + BLOCK_IDX_MB[cur_idx][0]; + + getMVIntra(IntraInterConc, LIST_0, y_block, x_block, mv_intra); + + getMVInter(IntraInterConc, LIST_0, y_block, x_block, mv_inter); + + if (mv_inter[2] < 0 || mv_intra[2] < 0) + { + return; //!< Cannot conceal anymore + } + + //! Compare mv candidates + //! If MSE Threshold is equal to zero we do not need to compare MSE, just the prediction + if (mv_intra[0] == mv_inter[0] && mv_intra[1] == mv_inter[1]) + { + //! Check reference frames to confirm + if (mv_intra[2] == mv_inter[2]) + { + if ((*block_size) == 0 || (mv_intra[0] == MV[0] && mv_intra[1] == MV[1] && mv_intra[2] == MV[2])) + { + MV[0] = mv_intra[0]; + MV[1] = mv_intra[1]; + MV[2] = mv_intra[2]; + cur_idx++; + continue; + } + } + } + + return; + } while (cur_idx < block_idx + target_cluster(*block_size)); + (*block_size) = (*block_size) ? (*block_size) * 2 : BLOCK_SIZE; + } + return; +} + + +/*! + ************************************************************************ + * \brief + * Compute the block based disparity for each pixel using a square block + * for a symmetric window + * + * The Disparity are normalized between + * [-WindowsSize, WindowsSize] + * Disp = Disp + GlobalWindowsSize + LocalWindowsSize; + ************************************************************************ + */ +static void FullSearchMotionEstimation(imgpel **currImg, imgpel**refImg, int size_x, int size_y, int window, int block_size, int pos_x, int pos_y, + MotionVector *curr_mv) +{ + int x, y, xx, yy, count; + int x_min, x_max, y_min, y_max; + uint16 sad, **sad_vector; + + get_mem2Duint16(&sad_vector, window * 2, window * 2); + + x_min = imax(0, pos_x - window); + y_max = imin(size_y - block_size, pos_y + window); + + y_min = imax(0, pos_y - window); + x_max = imin(size_x - block_size, pos_x + window); + + count = 0; + for (y = y_min; y < y_max; y++) + { + for (x = x_min; x < x_max; x++) + { + + // SAD between the current block and a possible ref block + for (yy = 0; yy <= block_size; yy++) + { + for (xx = 0; xx <= block_size; xx++) + { + sad += iabs(currImg[y + yy][x + xx] - refImg[y + yy][x + xx]); + } + } + (*sad_vector)[count++] = sad; + } + } + qsort(sad_vector, count, sizeof(uint16), sort_by_uint16_up); + // uint16_min_position( sad_vector, unsigned int n_elem, uint16 *min_value, unsigned int *min_pos) + free_mem2Duint16(sad_vector); + return; +} \ No newline at end of file diff --git a/ldecod/src/filehandle.c b/ldecod/src/filehandle.c new file mode 100644 index 0000000..2b5ff2b --- /dev/null +++ b/ldecod/src/filehandle.c @@ -0,0 +1,177 @@ + +/*! + ************************************************************************************** + * \file + * filehandle.c + * \brief + * Trace file handling and standard error handling function. + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * - Karsten Suehring + *************************************************************************************** + */ + +#include "contributors.h" +#include "global.h" +#include "mbuffer.h" + + +#if TRACE + +#include + +/*! +************************************************************************ +* \brief +* decrement trace p_Dec->bitcounter (used for special case in mb aff) +************************************************************************ +*/ +void dectracebitcnt(int count) +{ + p_Dec->bitcounter -= count; +} + +/*! + ************************************************************************ + * \brief + * Tracing bitpatterns for symbols + * A code word has the following format: 0 Xn...0 X2 0 X1 0 X0 1 + ************************************************************************ + */ +void tracebits( + const char *trace_str, //!< tracing information, char array describing the symbol + int len, //!< length of syntax element in bits + int info, //!< infoword of syntax element + int value1) +{ + int i, chars; + // int outint = 1; + + if(len>=64) + { + snprintf(errortext, ET_SIZE, "Length argument to put too long for trace to work"); + error (errortext, 600); + } + + putc('@', p_Dec->p_trace); + chars = fprintf(p_Dec->p_trace, "%i", p_Dec->bitcounter); + while(chars++ < 5) + putc(' ',p_Dec->p_trace); + + chars += fprintf(p_Dec->p_trace, " %s", trace_str); + while(chars++ < 55) + putc(' ',p_Dec->p_trace); + + // Align bitpattern + if(len<15) + { + for(i=0 ; i<15-len ; i++) + fputc(' ', p_Dec->p_trace); + } + + // Print bitpattern + for(i=0 ; ip_trace); + } + // put 1 + fprintf(p_Dec->p_trace, "1"); + + // Print bitpattern + for(i=0 ; i> ((len/2-i)-1))) + fputc('1', p_Dec->p_trace); + else + fputc('0', p_Dec->p_trace); + } + + fprintf(p_Dec->p_trace, " (%3d) \n", value1); + p_Dec->bitcounter += len; + + fflush (p_Dec->p_trace); +} + +/*! + ************************************************************************ + * \brief + * Tracing bitpatterns + ************************************************************************ + */ +void tracebits2( + const char *trace_str, //!< tracing information, char array describing the symbol + int len, //!< length of syntax element in bits + int info) +{ + + int i, chars; + // int outint = 1; + + if(len>=64) + { + snprintf(errortext, ET_SIZE, "Length argument to put too long for trace to work"); + error (errortext, 600); + } + + putc('@', p_Dec->p_trace); + chars = fprintf(p_Dec->p_trace, "%i", p_Dec->bitcounter); + + while(chars++ < 5) + putc(' ',p_Dec->p_trace); + + chars += fprintf(p_Dec->p_trace, " %s", trace_str); + + while(chars++ < 55) + putc(' ',p_Dec->p_trace); + + // Align bitpattern + if(len < 15) + { + for(i = 0; i < 15 - len; i++) + fputc(' ', p_Dec->p_trace); + } + + p_Dec->bitcounter += len; + while (len >= 32) + { + for(i = 0; i < 8; i++) + { + fputc('0', p_Dec->p_trace); + } + len -= 8; + } + + // Print bitpattern + for(i=0 ; i> (len-i-1))) + fputc('1', p_Dec->p_trace); + else + fputc('0', p_Dec->p_trace); + } + + fprintf(p_Dec->p_trace, " (%3d) \n", info); + + fflush (p_Dec->p_trace); +} + +/*! + ************************************************************************ + * \brief + * Tracing information such as motion/ref_idx etc + ************************************************************************ + */ +void trace_info( + SyntaxElement *currSE, //!< syntax element to update + const char *description_str, //!< tracing information, char array describing the symbol + int value1 //!< value to be recorded +) + +{ + char tstring[20]; + sprintf( tstring, "%s%d", description_str, value1); + strncpy(currSE->tracestring, tstring, TRACESTRING_SIZE); +} + +#endif + diff --git a/ldecod/src/fmo.c b/ldecod/src/fmo.c new file mode 100644 index 0000000..d531723 --- /dev/null +++ b/ldecod/src/fmo.c @@ -0,0 +1,555 @@ + +/*! + ***************************************************************************** + * + * \file fmo.c + * + * \brief + * Support for Flexible Macroblock Ordering (FMO) + * + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * - Stephan Wenger stewe@cs.tu-berlin.de + * - Karsten Suehring + ****************************************************************************** + */ + +#include "global.h" +#include "elements.h" +#include "defines.h" +#include "header.h" +#include "fmo.h" +#include "fast_memory.h" + +//#define PRINT_FMO_MAPS + +static void FmoGenerateType0MapUnitMap (VideoParameters *p_Vid, unsigned PicSizeInMapUnits ); +static void FmoGenerateType1MapUnitMap (VideoParameters *p_Vid, unsigned PicSizeInMapUnits ); +static void FmoGenerateType2MapUnitMap (VideoParameters *p_Vid, unsigned PicSizeInMapUnits ); +static void FmoGenerateType3MapUnitMap (VideoParameters *p_Vid, unsigned PicSizeInMapUnits, Slice *currSlice ); +static void FmoGenerateType4MapUnitMap (VideoParameters *p_Vid, unsigned PicSizeInMapUnits, Slice *currSlice ); +static void FmoGenerateType5MapUnitMap (VideoParameters *p_Vid, unsigned PicSizeInMapUnits, Slice *currSlice ); +static void FmoGenerateType6MapUnitMap (VideoParameters *p_Vid, unsigned PicSizeInMapUnits ); + + +/*! + ************************************************************************ + * \brief + * Generates p_Vid->MapUnitToSliceGroupMap + * Has to be called every time a new Picture Parameter Set is used + * + * \param p_Vid + * video encoding parameters for current picture + * + ************************************************************************ + */ +static int FmoGenerateMapUnitToSliceGroupMap (VideoParameters *p_Vid, Slice *currSlice) +{ + seq_parameter_set_rbsp_t* sps = p_Vid->active_sps; + pic_parameter_set_rbsp_t* pps = p_Vid->active_pps; + + unsigned int NumSliceGroupMapUnits; + + NumSliceGroupMapUnits = (sps->pic_height_in_map_units_minus1+1)* (sps->pic_width_in_mbs_minus1+1); + + if (pps->slice_group_map_type == 6) + { + if ((pps->pic_size_in_map_units_minus1 + 1) != NumSliceGroupMapUnits) + { + error ("wrong pps->pic_size_in_map_units_minus1 for used SPS and FMO type 6", 500); + } + } + + // allocate memory for p_Vid->MapUnitToSliceGroupMap + if (p_Vid->MapUnitToSliceGroupMap) + free (p_Vid->MapUnitToSliceGroupMap); + if ((p_Vid->MapUnitToSliceGroupMap = malloc ((NumSliceGroupMapUnits) * sizeof (int))) == NULL) + { + printf ("cannot allocated %d bytes for p_Vid->MapUnitToSliceGroupMap, exit\n", (int) ( (pps->pic_size_in_map_units_minus1+1) * sizeof (int))); + exit (-1); + } + + if (pps->num_slice_groups_minus1 == 0) // only one slice group + { + fast_memset (p_Vid->MapUnitToSliceGroupMap, 0, NumSliceGroupMapUnits * sizeof (int)); + return 0; + } + + switch (pps->slice_group_map_type) + { + case 0: + FmoGenerateType0MapUnitMap (p_Vid, NumSliceGroupMapUnits); + break; + case 1: + FmoGenerateType1MapUnitMap (p_Vid, NumSliceGroupMapUnits); + break; + case 2: + FmoGenerateType2MapUnitMap (p_Vid, NumSliceGroupMapUnits); + break; + case 3: + FmoGenerateType3MapUnitMap (p_Vid, NumSliceGroupMapUnits, currSlice); + break; + case 4: + FmoGenerateType4MapUnitMap (p_Vid, NumSliceGroupMapUnits, currSlice); + break; + case 5: + FmoGenerateType5MapUnitMap (p_Vid, NumSliceGroupMapUnits, currSlice); + break; + case 6: + FmoGenerateType6MapUnitMap (p_Vid, NumSliceGroupMapUnits); + break; + default: + printf ("Illegal slice_group_map_type %d , exit \n", (int) pps->slice_group_map_type); + exit (-1); + } + return 0; +} + + +/*! + ************************************************************************ + * \brief + * Generates p_Vid->MbToSliceGroupMap from p_Vid->MapUnitToSliceGroupMap + * + * \param p_Vid + * video encoding parameters for current picture + * + ************************************************************************ + */ +static int FmoGenerateMbToSliceGroupMap (VideoParameters *p_Vid, Slice *pSlice) +{ + seq_parameter_set_rbsp_t* sps = p_Vid->active_sps; + + unsigned i; + + // allocate memory for p_Vid->MbToSliceGroupMap + if (p_Vid->MbToSliceGroupMap) + free (p_Vid->MbToSliceGroupMap); + + if ((p_Vid->MbToSliceGroupMap = malloc ((p_Vid->PicSizeInMbs) * sizeof (int))) == NULL) + { + printf ("cannot allocate %d bytes for p_Vid->MbToSliceGroupMap, exit\n", (int) ((p_Vid->PicSizeInMbs) * sizeof (int))); + exit (-1); + } + + + if ((sps->frame_mbs_only_flag)|| pSlice->field_pic_flag) + { + int *MbToSliceGroupMap = p_Vid->MbToSliceGroupMap; + int *MapUnitToSliceGroupMap = p_Vid->MapUnitToSliceGroupMap; + for (i=0; iPicSizeInMbs; i++) + { + *MbToSliceGroupMap++ = *MapUnitToSliceGroupMap++; + } + } + else + if (sps->mb_adaptive_frame_field_flag && (!pSlice->field_pic_flag)) + { + for (i=0; iPicSizeInMbs; i++) + { + p_Vid->MbToSliceGroupMap[i] = p_Vid->MapUnitToSliceGroupMap[i/2]; + } + } + else + { + for (i=0; iPicSizeInMbs; i++) + { + p_Vid->MbToSliceGroupMap[i] = p_Vid->MapUnitToSliceGroupMap[(i/(2*p_Vid->PicWidthInMbs))*p_Vid->PicWidthInMbs+(i%p_Vid->PicWidthInMbs)]; + } + } + return 0; +} + + +/*! + ************************************************************************ + * \brief + * FMO initialization: Generates p_Vid->MapUnitToSliceGroupMap and p_Vid->MbToSliceGroupMap. + * + * \param p_Vid + * video encoding parameters for current picture + ************************************************************************ + */ +int fmo_init(VideoParameters *p_Vid, Slice *pSlice) +{ + pic_parameter_set_rbsp_t* pps = p_Vid->active_pps; + +#ifdef PRINT_FMO_MAPS + unsigned i,j; +#endif + + FmoGenerateMapUnitToSliceGroupMap(p_Vid, pSlice); + FmoGenerateMbToSliceGroupMap(p_Vid, pSlice); + + p_Vid->NumberOfSliceGroups = pps->num_slice_groups_minus1 + 1; + +#ifdef PRINT_FMO_MAPS + printf("\n"); + printf("FMO Map (Units):\n"); + + for (j=0; jPicHeightInMapUnits; j++) + { + for (i=0; iPicWidthInMbs; i++) + { + printf("%c",48+p_Vid->MapUnitToSliceGroupMap[i+j*p_Vid->PicWidthInMbs]); + } + printf("\n"); + } + printf("\n"); + printf("FMO Map (Mb):\n"); + + for (j=0; jPicHeightInMbs; j++) + { + for (i=0; iPicWidthInMbs; i++) + { + printf("%c",48 + p_Vid->MbToSliceGroupMap[i + j * p_Vid->PicWidthInMbs]); + } + printf("\n"); + } + printf("\n"); + +#endif + + return 0; +} + + +/*! + ************************************************************************ + * \brief + * Free memory allocated by FMO functions + ************************************************************************ + */ +int FmoFinit(VideoParameters *p_Vid) +{ + if (p_Vid->MbToSliceGroupMap) + { + free (p_Vid->MbToSliceGroupMap); + p_Vid->MbToSliceGroupMap = NULL; + } + if (p_Vid->MapUnitToSliceGroupMap) + { + free (p_Vid->MapUnitToSliceGroupMap); + p_Vid->MapUnitToSliceGroupMap = NULL; + } + return 0; +} + + +/*! + ************************************************************************ + * \brief + * FmoGetNumberOfSliceGroup(p_Vid) + * + * \par p_Vid: + * VideoParameters + ************************************************************************ + */ +int FmoGetNumberOfSliceGroup(VideoParameters *p_Vid) +{ + return p_Vid->NumberOfSliceGroups; +} + + +/*! + ************************************************************************ + * \brief + * FmoGetLastMBOfPicture(p_Vid) + * returns the macroblock number of the last MB in a picture. This + * mb happens to be the last macroblock of the picture if there is only + * one slice group + * + * \par Input: + * None + ************************************************************************ + */ +int FmoGetLastMBOfPicture(VideoParameters *p_Vid) +{ + return FmoGetLastMBInSliceGroup (p_Vid, FmoGetNumberOfSliceGroup(p_Vid)-1); +} + + +/*! + ************************************************************************ + * \brief + * FmoGetLastMBInSliceGroup: Returns MB number of last MB in SG + * + * \par Input: + * SliceGroupID (0 to 7) + ************************************************************************ + */ + +int FmoGetLastMBInSliceGroup (VideoParameters *p_Vid, int SliceGroup) +{ + int i; + + for (i=p_Vid->PicSizeInMbs-1; i>=0; i--) + if (FmoGetSliceGroupId (p_Vid, i) == SliceGroup) + return i; + return -1; + +} + + +/*! + ************************************************************************ + * \brief + * Returns SliceGroupID for a given MB + * + * \param p_Vid + * video encoding parameters for current picture + * \param mb + * Macroblock number (in scan order) + ************************************************************************ + */ +int FmoGetSliceGroupId (VideoParameters *p_Vid, int mb) +{ + assert (mb < (int) p_Vid->PicSizeInMbs); + assert (p_Vid->MbToSliceGroupMap != NULL); + return p_Vid->MbToSliceGroupMap[mb]; +} + + +/*! + ************************************************************************ + * \brief + * FmoGetNextMBBr: Returns the MB-Nr (in scan order) of the next + * MB in the (scattered) Slice, -1 if the slice is finished + * \param p_Vid + * video encoding parameters for current picture + * + * \param CurrentMbNr + * number of the current macroblock + ************************************************************************ + */ +int FmoGetNextMBNr (VideoParameters *p_Vid, int CurrentMbNr) +{ + int SliceGroup = FmoGetSliceGroupId (p_Vid, CurrentMbNr); + + while (++CurrentMbNr<(int)p_Vid->PicSizeInMbs && p_Vid->MbToSliceGroupMap [CurrentMbNr] != SliceGroup) + ; + + if (CurrentMbNr >= (int)p_Vid->PicSizeInMbs) + return -1; // No further MB in this slice (could be end of picture) + else + return CurrentMbNr; +} + + +/*! + ************************************************************************ + * \brief + * Generate interleaved slice group map type MapUnit map (type 0) + * + ************************************************************************ + */ +static void FmoGenerateType0MapUnitMap (VideoParameters *p_Vid, unsigned PicSizeInMapUnits ) +{ + pic_parameter_set_rbsp_t* pps = p_Vid->active_pps; + unsigned iGroup, j; + unsigned i = 0; + do + { + for( iGroup = 0; + (iGroup <= pps->num_slice_groups_minus1) && (i < PicSizeInMapUnits); + i += pps->run_length_minus1[iGroup++] + 1 ) + { + for( j = 0; j <= pps->run_length_minus1[ iGroup ] && i + j < PicSizeInMapUnits; j++ ) + p_Vid->MapUnitToSliceGroupMap[i+j] = iGroup; + } + } + while( i < PicSizeInMapUnits ); +} + + +/*! + ************************************************************************ + * \brief + * Generate dispersed slice group map type MapUnit map (type 1) + * + ************************************************************************ + */ +static void FmoGenerateType1MapUnitMap (VideoParameters *p_Vid, unsigned PicSizeInMapUnits ) +{ + pic_parameter_set_rbsp_t* pps = p_Vid->active_pps; + unsigned i; + for( i = 0; i < PicSizeInMapUnits; i++ ) + { + p_Vid->MapUnitToSliceGroupMap[i] = ((i%p_Vid->PicWidthInMbs)+(((i/p_Vid->PicWidthInMbs)*(pps->num_slice_groups_minus1+1))/2)) + %(pps->num_slice_groups_minus1+1); + } +} + +/*! + ************************************************************************ + * \brief + * Generate foreground with left-over slice group map type MapUnit map (type 2) + * + ************************************************************************ + */ +static void FmoGenerateType2MapUnitMap (VideoParameters *p_Vid, unsigned PicSizeInMapUnits ) +{ + pic_parameter_set_rbsp_t* pps = p_Vid->active_pps; + int iGroup; + unsigned i, x, y; + unsigned yTopLeft, xTopLeft, yBottomRight, xBottomRight; + + for( i = 0; i < PicSizeInMapUnits; i++ ) + p_Vid->MapUnitToSliceGroupMap[ i ] = pps->num_slice_groups_minus1; + + for( iGroup = pps->num_slice_groups_minus1 - 1 ; iGroup >= 0; iGroup-- ) + { + yTopLeft = pps->top_left[ iGroup ] / p_Vid->PicWidthInMbs; + xTopLeft = pps->top_left[ iGroup ] % p_Vid->PicWidthInMbs; + yBottomRight = pps->bottom_right[ iGroup ] / p_Vid->PicWidthInMbs; + xBottomRight = pps->bottom_right[ iGroup ] % p_Vid->PicWidthInMbs; + for( y = yTopLeft; y <= yBottomRight; y++ ) + for( x = xTopLeft; x <= xBottomRight; x++ ) + p_Vid->MapUnitToSliceGroupMap[ y * p_Vid->PicWidthInMbs + x ] = iGroup; + } +} + + +/*! + ************************************************************************ + * \brief + * Generate box-out slice group map type MapUnit map (type 3) + * + ************************************************************************ + */ +static void FmoGenerateType3MapUnitMap (VideoParameters *p_Vid, unsigned PicSizeInMapUnits, Slice *currSlice ) +{ + pic_parameter_set_rbsp_t* pps = p_Vid->active_pps; + unsigned i, k; + int leftBound, topBound, rightBound, bottomBound; + int x, y, xDir, yDir; + int mapUnitVacant; + + unsigned mapUnitsInSliceGroup0 = imin((pps->slice_group_change_rate_minus1 + 1) * currSlice->slice_group_change_cycle, PicSizeInMapUnits); + + for( i = 0; i < PicSizeInMapUnits; i++ ) + p_Vid->MapUnitToSliceGroupMap[ i ] = 2; + + x = ( p_Vid->PicWidthInMbs - pps->slice_group_change_direction_flag ) / 2; + y = ( p_Vid->PicHeightInMapUnits - pps->slice_group_change_direction_flag ) / 2; + + leftBound = x; + topBound = y; + rightBound = x; + bottomBound = y; + + xDir = pps->slice_group_change_direction_flag - 1; + yDir = pps->slice_group_change_direction_flag; + + for( k = 0; k < PicSizeInMapUnits; k += mapUnitVacant ) + { + mapUnitVacant = ( p_Vid->MapUnitToSliceGroupMap[ y * p_Vid->PicWidthInMbs + x ] == 2 ); + if( mapUnitVacant ) + p_Vid->MapUnitToSliceGroupMap[ y * p_Vid->PicWidthInMbs + x ] = ( k >= mapUnitsInSliceGroup0 ); + + if( xDir == -1 && x == leftBound ) + { + leftBound = imax( leftBound - 1, 0 ); + x = leftBound; + xDir = 0; + yDir = 2 * pps->slice_group_change_direction_flag - 1; + } + else + if( xDir == 1 && x == rightBound ) + { + rightBound = imin( rightBound + 1, (int)p_Vid->PicWidthInMbs - 1 ); + x = rightBound; + xDir = 0; + yDir = 1 - 2 * pps->slice_group_change_direction_flag; + } + else + if( yDir == -1 && y == topBound ) + { + topBound = imax( topBound - 1, 0 ); + y = topBound; + xDir = 1 - 2 * pps->slice_group_change_direction_flag; + yDir = 0; + } + else + if( yDir == 1 && y == bottomBound ) + { + bottomBound = imin( bottomBound + 1, (int)p_Vid->PicHeightInMapUnits - 1 ); + y = bottomBound; + xDir = 2 * pps->slice_group_change_direction_flag - 1; + yDir = 0; + } + else + { + x = x + xDir; + y = y + yDir; + } + } + +} + +/*! + ************************************************************************ + * \brief + * Generate raster scan slice group map type MapUnit map (type 4) + * + ************************************************************************ + */ +static void FmoGenerateType4MapUnitMap (VideoParameters *p_Vid, unsigned PicSizeInMapUnits, Slice *currSlice ) +{ + pic_parameter_set_rbsp_t* pps = p_Vid->active_pps; + + unsigned mapUnitsInSliceGroup0 = imin((pps->slice_group_change_rate_minus1 + 1) * currSlice->slice_group_change_cycle, PicSizeInMapUnits); + unsigned sizeOfUpperLeftGroup = pps->slice_group_change_direction_flag ? ( PicSizeInMapUnits - mapUnitsInSliceGroup0 ) : mapUnitsInSliceGroup0; + + unsigned i; + + for( i = 0; i < PicSizeInMapUnits; i++ ) + if( i < sizeOfUpperLeftGroup ) + p_Vid->MapUnitToSliceGroupMap[ i ] = pps->slice_group_change_direction_flag; + else + p_Vid->MapUnitToSliceGroupMap[ i ] = 1 - pps->slice_group_change_direction_flag; + +} + +/*! + ************************************************************************ + * \brief + * Generate wipe slice group map type MapUnit map (type 5) + * + ************************************************************************ + */ +static void FmoGenerateType5MapUnitMap (VideoParameters *p_Vid, unsigned PicSizeInMapUnits, Slice *currSlice ) +{ + pic_parameter_set_rbsp_t* pps = p_Vid->active_pps; + + unsigned mapUnitsInSliceGroup0 = imin((pps->slice_group_change_rate_minus1 + 1) * currSlice->slice_group_change_cycle, PicSizeInMapUnits); + unsigned sizeOfUpperLeftGroup = pps->slice_group_change_direction_flag ? ( PicSizeInMapUnits - mapUnitsInSliceGroup0 ) : mapUnitsInSliceGroup0; + + unsigned i,j, k = 0; + + for( j = 0; j < p_Vid->PicWidthInMbs; j++ ) + for( i = 0; i < p_Vid->PicHeightInMapUnits; i++ ) + if( k++ < sizeOfUpperLeftGroup ) + p_Vid->MapUnitToSliceGroupMap[ i * p_Vid->PicWidthInMbs + j ] = pps->slice_group_change_direction_flag; + else + p_Vid->MapUnitToSliceGroupMap[ i * p_Vid->PicWidthInMbs + j ] = 1 - pps->slice_group_change_direction_flag; + +} + +/*! + ************************************************************************ + * \brief + * Generate explicit slice group map type MapUnit map (type 6) + * + ************************************************************************ + */ +static void FmoGenerateType6MapUnitMap (VideoParameters *p_Vid, unsigned PicSizeInMapUnits ) +{ + pic_parameter_set_rbsp_t* pps = p_Vid->active_pps; + unsigned i; + for (i=0; iMapUnitToSliceGroupMap[i] = pps->slice_group_id[i]; + } +} + diff --git a/ldecod/src/header.c b/ldecod/src/header.c new file mode 100644 index 0000000..aa20857 --- /dev/null +++ b/ldecod/src/header.c @@ -0,0 +1,995 @@ + +/*! + ************************************************************************************* + * \file header.c + * + * \brief + * H.264 Slice headers + * + ************************************************************************************* + */ + +#include "global.h" +#include "elements.h" +#include "defines.h" +#include "fmo.h" +#include "vlc.h" +#include "mbuffer.h" +#include "header.h" + +#include "ctx_tables.h" + + +#if TRACE +#define SYMTRACESTRING(s) strncpy(sym.tracestring,s,TRACESTRING_SIZE) +#else +#define SYMTRACESTRING(s) // do nothing +#endif + +static void ref_pic_list_reordering(Slice *currSlice); +static void pred_weight_table(Slice *currSlice); +#if (MVC_EXTENSION_ENABLE) +static void ref_pic_list_mvc_modification(Slice *currSlice); +#endif + + +/*! + ************************************************************************ + * \brief + * calculate Ceil(Log2(uiVal)) + ************************************************************************ + */ +unsigned CeilLog2( unsigned uiVal) +{ + unsigned uiTmp = uiVal-1; + unsigned uiRet = 0; + + while( uiTmp != 0 ) + { + uiTmp >>= 1; + uiRet++; + } + return uiRet; +} + +unsigned CeilLog2_sf( unsigned uiVal) +{ + unsigned uiTmp = uiVal-1; + unsigned uiRet = 0; + + while( uiTmp > 0 ) + { + uiTmp >>= 1; + uiRet++; + } + return uiRet; +} + +/*! + ************************************************************************ + * \brief + * read the first part of the header (only the pic_parameter_set_id) + * \return + * Length of the first part of the slice header (in bits) + ************************************************************************ + */ +int FirstPartOfSliceHeader(Slice *currSlice) +{ + VideoParameters *p_Vid = currSlice->p_Vid; + byte dP_nr = assignSE2partition[currSlice->dp_mode][SE_HEADER]; + DataPartition *partition = &(currSlice->partArr[dP_nr]); + Bitstream *currStream = partition->bitstream; + int tmp; + + p_Dec->UsedBits= partition->bitstream->frame_bitoffset; // was hardcoded to 31 for previous start-code. This is better. + + // Get first_mb_in_slice + currSlice->start_mb_nr = read_ue_v ("SH: first_mb_in_slice", currStream, &p_Dec->UsedBits); + + tmp = read_ue_v ("SH: slice_type", currStream, &p_Dec->UsedBits); + + if (tmp > 4) tmp -= 5; + + p_Vid->type = currSlice->slice_type = (SliceType) tmp; + + currSlice->pic_parameter_set_id = read_ue_v ("SH: pic_parameter_set_id", currStream, &p_Dec->UsedBits); + + if( p_Vid->separate_colour_plane_flag ) + currSlice->colour_plane_id = read_u_v (2, "SH: colour_plane_id", currStream, &p_Dec->UsedBits); + else + currSlice->colour_plane_id = PLANE_Y; + + return p_Dec->UsedBits; +} + +/*! + ************************************************************************ + * \brief + * read the scond part of the header (without the pic_parameter_set_id + * \return + * Length of the second part of the Slice header in bits + ************************************************************************ + */ +int RestOfSliceHeader(Slice *currSlice) +{ + VideoParameters *p_Vid = currSlice->p_Vid; + InputParameters *p_Inp = currSlice->p_Inp; + seq_parameter_set_rbsp_t *active_sps = p_Vid->active_sps; + + byte dP_nr = assignSE2partition[currSlice->dp_mode][SE_HEADER]; + DataPartition *partition = &(currSlice->partArr[dP_nr]); + Bitstream *currStream = partition->bitstream; + + int val, len; + + currSlice->frame_num = read_u_v (active_sps->log2_max_frame_num_minus4 + 4, "SH: frame_num", currStream, &p_Dec->UsedBits); + + /* Tian Dong: frame_num gap processing, if found */ + if(currSlice->idr_flag) //if (p_Vid->idr_flag) + { +//#if (MVC_EXTENSION_ENABLE) +// int i; +// for(i=1;ipre_frame_num[i] = -1; +// p_Vid->last_ref_pic_poc[i] = 0; +// } +// p_Vid->pre_frame_num[0] = 0; +// p_Vid->last_ref_pic_poc[currSlice->view_id] = 0; +//#else +// p_Vid->last_ref_pic_poc = 0; +// p_Vid->pre_frame_num = currSlice->frame_num; +//#endif +// // picture error concealment +// + assert(currSlice->frame_num == 0); + } + + if (active_sps->frame_mbs_only_flag) + { + p_Vid->structure = FRAME; + currSlice->field_pic_flag=0; + } + else + { + // field_pic_flag u(1) + currSlice->field_pic_flag = read_u_1("SH: field_pic_flag", currStream, &p_Dec->UsedBits); + if (currSlice->field_pic_flag) + { + // bottom_field_flag u(1) + currSlice->bottom_field_flag = (byte) read_u_1("SH: bottom_field_flag", currStream, &p_Dec->UsedBits); + p_Vid->structure = currSlice->bottom_field_flag ? BOTTOM_FIELD : TOP_FIELD; + } + else + { + p_Vid->structure = FRAME; + currSlice->bottom_field_flag = FALSE; + } + } + + currSlice->structure = (PictureStructure) p_Vid->structure; + + currSlice->mb_aff_frame_flag = (active_sps->mb_adaptive_frame_field_flag && (currSlice->field_pic_flag==0)); + //currSlice->mb_aff_frame_flag = p_Vid->mb_aff_frame_flag; + + if (p_Vid->structure == FRAME ) + assert (currSlice->field_pic_flag == 0); + if (p_Vid->structure == TOP_FIELD ) + assert (currSlice->field_pic_flag == 1 && (currSlice->bottom_field_flag == FALSE)); + if (p_Vid->structure == BOTTOM_FIELD) + assert (currSlice->field_pic_flag == 1 && (currSlice->bottom_field_flag == TRUE )); + + if (currSlice->idr_flag) + { + currSlice->idr_pic_id = read_ue_v("SH: idr_pic_id", currStream, &p_Dec->UsedBits); + } +#if (MVC_EXTENSION_ENABLE) + else if ( currSlice->svc_extension_flag == 0 && currSlice->NaluHeaderMVCExt.non_idr_flag == 0 ) + { + currSlice->idr_pic_id = read_ue_v("SH: idr_pic_id", currStream, &p_Dec->UsedBits); + } +#endif + + if (active_sps->pic_order_cnt_type == 0) + { + currSlice->pic_order_cnt_lsb = read_u_v(active_sps->log2_max_pic_order_cnt_lsb_minus4 + 4, "SH: pic_order_cnt_lsb", currStream, &p_Dec->UsedBits); + if( p_Vid->active_pps->bottom_field_pic_order_in_frame_present_flag == 1 && !currSlice->field_pic_flag ) + currSlice->delta_pic_order_cnt_bottom = read_se_v("SH: delta_pic_order_cnt_bottom", currStream, &p_Dec->UsedBits); + else + currSlice->delta_pic_order_cnt_bottom = 0; + } + if( active_sps->pic_order_cnt_type == 1 ) + { + if ( !active_sps->delta_pic_order_always_zero_flag ) + { + currSlice->delta_pic_order_cnt[ 0 ] = read_se_v("SH: delta_pic_order_cnt[0]", currStream, &p_Dec->UsedBits); + if( p_Vid->active_pps->bottom_field_pic_order_in_frame_present_flag == 1 && !currSlice->field_pic_flag ) + currSlice->delta_pic_order_cnt[ 1 ] = read_se_v("SH: delta_pic_order_cnt[1]", currStream, &p_Dec->UsedBits); + else + currSlice->delta_pic_order_cnt[ 1 ] = 0; // set to zero if not in stream + } + else + { + currSlice->delta_pic_order_cnt[ 0 ] = 0; + currSlice->delta_pic_order_cnt[ 1 ] = 0; + } + } + + //! redundant_pic_cnt is missing here + if (p_Vid->active_pps->redundant_pic_cnt_present_flag) + { + currSlice->redundant_pic_cnt = read_ue_v ("SH: redundant_pic_cnt", currStream, &p_Dec->UsedBits); + } + + if(currSlice->slice_type == B_SLICE) + { + currSlice->direct_spatial_mv_pred_flag = read_u_1 ("SH: direct_spatial_mv_pred_flag", currStream, &p_Dec->UsedBits); + } + + currSlice->num_ref_idx_active[LIST_0] = p_Vid->active_pps->num_ref_idx_l0_default_active_minus1 + 1; + currSlice->num_ref_idx_active[LIST_1] = p_Vid->active_pps->num_ref_idx_l1_default_active_minus1 + 1; + + if(currSlice->slice_type == P_SLICE || currSlice->slice_type == SP_SLICE || currSlice->slice_type == B_SLICE) + { + val = read_u_1 ("SH: num_ref_idx_override_flag", currStream, &p_Dec->UsedBits); + if (val) + { + currSlice->num_ref_idx_active[LIST_0] = 1 + read_ue_v ("SH: num_ref_idx_l0_active_minus1", currStream, &p_Dec->UsedBits); + + if(currSlice->slice_type == B_SLICE) + { + currSlice->num_ref_idx_active[LIST_1] = 1 + read_ue_v ("SH: num_ref_idx_l1_active_minus1", currStream, &p_Dec->UsedBits); + } + } + } + if (currSlice->slice_type!=B_SLICE) + { + currSlice->num_ref_idx_active[LIST_1] = 0; + } + +#if (MVC_EXTENSION_ENABLE) + if (currSlice->svc_extension_flag == 0 || currSlice->svc_extension_flag == 1) + ref_pic_list_mvc_modification(currSlice); + else + ref_pic_list_reordering(currSlice); +#else + ref_pic_list_reordering(currSlice); +#endif + + currSlice->weighted_pred_flag = (unsigned short) ((currSlice->slice_type == P_SLICE || currSlice->slice_type == SP_SLICE) + ? p_Vid->active_pps->weighted_pred_flag + : (currSlice->slice_type == B_SLICE && p_Vid->active_pps->weighted_bipred_idc == 1)); + currSlice->weighted_bipred_idc = (unsigned short) (currSlice->slice_type == B_SLICE && p_Vid->active_pps->weighted_bipred_idc > 0); + + if ((p_Vid->active_pps->weighted_pred_flag&&(currSlice->slice_type == P_SLICE|| currSlice->slice_type == SP_SLICE))|| + (p_Vid->active_pps->weighted_bipred_idc==1 && (currSlice->slice_type == B_SLICE))) + { + pred_weight_table(currSlice); + } + + if (currSlice->nal_reference_idc) + dec_ref_pic_marking(p_Vid, currStream, currSlice); + + if (p_Vid->active_pps->entropy_coding_mode_flag && currSlice->slice_type != I_SLICE && currSlice->slice_type != SI_SLICE) + { + currSlice->model_number = read_ue_v("SH: cabac_init_idc", currStream, &p_Dec->UsedBits); + } + else + { + currSlice->model_number = 0; + } + + currSlice->slice_qp_delta = val = read_se_v("SH: slice_qp_delta", currStream, &p_Dec->UsedBits); + //currSlice->qp = p_Vid->qp = 26 + p_Vid->active_pps->pic_init_qp_minus26 + val; + currSlice->qp = 26 + p_Vid->active_pps->pic_init_qp_minus26 + val; + + if ((currSlice->qp < -p_Vid->bitdepth_luma_qp_scale) || (currSlice->qp > 51)) + error ("slice_qp_delta makes slice_qp_y out of range", 500); + + if(currSlice->slice_type == SP_SLICE || currSlice->slice_type == SI_SLICE) + { + if(currSlice->slice_type==SP_SLICE) + { + currSlice->sp_switch = read_u_1 ("SH: sp_for_switch_flag", currStream, &p_Dec->UsedBits); + } + currSlice->slice_qs_delta = val = read_se_v("SH: slice_qs_delta", currStream, &p_Dec->UsedBits); + currSlice->qs = 26 + p_Vid->active_pps->pic_init_qs_minus26 + val; + if ((currSlice->qs < 0) || (currSlice->qs > 51)) + error ("slice_qs_delta makes slice_qs_y out of range", 500); + } + + if ( !HI_intra_only_profile(active_sps->profile_idc, active_sps->constrained_set3_flag) || (p_Inp->intra_profile_deblocking == 1) ) + //then read flags and parameters from bistream + { +#if DPF_PARAM_DISP + printf("deblocking_filter_control_present_flag:%d\n", p_Vid->active_pps->deblocking_filter_control_present_flag); +#endif + if (p_Vid->active_pps->deblocking_filter_control_present_flag) + { + currSlice->DFDisableIdc = (short) read_ue_v ("SH: disable_deblocking_filter_idc", currStream, &p_Dec->UsedBits); + + if (currSlice->DFDisableIdc!=1) + { + currSlice->DFAlphaC0Offset = (short) (2 * read_se_v("SH: slice_alpha_c0_offset_div2", currStream, &p_Dec->UsedBits)); + currSlice->DFBetaOffset = (short) (2 * read_se_v("SH: slice_beta_offset_div2", currStream, &p_Dec->UsedBits)); + } + else + { + currSlice->DFAlphaC0Offset = currSlice->DFBetaOffset = 0; + } + } + else + { + currSlice->DFDisableIdc = currSlice->DFAlphaC0Offset = currSlice->DFBetaOffset = 0; + } +#if DPF_PARAM_DISP + printf("Slice:%d, DFParameters:(%d,%d,%d)\n\n", currSlice->current_slice_nr, currSlice->DFDisableIdc, currSlice->DFAlphaC0Offset, currSlice->DFBetaOffset); +#endif + } + else //By default the Loop Filter is Off + { //444_TEMP_NOTE: change made below. 08/07/07 + //still need to parse the SEs (read flags and parameters from bistream) but will ignore + if (p_Vid->active_pps->deblocking_filter_control_present_flag) + { + currSlice->DFDisableIdc = (short) read_ue_v ("SH: disable_deblocking_filter_idc", currStream, &p_Dec->UsedBits); + + if (currSlice->DFDisableIdc!=1) + { + currSlice->DFAlphaC0Offset = (short) (2 * read_se_v("SH: slice_alpha_c0_offset_div2", currStream, &p_Dec->UsedBits)); + currSlice->DFBetaOffset = (short) (2 * read_se_v("SH: slice_beta_offset_div2", currStream, &p_Dec->UsedBits)); + } + }//444_TEMP_NOTE. the end of change. 08/07/07 + //Ignore the SEs, by default the Loop Filter is Off + currSlice->DFDisableIdc =1; + currSlice->DFAlphaC0Offset = currSlice->DFBetaOffset = 0; + } + + + if (p_Vid->active_pps->num_slice_groups_minus1>0 && p_Vid->active_pps->slice_group_map_type>=3 && + p_Vid->active_pps->slice_group_map_type<=5) + { + len = (active_sps->pic_height_in_map_units_minus1+1)*(active_sps->pic_width_in_mbs_minus1+1)/ + (p_Vid->active_pps->slice_group_change_rate_minus1+1); + if (((active_sps->pic_height_in_map_units_minus1+1)*(active_sps->pic_width_in_mbs_minus1+1))% + (p_Vid->active_pps->slice_group_change_rate_minus1+1)) + len +=1; + + len = CeilLog2(len+1); + + currSlice->slice_group_change_cycle = read_u_v (len, "SH: slice_group_change_cycle", currStream, &p_Dec->UsedBits); + } + p_Vid->PicHeightInMbs = p_Vid->FrameHeightInMbs / ( 1 + currSlice->field_pic_flag ); + p_Vid->PicSizeInMbs = p_Vid->PicWidthInMbs * p_Vid->PicHeightInMbs; + p_Vid->FrameSizeInMbs = p_Vid->PicWidthInMbs * p_Vid->FrameHeightInMbs; + + return p_Dec->UsedBits; +} + + +/*! + ************************************************************************ + * \brief + * read the reference picture reordering information + ************************************************************************ + */ +static void ref_pic_list_reordering(Slice *currSlice) +{ + //VideoParameters *p_Vid = currSlice->p_Vid; + byte dP_nr = assignSE2partition[currSlice->dp_mode][SE_HEADER]; + DataPartition *partition = &(currSlice->partArr[dP_nr]); + Bitstream *currStream = partition->bitstream; + int i, val; + + alloc_ref_pic_list_reordering_buffer(currSlice); + + if (currSlice->slice_type != I_SLICE && currSlice->slice_type != SI_SLICE) + { + val = currSlice->ref_pic_list_reordering_flag[LIST_0] = read_u_1 ("SH: ref_pic_list_reordering_flag_l0", currStream, &p_Dec->UsedBits); + + if (val) + { + i=0; + do + { + val = currSlice->modification_of_pic_nums_idc[LIST_0][i] = read_ue_v("SH: modification_of_pic_nums_idc_l0", currStream, &p_Dec->UsedBits); + if (val==0 || val==1) + { + currSlice->abs_diff_pic_num_minus1[LIST_0][i] = read_ue_v("SH: abs_diff_pic_num_minus1_l0", currStream, &p_Dec->UsedBits); + } + else + { + if (val==2) + { + currSlice->long_term_pic_idx[LIST_0][i] = read_ue_v("SH: long_term_pic_idx_l0", currStream, &p_Dec->UsedBits); + } + } + i++; + // assert (i>currSlice->num_ref_idx_active[LIST_0]); + } while (val != 3); + } + } + + if (currSlice->slice_type == B_SLICE) + { + val = currSlice->ref_pic_list_reordering_flag[LIST_1] = read_u_1 ("SH: ref_pic_list_reordering_flag_l1", currStream, &p_Dec->UsedBits); + + if (val) + { + i=0; + do + { + val = currSlice->modification_of_pic_nums_idc[LIST_1][i] = read_ue_v("SH: modification_of_pic_nums_idc_l1", currStream, &p_Dec->UsedBits); + if (val==0 || val==1) + { + currSlice->abs_diff_pic_num_minus1[LIST_1][i] = read_ue_v("SH: abs_diff_pic_num_minus1_l1", currStream, &p_Dec->UsedBits); + } + else + { + if (val==2) + { + currSlice->long_term_pic_idx[LIST_1][i] = read_ue_v("SH: long_term_pic_idx_l1", currStream, &p_Dec->UsedBits); + } + } + i++; + // assert (i>currSlice->num_ref_idx_active[LIST_1]); + } while (val != 3); + } + } + + // set reference index of redundant slices. + if(currSlice->redundant_pic_cnt && (currSlice->slice_type != I_SLICE) ) + { + currSlice->redundant_slice_ref_idx = currSlice->abs_diff_pic_num_minus1[LIST_0][0] + 1; + } +} + +/*! + ************************************************************************ + * \brief + * read the MVC reference picture reordering information + ************************************************************************ + */ +#if (MVC_EXTENSION_ENABLE) +static void ref_pic_list_mvc_modification(Slice *currSlice) +{ + //VideoParameters *p_Vid = currSlice->p_Vid; + byte dP_nr = assignSE2partition[currSlice->dp_mode][SE_HEADER]; + DataPartition *partition = &(currSlice->partArr[dP_nr]); + Bitstream *currStream = partition->bitstream; + int i, val; + + alloc_ref_pic_list_reordering_buffer(currSlice); + + if ((currSlice->slice_type % 5) != I_SLICE && (currSlice->slice_type % 5) != SI_SLICE) + { + val = currSlice->ref_pic_list_reordering_flag[LIST_0] = read_u_1 ("SH: ref_pic_list_modification_flag_l0", currStream, &p_Dec->UsedBits); + + if (val) + { + i=0; + do + { + val = currSlice->modification_of_pic_nums_idc[LIST_0][i] = read_ue_v("SH: modification_of_pic_nums_idc_l0", currStream, &p_Dec->UsedBits); + if (val==0 || val==1) + { + currSlice->abs_diff_pic_num_minus1[LIST_0][i] = read_ue_v("SH: abs_diff_pic_num_minus1_l0", currStream, &p_Dec->UsedBits); + } + else + { + if (val==2) + { + currSlice->long_term_pic_idx[LIST_0][i] = read_ue_v("SH: long_term_pic_idx_l0", currStream, &p_Dec->UsedBits); + } + else if (val==4 || val==5) + { + currSlice->abs_diff_view_idx_minus1[LIST_0][i] = read_ue_v("SH: abs_diff_view_idx_minus1_l0", currStream, &p_Dec->UsedBits); + } + } + i++; + // assert (i>img->num_ref_idx_l0_active); + } while (val != 3); + } + } + + if ((currSlice->slice_type % 5) == B_SLICE) + { + val = currSlice->ref_pic_list_reordering_flag[LIST_1] = read_u_1 ("SH: ref_pic_list_reordering_flag_l1", currStream, &p_Dec->UsedBits); + + if (val) + { + i=0; + do + { + val = currSlice->modification_of_pic_nums_idc[LIST_1][i] = read_ue_v("SH: modification_of_pic_nums_idc_l1", currStream, &p_Dec->UsedBits); + if (val==0 || val==1) + { + currSlice->abs_diff_pic_num_minus1[LIST_1][i] = read_ue_v("SH: abs_diff_pic_num_minus1_l1", currStream, &p_Dec->UsedBits); + } + else + { + if (val==2) + { + currSlice->long_term_pic_idx[LIST_1][i] = read_ue_v("SH: long_term_pic_idx_l1", currStream, &p_Dec->UsedBits); + } + else if (val==4 || val==5) + { + currSlice->abs_diff_view_idx_minus1[LIST_1][i] = read_ue_v("SH: abs_diff_view_idx_minus1_l1", currStream, &p_Dec->UsedBits); + } + } + i++; + // assert (i>img->num_ref_idx_l1_active); + } while (val != 3); + } + } + + // set reference index of redundant slices. + if(currSlice->redundant_pic_cnt && (currSlice->slice_type != I_SLICE) ) + { + currSlice->redundant_slice_ref_idx = currSlice->abs_diff_pic_num_minus1[LIST_0][0] + 1; + } +} +#endif + +static void reset_wp_params(Slice *currSlice) +{ + int i,comp; + int log_weight_denom; + + for (i=0; iluma_log2_weight_denom : currSlice->chroma_log2_weight_denom; + currSlice->wp_weight[0][i][comp] = 1 << log_weight_denom; + currSlice->wp_weight[1][i][comp] = 1 << log_weight_denom; + } + } +} + +/*! + ************************************************************************ + * \brief + * read the weighted prediction tables + ************************************************************************ + */ +static void pred_weight_table(Slice *currSlice) +{ + VideoParameters *p_Vid = currSlice->p_Vid; + seq_parameter_set_rbsp_t *active_sps = p_Vid->active_sps; + byte dP_nr = assignSE2partition[currSlice->dp_mode][SE_HEADER]; + DataPartition *partition = &(currSlice->partArr[dP_nr]); + Bitstream *currStream = partition->bitstream; + int luma_weight_flag_l0, luma_weight_flag_l1, chroma_weight_flag_l0, chroma_weight_flag_l1; + int i,j; + + currSlice->luma_log2_weight_denom = (unsigned short) read_ue_v ("SH: luma_log2_weight_denom", currStream, &p_Dec->UsedBits); + currSlice->wp_round_luma = currSlice->luma_log2_weight_denom ? 1<<(currSlice->luma_log2_weight_denom - 1): 0; + + if ( 0 != active_sps->chroma_format_idc) + { + currSlice->chroma_log2_weight_denom = (unsigned short) read_ue_v ("SH: chroma_log2_weight_denom", currStream, &p_Dec->UsedBits); + currSlice->wp_round_chroma = currSlice->chroma_log2_weight_denom ? 1<<(currSlice->chroma_log2_weight_denom - 1): 0; + } + + reset_wp_params(currSlice); + + for (i=0; inum_ref_idx_active[LIST_0]; i++) + { + luma_weight_flag_l0 = read_u_1("SH: luma_weight_flag_l0", currStream, &p_Dec->UsedBits); + + if (luma_weight_flag_l0) + { + currSlice->wp_weight[LIST_0][i][0] = read_se_v ("SH: luma_weight_l0", currStream, &p_Dec->UsedBits); + currSlice->wp_offset[LIST_0][i][0] = read_se_v ("SH: luma_offset_l0", currStream, &p_Dec->UsedBits); + currSlice->wp_offset[LIST_0][i][0] = currSlice->wp_offset[LIST_0][i][0]<<(p_Vid->bitdepth_luma - 8); + } + else + { + currSlice->wp_weight[LIST_0][i][0] = 1 << currSlice->luma_log2_weight_denom; + currSlice->wp_offset[LIST_0][i][0] = 0; + } + + if (active_sps->chroma_format_idc != 0) + { + chroma_weight_flag_l0 = read_u_1 ("SH: chroma_weight_flag_l0", currStream, &p_Dec->UsedBits); + + for (j=1; j<3; j++) + { + if (chroma_weight_flag_l0) + { + currSlice->wp_weight[LIST_0][i][j] = read_se_v("SH: chroma_weight_l0", currStream, &p_Dec->UsedBits); + currSlice->wp_offset[LIST_0][i][j] = read_se_v("SH: chroma_offset_l0", currStream, &p_Dec->UsedBits); + currSlice->wp_offset[LIST_0][i][j] = currSlice->wp_offset[LIST_0][i][j]<<(p_Vid->bitdepth_chroma-8); + } + else + { + currSlice->wp_weight[LIST_0][i][j] = 1<chroma_log2_weight_denom; + currSlice->wp_offset[LIST_0][i][j] = 0; + } + } + } + } + if ((currSlice->slice_type == B_SLICE) && p_Vid->active_pps->weighted_bipred_idc == 1) + { + for (i=0; inum_ref_idx_active[LIST_1]; i++) + { + luma_weight_flag_l1 = read_u_1("SH: luma_weight_flag_l1", currStream, &p_Dec->UsedBits); + + if (luma_weight_flag_l1) + { + currSlice->wp_weight[LIST_1][i][0] = read_se_v ("SH: luma_weight_l1", currStream, &p_Dec->UsedBits); + currSlice->wp_offset[LIST_1][i][0] = read_se_v ("SH: luma_offset_l1", currStream, &p_Dec->UsedBits); + currSlice->wp_offset[LIST_1][i][0] = currSlice->wp_offset[LIST_1][i][0]<<(p_Vid->bitdepth_luma-8); + } + else + { + currSlice->wp_weight[LIST_1][i][0] = 1<luma_log2_weight_denom; + currSlice->wp_offset[LIST_1][i][0] = 0; + } + + if (active_sps->chroma_format_idc != 0) + { + chroma_weight_flag_l1 = read_u_1 ("SH: chroma_weight_flag_l1", currStream, &p_Dec->UsedBits); + + for (j=1; j<3; j++) + { + if (chroma_weight_flag_l1) + { + currSlice->wp_weight[LIST_1][i][j] = read_se_v("SH: chroma_weight_l1", currStream, &p_Dec->UsedBits); + currSlice->wp_offset[LIST_1][i][j] = read_se_v("SH: chroma_offset_l1", currStream, &p_Dec->UsedBits); + currSlice->wp_offset[LIST_1][i][j] = currSlice->wp_offset[LIST_1][i][j]<<(p_Vid->bitdepth_chroma-8); + } + else + { + currSlice->wp_weight[LIST_1][i][j] = 1<chroma_log2_weight_denom; + currSlice->wp_offset[LIST_1][i][j] = 0; + } + } + } + } + } +} + + +/*! + ************************************************************************ + * \brief + * read the memory control operations + ************************************************************************ + */ +void dec_ref_pic_marking(VideoParameters *p_Vid, Bitstream *currStream, Slice *pSlice) +{ + int val; + + DecRefPicMarking_t *tmp_drpm,*tmp_drpm2; + + // free old buffer content + while (pSlice->dec_ref_pic_marking_buffer) + { + tmp_drpm=pSlice->dec_ref_pic_marking_buffer; + + pSlice->dec_ref_pic_marking_buffer=tmp_drpm->Next; + free (tmp_drpm); + } + +#if (MVC_EXTENSION_ENABLE) + if ( pSlice->idr_flag || (pSlice->svc_extension_flag == 0 && pSlice->NaluHeaderMVCExt.non_idr_flag == 0) ) +#else + if (pSlice->idr_flag) +#endif + { + pSlice->no_output_of_prior_pics_flag = read_u_1("SH: no_output_of_prior_pics_flag", currStream, &p_Dec->UsedBits); + p_Vid->no_output_of_prior_pics_flag = pSlice->no_output_of_prior_pics_flag; + pSlice->long_term_reference_flag = read_u_1("SH: long_term_reference_flag", currStream, &p_Dec->UsedBits); + } + else + { + pSlice->adaptive_ref_pic_buffering_flag = read_u_1("SH: adaptive_ref_pic_buffering_flag", currStream, &p_Dec->UsedBits); + if (pSlice->adaptive_ref_pic_buffering_flag) + { + // read Memory Management Control Operation + do + { + tmp_drpm=(DecRefPicMarking_t*)calloc (1,sizeof (DecRefPicMarking_t)); + tmp_drpm->Next=NULL; + + val = tmp_drpm->memory_management_control_operation = read_ue_v("SH: memory_management_control_operation", currStream, &p_Dec->UsedBits); + + if ((val==1)||(val==3)) + { + tmp_drpm->difference_of_pic_nums_minus1 = read_ue_v("SH: difference_of_pic_nums_minus1", currStream, &p_Dec->UsedBits); + } + if (val==2) + { + tmp_drpm->long_term_pic_num = read_ue_v("SH: long_term_pic_num", currStream, &p_Dec->UsedBits); + } + + if ((val==3)||(val==6)) + { + tmp_drpm->long_term_frame_idx = read_ue_v("SH: long_term_frame_idx", currStream, &p_Dec->UsedBits); + } + if (val==4) + { + tmp_drpm->max_long_term_frame_idx_plus1 = read_ue_v("SH: max_long_term_pic_idx_plus1", currStream, &p_Dec->UsedBits); + } + + // add command + if (pSlice->dec_ref_pic_marking_buffer==NULL) + { + pSlice->dec_ref_pic_marking_buffer=tmp_drpm; + } + else + { + tmp_drpm2=pSlice->dec_ref_pic_marking_buffer; + while (tmp_drpm2->Next!=NULL) tmp_drpm2=tmp_drpm2->Next; + tmp_drpm2->Next=tmp_drpm; + } + + } + while (val != 0); + } + } +} + +/*! + ************************************************************************ + * \brief + * To calculate the poc values + * based upon JVT-F100d2 + * POC200301: Until Jan 2003, this function will calculate the correct POC + * values, but the management of POCs in buffered pictures may need more work. + * \return + * none + ************************************************************************ + */ +void decode_poc(VideoParameters *p_Vid, Slice *pSlice) +{ + seq_parameter_set_rbsp_t *active_sps = p_Vid->active_sps; + int i; + // for POC mode 0: + unsigned int MaxPicOrderCntLsb = (1<<(active_sps->log2_max_pic_order_cnt_lsb_minus4+4)); + + switch ( active_sps->pic_order_cnt_type ) + { + case 0: // POC MODE 0 + // 1st + if(pSlice->idr_flag) + { + p_Vid->PrevPicOrderCntMsb = 0; + p_Vid->PrevPicOrderCntLsb = 0; + } + else + { + if (p_Vid->last_has_mmco_5) + { + if (p_Vid->last_pic_bottom_field) + { + p_Vid->PrevPicOrderCntMsb = 0; + p_Vid->PrevPicOrderCntLsb = 0; + } + else + { + p_Vid->PrevPicOrderCntMsb = 0; + p_Vid->PrevPicOrderCntLsb = pSlice->toppoc; + } + } + } + // Calculate the MSBs of current picture + if( pSlice->pic_order_cnt_lsb < p_Vid->PrevPicOrderCntLsb && + ( p_Vid->PrevPicOrderCntLsb - pSlice->pic_order_cnt_lsb ) >= ( MaxPicOrderCntLsb / 2 ) ) + pSlice->PicOrderCntMsb = p_Vid->PrevPicOrderCntMsb + MaxPicOrderCntLsb; + else if ( pSlice->pic_order_cnt_lsb > p_Vid->PrevPicOrderCntLsb && + ( pSlice->pic_order_cnt_lsb - p_Vid->PrevPicOrderCntLsb ) > ( MaxPicOrderCntLsb / 2 ) ) + pSlice->PicOrderCntMsb = p_Vid->PrevPicOrderCntMsb - MaxPicOrderCntLsb; + else + pSlice->PicOrderCntMsb = p_Vid->PrevPicOrderCntMsb; + + // 2nd + + if(pSlice->field_pic_flag==0) + { //frame pix + pSlice->toppoc = pSlice->PicOrderCntMsb + pSlice->pic_order_cnt_lsb; + pSlice->bottompoc = pSlice->toppoc + pSlice->delta_pic_order_cnt_bottom; + pSlice->ThisPOC = pSlice->framepoc = (pSlice->toppoc < pSlice->bottompoc)? pSlice->toppoc : pSlice->bottompoc; // POC200301 + } + else if (pSlice->bottom_field_flag == FALSE) + { //top field + pSlice->ThisPOC= pSlice->toppoc = pSlice->PicOrderCntMsb + pSlice->pic_order_cnt_lsb; + } + else + { //bottom field + pSlice->ThisPOC= pSlice->bottompoc = pSlice->PicOrderCntMsb + pSlice->pic_order_cnt_lsb; + } + pSlice->framepoc = pSlice->ThisPOC; + + p_Vid->ThisPOC = pSlice->ThisPOC; + + //if ( pSlice->frame_num != p_Vid->PreviousFrameNum) //Seems redundant + p_Vid->PreviousFrameNum = pSlice->frame_num; + + if(pSlice->nal_reference_idc) + { + p_Vid->PrevPicOrderCntLsb = pSlice->pic_order_cnt_lsb; + p_Vid->PrevPicOrderCntMsb = pSlice->PicOrderCntMsb; + } + + break; + + case 1: // POC MODE 1 + // 1st + if(pSlice->idr_flag) + { + p_Vid->FrameNumOffset=0; // first pix of IDRGOP, + if(pSlice->frame_num) + error("frame_num not equal to zero in IDR picture", -1020); + } + else + { + if (p_Vid->last_has_mmco_5) + { + p_Vid->PreviousFrameNumOffset = 0; + p_Vid->PreviousFrameNum = 0; + } + if (pSlice->frame_numPreviousFrameNum) + { //not first pix of IDRGOP + p_Vid->FrameNumOffset = p_Vid->PreviousFrameNumOffset + p_Vid->max_frame_num; + } + else + { + p_Vid->FrameNumOffset = p_Vid->PreviousFrameNumOffset; + } + } + + // 2nd + if(active_sps->num_ref_frames_in_pic_order_cnt_cycle) + pSlice->AbsFrameNum = p_Vid->FrameNumOffset+pSlice->frame_num; + else + pSlice->AbsFrameNum=0; + if( (!pSlice->nal_reference_idc) && pSlice->AbsFrameNum > 0) + pSlice->AbsFrameNum--; + + // 3rd + p_Vid->ExpectedDeltaPerPicOrderCntCycle=0; + + if(active_sps->num_ref_frames_in_pic_order_cnt_cycle) + for(i=0;i<(int) active_sps->num_ref_frames_in_pic_order_cnt_cycle;i++) + p_Vid->ExpectedDeltaPerPicOrderCntCycle += active_sps->offset_for_ref_frame[i]; + + if(pSlice->AbsFrameNum) + { + p_Vid->PicOrderCntCycleCnt = (pSlice->AbsFrameNum-1)/active_sps->num_ref_frames_in_pic_order_cnt_cycle; + p_Vid->FrameNumInPicOrderCntCycle = (pSlice->AbsFrameNum-1)%active_sps->num_ref_frames_in_pic_order_cnt_cycle; + p_Vid->ExpectedPicOrderCnt = p_Vid->PicOrderCntCycleCnt*p_Vid->ExpectedDeltaPerPicOrderCntCycle; + for(i=0;i<=(int)p_Vid->FrameNumInPicOrderCntCycle;i++) + p_Vid->ExpectedPicOrderCnt += active_sps->offset_for_ref_frame[i]; + } + else + p_Vid->ExpectedPicOrderCnt=0; + + if(!pSlice->nal_reference_idc) + p_Vid->ExpectedPicOrderCnt += active_sps->offset_for_non_ref_pic; + + if(pSlice->field_pic_flag==0) + { //frame pix + pSlice->toppoc = p_Vid->ExpectedPicOrderCnt + pSlice->delta_pic_order_cnt[0]; + pSlice->bottompoc = pSlice->toppoc + active_sps->offset_for_top_to_bottom_field + pSlice->delta_pic_order_cnt[1]; + pSlice->ThisPOC = pSlice->framepoc = (pSlice->toppoc < pSlice->bottompoc)? pSlice->toppoc : pSlice->bottompoc; // POC200301 + } + else if (pSlice->bottom_field_flag == FALSE) + { //top field + pSlice->ThisPOC = pSlice->toppoc = p_Vid->ExpectedPicOrderCnt + pSlice->delta_pic_order_cnt[0]; + } + else + { //bottom field + pSlice->ThisPOC = pSlice->bottompoc = p_Vid->ExpectedPicOrderCnt + active_sps->offset_for_top_to_bottom_field + pSlice->delta_pic_order_cnt[0]; + } + pSlice->framepoc=pSlice->ThisPOC; + + p_Vid->PreviousFrameNum=pSlice->frame_num; + p_Vid->PreviousFrameNumOffset=p_Vid->FrameNumOffset; + + break; + + + case 2: // POC MODE 2 + if(pSlice->idr_flag) // IDR picture + { + p_Vid->FrameNumOffset=0; // first pix of IDRGOP, + pSlice->ThisPOC = pSlice->framepoc = pSlice->toppoc = pSlice->bottompoc = 0; + if(pSlice->frame_num) + error("frame_num not equal to zero in IDR picture", -1020); + } + else + { + if (p_Vid->last_has_mmco_5) + { + p_Vid->PreviousFrameNum = 0; + p_Vid->PreviousFrameNumOffset = 0; + } + if (pSlice->frame_numPreviousFrameNum) + p_Vid->FrameNumOffset = p_Vid->PreviousFrameNumOffset + p_Vid->max_frame_num; + else + p_Vid->FrameNumOffset = p_Vid->PreviousFrameNumOffset; + + + pSlice->AbsFrameNum = p_Vid->FrameNumOffset+pSlice->frame_num; + if(!pSlice->nal_reference_idc) + pSlice->ThisPOC = (2*pSlice->AbsFrameNum - 1); + else + pSlice->ThisPOC = (2*pSlice->AbsFrameNum); + + if (pSlice->field_pic_flag==0) + pSlice->toppoc = pSlice->bottompoc = pSlice->framepoc = pSlice->ThisPOC; + else if (pSlice->bottom_field_flag == FALSE) + pSlice->toppoc = pSlice->framepoc = pSlice->ThisPOC; + else + pSlice->bottompoc = pSlice->framepoc = pSlice->ThisPOC; + } + + p_Vid->PreviousFrameNum=pSlice->frame_num; + p_Vid->PreviousFrameNumOffset=p_Vid->FrameNumOffset; + break; + + + default: + //error must occurs + assert( 1==0 ); + break; + } +} + +/*! + ************************************************************************ + * \brief + * A little helper for the debugging of POC code + * \return + * none + ************************************************************************ + */ +int dumppoc(VideoParameters *p_Vid) +{ + seq_parameter_set_rbsp_t *active_sps = p_Vid->active_sps; + + printf ("\nPOC locals...\n"); + printf ("toppoc %d\n", (int) p_Vid->ppSliceList[0]->toppoc); + printf ("bottompoc %d\n", (int) p_Vid->ppSliceList[0]->bottompoc); + printf ("frame_num %d\n", (int) p_Vid->ppSliceList[0]->frame_num); + printf ("field_pic_flag %d\n", (int) p_Vid->ppSliceList[0]->field_pic_flag); + printf ("bottom_field_flag %d\n", (int) p_Vid->ppSliceList[0]->bottom_field_flag); + printf ("POC SPS\n"); + printf ("log2_max_frame_num_minus4 %d\n", (int) active_sps->log2_max_frame_num_minus4); // POC200301 + printf ("log2_max_pic_order_cnt_lsb_minus4 %d\n", (int) active_sps->log2_max_pic_order_cnt_lsb_minus4); + printf ("pic_order_cnt_type %d\n", (int) active_sps->pic_order_cnt_type); + printf ("num_ref_frames_in_pic_order_cnt_cycle %d\n", (int) active_sps->num_ref_frames_in_pic_order_cnt_cycle); + printf ("delta_pic_order_always_zero_flag %d\n", (int) active_sps->delta_pic_order_always_zero_flag); + printf ("offset_for_non_ref_pic %d\n", (int) active_sps->offset_for_non_ref_pic); + printf ("offset_for_top_to_bottom_field %d\n", (int) active_sps->offset_for_top_to_bottom_field); + printf ("offset_for_ref_frame[0] %d\n", (int) active_sps->offset_for_ref_frame[0]); + printf ("offset_for_ref_frame[1] %d\n", (int) active_sps->offset_for_ref_frame[1]); + printf ("POC in SLice Header\n"); + printf ("bottom_field_pic_order_in_frame_present_flag %d\n", (int) p_Vid->active_pps->bottom_field_pic_order_in_frame_present_flag); + printf ("delta_pic_order_cnt[0] %d\n", (int) p_Vid->ppSliceList[0]->delta_pic_order_cnt[0]); + printf ("delta_pic_order_cnt[1] %d\n", (int) p_Vid->ppSliceList[0]->delta_pic_order_cnt[1]); + printf ("idr_flag %d\n", (int) p_Vid->ppSliceList[0]->idr_flag); + printf ("max_frame_num %d\n", (int) p_Vid->max_frame_num); + + return 0; +} + +/*! + ************************************************************************ + * \brief + * return the poc of p_Vid as per (8-1) JVT-F100d2 + * POC200301 + ************************************************************************ + */ +int picture_order( Slice *pSlice ) +{ + if (pSlice->field_pic_flag==0) // is a frame + return pSlice->framepoc; + else if (pSlice->bottom_field_flag == FALSE) // top field + return pSlice->toppoc; + else // bottom field + return pSlice->bottompoc; +} + diff --git a/ldecod/src/image.c b/ldecod/src/image.c new file mode 100644 index 0000000..fa6279b --- /dev/null +++ b/ldecod/src/image.c @@ -0,0 +1,2785 @@ +/*! + *********************************************************************** + * \file image.c + * + * \brief + * Decode a Slice + * + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * - Inge Lille-Langoy + * - Rickard Sjoberg + * - Jani Lainema + * - Sebastian Purreiter + * - Byeong-Moon Jeon + * - Thomas Wedi + * - Gabi Blaettermann + * - Ye-Kui Wang + * - Antti Hallapuro + * - Alexis Tourapis + * - Jill Boyce + * - Saurav K Bandyopadhyay + * - Zhenyu Wu + * + *********************************************************************** + */ + +#include "contributors.h" + +#include +#include + +#include "global.h" +#include "image.h" +#include "fmo.h" +#include "annexb.h" +#include "nalu.h" +#include "parset.h" +#include "header.h" + +#include "sei.h" +#include "output.h" +#include "mb_access.h" +#include "memalloc.h" +#include "macroblock.h" + +#include "loopfilter.h" + +#include "biaridecod.h" +#include "context_ini.h" +#include "cabac.h" +#include "vlc.h" +#include "quant.h" + +#include "errorconcealment.h" +#include "erc_api.h" + +/* Edited by Joao Carreira */ +#include "transcode_dec.h" +#include "ferc_api.h" +#include "ferc_opencv_api.h" + +#include "mbuffer_common.h" +#include "mbuffer_mvc.h" +#include "fast_memory.h" + +#include "mc_prediction.h" + +#include "stream_parser.h" + +extern int testEndian( void); +void reorder_lists( Slice *currSlice); + +static inline void reset_mbs( Macroblock *currMB) +{ + currMB->slice_nr = -1; + currMB->ei_flag = 1; + currMB->dpl_flag = 0; + currMB->is_decoded = 0; +} + +static void setup_buffers( VideoParameters *p_Vid, int layer_id) +{ + CodingParameters *cps = p_Vid->p_EncodePar[layer_id]; + int i; + + if( p_Vid->last_dec_layer_id != layer_id ) + { + p_Vid->imgY_ref = cps->imgY_ref; + p_Vid->imgUV_ref = cps->imgUV_ref; + if( cps->separate_colour_plane_flag ) + { + for( i = 0; i < MAX_PLANE; i++ ) + { + p_Vid->mb_data_JV[i] = cps->mb_data_JV[i]; + p_Vid->intra_block_JV[i] = cps->intra_block_JV[i]; + p_Vid->ipredmode_JV[i] = cps->ipredmode_JV[i]; + p_Vid->siblock_JV[i] = cps->siblock_JV[i]; + } + p_Vid->mb_data = NULL; + p_Vid->intra_block = NULL; + p_Vid->ipredmode = NULL; + p_Vid->siblock = NULL; + } + else + { + p_Vid->mb_data = cps->mb_data; + p_Vid->intra_block = cps->intra_block; + p_Vid->ipredmode = cps->ipredmode; + p_Vid->siblock = cps->siblock; + } + p_Vid->PicPos = cps->PicPos; + p_Vid->nz_coeff = cps->nz_coeff; + p_Vid->qp_per_matrix = cps->qp_per_matrix; + p_Vid->qp_rem_matrix = cps->qp_rem_matrix; + p_Vid->oldFrameSizeInMbs = cps->oldFrameSizeInMbs; + p_Vid->img2buf = cps->img2buf; + p_Vid->last_dec_layer_id = layer_id; + } +} + +#if MVC_EXTENSION_ENABLE +void init_mvc_picture( Slice *currSlice) +{ + int l, i, v, find; + VideoParameters *p_Vid = currSlice->p_Vid; + DecodedPictureBuffer *p_Dpb = p_Vid->p_Dpb_layer[0]; + // Edited by Joao Carreira + int max_view_num = p_Vid->active_subset_sps->num_views_minus1 + 1; + int curr_view_id = currSlice->view_id; + + StorablePicture *p_pic[MAX_NUM_DPB_LAYERS - 1]; + int find_refs = 0; + + memset( p_pic, NULL, (MAX_NUM_DPB_LAYERS - 1) * sizeof(StorablePicture *)); + + // find BL reconstructed picture + if( currSlice->structure == FRAME ) + { + for( l = 0; l < p_Vid->active_subset_sps->num_views_minus1 + 1; l++ ) + { + p_Dpb = p_Vid->p_Dpb_layer[l]; + for( i = 0; i < (int) p_Dpb->used_size/*size*/; i++ ) + { + FrameStore *fs = p_Dpb->fs[i]; + if( (fs->frame->view_id != currSlice->view_id) && (fs->frame->frame_poc == currSlice->framepoc) ) + { + p_pic[find_refs++] = fs->frame; + //break; + } + } + } + } + else if( currSlice->structure == TOP_FIELD ) + { + for( l = 0; l < p_Vid->active_subset_sps->num_views_minus1 + 1; l++ ) + { + p_Dpb = p_Vid->p_Dpb_layer[l]; + for( i = 0; i < (int) p_Dpb->used_size/*size*/; i++ ) + { + FrameStore *fs = p_Dpb->fs[i]; + if( (fs->top_field->view_id != currSlice->view_id) && (fs->top_field->top_poc == currSlice->toppoc) ) + { + p_pic[find_refs++] = fs->top_field; + //break; + } + } + } + } + else + { + for( l = 0; l < p_Vid->active_subset_sps->num_views_minus1 + 1; l++ ) + { + p_Dpb = p_Vid->p_Dpb_layer[l]; + for( i = 0; i < (int) p_Dpb->used_size/*size*/; i++ ) + { + FrameStore *fs = p_Dpb->fs[i]; + if( (fs->bottom_field->view_id != currSlice->view_id) && (fs->bottom_field->bottom_poc == currSlice->bottompoc) ) + { + p_pic[find_refs++] = fs->bottom_field; + //break; + } + } + } + } + + //picture error concealment - only works for stereo video + if( !p_pic[0] ) + { + p_Dpb = p_Vid->p_Dpb_layer[0]; + fercConcealLostInterviewRefFrames( p_Dpb, &p_pic[0], currSlice, 0); + find_refs++; + } + + //picture error concealment - multi-view coding scenario + for( v = 0; v < curr_view_id; v++ ) + { + find = 0; + for( i = 0; i < find_refs; i++ ) + { + if( p_pic[i]->view_id == v ) + { + find = 1; + break; + } + } + if( !find ) + { + p_Dpb = p_Vid->p_Dpb_layer[v]; + fercConcealLostInterviewRefFrames( p_Vid->p_Dpb_layer[v], &p_pic[find_refs], currSlice, v); + find_refs++; + } + } + + if( !p_pic[0] ) + { + p_Vid->bFrameInit = 0; + } + else + { + int i; + check_for_previous_interview_frames( currSlice->p_Dpb, p_pic[0]); + i = 0; + for( i = 0; i < find_refs; i++ ) + { + process_picture_in_dpb_s( p_Vid, p_pic[i]); + store_proc_picture_in_dpb( currSlice->p_Dpb, clone_storable_picture( p_Vid, p_pic[i])); + } + } +} +#endif + +/*! + ************************************************************************ + * \brief + * Initializes the parameters for a new picture + ************************************************************************ + */ +static void init_picture( VideoParameters *p_Vid, Slice *currSlice, InputParameters *p_Inp) +{ + int i; + int nplane; + StorablePicture *dec_picture = NULL; + seq_parameter_set_rbsp_t *active_sps = p_Vid->active_sps; + DecodedPictureBuffer *p_Dpb = currSlice->p_Dpb; + + p_Vid->PicHeightInMbs = p_Vid->FrameHeightInMbs / (1 + currSlice->field_pic_flag); + p_Vid->PicSizeInMbs = p_Vid->PicWidthInMbs * p_Vid->PicHeightInMbs; + p_Vid->FrameSizeInMbs = p_Vid->PicWidthInMbs * p_Vid->FrameHeightInMbs; + + p_Vid->bFrameInit = 1; + if( p_Vid->dec_picture ) // && p_Vid->num_dec_mb == p_Vid->PicSizeInMbs) + { + // this may only happen on slice loss + exit_picture( p_Vid, &p_Vid->dec_picture); + } + p_Vid->dpb_layer_id = currSlice->layer_id; + //set buffers; + setup_buffers( p_Vid, currSlice->layer_id); + + if( p_Vid->recovery_point ) + p_Vid->recovery_frame_num = (currSlice->frame_num + p_Vid->recovery_frame_cnt) % p_Vid->max_frame_num; + + if( currSlice->idr_flag ) + { +#if (MVC_EXTENSION_ENABLE) + p_Vid->pre_frame_num[currSlice->view_id] = 0; +#else + p_Vid->pre_frame_num = 0; +#endif + p_Vid->recovery_frame_num = currSlice->frame_num; + } + +#if (MVC_EXTENSION_ENABLE) + if( (currSlice->layer_id > 0) && (currSlice->svc_extension_flag == 0 && currSlice->NaluHeaderMVCExt.non_idr_flag == 0) ) + { + p_Vid->pre_frame_num[currSlice->view_id] = 0; + } +#endif + +#if ( MVC_EXTENSION_ENABLE ) + /* Edit by Joao Carreira */ + if( p_Vid->recovery_point == 0 && currSlice->frame_num != p_Vid->pre_frame_num[currSlice->view_id] + && currSlice->frame_num != (p_Vid->pre_frame_num[currSlice->view_id] + 1) % p_Vid->max_frame_num ) + { +#else + if (p_Vid->recovery_point == 0 && + currSlice->frame_num != p_Vid->pre_frame_num && + currSlice->frame_num != (p_Vid->pre_frame_num + 1) % p_Vid->max_frame_num) + { +#endif + //if (active_sps->gaps_in_frame_num_value_allowed_flag == 0) + if( 0 == 0 ) + { + // picture error concealment + if( p_Inp->conceal_mode != 0 ) + { +#if ( MVC_EXTENSION_ENABLE ) + if( ((currSlice->frame_num) < ((p_Vid->pre_frame_num[currSlice->view_id] + 1) % p_Vid->max_frame_num)) /* && currSlice->view_id != BASE_VIEW_IDX */) +#else + if((currSlice->frame_num) < ((p_Vid->pre_frame_num + 1) % p_Vid->max_frame_num)) +#endif + { + fercConcealLostRefFrames( p_Dpb, currSlice, 1); + } + else + { + fercConcealLostRefFrames( p_Dpb, currSlice, 0); + } + } + else + { /* Advanced Error Concealment would be called here to combat unintentional loss of pictures. */ + error( "An unintentional loss of pictures occurs! Exit\n", 100); + } + } + if( p_Vid->conceal_mode == 0 ) + fill_frame_num_gap( p_Vid, currSlice); + } + + if( currSlice->nal_reference_idc ) + { +#if(MVC_EXTENSION_ENABLE) + p_Vid->pre_frame_num[currSlice->view_id] = currSlice->frame_num; +#else + p_Vid->pre_frame_num = currSlice->frame_num; +#endif + } + + //p_Vid->num_dec_mb = 0; + + //calculate POC + decode_poc( p_Vid, currSlice); + + if( p_Vid->recovery_frame_num == (int) currSlice->frame_num && p_Vid->recovery_poc == 0x7fffffff ) + p_Vid->recovery_poc = currSlice->framepoc; + + if( currSlice->nal_reference_idc ) +#if ( MVC_EXTENSION_ENABLE ) + p_Vid->last_ref_pic_poc[currSlice->view_id] = currSlice->framepoc; + if( ( (currSlice->framepoc/p_Inp->poc_scale)%p_Vid->p_Inp->GOP_Size ) == 0 ) + p_Vid->last_ref_hier0_poc[currSlice->view_id] = currSlice->framepoc; +#else + p_Vid->last_ref_pic_poc = currSlice->framepoc; +#endif + + // dumppoc (p_Vid); + + if( currSlice->structure == FRAME || currSlice->structure == TOP_FIELD ) + { + gettime( &(p_Vid->start_time)); // start time + } + + dec_picture = p_Vid->dec_picture = alloc_storable_picture( p_Vid, currSlice->structure, p_Vid->width, p_Vid->height, p_Vid->width_cr, p_Vid->height_cr, 1); + dec_picture->top_poc = currSlice->toppoc; + dec_picture->bottom_poc = currSlice->bottompoc; + dec_picture->frame_poc = currSlice->framepoc; + dec_picture->qp = currSlice->qp; + dec_picture->slice_qp_delta = currSlice->slice_qp_delta; + dec_picture->chroma_qp_offset[0] = p_Vid->active_pps->chroma_qp_index_offset; + dec_picture->chroma_qp_offset[1] = p_Vid->active_pps->second_chroma_qp_index_offset; + dec_picture->iCodingType = currSlice->structure == FRAME ? (currSlice->mb_aff_frame_flag ? FRAME_MB_PAIR_CODING : FRAME_CODING) : FIELD_CODING; //currSlice->slice_type; + dec_picture->layer_id = currSlice->layer_id; +#if (MVC_EXTENSION_ENABLE) + dec_picture->view_id = currSlice->view_id; + dec_picture->inter_view_flag = currSlice->inter_view_flag; + dec_picture->anchor_pic_flag = currSlice->anchor_pic_flag; + if( dec_picture->view_id >= 1 ) + { + if( (p_Vid->profile_idc == MVC_HIGH) || (p_Vid->profile_idc == STEREO_HIGH) ) + init_mvc_picture( currSlice); + } +#endif + + fercFillCurrPicture(p_Vid, dec_picture); + + // reset all variables of the error concealment instance before decoding of every frame. + // here the third parameter should, if perfectly, be equal to the number of slices per frame. + // using little value is ok, the code will allocate more memory if the slice number is larger +#if (DISABLE_ERC == 0) + ercReset(p_Vid->erc_errorVar, p_Vid->PicSizeInMbs, p_Vid->PicSizeInMbs, dec_picture->size_x); +#endif + p_Vid->erc_mvperMB = 0; + + switch( currSlice->structure ) + { + case TOP_FIELD: + { + dec_picture->poc = currSlice->toppoc; + p_Vid->number *= 2; + break; + } + case BOTTOM_FIELD: + { + dec_picture->poc = currSlice->bottompoc; + p_Vid->number = p_Vid->number * 2 + 1; + break; + } + case FRAME: + { + dec_picture->poc = currSlice->framepoc; + break; + } + default: + error( "p_Vid->structure not initialized", 235); + } + + //p_Vid->current_slice_nr=0; + + if( p_Vid->type > SI_SLICE ) + { + set_ec_flag( p_Vid, SE_PTYPE); + p_Vid->type = P_SLICE; // concealed element + } + + // CAVLC init + if( p_Vid->active_pps->entropy_coding_mode_flag == (Boolean) CAVLC ) + { + memset( p_Vid->nz_coeff[0][0][0], -1, p_Vid->PicSizeInMbs * 48 * sizeof(byte)); // 3 * 4 * 4 + } + + // Set the slice_nr member of each MB to -1, to ensure correct when packet loss occurs + // TO set Macroblock Map (mark all MBs as 'have to be concealed') + if( (p_Vid->separate_colour_plane_flag != 0) ) + { + for( nplane = 0; nplane < MAX_PLANE; ++nplane ) + { + Macroblock *currMB = p_Vid->mb_data_JV[nplane]; + char *intra_block = p_Vid->intra_block_JV[nplane]; + for( i = 0; i < (int) p_Vid->PicSizeInMbs; ++i ) + { + reset_mbs( currMB++); + } + fast_memset( p_Vid->ipredmode_JV[nplane][0], DC_PRED, 16 * p_Vid->FrameHeightInMbs * p_Vid->PicWidthInMbs * sizeof(char)); + if( p_Vid->active_pps->constrained_intra_pred_flag ) + { + for( i = 0; i < (int) p_Vid->PicSizeInMbs; ++i ) + { + intra_block[i] = 1; + } + } + } + } + else + { +#if 0 //defined(OPENMP) +#pragma omp parallel for + for(i=0; i<(int)p_Vid->PicSizeInMbs; ++i) + reset_mbs(&p_Vid->mb_data[i]); +#else + Macroblock *currMB = p_Vid->mb_data; + for( i = 0; i < (int) p_Vid->PicSizeInMbs; ++i ) + reset_mbs( currMB++); +#endif + if( p_Vid->active_pps->constrained_intra_pred_flag ) + { + for( i = 0; i < (int) p_Vid->PicSizeInMbs; ++i ) + { + p_Vid->intra_block[i] = 1; + } + } + fast_memset( p_Vid->ipredmode[0], DC_PRED, 16 * p_Vid->FrameHeightInMbs * p_Vid->PicWidthInMbs * sizeof(char)); + } + + dec_picture->slice_type = p_Vid->type; + dec_picture->used_for_reference = (currSlice->nal_reference_idc != 0); + dec_picture->idr_flag = currSlice->idr_flag; + dec_picture->no_output_of_prior_pics_flag = currSlice->no_output_of_prior_pics_flag; + dec_picture->long_term_reference_flag = currSlice->long_term_reference_flag; + dec_picture->adaptive_ref_pic_buffering_flag = currSlice->adaptive_ref_pic_buffering_flag; + + dec_picture->dec_ref_pic_marking_buffer = currSlice->dec_ref_pic_marking_buffer; + currSlice->dec_ref_pic_marking_buffer = NULL; + + dec_picture->mb_aff_frame_flag = currSlice->mb_aff_frame_flag; + dec_picture->PicWidthInMbs = p_Vid->PicWidthInMbs; + + p_Vid->get_mb_block_pos = dec_picture->mb_aff_frame_flag ? get_mb_block_pos_mbaff : get_mb_block_pos_normal; + p_Vid->getNeighbour = dec_picture->mb_aff_frame_flag ? getAffNeighbour : getNonAffNeighbour; + + dec_picture->pic_num = currSlice->frame_num; + dec_picture->frame_num = currSlice->frame_num; + + dec_picture->recovery_frame = (unsigned int) ((int) currSlice->frame_num == p_Vid->recovery_frame_num); + + dec_picture->coded_frame = (currSlice->structure == FRAME); + + dec_picture->chroma_format_idc = active_sps->chroma_format_idc; + + dec_picture->frame_mbs_only_flag = active_sps->frame_mbs_only_flag; + dec_picture->frame_cropping_flag = active_sps->frame_cropping_flag; + + if( dec_picture->frame_cropping_flag ) + { + dec_picture->frame_crop_left_offset = active_sps->frame_crop_left_offset; + dec_picture->frame_crop_right_offset = active_sps->frame_crop_right_offset; + dec_picture->frame_crop_top_offset = active_sps->frame_crop_top_offset; + dec_picture->frame_crop_bottom_offset = active_sps->frame_crop_bottom_offset; + } + +#if (ENABLE_OUTPUT_TONEMAPPING) + // store the necessary tone mapping sei into StorablePicture structure + if( p_Vid->seiToneMapping->seiHasTone_mapping ) + { + int coded_data_bit_max = (1 << p_Vid->seiToneMapping->coded_data_bit_depth); + dec_picture->seiHasTone_mapping = 1; + dec_picture->tone_mapping_model_id = p_Vid->seiToneMapping->model_id; + dec_picture->tonemapped_bit_depth = p_Vid->seiToneMapping->sei_bit_depth; + dec_picture->tone_mapping_lut = malloc( coded_data_bit_max * sizeof(int)); + if( NULL == dec_picture->tone_mapping_lut ) + { + no_mem_exit( "init_picture: tone_mapping_lut"); + } + memcpy( dec_picture->tone_mapping_lut, p_Vid->seiToneMapping->lut, sizeof(imgpel) * coded_data_bit_max); + update_tone_mapping_sei( p_Vid->seiToneMapping); + } + else + dec_picture->seiHasTone_mapping = 0; +#endif + + if( (p_Vid->separate_colour_plane_flag != 0) ) + { + p_Vid->dec_picture_JV[0] = p_Vid->dec_picture; + p_Vid->dec_picture_JV[1] = alloc_storable_picture( p_Vid, (PictureStructure) currSlice->structure, p_Vid->width, p_Vid->height, p_Vid->width_cr, + p_Vid->height_cr, 1); + copy_dec_picture_JV( p_Vid, p_Vid->dec_picture_JV[1], p_Vid->dec_picture_JV[0]); + p_Vid->dec_picture_JV[2] = alloc_storable_picture( p_Vid, (PictureStructure) currSlice->structure, p_Vid->width, p_Vid->height, p_Vid->width_cr, + p_Vid->height_cr, 1); + copy_dec_picture_JV( p_Vid, p_Vid->dec_picture_JV[2], p_Vid->dec_picture_JV[0]); + } +} + +static void update_mbaff_macroblock_data( imgpel **cur_img, imgpel (*temp)[16], int x0, int width, int height) +{ + imgpel (*temp_evn)[16] = temp; + imgpel (*temp_odd)[16] = temp + height; + imgpel **temp_img = cur_img; + int y; + + for( y = 0; y < 2 * height; ++y ) + memcpy( *temp++, (*temp_img++ + x0), width * sizeof(imgpel)); + + for( y = 0; y < height; ++y ) + { + memcpy( (*cur_img++ + x0), *temp_evn++, width * sizeof(imgpel)); + memcpy( (*cur_img++ + x0), *temp_odd++, width * sizeof(imgpel)); + } +} + +static void MbAffPostProc( VideoParameters *p_Vid) +{ + + imgpel temp_buffer[32][16]; + + StorablePicture *dec_picture = p_Vid->dec_picture; + imgpel ** imgY = dec_picture->imgY; + imgpel ***imgUV = dec_picture->imgUV; + + short i, x0, y0; + + for( i = 0; i < (int) dec_picture->PicSizeInMbs; i += 2 ) + { + if( dec_picture->motion.mb_field[i] ) + { + get_mb_pos( p_Vid, i, p_Vid->mb_size[IS_LUMA], &x0, &y0); + update_mbaff_macroblock_data( imgY + y0, temp_buffer, x0, MB_BLOCK_SIZE, MB_BLOCK_SIZE); + + if( dec_picture->chroma_format_idc != YUV400 ) + { + x0 = (short) ((x0 * p_Vid->mb_cr_size_x) >> 4); + y0 = (short) ((y0 * p_Vid->mb_cr_size_y) >> 4); + + update_mbaff_macroblock_data( imgUV[0] + y0, temp_buffer, x0, p_Vid->mb_cr_size_x, p_Vid->mb_cr_size_y); + update_mbaff_macroblock_data( imgUV[1] + y0, temp_buffer, x0, p_Vid->mb_cr_size_x, p_Vid->mb_cr_size_y); + } + } + } +} + +static void fill_wp_params( Slice *currSlice) +{ + if( currSlice->slice_type == B_SLICE ) + { + int i, j, k; + int comp; + int log_weight_denom; + int tb, td; + int tx, DistScaleFactor; + + int max_l0_ref = currSlice->num_ref_idx_active[LIST_0]; + int max_l1_ref = currSlice->num_ref_idx_active[LIST_1]; + + if( currSlice->active_pps->weighted_bipred_idc == 2 ) + { + currSlice->luma_log2_weight_denom = 5; + currSlice->chroma_log2_weight_denom = 5; + currSlice->wp_round_luma = 16; + currSlice->wp_round_chroma = 16; + + for( i = 0; i < MAX_REFERENCE_PICTURES; ++i ) + { + for( comp = 0; comp < 3; ++comp ) + { + log_weight_denom = (comp == 0) ? currSlice->luma_log2_weight_denom : currSlice->chroma_log2_weight_denom; + currSlice->wp_weight[0][i][comp] = 1 << log_weight_denom; + currSlice->wp_weight[1][i][comp] = 1 << log_weight_denom; + currSlice->wp_offset[0][i][comp] = 0; + currSlice->wp_offset[1][i][comp] = 0; + } + } + } + + for( i = 0; i < max_l0_ref; ++i ) + { + for( j = 0; j < max_l1_ref; ++j ) + { + for( comp = 0; comp < 3; ++comp ) + { + log_weight_denom = (comp == 0) ? currSlice->luma_log2_weight_denom : currSlice->chroma_log2_weight_denom; + if( currSlice->active_pps->weighted_bipred_idc == 1 ) + { + currSlice->wbp_weight[0][i][j][comp] = currSlice->wp_weight[0][i][comp]; + currSlice->wbp_weight[1][i][j][comp] = currSlice->wp_weight[1][j][comp]; + } + else if( currSlice->active_pps->weighted_bipred_idc == 2 ) + { + td = iClip3( -128, 127, currSlice->listX[LIST_1][j]->poc - currSlice->listX[LIST_0][i]->poc); + if( td == 0 || currSlice->listX[LIST_1][j]->is_long_term || currSlice->listX[LIST_0][i]->is_long_term ) + { + currSlice->wbp_weight[0][i][j][comp] = 32; + currSlice->wbp_weight[1][i][j][comp] = 32; + } + else + { + tb = iClip3( -128, 127, currSlice->ThisPOC - currSlice->listX[LIST_0][i]->poc); + + tx = (16384 + iabs( td / 2)) / td; + DistScaleFactor = iClip3( -1024, 1023, (tx * tb + 32) >> 6); + + currSlice->wbp_weight[1][i][j][comp] = DistScaleFactor >> 2; + currSlice->wbp_weight[0][i][j][comp] = 64 - currSlice->wbp_weight[1][i][j][comp]; + if( currSlice->wbp_weight[1][i][j][comp] < -64 || currSlice->wbp_weight[1][i][j][comp] > 128 ) + { + currSlice->wbp_weight[0][i][j][comp] = 32; + currSlice->wbp_weight[1][i][j][comp] = 32; + currSlice->wp_offset[0][i][comp] = 0; + currSlice->wp_offset[1][j][comp] = 0; + } + } + } + } + } + } + + if( currSlice->mb_aff_frame_flag ) + { + for( i = 0; i < 2 * max_l0_ref; ++i ) + { + for( j = 0; j < 2 * max_l1_ref; ++j ) + { + for( comp = 0; comp < 3; ++comp ) + { + for( k = 2; k < 6; k += 2 ) + { + currSlice->wp_offset[k + 0][i][comp] = currSlice->wp_offset[0][i >> 1][comp]; + currSlice->wp_offset[k + 1][j][comp] = currSlice->wp_offset[1][j >> 1][comp]; + + log_weight_denom = (comp == 0) ? currSlice->luma_log2_weight_denom : currSlice->chroma_log2_weight_denom; + if( currSlice->active_pps->weighted_bipred_idc == 1 ) + { + currSlice->wbp_weight[k + 0][i][j][comp] = currSlice->wp_weight[0][i >> 1][comp]; + currSlice->wbp_weight[k + 1][i][j][comp] = currSlice->wp_weight[1][j >> 1][comp]; + } + else if( currSlice->active_pps->weighted_bipred_idc == 2 ) + { + td = iClip3( -128, 127, currSlice->listX[k + LIST_1][j]->poc - currSlice->listX[k + LIST_0][i]->poc); + if( td == 0 || currSlice->listX[k + LIST_1][j]->is_long_term || currSlice->listX[k + LIST_0][i]->is_long_term ) + { + currSlice->wbp_weight[k + 0][i][j][comp] = 32; + currSlice->wbp_weight[k + 1][i][j][comp] = 32; + } + else + { + tb = iClip3( -128, 127, ((k == 2) ? currSlice->toppoc : currSlice->bottompoc) - currSlice->listX[k + LIST_0][i]->poc); + + tx = (16384 + iabs( td / 2)) / td; + DistScaleFactor = iClip3( -1024, 1023, (tx * tb + 32) >> 6); + + currSlice->wbp_weight[k + 1][i][j][comp] = DistScaleFactor >> 2; + currSlice->wbp_weight[k + 0][i][j][comp] = 64 - currSlice->wbp_weight[k + 1][i][j][comp]; + if( currSlice->wbp_weight[k + 1][i][j][comp] < -64 || currSlice->wbp_weight[k + 1][i][j][comp] > 128 ) + { + currSlice->wbp_weight[k + 1][i][j][comp] = 32; + currSlice->wbp_weight[k + 0][i][j][comp] = 32; + currSlice->wp_offset[k + 0][i][comp] = 0; + currSlice->wp_offset[k + 1][j][comp] = 0; + } + } + } + } + } + } + } + } + } +} + +static void init_picture_decoding( VideoParameters *p_Vid) +{ + Slice *pSlice = p_Vid->ppSliceList[0]; + int j, i, iDeblockMode = 1; + + if( p_Vid->iSliceNumOfCurrPic >= MAX_NUM_SLICES ) + { + error( "Maximum number of supported slices exceeded. \nPlease recompile with increased value for MAX_NUM_SLICES", 200); + } + + if( p_Vid->pNextPPS->Valid && (int) p_Vid->pNextPPS->pic_parameter_set_id == pSlice->pic_parameter_set_id ) + { + pic_parameter_set_rbsp_t tmpPPS; + memcpy( &tmpPPS, &(p_Vid->PicParSet[pSlice->pic_parameter_set_id]), sizeof(pic_parameter_set_rbsp_t)); + (p_Vid->PicParSet[pSlice->pic_parameter_set_id]).slice_group_id = NULL; + MakePPSavailable( p_Vid, p_Vid->pNextPPS->pic_parameter_set_id, p_Vid->pNextPPS); + memcpy( p_Vid->pNextPPS, &tmpPPS, sizeof(pic_parameter_set_rbsp_t)); + tmpPPS.slice_group_id = NULL; + } + + UseParameterSet( pSlice); + if( pSlice->idr_flag ) + p_Vid->number = 0; + + p_Vid->PicHeightInMbs = p_Vid->FrameHeightInMbs / (1 + pSlice->field_pic_flag); + p_Vid->PicSizeInMbs = p_Vid->PicWidthInMbs * p_Vid->PicHeightInMbs; + p_Vid->FrameSizeInMbs = p_Vid->PicWidthInMbs * p_Vid->FrameHeightInMbs; + p_Vid->structure = pSlice->structure; + + fmo_init( p_Vid, pSlice); + +#if (MVC_EXTENSION_ENABLE) + //! Bugfix from previous versions of JM Encoder + //if(pSlice->frame_num==0 && pSlice->view_id>0 ) + //{ + // pSlice->NaluHeaderMVCExt.non_idr_flag=0; + //} + if( (pSlice->layer_id > 0) && (pSlice->svc_extension_flag == 0 && pSlice->NaluHeaderMVCExt.non_idr_flag == 0) ) + { + idr_memory_management( p_Vid->p_Dpb_layer[pSlice->layer_id], p_Vid->dec_picture); + } + update_ref_list( p_Vid->p_Dpb_layer[pSlice->view_id]); + update_ltref_list( p_Vid->p_Dpb_layer[pSlice->view_id]); + update_pic_num( pSlice); + i = pSlice->view_id; +#else + update_pic_num(pSlice); + i = 0; +#endif + init_Deblock( p_Vid, pSlice->mb_aff_frame_flag); + //init mb_data; + for( j = 0; j < p_Vid->iSliceNumOfCurrPic; j++ ) + { + if( p_Vid->ppSliceList[j]->DFDisableIdc != 1 ) + iDeblockMode = 0; +#if (MVC_EXTENSION_ENABLE) + assert( p_Vid->ppSliceList[j]->view_id == i); +#endif + } + p_Vid->iDeblockMode = iDeblockMode; + + //! Edited by Joao Carreira + if( p_Vid->p_Transcode == NULL ) + initTranscodeModule( p_Vid); + +} + +//!Edited by Joao Carreira +void check_concealed_references( Slice *currSlice) +{ + //VideoParameters *p_Vid = currSlice->p_Vid; + int idx_ref; + int view = currSlice->view_id; + + if( currSlice->slice_type == I_SLICE ) + { + currSlice->have_concealed_reference = 0; + return; + } + for( idx_ref = 0; idx_ref < currSlice->num_ref_idx_active[LIST_0]; idx_ref++ ) + { + if( currSlice->listX[LIST_0][idx_ref]->concealed_pic == 1 ) + { + currSlice->have_concealed_reference = 1; + } + } + for( idx_ref = 0; idx_ref < currSlice->num_ref_idx_active[LIST_0]; idx_ref++ ) + { + if( currSlice->listX[LIST_1][idx_ref]->concealed_pic == 1 ) + { + currSlice->have_concealed_reference = 1; + } + } +} + +void init_slice( VideoParameters *p_Vid, Slice *currSlice) +{ + int i; + + if( currSlice->framepoc == 6 ) + { + int a = 0; + } + + p_Vid->active_sps = currSlice->active_sps; + p_Vid->active_pps = currSlice->active_pps; + + currSlice->init_lists( currSlice); + + //dump_ref_lists(currSlice); + +#if (MVC_EXTENSION_ENABLE) + if( currSlice->svc_extension_flag == 0 || currSlice->svc_extension_flag == 1 ) + reorder_lists_mvc( currSlice, currSlice->ThisPOC); + else + reorder_lists( currSlice); + + if( currSlice->fs_listinterview0 ) + { + free( currSlice->fs_listinterview0); + currSlice->fs_listinterview0 = NULL; + } + if( currSlice->fs_listinterview1 ) + { + free( currSlice->fs_listinterview1); + currSlice->fs_listinterview1 = NULL; + } + + currSlice->have_concealed_reference = 0; + check_concealed_references( currSlice); + +#else + reorder_lists (currSlice); +#endif + +#if PRINTREFLIST_JC + dump_ref_lists(currSlice); +#endif + + if( currSlice->structure == FRAME ) + { + init_mbaff_lists( p_Vid, currSlice); + } + //p_Vid->recovery_point = 0; + + // update reference flags and set current p_Vid->ref_flag + if( !(currSlice->redundant_pic_cnt != 0 && p_Vid->previous_frame_num == currSlice->frame_num) ) + { + for( i = 16; i > 0; i-- ) + { + currSlice->ref_flag[i] = currSlice->ref_flag[i - 1]; + } + } + currSlice->ref_flag[0] = currSlice->redundant_pic_cnt == 0 ? p_Vid->Is_primary_correct : p_Vid->Is_redundant_correct; + //p_Vid->previous_frame_num = currSlice->frame_num; //p_Vid->frame_num; + + if( (currSlice->active_sps->chroma_format_idc == 0) || (currSlice->active_sps->chroma_format_idc == 3) ) + { + currSlice->linfo_cbp_intra = linfo_cbp_intra_other; + currSlice->linfo_cbp_inter = linfo_cbp_inter_other; + } + else + { + currSlice->linfo_cbp_intra = linfo_cbp_intra_normal; + currSlice->linfo_cbp_inter = linfo_cbp_inter_normal; + } +} + +void decode_slice( Slice *currSlice, int current_header) +{ + if( currSlice->active_pps->entropy_coding_mode_flag ) + { + init_contexts( currSlice); + cabac_new_slice( currSlice); + } + + if( (currSlice->active_pps->weighted_bipred_idc > 0 && (currSlice->slice_type == B_SLICE)) + || (currSlice->active_pps->weighted_pred_flag && currSlice->slice_type != I_SLICE) ) + fill_wp_params( currSlice); + + //printf("frame picture %d %d %d\n",currSlice->structure,currSlice->ThisPOC,currSlice->direct_spatial_mv_pred_flag); + + // decode main slice information + if( (current_header == SOP || current_header == SOS) && currSlice->ei_flag == 0 ) + decode_one_slice( currSlice); + + // setMB-Nr in case this slice was lost + // if(currSlice->ei_flag) + // p_Vid->current_mb_nr = currSlice->last_mb_nr + 1; + +} + +/*! + ************************************************************************ + * \brief + * Error tracking: if current frame is lost or any reference frame of + * current frame is lost, current frame is incorrect. + ************************************************************************ + */ +static void Error_tracking( VideoParameters *p_Vid, Slice *currSlice) +{ + int i; + + if( currSlice->redundant_pic_cnt == 0 ) + { + p_Vid->Is_primary_correct = p_Vid->Is_redundant_correct = 1; + } + + if( currSlice->redundant_pic_cnt == 0 && p_Vid->type != I_SLICE ) + { + for( i = 0; i < currSlice->num_ref_idx_active[LIST_0]; ++i ) + { + if( currSlice->ref_flag[i] == 0 ) // any reference of primary slice is incorrect + { + p_Vid->Is_primary_correct = 0; // primary slice is incorrect + } + } + } + else if( currSlice->redundant_pic_cnt != 0 && p_Vid->type != I_SLICE ) + { + if( currSlice->ref_flag[currSlice->redundant_slice_ref_idx] == 0 ) // reference of redundant slice is incorrect + { + p_Vid->Is_redundant_correct = 0; // redundant slice is incorrect + } + } +} + +static void CopyPOC( Slice *pSlice0, Slice *currSlice) +{ + currSlice->framepoc = pSlice0->framepoc; + currSlice->toppoc = pSlice0->toppoc; + currSlice->bottompoc = pSlice0->bottompoc; + currSlice->ThisPOC = pSlice0->ThisPOC; +} + +/*! + *********************************************************************** + * \brief + * decodes one I- or P-frame + * + *********************************************************************** + */ +int decode_one_frame( DecoderParams *pDecoder) +{ + VideoParameters *p_Vid = pDecoder->p_Vid; + InputParameters *p_Inp = p_Vid->p_Inp; + int current_header, iRet; + Slice *currSlice; // = p_Vid->currentSlice; + Slice **ppSliceList = p_Vid->ppSliceList; + int iSliceNo; + + //read one picture first; + p_Vid->iSliceNumOfCurrPic = 0; + current_header = 0; + p_Vid->iNumOfSlicesDecoded = 0; + p_Vid->num_dec_mb = 0; + if( p_Vid->newframe ) + { + if( p_Vid->pNextPPS->Valid ) + { + //assert((int) p_Vid->pNextPPS->pic_parameter_set_id == p_Vid->pNextSlice->pic_parameter_set_id); + MakePPSavailable( p_Vid, p_Vid->pNextPPS->pic_parameter_set_id, p_Vid->pNextPPS); + p_Vid->pNextPPS->Valid = 0; + } + + //get the first slice from currentslice; + assert( ppSliceList[p_Vid->iSliceNumOfCurrPic]); + currSlice = ppSliceList[p_Vid->iSliceNumOfCurrPic]; + ppSliceList[p_Vid->iSliceNumOfCurrPic] = p_Vid->pNextSlice; + p_Vid->pNextSlice = currSlice; + assert( ppSliceList[p_Vid->iSliceNumOfCurrPic]->current_slice_nr == 0); + + currSlice = ppSliceList[p_Vid->iSliceNumOfCurrPic]; + + UseParameterSet( currSlice); + + init_picture( p_Vid, currSlice, p_Inp); + + p_Vid->iSliceNumOfCurrPic++; + current_header = SOS; + } + while( current_header != SOP && current_header != EOS ) + { + //no pending slices; + assert( p_Vid->iSliceNumOfCurrPic < p_Vid->iNumOfSlicesAllocated); + if( !ppSliceList[p_Vid->iSliceNumOfCurrPic] ) + { + ppSliceList[p_Vid->iSliceNumOfCurrPic] = malloc_slice( p_Inp, p_Vid); + } + currSlice = ppSliceList[p_Vid->iSliceNumOfCurrPic]; + + //p_Vid->currentSlice = currSlice; + currSlice->p_Vid = p_Vid; + currSlice->p_Inp = p_Inp; + currSlice->p_Dpb = p_Vid->p_Dpb_layer[0]; //set default value; + currSlice->next_header = -8888; + currSlice->num_dec_mb = 0; + currSlice->coeff_ctr = -1; + currSlice->pos = 0; + currSlice->is_reset_coeff = FALSE; + currSlice->is_reset_coeff_cr = FALSE; + + current_header = read_new_slice( currSlice); + //init; + currSlice->current_header = current_header; + + // error tracking of primary and redundant slices. + Error_tracking( p_Vid, currSlice); + // If primary and redundant are received and primary is correct, discard the redundant + // else, primary slice will be replaced with redundant slice. + if( currSlice->frame_num == p_Vid->previous_frame_num && currSlice->redundant_pic_cnt != 0 && p_Vid->Is_primary_correct != 0 && current_header != EOS ) + { + continue; + } + + if( (current_header != SOP && current_header != EOS) || (p_Vid->iSliceNumOfCurrPic == 0 && current_header == SOP) ) + { + currSlice->current_slice_nr = (short) p_Vid->iSliceNumOfCurrPic; + p_Vid->dec_picture->max_slice_id = (short) imax( currSlice->current_slice_nr, p_Vid->dec_picture->max_slice_id); + if( p_Vid->iSliceNumOfCurrPic > 0 ) + { + CopyPOC( *ppSliceList, currSlice); + ppSliceList[p_Vid->iSliceNumOfCurrPic - 1]->end_mb_nr_plus1 = currSlice->start_mb_nr; + } + p_Vid->iSliceNumOfCurrPic++; + if( p_Vid->iSliceNumOfCurrPic >= p_Vid->iNumOfSlicesAllocated ) + { + Slice **tmpSliceList = (Slice **) realloc( p_Vid->ppSliceList, (p_Vid->iNumOfSlicesAllocated + MAX_NUM_DECSLICES) * sizeof(Slice*)); + if( !tmpSliceList ) + { + tmpSliceList = (Slice **) calloc( (p_Vid->iNumOfSlicesAllocated + MAX_NUM_DECSLICES), sizeof(Slice*)); + memcpy( tmpSliceList, p_Vid->ppSliceList, p_Vid->iSliceNumOfCurrPic * sizeof(Slice*)); + //free; + free( p_Vid->ppSliceList); + ppSliceList = p_Vid->ppSliceList = tmpSliceList; + } + else + { + //assert(tmpSliceList == p_Vid->ppSliceList); + ppSliceList = p_Vid->ppSliceList = tmpSliceList; + memset( p_Vid->ppSliceList + p_Vid->iSliceNumOfCurrPic, 0, sizeof(Slice*) * MAX_NUM_DECSLICES); + } + p_Vid->iNumOfSlicesAllocated += MAX_NUM_DECSLICES; + } + current_header = SOS; + } + else + { + if( ppSliceList[p_Vid->iSliceNumOfCurrPic - 1]->mb_aff_frame_flag ) + ppSliceList[p_Vid->iSliceNumOfCurrPic - 1]->end_mb_nr_plus1 = p_Vid->FrameSizeInMbs / 2; + else + ppSliceList[p_Vid->iSliceNumOfCurrPic - 1]->end_mb_nr_plus1 = p_Vid->FrameSizeInMbs / (1 + ppSliceList[p_Vid->iSliceNumOfCurrPic - 1]->field_pic_flag); + p_Vid->newframe = 1; + currSlice->current_slice_nr = 0; + //keep it in currentslice; + ppSliceList[p_Vid->iSliceNumOfCurrPic] = p_Vid->pNextSlice; + p_Vid->pNextSlice = currSlice; + } + + copy_slice_info( currSlice, p_Vid->old_slice); + } + iRet = current_header; + init_picture_decoding( p_Vid); + + calculate_frame_no( p_Vid, p_Vid->dec_picture); + + for( iSliceNo = 0; iSliceNo < p_Vid->iSliceNumOfCurrPic; iSliceNo++ ) + { + currSlice = ppSliceList[iSliceNo]; + current_header = currSlice->current_header; + //p_Vid->currentSlice = currSlice; + + assert( current_header != EOS); + assert( currSlice->current_slice_nr == iSliceNo); + + init_slice( p_Vid, currSlice); + decode_slice( currSlice, current_header); + + p_Vid->iNumOfSlicesDecoded++; + p_Vid->num_dec_mb += currSlice->num_dec_mb; + p_Vid->erc_mvperMB += currSlice->erc_mvperMB; + + if( currSlice->have_concealed_reference == 1 ) + p_Vid->dec_picture->have_concealed_reference = 1; + + } + +#if MVC_EXTENSION_ENABLE + p_Vid->last_dec_view_id = p_Vid->dec_picture->view_id; +#endif + if( p_Vid->dec_picture->structure == FRAME ) + p_Vid->last_dec_poc = p_Vid->dec_picture->frame_poc; + else if( p_Vid->dec_picture->structure == TOP_FIELD ) + p_Vid->last_dec_poc = p_Vid->dec_picture->top_poc; + else if( p_Vid->dec_picture->structure == BOTTOM_FIELD ) + p_Vid->last_dec_poc = p_Vid->dec_picture->bottom_poc; + exit_picture( p_Vid, &p_Vid->dec_picture); + p_Vid->previous_frame_num = ppSliceList[0]->frame_num; + return (iRet); +} + +/*! + ************************************************************************ + * \brief + * Convert file read buffer to source picture structure + * \param imgX + * Pointer to image plane + * \param buf + * Buffer for file output + * \param size_x + * horizontal image size in pixel + * \param size_y + * vertical image size in pixel + * \param symbol_size_in_bytes + * number of bytes used per pel + ************************************************************************ + */ +void buffer2img( imgpel** imgX, unsigned char* buf, int size_x, int size_y, int symbol_size_in_bytes) +{ + int i, j; + + uint16 tmp16, ui16; + unsigned long tmp32, ui32; + + if( symbol_size_in_bytes > sizeof(imgpel) ) + { + error( "Source picture has higher bit depth than imgpel data type. \nPlease recompile with larger data type for imgpel.", 500); + } + + if( (sizeof(char) == sizeof(imgpel)) && (sizeof(char) == symbol_size_in_bytes) ) + { + // imgpel == pixel_in_file == 1 byte -> simple copy + fast_memcpy( &imgX[0][0], buf, size_x * size_y); + } + else + { + // sizeof (imgpel) > sizeof(char) + if( testEndian( ) ) + { + // big endian + switch( symbol_size_in_bytes ) + { + case 1: + { + for( j = 0; j < size_y; ++j ) + for( i = 0; i < size_x; ++i ) + { + imgX[j][i] = buf[i + j * size_x]; + } + break; + } + case 2: + { + for( j = 0; j < size_y; ++j ) + for( i = 0; i < size_x; ++i ) + { + memcpy( &tmp16, buf + ((i + j * size_x) * 2), 2); + ui16 = (uint16) ((tmp16 >> 8) | ((tmp16 & 0xFF) << 8)); + imgX[j][i] = (imgpel) ui16; + } + break; + } + case 4: + { + for( j = 0; j < size_y; ++j ) + for( i = 0; i < size_x; ++i ) + { + memcpy( &tmp32, buf + ((i + j * size_x) * 4), 4); + ui32 = ((tmp32 & 0xFF00) << 8) | ((tmp32 & 0xFF) << 24) | ((tmp32 & 0xFF0000) >> 8) | ((tmp32 & 0xFF000000) >> 24); + imgX[j][i] = (imgpel) ui32; + } + } + default: + { + error( "reading only from formats of 8, 16 or 32 bit allowed on big endian architecture", 500); + break; + } + } + + } + else + { + // little endian + if( symbol_size_in_bytes == 1 ) + { + for( j = 0; j < size_y; ++j ) + { + for( i = 0; i < size_x; ++i ) + { + imgX[j][i] = *(buf++); + } + } + } + else + { + for( j = 0; j < size_y; ++j ) + { + int jpos = j * size_x; + for( i = 0; i < size_x; ++i ) + { + imgX[j][i] = 0; + memcpy( &(imgX[j][i]), buf + ((i + jpos) * symbol_size_in_bytes), symbol_size_in_bytes); + } + } + } + + } + } +} + +/*! + *********************************************************************** + * \brief + * compute generic SSE + *********************************************************************** + */ +int64 compute_SSE( imgpel **imgRef, imgpel **imgSrc, int xRef, int xSrc, int ySize, int xSize) +{ + int i, j; + imgpel *lineRef, *lineSrc; + int64 distortion = 0; + + for( j = 0; j < ySize; j++ ) + { + lineRef = &imgRef[j][xRef]; + lineSrc = &imgSrc[j][xSrc]; + + for( i = 0; i < xSize; i++ ) + { + distortion += iabs2( *lineRef++ - *lineSrc++); + } + } + return distortion; +} + +/*! + ************************************************************************ + * \brief + * Calculate the value of frame_no + * Edit by Joao Carreira + ************************************************************************ + */ +void calculate_frame_no( VideoParameters *p_Vid, StorablePicture *p) +{ + InputParameters *p_Inp = p_Vid->p_Inp; + int FrameSkip = p_Inp->FrameSkip; + // calculate frame number + int POC = p_Vid->active_sps->mb_adaptive_frame_field_flag ? p->poc / (p_Inp->poc_scale) : p->poc / (p_Inp->poc_scale); + + if( POC == 0 && p->idr_flag ) // && p_Vid->psnr_number) + { + p_Vid->idr_psnr_number = p_Vid->g_nFrame * p_Vid->ref_poc_gap / (p_Inp->poc_scale); + p_Vid->idr_psnr_number = p_Vid->p_Inp->IDR_Period * p_Vid->IDRframe_ctr; + + p_Vid->frame_no = p->frame_no = p_Vid->p_Inp->IDR_Period * p_Vid->IDRframe_ctr + FrameSkip; + } + else + { + p_Vid->frame_no = p->frame_no = p_Vid->frame_no_idr + POC + FrameSkip; + } + //p_Vid->psnr_number = imax(p_Vid->psnr_number, p_Vid->idr_psnr_number+psnrPOC); + + /* Edited by Joao Carreira */ + //p_Vid->frame_no = p_Vid->idr_psnr_number + psnrPOC; + //p_Vid->frame_no = p->frame_no = p_Vid->idr_psnr_number + psnrPOC + FrameSkip; +} + +/*! + ************************************************************************ + * \brief + * Find PSNR for all three components. + * Compare decoded frame with the original sequence. + * Read p_Inp->jumpd frames to reflect frame skipping. + * \param p_Vid + * video encoding parameters for current picture + * \param p + * picture to be compared + * \param p_ref + * file pointer piont to reference YUV reference file + ************************************************************************ + */ +void find_snr( VideoParameters *p_Vid, StorablePicture *p, int *p_ref) +{ + InputParameters *p_Inp = p_Vid->p_Inp; + SNRParameters *snr = p_Vid->snr[p->view_id]; + + int k; + int ret; + int64 diff_comp[3] = + { 0 }; + int64 status; + int symbol_size_in_bytes = (p_Vid->pic_unit_bitsize_on_disk >> 3); + int comp_size_x[3], comp_size_y[3]; + int64 framesize_in_bytes; + + unsigned int max_pix_value_sqd[3]; + + Boolean rgb_output = (Boolean) (p_Vid->active_sps->vui_seq_parameters.matrix_coefficients == 0); + unsigned char *buf; + imgpel **cur_ref[3]; + imgpel **cur_comp[3]; + // picture error concealment + char yuv_types[4][6] = + { "4:0:0", "4:2:0", "4:2:2", "4:4:4" }; + + max_pix_value_sqd[0] = iabs2( p_Vid->max_pel_value_comp[0]); + max_pix_value_sqd[1] = iabs2( p_Vid->max_pel_value_comp[1]); + max_pix_value_sqd[2] = iabs2( p_Vid->max_pel_value_comp[2]); + + cur_ref[0] = p_Vid->imgY_ref; + cur_ref[1] = p->chroma_format_idc != YUV400 ? p_Vid->imgUV_ref[0] : NULL; + cur_ref[2] = p->chroma_format_idc != YUV400 ? p_Vid->imgUV_ref[1] : NULL; + + cur_comp[0] = p->imgY; + cur_comp[1] = p->chroma_format_idc != YUV400 ? p->imgUV[0] : NULL; + cur_comp[2] = p->chroma_format_idc != YUV400 ? p->imgUV[1] : NULL; + + comp_size_x[0] = p_Inp->source.width[0]; + comp_size_y[0] = p_Inp->source.height[0]; + comp_size_x[1] = comp_size_x[2] = p_Inp->source.width[1]; + comp_size_y[1] = comp_size_y[2] = p_Inp->source.height[1]; + + framesize_in_bytes = (((int64) comp_size_x[0] * comp_size_y[0]) + ((int64) comp_size_x[1] * comp_size_y[1]) * 2) * symbol_size_in_bytes; + + // KS: this buffer should actually be allocated only once, but this is still much faster than the previous version + buf = malloc( comp_size_x[0] * comp_size_y[0] * symbol_size_in_bytes); + + if( NULL == buf ) + { + no_mem_exit( "find_snr: buf"); + } + + status = lseek( *p_ref, framesize_in_bytes * p_Vid->frame_no, SEEK_SET); + if( status == -1 ) + { + fprintf( stdout, "Warning: Could not seek to frame number %d in reference file. Shown PSNR might be wrong.\n", p_Vid->frame_no); + free( buf); + return; + } + + if( rgb_output ) + lseek( *p_ref, framesize_in_bytes / 3, SEEK_CUR); + + for( k = 0; k < ((p->chroma_format_idc != YUV400) ? 3 : 1); ++k ) + { + + if( rgb_output && k == 2 ) + lseek( *p_ref, -framesize_in_bytes, SEEK_CUR); + + ret = read( *p_ref, buf, comp_size_x[k] * comp_size_y[k] * symbol_size_in_bytes); + if( ret != comp_size_x[k] * comp_size_y[k] * symbol_size_in_bytes ) + { + printf( "Warning: could not read from reconstructed file\n"); + fast_memset( buf, 0, comp_size_x[k] * comp_size_y[k] * symbol_size_in_bytes); + close( *p_ref); + *p_ref = -1; + break; + } + buffer2img( cur_ref[k], buf, comp_size_x[k], comp_size_y[k], symbol_size_in_bytes); + + // Compute SSE + diff_comp[k] = compute_SSE( cur_ref[k], cur_comp[k], 0, 0, comp_size_y[k], comp_size_x[k]); + + // Collecting SNR statistics + snr->snr[k] = psnr( max_pix_value_sqd[k], comp_size_x[k] * comp_size_y[k], (float) diff_comp[k]); + + if( snr->frame_ctr == 0 ) // first + { + snr->snra[k] = snr->snr[k]; // keep luma snr for first frame + } + else + { + snr->snra[k] = (float) (snr->snra[k] * (snr->frame_ctr) + snr->snr[k]) / (snr->frame_ctr + 1); // average snr chroma for all frames + } + } + + if( rgb_output ) + lseek( *p_ref, framesize_in_bytes * 2 / 3, SEEK_CUR); + + free( buf); +} + +void reorder_lists( Slice *currSlice) +{ + VideoParameters *p_Vid = currSlice->p_Vid; + + if( (currSlice->slice_type != I_SLICE) && (currSlice->slice_type != SI_SLICE) ) + { + if( currSlice->ref_pic_list_reordering_flag[LIST_0] ) + { + reorder_ref_pic_list( currSlice, LIST_0); + } + if( p_Vid->no_reference_picture == currSlice->listX[0][currSlice->num_ref_idx_active[LIST_0] - 1] ) + { + if( p_Vid->non_conforming_stream ) + printf( "RefPicList0[ %d ] is equal to 'no reference picture'\n", currSlice->num_ref_idx_active[LIST_0] - 1); + else + error( "RefPicList0[ num_ref_idx_l0_active_minus1 ] is equal to 'no reference picture', invalid bitstream", 500); + } + // that's a definition + currSlice->listXsize[0] = (char) currSlice->num_ref_idx_active[LIST_0]; + } + + if( currSlice->slice_type == B_SLICE ) + { + if( currSlice->ref_pic_list_reordering_flag[LIST_1] ) + { + reorder_ref_pic_list( currSlice, LIST_1); + } + if( p_Vid->no_reference_picture == currSlice->listX[1][currSlice->num_ref_idx_active[LIST_1] - 1] ) + { + if( p_Vid->non_conforming_stream ) + printf( "RefPicList1[ %d ] is equal to 'no reference picture'\n", currSlice->num_ref_idx_active[LIST_1] - 1); + else + error( "RefPicList1[ num_ref_idx_l1_active_minus1 ] is equal to 'no reference picture', invalid bitstream", 500); + } + // that's a definition + currSlice->listXsize[1] = (char) currSlice->num_ref_idx_active[LIST_1]; + } + + free_ref_pic_list_reordering_buffer( currSlice); + + if( currSlice->slice_type == P_SLICE ) + { +#if PRINTREFLIST + unsigned int i; +#if (MVC_EXTENSION_ENABLE) + // print out for debug purpose + if( (p_Vid->profile_idc == MVC_HIGH || p_Vid->profile_idc == STEREO_HIGH) && currSlice->current_slice_nr == 0 ) + { + if( currSlice->listXsize[0] > 0 ) + { + printf( "\n"); + printf( " ** (FinalViewID:%d) %s Ref Pic List 0 ****\n", currSlice->view_id, + currSlice->structure == FRAME ? "FRM" : (currSlice->structure == TOP_FIELD ? "TOP" : "BOT")); + for( i = 0; i < (unsigned int) (currSlice->listXsize[0]); i++ ) //ref list 0 + { + printf( " %2d -> POC: %4d PicNum: %4d ViewID: %d\n", i, currSlice->listX[0][i]->poc, currSlice->listX[0][i]->pic_num, + currSlice->listX[0][i]->view_id); + } + } + } +#endif +#endif + } + else if( currSlice->slice_type == B_SLICE ) + { +#if PRINTREFLIST + unsigned int i; +#if (MVC_EXTENSION_ENABLE) + // print out for debug purpose + if( (p_Vid->profile_idc == MVC_HIGH || p_Vid->profile_idc == STEREO_HIGH) && currSlice->current_slice_nr == 0 ) + { + if( (currSlice->listXsize[0] > 0) || (currSlice->listXsize[1] > 0) ) + printf( "\n"); + if( currSlice->listXsize[0] > 0 ) + { + printf( " ** (FinalViewID:%d) %s Ref Pic List 0 ****\n", currSlice->view_id, + currSlice->structure == FRAME ? "FRM" : (currSlice->structure == TOP_FIELD ? "TOP" : "BOT")); + for( i = 0; i < (unsigned int) (currSlice->listXsize[0]); i++ ) //ref list 0 + { + printf( " %2d -> POC: %4d PicNum: %4d ViewID: %d\n", i, currSlice->listX[0][i]->poc, currSlice->listX[0][i]->pic_num, + currSlice->listX[0][i]->view_id); + } + } + if( currSlice->listXsize[1] > 0 ) + { + printf( " ** (FinalViewID:%d) %s Ref Pic List 1 ****\n", currSlice->view_id, + currSlice->structure == FRAME ? "FRM" : (currSlice->structure == TOP_FIELD ? "TOP" : "BOT")); + for( i = 0; i < (unsigned int) (currSlice->listXsize[1]); i++ ) //ref list 1 + { + printf( " %2d -> POC: %4d PicNum: %4d ViewID: %d\n", i, currSlice->listX[1][i]->poc, currSlice->listX[1][i]->pic_num, + currSlice->listX[1][i]->view_id); + } + } + } +#endif + +#endif + } +} + +/*! + ************************************************************************ + * \brief + * Reads new slice from bit_stream_dec + ************************************************************************ + */ +int read_new_slice( Slice *currSlice) +{ + VideoParameters *p_Vid = currSlice->p_Vid; + InputParameters *p_Inp = currSlice->p_Inp; + + NALU_t *nalu = p_Vid->nalu; + int current_header = 0; + int BitsUsedByHeader; + Bitstream *currStream = NULL; + + int slice_id_a, slice_id_b, slice_id_c; + + for( ;; ) + { +#if (MVC_EXTENSION_ENABLE) + currSlice->svc_extension_flag = -1; +#endif + if( 0 == read_next_nalu( p_Vid, nalu) ) + return EOS; + +#if (MVC_EXTENSION_ENABLE) + if( p_Inp->DecodeAllLayers == 1 && (nalu->nal_unit_type == NALU_TYPE_PREFIX || nalu->nal_unit_type == NALU_TYPE_SLC_EXT) ) + { + currStream = currSlice->partArr[0].bitstream; + currStream->ei_flag = 0; + currStream->frame_bitoffset = currStream->read_len = 0; + fast_memcpy( currStream->streamBuffer, &nalu->buf[1], nalu->len - 1); + currStream->code_len = currStream->bitstream_length = RBSPtoSODB( currStream->streamBuffer, nalu->len - 1); + + currSlice->svc_extension_flag = read_u_1( "svc_extension_flag", currStream, &p_Dec->UsedBits); + + if( currSlice->svc_extension_flag ) + { + nal_unit_header_svc_extension( ); + } + else + { + nal_unit_header_mvc_extension( &currSlice->NaluHeaderMVCExt, currStream); + currSlice->NaluHeaderMVCExt.iPrefixNALU = (nalu->nal_unit_type == NALU_TYPE_PREFIX); + } + + if( nalu->nal_unit_type == NALU_TYPE_SLC_EXT ) + { + if( currSlice->svc_extension_flag ) + { + //to be implemented for Annex G; + } + else + { + nalu->nal_unit_type = NALU_TYPE_SLICE; //currSlice->NaluHeaderMVCExt.non_idr_flag==0? NALU_TYPE_IDR: NALU_TYPE_SLICE; + } + } + } +#endif + + process_nalu: switch( nalu->nal_unit_type ) + { + case NALU_TYPE_SLICE: + case NALU_TYPE_IDR: +#if( FOUND_NALU ) + printf ("Found NALU_TYPE_SLICE\n"); +#endif + + if( p_Vid->recovery_point || nalu->nal_unit_type == NALU_TYPE_IDR ) + { + if( p_Vid->recovery_point_found == 0 ) + { + if( nalu->nal_unit_type != NALU_TYPE_IDR ) + { + printf( "Warning: Decoding does not start with an IDR picture.\n"); + p_Vid->non_conforming_stream = 1; + } + else + p_Vid->non_conforming_stream = 0; + } + p_Vid->recovery_point_found = 1; + } + + if( p_Vid->recovery_point_found == 0 ) + break; + + currSlice->idr_flag = (nalu->nal_unit_type == NALU_TYPE_IDR); + currSlice->nal_reference_idc = nalu->nal_reference_idc; + currSlice->dp_mode = PAR_DP_1; + currSlice->max_part_nr = 1; +#if (MVC_EXTENSION_ENABLE) + if( currSlice->svc_extension_flag != 0 ) + { + currStream = currSlice->partArr[0].bitstream; + currStream->ei_flag = 0; + currStream->frame_bitoffset = currStream->read_len = 0; + fast_memcpy( currStream->streamBuffer, &nalu->buf[1], nalu->len - 1); + currStream->code_len = currStream->bitstream_length = RBSPtoSODB( currStream->streamBuffer, nalu->len - 1); + } +#else + currStream = currSlice->partArr[0].bitstream; + currStream->ei_flag = 0; + currStream->frame_bitoffset = currStream->read_len = 0; + memcpy (currStream->streamBuffer, &nalu->buf[1], nalu->len-1); + currStream->code_len = currStream->bitstream_length = RBSPtoSODB(currStream->streamBuffer, nalu->len-1); +#endif + +#if (MVC_EXTENSION_ENABLE) + if( currSlice->svc_extension_flag == 0 ) + { //MVC + //if(is_MVC_profile(p_Vid->active_sps->profile_idc)) + //{ + currSlice->view_id = currSlice->NaluHeaderMVCExt.view_id; + currSlice->inter_view_flag = currSlice->NaluHeaderMVCExt.inter_view_flag; + currSlice->anchor_pic_flag = currSlice->NaluHeaderMVCExt.anchor_pic_flag; + //} + } + else if( currSlice->svc_extension_flag == -1 ) //SVC and the normal AVC; + { + if( p_Vid->active_subset_sps == NULL ) + { + currSlice->view_id = GetBaseViewId( p_Vid, &p_Vid->active_subset_sps); + if( currSlice->NaluHeaderMVCExt.iPrefixNALU > 0 ) + { + assert( currSlice->view_id == currSlice->NaluHeaderMVCExt.view_id); + currSlice->inter_view_flag = currSlice->NaluHeaderMVCExt.inter_view_flag; + currSlice->anchor_pic_flag = currSlice->NaluHeaderMVCExt.anchor_pic_flag; + } + else + { + currSlice->inter_view_flag = 1; + currSlice->anchor_pic_flag = currSlice->idr_flag; + } + } + else + { + assert( p_Vid->active_subset_sps->num_views_minus1 >=0); + // prefix NALU available + if( currSlice->NaluHeaderMVCExt.iPrefixNALU > 0 ) + { + currSlice->view_id = currSlice->NaluHeaderMVCExt.view_id; + currSlice->inter_view_flag = currSlice->NaluHeaderMVCExt.inter_view_flag; + currSlice->anchor_pic_flag = currSlice->NaluHeaderMVCExt.anchor_pic_flag; + } + else + { //no prefix NALU; + currSlice->view_id = p_Vid->active_subset_sps->view_id[0]; + currSlice->inter_view_flag = 1; + currSlice->anchor_pic_flag = currSlice->idr_flag; + } + } + } + //! Edited by Joao Carreira + currSlice->layer_id = currSlice->view_id = GetVOIdx( p_Vid, currSlice->view_id); + //currSlice->layer_id = GetVOIdx( p_Vid, currSlice->view_id ); + /* + if(currSlice->view_id >=0) + { + currSlice->p_Dpb = p_Vid->p_Dpb_layer[currSlice->view_id]; + currSlice->imgtype = currSlice->p_Dpb->imgtype; + } + */ +#endif + + // Some syntax of the Slice Header depends on the parameter set, which depends on + // the parameter set ID of the SLice header. Hence, read the pic_parameter_set_id + // of the slice header first, then setup the active parameter sets, and then read + // the rest of the slice header + BitsUsedByHeader = FirstPartOfSliceHeader( currSlice); + UseParameterSet( currSlice); + currSlice->active_sps = p_Vid->active_sps; + currSlice->active_pps = p_Vid->active_pps; + currSlice->Transform8x8Mode = p_Vid->active_pps->transform_8x8_mode_flag; + currSlice->chroma444_not_separate = (p_Vid->active_sps->chroma_format_idc == YUV444) && ((p_Vid->separate_colour_plane_flag == 0)); + + BitsUsedByHeader += RestOfSliceHeader( currSlice); +#if (MVC_EXTENSION_ENABLE) + if( currSlice->view_id >= 0 ) + { + currSlice->p_Dpb = p_Vid->p_Dpb_layer[currSlice->view_id]; + } +#endif + + assign_quant_params( currSlice); + + // if primary slice is replaced with redundant slice, set the correct image type + if( currSlice->redundant_pic_cnt && p_Vid->Is_primary_correct == 0 && p_Vid->Is_redundant_correct ) + { + p_Vid->dec_picture->slice_type = p_Vid->type; + } + + if( is_new_picture( p_Vid->dec_picture, currSlice, p_Vid->old_slice) ) + { + if( p_Vid->iSliceNumOfCurrPic == 0 ) + init_picture( p_Vid, currSlice, p_Inp); + + current_header = SOP; + //check zero_byte if it is also the first NAL unit in the access unit + CheckZeroByteVCL( p_Vid, nalu); + } + else + current_header = SOS; + + setup_slice_methods( currSlice); + + // From here on, p_Vid->active_sps, p_Vid->active_pps and the slice header are valid + if( currSlice->mb_aff_frame_flag ) + currSlice->current_mb_nr = currSlice->start_mb_nr << 1; + else + currSlice->current_mb_nr = currSlice->start_mb_nr; + + if( p_Vid->active_pps->entropy_coding_mode_flag ) + { + int ByteStartPosition = currStream->frame_bitoffset / 8; + if( currStream->frame_bitoffset % 8 != 0 ) + { + ++ByteStartPosition; + } + arideco_start_decoding( &currSlice->partArr[0].de_cabac, currStream->streamBuffer, ByteStartPosition, &currStream->read_len); + } + // printf ("read_new_slice: returning %s\n", current_header == SOP?"SOP":"SOS"); + //FreeNALU(nalu); + p_Vid->recovery_point = 0; + return current_header; + break; + case NALU_TYPE_DPA: + if( p_Vid->recovery_point_found == 0 ) + break; + + // read DP_A + currSlice->dpB_NotPresent = 1; + currSlice->dpC_NotPresent = 1; + + currSlice->idr_flag = FALSE; + currSlice->nal_reference_idc = nalu->nal_reference_idc; + currSlice->dp_mode = PAR_DP_3; + currSlice->max_part_nr = 3; + currSlice->ei_flag = 0; + currSlice->p_Dpb = p_Vid->p_Dpb_layer[0]; + currStream = currSlice->partArr[0].bitstream; + currStream->ei_flag = 0; + currStream->frame_bitoffset = currStream->read_len = 0; + memcpy( currStream->streamBuffer, &nalu->buf[1], nalu->len - 1); + currStream->code_len = currStream->bitstream_length = RBSPtoSODB( currStream->streamBuffer, nalu->len - 1); +#if (MVC_EXTENSION_ENABLE) + currSlice->view_id = GetBaseViewId( p_Vid, &p_Vid->active_subset_sps); + currSlice->inter_view_flag = 1; + currSlice->layer_id = currSlice->view_id = GetVOIdx( p_Vid, currSlice->view_id); + //currSlice->layer_id = GetVOIdx( p_Vid, currSlice->view_id ); + currSlice->anchor_pic_flag = currSlice->idr_flag; +#endif + + BitsUsedByHeader = FirstPartOfSliceHeader( currSlice); + UseParameterSet( currSlice); + currSlice->active_sps = p_Vid->active_sps; + currSlice->active_pps = p_Vid->active_pps; + currSlice->Transform8x8Mode = p_Vid->active_pps->transform_8x8_mode_flag; + currSlice->chroma444_not_separate = (p_Vid->active_sps->chroma_format_idc == YUV444) && ((p_Vid->separate_colour_plane_flag == 0)); + + BitsUsedByHeader += RestOfSliceHeader( currSlice); +#if (MVC_EXTENSION_ENABLE) + currSlice->p_Dpb = p_Vid->p_Dpb_layer[currSlice->view_id]; +#else + currSlice->p_Dpb = p_Vid->p_Dpb_layer[0]; +#endif + assign_quant_params( currSlice); + + if( is_new_picture( p_Vid->dec_picture, currSlice, p_Vid->old_slice) ) + { + if( p_Vid->iSliceNumOfCurrPic == 0 ) + init_picture( p_Vid, currSlice, p_Inp); + + current_header = SOP; + //check zero_byte if it is also the first NAL unit in the access unit + CheckZeroByteVCL( p_Vid, nalu); + } + else + current_header = SOS; + + setup_slice_methods( currSlice); + + // From here on, p_Vid->active_sps, p_Vid->active_pps and the slice header are valid + if( currSlice->mb_aff_frame_flag ) + currSlice->current_mb_nr = currSlice->start_mb_nr << 1; + else + currSlice->current_mb_nr = currSlice->start_mb_nr; + + // Now I need to read the slice ID, which depends on the value of + // redundant_pic_cnt_present_flag + + slice_id_a = read_ue_v( "NALU: DP_A slice_id", currStream, &p_Dec->UsedBits); + + if( p_Vid->active_pps->entropy_coding_mode_flag ) + error( "received data partition with CABAC, this is not allowed", 500); + + // continue with reading next DP + if( 0 == read_next_nalu( p_Vid, nalu) ) + return current_header; + + if( NALU_TYPE_DPB == nalu->nal_unit_type ) + { + // we got a DPB + currStream = currSlice->partArr[1].bitstream; + currStream->ei_flag = 0; + currStream->frame_bitoffset = currStream->read_len = 0; + + memcpy( currStream->streamBuffer, &nalu->buf[1], nalu->len - 1); + currStream->code_len = currStream->bitstream_length = RBSPtoSODB( currStream->streamBuffer, nalu->len - 1); + + slice_id_b = read_ue_v( "NALU: DP_B slice_id", currStream, &p_Dec->UsedBits); + + currSlice->dpB_NotPresent = 0; + + if( (slice_id_b != slice_id_a) || (nalu->lost_packets) ) + { + printf( "Waning: got a data partition B which does not match DP_A (DP loss!)\n"); + currSlice->dpB_NotPresent = 1; + currSlice->dpC_NotPresent = 1; + } + else + { + if( p_Vid->active_pps->redundant_pic_cnt_present_flag ) + read_ue_v( "NALU: DP_B redundant_pic_cnt", currStream, &p_Dec->UsedBits); + + // we're finished with DP_B, so let's continue with next DP + if( 0 == read_next_nalu( p_Vid, nalu) ) + return current_header; + } + } + else + { + currSlice->dpB_NotPresent = 1; + } + + // check if we got DP_C + if( NALU_TYPE_DPC == nalu->nal_unit_type ) + { + currStream = currSlice->partArr[2].bitstream; + currStream->ei_flag = 0; + currStream->frame_bitoffset = currStream->read_len = 0; + + memcpy( currStream->streamBuffer, &nalu->buf[1], nalu->len - 1); + currStream->code_len = currStream->bitstream_length = RBSPtoSODB( currStream->streamBuffer, nalu->len - 1); + + currSlice->dpC_NotPresent = 0; + + slice_id_c = read_ue_v( "NALU: DP_C slice_id", currStream, &p_Dec->UsedBits); + if( (slice_id_c != slice_id_a) || (nalu->lost_packets) ) + { + printf( "Warning: got a data partition C which does not match DP_A(DP loss!)\n"); + //currSlice->dpB_NotPresent =1; + currSlice->dpC_NotPresent = 1; + } + + if( p_Vid->active_pps->redundant_pic_cnt_present_flag ) + read_ue_v( "NALU:SLICE_C redudand_pic_cnt", currStream, &p_Dec->UsedBits); + } + else + { + currSlice->dpC_NotPresent = 1; + } + + // check if we read anything else than the expected partitions + if( (nalu->nal_unit_type != NALU_TYPE_DPB) && (nalu->nal_unit_type != NALU_TYPE_DPC) ) + { + // we have a NALI that we can't process here, so restart processing + goto process_nalu; + // yes, "goto" should not be used, but it's really the best way here before we restructure the decoding loop + // (which should be taken care of anyway) + } + + //FreeNALU(nalu); + return current_header; + break; + case NALU_TYPE_DPB: + if( p_Inp->silent == FALSE ) + { + printf( "found data partition B without matching DP A, discarding\n"); + } + break; + case NALU_TYPE_DPC: + if( p_Inp->silent == FALSE ) + { + printf( "found data partition C without matching DP A, discarding\n"); + } + break; + case NALU_TYPE_SEI: +#if( FOUND_NALU ) + printf ("read_new_slice: Found NALU_TYPE_SEI, len %d\n", nalu->len); +#endif + InterpretSEIMessage( nalu->buf, nalu->len, p_Vid, currSlice); + break; + case NALU_TYPE_PPS: +#if( FOUND_NALU ) + printf ("Found NALU_TYPE_PPS\n"); +#endif + ProcessPPS( p_Vid, nalu); + break; + case NALU_TYPE_SPS: +#if( FOUND_NALU ) + printf ("Found NALU_TYPE_SPS\n"); +#endif + ProcessSPS( p_Vid, nalu); + break; + case NALU_TYPE_AUD: +#if( FOUND_NALU ) + printf ("Found NALU_TYPE_AUD\n"); + printf ("read_new_slice: Found 'Access Unit Delimiter' NAL unit, len %d, ignored\n", nalu->len); +#endif + + break; + case NALU_TYPE_EOSEQ: +#if( FOUND_NALU ) + printf ("read_new_slice: Found 'End of Sequence' NAL unit, len %d, ignored\n", nalu->len); +#endif + break; + case NALU_TYPE_EOSTREAM: +#if( FOUND_NALU ) + printf ("read_new_slice: Found 'End of Stream' NAL unit, len %d, ignored\n", nalu->len); +#endif + break; + case NALU_TYPE_FILL: + if( p_Inp->silent == FALSE ) + { + +#if( FOUND_NALU ) + printf ("read_new_slice: Found NALU_TYPE_FILL, len %d\n", (int) nalu->len); + printf ("Skipping these filling bits, proceeding w/ next NALU\n"); +#endif + } + break; +#if (MVC_EXTENSION_ENABLE) + case NALU_TYPE_VDRD: +#if( FOUND_NALU ) + printf ("Found NALU_TYPE_VDRD\n"); +#endif + // printf ("read_new_slice: Found 'View and Dependency Representation Delimiter' NAL unit, len %d, ignored\n", nalu->len); + break; + case NALU_TYPE_PREFIX: +#if( FOUND_NALU ) + printf ("Found NALU_TYPE_PREFIX\n"); +#endif + if( currSlice->svc_extension_flag == 1 ) + prefix_nal_unit_svc( ); + break; + case NALU_TYPE_SUB_SPS: +#if( FOUND_NALU ) + printf ("Found NALU_TYPE_SUB_SPS\n"); +#endif + if( p_Inp->DecodeAllLayers == 1 ) + { + ProcessSubsetSPS( p_Vid, nalu); + } + else + { + if( p_Inp->silent == FALSE ) + printf( "Found Subsequence SPS NALU. Ignoring.\n"); + } + break; + case NALU_TYPE_SLC_EXT: +#if( FOUND_NALU ) + printf ("Found NALU_TYPE_SLC_EXT\n"); +#endif + if( p_Inp->DecodeAllLayers == 0 && (p_Inp->silent == FALSE) ) + printf( "Found SVC extension NALU (%d). Ignoring.\n", (int) nalu->nal_unit_type); + break; +#endif + default: + { + if( p_Inp->silent == FALSE ) + printf( "Found NALU type %d, len %d undefined, ignore NALU, moving on\n", (int) nalu->nal_unit_type, (int) nalu->len); + } + break; + } + } +} + +void pad_buf( imgpel *pImgBuf, int iWidth, int iHeight, int iStride, int iPadX, int iPadY) +{ + int j; + imgpel *pLine0 = pImgBuf - iPadX, *pLine; +#if (IMGTYPE==0) + int pad_width = iPadX + iWidth; + fast_memset( pImgBuf - iPadX, *pImgBuf, iPadX * sizeof(imgpel)); + fast_memset( pImgBuf + iWidth, *(pImgBuf + iWidth - 1), iPadX * sizeof(imgpel)); + + pLine = pLine0 - iPadY * iStride; + + for( j = -iPadY; j < 0; j++ ) + { + fast_memcpy( pLine, pLine0, iStride * sizeof(imgpel)); + pLine += iStride; + } + + for( j = 1; j < iHeight; j++ ) + { + pLine += iStride; + fast_memset( pLine, *(pLine + iPadX), iPadX * sizeof(imgpel)); + fast_memset( pLine + pad_width, *(pLine + pad_width - 1), iPadX * sizeof(imgpel)); + } + + pLine0 = pLine + iStride; + + for( j = iHeight; j < iHeight + iPadY; j++ ) + { + fast_memcpy( pLine0, pLine, iStride * sizeof(imgpel)); + pLine0 += iStride; + } +#else + int i; + for(i=-iPadX; i<0; i++) + pImgBuf[i] = *pImgBuf; + for(i=0; iiLumaPadX; + int iPadY = p_Vid->iLumaPadY; + int iWidth = dec_picture->size_x; + int iHeight = dec_picture->size_y; + int iStride = dec_picture->iLumaStride; + + pad_buf( *dec_picture->imgY, iWidth, iHeight, iStride, iPadX, iPadY); + + if( dec_picture->chroma_format_idc != YUV400 ) + { + iPadX = p_Vid->iChromaPadX; + iPadY = p_Vid->iChromaPadY; + iWidth = dec_picture->size_x_cr; + iHeight = dec_picture->size_y_cr; + iStride = dec_picture->iChromaStride; + pad_buf( *dec_picture->imgUV[0], iWidth, iHeight, iStride, iPadX, iPadY); + pad_buf( *dec_picture->imgUV[1], iWidth, iHeight, iStride, iPadX, iPadY); + } +} + +/*! + ************************************************************************ + * \brief + * finish decoding of a picture, conceal errors and store it + * into the DPB + ************************************************************************ + */ +void exit_picture( VideoParameters *p_Vid, StorablePicture **dec_picture) +{ + InputParameters *p_Inp = p_Vid->p_Inp; +#if( MVC_EXTENSION_ENABLE) + SNRParameters *snr = p_Vid->snr[(*dec_picture)->view_id]; +#else + SNRParameters *snr = p_Vid->snr; +#endif + char yuv_types[4][6] = + { "4:0:0", "4:2:0", "4:2:2", "4:4:4" }; +#if (DISABLE_ERC == 0) + int ercStartMB; + int ercSegment; + frame recfr; +#endif + +#if(MVC_EXTENSION_ENABLE) + int view_id; +#endif + int structure, frame_poc, slice_type, refpic, qp, pic_num, chroma_format_idc, is_idr; + + int64 tmp_time; // time used by decoding the last frame + char yuvFormat[10]; + + // return if the last picture has already been finished + if( *dec_picture == NULL || p_Vid->num_dec_mb == 0 ) + //if (*dec_picture==NULL || (p_Vid->num_dec_mb != p_Vid->PicSizeInMbs && (p_Vid->yuv_format != YUV444 || !p_Vid->separate_colour_plane_flag))) + { + return; + } + + showStorablePicture( p_Vid , *dec_picture , 0 , 1 , "Decoded Frame", 0); + +//#define LOST_POC 8 +// if( (*dec_picture)->poc == LOST_POC ) +// { +// fercModLostRefFrames( p_Vid, (*dec_picture) ); +// } + + //! Save some information for error concealment + fercSaveListsInfo( p_Vid, p_Vid->ppSliceList[0]); + + saveTranscodingInfo( p_Vid, (*dec_picture)); + +#ifndef DISABLE_MB_DECODING + +#if (DISABLE_ERC == 0) + recfr.p_Vid = p_Vid; + recfr.yptr = &(*dec_picture)->imgY[0][0]; + if ((*dec_picture)->chroma_format_idc != YUV400) + { + recfr.uptr = &(*dec_picture)->imgUV[0][0][0]; + recfr.vptr = &(*dec_picture)->imgUV[1][0][0]; + } + + //! this is always true at the beginning of a picture + ercStartMB = 0; + ercSegment = 0; + + //! mark the start of the first segment + if (!(*dec_picture)->mb_aff_frame_flag) + { + unsigned int i; + ercStartSegment(0, ercSegment, 0 , p_Vid->erc_errorVar); + //! generate the segments according to the macroblock map + for(i = 1; i<(*dec_picture)->PicSizeInMbs; ++i) + { + if(p_Vid->mb_data[i].ei_flag != p_Vid->mb_data[i-1].ei_flag) + { + ercStopSegment(i-1, ercSegment, 0, p_Vid->erc_errorVar); //! stop current segment + + //! mark current segment as lost or OK + if(p_Vid->mb_data[i-1].ei_flag) + ercMarkCurrSegmentLost((*dec_picture)->size_x, p_Vid->erc_errorVar); + else + ercMarkCurrSegmentOK((*dec_picture)->size_x, p_Vid->erc_errorVar); + + ++ercSegment;//! next segment + ercStartSegment(i, ercSegment, 0 , p_Vid->erc_errorVar);//! start new segment + ercStartMB = i;//! save start MB for this segment + } + } + //! mark end of the last segment + ercStopSegment((*dec_picture)->PicSizeInMbs-1, ercSegment, 0, p_Vid->erc_errorVar); + if(p_Vid->mb_data[i-1].ei_flag) + ercMarkCurrSegmentLost((*dec_picture)->size_x, p_Vid->erc_errorVar); + else + ercMarkCurrSegmentOK((*dec_picture)->size_x, p_Vid->erc_errorVar); + + //! call the right error concealment function depending on the frame type. + p_Vid->erc_mvperMB /= (*dec_picture)->PicSizeInMbs; + + p_Vid->erc_img = p_Vid; + + if((*dec_picture)->slice_type == I_SLICE || (*dec_picture)->slice_type == SI_SLICE)// I-frame + ercConcealIntraFrame(p_Vid, &recfr, (*dec_picture)->size_x, (*dec_picture)->size_y, p_Vid->erc_errorVar); + else + ercConcealInterFrame(&recfr, p_Vid->erc_object_list, (*dec_picture)->size_x, (*dec_picture)->size_y, p_Vid->erc_errorVar, (*dec_picture)->chroma_format_idc); + } +#endif + + if( !p_Vid->iDeblockMode && (p_Vid->bDeblockEnable & (1 << (*dec_picture)->used_for_reference)) ) + { + //deblocking for frame or field + if( (p_Vid->separate_colour_plane_flag != 0) ) + { + int nplane; + int colour_plane_id = p_Vid->ppSliceList[0]->colour_plane_id; + for( nplane = 0; nplane < MAX_PLANE; ++nplane ) + { + p_Vid->ppSliceList[0]->colour_plane_id = nplane; + change_plane_JV( p_Vid, nplane, NULL ); + DeblockPicture( p_Vid, *dec_picture); + } + p_Vid->ppSliceList[0]->colour_plane_id = colour_plane_id; + make_frame_picture_JV( p_Vid); + } + else + { + DeblockPicture( p_Vid, *dec_picture); + } + } + else + { + if( (p_Vid->separate_colour_plane_flag != 0) ) + { + make_frame_picture_JV( p_Vid); + } + } +#endif + + if( (*dec_picture)->mb_aff_frame_flag ) + MbAffPostProc( p_Vid); + + if( p_Vid->structure == FRAME ) // buffer mgt. for frame mode + frame_postprocessing( p_Vid); + else + field_postprocessing( p_Vid); // reset all interlaced variables +#if (MVC_EXTENSION_ENABLE) + if( (*dec_picture)->used_for_reference || ((*dec_picture)->inter_view_flag == 1) ) + pad_dec_picture( p_Vid, *dec_picture); +#else + if((*dec_picture)->used_for_reference) + pad_dec_picture(p_Vid, *dec_picture); +#endif + structure = (*dec_picture)->structure; + slice_type = (*dec_picture)->slice_type; + frame_poc = (*dec_picture)->frame_poc; + refpic = (*dec_picture)->used_for_reference; + qp = (*dec_picture)->qp; + pic_num = (*dec_picture)->pic_num; + is_idr = (*dec_picture)->idr_flag; +#if(MVC_EXTENSION_ENABLE) + view_id = (*dec_picture)->view_id; +#endif + + chroma_format_idc = (*dec_picture)->chroma_format_idc; + +#if MVC_EXTENSION_ENABLE + store_picture_in_dpb( p_Vid->p_Dpb_layer[(*dec_picture)->view_id], *dec_picture); +#else + store_picture_in_dpb(p_Vid->p_Dpb_layer[0], *dec_picture); +#endif + + calculate_frame_no( p_Vid, (*dec_picture)); + + p_Vid->nFramesDecoded[view_id]++; + + /* Edit by Jo���o Carreira - Control de Number of IDR frames to control the frame_no */ + if( (*dec_picture)->idr_flag ) + { + p_Vid->IDRframe_ctr++; + p_Vid->frame_no_idr = p_Vid->frame_no; + } + +#if(MVC_EXTENSION_ENABLE) + if( p_Vid->p_ref_mvc[GetViewIdx( p_Vid, view_id)] != -1 ) + find_snr( p_Vid, (*dec_picture), &(p_Vid->p_ref_mvc[GetViewIdx( p_Vid, view_id)])); +#else + if( p_Vid->p_ref != -1) + find_snr( p_Vid, (*dec_picture) , &p_Vid->p_ref); +#endif + + if( p_Vid->last_has_mmco_5 ) + { +#if (MVC_EXTENSION_ENABLE) + // p_Vid->pre_frame_num[(*dec_picture)->view_id] = 0; +#else + // p_Vid->pre_frame_num = 0; +#endif + } + + *dec_picture = NULL; + + if( p_Inp->silent == FALSE ) + { + if( structure == TOP_FIELD || structure == FRAME ) + { + if( slice_type == I_SLICE && is_idr ) // IDR picture + strcpy( p_Vid->cslice_type, "IDR"); + else if( slice_type == I_SLICE ) // I picture + strcpy( p_Vid->cslice_type, " I "); + else if( slice_type == P_SLICE ) // P pictures + strcpy( p_Vid->cslice_type, " P "); + else if( slice_type == SP_SLICE ) // SP pictures + strcpy( p_Vid->cslice_type, "SP "); + else if( slice_type == SI_SLICE ) + strcpy( p_Vid->cslice_type, "SI "); + else if( refpic ) // stored B pictures + strcpy( p_Vid->cslice_type, " B "); + else + // B pictures + strcpy( p_Vid->cslice_type, " b "); + + if( structure == FRAME ) + { + strncat( p_Vid->cslice_type, ") ", 8 - strlen( p_Vid->cslice_type)); + } + } + else if( structure == BOTTOM_FIELD ) + { + if( slice_type == I_SLICE && is_idr ) // IDR picture + strncat( p_Vid->cslice_type, "|IDR)", 8 - strlen( p_Vid->cslice_type)); + else if( slice_type == I_SLICE ) // I picture + strncat( p_Vid->cslice_type, "| I )", 8 - strlen( p_Vid->cslice_type)); + else if( slice_type == P_SLICE ) // P pictures + strncat( p_Vid->cslice_type, "| P )", 8 - strlen( p_Vid->cslice_type)); + else if( slice_type == SP_SLICE ) // SP pictures + strncat( p_Vid->cslice_type, "|SP )", 8 - strlen( p_Vid->cslice_type)); + else if( slice_type == SI_SLICE ) + strncat( p_Vid->cslice_type, "|SI )", 8 - strlen( p_Vid->cslice_type)); + else if( refpic ) // stored B pictures + strncat( p_Vid->cslice_type, "| B )", 8 - strlen( p_Vid->cslice_type)); + else + // B pictures + strncat( p_Vid->cslice_type, "| b )", 8 - strlen( p_Vid->cslice_type)); + } + } + + if( (structure == FRAME) || structure == BOTTOM_FIELD ) + { + gettime( &(p_Vid->end_time)); // end time + + tmp_time = timediff( &(p_Vid->start_time), &(p_Vid->end_time)); + p_Vid->tot_time += tmp_time; + tmp_time = timenorm( tmp_time); + sprintf( yuvFormat, "%s", yuv_types[chroma_format_idc]); + + if( p_Inp->silent == FALSE ) + { +#if( MVC_EXTENSION_ENABLE) + SNRParameters *snr = p_Vid->snr[view_id]; + if( p_Vid->p_ref_mvc[view_id] != -1 ) + fprintf( stdout, "%05d(%s%3d|%2d %5d %5d %8.4f %8.4f %8.4f %s %7d\n", p_Vid->frame_no, p_Vid->cslice_type, frame_poc, GetViewIdx( p_Vid, view_id), + pic_num, qp, snr->snr[0], snr->snr[1], snr->snr[2], yuvFormat, (int) tmp_time); + else + fprintf( stdout, "%05d(%s%3d|%2d %5d %5d %s %7d\n", p_Vid->frame_no, p_Vid->cslice_type, frame_poc, + GetViewIdx( p_Vid, view_id), pic_num, qp, yuvFormat, (int) tmp_time); +#else + SNRParameters *snr = p_Vid->snr; + if (p_Vid->p_ref != -1) + fprintf(stdout,"%05d(%s%5d %5d %5d %8.4f %8.4f %8.4f %s %7d\n", + p_Vid->frame_no, p_Vid->cslice_type, frame_poc, pic_num, qp, snr->snr[0], snr->snr[1], snr->snr[2], yuvFormat, (int) tmp_time); + else + fprintf(stdout,"%05d(%s%5d %5d %5d %s %7d\n", + p_Vid->frame_no, p_Vid->cslice_type, frame_poc, pic_num, qp, yuvFormat, (int)tmp_time); +#endif + } + else + { + fprintf( stdout, "Completed Decoding frame %05d.\r", snr->frame_ctr); + } + fflush( stdout); + + if( slice_type == I_SLICE || slice_type == SI_SLICE || slice_type == P_SLICE || refpic ) // I or P pictures + { +#if (MVC_EXTENSION_ENABLE) + if( (p_Vid->ppSliceList[0])->view_id != 0 ) +#endif + ++(p_Vid->number); + } + else + ++(p_Vid->Bframe_ctr); // B pictures + ++(snr->frame_ctr); + +#if (MVC_EXTENSION_ENABLE) + if( (p_Vid->ppSliceList[0])->view_id != 0 ) +#endif + ++(p_Vid->g_nFrame); + } + + //p_Vid->currentSlice->current_mb_nr = -4712; // impossible value for debugging, StW + //p_Vid->currentSlice->current_slice_nr = 0; +} + +/*! + ************************************************************************ + * \brief + * set defaults for old_slice + * NAL unit of a picture" + ************************************************************************ + */ +void init_old_slice( OldSliceParams *p_old_slice) +{ + p_old_slice->field_pic_flag = 0; + p_old_slice->pps_id = INT_MAX; + p_old_slice->frame_num = INT_MAX; + p_old_slice->nal_ref_idc = INT_MAX; + p_old_slice->idr_flag = FALSE; + + p_old_slice->pic_oder_cnt_lsb = UINT_MAX; + p_old_slice->delta_pic_oder_cnt_bottom = INT_MAX; + + p_old_slice->delta_pic_order_cnt[0] = INT_MAX; + p_old_slice->delta_pic_order_cnt[1] = INT_MAX; +} + +void copy_slice_info( Slice *currSlice, OldSliceParams *p_old_slice) +{ + VideoParameters *p_Vid = currSlice->p_Vid; + + p_old_slice->pps_id = currSlice->pic_parameter_set_id; + p_old_slice->frame_num = currSlice->frame_num; //p_Vid->frame_num; + p_old_slice->field_pic_flag = currSlice->field_pic_flag; //p_Vid->field_pic_flag; + + if( currSlice->field_pic_flag ) + { + p_old_slice->bottom_field_flag = currSlice->bottom_field_flag; + } + + p_old_slice->nal_ref_idc = currSlice->nal_reference_idc; + p_old_slice->idr_flag = (byte) currSlice->idr_flag; + + if( currSlice->idr_flag ) + { + p_old_slice->idr_pic_id = currSlice->idr_pic_id; + } + + if( p_Vid->active_sps->pic_order_cnt_type == 0 ) + { + p_old_slice->pic_oder_cnt_lsb = currSlice->pic_order_cnt_lsb; + p_old_slice->delta_pic_oder_cnt_bottom = currSlice->delta_pic_order_cnt_bottom; + } + + if( p_Vid->active_sps->pic_order_cnt_type == 1 ) + { + p_old_slice->delta_pic_order_cnt[0] = currSlice->delta_pic_order_cnt[0]; + p_old_slice->delta_pic_order_cnt[1] = currSlice->delta_pic_order_cnt[1]; + } +#if (MVC_EXTENSION_ENABLE) + p_old_slice->view_id = currSlice->view_id; + p_old_slice->inter_view_flag = currSlice->inter_view_flag; + p_old_slice->anchor_pic_flag = currSlice->anchor_pic_flag; +#endif + p_old_slice->layer_id = currSlice->layer_id; +} + +/*! + ************************************************************************ + * \brief + * detect if current slice is "first VCL NAL unit of a picture" + ************************************************************************ + */ +int is_new_picture( StorablePicture *dec_picture, Slice *currSlice, OldSliceParams *p_old_slice) +{ + VideoParameters *p_Vid = currSlice->p_Vid; + + int result = 0; + + result |= (NULL == dec_picture); + + result |= (p_old_slice->pps_id != currSlice->pic_parameter_set_id); + + result |= (p_old_slice->frame_num != currSlice->frame_num); + + result |= (p_old_slice->field_pic_flag != currSlice->field_pic_flag); + + if( currSlice->field_pic_flag && p_old_slice->field_pic_flag ) + { + result |= (p_old_slice->bottom_field_flag != currSlice->bottom_field_flag); + } + + result |= (p_old_slice->nal_ref_idc != currSlice->nal_reference_idc) && ((p_old_slice->nal_ref_idc == 0) || (currSlice->nal_reference_idc == 0)); + result |= (p_old_slice->idr_flag != currSlice->idr_flag); + + if( currSlice->idr_flag && p_old_slice->idr_flag ) + { + result |= (p_old_slice->idr_pic_id != currSlice->idr_pic_id); + } + + if( p_Vid->active_sps->pic_order_cnt_type == 0 ) + { + result |= (p_old_slice->pic_oder_cnt_lsb != currSlice->pic_order_cnt_lsb); + if( p_Vid->active_pps->bottom_field_pic_order_in_frame_present_flag == 1 && !currSlice->field_pic_flag ) + { + result |= (p_old_slice->delta_pic_oder_cnt_bottom != currSlice->delta_pic_order_cnt_bottom); + } + } + + if( p_Vid->active_sps->pic_order_cnt_type == 1 ) + { + if( !p_Vid->active_sps->delta_pic_order_always_zero_flag ) + { + result |= (p_old_slice->delta_pic_order_cnt[0] != currSlice->delta_pic_order_cnt[0]); + if( p_Vid->active_pps->bottom_field_pic_order_in_frame_present_flag == 1 && !currSlice->field_pic_flag ) + { + result |= (p_old_slice->delta_pic_order_cnt[1] != currSlice->delta_pic_order_cnt[1]); + } + } + } + +#if (MVC_EXTENSION_ENABLE) + result |= (currSlice->view_id != p_old_slice->view_id); + result |= (currSlice->inter_view_flag != p_old_slice->inter_view_flag); + result |= (currSlice->anchor_pic_flag != p_old_slice->anchor_pic_flag); +#endif + result |= (currSlice->layer_id != p_old_slice->layer_id); + return result; +} + +/*! + ************************************************************************ + * \brief + * Prepare field and frame buffer after frame decoding + ************************************************************************ + */ +void frame_postprocessing( VideoParameters *p_Vid) +{ +} + +/*! + ************************************************************************ + * \brief + * Prepare field and frame buffer after field decoding + ************************************************************************ + */ +void field_postprocessing( VideoParameters *p_Vid) +{ + p_Vid->number /= 2; +} + +/*! + ************************************************************************ + * \brief + * copy StorablePicture *src -> StorablePicture *dst + * for 4:4:4 Independent mode + ************************************************************************ + */ +void copy_dec_picture_JV( VideoParameters *p_Vid, StorablePicture *dst, StorablePicture *src) +{ + dst->top_poc = src->top_poc; + dst->bottom_poc = src->bottom_poc; + dst->frame_poc = src->frame_poc; + dst->qp = src->qp; + dst->slice_qp_delta = src->slice_qp_delta; + dst->chroma_qp_offset[0] = src->chroma_qp_offset[0]; + dst->chroma_qp_offset[1] = src->chroma_qp_offset[1]; + + dst->poc = src->poc; + + dst->slice_type = src->slice_type; + dst->used_for_reference = src->used_for_reference; + dst->idr_flag = src->idr_flag; + dst->no_output_of_prior_pics_flag = src->no_output_of_prior_pics_flag; + dst->long_term_reference_flag = src->long_term_reference_flag; + dst->adaptive_ref_pic_buffering_flag = src->adaptive_ref_pic_buffering_flag; + + dst->dec_ref_pic_marking_buffer = src->dec_ref_pic_marking_buffer; + + dst->mb_aff_frame_flag = src->mb_aff_frame_flag; + dst->PicWidthInMbs = src->PicWidthInMbs; + dst->pic_num = src->pic_num; + dst->frame_num = src->frame_num; + dst->recovery_frame = src->recovery_frame; + dst->coded_frame = src->coded_frame; + + dst->chroma_format_idc = src->chroma_format_idc; + + dst->frame_mbs_only_flag = src->frame_mbs_only_flag; + dst->frame_cropping_flag = src->frame_cropping_flag; + + dst->frame_crop_left_offset = src->frame_crop_left_offset; + dst->frame_crop_right_offset = src->frame_crop_right_offset; + dst->frame_crop_top_offset = src->frame_crop_top_offset; + dst->frame_crop_bottom_offset = src->frame_crop_bottom_offset; + +#if (ENABLE_OUTPUT_TONEMAPPING) + // store the necessary tone mapping sei into StorablePicture structure + dst->seiHasTone_mapping = src->seiHasTone_mapping; + + dst->seiHasTone_mapping = src->seiHasTone_mapping; + dst->tone_mapping_model_id = src->tone_mapping_model_id; + dst->tonemapped_bit_depth = src->tonemapped_bit_depth; + if( src->tone_mapping_lut ) + { + int coded_data_bit_max = (1 << p_Vid->seiToneMapping->coded_data_bit_depth); + dst->tone_mapping_lut = malloc( sizeof(int) * coded_data_bit_max); + if( NULL == dst->tone_mapping_lut ) + { + no_mem_exit( "copy_dec_picture_JV: tone_mapping_lut"); + } + memcpy( dst->tone_mapping_lut, src->tone_mapping_lut, sizeof(imgpel) * coded_data_bit_max); + } +#endif +} + +// this is intended to make get_block_luma faster by doing this at a more appropriate level +// i.e. per slice rather than per MB +static void init_cur_imgy( Slice *currSlice, VideoParameters *p_Vid) +{ + int i, j; + if( (p_Vid->separate_colour_plane_flag != 0) ) + { + StorablePicture *vidref = p_Vid->no_reference_picture; + int noref = (currSlice->framepoc < p_Vid->recovery_poc); + switch( currSlice->colour_plane_id ) + { + case 0: + for( j = 0; j < 6; j++ ) //for (j = 0; j < (currSlice->slice_type==B_SLICE?2:1); j++) { + { + for( i = 0; i < MAX_LIST_SIZE; i++ ) + { + StorablePicture *curr_ref = currSlice->listX[j][i]; + if( curr_ref ) + { + curr_ref->no_ref = noref && (curr_ref == vidref); + curr_ref->cur_imgY = curr_ref->imgY; + } + } + } + break; + } + } + else + { + StorablePicture *vidref = p_Vid->no_reference_picture; + int noref = (currSlice->framepoc < p_Vid->recovery_poc); + int total_lists = currSlice->mb_aff_frame_flag ? 6 : (currSlice->slice_type == B_SLICE ? 2 : 1); + // for (j = 0; j < 6; j++) { //for (j = 0; j < (currSlice->slice_type==B_SLICE?2:1); j++) { + for( j = 0; j < total_lists; j++ ) + { + // note that if we always set this to MAX_LIST_SIZE, we avoid crashes with invalid ref_idx being set + // since currently this is done at the slice level, it seems safe to do so. + // Note for some reason I get now a mismatch between version 12 and this one in cabac. I wonder why. + //for (i = 0; i < currSlice->listXsize[j]; i++) + for( i = 0; i < MAX_LIST_SIZE; i++ ) + { + StorablePicture *curr_ref = currSlice->listX[j][i]; + if( curr_ref ) + { + curr_ref->no_ref = noref && (curr_ref == vidref); + curr_ref->cur_imgY = curr_ref->imgY; + } + } + } + } +} + +/*! + ************************************************************************ + * \brief + * decodes one slice + ************************************************************************ + */ +void decode_one_slice( Slice *currSlice) +{ + VideoParameters *p_Vid = currSlice->p_Vid; + Boolean end_of_slice = FALSE; + Macroblock *currMB = NULL; + + currSlice->cod_counter = -1; + + if( (p_Vid->separate_colour_plane_flag != 0) ) + { + change_plane_JV( p_Vid, currSlice->colour_plane_id, currSlice); + } + else + { + currSlice->mb_data = p_Vid->mb_data; + currSlice->dec_picture = p_Vid->dec_picture; + currSlice->siblock = p_Vid->siblock; + currSlice->ipredmode = p_Vid->ipredmode; + currSlice->intra_block = p_Vid->intra_block; + } + + if( currSlice->slice_type == B_SLICE ) + { + compute_colocated( currSlice, currSlice->listX); + } + + if( currSlice->slice_type != I_SLICE && currSlice->slice_type != SI_SLICE ) + init_cur_imgy( currSlice, p_Vid); + + /* Edited by Joao Carreira */ + if( p_Vid->p_Inp->error_prop && currSlice->slice_type != I_SLICE ) + ProjectMotionToCurrSlice( p_Vid, currSlice->dec_picture, currSlice); + + //reset_ec_flags(p_Vid); + + //#ifdef DISABLE_MB_DECODING + // currSlice->num_dec_mb = p_Vid->PicSizeInMbs; + // return; + //#endif + currSlice->p_Vid->n_SKIP = 0; + + while( end_of_slice == FALSE ) // loop over macroblocks + { + +#if TRACE + fprintf(p_Dec->p_trace,"\n*********** POC: %i (I/P) MB: %i Slice: %i Type %d **********\n", currSlice->ThisPOC, currSlice->current_mb_nr, currSlice->current_slice_nr, currSlice->slice_type); +#endif + + // Initializes the current macroblock + start_macroblock( currSlice, &currMB); + // Get the syntax elements from the NAL + currSlice->read_one_macroblock( currMB); + + //if( MBCounter++ == 1536 ) + // continue; + + decode_one_macroblock( currMB, currSlice->dec_picture); + + if( currSlice->mb_aff_frame_flag && currMB->mb_field ) + { + currSlice->num_ref_idx_active[LIST_0] >>= 1; + currSlice->num_ref_idx_active[LIST_1] >>= 1; + } + +#if (DISABLE_ERC == 0) + ercWriteMBMODEandMV(currMB); +#endif + + end_of_slice = exit_macroblock( currSlice, (!currSlice->mb_aff_frame_flag || currSlice->current_mb_nr % 2)); + + currMB->is_decoded = 1; + } + //reset_ec_flags(p_Vid); + + /* Edited by Jo���o Carreira */ + if( currSlice->dec_picture->disparity ) + free_storable_picture( currSlice->dec_picture->disparity); + if( p_Vid->p_Inp->error_prop ) + fercFreeMotionPropInfo( currSlice->dec_picture); + + //! Edited by Joao Carreira -> simple prediction parser +// if( p_Vid->p_Inp->PredictionParser ) +// { +// //parse_prediction( p_Vid, currSlice, p_Vid->dec_picture); +// //parse_mv_prediction( p_Vid, currSlice, p_Vid->dec_picture); +// if( p_Vid->dec_picture->frame_no == 4 && p_Vid->dec_picture->view_id == 1 ) +// parse_mv_values( p_Vid, currSlice, p_Vid->dec_picture, 1); +// +// if( p_Vid->dec_picture->frame_no == 4 && p_Vid->dec_picture->view_id == 0 ) +// parse_mv_values( p_Vid, currSlice, p_Vid->dec_picture, 1); +// } +} + +#if (MVC_EXTENSION_ENABLE) +int GetVOIdx( VideoParameters *p_Vid, int iViewId) +{ + int iVOIdx = -1; + int *piViewIdMap; + if( p_Vid->active_subset_sps ) + { + piViewIdMap = p_Vid->active_subset_sps->view_id; + for( iVOIdx = p_Vid->active_subset_sps->num_views_minus1; iVOIdx >= 0; iVOIdx-- ) + if( piViewIdMap[iVOIdx] == iViewId ) + break; + } + else + { + subset_seq_parameter_set_rbsp_t *curr_subset_sps; + int i; + + curr_subset_sps = p_Vid->SubsetSeqParSet; + for( i = 0; i < MAXSPS; i++ ) + { + if( curr_subset_sps->num_views_minus1 >= 0 && curr_subset_sps->sps.Valid ) + { + break; + } + curr_subset_sps++; + } + + if( i < MAXSPS ) + { + p_Vid->active_subset_sps = curr_subset_sps; + + piViewIdMap = p_Vid->active_subset_sps->view_id; + for( iVOIdx = p_Vid->active_subset_sps->num_views_minus1; iVOIdx >= 0; iVOIdx-- ) + if( piViewIdMap[iVOIdx] == iViewId ) + break; + + return iVOIdx; + } + else + { + iVOIdx = 0; + } + } + + return iVOIdx; +} + +int GetViewIdx( VideoParameters *p_Vid, int iVOIdx) +{ + int iViewIdx = iVOIdx; + int *piViewIdMap; + + if( p_Vid->active_subset_sps ) + { + assert( p_Vid->active_subset_sps->num_views_minus1 >= iVOIdx && iVOIdx >= 0); + piViewIdMap = p_Vid->active_subset_sps->view_id; + iViewIdx = piViewIdMap[iVOIdx]; + } + + return iViewIdx; +} +int get_maxViewIdx( VideoParameters *p_Vid, int view_id, int anchor_pic_flag, int listidx) +{ + int VOIdx; + int maxViewIdx = 0; + + VOIdx = view_id; + if( VOIdx >= 0 ) + { + if( anchor_pic_flag ) + maxViewIdx = listidx ? p_Vid->active_subset_sps->num_anchor_refs_l1[VOIdx] : p_Vid->active_subset_sps->num_anchor_refs_l0[VOIdx]; + else + maxViewIdx = listidx ? p_Vid->active_subset_sps->num_non_anchor_refs_l1[VOIdx] : p_Vid->active_subset_sps->num_non_anchor_refs_l0[VOIdx]; + } + + return maxViewIdx; +} +#endif diff --git a/ldecod/src/intra16x16_pred.c b/ldecod/src/intra16x16_pred.c new file mode 100644 index 0000000..ec7e665 --- /dev/null +++ b/ldecod/src/intra16x16_pred.c @@ -0,0 +1,348 @@ +/*! + ************************************************************************************* + * \file intra16x16_pred.c + * + * \brief + * Functions for intra 16x16 prediction + * + * \author + * Main contributors (see contributors.h for copyright, + * address and affiliation details) + * - Alexis Michael Tourapis + * + ************************************************************************************* + */ +#include "global.h" +#include "intra16x16_pred.h" +#include "mb_access.h" +#include "image.h" + +/*! + *********************************************************************** + * \brief + * makes and returns 16x16 DC prediction mode + * + * \return + * DECODING_OK decoding of intra_prediction mode was successful \n + * + *********************************************************************** + */ +static int intra16x16_dc_pred(Macroblock *currMB, ColorPlane pl) +{ + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + + int s0 = 0, s1 = 0, s2 = 0; + + int i,j; + + imgpel **imgY = (pl) ? currSlice->dec_picture->imgUV[pl - 1] : currSlice->dec_picture->imgY; + imgpel **mb_pred = &(currSlice->mb_pred[pl][0]); + + PixelPos a, b; + + int up_avail, left_avail; + + getNonAffNeighbour(currMB, -1, 0, p_Vid->mb_size[IS_LUMA], &a); + getNonAffNeighbour(currMB, 0, -1, p_Vid->mb_size[IS_LUMA], &b); + + if (!p_Vid->active_pps->constrained_intra_pred_flag) + { + up_avail = b.available; + left_avail = a.available; + } + else + { + up_avail = b.available ? currSlice->intra_block[b.mb_addr] : 0; + left_avail = a.available ? currSlice->intra_block[a.mb_addr]: 0; + } + + // Sum top predictors + if (up_avail) + { + imgpel *pel = &imgY[b.pos_y][b.pos_x]; + for (i = 0; i < MB_BLOCK_SIZE; ++i) + { + s1 += *pel++; + } + } + + // Sum left predictors + if (left_avail) + { + int pos_y = a.pos_y; + int pos_x = a.pos_x; + for (i = 0; i < MB_BLOCK_SIZE; ++i) + { + s2 += imgY[pos_y++][pos_x]; + } + } + + if (up_avail && left_avail) + s0 = (s1 + s2 + 16)>>5; // no edge + else if (!up_avail && left_avail) + s0 = (s2 + 8)>>4; // upper edge + else if (up_avail && !left_avail) + s0 = (s1 + 8)>>4; // left edge + else + s0 = p_Vid->dc_pred_value_comp[pl]; // top left corner, nothing to predict from + + for(j = 0; j < MB_BLOCK_SIZE; ++j) + { +#if (IMGTYPE == 0) + memset(mb_pred[j], s0, MB_BLOCK_SIZE * sizeof(imgpel)); +#else + for(i = 0; i < MB_BLOCK_SIZE; i += 4) + { + mb_pred[j][i ]=(imgpel) s0; + mb_pred[j][i + 1]=(imgpel) s0; + mb_pred[j][i + 2]=(imgpel) s0; + mb_pred[j][i + 3]=(imgpel) s0; + } +#endif + } + + return DECODING_OK; + +} + + +/*! + *********************************************************************** + * \brief + * makes and returns 16x16 vertical prediction mode + * + * \return + * DECODING_OK decoding of intra prediction mode was successful \n + * + *********************************************************************** + */ +static int intra16x16_vert_pred(Macroblock *currMB, ColorPlane pl) +{ + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + + int j; + + imgpel **imgY = (pl) ? currSlice->dec_picture->imgUV[pl - 1] : currSlice->dec_picture->imgY; + + PixelPos b; //!< pixel position p(0,-1) + + int up_avail; + + getNonAffNeighbour(currMB, 0, -1, p_Vid->mb_size[IS_LUMA], &b); + + if (!p_Vid->active_pps->constrained_intra_pred_flag) + { + up_avail = b.available; + } + else + { + up_avail = b.available ? currSlice->intra_block[b.mb_addr] : 0; + } + + if (!up_avail) + error ("invalid 16x16 intra pred Mode VERT_PRED_16",500); + { + imgpel **prd = &currSlice->mb_pred[pl][0]; + imgpel *src = &(imgY[b.pos_y][b.pos_x]); + + for(j=0;jp_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + int j; + + imgpel **imgY = (pl) ? currSlice->dec_picture->imgUV[pl - 1] : currSlice->dec_picture->imgY; + imgpel **mb_pred = &(currSlice->mb_pred[pl][0]); + imgpel prediction; + int pos_y, pos_x; + + PixelPos a; + + int left_avail; + + getNonAffNeighbour(currMB, -1, 0, p_Vid->mb_size[IS_LUMA], &a); + + if (!p_Vid->active_pps->constrained_intra_pred_flag) + { + left_avail = a.available; + } + else + { + left_avail = a.available ? currSlice->intra_block[a.mb_addr]: 0; + } + + if (!left_avail) + error ("invalid 16x16 intra pred Mode HOR_PRED_16",500); + + pos_y = a.pos_y; + pos_x = a.pos_x; + + for(j = 0; j < MB_BLOCK_SIZE; ++j) + { +#if (IMGTYPE == 0) + imgpel *prd = mb_pred[j]; + prediction = imgY[pos_y++][pos_x]; + + memset(prd, prediction, MB_BLOCK_SIZE * sizeof(imgpel)); +#else + int i; + imgpel *prd = mb_pred[j]; + prediction = imgY[pos_y++][pos_x]; + + for(i = 0; i < MB_BLOCK_SIZE; i += 4) + { + *prd++= prediction; // store predicted 16x16 block + *prd++= prediction; // store predicted 16x16 block + *prd++= prediction; // store predicted 16x16 block + *prd++= prediction; // store predicted 16x16 block + } +#endif + } + + return DECODING_OK; +} + + +/*! + *********************************************************************** + * \brief + * makes and returns 16x16 horizontal prediction mode + * + * \return + * DECODING_OK decoding of intra prediction mode was successful \n + * + *********************************************************************** + */ +static int intra16x16_plane_pred(Macroblock *currMB, ColorPlane pl) +{ + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + + int i,j; + + int ih = 0, iv = 0; + int ib,ic,iaa; + + imgpel **imgY = (pl) ? currSlice->dec_picture->imgUV[pl - 1] : currSlice->dec_picture->imgY; + imgpel **mb_pred = &(currSlice->mb_pred[pl][0]); + imgpel *mpr_line; + int max_imgpel_value = p_Vid->max_pel_value_comp[pl]; + int pos_y, pos_x; + + PixelPos a, b, d; + + int up_avail, left_avail, left_up_avail; + + getNonAffNeighbour(currMB, -1, -1, p_Vid->mb_size[IS_LUMA], &d); + getNonAffNeighbour(currMB, -1, 0, p_Vid->mb_size[IS_LUMA], &a); + getNonAffNeighbour(currMB, 0, -1, p_Vid->mb_size[IS_LUMA], &b); + + if (!p_Vid->active_pps->constrained_intra_pred_flag) + { + up_avail = b.available; + left_avail = a.available; + left_up_avail = d.available; + } + else + { + up_avail = b.available ? currSlice->intra_block[b.mb_addr] : 0; + left_avail = a.available ? currSlice->intra_block[a.mb_addr] : 0; + left_up_avail = d.available ? currSlice->intra_block[d.mb_addr] : 0; + } + + if (!up_avail || !left_up_avail || !left_avail) + error ("invalid 16x16 intra pred Mode PLANE_16",500); + + mpr_line = &imgY[b.pos_y][b.pos_x+7]; + pos_y = a.pos_y + 7; + pos_x = a.pos_x; + for (i = 1; i < 8; ++i) + { + ih += i*(mpr_line[i] - mpr_line[-i]); + iv += i*(imgY[pos_y + i][pos_x] - imgY[pos_y - i][pos_x]); + } + + ih += 8*(mpr_line[8] - imgY[d.pos_y][d.pos_x]); + iv += 8*(imgY[pos_y + 8][pos_x] - imgY[d.pos_y][d.pos_x]); + + ib=(5 * ih + 32)>>6; + ic=(5 * iv + 32)>>6; + + iaa=16 * (mpr_line[8] + imgY[pos_y + 8][pos_x]); + for (j = 0;j < MB_BLOCK_SIZE; ++j) + { + int ibb = iaa + (j - 7) * ic + 16; + imgpel *prd = mb_pred[j]; + for (i = 0;i < MB_BLOCK_SIZE; i += 4) + { + *prd++ = (imgpel) iClip1(max_imgpel_value, ((ibb + (i - 7) * ib) >> 5)); + *prd++ = (imgpel) iClip1(max_imgpel_value, ((ibb + (i - 6) * ib) >> 5)); + *prd++ = (imgpel) iClip1(max_imgpel_value, ((ibb + (i - 5) * ib) >> 5)); + *prd++ = (imgpel) iClip1(max_imgpel_value, ((ibb + (i - 4) * ib) >> 5)); + } + }// store plane prediction + + return DECODING_OK; +} + +/*! + *********************************************************************** + * \brief + * makes and returns 16x16 intra prediction blocks + * + * \return + * DECODING_OK decoding of intra prediction mode was successful \n + * SEARCH_SYNC search next sync element as errors while decoding occured + *********************************************************************** + */ +int intra_pred_16x16_normal(Macroblock *currMB, //!< Current Macroblock + ColorPlane pl, //!< Current colorplane (for 4:4:4) + int predmode) //!< prediction mode +{ + switch (predmode) + { + case VERT_PRED_16: // vertical prediction from block above + return (intra16x16_vert_pred(currMB, pl)); + break; + case HOR_PRED_16: // horizontal prediction from left block + return (intra16x16_hor_pred(currMB, pl)); + break; + case DC_PRED_16: // DC prediction + return (intra16x16_dc_pred(currMB, pl)); + break; + case PLANE_16:// 16 bit integer plan pred + return (intra16x16_plane_pred(currMB, pl)); + break; + default: + { // indication of fault in bitstream,exit + printf("illegal 16x16 intra prediction mode input: %d\n",predmode); + return SEARCH_SYNC; + } + } +} + diff --git a/ldecod/src/intra16x16_pred_mbaff.c b/ldecod/src/intra16x16_pred_mbaff.c new file mode 100644 index 0000000..741e86b --- /dev/null +++ b/ldecod/src/intra16x16_pred_mbaff.c @@ -0,0 +1,320 @@ +/*! + ************************************************************************************* + * \file intra16x16_pred_mbaff.c + * + * \brief + * Functions for intra 16x16 prediction (MBAFF) + * + * \author + * Main contributors (see contributors.h for copyright, + * address and affiliation details) + * - Yuri Vatis + * - Jan Muenster + * - Alexis Michael Tourapis + * + ************************************************************************************* + */ +#include "global.h" +#include "intra16x16_pred.h" +#include "mb_access.h" +#include "image.h" + +/*! + *********************************************************************** + * \brief + * makes and returns 16x16 DC prediction mode + * + * \return + * DECODING_OK decoding of intra prediction mode was successful \n + * + *********************************************************************** + */ +static int intra16x16_dc_pred_mbaff(Macroblock *currMB, ColorPlane pl) +{ + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + + int s0 = 0, s1 = 0, s2 = 0; + + int i,j; + + imgpel **imgY = (pl) ? currSlice->dec_picture->imgUV[pl - 1] : currSlice->dec_picture->imgY; + imgpel **mb_pred = &(currSlice->mb_pred[pl][0]); + + PixelPos b; //!< pixel position p(0,-1) + PixelPos left[17]; //!< pixel positions p(-1, -1..15) + + int up_avail, left_avail; + + s1=s2=0; + + for (i=0;i<17;++i) + { + getAffNeighbour(currMB, -1, i-1, p_Vid->mb_size[IS_LUMA], &left[i]); + } + getAffNeighbour(currMB, 0, -1, p_Vid->mb_size[IS_LUMA], &b); + + if (!p_Vid->active_pps->constrained_intra_pred_flag) + { + up_avail = b.available; + left_avail = left[1].available; + } + else + { + up_avail = b.available ? currSlice->intra_block[b.mb_addr] : 0; + for (i = 1, left_avail = 1; i < 17; ++i) + left_avail &= left[i].available ? currSlice->intra_block[left[i].mb_addr]: 0; + } + + for (i = 0; i < MB_BLOCK_SIZE; ++i) + { + if (up_avail) + s1 += imgY[b.pos_y][b.pos_x+i]; // sum hor pix + if (left_avail) + s2 += imgY[left[i + 1].pos_y][left[i + 1].pos_x]; // sum vert pix + } + if (up_avail && left_avail) + s0 = (s1 + s2 + 16)>>5; // no edge + else if (!up_avail && left_avail) + s0 = (s2 + 8)>>4; // upper edge + else if (up_avail && !left_avail) + s0 = (s1 + 8)>>4; // left edge + else + s0 = p_Vid->dc_pred_value_comp[pl]; // top left corner, nothing to predict from + + for(j = 0; j < MB_BLOCK_SIZE; ++j) + { +#if (IMGTYPE == 0) + memset(mb_pred[j], s0, MB_BLOCK_SIZE * sizeof(imgpel)); +#else + for(i = 0; i < MB_BLOCK_SIZE; ++i) + { + mb_pred[j][i]=(imgpel) s0; + } +#endif + } + + return DECODING_OK; +} + + + +/*! + *********************************************************************** + * \brief + * makes and returns 16x16 vertical prediction mode + * + * \return + * DECODING_OK decoding of intra prediction mode was successful \n + * + *********************************************************************** + */ +static int intra16x16_vert_pred_mbaff(Macroblock *currMB, ColorPlane pl) +{ + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + + int j; + + imgpel **imgY = (pl) ? currSlice->dec_picture->imgUV[pl - 1] : currSlice->dec_picture->imgY; + + PixelPos b; //!< pixel position p(0,-1) + + int up_avail; + + getAffNeighbour(currMB, 0, -1, p_Vid->mb_size[IS_LUMA], &b); + + if (!p_Vid->active_pps->constrained_intra_pred_flag) + { + up_avail = b.available; + } + else + { + up_avail = b.available ? currSlice->intra_block[b.mb_addr] : 0; + } + + if (!up_avail) + error ("invalid 16x16 intra pred Mode VERT_PRED_16",500); + { + imgpel **prd = &currSlice->mb_pred[pl][0]; + imgpel *src = &(imgY[b.pos_y][b.pos_x]); + + for(j=0;jp_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + int i,j; + + imgpel **imgY = (pl) ? currSlice->dec_picture->imgUV[pl - 1] : currSlice->dec_picture->imgY; + imgpel **mb_pred = &(currSlice->mb_pred[pl][0]); + imgpel prediction; + + PixelPos left[17]; //!< pixel positions p(-1, -1..15) + + int left_avail; + + for (i=0;i<17;++i) + { + getAffNeighbour(currMB, -1, i-1, p_Vid->mb_size[IS_LUMA], &left[i]); + } + + if (!p_Vid->active_pps->constrained_intra_pred_flag) + { + left_avail = left[1].available; + } + else + { + for (i = 1, left_avail = 1; i < 17; ++i) + left_avail &= left[i].available ? currSlice->intra_block[left[i].mb_addr]: 0; + } + + if (!left_avail) + error ("invalid 16x16 intra pred Mode HOR_PRED_16",500); + + for(j = 0; j < MB_BLOCK_SIZE; ++j) + { + prediction = imgY[left[j+1].pos_y][left[j+1].pos_x]; + for(i = 0; i < MB_BLOCK_SIZE; ++i) + mb_pred[j][i]= prediction; // store predicted 16x16 block + } + + return DECODING_OK; +} + +/*! + *********************************************************************** + * \brief + * makes and returns 16x16 horizontal prediction mode + * + * \return + * DECODING_OK decoding of intra prediction mode was successful \n + * + *********************************************************************** + */ +static int intra16x16_plane_pred_mbaff(Macroblock *currMB, ColorPlane pl) +{ + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + + int i,j; + + int ih = 0, iv = 0; + int ib,ic,iaa; + + imgpel **imgY = (pl) ? currSlice->dec_picture->imgUV[pl - 1] : currSlice->dec_picture->imgY; + imgpel **mb_pred = &(currSlice->mb_pred[pl][0]); + imgpel *mpr_line; + int max_imgpel_value = p_Vid->max_pel_value_comp[pl]; + + PixelPos b; //!< pixel position p(0,-1) + PixelPos left[17]; //!< pixel positions p(-1, -1..15) + + int up_avail, left_avail, left_up_avail; + + for (i=0;i<17; ++i) + { + getAffNeighbour(currMB, -1, i-1, p_Vid->mb_size[IS_LUMA], &left[i]); + } + getAffNeighbour(currMB, 0, -1, p_Vid->mb_size[IS_LUMA], &b); + + if (!p_Vid->active_pps->constrained_intra_pred_flag) + { + up_avail = b.available; + left_avail = left[1].available; + left_up_avail = left[0].available; + } + else + { + up_avail = b.available ? currSlice->intra_block[b.mb_addr] : 0; + for (i = 1, left_avail = 1; i < 17; ++i) + left_avail &= left[i].available ? currSlice->intra_block[left[i].mb_addr]: 0; + left_up_avail = left[0].available ? currSlice->intra_block[left[0].mb_addr]: 0; + } + + if (!up_avail || !left_up_avail || !left_avail) + error ("invalid 16x16 intra pred Mode PLANE_16",500); + + mpr_line = &imgY[b.pos_y][b.pos_x+7]; + for (i = 1; i < 8; ++i) + { + ih += i*(mpr_line[i] - mpr_line[-i]); + iv += i*(imgY[left[8+i].pos_y][left[8+i].pos_x] - imgY[left[8-i].pos_y][left[8-i].pos_x]); + } + + ih += 8*(mpr_line[8] - imgY[left[0].pos_y][left[0].pos_x]); + iv += 8*(imgY[left[16].pos_y][left[16].pos_x] - imgY[left[0].pos_y][left[0].pos_x]); + + ib=(5 * ih + 32)>>6; + ic=(5 * iv + 32)>>6; + + iaa=16 * (mpr_line[8] + imgY[left[16].pos_y][left[16].pos_x]); + for (j = 0;j < MB_BLOCK_SIZE; ++j) + { + for (i = 0;i < MB_BLOCK_SIZE; ++i) + { + mb_pred[j][i] = (imgpel) iClip1(max_imgpel_value, ((iaa + (i - 7) * ib + (j - 7) * ic + 16) >> 5)); + } + }// store plane prediction + + return DECODING_OK; +} + +/*! + *********************************************************************** + * \brief + * makes and returns 16x16 intra prediction blocks + * + * \return + * DECODING_OK decoding of intra prediction mode was successful \n + * SEARCH_SYNC search next sync element as errors while decoding occured + *********************************************************************** + */ +int intra_pred_16x16_mbaff(Macroblock *currMB, //!< Current Macroblock + ColorPlane pl, //!< Current colorplane (for 4:4:4) + int predmode) //!< prediction mode +{ + switch (predmode) + { + case VERT_PRED_16: // vertical prediction from block above + return (intra16x16_vert_pred_mbaff(currMB, pl)); + break; + case HOR_PRED_16: // horizontal prediction from left block + return (intra16x16_hor_pred_mbaff(currMB, pl)); + break; + case DC_PRED_16: // DC prediction + return (intra16x16_dc_pred_mbaff(currMB, pl)); + break; + case PLANE_16:// 16 bit integer plan pred + return (intra16x16_plane_pred_mbaff(currMB, pl)); + break; + default: + { // indication of fault in bitstream,exit + printf("illegal 16x16 intra prediction mode input: %d\n",predmode); + return SEARCH_SYNC; + } + } +} + diff --git a/ldecod/src/intra16x16_pred_normal.c b/ldecod/src/intra16x16_pred_normal.c new file mode 100644 index 0000000..1854b33 --- /dev/null +++ b/ldecod/src/intra16x16_pred_normal.c @@ -0,0 +1,350 @@ +/*! + ************************************************************************************* + * \file intra16x16_pred.c + * + * \brief + * Functions for intra 16x16 prediction + * + * \author + * Main contributors (see contributors.h for copyright, + * address and affiliation details) + * - Yuri Vatis + * - Jan Muenster + * - Alexis Michael Tourapis + * + ************************************************************************************* + */ +#include "global.h" +#include "intra16x16_pred.h" +#include "mb_access.h" +#include "image.h" + +/*! + *********************************************************************** + * \brief + * makes and returns 16x16 DC prediction mode + * + * \return + * DECODING_OK decoding of intraprediction mode was successful \n + * + *********************************************************************** + */ +static int intra16x16_dc_pred(Macroblock *currMB, ColorPlane pl) +{ + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + + int s0 = 0, s1 = 0, s2 = 0; + + int i,j; + + imgpel **imgY = (pl) ? currSlice->dec_picture->imgUV[pl - 1] : currSlice->dec_picture->imgY; + imgpel **mb_pred = &(currSlice->mb_pred[pl][0]); + + PixelPos a, b; + + int up_avail, left_avail; + + getNonAffNeighbour(currMB, -1, 0, p_Vid->mb_size[IS_LUMA], &a); + getNonAffNeighbour(currMB, 0, -1, p_Vid->mb_size[IS_LUMA], &b); + + if (!p_Vid->active_pps->constrained_intra_pred_flag) + { + up_avail = b.available; + left_avail = a.available; + } + else + { + up_avail = b.available ? currSlice->intra_block[b.mb_addr] : 0; + left_avail = a.available ? currSlice->intra_block[a.mb_addr]: 0; + } + + // Sum top predictors + if (up_avail) + { + imgpel *pel = &imgY[b.pos_y][b.pos_x]; + for (i = 0; i < MB_BLOCK_SIZE; ++i) + { + s1 += *pel++; + } + } + + // Sum left predictors + if (left_avail) + { + int pos_y = a.pos_y; + int pos_x = a.pos_x; + for (i = 0; i < MB_BLOCK_SIZE; ++i) + { + s2 += imgY[pos_y++][pos_x]; + } + } + + if (up_avail && left_avail) + s0 = (s1 + s2 + 16)>>5; // no edge + else if (!up_avail && left_avail) + s0 = (s2 + 8)>>4; // upper edge + else if (up_avail && !left_avail) + s0 = (s1 + 8)>>4; // left edge + else + s0 = p_Vid->dc_pred_value_comp[pl]; // top left corner, nothing to predict from + + for(j = 0; j < MB_BLOCK_SIZE; ++j) + { +#if (IMGTYPE == 0) + memset(mb_pred[j], s0, MB_BLOCK_SIZE * sizeof(imgpel)); +#else + for(i = 0; i < MB_BLOCK_SIZE; i += 4) + { + mb_pred[j][i ]=(imgpel) s0; + mb_pred[j][i + 1]=(imgpel) s0; + mb_pred[j][i + 2]=(imgpel) s0; + mb_pred[j][i + 3]=(imgpel) s0; + } +#endif + } + + return DECODING_OK; + +} + + +/*! + *********************************************************************** + * \brief + * makes and returns 16x16 vertical prediction mode + * + * \return + * DECODING_OK decoding of intraprediction mode was successful \n + * + *********************************************************************** + */ +static int intra16x16_vert_pred(Macroblock *currMB, ColorPlane pl) +{ + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + + int j; + + imgpel **imgY = (pl) ? currSlice->dec_picture->imgUV[pl - 1] : currSlice->dec_picture->imgY; + + PixelPos b; //!< pixel position p(0,-1) + + int up_avail; + + //getNonAffNeighbour(currMB, 0, -1, p_Vid->mb_size[IS_LUMA], &b); + p_Vid->getNeighbour(currMB, 0, -1, p_Vid->mb_size[IS_LUMA], &b); + + if (!p_Vid->active_pps->constrained_intra_pred_flag) + { + up_avail = b.available; + } + else + { + up_avail = b.available ? currSlice->intra_block[b.mb_addr] : 0; + } + + if (!up_avail) + error ("invalid 16x16 intra pred Mode VERT_PRED_16",500); + { + imgpel **prd = &currSlice->mb_pred[pl][0]; + imgpel *src = &(imgY[b.pos_y][b.pos_x]); + + for(j=0;jp_Slice; + VideoParameters *p_Vid = currMB->p_Vid; +#if (IMGTYPE == 0) + int j; +#else + int i, j; +#endif + + imgpel **imgY = (pl) ? currSlice->dec_picture->imgUV[pl - 1] : currSlice->dec_picture->imgY; + imgpel **mb_pred = &(currSlice->mb_pred[pl][0]); + imgpel prediction; + int pos_y, pos_x; + + PixelPos a; + + int left_avail; + + getNonAffNeighbour(currMB, -1, 0, p_Vid->mb_size[IS_LUMA], &a); + + if (!p_Vid->active_pps->constrained_intra_pred_flag) + { + left_avail = a.available; + } + else + { + left_avail = a.available ? currSlice->intra_block[a.mb_addr]: 0; + } + + if (!left_avail) + error ("invalid 16x16 intra pred Mode HOR_PRED_16",500); + + pos_y = a.pos_y; + pos_x = a.pos_x; + + for(j = 0; j < MB_BLOCK_SIZE; ++j) + { + imgpel *prd = mb_pred[j]; + prediction = imgY[pos_y++][pos_x]; +#if (IMGTYPE == 0) + memset(prd, prediction, MB_BLOCK_SIZE * sizeof(imgpel)); +#else + for(i = 0; i < MB_BLOCK_SIZE; i += 4) + { + *prd++= prediction; // store predicted 16x16 block + *prd++= prediction; // store predicted 16x16 block + *prd++= prediction; // store predicted 16x16 block + *prd++= prediction; // store predicted 16x16 block + } +#endif + } + + return DECODING_OK; +} + + +/*! + *********************************************************************** + * \brief + * makes and returns 16x16 horizontal prediction mode + * + * \return + * DECODING_OK decoding of intraprediction mode was successful \n + * + *********************************************************************** + */ +static int intra16x16_plane_pred(Macroblock *currMB, ColorPlane pl) +{ + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + + int i,j; + + int ih = 0, iv = 0; + int ib,ic,iaa; + + imgpel **imgY = (pl) ? currSlice->dec_picture->imgUV[pl - 1] : currSlice->dec_picture->imgY; + imgpel **mb_pred = &(currSlice->mb_pred[pl][0]); + imgpel *mpr_line; + int max_imgpel_value = p_Vid->max_pel_value_comp[pl]; + int pos_y, pos_x; + + PixelPos a, b, d; + + int up_avail, left_avail, left_up_avail; + + getNonAffNeighbour(currMB, -1, -1, p_Vid->mb_size[IS_LUMA], &d); + getNonAffNeighbour(currMB, -1, 0, p_Vid->mb_size[IS_LUMA], &a); + getNonAffNeighbour(currMB, 0, -1, p_Vid->mb_size[IS_LUMA], &b); + + if (!p_Vid->active_pps->constrained_intra_pred_flag) + { + up_avail = b.available; + left_avail = a.available; + left_up_avail = d.available; + } + else + { + up_avail = b.available ? currSlice->intra_block[b.mb_addr] : 0; + left_avail = a.available ? currSlice->intra_block[a.mb_addr] : 0; + left_up_avail = d.available ? currSlice->intra_block[d.mb_addr] : 0; + } + + if (!up_avail || !left_up_avail || !left_avail) + error ("invalid 16x16 intra pred Mode PLANE_16",500); + + mpr_line = &imgY[b.pos_y][b.pos_x+7]; + pos_y = a.pos_y + 7; + pos_x = a.pos_x; + for (i = 1; i < 8; ++i) + { + ih += i*(mpr_line[i] - mpr_line[-i]); + iv += i*(imgY[pos_y + i][pos_x] - imgY[pos_y - i][pos_x]); + } + + ih += 8*(mpr_line[8] - imgY[d.pos_y][d.pos_x]); + iv += 8*(imgY[pos_y + 8][pos_x] - imgY[d.pos_y][d.pos_x]); + + ib=(5 * ih + 32)>>6; + ic=(5 * iv + 32)>>6; + + iaa=16 * (mpr_line[8] + imgY[pos_y + 8][pos_x]); + for (j = 0;j < MB_BLOCK_SIZE; ++j) + { + int ibb = iaa + (j - 7) * ic + 16; + imgpel *prd = mb_pred[j]; + for (i = 0;i < MB_BLOCK_SIZE; i += 4) + { + *prd++ = (imgpel) iClip1(max_imgpel_value, ((ibb + (i - 7) * ib) >> 5)); + *prd++ = (imgpel) iClip1(max_imgpel_value, ((ibb + (i - 6) * ib) >> 5)); + *prd++ = (imgpel) iClip1(max_imgpel_value, ((ibb + (i - 5) * ib) >> 5)); + *prd++ = (imgpel) iClip1(max_imgpel_value, ((ibb + (i - 4) * ib) >> 5)); + } + }// store plane prediction + + return DECODING_OK; +} + +/*! + *********************************************************************** + * \brief + * makes and returns 16x16 intra prediction blocks + * + * \return + * DECODING_OK decoding of intraprediction mode was successful \n + * SEARCH_SYNC search next sync element as errors while decoding occured + *********************************************************************** + */ +int intrapred_16x16_normal(Macroblock *currMB, //!< Current Macroblock + ColorPlane pl, //!< Current colorplane (for 4:4:4) + int predmode) //!< prediction mode +{ + switch (predmode) + { + case VERT_PRED_16: // vertical prediction from block above + return (intra16x16_vert_pred(currMB, pl)); + break; + case HOR_PRED_16: // horizontal prediction from left block + return (intra16x16_hor_pred(currMB, pl)); + break; + case DC_PRED_16: // DC prediction + return (intra16x16_dc_pred(currMB, pl)); + break; + case PLANE_16:// 16 bit integer plan pred + return (intra16x16_plane_pred(currMB, pl)); + break; + default: + { // indication of fault in bitstream,exit + printf("illegal 16x16 intra prediction mode input: %d\n",predmode); + return SEARCH_SYNC; + } + } +} + diff --git a/ldecod/src/intra4x4_pred.c b/ldecod/src/intra4x4_pred.c new file mode 100644 index 0000000..fa93b5a --- /dev/null +++ b/ldecod/src/intra4x4_pred.c @@ -0,0 +1,833 @@ +/*! + ************************************************************************************* + * \file intra4x4_pred.c + * + * \brief + * Functions for intra 4x4 prediction + * + * \author + * Main contributors (see contributors.h for copyright, + * address and affiliation details) + * - Alexis Michael Tourapis + * + ************************************************************************************* + */ +#include "global.h" +#include "intra4x4_pred.h" +#include "mb_access.h" +#include "image.h" + +// Notation for comments regarding prediction and predictors. +// The pels of the 4x4 block are labelled a..p. The predictor pels above +// are labelled A..H, from the left I..L, and from above left X, as follows: +// +// X A B C D E F G H +// I a b c d +// J e f g h +// K i j k l +// L m n o p +// + +// Predictor array index definitions +#define P_X (PredPel[0]) +#define P_A (PredPel[1]) +#define P_B (PredPel[2]) +#define P_C (PredPel[3]) +#define P_D (PredPel[4]) +#define P_E (PredPel[5]) +#define P_F (PredPel[6]) +#define P_G (PredPel[7]) +#define P_H (PredPel[8]) +#define P_I (PredPel[9]) +#define P_J (PredPel[10]) +#define P_K (PredPel[11]) +#define P_L (PredPel[12]) + +/*! + *********************************************************************** + * \brief + * makes and returns 4x4 DC prediction mode + * + * \param currMB + * current MB structure + * \param pl + * color plane + * \param ioff + * pixel offset X within MB + * \param joff + * pixel offset Y within MB + * \return + * DECODING_OK decoding of intra prediction mode was successful \n + * + *********************************************************************** + */ +static int intra4x4_dc_pred(Macroblock *currMB, + ColorPlane pl, + int ioff, + int joff) +{ + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + + int j; + int s0 = 0; + imgpel **imgY = (pl) ? currSlice->dec_picture->imgUV[pl - 1] : currSlice->dec_picture->imgY; + imgpel *curpel = NULL; + + PixelPos pix_a, pix_b; + + int block_available_up; + int block_available_left; + + imgpel **mb_pred = currSlice->mb_pred[pl]; + + getNonAffNeighbour(currMB, ioff - 1, joff , p_Vid->mb_size[IS_LUMA], &pix_a); + getNonAffNeighbour(currMB, ioff , joff -1, p_Vid->mb_size[IS_LUMA], &pix_b); + + if (p_Vid->active_pps->constrained_intra_pred_flag) + { + block_available_left = pix_a.available ? currSlice->intra_block [pix_a.mb_addr] : 0; + block_available_up = pix_b.available ? currSlice->intra_block [pix_b.mb_addr] : 0; + } + else + { + block_available_left = pix_a.available; + block_available_up = pix_b.available; + } + + // form predictor pels + if (block_available_up) + { + curpel = &imgY[pix_b.pos_y][pix_b.pos_x]; + s0 += *curpel++; + s0 += *curpel++; + s0 += *curpel++; + s0 += *curpel; + } + + if (block_available_left) + { + imgpel **img_pred = &imgY[pix_a.pos_y]; + int pos_x = pix_a.pos_x; + s0 += *(*(img_pred ++) + pos_x); + s0 += *(*(img_pred ++) + pos_x); + s0 += *(*(img_pred ++) + pos_x); + s0 += *(*(img_pred ) + pos_x); + } + + if (block_available_up && block_available_left) + { + // no edge + s0 = (s0 + 4)>>3; + } + else if (!block_available_up && block_available_left) + { + // upper edge + s0 = (s0 + 2)>>2; + } + else if (block_available_up && !block_available_left) + { + // left edge + s0 = (s0 + 2)>>2; + } + else //if (!block_available_up && !block_available_left) + { + // top left corner, nothing to predict from + s0 = p_Vid->dc_pred_value_comp[pl]; + } + + for (j=joff; j < joff + BLOCK_SIZE; ++j) + { + // store DC prediction + mb_pred[j][ioff ] = (imgpel) s0; + mb_pred[j][ioff + 1] = (imgpel) s0; + mb_pred[j][ioff + 2] = (imgpel) s0; + mb_pred[j][ioff + 3] = (imgpel) s0; + } + return DECODING_OK; +} + +/*! + *********************************************************************** + * \brief + * makes and returns 4x4 vertical prediction mode + * + * \return + * DECODING_OK decoding of intra prediction mode was successful \n + * + *********************************************************************** + */ +static int intra4x4_vert_pred(Macroblock *currMB, //!< current macroblock + ColorPlane pl, //!< current image plane + int ioff, //!< pixel offset X within MB + int joff) //!< pixel offset Y within MB +{ + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + + int block_available_up; + PixelPos pix_b; + + getNonAffNeighbour(currMB, ioff, joff - 1 , p_Vid->mb_size[IS_LUMA], &pix_b); + + if (p_Vid->active_pps->constrained_intra_pred_flag) + { + block_available_up = pix_b.available ? currSlice->intra_block [pix_b.mb_addr] : 0; + } + else + { + block_available_up = pix_b.available; + } + + if (!block_available_up) + { + printf ("warning: Intra_4x4_Vertical prediction mode not allowed at mb %d\n", (int) currSlice->current_mb_nr); + } + else + { + imgpel **mb_pred = currSlice->mb_pred[pl]; + imgpel *imgY = (pl) ? &currSlice->dec_picture->imgUV[pl - 1][pix_b.pos_y][pix_b.pos_x] : &currSlice->dec_picture->imgY[pix_b.pos_y][pix_b.pos_x]; + memcpy(&(mb_pred[joff++][ioff]), imgY, BLOCK_SIZE * sizeof(imgpel)); + memcpy(&(mb_pred[joff++][ioff]), imgY, BLOCK_SIZE * sizeof(imgpel)); + memcpy(&(mb_pred[joff++][ioff]), imgY, BLOCK_SIZE * sizeof(imgpel)); + memcpy(&(mb_pred[joff ][ioff]), imgY, BLOCK_SIZE * sizeof(imgpel)); + } + return DECODING_OK; +} + +/*! + *********************************************************************** + * \brief + * makes and returns 4x4 horizontal prediction mode + * + * \param currMB + * current MB structure + * \param pl + * color plane + * \param ioff + * pixel offset X within MB + * \param joff + * pixel offset Y within MB + * + * \return + * DECODING_OK decoding of intra prediction mode was successful + * + *********************************************************************** + */ +static int intra4x4_hor_pred(Macroblock *currMB, + ColorPlane pl, + int ioff, + int joff) +{ + VideoParameters *p_Vid = currMB->p_Vid; + Slice *currSlice = currMB->p_Slice; + + PixelPos pix_a; + + int block_available_left; + + getNonAffNeighbour(currMB, ioff - 1 , joff, p_Vid->mb_size[IS_LUMA], &pix_a); + + if (p_Vid->active_pps->constrained_intra_pred_flag) + { + block_available_left = pix_a.available ? currSlice->intra_block[pix_a.mb_addr]: 0; + } + else + { + block_available_left = pix_a.available; + } + + if (!block_available_left) + printf ("warning: Intra_4x4_Horizontal prediction mode not allowed at mb %d\n",(int) currSlice->current_mb_nr); + else +#if (IMGTYPE == 0) + { + imgpel **imgY = (pl) ? currSlice->dec_picture->imgUV[pl - 1] : currSlice->dec_picture->imgY; + imgpel **mb_pred = &currSlice->mb_pred[pl][joff]; + imgpel **img_pred = &imgY[pix_a.pos_y]; + int pos_x = pix_a.pos_x; + + memset((*(mb_pred++) + ioff), *(*(img_pred++) + pos_x), BLOCK_SIZE * sizeof (imgpel)); + memset((*(mb_pred++) + ioff), *(*(img_pred++) + pos_x), BLOCK_SIZE * sizeof (imgpel)); + memset((*(mb_pred++) + ioff), *(*(img_pred++) + pos_x), BLOCK_SIZE * sizeof (imgpel)); + memset((*(mb_pred ) + ioff), *(*(img_pred ) + pos_x), BLOCK_SIZE * sizeof (imgpel)); + } +#else + { + int j; + int pos_y = pix_a.pos_y; + int pos_x = pix_a.pos_x; + imgpel *predrow, prediction; + imgpel **mb_pred = &currSlice->mb_pred[pl][joff]; + imgpel **imgY = (pl) ? currSlice->dec_picture->imgUV[pl - 1] : currSlice->dec_picture->imgY; + + for(j=0;jp_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + + imgpel **imgY = (pl) ? currSlice->dec_picture->imgUV[pl - 1] : currSlice->dec_picture->imgY; + + PixelPos pix_a; + PixelPos pix_b, pix_d; + + int block_available_up; + int block_available_left; + int block_available_up_left; + + imgpel **mb_pred = currSlice->mb_pred[pl]; + + getNonAffNeighbour(currMB, ioff -1 , joff , p_Vid->mb_size[IS_LUMA], &pix_a); + getNonAffNeighbour(currMB, ioff , joff -1 , p_Vid->mb_size[IS_LUMA], &pix_b); + getNonAffNeighbour(currMB, ioff -1 , joff -1 , p_Vid->mb_size[IS_LUMA], &pix_d); + + if (p_Vid->active_pps->constrained_intra_pred_flag) + { + block_available_left = pix_a.available ? currSlice->intra_block [pix_a.mb_addr]: 0; + block_available_up = pix_b.available ? currSlice->intra_block [pix_b.mb_addr] : 0; + block_available_up_left = pix_d.available ? currSlice->intra_block [pix_d.mb_addr] : 0; + } + else + { + block_available_left = pix_a.available; + block_available_up = pix_b.available; + block_available_up_left = pix_d.available; + } + + if ((!block_available_up)||(!block_available_left)||(!block_available_up_left)) + printf ("warning: Intra_4x4_Diagonal_Down_Right prediction mode not allowed at mb %d\n",(int) currSlice->current_mb_nr); + else + { + imgpel PredPixel[7]; + imgpel PredPel[13]; + imgpel **img_pred = &imgY[pix_a.pos_y]; + int pix_x = pix_a.pos_x; + imgpel *pred_pel = &imgY[pix_b.pos_y][pix_b.pos_x]; + // form predictor pels + // P_A through P_D + memcpy(&PredPel[1], pred_pel, BLOCK_SIZE * sizeof(imgpel)); + + P_I = *(*(img_pred++) + pix_x); + P_J = *(*(img_pred++) + pix_x); + P_K = *(*(img_pred++) + pix_x); + P_L = *(*(img_pred ) + pix_x); + + P_X = imgY[pix_d.pos_y][pix_d.pos_x]; + + PredPixel[0] = (imgpel) ((P_L + (P_K << 1) + P_J + 2) >> 2); + PredPixel[1] = (imgpel) ((P_K + (P_J << 1) + P_I + 2) >> 2); + PredPixel[2] = (imgpel) ((P_J + (P_I << 1) + P_X + 2) >> 2); + PredPixel[3] = (imgpel) ((P_I + (P_X << 1) + P_A + 2) >> 2); + PredPixel[4] = (imgpel) ((P_X + 2*P_A + P_B + 2) >> 2); + PredPixel[5] = (imgpel) ((P_A + 2*P_B + P_C + 2) >> 2); + PredPixel[6] = (imgpel) ((P_B + 2*P_C + P_D + 2) >> 2); + + memcpy(&mb_pred[joff++][ioff], &PredPixel[3], 4 * sizeof(imgpel)); + memcpy(&mb_pred[joff++][ioff], &PredPixel[2], 4 * sizeof(imgpel)); + memcpy(&mb_pred[joff++][ioff], &PredPixel[1], 4 * sizeof(imgpel)); + memcpy(&mb_pred[joff ][ioff], &PredPixel[0], 4 * sizeof(imgpel)); + } + + return DECODING_OK; +} + +/*! + *********************************************************************** + * \brief + * makes and returns 4x4 diagonal down left prediction mode + * + * \return + * DECODING_OK decoding of intra prediction mode was successful \n + * + *********************************************************************** + */ +static int intra4x4_diag_down_left_pred(Macroblock *currMB, //!< current macroblock + ColorPlane pl, //!< current image plane + int ioff, //!< pixel offset X within MB + int joff) //!< pixel offset Y within MB +{ + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + + PixelPos pix_b, pix_c; + + int block_available_up; + int block_available_up_right; + + getNonAffNeighbour(currMB, ioff , joff - 1, p_Vid->mb_size[IS_LUMA], &pix_b); + getNonAffNeighbour(currMB, ioff + 4, joff - 1, p_Vid->mb_size[IS_LUMA], &pix_c); + + pix_c.available = pix_c.available && !((ioff==4) && ((joff==4)||(joff==12))); + + if (p_Vid->active_pps->constrained_intra_pred_flag) + { + block_available_up = pix_b.available ? currSlice->intra_block [pix_b.mb_addr] : 0; + block_available_up_right = pix_c.available ? currSlice->intra_block [pix_c.mb_addr] : 0; + } + else + { + block_available_up = pix_b.available; + block_available_up_right = pix_c.available; + } + + if (!block_available_up) + printf ("warning: Intra_4x4_Diagonal_Down_Left prediction mode not allowed at mb %d\n", (int) currSlice->current_mb_nr); + else + { + imgpel **imgY = (pl) ? currSlice->dec_picture->imgUV[pl - 1] : currSlice->dec_picture->imgY; + imgpel **mb_pred = currSlice->mb_pred[pl]; + + imgpel PredPixel[8]; + imgpel PredPel[25]; + imgpel *pred_pel = &imgY[pix_b.pos_y][pix_b.pos_x]; + + // form predictor pels + // P_A through P_D + memcpy(&PredPel[1], pred_pel, BLOCK_SIZE * sizeof(imgpel)); + + + // P_E through P_H + if (block_available_up_right) + { + memcpy(&PredPel[5], &imgY[pix_c.pos_y][pix_c.pos_x], BLOCK_SIZE * sizeof(imgpel)); + } + else + { +#if (IMGTYPE == 0) + memset(&PredPel[5], PredPel[4], BLOCK_SIZE * sizeof(imgpel)); +#else + P_E = P_F = P_G = P_H = P_D; +#endif + } + + PredPixel[0] = (imgpel) ((P_A + P_C + 2*(P_B) + 2) >> 2); + PredPixel[1] = (imgpel) ((P_B + P_D + 2*(P_C) + 2) >> 2); + PredPixel[2] = (imgpel) ((P_C + P_E + 2*(P_D) + 2) >> 2); + PredPixel[3] = (imgpel) ((P_D + P_F + 2*(P_E) + 2) >> 2); + PredPixel[4] = (imgpel) ((P_E + P_G + 2*(P_F) + 2) >> 2); + PredPixel[5] = (imgpel) ((P_F + P_H + 2*(P_G) + 2) >> 2); + PredPixel[6] = (imgpel) ((P_G + 3*(P_H) + 2) >> 2); + + memcpy(&mb_pred[joff++][ioff], &PredPixel[0], 4 * sizeof(imgpel)); + memcpy(&mb_pred[joff++][ioff], &PredPixel[1], 4 * sizeof(imgpel)); + memcpy(&mb_pred[joff++][ioff], &PredPixel[2], 4 * sizeof(imgpel)); + memcpy(&mb_pred[joff ][ioff], &PredPixel[3], 4 * sizeof(imgpel)); + } + + return DECODING_OK; +} + +/*! + *********************************************************************** + * \brief + * makes and returns 4x4 vertical right prediction mode + * + * \return + * DECODING_OK decoding of intra prediction mode was successful \n + * + *********************************************************************** + */ +static int intra4x4_vert_right_pred(Macroblock *currMB, //!< current macroblock + ColorPlane pl, //!< current image plane + int ioff, //!< pixel offset X within MB + int joff) //!< pixel offset Y within MB +{ + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + + PixelPos pix_a, pix_b, pix_d; + + int block_available_up; + int block_available_left; + int block_available_up_left; + + getNonAffNeighbour(currMB, ioff -1 , joff , p_Vid->mb_size[IS_LUMA], &pix_a); + getNonAffNeighbour(currMB, ioff , joff -1 , p_Vid->mb_size[IS_LUMA], &pix_b); + getNonAffNeighbour(currMB, ioff -1 , joff -1 , p_Vid->mb_size[IS_LUMA], &pix_d); + + if (p_Vid->active_pps->constrained_intra_pred_flag) + { + block_available_left = pix_a.available ? currSlice->intra_block[pix_a.mb_addr]: 0; + block_available_up = pix_b.available ? currSlice->intra_block [pix_b.mb_addr] : 0; + block_available_up_left = pix_d.available ? currSlice->intra_block [pix_d.mb_addr] : 0; + } + else + { + block_available_left = pix_a.available; + block_available_up = pix_b.available; + block_available_up_left = pix_d.available; + } + + if ((!block_available_up)||(!block_available_left)||(!block_available_up_left)) + printf ("warning: Intra_4x4_Vertical_Right prediction mode not allowed at mb %d\n", (int) currSlice->current_mb_nr); + { + imgpel **imgY = (pl) ? currSlice->dec_picture->imgUV[pl - 1] : currSlice->dec_picture->imgY; + imgpel **mb_pred = currSlice->mb_pred[pl]; + imgpel PredPixel[10]; + imgpel PredPel[13]; + + imgpel **img_pred = &imgY[pix_a.pos_y]; + int pix_x = pix_a.pos_x; + imgpel *pred_pel = &imgY[pix_b.pos_y][pix_b.pos_x]; + // form predictor pels + // P_A through P_D + memcpy(&PredPel[1], pred_pel, BLOCK_SIZE * sizeof(imgpel)); + + + P_I = *(*(img_pred++) + pix_x); + P_J = *(*(img_pred++) + pix_x); + P_K = *(*(img_pred++) + pix_x); + + P_X = imgY[pix_d.pos_y][pix_d.pos_x]; + + PredPixel[0] = (imgpel) ((P_X + (P_I << 1) + P_J + 2) >> 2); + PredPixel[1] = (imgpel) ((P_X + P_A + 1) >> 1); + PredPixel[2] = (imgpel) ((P_A + P_B + 1) >> 1); + PredPixel[3] = (imgpel) ((P_B + P_C + 1) >> 1); + PredPixel[4] = (imgpel) ((P_C + P_D + 1) >> 1); + PredPixel[5] = (imgpel) ((P_I + (P_J << 1) + P_K + 2) >> 2); + PredPixel[6] = (imgpel) ((P_I + (P_X << 1) + P_A + 2) >> 2); + PredPixel[7] = (imgpel) ((P_X + 2*P_A + P_B + 2) >> 2); + PredPixel[8] = (imgpel) ((P_A + 2*P_B + P_C + 2) >> 2); + PredPixel[9] = (imgpel) ((P_B + 2*P_C + P_D + 2) >> 2); + + memcpy(&mb_pred[joff++][ioff], &PredPixel[1], 4 * sizeof(imgpel)); + memcpy(&mb_pred[joff++][ioff], &PredPixel[6], 4 * sizeof(imgpel)); + memcpy(&mb_pred[joff++][ioff], &PredPixel[0], 4 * sizeof(imgpel)); + memcpy(&mb_pred[joff ][ioff], &PredPixel[5], 4 * sizeof(imgpel)); + + } + + return DECODING_OK; +} + + +/*! + *********************************************************************** + * \brief + * makes and returns 4x4 vertical left prediction mode + * + * \return + * DECODING_OK decoding of intra prediction mode was successful \n + * + *********************************************************************** + */ +static int intra4x4_vert_left_pred(Macroblock *currMB, //!< current macroblock + ColorPlane pl, //!< current image plane + int ioff, //!< pixel offset X within MB + int joff) //!< pixel offset Y within MB +{ + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + + PixelPos pix_b, pix_c; + + int block_available_up; + int block_available_up_right; + + getNonAffNeighbour(currMB, ioff , joff -1 , p_Vid->mb_size[IS_LUMA], &pix_b); + getNonAffNeighbour(currMB, ioff +4 , joff -1 , p_Vid->mb_size[IS_LUMA], &pix_c); + + pix_c.available = pix_c.available && !((ioff==4) && ((joff==4)||(joff==12))); + + if (p_Vid->active_pps->constrained_intra_pred_flag) + { + block_available_up = pix_b.available ? currSlice->intra_block [pix_b.mb_addr] : 0; + block_available_up_right = pix_c.available ? currSlice->intra_block [pix_c.mb_addr] : 0; + } + else + { + block_available_up = pix_b.available; + block_available_up_right = pix_c.available; + } + + + if (!block_available_up) + printf ("warning: Intra_4x4_Vertical_Left prediction mode not allowed at mb %d\n", (int) currSlice->current_mb_nr); + { + imgpel PredPixel[10]; + imgpel PredPel[13]; + imgpel **imgY = (pl) ? currSlice->dec_picture->imgUV[pl - 1] : currSlice->dec_picture->imgY; + imgpel **mb_pred = currSlice->mb_pred[pl]; + imgpel *pred_pel = &imgY[pix_b.pos_y][pix_b.pos_x]; + + // form predictor pels + // P_A through P_D + memcpy(&PredPel[1], pred_pel, BLOCK_SIZE * sizeof(imgpel)); + + + // P_E through P_H + if (block_available_up_right) + { + memcpy(&PredPel[5], &imgY[pix_c.pos_y][pix_c.pos_x], BLOCK_SIZE * sizeof(imgpel)); + } + else + { +#if (IMGTYPE == 0) + memset(&PredPel[5], PredPel[4], BLOCK_SIZE * sizeof(imgpel)); +#else + P_E = P_F = P_G = P_H = P_D; +#endif + } + + PredPixel[0] = (imgpel) ((P_A + P_B + 1) >> 1); + PredPixel[1] = (imgpel) ((P_B + P_C + 1) >> 1); + PredPixel[2] = (imgpel) ((P_C + P_D + 1) >> 1); + PredPixel[3] = (imgpel) ((P_D + P_E + 1) >> 1); + PredPixel[4] = (imgpel) ((P_E + P_F + 1) >> 1); + PredPixel[5] = (imgpel) ((P_A + 2*P_B + P_C + 2) >> 2); + PredPixel[6] = (imgpel) ((P_B + 2*P_C + P_D + 2) >> 2); + PredPixel[7] = (imgpel) ((P_C + 2*P_D + P_E + 2) >> 2); + PredPixel[8] = (imgpel) ((P_D + 2*P_E + P_F + 2) >> 2); + PredPixel[9] = (imgpel) ((P_E + 2*P_F + P_G + 2) >> 2); + + memcpy(&mb_pred[joff++][ioff], &PredPixel[0], 4 * sizeof(imgpel)); + memcpy(&mb_pred[joff++][ioff], &PredPixel[5], 4 * sizeof(imgpel)); + memcpy(&mb_pred[joff++][ioff], &PredPixel[1], 4 * sizeof(imgpel)); + memcpy(&mb_pred[joff ][ioff], &PredPixel[6], 4 * sizeof(imgpel)); + } + return DECODING_OK; +} + +/*! + *********************************************************************** + * \brief + * makes and returns 4x4 horizontal up prediction mode + * + * \return + * DECODING_OK decoding of intra prediction mode was successful \n + * + *********************************************************************** + */ +static int intra4x4_hor_up_pred(Macroblock *currMB, //!< current macroblock + ColorPlane pl, //!< current image plane + int ioff, //!< pixel offset X within MB + int joff) //!< pixel offset Y within MB +{ + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + + PixelPos pix_a; + + int block_available_left; + + getNonAffNeighbour(currMB, ioff -1 , joff, p_Vid->mb_size[IS_LUMA], &pix_a); + + if (p_Vid->active_pps->constrained_intra_pred_flag) + { + block_available_left = pix_a.available ? currSlice->intra_block[pix_a.mb_addr]: 0; + } + else + { + block_available_left = pix_a.available; + } + + if (!block_available_left) + printf ("warning: Intra_4x4_Horizontal_Up prediction mode not allowed at mb %d\n",(int) currSlice->current_mb_nr); + else + { + imgpel PredPixel[10]; + imgpel PredPel[13]; + imgpel **imgY = (pl) ? currSlice->dec_picture->imgUV[pl - 1] : currSlice->dec_picture->imgY; + imgpel **mb_pred = currSlice->mb_pred[pl]; + + imgpel **img_pred = &imgY[pix_a.pos_y]; + int pix_x = pix_a.pos_x; + + // form predictor pels + P_I = *(*(img_pred++) + pix_x); + P_J = *(*(img_pred++) + pix_x); + P_K = *(*(img_pred++) + pix_x); + P_L = *(*(img_pred ) + pix_x); + + PredPixel[0] = (imgpel) ((P_I + P_J + 1) >> 1); + PredPixel[1] = (imgpel) ((P_I + (P_J << 1) + P_K + 2) >> 2); + PredPixel[2] = (imgpel) ((P_J + P_K + 1) >> 1); + PredPixel[3] = (imgpel) ((P_J + (P_K << 1) + P_L + 2) >> 2); + PredPixel[4] = (imgpel) ((P_K + P_L + 1) >> 1); + PredPixel[5] = (imgpel) ((P_K + (P_L << 1) + P_L + 2) >> 2); + PredPixel[6] = (imgpel) P_L; + PredPixel[7] = (imgpel) P_L; + PredPixel[8] = (imgpel) P_L; + PredPixel[9] = (imgpel) P_L; + + memcpy(&mb_pred[joff++][ioff], &PredPixel[0], 4 * sizeof(imgpel)); + memcpy(&mb_pred[joff++][ioff], &PredPixel[2], 4 * sizeof(imgpel)); + memcpy(&mb_pred[joff++][ioff], &PredPixel[4], 4 * sizeof(imgpel)); + memcpy(&mb_pred[joff++][ioff], &PredPixel[6], 4 * sizeof(imgpel)); + } + + return DECODING_OK; +} + +/*! + *********************************************************************** + * \brief + * makes and returns 4x4 horizontal down prediction mode + * + * \return + * DECODING_OK decoding of intra prediction mode was successful \n + * + *********************************************************************** + */ +static int intra4x4_hor_down_pred(Macroblock *currMB, //!< current macroblock + ColorPlane pl, //!< current image plane + int ioff, //!< pixel offset X within MB + int joff) //!< pixel offset Y within MB +{ + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + + PixelPos pix_a, pix_b, pix_d; + + int block_available_up; + int block_available_left; + int block_available_up_left; + + getNonAffNeighbour(currMB, ioff -1 , joff , p_Vid->mb_size[IS_LUMA], &pix_a); + getNonAffNeighbour(currMB, ioff , joff -1 , p_Vid->mb_size[IS_LUMA], &pix_b); + getNonAffNeighbour(currMB, ioff -1 , joff -1 , p_Vid->mb_size[IS_LUMA], &pix_d); + + if (p_Vid->active_pps->constrained_intra_pred_flag) + { + block_available_left = pix_a.available ? currSlice->intra_block [pix_a.mb_addr]: 0; + block_available_up = pix_b.available ? currSlice->intra_block [pix_b.mb_addr] : 0; + block_available_up_left = pix_d.available ? currSlice->intra_block [pix_d.mb_addr] : 0; + } + else + { + block_available_left = pix_a.available; + block_available_up = pix_b.available; + block_available_up_left = pix_d.available; + } + + if ((!block_available_up)||(!block_available_left)||(!block_available_up_left)) + printf ("warning: Intra_4x4_Horizontal_Down prediction mode not allowed at mb %d\n", (int) currSlice->current_mb_nr); + else + { + imgpel PredPixel[10]; + imgpel PredPel[13]; + imgpel **imgY = (pl) ? currSlice->dec_picture->imgUV[pl - 1] : currSlice->dec_picture->imgY; + imgpel **mb_pred = currSlice->mb_pred[pl]; + + imgpel **img_pred = &imgY[pix_a.pos_y]; + int pix_x = pix_a.pos_x; + imgpel *pred_pel = &imgY[pix_b.pos_y][pix_b.pos_x]; + + // form predictor pels + // P_A through P_D + memcpy(&PredPel[1], pred_pel, BLOCK_SIZE * sizeof(imgpel)); + + + P_I = *(*(img_pred++) + pix_x); + P_J = *(*(img_pred++) + pix_x); + P_K = *(*(img_pred++) + pix_x); + P_L = *(*(img_pred ) + pix_x); + + P_X = imgY[pix_d.pos_y][pix_d.pos_x]; + + PredPixel[0] = (imgpel) ((P_K + P_L + 1) >> 1); + PredPixel[1] = (imgpel) ((P_J + (P_K << 1) + P_L + 2) >> 2); + PredPixel[2] = (imgpel) ((P_J + P_K + 1) >> 1); + PredPixel[3] = (imgpel) ((P_I + (P_J << 1) + P_K + 2) >> 2); + PredPixel[4] = (imgpel) ((P_I + P_J + 1) >> 1); + PredPixel[5] = (imgpel) ((P_X + (P_I << 1) + P_J + 2) >> 2); + PredPixel[6] = (imgpel) ((P_X + P_I + 1) >> 1); + PredPixel[7] = (imgpel) ((P_I + (P_X << 1) + P_A + 2) >> 2); + PredPixel[8] = (imgpel) ((P_X + 2*P_A + P_B + 2) >> 2); + PredPixel[9] = (imgpel) ((P_A + 2*P_B + P_C + 2) >> 2); + + memcpy(&mb_pred[joff++][ioff], &PredPixel[6], 4 * sizeof(imgpel)); + memcpy(&mb_pred[joff++][ioff], &PredPixel[4], 4 * sizeof(imgpel)); + memcpy(&mb_pred[joff++][ioff], &PredPixel[2], 4 * sizeof(imgpel)); + memcpy(&mb_pred[joff ][ioff], &PredPixel[0], 4 * sizeof(imgpel)); + } + + return DECODING_OK; +} + + +/*! + *********************************************************************** + * \brief + * makes and returns 4x4 intra prediction blocks + * + * \return + * DECODING_OK decoding of intra prediction mode was successful \n + * SEARCH_SYNC search next sync element as errors while decoding occured + *********************************************************************** + */ +int intra_pred_4x4_normal(Macroblock *currMB, //!< current macroblock + ColorPlane pl, //!< current image plane + int ioff, //!< pixel offset X within MB + int joff, //!< pixel offset Y within MB + int img_block_x, //!< location of block X, multiples of 4 + int img_block_y) //!< location of block Y, multiples of 4 +{ + VideoParameters *p_Vid = currMB->p_Vid; + byte predmode = p_Vid->ipredmode[img_block_y][img_block_x]; + currMB->ipmode_DPCM = predmode; //For residual DPCM + + switch (predmode) + { + case DC_PRED: + return (intra4x4_dc_pred(currMB, pl, ioff, joff)); + break; + case VERT_PRED: + return (intra4x4_vert_pred(currMB, pl, ioff, joff)); + break; + case HOR_PRED: + return (intra4x4_hor_pred(currMB, pl, ioff, joff)); + break; + case DIAG_DOWN_RIGHT_PRED: + return (intra4x4_diag_down_right_pred(currMB, pl, ioff, joff)); + break; + case DIAG_DOWN_LEFT_PRED: + return (intra4x4_diag_down_left_pred(currMB, pl, ioff, joff)); + break; + case VERT_RIGHT_PRED: + return (intra4x4_vert_right_pred(currMB, pl, ioff, joff)); + break; + case VERT_LEFT_PRED: + return (intra4x4_vert_left_pred(currMB, pl, ioff, joff)); + break; + case HOR_UP_PRED: + return (intra4x4_hor_up_pred(currMB, pl, ioff, joff)); + break; + case HOR_DOWN_PRED: + return (intra4x4_hor_down_pred(currMB, pl, ioff, joff)); + default: + printf("Error: illegal intra_4x4 prediction mode: %d\n", (int) predmode); + return SEARCH_SYNC; + break; + } +} diff --git a/ldecod/src/intra4x4_pred_mbaff.c b/ldecod/src/intra4x4_pred_mbaff.c new file mode 100644 index 0000000..a4cc79b --- /dev/null +++ b/ldecod/src/intra4x4_pred_mbaff.c @@ -0,0 +1,826 @@ +/*! + ************************************************************************************* + * \file intra4x4_pred.c + * + * \brief + * Functions for intra 4x4 prediction + * + * \author + * Main contributors (see contributors.h for copyright, + * address and affiliation details) + * - Alexis Michael Tourapis + * + ************************************************************************************* + */ +#include "global.h" +#include "intra4x4_pred.h" +#include "mb_access.h" +#include "image.h" + +// Notation for comments regarding prediction and predictors. +// The pels of the 4x4 block are labelled a..p. The predictor pels above +// are labelled A..H, from the left I..L, and from above left X, as follows: +// +// X A B C D E F G H +// I a b c d +// J e f g h +// K i j k l +// L m n o p +// + +// Predictor array index definitions +#define P_X (PredPel[0]) +#define P_A (PredPel[1]) +#define P_B (PredPel[2]) +#define P_C (PredPel[3]) +#define P_D (PredPel[4]) +#define P_E (PredPel[5]) +#define P_F (PredPel[6]) +#define P_G (PredPel[7]) +#define P_H (PredPel[8]) +#define P_I (PredPel[9]) +#define P_J (PredPel[10]) +#define P_K (PredPel[11]) +#define P_L (PredPel[12]) + +/*! + *********************************************************************** + * \brief + * makes and returns 4x4 DC prediction mode + * + * \param currMB + * current MB structure + * \param pl + * color plane + * \param ioff + * pixel offset X within MB + * \param joff + * pixel offset Y within MB + * \return + * DECODING_OK decoding of intra prediction mode was successful \n + * + *********************************************************************** + */ +static int intra4x4_dc_pred_mbaff(Macroblock *currMB, + ColorPlane pl, + int ioff, + int joff) +{ + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + + int i,j; + int s0 = 0; + imgpel **imgY = (pl) ? currSlice->dec_picture->imgUV[pl - 1] : currSlice->dec_picture->imgY; + + PixelPos pix_a[4], pix_b; + + int block_available_up; + int block_available_left; + + imgpel **mb_pred = currSlice->mb_pred[pl]; + + for (i=0;i<4;++i) + { + getAffNeighbour(currMB, ioff - 1, joff + i, p_Vid->mb_size[IS_LUMA], &pix_a[i]); + } + getAffNeighbour(currMB, ioff , joff -1 , p_Vid->mb_size[IS_LUMA], &pix_b); + + if (p_Vid->active_pps->constrained_intra_pred_flag) + { + for (i=0, block_available_left=1; i<4;++i) + block_available_left &= pix_a[i].available ? currSlice->intra_block[pix_a[i].mb_addr]: 0; + block_available_up = pix_b.available ? currSlice->intra_block [pix_b.mb_addr] : 0; + } + else + { + block_available_left = pix_a[0].available; + block_available_up = pix_b.available; + } + + // form predictor pels + if (block_available_up) + { + s0 += imgY[pix_b.pos_y][pix_b.pos_x + 0]; + s0 += imgY[pix_b.pos_y][pix_b.pos_x + 1]; + s0 += imgY[pix_b.pos_y][pix_b.pos_x + 2]; + s0 += imgY[pix_b.pos_y][pix_b.pos_x + 3]; + } + + if (block_available_left) + { + s0 += imgY[pix_a[0].pos_y][pix_a[0].pos_x]; + s0 += imgY[pix_a[1].pos_y][pix_a[1].pos_x]; + s0 += imgY[pix_a[2].pos_y][pix_a[2].pos_x]; + s0 += imgY[pix_a[3].pos_y][pix_a[3].pos_x]; + } + + if (block_available_up && block_available_left) + { + // no edge + s0 = (s0 + 4)>>3; + } + else if (!block_available_up && block_available_left) + { + // upper edge + s0 = (s0 + 2)>>2; + } + else if (block_available_up && !block_available_left) + { + // left edge + s0 = (s0 + 2)>>2; + } + else //if (!block_available_up && !block_available_left) + { + // top left corner, nothing to predict from + s0 = p_Vid->dc_pred_value_comp[pl]; + } + + for (j=joff; j < joff + BLOCK_SIZE; ++j) + { + // store DC prediction + mb_pred[j][ioff ] = (imgpel) s0; + mb_pred[j][ioff + 1] = (imgpel) s0; + mb_pred[j][ioff + 2] = (imgpel) s0; + mb_pred[j][ioff + 3] = (imgpel) s0; + } + return DECODING_OK; +} + +/*! + *********************************************************************** + * \brief + * makes and returns 4x4 vertical prediction mode + * + * \return + * DECODING_OK decoding of intra prediction mode was successful \n + * + *********************************************************************** + */ +static int intra4x4_vert_pred_mbaff(Macroblock *currMB, //!< current macroblock + ColorPlane pl, //!< current image plane + int ioff, //!< pixel offset X within MB + int joff) //!< pixel offset Y within MB +{ + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + + int block_available_up; + PixelPos pix_b; + + getAffNeighbour(currMB, ioff, joff - 1 , p_Vid->mb_size[IS_LUMA], &pix_b); + + if (p_Vid->active_pps->constrained_intra_pred_flag) + { + block_available_up = pix_b.available ? currSlice->intra_block [pix_b.mb_addr] : 0; + } + else + { + block_available_up = pix_b.available; + } + + if (!block_available_up) + { + printf ("warning: Intra_4x4_Vertical prediction mode not allowed at mb %d\n", (int) currSlice->current_mb_nr); + } + else + { + imgpel **mb_pred = currSlice->mb_pred[pl]; + imgpel *imgY = (pl) ? &currSlice->dec_picture->imgUV[pl - 1][pix_b.pos_y][pix_b.pos_x] : &currSlice->dec_picture->imgY[pix_b.pos_y][pix_b.pos_x]; + memcpy(&(mb_pred[joff++][ioff]), imgY, BLOCK_SIZE * sizeof(imgpel)); + memcpy(&(mb_pred[joff++][ioff]), imgY, BLOCK_SIZE * sizeof(imgpel)); + memcpy(&(mb_pred[joff++][ioff]), imgY, BLOCK_SIZE * sizeof(imgpel)); + memcpy(&(mb_pred[joff ][ioff]), imgY, BLOCK_SIZE * sizeof(imgpel)); + } + return DECODING_OK; +} + +/*! + *********************************************************************** + * \brief + * makes and returns 4x4 horizontal prediction mode + * + * \param currMB + * current MB structure + * \param pl + * color plane + * \param ioff + * pixel offset X within MB + * \param joff + * pixel offset Y within MB + * + * \return + * DECODING_OK decoding of intra prediction mode was successful + * + *********************************************************************** + */ +static int intra4x4_hor_pred_mbaff(Macroblock *currMB, + ColorPlane pl, + int ioff, + int joff) +{ + VideoParameters *p_Vid = currMB->p_Vid; + Slice *currSlice = currMB->p_Slice; + + int i,j; + imgpel **imgY = (pl) ? currSlice->dec_picture->imgUV[pl - 1] : currSlice->dec_picture->imgY; + + PixelPos pix_a[4]; + + int block_available_left; + + imgpel *predrow, prediction, **mb_pred = currSlice->mb_pred[pl]; + + for (i=0;i<4;++i) + { + getAffNeighbour(currMB, ioff -1 , joff +i , p_Vid->mb_size[IS_LUMA], &pix_a[i]); + } + + if (p_Vid->active_pps->constrained_intra_pred_flag) + { + for (i=0, block_available_left=1; i<4;++i) + block_available_left &= pix_a[i].available ? currSlice->intra_block[pix_a[i].mb_addr]: 0; + } + else + { + block_available_left = pix_a[0].available; + } + + if (!block_available_left) + printf ("warning: Intra_4x4_Horizontal prediction mode not allowed at mb %d\n",(int) currSlice->current_mb_nr); + + for(j=0;jp_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + + int i; + imgpel **imgY = (pl) ? currSlice->dec_picture->imgUV[pl - 1] : currSlice->dec_picture->imgY; + + PixelPos pix_a[4]; + PixelPos pix_b, pix_d; + + int block_available_up; + int block_available_left; + int block_available_up_left; + + imgpel **mb_pred = currSlice->mb_pred[pl]; + + for (i=0;i<4;++i) + { + getAffNeighbour(currMB, ioff -1 , joff +i , p_Vid->mb_size[IS_LUMA], &pix_a[i]); + } + + getAffNeighbour(currMB, ioff , joff -1 , p_Vid->mb_size[IS_LUMA], &pix_b); + getAffNeighbour(currMB, ioff -1 , joff -1 , p_Vid->mb_size[IS_LUMA], &pix_d); + + if (p_Vid->active_pps->constrained_intra_pred_flag) + { + for (i=0, block_available_left=1; i<4;++i) + block_available_left &= pix_a[i].available ? currSlice->intra_block[pix_a[i].mb_addr]: 0; + block_available_up = pix_b.available ? currSlice->intra_block [pix_b.mb_addr] : 0; + block_available_up_left = pix_d.available ? currSlice->intra_block [pix_d.mb_addr] : 0; + } + else + { + block_available_left = pix_a[0].available; + block_available_up = pix_b.available; + block_available_up_left = pix_d.available; + } + + if ((!block_available_up)||(!block_available_left)||(!block_available_up_left)) + printf ("warning: Intra_4x4_Diagonal_Down_Right prediction mode not allowed at mb %d\n",(int) currSlice->current_mb_nr); + else + { + imgpel PredPixel[7]; + imgpel PredPel[13]; + imgpel *pred_pel = &imgY[pix_b.pos_y][pix_b.pos_x]; + // form predictor pels + // P_A through P_D + memcpy(&PredPel[1], pred_pel, BLOCK_SIZE * sizeof(imgpel)); + + + P_I = imgY[pix_a[0].pos_y][pix_a[0].pos_x]; + P_J = imgY[pix_a[1].pos_y][pix_a[1].pos_x]; + P_K = imgY[pix_a[2].pos_y][pix_a[2].pos_x]; + P_L = imgY[pix_a[3].pos_y][pix_a[3].pos_x]; + + P_X = imgY[pix_d.pos_y][pix_d.pos_x]; + + PredPixel[0] = (imgpel) ((P_L + (P_K << 1) + P_J + 2) >> 2); + PredPixel[1] = (imgpel) ((P_K + (P_J << 1) + P_I + 2) >> 2); + PredPixel[2] = (imgpel) ((P_J + (P_I << 1) + P_X + 2) >> 2); + PredPixel[3] = (imgpel) ((P_I + (P_X << 1) + P_A + 2) >> 2); + PredPixel[4] = (imgpel) ((P_X + 2*P_A + P_B + 2) >> 2); + PredPixel[5] = (imgpel) ((P_A + 2*P_B + P_C + 2) >> 2); + PredPixel[6] = (imgpel) ((P_B + 2*P_C + P_D + 2) >> 2); + + memcpy(&mb_pred[joff++][ioff], &PredPixel[3], 4 * sizeof(imgpel)); + memcpy(&mb_pred[joff++][ioff], &PredPixel[2], 4 * sizeof(imgpel)); + memcpy(&mb_pred[joff++][ioff], &PredPixel[1], 4 * sizeof(imgpel)); + memcpy(&mb_pred[joff ][ioff], &PredPixel[0], 4 * sizeof(imgpel)); + } + + return DECODING_OK; +} + +/*! + *********************************************************************** + * \brief + * makes and returns 4x4 diagonal down left prediction mode + * + * \return + * DECODING_OK decoding of intra prediction mode was successful \n + * + *********************************************************************** + */ +static int intra4x4_diag_down_left_pred_mbaff(Macroblock *currMB, //!< current macroblock + ColorPlane pl, //!< current image plane + int ioff, //!< pixel offset X within MB + int joff) //!< pixel offset Y within MB +{ + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + + PixelPos pix_b, pix_c; + + int block_available_up; + int block_available_up_right; + + getAffNeighbour(currMB, ioff , joff - 1, p_Vid->mb_size[IS_LUMA], &pix_b); + getAffNeighbour(currMB, ioff + 4, joff - 1, p_Vid->mb_size[IS_LUMA], &pix_c); + + pix_c.available = pix_c.available && !((ioff==4) && ((joff==4)||(joff==12))); + + if (p_Vid->active_pps->constrained_intra_pred_flag) + { + block_available_up = pix_b.available ? currSlice->intra_block [pix_b.mb_addr] : 0; + block_available_up_right = pix_c.available ? currSlice->intra_block [pix_c.mb_addr] : 0; + } + else + { + block_available_up = pix_b.available; + block_available_up_right = pix_c.available; + } + + if (!block_available_up) + printf ("warning: Intra_4x4_Diagonal_Down_Left prediction mode not allowed at mb %d\n", (int) currSlice->current_mb_nr); + else + { + imgpel **imgY = (pl) ? currSlice->dec_picture->imgUV[pl - 1] : currSlice->dec_picture->imgY; + imgpel **mb_pred = currSlice->mb_pred[pl]; + + imgpel PredPixel[8]; + imgpel PredPel[25]; + imgpel *pred_pel = &imgY[pix_b.pos_y][pix_b.pos_x]; + + // form predictor pels + // P_A through P_D + memcpy(&PredPel[1], pred_pel, BLOCK_SIZE * sizeof(imgpel)); + + // P_E through P_H + if (block_available_up_right) + { + memcpy(&PredPel[5], &imgY[pix_c.pos_y][pix_c.pos_x], BLOCK_SIZE * sizeof(imgpel)); + } + else + { + P_E = P_F = P_G = P_H = P_D; + } + + PredPixel[0] = (imgpel) ((P_A + P_C + 2*(P_B) + 2) >> 2); + PredPixel[1] = (imgpel) ((P_B + P_D + 2*(P_C) + 2) >> 2); + PredPixel[2] = (imgpel) ((P_C + P_E + 2*(P_D) + 2) >> 2); + PredPixel[3] = (imgpel) ((P_D + P_F + 2*(P_E) + 2) >> 2); + PredPixel[4] = (imgpel) ((P_E + P_G + 2*(P_F) + 2) >> 2); + PredPixel[5] = (imgpel) ((P_F + P_H + 2*(P_G) + 2) >> 2); + PredPixel[6] = (imgpel) ((P_G + 3*(P_H) + 2) >> 2); + + memcpy(&mb_pred[joff++][ioff], &PredPixel[0], 4 * sizeof(imgpel)); + memcpy(&mb_pred[joff++][ioff], &PredPixel[1], 4 * sizeof(imgpel)); + memcpy(&mb_pred[joff++][ioff], &PredPixel[2], 4 * sizeof(imgpel)); + memcpy(&mb_pred[joff ][ioff], &PredPixel[3], 4 * sizeof(imgpel)); + } + + return DECODING_OK; +} + +/*! + *********************************************************************** + * \brief + * makes and returns 4x4 vertical right prediction mode + * + * \return + * DECODING_OK decoding of intra prediction mode was successful \n + * + *********************************************************************** + */ +static int intra4x4_vert_right_pred_mbaff(Macroblock *currMB, //!< current macroblock + ColorPlane pl, //!< current image plane + int ioff, //!< pixel offset X within MB + int joff) //!< pixel offset Y within MB +{ + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + + int i; + imgpel **imgY = (pl) ? currSlice->dec_picture->imgUV[pl - 1] : currSlice->dec_picture->imgY; + + PixelPos pix_a[4]; + PixelPos pix_b, pix_d; + + int block_available_up; + int block_available_left; + int block_available_up_left; + + imgpel **mb_pred = currSlice->mb_pred[pl]; + + for (i=0;i<4;++i) + { + getAffNeighbour(currMB, ioff -1 , joff +i , p_Vid->mb_size[IS_LUMA], &pix_a[i]); + } + + getAffNeighbour(currMB, ioff , joff -1 , p_Vid->mb_size[IS_LUMA], &pix_b); + getAffNeighbour(currMB, ioff -1 , joff -1 , p_Vid->mb_size[IS_LUMA], &pix_d); + + if (p_Vid->active_pps->constrained_intra_pred_flag) + { + for (i=0, block_available_left=1; i<4;++i) + block_available_left &= pix_a[i].available ? currSlice->intra_block[pix_a[i].mb_addr]: 0; + block_available_up = pix_b.available ? currSlice->intra_block [pix_b.mb_addr] : 0; + block_available_up_left = pix_d.available ? currSlice->intra_block [pix_d.mb_addr] : 0; + } + else + { + block_available_left = pix_a[0].available; + block_available_up = pix_b.available; + block_available_up_left = pix_d.available; + } + + if ((!block_available_up)||(!block_available_left)||(!block_available_up_left)) + printf ("warning: Intra_4x4_Vertical_Right prediction mode not allowed at mb %d\n", (int) currSlice->current_mb_nr); + { + imgpel PredPixel[10]; + imgpel PredPel[13]; + imgpel *pred_pel = &imgY[pix_b.pos_y][pix_b.pos_x]; + // form predictor pels + // P_A through P_D + memcpy(&PredPel[1], pred_pel, BLOCK_SIZE * sizeof(imgpel)); + + + P_I = imgY[pix_a[0].pos_y][pix_a[0].pos_x]; + P_J = imgY[pix_a[1].pos_y][pix_a[1].pos_x]; + P_K = imgY[pix_a[2].pos_y][pix_a[2].pos_x]; + + P_X = imgY[pix_d.pos_y][pix_d.pos_x]; + + PredPixel[0] = (imgpel) ((P_X + (P_I << 1) + P_J + 2) >> 2); + PredPixel[1] = (imgpel) ((P_X + P_A + 1) >> 1); + PredPixel[2] = (imgpel) ((P_A + P_B + 1) >> 1); + PredPixel[3] = (imgpel) ((P_B + P_C + 1) >> 1); + PredPixel[4] = (imgpel) ((P_C + P_D + 1) >> 1); + PredPixel[5] = (imgpel) ((P_I + (P_J << 1) + P_K + 2) >> 2); + PredPixel[6] = (imgpel) ((P_I + (P_X << 1) + P_A + 2) >> 2); + PredPixel[7] = (imgpel) ((P_X + 2*P_A + P_B + 2) >> 2); + PredPixel[8] = (imgpel) ((P_A + 2*P_B + P_C + 2) >> 2); + PredPixel[9] = (imgpel) ((P_B + 2*P_C + P_D + 2) >> 2); + + memcpy(&mb_pred[joff++][ioff], &PredPixel[1], 4 * sizeof(imgpel)); + memcpy(&mb_pred[joff++][ioff], &PredPixel[6], 4 * sizeof(imgpel)); + memcpy(&mb_pred[joff++][ioff], &PredPixel[0], 4 * sizeof(imgpel)); + memcpy(&mb_pred[joff ][ioff], &PredPixel[5], 4 * sizeof(imgpel)); + + } + + return DECODING_OK; +} + + +/*! + *********************************************************************** + * \brief + * makes and returns 4x4 vertical left prediction mode + * + * \return + * DECODING_OK decoding of intra prediction mode was successful \n + * + *********************************************************************** + */ +static int intra4x4_vert_left_pred_mbaff(Macroblock *currMB, //!< current macroblock + ColorPlane pl, //!< current image plane + int ioff, //!< pixel offset X within MB + int joff) //!< pixel offset Y within MB +{ + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + + PixelPos pix_b, pix_c; + + int block_available_up; + int block_available_up_right; + + getAffNeighbour(currMB, ioff , joff -1 , p_Vid->mb_size[IS_LUMA], &pix_b); + getAffNeighbour(currMB, ioff +4 , joff -1 , p_Vid->mb_size[IS_LUMA], &pix_c); + + pix_c.available = pix_c.available && !((ioff==4) && ((joff==4)||(joff==12))); + + if (p_Vid->active_pps->constrained_intra_pred_flag) + { + block_available_up = pix_b.available ? currSlice->intra_block [pix_b.mb_addr] : 0; + block_available_up_right = pix_c.available ? currSlice->intra_block [pix_c.mb_addr] : 0; + } + else + { + block_available_up = pix_b.available; + block_available_up_right = pix_c.available; + } + + + if (!block_available_up) + printf ("warning: Intra_4x4_Vertical_Left prediction mode not allowed at mb %d\n", (int) currSlice->current_mb_nr); + { + imgpel PredPixel[10]; + imgpel PredPel[13]; + imgpel **imgY = (pl) ? currSlice->dec_picture->imgUV[pl - 1] : currSlice->dec_picture->imgY; + imgpel **mb_pred = currSlice->mb_pred[pl]; + imgpel *pred_pel = &imgY[pix_b.pos_y][pix_b.pos_x]; + + // form predictor pels + // P_A through P_D + memcpy(&PredPel[1], pred_pel, BLOCK_SIZE * sizeof(imgpel)); + + // P_E through P_H + if (block_available_up_right) + { + memcpy(&PredPel[5], &imgY[pix_c.pos_y][pix_c.pos_x], BLOCK_SIZE * sizeof(imgpel)); + } + else + { + P_E = P_F = P_G = P_H = P_D; + } + + PredPixel[0] = (imgpel) ((P_A + P_B + 1) >> 1); + PredPixel[1] = (imgpel) ((P_B + P_C + 1) >> 1); + PredPixel[2] = (imgpel) ((P_C + P_D + 1) >> 1); + PredPixel[3] = (imgpel) ((P_D + P_E + 1) >> 1); + PredPixel[4] = (imgpel) ((P_E + P_F + 1) >> 1); + PredPixel[5] = (imgpel) ((P_A + 2*P_B + P_C + 2) >> 2); + PredPixel[6] = (imgpel) ((P_B + 2*P_C + P_D + 2) >> 2); + PredPixel[7] = (imgpel) ((P_C + 2*P_D + P_E + 2) >> 2); + PredPixel[8] = (imgpel) ((P_D + 2*P_E + P_F + 2) >> 2); + PredPixel[9] = (imgpel) ((P_E + 2*P_F + P_G + 2) >> 2); + + memcpy(&mb_pred[joff++][ioff], &PredPixel[0], 4 * sizeof(imgpel)); + memcpy(&mb_pred[joff++][ioff], &PredPixel[5], 4 * sizeof(imgpel)); + memcpy(&mb_pred[joff++][ioff], &PredPixel[1], 4 * sizeof(imgpel)); + memcpy(&mb_pred[joff ][ioff], &PredPixel[6], 4 * sizeof(imgpel)); + } + return DECODING_OK; +} + +/*! + *********************************************************************** + * \brief + * makes and returns 4x4 horizontal up prediction mode + * + * \return + * DECODING_OK decoding of intra prediction mode was successful \n + * + *********************************************************************** + */ +static int intra4x4_hor_up_pred_mbaff(Macroblock *currMB, //!< current macroblock + ColorPlane pl, //!< current image plane + int ioff, //!< pixel offset X within MB + int joff) //!< pixel offset Y within MB +{ + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + + int i; + imgpel **imgY = (pl) ? currSlice->dec_picture->imgUV[pl - 1] : currSlice->dec_picture->imgY; + + PixelPos pix_a[4]; + + int block_available_left; + + imgpel **mb_pred = currSlice->mb_pred[pl]; + + for (i=0;i<4;++i) + { + getAffNeighbour(currMB, ioff -1 , joff +i , p_Vid->mb_size[IS_LUMA], &pix_a[i]); + } + + if (p_Vid->active_pps->constrained_intra_pred_flag) + { + for (i=0, block_available_left=1; i<4;++i) + block_available_left &= pix_a[i].available ? currSlice->intra_block[pix_a[i].mb_addr]: 0; + } + else + { + block_available_left = pix_a[0].available; + } + + if (!block_available_left) + printf ("warning: Intra_4x4_Horizontal_Up prediction mode not allowed at mb %d\n",(int) currSlice->current_mb_nr); + else + { + imgpel PredPixel[10]; + imgpel PredPel[13]; + + // form predictor pels + P_I = imgY[pix_a[0].pos_y][pix_a[0].pos_x]; + P_J = imgY[pix_a[1].pos_y][pix_a[1].pos_x]; + P_K = imgY[pix_a[2].pos_y][pix_a[2].pos_x]; + P_L = imgY[pix_a[3].pos_y][pix_a[3].pos_x]; + + PredPixel[0] = (imgpel) ((P_I + P_J + 1) >> 1); + PredPixel[1] = (imgpel) ((P_I + (P_J << 1) + P_K + 2) >> 2); + PredPixel[2] = (imgpel) ((P_J + P_K + 1) >> 1); + PredPixel[3] = (imgpel) ((P_J + (P_K << 1) + P_L + 2) >> 2); + PredPixel[4] = (imgpel) ((P_K + P_L + 1) >> 1); + PredPixel[5] = (imgpel) ((P_K + (P_L << 1) + P_L + 2) >> 2); + PredPixel[6] = (imgpel) P_L; + PredPixel[7] = (imgpel) P_L; + PredPixel[8] = (imgpel) P_L; + PredPixel[9] = (imgpel) P_L; + + memcpy(&mb_pred[joff++][ioff], &PredPixel[0], 4 * sizeof(imgpel)); + memcpy(&mb_pred[joff++][ioff], &PredPixel[2], 4 * sizeof(imgpel)); + memcpy(&mb_pred[joff++][ioff], &PredPixel[4], 4 * sizeof(imgpel)); + memcpy(&mb_pred[joff++][ioff], &PredPixel[6], 4 * sizeof(imgpel)); + } + + return DECODING_OK; +} + +/*! + *********************************************************************** + * \brief + * makes and returns 4x4 horizontal down prediction mode + * + * \return + * DECODING_OK decoding of intra prediction mode was successful \n + * + *********************************************************************** + */ +static int intra4x4_hor_down_pred_mbaff(Macroblock *currMB, //!< current macroblock + ColorPlane pl, //!< current image plane + int ioff, //!< pixel offset X within MB + int joff) //!< pixel offset Y within MB +{ + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + + int i; + imgpel **imgY = (pl) ? currSlice->dec_picture->imgUV[pl - 1] : currSlice->dec_picture->imgY; + + PixelPos pix_a[4]; + PixelPos pix_b, pix_d; + + int block_available_up; + int block_available_left; + int block_available_up_left; + + imgpel **mb_pred = currSlice->mb_pred[pl]; + + for (i=0;i<4;++i) + { + getAffNeighbour(currMB, ioff -1 , joff +i , p_Vid->mb_size[IS_LUMA], &pix_a[i]); + } + + getAffNeighbour(currMB, ioff , joff -1 , p_Vid->mb_size[IS_LUMA], &pix_b); + getAffNeighbour(currMB, ioff -1 , joff -1 , p_Vid->mb_size[IS_LUMA], &pix_d); + + if (p_Vid->active_pps->constrained_intra_pred_flag) + { + for (i=0, block_available_left=1; i<4;++i) + block_available_left &= pix_a[i].available ? currSlice->intra_block[pix_a[i].mb_addr]: 0; + block_available_up = pix_b.available ? currSlice->intra_block [pix_b.mb_addr] : 0; + block_available_up_left = pix_d.available ? currSlice->intra_block [pix_d.mb_addr] : 0; + } + else + { + block_available_left = pix_a[0].available; + block_available_up = pix_b.available; + block_available_up_left = pix_d.available; + } + + if ((!block_available_up)||(!block_available_left)||(!block_available_up_left)) + printf ("warning: Intra_4x4_Horizontal_Down prediction mode not allowed at mb %d\n", (int) currSlice->current_mb_nr); + else + { + imgpel PredPixel[10]; + imgpel PredPel[13]; + imgpel *pred_pel = &imgY[pix_b.pos_y][pix_b.pos_x]; + + // form predictor pels + // P_A through P_D + memcpy(&PredPel[1], pred_pel, BLOCK_SIZE * sizeof(imgpel)); + + P_I = imgY[pix_a[0].pos_y][pix_a[0].pos_x]; + P_J = imgY[pix_a[1].pos_y][pix_a[1].pos_x]; + P_K = imgY[pix_a[2].pos_y][pix_a[2].pos_x]; + P_L = imgY[pix_a[3].pos_y][pix_a[3].pos_x]; + + P_X = imgY[pix_d.pos_y][pix_d.pos_x]; + + PredPixel[0] = (imgpel) ((P_K + P_L + 1) >> 1); + PredPixel[1] = (imgpel) ((P_J + (P_K << 1) + P_L + 2) >> 2); + PredPixel[2] = (imgpel) ((P_J + P_K + 1) >> 1); + PredPixel[3] = (imgpel) ((P_I + (P_J << 1) + P_K + 2) >> 2); + PredPixel[4] = (imgpel) ((P_I + P_J + 1) >> 1); + PredPixel[5] = (imgpel) ((P_X + (P_I << 1) + P_J + 2) >> 2); + PredPixel[6] = (imgpel) ((P_X + P_I + 1) >> 1); + PredPixel[7] = (imgpel) ((P_I + (P_X << 1) + P_A + 2) >> 2); + PredPixel[8] = (imgpel) ((P_X + 2*P_A + P_B + 2) >> 2); + PredPixel[9] = (imgpel) ((P_A + 2*P_B + P_C + 2) >> 2); + + memcpy(&mb_pred[joff++][ioff], &PredPixel[6], 4 * sizeof(imgpel)); + memcpy(&mb_pred[joff++][ioff], &PredPixel[4], 4 * sizeof(imgpel)); + memcpy(&mb_pred[joff++][ioff], &PredPixel[2], 4 * sizeof(imgpel)); + memcpy(&mb_pred[joff ][ioff], &PredPixel[0], 4 * sizeof(imgpel)); + } + + return DECODING_OK; +} + + +/*! + *********************************************************************** + * \brief + * makes and returns 4x4 intra prediction blocks + * + * \return + * DECODING_OK decoding of intra prediction mode was successful \n + * SEARCH_SYNC search next sync element as errors while decoding occured + *********************************************************************** + */ +int intra_pred_4x4_mbaff(Macroblock *currMB, //!< current macroblock + ColorPlane pl, //!< current image plane + int ioff, //!< pixel offset X within MB + int joff, //!< pixel offset Y within MB + int img_block_x, //!< location of block X, multiples of 4 + int img_block_y) //!< location of block Y, multiples of 4 +{ + VideoParameters *p_Vid = currMB->p_Vid; + byte predmode = p_Vid->ipredmode[img_block_y][img_block_x]; + currMB->ipmode_DPCM = predmode; //For residual DPCM + + switch (predmode) + { + case DC_PRED: + return (intra4x4_dc_pred_mbaff(currMB, pl, ioff, joff)); + break; + case VERT_PRED: + return (intra4x4_vert_pred_mbaff(currMB, pl, ioff, joff)); + break; + case HOR_PRED: + return (intra4x4_hor_pred_mbaff(currMB, pl, ioff, joff)); + break; + case DIAG_DOWN_RIGHT_PRED: + return (intra4x4_diag_down_right_pred_mbaff(currMB, pl, ioff, joff)); + break; + case DIAG_DOWN_LEFT_PRED: + return (intra4x4_diag_down_left_pred_mbaff(currMB, pl, ioff, joff)); + break; + case VERT_RIGHT_PRED: + return (intra4x4_vert_right_pred_mbaff(currMB, pl, ioff, joff)); + break; + case VERT_LEFT_PRED: + return (intra4x4_vert_left_pred_mbaff(currMB, pl, ioff, joff)); + break; + case HOR_UP_PRED: + return (intra4x4_hor_up_pred_mbaff(currMB, pl, ioff, joff)); + break; + case HOR_DOWN_PRED: + return (intra4x4_hor_down_pred_mbaff(currMB, pl, ioff, joff)); + default: + printf("Error: illegal intra_4x4 prediction mode: %d\n", (int) predmode); + return SEARCH_SYNC; + break; + } +} diff --git a/ldecod/src/intra4x4_pred_normal.c b/ldecod/src/intra4x4_pred_normal.c new file mode 100644 index 0000000..0c37933 --- /dev/null +++ b/ldecod/src/intra4x4_pred_normal.c @@ -0,0 +1,816 @@ +/*! + ************************************************************************************* + * \file intra4x4_pred.c + * + * \brief + * Functions for intra 4x4 prediction + * + * \author + * Main contributors (see contributors.h for copyright, + * address and affiliation details) + * - Alexis Michael Tourapis + * + ************************************************************************************* + */ +#include "global.h" +#include "intra4x4_pred.h" +#include "mb_access.h" +#include "image.h" + +// Notation for comments regarding prediction and predictors. +// The pels of the 4x4 block are labelled a..p. The predictor pels above +// are labelled A..H, from the left I..L, and from above left X, as follows: +// +// X A B C D E F G H +// I a b c d +// J e f g h +// K i j k l +// L m n o p +// + +/*! + *********************************************************************** + * \brief + * makes and returns 4x4 DC prediction mode + * + * \param currMB + * current MB structure + * \param pl + * color plane + * \param ioff + * pixel offset X within MB + * \param joff + * pixel offset Y within MB + * \return + * DECODING_OK decoding of intra prediction mode was successful \n + * + *********************************************************************** + */ +static int intra4x4_dc_pred(Macroblock *currMB, + ColorPlane pl, + int ioff, + int joff) +{ + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + + int j; + int s0 = 0; + imgpel **imgY = (pl) ? currSlice->dec_picture->imgUV[pl - 1] : currSlice->dec_picture->imgY; + imgpel *curpel = NULL; + + PixelPos pix_a, pix_b; + + int block_available_up; + int block_available_left; + + imgpel **mb_pred = currSlice->mb_pred[pl]; + + getNonAffNeighbour(currMB, ioff - 1, joff , p_Vid->mb_size[IS_LUMA], &pix_a); + getNonAffNeighbour(currMB, ioff , joff -1, p_Vid->mb_size[IS_LUMA], &pix_b); + + if (p_Vid->active_pps->constrained_intra_pred_flag) + { + block_available_left = pix_a.available ? currSlice->intra_block [pix_a.mb_addr] : 0; + block_available_up = pix_b.available ? currSlice->intra_block [pix_b.mb_addr] : 0; + } + else + { + block_available_left = pix_a.available; + block_available_up = pix_b.available; + } + + // form predictor pels + if (block_available_up) + { + curpel = &imgY[pix_b.pos_y][pix_b.pos_x]; + s0 += *curpel++; + s0 += *curpel++; + s0 += *curpel++; + s0 += *curpel; + } + + if (block_available_left) + { + imgpel **img_pred = &imgY[pix_a.pos_y]; + int pos_x = pix_a.pos_x; + s0 += *(*(img_pred ++) + pos_x); + s0 += *(*(img_pred ++) + pos_x); + s0 += *(*(img_pred ++) + pos_x); + s0 += *(*(img_pred ) + pos_x); + } + + if (block_available_up && block_available_left) + { + // no edge + s0 = (s0 + 4)>>3; + } + else if (!block_available_up && block_available_left) + { + // upper edge + s0 = (s0 + 2)>>2; + } + else if (block_available_up && !block_available_left) + { + // left edge + s0 = (s0 + 2)>>2; + } + else //if (!block_available_up && !block_available_left) + { + // top left corner, nothing to predict from + s0 = p_Vid->dc_pred_value_comp[pl]; + } + + for (j=joff; j < joff + BLOCK_SIZE; ++j) + { + // store DC prediction + mb_pred[j][ioff ] = (imgpel) s0; + mb_pred[j][ioff + 1] = (imgpel) s0; + mb_pred[j][ioff + 2] = (imgpel) s0; + mb_pred[j][ioff + 3] = (imgpel) s0; + } + return DECODING_OK; +} + +/*! + *********************************************************************** + * \brief + * makes and returns 4x4 vertical prediction mode + * + * \return + * DECODING_OK decoding of intraprediction mode was successful \n + * + *********************************************************************** + */ +static int intra4x4_vert_pred(Macroblock *currMB, //!< current macroblock + ColorPlane pl, //!< current image plane + int ioff, //!< pixel offset X within MB + int joff) //!< pixel offset Y within MB +{ + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + + int block_available_up; + PixelPos pix_b; + + getNonAffNeighbour(currMB, ioff, joff - 1 , p_Vid->mb_size[IS_LUMA], &pix_b); + + if (p_Vid->active_pps->constrained_intra_pred_flag) + { + block_available_up = pix_b.available ? currSlice->intra_block [pix_b.mb_addr] : 0; + } + else + { + block_available_up = pix_b.available; + } + + if (!block_available_up) + { + printf ("warning: Intra_4x4_Vertical prediction mode not allowed at mb %d\n", (int) currSlice->current_mb_nr); + } + else + { + imgpel **mb_pred = currSlice->mb_pred[pl]; + imgpel *imgY = (pl) ? &currSlice->dec_picture->imgUV[pl - 1][pix_b.pos_y][pix_b.pos_x] : &currSlice->dec_picture->imgY[pix_b.pos_y][pix_b.pos_x]; + memcpy(&(mb_pred[joff++][ioff]), imgY, BLOCK_SIZE * sizeof(imgpel)); + memcpy(&(mb_pred[joff++][ioff]), imgY, BLOCK_SIZE * sizeof(imgpel)); + memcpy(&(mb_pred[joff++][ioff]), imgY, BLOCK_SIZE * sizeof(imgpel)); + memcpy(&(mb_pred[joff ][ioff]), imgY, BLOCK_SIZE * sizeof(imgpel)); + } + return DECODING_OK; +} + +/*! + *********************************************************************** + * \brief + * makes and returns 4x4 horizontal prediction mode + * + * \param currMB + * current MB structure + * \param pl + * color plane + * \param ioff + * pixel offset X within MB + * \param joff + * pixel offset Y within MB + * + * \return + * DECODING_OK decoding of intra prediction mode was successful + * + *********************************************************************** + */ +static int intra4x4_hor_pred(Macroblock *currMB, + ColorPlane pl, + int ioff, + int joff) +{ + VideoParameters *p_Vid = currMB->p_Vid; + Slice *currSlice = currMB->p_Slice; + + PixelPos pix_a; + + int block_available_left; + + getNonAffNeighbour(currMB, ioff - 1 , joff, p_Vid->mb_size[IS_LUMA], &pix_a); + + if (p_Vid->active_pps->constrained_intra_pred_flag) + { + block_available_left = pix_a.available ? currSlice->intra_block[pix_a.mb_addr]: 0; + } + else + { + block_available_left = pix_a.available; + } + + if (!block_available_left) + printf ("warning: Intra_4x4_Horizontal prediction mode not allowed at mb %d\n",(int) currSlice->current_mb_nr); + else +#if (IMGTYPE == 0) + { + imgpel **imgY = (pl) ? currSlice->dec_picture->imgUV[pl - 1] : currSlice->dec_picture->imgY; + imgpel **mb_pred = &currSlice->mb_pred[pl][joff]; + imgpel **img_pred = &imgY[pix_a.pos_y]; + int pos_x = pix_a.pos_x; + + memset((*(mb_pred++) + ioff), *(*(img_pred++) + pos_x), BLOCK_SIZE * sizeof (imgpel)); + memset((*(mb_pred++) + ioff), *(*(img_pred++) + pos_x), BLOCK_SIZE * sizeof (imgpel)); + memset((*(mb_pred++) + ioff), *(*(img_pred++) + pos_x), BLOCK_SIZE * sizeof (imgpel)); + memset((*(mb_pred ) + ioff), *(*(img_pred ) + pos_x), BLOCK_SIZE * sizeof (imgpel)); + } +#else + { + int j; + int pos_y = pix_a.pos_y; + int pos_x = pix_a.pos_x; + imgpel *predrow, prediction; + imgpel **mb_pred = &currSlice->mb_pred[pl][joff]; + imgpel **imgY = (pl) ? currSlice->dec_picture->imgUV[pl - 1] : currSlice->dec_picture->imgY; + + for(j=0;jp_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + + imgpel **imgY = (pl) ? currSlice->dec_picture->imgUV[pl - 1] : currSlice->dec_picture->imgY; + + PixelPos pix_a; + PixelPos pix_b, pix_d; + + int block_available_up; + int block_available_left; + int block_available_up_left; + + imgpel **mb_pred = currSlice->mb_pred[pl]; + + getNonAffNeighbour(currMB, ioff -1 , joff , p_Vid->mb_size[IS_LUMA], &pix_a); + getNonAffNeighbour(currMB, ioff , joff -1 , p_Vid->mb_size[IS_LUMA], &pix_b); + getNonAffNeighbour(currMB, ioff -1 , joff -1 , p_Vid->mb_size[IS_LUMA], &pix_d); + + if (p_Vid->active_pps->constrained_intra_pred_flag) + { + block_available_left = pix_a.available ? currSlice->intra_block [pix_a.mb_addr]: 0; + block_available_up = pix_b.available ? currSlice->intra_block [pix_b.mb_addr] : 0; + block_available_up_left = pix_d.available ? currSlice->intra_block [pix_d.mb_addr] : 0; + } + else + { + block_available_left = pix_a.available; + block_available_up = pix_b.available; + block_available_up_left = pix_d.available; + } + + if ((!block_available_up)||(!block_available_left)||(!block_available_up_left)) + printf ("warning: Intra_4x4_Diagonal_Down_Right prediction mode not allowed at mb %d\n",(int) currSlice->current_mb_nr); + else + { + imgpel PredPixel[7]; + imgpel **img_pred = &imgY[pix_a.pos_y]; + int pix_x = pix_a.pos_x; + imgpel *pred_pel = &imgY[pix_b.pos_y][pix_b.pos_x]; + // form predictor pels + imgpel p_a = *pred_pel++; + imgpel p_b = *pred_pel++; + imgpel p_c = *pred_pel++; + imgpel p_d = *pred_pel ; + + imgpel p_i = *(*(img_pred++) + pix_x); + imgpel p_j = *(*(img_pred++) + pix_x); + imgpel p_k = *(*(img_pred++) + pix_x); + imgpel p_l = *(*(img_pred ) + pix_x); + + imgpel p_x = imgY[pix_d.pos_y][pix_d.pos_x]; + + PredPixel[0] = (imgpel) ((p_l + 2*p_k + p_j + 2) >> 2); + PredPixel[1] = (imgpel) ((p_k + 2*p_j + p_i + 2) >> 2); + PredPixel[2] = (imgpel) ((p_j + 2*p_i + p_x + 2) >> 2); + PredPixel[3] = (imgpel) ((p_i + 2*p_x + p_a + 2) >> 2); + PredPixel[4] = (imgpel) ((p_x + 2*p_a + p_b + 2) >> 2); + PredPixel[5] = (imgpel) ((p_a + 2*p_b + p_c + 2) >> 2); + PredPixel[6] = (imgpel) ((p_b + 2*p_c + p_d + 2) >> 2); + + memcpy(&mb_pred[joff++][ioff], &PredPixel[3], 4 * sizeof(imgpel)); + memcpy(&mb_pred[joff++][ioff], &PredPixel[2], 4 * sizeof(imgpel)); + memcpy(&mb_pred[joff++][ioff], &PredPixel[1], 4 * sizeof(imgpel)); + memcpy(&mb_pred[joff ][ioff], &PredPixel[0], 4 * sizeof(imgpel)); + } + + return DECODING_OK; +} + +/*! + *********************************************************************** + * \brief + * makes and returns 4x4 diagonal down left prediction mode + * + * \return + * DECODING_OK decoding of intraprediction mode was successful \n + * + *********************************************************************** + */ +static int intra4x4_diag_down_left_pred(Macroblock *currMB, //!< current macroblock + ColorPlane pl, //!< current image plane + int ioff, //!< pixel offset X within MB + int joff) //!< pixel offset Y within MB +{ + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + + PixelPos pix_b, pix_c; + + int block_available_up; + int block_available_up_right; + + p_Vid->getNeighbour(currMB, ioff , joff - 1, p_Vid->mb_size[IS_LUMA], &pix_b); + p_Vid->getNeighbour(currMB, ioff + 4, joff - 1, p_Vid->mb_size[IS_LUMA], &pix_c); + + pix_c.available = pix_c.available && !((ioff==4) && ((joff==4)||(joff==12))); + + if (p_Vid->active_pps->constrained_intra_pred_flag) + { + block_available_up = pix_b.available ? currSlice->intra_block [pix_b.mb_addr] : 0; + block_available_up_right = pix_c.available ? currSlice->intra_block [pix_c.mb_addr] : 0; + } + else + { + block_available_up = pix_b.available; + block_available_up_right = pix_c.available; + } + + if (!block_available_up) + printf ("warning: Intra_4x4_Diagonal_Down_Left prediction mode not allowed at mb %d\n", (int) currSlice->current_mb_nr); + else + { + imgpel **imgY = (pl) ? currSlice->dec_picture->imgUV[pl - 1] : currSlice->dec_picture->imgY; + imgpel **mb_pred = currSlice->mb_pred[pl]; + + imgpel p_e, p_f, p_g, p_h; + imgpel PredPixel[8]; + imgpel *pred_pel = &imgY[pix_b.pos_y][pix_b.pos_x]; + + // form predictor pels + imgpel p_a = *pred_pel++; + imgpel p_b = *pred_pel++; + imgpel p_c = *pred_pel++; + imgpel p_d = *pred_pel ; + + if (block_available_up_right) + { + pred_pel = &imgY[pix_c.pos_y][pix_c.pos_x]; + p_e = *pred_pel++; + p_f = *pred_pel++; + p_g = *pred_pel++; + p_h = *pred_pel ; + } + else + { + p_e = p_f = p_g = p_h = p_d; + } + + PredPixel[0] = (imgpel) ((p_a + p_c + 2*(p_b) + 2) >> 2); + PredPixel[1] = (imgpel) ((p_b + p_d + 2*(p_c) + 2) >> 2); + PredPixel[2] = (imgpel) ((p_c + p_e + 2*(p_d) + 2) >> 2); + PredPixel[3] = (imgpel) ((p_d + p_f + 2*(p_e) + 2) >> 2); + PredPixel[4] = (imgpel) ((p_e + p_g + 2*(p_f) + 2) >> 2); + PredPixel[5] = (imgpel) ((p_f + p_h + 2*(p_g) + 2) >> 2); + PredPixel[6] = (imgpel) ((p_g + 3*(p_h) + 2) >> 2); + + memcpy(&mb_pred[joff++][ioff], &PredPixel[0], 4 * sizeof(imgpel)); + memcpy(&mb_pred[joff++][ioff], &PredPixel[1], 4 * sizeof(imgpel)); + memcpy(&mb_pred[joff++][ioff], &PredPixel[2], 4 * sizeof(imgpel)); + memcpy(&mb_pred[joff ][ioff], &PredPixel[3], 4 * sizeof(imgpel)); + } + + return DECODING_OK; +} + +/*! + *********************************************************************** + * \brief + * makes and returns 4x4 vertical right prediction mode + * + * \return + * DECODING_OK decoding of intraprediction mode was successful \n + * + *********************************************************************** + */ +static int intra4x4_vert_right_pred(Macroblock *currMB, //!< current macroblock + ColorPlane pl, //!< current image plane + int ioff, //!< pixel offset X within MB + int joff) //!< pixel offset Y within MB +{ + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + + PixelPos pix_a, pix_b, pix_d; + + int block_available_up; + int block_available_left; + int block_available_up_left; + + getNonAffNeighbour(currMB, ioff -1 , joff , p_Vid->mb_size[IS_LUMA], &pix_a); + getNonAffNeighbour(currMB, ioff , joff -1 , p_Vid->mb_size[IS_LUMA], &pix_b); + getNonAffNeighbour(currMB, ioff -1 , joff -1 , p_Vid->mb_size[IS_LUMA], &pix_d); + + if (p_Vid->active_pps->constrained_intra_pred_flag) + { + block_available_left = pix_a.available ? currSlice->intra_block[pix_a.mb_addr]: 0; + block_available_up = pix_b.available ? currSlice->intra_block [pix_b.mb_addr] : 0; + block_available_up_left = pix_d.available ? currSlice->intra_block [pix_d.mb_addr] : 0; + } + else + { + block_available_left = pix_a.available; + block_available_up = pix_b.available; + block_available_up_left = pix_d.available; + } + + if ((!block_available_up)||(!block_available_left)||(!block_available_up_left)) + printf ("warning: Intra_4x4_Vertical_Right prediction mode not allowed at mb %d\n", (int) currSlice->current_mb_nr); + { + imgpel **imgY = (pl) ? currSlice->dec_picture->imgUV[pl - 1] : currSlice->dec_picture->imgY; + imgpel **mb_pred = currSlice->mb_pred[pl]; + imgpel PredPixel[10]; + + imgpel **img_pred = &imgY[pix_a.pos_y]; + int pix_x = pix_a.pos_x; + imgpel *pred_pel = &imgY[pix_b.pos_y][pix_b.pos_x]; + // form predictor pels + imgpel p_a = *pred_pel++; + imgpel p_b = *pred_pel++; + imgpel p_c = *pred_pel++; + imgpel p_d = *pred_pel ; + + imgpel p_i = *(*(img_pred++) + pix_x); + imgpel p_j = *(*(img_pred++) + pix_x); + imgpel p_k = *(*(img_pred++) + pix_x); + + imgpel p_x = imgY[pix_d.pos_y][pix_d.pos_x]; + + PredPixel[0] = (imgpel) ((p_x + 2*p_i + p_j + 2) >> 2); + PredPixel[1] = (imgpel) ((p_x + p_a + 1) >> 1); + PredPixel[2] = (imgpel) ((p_a + p_b + 1) >> 1); + PredPixel[3] = (imgpel) ((p_b + p_c + 1) >> 1); + PredPixel[4] = (imgpel) ((p_c + p_d + 1) >> 1); + PredPixel[5] = (imgpel) ((p_i + 2*p_j + p_k + 2) >> 2); + PredPixel[6] = (imgpel) ((p_i + 2*p_x + p_a + 2) >> 2); + PredPixel[7] = (imgpel) ((p_x + 2*p_a + p_b + 2) >> 2); + PredPixel[8] = (imgpel) ((p_a + 2*p_b + p_c + 2) >> 2); + PredPixel[9] = (imgpel) ((p_b + 2*p_c + p_d + 2) >> 2); + + memcpy(&mb_pred[joff++][ioff], &PredPixel[1], 4 * sizeof(imgpel)); + memcpy(&mb_pred[joff++][ioff], &PredPixel[6], 4 * sizeof(imgpel)); + memcpy(&mb_pred[joff++][ioff], &PredPixel[0], 4 * sizeof(imgpel)); + memcpy(&mb_pred[joff ][ioff], &PredPixel[5], 4 * sizeof(imgpel)); + + } + + return DECODING_OK; +} + + +/*! + *********************************************************************** + * \brief + * makes and returns 4x4 vertical left prediction mode + * + * \return + * DECODING_OK decoding of intraprediction mode was successful \n + * + *********************************************************************** + */ +static int intra4x4_vert_left_pred(Macroblock *currMB, //!< current macroblock + ColorPlane pl, //!< current image plane + int ioff, //!< pixel offset X within MB + int joff) //!< pixel offset Y within MB +{ + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + + PixelPos pix_b, pix_c; + + int block_available_up; + int block_available_up_right; + + p_Vid->getNeighbour(currMB, ioff , joff -1 , p_Vid->mb_size[IS_LUMA], &pix_b); + p_Vid->getNeighbour(currMB, ioff +4 , joff -1 , p_Vid->mb_size[IS_LUMA], &pix_c); + + pix_c.available = pix_c.available && !((ioff==4) && ((joff==4)||(joff==12))); + + if (p_Vid->active_pps->constrained_intra_pred_flag) + { + block_available_up = pix_b.available ? currSlice->intra_block [pix_b.mb_addr] : 0; + block_available_up_right = pix_c.available ? currSlice->intra_block [pix_c.mb_addr] : 0; + } + else + { + block_available_up = pix_b.available; + block_available_up_right = pix_c.available; + } + + + if (!block_available_up) + printf ("warning: Intra_4x4_Vertical_Left prediction mode not allowed at mb %d\n", (int) currSlice->current_mb_nr); + { + imgpel PredPixel[10]; + imgpel **imgY = (pl) ? currSlice->dec_picture->imgUV[pl - 1] : currSlice->dec_picture->imgY; + imgpel **mb_pred = currSlice->mb_pred[pl]; + imgpel *pred_pel = &imgY[pix_b.pos_y][pix_b.pos_x]; + + // form predictor pels + imgpel p_a = *pred_pel++; + imgpel p_b = *pred_pel++; + imgpel p_c = *pred_pel++; + imgpel p_d = *pred_pel ; + imgpel p_e, p_f, p_g; + + if (block_available_up_right) + { + imgpel *pred_pel = &imgY[pix_c.pos_y][pix_c.pos_x]; + p_e = *pred_pel++; + p_f = *pred_pel++; + p_g = *pred_pel++; + } + else + { + p_e = p_f = p_g = p_d; + } + + PredPixel[0] = (imgpel) ((p_a + p_b + 1) >> 1); + PredPixel[1] = (imgpel) ((p_b + p_c + 1) >> 1); + PredPixel[2] = (imgpel) ((p_c + p_d + 1) >> 1); + PredPixel[3] = (imgpel) ((p_d + p_e + 1) >> 1); + PredPixel[4] = (imgpel) ((p_e + p_f + 1) >> 1); + PredPixel[5] = (imgpel) ((p_a + 2*p_b + p_c + 2) >> 2); + PredPixel[6] = (imgpel) ((p_b + 2*p_c + p_d + 2) >> 2); + PredPixel[7] = (imgpel) ((p_c + 2*p_d + p_e + 2) >> 2); + PredPixel[8] = (imgpel) ((p_d + 2*p_e + p_f + 2) >> 2); + PredPixel[9] = (imgpel) ((p_e + 2*p_f + p_g + 2) >> 2); + + memcpy(&mb_pred[joff++][ioff], &PredPixel[0], 4 * sizeof(imgpel)); + memcpy(&mb_pred[joff++][ioff], &PredPixel[5], 4 * sizeof(imgpel)); + memcpy(&mb_pred[joff++][ioff], &PredPixel[1], 4 * sizeof(imgpel)); + memcpy(&mb_pred[joff ][ioff], &PredPixel[6], 4 * sizeof(imgpel)); + } + return DECODING_OK; +} + +/*! + *********************************************************************** + * \brief + * makes and returns 4x4 horizontal up prediction mode + * + * \return + * DECODING_OK decoding of intraprediction mode was successful \n + * + *********************************************************************** + */ +static int intra4x4_hor_up_pred(Macroblock *currMB, //!< current macroblock + ColorPlane pl, //!< current image plane + int ioff, //!< pixel offset X within MB + int joff) //!< pixel offset Y within MB +{ + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + + PixelPos pix_a; + + int block_available_left; + + getNonAffNeighbour(currMB, ioff -1 , joff, p_Vid->mb_size[IS_LUMA], &pix_a); + + if (p_Vid->active_pps->constrained_intra_pred_flag) + { + block_available_left = pix_a.available ? currSlice->intra_block[pix_a.mb_addr]: 0; + } + else + { + block_available_left = pix_a.available; + } + + if (!block_available_left) + printf ("warning: Intra_4x4_Horizontal_Up prediction mode not allowed at mb %d\n",(int) currSlice->current_mb_nr); + else + { + imgpel PredPixel[10]; + imgpel **imgY = (pl) ? currSlice->dec_picture->imgUV[pl - 1] : currSlice->dec_picture->imgY; + imgpel **mb_pred = currSlice->mb_pred[pl]; + + imgpel **img_pred = &imgY[pix_a.pos_y]; + int pix_x = pix_a.pos_x; + + // form predictor pels + imgpel p_i = *(*(img_pred++) + pix_x); + imgpel p_j = *(*(img_pred++) + pix_x); + imgpel p_k = *(*(img_pred++) + pix_x); + imgpel p_l = *(*(img_pred ) + pix_x); + + PredPixel[0] = (imgpel) ((p_i + p_j + 1) >> 1); + PredPixel[1] = (imgpel) ((p_i + 2*p_j + p_k + 2) >> 2); + PredPixel[2] = (imgpel) ((p_j + p_k + 1) >> 1); + PredPixel[3] = (imgpel) ((p_j + 2*p_k + p_l + 2) >> 2); + PredPixel[4] = (imgpel) ((p_k + p_l + 1) >> 1); + PredPixel[5] = (imgpel) ((p_k + 2*p_l + p_l + 2) >> 2); + PredPixel[6] = (imgpel) p_l; + PredPixel[7] = (imgpel) p_l; + PredPixel[8] = (imgpel) p_l; + PredPixel[9] = (imgpel) p_l; + + memcpy(&mb_pred[joff++][ioff], &PredPixel[0], 4 * sizeof(imgpel)); + memcpy(&mb_pred[joff++][ioff], &PredPixel[2], 4 * sizeof(imgpel)); + memcpy(&mb_pred[joff++][ioff], &PredPixel[4], 4 * sizeof(imgpel)); + memcpy(&mb_pred[joff++][ioff], &PredPixel[6], 4 * sizeof(imgpel)); + } + + return DECODING_OK; +} + +/*! + *********************************************************************** + * \brief + * makes and returns 4x4 horizontal down prediction mode + * + * \return + * DECODING_OK decoding of intraprediction mode was successful \n + * + *********************************************************************** + */ +static int intra4x4_hor_down_pred(Macroblock *currMB, //!< current macroblock + ColorPlane pl, //!< current image plane + int ioff, //!< pixel offset X within MB + int joff) //!< pixel offset Y within MB +{ + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + + PixelPos pix_a, pix_b, pix_d; + + int block_available_up; + int block_available_left; + int block_available_up_left; + + getNonAffNeighbour(currMB, ioff -1 , joff , p_Vid->mb_size[IS_LUMA], &pix_a); + getNonAffNeighbour(currMB, ioff , joff -1 , p_Vid->mb_size[IS_LUMA], &pix_b); + getNonAffNeighbour(currMB, ioff -1 , joff -1 , p_Vid->mb_size[IS_LUMA], &pix_d); + + if (p_Vid->active_pps->constrained_intra_pred_flag) + { + block_available_left = pix_a.available ? currSlice->intra_block [pix_a.mb_addr]: 0; + block_available_up = pix_b.available ? currSlice->intra_block [pix_b.mb_addr] : 0; + block_available_up_left = pix_d.available ? currSlice->intra_block [pix_d.mb_addr] : 0; + } + else + { + block_available_left = pix_a.available; + block_available_up = pix_b.available; + block_available_up_left = pix_d.available; + } + + if ((!block_available_up)||(!block_available_left)||(!block_available_up_left)) + printf ("warning: Intra_4x4_Horizontal_Down prediction mode not allowed at mb %d\n", (int) currSlice->current_mb_nr); + else + { + imgpel PredPixel[10]; + imgpel **imgY = (pl) ? currSlice->dec_picture->imgUV[pl - 1] : currSlice->dec_picture->imgY; + imgpel **mb_pred = currSlice->mb_pred[pl]; + + imgpel **img_pred = &imgY[pix_a.pos_y]; + int pix_x = pix_a.pos_x; + imgpel *pred_pel = &imgY[pix_b.pos_y][pix_b.pos_x]; + + // form predictor pels + imgpel p_a = *pred_pel++; + imgpel p_b = *pred_pel++; + imgpel p_c = *pred_pel++; + + imgpel p_i = *(*(img_pred++) + pix_x); + imgpel p_j = *(*(img_pred++) + pix_x); + imgpel p_k = *(*(img_pred++) + pix_x); + imgpel p_l = *(*(img_pred ) + pix_x); + + imgpel p_x = imgY[pix_d.pos_y][pix_d.pos_x]; + + PredPixel[0] = (imgpel) ((p_k + p_l + 1) >> 1); + PredPixel[1] = (imgpel) ((p_j + 2*p_k + p_l + 2) >> 2); + PredPixel[2] = (imgpel) ((p_j + p_k + 1) >> 1); + PredPixel[3] = (imgpel) ((p_i + 2*p_j + p_k + 2) >> 2); + PredPixel[4] = (imgpel) ((p_i + p_j + 1) >> 1); + PredPixel[5] = (imgpel) ((p_x + 2*p_i + p_j + 2) >> 2); + PredPixel[6] = (imgpel) ((p_x + p_i + 1) >> 1); + PredPixel[7] = (imgpel) ((p_i + 2*p_x + p_a + 2) >> 2); + PredPixel[8] = (imgpel) ((p_x + 2*p_a + p_b + 2) >> 2); + PredPixel[9] = (imgpel) ((p_a + 2*p_b + p_c + 2) >> 2); + + memcpy(&mb_pred[joff++][ioff], &PredPixel[6], 4 * sizeof(imgpel)); + memcpy(&mb_pred[joff++][ioff], &PredPixel[4], 4 * sizeof(imgpel)); + memcpy(&mb_pred[joff++][ioff], &PredPixel[2], 4 * sizeof(imgpel)); + memcpy(&mb_pred[joff ][ioff], &PredPixel[0], 4 * sizeof(imgpel)); + } + + return DECODING_OK; +} + + +/*! + *********************************************************************** + * \brief + * makes and returns 4x4 intra prediction blocks + * + * \return + * DECODING_OK decoding of intraprediction mode was successful \n + * SEARCH_SYNC search next sync element as errors while decoding occured + *********************************************************************** + */ +int intra4x4_pred_normal(Macroblock *currMB, //!< current macroblock + ColorPlane pl, //!< current image plane + int ioff, //!< pixel offset X within MB + int joff, //!< pixel offset Y within MB + int img_block_x, //!< location of block X, multiples of 4 + int img_block_y) //!< location of block Y, multiples of 4 +{ + VideoParameters *p_Vid = currMB->p_Vid; + byte predmode = p_Vid->ipredmode[img_block_y][img_block_x]; + currMB->ipmode_DPCM = predmode; //For residual DPCM + + switch (predmode) + { + case DC_PRED: + return (intra4x4_dc_pred(currMB, pl, ioff, joff)); + break; + case VERT_PRED: + return (intra4x4_vert_pred(currMB, pl, ioff, joff)); + break; + case HOR_PRED: + return (intra4x4_hor_pred(currMB, pl, ioff, joff)); + break; + case DIAG_DOWN_RIGHT_PRED: + return (intra4x4_diag_down_right_pred(currMB, pl, ioff, joff)); + break; + case DIAG_DOWN_LEFT_PRED: + return (intra4x4_diag_down_left_pred(currMB, pl, ioff, joff)); + break; + case VERT_RIGHT_PRED: + return (intra4x4_vert_right_pred(currMB, pl, ioff, joff)); + break; + case VERT_LEFT_PRED: + return (intra4x4_vert_left_pred(currMB, pl, ioff, joff)); + break; + case HOR_UP_PRED: + return (intra4x4_hor_up_pred(currMB, pl, ioff, joff)); + break; + case HOR_DOWN_PRED: + return (intra4x4_hor_down_pred(currMB, pl, ioff, joff)); + default: + printf("Error: illegal intra_4x4 prediction mode: %d\n", (int) predmode); + return SEARCH_SYNC; + break; + } +} diff --git a/ldecod/src/intra8x8_pred.c b/ldecod/src/intra8x8_pred.c new file mode 100644 index 0000000..5e276d2 --- /dev/null +++ b/ldecod/src/intra8x8_pred.c @@ -0,0 +1,1546 @@ +/*! + ************************************************************************************* + * \file intra8x8_pred.c + * + * \brief + * Functions for intra 8x8 prediction + * + * \author + * Main contributors (see contributors.h for copyright, + * address and affiliation details) + * - Yuri Vatis + * - Jan Muenster + * - Alexis Michael Tourapis + * + ************************************************************************************* + */ +#include "global.h" +#include "intra8x8_pred.h" +#include "mb_access.h" +#include "image.h" + +// Notation for comments regarding prediction and predictors. +// The pels of the 8x8 block are labeled a..p. The predictor pels above +// are labeled A..H, from the left I..P, and from above left X, as follows: +// +// Z A B C D E F G H I J K L M N O P +// Q a1 b1 c1 d1 e1 f1 g1 h1 +// R a2 b2 c2 d2 e2 f2 g2 h2 +// S a3 b3 c3 d3 e3 f3 g3 h3 +// T a4 b4 c4 d4 e4 f4 g4 h4 +// U a5 b5 c5 d5 e5 f5 g5 h5 +// V a6 b6 c6 d6 e6 f6 g6 h6 +// W a7 b7 c7 d7 e7 f7 g7 h7 +// X a8 b8 c8 d8 e8 f8 g8 h8 + + +// Predictor array index definitions +#define P_Z (PredPel[0]) +#define P_A (PredPel[1]) +#define P_B (PredPel[2]) +#define P_C (PredPel[3]) +#define P_D (PredPel[4]) +#define P_E (PredPel[5]) +#define P_F (PredPel[6]) +#define P_G (PredPel[7]) +#define P_H (PredPel[8]) +#define P_I (PredPel[9]) +#define P_J (PredPel[10]) +#define P_K (PredPel[11]) +#define P_L (PredPel[12]) +#define P_M (PredPel[13]) +#define P_N (PredPel[14]) +#define P_O (PredPel[15]) +#define P_P (PredPel[16]) +#define P_Q (PredPel[17]) +#define P_R (PredPel[18]) +#define P_S (PredPel[19]) +#define P_T (PredPel[20]) +#define P_U (PredPel[21]) +#define P_V (PredPel[22]) +#define P_W (PredPel[23]) +#define P_X (PredPel[24]) + +/*! + ************************************************************************************* + * \brief + * Prefiltering for Intra8x8 prediction + ************************************************************************************* + */ +static inline void LowPassForIntra8x8Pred(imgpel *PredPel, int block_up_left, int block_up, int block_left) +{ + int i; + imgpel LoopArray[25]; + + memcpy(&LoopArray[0], &PredPel[0], 25 * sizeof(imgpel)); + + if(block_up_left) + { + if(block_up && block_left) + { + LoopArray[0] = (imgpel) ((P_Q + (P_Z<<1) + P_A + 2)>>2); + } + else + { + if(block_up) + LoopArray[0] = (imgpel) ((P_Z + (P_Z<<1) + P_A + 2)>>2); + else if (block_left) + LoopArray[0] = (imgpel) ((P_Z + (P_Z<<1) + P_Q + 2)>>2); + } + } + + if(block_up) + { + if(block_up_left) + { + LoopArray[1] = (imgpel) ((PredPel[0] + (PredPel[1]<<1) + PredPel[2] + 2)>>2); + } + else + LoopArray[1] = (imgpel) ((PredPel[1] + (PredPel[1]<<1) + PredPel[2] + 2)>>2); + + + for(i = 2; i <16; i++) + { + LoopArray[i] = (imgpel) ((PredPel[i-1] + (PredPel[i]<<1) + PredPel[i+1] + 2)>>2); + } + LoopArray[16] = (imgpel) ((P_P + (P_P<<1) + P_O + 2)>>2); + } + + if(block_left) + { + if(block_up_left) + LoopArray[17] = (imgpel) ((P_Z + (P_Q<<1) + P_R + 2)>>2); + else + LoopArray[17] = (imgpel) ((P_Q + (P_Q<<1) + P_R + 2)>>2); + + for(i = 18; i <24; i++) + { + LoopArray[i] = (imgpel) ((PredPel[i-1] + (PredPel[i]<<1) + PredPel[i+1] + 2)>>2); + } + LoopArray[24] = (imgpel) ((P_W + (P_X<<1) + P_X + 2) >> 2); + } + + memcpy(&PredPel[0], &LoopArray[0], 25 * sizeof(imgpel)); +} + +/*! + ************************************************************************************* + * \brief + * Prefiltering for Intra8x8 prediction (Horizontal) + ************************************************************************************* + */ +static inline void LowPassForIntra8x8PredHor(imgpel *PredPel, int block_up_left, int block_up, int block_left) +{ + int i; + imgpel LoopArray[25]; + + memcpy(&LoopArray[0], &PredPel[0], 25 * sizeof(imgpel)); + + if(block_up_left) + { + if(block_up && block_left) + { + LoopArray[0] = (imgpel) ((P_Q + (P_Z<<1) + P_A + 2)>>2); + } + else + { + if(block_up) + LoopArray[0] = (imgpel) ((P_Z + (P_Z<<1) + P_A + 2)>>2); + else if (block_left) + LoopArray[0] = (imgpel) ((P_Z + (P_Z<<1) + P_Q + 2)>>2); + } + } + + if(block_up) + { + if(block_up_left) + { + LoopArray[1] = (imgpel) ((PredPel[0] + (PredPel[1]<<1) + PredPel[2] + 2)>>2); + } + else + LoopArray[1] = (imgpel) ((PredPel[1] + (PredPel[1]<<1) + PredPel[2] + 2)>>2); + + + for(i = 2; i <16; i++) + { + LoopArray[i] = (imgpel) ((PredPel[i-1] + (PredPel[i]<<1) + PredPel[i+1] + 2)>>2); + } + LoopArray[16] = (imgpel) ((P_P + (P_P<<1) + P_O + 2)>>2); + } + + + memcpy(&PredPel[0], &LoopArray[0], 17 * sizeof(imgpel)); +} + +/*! + ************************************************************************************* + * \brief + * Prefiltering for Intra8x8 prediction (Vertical) + ************************************************************************************* + */ +static inline void LowPassForIntra8x8PredVer(imgpel *PredPel, int block_up_left, int block_up, int block_left) +{ + // These functions need some cleanup and can be further optimized. + // For convenience, let us copy all data for now. It is obvious that the filtering makes things a bit more "complex" + int i; + imgpel LoopArray[25]; + + memcpy(&LoopArray[0], &PredPel[0], 25 * sizeof(imgpel)); + + if(block_up_left) + { + if(block_up && block_left) + { + LoopArray[0] = (imgpel) ((P_Q + (P_Z<<1) + P_A + 2)>>2); + } + else + { + if(block_up) + LoopArray[0] = (imgpel) ((P_Z + (P_Z<<1) + P_A + 2)>>2); + else if (block_left) + LoopArray[0] = (imgpel) ((P_Z + (P_Z<<1) + P_Q + 2)>>2); + } + } + + if(block_left) + { + if(block_up_left) + LoopArray[17] = (imgpel) ((P_Z + (P_Q<<1) + P_R + 2)>>2); + else + LoopArray[17] = (imgpel) ((P_Q + (P_Q<<1) + P_R + 2)>>2); + + for(i = 18; i <24; i++) + { + LoopArray[i] = (imgpel) ((PredPel[i-1] + (PredPel[i]<<1) + PredPel[i+1] + 2)>>2); + } + LoopArray[24] = (imgpel) ((P_W + (P_X<<1) + P_X + 2) >> 2); + } + + memcpy(&PredPel[0], &LoopArray[0], 25 * sizeof(imgpel)); +} + + +/*! + *********************************************************************** + * \brief + * makes and returns 8x8 DC prediction mode + * + * \return + * DECODING_OK decoding of intra prediction mode was successful \n + * + *********************************************************************** + */ +static inline int intra8x8_dc_pred(Macroblock *currMB, //!< current macroblock + ColorPlane pl, //!< current image plane + int ioff, //!< pixel offset X within MB + int joff) //!< pixel offset Y within MB +{ + int i,j; + int s0 = 0; + imgpel PredPel[25]; // array of predictor pels + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + + StorablePicture *dec_picture = currSlice->dec_picture; + imgpel **imgY = (pl) ? dec_picture->imgUV[pl - 1] : dec_picture->imgY; // For MB level frame/field coding tools -- set default to imgY + + PixelPos pix_a; + PixelPos pix_b, pix_c, pix_d; + + int block_available_up; + int block_available_left; + int block_available_up_left; + int block_available_up_right; + + imgpel **mpr = currSlice->mb_pred[pl]; + int *mb_size = p_Vid->mb_size[IS_LUMA]; + + getNonAffNeighbour(currMB, ioff - 1, joff, mb_size, &pix_a); + getNonAffNeighbour(currMB, ioff , joff - 1, mb_size, &pix_b); + getNonAffNeighbour(currMB, ioff + 8, joff - 1, mb_size, &pix_c); + getNonAffNeighbour(currMB, ioff - 1, joff - 1, mb_size, &pix_d); + + pix_c.available = pix_c.available &&!(ioff == 8 && joff == 8); + + if (p_Vid->active_pps->constrained_intra_pred_flag) + { + block_available_left = pix_a.available ? currSlice->intra_block [pix_a.mb_addr]: 0; + block_available_up = pix_b.available ? currSlice->intra_block [pix_b.mb_addr] : 0; + block_available_up_right = pix_c.available ? currSlice->intra_block [pix_c.mb_addr] : 0; + block_available_up_left = pix_d.available ? currSlice->intra_block [pix_d.mb_addr] : 0; + } + else + { + block_available_left = pix_a.available; + block_available_up = pix_b.available; + block_available_up_right = pix_c.available; + block_available_up_left = pix_d.available; + } + + // form predictor pels + if (block_available_up) + { + memcpy(&PredPel[1], &imgY[pix_b.pos_y][pix_b.pos_x], BLOCK_SIZE_8x8 * sizeof(imgpel)); + } + else + { +#if (IMGTYPE == 0) + memset(&PredPel[1], p_Vid->dc_pred_value_comp[pl], BLOCK_SIZE_8x8 * sizeof(imgpel)); +#else + P_A = P_B = P_C = P_D = P_E = P_F = P_G = P_H = (imgpel) p_Vid->dc_pred_value_comp[pl]; +#endif + } + + if (block_available_up_right) + { + memcpy(&PredPel[9], &imgY[pix_c.pos_y][pix_c.pos_x], BLOCK_SIZE_8x8 * sizeof(imgpel)); + } + else + { +#if (IMGTYPE == 0) + memset(&PredPel[9], PredPel[8], BLOCK_SIZE_8x8 * sizeof(imgpel)); +#else + P_I = P_J = P_K = P_L = P_M = P_N = P_O = P_P = P_H; +#endif + } + + if (block_available_left) + { + imgpel **img_pred = &imgY[pix_a.pos_y]; + int pos_x = pix_a.pos_x; + P_Q = *(*(img_pred ++) + pos_x); + P_R = *(*(img_pred ++) + pos_x); + P_S = *(*(img_pred ++) + pos_x); + P_T = *(*(img_pred ++) + pos_x); + P_U = *(*(img_pred ++) + pos_x); + P_V = *(*(img_pred ++) + pos_x); + P_W = *(*(img_pred ++) + pos_x); + P_X = *(*(img_pred ) + pos_x); + } + else + { + P_Q = P_R = P_S = P_T = P_U = P_V = P_W = P_X = (imgpel) p_Vid->dc_pred_value_comp[pl]; + } + + if (block_available_up_left) + { + P_Z = imgY[pix_d.pos_y][pix_d.pos_x]; + } + else + { + P_Z = (imgpel) p_Vid->dc_pred_value_comp[pl]; + } + + LowPassForIntra8x8Pred(PredPel, block_available_up_left, block_available_up, block_available_left); + + if (block_available_up && block_available_left) + { + // no edge + s0 = (P_A + P_B + P_C + P_D + P_E + P_F + P_G + P_H + P_Q + P_R + P_S + P_T + P_U + P_V + P_W + P_X + 8) >> 4; + } + else if (!block_available_up && block_available_left) + { + // upper edge + s0 = (P_Q + P_R + P_S + P_T + P_U + P_V + P_W + P_X + 4) >> 3; + } + else if (block_available_up && !block_available_left) + { + // left edge + s0 = (P_A + P_B + P_C + P_D + P_E + P_F + P_G + P_H + 4) >> 3; + } + else //if (!block_available_up && !block_available_left) + { + // top left corner, nothing to predict from + s0 = p_Vid->dc_pred_value_comp[pl]; + } + + for(i = ioff; i < ioff + BLOCK_SIZE_8x8; i++) + mpr[joff][i] = (imgpel) s0; + + for(j = joff + 1; j < joff + BLOCK_SIZE_8x8; j++) + memcpy(&mpr[j][ioff], &mpr[j - 1][ioff], BLOCK_SIZE_8x8 * sizeof(imgpel)); + + return DECODING_OK; +} + +/*! + *********************************************************************** + * \brief + * makes and returns 8x8 vertical prediction mode + * + * \return + * DECODING_OK decoding of intra prediction mode was successful \n + * + *********************************************************************** + */ +static inline int intra8x8_vert_pred(Macroblock *currMB, //!< current macroblock + ColorPlane pl, //!< current image plane + int ioff, //!< pixel offset X within MB + int joff) //!< pixel offset Y within MB +{ + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + + int i; + imgpel PredPel[25]; // array of predictor pels + imgpel **imgY = (pl) ? currSlice->dec_picture->imgUV[pl - 1] : currSlice->dec_picture->imgY; // For MB level frame/field coding tools -- set default to imgY + + PixelPos pix_a; + PixelPos pix_b, pix_c, pix_d; + + int block_available_up; + int block_available_left; + int block_available_up_left; + int block_available_up_right; + + + imgpel **mpr = currSlice->mb_pred[pl]; + int *mb_size = p_Vid->mb_size[IS_LUMA]; + + getNonAffNeighbour(currMB, ioff - 1, joff , mb_size, &pix_a); + getNonAffNeighbour(currMB, ioff , joff - 1, mb_size, &pix_b); + getNonAffNeighbour(currMB, ioff + 8, joff - 1, mb_size, &pix_c); + getNonAffNeighbour(currMB, ioff - 1, joff - 1, mb_size, &pix_d); + + pix_c.available = pix_c.available &&!(ioff == 8 && joff == 8); + + if (p_Vid->active_pps->constrained_intra_pred_flag) + { + block_available_left = pix_a.available ? currSlice->intra_block [pix_a.mb_addr] : 0; + block_available_up = pix_b.available ? currSlice->intra_block [pix_b.mb_addr] : 0; + block_available_up_right = pix_c.available ? currSlice->intra_block [pix_c.mb_addr] : 0; + block_available_up_left = pix_d.available ? currSlice->intra_block [pix_d.mb_addr] : 0; + } + else + { + block_available_left = pix_a.available; + block_available_up = pix_b.available; + block_available_up_right = pix_c.available; + block_available_up_left = pix_d.available; + } + + if (!block_available_up) + printf ("warning: Intra_8x8_Vertical prediction mode not allowed at mb %d\n", (int) currSlice->current_mb_nr); + + // form predictor pels + if (block_available_up) + { + memcpy(&PredPel[1], &imgY[pix_b.pos_y][pix_b.pos_x], BLOCK_SIZE_8x8 * sizeof(imgpel)); + } + else + { +#if (IMGTYPE == 0) + memset(&PredPel[1], p_Vid->dc_pred_value_comp[pl], BLOCK_SIZE_8x8 * sizeof(imgpel)); +#else + P_A = P_B = P_C = P_D = P_E = P_F = P_G = P_H = (imgpel) p_Vid->dc_pred_value_comp[pl]; +#endif + } + + if (block_available_up_right) + { + memcpy(&PredPel[9], &imgY[pix_c.pos_y][pix_c.pos_x], BLOCK_SIZE_8x8 * sizeof(imgpel)); + } + else + { +#if (IMGTYPE == 0) + memset(&PredPel[9], PredPel[8], BLOCK_SIZE_8x8 * sizeof(imgpel)); +#else + P_I = P_J = P_K = P_L = P_M = P_N = P_O = P_P = P_H; +#endif + } + + if (block_available_up_left) + { + P_Z = imgY[pix_d.pos_y][pix_d.pos_x]; + } + else + { + P_Z = (imgpel) p_Vid->dc_pred_value_comp[pl]; + } + + LowPassForIntra8x8PredHor(&(P_Z), block_available_up_left, block_available_up, block_available_left); + + for (i=joff; i < joff + BLOCK_SIZE_8x8; i++) + { + memcpy(&mpr[i][ioff], &PredPel[1], BLOCK_SIZE_8x8 * sizeof(imgpel)); + } + + return DECODING_OK; +} + +/*! + *********************************************************************** + * \brief + * makes and returns 8x8 horizontal prediction mode + * + * \return + * DECODING_OK decoding of intra prediction mode was successful \n + * + *********************************************************************** + */ +static inline int intra8x8_hor_pred(Macroblock *currMB, //!< current macroblock + ColorPlane pl, //!< current image plane + int ioff, //!< pixel offset X within MB + int joff) //!< pixel offset Y within MB +{ + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + + + int j; + imgpel PredPel[25]; // array of predictor pels + imgpel **imgY = (pl) ? currSlice->dec_picture->imgUV[pl - 1] : currSlice->dec_picture->imgY; // For MB level frame/field coding tools -- set default to imgY + + PixelPos pix_a; + PixelPos pix_b, pix_d; + + int block_available_up; + int block_available_left; + int block_available_up_left; + +#if (IMGTYPE != 0) + int ipos0 = ioff , ipos1 = ioff + 1, ipos2 = ioff + 2, ipos3 = ioff + 3; + int ipos4 = ioff + 4, ipos5 = ioff + 5, ipos6 = ioff + 6, ipos7 = ioff + 7; +#endif + int jpos; + imgpel **mpr = currSlice->mb_pred[pl]; + int *mb_size = p_Vid->mb_size[IS_LUMA]; + + getNonAffNeighbour(currMB, ioff - 1, joff , mb_size, &pix_a); + getNonAffNeighbour(currMB, ioff , joff - 1, mb_size, &pix_b); + getNonAffNeighbour(currMB, ioff - 1, joff - 1, mb_size, &pix_d); + + if (p_Vid->active_pps->constrained_intra_pred_flag) + { + block_available_left = pix_a.available ? currSlice->intra_block [pix_a.mb_addr]: 0; + block_available_up = pix_b.available ? currSlice->intra_block [pix_b.mb_addr] : 0; + block_available_up_left = pix_d.available ? currSlice->intra_block [pix_d.mb_addr] : 0; + } + else + { + block_available_left = pix_a.available; + block_available_up = pix_b.available; + block_available_up_left = pix_d.available; + } + + if (!block_available_left) + printf ("warning: Intra_8x8_Horizontal prediction mode not allowed at mb %d\n", (int) currSlice->current_mb_nr); + + // form predictor pels + if (block_available_left) + { + imgpel **img_pred = &imgY[pix_a.pos_y]; + int pos_x = pix_a.pos_x; + P_Q = *(*(img_pred ++) + pos_x); + P_R = *(*(img_pred ++) + pos_x); + P_S = *(*(img_pred ++) + pos_x); + P_T = *(*(img_pred ++) + pos_x); + P_U = *(*(img_pred ++) + pos_x); + P_V = *(*(img_pred ++) + pos_x); + P_W = *(*(img_pred ++) + pos_x); + P_X = *(*(img_pred ) + pos_x); + } + else + { + P_Q = P_R = P_S = P_T = P_U = P_V = P_W = P_X = (imgpel) p_Vid->dc_pred_value_comp[pl]; + } + + if (block_available_up_left) + { + P_Z = imgY[pix_d.pos_y][pix_d.pos_x]; + } + else + { + P_Z = (imgpel) p_Vid->dc_pred_value_comp[pl]; + } + + LowPassForIntra8x8PredVer(&(P_Z), block_available_up_left, block_available_up, block_available_left); + + for (j=0; j < BLOCK_SIZE_8x8; j++) + { + jpos = j + joff; +#if (IMGTYPE == 0) + memset(&mpr[jpos][ioff], (imgpel) (&P_Q)[j], 8 * sizeof(imgpel)); +#else + mpr[jpos][ipos0] = + mpr[jpos][ipos1] = + mpr[jpos][ipos2] = + mpr[jpos][ipos3] = + mpr[jpos][ipos4] = + mpr[jpos][ipos5] = + mpr[jpos][ipos6] = + mpr[jpos][ipos7] = (imgpel) (&P_Q)[j]; +#endif + } + + return DECODING_OK; +} + +/*! + *********************************************************************** + * \brief + * makes and returns 8x8 diagonal down right prediction mode + * + * \return + * DECODING_OK decoding of intra prediction mode was successful \n + * + *********************************************************************** + */ +static inline int intra8x8_diag_down_right_pred(Macroblock *currMB, //!< current macroblock + ColorPlane pl, //!< current image plane + int ioff, //!< pixel offset X within MB + int joff) //!< pixel offset Y within MB +{ + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + + imgpel PredPel[25]; // array of predictor pels + imgpel PredArray[16]; // array of final prediction values + imgpel **imgY = (pl) ? currSlice->dec_picture->imgUV[pl - 1] : currSlice->dec_picture->imgY; // For MB level frame/field coding tools -- set default to imgY + + PixelPos pix_a; + PixelPos pix_b, pix_c, pix_d; + + int block_available_up; + int block_available_left; + int block_available_up_left; + int block_available_up_right; + + imgpel *pred_pels; + imgpel **mb_pred = &currSlice->mb_pred[pl][joff]; + int *mb_size = p_Vid->mb_size[IS_LUMA]; + + getNonAffNeighbour(currMB, ioff - 1, joff , mb_size, &pix_a); + getNonAffNeighbour(currMB, ioff , joff - 1, mb_size, &pix_b); + getNonAffNeighbour(currMB, ioff + 8, joff - 1, mb_size, &pix_c); + getNonAffNeighbour(currMB, ioff - 1, joff - 1, mb_size, &pix_d); + + pix_c.available = pix_c.available &&!(ioff == 8 && joff == 8); + + if (p_Vid->active_pps->constrained_intra_pred_flag) + { + block_available_left = pix_a.available ? currSlice->intra_block [pix_a.mb_addr]: 0; + block_available_up = pix_b.available ? currSlice->intra_block [pix_b.mb_addr] : 0; + block_available_up_right = pix_c.available ? currSlice->intra_block [pix_c.mb_addr] : 0; + block_available_up_left = pix_d.available ? currSlice->intra_block [pix_d.mb_addr] : 0; + } + else + { + block_available_left = pix_a.available; + block_available_up = pix_b.available; + block_available_up_right = pix_c.available; + block_available_up_left = pix_d.available; + } + + if ((!block_available_up)||(!block_available_left)||(!block_available_up_left)) + printf ("warning: Intra_8x8_Diagonal_Down_Right prediction mode not allowed at mb %d\n", (int) currSlice->current_mb_nr); + + // form predictor pels + if (block_available_up) + { + memcpy(&PredPel[1], &imgY[pix_b.pos_y][pix_b.pos_x], BLOCK_SIZE_8x8 * sizeof(imgpel)); + } + else + { +#if (IMGTYPE == 0) + memset(&PredPel[1], p_Vid->dc_pred_value_comp[pl], BLOCK_SIZE_8x8 * sizeof(imgpel)); +#else + P_A = P_B = P_C = P_D = P_E = P_F = P_G = P_H = (imgpel) p_Vid->dc_pred_value_comp[pl]; +#endif + } + + if (block_available_up_right) + { + memcpy(&PredPel[9], &imgY[pix_c.pos_y][pix_c.pos_x], BLOCK_SIZE_8x8 * sizeof(imgpel)); + } + else + { +#if (IMGTYPE == 0) + memset(&PredPel[9], PredPel[8], BLOCK_SIZE_8x8 * sizeof(imgpel)); +#else + P_I = P_J = P_K = P_L = P_M = P_N = P_O = P_P = P_H; +#endif + } + + if (block_available_left) + { + imgpel **img_pred = &imgY[pix_a.pos_y]; + int pos_x = pix_a.pos_x; + P_Q = *(*(img_pred ++) + pos_x); + P_R = *(*(img_pred ++) + pos_x); + P_S = *(*(img_pred ++) + pos_x); + P_T = *(*(img_pred ++) + pos_x); + P_U = *(*(img_pred ++) + pos_x); + P_V = *(*(img_pred ++) + pos_x); + P_W = *(*(img_pred ++) + pos_x); + P_X = *(*(img_pred ) + pos_x); + } + else + { + P_Q = P_R = P_S = P_T = P_U = P_V = P_W = P_X = (imgpel) p_Vid->dc_pred_value_comp[pl]; + } + + if (block_available_up_left) + { + P_Z = imgY[pix_d.pos_y][pix_d.pos_x]; + } + else + { + P_Z = (imgpel) p_Vid->dc_pred_value_comp[pl]; + } + + LowPassForIntra8x8Pred(PredPel, block_available_up_left, block_available_up, block_available_left); + + // Mode DIAG_DOWN_RIGHT_PRED + PredArray[ 0] = (imgpel) ((P_X + P_V + ((P_W) << 1) + 2) >> 2); + PredArray[ 1] = (imgpel) ((P_W + P_U + ((P_V) << 1) + 2) >> 2); + PredArray[ 2] = (imgpel) ((P_V + P_T + ((P_U) << 1) + 2) >> 2); + PredArray[ 3] = (imgpel) ((P_U + P_S + ((P_T) << 1) + 2) >> 2); + PredArray[ 4] = (imgpel) ((P_T + P_R + ((P_S) << 1) + 2) >> 2); + PredArray[ 5] = (imgpel) ((P_S + P_Q + ((P_R) << 1) + 2) >> 2); + PredArray[ 6] = (imgpel) ((P_R + P_Z + ((P_Q) << 1) + 2) >> 2); + PredArray[ 7] = (imgpel) ((P_Q + P_A + ((P_Z) << 1) + 2) >> 2); + PredArray[ 8] = (imgpel) ((P_Z + P_B + ((P_A) << 1) + 2) >> 2); + PredArray[ 9] = (imgpel) ((P_A + P_C + ((P_B) << 1) + 2) >> 2); + PredArray[10] = (imgpel) ((P_B + P_D + ((P_C) << 1) + 2) >> 2); + PredArray[11] = (imgpel) ((P_C + P_E + ((P_D) << 1) + 2) >> 2); + PredArray[12] = (imgpel) ((P_D + P_F + ((P_E) << 1) + 2) >> 2); + PredArray[13] = (imgpel) ((P_E + P_G + ((P_F) << 1) + 2) >> 2); + PredArray[14] = (imgpel) ((P_F + P_H + ((P_G) << 1) + 2) >> 2); + + pred_pels = &PredArray[7]; + + memcpy((*mb_pred++) + ioff, pred_pels--, 8 * sizeof(imgpel)); + memcpy((*mb_pred++) + ioff, pred_pels--, 8 * sizeof(imgpel)); + memcpy((*mb_pred++) + ioff, pred_pels--, 8 * sizeof(imgpel)); + memcpy((*mb_pred++) + ioff, pred_pels--, 8 * sizeof(imgpel)); + memcpy((*mb_pred++) + ioff, pred_pels--, 8 * sizeof(imgpel)); + memcpy((*mb_pred++) + ioff, pred_pels--, 8 * sizeof(imgpel)); + memcpy((*mb_pred++) + ioff, pred_pels--, 8 * sizeof(imgpel)); + memcpy((*mb_pred ) + ioff, pred_pels , 8 * sizeof(imgpel)); + + return DECODING_OK; +} + +/*! + *********************************************************************** + * \brief + * makes and returns 8x8 diagonal down left prediction mode + * + * \return + * DECODING_OK decoding of intra prediction mode was successful \n + * + *********************************************************************** + */ +static inline int intra8x8_diag_down_left_pred(Macroblock *currMB, //!< current macroblock + ColorPlane pl, //!< current image plane + int ioff, //!< pixel offset X within MB + int joff) //!< pixel offset Y within MB +{ + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + + imgpel PredPel[25]; // array of predictor pels + imgpel PredArray[16]; // array of final prediction values + imgpel *Pred = &PredArray[0]; + imgpel **imgY = (pl) ? currSlice->dec_picture->imgUV[pl - 1] : currSlice->dec_picture->imgY; // For MB level frame/field coding tools -- set default to imgY + + PixelPos pix_a; + PixelPos pix_b, pix_c, pix_d; + + int block_available_up; + int block_available_left; + int block_available_up_left; + int block_available_up_right; + + imgpel **mb_pred = &currSlice->mb_pred[pl][joff]; + int *mb_size = p_Vid->mb_size[IS_LUMA]; + + getNonAffNeighbour(currMB, ioff - 1, joff , mb_size, &pix_a); + getNonAffNeighbour(currMB, ioff , joff - 1, mb_size, &pix_b); + getNonAffNeighbour(currMB, ioff + 8, joff - 1, mb_size, &pix_c); + getNonAffNeighbour(currMB, ioff - 1, joff - 1, mb_size, &pix_d); + + pix_c.available = pix_c.available &&!(ioff == 8 && joff == 8); + + if (p_Vid->active_pps->constrained_intra_pred_flag) + { + block_available_left = pix_a.available ? currSlice->intra_block [pix_a.mb_addr]: 0; + block_available_up = pix_b.available ? currSlice->intra_block [pix_b.mb_addr] : 0; + block_available_up_right = pix_c.available ? currSlice->intra_block [pix_c.mb_addr] : 0; + block_available_up_left = pix_d.available ? currSlice->intra_block [pix_d.mb_addr] : 0; + } + else + { + block_available_left = pix_a.available; + block_available_up = pix_b.available; + block_available_up_right = pix_c.available; + block_available_up_left = pix_d.available; + } + + if (!block_available_up) + printf ("warning: Intra_8x8_Diagonal_Down_Left prediction mode not allowed at mb %d\n", (int) currSlice->current_mb_nr); + + // form predictor pels + if (block_available_up) + { + memcpy(&PredPel[1], &imgY[pix_b.pos_y][pix_b.pos_x], BLOCK_SIZE_8x8 * sizeof(imgpel)); + } + else + { +#if (IMGTYPE == 0) + memset(&PredPel[1], p_Vid->dc_pred_value_comp[pl], BLOCK_SIZE_8x8 * sizeof(imgpel)); +#else + P_A = P_B = P_C = P_D = P_E = P_F = P_G = P_H = (imgpel) p_Vid->dc_pred_value_comp[pl]; +#endif + } + + if (block_available_up_right) + { + memcpy(&PredPel[9], &imgY[pix_c.pos_y][pix_c.pos_x], BLOCK_SIZE_8x8 * sizeof(imgpel)); + } + else + { +#if (IMGTYPE == 0) + memset(&PredPel[9], PredPel[8], BLOCK_SIZE_8x8 * sizeof(imgpel)); +#else + P_I = P_J = P_K = P_L = P_M = P_N = P_O = P_P = P_H; +#endif + } + + if (block_available_left) + { + imgpel **img_pred = &imgY[pix_a.pos_y]; + int pos_x = pix_a.pos_x; + P_Q = *(*(img_pred ++) + pos_x); + P_R = *(*(img_pred ++) + pos_x); + P_S = *(*(img_pred ++) + pos_x); + P_T = *(*(img_pred ++) + pos_x); + P_U = *(*(img_pred ++) + pos_x); + P_V = *(*(img_pred ++) + pos_x); + P_W = *(*(img_pred ++) + pos_x); + P_X = *(*(img_pred ) + pos_x); + } + else + { + P_Q = P_R = P_S = P_T = P_U = P_V = P_W = P_X = (imgpel) p_Vid->dc_pred_value_comp[pl]; + } + + if (block_available_up_left) + { + P_Z = imgY[pix_d.pos_y][pix_d.pos_x]; + } + else + { + P_Z = (imgpel) p_Vid->dc_pred_value_comp[pl]; + } + + LowPassForIntra8x8Pred(PredPel, block_available_up_left, block_available_up, block_available_left); + + // Mode DIAG_DOWN_LEFT_PRED + *Pred++ = (imgpel) ((P_A + P_C + ((P_B) << 1) + 2) >> 2); + *Pred++ = (imgpel) ((P_B + P_D + ((P_C) << 1) + 2) >> 2); + *Pred++ = (imgpel) ((P_C + P_E + ((P_D) << 1) + 2) >> 2); + *Pred++ = (imgpel) ((P_D + P_F + ((P_E) << 1) + 2) >> 2); + *Pred++ = (imgpel) ((P_E + P_G + ((P_F) << 1) + 2) >> 2); + *Pred++ = (imgpel) ((P_F + P_H + ((P_G) << 1) + 2) >> 2); + *Pred++ = (imgpel) ((P_G + P_I + ((P_H) << 1) + 2) >> 2); + *Pred++ = (imgpel) ((P_H + P_J + ((P_I) << 1) + 2) >> 2); + *Pred++ = (imgpel) ((P_I + P_K + ((P_J) << 1) + 2) >> 2); + *Pred++ = (imgpel) ((P_J + P_L + ((P_K) << 1) + 2) >> 2); + *Pred++ = (imgpel) ((P_K + P_M + ((P_L) << 1) + 2) >> 2); + *Pred++ = (imgpel) ((P_L + P_N + ((P_M) << 1) + 2) >> 2); + *Pred++ = (imgpel) ((P_M + P_O + ((P_N) << 1) + 2) >> 2); + *Pred++ = (imgpel) ((P_N + P_P + ((P_O) << 1) + 2) >> 2); + *Pred = (imgpel) ((P_O + P_P + ((P_P) << 1) + 2) >> 2); + + Pred = &PredArray[ 0]; + + memcpy((*mb_pred++) + ioff, Pred++, 8 * sizeof(imgpel)); + memcpy((*mb_pred++) + ioff, Pred++, 8 * sizeof(imgpel)); + memcpy((*mb_pred++) + ioff, Pred++, 8 * sizeof(imgpel)); + memcpy((*mb_pred++) + ioff, Pred++, 8 * sizeof(imgpel)); + memcpy((*mb_pred++) + ioff, Pred++, 8 * sizeof(imgpel)); + memcpy((*mb_pred++) + ioff, Pred++, 8 * sizeof(imgpel)); + memcpy((*mb_pred++) + ioff, Pred++, 8 * sizeof(imgpel)); + memcpy((*mb_pred ) + ioff, Pred , 8 * sizeof(imgpel)); + + return DECODING_OK; +} + +/*! + *********************************************************************** + * \brief + * makes and returns 8x8 vertical right prediction mode + * + * \return + * DECODING_OK decoding of intra prediction mode was successful \n + * + *********************************************************************** + */ +static inline int intra8x8_vert_right_pred(Macroblock *currMB, //!< current macroblock + ColorPlane pl, //!< current image plane + int ioff, //!< pixel offset X within MB + int joff) //!< pixel offset Y within MB +{ + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + + imgpel PredPel[25]; // array of predictor pels + imgpel PredArray[22]; // array of final prediction values + imgpel **imgY = (pl) ? currSlice->dec_picture->imgUV[pl - 1] : currSlice->dec_picture->imgY; // For MB level frame/field coding tools -- set default to imgY + + PixelPos pix_a; + PixelPos pix_b, pix_c, pix_d; + + int block_available_up; + int block_available_left; + int block_available_up_left; + int block_available_up_right; + + imgpel *pred_pels; + imgpel **mb_pred = &currSlice->mb_pred[pl][joff]; + int *mb_size = p_Vid->mb_size[IS_LUMA]; + + getNonAffNeighbour(currMB, ioff - 1, joff , mb_size, &pix_a); + getNonAffNeighbour(currMB, ioff , joff - 1, mb_size, &pix_b); + getNonAffNeighbour(currMB, ioff + 8, joff - 1, mb_size, &pix_c); + getNonAffNeighbour(currMB, ioff - 1, joff - 1, mb_size, &pix_d); + + pix_c.available = pix_c.available &&!(ioff == 8 && joff == 8); + + if (p_Vid->active_pps->constrained_intra_pred_flag) + { + block_available_left = pix_a.available ? currSlice->intra_block [pix_a.mb_addr]: 0; + block_available_up = pix_b.available ? currSlice->intra_block [pix_b.mb_addr] : 0; + block_available_up_right = pix_c.available ? currSlice->intra_block [pix_c.mb_addr] : 0; + block_available_up_left = pix_d.available ? currSlice->intra_block [pix_d.mb_addr] : 0; + } + else + { + block_available_left = pix_a.available; + block_available_up = pix_b.available; + block_available_up_right = pix_c.available; + block_available_up_left = pix_d.available; + } + + if ((!block_available_up)||(!block_available_left)||(!block_available_up_left)) + printf ("warning: Intra_8x8_Vertical_Right prediction mode not allowed at mb %d\n", (int) currSlice->current_mb_nr); + + // form predictor pels + if (block_available_up) + { + memcpy(&PredPel[1], &imgY[pix_b.pos_y][pix_b.pos_x], BLOCK_SIZE_8x8 * sizeof(imgpel)); + } + else + { +#if (IMGTYPE == 0) + memset(&PredPel[1], p_Vid->dc_pred_value_comp[pl], BLOCK_SIZE_8x8 * sizeof(imgpel)); +#else + P_A = P_B = P_C = P_D = P_E = P_F = P_G = P_H = (imgpel) p_Vid->dc_pred_value_comp[pl]; +#endif + } + + if (block_available_up_right) + { + memcpy(&PredPel[9], &imgY[pix_c.pos_y][pix_c.pos_x], BLOCK_SIZE_8x8 * sizeof(imgpel)); + } + else + { +#if (IMGTYPE == 0) + memset(&PredPel[9], PredPel[8], BLOCK_SIZE_8x8 * sizeof(imgpel)); +#else + P_I = P_J = P_K = P_L = P_M = P_N = P_O = P_P = P_H; +#endif + } + + if (block_available_left) + { + imgpel **img_pred = &imgY[pix_a.pos_y]; + int pos_x = pix_a.pos_x; + P_Q = *(*(img_pred ++) + pos_x); + P_R = *(*(img_pred ++) + pos_x); + P_S = *(*(img_pred ++) + pos_x); + P_T = *(*(img_pred ++) + pos_x); + P_U = *(*(img_pred ++) + pos_x); + P_V = *(*(img_pred ++) + pos_x); + P_W = *(*(img_pred ++) + pos_x); + P_X = *(*(img_pred ) + pos_x); + } + else + { + P_Q = P_R = P_S = P_T = P_U = P_V = P_W = P_X = (imgpel) p_Vid->dc_pred_value_comp[pl]; + } + + if (block_available_up_left) + { + P_Z = imgY[pix_d.pos_y][pix_d.pos_x]; + } + else + { + P_Z = (imgpel) p_Vid->dc_pred_value_comp[pl]; + } + + LowPassForIntra8x8Pred(PredPel, block_available_up_left, block_available_up, block_available_left); + + pred_pels = PredArray; + *pred_pels++ = (imgpel) ((P_V + P_T + ((P_U) << 1) + 2) >> 2); + *pred_pels++ = (imgpel) ((P_T + P_R + ((P_S) << 1) + 2) >> 2); + *pred_pels++ = (imgpel) ((P_R + P_Z + ((P_Q) << 1) + 2) >> 2); + *pred_pels++ = (imgpel) ((P_Z + P_A + 1) >> 1); + *pred_pels++ = (imgpel) ((P_A + P_B + 1) >> 1); + *pred_pels++ = (imgpel) ((P_B + P_C + 1) >> 1); + *pred_pels++ = (imgpel) ((P_C + P_D + 1) >> 1); + *pred_pels++ = (imgpel) ((P_D + P_E + 1) >> 1); + *pred_pels++ = (imgpel) ((P_E + P_F + 1) >> 1); + *pred_pels++ = (imgpel) ((P_F + P_G + 1) >> 1); + *pred_pels++ = (imgpel) ((P_G + P_H + 1) >> 1); + + *pred_pels++ = (imgpel) ((P_W + P_U + ((P_V) << 1) + 2) >> 2); + *pred_pels++ = (imgpel) ((P_U + P_S + ((P_T) << 1) + 2) >> 2); + *pred_pels++ = (imgpel) ((P_S + P_Q + ((P_R) << 1) + 2) >> 2); + *pred_pels++ = (imgpel) ((P_Q + P_A + ((P_Z) << 1) + 2) >> 2); + *pred_pels++ = (imgpel) ((P_Z + P_B + ((P_A) << 1) + 2) >> 2); + *pred_pels++ = (imgpel) ((P_A + P_C + ((P_B) << 1) + 2) >> 2); + *pred_pels++ = (imgpel) ((P_B + P_D + ((P_C) << 1) + 2) >> 2); + *pred_pels++ = (imgpel) ((P_C + P_E + ((P_D) << 1) + 2) >> 2); + *pred_pels++ = (imgpel) ((P_D + P_F + ((P_E) << 1) + 2) >> 2); + *pred_pels++ = (imgpel) ((P_E + P_G + ((P_F) << 1) + 2) >> 2); + *pred_pels = (imgpel) ((P_F + P_H + ((P_G) << 1) + 2) >> 2); + + memcpy((*mb_pred++) + ioff, &PredArray[ 3], 8 * sizeof(imgpel)); + memcpy((*mb_pred++) + ioff, &PredArray[14], 8 * sizeof(imgpel)); + memcpy((*mb_pred++) + ioff, &PredArray[ 2], 8 * sizeof(imgpel)); + memcpy((*mb_pred++) + ioff, &PredArray[13], 8 * sizeof(imgpel)); + memcpy((*mb_pred++) + ioff, &PredArray[ 1], 8 * sizeof(imgpel)); + memcpy((*mb_pred++) + ioff, &PredArray[12], 8 * sizeof(imgpel)); + memcpy((*mb_pred++) + ioff, &PredArray[ 0], 8 * sizeof(imgpel)); + memcpy((*mb_pred ) + ioff, &PredArray[11], 8 * sizeof(imgpel)); + + return DECODING_OK; +} + + +/*! + *********************************************************************** + * \brief + * makes and returns 8x8 vertical left prediction mode + * + * \return + * DECODING_OK decoding of intra prediction mode was successful \n + * + *********************************************************************** + */ +static inline int intra8x8_vert_left_pred(Macroblock *currMB, //!< current macroblock + ColorPlane pl, //!< current image plane + int ioff, //!< pixel offset X within MB + int joff) //!< pixel offset Y within MB +{ + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + + imgpel PredPel[25]; // array of predictor pels + imgpel PredArray[22]; // array of final prediction values + imgpel *pred_pel = &PredArray[0]; + imgpel **imgY = (pl) ? currSlice->dec_picture->imgUV[pl - 1] : currSlice->dec_picture->imgY; // For MB level frame/field coding tools -- set default to imgY + + PixelPos pix_a; + PixelPos pix_b, pix_c, pix_d; + + int block_available_up; + int block_available_left; + int block_available_up_left; + int block_available_up_right; + + imgpel **mb_pred = &currSlice->mb_pred[pl][joff]; + int *mb_size = p_Vid->mb_size[IS_LUMA]; + + getNonAffNeighbour(currMB, ioff - 1, joff , mb_size, &pix_a); + getNonAffNeighbour(currMB, ioff , joff - 1, mb_size, &pix_b); + getNonAffNeighbour(currMB, ioff + 8, joff - 1, mb_size, &pix_c); + getNonAffNeighbour(currMB, ioff - 1, joff - 1, mb_size, &pix_d); + + pix_c.available = pix_c.available &&!(ioff == 8 && joff == 8); + + if (p_Vid->active_pps->constrained_intra_pred_flag) + { + block_available_left = pix_a.available ? currSlice->intra_block [pix_a.mb_addr] : 0; + block_available_up = pix_b.available ? currSlice->intra_block [pix_b.mb_addr] : 0; + block_available_up_right = pix_c.available ? currSlice->intra_block [pix_c.mb_addr] : 0; + block_available_up_left = pix_d.available ? currSlice->intra_block [pix_d.mb_addr] : 0; + } + else + { + block_available_left = pix_a.available; + block_available_up = pix_b.available; + block_available_up_right = pix_c.available; + block_available_up_left = pix_d.available; + } + + if (!block_available_up) + printf ("warning: Intra_4x4_Vertical_Left prediction mode not allowed at mb %d\n", (int) currSlice->current_mb_nr); + + // form predictor pels + if (block_available_up) + { + memcpy(&PredPel[1], &imgY[pix_b.pos_y][pix_b.pos_x], BLOCK_SIZE_8x8 * sizeof(imgpel)); + } + else + { +#if (IMGTYPE == 0) + memset(&PredPel[1], p_Vid->dc_pred_value_comp[pl], BLOCK_SIZE_8x8 * sizeof(imgpel)); +#else + P_A = P_B = P_C = P_D = P_E = P_F = P_G = P_H = (imgpel) p_Vid->dc_pred_value_comp[pl]; +#endif + } + + if (block_available_up_right) + { + memcpy(&PredPel[9], &imgY[pix_c.pos_y][pix_c.pos_x], BLOCK_SIZE_8x8 * sizeof(imgpel)); + } + else + { +#if (IMGTYPE == 0) + memset(&PredPel[9], PredPel[8], BLOCK_SIZE_8x8 * sizeof(imgpel)); +#else + P_I = P_J = P_K = P_L = P_M = P_N = P_O = P_P = P_H; +#endif + } + + if (block_available_left) + { + imgpel **img_pred = &imgY[pix_a.pos_y]; + int pos_x = pix_a.pos_x; + P_Q = *(*(img_pred ++) + pos_x); + P_R = *(*(img_pred ++) + pos_x); + P_S = *(*(img_pred ++) + pos_x); + P_T = *(*(img_pred ++) + pos_x); + P_U = *(*(img_pred ++) + pos_x); + P_V = *(*(img_pred ++) + pos_x); + P_W = *(*(img_pred ++) + pos_x); + P_X = *(*(img_pred ) + pos_x); + } + else + { + P_Q = P_R = P_S = P_T = P_U = P_V = P_W = P_X = (imgpel) p_Vid->dc_pred_value_comp[pl]; + } + + if (block_available_up_left) + { + P_Z = imgY[pix_d.pos_y][pix_d.pos_x]; + } + else + { + P_Z = (imgpel) p_Vid->dc_pred_value_comp[pl]; + } + + LowPassForIntra8x8Pred(PredPel, block_available_up_left, block_available_up, block_available_left); + + *pred_pel++ = (imgpel) ((P_A + P_B + 1) >> 1); + *pred_pel++ = (imgpel) ((P_B + P_C + 1) >> 1); + *pred_pel++ = (imgpel) ((P_C + P_D + 1) >> 1); + *pred_pel++ = (imgpel) ((P_D + P_E + 1) >> 1); + *pred_pel++ = (imgpel) ((P_E + P_F + 1) >> 1); + *pred_pel++ = (imgpel) ((P_F + P_G + 1) >> 1); + *pred_pel++ = (imgpel) ((P_G + P_H + 1) >> 1); + *pred_pel++ = (imgpel) ((P_H + P_I + 1) >> 1); + *pred_pel++ = (imgpel) ((P_I + P_J + 1) >> 1); + *pred_pel++ = (imgpel) ((P_J + P_K + 1) >> 1); + *pred_pel++ = (imgpel) ((P_K + P_L + 1) >> 1); + *pred_pel++ = (imgpel) ((P_A + P_C + ((P_B) << 1) + 2) >> 2); + *pred_pel++ = (imgpel) ((P_B + P_D + ((P_C) << 1) + 2) >> 2); + *pred_pel++ = (imgpel) ((P_C + P_E + ((P_D) << 1) + 2) >> 2); + *pred_pel++ = (imgpel) ((P_D + P_F + ((P_E) << 1) + 2) >> 2); + *pred_pel++ = (imgpel) ((P_E + P_G + ((P_F) << 1) + 2) >> 2); + *pred_pel++ = (imgpel) ((P_F + P_H + ((P_G) << 1) + 2) >> 2); + *pred_pel++ = (imgpel) ((P_G + P_I + ((P_H) << 1) + 2) >> 2); + *pred_pel++ = (imgpel) ((P_H + P_J + ((P_I) << 1) + 2) >> 2); + *pred_pel++ = (imgpel) ((P_I + P_K + ((P_J) << 1) + 2) >> 2); + *pred_pel++ = (imgpel) ((P_J + P_L + ((P_K) << 1) + 2) >> 2); + *pred_pel = (imgpel) ((P_K + P_M + ((P_L) << 1) + 2) >> 2); + + memcpy((*mb_pred++) + ioff, &PredArray[ 0], 8 * sizeof(imgpel)); + memcpy((*mb_pred++) + ioff, &PredArray[11], 8 * sizeof(imgpel)); + memcpy((*mb_pred++) + ioff, &PredArray[ 1], 8 * sizeof(imgpel)); + memcpy((*mb_pred++) + ioff, &PredArray[12], 8 * sizeof(imgpel)); + memcpy((*mb_pred++) + ioff, &PredArray[ 2], 8 * sizeof(imgpel)); + memcpy((*mb_pred++) + ioff, &PredArray[13], 8 * sizeof(imgpel)); + memcpy((*mb_pred++) + ioff, &PredArray[ 3], 8 * sizeof(imgpel)); + memcpy((*mb_pred ) + ioff, &PredArray[14], 8 * sizeof(imgpel)); + + return DECODING_OK; +} + +/*! + *********************************************************************** + * \brief + * makes and returns 8x8 horizontal up prediction mode + * + * \return + * DECODING_OK decoding of intra prediction mode was successful \n + * + *********************************************************************** + */ +static inline int intra8x8_hor_up_pred(Macroblock *currMB, //!< current macroblock + ColorPlane pl, //!< current image plane + int ioff, //!< pixel offset X within MB + int joff) //!< pixel offset Y within MB +{ + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + + imgpel PredPel[25]; // array of predictor pels + imgpel PredArray[22]; // array of final prediction values + imgpel **imgY = (pl) ? currSlice->dec_picture->imgUV[pl - 1] : currSlice->dec_picture->imgY; // For MB level frame/field coding tools -- set default to imgY + + PixelPos pix_a; + PixelPos pix_b, pix_c, pix_d; + + int block_available_up; + int block_available_left; + int block_available_up_left; + int block_available_up_right; + int jpos0 = joff , jpos1 = joff + 1, jpos2 = joff + 2, jpos3 = joff + 3; + int jpos4 = joff + 4, jpos5 = joff + 5, jpos6 = joff + 6, jpos7 = joff + 7; + + imgpel **mpr = currSlice->mb_pred[pl]; + int *mb_size = p_Vid->mb_size[IS_LUMA]; + + getNonAffNeighbour(currMB, ioff - 1, joff , mb_size, &pix_a); + getNonAffNeighbour(currMB, ioff , joff - 1, mb_size, &pix_b); + getNonAffNeighbour(currMB, ioff + 8, joff - 1, mb_size, &pix_c); + getNonAffNeighbour(currMB, ioff - 1, joff - 1, mb_size, &pix_d); + + pix_c.available = pix_c.available &&!(ioff == 8 && joff == 8); + + if (p_Vid->active_pps->constrained_intra_pred_flag) + { + block_available_left = pix_a.available ? currSlice->intra_block [pix_a.mb_addr] : 0; + block_available_up = pix_b.available ? currSlice->intra_block [pix_b.mb_addr] : 0; + block_available_up_right = pix_c.available ? currSlice->intra_block [pix_c.mb_addr] : 0; + block_available_up_left = pix_d.available ? currSlice->intra_block [pix_d.mb_addr] : 0; + } + else + { + block_available_left = pix_a.available; + block_available_up = pix_b.available; + block_available_up_right = pix_c.available; + block_available_up_left = pix_d.available; + } + + if (!block_available_left) + printf ("warning: Intra_8x8_Horizontal_Up prediction mode not allowed at mb %d\n", (int) currSlice->current_mb_nr); + + // form predictor pels + if (block_available_up) + { + memcpy(&PredPel[1], &imgY[pix_b.pos_y][pix_b.pos_x], BLOCK_SIZE_8x8 * sizeof(imgpel)); + } + else + { +#if (IMGTYPE == 0) + memset(&PredPel[1], p_Vid->dc_pred_value_comp[pl], BLOCK_SIZE_8x8 * sizeof(imgpel)); +#else + P_A = P_B = P_C = P_D = P_E = P_F = P_G = P_H = (imgpel) p_Vid->dc_pred_value_comp[pl]; +#endif + } + + if (block_available_up_right) + { + memcpy(&PredPel[9], &imgY[pix_c.pos_y][pix_c.pos_x], BLOCK_SIZE_8x8 * sizeof(imgpel)); + } + else + { +#if (IMGTYPE == 0) + memset(&PredPel[9], PredPel[8], BLOCK_SIZE_8x8 * sizeof(imgpel)); +#else + P_I = P_J = P_K = P_L = P_M = P_N = P_O = P_P = P_H; +#endif + } + + if (block_available_left) + { + imgpel **img_pred = &imgY[pix_a.pos_y]; + int pos_x = pix_a.pos_x; + P_Q = *(*(img_pred ++) + pos_x); + P_R = *(*(img_pred ++) + pos_x); + P_S = *(*(img_pred ++) + pos_x); + P_T = *(*(img_pred ++) + pos_x); + P_U = *(*(img_pred ++) + pos_x); + P_V = *(*(img_pred ++) + pos_x); + P_W = *(*(img_pred ++) + pos_x); + P_X = *(*(img_pred ) + pos_x); + } + else + { + P_Q = P_R = P_S = P_T = P_U = P_V = P_W = P_X = (imgpel) p_Vid->dc_pred_value_comp[pl]; + } + + if (block_available_up_left) + { + P_Z = imgY[pix_d.pos_y][pix_d.pos_x]; + } + else + { + P_Z = (imgpel) p_Vid->dc_pred_value_comp[pl]; + } + + LowPassForIntra8x8Pred(PredPel, block_available_up_left, block_available_up, block_available_left); + + PredArray[ 0] = (imgpel) ((P_Q + P_R + 1) >> 1); + PredArray[ 1] = (imgpel) ((P_S + P_Q + ((P_R) << 1) + 2) >> 2); + PredArray[ 2] = (imgpel) ((P_R + P_S + 1) >> 1); + PredArray[ 3] = (imgpel) ((P_T + P_R + ((P_S) << 1) + 2) >> 2); + PredArray[ 4] = (imgpel) ((P_S + P_T + 1) >> 1); + PredArray[ 5] = (imgpel) ((P_U + P_S + ((P_T) << 1) + 2) >> 2); + PredArray[ 6] = (imgpel) ((P_T + P_U + 1) >> 1); + PredArray[ 7] = (imgpel) ((P_V + P_T + ((P_U) << 1) + 2) >> 2); + PredArray[ 8] = (imgpel) ((P_U + P_V + 1) >> 1); + PredArray[ 9] = (imgpel) ((P_W + P_U + ((P_V) << 1) + 2) >> 2); + PredArray[10] = (imgpel) ((P_V + P_W + 1) >> 1); + PredArray[11] = (imgpel) ((P_X + P_V + ((P_W) << 1) + 2) >> 2); + PredArray[12] = (imgpel) ((P_W + P_X + 1) >> 1); + PredArray[13] = (imgpel) ((P_W + P_X + ((P_X) << 1) + 2) >> 2); + PredArray[14] = (imgpel) P_X; + PredArray[15] = (imgpel) P_X; + PredArray[16] = (imgpel) P_X; + PredArray[17] = (imgpel) P_X; + PredArray[18] = (imgpel) P_X; + PredArray[19] = (imgpel) P_X; + PredArray[20] = (imgpel) P_X; + PredArray[21] = (imgpel) P_X; + + memcpy(&mpr[jpos0][ioff], &PredArray[0], 8 * sizeof(imgpel)); + memcpy(&mpr[jpos1][ioff], &PredArray[2], 8 * sizeof(imgpel)); + memcpy(&mpr[jpos2][ioff], &PredArray[4], 8 * sizeof(imgpel)); + memcpy(&mpr[jpos3][ioff], &PredArray[6], 8 * sizeof(imgpel)); + memcpy(&mpr[jpos4][ioff], &PredArray[8], 8 * sizeof(imgpel)); + memcpy(&mpr[jpos5][ioff], &PredArray[10], 8 * sizeof(imgpel)); + memcpy(&mpr[jpos6][ioff], &PredArray[12], 8 * sizeof(imgpel)); + memcpy(&mpr[jpos7][ioff], &PredArray[14], 8 * sizeof(imgpel)); + + return DECODING_OK; +} + +/*! + *********************************************************************** + * \brief + * makes and returns 8x8 horizontal down prediction mode + * + * \return + * DECODING_OK decoding of intra prediction mode was successful \n + * + *********************************************************************** + */ +static inline int intra8x8_hor_down_pred(Macroblock *currMB, //!< current macroblock + ColorPlane pl, //!< current image plane + int ioff, //!< pixel offset X within MB + int joff) //!< pixel offset Y within MB +{ + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + + imgpel PredPel[25]; // array of predictor pels + imgpel PredArray[22]; // array of final prediction values + imgpel **imgY = (pl) ? currSlice->dec_picture->imgUV[pl - 1] : currSlice->dec_picture->imgY; // For MB level frame/field coding tools -- set default to imgY + + PixelPos pix_a; + PixelPos pix_b, pix_c, pix_d; + + int block_available_up; + int block_available_left; + int block_available_up_left; + int block_available_up_right; + + imgpel *pred_pels; + imgpel **mb_pred = &currSlice->mb_pred[pl][joff]; + int *mb_size = p_Vid->mb_size[IS_LUMA]; + + getNonAffNeighbour(currMB, ioff - 1, joff , mb_size, &pix_a); + getNonAffNeighbour(currMB, ioff , joff - 1, mb_size, &pix_b); + getNonAffNeighbour(currMB, ioff + 8, joff - 1, mb_size, &pix_c); + getNonAffNeighbour(currMB, ioff - 1, joff - 1, mb_size, &pix_d); + + pix_c.available = pix_c.available &&!(ioff == 8 && joff == 8); + + if (p_Vid->active_pps->constrained_intra_pred_flag) + { + block_available_left = pix_a.available ? currSlice->intra_block [pix_a.mb_addr] : 0; + block_available_up = pix_b.available ? currSlice->intra_block [pix_b.mb_addr] : 0; + block_available_up_right = pix_c.available ? currSlice->intra_block [pix_c.mb_addr] : 0; + block_available_up_left = pix_d.available ? currSlice->intra_block [pix_d.mb_addr] : 0; + } + else + { + block_available_left = pix_a.available; + block_available_up = pix_b.available; + block_available_up_right = pix_c.available; + block_available_up_left = pix_d.available; + } + + if ((!block_available_up)||(!block_available_left)||(!block_available_up_left)) + printf ("warning: Intra_8x8_Horizontal_Down prediction mode not allowed at mb %d\n", (int) currSlice->current_mb_nr); + + // form predictor pels + if (block_available_up) + { + memcpy(&PredPel[1], &imgY[pix_b.pos_y][pix_b.pos_x], BLOCK_SIZE_8x8 * sizeof(imgpel)); + } + else + { +#if (IMGTYPE == 0) + memset(&PredPel[1], p_Vid->dc_pred_value_comp[pl], BLOCK_SIZE_8x8 * sizeof(imgpel)); +#else + P_A = P_B = P_C = P_D = P_E = P_F = P_G = P_H = (imgpel) p_Vid->dc_pred_value_comp[pl]; +#endif + } + + if (block_available_up_right) + { + memcpy(&PredPel[9], &imgY[pix_c.pos_y][pix_c.pos_x], BLOCK_SIZE_8x8 * sizeof(imgpel)); + } + else + { +#if (IMGTYPE == 0) + memset(&PredPel[9], PredPel[8], BLOCK_SIZE_8x8 * sizeof(imgpel)); +#else + P_I = P_J = P_K = P_L = P_M = P_N = P_O = P_P = P_H; +#endif + } + + if (block_available_left) + { + imgpel **img_pred = &imgY[pix_a.pos_y]; + int pos_x = pix_a.pos_x; + P_Q = *(*(img_pred ++) + pos_x); + P_R = *(*(img_pred ++) + pos_x); + P_S = *(*(img_pred ++) + pos_x); + P_T = *(*(img_pred ++) + pos_x); + P_U = *(*(img_pred ++) + pos_x); + P_V = *(*(img_pred ++) + pos_x); + P_W = *(*(img_pred ++) + pos_x); + P_X = *(*(img_pred ) + pos_x); + } + else + { + P_Q = P_R = P_S = P_T = P_U = P_V = P_W = P_X = (imgpel) p_Vid->dc_pred_value_comp[pl]; + } + + if (block_available_up_left) + { + P_Z = imgY[pix_d.pos_y][pix_d.pos_x]; + } + else + { + P_Z = (imgpel) p_Vid->dc_pred_value_comp[pl]; + } + + LowPassForIntra8x8Pred(PredPel, block_available_up_left, block_available_up, block_available_left); + + pred_pels = PredArray; + + *pred_pels++ = (imgpel) ((P_X + P_W + 1) >> 1); + *pred_pels++ = (imgpel) ((P_V + P_X + (P_W << 1) + 2) >> 2); + *pred_pels++ = (imgpel) ((P_W + P_V + 1) >> 1); + *pred_pels++ = (imgpel) ((P_U + P_W + ((P_V) << 1) + 2) >> 2); + *pred_pels++ = (imgpel) ((P_V + P_U + 1) >> 1); + *pred_pels++ = (imgpel) ((P_T + P_V + ((P_U) << 1) + 2) >> 2); + *pred_pels++ = (imgpel) ((P_U + P_T + 1) >> 1); + *pred_pels++ = (imgpel) ((P_S + P_U + ((P_T) << 1) + 2) >> 2); + *pred_pels++ = (imgpel) ((P_T + P_S + 1) >> 1); + *pred_pels++ = (imgpel) ((P_R + P_T + ((P_S) << 1) + 2) >> 2); + *pred_pels++ = (imgpel) ((P_S + P_R + 1) >> 1); + *pred_pels++ = (imgpel) ((P_Q + P_S + ((P_R) << 1) + 2) >> 2); + *pred_pels++ = (imgpel) ((P_R + P_Q + 1) >> 1); + *pred_pels++ = (imgpel) ((P_Z + P_R + ((P_Q) << 1) + 2) >> 2); + *pred_pels++ = (imgpel) ((P_Q + P_Z + 1) >> 1); + *pred_pels++ = (imgpel) ((P_Q + P_A + ((P_Z) << 1) + 2) >> 2); + *pred_pels++ = (imgpel) ((P_Z + P_B + ((P_A) << 1) + 2) >> 2); + *pred_pels++ = (imgpel) ((P_A + P_C + ((P_B) << 1) + 2) >> 2); + *pred_pels++ = (imgpel) ((P_B + P_D + ((P_C) << 1) + 2) >> 2); + *pred_pels++ = (imgpel) ((P_C + P_E + ((P_D) << 1) + 2) >> 2); + *pred_pels++ = (imgpel) ((P_D + P_F + ((P_E) << 1) + 2) >> 2); + *pred_pels = (imgpel) ((P_E + P_G + ((P_F) << 1) + 2) >> 2); + + pred_pels = &PredArray[14]; + memcpy((*mb_pred++) + ioff, pred_pels, 8 * sizeof(imgpel)); + pred_pels -= 2; + memcpy((*mb_pred++) + ioff, pred_pels, 8 * sizeof(imgpel)); + pred_pels -= 2; + memcpy((*mb_pred++) + ioff, pred_pels, 8 * sizeof(imgpel)); + pred_pels -= 2; + memcpy((*mb_pred++) + ioff, pred_pels, 8 * sizeof(imgpel)); + pred_pels -= 2; + memcpy((*mb_pred++) + ioff, pred_pels, 8 * sizeof(imgpel)); + pred_pels -= 2; + memcpy((*mb_pred++) + ioff, pred_pels, 8 * sizeof(imgpel)); + pred_pels -= 2; + memcpy((*mb_pred++) + ioff, pred_pels, 8 * sizeof(imgpel)); + pred_pels -= 2; + memcpy((*mb_pred ) + ioff, pred_pels, 8 * sizeof(imgpel)); + + return DECODING_OK; +} + +/*! + ************************************************************************ + * \brief + * Make intra 8x8 prediction according to all 9 prediction modes. + * The routine uses left and upper neighbouring points from + * previous coded blocks to do this (if available). Notice that + * inaccessible neighbouring points are signalled with a negative + * value in the predmode array . + * + * \par Input: + * Starting point of current 8x8 block image position + * + ************************************************************************ + */ +int intra_pred_8x8_normal(Macroblock *currMB, //!< Current Macroblock + ColorPlane pl, //!< Current color plane + int ioff, //!< ioff + int joff) //!< joff + +{ + int block_x = (currMB->block_x) + (ioff >> 2); + int block_y = (currMB->block_y) + (joff >> 2); + byte predmode = currMB->p_Slice->ipredmode[block_y][block_x]; + + currMB->ipmode_DPCM = predmode; //For residual DPCM + + switch (predmode) + { + case DC_PRED: + return (intra8x8_dc_pred(currMB, pl, ioff, joff)); + break; + case VERT_PRED: + return (intra8x8_vert_pred(currMB, pl, ioff, joff)); + break; + case HOR_PRED: + return (intra8x8_hor_pred(currMB, pl, ioff, joff)); + break; + case DIAG_DOWN_RIGHT_PRED: + return (intra8x8_diag_down_right_pred(currMB, pl, ioff, joff)); + break; + case DIAG_DOWN_LEFT_PRED: + return (intra8x8_diag_down_left_pred(currMB, pl, ioff, joff)); + break; + case VERT_RIGHT_PRED: + return (intra8x8_vert_right_pred(currMB, pl, ioff, joff)); + break; + case VERT_LEFT_PRED: + return (intra8x8_vert_left_pred(currMB, pl, ioff, joff)); + break; + case HOR_UP_PRED: + return (intra8x8_hor_up_pred(currMB, pl, ioff, joff)); + break; + case HOR_DOWN_PRED: + return (intra8x8_hor_down_pred(currMB, pl, ioff, joff)); + default: + printf("Error: illegal intra_8x8 prediction mode: %d\n", (int) predmode); + return SEARCH_SYNC; + break; + } +} + + diff --git a/ldecod/src/intra8x8_pred_mbaff.c b/ldecod/src/intra8x8_pred_mbaff.c new file mode 100644 index 0000000..f599415 --- /dev/null +++ b/ldecod/src/intra8x8_pred_mbaff.c @@ -0,0 +1,1571 @@ +/*! + ************************************************************************************* + * \file intra8x8_pred.c + * + * \brief + * Functions for intra 8x8 prediction + * + * \author + * Main contributors (see contributors.h for copyright, + * address and affiliation details) + * - Yuri Vatis + * - Jan Muenster + * - Alexis Michael Tourapis + * + ************************************************************************************* + */ +#include "global.h" +#include "intra8x8_pred.h" +#include "mb_access.h" +#include "image.h" + +// Notation for comments regarding prediction and predictors. +// The pels of the 8x8 block are labeled a..p. The predictor pels above +// are labeled A..H, from the left I..P, and from above left X, as follows: +// +// Z A B C D E F G H I J K L M N O P +// Q a1 b1 c1 d1 e1 f1 g1 h1 +// R a2 b2 c2 d2 e2 f2 g2 h2 +// S a3 b3 c3 d3 e3 f3 g3 h3 +// T a4 b4 c4 d4 e4 f4 g4 h4 +// U a5 b5 c5 d5 e5 f5 g5 h5 +// V a6 b6 c6 d6 e6 f6 g6 h6 +// W a7 b7 c7 d7 e7 f7 g7 h7 +// X a8 b8 c8 d8 e8 f8 g8 h8 + + +// Predictor array index definitions +#define P_Z (PredPel[0]) +#define P_A (PredPel[1]) +#define P_B (PredPel[2]) +#define P_C (PredPel[3]) +#define P_D (PredPel[4]) +#define P_E (PredPel[5]) +#define P_F (PredPel[6]) +#define P_G (PredPel[7]) +#define P_H (PredPel[8]) +#define P_I (PredPel[9]) +#define P_J (PredPel[10]) +#define P_K (PredPel[11]) +#define P_L (PredPel[12]) +#define P_M (PredPel[13]) +#define P_N (PredPel[14]) +#define P_O (PredPel[15]) +#define P_P (PredPel[16]) +#define P_Q (PredPel[17]) +#define P_R (PredPel[18]) +#define P_S (PredPel[19]) +#define P_T (PredPel[20]) +#define P_U (PredPel[21]) +#define P_V (PredPel[22]) +#define P_W (PredPel[23]) +#define P_X (PredPel[24]) + +/*! + ************************************************************************************* + * \brief + * Prefiltering for Intra8x8 prediction + ************************************************************************************* + */ +static inline void LowPassForIntra8x8Pred(imgpel *PredPel, int block_up_left, int block_up, int block_left) +{ + int i; + imgpel LoopArray[25]; + + memcpy(&LoopArray[0], &PredPel[0], 25 * sizeof(imgpel)); + + if(block_up_left) + { + if(block_up && block_left) + { + LoopArray[0] = (imgpel) ((P_Q + (P_Z<<1) + P_A + 2)>>2); + } + else + { + if(block_up) + LoopArray[0] = (imgpel) ((P_Z + (P_Z<<1) + P_A + 2)>>2); + else if (block_left) + LoopArray[0] = (imgpel) ((P_Z + (P_Z<<1) + P_Q + 2)>>2); + } + } + + if(block_up) + { + if(block_up_left) + { + LoopArray[1] = (imgpel) ((PredPel[0] + (PredPel[1]<<1) + PredPel[2] + 2)>>2); + } + else + LoopArray[1] = (imgpel) ((PredPel[1] + (PredPel[1]<<1) + PredPel[2] + 2)>>2); + + + for(i = 2; i <16; i++) + { + LoopArray[i] = (imgpel) ((PredPel[i-1] + (PredPel[i]<<1) + PredPel[i+1] + 2)>>2); + } + LoopArray[16] = (imgpel) ((P_P + (P_P<<1) + P_O + 2)>>2); + } + + if(block_left) + { + if(block_up_left) + LoopArray[17] = (imgpel) ((P_Z + (P_Q<<1) + P_R + 2)>>2); + else + LoopArray[17] = (imgpel) ((P_Q + (P_Q<<1) + P_R + 2)>>2); + + for(i = 18; i <24; i++) + { + LoopArray[i] = (imgpel) ((PredPel[i-1] + (PredPel[i]<<1) + PredPel[i+1] + 2)>>2); + } + LoopArray[24] = (imgpel) ((P_W + (P_X<<1) + P_X + 2) >> 2); + } + + memcpy(&PredPel[0], &LoopArray[0], 25 * sizeof(imgpel)); +} + +/*! + ************************************************************************************* + * \brief + * Prefiltering for Intra8x8 prediction (Horizontal) + ************************************************************************************* + */ +static inline void LowPassForIntra8x8PredHor(imgpel *PredPel, int block_up_left, int block_up, int block_left) +{ + int i; + imgpel LoopArray[25]; + + memcpy(&LoopArray[0], &PredPel[0], 25 * sizeof(imgpel)); + + if(block_up_left) + { + if(block_up && block_left) + { + LoopArray[0] = (imgpel) ((P_Q + (P_Z<<1) + P_A + 2)>>2); + } + else + { + if(block_up) + LoopArray[0] = (imgpel) ((P_Z + (P_Z<<1) + P_A + 2)>>2); + else if (block_left) + LoopArray[0] = (imgpel) ((P_Z + (P_Z<<1) + P_Q + 2)>>2); + } + } + + if(block_up) + { + if(block_up_left) + { + LoopArray[1] = (imgpel) ((PredPel[0] + (PredPel[1]<<1) + PredPel[2] + 2)>>2); + } + else + LoopArray[1] = (imgpel) ((PredPel[1] + (PredPel[1]<<1) + PredPel[2] + 2)>>2); + + + for(i = 2; i <16; i++) + { + LoopArray[i] = (imgpel) ((PredPel[i-1] + (PredPel[i]<<1) + PredPel[i+1] + 2)>>2); + } + LoopArray[16] = (imgpel) ((P_P + (P_P<<1) + P_O + 2)>>2); + } + + + memcpy(&PredPel[0], &LoopArray[0], 17 * sizeof(imgpel)); +} + +/*! + ************************************************************************************* + * \brief + * Prefiltering for Intra8x8 prediction (Vertical) + ************************************************************************************* + */ +static inline void LowPassForIntra8x8PredVer(imgpel *PredPel, int block_up_left, int block_up, int block_left) +{ + // These functions need some cleanup and can be further optimized. + // For convenience, let us copy all data for now. It is obvious that the filtering makes things a bit more "complex" + int i; + imgpel LoopArray[25]; + + memcpy(&LoopArray[0], &PredPel[0], 25 * sizeof(imgpel)); + + if(block_up_left) + { + if(block_up && block_left) + { + LoopArray[0] = (imgpel) ((P_Q + (P_Z<<1) + P_A + 2)>>2); + } + else + { + if(block_up) + LoopArray[0] = (imgpel) ((P_Z + (P_Z<<1) + P_A + 2)>>2); + else if (block_left) + LoopArray[0] = (imgpel) ((P_Z + (P_Z<<1) + P_Q + 2)>>2); + } + } + + if(block_left) + { + if(block_up_left) + LoopArray[17] = (imgpel) ((P_Z + (P_Q<<1) + P_R + 2)>>2); + else + LoopArray[17] = (imgpel) ((P_Q + (P_Q<<1) + P_R + 2)>>2); + + for(i = 18; i <24; i++) + { + LoopArray[i] = (imgpel) ((PredPel[i-1] + (PredPel[i]<<1) + PredPel[i+1] + 2)>>2); + } + LoopArray[24] = (imgpel) ((P_W + (P_X<<1) + P_X + 2) >> 2); + } + + memcpy(&PredPel[0], &LoopArray[0], 25 * sizeof(imgpel)); +} + + +/*! + *********************************************************************** + * \brief + * makes and returns 8x8 DC prediction mode + * + * \return + * DECODING_OK decoding of intra_prediction mode was successful \n + * + *********************************************************************** + */ +static inline int intra8x8_dc_pred_mbaff(Macroblock *currMB, //!< current macroblock + ColorPlane pl, //!< current image plane + int ioff, //!< pixel offset X within MB + int joff) //!< pixel offset Y within MB +{ + int i,j; + int s0 = 0; + imgpel PredPel[25]; // array of predictor pels + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + + StorablePicture *dec_picture = currSlice->dec_picture; + imgpel **imgY = (pl) ? dec_picture->imgUV[pl - 1] : dec_picture->imgY; // For MB level frame/field coding tools -- set default to imgY + + PixelPos pix_a[8]; + PixelPos pix_b, pix_c, pix_d; + + int block_available_up; + int block_available_left; + int block_available_up_left; + int block_available_up_right; + + + imgpel **mpr = currSlice->mb_pred[pl]; + int *mb_size = p_Vid->mb_size[IS_LUMA]; + + for (i=0;i<8;i++) + { + getAffNeighbour(currMB, ioff - 1, joff + i, mb_size, &pix_a[i]); + } + + getAffNeighbour(currMB, ioff , joff - 1, mb_size, &pix_b); + getAffNeighbour(currMB, ioff + 8, joff - 1, mb_size, &pix_c); + getAffNeighbour(currMB, ioff - 1, joff - 1, mb_size, &pix_d); + + pix_c.available = pix_c.available &&!(ioff == 8 && joff == 8); + + if (p_Vid->active_pps->constrained_intra_pred_flag) + { + for (i=0, block_available_left=1; i<8;i++) + block_available_left &= pix_a[i].available ? currSlice->intra_block[pix_a[i].mb_addr]: 0; + block_available_up = pix_b.available ? currSlice->intra_block [pix_b.mb_addr] : 0; + block_available_up_right = pix_c.available ? currSlice->intra_block [pix_c.mb_addr] : 0; + block_available_up_left = pix_d.available ? currSlice->intra_block [pix_d.mb_addr] : 0; + } + else + { + block_available_left = pix_a[0].available; + block_available_up = pix_b.available; + block_available_up_right = pix_c.available; + block_available_up_left = pix_d.available; + } + + // form predictor pels + if (block_available_up) + { + memcpy(&PredPel[1], &imgY[pix_b.pos_y][pix_b.pos_x], BLOCK_SIZE_8x8 * sizeof(imgpel)); + } + else + { +#if (IMGTYPE == 0) + memset(&PredPel[1], p_Vid->dc_pred_value_comp[pl], BLOCK_SIZE_8x8 * sizeof(imgpel)); +#else + P_A = P_B = P_C = P_D = P_E = P_F = P_G = P_H = (imgpel) p_Vid->dc_pred_value_comp[pl]; +#endif + } + + if (block_available_up_right) + { + memcpy(&PredPel[9], &imgY[pix_c.pos_y][pix_c.pos_x], BLOCK_SIZE_8x8 * sizeof(imgpel)); + } + else + { +#if (IMGTYPE == 0) + memset(&PredPel[9], PredPel[8], BLOCK_SIZE_8x8 * sizeof(imgpel)); +#else + P_I = P_J = P_K = P_L = P_M = P_N = P_O = P_P = P_H; +#endif + } + + if (block_available_left) + { + P_Q = imgY[pix_a[0].pos_y][pix_a[0].pos_x]; + P_R = imgY[pix_a[1].pos_y][pix_a[1].pos_x]; + P_S = imgY[pix_a[2].pos_y][pix_a[2].pos_x]; + P_T = imgY[pix_a[3].pos_y][pix_a[3].pos_x]; + P_U = imgY[pix_a[4].pos_y][pix_a[4].pos_x]; + P_V = imgY[pix_a[5].pos_y][pix_a[5].pos_x]; + P_W = imgY[pix_a[6].pos_y][pix_a[6].pos_x]; + P_X = imgY[pix_a[7].pos_y][pix_a[7].pos_x]; + } + else + { + P_Q = P_R = P_S = P_T = P_U = P_V = P_W = P_X = (imgpel) p_Vid->dc_pred_value_comp[pl]; + } + + if (block_available_up_left) + { + P_Z = imgY[pix_d.pos_y][pix_d.pos_x]; + } + else + { + P_Z = (imgpel) p_Vid->dc_pred_value_comp[pl]; + } + + LowPassForIntra8x8Pred(PredPel, block_available_up_left, block_available_up, block_available_left); + + if (block_available_up && block_available_left) + { + // no edge + s0 = (P_A + P_B + P_C + P_D + P_E + P_F + P_G + P_H + P_Q + P_R + P_S + P_T + P_U + P_V + P_W + P_X + 8) >> 4; + } + else if (!block_available_up && block_available_left) + { + // upper edge + s0 = (P_Q + P_R + P_S + P_T + P_U + P_V + P_W + P_X + 4) >> 3; + } + else if (block_available_up && !block_available_left) + { + // left edge + s0 = (P_A + P_B + P_C + P_D + P_E + P_F + P_G + P_H + 4) >> 3; + } + else //if (!block_available_up && !block_available_left) + { + // top left corner, nothing to predict from + s0 = p_Vid->dc_pred_value_comp[pl]; + } + + for(j = joff; j < joff + BLOCK_SIZE_8x8; j++) + for(i = ioff; i < ioff + BLOCK_SIZE_8x8; i++) + mpr[j][i] = (imgpel) s0; + + return DECODING_OK; +} + +/*! + *********************************************************************** + * \brief + * makes and returns 8x8 vertical prediction mode + * + * \return + * DECODING_OK decoding of intra_prediction mode was successful \n + * + *********************************************************************** + */ +static inline int intra8x8_vert_pred_mbaff(Macroblock *currMB, //!< current macroblock + ColorPlane pl, //!< current image plane + int ioff, //!< pixel offset X within MB + int joff) //!< pixel offset Y within MB +{ + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + + int i; + imgpel PredPel[25]; // array of predictor pels + imgpel **imgY = (pl) ? currSlice->dec_picture->imgUV[pl - 1] : currSlice->dec_picture->imgY; // For MB level frame/field coding tools -- set default to imgY + + PixelPos pix_a[8]; + PixelPos pix_b, pix_c, pix_d; + + int block_available_up; + int block_available_left; + int block_available_up_left; + int block_available_up_right; + + + imgpel **mpr = currSlice->mb_pred[pl]; + int *mb_size = p_Vid->mb_size[IS_LUMA]; + + for (i=0;i<8;i++) + { + getAffNeighbour(currMB, ioff - 1, joff + i, mb_size, &pix_a[i]); + } + + getAffNeighbour(currMB, ioff , joff - 1, mb_size, &pix_b); + getAffNeighbour(currMB, ioff + 8, joff - 1, mb_size, &pix_c); + getAffNeighbour(currMB, ioff - 1, joff - 1, mb_size, &pix_d); + + pix_c.available = pix_c.available &&!(ioff == 8 && joff == 8); + + if (p_Vid->active_pps->constrained_intra_pred_flag) + { + for (i=0, block_available_left=1; i<8;i++) + block_available_left &= pix_a[i].available ? currSlice->intra_block[pix_a[i].mb_addr]: 0; + block_available_up = pix_b.available ? currSlice->intra_block [pix_b.mb_addr] : 0; + block_available_up_right = pix_c.available ? currSlice->intra_block [pix_c.mb_addr] : 0; + block_available_up_left = pix_d.available ? currSlice->intra_block [pix_d.mb_addr] : 0; + } + else + { + block_available_left = pix_a[0].available; + block_available_up = pix_b.available; + block_available_up_right = pix_c.available; + block_available_up_left = pix_d.available; + } + + if (!block_available_up) + printf ("warning: Intra_8x8_Vertical prediction mode not allowed at mb %d\n", (int) currSlice->current_mb_nr); + + // form predictor pels + if (block_available_up) + { + memcpy(&PredPel[1], &imgY[pix_b.pos_y][pix_b.pos_x], BLOCK_SIZE_8x8 * sizeof(imgpel)); + } + else + { +#if (IMGTYPE == 0) + memset(&PredPel[1], p_Vid->dc_pred_value_comp[pl], BLOCK_SIZE_8x8 * sizeof(imgpel)); +#else + P_A = P_B = P_C = P_D = P_E = P_F = P_G = P_H = (imgpel) p_Vid->dc_pred_value_comp[pl]; +#endif + } + + if (block_available_up_right) + { + memcpy(&PredPel[9], &imgY[pix_c.pos_y][pix_c.pos_x], BLOCK_SIZE_8x8 * sizeof(imgpel)); + } + else + { +#if (IMGTYPE == 0) + memset(&PredPel[9], PredPel[8], BLOCK_SIZE_8x8 * sizeof(imgpel)); +#else + P_I = P_J = P_K = P_L = P_M = P_N = P_O = P_P = P_H; +#endif + } + + if (block_available_up_left) + { + P_Z = imgY[pix_d.pos_y][pix_d.pos_x]; + } + else + { + P_Z = (imgpel) p_Vid->dc_pred_value_comp[pl]; + } + + LowPassForIntra8x8PredHor(&(P_Z), block_available_up_left, block_available_up, block_available_left); + + for (i=joff; i < joff + BLOCK_SIZE_8x8; i++) + { + memcpy(&mpr[i][ioff], &PredPel[1], BLOCK_SIZE_8x8 * sizeof(imgpel)); + } + + return DECODING_OK; +} + +/*! + *********************************************************************** + * \brief + * makes and returns 8x8 horizontal prediction mode + * + * \return + * DECODING_OK decoding of intra_prediction mode was successful \n + * + *********************************************************************** + */ +static inline int intra8x8_hor_pred_mbaff(Macroblock *currMB, //!< current macroblock + ColorPlane pl, //!< current image plane + int ioff, //!< pixel offset X within MB + int joff) //!< pixel offset Y within MB +{ + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + + + int i,j; + imgpel PredPel[25]; // array of predictor pels + imgpel **imgY = (pl) ? currSlice->dec_picture->imgUV[pl - 1] : currSlice->dec_picture->imgY; // For MB level frame/field coding tools -- set default to imgY + + PixelPos pix_a[8]; + PixelPos pix_b, pix_c, pix_d; + + int block_available_up; + int block_available_left; + int block_available_up_left; + +#if (IMGTYPE != 0) + int ipos0 = ioff , ipos1 = ioff + 1, ipos2 = ioff + 2, ipos3 = ioff + 3; + int ipos4 = ioff + 4, ipos5 = ioff + 5, ipos6 = ioff + 6, ipos7 = ioff + 7; +#endif + int jpos; + imgpel **mpr = currSlice->mb_pred[pl]; + int *mb_size = p_Vid->mb_size[IS_LUMA]; + + for (i=0;i<8;i++) + { + getAffNeighbour(currMB, ioff - 1, joff + i, mb_size, &pix_a[i]); + } + + getAffNeighbour(currMB, ioff , joff - 1, mb_size, &pix_b); + getAffNeighbour(currMB, ioff + 8, joff - 1, mb_size, &pix_c); + getAffNeighbour(currMB, ioff - 1, joff - 1, mb_size, &pix_d); + + pix_c.available = pix_c.available &&!(ioff == 8 && joff == 8); + + if (p_Vid->active_pps->constrained_intra_pred_flag) + { + for (i=0, block_available_left=1; i<8;i++) + block_available_left &= pix_a[i].available ? currSlice->intra_block[pix_a[i].mb_addr]: 0; + block_available_up = pix_b.available ? currSlice->intra_block [pix_b.mb_addr] : 0; + block_available_up_left = pix_d.available ? currSlice->intra_block [pix_d.mb_addr] : 0; + } + else + { + block_available_left = pix_a[0].available; + block_available_up = pix_b.available; + block_available_up_left = pix_d.available; + } + + if (!block_available_left) + printf ("warning: Intra_8x8_Horizontal prediction mode not allowed at mb %d\n", (int) currSlice->current_mb_nr); + + // form predictor pels + if (block_available_left) + { + P_Q = imgY[pix_a[0].pos_y][pix_a[0].pos_x]; + P_R = imgY[pix_a[1].pos_y][pix_a[1].pos_x]; + P_S = imgY[pix_a[2].pos_y][pix_a[2].pos_x]; + P_T = imgY[pix_a[3].pos_y][pix_a[3].pos_x]; + P_U = imgY[pix_a[4].pos_y][pix_a[4].pos_x]; + P_V = imgY[pix_a[5].pos_y][pix_a[5].pos_x]; + P_W = imgY[pix_a[6].pos_y][pix_a[6].pos_x]; + P_X = imgY[pix_a[7].pos_y][pix_a[7].pos_x]; + } + else + { + P_Q = P_R = P_S = P_T = P_U = P_V = P_W = P_X = (imgpel) p_Vid->dc_pred_value_comp[pl]; + } + + if (block_available_up_left) + { + P_Z = imgY[pix_d.pos_y][pix_d.pos_x]; + } + else + { + P_Z = (imgpel) p_Vid->dc_pred_value_comp[pl]; + } + + LowPassForIntra8x8PredVer(&(P_Z), block_available_up_left, block_available_up, block_available_left); + + for (j=0; j < BLOCK_SIZE_8x8; j++) + { + jpos = j + joff; +#if (IMGTYPE == 0) + memset(&mpr[jpos][ioff], (imgpel) (&P_Q)[j], 8 * sizeof(imgpel)); +#else + mpr[jpos][ipos0] = + mpr[jpos][ipos1] = + mpr[jpos][ipos2] = + mpr[jpos][ipos3] = + mpr[jpos][ipos4] = + mpr[jpos][ipos5] = + mpr[jpos][ipos6] = + mpr[jpos][ipos7] = (imgpel) (&P_Q)[j]; +#endif + } + + return DECODING_OK; +} + + /*! + *********************************************************************** + * \brief + * makes and returns 8x8 diagonal down right prediction mode + * + * \return + * DECODING_OK decoding of intra prediction mode was successful \n + * + *********************************************************************** + */ +static inline int intra8x8_diag_down_right_pred_mbaff(Macroblock *currMB, //!< current macroblock + ColorPlane pl, //!< current image plane + int ioff, //!< pixel offset X within MB + int joff) //!< pixel offset Y within MB +{ + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + + + int i; + imgpel PredPel[25]; // array of predictor pels + imgpel PredArray[16]; // array of final prediction values + imgpel **imgY = (pl) ? currSlice->dec_picture->imgUV[pl - 1] : currSlice->dec_picture->imgY; // For MB level frame/field coding tools -- set default to imgY + + PixelPos pix_a[8]; + PixelPos pix_b, pix_c, pix_d; + + int block_available_up; + int block_available_left; + int block_available_up_left; + int block_available_up_right; + + imgpel **mpr = currSlice->mb_pred[pl]; + int *mb_size = p_Vid->mb_size[IS_LUMA]; + + for (i=0;i<8;i++) + { + getAffNeighbour(currMB, ioff - 1, joff + i, mb_size, &pix_a[i]); + } + + getAffNeighbour(currMB, ioff , joff - 1, mb_size, &pix_b); + getAffNeighbour(currMB, ioff + 8, joff - 1, mb_size, &pix_c); + getAffNeighbour(currMB, ioff - 1, joff - 1, mb_size, &pix_d); + + pix_c.available = pix_c.available &&!(ioff == 8 && joff == 8); + + if (p_Vid->active_pps->constrained_intra_pred_flag) + { + for (i=0, block_available_left=1; i<8;i++) + block_available_left &= pix_a[i].available ? currSlice->intra_block[pix_a[i].mb_addr]: 0; + block_available_up = pix_b.available ? currSlice->intra_block [pix_b.mb_addr] : 0; + block_available_up_right = pix_c.available ? currSlice->intra_block [pix_c.mb_addr] : 0; + block_available_up_left = pix_d.available ? currSlice->intra_block [pix_d.mb_addr] : 0; + } + else + { + block_available_left = pix_a[0].available; + block_available_up = pix_b.available; + block_available_up_right = pix_c.available; + block_available_up_left = pix_d.available; + } + + if ((!block_available_up)||(!block_available_left)||(!block_available_up_left)) + printf ("warning: Intra_8x8_Diagonal_Down_Right prediction mode not allowed at mb %d\n", (int) currSlice->current_mb_nr); + + // form predictor pels + if (block_available_up) + { + memcpy(&PredPel[1], &imgY[pix_b.pos_y][pix_b.pos_x], BLOCK_SIZE_8x8 * sizeof(imgpel)); + } + else + { +#if (IMGTYPE == 0) + memset(&PredPel[1], p_Vid->dc_pred_value_comp[pl], BLOCK_SIZE_8x8 * sizeof(imgpel)); +#else + P_A = P_B = P_C = P_D = P_E = P_F = P_G = P_H = (imgpel) p_Vid->dc_pred_value_comp[pl]; +#endif + } + + if (block_available_up_right) + { + memcpy(&PredPel[9], &imgY[pix_c.pos_y][pix_c.pos_x], BLOCK_SIZE_8x8 * sizeof(imgpel)); + } + else + { +#if (IMGTYPE == 0) + memset(&PredPel[9], PredPel[8], BLOCK_SIZE_8x8 * sizeof(imgpel)); +#else + P_I = P_J = P_K = P_L = P_M = P_N = P_O = P_P = P_H; +#endif + } + + if (block_available_left) + { + P_Q = imgY[pix_a[0].pos_y][pix_a[0].pos_x]; + P_R = imgY[pix_a[1].pos_y][pix_a[1].pos_x]; + P_S = imgY[pix_a[2].pos_y][pix_a[2].pos_x]; + P_T = imgY[pix_a[3].pos_y][pix_a[3].pos_x]; + P_U = imgY[pix_a[4].pos_y][pix_a[4].pos_x]; + P_V = imgY[pix_a[5].pos_y][pix_a[5].pos_x]; + P_W = imgY[pix_a[6].pos_y][pix_a[6].pos_x]; + P_X = imgY[pix_a[7].pos_y][pix_a[7].pos_x]; + } + else + { + P_Q = P_R = P_S = P_T = P_U = P_V = P_W = P_X = (imgpel) p_Vid->dc_pred_value_comp[pl]; + } + + if (block_available_up_left) + { + P_Z = imgY[pix_d.pos_y][pix_d.pos_x]; + } + else + { + P_Z = (imgpel) p_Vid->dc_pred_value_comp[pl]; + } + + LowPassForIntra8x8Pred(PredPel, block_available_up_left, block_available_up, block_available_left); + + // Mode DIAG_DOWN_RIGHT_PRED + PredArray[ 0] = (imgpel) ((P_X + P_V + 2*(P_W) + 2) >> 2); + PredArray[ 1] = (imgpel) ((P_W + P_U + 2*(P_V) + 2) >> 2); + PredArray[ 2] = (imgpel) ((P_V + P_T + 2*(P_U) + 2) >> 2); + PredArray[ 3] = (imgpel) ((P_U + P_S + 2*(P_T) + 2) >> 2); + PredArray[ 4] = (imgpel) ((P_T + P_R + 2*(P_S) + 2) >> 2); + PredArray[ 5] = (imgpel) ((P_S + P_Q + 2*(P_R) + 2) >> 2); + PredArray[ 6] = (imgpel) ((P_R + P_Z + 2*(P_Q) + 2) >> 2); + PredArray[ 7] = (imgpel) ((P_Q + P_A + 2*(P_Z) + 2) >> 2); + PredArray[ 8] = (imgpel) ((P_Z + P_B + 2*(P_A) + 2) >> 2); + PredArray[ 9] = (imgpel) ((P_A + P_C + 2*(P_B) + 2) >> 2); + PredArray[10] = (imgpel) ((P_B + P_D + 2*(P_C) + 2) >> 2); + PredArray[11] = (imgpel) ((P_C + P_E + 2*(P_D) + 2) >> 2); + PredArray[12] = (imgpel) ((P_D + P_F + 2*(P_E) + 2) >> 2); + PredArray[13] = (imgpel) ((P_E + P_G + 2*(P_F) + 2) >> 2); + PredArray[14] = (imgpel) ((P_F + P_H + 2*(P_G) + 2) >> 2); + + memcpy(&mpr[joff++][ioff], &PredArray[7], 8 * sizeof(imgpel)); + memcpy(&mpr[joff++][ioff], &PredArray[6], 8 * sizeof(imgpel)); + memcpy(&mpr[joff++][ioff], &PredArray[5], 8 * sizeof(imgpel)); + memcpy(&mpr[joff++][ioff], &PredArray[4], 8 * sizeof(imgpel)); + memcpy(&mpr[joff++][ioff], &PredArray[3], 8 * sizeof(imgpel)); + memcpy(&mpr[joff++][ioff], &PredArray[2], 8 * sizeof(imgpel)); + memcpy(&mpr[joff++][ioff], &PredArray[1], 8 * sizeof(imgpel)); + memcpy(&mpr[joff ][ioff], &PredArray[0], 8 * sizeof(imgpel)); + + return DECODING_OK; +} + +/*! + *********************************************************************** + * \brief + * makes and returns 8x8 diagonal down left prediction mode + * + * \return + * DECODING_OK decoding of intra prediction mode was successful \n + * + *********************************************************************** + */ +static inline int intra8x8_diag_down_left_pred_mbaff(Macroblock *currMB, //!< current macroblock + ColorPlane pl, //!< current image plane + int ioff, //!< pixel offset X within MB + int joff) //!< pixel offset Y within MB +{ + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + + int i; + imgpel PredPel[25]; // array of predictor pels + imgpel PredArray[16]; // array of final prediction values + imgpel *Pred = &PredArray[0]; + imgpel **imgY = (pl) ? currSlice->dec_picture->imgUV[pl - 1] : currSlice->dec_picture->imgY; // For MB level frame/field coding tools -- set default to imgY + + PixelPos pix_a[8]; + PixelPos pix_b, pix_c, pix_d; + + int block_available_up; + int block_available_left; + int block_available_up_left; + int block_available_up_right; + + imgpel **mpr = currSlice->mb_pred[pl]; + int *mb_size = p_Vid->mb_size[IS_LUMA]; + + for (i=0;i<8;i++) + { + getAffNeighbour(currMB, ioff - 1, joff + i, mb_size, &pix_a[i]); + } + + getAffNeighbour(currMB, ioff , joff - 1, mb_size, &pix_b); + getAffNeighbour(currMB, ioff + 8, joff - 1, mb_size, &pix_c); + getAffNeighbour(currMB, ioff - 1, joff - 1, mb_size, &pix_d); + + pix_c.available = pix_c.available &&!(ioff == 8 && joff == 8); + + if (p_Vid->active_pps->constrained_intra_pred_flag) + { + for (i=0, block_available_left=1; i<8;i++) + block_available_left &= pix_a[i].available ? currSlice->intra_block[pix_a[i].mb_addr]: 0; + block_available_up = pix_b.available ? currSlice->intra_block [pix_b.mb_addr] : 0; + block_available_up_right = pix_c.available ? currSlice->intra_block [pix_c.mb_addr] : 0; + block_available_up_left = pix_d.available ? currSlice->intra_block [pix_d.mb_addr] : 0; + } + else + { + block_available_left = pix_a[0].available; + block_available_up = pix_b.available; + block_available_up_right = pix_c.available; + block_available_up_left = pix_d.available; + } + + if (!block_available_up) + printf ("warning: Intra_8x8_Diagonal_Down_Left prediction mode not allowed at mb %d\n", (int) currSlice->current_mb_nr); + + // form predictor pels + if (block_available_up) + { + memcpy(&PredPel[1], &imgY[pix_b.pos_y][pix_b.pos_x], BLOCK_SIZE_8x8 * sizeof(imgpel)); + } + else + { +#if (IMGTYPE == 0) + memset(&PredPel[1], p_Vid->dc_pred_value_comp[pl], BLOCK_SIZE_8x8 * sizeof(imgpel)); +#else + P_A = P_B = P_C = P_D = P_E = P_F = P_G = P_H = (imgpel) p_Vid->dc_pred_value_comp[pl]; +#endif + } + + if (block_available_up_right) + { + memcpy(&PredPel[9], &imgY[pix_c.pos_y][pix_c.pos_x], BLOCK_SIZE_8x8 * sizeof(imgpel)); + } + else + { +#if (IMGTYPE == 0) + memset(&PredPel[9], PredPel[8], BLOCK_SIZE_8x8 * sizeof(imgpel)); +#else + P_I = P_J = P_K = P_L = P_M = P_N = P_O = P_P = P_H; +#endif + } + + if (block_available_left) + { + P_Q = imgY[pix_a[0].pos_y][pix_a[0].pos_x]; + P_R = imgY[pix_a[1].pos_y][pix_a[1].pos_x]; + P_S = imgY[pix_a[2].pos_y][pix_a[2].pos_x]; + P_T = imgY[pix_a[3].pos_y][pix_a[3].pos_x]; + P_U = imgY[pix_a[4].pos_y][pix_a[4].pos_x]; + P_V = imgY[pix_a[5].pos_y][pix_a[5].pos_x]; + P_W = imgY[pix_a[6].pos_y][pix_a[6].pos_x]; + P_X = imgY[pix_a[7].pos_y][pix_a[7].pos_x]; + } + else + { + P_Q = P_R = P_S = P_T = P_U = P_V = P_W = P_X = (imgpel) p_Vid->dc_pred_value_comp[pl]; + } + + if (block_available_up_left) + { + P_Z = imgY[pix_d.pos_y][pix_d.pos_x]; + } + else + { + P_Z = (imgpel) p_Vid->dc_pred_value_comp[pl]; + } + + LowPassForIntra8x8Pred(PredPel, block_available_up_left, block_available_up, block_available_left); + + // Mode DIAG_DOWN_LEFT_PRED + *Pred++ = (imgpel) ((P_A + P_C + 2*(P_B) + 2) >> 2); + *Pred++ = (imgpel) ((P_B + P_D + 2*(P_C) + 2) >> 2); + *Pred++ = (imgpel) ((P_C + P_E + 2*(P_D) + 2) >> 2); + *Pred++ = (imgpel) ((P_D + P_F + 2*(P_E) + 2) >> 2); + *Pred++ = (imgpel) ((P_E + P_G + 2*(P_F) + 2) >> 2); + *Pred++ = (imgpel) ((P_F + P_H + 2*(P_G) + 2) >> 2); + *Pred++ = (imgpel) ((P_G + P_I + 2*(P_H) + 2) >> 2); + *Pred++ = (imgpel) ((P_H + P_J + 2*(P_I) + 2) >> 2); + *Pred++ = (imgpel) ((P_I + P_K + 2*(P_J) + 2) >> 2); + *Pred++ = (imgpel) ((P_J + P_L + 2*(P_K) + 2) >> 2); + *Pred++ = (imgpel) ((P_K + P_M + 2*(P_L) + 2) >> 2); + *Pred++ = (imgpel) ((P_L + P_N + 2*(P_M) + 2) >> 2); + *Pred++ = (imgpel) ((P_M + P_O + 2*(P_N) + 2) >> 2); + *Pred++ = (imgpel) ((P_N + P_P + 2*(P_O) + 2) >> 2); + *Pred = (imgpel) ((P_O + 3*(P_P) + 2) >> 2); + + Pred = &PredArray[ 0]; + + memcpy(&mpr[joff++][ioff], Pred++, 8 * sizeof(imgpel)); + memcpy(&mpr[joff++][ioff], Pred++, 8 * sizeof(imgpel)); + memcpy(&mpr[joff++][ioff], Pred++, 8 * sizeof(imgpel)); + memcpy(&mpr[joff++][ioff], Pred++, 8 * sizeof(imgpel)); + memcpy(&mpr[joff++][ioff], Pred++, 8 * sizeof(imgpel)); + memcpy(&mpr[joff++][ioff], Pred++, 8 * sizeof(imgpel)); + memcpy(&mpr[joff++][ioff], Pred++, 8 * sizeof(imgpel)); + memcpy(&mpr[joff ][ioff], Pred , 8 * sizeof(imgpel)); + + return DECODING_OK; +} + +/*! + *********************************************************************** + * \brief + * makes and returns 8x8 vertical right prediction mode + * + * \return + * DECODING_OK decoding of intra prediction mode was successful \n + * + *********************************************************************** + */ +static inline int intra8x8_vert_right_pred_mbaff(Macroblock *currMB, //!< current macroblock + ColorPlane pl, //!< current image plane + int ioff, //!< pixel offset X within MB + int joff) //!< pixel offset Y within MB +{ + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + + int i; + imgpel PredPel[25]; // array of predictor pels + imgpel PredArray[22]; // array of final prediction values + imgpel **imgY = (pl) ? currSlice->dec_picture->imgUV[pl - 1] : currSlice->dec_picture->imgY; // For MB level frame/field coding tools -- set default to imgY + + PixelPos pix_a[8]; + PixelPos pix_b, pix_c, pix_d; + + int block_available_up; + int block_available_left; + int block_available_up_left; + int block_available_up_right; + + imgpel **mpr = currSlice->mb_pred[pl]; + int *mb_size = p_Vid->mb_size[IS_LUMA]; + + for (i=0;i<8;i++) + { + getAffNeighbour(currMB, ioff - 1, joff + i, mb_size, &pix_a[i]); + } + + getAffNeighbour(currMB, ioff , joff - 1, mb_size, &pix_b); + getAffNeighbour(currMB, ioff + 8, joff - 1, mb_size, &pix_c); + getAffNeighbour(currMB, ioff - 1, joff - 1, mb_size, &pix_d); + + pix_c.available = pix_c.available &&!(ioff == 8 && joff == 8); + + if (p_Vid->active_pps->constrained_intra_pred_flag) + { + for (i=0, block_available_left=1; i<8;i++) + block_available_left &= pix_a[i].available ? currSlice->intra_block[pix_a[i].mb_addr]: 0; + block_available_up = pix_b.available ? currSlice->intra_block [pix_b.mb_addr] : 0; + block_available_up_right = pix_c.available ? currSlice->intra_block [pix_c.mb_addr] : 0; + block_available_up_left = pix_d.available ? currSlice->intra_block [pix_d.mb_addr] : 0; + } + else + { + block_available_left = pix_a[0].available; + block_available_up = pix_b.available; + block_available_up_right = pix_c.available; + block_available_up_left = pix_d.available; + } + + if ((!block_available_up)||(!block_available_left)||(!block_available_up_left)) + printf ("warning: Intra_8x8_Vertical_Right prediction mode not allowed at mb %d\n", (int) currSlice->current_mb_nr); + + // form predictor pels + if (block_available_up) + { + memcpy(&PredPel[1], &imgY[pix_b.pos_y][pix_b.pos_x], BLOCK_SIZE_8x8 * sizeof(imgpel)); + } + else + { +#if (IMGTYPE == 0) + memset(&PredPel[1], p_Vid->dc_pred_value_comp[pl], BLOCK_SIZE_8x8 * sizeof(imgpel)); +#else + P_A = P_B = P_C = P_D = P_E = P_F = P_G = P_H = (imgpel) p_Vid->dc_pred_value_comp[pl]; +#endif + } + + if (block_available_up_right) + { + memcpy(&PredPel[9], &imgY[pix_c.pos_y][pix_c.pos_x], BLOCK_SIZE_8x8 * sizeof(imgpel)); + } + else + { +#if (IMGTYPE == 0) + memset(&PredPel[9], PredPel[8], BLOCK_SIZE_8x8 * sizeof(imgpel)); +#else + P_I = P_J = P_K = P_L = P_M = P_N = P_O = P_P = P_H; +#endif + } + + if (block_available_left) + { + P_Q = imgY[pix_a[0].pos_y][pix_a[0].pos_x]; + P_R = imgY[pix_a[1].pos_y][pix_a[1].pos_x]; + P_S = imgY[pix_a[2].pos_y][pix_a[2].pos_x]; + P_T = imgY[pix_a[3].pos_y][pix_a[3].pos_x]; + P_U = imgY[pix_a[4].pos_y][pix_a[4].pos_x]; + P_V = imgY[pix_a[5].pos_y][pix_a[5].pos_x]; + P_W = imgY[pix_a[6].pos_y][pix_a[6].pos_x]; + P_X = imgY[pix_a[7].pos_y][pix_a[7].pos_x]; + } + else + { + P_Q = P_R = P_S = P_T = P_U = P_V = P_W = P_X = (imgpel) p_Vid->dc_pred_value_comp[pl]; + } + + if (block_available_up_left) + { + P_Z = imgY[pix_d.pos_y][pix_d.pos_x]; + } + else + { + P_Z = (imgpel) p_Vid->dc_pred_value_comp[pl]; + } + + LowPassForIntra8x8Pred(PredPel, block_available_up_left, block_available_up, block_available_left); + + PredArray[ 0] = (imgpel) ((P_V + P_T + (P_U << 1) + 2) >> 2); + PredArray[ 1] = (imgpel) ((P_T + P_R + (P_S << 1) + 2) >> 2); + PredArray[ 2] = (imgpel) ((P_R + P_Z + (P_Q << 1) + 2) >> 2); + PredArray[ 3] = (imgpel) ((P_Z + P_A + 1) >> 1); + PredArray[ 4] = (imgpel) ((P_A + P_B + 1) >> 1); + PredArray[ 5] = (imgpel) ((P_B + P_C + 1) >> 1); + PredArray[ 6] = (imgpel) ((P_C + P_D + 1) >> 1); + PredArray[ 7] = (imgpel) ((P_D + P_E + 1) >> 1); + PredArray[ 8] = (imgpel) ((P_E + P_F + 1) >> 1); + PredArray[ 9] = (imgpel) ((P_F + P_G + 1) >> 1); + PredArray[10] = (imgpel) ((P_G + P_H + 1) >> 1); + + PredArray[11] = (imgpel) ((P_W + P_U + (P_V << 1) + 2) >> 2); + PredArray[12] = (imgpel) ((P_U + P_S + (P_T << 1) + 2) >> 2); + PredArray[13] = (imgpel) ((P_S + P_Q + (P_R << 1) + 2) >> 2); + PredArray[14] = (imgpel) ((P_Q + P_A + 2*P_Z + 2) >> 2); + PredArray[15] = (imgpel) ((P_Z + P_B + 2*P_A + 2) >> 2); + PredArray[16] = (imgpel) ((P_A + P_C + 2*P_B + 2) >> 2); + PredArray[17] = (imgpel) ((P_B + P_D + 2*P_C + 2) >> 2); + PredArray[18] = (imgpel) ((P_C + P_E + 2*P_D + 2) >> 2); + PredArray[19] = (imgpel) ((P_D + P_F + 2*P_E + 2) >> 2); + PredArray[20] = (imgpel) ((P_E + P_G + 2*P_F + 2) >> 2); + PredArray[21] = (imgpel) ((P_F + P_H + 2*P_G + 2) >> 2); + + memcpy(&mpr[joff++][ioff], &PredArray[ 3], 8 * sizeof(imgpel)); + memcpy(&mpr[joff++][ioff], &PredArray[14], 8 * sizeof(imgpel)); + memcpy(&mpr[joff++][ioff], &PredArray[ 2], 8 * sizeof(imgpel)); + memcpy(&mpr[joff++][ioff], &PredArray[13], 8 * sizeof(imgpel)); + memcpy(&mpr[joff++][ioff], &PredArray[ 1], 8 * sizeof(imgpel)); + memcpy(&mpr[joff++][ioff], &PredArray[12], 8 * sizeof(imgpel)); + memcpy(&mpr[joff++][ioff], &PredArray[ 0], 8 * sizeof(imgpel)); + memcpy(&mpr[joff ][ioff], &PredArray[11], 8 * sizeof(imgpel)); + + return DECODING_OK; +} + + +/*! + *********************************************************************** + * \brief + * makes and returns 8x8 vertical left prediction mode + * + * \return + * DECODING_OK decoding of intra prediction mode was successful \n + * + *********************************************************************** + */ +static inline int intra8x8_vert_left_pred_mbaff(Macroblock *currMB, //!< current macroblock + ColorPlane pl, //!< current image plane + int ioff, //!< pixel offset X within MB + int joff) //!< pixel offset Y within MB +{ + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + + int i; + imgpel PredPel[25]; // array of predictor pels + imgpel PredArray[22]; // array of final prediction values + imgpel *pred_pel = &PredArray[0]; + imgpel **imgY = (pl) ? currSlice->dec_picture->imgUV[pl - 1] : currSlice->dec_picture->imgY; // For MB level frame/field coding tools -- set default to imgY + + PixelPos pix_a[8]; + PixelPos pix_b, pix_c, pix_d; + + int block_available_up; + int block_available_left; + int block_available_up_left; + int block_available_up_right; + + imgpel **mpr = currSlice->mb_pred[pl]; + int *mb_size = p_Vid->mb_size[IS_LUMA]; + + for (i=0;i<8;i++) + { + getAffNeighbour(currMB, ioff - 1, joff + i, mb_size, &pix_a[i]); + } + + getAffNeighbour(currMB, ioff , joff - 1, mb_size, &pix_b); + getAffNeighbour(currMB, ioff + 8, joff - 1, mb_size, &pix_c); + getAffNeighbour(currMB, ioff - 1, joff - 1, mb_size, &pix_d); + + pix_c.available = pix_c.available &&!(ioff == 8 && joff == 8); + + if (p_Vid->active_pps->constrained_intra_pred_flag) + { + for (i=0, block_available_left=1; i<8;i++) + block_available_left &= pix_a[i].available ? currSlice->intra_block[pix_a[i].mb_addr]: 0; + block_available_up = pix_b.available ? currSlice->intra_block [pix_b.mb_addr] : 0; + block_available_up_right = pix_c.available ? currSlice->intra_block [pix_c.mb_addr] : 0; + block_available_up_left = pix_d.available ? currSlice->intra_block [pix_d.mb_addr] : 0; + } + else + { + block_available_left = pix_a[0].available; + block_available_up = pix_b.available; + block_available_up_right = pix_c.available; + block_available_up_left = pix_d.available; + } + + if (!block_available_up) + printf ("warning: Intra_4x4_Vertical_Left prediction mode not allowed at mb %d\n", (int) currSlice->current_mb_nr); + + // form predictor pels + if (block_available_up) + { + memcpy(&PredPel[1], &imgY[pix_b.pos_y][pix_b.pos_x], BLOCK_SIZE_8x8 * sizeof(imgpel)); + } + else + { +#if (IMGTYPE == 0) + memset(&PredPel[1], p_Vid->dc_pred_value_comp[pl], BLOCK_SIZE_8x8 * sizeof(imgpel)); +#else + P_A = P_B = P_C = P_D = P_E = P_F = P_G = P_H = (imgpel) p_Vid->dc_pred_value_comp[pl]; +#endif + } + + if (block_available_up_right) + { + memcpy(&PredPel[9], &imgY[pix_c.pos_y][pix_c.pos_x], BLOCK_SIZE_8x8 * sizeof(imgpel)); + } + else + { +#if (IMGTYPE == 0) + memset(&PredPel[9], PredPel[8], BLOCK_SIZE_8x8 * sizeof(imgpel)); +#else + P_I = P_J = P_K = P_L = P_M = P_N = P_O = P_P = P_H; +#endif + } + + if (block_available_left) + { + P_Q = imgY[pix_a[0].pos_y][pix_a[0].pos_x]; + P_R = imgY[pix_a[1].pos_y][pix_a[1].pos_x]; + P_S = imgY[pix_a[2].pos_y][pix_a[2].pos_x]; + P_T = imgY[pix_a[3].pos_y][pix_a[3].pos_x]; + P_U = imgY[pix_a[4].pos_y][pix_a[4].pos_x]; + P_V = imgY[pix_a[5].pos_y][pix_a[5].pos_x]; + P_W = imgY[pix_a[6].pos_y][pix_a[6].pos_x]; + P_X = imgY[pix_a[7].pos_y][pix_a[7].pos_x]; + } + else + { + P_Q = P_R = P_S = P_T = P_U = P_V = P_W = P_X = (imgpel) p_Vid->dc_pred_value_comp[pl]; + } + + if (block_available_up_left) + { + P_Z = imgY[pix_d.pos_y][pix_d.pos_x]; + } + else + { + P_Z = (imgpel) p_Vid->dc_pred_value_comp[pl]; + } + + LowPassForIntra8x8Pred(PredPel, block_available_up_left, block_available_up, block_available_left); + + *pred_pel++ = (imgpel) ((P_A + P_B + 1) >> 1); + *pred_pel++ = (imgpel) ((P_B + P_C + 1) >> 1); + *pred_pel++ = (imgpel) ((P_C + P_D + 1) >> 1); + *pred_pel++ = (imgpel) ((P_D + P_E + 1) >> 1); + *pred_pel++ = (imgpel) ((P_E + P_F + 1) >> 1); + *pred_pel++ = (imgpel) ((P_F + P_G + 1) >> 1); + *pred_pel++ = (imgpel) ((P_G + P_H + 1) >> 1); + *pred_pel++ = (imgpel) ((P_H + P_I + 1) >> 1); + *pred_pel++ = (imgpel) ((P_I + P_J + 1) >> 1); + *pred_pel++ = (imgpel) ((P_J + P_K + 1) >> 1); + *pred_pel++ = (imgpel) ((P_K + P_L + 1) >> 1); + *pred_pel++ = (imgpel) ((P_A + P_C + 2*P_B + 2) >> 2); + *pred_pel++ = (imgpel) ((P_B + P_D + 2*P_C + 2) >> 2); + *pred_pel++ = (imgpel) ((P_C + P_E + 2*P_D + 2) >> 2); + *pred_pel++ = (imgpel) ((P_D + P_F + 2*P_E + 2) >> 2); + *pred_pel++ = (imgpel) ((P_E + P_G + 2*P_F + 2) >> 2); + *pred_pel++ = (imgpel) ((P_F + P_H + 2*P_G + 2) >> 2); + *pred_pel++ = (imgpel) ((P_G + P_I + 2*P_H + 2) >> 2); + *pred_pel++ = (imgpel) ((P_H + P_J + (P_I << 1) + 2) >> 2); + *pred_pel++ = (imgpel) ((P_I + P_K + (P_J << 1) + 2) >> 2); + *pred_pel++ = (imgpel) ((P_J + P_L + (P_K << 1) + 2) >> 2); + *pred_pel = (imgpel) ((P_K + P_M + (P_L << 1) + 2) >> 2); + + memcpy(&mpr[joff++][ioff], &PredArray[ 0], 8 * sizeof(imgpel)); + memcpy(&mpr[joff++][ioff], &PredArray[11], 8 * sizeof(imgpel)); + memcpy(&mpr[joff++][ioff], &PredArray[ 1], 8 * sizeof(imgpel)); + memcpy(&mpr[joff++][ioff], &PredArray[12], 8 * sizeof(imgpel)); + memcpy(&mpr[joff++][ioff], &PredArray[ 2], 8 * sizeof(imgpel)); + memcpy(&mpr[joff++][ioff], &PredArray[13], 8 * sizeof(imgpel)); + memcpy(&mpr[joff++][ioff], &PredArray[ 3], 8 * sizeof(imgpel)); + memcpy(&mpr[joff ][ioff], &PredArray[14], 8 * sizeof(imgpel)); + + return DECODING_OK; +} + +/*! + *********************************************************************** + * \brief + * makes and returns 8x8 horizontal up prediction mode + * + * \return + * DECODING_OK decoding of intra prediction mode was successful \n + * + *********************************************************************** + */ +static inline int intra8x8_hor_up_pred_mbaff(Macroblock *currMB, //!< current macroblock + ColorPlane pl, //!< current image plane + int ioff, //!< pixel offset X within MB + int joff) //!< pixel offset Y within MB +{ + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + + int i; + imgpel PredPel[25]; // array of predictor pels + imgpel PredArray[22]; // array of final prediction values + imgpel **imgY = (pl) ? currSlice->dec_picture->imgUV[pl - 1] : currSlice->dec_picture->imgY; // For MB level frame/field coding tools -- set default to imgY + + PixelPos pix_a[8]; + PixelPos pix_b, pix_c, pix_d; + + int block_available_up; + int block_available_left; + int block_available_up_left; + int block_available_up_right; + int jpos0 = joff , jpos1 = joff + 1, jpos2 = joff + 2, jpos3 = joff + 3; + int jpos4 = joff + 4, jpos5 = joff + 5, jpos6 = joff + 6, jpos7 = joff + 7; + + imgpel **mpr = currSlice->mb_pred[pl]; + int *mb_size = p_Vid->mb_size[IS_LUMA]; + + for (i=0;i<8;i++) + { + getAffNeighbour(currMB, ioff - 1, joff + i, mb_size, &pix_a[i]); + } + + getAffNeighbour(currMB, ioff , joff - 1, mb_size, &pix_b); + getAffNeighbour(currMB, ioff + 8, joff - 1, mb_size, &pix_c); + getAffNeighbour(currMB, ioff - 1, joff - 1, mb_size, &pix_d); + + pix_c.available = pix_c.available &&!(ioff == 8 && joff == 8); + + if (p_Vid->active_pps->constrained_intra_pred_flag) + { + for (i=0, block_available_left=1; i<8;i++) + block_available_left &= pix_a[i].available ? currSlice->intra_block[pix_a[i].mb_addr]: 0; + block_available_up = pix_b.available ? currSlice->intra_block [pix_b.mb_addr] : 0; + block_available_up_right = pix_c.available ? currSlice->intra_block [pix_c.mb_addr] : 0; + block_available_up_left = pix_d.available ? currSlice->intra_block [pix_d.mb_addr] : 0; + } + else + { + block_available_left = pix_a[0].available; + block_available_up = pix_b.available; + block_available_up_right = pix_c.available; + block_available_up_left = pix_d.available; + } + + if (!block_available_left) + printf ("warning: Intra_8x8_Horizontal_Up prediction mode not allowed at mb %d\n", (int) currSlice->current_mb_nr); + + // form predictor pels + if (block_available_up) + { + memcpy(&PredPel[1], &imgY[pix_b.pos_y][pix_b.pos_x], BLOCK_SIZE_8x8 * sizeof(imgpel)); + } + else + { +#if (IMGTYPE == 0) + memset(&PredPel[1], p_Vid->dc_pred_value_comp[pl], BLOCK_SIZE_8x8 * sizeof(imgpel)); +#else + P_A = P_B = P_C = P_D = P_E = P_F = P_G = P_H = (imgpel) p_Vid->dc_pred_value_comp[pl]; +#endif + } + + if (block_available_up_right) + { + memcpy(&PredPel[9], &imgY[pix_c.pos_y][pix_c.pos_x], BLOCK_SIZE_8x8 * sizeof(imgpel)); + } + else + { +#if (IMGTYPE == 0) + memset(&PredPel[9], PredPel[8], BLOCK_SIZE_8x8 * sizeof(imgpel)); +#else + P_I = P_J = P_K = P_L = P_M = P_N = P_O = P_P = P_H; +#endif + } + + if (block_available_left) + { + P_Q = imgY[pix_a[0].pos_y][pix_a[0].pos_x]; + P_R = imgY[pix_a[1].pos_y][pix_a[1].pos_x]; + P_S = imgY[pix_a[2].pos_y][pix_a[2].pos_x]; + P_T = imgY[pix_a[3].pos_y][pix_a[3].pos_x]; + P_U = imgY[pix_a[4].pos_y][pix_a[4].pos_x]; + P_V = imgY[pix_a[5].pos_y][pix_a[5].pos_x]; + P_W = imgY[pix_a[6].pos_y][pix_a[6].pos_x]; + P_X = imgY[pix_a[7].pos_y][pix_a[7].pos_x]; + } + else + { + P_Q = P_R = P_S = P_T = P_U = P_V = P_W = P_X = (imgpel) p_Vid->dc_pred_value_comp[pl]; + } + + if (block_available_up_left) + { + P_Z = imgY[pix_d.pos_y][pix_d.pos_x]; + } + else + { + P_Z = (imgpel) p_Vid->dc_pred_value_comp[pl]; + } + + LowPassForIntra8x8Pred(PredPel, block_available_up_left, block_available_up, block_available_left); + + PredArray[ 0] = (imgpel) ((P_Q + P_R + 1) >> 1); + PredArray[ 1] = (imgpel) ((P_S + P_Q + (P_R << 1) + 2) >> 2); + PredArray[ 2] = (imgpel) ((P_R + P_S + 1) >> 1); + PredArray[ 3] = (imgpel) ((P_T + P_R + (P_S << 1) + 2) >> 2); + PredArray[ 4] = (imgpel) ((P_S + P_T + 1) >> 1); + PredArray[ 5] = (imgpel) ((P_U + P_S + (P_T << 1) + 2) >> 2); + PredArray[ 6] = (imgpel) ((P_T + P_U + 1) >> 1); + PredArray[ 7] = (imgpel) ((P_V + P_T + (P_U << 1) + 2) >> 2); + PredArray[ 8] = (imgpel) ((P_U + P_V + 1) >> 1); + PredArray[ 9] = (imgpel) ((P_W + P_U + (P_V << 1) + 2) >> 2); + PredArray[10] = (imgpel) ((P_V + P_W + 1) >> 1); + PredArray[11] = (imgpel) ((P_X + P_V + (P_W << 1) + 2) >> 2); + PredArray[12] = (imgpel) ((P_W + P_X + 1) >> 1); + PredArray[13] = (imgpel) ((P_W + P_X + (P_X << 1) + 2) >> 2); + PredArray[14] = (imgpel) P_X; + PredArray[15] = (imgpel) P_X; + PredArray[16] = (imgpel) P_X; + PredArray[17] = (imgpel) P_X; + PredArray[18] = (imgpel) P_X; + PredArray[19] = (imgpel) P_X; + PredArray[20] = (imgpel) P_X; + PredArray[21] = (imgpel) P_X; + + memcpy(&mpr[jpos0][ioff], &PredArray[0], 8 * sizeof(imgpel)); + memcpy(&mpr[jpos1][ioff], &PredArray[2], 8 * sizeof(imgpel)); + memcpy(&mpr[jpos2][ioff], &PredArray[4], 8 * sizeof(imgpel)); + memcpy(&mpr[jpos3][ioff], &PredArray[6], 8 * sizeof(imgpel)); + memcpy(&mpr[jpos4][ioff], &PredArray[8], 8 * sizeof(imgpel)); + memcpy(&mpr[jpos5][ioff], &PredArray[10], 8 * sizeof(imgpel)); + memcpy(&mpr[jpos6][ioff], &PredArray[12], 8 * sizeof(imgpel)); + memcpy(&mpr[jpos7][ioff], &PredArray[14], 8 * sizeof(imgpel)); + + return DECODING_OK; +} + +/*! + *********************************************************************** + * \brief + * makes and returns 8x8 horizontal down prediction mode + * + * \return + * DECODING_OK decoding of intra prediction mode was successful \n + * + *********************************************************************** + */ +static inline int intra8x8_hor_down_pred_mbaff(Macroblock *currMB, //!< current macroblock + ColorPlane pl, //!< current image plane + int ioff, //!< pixel offset X within MB + int joff) //!< pixel offset Y within MB +{ + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + + int i; + imgpel PredPel[25]; // array of predictor pels + imgpel PredArray[22]; // array of final prediction values + imgpel **imgY = (pl) ? currSlice->dec_picture->imgUV[pl - 1] : currSlice->dec_picture->imgY; // For MB level frame/field coding tools -- set default to imgY + + PixelPos pix_a[8]; + PixelPos pix_b, pix_c, pix_d; + + int block_available_up; + int block_available_left; + int block_available_up_left; + int block_available_up_right; + int jpos0 = joff , jpos1 = joff + 1, jpos2 = joff + 2, jpos3 = joff + 3; + int jpos4 = joff + 4, jpos5 = joff + 5, jpos6 = joff + 6, jpos7 = joff + 7; + + imgpel **mpr = currSlice->mb_pred[pl]; + int *mb_size = p_Vid->mb_size[IS_LUMA]; + + for (i=0;i<8;i++) + { + getAffNeighbour(currMB, ioff - 1, joff + i, mb_size, &pix_a[i]); + } + + getAffNeighbour(currMB, ioff , joff - 1, mb_size, &pix_b); + getAffNeighbour(currMB, ioff + 8, joff - 1, mb_size, &pix_c); + getAffNeighbour(currMB, ioff - 1, joff - 1, mb_size, &pix_d); + + pix_c.available = pix_c.available &&!(ioff == 8 && joff == 8); + + if (p_Vid->active_pps->constrained_intra_pred_flag) + { + for (i=0, block_available_left=1; i<8;i++) + block_available_left &= pix_a[i].available ? currSlice->intra_block[pix_a[i].mb_addr]: 0; + block_available_up = pix_b.available ? currSlice->intra_block [pix_b.mb_addr] : 0; + block_available_up_right = pix_c.available ? currSlice->intra_block [pix_c.mb_addr] : 0; + block_available_up_left = pix_d.available ? currSlice->intra_block [pix_d.mb_addr] : 0; + } + else + { + block_available_left = pix_a[0].available; + block_available_up = pix_b.available; + block_available_up_right = pix_c.available; + block_available_up_left = pix_d.available; + } + + if ((!block_available_up)||(!block_available_left)||(!block_available_up_left)) + printf ("warning: Intra_8x8_Horizontal_Down prediction mode not allowed at mb %d\n", (int) currSlice->current_mb_nr); + + // form predictor pels + if (block_available_up) + { + memcpy(&PredPel[1], &imgY[pix_b.pos_y][pix_b.pos_x], BLOCK_SIZE_8x8 * sizeof(imgpel)); + } + else + { +#if (IMGTYPE == 0) + memset(&PredPel[1], p_Vid->dc_pred_value_comp[pl], BLOCK_SIZE_8x8 * sizeof(imgpel)); +#else + P_A = P_B = P_C = P_D = P_E = P_F = P_G = P_H = (imgpel) p_Vid->dc_pred_value_comp[pl]; +#endif + } + + if (block_available_up_right) + { + memcpy(&PredPel[9], &imgY[pix_c.pos_y][pix_c.pos_x], BLOCK_SIZE_8x8 * sizeof(imgpel)); + } + else + { +#if (IMGTYPE == 0) + memset(&PredPel[9], PredPel[8], BLOCK_SIZE_8x8 * sizeof(imgpel)); +#else + P_I = P_J = P_K = P_L = P_M = P_N = P_O = P_P = P_H; +#endif + } + + if (block_available_left) + { + P_Q = imgY[pix_a[0].pos_y][pix_a[0].pos_x]; + P_R = imgY[pix_a[1].pos_y][pix_a[1].pos_x]; + P_S = imgY[pix_a[2].pos_y][pix_a[2].pos_x]; + P_T = imgY[pix_a[3].pos_y][pix_a[3].pos_x]; + P_U = imgY[pix_a[4].pos_y][pix_a[4].pos_x]; + P_V = imgY[pix_a[5].pos_y][pix_a[5].pos_x]; + P_W = imgY[pix_a[6].pos_y][pix_a[6].pos_x]; + P_X = imgY[pix_a[7].pos_y][pix_a[7].pos_x]; + } + else + { + P_Q = P_R = P_S = P_T = P_U = P_V = P_W = P_X = (imgpel) p_Vid->dc_pred_value_comp[pl]; + } + + if (block_available_up_left) + { + P_Z = imgY[pix_d.pos_y][pix_d.pos_x]; + } + else + { + P_Z = (imgpel) p_Vid->dc_pred_value_comp[pl]; + } + + LowPassForIntra8x8Pred(PredPel, block_available_up_left, block_available_up, block_available_left); + + PredArray[ 0] = (imgpel) ((P_X + P_W + 1) >> 1); + PredArray[ 1] = (imgpel) ((P_V + P_X + (P_W << 1) + 2) >> 2); + PredArray[ 2] = (imgpel) ((P_W + P_V + 1) >> 1); + PredArray[ 3] = (imgpel) ((P_U + P_W + (P_V << 1) + 2) >> 2); + PredArray[ 4] = (imgpel) ((P_V + P_U + 1) >> 1); + PredArray[ 5] = (imgpel) ((P_T + P_V + (P_U << 1) + 2) >> 2); + PredArray[ 6] = (imgpel) ((P_U + P_T + 1) >> 1); + PredArray[ 7] = (imgpel) ((P_S + P_U + (P_T << 1) + 2) >> 2); + PredArray[ 8] = (imgpel) ((P_T + P_S + 1) >> 1); + PredArray[ 9] = (imgpel) ((P_R + P_T + (P_S << 1) + 2) >> 2); + PredArray[10] = (imgpel) ((P_S + P_R + 1) >> 1); + PredArray[11] = (imgpel) ((P_Q + P_S + (P_R << 1) + 2) >> 2); + PredArray[12] = (imgpel) ((P_R + P_Q + 1) >> 1); + PredArray[13] = (imgpel) ((P_Z + P_R + (P_Q << 1) + 2) >> 2); + PredArray[14] = (imgpel) ((P_Q + P_Z + 1) >> 1); + PredArray[15] = (imgpel) ((P_Q + P_A + 2*P_Z + 2) >> 2); + PredArray[16] = (imgpel) ((P_Z + P_B + 2*P_A + 2) >> 2); + PredArray[17] = (imgpel) ((P_A + P_C + 2*P_B + 2) >> 2); + PredArray[18] = (imgpel) ((P_B + P_D + 2*P_C + 2) >> 2); + PredArray[19] = (imgpel) ((P_C + P_E + 2*P_D + 2) >> 2); + PredArray[20] = (imgpel) ((P_D + P_F + 2*P_E + 2) >> 2); + PredArray[21] = (imgpel) ((P_E + P_G + 2*P_F + 2) >> 2); + + memcpy(&mpr[jpos0][ioff], &PredArray[14], 8 * sizeof(imgpel)); + memcpy(&mpr[jpos1][ioff], &PredArray[12], 8 * sizeof(imgpel)); + memcpy(&mpr[jpos2][ioff], &PredArray[10], 8 * sizeof(imgpel)); + memcpy(&mpr[jpos3][ioff], &PredArray[ 8], 8 * sizeof(imgpel)); + memcpy(&mpr[jpos4][ioff], &PredArray[ 6], 8 * sizeof(imgpel)); + memcpy(&mpr[jpos5][ioff], &PredArray[ 4], 8 * sizeof(imgpel)); + memcpy(&mpr[jpos6][ioff], &PredArray[ 2], 8 * sizeof(imgpel)); + memcpy(&mpr[jpos7][ioff], &PredArray[ 0], 8 * sizeof(imgpel)); + + return DECODING_OK; +} + +/*! + ************************************************************************ + * \brief + * Make intra 8x8 prediction according to all 9 prediction modes. + * The routine uses left and upper neighbouring points from + * previous coded blocks to do this (if available). Notice that + * inaccessible neighbouring points are signalled with a negative + * value in the predmode array . + * + * \par Input: + * Starting point of current 8x8 block image position + * + ************************************************************************ + */ +int intra_pred_8x8_mbaff(Macroblock *currMB, //!< Current Macroblock + ColorPlane pl, //!< Current color plane + int ioff, //!< ioff + int joff) //!< joff + +{ + int block_x = (currMB->block_x) + (ioff >> 2); + int block_y = (currMB->block_y) + (joff >> 2); + byte predmode = currMB->p_Slice->ipredmode[block_y][block_x]; + + currMB->ipmode_DPCM = predmode; //For residual DPCM + + switch (predmode) + { + case DC_PRED: + return (intra8x8_dc_pred_mbaff(currMB, pl, ioff, joff)); + break; + case VERT_PRED: + return (intra8x8_vert_pred_mbaff(currMB, pl, ioff, joff)); + break; + case HOR_PRED: + return (intra8x8_hor_pred_mbaff(currMB, pl, ioff, joff)); + break; + case DIAG_DOWN_RIGHT_PRED: + return (intra8x8_diag_down_right_pred_mbaff(currMB, pl, ioff, joff)); + break; + case DIAG_DOWN_LEFT_PRED: + return (intra8x8_diag_down_left_pred_mbaff(currMB, pl, ioff, joff)); + break; + case VERT_RIGHT_PRED: + return (intra8x8_vert_right_pred_mbaff(currMB, pl, ioff, joff)); + break; + case VERT_LEFT_PRED: + return (intra8x8_vert_left_pred_mbaff(currMB, pl, ioff, joff)); + break; + case HOR_UP_PRED: + return (intra8x8_hor_up_pred_mbaff(currMB, pl, ioff, joff)); + break; + case HOR_DOWN_PRED: + return (intra8x8_hor_down_pred_mbaff(currMB, pl, ioff, joff)); + default: + printf("Error: illegal intra_8x8 prediction mode: %d\n", (int) predmode); + return SEARCH_SYNC; + break; + } +} + + + diff --git a/ldecod/src/intra8x8_pred_normal.c b/ldecod/src/intra8x8_pred_normal.c new file mode 100644 index 0000000..1122f6f --- /dev/null +++ b/ldecod/src/intra8x8_pred_normal.c @@ -0,0 +1,1612 @@ +/*! + ************************************************************************************* + * \file intra8x8_pred.c + * + * \brief + * Functions for intra 8x8 prediction + * + * \author + * Main contributors (see contributors.h for copyright, + * address and affiliation details) + * - Yuri Vatis + * - Jan Muenster + * - Alexis Michael Tourapis + * + ************************************************************************************* + */ +#include "global.h" +#include "intra8x8_pred.h" +#include "mb_access.h" +#include "image.h" + +// Notation for comments regarding prediction and predictors. +// The pels of the 8x8 block are labeled a..p. The predictor pels above +// are labeled A..H, from the left I..P, and from above left X, as follows: +// +// Z A B C D E F G H I J K L M N O P +// Q a1 b1 c1 d1 e1 f1 g1 h1 +// R a2 b2 c2 d2 e2 f2 g2 h2 +// S a3 b3 c3 d3 e3 f3 g3 h3 +// T a4 b4 c4 d4 e4 f4 g4 h4 +// U a5 b5 c5 d5 e5 f5 g5 h5 +// V a6 b6 c6 d6 e6 f6 g6 h6 +// W a7 b7 c7 d7 e7 f7 g7 h7 +// X a8 b8 c8 d8 e8 f8 g8 h8 + + +// Predictor array index definitions +#define P_Z (PredPel[0]) +#define P_A (PredPel[1]) +#define P_B (PredPel[2]) +#define P_C (PredPel[3]) +#define P_D (PredPel[4]) +#define P_E (PredPel[5]) +#define P_F (PredPel[6]) +#define P_G (PredPel[7]) +#define P_H (PredPel[8]) +#define P_I (PredPel[9]) +#define P_J (PredPel[10]) +#define P_K (PredPel[11]) +#define P_L (PredPel[12]) +#define P_M (PredPel[13]) +#define P_N (PredPel[14]) +#define P_O (PredPel[15]) +#define P_P (PredPel[16]) +#define P_Q (PredPel[17]) +#define P_R (PredPel[18]) +#define P_S (PredPel[19]) +#define P_T (PredPel[20]) +#define P_U (PredPel[21]) +#define P_V (PredPel[22]) +#define P_W (PredPel[23]) +#define P_X (PredPel[24]) + +/*! + ************************************************************************************* + * \brief + * Prefiltering for Intra8x8 prediction + ************************************************************************************* + */ +static inline void LowPassForIntra8x8Pred(imgpel *PredPel, int block_up_left, int block_up, int block_left) +{ + int i; + imgpel LoopArray[25]; + + memcpy(&LoopArray[0], &PredPel[0], 25 * sizeof(imgpel)); + + if(block_up_left) + { + if(block_up && block_left) + { + LoopArray[0] = (imgpel) ((P_Q + (P_Z<<1) + P_A + 2)>>2); + } + else + { + if(block_up) + LoopArray[0] = (imgpel) ((P_Z + (P_Z<<1) + P_A + 2)>>2); + else if (block_left) + LoopArray[0] = (imgpel) ((P_Z + (P_Z<<1) + P_Q + 2)>>2); + } + } + + if(block_up) + { + if(block_up_left) + { + LoopArray[1] = (imgpel) ((PredPel[0] + (PredPel[1]<<1) + PredPel[2] + 2)>>2); + } + else + LoopArray[1] = (imgpel) ((PredPel[1] + (PredPel[1]<<1) + PredPel[2] + 2)>>2); + + + for(i = 2; i <16; i++) + { + LoopArray[i] = (imgpel) ((PredPel[i-1] + (PredPel[i]<<1) + PredPel[i+1] + 2)>>2); + } + LoopArray[16] = (imgpel) ((P_P + (P_P<<1) + P_O + 2)>>2); + } + + if(block_left) + { + if(block_up_left) + LoopArray[17] = (imgpel) ((P_Z + (P_Q<<1) + P_R + 2)>>2); + else + LoopArray[17] = (imgpel) ((P_Q + (P_Q<<1) + P_R + 2)>>2); + + for(i = 18; i <24; i++) + { + LoopArray[i] = (imgpel) ((PredPel[i-1] + (PredPel[i]<<1) + PredPel[i+1] + 2)>>2); + } + LoopArray[24] = (imgpel) ((P_W + (P_X<<1) + P_X + 2) >> 2); + } + + memcpy(&PredPel[0], &LoopArray[0], 25 * sizeof(imgpel)); +} + +/*! + ************************************************************************************* + * \brief + * Prefiltering for Intra8x8 prediction (Horizontal) + ************************************************************************************* + */ +static inline void LowPassForIntra8x8PredHor(imgpel *PredPel, int block_up_left, int block_up, int block_left) +{ + int i; + imgpel LoopArray[25]; + + memcpy(&LoopArray[0], &PredPel[0], 25 * sizeof(imgpel)); + + if(block_up_left) + { + if(block_up && block_left) + { + LoopArray[0] = (imgpel) ((P_Q + (P_Z<<1) + P_A + 2)>>2); + } + else + { + if(block_up) + LoopArray[0] = (imgpel) ((P_Z + (P_Z<<1) + P_A + 2)>>2); + else if (block_left) + LoopArray[0] = (imgpel) ((P_Z + (P_Z<<1) + P_Q + 2)>>2); + } + } + + if(block_up) + { + if(block_up_left) + { + LoopArray[1] = (imgpel) ((PredPel[0] + (PredPel[1]<<1) + PredPel[2] + 2)>>2); + } + else + LoopArray[1] = (imgpel) ((PredPel[1] + (PredPel[1]<<1) + PredPel[2] + 2)>>2); + + + for(i = 2; i <16; i++) + { + LoopArray[i] = (imgpel) ((PredPel[i-1] + (PredPel[i]<<1) + PredPel[i+1] + 2)>>2); + } + LoopArray[16] = (imgpel) ((P_P + (P_P<<1) + P_O + 2)>>2); + } + + + memcpy(&PredPel[0], &LoopArray[0], 17 * sizeof(imgpel)); +} + +/*! + ************************************************************************************* + * \brief + * Prefiltering for Intra8x8 prediction (Vertical) + ************************************************************************************* + */ +static inline void LowPassForIntra8x8PredVer(imgpel *PredPel, int block_up_left, int block_up, int block_left) +{ + // These functions need some cleanup and can be further optimized. + // For convenience, let us copy all data for now. It is obvious that the filtering makes things a bit more "complex" + int i; + imgpel LoopArray[25]; + + memcpy(&LoopArray[0], &PredPel[0], 25 * sizeof(imgpel)); + + if(block_up_left) + { + if(block_up && block_left) + { + LoopArray[0] = (imgpel) ((P_Q + (P_Z<<1) + P_A + 2)>>2); + } + else + { + if(block_up) + LoopArray[0] = (imgpel) ((P_Z + (P_Z<<1) + P_A + 2)>>2); + else if (block_left) + LoopArray[0] = (imgpel) ((P_Z + (P_Z<<1) + P_Q + 2)>>2); + } + } + + if(block_left) + { + if(block_up_left) + LoopArray[17] = (imgpel) ((P_Z + (P_Q<<1) + P_R + 2)>>2); + else + LoopArray[17] = (imgpel) ((P_Q + (P_Q<<1) + P_R + 2)>>2); + + for(i = 18; i <24; i++) + { + LoopArray[i] = (imgpel) ((PredPel[i-1] + (PredPel[i]<<1) + PredPel[i+1] + 2)>>2); + } + LoopArray[24] = (imgpel) ((P_W + (P_X<<1) + P_X + 2) >> 2); + } + + memcpy(&PredPel[0], &LoopArray[0], 25 * sizeof(imgpel)); +} + + +/*! + *********************************************************************** + * \brief + * makes and returns 8x8 DC prediction mode + * + * \return + * DECODING_OK decoding of intraprediction mode was successful \n + * + *********************************************************************** + */ +static inline int intra8x8_dc_pred(Macroblock *currMB, //!< current macroblock + ColorPlane pl, //!< current image plane + int ioff, //!< pixel offset X within MB + int joff) //!< pixel offset Y within MB +{ + int i,j; + int s0 = 0; + imgpel PredPel[25]; // array of predictor pels + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + + StorablePicture *dec_picture = currSlice->dec_picture; + imgpel **imgY = (pl) ? dec_picture->imgUV[pl - 1] : dec_picture->imgY; // For MB level frame/field coding tools -- set default to imgY + + PixelPos pix_a; + PixelPos pix_b, pix_c, pix_d; + + int block_available_up; + int block_available_left; + int block_available_up_left; + int block_available_up_right; + + + imgpel *pred_pels, **mpr = currSlice->mb_pred[pl]; + int *mb_size = p_Vid->mb_size[IS_LUMA]; + + getNonAffNeighbour(currMB, ioff - 1, joff, mb_size, &pix_a); + getNonAffNeighbour(currMB, ioff , joff - 1, mb_size, &pix_b); + getNonAffNeighbour(currMB, ioff + 8, joff - 1, mb_size, &pix_c); + getNonAffNeighbour(currMB, ioff - 1, joff - 1, mb_size, &pix_d); + + pix_c.available = pix_c.available &&!(ioff == 8 && joff == 8); + + if (p_Vid->active_pps->constrained_intra_pred_flag) + { + block_available_left = pix_a.available ? currSlice->intra_block [pix_a.mb_addr]: 0; + block_available_up = pix_b.available ? currSlice->intra_block [pix_b.mb_addr] : 0; + block_available_up_right = pix_c.available ? currSlice->intra_block [pix_c.mb_addr] : 0; + block_available_up_left = pix_d.available ? currSlice->intra_block [pix_d.mb_addr] : 0; + } + else + { + block_available_left = pix_a.available; + block_available_up = pix_b.available; + block_available_up_right = pix_c.available; + block_available_up_left = pix_d.available; + } + + // form predictor pels + if (block_available_up) + { + pred_pels = &imgY[pix_b.pos_y][pix_b.pos_x]; + P_A = pred_pels[0]; + P_B = pred_pels[1]; + P_C = pred_pels[2]; + P_D = pred_pels[3]; + P_E = pred_pels[4]; + P_F = pred_pels[5]; + P_G = pred_pels[6]; + P_H = pred_pels[7]; + } + else + { + P_A = P_B = P_C = P_D = P_E = P_F = P_G = P_H = (imgpel) p_Vid->dc_pred_value_comp[pl]; + } + + if (block_available_up_right) + { + pred_pels = &imgY[pix_c.pos_y][pix_c.pos_x]; + P_I = pred_pels[0]; + P_J = pred_pels[1]; + P_K = pred_pels[2]; + P_L = pred_pels[3]; + P_M = pred_pels[4]; + P_N = pred_pels[5]; + P_O = pred_pels[6]; + P_P = pred_pels[7]; + + } + else + { + P_I = P_J = P_K = P_L = P_M = P_N = P_O = P_P = P_H; + } + + if (block_available_left) + { + imgpel **img_pred = &imgY[pix_a.pos_y]; + int pos_x = pix_a.pos_x; + P_Q = *(*(img_pred ++) + pos_x); + P_R = *(*(img_pred ++) + pos_x); + P_S = *(*(img_pred ++) + pos_x); + P_T = *(*(img_pred ++) + pos_x); + P_U = *(*(img_pred ++) + pos_x); + P_V = *(*(img_pred ++) + pos_x); + P_W = *(*(img_pred ++) + pos_x); + P_X = *(*(img_pred ) + pos_x); + } + else + { + P_Q = P_R = P_S = P_T = P_U = P_V = P_W = P_X = (imgpel) p_Vid->dc_pred_value_comp[pl]; + } + + if (block_available_up_left) + { + P_Z = imgY[pix_d.pos_y][pix_d.pos_x]; + } + else + { + P_Z = (imgpel) p_Vid->dc_pred_value_comp[pl]; + } + + LowPassForIntra8x8Pred(&(P_Z), block_available_up_left, block_available_up, block_available_left); + + if (block_available_up && block_available_left) + { + // no edge + s0 = (P_A + P_B + P_C + P_D + P_E + P_F + P_G + P_H + P_Q + P_R + P_S + P_T + P_U + P_V + P_W + P_X + 8) >> 4; + } + else if (!block_available_up && block_available_left) + { + // upper edge + s0 = (P_Q + P_R + P_S + P_T + P_U + P_V + P_W + P_X + 4) >> 3; + } + else if (block_available_up && !block_available_left) + { + // left edge + s0 = (P_A + P_B + P_C + P_D + P_E + P_F + P_G + P_H + 4) >> 3; + } + else //if (!block_available_up && !block_available_left) + { + // top left corner, nothing to predict from + s0 = p_Vid->dc_pred_value_comp[pl]; + } + + for(j = joff; j < joff + BLOCK_SIZE_8x8; j++) + for(i = ioff; i < ioff + BLOCK_SIZE_8x8; i++) + mpr[j][i] = (imgpel) s0; + + return DECODING_OK; +} + +/*! + *********************************************************************** + * \brief + * makes and returns 8x8 vertical prediction mode + * + * \return + * DECODING_OK decoding of intraprediction mode was successful \n + * + *********************************************************************** + */ +static inline int intra8x8_vert_pred(Macroblock *currMB, //!< current macroblock + ColorPlane pl, //!< current image plane + int ioff, //!< pixel offset X within MB + int joff) //!< pixel offset Y within MB +{ + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + + int i; + imgpel PredPel[25]; // array of predictor pels + imgpel **imgY = (pl) ? currSlice->dec_picture->imgUV[pl - 1] : currSlice->dec_picture->imgY; // For MB level frame/field coding tools -- set default to imgY + + PixelPos pix_a; + PixelPos pix_b, pix_c, pix_d; + + int block_available_up; + int block_available_left; + int block_available_up_left; + int block_available_up_right; + + + imgpel *pred_pels, **mpr = currSlice->mb_pred[pl]; + int *mb_size = p_Vid->mb_size[IS_LUMA]; + + getNonAffNeighbour(currMB, ioff - 1, joff , mb_size, &pix_a); + getNonAffNeighbour(currMB, ioff , joff - 1, mb_size, &pix_b); + getNonAffNeighbour(currMB, ioff + 8, joff - 1, mb_size, &pix_c); + getNonAffNeighbour(currMB, ioff - 1, joff - 1, mb_size, &pix_d); + + pix_c.available = pix_c.available &&!(ioff == 8 && joff == 8); + + if (p_Vid->active_pps->constrained_intra_pred_flag) + { + block_available_left = pix_a.available ? currSlice->intra_block [pix_a.mb_addr] : 0; + block_available_up = pix_b.available ? currSlice->intra_block [pix_b.mb_addr] : 0; + block_available_up_right = pix_c.available ? currSlice->intra_block [pix_c.mb_addr] : 0; + block_available_up_left = pix_d.available ? currSlice->intra_block [pix_d.mb_addr] : 0; + } + else + { + block_available_left = pix_a.available; + block_available_up = pix_b.available; + block_available_up_right = pix_c.available; + block_available_up_left = pix_d.available; + } + + if (!block_available_up) + printf ("warning: Intra_8x8_Vertical prediction mode not allowed at mb %d\n", (int) currSlice->current_mb_nr); + + // form predictor pels + if (block_available_up) + { + pred_pels = &imgY[pix_b.pos_y][pix_b.pos_x]; + P_A = *(pred_pels ++); + P_B = *(pred_pels ++); + P_C = *(pred_pels ++); + P_D = *(pred_pels ++); + P_E = *(pred_pels ++); + P_F = *(pred_pels ++); + P_G = *(pred_pels ++); + P_H = *pred_pels; + } + else + { + P_A = P_B = P_C = P_D = P_E = P_F = P_G = P_H = (imgpel) p_Vid->dc_pred_value_comp[pl]; + } + + if (block_available_up_right) + { + pred_pels = &imgY[pix_c.pos_y][pix_c.pos_x]; + P_I = *(pred_pels ++); + P_J = *(pred_pels ++); + P_K = *(pred_pels ++); + P_L = *(pred_pels ++); + P_M = *(pred_pels ++); + P_N = *(pred_pels ++); + P_O = *(pred_pels ++); + P_P = *pred_pels; + } + else + { + P_I = P_J = P_K = P_L = P_M = P_N = P_O = P_P = P_H; + } + + if (block_available_up_left) + { + P_Z = imgY[pix_d.pos_y][pix_d.pos_x]; + } + else + { + P_Z = (imgpel) p_Vid->dc_pred_value_comp[pl]; + } + + LowPassForIntra8x8PredHor(&(P_Z), block_available_up_left, block_available_up, block_available_left); + + for (i=joff; i < joff + BLOCK_SIZE_8x8; i++) + { + memcpy(&mpr[i][ioff], (&P_A), BLOCK_SIZE_8x8 * sizeof(imgpel)); + } + + return DECODING_OK; +} + +/*! + *********************************************************************** + * \brief + * makes and returns 8x8 horizontal prediction mode + * + * \return + * DECODING_OK decoding of intraprediction mode was successful \n + * + *********************************************************************** + */ +static inline int intra8x8_hor_pred(Macroblock *currMB, //!< current macroblock + ColorPlane pl, //!< current image plane + int ioff, //!< pixel offset X within MB + int joff) //!< pixel offset Y within MB +{ + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + + + int j; + imgpel PredPel[25]; // array of predictor pels + imgpel **imgY = (pl) ? currSlice->dec_picture->imgUV[pl - 1] : currSlice->dec_picture->imgY; // For MB level frame/field coding tools -- set default to imgY + + PixelPos pix_a; + PixelPos pix_b, pix_d; + + int block_available_up; + int block_available_left; + int block_available_up_left; + +#if (IMGTYPE != 0) + int ipos0 = ioff , ipos1 = ioff + 1, ipos2 = ioff + 2, ipos3 = ioff + 3; + int ipos4 = ioff + 4, ipos5 = ioff + 5, ipos6 = ioff + 6, ipos7 = ioff + 7; +#endif + int jpos; + imgpel **mpr = currSlice->mb_pred[pl]; + int *mb_size = p_Vid->mb_size[IS_LUMA]; + + getNonAffNeighbour(currMB, ioff - 1, joff , mb_size, &pix_a); + getNonAffNeighbour(currMB, ioff , joff - 1, mb_size, &pix_b); + getNonAffNeighbour(currMB, ioff - 1, joff - 1, mb_size, &pix_d); + + if (p_Vid->active_pps->constrained_intra_pred_flag) + { + block_available_left = pix_a.available ? currSlice->intra_block [pix_a.mb_addr]: 0; + block_available_up = pix_b.available ? currSlice->intra_block [pix_b.mb_addr] : 0; + block_available_up_left = pix_d.available ? currSlice->intra_block [pix_d.mb_addr] : 0; + } + else + { + block_available_left = pix_a.available; + block_available_up = pix_b.available; + block_available_up_left = pix_d.available; + } + + if (!block_available_left) + printf ("warning: Intra_8x8_Horizontal prediction mode not allowed at mb %d\n", (int) currSlice->current_mb_nr); + + // form predictor pels + if (block_available_left) + { + imgpel **img_pred = &imgY[pix_a.pos_y]; + int pos_x = pix_a.pos_x; + P_Q = *(*(img_pred ++) + pos_x); + P_R = *(*(img_pred ++) + pos_x); + P_S = *(*(img_pred ++) + pos_x); + P_T = *(*(img_pred ++) + pos_x); + P_U = *(*(img_pred ++) + pos_x); + P_V = *(*(img_pred ++) + pos_x); + P_W = *(*(img_pred ++) + pos_x); + P_X = *(*(img_pred ) + pos_x); + } + else + { + P_Q = P_R = P_S = P_T = P_U = P_V = P_W = P_X = (imgpel) p_Vid->dc_pred_value_comp[pl]; + } + + if (block_available_up_left) + { + P_Z = imgY[pix_d.pos_y][pix_d.pos_x]; + } + else + { + P_Z = (imgpel) p_Vid->dc_pred_value_comp[pl]; + } + + LowPassForIntra8x8PredVer(&(P_Z), block_available_up_left, block_available_up, block_available_left); + + for (j=0; j < BLOCK_SIZE_8x8; j++) + { + jpos = j + joff; +#if (IMGTYPE == 0) + memset(&mpr[jpos][ioff], (imgpel) (&P_Q)[j], 8 * sizeof(imgpel)); +#else + mpr[jpos][ipos0] = + mpr[jpos][ipos1] = + mpr[jpos][ipos2] = + mpr[jpos][ipos3] = + mpr[jpos][ipos4] = + mpr[jpos][ipos5] = + mpr[jpos][ipos6] = + mpr[jpos][ipos7] = (imgpel) (&P_Q)[j]; +#endif + } + + return DECODING_OK; +} + +/*! + *********************************************************************** + * \brief + * makes and returns 8x8 diagonal down right prediction mode + * + * \return + * DECODING_OK decoding of intraprediction mode was successful \n + * + *********************************************************************** + */ +static inline int intra8x8_diag_down_right_pred(Macroblock *currMB, //!< current macroblock + ColorPlane pl, //!< current image plane + int ioff, //!< pixel offset X within MB + int joff) //!< pixel offset Y within MB +{ + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + + imgpel PredPel[25]; // array of predictor pels + imgpel PredArray[16]; // array of final prediction values + imgpel **imgY = (pl) ? currSlice->dec_picture->imgUV[pl - 1] : currSlice->dec_picture->imgY; // For MB level frame/field coding tools -- set default to imgY + + PixelPos pix_a; + PixelPos pix_b, pix_c, pix_d; + + int block_available_up; + int block_available_left; + int block_available_up_left; + int block_available_up_right; + + imgpel *pred_pels, **mpr = currSlice->mb_pred[pl]; + int *mb_size = p_Vid->mb_size[IS_LUMA]; + + getNonAffNeighbour(currMB, ioff - 1, joff , mb_size, &pix_a); + getNonAffNeighbour(currMB, ioff , joff - 1, mb_size, &pix_b); + getNonAffNeighbour(currMB, ioff + 8, joff - 1, mb_size, &pix_c); + getNonAffNeighbour(currMB, ioff - 1, joff - 1, mb_size, &pix_d); + + pix_c.available = pix_c.available &&!(ioff == 8 && joff == 8); + + if (p_Vid->active_pps->constrained_intra_pred_flag) + { + block_available_left = pix_a.available ? currSlice->intra_block [pix_a.mb_addr]: 0; + block_available_up = pix_b.available ? currSlice->intra_block [pix_b.mb_addr] : 0; + block_available_up_right = pix_c.available ? currSlice->intra_block [pix_c.mb_addr] : 0; + block_available_up_left = pix_d.available ? currSlice->intra_block [pix_d.mb_addr] : 0; + } + else + { + block_available_left = pix_a.available; + block_available_up = pix_b.available; + block_available_up_right = pix_c.available; + block_available_up_left = pix_d.available; + } + + if ((!block_available_up)||(!block_available_left)||(!block_available_up_left)) + printf ("warning: Intra_8x8_Diagonal_Down_Right prediction mode not allowed at mb %d\n", (int) currSlice->current_mb_nr); + + // form predictor pels + if (block_available_up) + { + pred_pels = &imgY[pix_b.pos_y][pix_b.pos_x]; + P_A = pred_pels[0]; + P_B = pred_pels[1]; + P_C = pred_pels[2]; + P_D = pred_pels[3]; + P_E = pred_pels[4]; + P_F = pred_pels[5]; + P_G = pred_pels[6]; + P_H = pred_pels[7]; + } + else + { + P_A = P_B = P_C = P_D = P_E = P_F = P_G = P_H = (imgpel) p_Vid->dc_pred_value_comp[pl]; + } + + if (block_available_up_right) + { + pred_pels = &imgY[pix_c.pos_y][pix_c.pos_x]; + P_I = pred_pels[0]; + P_J = pred_pels[1]; + P_K = pred_pels[2]; + P_L = pred_pels[3]; + P_M = pred_pels[4]; + P_N = pred_pels[5]; + P_O = pred_pels[6]; + P_P = pred_pels[7]; + + } + else + { + P_I = P_J = P_K = P_L = P_M = P_N = P_O = P_P = P_H; + } + + if (block_available_left) + { + imgpel **img_pred = &imgY[pix_a.pos_y]; + int pos_x = pix_a.pos_x; + P_Q = *(*(img_pred ++) + pos_x); + P_R = *(*(img_pred ++) + pos_x); + P_S = *(*(img_pred ++) + pos_x); + P_T = *(*(img_pred ++) + pos_x); + P_U = *(*(img_pred ++) + pos_x); + P_V = *(*(img_pred ++) + pos_x); + P_W = *(*(img_pred ++) + pos_x); + P_X = *(*(img_pred ) + pos_x); + } + else + { + P_Q = P_R = P_S = P_T = P_U = P_V = P_W = P_X = (imgpel) p_Vid->dc_pred_value_comp[pl]; + } + + if (block_available_up_left) + { + P_Z = imgY[pix_d.pos_y][pix_d.pos_x]; + } + else + { + P_Z = (imgpel) p_Vid->dc_pred_value_comp[pl]; + } + + LowPassForIntra8x8Pred(&(P_Z), block_available_up_left, block_available_up, block_available_left); + + // Mode DIAG_DOWN_RIGHT_PRED + PredArray[ 0] = (imgpel) ((P_X + P_V + 2*(P_W) + 2) >> 2); + PredArray[ 1] = (imgpel) ((P_W + P_U + 2*(P_V) + 2) >> 2); + PredArray[ 2] = (imgpel) ((P_V + P_T + 2*(P_U) + 2) >> 2); + PredArray[ 3] = (imgpel) ((P_U + P_S + 2*(P_T) + 2) >> 2); + PredArray[ 4] = (imgpel) ((P_T + P_R + 2*(P_S) + 2) >> 2); + PredArray[ 5] = (imgpel) ((P_S + P_Q + 2*(P_R) + 2) >> 2); + PredArray[ 6] = (imgpel) ((P_R + P_Z + 2*(P_Q) + 2) >> 2); + PredArray[ 7] = (imgpel) ((P_Q + P_A + 2*(P_Z) + 2) >> 2); + PredArray[ 8] = (imgpel) ((P_Z + P_B + 2*(P_A) + 2) >> 2); + PredArray[ 9] = (imgpel) ((P_A + P_C + 2*(P_B) + 2) >> 2); + PredArray[10] = (imgpel) ((P_B + P_D + 2*(P_C) + 2) >> 2); + PredArray[11] = (imgpel) ((P_C + P_E + 2*(P_D) + 2) >> 2); + PredArray[12] = (imgpel) ((P_D + P_F + 2*(P_E) + 2) >> 2); + PredArray[13] = (imgpel) ((P_E + P_G + 2*(P_F) + 2) >> 2); + PredArray[14] = (imgpel) ((P_F + P_H + 2*(P_G) + 2) >> 2); + + memcpy(&mpr[joff++][ioff], &PredArray[7], 8 * sizeof(imgpel)); + memcpy(&mpr[joff++][ioff], &PredArray[6], 8 * sizeof(imgpel)); + memcpy(&mpr[joff++][ioff], &PredArray[5], 8 * sizeof(imgpel)); + memcpy(&mpr[joff++][ioff], &PredArray[4], 8 * sizeof(imgpel)); + memcpy(&mpr[joff++][ioff], &PredArray[3], 8 * sizeof(imgpel)); + memcpy(&mpr[joff++][ioff], &PredArray[2], 8 * sizeof(imgpel)); + memcpy(&mpr[joff++][ioff], &PredArray[1], 8 * sizeof(imgpel)); + memcpy(&mpr[joff ][ioff], &PredArray[0], 8 * sizeof(imgpel)); + + return DECODING_OK; +} + +/*! + *********************************************************************** + * \brief + * makes and returns 8x8 diagonal down left prediction mode + * + * \return + * DECODING_OK decoding of intraprediction mode was successful \n + * + *********************************************************************** + */ +static inline int intra8x8_diag_down_left_pred(Macroblock *currMB, //!< current macroblock + ColorPlane pl, //!< current image plane + int ioff, //!< pixel offset X within MB + int joff) //!< pixel offset Y within MB +{ + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + + imgpel PredPel[25]; // array of predictor pels + imgpel PredArray[16]; // array of final prediction values + imgpel *Pred = &PredArray[0]; + imgpel **imgY = (pl) ? currSlice->dec_picture->imgUV[pl - 1] : currSlice->dec_picture->imgY; // For MB level frame/field coding tools -- set default to imgY + + PixelPos pix_a; + PixelPos pix_b, pix_c, pix_d; + + int block_available_up; + int block_available_left; + int block_available_up_left; + int block_available_up_right; + + imgpel *pred_pels, **mpr = currSlice->mb_pred[pl]; + int *mb_size = p_Vid->mb_size[IS_LUMA]; + + getNonAffNeighbour(currMB, ioff - 1, joff , mb_size, &pix_a); + getNonAffNeighbour(currMB, ioff , joff - 1, mb_size, &pix_b); + getNonAffNeighbour(currMB, ioff + 8, joff - 1, mb_size, &pix_c); + getNonAffNeighbour(currMB, ioff - 1, joff - 1, mb_size, &pix_d); + + pix_c.available = pix_c.available &&!(ioff == 8 && joff == 8); + + if (p_Vid->active_pps->constrained_intra_pred_flag) + { + block_available_left = pix_a.available ? currSlice->intra_block [pix_a.mb_addr]: 0; + block_available_up = pix_b.available ? currSlice->intra_block [pix_b.mb_addr] : 0; + block_available_up_right = pix_c.available ? currSlice->intra_block [pix_c.mb_addr] : 0; + block_available_up_left = pix_d.available ? currSlice->intra_block [pix_d.mb_addr] : 0; + } + else + { + block_available_left = pix_a.available; + block_available_up = pix_b.available; + block_available_up_right = pix_c.available; + block_available_up_left = pix_d.available; + } + + if (!block_available_up) + printf ("warning: Intra_8x8_Diagonal_Down_Left prediction mode not allowed at mb %d\n", (int) currSlice->current_mb_nr); + + // form predictor pels + if (block_available_up) + { + pred_pels = &imgY[pix_b.pos_y][pix_b.pos_x]; + P_A = pred_pels[0]; + P_B = pred_pels[1]; + P_C = pred_pels[2]; + P_D = pred_pels[3]; + P_E = pred_pels[4]; + P_F = pred_pels[5]; + P_G = pred_pels[6]; + P_H = pred_pels[7]; + } + else + { + P_A = P_B = P_C = P_D = P_E = P_F = P_G = P_H = (imgpel) p_Vid->dc_pred_value_comp[pl]; + } + + if (block_available_up_right) + { + pred_pels = &imgY[pix_c.pos_y][pix_c.pos_x]; + P_I = pred_pels[0]; + P_J = pred_pels[1]; + P_K = pred_pels[2]; + P_L = pred_pels[3]; + P_M = pred_pels[4]; + P_N = pred_pels[5]; + P_O = pred_pels[6]; + P_P = pred_pels[7]; + + } + else + { + P_I = P_J = P_K = P_L = P_M = P_N = P_O = P_P = P_H; + } + + if (block_available_left) + { + imgpel **img_pred = &imgY[pix_a.pos_y]; + int pos_x = pix_a.pos_x; + P_Q = *(*(img_pred ++) + pos_x); + P_R = *(*(img_pred ++) + pos_x); + P_S = *(*(img_pred ++) + pos_x); + P_T = *(*(img_pred ++) + pos_x); + P_U = *(*(img_pred ++) + pos_x); + P_V = *(*(img_pred ++) + pos_x); + P_W = *(*(img_pred ++) + pos_x); + P_X = *(*(img_pred ) + pos_x); + } + else + { + P_Q = P_R = P_S = P_T = P_U = P_V = P_W = P_X = (imgpel) p_Vid->dc_pred_value_comp[pl]; + } + + if (block_available_up_left) + { + P_Z = imgY[pix_d.pos_y][pix_d.pos_x]; + } + else + { + P_Z = (imgpel) p_Vid->dc_pred_value_comp[pl]; + } + + LowPassForIntra8x8Pred(&(P_Z), block_available_up_left, block_available_up, block_available_left); + + // Mode DIAG_DOWN_LEFT_PRED + *Pred++ = (imgpel) ((P_A + P_C + 2*(P_B) + 2) >> 2); + *Pred++ = (imgpel) ((P_B + P_D + 2*(P_C) + 2) >> 2); + *Pred++ = (imgpel) ((P_C + P_E + 2*(P_D) + 2) >> 2); + *Pred++ = (imgpel) ((P_D + P_F + 2*(P_E) + 2) >> 2); + *Pred++ = (imgpel) ((P_E + P_G + 2*(P_F) + 2) >> 2); + *Pred++ = (imgpel) ((P_F + P_H + 2*(P_G) + 2) >> 2); + *Pred++ = (imgpel) ((P_G + P_I + 2*(P_H) + 2) >> 2); + *Pred++ = (imgpel) ((P_H + P_J + 2*(P_I) + 2) >> 2); + *Pred++ = (imgpel) ((P_I + P_K + 2*(P_J) + 2) >> 2); + *Pred++ = (imgpel) ((P_J + P_L + 2*(P_K) + 2) >> 2); + *Pred++ = (imgpel) ((P_K + P_M + 2*(P_L) + 2) >> 2); + *Pred++ = (imgpel) ((P_L + P_N + 2*(P_M) + 2) >> 2); + *Pred++ = (imgpel) ((P_M + P_O + 2*(P_N) + 2) >> 2); + *Pred++ = (imgpel) ((P_N + P_P + 2*(P_O) + 2) >> 2); + *Pred = (imgpel) ((P_O + 3*(P_P) + 2) >> 2); + + Pred = &PredArray[ 0]; + + memcpy(&mpr[joff++][ioff], Pred++, 8 * sizeof(imgpel)); + memcpy(&mpr[joff++][ioff], Pred++, 8 * sizeof(imgpel)); + memcpy(&mpr[joff++][ioff], Pred++, 8 * sizeof(imgpel)); + memcpy(&mpr[joff++][ioff], Pred++, 8 * sizeof(imgpel)); + memcpy(&mpr[joff++][ioff], Pred++, 8 * sizeof(imgpel)); + memcpy(&mpr[joff++][ioff], Pred++, 8 * sizeof(imgpel)); + memcpy(&mpr[joff++][ioff], Pred++, 8 * sizeof(imgpel)); + memcpy(&mpr[joff ][ioff], Pred , 8 * sizeof(imgpel)); + + return DECODING_OK; +} + +/*! + *********************************************************************** + * \brief + * makes and returns 8x8 vertical right prediction mode + * + * \return + * DECODING_OK decoding of intraprediction mode was successful \n + * + *********************************************************************** + */ +static inline int intra8x8_vert_right_pred(Macroblock *currMB, //!< current macroblock + ColorPlane pl, //!< current image plane + int ioff, //!< pixel offset X within MB + int joff) //!< pixel offset Y within MB +{ + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + + imgpel PredPel[25]; // array of predictor pels + imgpel PredArray[22]; // array of final prediction values + imgpel **imgY = (pl) ? currSlice->dec_picture->imgUV[pl - 1] : currSlice->dec_picture->imgY; // For MB level frame/field coding tools -- set default to imgY + + PixelPos pix_a; + PixelPos pix_b, pix_c, pix_d; + + int block_available_up; + int block_available_left; + int block_available_up_left; + int block_available_up_right; + + imgpel *pred_pels, **mpr = currSlice->mb_pred[pl]; + int *mb_size = p_Vid->mb_size[IS_LUMA]; + + getNonAffNeighbour(currMB, ioff - 1, joff , mb_size, &pix_a); + getNonAffNeighbour(currMB, ioff , joff - 1, mb_size, &pix_b); + getNonAffNeighbour(currMB, ioff + 8, joff - 1, mb_size, &pix_c); + getNonAffNeighbour(currMB, ioff - 1, joff - 1, mb_size, &pix_d); + + pix_c.available = pix_c.available &&!(ioff == 8 && joff == 8); + + if (p_Vid->active_pps->constrained_intra_pred_flag) + { + block_available_left = pix_a.available ? currSlice->intra_block [pix_a.mb_addr]: 0; + block_available_up = pix_b.available ? currSlice->intra_block [pix_b.mb_addr] : 0; + block_available_up_right = pix_c.available ? currSlice->intra_block [pix_c.mb_addr] : 0; + block_available_up_left = pix_d.available ? currSlice->intra_block [pix_d.mb_addr] : 0; + } + else + { + block_available_left = pix_a.available; + block_available_up = pix_b.available; + block_available_up_right = pix_c.available; + block_available_up_left = pix_d.available; + } + + if ((!block_available_up)||(!block_available_left)||(!block_available_up_left)) + printf ("warning: Intra_8x8_Vertical_Right prediction mode not allowed at mb %d\n", (int) currSlice->current_mb_nr); + + // form predictor pels + if (block_available_up) + { + pred_pels = &imgY[pix_b.pos_y][pix_b.pos_x]; + P_A = pred_pels[0]; + P_B = pred_pels[1]; + P_C = pred_pels[2]; + P_D = pred_pels[3]; + P_E = pred_pels[4]; + P_F = pred_pels[5]; + P_G = pred_pels[6]; + P_H = pred_pels[7]; + } + else + { + P_A = P_B = P_C = P_D = P_E = P_F = P_G = P_H = (imgpel) p_Vid->dc_pred_value_comp[pl]; + } + + if (block_available_up_right) + { + pred_pels = &imgY[pix_c.pos_y][pix_c.pos_x]; + P_I = pred_pels[0]; + P_J = pred_pels[1]; + P_K = pred_pels[2]; + P_L = pred_pels[3]; + P_M = pred_pels[4]; + P_N = pred_pels[5]; + P_O = pred_pels[6]; + P_P = pred_pels[7]; + + } + else + { + P_I = P_J = P_K = P_L = P_M = P_N = P_O = P_P = P_H; + } + + if (block_available_left) + { + imgpel **img_pred = &imgY[pix_a.pos_y]; + int pos_x = pix_a.pos_x; + P_Q = *(*(img_pred ++) + pos_x); + P_R = *(*(img_pred ++) + pos_x); + P_S = *(*(img_pred ++) + pos_x); + P_T = *(*(img_pred ++) + pos_x); + P_U = *(*(img_pred ++) + pos_x); + P_V = *(*(img_pred ++) + pos_x); + P_W = *(*(img_pred ++) + pos_x); + P_X = *(*(img_pred ) + pos_x); + } + else + { + P_Q = P_R = P_S = P_T = P_U = P_V = P_W = P_X = (imgpel) p_Vid->dc_pred_value_comp[pl]; + } + + if (block_available_up_left) + { + P_Z = imgY[pix_d.pos_y][pix_d.pos_x]; + } + else + { + P_Z = (imgpel) p_Vid->dc_pred_value_comp[pl]; + } + + LowPassForIntra8x8Pred(&(P_Z), block_available_up_left, block_available_up, block_available_left); + + PredArray[ 0] = (imgpel) ((P_V + P_T + 2*P_U + 2) >> 2); + PredArray[ 1] = (imgpel) ((P_T + P_R + 2*P_S + 2) >> 2); + PredArray[ 2] = (imgpel) ((P_R + P_Z + 2*P_Q + 2) >> 2); + PredArray[ 3] = (imgpel) ((P_Z + P_A + 1) >> 1); + PredArray[ 4] = (imgpel) ((P_A + P_B + 1) >> 1); + PredArray[ 5] = (imgpel) ((P_B + P_C + 1) >> 1); + PredArray[ 6] = (imgpel) ((P_C + P_D + 1) >> 1); + PredArray[ 7] = (imgpel) ((P_D + P_E + 1) >> 1); + PredArray[ 8] = (imgpel) ((P_E + P_F + 1) >> 1); + PredArray[ 9] = (imgpel) ((P_F + P_G + 1) >> 1); + PredArray[10] = (imgpel) ((P_G + P_H + 1) >> 1); + + PredArray[11] = (imgpel) ((P_W + P_U + 2*P_V + 2) >> 2); + PredArray[12] = (imgpel) ((P_U + P_S + 2*P_T + 2) >> 2); + PredArray[13] = (imgpel) ((P_S + P_Q + 2*P_R + 2) >> 2); + PredArray[14] = (imgpel) ((P_Q + P_A + 2*P_Z + 2) >> 2); + PredArray[15] = (imgpel) ((P_Z + P_B + 2*P_A + 2) >> 2); + PredArray[16] = (imgpel) ((P_A + P_C + 2*P_B + 2) >> 2); + PredArray[17] = (imgpel) ((P_B + P_D + 2*P_C + 2) >> 2); + PredArray[18] = (imgpel) ((P_C + P_E + 2*P_D + 2) >> 2); + PredArray[19] = (imgpel) ((P_D + P_F + 2*P_E + 2) >> 2); + PredArray[20] = (imgpel) ((P_E + P_G + 2*P_F + 2) >> 2); + PredArray[21] = (imgpel) ((P_F + P_H + 2*P_G + 2) >> 2); + + memcpy(&mpr[joff++][ioff], &PredArray[ 3], 8 * sizeof(imgpel)); + memcpy(&mpr[joff++][ioff], &PredArray[14], 8 * sizeof(imgpel)); + memcpy(&mpr[joff++][ioff], &PredArray[ 2], 8 * sizeof(imgpel)); + memcpy(&mpr[joff++][ioff], &PredArray[13], 8 * sizeof(imgpel)); + memcpy(&mpr[joff++][ioff], &PredArray[ 1], 8 * sizeof(imgpel)); + memcpy(&mpr[joff++][ioff], &PredArray[12], 8 * sizeof(imgpel)); + memcpy(&mpr[joff++][ioff], &PredArray[ 0], 8 * sizeof(imgpel)); + memcpy(&mpr[joff ][ioff], &PredArray[11], 8 * sizeof(imgpel)); + + return DECODING_OK; +} + + +/*! + *********************************************************************** + * \brief + * makes and returns 8x8 vertical left prediction mode + * + * \return + * DECODING_OK decoding of intraprediction mode was successful \n + * + *********************************************************************** + */ +static inline int intra8x8_vert_left_pred(Macroblock *currMB, //!< current macroblock + ColorPlane pl, //!< current image plane + int ioff, //!< pixel offset X within MB + int joff) //!< pixel offset Y within MB +{ + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + + imgpel PredPel[25]; // array of predictor pels + imgpel PredArray[22]; // array of final prediction values + imgpel *pred_pel = &PredArray[0]; + imgpel **imgY = (pl) ? currSlice->dec_picture->imgUV[pl - 1] : currSlice->dec_picture->imgY; // For MB level frame/field coding tools -- set default to imgY + + PixelPos pix_a; + PixelPos pix_b, pix_c, pix_d; + + int block_available_up; + int block_available_left; + int block_available_up_left; + int block_available_up_right; + + imgpel *pred_pels, **mpr = currSlice->mb_pred[pl]; + int *mb_size = p_Vid->mb_size[IS_LUMA]; + + getNonAffNeighbour(currMB, ioff - 1, joff , mb_size, &pix_a); + getNonAffNeighbour(currMB, ioff , joff - 1, mb_size, &pix_b); + getNonAffNeighbour(currMB, ioff + 8, joff - 1, mb_size, &pix_c); + getNonAffNeighbour(currMB, ioff - 1, joff - 1, mb_size, &pix_d); + + pix_c.available = pix_c.available &&!(ioff == 8 && joff == 8); + + if (p_Vid->active_pps->constrained_intra_pred_flag) + { + block_available_left = pix_a.available ? currSlice->intra_block [pix_a.mb_addr] : 0; + block_available_up = pix_b.available ? currSlice->intra_block [pix_b.mb_addr] : 0; + block_available_up_right = pix_c.available ? currSlice->intra_block [pix_c.mb_addr] : 0; + block_available_up_left = pix_d.available ? currSlice->intra_block [pix_d.mb_addr] : 0; + } + else + { + block_available_left = pix_a.available; + block_available_up = pix_b.available; + block_available_up_right = pix_c.available; + block_available_up_left = pix_d.available; + } + + if (!block_available_up) + printf ("warning: Intra_4x4_Vertical_Left prediction mode not allowed at mb %d\n", (int) currSlice->current_mb_nr); + + // form predictor pels + if (block_available_up) + { + pred_pels = &imgY[pix_b.pos_y][pix_b.pos_x]; + P_A = pred_pels[0]; + P_B = pred_pels[1]; + P_C = pred_pels[2]; + P_D = pred_pels[3]; + P_E = pred_pels[4]; + P_F = pred_pels[5]; + P_G = pred_pels[6]; + P_H = pred_pels[7]; + } + else + { + P_A = P_B = P_C = P_D = P_E = P_F = P_G = P_H = (imgpel) p_Vid->dc_pred_value_comp[pl]; + } + + if (block_available_up_right) + { + pred_pels = &imgY[pix_c.pos_y][pix_c.pos_x]; + P_I = pred_pels[0]; + P_J = pred_pels[1]; + P_K = pred_pels[2]; + P_L = pred_pels[3]; + P_M = pred_pels[4]; + P_N = pred_pels[5]; + P_O = pred_pels[6]; + P_P = pred_pels[7]; + + } + else + { + P_I = P_J = P_K = P_L = P_M = P_N = P_O = P_P = P_H; + } + + if (block_available_left) + { + imgpel **img_pred = &imgY[pix_a.pos_y]; + int pos_x = pix_a.pos_x; + P_Q = *(*(img_pred ++) + pos_x); + P_R = *(*(img_pred ++) + pos_x); + P_S = *(*(img_pred ++) + pos_x); + P_T = *(*(img_pred ++) + pos_x); + P_U = *(*(img_pred ++) + pos_x); + P_V = *(*(img_pred ++) + pos_x); + P_W = *(*(img_pred ++) + pos_x); + P_X = *(*(img_pred ) + pos_x); + } + else + { + P_Q = P_R = P_S = P_T = P_U = P_V = P_W = P_X = (imgpel) p_Vid->dc_pred_value_comp[pl]; + } + + if (block_available_up_left) + { + P_Z = imgY[pix_d.pos_y][pix_d.pos_x]; + } + else + { + P_Z = (imgpel) p_Vid->dc_pred_value_comp[pl]; + } + + LowPassForIntra8x8Pred(&(P_Z), block_available_up_left, block_available_up, block_available_left); + + *pred_pel++ = (imgpel) ((P_A + P_B + 1) >> 1); + *pred_pel++ = (imgpel) ((P_B + P_C + 1) >> 1); + *pred_pel++ = (imgpel) ((P_C + P_D + 1) >> 1); + *pred_pel++ = (imgpel) ((P_D + P_E + 1) >> 1); + *pred_pel++ = (imgpel) ((P_E + P_F + 1) >> 1); + *pred_pel++ = (imgpel) ((P_F + P_G + 1) >> 1); + *pred_pel++ = (imgpel) ((P_G + P_H + 1) >> 1); + *pred_pel++ = (imgpel) ((P_H + P_I + 1) >> 1); + *pred_pel++ = (imgpel) ((P_I + P_J + 1) >> 1); + *pred_pel++ = (imgpel) ((P_J + P_K + 1) >> 1); + *pred_pel++ = (imgpel) ((P_K + P_L + 1) >> 1); + *pred_pel++ = (imgpel) ((P_A + P_C + 2*P_B + 2) >> 2); + *pred_pel++ = (imgpel) ((P_B + P_D + 2*P_C + 2) >> 2); + *pred_pel++ = (imgpel) ((P_C + P_E + 2*P_D + 2) >> 2); + *pred_pel++ = (imgpel) ((P_D + P_F + 2*P_E + 2) >> 2); + *pred_pel++ = (imgpel) ((P_E + P_G + 2*P_F + 2) >> 2); + *pred_pel++ = (imgpel) ((P_F + P_H + 2*P_G + 2) >> 2); + *pred_pel++ = (imgpel) ((P_G + P_I + 2*P_H + 2) >> 2); + *pred_pel++ = (imgpel) ((P_H + P_J + 2*P_I + 2) >> 2); + *pred_pel++ = (imgpel) ((P_I + P_K + 2*P_J + 2) >> 2); + *pred_pel++ = (imgpel) ((P_J + P_L + 2*P_K + 2) >> 2); + *pred_pel = (imgpel) ((P_K + P_M + 2*P_L + 2) >> 2); + + memcpy(&mpr[joff++][ioff], &PredArray[ 0], 8 * sizeof(imgpel)); + memcpy(&mpr[joff++][ioff], &PredArray[11], 8 * sizeof(imgpel)); + memcpy(&mpr[joff++][ioff], &PredArray[ 1], 8 * sizeof(imgpel)); + memcpy(&mpr[joff++][ioff], &PredArray[12], 8 * sizeof(imgpel)); + memcpy(&mpr[joff++][ioff], &PredArray[ 2], 8 * sizeof(imgpel)); + memcpy(&mpr[joff++][ioff], &PredArray[13], 8 * sizeof(imgpel)); + memcpy(&mpr[joff++][ioff], &PredArray[ 3], 8 * sizeof(imgpel)); + memcpy(&mpr[joff ][ioff], &PredArray[14], 8 * sizeof(imgpel)); + + return DECODING_OK; +} + +/*! + *********************************************************************** + * \brief + * makes and returns 8x8 horizontal up prediction mode + * + * \return + * DECODING_OK decoding of intraprediction mode was successful \n + * + *********************************************************************** + */ +static inline int intra8x8_hor_up_pred(Macroblock *currMB, //!< current macroblock + ColorPlane pl, //!< current image plane + int ioff, //!< pixel offset X within MB + int joff) //!< pixel offset Y within MB +{ + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + + imgpel PredPel[25]; // array of predictor pels + imgpel PredArray[16]; // array of final prediction values + imgpel **imgY = (pl) ? currSlice->dec_picture->imgUV[pl - 1] : currSlice->dec_picture->imgY; // For MB level frame/field coding tools -- set default to imgY + + PixelPos pix_a; + PixelPos pix_b, pix_c, pix_d; + + int block_available_up; + int block_available_left; + int block_available_up_left; + int block_available_up_right; + int jpos0 = joff , jpos1 = joff + 1, jpos2 = joff + 2, jpos3 = joff + 3; + int jpos4 = joff + 4, jpos5 = joff + 5, jpos6 = joff + 6, jpos7 = joff + 7; + +#if (IMGTYPE == 0) + int ipos3 = ioff + 3, ipos5 = ioff + 5, ipos7 = ioff + 7; +#else + int ipos0 = ioff , ipos1 = ioff + 1, ipos2 = ioff + 2, ipos3 = ioff + 3; + int ipos4 = ioff + 4, ipos5 = ioff + 5, ipos6 = ioff + 6, ipos7 = ioff + 7; +#endif + imgpel *pred_pels, **mpr = currSlice->mb_pred[pl]; + int *mb_size = p_Vid->mb_size[IS_LUMA]; + + getNonAffNeighbour(currMB, ioff - 1, joff , mb_size, &pix_a); + getNonAffNeighbour(currMB, ioff , joff - 1, mb_size, &pix_b); + getNonAffNeighbour(currMB, ioff + 8, joff - 1, mb_size, &pix_c); + getNonAffNeighbour(currMB, ioff - 1, joff - 1, mb_size, &pix_d); + + pix_c.available = pix_c.available &&!(ioff == 8 && joff == 8); + + if (p_Vid->active_pps->constrained_intra_pred_flag) + { + block_available_left = pix_a.available ? currSlice->intra_block [pix_a.mb_addr] : 0; + block_available_up = pix_b.available ? currSlice->intra_block [pix_b.mb_addr] : 0; + block_available_up_right = pix_c.available ? currSlice->intra_block [pix_c.mb_addr] : 0; + block_available_up_left = pix_d.available ? currSlice->intra_block [pix_d.mb_addr] : 0; + } + else + { + block_available_left = pix_a.available; + block_available_up = pix_b.available; + block_available_up_right = pix_c.available; + block_available_up_left = pix_d.available; + } + + if (!block_available_left) + printf ("warning: Intra_8x8_Horizontal_Up prediction mode not allowed at mb %d\n", (int) currSlice->current_mb_nr); + + // form predictor pels + if (block_available_up) + { + pred_pels = &imgY[pix_b.pos_y][pix_b.pos_x]; + P_A = pred_pels[0]; + P_B = pred_pels[1]; + P_C = pred_pels[2]; + P_D = pred_pels[3]; + P_E = pred_pels[4]; + P_F = pred_pels[5]; + P_G = pred_pels[6]; + P_H = pred_pels[7]; + } + else + { + P_A = P_B = P_C = P_D = P_E = P_F = P_G = P_H = (imgpel) p_Vid->dc_pred_value_comp[pl]; + } + + if (block_available_up_right) + { + pred_pels = &imgY[pix_c.pos_y][pix_c.pos_x]; + P_I = pred_pels[0]; + P_J = pred_pels[1]; + P_K = pred_pels[2]; + P_L = pred_pels[3]; + P_M = pred_pels[4]; + P_N = pred_pels[5]; + P_O = pred_pels[6]; + P_P = pred_pels[7]; + + } + else + { + P_I = P_J = P_K = P_L = P_M = P_N = P_O = P_P = P_H; + } + + if (block_available_left) + { + imgpel **img_pred = &imgY[pix_a.pos_y]; + int pos_x = pix_a.pos_x; + P_Q = *(*(img_pred ++) + pos_x); + P_R = *(*(img_pred ++) + pos_x); + P_S = *(*(img_pred ++) + pos_x); + P_T = *(*(img_pred ++) + pos_x); + P_U = *(*(img_pred ++) + pos_x); + P_V = *(*(img_pred ++) + pos_x); + P_W = *(*(img_pred ++) + pos_x); + P_X = *(*(img_pred ) + pos_x); + } + else + { + P_Q = P_R = P_S = P_T = P_U = P_V = P_W = P_X = (imgpel) p_Vid->dc_pred_value_comp[pl]; + } + + if (block_available_up_left) + { + P_Z = imgY[pix_d.pos_y][pix_d.pos_x]; + } + else + { + P_Z = (imgpel) p_Vid->dc_pred_value_comp[pl]; + } + + LowPassForIntra8x8Pred(&(P_Z), block_available_up_left, block_available_up, block_available_left); + + PredArray[ 0] = (imgpel) ((P_Q + P_R + 1) >> 1); + PredArray[ 1] = (imgpel) ((P_S + P_Q + 2*P_R + 2) >> 2); + PredArray[ 2] = (imgpel) ((P_R + P_S + 1) >> 1); + PredArray[ 3] = (imgpel) ((P_T + P_R + 2*P_S + 2) >> 2); + PredArray[ 4] = (imgpel) ((P_S + P_T + 1) >> 1); + PredArray[ 5] = (imgpel) ((P_U + P_S + 2*P_T + 2) >> 2); + PredArray[ 6] = (imgpel) ((P_T + P_U + 1) >> 1); + PredArray[ 7] = (imgpel) ((P_V + P_T + 2*P_U + 2) >> 2); + PredArray[ 8] = (imgpel) ((P_U + P_V + 1) >> 1); + PredArray[ 9] = (imgpel) ((P_W + P_U + 2*P_V + 2) >> 2); + PredArray[10] = (imgpel) ((P_V + P_W + 1) >> 1); + PredArray[11] = (imgpel) ((P_X + P_V + 2*P_W + 2) >> 2); + PredArray[12] = (imgpel) ((P_W + P_X + 1) >> 1); + PredArray[13] = (imgpel) ((P_W + 3*P_X + 2) >> 2); + PredArray[14] = (imgpel) P_X; + + memcpy(&mpr[jpos0][ioff], &PredArray[0], 8 * sizeof(imgpel)); + memcpy(&mpr[jpos1][ioff], &PredArray[2], 8 * sizeof(imgpel)); + memcpy(&mpr[jpos2][ioff], &PredArray[4], 8 * sizeof(imgpel)); + memcpy(&mpr[jpos3][ioff], &PredArray[6], 8 * sizeof(imgpel)); + memcpy(&mpr[jpos4][ioff], &PredArray[8], 7 * sizeof(imgpel)); + memcpy(&mpr[jpos5][ioff], &PredArray[10], 5 * sizeof(imgpel)); + memcpy(&mpr[jpos6][ioff], &PredArray[12], 3 * sizeof(imgpel)); + +#if (IMGTYPE == 0) + mpr[jpos4][ipos7] = PredArray[14]; + memset(&mpr[jpos5][ipos5], PredArray[14], 3 * sizeof(imgpel)); + memset(&mpr[jpos6][ipos3], PredArray[14], 5 * sizeof(imgpel)); + memset(&mpr[jpos7][ioff ], PredArray[14], 8 * sizeof(imgpel)); +#else + mpr[jpos4][ipos7] = PredArray[14]; + mpr[jpos5][ipos5] = mpr[jpos5][ipos6] = mpr[jpos5][ipos7] = PredArray[14]; + mpr[jpos6][ipos3] = mpr[jpos6][ipos4] = mpr[jpos6][ipos5] = mpr[jpos6][ipos6] = mpr[jpos6][ipos7] = PredArray[14]; + mpr[jpos7][ipos0] = mpr[jpos7][ipos1] = mpr[jpos7][ipos2] = mpr[jpos7][ipos3] = + mpr[jpos7][ipos4] = mpr[jpos7][ipos5] = mpr[jpos7][ipos6] = mpr[jpos7][ipos7] = PredArray[14]; +#endif + return DECODING_OK; +} + +/*! + *********************************************************************** + * \brief + * makes and returns 8x8 horizontal down prediction mode + * + * \return + * DECODING_OK decoding of intraprediction mode was successful \n + * + *********************************************************************** + */ +static inline int intra8x8_hor_down_pred(Macroblock *currMB, //!< current macroblock + ColorPlane pl, //!< current image plane + int ioff, //!< pixel offset X within MB + int joff) //!< pixel offset Y within MB +{ + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + + imgpel PredPel[25]; // array of predictor pels + imgpel PredArray[22]; // array of final prediction values + imgpel **imgY = (pl) ? currSlice->dec_picture->imgUV[pl - 1] : currSlice->dec_picture->imgY; // For MB level frame/field coding tools -- set default to imgY + + PixelPos pix_a; + PixelPos pix_b, pix_c, pix_d; + + int block_available_up; + int block_available_left; + int block_available_up_left; + int block_available_up_right; + int jpos0 = joff , jpos1 = joff + 1, jpos2 = joff + 2, jpos3 = joff + 3; + int jpos4 = joff + 4, jpos5 = joff + 5, jpos6 = joff + 6, jpos7 = joff + 7; + + imgpel *pred_pels, **mpr = currSlice->mb_pred[pl]; + int *mb_size = p_Vid->mb_size[IS_LUMA]; + + getNonAffNeighbour(currMB, ioff - 1, joff , mb_size, &pix_a); + getNonAffNeighbour(currMB, ioff , joff - 1, mb_size, &pix_b); + getNonAffNeighbour(currMB, ioff + 8, joff - 1, mb_size, &pix_c); + getNonAffNeighbour(currMB, ioff - 1, joff - 1, mb_size, &pix_d); + + pix_c.available = pix_c.available &&!(ioff == 8 && joff == 8); + + if (p_Vid->active_pps->constrained_intra_pred_flag) + { + block_available_left = pix_a.available ? currSlice->intra_block [pix_a.mb_addr] : 0; + block_available_up = pix_b.available ? currSlice->intra_block [pix_b.mb_addr] : 0; + block_available_up_right = pix_c.available ? currSlice->intra_block [pix_c.mb_addr] : 0; + block_available_up_left = pix_d.available ? currSlice->intra_block [pix_d.mb_addr] : 0; + } + else + { + block_available_left = pix_a.available; + block_available_up = pix_b.available; + block_available_up_right = pix_c.available; + block_available_up_left = pix_d.available; + } + + if ((!block_available_up)||(!block_available_left)||(!block_available_up_left)) + printf ("warning: Intra_8x8_Horizontal_Down prediction mode not allowed at mb %d\n", (int) currSlice->current_mb_nr); + + // form predictor pels + if (block_available_up) + { + pred_pels = &imgY[pix_b.pos_y][pix_b.pos_x]; + P_A = pred_pels[0]; + P_B = pred_pels[1]; + P_C = pred_pels[2]; + P_D = pred_pels[3]; + P_E = pred_pels[4]; + P_F = pred_pels[5]; + P_G = pred_pels[6]; + P_H = pred_pels[7]; + } + else + { + P_A = P_B = P_C = P_D = P_E = P_F = P_G = P_H = (imgpel) p_Vid->dc_pred_value_comp[pl]; + } + + if (block_available_up_right) + { + pred_pels = &imgY[pix_c.pos_y][pix_c.pos_x]; + P_I = pred_pels[0]; + P_J = pred_pels[1]; + P_K = pred_pels[2]; + P_L = pred_pels[3]; + P_M = pred_pels[4]; + P_N = pred_pels[5]; + P_O = pred_pels[6]; + P_P = pred_pels[7]; + + } + else + { + P_I = P_J = P_K = P_L = P_M = P_N = P_O = P_P = P_H; + } + + if (block_available_left) + { + imgpel **img_pred = &imgY[pix_a.pos_y]; + int pos_x = pix_a.pos_x; + P_Q = *(*(img_pred ++) + pos_x); + P_R = *(*(img_pred ++) + pos_x); + P_S = *(*(img_pred ++) + pos_x); + P_T = *(*(img_pred ++) + pos_x); + P_U = *(*(img_pred ++) + pos_x); + P_V = *(*(img_pred ++) + pos_x); + P_W = *(*(img_pred ++) + pos_x); + P_X = *(*(img_pred ) + pos_x); + } + else + { + P_Q = P_R = P_S = P_T = P_U = P_V = P_W = P_X = (imgpel) p_Vid->dc_pred_value_comp[pl]; + } + + if (block_available_up_left) + { + P_Z = imgY[pix_d.pos_y][pix_d.pos_x]; + } + else + { + P_Z = (imgpel) p_Vid->dc_pred_value_comp[pl]; + } + + LowPassForIntra8x8Pred(&(P_Z), block_available_up_left, block_available_up, block_available_left); + + PredArray[ 0] = (imgpel) ((P_X + P_W + 1) >> 1); + PredArray[ 1] = (imgpel) ((P_V + P_X + 2*P_W + 2) >> 2); + PredArray[ 2] = (imgpel) ((P_W + P_V + 1) >> 1); + PredArray[ 3] = (imgpel) ((P_U + P_W + 2*P_V + 2) >> 2); + PredArray[ 4] = (imgpel) ((P_V + P_U + 1) >> 1); + PredArray[ 5] = (imgpel) ((P_T + P_V + 2*P_U + 2) >> 2); + PredArray[ 6] = (imgpel) ((P_U + P_T + 1) >> 1); + PredArray[ 7] = (imgpel) ((P_S + P_U + 2*P_T + 2) >> 2); + PredArray[ 8] = (imgpel) ((P_T + P_S + 1) >> 1); + PredArray[ 9] = (imgpel) ((P_R + P_T + 2*P_S + 2) >> 2); + PredArray[10] = (imgpel) ((P_S + P_R + 1) >> 1); + PredArray[11] = (imgpel) ((P_Q + P_S + 2*P_R + 2) >> 2); + PredArray[12] = (imgpel) ((P_R + P_Q + 1) >> 1); + PredArray[13] = (imgpel) ((P_Z + P_R + 2*P_Q + 2) >> 2); + PredArray[14] = (imgpel) ((P_Q + P_Z + 1) >> 1); + PredArray[15] = (imgpel) ((P_Q + P_A + 2*P_Z + 2) >> 2); + PredArray[16] = (imgpel) ((P_Z + P_B + 2*P_A + 2) >> 2); + PredArray[17] = (imgpel) ((P_A + P_C + 2*P_B + 2) >> 2); + PredArray[18] = (imgpel) ((P_B + P_D + 2*P_C + 2) >> 2); + PredArray[19] = (imgpel) ((P_C + P_E + 2*P_D + 2) >> 2); + PredArray[20] = (imgpel) ((P_D + P_F + 2*P_E + 2) >> 2); + PredArray[21] = (imgpel) ((P_E + P_G + 2*P_F + 2) >> 2); + + memcpy(&mpr[jpos0][ioff], &PredArray[14], 8 * sizeof(imgpel)); + memcpy(&mpr[jpos1][ioff], &PredArray[12], 8 * sizeof(imgpel)); + memcpy(&mpr[jpos2][ioff], &PredArray[10], 8 * sizeof(imgpel)); + memcpy(&mpr[jpos3][ioff], &PredArray[ 8], 8 * sizeof(imgpel)); + memcpy(&mpr[jpos4][ioff], &PredArray[ 6], 8 * sizeof(imgpel)); + memcpy(&mpr[jpos5][ioff], &PredArray[ 4], 8 * sizeof(imgpel)); + memcpy(&mpr[jpos6][ioff], &PredArray[ 2], 8 * sizeof(imgpel)); + memcpy(&mpr[jpos7][ioff], &PredArray[ 0], 8 * sizeof(imgpel)); + + return DECODING_OK; +} + +/*! + ************************************************************************ + * \brief + * Make intra 8x8 prediction according to all 9 prediction modes. + * The routine uses left and upper neighbouring points from + * previous coded blocks to do this (if available). Notice that + * inaccessible neighbouring points are signalled with a negative + * value in the predmode array . + * + * \par Input: + * Starting point of current 8x8 block image position + * + ************************************************************************ + */ +int intrapred8x8_normal(Macroblock *currMB, //!< Current Macroblock + ColorPlane pl, //!< Current color plane + int ioff, //!< ioff + int joff) //!< joff + +{ + int block_x = (currMB->block_x) + (ioff >> 2); + int block_y = (currMB->block_y) + (joff >> 2); + byte predmode = currMB->p_Slice->ipredmode[block_y][block_x]; + + currMB->ipmode_DPCM = predmode; //For residual DPCM + + switch (predmode) + { + case DC_PRED: + return (intra8x8_dc_pred(currMB, pl, ioff, joff)); + break; + case VERT_PRED: + return (intra8x8_vert_pred(currMB, pl, ioff, joff)); + break; + case HOR_PRED: + return (intra8x8_hor_pred(currMB, pl, ioff, joff)); + break; + case DIAG_DOWN_RIGHT_PRED: + return (intra8x8_diag_down_right_pred(currMB, pl, ioff, joff)); + break; + case DIAG_DOWN_LEFT_PRED: + return (intra8x8_diag_down_left_pred(currMB, pl, ioff, joff)); + break; + case VERT_RIGHT_PRED: + return (intra8x8_vert_right_pred(currMB, pl, ioff, joff)); + break; + case VERT_LEFT_PRED: + return (intra8x8_vert_left_pred(currMB, pl, ioff, joff)); + break; + case HOR_UP_PRED: + return (intra8x8_hor_up_pred(currMB, pl, ioff, joff)); + break; + case HOR_DOWN_PRED: + return (intra8x8_hor_down_pred(currMB, pl, ioff, joff)); + default: + printf("Error: illegal intra_8x8 prediction mode: %d\n", (int) predmode); + return SEARCH_SYNC; + break; + } +} + + diff --git a/ldecod/src/intra_chroma_pred.c b/ldecod/src/intra_chroma_pred.c new file mode 100644 index 0000000..be81ade --- /dev/null +++ b/ldecod/src/intra_chroma_pred.c @@ -0,0 +1,363 @@ +/*! + ************************************************************************************* + * \file intra_chroma_pred.c + * + * \brief + * Functions for intra chroma prediction + * + * \author + * Main contributors (see contributors.h for copyright, + * address and affiliation details) + * - Alexis Michael Tourapis + * + ************************************************************************************* + */ +#include "global.h" +#include "block.h" +#include "mb_access.h" +#include "image.h" + + +static void intra_chroma_DC_single(imgpel **curr_img, int up_avail, int left_avail, PixelPos up, PixelPos left, int blk_x, int blk_y, int *pred, int direction ) +{ + int i; + int s0 = 0; + + if ((direction && up_avail) || (!left_avail && up_avail)) + { + imgpel *cur_pel = &curr_img[up.pos_y][up.pos_x + blk_x]; + for (i = 0; i < 4;++i) + s0 += *(cur_pel++); + *pred = (s0 + 2) >> 2; + } + else if (left_avail) + { + imgpel **cur_pel = &(curr_img[left.pos_y + blk_y - 1]); + int pos_x = left.pos_x; + for (i = 0; i < 4;++i) + s0 += *((*cur_pel++) + pos_x); + *pred = (s0 + 2) >> 2; + } +} + + +static void intra_chroma_DC_all(imgpel **curr_img, int up_avail, int left_avail, PixelPos up, PixelPos left, int blk_x, int blk_y, int *pred ) +{ + int i; + int s0 = 0, s1 = 0; + + if (up_avail) + { + imgpel *cur_pel = &curr_img[up.pos_y][up.pos_x + blk_x]; + for (i = 0; i < 4;++i) + s0 += *(cur_pel++); + } + + if (left_avail) + { + imgpel **cur_pel = &(curr_img[left.pos_y + blk_y - 1]); + int pos_x = left.pos_x; + for (i = 0; i < 4;++i) + s1 += *((*cur_pel++) + pos_x); + } + + if (up_avail && left_avail) + *pred = (s0 + s1 + 4) >> 3; + else if (up_avail) + *pred = (s0 + 2) >> 2; + else if (left_avail) + *pred = (s1 + 2) >> 2; +} + +static void intrapred_chroma_dc(Macroblock *currMB) +{ + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + StorablePicture *dec_picture = currSlice->dec_picture; + int b8, b4; + int yuv = dec_picture->chroma_format_idc - 1; + int blk_x, blk_y; + int pred, pred1; + static const int block_pos[3][4][4]= //[yuv][b8][b4] + { + { {0, 1, 2, 3},{0, 0, 0, 0},{0, 0, 0, 0},{0, 0, 0, 0}}, + { {0, 1, 2, 3},{2, 3, 2, 3},{0, 0, 0, 0},{0, 0, 0, 0}}, + { {0, 1, 2, 3},{1, 1, 3, 3},{2, 3, 2, 3},{3, 3, 3, 3}} + }; + + PixelPos up; //!< pixel position p(0,-1) + PixelPos left; //!< pixel positions p(-1, -1..16) + int up_avail, left_avail; + imgpel **imgUV0 = dec_picture->imgUV[0]; + imgpel **imgUV1 = dec_picture->imgUV[1]; + imgpel **mb_pred0 = currSlice->mb_pred[0 + 1]; + imgpel **mb_pred1 = currSlice->mb_pred[1 + 1]; + + + getNonAffNeighbour(currMB, -1, 0, p_Vid->mb_size[IS_CHROMA], &left); + getNonAffNeighbour(currMB, 0, -1, p_Vid->mb_size[IS_CHROMA], &up); + + if (!p_Vid->active_pps->constrained_intra_pred_flag) + { + up_avail = up.available; + left_avail = left.available; + } + else + { + up_avail = up.available ? currSlice->intra_block[up.mb_addr] : 0; + left_avail = left.available ? currSlice->intra_block[left.mb_addr]: 0; + } + + // DC prediction + // Note that unlike what is stated in many presentations and papers, this mode does not operate + // the same way as I_16x16 DC prediction. + for(b8 = 0; b8 < (p_Vid->num_uv_blocks) ;++b8) + { + for (b4 = 0; b4 < 4; ++b4) + { + blk_y = subblk_offset_y[yuv][b8][b4]; + blk_x = subblk_offset_x[yuv][b8][b4]; + pred = p_Vid->dc_pred_value_comp[1]; + pred1 = p_Vid->dc_pred_value_comp[2]; + //===== get prediction value ===== + switch (block_pos[yuv][b8][b4]) + { + case 0: //===== TOP LEFT ===== + intra_chroma_DC_all (imgUV0, up_avail, left_avail, up, left, blk_x, blk_y + 1, &pred); + intra_chroma_DC_all (imgUV1, up_avail, left_avail, up, left, blk_x, blk_y + 1, &pred1); + break; + case 1: //===== TOP RIGHT ===== + intra_chroma_DC_single(imgUV0, up_avail, left_avail, up, left, blk_x, blk_y + 1, &pred, 1); + intra_chroma_DC_single(imgUV1, up_avail, left_avail, up, left, blk_x, blk_y + 1, &pred1, 1); + break; + case 2: //===== BOTTOM LEFT ===== + intra_chroma_DC_single(imgUV0, up_avail, left_avail, up, left, blk_x, blk_y + 1, &pred, 0); + intra_chroma_DC_single(imgUV1, up_avail, left_avail, up, left, blk_x, blk_y + 1, &pred1, 0); + break; + case 3: //===== BOTTOM RIGHT ===== + intra_chroma_DC_all (imgUV0, up_avail, left_avail, up, left, blk_x, blk_y + 1, &pred); + intra_chroma_DC_all (imgUV1, up_avail, left_avail, up, left, blk_x, blk_y + 1, &pred1); + break; + } + +#if (IMGTYPE == 0) + { + int jj; + for (jj = blk_y; jj < blk_y + BLOCK_SIZE; ++jj) + { + memset(&mb_pred0[jj][blk_x], pred, BLOCK_SIZE * sizeof(imgpel)); + memset(&mb_pred1[jj][blk_x], pred1, BLOCK_SIZE * sizeof(imgpel)); + } + } +#else + { + int jj, ii; + for (jj = blk_y; jj < blk_y + BLOCK_SIZE; ++jj) + { + for (ii = blk_x; ii < blk_x + BLOCK_SIZE; ++ii) + { + mb_pred0[jj][ii]=(imgpel) pred; + mb_pred1[jj][ii]=(imgpel) pred1; + } + } + } +#endif + } + } +} + +static void intrapred_chroma_hor(Macroblock *currMB) +{ + VideoParameters *p_Vid = currMB->p_Vid; + PixelPos a; //!< pixel positions p(-1, -1..16) + int left_avail; + + getNonAffNeighbour(currMB, -1, 0, p_Vid->mb_size[IS_CHROMA], &a); + + if (!p_Vid->active_pps->constrained_intra_pred_flag) + left_avail = a.available; + else + left_avail = a.available ? currMB->p_Slice->intra_block[a.mb_addr]: 0; + // Horizontal Prediction + if (!left_avail ) + error("unexpected HOR_PRED_8 chroma intra prediction mode",-1); + else + { + Slice *currSlice = currMB->p_Slice; + int cr_MB_x = p_Vid->mb_cr_size_x; + int cr_MB_y = p_Vid->mb_cr_size_y; + + int j; + StorablePicture *dec_picture = currSlice->dec_picture; +#if (IMGTYPE != 0) + int i, pred, pred1; +#endif + int pos_y = a.pos_y; + int pos_x = a.pos_x; + imgpel **mb_pred0 = currSlice->mb_pred[0 + 1]; + imgpel **mb_pred1 = currSlice->mb_pred[1 + 1]; + imgpel **i0 = &dec_picture->imgUV[0][pos_y]; + imgpel **i1 = &dec_picture->imgUV[1][pos_y]; + + for (j = 0; j < cr_MB_y; ++j) + { +#if (IMGTYPE == 0) + memset(mb_pred0[j], (*i0++)[pos_x], cr_MB_x * sizeof(imgpel)); + memset(mb_pred1[j], (*i1++)[pos_x], cr_MB_x * sizeof(imgpel)); +#else + pred = (*i0++)[pos_x]; + pred1 = (*i1++)[pos_x]; + for (i = 0; i < cr_MB_x; ++i) + { + mb_pred0[j][i]=(imgpel) pred; + mb_pred1[j][i]=(imgpel) pred1; + } +#endif + + } + } +} + +static void intrapred_chroma_ver(Macroblock *currMB) +{ + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + int j; + StorablePicture *dec_picture = currSlice->dec_picture; + + PixelPos up; //!< pixel position p(0,-1) + int up_avail; + int cr_MB_x = p_Vid->mb_cr_size_x; + int cr_MB_y = p_Vid->mb_cr_size_y; + getNonAffNeighbour(currMB, 0, -1, p_Vid->mb_size[IS_CHROMA], &up); + + if (!p_Vid->active_pps->constrained_intra_pred_flag) + up_avail = up.available; + else + up_avail = up.available ? currSlice->intra_block[up.mb_addr] : 0; + // Vertical Prediction + if (!up_avail) + error("unexpected VERT_PRED_8 chroma intra prediction mode",-1); + else + { + imgpel **mb_pred0 = currSlice->mb_pred[1]; + imgpel **mb_pred1 = currSlice->mb_pred[2]; + imgpel *i0 = &(dec_picture->imgUV[0][up.pos_y][up.pos_x]); + imgpel *i1 = &(dec_picture->imgUV[1][up.pos_y][up.pos_x]); + + for (j = 0; j < cr_MB_y; ++j) + { + memcpy(mb_pred0[j], i0, cr_MB_x * sizeof(imgpel)); + memcpy(mb_pred1[j], i1, cr_MB_x * sizeof(imgpel)); + } + } +} + +static void intrapred_chroma_plane(Macroblock *currMB) +{ + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + StorablePicture *dec_picture = currSlice->dec_picture; + + PixelPos up; //!< pixel position p(0,-1) + PixelPos up_left; + PixelPos left; //!< pixel positions p(-1, -1..16) + int up_avail, left_avail, left_up_avail; + + getNonAffNeighbour(currMB, -1, -1, p_Vid->mb_size[IS_CHROMA], &up_left); + getNonAffNeighbour(currMB, -1, 0, p_Vid->mb_size[IS_CHROMA], &left); + getNonAffNeighbour(currMB, 0, -1, p_Vid->mb_size[IS_CHROMA], &up); + + if (!p_Vid->active_pps->constrained_intra_pred_flag) + { + up_avail = up.available; + left_avail = left.available; + left_up_avail = up_left.available; + } + else + { + up_avail = up.available ? currSlice->intra_block[up.mb_addr] : 0; + left_avail = left.available ? currSlice->intra_block[left.mb_addr]: 0; + left_up_avail = up_left.available ? currSlice->intra_block[up_left.mb_addr]: 0; + } + // plane prediction + if (!left_up_avail || !left_avail || !up_avail) + error("unexpected PLANE_8 chroma intra prediction mode",-1); + else + { + int cr_MB_x = p_Vid->mb_cr_size_x; + int cr_MB_y = p_Vid->mb_cr_size_y; + int cr_MB_y2 = (cr_MB_y >> 1); + int cr_MB_x2 = (cr_MB_x >> 1); + + int i,j; + int ih, iv, ib, ic, iaa; + int uv; + for (uv = 0; uv < 2; uv++) + { + imgpel **imgUV = dec_picture->imgUV[uv]; + imgpel **mb_pred = currSlice->mb_pred[uv + 1]; + int max_imgpel_value = p_Vid->max_pel_value_comp[uv + 1]; + imgpel *upPred = &imgUV[up.pos_y][up.pos_x]; + int pos_x = up_left.pos_x; + int pos_y1 = left.pos_y + cr_MB_y2; + int pos_y2 = pos_y1 - 2; + //imgpel **predU1 = &imgUV[pos_y1]; + imgpel **predU2 = &imgUV[pos_y2]; + ih = cr_MB_x2 * (upPred[cr_MB_x - 1] - imgUV[up_left.pos_y][pos_x]); + + for (i = 0; i < cr_MB_x2 - 1; ++i) + ih += (i + 1) * (upPred[cr_MB_x2 + i] - upPred[cr_MB_x2 - 2 - i]); + + iv = cr_MB_y2 * (imgUV[left.pos_y + cr_MB_y - 1][pos_x] - imgUV[up_left.pos_y][pos_x]); + + for (i = 0; i < cr_MB_y2 - 1; ++i) + { + iv += (i + 1)*(*(imgUV[pos_y1++] + pos_x) - *((*predU2--) + pos_x)); + } + + ib= ((cr_MB_x == 8 ? 17 : 5) * ih + 2 * cr_MB_x)>>(cr_MB_x == 8 ? 5 : 6); + ic= ((cr_MB_y == 8 ? 17 : 5) * iv + 2 * cr_MB_y)>>(cr_MB_y == 8 ? 5 : 6); + + iaa = ((imgUV[pos_y1][pos_x] + upPred[cr_MB_x-1]) << 4); + + for (j = 0; j < cr_MB_y; ++j) + { + int plane = iaa + (j - cr_MB_y2 + 1) * ic + 16 - (cr_MB_x2 - 1) * ib; + + for (i = 0; i < cr_MB_x; ++i) + mb_pred[j][i]=(imgpel) iClip1(max_imgpel_value, ((i * ib + plane) >> 5)); + } + } + } +} + +/*! + ************************************************************************ + * \brief + * Chroma Intra prediction. Note that many operations can be moved + * outside since they are repeated for both components for no reason. + ************************************************************************ + */ +void intra_pred_chroma(Macroblock *currMB) +{ + switch (currMB->c_ipred_mode) + { + case DC_PRED_8: + intrapred_chroma_dc(currMB); + break; + case HOR_PRED_8: + intrapred_chroma_hor(currMB); + break; + case VERT_PRED_8: + intrapred_chroma_ver(currMB); + break; + case PLANE_8: + intrapred_chroma_plane(currMB); + break; + default: + error("illegal chroma intra prediction mode", 600); + break; + } +} + diff --git a/ldecod/src/intra_chroma_pred_mbaff.c b/ldecod/src/intra_chroma_pred_mbaff.c new file mode 100644 index 0000000..cbd0ae8 --- /dev/null +++ b/ldecod/src/intra_chroma_pred_mbaff.c @@ -0,0 +1,359 @@ +/*! + ************************************************************************************* + * \file intra_chroma_pred.c + * + * \brief + * Functions for intra chroma prediction + * + * \author + * Main contributors (see contributors.h for copyright, + * address and affiliation details) + * - Alexis Michael Tourapis + * + ************************************************************************************* + */ +#include "global.h" +#include "block.h" +#include "mb_access.h" +#include "image.h" + +static void intra_chroma_DC_single_mbaff(imgpel **curr_img, int up_avail, int left_avail, PixelPos up, PixelPos left[17], int blk_x, int blk_y, int *pred, int direction ) +{ + int i; + int s0 = 0; + + if ((direction && up_avail) || (!left_avail && up_avail)) + { + for (i = blk_x; i < (blk_x + 4);++i) + s0 += curr_img[up.pos_y][up.pos_x + i]; + *pred = (s0 + 2) >> 2; + } + else if (left_avail) + { + for (i = blk_y; i < (blk_y + 4);++i) + s0 += curr_img[left[i].pos_y][left[i].pos_x]; + *pred = (s0 + 2) >> 2; + } +} + +void intrapred_chroma_ver_mbaff(Macroblock *currMB) +{ + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + int j; + StorablePicture *dec_picture = currSlice->dec_picture; + + PixelPos up; //!< pixel position p(0,-1) + int up_avail; + int cr_MB_x = p_Vid->mb_cr_size_x; + int cr_MB_y = p_Vid->mb_cr_size_y; + + getAffNeighbour(currMB, 0, -1, p_Vid->mb_size[IS_CHROMA], &up); + + if (!p_Vid->active_pps->constrained_intra_pred_flag) + up_avail = up.available; + else + up_avail = up.available ? currSlice->intra_block[up.mb_addr] : 0; + // Vertical Prediction + if (!up_avail) + error("unexpected VERT_PRED_8 chroma intra prediction mode",-1); + else + { + imgpel **mb_pred0 = currSlice->mb_pred[1]; + imgpel **mb_pred1 = currSlice->mb_pred[2]; + imgpel *i0 = &(dec_picture->imgUV[0][up.pos_y][up.pos_x]); + imgpel *i1 = &(dec_picture->imgUV[1][up.pos_y][up.pos_x]); + + for (j = 0; j < cr_MB_y; ++j) + { + memcpy(&(mb_pred0[j][0]),i0, cr_MB_x * sizeof(imgpel)); + memcpy(&(mb_pred1[j][0]),i1, cr_MB_x * sizeof(imgpel)); + } + } +} + + +static void intra_chroma_DC_all_mbaff(imgpel **curr_img, int up_avail, int left_avail, PixelPos up, PixelPos left[17], int blk_x, int blk_y, int *pred ) +{ + int i; + int s0 = 0, s1 = 0; + + if (up_avail) + for (i = blk_x; i < (blk_x + 4);++i) + s0 += curr_img[up.pos_y][up.pos_x + i]; + + if (left_avail) + for (i = blk_y; i < (blk_y + 4);++i) + s1 += curr_img[left[i].pos_y][left[i].pos_x]; + + if (up_avail && left_avail) + *pred = (s0 + s1 + 4) >> 3; + else if (up_avail) + *pred = (s0 + 2) >> 2; + else if (left_avail) + *pred = (s1 + 2) >> 2; +} + + +/*! + ************************************************************************ + * \brief + * Chroma Intra prediction. Note that many operations can be moved + * outside since they are repeated for both components for no reason. + ************************************************************************ + */ +void intra_pred_chroma_mbaff(Macroblock *currMB) +{ + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + int i,j, ii, jj; + StorablePicture *dec_picture = currSlice->dec_picture; + + int ih, iv, ib, ic, iaa; + + int b8, b4; + int yuv = dec_picture->chroma_format_idc - 1; + int blk_x, blk_y; + int pred; + static const int block_pos[3][4][4]= //[yuv][b8][b4] + { + { {0, 1, 2, 3},{0, 0, 0, 0},{0, 0, 0, 0},{0, 0, 0, 0}}, + { {0, 1, 2, 3},{2, 3, 2, 3},{0, 0, 0, 0},{0, 0, 0, 0}}, + { {0, 1, 2, 3},{1, 1, 3, 3},{2, 3, 2, 3},{3, 3, 3, 3}} + }; + + switch (currMB->c_ipred_mode) + { + case DC_PRED_8: + { + PixelPos up; //!< pixel position p(0,-1) + PixelPos left[17]; //!< pixel positions p(-1, -1..16) + + int up_avail, left_avail[2]; + + int cr_MB_y = p_Vid->mb_cr_size_y; + int cr_MB_y2 = (cr_MB_y >> 1); + + for (i=0; i < cr_MB_y + 1 ; ++i) + getAffNeighbour(currMB, -1, i-1, p_Vid->mb_size[IS_CHROMA], &left[i]); + getAffNeighbour(currMB, 0, -1, p_Vid->mb_size[IS_CHROMA], &up); + + if (!p_Vid->active_pps->constrained_intra_pred_flag) + { + up_avail = up.available; + left_avail[0] = left_avail[1] = left[1].available; + } + else + { + up_avail = up.available ? currSlice->intra_block[up.mb_addr] : 0; + for (i=0, left_avail[0] = 1; i < cr_MB_y2;++i) + left_avail[0] &= left[i + 1].available ? currSlice->intra_block[left[i + 1].mb_addr]: 0; + + for (i = cr_MB_y2, left_avail[1] = 1; iintra_block[left[i + 1].mb_addr]: 0; + + } + // DC prediction + // Note that unlike what is stated in many presentations and papers, this mode does not operate + // the same way as I_16x16 DC prediction. + { + int pred1; + imgpel **imgUV0 = dec_picture->imgUV[0]; + imgpel **imgUV1 = dec_picture->imgUV[1]; + imgpel **mb_pred0 = currSlice->mb_pred[0 + 1]; + imgpel **mb_pred1 = currSlice->mb_pred[1 + 1]; + for(b8 = 0; b8 < (p_Vid->num_uv_blocks) ;++b8) + { + for (b4 = 0; b4 < 4; ++b4) + { + blk_y = subblk_offset_y[yuv][b8][b4]; + blk_x = subblk_offset_x[yuv][b8][b4]; + + pred = p_Vid->dc_pred_value_comp[1]; + pred1 = p_Vid->dc_pred_value_comp[2]; + //===== get prediction value ===== + switch (block_pos[yuv][b8][b4]) + { + case 0: //===== TOP LEFT ===== + intra_chroma_DC_all_mbaff (imgUV0, up_avail, left_avail[0], up, left, blk_x, blk_y + 1, &pred); + intra_chroma_DC_all_mbaff (imgUV1, up_avail, left_avail[0], up, left, blk_x, blk_y + 1, &pred1); + break; + case 1: //===== TOP RIGHT ===== + intra_chroma_DC_single_mbaff (imgUV0, up_avail, left_avail[0], up, left, blk_x, blk_y + 1, &pred, 1); + intra_chroma_DC_single_mbaff (imgUV1, up_avail, left_avail[0], up, left, blk_x, blk_y + 1, &pred1, 1); + break; + case 2: //===== BOTTOM LEFT ===== + intra_chroma_DC_single_mbaff (imgUV0, up_avail, left_avail[1], up, left, blk_x, blk_y + 1, &pred, 0); + intra_chroma_DC_single_mbaff (imgUV1, up_avail, left_avail[1], up, left, blk_x, blk_y + 1, &pred1, 0); + break; + case 3: //===== BOTTOM RIGHT ===== + intra_chroma_DC_all_mbaff (imgUV0, up_avail, left_avail[1], up, left, blk_x, blk_y + 1, &pred); + intra_chroma_DC_all_mbaff (imgUV1, up_avail, left_avail[1], up, left, blk_x, blk_y + 1, &pred1); + break; + } + + for (jj = blk_y; jj < blk_y + BLOCK_SIZE; ++jj) + { + for (ii = blk_x; ii < blk_x + BLOCK_SIZE; ++ii) + { + mb_pred0[jj][ii]=(imgpel) pred; + mb_pred1[jj][ii]=(imgpel) pred1; + } + } + } + } + } + } + break; + case HOR_PRED_8: + { + PixelPos left[17]; //!< pixel positions p(-1, -1..16) + + int left_avail[2]; + + int cr_MB_x = p_Vid->mb_cr_size_x; + int cr_MB_y = p_Vid->mb_cr_size_y; + int cr_MB_y2 = (cr_MB_y >> 1); + + for (i=0; i < cr_MB_y + 1 ; ++i) + getAffNeighbour(currMB, -1, i-1, p_Vid->mb_size[IS_CHROMA], &left[i]); + + if (!p_Vid->active_pps->constrained_intra_pred_flag) + { + left_avail[0] = left_avail[1] = left[1].available; + } + else + { + for (i=0, left_avail[0] = 1; i < cr_MB_y2;++i) + left_avail[0] &= left[i + 1].available ? currSlice->intra_block[left[i + 1].mb_addr]: 0; + + for (i = cr_MB_y2, left_avail[1] = 1; iintra_block[left[i + 1].mb_addr]: 0; + } + // Horizontal Prediction + if (!left_avail[0] || !left_avail[1]) + error("unexpected HOR_PRED_8 chroma intra prediction mode",-1); + else + { + int pred1; + imgpel **mb_pred0 = currSlice->mb_pred[0 + 1]; + imgpel **mb_pred1 = currSlice->mb_pred[1 + 1]; + imgpel **i0 = dec_picture->imgUV[0]; + imgpel **i1 = dec_picture->imgUV[1]; + for (j = 0; j < cr_MB_y; ++j) + { + pred = i0[left[1 + j].pos_y][left[1 + j].pos_x]; + pred1 = i1[left[1 + j].pos_y][left[1 + j].pos_x]; + for (i = 0; i < cr_MB_x; ++i) + { + mb_pred0[j][i]=(imgpel) pred; + mb_pred1[j][i]=(imgpel) pred1; + } + } + } + } + break; + case VERT_PRED_8: + { + PixelPos up; //!< pixel position p(0,-1) + + int up_avail; + + int cr_MB_x = p_Vid->mb_cr_size_x; + int cr_MB_y = p_Vid->mb_cr_size_y; + + getAffNeighbour(currMB, 0, -1, p_Vid->mb_size[IS_CHROMA], &up); + + if (!p_Vid->active_pps->constrained_intra_pred_flag) + up_avail = up.available; + else + up_avail = up.available ? currSlice->intra_block[up.mb_addr] : 0; + // Vertical Prediction + if (!up_avail) + error("unexpected VERT_PRED_8 chroma intra prediction mode",-1); + else + { + imgpel **mb_pred0 = currSlice->mb_pred[0 + 1]; + imgpel **mb_pred1 = currSlice->mb_pred[1 + 1]; + imgpel *i0 = &(dec_picture->imgUV[0][up.pos_y][up.pos_x]); + imgpel *i1 = &(dec_picture->imgUV[1][up.pos_y][up.pos_x]); + for (j = 0; j < cr_MB_y; ++j) + { + memcpy(&(mb_pred0[j][0]),i0, cr_MB_x * sizeof(imgpel)); + memcpy(&(mb_pred1[j][0]),i1, cr_MB_x * sizeof(imgpel)); + } + } + } + break; + case PLANE_8: + { + PixelPos up; //!< pixel position p(0,-1) + PixelPos left[17]; //!< pixel positions p(-1, -1..16) + + int up_avail, left_avail[2], left_up_avail; + + int cr_MB_x = p_Vid->mb_cr_size_x; + int cr_MB_y = p_Vid->mb_cr_size_y; + int cr_MB_y2 = (cr_MB_y >> 1); + int cr_MB_x2 = (cr_MB_x >> 1); + + for (i=0; i < cr_MB_y + 1 ; ++i) + getAffNeighbour(currMB, -1, i-1, p_Vid->mb_size[IS_CHROMA], &left[i]); + getAffNeighbour(currMB, 0, -1, p_Vid->mb_size[IS_CHROMA], &up); + + if (!p_Vid->active_pps->constrained_intra_pred_flag) + { + up_avail = up.available; + left_avail[0] = left_avail[1] = left[1].available; + left_up_avail = left[0].available; + } + else + { + up_avail = up.available ? currSlice->intra_block[up.mb_addr] : 0; + for (i=0, left_avail[0] = 1; i < cr_MB_y2;++i) + left_avail[0] &= left[i + 1].available ? currSlice->intra_block[left[i + 1].mb_addr]: 0; + + for (i = cr_MB_y2, left_avail[1] = 1; iintra_block[left[i + 1].mb_addr]: 0; + + left_up_avail = left[0].available ? currSlice->intra_block[left[0].mb_addr]: 0; + } + // plane prediction + if (!left_up_avail || !left_avail[0] || !left_avail[1] || !up_avail) + error("unexpected PLANE_8 chroma intra prediction mode",-1); + else + { + int uv; + for (uv = 0; uv < 2; uv++) + { + imgpel **imgUV = dec_picture->imgUV[uv]; + imgpel **mb_pred = currSlice->mb_pred[uv + 1]; + int max_imgpel_value = p_Vid->max_pel_value_comp[uv + 1]; + imgpel *upPred = &imgUV[up.pos_y][up.pos_x]; + + ih = cr_MB_x2 * (upPred[cr_MB_x - 1] - imgUV[left[0].pos_y][left[0].pos_x]); + for (i = 0; i < cr_MB_x2 - 1; ++i) + ih += (i + 1) * (upPred[cr_MB_x2 + i] - upPred[cr_MB_x2 - 2 - i]); + + iv = cr_MB_y2 * (imgUV[left[cr_MB_y].pos_y][left[cr_MB_y].pos_x] - imgUV[left[0].pos_y][left[0].pos_x]); + for (i = 0; i < cr_MB_y2 - 1; ++i) + iv += (i + 1)*(imgUV[left[cr_MB_y2 + 1 + i].pos_y][left[cr_MB_y2 + 1 + i].pos_x] - + imgUV[left[cr_MB_y2 - 1 - i].pos_y][left[cr_MB_y2 - 1 - i].pos_x]); + + ib= ((cr_MB_x == 8 ? 17 : 5) * ih + 2 * cr_MB_x)>>(cr_MB_x == 8 ? 5 : 6); + ic= ((cr_MB_y == 8 ? 17 : 5) * iv + 2 * cr_MB_y)>>(cr_MB_y == 8 ? 5 : 6); + + iaa=16*(imgUV[left[cr_MB_y].pos_y][left[cr_MB_y].pos_x] + upPred[cr_MB_x-1]); + + for (j = 0; j < cr_MB_y; ++j) + for (i = 0; i < cr_MB_x; ++i) + mb_pred[j][i]=(imgpel) iClip1(max_imgpel_value, ((iaa + (i - cr_MB_x2 + 1) * ib + (j - cr_MB_y2 + 1) * ic + 16) >> 5)); + } + } + } + break; + default: + error("illegal chroma intra prediction mode", 600); + break; + } +} diff --git a/ldecod/src/intra_pred_common.c b/ldecod/src/intra_pred_common.c new file mode 100644 index 0000000..d82e421 --- /dev/null +++ b/ldecod/src/intra_pred_common.c @@ -0,0 +1,43 @@ +/*! + ************************************************************************************* + * \file intra_pred_common.c + * + * \brief + * functions for setting up intra prediction modes + * + * \author + * Main contributors (see contributors.h for copyright, + * address and affiliation details) + * - Alexis Michael Tourapis + * + ************************************************************************************* + */ +#include "global.h" +#include "intra4x4_pred.h" +#include "intra8x8_pred.h" +#include "intra16x16_pred.h" +#include "mb_access.h" +#include "image.h" + + +extern void intra_pred_chroma (Macroblock *currMB); +extern void intra_pred_chroma_mbaff(Macroblock *currMB); + + +void set_intra_prediction_modes(Slice *currSlice) +{ + if (currSlice->mb_aff_frame_flag) + { + currSlice->intra_pred_4x4 = intra_pred_4x4_mbaff; + currSlice->intra_pred_8x8 = intra_pred_8x8_mbaff; + currSlice->intra_pred_16x16 = intra_pred_16x16_mbaff; + currSlice->intra_pred_chroma = intra_pred_chroma_mbaff; + } + else + { + currSlice->intra_pred_4x4 = intra_pred_4x4_normal; + currSlice->intra_pred_8x8 = intra_pred_8x8_normal; + currSlice->intra_pred_16x16 = intra_pred_16x16_normal; + currSlice->intra_pred_chroma = intra_pred_chroma; + } +} diff --git a/ldecod/src/ldecod.c b/ldecod/src/ldecod.c new file mode 100644 index 0000000..be81418 --- /dev/null +++ b/ldecod/src/ldecod.c @@ -0,0 +1,1693 @@ + +/*! +*********************************************************************** +* \mainpage +* This is the H.264/AVC decoder reference software. For detailed documentation +* see the comments in each file. +* +* The JM software web site is located at: +* http://iphome.hhi.de/suehring/tml +* +* For bug reporting and known issues see: +* https://ipbt.hhi.fraunhofer.de +* +* \author +* The main contributors are listed in contributors.h +* +* \version +* JM 18.1 (FRExt) +* +* \note +* tags are used for document system "doxygen" +* available at http://www.doxygen.org +*/ +/*! +* \file +* ldecod.c +* \brief +* H.264/AVC reference decoder project main() +* \author +* Main contributors (see contributors.h for copyright, address and affiliation details) +* - Inge Lille-Langoy +* - Rickard Sjoberg +* - Stephan Wenger +* - Jani Lainema +* - Sebastian Purreiter +* - Byeong-Moon Jeon +* - Gabi Blaettermann +* - Ye-Kui Wang +* - Valeri George +* - Karsten Suehring +* +*********************************************************************** +*/ + +#include "contributors.h" + +//#include + +#include "global.h" +#include "annexb.h" +#include "image.h" +#include "memalloc.h" +#include "mc_prediction.h" +#include "mbuffer.h" +#include "leaky_bucket.h" +#include "fmo.h" +#include "output.h" +#include "cabac.h" +#include "parset.h" +#include "sei.h" +#include "erc_api.h" +#include "quant.h" +#include "block.h" +#include "nalu.h" +#include "img_io.h" +#include "loopfilter.h" +#include "rtp.h" +#include "input.h" +#include "output.h" +#include "h264decoder.h" +#include "dec_statistics.h" +#include "typedefs.h" + +#include "report.h" +#include "report_dec.h" + +/* Edited by Joao Carreira */ +#include "transcode_dec.h" +#include "stream_parser.h" +#include "ferc_api.h" + +#define LOGFILE "log.dec" +#define DATADECFILE "dataDec.txt" +#define TRACEFILE "trace_dec.txt" + +// Decoder definition. This should be the only global variable in the entire +// software. Global variables should be avoided. +DecoderParams *p_Dec; +char errortext[ET_SIZE]; + +// Prototypes of static functions +static void Report (VideoParameters *p_Vid); +static void init (VideoParameters *p_Vid); + +void init_frext(VideoParameters *p_Vid); + + + + +/*! +************************************************************************ +* \brief +* Error handling procedure. Print error message to stdout and exit +* with supplied code. +* \param text +* Error message +* \param code +* Exit code +************************************************************************ +*/ +void error(char *text, int code) +{ + printf( "%s\n", text); + if (p_Dec) + { +#if (MVC_EXTENSION_ENABLE) + flush_dpb(p_Dec->p_Vid->p_Dpb_layer[1]); +#endif + flush_dpb(p_Dec->p_Vid->p_Dpb_layer[0]); + + } +#if(_DEBUG) +#if( WIN32 ) + system("pause"); +#endif +#endif + exit(code); +} + +static void reset_dpb( VideoParameters *p_Vid, DecodedPictureBuffer *p_Dpb ) +{ + p_Dpb->p_Vid = p_Vid; + p_Dpb->init_done = 0; +} +/*! +*********************************************************************** +* \brief +* Allocate the Video Parameters structure +* \par Output: +* Video Parameters VideoParameters *p_Vid +*********************************************************************** +*/ +static void alloc_video_params( VideoParameters **p_Vid) +{ + int i; + if ((*p_Vid = (VideoParameters *) calloc(1, sizeof(VideoParameters)))==NULL) + no_mem_exit("alloc_video_params: p_Vid"); + + if (((*p_Vid)->old_slice = (OldSliceParams *) calloc(1, sizeof(OldSliceParams)))==NULL) + no_mem_exit("alloc_video_params: p_Vid->old_slice"); +#if(MVC_EXTENSION_ENABLE) + for( i=0;isnr[i] = (SNRParameters *)calloc(1, sizeof(SNRParameters)))==NULL) + no_mem_exit("alloc_video_params: p_Vid->snr"); + } +#else + if (((*p_Vid)->snr = (SNRParameters *)calloc(1, sizeof(SNRParameters)))==NULL) + no_mem_exit("alloc_video_params: p_Vid->snr"); +#endif + + // Allocate new dpb buffer + for (i = 0; i < MAX_NUM_DPB_LAYERS; i++) + { + if (((*p_Vid)->p_Dpb_layer[i] = (DecodedPictureBuffer*)calloc(1, sizeof(DecodedPictureBuffer)))==NULL) + no_mem_exit("alloc_video_params: p_Vid->p_Dpb_layer[i]"); + (*p_Vid)->p_Dpb_layer[i]->layer_id = i; + reset_dpb(*p_Vid, (*p_Vid)->p_Dpb_layer[i]); + if(((*p_Vid)->p_EncodePar[i] = (CodingParameters *)calloc(1, sizeof(CodingParameters))) == NULL) + no_mem_exit("alloc_video_params:p_Vid->p_EncodePar[i]"); + ((*p_Vid)->p_EncodePar[i])->layer_id = i; + if(((*p_Vid)->p_LayerPar[i] = (LayerParameters *)calloc(1, sizeof(LayerParameters))) == NULL) + no_mem_exit("alloc_video_params:p_Vid->p_LayerPar[i]"); + ((*p_Vid)->p_LayerPar[i])->layer_id = i; + } + (*p_Vid)->global_init_done[0] = (*p_Vid)->global_init_done[1] = 0; + +#if (ENABLE_OUTPUT_TONEMAPPING) + if (((*p_Vid)->seiToneMapping = (ToneMappingSEI*)calloc(1, sizeof(ToneMappingSEI)))==NULL) + no_mem_exit("alloc_video_params: (*p_Vid)->seiToneMapping"); +#endif + + if(((*p_Vid)->ppSliceList = (Slice **) calloc(MAX_NUM_DECSLICES, sizeof(Slice *))) == NULL) + { + no_mem_exit("alloc_video_params: p_Vid->ppSliceList"); + } + (*p_Vid)->iNumOfSlicesAllocated = MAX_NUM_DECSLICES; + //(*p_Vid)->currentSlice = NULL; + (*p_Vid)->pNextSlice = NULL; + (*p_Vid)->nalu = AllocNALU(MAX_CODED_FRAME_SIZE); + (*p_Vid)->pDecOuputPic = (DecodedPicList *)calloc(1, sizeof(DecodedPicList)); + (*p_Vid)->pNextPPS = AllocPPS(); + (*p_Vid)->first_sps = TRUE; + + + for (i=0;inFramesConceal[i] = (*p_Vid)->nFramesDecoded[i] = (*p_Vid)->nFramesOut[i] = 0; + // Alloc frame pattern + if( !(*p_Vid)->p_PFrames[i]) + { + (*p_Vid)->p_PFrames[i] = (int *)calloc(INITAL_PFRAMES_LENGTH, sizeof(int)); + (*p_Vid)->n_PFrames[i] = INITAL_PFRAMES_LENGTH; + } + } + +} + + +/*! +*********************************************************************** +* \brief +* Allocate the Input structure +* \par Output: +* Input Parameters InputParameters *p_Vid +*********************************************************************** +*/ +static void alloc_params( InputParameters **p_Inp ) +{ + if ((*p_Inp = (InputParameters *) calloc(1, sizeof(InputParameters)))==NULL) + no_mem_exit("alloc_params: p_Inp"); +} + +/*! +*********************************************************************** +* \brief +* Allocate the Decoder Structure +* \par Output: +* Decoder Parameters +*********************************************************************** +*/ +static int alloc_decoder( DecoderParams **p_Dec) +{ + if ((*p_Dec = (DecoderParams *) calloc(1, sizeof(DecoderParams)))==NULL) + { + printf( "alloc_decoder: p_Dec\n"); + return -1; + } + + alloc_video_params(&((*p_Dec)->p_Vid)); + alloc_params(&((*p_Dec)->p_Inp)); + (*p_Dec)->p_Vid->p_Inp = (*p_Dec)->p_Inp; + (*p_Dec)->p_trace = NULL; + (*p_Dec)->bufferSize = 0; + (*p_Dec)->bitcounter = 0; + return 0; +} + +/*! +*********************************************************************** +* \brief +* Free the Image structure +* \par Input: +* Image Parameters VideoParameters *p_Vid +*********************************************************************** +*/ +static void free_img( VideoParameters *p_Vid) +{ + int i; + if (p_Vid != NULL) + { + if ( p_Vid->p_Inp->FileFormat == PAR_OF_ANNEXB ) + { + free_annex_b (&p_Vid->annex_b); + } +#if (ENABLE_OUTPUT_TONEMAPPING) + if (p_Vid->seiToneMapping != NULL) + { + free (p_Vid->seiToneMapping); + p_Vid->seiToneMapping = NULL; + } +#endif + + // Free new dpb layers + for (i = 0; i < MAX_NUM_DPB_LAYERS; i++) + { + if (p_Vid->p_Dpb_layer[i] != NULL) + { + free (p_Vid->p_Dpb_layer[i]); + p_Vid->p_Dpb_layer[i] = NULL; + } + if(p_Vid->p_EncodePar[i]) + { + free(p_Vid->p_EncodePar[i]); + p_Vid->p_EncodePar[i] = NULL; + } + if(p_Vid->p_LayerPar[i]) + { + free(p_Vid->p_LayerPar[i]); + p_Vid->p_LayerPar[i] = NULL; + } + } +#if( MVC_EXTENSION_ENABLE ) + for(i=0;isnr[i] != NULL) + { + free (p_Vid->snr[i]); + p_Vid->snr[i] = NULL; + } + } +#else + if (p_Vid->snr != NULL) + { + free (p_Vid->snr); + p_Vid->snr = NULL; + } +#endif + if (p_Vid->old_slice != NULL) + { + free (p_Vid->old_slice); + p_Vid->old_slice = NULL; + } + + if(p_Vid->pNextSlice) + { + free_slice(p_Vid->pNextSlice); + p_Vid->pNextSlice=NULL; + } + if(p_Vid->ppSliceList) + { + int i; + for(i=0; iiNumOfSlicesAllocated; i++) + if(p_Vid->ppSliceList[i]) + free_slice(p_Vid->ppSliceList[i]); + free(p_Vid->ppSliceList); + } + if(p_Vid->nalu) + { + FreeNALU(p_Vid->nalu); + p_Vid->nalu=NULL; + } + //free memory; + FreeDecPicList(p_Vid->pDecOuputPic); + if(p_Vid->pNextPPS) + { + FreePPS(p_Vid->pNextPPS); + p_Vid->pNextPPS = NULL; + } + + // clear decoder statistics +#if ENABLE_DEC_STATS + delete_dec_stats(p_Vid->dec_stats); + free (p_Vid->dec_stats); +#endif + + /* Edited by Joao Carreira */ + closeTranscodeModule(p_Vid); + free_parse_nalu(p_Vid); + free_prediction_parser( p_Vid ); + fercCloseFrameConcealment( p_Vid ); + for (i=0;ip_PFrames[i]) + { + free(p_Vid->p_PFrames[i]); + p_Vid->n_PFrames[i] = 0; + } + } + + + free (p_Vid); + p_Vid = NULL; + } +} + +void FreeDecPicList(DecodedPicList *pDecPicList) +{ + while(pDecPicList) + { + DecodedPicList *pPicNext = pDecPicList->pNext; + if(pDecPicList->pY) + { + free(pDecPicList->pY); + pDecPicList->pY = NULL; + pDecPicList->pU = NULL; + pDecPicList->pV = NULL; + } + free(pDecPicList); + pDecPicList = pPicNext; + } +} + +/*! +*********************************************************************** +* \brief +* Initilize some arrays +*********************************************************************** +*/ +static void init(VideoParameters *p_Vid) //!< video parameters +{ + int i; + InputParameters *p_Inp = p_Vid->p_Inp; + p_Vid->oldFrameSizeInMbs = (unsigned int) -1; + + p_Vid->imgY_ref = NULL; + p_Vid->imgUV_ref = NULL; + + p_Vid->recovery_point = 0; + p_Vid->recovery_point_found = 0; + p_Vid->recovery_poc = 0x7fffffff; /* set to a max value */ + + p_Vid->idr_psnr_number = p_Inp->ref_offset; + p_Vid->psnr_number=0; + + p_Vid->number = 0; + p_Vid->type = I_SLICE; + + //p_Vid->dec_ref_pic_marking_buffer = NULL; + + p_Vid->g_nFrame = 0; + // B pictures + p_Vid->Bframe_ctr = 0; + + for(i=0;isnr[i]->frame_ctr = 0; + } + + // time for total decoding session + p_Vid->tot_time = 0; + + p_Vid->dec_picture = NULL; + /*// reference flag initialization + for(i=0;i<17;++i) + { + p_Vid->ref_flag[i] = 1; + }*/ + + p_Vid->MbToSliceGroupMap = NULL; + p_Vid->MapUnitToSliceGroupMap = NULL; + + p_Vid->LastAccessUnitExists = 0; + p_Vid->NALUCount = 0; + + + p_Vid->out_buffer = NULL; + p_Vid->pending_output = NULL; + p_Vid->pending_output_state = FRAME; + p_Vid->recovery_flag = 0; + + +#if (ENABLE_OUTPUT_TONEMAPPING) + init_tone_mapping_sei(p_Vid->seiToneMapping); +#endif + +#if (MVC_EXTENSION_ENABLE) + p_Vid->last_pic_width_in_mbs_minus1 = 0; + p_Vid->last_pic_height_in_map_units_minus1 = 0; + p_Vid->last_max_dec_frame_buffering = 0; +#endif + + p_Vid->newframe = 0; + p_Vid->previous_frame_num = 0; + + p_Vid->iLumaPadX = MCBUF_LUMA_PAD_X; + p_Vid->iLumaPadY = MCBUF_LUMA_PAD_Y; + p_Vid->iChromaPadX = MCBUF_CHROMA_PAD_X; + p_Vid->iChromaPadY = MCBUF_CHROMA_PAD_Y; + + p_Vid->iPostProcess = 0; + p_Vid->bDeblockEnable = 0x3; + p_Vid->last_dec_view_id = -1; + p_Vid->last_dec_layer_id = -1; + +#if ENABLE_DEC_STATS + if ((p_Vid->dec_stats = (DecStatParameters *) malloc (sizeof (DecStatParameters)))== NULL) + no_mem_exit ("init: p_Vid->dec_stats"); + init_dec_stats(p_Vid->dec_stats); +#endif +} + +/*! +*********************************************************************** +* \brief +* Initialize FREXT variables +*********************************************************************** +*/ +void init_frext(VideoParameters *p_Vid) //!< video parameters +{ + //pel bitdepth init + p_Vid->bitdepth_luma_qp_scale = 6 * (p_Vid->bitdepth_luma - 8); + + if(p_Vid->bitdepth_luma > p_Vid->bitdepth_chroma || p_Vid->active_sps->chroma_format_idc == YUV400) + p_Vid->pic_unit_bitsize_on_disk = (p_Vid->bitdepth_luma > 8)? 16:8; + else + p_Vid->pic_unit_bitsize_on_disk = (p_Vid->bitdepth_chroma > 8)? 16:8; + p_Vid->dc_pred_value_comp[0] = 1<<(p_Vid->bitdepth_luma - 1); + p_Vid->max_pel_value_comp[0] = (1<bitdepth_luma) - 1; + p_Vid->mb_size[0][0] = p_Vid->mb_size[0][1] = MB_BLOCK_SIZE; + + if (p_Vid->active_sps->chroma_format_idc != YUV400) + { + //for chrominance part + p_Vid->bitdepth_chroma_qp_scale = 6 * (p_Vid->bitdepth_chroma - 8); + p_Vid->dc_pred_value_comp[1] = (1 << (p_Vid->bitdepth_chroma - 1)); + p_Vid->dc_pred_value_comp[2] = p_Vid->dc_pred_value_comp[1]; + p_Vid->max_pel_value_comp[1] = (1 << p_Vid->bitdepth_chroma) - 1; + p_Vid->max_pel_value_comp[2] = (1 << p_Vid->bitdepth_chroma) - 1; + p_Vid->num_blk8x8_uv = (1 << p_Vid->active_sps->chroma_format_idc) & (~(0x1)); + p_Vid->num_uv_blocks = (p_Vid->num_blk8x8_uv >> 1); + p_Vid->num_cdc_coeff = (p_Vid->num_blk8x8_uv << 1); + p_Vid->mb_size[1][0] = p_Vid->mb_size[2][0] = p_Vid->mb_cr_size_x = (p_Vid->active_sps->chroma_format_idc==YUV420 || p_Vid->active_sps->chroma_format_idc==YUV422)? 8 : 16; + p_Vid->mb_size[1][1] = p_Vid->mb_size[2][1] = p_Vid->mb_cr_size_y = (p_Vid->active_sps->chroma_format_idc==YUV444 || p_Vid->active_sps->chroma_format_idc==YUV422)? 16 : 8; + + p_Vid->subpel_x = p_Vid->mb_cr_size_x == 8 ? 7 : 3; + p_Vid->subpel_y = p_Vid->mb_cr_size_y == 8 ? 7 : 3; + p_Vid->shiftpel_x = p_Vid->mb_cr_size_x == 8 ? 3 : 2; + p_Vid->shiftpel_y = p_Vid->mb_cr_size_y == 8 ? 3 : 2; + p_Vid->total_scale = p_Vid->shiftpel_x + p_Vid->shiftpel_y; + } + else + { + p_Vid->bitdepth_chroma_qp_scale = 0; + p_Vid->max_pel_value_comp[1] = 0; + p_Vid->max_pel_value_comp[2] = 0; + p_Vid->num_blk8x8_uv = 0; + p_Vid->num_uv_blocks = 0; + p_Vid->num_cdc_coeff = 0; + p_Vid->mb_size[1][0] = p_Vid->mb_size[2][0] = p_Vid->mb_cr_size_x = 0; + p_Vid->mb_size[1][1] = p_Vid->mb_size[2][1] = p_Vid->mb_cr_size_y = 0; + p_Vid->subpel_x = 0; + p_Vid->subpel_y = 0; + p_Vid->shiftpel_x = 0; + p_Vid->shiftpel_y = 0; + p_Vid->total_scale = 0; + } + + p_Vid->mb_cr_size = p_Vid->mb_cr_size_x * p_Vid->mb_cr_size_y; + p_Vid->mb_size_blk[0][0] = p_Vid->mb_size_blk[0][1] = p_Vid->mb_size[0][0] >> 2; + p_Vid->mb_size_blk[1][0] = p_Vid->mb_size_blk[2][0] = p_Vid->mb_size[1][0] >> 2; + p_Vid->mb_size_blk[1][1] = p_Vid->mb_size_blk[2][1] = p_Vid->mb_size[1][1] >> 2; + + p_Vid->mb_size_shift[0][0] = p_Vid->mb_size_shift[0][1] = CeilLog2_sf (p_Vid->mb_size[0][0]); + p_Vid->mb_size_shift[1][0] = p_Vid->mb_size_shift[2][0] = CeilLog2_sf (p_Vid->mb_size[1][0]); + p_Vid->mb_size_shift[1][1] = p_Vid->mb_size_shift[2][1] = CeilLog2_sf (p_Vid->mb_size[1][1]); +} + +/*! +************************************************************************ +* \brief +* Reports the gathered information to appropriate outputs +* +* \par Input: +* InputParameters *p_Inp, +* VideoParameters *p_Vid, +* struct snr_par *stat +* +* \par Output: +* None +************************************************************************ +*/ +void Report(VideoParameters *p_Vid) +{ + static const char yuv_formats[4][4]= { {"400"}, {"420"}, {"422"}, {"444"} }; + pic_parameter_set_rbsp_t *active_pps = p_Vid->active_pps; + InputParameters *p_Inp = p_Vid->p_Inp; + SNRParameters *snr = p_Vid->snr[0]; +#define OUTSTRING_SIZE 255 + char string[OUTSTRING_SIZE]; + FILE *p_log; + +#ifndef WIN32 + time_t now; + struct tm *l_time; +#else + char timebuf[128]; +#endif + + // normalize time + p_Vid->tot_time = timenorm(p_Vid->tot_time); + + if (p_Inp->silent == FALSE) + { + printf("-------------------- Average SNR all frames ------------------------------\n"); + printf(" SNR Y(dB) : %5.2f\n",snr->snra[0]); + printf(" SNR U(dB) : %5.2f\n",snr->snra[1]); + printf(" SNR V(dB) : %5.2f\n",snr->snra[2]); + + printf(" Total decoding time : %.3f sec (%.3f fps)[%d frm/%" FORMAT_OFF_T " ms]\n",p_Vid->tot_time*0.001,(snr->frame_ctr ) * 1000.0 / p_Vid->tot_time, snr->frame_ctr, p_Vid->tot_time); + printf("--------------------------------------------------------------------------\n"); + printf(" Exit JM %s decoder, ver %s ",JM, VERSION); + printf("\n"); + } + else + { + printf("\n----------------------- Decoding Completed -------------------------------\n"); + printf(" Total decoding time : %.3f sec (%.3f fps)[%d frm/%" FORMAT_OFF_T " ms]\n",p_Vid->tot_time*0.001, (snr->frame_ctr) * 1000.0 / p_Vid->tot_time, snr->frame_ctr, p_Vid->tot_time); + printf("--------------------------------------------------------------------------\n"); + printf(" Exit JM %s decoder, ver %s ",JM, VERSION); + printf("\n"); + } + + // write to log file + printf(" Output status file : %s \n",LOGFILE); + snprintf(string, OUTSTRING_SIZE, "%s", LOGFILE); + + if ((p_log=fopen(string,"r"))==0) // check if file exist + { + if ((p_log=fopen(string,"a"))==0) + { + snprintf(errortext, ET_SIZE, "Error open file %s for appending",string); + error(errortext, 500); + } + else // Create header to new file + { + fprintf(p_log," -------------------------------------------------------------------------------------------------------------------\n"); + fprintf(p_log,"| Decoder statistics. This file is made first time, later runs are appended |\n"); + fprintf(p_log," ------------------------------------------------------------------------------------------------------------------- \n"); + fprintf(p_log,"| ver | Date | Time | Sequence |#Img| Format | YUV |Coding|SNRY 1|SNRU 1|SNRV 1|SNRY N|SNRU N|SNRV N|\n"); + fprintf(p_log," -------------------------------------------------------------------------------------------------------------------\n"); + } + } + else + { + fclose(p_log); + p_log=fopen(string,"a"); // File exist,just open for appending + } + + fprintf(p_log,"|%s/%-4s", VERSION, EXT_VERSION); + +#ifdef WIN32 + _strdate( timebuf ); + fprintf(p_log,"| %1.5s |",timebuf ); + + _strtime( timebuf); + fprintf(p_log," % 1.5s |",timebuf); +#else + now = time ((time_t *) NULL); // Get the system time and put it into 'now' as 'calender time' + time (&now); + l_time = localtime (&now); + strftime (string, sizeof string, "%d-%b-%Y", l_time); + fprintf(p_log,"| %1.5s |",string ); + + strftime (string, sizeof string, "%H:%M:%S", l_time); + fprintf(p_log,"| %1.5s |",string ); +#endif + + fprintf(p_log,"%20.20s|",p_Inp->infile); + + fprintf(p_log,"%3d |",p_Vid->number); + fprintf(p_log,"%4dx%-4d|", p_Vid->width, p_Vid->height); + fprintf(p_log," %s |", &(yuv_formats[p_Vid->yuv_format][0])); + + if (active_pps) + { + if (active_pps->entropy_coding_mode_flag == (Boolean) CAVLC) + fprintf(p_log," CAVLC|"); + else + fprintf(p_log," CABAC|"); + } + + fprintf(p_log,"%6.3f|",snr->snr1[0]); + fprintf(p_log,"%6.3f|",snr->snr1[1]); + fprintf(p_log,"%6.3f|",snr->snr1[2]); + fprintf(p_log,"%6.3f|",snr->snra[0]); + fprintf(p_log,"%6.3f|",snr->snra[1]); + fprintf(p_log,"%6.3f|",snr->snra[2]); + fprintf(p_log,"\n"); + fclose(p_log); + + snprintf(string, OUTSTRING_SIZE,"%s", DATADECFILE); + p_log=fopen(string,"a"); + + if(p_Vid->Bframe_ctr != 0) // B picture used + { + fprintf(p_log, "%3d %2d %2d %2.2f %2.2f %2.2f %5d " + "%2.2f %2.2f %2.2f %5d " + "%2.2f %2.2f %2.2f %5d %.3f\n", + p_Vid->number, 0, p_Vid->ppSliceList[0]->qp, + snr->snr1[0], + snr->snr1[1], + snr->snr1[2], + 0, + 0.0, + 0.0, + 0.0, + 0, + snr->snra[0], + snr->snra[1], + snr->snra[2], + 0, + (double)0.001*p_Vid->tot_time/(p_Vid->number + p_Vid->Bframe_ctr - 1)); + } + else + { + fprintf(p_log, "%3d %2d %2d %2.2f %2.2f %2.2f %5d " + "%2.2f %2.2f %2.2f %5d " + "%2.2f %2.2f %2.2f %5d %.3f\n", + p_Vid->number, 0, p_Vid->ppSliceList[0]? p_Vid->ppSliceList[0]->qp: 0, + snr->snr1[0], + snr->snr1[1], + snr->snr1[2], + 0, + 0.0, + 0.0, + 0.0, + 0, + snr->snra[0], + snr->snra[1], + snr->snra[2], + 0, + p_Vid->number ? ((double)0.001*p_Vid->tot_time/p_Vid->number) : 0.0); + } + fclose(p_log); +} + +/*! +************************************************************************ +* \brief +* Allocates a stand-alone partition structure. Structure should +* be freed by FreePartition(); +* data structures +* +* \par Input: +* n: number of partitions in the array +* \par return +* pointer to DataPartition Structure, zero-initialized +************************************************************************ +*/ + +DataPartition *AllocPartition(int n) +{ + DataPartition *partArr, *dataPart; + int i; + + partArr = (DataPartition *) calloc(n, sizeof(DataPartition)); + if (partArr == NULL) + { + snprintf(errortext, ET_SIZE, "AllocPartition: Memory allocation for Data Partition failed"); + error(errortext, 100); + } + + for (i = 0; i < n; ++i) // loop over all data partitions + { + dataPart = &(partArr[i]); + dataPart->bitstream = (Bitstream *) calloc(1, sizeof(Bitstream)); + if (dataPart->bitstream == NULL) + { + snprintf(errortext, ET_SIZE, "AllocPartition: Memory allocation for Bitstream failed"); + error(errortext, 100); + } + dataPart->bitstream->streamBuffer = (byte *) calloc(MAX_CODED_FRAME_SIZE, sizeof(byte)); + if (dataPart->bitstream->streamBuffer == NULL) + { + snprintf(errortext, ET_SIZE, "AllocPartition: Memory allocation for streamBuffer failed"); + error(errortext, 100); + } + } + return partArr; +} + + + + +/*! +************************************************************************ +* \brief +* Frees a partition structure (array). +* +* \par Input: +* Partition to be freed, size of partition Array (Number of Partitions) +* +* \par return +* None +* +* \note +* n must be the same as for the corresponding call of AllocPartition +************************************************************************ +*/ +void FreePartition (DataPartition *dp, int n) +{ + int i; + + assert (dp != NULL); + assert (dp->bitstream != NULL); + assert (dp->bitstream->streamBuffer != NULL); + for (i=0; istreamBuffer); + free (dp[i].bitstream); + } + free (dp); +} + + +/*! +************************************************************************ +* \brief +* Allocates the slice structure along with its dependent +* data structures +* +* \par Input: +* Input Parameters InputParameters *p_Inp, VideoParameters *p_Vid +************************************************************************ +*/ +Slice *malloc_slice(InputParameters *p_Inp, VideoParameters *p_Vid) +{ + int i, j, memory_size = 0; + Slice *currSlice; + + currSlice = (Slice *) calloc(1, sizeof(Slice)); + if ( currSlice == NULL) + { + snprintf(errortext, ET_SIZE, "Memory allocation for Slice datastruct in NAL-mode %d failed", p_Inp->FileFormat); + error(errortext,100); + } + + // create all context models + currSlice->mot_ctx = create_contexts_MotionInfo(); + currSlice->tex_ctx = create_contexts_TextureInfo(); + + currSlice->max_part_nr = 3; //! assume data partitioning (worst case) for the following mallocs() + currSlice->partArr = AllocPartition(currSlice->max_part_nr); + + memory_size += get_mem2Dwp (&(currSlice->wp_params), 2, MAX_REFERENCE_PICTURES); + + memory_size += get_mem3Dint(&(currSlice->wp_weight), 2, MAX_REFERENCE_PICTURES, 3); + memory_size += get_mem3Dint(&(currSlice->wp_offset), 6, MAX_REFERENCE_PICTURES, 3); + memory_size += get_mem4Dint(&(currSlice->wbp_weight), 6, MAX_REFERENCE_PICTURES, MAX_REFERENCE_PICTURES, 3); + + memory_size += get_mem3Dpel(&(currSlice->mb_pred), MAX_PLANE, MB_BLOCK_SIZE, MB_BLOCK_SIZE); + memory_size += get_mem3Dpel(&(currSlice->mb_rec ), MAX_PLANE, MB_BLOCK_SIZE, MB_BLOCK_SIZE); + memory_size += get_mem3Dint(&(currSlice->mb_rres), MAX_PLANE, MB_BLOCK_SIZE, MB_BLOCK_SIZE); + memory_size += get_mem3Dint(&(currSlice->cof ), MAX_PLANE, MB_BLOCK_SIZE, MB_BLOCK_SIZE); + // memory_size += get_mem3Dint(&(currSlice->fcf ), MAX_PLANE, MB_BLOCK_SIZE, MB_BLOCK_SIZE); + allocate_pred_mem(currSlice); +#if (MVC_EXTENSION_ENABLE) + currSlice->view_id = MVC_INIT_VIEW_ID; + currSlice->inter_view_flag = 0; + currSlice->anchor_pic_flag = 0; +#endif + // reference flag initialization + for(i=0;i<17;++i) + { + currSlice->ref_flag[i] = 1; + } + for (i = 0; i < 6; i++) + { + currSlice->listX[i] = calloc(MAX_LIST_SIZE, sizeof (StorablePicture*)); // +1 for reordering + if (NULL==currSlice->listX[i]) + no_mem_exit("malloc_slice: currSlice->listX[i]"); + } + for (j = 0; j < 6; j++) + { + for (i = 0; i < MAX_LIST_SIZE; i++) + { + currSlice->listX[j][i] = NULL; + } + currSlice->listXsize[j]=0; + } + + return currSlice; +} + + +/*! +************************************************************************ +* \brief +* Memory frees of the Slice structure and of its dependent +* data structures +* +* \par Input: +* Input Parameters Slice *currSlice +************************************************************************ +*/ +void free_slice(Slice *currSlice) +{ + int i; + + if (currSlice->slice_type != I_SLICE && currSlice->slice_type != SI_SLICE) + free_ref_pic_list_reordering_buffer(currSlice); + free_pred_mem(currSlice); + free_mem3Dint(currSlice->cof ); + free_mem3Dint(currSlice->mb_rres); + free_mem3Dpel(currSlice->mb_rec ); + free_mem3Dpel(currSlice->mb_pred); + + free_mem2Dwp (currSlice->wp_params ); + free_mem3Dint(currSlice->wp_weight ); + free_mem3Dint(currSlice->wp_offset ); + free_mem4Dint(currSlice->wbp_weight); + + FreePartition (currSlice->partArr, 3); + + //if (1) + { + // delete all context models + delete_contexts_MotionInfo (currSlice->mot_ctx); + delete_contexts_TextureInfo(currSlice->tex_ctx); + } + + for (i=0; i<6; i++) + { + if (currSlice->listX[i]) + { + free (currSlice->listX[i]); + currSlice->listX[i] = NULL; + } + } + while (currSlice->dec_ref_pic_marking_buffer) + { + DecRefPicMarking_t *tmp_drpm=currSlice->dec_ref_pic_marking_buffer; + currSlice->dec_ref_pic_marking_buffer=tmp_drpm->Next; + free (tmp_drpm); + } + + free(currSlice); + currSlice = NULL; +} + +/*! +************************************************************************ +* \brief +* Dynamic memory allocation of frame size related global buffers +* buffers are defined in global.h, allocated memory must be freed in +* void free_global_buffers() +* +* \par Input: +* Input Parameters VideoParameters *p_Vid +* +* \par Output: +* Number of allocated bytes +*********************************************************************** +*/ +int init_global_buffers(VideoParameters *p_Vid, int layer_id) +{ + int memory_size=0; + int i; + CodingParameters *cps = p_Vid->p_EncodePar[layer_id]; + BlockPos* PicPos; + + if (p_Vid->global_init_done[layer_id]) + { + free_layer_buffers(p_Vid, layer_id); + } + + // allocate memory for reference frame in find_snr + memory_size += get_mem2Dpel(&cps->imgY_ref, cps->height, cps->width); + if (cps->yuv_format != YUV400) + { + memory_size += get_mem3Dpel(&cps->imgUV_ref, 2, cps->height_cr, cps->width_cr); + } + else + cps->imgUV_ref = NULL; + + // allocate memory in structure p_Vid + if( (cps->separate_colour_plane_flag != 0) ) + { + for( i=0; imb_data_JV[i]) = (Macroblock *) calloc(cps->FrameSizeInMbs, sizeof(Macroblock))) == NULL) + no_mem_exit("init_global_buffers: cps->mb_data_JV"); + } + cps->mb_data = NULL; + } + else + { + if(((cps->mb_data) = (Macroblock *) calloc(cps->FrameSizeInMbs, sizeof(Macroblock))) == NULL) + no_mem_exit("init_global_buffers: cps->mb_data"); + } + if( (cps->separate_colour_plane_flag != 0) ) + { + for( i=0; iintra_block_JV[i]) = (char*) calloc(cps->FrameSizeInMbs, sizeof(char))) == NULL) + no_mem_exit("init_global_buffers: cps->intra_block_JV"); + } + cps->intra_block = NULL; + } + else + { + if(((cps->intra_block) = (char*) calloc(cps->FrameSizeInMbs, sizeof(char))) == NULL) + no_mem_exit("init_global_buffers: cps->intra_block"); + } + + + //memory_size += get_mem2Dint(&PicPos,p_Vid->FrameSizeInMbs + 1,2); //! Helper array to access macroblock positions. We add 1 to also consider last MB. + if(((cps->PicPos) = (BlockPos*) calloc(cps->FrameSizeInMbs + 1, sizeof(BlockPos))) == NULL) + no_mem_exit("init_global_buffers: PicPos"); + + PicPos = cps->PicPos; + for (i = 0; i < (int) cps->FrameSizeInMbs + 1;++i) + { + PicPos[i].x = (short) (i % cps->PicWidthInMbs); + PicPos[i].y = (short) (i / cps->PicWidthInMbs); + } + + if( (cps->separate_colour_plane_flag != 0) ) + { + for( i=0; iipredmode_JV[i]), 4*cps->FrameHeightInMbs, 4*cps->PicWidthInMbs); + } + cps->ipredmode = NULL; + } + else + memory_size += get_mem2D(&(cps->ipredmode), 4*cps->FrameHeightInMbs, 4*cps->PicWidthInMbs); + + // CAVLC mem + memory_size += get_mem4D(&(cps->nz_coeff), cps->FrameSizeInMbs, 3, BLOCK_SIZE, BLOCK_SIZE); + if( (cps->separate_colour_plane_flag != 0) ) + { + for( i=0; isiblock_JV[i]), cps->FrameHeightInMbs, cps->PicWidthInMbs); + if(cps->siblock_JV[i]== NULL) + no_mem_exit("init_global_buffers: p_Vid->siblock_JV"); + } + cps->siblock = NULL; + } + else + { + memory_size += get_mem2Dint(&(cps->siblock), cps->FrameHeightInMbs, cps->PicWidthInMbs); + } + init_qp_process(cps); + cps->oldFrameSizeInMbs = cps->FrameSizeInMbs; + + if(layer_id == 0 ) + init_output(cps, ((cps->pic_unit_bitsize_on_disk+7) >> 3)); + else + cps->img2buf = p_Vid->p_EncodePar[0]->img2buf; + p_Vid->global_init_done[layer_id] = 1; + + return (memory_size); +} + +/*! +************************************************************************ +* \brief +* Free allocated memory of frame size related global buffers +* buffers are defined in global.h, allocated memory is allocated in +* int init_global_buffers() +* +* \par Input: +* Input Parameters VideoParameters *p_Vid +* +* \par Output: +* none +* +************************************************************************ +*/ +void free_layer_buffers(VideoParameters *p_Vid, int layer_id) +{ + CodingParameters *cps = p_Vid->p_EncodePar[layer_id]; + + if(!p_Vid->global_init_done[layer_id]) + return; + + if (cps->imgY_ref) + { + free_mem2Dpel (cps->imgY_ref); + cps->imgY_ref = NULL; + } + if (cps->imgUV_ref) + { + free_mem3Dpel (cps->imgUV_ref); + cps->imgUV_ref = NULL; + } + // CAVLC free mem + if (cps->nz_coeff) + { + free_mem4D(cps->nz_coeff); + cps->nz_coeff = NULL; + } + + // free mem, allocated for structure p_Vid + if( (cps->separate_colour_plane_flag != 0) ) + { + int i; + for(i=0; imb_data_JV[i]); + cps->mb_data_JV[i] = NULL; + free_mem2Dint(cps->siblock_JV[i]); + cps->siblock_JV[i] = NULL; + free_mem2D(cps->ipredmode_JV[i]); + cps->ipredmode_JV[i] = NULL; + free (cps->intra_block_JV[i]); + cps->intra_block_JV[i] = NULL; + } + } + else + { + if (cps->mb_data != NULL) + { + free(cps->mb_data); + cps->mb_data = NULL; + } + if(cps->siblock) + { + free_mem2Dint(cps->siblock); + cps->siblock = NULL; + } + if(cps->ipredmode) + { + free_mem2D(cps->ipredmode); + cps->ipredmode = NULL; + } + if(cps->intra_block) + { + free (cps->intra_block); + cps->intra_block = NULL; + } + } + if(cps->PicPos) + { + free(cps->PicPos); + cps->PicPos = NULL; + } + + free_qp_matrices(cps); + + + p_Vid->global_init_done[layer_id] = 0; +} + +void free_global_buffers(VideoParameters *p_Vid) +{ + if(p_Vid->dec_picture) + { + free_storable_picture(p_Vid->dec_picture); + p_Vid->dec_picture = NULL; + } +#if MVC_EXTENSION_ENABLE + if(p_Vid->active_subset_sps && p_Vid->active_subset_sps->sps.Valid && (p_Vid->active_subset_sps->sps.profile_idc==MVC_HIGH||p_Vid->active_subset_sps->sps.profile_idc == STEREO_HIGH)) + free_img_data( p_Vid, &(p_Vid->tempData3) ); +#endif +} + +void report_stats_on_error(void) +{ + //free_encoder_memory(p_Vid); + exit (-1); +} + +void ClearDecPicList(VideoParameters *p_Vid) +{ + DecodedPicList *pPic = p_Vid->pDecOuputPic, *pPrior = NULL; + //find the head first; + while(pPic && !pPic->bValid) + { + pPrior = pPic; + pPic = pPic->pNext; + } + + if(pPic && (pPic != p_Vid->pDecOuputPic)) + { + //move all nodes before pPic to the end; + DecodedPicList *pPicTail = pPic; + while(pPicTail->pNext) + pPicTail = pPicTail->pNext; + + pPicTail->pNext = p_Vid->pDecOuputPic; + p_Vid->pDecOuputPic = pPic; + pPrior->pNext = NULL; + } +} + +DecodedPicList *get_one_avail_dec_pic_from_list(DecodedPicList *pDecPicList, int b3D, int view_id) +{ + DecodedPicList *pPic = pDecPicList, *pPrior = NULL; + if(b3D) + { + while(pPic && (pPic->bValid &(1<pNext; + } + } + else + { + while(pPic && (pPic->bValid)) + { + pPrior = pPic; + pPic = pPic->pNext; + } + } + + if(!pPic) + { + pPic = (DecodedPicList *)calloc(1, sizeof(*pPic)); + pPrior->pNext = pPic; + } + + return pPic; +} +/************************************ +Interface: OpenDecoder +Return: +0: NOERROR; +<0: ERROR; +************************************/ +int OpenDecoder(InputParameters *p_Inp) +{ + int iRet; + DecoderParams *pDecoder; + + iRet = alloc_decoder(&p_Dec); + if(iRet) + { + return (iRet|DEC_ERRMASK); + } + init_time(); + + pDecoder = p_Dec; + //Configure (pDecoder->p_Vid, pDecoder->p_Inp, argc, argv); + memcpy(pDecoder->p_Inp, p_Inp, sizeof(InputParameters)); + pDecoder->p_Vid->conceal_mode = p_Inp->conceal_mode; + pDecoder->p_Vid->ref_poc_gap = p_Inp->ref_poc_gap; + pDecoder->p_Vid->poc_gap = p_Inp->poc_gap; +#if TRACE + if ((pDecoder->p_trace = fopen(TRACEFILE,"w"))==0) // append new statistic at the end + { + snprintf(errortext, ET_SIZE, "Error open file %s!",TRACEFILE); + //error(errortext,500); + return -1; + } +#endif + +#if (!MVC_EXTENSION_ENABLE) + if((strcasecmp(p_Inp->outfile, "\"\"")!=0) && (strlen(p_Inp->outfile)>0)) + { + if ((pDecoder->p_Vid->p_out = open(p_Inp->outfile, OPENFLAGS_WRITE, OPEN_PERMISSIONS))==-1) + { + snprintf(errortext, ET_SIZE, "Error open file %s ",p_Inp->outfile); + error(errortext,500); + } + } + else + pDecoder->p_Vid->p_out = -1; +#else + { + int i; + VideoParameters *p_Vid = pDecoder->p_Vid; + // Set defaults + p_Vid->p_out = -1; + for(i = 0; i < MAX_VIEW_NUM; i++) + { + p_Vid->p_out_mvc[i] = -1; + } + + //! Edited by Joao Carreira + if (p_Inp->DecodeAllLayers == 1) + { + //int view; + //for(view=0; viewoutfile, "\"\"")!=0) && (strlen(p_Inp->outfile)>0)) + { + if( (strcasecmp(p_Inp->outfile, "\"\"")!=0) && ((p_Vid->p_out_mvc[0]=open(p_Inp->outfile, OPENFLAGS_WRITE, OPEN_PERMISSIONS))==-1) ) + { + snprintf(errortext, ET_SIZE, "Error open file %s ",p_Inp->outfile); + error(errortext,500); + } + } + p_Vid->p_out = p_Vid->p_out_mvc[0]; + } + } +#endif + +#if (!MVC_EXTENSION_ENABLE) + if(strlen(pDecoder->p_Inp->reffile)>0 && strcmp(pDecoder->p_Inp->reffile, "\"\"")) + { + if ((pDecoder->p_Vid->p_ref = open(pDecoder->p_Inp->reffile, OPENFLAGS_READ))==-1) + { + printf(" Input reference file : %s does not exist \n",pDecoder->p_Inp->reffile); + printf(" SNR values are not available\n"); + } + } + else + pDecoder->p_Vid->p_ref = -1; +#else + { + int i; + VideoParameters *p_Vid = pDecoder->p_Vid; + // Set defaults + p_Vid->p_out = -1; + for(i = 0; i < MAX_VIEW_NUM; i++) + { + p_Vid->p_ref_mvc[i] = -1; + } + + if (p_Inp->DecodeAllLayers == 1) + { + //OpenRefFiles(p_Vid, 0, 1); + OpenRefFiles_per_layer(p_Vid, 0); + } + else + { //Normal AVC + if((strcasecmp(p_Inp->outfile, "\"\"")!=0) && (strlen(p_Inp->outfile)>0)) + { + if( (strcasecmp(p_Inp->outfile, "\"\"")!=0) && ((p_Vid->p_out_mvc[0]=open(p_Inp->outfile, OPENFLAGS_READ))==-1) ) + { + snprintf(errortext, ET_SIZE, "Error open file %s ",p_Inp->outfile); + error(errortext,500); + } + } + p_Vid->p_out = p_Vid->p_out_mvc[0]; + } + } +#endif + + switch( pDecoder->p_Inp->FileFormat ) + { + default: + case PAR_OF_ANNEXB: + malloc_annex_b(pDecoder->p_Vid, &pDecoder->p_Vid->annex_b); + open_annex_b(pDecoder->p_Inp->infile, pDecoder->p_Vid->annex_b); + break; + case PAR_OF_RTP: + OpenRTPFile(pDecoder->p_Inp->infile, &pDecoder->p_Vid->BitStreamFile); + break; + } + + // Allocate Slice data struct + //pDecoder->p_Vid->currentSlice = NULL; //malloc_slice(pDecoder->p_Inp, pDecoder->p_Vid); + + init_old_slice(pDecoder->p_Vid->old_slice); + + init(pDecoder->p_Vid); + + init_out_buffer(pDecoder->p_Vid); + + init_parse_nalu(pDecoder->p_Vid); + init_prediction_parser(pDecoder->p_Vid); + + pDecoder->p_Vid->p_Transcode = NULL; + + //! Error Concealment + fercOpenFrameConcealment( pDecoder->p_Vid ); + +#if (MVC_EXTENSION_ENABLE) + pDecoder->p_Vid->active_sps = NULL; + pDecoder->p_Vid->active_subset_sps = NULL; + init_subset_sps_list(pDecoder->p_Vid->SubsetSeqParSet, MAXSPS); +#endif + + +#if _FLTDBG_ + pDecoder->p_Vid->fpDbg = fopen("c:/fltdbg.txt", "a"); + fprintf(pDecoder->p_Vid->fpDbg, "\ndecoder is opened.\n"); +#endif + + initInput( pDecoder->p_Vid , &(pDecoder->p_Inp->source) , &(pDecoder->p_Inp->output) ); + + return DEC_OPEN_NOERR; +} + +/************************************ +Interface: DecodeOneFrame +Return: +0: NOERROR; +1: Finished decoding; +others: Error Code; +************************************/ +int DecodeOneFrame(DecodedPicList **ppDecPicList) +{ + int iRet; + DecoderParams *pDecoder = p_Dec; + ClearDecPicList(pDecoder->p_Vid); + iRet = decode_one_frame(pDecoder); + if(iRet == SOP) + { + iRet = DEC_SUCCEED; + } + else if(iRet == EOS) + { + iRet = DEC_EOS; + } + else + { + iRet |= DEC_ERRMASK; + } + + *ppDecPicList = pDecoder->p_Vid->pDecOuputPic; + return iRet; +} + +int FinitDecoder(DecodedPicList **ppDecPicList) +{ + int i; + DecoderParams *pDecoder = p_Dec; + if(!pDecoder) + return DEC_GEN_NOERR; + ClearDecPicList(pDecoder->p_Vid); +#if (MVC_EXTENSION_ENABLE) + for(i=MAX_NUM_DPB_LAYERS-1; i>=0;i--) + { + flush_dpb(pDecoder->p_Vid->p_Dpb_layer[i]); + } + //flush_dpb(pDecoder->p_Vid->p_Dpb_layer[1]); + //flush_dpb(pDecoder->p_Vid->p_Dpb_layer[0]); +#else + flush_dpb(pDecoder->p_Vid->p_Dpb_layer[0]); +#endif +#if (PAIR_FIELDS_IN_OUTPUT) + flush_pending_output(pDecoder->p_Vid, pDecoder->p_Vid->p_out); +#endif + if (pDecoder->p_Inp->FileFormat == PAR_OF_ANNEXB) + { + reset_annex_b(pDecoder->p_Vid->annex_b); + } + pDecoder->p_Vid->newframe = 0; + pDecoder->p_Vid->previous_frame_num = 0; + *ppDecPicList = pDecoder->p_Vid->pDecOuputPic; + return DEC_GEN_NOERR; +} + +int CloseDecoder() +{ + int i; + + DecoderParams *pDecoder = p_Dec; + if(!pDecoder) + return DEC_CLOSE_NOERR; + + + //Report (pDecoder->p_Vid); + FinalReport (pDecoder->p_Vid); + + + FmoFinit(pDecoder->p_Vid); + for(i=0;ip_Vid, i); + } + //free_layer_buffers(pDecoder->p_Vid, i); + //free_layer_buffers(pDecoder->p_Vid, 1); + free_global_buffers(pDecoder->p_Vid); + switch( pDecoder->p_Inp->FileFormat ) + { + default: + case PAR_OF_ANNEXB: + close_annex_b(pDecoder->p_Vid->annex_b); + break; + case PAR_OF_RTP: + CloseRTPFile(&pDecoder->p_Vid->BitStreamFile); + break; + } + +#if (MVC_EXTENSION_ENABLE) + for(i=0;ip_Vid->p_out_mvc[i] != -1) + { + close(pDecoder->p_Vid->p_out_mvc[i]); + } + if (pDecoder->p_Vid->p_ref_mvc[i] != -1) + { + close(pDecoder->p_Vid->p_ref_mvc[i]); + } + } +#else + if(pDecoder->p_Vid->p_out >=0) + close(pDecoder->p_Vid->p_out); + + if (pDecoder->p_Vid->p_ref != -1) + close(pDecoder->p_Vid->p_ref); +#endif + + +#if TRACE + fclose(pDecoder->p_trace); +#endif + + ercClose(pDecoder->p_Vid, pDecoder->p_Vid->erc_errorVar); + + CleanUpPPS(pDecoder->p_Vid); +#if (MVC_EXTENSION_ENABLE) + for(i=0; ip_Vid->SubsetSeqParSet+i); + } +#endif + + for(i=0; ip_Vid->p_Dpb_layer[i]); + + + uninit_out_buffer(pDecoder->p_Vid); +#if _FLTDBG_ + if(pDecoder->p_Vid->fpDbg) + { + fprintf(pDecoder->p_Vid->fpDbg, "decoder is closed.\n"); + fclose(pDecoder->p_Vid->fpDbg); + pDecoder->p_Vid->fpDbg = NULL; + } +#endif + + free_img (pDecoder->p_Vid); + free (pDecoder->p_Inp); + + free(pDecoder); + + p_Dec = NULL; + return DEC_CLOSE_NOERR; +} + +#if (MVC_EXTENSION_ENABLE) +void OpenOutputFiles(VideoParameters *p_Vid, int view0_id, int view1_id) +{ + InputParameters *p_Inp = p_Vid->p_Inp; + char out_ViewFileName[2][FILE_NAME_SIZE], chBuf[FILE_NAME_SIZE], *pch; + if ((strcasecmp(p_Inp->outfile, "\"\"")!=0) && (strlen(p_Inp->outfile)>0)) + { + strcpy(chBuf, p_Inp->outfile); + pch = strrchr(chBuf, '.'); + if(pch) + *pch = '\0'; + if (strcmp("nul", chBuf)) + { + sprintf(out_ViewFileName[0], "%s_ViewId%02d.yuv", chBuf, view0_id); + sprintf(out_ViewFileName[1], "%s_ViewId%02d.yuv", chBuf, view1_id); + if(p_Vid->p_out_mvc[0] >= 0) + { + close(p_Vid->p_out_mvc[0]); + p_Vid->p_out_mvc[0] = -1; + } + if ((p_Vid->p_out_mvc[0]=open(out_ViewFileName[0], OPENFLAGS_WRITE, OPEN_PERMISSIONS))==-1) + { + snprintf(errortext, ET_SIZE, "Error open file %s ", out_ViewFileName[0]); + error(errortext,500); + } + + if(p_Vid->p_out_mvc[1] >= 0) + { + close(p_Vid->p_out_mvc[1]); + p_Vid->p_out_mvc[1] = -1; + } + if ((p_Vid->p_out_mvc[1]=open(out_ViewFileName[1], OPENFLAGS_WRITE, OPEN_PERMISSIONS))==-1) + { + snprintf(errortext, ET_SIZE, "Error open file %s ", out_ViewFileName[1]); + error(errortext,500); + } + } + } +} + +void OpenOutputFiles_per_layer(VideoParameters *p_Vid, int view_id) +{ + InputParameters *p_Inp = p_Vid->p_Inp; + char out_ViewFileName[FILE_NAME_SIZE], chBuf[FILE_NAME_SIZE], *pch; + if ((strcasecmp(p_Inp->outfile, "\"\"")!=0) && (strlen(p_Inp->outfile)>0)) + { + strcpy(chBuf, p_Inp->outfile); + pch = strrchr(chBuf, '.'); + if(pch) + *pch = '\0'; + if (strcmp("nul", chBuf)) + { + sprintf(out_ViewFileName, "%s_ViewId%02d.yuv", chBuf, view_id); + + if(p_Vid->p_out_mvc[view_id] >= 0) + { + return; + } + if ((p_Vid->p_out_mvc[view_id]=open(out_ViewFileName, OPENFLAGS_WRITE, OPEN_PERMISSIONS))==-1) + { + snprintf(errortext, ET_SIZE, "Error open file %s ", out_ViewFileName); + error(errortext,500); + } + } + } +} + +void OpenRefFiles(VideoParameters *p_Vid, int view0_id, int view1_id) +{ + InputParameters *p_Inp = p_Vid->p_Inp; + char out_ViewFileName[2][FILE_NAME_SIZE], chBuf[FILE_NAME_SIZE], *pch; + if ((strcasecmp(p_Inp->outfile, "\"\"")!=0) && (strlen(p_Inp->reffile)>0)) + { + strcpy(chBuf, p_Inp->reffile); + pch = strrchr(chBuf, '.'); + if(pch) + *pch = '\0'; + if (strcmp("nul", chBuf)) + { + sprintf(out_ViewFileName[0], "%s_ViewId%02d.yuv", chBuf, view0_id); + sprintf(out_ViewFileName[1], "%s_ViewId%02d.yuv", chBuf, view1_id); + if(p_Vid->p_ref_mvc[0] >= 0) + { + close(p_Vid->p_ref_mvc[0]); + p_Vid->p_ref_mvc[0] = -1; + } + if ((p_Vid->p_ref_mvc[0]=open(out_ViewFileName[0], OPENFLAGS_READ))==-1) + { + snprintf(errortext, ET_SIZE, "Error open file %s ", out_ViewFileName[0]); + p_Vid->p_ref_mvc[0] = -1; + } + + if(p_Vid->p_ref_mvc[1] >= 0) + { + close(p_Vid->p_ref_mvc[1]); + p_Vid->p_ref_mvc[1] = -1; + } + if ((p_Vid->p_ref_mvc[1]=open(out_ViewFileName[1], OPENFLAGS_READ))==-1) + { + snprintf(errortext, ET_SIZE, "Error open file %s ", out_ViewFileName[1]); + p_Vid->p_ref_mvc[1] = -1; + } + } + } +} + +void OpenRefFiles_per_layer(VideoParameters *p_Vid, int view_id) +{ + InputParameters *p_Inp = p_Vid->p_Inp; + char out_ViewFileName[FILE_NAME_SIZE], chBuf[FILE_NAME_SIZE], *pch; + if ((strcasecmp(p_Inp->outfile, "\"\"")!=0) && (strlen(p_Inp->reffile)>0)) + { + strcpy(chBuf, p_Inp->reffile); + pch = strrchr(chBuf, '.'); + if(pch) + *pch = '\0'; + if (strcmp("nul", chBuf)) + { + sprintf(out_ViewFileName, "%s_ViewId%02d.yuv", chBuf, view_id); + if(p_Vid->p_ref_mvc[view_id] >= 0) + { + return; + } + if ((p_Vid->p_ref_mvc[view_id]=open(out_ViewFileName, OPENFLAGS_READ))==-1) + { + snprintf(errortext, ET_SIZE, "Error open file %s ", out_ViewFileName); + p_Vid->p_ref_mvc[view_id] = -1; + } + } + } +} + +#endif + +void set_global_coding_par(VideoParameters *p_Vid, CodingParameters *cps) +{ + p_Vid->bitdepth_chroma = 0; + p_Vid->width_cr = 0; + p_Vid->height_cr = 0; + p_Vid->lossless_qpprime_flag = cps->lossless_qpprime_flag; + p_Vid->max_vmv_r = cps->max_vmv_r; + + // Fidelity Range Extensions stuff (part 1) + p_Vid->bitdepth_luma = cps->bitdepth_luma; + p_Vid->bitdepth_scale[0] = cps->bitdepth_scale[0]; + p_Vid->bitdepth_chroma = cps->bitdepth_chroma; + p_Vid->bitdepth_scale[1] = cps->bitdepth_scale[1]; + + p_Vid->max_frame_num = cps->max_frame_num; + p_Vid->PicWidthInMbs = cps->PicWidthInMbs; + p_Vid->PicHeightInMapUnits = cps->PicHeightInMapUnits; + p_Vid->FrameHeightInMbs = cps->FrameHeightInMbs; + p_Vid->FrameSizeInMbs = cps->FrameSizeInMbs; + + p_Vid->yuv_format = cps->yuv_format; + p_Vid->separate_colour_plane_flag = cps->separate_colour_plane_flag; + p_Vid->ChromaArrayType = cps->ChromaArrayType; + + p_Vid->width = cps->width; + p_Vid->height = cps->height; + p_Vid->iLumaPadX = MCBUF_LUMA_PAD_X; + p_Vid->iLumaPadY = MCBUF_LUMA_PAD_Y; + p_Vid->iChromaPadX = MCBUF_CHROMA_PAD_X; + p_Vid->iChromaPadY = MCBUF_CHROMA_PAD_Y; + if (p_Vid->yuv_format == YUV420) + { + p_Vid->width_cr = (p_Vid->width >> 1); + p_Vid->height_cr = (p_Vid->height >> 1); + } + else if (p_Vid->yuv_format == YUV422) + { + p_Vid->width_cr = (p_Vid->width >> 1); + p_Vid->height_cr = p_Vid->height; + p_Vid->iChromaPadY = MCBUF_CHROMA_PAD_Y*2; + } + else if (p_Vid->yuv_format == YUV444) + { + //YUV444 + p_Vid->width_cr = p_Vid->width; + p_Vid->height_cr = p_Vid->height; + p_Vid->iChromaPadX = p_Vid->iLumaPadX; + p_Vid->iChromaPadY = p_Vid->iLumaPadY; + } + + init_frext(p_Vid); +} diff --git a/ldecod/src/leaky_bucket.c b/ldecod/src/leaky_bucket.c new file mode 100644 index 0000000..46b2ef6 --- /dev/null +++ b/ldecod/src/leaky_bucket.c @@ -0,0 +1,139 @@ + +/*! + ************************************************************************ + * \file leaky_bucket.c + * + * \brief + * Calculate if decoder leaky bucket parameters meets HRD constraints specified by encoder. + * + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * - Shankar Regunathan + ************************************************************************ + */ + +#include "contributors.h" +#include "global.h" + +#ifdef _LEAKYBUCKET_ +/*! + *********************************************************************** + * \brief + * Function to get unsigned long word from a file. + * \param fp + * Filepointer + * \return + * unsigned long double word + * \par SideEffects + * None. + * \par Notes + * File should be opened to read in binary format. + * \author + * Shankar Regunathan shanre@microsoft.com + * \date + * December 06, 2001. + *********************************************************************** + */ +/* gets unsigned double stored in Big Endian Order */ +unsigned long GetBigDoubleWord(FILE *fp) +{ + unsigned long dw; + dw = (unsigned long) (fgetc(fp) & 0xFF); + dw = ((unsigned long) (fgetc(fp) & 0xFF)) | (dw << 0x08); + dw = ((unsigned long) (fgetc(fp) & 0xFF)) | (dw << 0x08); + dw = ((unsigned long) (fgetc(fp) & 0xFF)) | (dw << 0x08); + return(dw); +} + +/*! + *********************************************************************** + * \brief + * Calculates if decoder leaky bucket parameters meets HRD constraints specified by encoder. + * \param p_Inp + * Structure which contains decoder leaky bucket parameters. + * \return + * None + * \par SideEffects + * None. + * \par Notes + * Failure if LeakyBucketParam file is missing or if it does not have + * the correct number of entries. + * \author + * Shankar Regunathan shanre@microsoft.com + * \date + * December 06, 2001. + *********************************************************************** + */ + +/* Main Routine to verify HRD compliance */ +void calc_buffer(InputParameters *p_Inp) +{ + unsigned long NumberLeakyBuckets, *Rmin, *Bmin, *Fmin; + float B_interp, F_interp; + unsigned long iBucket; + float dnr, frac1, frac2; + unsigned long R_decoder, B_decoder, F_decoder; + FILE *outf; + + if ((outf=fopen(p_Inp->LeakyBucketParamFile,"rb"))==NULL) + { + snprintf(errortext, ET_SIZE, "Error open file %s \n",p_Inp->LeakyBucketParamFile); + error(errortext,1); + } + + NumberLeakyBuckets = GetBigDoubleWord(outf); + printf(" Number Leaky Buckets: %8ld \n\n", NumberLeakyBuckets); + Rmin = calloc(NumberLeakyBuckets, sizeof(unsigned long)); + Bmin = calloc(NumberLeakyBuckets, sizeof(unsigned long)); + Fmin = calloc(NumberLeakyBuckets, sizeof(unsigned long)); + + for(iBucket =0; iBucket < NumberLeakyBuckets; iBucket++) + { + Rmin[iBucket] = GetBigDoubleWord(outf); + Bmin[iBucket] = GetBigDoubleWord(outf); + Fmin[iBucket] = GetBigDoubleWord(outf); + printf(" %8ld %8ld %8ld \n", Rmin[iBucket], Bmin[iBucket], Fmin[iBucket]); + } + fclose(outf); + + R_decoder = p_Inp->R_decoder; + F_decoder = p_Inp->F_decoder; + B_decoder = p_Inp->B_decoder; + + for( iBucket =0; iBucket < NumberLeakyBuckets; iBucket++) + { + if(R_decoder < Rmin[iBucket]) + break; + } + + printf("\n"); + if(iBucket > 0 ) + { + if(iBucket < NumberLeakyBuckets) + { + dnr = (float) (Rmin[iBucket] - Rmin[iBucket-1]); + frac1 = (float) (R_decoder - Rmin[iBucket-1]); + frac2 = (float) (Rmin[iBucket] - R_decoder); + B_interp = (float) (Bmin[iBucket] * frac1 + Bmin[iBucket-1] * frac2) /dnr; + F_interp = (float) (Fmin[iBucket] * frac1 + Fmin[iBucket-1] * frac2) /dnr; + } + else { + B_interp = (float) Bmin[iBucket-1]; + F_interp = (float) Fmin[iBucket-1]; + } + printf(" Min.buffer %8.2f Decoder buffer size %ld \n Minimum Delay %8.2f DecoderDelay %ld \n", B_interp, B_decoder, F_interp, F_decoder); + if(B_decoder > B_interp && F_decoder > F_interp) + printf(" HRD Compliant \n"); + else + printf(" HRD Non Compliant \n"); + } + else { // (iBucket = 0) + printf(" Decoder Rate is too small; HRD cannot be verified \n"); + } + + free(Rmin); + free(Bmin); + free(Fmin); + return; +} +#endif diff --git a/ldecod/src/loopFilter.c b/ldecod/src/loopFilter.c new file mode 100644 index 0000000..79dfe32 --- /dev/null +++ b/ldecod/src/loopFilter.c @@ -0,0 +1,664 @@ + +/*! + ************************************************************************************* + * \file loopFilter.c + * + * \brief + * Filter to reduce blocking artifacts on a macroblock level. + * The filter strength is QP dependent. + * + * \author + * Contributors: + * - Peter List Peter.List@t-systems.de: Original code (13-Aug-2001) + * - Jani Lainema Jani.Lainema@nokia.com: Some bug fixing, removal of recursiveness (16-Aug-2001) + * - Peter List Peter.List@t-systems.de: inplace filtering and various simplifications (10-Jan-2002) + * - Anthony Joch anthony@ubvideo.com: Simplified switching between filters and + * non-recursive default filter. (08-Jul-2002) + * - Cristina Gomila cristina.gomila@thomson.net: Simplification of the chroma deblocking + * from JVT-E089 (21-Nov-2002) + * - Alexis Michael Tourapis atour@dolby.com: Speed/Architecture improvements (08-Feb-2007) + ************************************************************************************* + */ + +#include "global.h" +#include "image.h" +#include "mb_access.h" +#include "loopfilter.h" +#include "loop_filter.h" + +static void DeblockMb (VideoParameters *p_Vid, StorablePicture *p, int MbQAddr); +static void perform_db (VideoParameters *p_Vid, StorablePicture *p, int MbQAddr); +static void get_db_strength(VideoParameters *p_Vid, StorablePicture *p, int MbQAddr); + +extern void set_loop_filter_functions_mbaff(VideoParameters *p_Vid); +extern void set_loop_filter_functions_normal(VideoParameters *p_Vid); +extern void deblock_normal(VideoParameters *p_Vid, StorablePicture *p); +extern void get_strength_ver_MBAff (byte *Strength, Macroblock *MbQ, int edge, int mvlimit, StorablePicture *p); +extern void get_strength_hor_MBAff (byte *Strength, Macroblock *MbQ, int edge, int mvlimit, StorablePicture *p); + +#if (JM_PARALLEL_DEBLOCK == 0) +/*! + ***************************************************************************************** + * \brief + * Filter all macroblocks in order of increasing macroblock address. + ***************************************************************************************** + */ +void DeblockPicture(VideoParameters *p_Vid, StorablePicture *p) +{ + unsigned i; + if (p->mb_aff_frame_flag) + { + for (i = 0; i < p->PicSizeInMbs; ++i) + { + if( p_Vid->mb_data[i].is_decoded ) + DeblockMb( p_Vid, p, i ) ; + } + } + else + { + // deblock_normal( p_Vid, p); + + for (i = 0; i < p->PicSizeInMbs; ++i) + { + //if( p_Vid->mb_data[i].is_decoded ) + get_db_strength( p_Vid, p, i ) ; + } + for (i = 0; i < p->PicSizeInMbs; ++i) + { + //if( p_Vid->mb_data[i].is_decoded ) + perform_db( p_Vid, p, i ) ; + } + + } +} +#else +static void DeblockParallel(VideoParameters *p_Vid, StorablePicture *p, unsigned int column, int block, int n_last) +{ + int i, j; + + for (j = 0; j < GROUP_SIZE; j++) + { + i = block++ * (p_Vid->PicWidthInMbs - 2) + column; + + perform_db( p_Vid, p, i ) ; + if (block == n_last) break; + } +} + +/*! + ***************************************************************************************** + * \brief + * Filter all macroblocks in a diagonal manner to enable parallelization. + ***************************************************************************************** + */ +void DeblockPicture(VideoParameters *p_Vid, StorablePicture *p) +{ + int iheightMBs =(p_Vid->PicSizeInMbs/p_Vid->PicWidthInMbs); + unsigned int i, k = p->PicWidthInMbs + 2 * (iheightMBs - 1); + +#if defined(OPENMP) + int j; + #pragma omp parallel for +#endif + for (j = 0; j < p->PicSizeInMbs; ++j) + { + get_db_strength( p_Vid, p, j ) ; + } + + for (i = 0; i < k; i++) + { + int nn; + int n_last = imin(iheightMBs, (i >> 1) + 1); + int n_start = (i < p->PicWidthInMbs) ? 0 : ((i - p->PicWidthInMbs) >> 1) + 1; + +#if defined(OPENMP) + #pragma omp parallel for +#endif + for (nn = n_start; nn < n_last; nn += GROUP_SIZE) + DeblockParallel(p_Vid, p, i, nn, n_last); + } +} +#endif + +// likely already set - see testing via asserts +static void init_neighbors(VideoParameters *p_Vid) +{ + int i, j; + int width = p_Vid->PicWidthInMbs; + int height = p_Vid->PicHeightInMbs; + int size = p_Vid->PicSizeInMbs; + Macroblock *currMB = &p_Vid->mb_data[0]; + // do the top left corner + currMB->mbup = NULL; + currMB->mbleft = NULL; + currMB++; + // do top row + for (i = 1; i < width; i++) + { + currMB->mbup = NULL; + currMB->mbleft = currMB - 1; + currMB++; + } + + // do left edge + for (i = width; i < size; i += width) + { + currMB->mbup = currMB - width; + currMB->mbleft = NULL; + currMB += width; + } + // do all others + for (j = width + 1; j < width * height + 1; j += width) + { + currMB = &p_Vid->mb_data[j]; + for (i = 1; i < width; i++) + { + currMB->mbup = currMB - width; + currMB->mbleft = currMB - 1; + currMB++; + } + } +} + + +void init_Deblock(VideoParameters *p_Vid, int mb_aff_frame_flag) +{ + if(p_Vid->yuv_format == YUV444 && p_Vid->separate_colour_plane_flag) + { + change_plane_JV(p_Vid, PLANE_Y, NULL); + init_neighbors(p_Dec->p_Vid); + change_plane_JV(p_Vid, PLANE_U, NULL); + init_neighbors(p_Dec->p_Vid); + change_plane_JV(p_Vid, PLANE_V, NULL); + init_neighbors(p_Dec->p_Vid); + change_plane_JV(p_Vid, PLANE_Y, NULL); + } + else + init_neighbors(p_Dec->p_Vid); + if (mb_aff_frame_flag == 1) + { + set_loop_filter_functions_mbaff(p_Vid); + } + else + { + set_loop_filter_functions_normal(p_Vid); + } +} + +/*! + ***************************************************************************************** + * \brief + * Deblocking filter for one macroblock. + ***************************************************************************************** + */ + +static void DeblockMb(VideoParameters *p_Vid, StorablePicture *p, int MbQAddr) +{ + Macroblock *MbQ = &(p_Vid->mb_data[MbQAddr]) ; // current Mb + + // return, if filter is disabled + if (MbQ->DFDisableIdc == 1) + { + MbQ->DeblockCall = 0; + } + else + { + int edge; + + byte Strength[16]; + int64 *p_Strength64 = (int64 *) Strength; + short mb_x, mb_y; + + int filterNon8x8LumaEdgesFlag[4] = {1,1,1,1}; + int filterLeftMbEdgeFlag; + int filterTopMbEdgeFlag; + int edge_cr; + + imgpel **imgY = p->imgY; + imgpel ***imgUV = p->imgUV; + Slice *currSlice = MbQ->p_Slice; + int mvlimit = ((p->structure!=FRAME) || (p->mb_aff_frame_flag && MbQ->mb_field)) ? 2 : 4; + + seq_parameter_set_rbsp_t *active_sps = p_Vid->active_sps; + + MbQ->DeblockCall = 1; + get_mb_pos (p_Vid, MbQAddr, p_Vid->mb_size[IS_LUMA], &mb_x, &mb_y); + + if (MbQ->mb_type == I8MB) + assert(MbQ->luma_transform_size_8x8_flag); + + filterNon8x8LumaEdgesFlag[1] = + filterNon8x8LumaEdgesFlag[3] = !(MbQ->luma_transform_size_8x8_flag); + + filterLeftMbEdgeFlag = (mb_x != 0); + filterTopMbEdgeFlag = (mb_y != 0); + + if (p->mb_aff_frame_flag && mb_y == MB_BLOCK_SIZE && MbQ->mb_field) + filterTopMbEdgeFlag = 0; + + if (MbQ->DFDisableIdc==2) + { + // don't filter at slice boundaries + filterLeftMbEdgeFlag = MbQ->mbAvailA; + // if this the bottom of a frame macroblock pair then always filter the top edge + filterTopMbEdgeFlag = (p->mb_aff_frame_flag && !MbQ->mb_field && (MbQAddr & 0x01)) ? 1 : MbQ->mbAvailB; + } + + if (p->mb_aff_frame_flag == 1) + CheckAvailabilityOfNeighborsMBAFF(MbQ); + + // Vertical deblocking + for (edge = 0; edge < 4 ; ++edge ) + { + // If cbp == 0 then deblocking for some macroblock types could be skipped + if (MbQ->cbp == 0 && (currSlice->slice_type == P_SLICE || currSlice->slice_type == B_SLICE)) + { + if (filterNon8x8LumaEdgesFlag[edge] == 0 && active_sps->chroma_format_idc != YUV444) + continue; + else if (edge > 0) + { + if (((MbQ->mb_type == PSKIP && currSlice->slice_type == P_SLICE) || (MbQ->mb_type == P16x16) || (MbQ->mb_type == P16x8))) + continue; + else if ((edge & 0x01) && ((MbQ->mb_type == P8x16) || (currSlice->slice_type == B_SLICE && MbQ->mb_type == BSKIP_DIRECT && active_sps->direct_8x8_inference_flag))) + continue; + } + } + + if( edge || filterLeftMbEdgeFlag ) + { + // Strength for 4 blks in 1 stripe + get_strength_ver_MBAff(Strength, MbQ, edge << 2, mvlimit, p); + + if ((p_Strength64[0]) || (p_Strength64[1])) // only if one of the 16 Strength bytes is != 0 + { + if (filterNon8x8LumaEdgesFlag[edge]) + { + p_Vid->EdgeLoopLumaVer( PLANE_Y, imgY, Strength, MbQ, edge << 2); + if(currSlice->chroma444_not_separate) + { + p_Vid->EdgeLoopLumaVer(PLANE_U, imgUV[0], Strength, MbQ, edge << 2); + p_Vid->EdgeLoopLumaVer(PLANE_V, imgUV[1], Strength, MbQ, edge << 2); + } + } + if (active_sps->chroma_format_idc==YUV420 || active_sps->chroma_format_idc==YUV422) + { + edge_cr = chroma_edge[0][edge][p->chroma_format_idc]; + if( (imgUV != NULL) && (edge_cr >= 0)) + { + p_Vid->EdgeLoopChromaVer( imgUV[0], Strength, MbQ, edge_cr, 0, p); + p_Vid->EdgeLoopChromaVer( imgUV[1], Strength, MbQ, edge_cr, 1, p); + } + } + } + } + }//end edge + + // horizontal deblocking + for( edge = 0; edge < 4 ; ++edge ) + { + // If cbp == 0 then deblocking for some macroblock types could be skipped + if (MbQ->cbp == 0 && (currSlice->slice_type == P_SLICE || currSlice->slice_type == B_SLICE)) + { + if (filterNon8x8LumaEdgesFlag[edge] == 0 && active_sps->chroma_format_idc==YUV420) + continue; + else if (edge > 0) + { + if (((MbQ->mb_type == PSKIP && currSlice->slice_type == P_SLICE) || (MbQ->mb_type == P16x16) || (MbQ->mb_type == P8x16))) + continue; + else if ((edge & 0x01) && ((MbQ->mb_type == P16x8) || (currSlice->slice_type == B_SLICE && MbQ->mb_type == BSKIP_DIRECT && active_sps->direct_8x8_inference_flag))) + continue; + } + } + + if( edge || filterTopMbEdgeFlag ) + { + // Strength for 4 blks in 1 stripe + get_strength_hor_MBAff(Strength, MbQ, edge << 2, mvlimit, p); + + if ((p_Strength64[0]) || (p_Strength64[1])) // only if one of the 16 Strength bytes is != 0 + { + if (filterNon8x8LumaEdgesFlag[edge]) + { + p_Vid->EdgeLoopLumaHor( PLANE_Y, imgY, Strength, MbQ, edge << 2, p) ; + if(currSlice->chroma444_not_separate) + { + p_Vid->EdgeLoopLumaHor(PLANE_U, imgUV[0], Strength, MbQ, edge << 2, p); + p_Vid->EdgeLoopLumaHor(PLANE_V, imgUV[1], Strength, MbQ, edge << 2, p); + } + } + + if (active_sps->chroma_format_idc==YUV420 || active_sps->chroma_format_idc==YUV422) + { + edge_cr = chroma_edge[1][edge][p->chroma_format_idc]; + if( (imgUV != NULL) && (edge_cr >= 0)) + { + p_Vid->EdgeLoopChromaHor( imgUV[0], Strength, MbQ, edge_cr, 0, p); + p_Vid->EdgeLoopChromaHor( imgUV[1], Strength, MbQ, edge_cr, 1, p); + } + } + } + + if (!edge && !MbQ->mb_field && MbQ->mixedModeEdgeFlag) //currSlice->mixedModeEdgeFlag) + { + // this is the extra horizontal edge between a frame macroblock pair and a field above it + MbQ->DeblockCall = 2; + get_strength_hor_MBAff(Strength, MbQ, MB_BLOCK_SIZE, mvlimit, p); // Strength for 4 blks in 1 stripe + + //if( *((int*)Strength) ) // only if one of the 4 Strength bytes is != 0 + { + if (filterNon8x8LumaEdgesFlag[edge]) + { + + p_Vid->EdgeLoopLumaHor(PLANE_Y, imgY, Strength, MbQ, MB_BLOCK_SIZE, p) ; + if(currSlice->chroma444_not_separate) + { + p_Vid->EdgeLoopLumaHor(PLANE_U, imgUV[0], Strength, MbQ, MB_BLOCK_SIZE, p) ; + p_Vid->EdgeLoopLumaHor(PLANE_V, imgUV[1], Strength, MbQ, MB_BLOCK_SIZE, p) ; + } + } + if (active_sps->chroma_format_idc==YUV420 || active_sps->chroma_format_idc==YUV422) + { + edge_cr = chroma_edge[1][edge][p->chroma_format_idc]; + if( (imgUV != NULL) && (edge_cr >= 0)) + { + p_Vid->EdgeLoopChromaHor( imgUV[0], Strength, MbQ, MB_BLOCK_SIZE, 0, p) ; + p_Vid->EdgeLoopChromaHor( imgUV[1], Strength, MbQ, MB_BLOCK_SIZE, 1, p) ; + } + } + } + MbQ->DeblockCall = 1; + } + } + }//end edge + + MbQ->DeblockCall = 0; + } +} + +/*! + ***************************************************************************************** + * \brief + * Deblocking filter for one macroblock. + ***************************************************************************************** + */ +static void get_db_strength(VideoParameters *p_Vid, StorablePicture *p, int MbQAddr) +{ + Macroblock *MbQ = &(p_Vid->mb_data[MbQAddr]) ; // current Mb + + // return, if filter is disabled + if (MbQ->DFDisableIdc == 1) + { + MbQ->DeblockCall = 0; + } + else + { + int edge; + + short mb_x, mb_y; + + int filterNon8x8LumaEdgesFlag[4] = {1,1,1,1}; + int filterLeftMbEdgeFlag; + int filterTopMbEdgeFlag; + + Slice *currSlice = MbQ->p_Slice; + int mvlimit = ((p->structure!=FRAME) || (p->mb_aff_frame_flag && MbQ->mb_field)) ? 2 : 4; + + seq_parameter_set_rbsp_t *active_sps = p_Vid->active_sps; + + MbQ->DeblockCall = 1; + get_mb_pos (p_Vid, MbQAddr, p_Vid->mb_size[IS_LUMA], &mb_x, &mb_y); + + if (MbQ->mb_type == I8MB) + assert(MbQ->luma_transform_size_8x8_flag); + + filterNon8x8LumaEdgesFlag[1] = + filterNon8x8LumaEdgesFlag[3] = !(MbQ->luma_transform_size_8x8_flag); + + filterLeftMbEdgeFlag = (mb_x != 0); + filterTopMbEdgeFlag = (mb_y != 0); + + if (p->mb_aff_frame_flag && mb_y == MB_BLOCK_SIZE && MbQ->mb_field) + filterTopMbEdgeFlag = 0; + + if (MbQ->DFDisableIdc==2) + { + // don't filter at slice boundaries + filterLeftMbEdgeFlag = MbQ->mbAvailA; + // if this the bottom of a frame macroblock pair then always filter the top edge + filterTopMbEdgeFlag = (p->mb_aff_frame_flag && !MbQ->mb_field && (MbQAddr & 0x01)) ? 1 : MbQ->mbAvailB; + } + + if (p->mb_aff_frame_flag == 1) + CheckAvailabilityOfNeighborsMBAFF(MbQ); + + // Vertical deblocking + for (edge = 0; edge < 4 ; ++edge ) + { + // If cbp == 0 then deblocking for some macroblock types could be skipped + if (MbQ->cbp == 0 && (currSlice->slice_type == P_SLICE || currSlice->slice_type == B_SLICE)) + { + if (filterNon8x8LumaEdgesFlag[edge] == 0 && active_sps->chroma_format_idc != YUV444) + continue; + else if (edge > 0) + { + if (((MbQ->mb_type == PSKIP && currSlice->slice_type == P_SLICE) || (MbQ->mb_type == P16x16) || (MbQ->mb_type == P16x8))) + continue; + else if ((edge & 0x01) && ((MbQ->mb_type == P8x16) || (currSlice->slice_type == B_SLICE && MbQ->mb_type == BSKIP_DIRECT && active_sps->direct_8x8_inference_flag))) + continue; + } + } + + if( edge || filterLeftMbEdgeFlag ) + { + // Strength for 4 blks in 1 stripe + p_Vid->GetStrengthVer(MbQ, edge, mvlimit, p); + } + }//end edge + + // horizontal deblocking + for( edge = 0; edge < 4 ; ++edge ) + { + // If cbp == 0 then deblocking for some macroblock types could be skipped + if (MbQ->cbp == 0 && (currSlice->slice_type == P_SLICE || currSlice->slice_type == B_SLICE)) + { + if (filterNon8x8LumaEdgesFlag[edge] == 0 && active_sps->chroma_format_idc==YUV420) + continue; + else if (edge > 0) + { + if (((MbQ->mb_type == PSKIP && currSlice->slice_type == P_SLICE) || (MbQ->mb_type == P16x16) || (MbQ->mb_type == P8x16))) + continue; + else if ((edge & 0x01) && ((MbQ->mb_type == P16x8) || (currSlice->slice_type == B_SLICE && MbQ->mb_type == BSKIP_DIRECT && active_sps->direct_8x8_inference_flag))) + continue; + } + } + + if( edge || filterTopMbEdgeFlag ) + { + p_Vid->GetStrengthHor(MbQ, edge, mvlimit, p); + } + }//end edge + + MbQ->DeblockCall = 0; + } +} + + +static void perform_db(VideoParameters *p_Vid, StorablePicture *p, int MbQAddr) +{ + Macroblock *MbQ = &(p_Vid->mb_data[MbQAddr]) ; // current Mb + + // return, if filter is disabled + if (MbQ->DFDisableIdc == 1) + { + MbQ->DeblockCall = 0; + } + else + { + int edge; + + short mb_x, mb_y; + + int filterNon8x8LumaEdgesFlag[4] = {1,1,1,1}; + int filterLeftMbEdgeFlag; + int filterTopMbEdgeFlag; + int edge_cr; + + imgpel **imgY = p->imgY; + imgpel ***imgUV = p->imgUV; + Slice *currSlice = MbQ->p_Slice; + int mvlimit = ((p->structure!=FRAME) || (p->mb_aff_frame_flag && MbQ->mb_field)) ? 2 : 4; + + seq_parameter_set_rbsp_t *active_sps = p_Vid->active_sps; + + MbQ->DeblockCall = 1; + get_mb_pos (p_Vid, MbQAddr, p_Vid->mb_size[IS_LUMA], &mb_x, &mb_y); + + if (MbQ->mb_type == I8MB) + assert(MbQ->luma_transform_size_8x8_flag); + + filterNon8x8LumaEdgesFlag[1] = + filterNon8x8LumaEdgesFlag[3] = !(MbQ->luma_transform_size_8x8_flag); + + filterLeftMbEdgeFlag = (mb_x != 0); + filterTopMbEdgeFlag = (mb_y != 0); + + if (p->mb_aff_frame_flag && mb_y == MB_BLOCK_SIZE && MbQ->mb_field) + filterTopMbEdgeFlag = 0; + + if (MbQ->DFDisableIdc==2) + { + // don't filter at slice boundaries + filterLeftMbEdgeFlag = MbQ->mbAvailA; + // if this the bottom of a frame macroblock pair then always filter the top edge + filterTopMbEdgeFlag = (p->mb_aff_frame_flag && !MbQ->mb_field && (MbQAddr & 0x01)) ? 1 : MbQ->mbAvailB; + } + + if (p->mb_aff_frame_flag == 1) + CheckAvailabilityOfNeighborsMBAFF(MbQ); + + // Vertical deblocking + for (edge = 0; edge < 4 ; ++edge ) + { + // If cbp == 0 then deblocking for some macroblock types could be skipped + if (MbQ->cbp == 0 && (currSlice->slice_type == P_SLICE || currSlice->slice_type == B_SLICE)) + { + if (filterNon8x8LumaEdgesFlag[edge] == 0 && active_sps->chroma_format_idc != YUV444) + continue; + else if (edge > 0) + { + if (((MbQ->mb_type == PSKIP && currSlice->slice_type == P_SLICE) || (MbQ->mb_type == P16x16) || (MbQ->mb_type == P16x8))) + continue; + else if ((edge & 0x01) && ((MbQ->mb_type == P8x16) || (currSlice->slice_type == B_SLICE && MbQ->mb_type == BSKIP_DIRECT && active_sps->direct_8x8_inference_flag))) + continue; + } + } + + if( edge || filterLeftMbEdgeFlag ) + { + byte *Strength = MbQ->strength_ver[edge]; + + if ((*((int *) Strength))) // only if one of the 16 Strength bytes is != 0 + { + if (filterNon8x8LumaEdgesFlag[edge]) + { + p_Vid->EdgeLoopLumaVer( PLANE_Y, imgY, Strength, MbQ, edge << 2); + if(currSlice->chroma444_not_separate) + { + p_Vid->EdgeLoopLumaVer(PLANE_U, imgUV[0], Strength, MbQ, edge << 2); + p_Vid->EdgeLoopLumaVer(PLANE_V, imgUV[1], Strength, MbQ, edge << 2); + } + } + if (active_sps->chroma_format_idc==YUV420 || active_sps->chroma_format_idc==YUV422) + { + edge_cr = chroma_edge[0][edge][p->chroma_format_idc]; + if( (imgUV != NULL) && (edge_cr >= 0)) + { + p_Vid->EdgeLoopChromaVer( imgUV[0], Strength, MbQ, edge_cr, 0, p); + p_Vid->EdgeLoopChromaVer( imgUV[1], Strength, MbQ, edge_cr, 1, p); + } + } + } + } + }//end edge + + // horizontal deblocking + for( edge = 0; edge < 4 ; ++edge ) + { + // If cbp == 0 then deblocking for some macroblock types could be skipped + if (MbQ->cbp == 0 && (currSlice->slice_type == P_SLICE || currSlice->slice_type == B_SLICE)) + { + if (filterNon8x8LumaEdgesFlag[edge] == 0 && active_sps->chroma_format_idc==YUV420) + continue; + else if (edge > 0) + { + if (((MbQ->mb_type == PSKIP && currSlice->slice_type == P_SLICE) || (MbQ->mb_type == P16x16) || (MbQ->mb_type == P8x16))) + continue; + else if ((edge & 0x01) && ((MbQ->mb_type == P16x8) || (currSlice->slice_type == B_SLICE && MbQ->mb_type == BSKIP_DIRECT && active_sps->direct_8x8_inference_flag))) + continue; + } + } + + if( edge || filterTopMbEdgeFlag ) + { + byte *Strength = MbQ->strength_hor[edge]; + + if ((*((int64 *) Strength)) || ((*(((int64 *) Strength) + 1)))) // only if one of the 16 Strength bytes is != 0 + { + if (filterNon8x8LumaEdgesFlag[edge]) + { + p_Vid->EdgeLoopLumaHor( PLANE_Y, imgY, Strength, MbQ, edge << 2, p) ; + if(currSlice->chroma444_not_separate) + { + p_Vid->EdgeLoopLumaHor(PLANE_U, imgUV[0], Strength, MbQ, edge << 2, p); + p_Vid->EdgeLoopLumaHor(PLANE_V, imgUV[1], Strength, MbQ, edge << 2, p); + } + } + + if (active_sps->chroma_format_idc==YUV420 || active_sps->chroma_format_idc==YUV422) + { + edge_cr = chroma_edge[1][edge][p->chroma_format_idc]; + if( (imgUV != NULL) && (edge_cr >= 0)) + { + p_Vid->EdgeLoopChromaHor( imgUV[0], Strength, MbQ, edge_cr, 0, p); + p_Vid->EdgeLoopChromaHor( imgUV[1], Strength, MbQ, edge_cr, 1, p); + } + } + } + + if (!edge && !MbQ->mb_field && MbQ->mixedModeEdgeFlag) //currSlice->mixedModeEdgeFlag) + { + // this is the extra horizontal edge between a frame macroblock pair and a field above it + MbQ->DeblockCall = 2; + p_Vid->GetStrengthHor(MbQ, 4, mvlimit, p); // Strength for 4 blks in 1 stripe + + //if( *((int*)Strength) ) // only if one of the 4 Strength bytes is != 0 + { + if (filterNon8x8LumaEdgesFlag[edge]) + { + + p_Vid->EdgeLoopLumaHor(PLANE_Y, imgY, Strength, MbQ, MB_BLOCK_SIZE, p) ; + if(currSlice->chroma444_not_separate) + { + p_Vid->EdgeLoopLumaHor(PLANE_U, imgUV[0], Strength, MbQ, MB_BLOCK_SIZE, p) ; + p_Vid->EdgeLoopLumaHor(PLANE_V, imgUV[1], Strength, MbQ, MB_BLOCK_SIZE, p) ; + } + } + if (active_sps->chroma_format_idc==YUV420 || active_sps->chroma_format_idc==YUV422) + { + edge_cr = chroma_edge[1][edge][p->chroma_format_idc]; + if( (imgUV != NULL) && (edge_cr >= 0)) + { + p_Vid->EdgeLoopChromaHor( imgUV[0], Strength, MbQ, MB_BLOCK_SIZE, 0, p) ; + p_Vid->EdgeLoopChromaHor( imgUV[1], Strength, MbQ, MB_BLOCK_SIZE, 1, p) ; + } + } + } + MbQ->DeblockCall = 1; + } + } + }//end edge + + MbQ->DeblockCall = 0; + } +} + diff --git a/ldecod/src/loop_filter_mbaff.c b/ldecod/src/loop_filter_mbaff.c new file mode 100644 index 0000000..87cbc41 --- /dev/null +++ b/ldecod/src/loop_filter_mbaff.c @@ -0,0 +1,1147 @@ + +/*! + ************************************************************************************* + * \file loop_filter_mbaff.c + * + * \brief + * Filter to reduce blocking artifacts on a macroblock level. + * The filter strength is QP dependent. + * + * \author + * Contributors: + * - Peter List Peter.List@t-systems.de: Original code (13-Aug-2001) + * - Jani Lainema Jani.Lainema@nokia.com: Some bug fixing, removal of recursiveness (16-Aug-2001) + * - Peter List Peter.List@t-systems.de: inplace filtering and various simplifications (10-Jan-2002) + * - Anthony Joch anthony@ubvideo.com: Simplified switching between filters and + * non-recursive default filter. (08-Jul-2002) + * - Cristina Gomila cristina.gomila@thomson.net: Simplification of the chroma deblocking + * from JVT-E089 (21-Nov-2002) + * - Alexis Michael Tourapis atour@dolby.com: Speed/Architecture improvements (08-Feb-2007) + ************************************************************************************* + */ + +#include "global.h" +#include "image.h" +#include "mb_access.h" +#include "loopfilter.h" +#include "loop_filter.h" + +//static void get_strength_ver_MBAff (Macroblock *MbQ, int edge, int mvlimit, StorablePicture *p); +//static void get_strength_hor_MBAff (Macroblock *MbQ, int edge, int mvlimit, StorablePicture *p); +static void edge_loop_luma_ver_MBAff (ColorPlane pl, imgpel** Img, byte *Strength, Macroblock *MbQ, int edge); +static void edge_loop_luma_hor_MBAff (ColorPlane pl, imgpel** Img, byte *Strength, Macroblock *MbQ, int edge, StorablePicture *p); +static void edge_loop_chroma_ver_MBAff (imgpel** Img, byte *Strength, Macroblock *MbQ, int edge, int uv, StorablePicture *p); +static void edge_loop_chroma_hor_MBAff (imgpel** Img, byte *Strength, Macroblock *MbQ, int edge, int uv, StorablePicture *p); + +void set_loop_filter_functions_mbaff(VideoParameters *p_Vid) +{ + //p_Vid->GetStrengthVer = get_strength_ver_MBAff; + //p_Vid->GetStrengthHor = get_strength_hor_MBAff; + p_Vid->EdgeLoopLumaVer = edge_loop_luma_ver_MBAff; + p_Vid->EdgeLoopLumaHor = edge_loop_luma_hor_MBAff; + p_Vid->EdgeLoopChromaVer = edge_loop_chroma_ver_MBAff; + p_Vid->EdgeLoopChromaHor = edge_loop_chroma_hor_MBAff; +} + + +Macroblock* get_non_aff_neighbor_luma(Macroblock *mb, int xN, int yN) +{ + if (xN < 0) + return(mb->mbleft); + else if (yN < 0) + return(mb->mbup); + else + return(mb); +} + +Macroblock* get_non_aff_neighbor_chroma(Macroblock *mb, int xN, int yN, int block_width,int block_height) +{ + if (xN < 0) + { + if (yN < block_height) + return(mb->mbleft); + else + return(NULL); + } + else if (xN < block_width) + { + if (yN < 0) + return(mb->mbup); + else if (yN < block_height) + return(mb); + else + return(NULL); + } + else + return(NULL); +} + + +/*! + ********************************************************************************************* + * \brief + * returns a buffer of 16 Strength values for one stripe in a mb (for MBAFF) + ********************************************************************************************* + */ +void get_strength_ver_MBAff(byte *Strength, Macroblock *MbQ, int edge, int mvlimit, StorablePicture *p) +{ + //byte *Strength = MbQ->strength_ver[edge]; + short blkP, blkQ, idx; + //short blk_x, blk_x2, blk_y, blk_y2 ; + + int StrValue; + short mb_x, mb_y; + + Macroblock *MbP; + + PixelPos pixP; + VideoParameters *p_Vid = MbQ->p_Vid; + BlockPos *PicPos = p_Vid->PicPos; + + if ((p->slice_type==SP_SLICE)||(p->slice_type==SI_SLICE) ) + { + for( idx = 0; idx < MB_BLOCK_SIZE; ++idx ) + { + getAffNeighbour(MbQ, edge - 1, idx, p_Vid->mb_size[IS_LUMA], &pixP); + blkQ = (short) ((idx & 0xFFFC) + (edge >> 2)); + blkP = (short) ((pixP.y & 0xFFFC) + (pixP.x >> 2)); + + MbP = &(p_Vid->mb_data[pixP.mb_addr]); + MbQ->mixedModeEdgeFlag = (byte) (MbQ->mb_field != MbP->mb_field); //currSlice->mixedModeEdgeFlag = (byte) (MbQ->mb_field != MbP->mb_field); + + Strength[idx] = (edge == 0) ? 4 : 3; + } + } + else + { + getAffNeighbour(MbQ, edge - 1, 0, p_Vid->mb_size[IS_LUMA], &pixP); + + MbP = &(p_Vid->mb_data[pixP.mb_addr]); + // Neighboring Frame MBs + if ((MbQ->mb_field == FALSE && MbP->mb_field == FALSE)) + { + MbQ->mixedModeEdgeFlag = (byte) (MbQ->mb_field != MbP->mb_field); + if (MbQ->is_intra_block == TRUE || MbP->is_intra_block == TRUE) + { + //printf("idx %d %d %d %d %d\n", idx, pixP.x, pixP.y, pixP.pos_x, pixP.pos_y); + // Start with Strength=3. or Strength=4 for Mb-edge + StrValue = (edge == 0) ? 4 : 3; + memset(Strength, (byte) StrValue, MB_BLOCK_SIZE * sizeof(byte)); + } + else + { + get_mb_block_pos_mbaff (PicPos, MbQ->mbAddrX, &mb_x, &mb_y); + for( idx = 0; idx < MB_BLOCK_SIZE; idx += BLOCK_SIZE) + { + blkQ = (short) ((idx & 0xFFFC) + (edge >> 2)); + blkP = (short) ((pixP.y & 0xFFFC) + (pixP.x >> 2)); + + if (((MbQ->s_cbp[0].blk & i64_power2(blkQ)) != 0) || ((MbP->s_cbp[0].blk & i64_power2(blkP)) != 0)) + StrValue = 2; + else if (edge && ((MbQ->mb_type == 1) || (MbQ->mb_type == 2))) + StrValue = 0; // if internal edge of certain types, we already know StrValue should be 0 + else // for everything else, if no coefs, but vector difference >= 1 set Strength=1 + { + int blk_y = ((mb_y<<2) + (blkQ >> 2)); + int blk_x = ((mb_x<<2) + (blkQ & 3)); + int blk_y2 = (pixP.pos_y >> 2); + int blk_x2 = (pixP.pos_x >> 2); + + PicMotionParams *mv_info_p = &p->mv_info[blk_y ][blk_x ]; + PicMotionParams *mv_info_q = &p->mv_info[blk_y2][blk_x2]; + StorablePicturePtr ref_p0 = mv_info_p->ref_pic[LIST_0]; + StorablePicturePtr ref_q0 = mv_info_q->ref_pic[LIST_0]; + StorablePicturePtr ref_p1 = mv_info_p->ref_pic[LIST_1]; + StorablePicturePtr ref_q1 = mv_info_q->ref_pic[LIST_1]; + + if ( ((ref_p0==ref_q0) && (ref_p1==ref_q1))||((ref_p0==ref_q1) && (ref_p1==ref_q0))) + { + // L0 and L1 reference pictures of p0 are different; q0 as well + if (ref_p0 != ref_p1) + { + // compare MV for the same reference picture + if (ref_p0==ref_q0) + { + StrValue = (byte) ( + compare_mvs(&mv_info_p->mv[LIST_0], &mv_info_q->mv[LIST_0], mvlimit) || + compare_mvs(&mv_info_p->mv[LIST_1], &mv_info_q->mv[LIST_1], mvlimit)); + } + else + { + StrValue = (byte) ( + compare_mvs(&mv_info_p->mv[LIST_0], &mv_info_q->mv[LIST_1], mvlimit) || + compare_mvs(&mv_info_p->mv[LIST_1], &mv_info_q->mv[LIST_0], mvlimit)); + } + } + else + { // L0 and L1 reference pictures of p0 are the same; q0 as well + + StrValue = (byte) (( + compare_mvs(&mv_info_p->mv[LIST_0], &mv_info_q->mv[LIST_0], mvlimit) || + compare_mvs(&mv_info_p->mv[LIST_1], &mv_info_q->mv[LIST_1], mvlimit)) + &&( + compare_mvs(&mv_info_p->mv[LIST_0], &mv_info_q->mv[LIST_1], mvlimit) || + compare_mvs(&mv_info_p->mv[LIST_1], &mv_info_q->mv[LIST_0], mvlimit))); + } + } + else + { + StrValue = 1; + } + } + *(int*)(Strength+idx) = StrValue * 0x01010101; + pixP.y += 4; + pixP.pos_y += 4; + } + } + } + else + { + for( idx = 0; idx < MB_BLOCK_SIZE; ++idx ) + { + getAffNeighbour(MbQ, edge - 1, idx, p_Vid->mb_size[IS_LUMA], &pixP); + blkQ = (short) ((idx & 0xFFFC) + (edge >> 2)); + blkP = (short) ((pixP.y & 0xFFFC) + (pixP.x >> 2)); + + MbP = &(p_Vid->mb_data[pixP.mb_addr]); + MbQ->mixedModeEdgeFlag = (byte) (MbQ->mb_field != MbP->mb_field); + + // Start with Strength=3. or Strength=4 for Mb-edge + Strength[idx] = (edge == 0 && (((!p->mb_aff_frame_flag && (p->structure==FRAME)) || + (p->mb_aff_frame_flag && !MbP->mb_field && !MbQ->mb_field)) || + ((p->mb_aff_frame_flag || (p->structure!=FRAME))))) ? 4 : 3; + + if (MbQ->is_intra_block == FALSE && MbP->is_intra_block == FALSE) + { + if (((MbQ->s_cbp[0].blk & i64_power2(blkQ)) != 0) || ((MbP->s_cbp[0].blk & i64_power2(blkP)) != 0)) + Strength[idx] = 2 ; + else + { + // if no coefs, but vector difference >= 1 set Strength=1 + // if this is a mixed mode edge then one set of reference pictures will be frame and the + // other will be field + if(MbQ->mixedModeEdgeFlag) //if (currSlice->mixedModeEdgeFlag) + { + Strength[idx] = 1; + } + else + { + get_mb_block_pos_mbaff (PicPos, MbQ->mbAddrX, &mb_x, &mb_y); + { + int blk_y = ((mb_y<<2) + (blkQ >> 2)); + int blk_x = ((mb_x<<2) + (blkQ & 3)); + int blk_y2 = (pixP.pos_y >> 2); + int blk_x2 = (pixP.pos_x >> 2); + + PicMotionParams *mv_info_p = &p->mv_info[blk_y ][blk_x ]; + PicMotionParams *mv_info_q = &p->mv_info[blk_y2][blk_x2]; + StorablePicturePtr ref_p0 = mv_info_p->ref_pic[LIST_0]; + StorablePicturePtr ref_q0 = mv_info_q->ref_pic[LIST_0]; + StorablePicturePtr ref_p1 = mv_info_p->ref_pic[LIST_1]; + StorablePicturePtr ref_q1 = mv_info_q->ref_pic[LIST_1]; + + if ( ((ref_p0==ref_q0) && (ref_p1==ref_q1))||((ref_p0==ref_q1) && (ref_p1==ref_q0))) + { + Strength[idx]=0; + // L0 and L1 reference pictures of p0 are different; q0 as well + if (ref_p0 != ref_p1) + { + // compare MV for the same reference picture + if (ref_p0==ref_q0) + { + Strength[idx] = (byte) ( + compare_mvs(&mv_info_p->mv[LIST_0], &mv_info_q->mv[LIST_0], mvlimit) || + compare_mvs(&mv_info_p->mv[LIST_1], &mv_info_q->mv[LIST_1], mvlimit)); + } + else + { + Strength[idx] = (byte) ( + compare_mvs(&mv_info_p->mv[LIST_0], &mv_info_q->mv[LIST_1], mvlimit) || + compare_mvs(&mv_info_p->mv[LIST_1], &mv_info_q->mv[LIST_0], mvlimit)); + } + } + else + { // L0 and L1 reference pictures of p0 are the same; q0 as well + + Strength[idx] = (byte) (( + compare_mvs(&mv_info_p->mv[LIST_0], &mv_info_q->mv[LIST_0], mvlimit) || + compare_mvs(&mv_info_p->mv[LIST_1], &mv_info_q->mv[LIST_1], mvlimit)) + &&( + compare_mvs(&mv_info_p->mv[LIST_0], &mv_info_q->mv[LIST_1], mvlimit) || + compare_mvs(&mv_info_p->mv[LIST_1], &mv_info_q->mv[LIST_0], mvlimit))); + } + } + else + { + Strength[idx] = 1; + } + } + } + } + } + } + } + } +} + +/*! + ********************************************************************************************* + * \brief + * returns a buffer of 16 Strength values for one stripe in a mb (for MBAFF) + ********************************************************************************************* + */ +void get_strength_hor_MBAff(byte *Strength, Macroblock *MbQ, int edge, int mvlimit, StorablePicture *p) +{ + short blkP, blkQ, idx; + short blk_x, blk_x2, blk_y, blk_y2 ; + + int StrValue; + int xQ, yQ = (edge < MB_BLOCK_SIZE ? edge : 1); + short mb_x, mb_y; + + Macroblock *MbP; + + PixelPos pixP; + VideoParameters *p_Vid = MbQ->p_Vid; + BlockPos *PicPos = p_Vid->PicPos; + + if ((p->slice_type==SP_SLICE)||(p->slice_type==SI_SLICE) ) + { + for( idx = 0; idx < MB_BLOCK_SIZE; idx += BLOCK_SIZE) + { + xQ = idx; + getAffNeighbour(MbQ, xQ, yQ - 1, p_Vid->mb_size[IS_LUMA], &pixP); + + blkQ = (short) ((yQ & 0xFFFC) + (xQ >> 2)); + blkP = (short) ((pixP.y & 0xFFFC) + (pixP.x >> 2)); + + MbP = &(p_Vid->mb_data[pixP.mb_addr]); + MbQ->mixedModeEdgeFlag = (byte) (MbQ->mb_field != MbP->mb_field); + + StrValue = (edge == 0 && (!MbP->mb_field && !MbQ->mb_field)) ? 4 : 3; + + *(int*)(Strength+idx) = StrValue * 0x01010101; + } + } + else + { + getAffNeighbour(MbQ, 0, yQ - 1, p_Vid->mb_size[IS_LUMA], &pixP); + MbP = &(p_Vid->mb_data[pixP.mb_addr]); + MbQ->mixedModeEdgeFlag = (byte) (MbQ->mb_field != MbP->mb_field); + + // Set intra mode deblocking + if (MbQ->is_intra_block == TRUE || MbP->is_intra_block == TRUE) + { + StrValue = (edge == 0 && (!MbP->mb_field && !MbQ->mb_field)) ? 4 : 3; + memset(Strength, (byte) StrValue, MB_BLOCK_SIZE * sizeof(byte)); + } + else + { + for( idx = 0; idx < MB_BLOCK_SIZE; idx += BLOCK_SIZE ) + { + xQ = idx; + getAffNeighbour(MbQ, xQ, yQ - 1, p_Vid->mb_size[IS_LUMA], &pixP); + + blkQ = (short) ((yQ & 0xFFFC) + (xQ >> 2)); + blkP = (short) ((pixP.y & 0xFFFC) + (pixP.x >> 2)); + + if (((MbQ->s_cbp[0].blk & i64_power2(blkQ)) != 0) || ((MbP->s_cbp[0].blk & i64_power2(blkP)) != 0)) + { + StrValue = 2; + } + else + { + // if no coefs, but vector difference >= 1 set Strength=1 + // if this is a mixed mode edge then one set of reference pictures will be frame and the + // other will be field + if(MbQ->mixedModeEdgeFlag) //if (currSlice->mixedModeEdgeFlag) + { + StrValue = 1; + } + else + { + get_mb_block_pos_mbaff (PicPos, MbQ->mbAddrX, &mb_x, &mb_y); + blk_y = (short) ((mb_y<<2) + (blkQ >> 2)); + blk_x = (short) ((mb_x<<2) + (blkQ & 3)); + blk_y2 = (short) (pixP.pos_y >> 2); + blk_x2 = (short) (pixP.pos_x >> 2); + + { + PicMotionParams *mv_info_p = &p->mv_info[blk_y ][blk_x ]; + PicMotionParams *mv_info_q = &p->mv_info[blk_y2][blk_x2]; + StorablePicturePtr ref_p0 = mv_info_p->ref_pic[LIST_0]; + StorablePicturePtr ref_q0 = mv_info_q->ref_pic[LIST_0]; + StorablePicturePtr ref_p1 = mv_info_p->ref_pic[LIST_1]; + StorablePicturePtr ref_q1 = mv_info_q->ref_pic[LIST_1]; + + if ( ((ref_p0==ref_q0) && (ref_p1==ref_q1)) || + ((ref_p0==ref_q1) && (ref_p1==ref_q0))) + { + StrValue = 0; + // L0 and L1 reference pictures of p0 are different; q0 as well + if (ref_p0 != ref_p1) + { + // compare MV for the same reference picture + if (ref_p0==ref_q0) + { + StrValue = (byte) ( + compare_mvs(&mv_info_p->mv[LIST_0], &mv_info_q->mv[LIST_0], mvlimit) || + compare_mvs(&mv_info_p->mv[LIST_1], &mv_info_q->mv[LIST_1], mvlimit)); + } + else + { + StrValue = (byte) ( + compare_mvs(&mv_info_p->mv[LIST_0], &mv_info_q->mv[LIST_1], mvlimit) || + compare_mvs(&mv_info_p->mv[LIST_1], &mv_info_q->mv[LIST_0], mvlimit)); + } + } + else + { // L0 and L1 reference pictures of p0 are the same; q0 as well + StrValue = (byte) (( + compare_mvs(&mv_info_p->mv[LIST_0], &mv_info_q->mv[LIST_0], mvlimit) || + compare_mvs(&mv_info_p->mv[LIST_1], &mv_info_q->mv[LIST_1], mvlimit)) + &&( + compare_mvs(&mv_info_p->mv[LIST_0], &mv_info_q->mv[LIST_1], mvlimit) || + compare_mvs(&mv_info_p->mv[LIST_1], &mv_info_q->mv[LIST_0], mvlimit))); + } + } + else + { + StrValue = 1; + } + } + } + } + *(int*)(Strength+idx) = StrValue * 0x01010101; + } + } + } +} + + +/*! + ***************************************************************************************** + * \brief + * Filters 16 pel block edge of Super MB Frame coded MBs + ***************************************************************************************** + */ +static void edge_loop_luma_ver_MBAff(ColorPlane pl, imgpel** Img, byte *Strength, Macroblock *MbQ, int edge) +{ + int pel, Strng ; + imgpel L2 = 0, L1, L0, R0, R1, R2 = 0; + int Alpha = 0, Beta = 0 ; + const byte* ClipTab = NULL; + int indexA, indexB; + + int QP; + + PixelPos pixP, pixQ; + + VideoParameters *p_Vid = MbQ->p_Vid; + int bitdepth_scale = pl? p_Vid->bitdepth_scale[IS_CHROMA] : p_Vid->bitdepth_scale[IS_LUMA]; + int max_imgpel_value = p_Vid->max_pel_value_comp[pl]; + + int AlphaC0Offset = MbQ->DFAlphaC0Offset; + int BetaOffset = MbQ->DFBetaOffset; + + Macroblock *MbP; + imgpel *SrcPtrP, *SrcPtrQ; + + if (MbQ->DFDisableIdc == 0) + { + for( pel = 0 ; pel < MB_BLOCK_SIZE ; ++pel ) + { + getAffNeighbour(MbQ, edge - 1, pel, p_Vid->mb_size[IS_LUMA], &pixP); + + if (pixP.available) + { + if( (Strng = Strength[pel]) != 0) + { + getAffNeighbour(MbQ, edge, pel, p_Vid->mb_size[IS_LUMA], &pixQ); + + MbP = &(p_Vid->mb_data[pixP.mb_addr]); + + SrcPtrQ = &(Img[pixQ.pos_y][pixQ.pos_x]); + SrcPtrP = &(Img[pixP.pos_y][pixP.pos_x]); + + // Average QP of the two blocks + QP = pl? ((MbP->qpc[pl-1] + MbQ->qpc[pl-1] + 1) >> 1) : (MbP->qp + MbQ->qp + 1) >> 1; + + indexA = iClip3(0, MAX_QP, QP + AlphaC0Offset); + indexB = iClip3(0, MAX_QP, QP + BetaOffset); + + Alpha = ALPHA_TABLE[indexA] * bitdepth_scale; + Beta = BETA_TABLE [indexB] * bitdepth_scale; + ClipTab = CLIP_TAB[indexA]; + + L0 = SrcPtrP[ 0] ; + R0 = SrcPtrQ[ 0] ; + + if( iabs( R0 - L0 ) < Alpha ) + { + L1 = SrcPtrP[-1]; + R1 = SrcPtrQ[ 1]; + + if ((iabs( R0 - R1) < Beta ) && (iabs(L0 - L1) < Beta )) + { + L2 = SrcPtrP[-2]; + R2 = SrcPtrQ[ 2]; + if(Strng == 4 ) // INTRA strong filtering + { + int RL0 = L0 + R0; + int small_gap = (iabs( R0 - L0 ) < ((Alpha >> 2) + 2)); + int aq = ( iabs( R0 - R2) < Beta ) & small_gap; + int ap = ( iabs( L0 - L2) < Beta ) & small_gap; + + if (ap) + { + int L3 = SrcPtrP[-3]; + SrcPtrP[-2 ] = (imgpel) ((((L3 + L2) << 1) + L2 + L1 + RL0 + 4) >> 3); + SrcPtrP[-1 ] = (imgpel) (( L2 + L1 + L0 + R0 + 2) >> 2); + SrcPtrP[ 0 ] = (imgpel) (( R1 + ((L1 + RL0) << 1) + L2 + 4) >> 3); + } + else + { + SrcPtrP[ 0 ] = (imgpel) (((L1 << 1) + L0 + R1 + 2) >> 2) ; + } + + if (aq) + { + imgpel R3 = SrcPtrQ[ 3]; + SrcPtrQ[ 0 ] = (imgpel) (( L1 + ((R1 + RL0) << 1) + R2 + 4) >> 3); + SrcPtrQ[ 1 ] = (imgpel) (( R2 + R0 + R1 + L0 + 2) >> 2); + SrcPtrQ[ 2 ] = (imgpel) ((((R3 + R2) << 1) + R2 + R1 + RL0 + 4) >> 3); + } + else + { + SrcPtrQ[ 0 ] = (imgpel) (((R1 << 1) + R0 + L1 + 2) >> 2); + } + } + else // normal filtering + { + int RL0 = (L0 + R0 + 1) >> 1; + int aq = (iabs( R0 - R2) < Beta); + int ap = (iabs( L0 - L2) < Beta); + + int C0 = ClipTab[ Strng ] * bitdepth_scale; + int tc0 = (C0 + ap + aq) ; + int dif = iClip3( -tc0, tc0, (((R0 - L0) << 2) + (L1 - R1) + 4) >> 3) ; + + if( ap && (C0 != 0)) + *(SrcPtrP - 1) += iClip3( -C0, C0, ( L2 + RL0 - (L1 << 1)) >> 1 ) ; + + if (dif) + { + *SrcPtrP = (imgpel) iClip1 (max_imgpel_value, L0 + dif) ; + *SrcPtrQ = (imgpel) iClip1 (max_imgpel_value, R0 - dif) ; + } + + if( aq && (C0 != 0)) + *(SrcPtrQ + 1) += iClip3( -C0, C0, ( R2 + RL0 - (R1 << 1)) >> 1 ) ; + } + } + } + } + } + } + } +} + +/*! + ***************************************************************************************** + * \brief + * Filters 16 pel block edge of Super MB Frame coded MBs + ***************************************************************************************** + */ +static void edge_loop_luma_hor_MBAff(ColorPlane pl, imgpel** Img, byte *Strength, Macroblock *MbQ, + int edge, StorablePicture *p) +{ + int width = p->iLumaStride; //p->size_x; + int pel, Strng ; + int PelNum = pl? pelnum_cr[1][p->chroma_format_idc] : MB_BLOCK_SIZE; + + int yQ = (edge < MB_BLOCK_SIZE ? edge : 1); + + PixelPos pixP, pixQ; + + VideoParameters *p_Vid = MbQ->p_Vid; + int bitdepth_scale = pl? p_Vid->bitdepth_scale[IS_CHROMA] : p_Vid->bitdepth_scale[IS_LUMA]; + int max_imgpel_value = p_Vid->max_pel_value_comp[pl]; + + getAffNeighbour(MbQ, 0, yQ - 1, p_Vid->mb_size[IS_LUMA], &pixP); + + if (pixP.available || (MbQ->DFDisableIdc == 0)) + { + int AlphaC0Offset = MbQ->DFAlphaC0Offset; + int BetaOffset = MbQ->DFBetaOffset; + + Macroblock *MbP = &(p_Vid->mb_data[pixP.mb_addr]); + + int incQ = ((MbP->mb_field && !MbQ->mb_field) ? 2 * width : width); + int incP = ((MbQ->mb_field && !MbP->mb_field) ? 2 * width : width); + + // Average QP of the two blocks + int QP = pl? ((MbP->qpc[pl - 1] + MbQ->qpc[pl - 1] + 1) >> 1) : (MbP->qp + MbQ->qp + 1) >> 1; + + int indexA = iClip3(0, MAX_QP, QP + AlphaC0Offset); + int indexB = iClip3(0, MAX_QP, QP + BetaOffset); + + int Alpha = ALPHA_TABLE[indexA] * bitdepth_scale; + int Beta = BETA_TABLE [indexB] * bitdepth_scale; + + if ((Alpha | Beta )!= 0) + { + const byte* ClipTab = CLIP_TAB[indexA]; + getAffNeighbour(MbQ, 0, yQ, p_Vid->mb_size[IS_LUMA], &pixQ); + + for( pel = 0 ; pel < PelNum ; ++pel ) + { + if( (Strng = Strength[pel]) != 0) + { + imgpel *SrcPtrQ = &(Img[pixQ.pos_y][pixQ.pos_x]); + imgpel *SrcPtrP = &(Img[pixP.pos_y][pixP.pos_x]); + + imgpel L0 = *SrcPtrP; + imgpel R0 = *SrcPtrQ; + + if( iabs( R0 - L0 ) < Alpha ) + { + imgpel L1 = SrcPtrP[-incP]; + imgpel R1 = SrcPtrQ[ incQ]; + + if ((iabs( R0 - R1) < Beta ) && (iabs(L0 - L1) < Beta )) + { + imgpel L2 = SrcPtrP[-incP*2]; + imgpel R2 = SrcPtrQ[ incQ*2]; + if(Strng == 4 ) // INTRA strong filtering + { + int RL0 = L0 + R0; + int small_gap = (iabs( R0 - L0 ) < ((Alpha >> 2) + 2)); + int aq = ( iabs( R0 - R2) < Beta ) & small_gap; + int ap = ( iabs( L0 - L2) < Beta ) & small_gap; + + if (ap) + { + imgpel L3 = SrcPtrP[-incP*3]; + SrcPtrP[-incP * 2] = (imgpel) ((((L3 + L2) << 1) + L2 + L1 + RL0 + 4) >> 3); + SrcPtrP[-incP ] = (imgpel) (( L2 + L1 + L0 + R0 + 2) >> 2); + SrcPtrP[ 0 ] = (imgpel) (( R1 + ((L1 + RL0) << 1) + L2 + 4) >> 3); + } + else + { + SrcPtrP[ 0 ] = (imgpel) (((L1 << 1) + L0 + R1 + 2) >> 2) ; + } + + if (aq) + { + imgpel R3 = SrcPtrQ[ incQ*3]; + SrcPtrQ[ 0 ] = (imgpel) (( L1 + ((R1 + RL0) << 1) + R2 + 4) >> 3); + SrcPtrQ[ incQ ] = (imgpel) (( R2 + R0 + R1 + L0 + 2) >> 2); + SrcPtrQ[ incQ * 2 ] = (imgpel) ((((R3 + R2) << 1) + R2 + R1 + RL0 + 4) >> 3); + } + else + { + SrcPtrQ[ 0 ] = (imgpel) (((R1 << 1) + R0 + L1 + 2) >> 2); + } + } + else // normal filtering + { + int RL0 = (L0 + R0 + 1) >> 1; + int aq = (iabs( R0 - R2) < Beta); + int ap = (iabs( L0 - L2) < Beta); + + int C0 = ClipTab[ Strng ] * bitdepth_scale; + int tc0 = (C0 + ap + aq) ; + int dif = iClip3( -tc0, tc0, (((R0 - L0) << 2) + (L1 - R1) + 4) >> 3) ; + + if( ap && (C0 != 0)) + *(SrcPtrP - incP) += iClip3( -C0, C0, ( L2 + RL0 - (L1 << 1)) >> 1 ) ; + + if (dif) + { + *SrcPtrP = (imgpel) iClip1 (max_imgpel_value, L0 + dif) ; + *SrcPtrQ = (imgpel) iClip1 (max_imgpel_value, R0 - dif) ; + } + + if( aq && (C0 != 0)) + *(SrcPtrQ + incQ) += iClip3( -C0, C0, ( R2 + RL0 - (R1 << 1)) >> 1 ) ; + } + } + } + } + pixP.pos_x++; + pixQ.pos_x++; + } + } + } +} + + + +/*! +***************************************************************************************** +* \brief +* Filters chroma block edge for MBAFF types +***************************************************************************************** + */ +static void edge_loop_chroma_ver_MBAff(imgpel** Img, byte *Strength, Macroblock *MbQ, int edge, int uv, StorablePicture *p) +{ + int pel, Strng ; + + imgpel L1, L0, R0, R1; + int Alpha = 0, Beta = 0; + const byte* ClipTab = NULL; + int indexA, indexB; + VideoParameters *p_Vid = MbQ->p_Vid; + int PelNum = pelnum_cr[0][p->chroma_format_idc]; + int StrengthIdx; + int QP; + int xQ = edge, yQ; + PixelPos pixP, pixQ; + int bitdepth_scale = p_Vid->bitdepth_scale[IS_CHROMA]; + int max_imgpel_value = p_Vid->max_pel_value_comp[uv + 1]; + + int AlphaC0Offset = MbQ->DFAlphaC0Offset; + int BetaOffset = MbQ->DFBetaOffset; + Macroblock *MbP; + imgpel *SrcPtrP, *SrcPtrQ; + + + for( pel = 0 ; pel < PelNum ; ++pel ) + { + yQ = pel; + getAffNeighbour(MbQ, xQ, yQ, p_Vid->mb_size[IS_CHROMA], &pixQ); + getAffNeighbour(MbQ, xQ - 1, yQ, p_Vid->mb_size[IS_CHROMA], &pixP); + MbP = &(p_Vid->mb_data[pixP.mb_addr]); + StrengthIdx = (PelNum == 8) ? ((MbQ->mb_field && !MbP->mb_field) ? pel << 1 :((pel >> 1) << 2) + (pel & 0x01)) : pel; + + if (pixP.available || (MbQ->DFDisableIdc == 0)) + { + if( (Strng = Strength[StrengthIdx]) != 0) + { + SrcPtrQ = &(Img[pixQ.pos_y][pixQ.pos_x]); + SrcPtrP = &(Img[pixP.pos_y][pixP.pos_x]); + + // Average QP of the two blocks + QP = (MbP->qpc[uv] + MbQ->qpc[uv] + 1) >> 1; + + indexA = iClip3(0, MAX_QP, QP + AlphaC0Offset); + indexB = iClip3(0, MAX_QP, QP + BetaOffset); + + Alpha = ALPHA_TABLE[indexA] * bitdepth_scale; + Beta = BETA_TABLE [indexB] * bitdepth_scale; + ClipTab = CLIP_TAB[indexA]; + + L0 = *SrcPtrP; + R0 = *SrcPtrQ; + + if( iabs( R0 - L0 ) < Alpha ) + { + L1 = SrcPtrP[-1]; + R1 = SrcPtrQ[ 1]; + if( ((iabs( R0 - R1) - Beta < 0) && (iabs(L0 - L1) - Beta < 0 )) ) + { + if( Strng == 4 ) // INTRA strong filtering + { + SrcPtrQ[0] = (imgpel) ( ((R1 << 1) + R0 + L1 + 2) >> 2 ); + SrcPtrP[0] = (imgpel) ( ((L1 << 1) + L0 + R1 + 2) >> 2 ); + } + else + { + int C0 = ClipTab[ Strng ] * bitdepth_scale; + int tc0 = (C0 + 1); + int dif = iClip3( -tc0, tc0, ( ((R0 - L0) << 2) + (L1 - R1) + 4) >> 3 ); + + if (dif) + { + *SrcPtrP = (imgpel) iClip1 ( max_imgpel_value, L0 + dif ); + *SrcPtrQ = (imgpel) iClip1 ( max_imgpel_value, R0 - dif ); + } + } + } + } + } + } + } +} + +/*! +***************************************************************************************** +* \brief +* Filters chroma block edge for MBAFF types +***************************************************************************************** + */ +static void edge_loop_chroma_hor_MBAff(imgpel** Img, byte *Strength, Macroblock *MbQ, int edge, int uv, StorablePicture *p) +{ + VideoParameters *p_Vid = MbQ->p_Vid; + int PelNum = pelnum_cr[1][p->chroma_format_idc]; + int yQ = (edge < MB_BLOCK_SIZE? edge : 1); + PixelPos pixP, pixQ; + int bitdepth_scale = p_Vid->bitdepth_scale[IS_CHROMA]; + int max_imgpel_value = p_Vid->max_pel_value_comp[uv + 1]; + + int AlphaC0Offset = MbQ->DFAlphaC0Offset; + int BetaOffset = MbQ->DFBetaOffset; + int width = p->iChromaStride; //p->size_x_cr; + + getAffNeighbour(MbQ, 0, yQ - 1, p_Vid->mb_size[IS_CHROMA], &pixP); + getAffNeighbour(MbQ, 0, yQ, p_Vid->mb_size[IS_CHROMA], &pixQ); + + if (pixP.available || (MbQ->DFDisableIdc == 0)) + { + Macroblock *MbP = &(p_Vid->mb_data[pixP.mb_addr]); + + int incQ = ((MbP->mb_field && !MbQ->mb_field) ? 2 * width : width); + int incP = ((MbQ->mb_field && !MbP->mb_field) ? 2 * width : width); + + // Average QP of the two blocks + int QP = (MbP->qpc[uv] + MbQ->qpc[uv] + 1) >> 1; + + int indexA = iClip3(0, MAX_QP, QP + AlphaC0Offset); + int indexB = iClip3(0, MAX_QP, QP + BetaOffset); + + int Alpha = ALPHA_TABLE[indexA] * bitdepth_scale; + int Beta = BETA_TABLE [indexB] * bitdepth_scale; + + if ((Alpha | Beta )!= 0) + { + const byte* ClipTab = CLIP_TAB[indexA]; + int pel, Strng ; + int StrengthIdx; + for( pel = 0 ; pel < PelNum ; ++pel ) + { + StrengthIdx = (PelNum == 8) ? ((MbQ->mb_field && !MbP->mb_field) ? pel << 1 :((pel >> 1) << 2) + (pel & 0x01)) : pel; + + if( (Strng = Strength[StrengthIdx]) != 0) + { + imgpel *SrcPtrQ = &(Img[pixQ.pos_y][pixQ.pos_x]); + imgpel *SrcPtrP = &(Img[pixP.pos_y][pixP.pos_x]); + + imgpel L0 = *SrcPtrP; + imgpel R0 = *SrcPtrQ; + + if( iabs( R0 - L0 ) < Alpha ) + { + imgpel L1 = SrcPtrP[-incP]; + imgpel R1 = SrcPtrQ[ incQ]; + if( ((iabs( R0 - R1) - Beta < 0) && (iabs(L0 - L1) - Beta < 0 )) ) + { + if( Strng == 4 ) // INTRA strong filtering + { + SrcPtrQ[0] = (imgpel) ( ((R1 << 1) + R0 + L1 + 2) >> 2 ); + SrcPtrP[0] = (imgpel) ( ((L1 << 1) + L0 + R1 + 2) >> 2 ); + } + else + { + int C0 = ClipTab[ Strng ] * bitdepth_scale; + int tc0 = (C0 + 1); + int dif = iClip3( -tc0, tc0, ( ((R0 - L0) << 2) + (L1 - R1) + 4) >> 3 ); + + if (dif) + { + *SrcPtrP = (imgpel) iClip1 ( max_imgpel_value, L0 + dif ); + *SrcPtrQ = (imgpel) iClip1 ( max_imgpel_value, R0 - dif ); + } + } + } + } + } + pixP.pos_x++; + pixQ.pos_x++; + } + } + } +} + + +/*! + ***************************************************************************************** + * \brief + * Get Deblocking filter strength for one macroblock. + ***************************************************************************************** + */ +void get_db_strength_mbaff(VideoParameters *p_Vid, StorablePicture *p, int MbQAddr) +{ + Macroblock *MbQ = &(p_Vid->mb_data[MbQAddr]) ; // current Mb + + // return, if filter is disabled + if (MbQ->DFDisableIdc == 1) + { + MbQ->DeblockCall = 0; + } + else + { + int edge; + + short mb_x, mb_y; + + int filterNon8x8LumaEdgesFlag[4] = {1,1,1,1}; + int filterLeftMbEdgeFlag; + int filterTopMbEdgeFlag; + + Slice *currSlice = MbQ->p_Slice; + int mvlimit = ((p->structure!=FRAME) || (p->mb_aff_frame_flag && MbQ->mb_field)) ? 2 : 4; + + seq_parameter_set_rbsp_t *active_sps = p_Vid->active_sps; + + MbQ->DeblockCall = 1; + get_mb_pos (p_Vid, MbQAddr, p_Vid->mb_size[IS_LUMA], &mb_x, &mb_y); + + if (MbQ->mb_type == I8MB) + assert(MbQ->luma_transform_size_8x8_flag); + + filterNon8x8LumaEdgesFlag[1] = + filterNon8x8LumaEdgesFlag[3] = !(MbQ->luma_transform_size_8x8_flag); + + filterLeftMbEdgeFlag = (mb_x != 0); + filterTopMbEdgeFlag = (mb_y != 0); + + if (p->mb_aff_frame_flag && mb_y == MB_BLOCK_SIZE && MbQ->mb_field) + filterTopMbEdgeFlag = 0; + + if (MbQ->DFDisableIdc==2) + { + // don't filter at slice boundaries + filterLeftMbEdgeFlag = MbQ->mbAvailA; + // if this the bottom of a frame macroblock pair then always filter the top edge + filterTopMbEdgeFlag = (p->mb_aff_frame_flag && !MbQ->mb_field && (MbQAddr & 0x01)) ? 1 : MbQ->mbAvailB; + } + + CheckAvailabilityOfNeighborsMBAFF(MbQ); + + // Vertical deblocking + for (edge = 0; edge < 4 ; ++edge ) + { + // If cbp == 0 then deblocking for some macroblock types could be skipped + if (MbQ->cbp == 0) + { + if (filterNon8x8LumaEdgesFlag[edge] == 0 && active_sps->chroma_format_idc != YUV444) + continue; + else if (edge > 0) + { + if (((MbQ->mb_type == PSKIP && currSlice->slice_type == P_SLICE) || (MbQ->mb_type == P16x16) || (MbQ->mb_type == P16x8))) + continue; + else if ((edge & 0x01) && ((MbQ->mb_type == P8x16) || (currSlice->slice_type == B_SLICE && MbQ->mb_type == BSKIP_DIRECT && active_sps->direct_8x8_inference_flag))) + continue; + } + } + + if( edge || filterLeftMbEdgeFlag ) + { + // Strength for 4 blks in 1 stripe + p_Vid->GetStrengthVer(MbQ, edge, mvlimit, p); + } + }//end edge + + // horizontal deblocking + for( edge = 0; edge < 4 ; ++edge ) + { + // If cbp == 0 then deblocking for some macroblock types could be skipped + if (MbQ->cbp == 0) + { + if (filterNon8x8LumaEdgesFlag[edge] == 0 && active_sps->chroma_format_idc==YUV420) + continue; + else if (edge > 0) + { + if (((MbQ->mb_type == PSKIP && currSlice->slice_type == P_SLICE) || (MbQ->mb_type == P16x16) || (MbQ->mb_type == P8x16))) + continue; + else if ((edge & 0x01) && ((MbQ->mb_type == P16x8) || (currSlice->slice_type == B_SLICE && MbQ->mb_type == BSKIP_DIRECT && active_sps->direct_8x8_inference_flag))) + continue; + } + } + + if( edge || filterTopMbEdgeFlag ) + { + p_Vid->GetStrengthHor(MbQ, edge, mvlimit, p); + } + }//end edge + + MbQ->DeblockCall = 0; + } +} + + +/*! + ***************************************************************************************** + * \brief + * Performing Deblocking for one macroblock. + ***************************************************************************************** + */ +void perform_db_mbaff(VideoParameters *p_Vid, StorablePicture *p, int MbQAddr) +{ + Macroblock *MbQ = &(p_Vid->mb_data[MbQAddr]) ; // current Mb + + // return, if filter is disabled + if (MbQ->DFDisableIdc == 1) + { + MbQ->DeblockCall = 0; + } + else + { + int edge; + + short mb_x, mb_y; + + int filterNon8x8LumaEdgesFlag[4] = {1,1,1,1}; + int filterLeftMbEdgeFlag; + int filterTopMbEdgeFlag; + int edge_cr; + + imgpel **imgY = p->imgY; + imgpel ***imgUV = p->imgUV; + Slice *currSlice = MbQ->p_Slice; + int mvlimit = ((p->structure!=FRAME) || (p->mb_aff_frame_flag && MbQ->mb_field)) ? 2 : 4; + + seq_parameter_set_rbsp_t *active_sps = p_Vid->active_sps; + + MbQ->DeblockCall = 1; + get_mb_pos (p_Vid, MbQAddr, p_Vid->mb_size[IS_LUMA], &mb_x, &mb_y); + + if (MbQ->mb_type == I8MB) + assert(MbQ->luma_transform_size_8x8_flag); + + filterNon8x8LumaEdgesFlag[1] = + filterNon8x8LumaEdgesFlag[3] = !(MbQ->luma_transform_size_8x8_flag); + + filterLeftMbEdgeFlag = (mb_x != 0); + filterTopMbEdgeFlag = (mb_y != 0); + + if (p->mb_aff_frame_flag && mb_y == MB_BLOCK_SIZE && MbQ->mb_field) + filterTopMbEdgeFlag = 0; + + if (MbQ->DFDisableIdc==2) + { + // don't filter at slice boundaries + filterLeftMbEdgeFlag = MbQ->mbAvailA; + // if this the bottom of a frame macroblock pair then always filter the top edge + filterTopMbEdgeFlag = (p->mb_aff_frame_flag && !MbQ->mb_field && (MbQAddr & 0x01)) ? 1 : MbQ->mbAvailB; + } + + CheckAvailabilityOfNeighborsMBAFF(MbQ); + + // Vertical deblocking + for (edge = 0; edge < 4 ; ++edge ) + { + // If cbp == 0 then deblocking for some macroblock types could be skipped + if (MbQ->cbp == 0) + { + if (filterNon8x8LumaEdgesFlag[edge] == 0 && active_sps->chroma_format_idc != YUV444) + continue; + else if (edge > 0 && (currSlice->slice_type == P_SLICE || currSlice->slice_type == B_SLICE)) + { + if (((MbQ->mb_type == PSKIP && currSlice->slice_type == P_SLICE) || (MbQ->mb_type == P16x16) || (MbQ->mb_type == P16x8))) + continue; + else if ((edge & 0x01) && ((MbQ->mb_type == P8x16) || (currSlice->slice_type == B_SLICE && MbQ->mb_type == BSKIP_DIRECT && active_sps->direct_8x8_inference_flag))) + continue; + } + } + + if( edge || filterLeftMbEdgeFlag ) + { + byte *Strength = MbQ->strength_ver[edge]; + + if ((*((int64 *) Strength)) || ((*(((int64 *) Strength) + 1)))) // only if one of the 16 Strength bytes is != 0 + { + if (filterNon8x8LumaEdgesFlag[edge]) + { + p_Vid->EdgeLoopLumaVer( PLANE_Y, imgY, Strength, MbQ, edge << 2); + if(currSlice->chroma444_not_separate) + { + p_Vid->EdgeLoopLumaVer(PLANE_U, imgUV[0], Strength, MbQ, edge << 2); + p_Vid->EdgeLoopLumaVer(PLANE_V, imgUV[1], Strength, MbQ, edge << 2); + } + } + if (active_sps->chroma_format_idc==YUV420 || active_sps->chroma_format_idc==YUV422) + { + edge_cr = chroma_edge[0][edge][p->chroma_format_idc]; + if( (imgUV != NULL) && (edge_cr >= 0)) + { + p_Vid->EdgeLoopChromaVer( imgUV[0], Strength, MbQ, edge_cr, 0, p); + p_Vid->EdgeLoopChromaVer( imgUV[1], Strength, MbQ, edge_cr, 1, p); + } + } + } + } + }//end edge + + // horizontal deblocking + for( edge = 0; edge < 4 ; ++edge ) + { + // If cbp == 0 then deblocking for some macroblock types could be skipped + if (MbQ->cbp == 0) + { + if (filterNon8x8LumaEdgesFlag[edge] == 0 && active_sps->chroma_format_idc==YUV420) + continue; + else if (edge > 0 && (currSlice->slice_type == P_SLICE || currSlice->slice_type == B_SLICE)) + { + if (((MbQ->mb_type == PSKIP && currSlice->slice_type == P_SLICE) || (MbQ->mb_type == P16x16) || (MbQ->mb_type == P8x16))) + continue; + else if ((edge & 0x01) && ((MbQ->mb_type == P16x8) || (currSlice->slice_type == B_SLICE && MbQ->mb_type == BSKIP_DIRECT && active_sps->direct_8x8_inference_flag))) + continue; + } + } + + if( edge || filterTopMbEdgeFlag ) + { + byte *Strength = MbQ->strength_hor[edge]; + + if ((*((int64 *) Strength)) || ((*(((int64 *) Strength) + 1)))) // only if one of the 16 Strength bytes is != 0 + { + if (filterNon8x8LumaEdgesFlag[edge]) + { + p_Vid->EdgeLoopLumaHor( PLANE_Y, imgY, Strength, MbQ, edge << 2, p) ; + if(currSlice->chroma444_not_separate) + { + p_Vid->EdgeLoopLumaHor(PLANE_U, imgUV[0], Strength, MbQ, edge << 2, p); + p_Vid->EdgeLoopLumaHor(PLANE_V, imgUV[1], Strength, MbQ, edge << 2, p); + } + } + + if (active_sps->chroma_format_idc==YUV420 || active_sps->chroma_format_idc==YUV422) + { + edge_cr = chroma_edge[1][edge][p->chroma_format_idc]; + if( (imgUV != NULL) && (edge_cr >= 0)) + { + p_Vid->EdgeLoopChromaHor( imgUV[0], Strength, MbQ, edge_cr, 0, p); + p_Vid->EdgeLoopChromaHor( imgUV[1], Strength, MbQ, edge_cr, 1, p); + } + } + } + + if (!edge && !MbQ->mb_field && MbQ->mixedModeEdgeFlag) //currSlice->mixedModeEdgeFlag) + { + // this is the extra horizontal edge between a frame macroblock pair and a field above it + MbQ->DeblockCall = 2; + p_Vid->GetStrengthHor(MbQ, 4, mvlimit, p); // Strength for 4 blks in 1 stripe + + //if( *((int*)Strength) ) // only if one of the 4 Strength bytes is != 0 + { + if (filterNon8x8LumaEdgesFlag[edge]) + { + + p_Vid->EdgeLoopLumaHor(PLANE_Y, imgY, Strength, MbQ, MB_BLOCK_SIZE, p) ; + if(currSlice->chroma444_not_separate) + { + p_Vid->EdgeLoopLumaHor(PLANE_U, imgUV[0], Strength, MbQ, MB_BLOCK_SIZE, p) ; + p_Vid->EdgeLoopLumaHor(PLANE_V, imgUV[1], Strength, MbQ, MB_BLOCK_SIZE, p) ; + } + } + if (active_sps->chroma_format_idc==YUV420 || active_sps->chroma_format_idc==YUV422) + { + edge_cr = chroma_edge[1][edge][p->chroma_format_idc]; + if( (imgUV != NULL) && (edge_cr >= 0)) + { + p_Vid->EdgeLoopChromaHor( imgUV[0], Strength, MbQ, MB_BLOCK_SIZE, 0, p) ; + p_Vid->EdgeLoopChromaHor( imgUV[1], Strength, MbQ, MB_BLOCK_SIZE, 1, p) ; + } + } + } + MbQ->DeblockCall = 1; + } + } + }//end edge + + MbQ->DeblockCall = 0; + } +} + diff --git a/ldecod/src/loop_filter_normal.c b/ldecod/src/loop_filter_normal.c new file mode 100644 index 0000000..7fc353b --- /dev/null +++ b/ldecod/src/loop_filter_normal.c @@ -0,0 +1,1576 @@ + +/*! + ************************************************************************************* + * \file loop_filter_normal.c + * + * \brief + * Loop filter to reduce blocking artifacts on a macroblock level (normal). + * The filter strength is QP dependent. + * + * \author + * Contributors: + * - Peter List Peter.List@t-systems.de: Original code (13-Aug-2001) + * - Jani Lainema Jani.Lainema@nokia.com: Some bug fixing, removal of recursiveness (16-Aug-2001) + * - Peter List Peter.List@t-systems.de: inplace filtering and various simplifications (10-Jan-2002) + * - Anthony Joch anthony@ubvideo.com: Simplified switching between filters and + * non-recursive default filter. (08-Jul-2002) + * - Cristina Gomila cristina.gomila@thomson.net: Simplification of the chroma deblocking + * from JVT-E089 (21-Nov-2002) + * - Alexis Michael Tourapis atour@dolby.com: Speed/Architecture improvements (08-Feb-2007) + ************************************************************************************* + */ + +#include "global.h" +#include "image.h" +#include "mb_access.h" +#include "loopfilter.h" +#include "loop_filter.h" + +static void get_strength_ver (Macroblock *MbQ, int edge, int mvlimit, StorablePicture *p); +static void get_strength_hor (Macroblock *MbQ, int edge, int mvlimit, StorablePicture *p); +static void edge_loop_luma_ver (ColorPlane pl, imgpel** Img, byte *Strength, Macroblock *MbQ, int edge); +static void edge_loop_luma_hor (ColorPlane pl, imgpel** Img, byte *Strength, Macroblock *MbQ, int edge, StorablePicture *p); +static void edge_loop_chroma_ver (imgpel** Img, byte *Strength, Macroblock *MbQ, int edge, int uv, StorablePicture *p); +static void edge_loop_chroma_hor (imgpel** Img, byte *Strength, Macroblock *MbQ, int edge, int uv, StorablePicture *p); + + +void set_loop_filter_functions_normal(VideoParameters *p_Vid) +{ + p_Vid->GetStrengthVer = get_strength_ver; + p_Vid->GetStrengthHor = get_strength_hor; + p_Vid->EdgeLoopLumaVer = edge_loop_luma_ver; + p_Vid->EdgeLoopLumaHor = edge_loop_luma_hor; + p_Vid->EdgeLoopChromaVer = edge_loop_chroma_ver; + p_Vid->EdgeLoopChromaHor = edge_loop_chroma_hor; +} + + +static Macroblock* get_non_aff_neighbor_luma(Macroblock *mb, int xN, int yN) +{ + if (xN < 0) + return(mb->mbleft); + else if (yN < 0) + return(mb->mbup); + else + return(mb); +} + +static Macroblock* get_non_aff_neighbor_chroma(Macroblock *mb, int xN, int yN, int block_width,int block_height) +{ + if (xN < 0) + { + if (yN < block_height) + return(mb->mbleft); + else + return(NULL); + } + else if (xN < block_width) + { + if (yN < 0) + return(mb->mbup); + else if (yN < block_height) + return(mb); + else + return(NULL); + } + else + return(NULL); +} + +#define get_x_luma(x) (x & 15) +#define get_y_luma(y) (y & 15) +#define get_pos_x_luma(mb,x) (mb->pix_x + (x & 15)) +#define get_pos_y_luma(mb,y) (mb->pix_y + (y & 15)) +#define get_pos_x_chroma(mb,x,max) (mb->pix_c_x + (x & max)) +#define get_pos_y_chroma(mb,y,max) (mb->pix_c_y + (y & max)) + + /*! + ********************************************************************************************* + * \brief + * returns a buffer of 16 Strength values for one stripe in a mb (for different Frame or Field types) + ********************************************************************************************* + */ +static void get_strength_ver(Macroblock *MbQ, int edge, int mvlimit, StorablePicture *p) +{ + byte *Strength = MbQ->strength_ver[edge]; + Slice *currSlice = MbQ->p_Slice; + int StrValue; + BlockPos *PicPos = MbQ->p_Vid->PicPos; + + if ((currSlice->slice_type==SP_SLICE)||(currSlice->slice_type==SI_SLICE) ) + { + // Set strength to either 3 or 4 regardless of pixel position + StrValue = (edge == 0) ? 4 : 3; + memset(Strength, (byte) StrValue, BLOCK_SIZE * sizeof(byte)); + } + else + { + if (MbQ->is_intra_block == FALSE) + { + Macroblock *MbP; + int xQ = (edge << 2) - 1; + Macroblock *neighbor = get_non_aff_neighbor_luma(MbQ, xQ, 0); + MbP = (edge) ? MbQ : neighbor; + + if (edge || MbP->is_intra_block == FALSE) + { + if (edge && (currSlice->slice_type == P_SLICE && MbQ->mb_type == PSKIP)) + { + memset(Strength, 0, BLOCK_SIZE * sizeof(byte)); + } + else if (edge && ((MbQ->mb_type == P16x16) || (MbQ->mb_type == P16x8))) + { + int blkP, blkQ, idx; + + for( idx = 0 ; idx < MB_BLOCK_SIZE ; idx += BLOCK_SIZE ) + { + blkQ = idx + (edge); + blkP = idx + (get_x_luma(xQ) >> 2); + if ((MbQ->s_cbp[0].blk & (i64_power2(blkQ) | i64_power2(blkP))) != 0) + StrValue = 2; + else + StrValue = 0; // if internal edge of certain types, then we already know StrValue should be 0 + + Strength[idx >> 2] = StrValue; + } + } + else + { + int blkP, blkQ, idx; + BlockPos mb = PicPos[ MbQ->mbAddrX ]; + mb.x <<= BLOCK_SHIFT; + mb.y <<= BLOCK_SHIFT; + + for( idx = 0 ; idx < MB_BLOCK_SIZE ; idx += BLOCK_SIZE ) + { + blkQ = idx + (edge); + blkP = idx + (get_x_luma(xQ) >> 2); + if (((MbQ->s_cbp[0].blk & i64_power2(blkQ)) != 0) || ((MbP->s_cbp[0].blk & i64_power2(blkP)) != 0)) + StrValue = 2; + else // for everything else, if no coefs, but vector difference >= 1 set Strength=1 + { + int blk_y = mb.y + (blkQ >> 2); + int blk_x = mb.x + (blkQ & 3); + int blk_y2 = (short)(get_pos_y_luma(neighbor, 0) + idx) >> 2; + int blk_x2 = (short)(get_pos_x_luma(neighbor, xQ) ) >> 2; + PicMotionParams *mv_info_p = &p->mv_info[blk_y ][blk_x ]; + PicMotionParams *mv_info_q = &p->mv_info[blk_y2][blk_x2]; + StorablePicturePtr ref_p0 = mv_info_p->ref_pic[LIST_0]; + StorablePicturePtr ref_q0 = mv_info_q->ref_pic[LIST_0]; + StorablePicturePtr ref_p1 = mv_info_p->ref_pic[LIST_1]; + StorablePicturePtr ref_q1 = mv_info_q->ref_pic[LIST_1]; + + if ( ((ref_p0==ref_q0) && (ref_p1==ref_q1)) || ((ref_p0==ref_q1) && (ref_p1==ref_q0))) + { + // L0 and L1 reference pictures of p0 are different; q0 as well + if (ref_p0 != ref_p1) + { + // compare MV for the same reference picture + if (ref_p0 == ref_q0) + { + StrValue = + compare_mvs(&mv_info_p->mv[LIST_0], &mv_info_q->mv[LIST_0], mvlimit) | + compare_mvs(&mv_info_p->mv[LIST_1], &mv_info_q->mv[LIST_1], mvlimit); + } + else + { + StrValue = + compare_mvs(&mv_info_p->mv[LIST_0], &mv_info_q->mv[LIST_1], mvlimit) | + compare_mvs(&mv_info_p->mv[LIST_1], &mv_info_q->mv[LIST_0], mvlimit); + } + } + else + { // L0 and L1 reference pictures of p0 are the same; q0 as well + StrValue = (( + compare_mvs(&mv_info_p->mv[LIST_0], &mv_info_q->mv[LIST_0], mvlimit) | + compare_mvs(&mv_info_p->mv[LIST_1], &mv_info_q->mv[LIST_1], mvlimit)) + && ( + compare_mvs(&mv_info_p->mv[LIST_0], &mv_info_q->mv[LIST_1], mvlimit) | + compare_mvs(&mv_info_p->mv[LIST_1], &mv_info_q->mv[LIST_0], mvlimit) + )); + } + } + else + StrValue = 1; + } + //*(int*)(Strength+(idx >> 2)) = StrValue; // * 0x01010101; + Strength[idx >> 2] = StrValue; + } + } + } + else + { + // Start with Strength=3. or Strength=4 for Mb-edge + StrValue = (edge == 0) ? 4 : 3; + memset(Strength, (byte) StrValue, BLOCK_SIZE * sizeof(byte)); + } + } + else + { + // Start with Strength=3. or Strength=4 for Mb-edge + StrValue = (edge == 0) ? 4 : 3; + memset(Strength, (byte) StrValue, BLOCK_SIZE * sizeof(byte)); + } + } +} + + /*! + ********************************************************************************************* + * \brief + * returns a buffer of 16 Strength values for one stripe in a mb (for different Frame or Field types) + ********************************************************************************************* + */ +static void get_strength_hor(Macroblock *MbQ, int edge, int mvlimit, StorablePicture *p) +{ + byte *Strength = MbQ->strength_hor[edge]; + int StrValue; + Slice *currSlice = MbQ->p_Slice; + BlockPos *PicPos = MbQ->p_Vid->PicPos; + + if ((currSlice->slice_type==SP_SLICE)||(currSlice->slice_type==SI_SLICE) ) + { + // Set strength to either 3 or 4 regardless of pixel position + StrValue = (edge == 0 && (((p->structure==FRAME)))) ? 4 : 3; + memset(Strength, (byte) StrValue, BLOCK_SIZE * sizeof(byte)); + } + else + { + if (MbQ->is_intra_block == FALSE) + { + Macroblock *MbP; + int yQ = (edge < BLOCK_SIZE ? (edge << 2) - 1: 0); + + Macroblock *neighbor = get_non_aff_neighbor_luma(MbQ, 0, yQ); + + MbP = (edge) ? MbQ : neighbor; + + if (edge || MbP->is_intra_block == FALSE) + { + if (edge && (currSlice->slice_type == P_SLICE && MbQ->mb_type == PSKIP)) + { + memset(Strength, 0, BLOCK_SIZE * sizeof(byte)); + } + else if (edge && ((MbQ->mb_type == P16x16) || (MbQ->mb_type == P8x16))) + { + int blkP, blkQ, idx; + + for( idx = 0 ; idx < BLOCK_SIZE ; idx ++ ) + { + blkQ = (yQ + 1) + idx; + blkP = (get_y_luma(yQ) & 0xFFFC) + idx; + + if ((MbQ->s_cbp[0].blk & (i64_power2(blkQ) | i64_power2(blkP))) != 0) + StrValue = 2; + else + StrValue = 0; // if internal edge of certain types, we already know StrValue should be 0 + + *(int*)(Strength + idx) = StrValue; + } + } + else + { + int blkP, blkQ, idx; + BlockPos mb = PicPos[ MbQ->mbAddrX ]; + mb.x <<= 2; + mb.y <<= 2; + + for( idx = 0 ; idx < BLOCK_SIZE ; idx ++) + { + blkQ = (yQ + 1) + idx; + blkP = (get_y_luma(yQ) & 0xFFFC) + idx; + + if (((MbQ->s_cbp[0].blk & i64_power2(blkQ)) != 0) || ((MbP->s_cbp[0].blk & i64_power2(blkP)) != 0)) + StrValue = 2; + else // for everything else, if no coefs, but vector difference >= 1 set Strength=1 + { + int blk_y = mb.y + (blkQ >> 2); + int blk_x = mb.x + (blkQ & 3); + int blk_y2 = get_pos_y_luma(neighbor,yQ) >> 2; + int blk_x2 = ((short)(get_pos_x_luma(neighbor,0)) >> 2) + idx; + + PicMotionParams *mv_info_p = &p->mv_info[blk_y ][blk_x ]; + PicMotionParams *mv_info_q = &p->mv_info[blk_y2][blk_x2]; + + StorablePicturePtr ref_p0 = mv_info_p->ref_pic[LIST_0]; + StorablePicturePtr ref_q0 = mv_info_q->ref_pic[LIST_0]; + StorablePicturePtr ref_p1 = mv_info_p->ref_pic[LIST_1]; + StorablePicturePtr ref_q1 = mv_info_q->ref_pic[LIST_1]; + + if ( ((ref_p0==ref_q0) && (ref_p1==ref_q1)) || ((ref_p0==ref_q1) && (ref_p1==ref_q0))) + { + // L0 and L1 reference pictures of p0 are different; q0 as well + if (ref_p0 != ref_p1) + { + // compare MV for the same reference picture + if (ref_p0 == ref_q0) + { + StrValue = + compare_mvs(&mv_info_p->mv[LIST_0], &mv_info_q->mv[LIST_0], mvlimit) | + compare_mvs(&mv_info_p->mv[LIST_1], &mv_info_q->mv[LIST_1], mvlimit); + } + else + { + StrValue = + compare_mvs(&mv_info_p->mv[LIST_0], &mv_info_q->mv[LIST_1], mvlimit) | + compare_mvs(&mv_info_p->mv[LIST_1], &mv_info_q->mv[LIST_0], mvlimit); + } + } + else + { // L0 and L1 reference pictures of p0 are the same; q0 as well + StrValue = (( + compare_mvs(&mv_info_p->mv[LIST_0], &mv_info_q->mv[LIST_0], mvlimit) | + compare_mvs(&mv_info_p->mv[LIST_1], &mv_info_q->mv[LIST_1], mvlimit)) + && ( + compare_mvs(&mv_info_p->mv[LIST_0], &mv_info_q->mv[LIST_1], mvlimit) | + compare_mvs(&mv_info_p->mv[LIST_1], &mv_info_q->mv[LIST_0], mvlimit) + )); + } + } + else + StrValue = 1; + } + *(int*)(Strength + idx) = StrValue; + } + } + } + else + { + // Start with Strength=3. or Strength=4 for Mb-edge + StrValue = (edge == 0 && (p->structure == FRAME)) ? 4 : 3; + memset(Strength, (byte) StrValue, BLOCK_SIZE * sizeof(byte)); + } + } + else + { + // Start with Strength=3. or Strength=4 for Mb-edge + StrValue = (edge == 0 && (p->structure == FRAME)) ? 4 : 3; + memset(Strength, (byte) StrValue, BLOCK_SIZE * sizeof(byte)); + } + } +} + +/*! + ***************************************************************************************** + * \brief + * Vertical Deblocking with Strength = 4 + ***************************************************************************************** + */ +static void luma_ver_deblock_strong(imgpel **cur_img, int pos_x1, int Alpha, int Beta) +{ + int i; + for( i = 0 ; i < BLOCK_SIZE ; ++i ) + { + imgpel *SrcPtrP = *(cur_img++) + pos_x1; + imgpel *SrcPtrQ = SrcPtrP + 1; + imgpel L0 = *SrcPtrP; + imgpel R0 = *SrcPtrQ; + + if( iabs( R0 - L0 ) < Alpha ) + { + imgpel R1 = *(SrcPtrQ + 1); + imgpel L1 = *(SrcPtrP - 1); + if ((iabs( R0 - R1) < Beta) && (iabs(L0 - L1) < Beta)) + { + if ((iabs( R0 - L0 ) < ((Alpha >> 2) + 2))) + { + imgpel R2 = *(SrcPtrQ + 2); + imgpel L2 = *(SrcPtrP - 2); + int RL0 = L0 + R0; + + if (( iabs( L0 - L2) < Beta )) + { + imgpel L3 = *(SrcPtrP - 3); + *(SrcPtrP--) = (imgpel) (( R1 + ((L1 + RL0) << 1) + L2 + 4) >> 3); + *(SrcPtrP--) = (imgpel) (( L2 + L1 + RL0 + 2) >> 2); + *(SrcPtrP ) = (imgpel) ((((L3 + L2) <<1) + L2 + L1 + RL0 + 4) >> 3); + } + else + { + *SrcPtrP = (imgpel) (((L1 << 1) + L0 + R1 + 2) >> 2); + } + + if (( iabs( R0 - R2) < Beta )) + { + imgpel R3 = *(SrcPtrQ + 3); + *(SrcPtrQ++) = (imgpel) (( L1 + ((R1 + RL0) << 1) + R2 + 4) >> 3); + *(SrcPtrQ++) = (imgpel) (( R2 + R0 + L0 + R1 + 2) >> 2); + *(SrcPtrQ ) = (imgpel) ((((R3 + R2) <<1) + R2 + R1 + RL0 + 4) >> 3); + } + else + { + *SrcPtrQ = (imgpel) (((R1 << 1) + R0 + L1 + 2) >> 2); + } + } + else + { + *SrcPtrP = (imgpel) (((L1 << 1) + L0 + R1 + 2) >> 2); + *SrcPtrQ = (imgpel) (((R1 << 1) + R0 + L1 + 2) >> 2); + } + } + } + } +} + +/*! + ***************************************************************************************** + * \brief + * Vertical Deblocking with Normal Strength + ***************************************************************************************** + */ +static void luma_ver_deblock_normal(imgpel **cur_img, int pos_x1, int Alpha, int Beta, int C0, int max_imgpel_value) +{ + int i; + imgpel *SrcPtrP, *SrcPtrQ; + int edge_diff; + + if (C0 == 0) + { + for( i= 0 ; i < BLOCK_SIZE ; ++i ) + { + SrcPtrP = *(cur_img++) + pos_x1; + SrcPtrQ = SrcPtrP + 1; + edge_diff = *SrcPtrQ - *SrcPtrP; + + if( iabs( edge_diff ) < Alpha ) + { + imgpel *SrcPtrQ1 = SrcPtrQ + 1; + imgpel *SrcPtrP1 = SrcPtrP - 1; + + if ((iabs( *SrcPtrQ - *SrcPtrQ1) < Beta) && (iabs(*SrcPtrP - *SrcPtrP1) < Beta)) + { + imgpel R2 = *(SrcPtrQ1 + 1); + imgpel L2 = *(SrcPtrP1 - 1); + + int aq = (iabs(*SrcPtrQ - R2) < Beta); + int ap = (iabs(*SrcPtrP - L2) < Beta); + + int tc0 = (ap + aq) ; + int dif = iClip3( -tc0, tc0, (((edge_diff) << 2) + (*SrcPtrP1 - *SrcPtrQ1) + 4) >> 3 ); + + if (dif != 0) + { + *SrcPtrP = (imgpel) iClip1(max_imgpel_value, *SrcPtrP + dif); + *SrcPtrQ = (imgpel) iClip1(max_imgpel_value, *SrcPtrQ - dif); + } + } + } + } + } + else + { + for( i= 0 ; i < BLOCK_SIZE ; ++i ) + { + SrcPtrP = *(cur_img++) + pos_x1; + SrcPtrQ = SrcPtrP + 1; + edge_diff = *SrcPtrQ - *SrcPtrP; + + if( iabs( edge_diff ) < Alpha ) + { + imgpel *SrcPtrQ1 = SrcPtrQ + 1; + imgpel *SrcPtrP1 = SrcPtrP - 1; + + if ((iabs( *SrcPtrQ - *SrcPtrQ1) < Beta) && (iabs(*SrcPtrP - *SrcPtrP1) < Beta)) + { + int RL0 = (*SrcPtrP + *SrcPtrQ + 1) >> 1; + imgpel R2 = *(SrcPtrQ1 + 1); + imgpel L2 = *(SrcPtrP1 - 1); + + int aq = (iabs(*SrcPtrQ - R2) < Beta); + int ap = (iabs(*SrcPtrP - L2) < Beta); + + int tc0 = (C0 + ap + aq) ; + int dif = iClip3( -tc0, tc0, (((edge_diff) << 2) + (*SrcPtrP1 - *SrcPtrQ1) + 4) >> 3 ); + + if( ap ) + *SrcPtrP1 = (imgpel) (*SrcPtrP1 + iClip3( -C0, C0, (L2 + RL0 - (*SrcPtrP1<<1)) >> 1 )); + + if (dif != 0) + { + *SrcPtrP = (imgpel) iClip1(max_imgpel_value, *SrcPtrP + dif); + *SrcPtrQ = (imgpel) iClip1(max_imgpel_value, *SrcPtrQ - dif); + } + + if( aq ) + *SrcPtrQ1 = (imgpel) (*SrcPtrQ1 + iClip3( -C0, C0, (R2 + RL0 - (*SrcPtrQ1<<1)) >> 1 )); + } + } + } + } +} + +/*! + ***************************************************************************************** + * \brief + * Filters 16 pel block edge of Frame or Field coded MBs + ***************************************************************************************** + */ +static void edge_loop_luma_ver(ColorPlane pl, imgpel** Img, byte *Strength, Macroblock *MbQ, int edge) +{ + VideoParameters *p_Vid = MbQ->p_Vid; + + Macroblock *MbP = get_non_aff_neighbor_luma(MbQ, edge - 1, 0); + + if (MbP || (MbQ->DFDisableIdc== 0)) + { + int bitdepth_scale = pl ? p_Vid->bitdepth_scale[IS_CHROMA] : p_Vid->bitdepth_scale[IS_LUMA]; + + // Average QP of the two blocks + int QP = pl? ((MbP->qpc[pl-1] + MbQ->qpc[pl-1] + 1) >> 1) : (MbP->qp + MbQ->qp + 1) >> 1; + + int indexA = iClip3(0, MAX_QP, QP + MbQ->DFAlphaC0Offset); + int indexB = iClip3(0, MAX_QP, QP + MbQ->DFBetaOffset); + + int Alpha = ALPHA_TABLE[indexA] * bitdepth_scale; + int Beta = BETA_TABLE [indexB] * bitdepth_scale; + + if ((Alpha | Beta )!= 0) + { + const byte *ClipTab = CLIP_TAB[indexA]; + int max_imgpel_value = p_Vid->max_pel_value_comp[pl]; + + int pos_x1 = get_pos_x_luma(MbP, (edge - 1)); + imgpel **cur_img = &Img[get_pos_y_luma(MbP, 0)]; + int pel; + + for( pel = 0 ; pel < MB_BLOCK_SIZE ; pel += 4 ) + { + if(*Strength == 4 ) // INTRA strong filtering + { + luma_ver_deblock_strong(cur_img, pos_x1, Alpha, Beta); + } + else if( *Strength != 0) // normal filtering + { + luma_ver_deblock_normal(cur_img, pos_x1, Alpha, Beta, ClipTab[ *Strength ] * bitdepth_scale, max_imgpel_value); + } + cur_img += 4; + Strength ++; + } + } + } +} + +/*! + ***************************************************************************************** + * \brief + * Horizontal Deblocking with Strength = 4 + ***************************************************************************************** + */ +static void luma_hor_deblock_strong(imgpel *imgP, imgpel *imgQ, int width, int Alpha, int Beta) +{ + int pixel; + int inc_dim2 = width * 2; + int inc_dim3 = width * 3; + for( pixel = 0 ; pixel < BLOCK_SIZE ; ++pixel ) + { + imgpel *SrcPtrP = imgP++; + imgpel *SrcPtrQ = imgQ++; + imgpel L0 = *SrcPtrP; + imgpel R0 = *SrcPtrQ; + + if( iabs( R0 - L0 ) < Alpha ) + { + imgpel L1 = *(SrcPtrP - width); + imgpel R1 = *(SrcPtrQ + width); + + if ((iabs( R0 - R1) < Beta) && (iabs(L0 - L1) < Beta)) + { + if ((iabs( R0 - L0 ) < ((Alpha >> 2) + 2))) + { + imgpel L2 = *(SrcPtrP - inc_dim2); + imgpel R2 = *(SrcPtrQ + inc_dim2); + int RL0 = L0 + R0; + + if (( iabs( L0 - L2) < Beta )) + { + imgpel L3 = *(SrcPtrP - inc_dim3); + *(SrcPtrP ) = (imgpel) (( R1 + ((L1 + RL0) << 1) + L2 + 4) >> 3); + *(SrcPtrP -= width) = (imgpel) (( L2 + L1 + RL0 + 2) >> 2); + *(SrcPtrP - width) = (imgpel) ((((L3 + L2) <<1) + L2 + L1 + RL0 + 4) >> 3); + } + else + { + *SrcPtrP = (imgpel) (((L1 << 1) + L0 + R1 + 2) >> 2); + } + + if (( iabs( R0 - R2) < Beta )) + { + imgpel R3 = *(SrcPtrQ + inc_dim3); + *(SrcPtrQ ) = (imgpel) (( L1 + ((R1 + RL0) << 1) + R2 + 4) >> 3); + *(SrcPtrQ += width ) = (imgpel) (( R2 + R0 + L0 + R1 + 2) >> 2); + *(SrcPtrQ + width ) = (imgpel) ((((R3 + R2) <<1) + R2 + R1 + RL0 + 4) >> 3); + } + else + { + *SrcPtrQ = (imgpel) (((R1 << 1) + R0 + L1 + 2) >> 2); + } + } + else + { + *SrcPtrP = (imgpel) (((L1 << 1) + L0 + R1 + 2) >> 2); + *SrcPtrQ = (imgpel) (((R1 << 1) + R0 + L1 + 2) >> 2); + } + } + } + } +} + +/*! + ***************************************************************************************** + * \brief + * Horizontal Deblocking with Strength = 4 + ***************************************************************************************** + */ +static void luma_hor_deblock_normal(imgpel *imgP, imgpel *imgQ, int width, int Alpha, int Beta, int C0, int max_imgpel_value) +{ + int i; + int edge_diff; + int tc0, dif, aq, ap; + + if (C0 == 0) + { + for( i= 0 ; i < BLOCK_SIZE ; ++i ) + { + edge_diff = *imgQ - *imgP; + + if( iabs( edge_diff ) < Alpha ) + { + imgpel *SrcPtrQ1 = imgQ + width; + imgpel *SrcPtrP1 = imgP - width; + + if ((iabs( *imgQ - *SrcPtrQ1) < Beta) && (iabs(*imgP - *SrcPtrP1) < Beta)) + { + imgpel R2 = *(SrcPtrQ1 + width); + imgpel L2 = *(SrcPtrP1 - width); + + aq = (iabs(*imgQ - R2) < Beta); + ap = (iabs(*imgP - L2) < Beta); + + tc0 = (ap + aq) ; + dif = iClip3( -tc0, tc0, (((edge_diff) << 2) + (*SrcPtrP1 - *SrcPtrQ1) + 4) >> 3 ); + + if (dif != 0) + { + *imgP = (imgpel) iClip1(max_imgpel_value, *imgP + dif); + *imgQ = (imgpel) iClip1(max_imgpel_value, *imgQ - dif); + } + } + } + imgP++; + imgQ++; + } + } + else + { + for( i= 0 ; i < BLOCK_SIZE ; ++i ) + { + edge_diff = *imgQ - *imgP; + + if( iabs( edge_diff ) < Alpha ) + { + imgpel *SrcPtrQ1 = imgQ + width; + imgpel *SrcPtrP1 = imgP - width; + + if ((iabs( *imgQ - *SrcPtrQ1) < Beta) && (iabs(*imgP - *SrcPtrP1) < Beta)) + { + int RL0 = (*imgP + *imgQ + 1) >> 1; + imgpel R2 = *(SrcPtrQ1 + width); + imgpel L2 = *(SrcPtrP1 - width); + + aq = (iabs(*imgQ - R2) < Beta); + ap = (iabs(*imgP - L2) < Beta); + + tc0 = (C0 + ap + aq) ; + dif = iClip3( -tc0, tc0, (((edge_diff) << 2) + (*SrcPtrP1 - *SrcPtrQ1) + 4) >> 3 ); + + if( ap ) + *SrcPtrP1 = (imgpel) (*SrcPtrP1 + iClip3( -C0, C0, (L2 + RL0 - (*SrcPtrP1<<1)) >> 1 )); + + if (dif != 0) + { + *imgP = (imgpel) iClip1(max_imgpel_value, *imgP + dif); + *imgQ = (imgpel) iClip1(max_imgpel_value, *imgQ - dif); + } + + if( aq ) + *SrcPtrQ1 = (imgpel) (*SrcPtrQ1 + iClip3( -C0, C0, (R2 + RL0 - (*SrcPtrQ1<<1)) >> 1 )); + } + } + imgP++; + imgQ++; + } + } +} + +/*! + ***************************************************************************************** + * \brief + * Filters 16 pel block edge of Frame or Field coded MBs + ***************************************************************************************** + */ +static void edge_loop_luma_hor(ColorPlane pl, imgpel** Img, byte *Strength, Macroblock *MbQ, int edge, StorablePicture *p) +{ + VideoParameters *p_Vid = MbQ->p_Vid; + + int ypos = (edge < MB_BLOCK_SIZE ? edge - 1: 0); + Macroblock *MbP = get_non_aff_neighbor_luma(MbQ, 0, ypos); + + if (MbP || (MbQ->DFDisableIdc== 0)) + { + int bitdepth_scale = pl ? p_Vid->bitdepth_scale[IS_CHROMA] : p_Vid->bitdepth_scale[IS_LUMA]; + + // Average QP of the two blocks + int QP = pl? ((MbP->qpc[pl-1] + MbQ->qpc[pl-1] + 1) >> 1) : (MbP->qp + MbQ->qp + 1) >> 1; + + int indexA = iClip3(0, MAX_QP, QP + MbQ->DFAlphaC0Offset); + int indexB = iClip3(0, MAX_QP, QP + MbQ->DFBetaOffset); + + int Alpha = ALPHA_TABLE[indexA] * bitdepth_scale; + int Beta = BETA_TABLE [indexB] * bitdepth_scale; + + if ((Alpha | Beta )!= 0) + { + const byte *ClipTab = CLIP_TAB[indexA]; + int max_imgpel_value = p_Vid->max_pel_value_comp[pl]; + int width = p->iLumaStride; //p->size_x; + + imgpel *imgP = &Img[get_pos_y_luma(MbP, ypos)][get_pos_x_luma(MbP, 0)]; + imgpel *imgQ = imgP + width; + int pel; + + for( pel = 0 ; pel < BLOCK_SIZE ; pel++ ) + { + if(*Strength == 4 ) // INTRA strong filtering + { + luma_hor_deblock_strong(imgP, imgQ, width, Alpha, Beta); + } + else if( *Strength != 0) // normal filtering + { + luma_hor_deblock_normal(imgP, imgQ, width, Alpha, Beta, ClipTab[ *Strength ] * bitdepth_scale, max_imgpel_value); + } + imgP += 4; + imgQ += 4; + Strength ++; + } + } + } +} + + +/*! + ***************************************************************************************** + * \brief + * Filters chroma block edge for Frame or Field coded pictures + ***************************************************************************************** + */ +static void edge_loop_chroma_ver(imgpel** Img, byte *Strength, Macroblock *MbQ, int edge, int uv, StorablePicture *p) +{ + VideoParameters *p_Vid = MbQ->p_Vid; + + int block_width = p_Vid->mb_cr_size_x; + int block_height = p_Vid->mb_cr_size_y; + int xQ = edge - 1; + int yQ = 0; + + Macroblock *MbP = get_non_aff_neighbor_chroma(MbQ,xQ,yQ,block_width,block_height); + + if (MbP || (MbQ->DFDisableIdc == 0)) + { + int bitdepth_scale = p_Vid->bitdepth_scale[IS_CHROMA]; + int max_imgpel_value = p_Vid->max_pel_value_comp[uv + 1]; + + int AlphaC0Offset = MbQ->DFAlphaC0Offset; + int BetaOffset = MbQ->DFBetaOffset; + + // Average QP of the two blocks + int QP = (MbP->qpc[uv] + MbQ->qpc[uv] + 1) >> 1; + + int indexA = iClip3(0, MAX_QP, QP + AlphaC0Offset); + int indexB = iClip3(0, MAX_QP, QP + BetaOffset); + + int Alpha = ALPHA_TABLE[indexA] * bitdepth_scale; + int Beta = BETA_TABLE [indexB] * bitdepth_scale; + + if ((Alpha | Beta) != 0) + { + const int PelNum = pelnum_cr[0][p->chroma_format_idc]; + const byte *ClipTab = CLIP_TAB[indexA]; + + int pel; + int pos_x1 = get_pos_x_chroma(MbP, xQ, (block_width - 1)); + imgpel **cur_img = &Img[get_pos_y_chroma(MbP,yQ, (block_height - 1))]; + + for( pel = 0 ; pel < PelNum ; ++pel ) + { + int Strng = Strength[(PelNum == 8) ? (pel >> 1) : (pel >> 2)]; + + if( Strng != 0) + { + imgpel *SrcPtrP = *cur_img + pos_x1; + imgpel *SrcPtrQ = SrcPtrP + 1; + int edge_diff = *SrcPtrQ - *SrcPtrP; + + if ( iabs( edge_diff ) < Alpha ) + { + imgpel R1 = *(SrcPtrQ + 1); + if ( iabs(*SrcPtrQ - R1) < Beta ) + { + imgpel L1 = *(SrcPtrP - 1); + if ( iabs(*SrcPtrP - L1) < Beta ) + { + if( Strng == 4 ) // INTRA strong filtering + { + *SrcPtrP = (imgpel) ( ((L1 << 1) + *SrcPtrP + R1 + 2) >> 2 ); + *SrcPtrQ = (imgpel) ( ((R1 << 1) + *SrcPtrQ + L1 + 2) >> 2 ); + } + else + { + int tc0 = ClipTab[ Strng ] * bitdepth_scale + 1; + int dif = iClip3( -tc0, tc0, ( ((edge_diff) << 2) + (L1 - R1) + 4) >> 3 ); + + if (dif != 0) + { + *SrcPtrP = (imgpel) iClip1 ( max_imgpel_value, *SrcPtrP + dif ); + *SrcPtrQ = (imgpel) iClip1 ( max_imgpel_value, *SrcPtrQ - dif ); + } + } + } + } + } + } + cur_img++; + } + } + } +} + + +/*! + ***************************************************************************************** + * \brief + * Filters chroma block edge for Frame or Field coded pictures + ***************************************************************************************** + */ +static void edge_loop_chroma_hor(imgpel** Img, byte *Strength, Macroblock *MbQ, int edge, int uv, StorablePicture *p) +{ + VideoParameters *p_Vid = MbQ->p_Vid; + int block_width = p_Vid->mb_cr_size_x; + int block_height = p_Vid->mb_cr_size_y; + int xQ = 0; + int yQ = (edge < 16 ? edge - 1: 0); + + Macroblock *MbP = get_non_aff_neighbor_chroma(MbQ,xQ,yQ,block_width,block_height); + + if (MbP || (MbQ->DFDisableIdc == 0)) + { + int bitdepth_scale = p_Vid->bitdepth_scale[IS_CHROMA]; + int max_imgpel_value = p_Vid->max_pel_value_comp[uv + 1]; + + int AlphaC0Offset = MbQ->DFAlphaC0Offset; + int BetaOffset = MbQ->DFBetaOffset; + int width = p->iChromaStride; //p->size_x_cr; + + // Average QP of the two blocks + int QP = (MbP->qpc[uv] + MbQ->qpc[uv] + 1) >> 1; + + int indexA = iClip3(0, MAX_QP, QP + AlphaC0Offset); + int indexB = iClip3(0, MAX_QP, QP + BetaOffset); + + int Alpha = ALPHA_TABLE[indexA] * bitdepth_scale; + int Beta = BETA_TABLE [indexB] * bitdepth_scale; + + if ((Alpha | Beta) != 0) + { + const int PelNum = pelnum_cr[1][p->chroma_format_idc]; + const byte *ClipTab = CLIP_TAB[indexA]; + + int pel; + + imgpel *imgP = &Img[get_pos_y_chroma(MbP,yQ, (block_height-1))][get_pos_x_chroma(MbP,xQ, (block_width - 1))]; + imgpel *imgQ = imgP + width ; + + for( pel = 0 ; pel < PelNum ; ++pel ) + { + int Strng = Strength[(PelNum == 8) ? (pel >> 1) : (pel >> 2)]; + + if( Strng != 0) + { + imgpel *SrcPtrP = imgP; + imgpel *SrcPtrQ = imgQ; + int edge_diff = *imgQ - *imgP; + + if ( iabs( edge_diff ) < Alpha ) + { + imgpel R1 = *(SrcPtrQ + width); + if ( iabs(*SrcPtrQ - R1) < Beta ) + { + imgpel L1 = *(SrcPtrP - width); + if ( iabs(*SrcPtrP - L1) < Beta ) + { + if( Strng == 4 ) // INTRA strong filtering + { + *SrcPtrP = (imgpel) ( ((L1 << 1) + *SrcPtrP + R1 + 2) >> 2 ); + *SrcPtrQ = (imgpel) ( ((R1 << 1) + *SrcPtrQ + L1 + 2) >> 2 ); + } + else + { + int tc0 = ClipTab[ Strng ] * bitdepth_scale + 1; + int dif = iClip3( -tc0, tc0, ( ((edge_diff) << 2) + (L1 - R1) + 4) >> 3 ); + + if (dif != 0) + { + *SrcPtrP = (imgpel) iClip1 ( max_imgpel_value, *SrcPtrP + dif ); + *SrcPtrQ = (imgpel) iClip1 ( max_imgpel_value, *SrcPtrQ - dif ); + } + } + } + } + } + } + imgP++; + imgQ++; + } + } + } +} + +static void perform_db_dep_normal(Macroblock *MbQ, StorablePicture *p) +{ + VideoParameters *p_Vid = MbQ->p_Vid; + Slice *currSlice = MbQ->p_Slice; + int edge; + + short mb_x, mb_y; + + int filterLeftMbEdgeFlag; + int filterTopMbEdgeFlag; + + imgpel **imgY = p->imgY; + imgpel ***imgUV = p->imgUV; + + seq_parameter_set_rbsp_t *active_sps = p_Vid->active_sps; + + MbQ->DeblockCall = 1; + get_mb_pos (p_Vid, MbQ->mbAddrX, p_Vid->mb_size[IS_LUMA], &mb_x, &mb_y); + + filterLeftMbEdgeFlag = (mb_x != 0); + filterTopMbEdgeFlag = (mb_y != 0); + + if (MbQ->DFDisableIdc == 2) + { + // don't filter at slice boundaries + filterLeftMbEdgeFlag = MbQ->mbAvailA; + // if this the bottom of a frame macroblock pair then always filter the top edge + filterTopMbEdgeFlag = MbQ->mbAvailB; + } + + if (MbQ->luma_transform_size_8x8_flag) + { + // Vertical deblocking + for (edge = 0; edge < 4 ; edge += 2) + { + // If cbp == 0 then deblocking for some macroblock types could be skipped + if (MbQ->cbp == 0 && (currSlice->slice_type == P_SLICE || currSlice->slice_type == B_SLICE)) + { + //if (filterNon8x8LumaEdgesFlag[edge] == 0 && active_sps->chroma_format_idc != YUV444) + if (edge > 0) + { + if (((MbQ->mb_type == PSKIP && currSlice->slice_type == P_SLICE) || (MbQ->mb_type == P16x16) || (MbQ->mb_type == P16x8))) + continue; + } + } + + if( edge || filterLeftMbEdgeFlag ) + { + byte *Strength = MbQ->strength_ver[edge]; + + if ((*((int *) Strength))) // only if one of the 16 Strength bytes is != 0 + { + edge_loop_luma_ver( PLANE_Y, imgY, Strength, MbQ, edge << 2); + edge_loop_luma_ver(PLANE_U, imgUV[0], Strength, MbQ, edge << 2); + edge_loop_luma_ver(PLANE_V, imgUV[1], Strength, MbQ, edge << 2); + } + } + }//end edge + + // horizontal deblocking + for( edge = 0; edge < 4 ; edge += 2 ) + { + // If cbp == 0 then deblocking for some macroblock types could be skipped + if (MbQ->cbp == 0 && (currSlice->slice_type == P_SLICE || currSlice->slice_type == B_SLICE)) + { + if (edge > 0) + { + if (((MbQ->mb_type == PSKIP && currSlice->slice_type == P_SLICE) || (MbQ->mb_type == P16x16) || (MbQ->mb_type == P8x16))) + continue; + } + } + + if( edge || filterTopMbEdgeFlag ) + { + byte *Strength = MbQ->strength_hor[edge]; + + if ((*((int *) Strength))) // only if one of the 16 Strength bytes is != 0 + { + edge_loop_luma_hor( PLANE_Y, imgY, Strength, MbQ, edge << 2, p) ; + edge_loop_luma_hor(PLANE_U, imgUV[0], Strength, MbQ, edge << 2, p); + edge_loop_luma_hor(PLANE_V, imgUV[1], Strength, MbQ, edge << 2, p); + } + } + }//end edge + } + else + { + // Vertical deblocking + for (edge = 0; edge < 4 ; ++edge ) + { + // If cbp == 0 then deblocking for some macroblock types could be skipped + if (MbQ->cbp == 0 && (currSlice->slice_type == P_SLICE || currSlice->slice_type == B_SLICE)) + { + if (edge > 0) + { + if (((MbQ->mb_type == PSKIP && currSlice->slice_type == P_SLICE) || (MbQ->mb_type == P16x16) || (MbQ->mb_type == P16x8))) + continue; + else if ((edge & 0x01) && ((MbQ->mb_type == P8x16) || (currSlice->slice_type == B_SLICE && MbQ->mb_type == BSKIP_DIRECT && active_sps->direct_8x8_inference_flag))) + continue; + } + } + + if( edge || filterLeftMbEdgeFlag ) + { + byte *Strength = MbQ->strength_ver[edge]; + + if ((*((int *) Strength))) // only if one of the 16 Strength bytes is != 0 + { + edge_loop_luma_ver( PLANE_Y, imgY, Strength, MbQ, edge << 2); + edge_loop_luma_ver(PLANE_U, imgUV[0], Strength, MbQ, edge << 2); + edge_loop_luma_ver(PLANE_V, imgUV[1], Strength, MbQ, edge << 2); + } + } + }//end edge + + // horizontal deblocking + for( edge = 0; edge < 4 ; ++edge ) + { + // If cbp == 0 then deblocking for some macroblock types could be skipped + if (MbQ->cbp == 0 && (currSlice->slice_type == P_SLICE || currSlice->slice_type == B_SLICE)) + { + if (edge > 0) + { + if (((MbQ->mb_type == PSKIP && currSlice->slice_type == P_SLICE) || (MbQ->mb_type == P16x16) || (MbQ->mb_type == P8x16))) + continue; + else if ((edge & 0x01) && ((MbQ->mb_type == P16x8) || (currSlice->slice_type == B_SLICE && MbQ->mb_type == BSKIP_DIRECT && active_sps->direct_8x8_inference_flag))) + continue; + } + } + + if( edge || filterTopMbEdgeFlag ) + { + byte *Strength = MbQ->strength_hor[edge]; + + if ((*((int *) Strength))) // only if one of the 16 Strength bytes is != 0 + { + edge_loop_luma_hor( PLANE_Y, imgY, Strength, MbQ, edge << 2, p) ; + edge_loop_luma_hor(PLANE_U, imgUV[0], Strength, MbQ, edge << 2, p); + edge_loop_luma_hor(PLANE_V, imgUV[1], Strength, MbQ, edge << 2, p); + } + } + }//end edge + } +} + +static void perform_db_ind_normal(Macroblock *MbQ, StorablePicture *p) +{ + VideoParameters *p_Vid = MbQ->p_Vid; + Slice *currSlice = MbQ->p_Slice; + //short mb_x, mb_y; + + int filterLeftMbEdgeFlag; + int filterTopMbEdgeFlag; + + imgpel **imgY = p->imgY; + imgpel ***imgUV = p->imgUV; + + seq_parameter_set_rbsp_t *active_sps = p_Vid->active_sps; + + MbQ->DeblockCall = 1; + //get_mb_pos (p_Vid, MbQ->mbAddrX, p_Vid->mb_size[IS_LUMA], &mb_x, &mb_y); + + filterLeftMbEdgeFlag = (MbQ->pix_x != 0); + filterTopMbEdgeFlag = (MbQ->pix_y != 0); + + if (MbQ->DFDisableIdc == 2) + { + // don't filter at slice boundaries + filterLeftMbEdgeFlag = MbQ->mbAvailA; + // if this the bottom of a frame macroblock pair then always filter the top edge + filterTopMbEdgeFlag = MbQ->mbAvailB; + } + + if (MbQ->luma_transform_size_8x8_flag) + { + int edge, edge_cr; + + // Vertical deblocking + for (edge = 0; edge < 4 ; edge += 2) + { + if( edge || filterLeftMbEdgeFlag ) + { + byte *Strength = MbQ->strength_ver[edge]; + + if ((*((int *) Strength))) // only if one of the 16 Strength bytes is != 0 + { + edge_loop_luma_ver( PLANE_Y, imgY, Strength, MbQ, edge << 2); + + if (active_sps->chroma_format_idc==YUV420 || active_sps->chroma_format_idc==YUV422) + { + edge_cr = chroma_edge[0][edge][p->chroma_format_idc]; + if( (imgUV != NULL) && (edge_cr >= 0)) + { + edge_loop_chroma_ver( imgUV[0], Strength, MbQ, edge_cr, 0, p); + edge_loop_chroma_ver( imgUV[1], Strength, MbQ, edge_cr, 1, p); + } + } + } + } + }//end edge + + // horizontal deblocking + for( edge = 0; edge < 4 ; edge += 2 ) + { + if( edge || filterTopMbEdgeFlag ) + { + byte *Strength = MbQ->strength_hor[edge]; + + if ((*((int *) Strength))) // only if one of the 16 Strength bytes is != 0 + { + edge_loop_luma_hor( PLANE_Y, imgY, Strength, MbQ, edge << 2, p) ; + + if (active_sps->chroma_format_idc==YUV420 || active_sps->chroma_format_idc==YUV422) + { + edge_cr = chroma_edge[1][edge][p->chroma_format_idc]; + if( (imgUV != NULL) && (edge_cr >= 0)) + { + edge_loop_chroma_hor( imgUV[0], Strength, MbQ, edge_cr, 0, p); + edge_loop_chroma_hor( imgUV[1], Strength, MbQ, edge_cr, 1, p); + } + } + } + } + }//end edge + } + else + { + if (((MbQ->mb_type == PSKIP) && (currSlice->slice_type == P_SLICE)) || ((MbQ->mb_type == P16x16) && (MbQ->cbp == 0))) + { + // Vertical deblocking + if( filterLeftMbEdgeFlag ) + { + byte *Strength = MbQ->strength_ver[0]; + + if ((*((int *) Strength))) // only if one of the 16 Strength bytes is != 0 + { + edge_loop_luma_ver( PLANE_Y, imgY, Strength, MbQ, 0); + + if (active_sps->chroma_format_idc==YUV420 || active_sps->chroma_format_idc==YUV422) + { + if( (imgUV != NULL)) + { + edge_loop_chroma_ver( imgUV[0], Strength, MbQ, 0, 0, p); + edge_loop_chroma_ver( imgUV[1], Strength, MbQ, 0, 1, p); + } + } + } + } + + // horizontal deblocking + + if( filterTopMbEdgeFlag ) + { + byte *Strength = MbQ->strength_hor[0]; + + if ((*((int *) Strength))) // only if one of the 16 Strength bytes is != 0 + { + edge_loop_luma_hor( PLANE_Y, imgY, Strength, MbQ, 0, p) ; + + if (active_sps->chroma_format_idc==YUV420 || active_sps->chroma_format_idc==YUV422) + { + if( (imgUV != NULL)) + { + edge_loop_chroma_hor( imgUV[0], Strength, MbQ, 0, 0, p); + edge_loop_chroma_hor( imgUV[1], Strength, MbQ, 0, 1, p); + } + } + } + } + } + else if ((MbQ->mb_type == P16x8) && (MbQ->cbp == 0)) + { + int edge, edge_cr; + // Vertical deblocking + if( filterLeftMbEdgeFlag ) + { + byte *Strength = MbQ->strength_ver[0]; + + if ((*((int *) Strength))) // only if one of the 16 Strength bytes is != 0 + { + edge_loop_luma_ver( PLANE_Y, imgY, Strength, MbQ, 0); + + if (active_sps->chroma_format_idc==YUV420 || active_sps->chroma_format_idc==YUV422) + { + if (imgUV != NULL) + { + edge_loop_chroma_ver( imgUV[0], Strength, MbQ, 0, 0, p); + edge_loop_chroma_ver( imgUV[1], Strength, MbQ, 0, 1, p); + } + } + } + } + + // horizontal deblocking + for( edge = 0; edge < 4 ; edge += 2) + { + if( edge || filterTopMbEdgeFlag ) + { + byte *Strength = MbQ->strength_hor[edge]; + + if ((*((int *) Strength))) // only if one of the 16 Strength bytes is != 0 + { + edge_loop_luma_hor( PLANE_Y, imgY, Strength, MbQ, edge << 2, p) ; + + if (active_sps->chroma_format_idc==YUV420 || active_sps->chroma_format_idc==YUV422) + { + edge_cr = chroma_edge[1][edge][p->chroma_format_idc]; + if( (imgUV != NULL) && (edge_cr >= 0)) + { + edge_loop_chroma_hor( imgUV[0], Strength, MbQ, edge_cr, 0, p); + edge_loop_chroma_hor( imgUV[1], Strength, MbQ, edge_cr, 1, p); + } + } + } + } + }//end edge + } + else if ((MbQ->mb_type == P8x16) && (MbQ->cbp == 0)) + { + int edge, edge_cr; + // Vertical deblocking + for (edge = 0; edge < 4 ; edge += 2) + { + if( edge || filterLeftMbEdgeFlag ) + { + byte *Strength = MbQ->strength_ver[edge]; + + if ((*((int *) Strength))) // only if one of the 16 Strength bytes is != 0 + { + edge_loop_luma_ver( PLANE_Y, imgY, Strength, MbQ, edge << 2); + + if (active_sps->chroma_format_idc==YUV420 || active_sps->chroma_format_idc==YUV422) + { + edge_cr = chroma_edge[0][edge][p->chroma_format_idc]; + if( (imgUV != NULL) && (edge_cr >= 0)) + { + edge_loop_chroma_ver( imgUV[0], Strength, MbQ, edge_cr, 0, p); + edge_loop_chroma_ver( imgUV[1], Strength, MbQ, edge_cr, 1, p); + } + } + } + } + }//end edge + + // horizontal deblocking + if( filterTopMbEdgeFlag ) + { + byte *Strength = MbQ->strength_hor[0]; + + if ((*((int *) Strength))) // only if one of the 16 Strength bytes is != 0 + { + edge_loop_luma_hor( PLANE_Y, imgY, Strength, MbQ, 0, p) ; + + if (active_sps->chroma_format_idc==YUV420 || active_sps->chroma_format_idc==YUV422) + { + if (imgUV != NULL) + { + edge_loop_chroma_hor( imgUV[0], Strength, MbQ, 0, 0, p); + edge_loop_chroma_hor( imgUV[1], Strength, MbQ, 0, 1, p); + } + } + } + } + } + else if ((currSlice->slice_type == B_SLICE) && (MbQ->mb_type == BSKIP_DIRECT) && (active_sps->direct_8x8_inference_flag) && (MbQ->cbp == 0)) + { + int edge, edge_cr; + // Vertical deblocking + for (edge = 0; edge < 4 ; edge += 2) + { + if( edge || filterLeftMbEdgeFlag ) + { + byte *Strength = MbQ->strength_ver[edge]; + + if ((*((int *) Strength))) // only if one of the 16 Strength bytes is != 0 + { + edge_loop_luma_ver( PLANE_Y, imgY, Strength, MbQ, edge << 2); + + if (active_sps->chroma_format_idc==YUV420 || active_sps->chroma_format_idc==YUV422) + { + edge_cr = chroma_edge[0][edge][p->chroma_format_idc]; + if( (imgUV != NULL) && (edge_cr >= 0)) + { + edge_loop_chroma_ver( imgUV[0], Strength, MbQ, edge_cr, 0, p); + edge_loop_chroma_ver( imgUV[1], Strength, MbQ, edge_cr, 1, p); + } + } + } + } + }//end edge + + // horizontal deblocking + for( edge = 0; edge < 4 ; edge += 2) + { + if( edge || filterTopMbEdgeFlag ) + { + byte *Strength = MbQ->strength_hor[edge]; + + if ((*((int *) Strength))) // only if one of the 16 Strength bytes is != 0 + { + edge_loop_luma_hor( PLANE_Y, imgY, Strength, MbQ, edge << 2, p) ; + + if (active_sps->chroma_format_idc==YUV420 || active_sps->chroma_format_idc==YUV422) + { + edge_cr = chroma_edge[1][edge][p->chroma_format_idc]; + if( (imgUV != NULL) && (edge_cr >= 0)) + { + edge_loop_chroma_hor( imgUV[0], Strength, MbQ, edge_cr, 0, p); + edge_loop_chroma_hor( imgUV[1], Strength, MbQ, edge_cr, 1, p); + } + } + } + } + }//end edge + } + else + { + int edge, edge_cr; + // Vertical deblocking + for (edge = 0; edge < 4 ; ++edge ) + { + if( edge || filterLeftMbEdgeFlag ) + { + byte *Strength = MbQ->strength_ver[edge]; + + if ((*((int *) Strength))) // only if one of the 16 Strength bytes is != 0 + { + edge_loop_luma_ver( PLANE_Y, imgY, Strength, MbQ, edge << 2); + + if (active_sps->chroma_format_idc==YUV420 || active_sps->chroma_format_idc==YUV422) + { + edge_cr = chroma_edge[0][edge][p->chroma_format_idc]; + if( (imgUV != NULL) && (edge_cr >= 0)) + { + edge_loop_chroma_ver( imgUV[0], Strength, MbQ, edge_cr, 0, p); + edge_loop_chroma_ver( imgUV[1], Strength, MbQ, edge_cr, 1, p); + } + } + } + } + }//end edge + + // horizontal deblocking + for( edge = 0; edge < 4 ; ++edge ) + { + if( edge || filterTopMbEdgeFlag ) + { + byte *Strength = MbQ->strength_hor[edge]; + + if ((*((int *) Strength))) // only if one of the 16 Strength bytes is != 0 + { + edge_loop_luma_hor( PLANE_Y, imgY, Strength, MbQ, edge << 2, p) ; + + if (active_sps->chroma_format_idc==YUV420 || active_sps->chroma_format_idc==YUV422) + { + edge_cr = chroma_edge[1][edge][p->chroma_format_idc]; + if( (imgUV != NULL) && (edge_cr >= 0)) + { + edge_loop_chroma_hor( imgUV[0], Strength, MbQ, edge_cr, 0, p); + edge_loop_chroma_hor( imgUV[1], Strength, MbQ, edge_cr, 1, p); + } + } + } + } + }//end edge + } + } +} + +/*! + ***************************************************************************************** + * \brief + * Deblocking filter for one macroblock. + ***************************************************************************************** + */ +static void perform_db_normal(VideoParameters *p_Vid, StorablePicture *p, int MbQAddr) +{ + Macroblock *MbQ = &(p_Vid->mb_data[MbQAddr]) ; // current Mb + + // return, if filter is disabled + if (MbQ->DFDisableIdc == 1) + { + MbQ->DeblockCall = 0; + } + else + { + if(MbQ->p_Slice->chroma444_not_separate) + perform_db_dep_normal(MbQ, p); + else + perform_db_ind_normal(MbQ, p); + MbQ->DeblockCall = 0; + } +} + +/*! + ***************************************************************************************** + * \brief + * Get deblocking filter strength parameters. + ***************************************************************************************** + */ +static void get_db_strength_normal(VideoParameters *p_Vid, StorablePicture *p, int MbQAddr, int *piCnt) +{ + Macroblock *MbQ = &(p_Vid->mb_data[MbQAddr]) ; // current Mb + + // return, if filter is disabled + if (MbQ->DFDisableIdc == 1) + { + MbQ->DeblockCall = 0; + } + else + { + *piCnt = (*piCnt < 0)? MbQAddr: (*piCnt); + if(MbQ->luma_transform_size_8x8_flag) + { + int filterLeftMbEdgeFlag = (MbQ->pix_x != 0); + int filterTopMbEdgeFlag = (MbQ->pix_y != 0); + + int mvlimit = (p->structure!=FRAME) ? 2 : 4; + + MbQ->DeblockCall = 1; + //get_mb_pos (p_Vid, MbQAddr, p_Vid->mb_size[IS_LUMA], &mb_x, &mb_y); + + if (MbQ->DFDisableIdc==2) + { + // don't filter at slice boundaries + filterLeftMbEdgeFlag = MbQ->mbAvailA; + // if this the bottom of a frame macroblock pair then always filter the top edge + filterTopMbEdgeFlag = MbQ->mbAvailB; + } + + // Vertical deblocking + if( filterLeftMbEdgeFlag ) + get_strength_ver(MbQ, 0, mvlimit, p); + get_strength_ver(MbQ, 2, mvlimit, p); + + // horizontal deblocking + if( filterTopMbEdgeFlag ) + get_strength_hor(MbQ, 0, mvlimit, p); + get_strength_hor(MbQ, 2, mvlimit, p); + } + else + { + int filterLeftMbEdgeFlag; + int filterTopMbEdgeFlag; + + Slice *currSlice = MbQ->p_Slice; + int mvlimit = (p->structure!=FRAME) ? 2 : 4; + + MbQ->DeblockCall = 1; + //get_mb_pos (p_Vid, MbQAddr, p_Vid->mb_size[IS_LUMA], &mb_x, &mb_y); + + filterLeftMbEdgeFlag = (MbQ->pix_x != 0); + filterTopMbEdgeFlag = (MbQ->pix_y != 0); + + if (MbQ->DFDisableIdc==2) + { + // don't filter at slice boundaries + filterLeftMbEdgeFlag = MbQ->mbAvailA; + // if this the bottom of a frame macroblock pair then always filter the top edge + filterTopMbEdgeFlag = MbQ->mbAvailB; + } + + if ((currSlice->slice_type == P_SLICE && MbQ->mb_type == PSKIP) || ((MbQ->mb_type == P16x16) && (MbQ->cbp == 0))) + { + // Vertical deblocking + if( filterLeftMbEdgeFlag ) + get_strength_ver(MbQ, 0, mvlimit, p); + + // horizontal deblocking + if( filterTopMbEdgeFlag ) + get_strength_hor(MbQ, 0, mvlimit, p); + } + else if ((MbQ->mb_type == P16x8) && (MbQ->cbp == 0)) + { + // Vertical deblocking + if( filterLeftMbEdgeFlag ) + get_strength_ver(MbQ, 0, mvlimit, p); + + // horizontal deblocking + if( filterTopMbEdgeFlag ) + get_strength_hor(MbQ, 0, mvlimit, p); + get_strength_hor(MbQ, 2, mvlimit, p); + } + else if ((MbQ->mb_type == P8x16) && (MbQ->cbp == 0)) + { + // Vertical deblocking + if( filterLeftMbEdgeFlag ) + get_strength_ver(MbQ, 0, mvlimit, p); + get_strength_ver(MbQ, 2, mvlimit, p); + + // horizontal deblocking + if( filterTopMbEdgeFlag ) + get_strength_hor(MbQ, 0, mvlimit, p); + } + else if ((currSlice->slice_type == B_SLICE) && (MbQ->mb_type == BSKIP_DIRECT) && (p_Vid->active_sps->direct_8x8_inference_flag) && (MbQ->cbp == 0)) + { + // Vertical + if( filterLeftMbEdgeFlag ) + get_strength_ver(MbQ, 0, mvlimit, p); + get_strength_ver(MbQ, 2, mvlimit, p); + + // Horizontal + if( filterTopMbEdgeFlag ) + get_strength_hor(MbQ, 0, mvlimit, p); + get_strength_hor(MbQ, 2, mvlimit, p); + } + else + { + // Vertical deblocking + if( filterLeftMbEdgeFlag ) + get_strength_ver(MbQ, 0, mvlimit, p); + get_strength_ver(MbQ, 1, mvlimit, p); + get_strength_ver(MbQ, 2, mvlimit, p); + get_strength_ver(MbQ, 3, mvlimit, p); + + // Horizontal deblocking + if( filterTopMbEdgeFlag ) + get_strength_hor(MbQ, 0, mvlimit, p); + get_strength_hor(MbQ, 1, mvlimit, p); + get_strength_hor(MbQ, 2, mvlimit, p); + get_strength_hor(MbQ, 3, mvlimit, p); + } + } + MbQ->DeblockCall = 0; + } +} + + +void deblock_normal(VideoParameters *p_Vid, StorablePicture *p) +{ + unsigned int i; + int j=-1; + for (i = 0; i < p->PicSizeInMbs; ++i) + { + get_db_strength_normal( p_Vid, p, i, &j) ; + } + for (i = 0; i < p->PicSizeInMbs; ++i) + { + perform_db_normal( p_Vid, p, i ) ; + } +} diff --git a/ldecod/src/macroblock.c b/ldecod/src/macroblock.c new file mode 100644 index 0000000..026466e --- /dev/null +++ b/ldecod/src/macroblock.c @@ -0,0 +1,1494 @@ +/*! + *********************************************************************** + * \file macroblock.c + * + * \brief + * Decode a Macroblock + * + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * - Inge Lille-Langøy + * - Rickard Sjoberg + * - Jani Lainema + * - Sebastian Purreiter + * - Thomas Wedi + * - Detlev Marpe + * - Gabi Blaettermann + * - Ye-Kui Wang + * - Lowell Winger + * - Alexis Michael Tourapis + *********************************************************************** +*/ + +#include "contributors.h" + +#include + +#include "block.h" +#include "global.h" +#include "mbuffer.h" +#include "mbuffer_mvc.h" +#include "elements.h" +//#include "errorconcealment.h" +#include "macroblock.h" +#include "fmo.h" +#include "cabac.h" +#include "vlc.h" +#include "image.h" +#include "mb_access.h" +#include "biaridecod.h" +#include "transform.h" +#include "mc_prediction.h" +#include "quant.h" +#include "mv_prediction.h" +#include "mb_prediction.h" +#include "fast_memory.h" +#include "filehandle.h" + + +#if TRACE +#define TRACE_STRING(s) strncpy(currSE.tracestring, s, TRACESTRING_SIZE) +#define TRACE_DECBITS(i) dectracebitcnt(1) +#define TRACE_printf(s) sprintf(type, "%s", s); +#define TRACE_STRING_P(s) strncpy(currSE->tracestring, s, TRACESTRING_SIZE) +#else +#define TRACE_STRING(s) +#define TRACE_DECBITS(i) +#define TRACE_printf(s) +#define TRACE_STRING_P(s) +#endif + +//! look up tables for FRExt_chroma support +void dectracebitcnt(int count); + + +extern void setup_read_macroblock (Slice *currSlice); +extern void set_read_CBP_and_coeffs_cabac (Slice *currSlice); +extern void set_read_CBP_and_coeffs_cavlc (Slice *currSlice); +extern void read_coeff_4x4_CAVLC (Macroblock *currMB, int block_type, int i, int j, int levarr[16], int runarr[16], int *number_coefficients); +extern void read_coeff_4x4_CAVLC_444 (Macroblock *currMB, int block_type, int i, int j, int levarr[16], int runarr[16], int *number_coefficients); + +static void read_motion_info_from_NAL_p_slice (Macroblock *currMB); +static void read_motion_info_from_NAL_b_slice (Macroblock *currMB); + +static int decode_one_component_i_slice (Macroblock *currMB, ColorPlane curr_plane, imgpel **currImg, StorablePicture *dec_picture); +static int decode_one_component_p_slice (Macroblock *currMB, ColorPlane curr_plane, imgpel **currImg, StorablePicture *dec_picture); +static int decode_one_component_b_slice (Macroblock *currMB, ColorPlane curr_plane, imgpel **currImg, StorablePicture *dec_picture); +static int decode_one_component_sp_slice (Macroblock *currMB, ColorPlane curr_plane, imgpel **currImg, StorablePicture *dec_picture); +extern void update_direct_types (Slice *currSlice); +extern void set_intra_prediction_modes (Slice *currSlice); + +/*! + ************************************************************************ + * \brief + * Set context for reference frames + ************************************************************************ + */ +static inline int BType2CtxRef (int btype) +{ + return (btype >= 4); +} + +/*! + ************************************************************************ + * \brief + * Function for reading the reference picture indices using VLC + ************************************************************************ + */ +static char readRefPictureIdx_VLC(Macroblock *currMB, SyntaxElement *currSE, DataPartition *dP, char b8mode, int list) +{ +#if TRACE + trace_info(currSE, "ref_idx_l", list); +#endif + currSE->context = BType2CtxRef (b8mode); + currSE->value2 = list; + dP->readSyntaxElement (currMB, currSE, dP); + return (char) currSE->value1; +} + +/*! + ************************************************************************ + * \brief + * Function for reading the reference picture indices using FLC + ************************************************************************ + */ +static char readRefPictureIdx_FLC(Macroblock *currMB, SyntaxElement *currSE, DataPartition *dP, char b8mode, int list) +{ +#if TRACE + trace_info(currSE, "ref_idx_l", list); +#endif + + currSE->context = BType2CtxRef (b8mode); + currSE->len = 1; + readSyntaxElement_FLC(currSE, dP->bitstream); + currSE->value1 = 1 - currSE->value1; + + return (char) currSE->value1; +} + +/*! + ************************************************************************ + * \brief + * Dummy Function for reading the reference picture indices + ************************************************************************ + */ +static char readRefPictureIdx_Null(Macroblock *currMB, SyntaxElement *currSE, DataPartition *dP, char b8mode, int list) +{ + return 0; +} + +/*! + ************************************************************************ + * \brief + * Function to prepare reference picture indice function pointer + ************************************************************************ + */ +static void prepareListforRefIdx ( Macroblock *currMB, SyntaxElement *currSE, DataPartition *dP, int num_ref_idx_active, int refidx_present) +{ + if(num_ref_idx_active > 1) + { + if (currMB->p_Vid->active_pps->entropy_coding_mode_flag == (Boolean) CAVLC || dP->bitstream->ei_flag) + { + currSE->mapping = linfo_ue; + if (refidx_present) + currMB->readRefPictureIdx = (num_ref_idx_active == 2) ? readRefPictureIdx_FLC : readRefPictureIdx_VLC; + else + currMB->readRefPictureIdx = readRefPictureIdx_Null; + } + else + { + currSE->reading = readRefFrame_CABAC; + currMB->readRefPictureIdx = (refidx_present) ? readRefPictureIdx_VLC : readRefPictureIdx_Null; + } + } + else + currMB->readRefPictureIdx = readRefPictureIdx_Null; +} + +void set_chroma_qp(Macroblock* currMB) +{ + VideoParameters *p_Vid = currMB->p_Vid; + StorablePicture *dec_picture = currMB->p_Slice->dec_picture; + int i; + + for (i=0; i<2; ++i) + { + currMB->qpc[i] = iClip3 ( -p_Vid->bitdepth_chroma_qp_scale, 51, currMB->qp + dec_picture->chroma_qp_offset[i] ); + currMB->qpc[i] = currMB->qpc[i] < 0 ? currMB->qpc[i] : QP_SCALE_CR[currMB->qpc[i]]; + currMB->qp_scaled[i + 1] = currMB->qpc[i] + p_Vid->bitdepth_chroma_qp_scale; + } +} + +/*! +************************************************************************ +* \brief +* updates chroma QP according to luma QP and bit depth +************************************************************************ +*/ +void update_qp(Macroblock *currMB, int qp) +{ + VideoParameters *p_Vid = currMB->p_Vid; + currMB->qp = qp; + currMB->qp_scaled[0] = qp + p_Vid->bitdepth_luma_qp_scale; + set_chroma_qp(currMB); + currMB->is_lossless = (Boolean) ((currMB->qp_scaled[0] == 0) && (p_Vid->lossless_qpprime_flag == 1)); +} + +void read_delta_quant(SyntaxElement *currSE, DataPartition *dP, Macroblock *currMB, const byte *partMap, int type) +{ + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + + currSE->type = type; + + dP = &(currSlice->partArr[partMap[currSE->type]]); + + if (p_Vid->active_pps->entropy_coding_mode_flag == (Boolean) CAVLC || dP->bitstream->ei_flag) + { + currSE->mapping = linfo_se; + } + else + currSE->reading= read_dQuant_CABAC; + + TRACE_STRING_P("mb_qp_delta"); + + dP->readSyntaxElement(currMB, currSE, dP); + currMB->delta_quant = (short) currSE->value1; + if ((currMB->delta_quant < -(26 + p_Vid->bitdepth_luma_qp_scale/2)) || (currMB->delta_quant > (25 + p_Vid->bitdepth_luma_qp_scale/2))) + { + printf("mb_qp_delta is out of range (%d)\n", currMB->delta_quant); + currMB->delta_quant = iClip3(-(26 + p_Vid->bitdepth_luma_qp_scale/2), (25 + p_Vid->bitdepth_luma_qp_scale/2), currMB->delta_quant); + + //error ("mb_qp_delta is out of range", 500); + } + + currSlice->qp = ((currSlice->qp + currMB->delta_quant + 52 + 2*p_Vid->bitdepth_luma_qp_scale)%(52+p_Vid->bitdepth_luma_qp_scale)) - p_Vid->bitdepth_luma_qp_scale; + update_qp(currMB, currSlice->qp); +} + +/*! + ************************************************************************ + * \brief + * Function to read reference picture indice values + ************************************************************************ + */ +static void readMBRefPictureIdx (SyntaxElement *currSE, DataPartition *dP, Macroblock *currMB, PicMotionParams **mv_info, int list, int step_v0, int step_h0) +{ + if (currMB->mb_type == 1) + { + if ((currMB->b8pdir[0] == list || currMB->b8pdir[0] == BI_PRED)) + { + int j, i; + char refframe; + + + currMB->subblock_x = 0; + currMB->subblock_y = 0; + refframe = currMB->readRefPictureIdx(currMB, currSE, dP, 1, list); + for (j = 0; j < step_v0; ++j) + { + char *ref_idx = &mv_info[j][currMB->block_x].ref_idx[list]; + // for (i = currMB->block_x; i < currMB->block_x + step_h0; ++i) + for (i = 0; i < step_h0; ++i) + { + //mv_info[j][i].ref_idx[list] = refframe; + *ref_idx = refframe; + ref_idx += sizeof(PicMotionParams); + } + } + } + } + else if (currMB->mb_type == 2) + { + int k, j, i, j0; + char refframe; + + for (j0 = 0; j0 < 4; j0 += step_v0) + { + k = j0; + + if ((currMB->b8pdir[k] == list || currMB->b8pdir[k] == BI_PRED)) + { + currMB->subblock_y = j0 << 2; + currMB->subblock_x = 0; + refframe = currMB->readRefPictureIdx(currMB, currSE, dP, currMB->b8mode[k], list); + for (j = j0; j < j0 + step_v0; ++j) + { + char *ref_idx = &mv_info[j][currMB->block_x].ref_idx[list]; + // for (i = currMB->block_x; i < currMB->block_x + step_h0; ++i) + for (i = 0; i < step_h0; ++i) + { + //mv_info[j][i].ref_idx[list] = refframe; + *ref_idx = refframe; + ref_idx += sizeof(PicMotionParams); + } + } + } + } + } + else if (currMB->mb_type == 3) + { + int k, j, i, i0; + char refframe; + + currMB->subblock_y = 0; + for (i0 = 0; i0 < 4; i0 += step_h0) + { + k = (i0 >> 1); + + if ((currMB->b8pdir[k] == list || currMB->b8pdir[k] == BI_PRED) && currMB->b8mode[k] != 0) + { + currMB->subblock_x = i0 << 2; + refframe = currMB->readRefPictureIdx(currMB, currSE, dP, currMB->b8mode[k], list); + for (j = 0; j < step_v0; ++j) + { + char *ref_idx = &mv_info[j][currMB->block_x + i0].ref_idx[list]; + // for (i = currMB->block_x; i < currMB->block_x + step_h0; ++i) + for (i = 0; i < step_h0; ++i) + { + //mv_info[j][i].ref_idx[list] = refframe; + *ref_idx = refframe; + ref_idx += sizeof(PicMotionParams); + } + } + } + } + } + else + { + int k, j, i, j0, i0; + char refframe; + + for (j0 = 0; j0 < 4; j0 += step_v0) + { + currMB->subblock_y = j0 << 2; + for (i0 = 0; i0 < 4; i0 += step_h0) + { + k = 2 * (j0 >> 1) + (i0 >> 1); + + if ((currMB->b8pdir[k] == list || currMB->b8pdir[k] == BI_PRED) && currMB->b8mode[k] != 0) + { + currMB->subblock_x = i0 << 2; + refframe = currMB->readRefPictureIdx(currMB, currSE, dP, currMB->b8mode[k], list); + for (j = j0; j < j0 + step_v0; ++j) + { + char *ref_idx = &mv_info[j][currMB->block_x + i0].ref_idx[list]; + //PicMotionParams *mvinfo = mv_info[j] + currMB->block_x + i0; + for (i = 0; i < step_h0; ++i) + { + //(mvinfo++)->ref_idx[list] = refframe; + *ref_idx = refframe; + ref_idx += sizeof(PicMotionParams); + } + } + } + } + } + } +} + +/*! + ************************************************************************ + * \brief + * Function to read reference picture indice values + ************************************************************************ + */ +static void readMBMotionVectors (SyntaxElement *currSE, DataPartition *dP, Macroblock *currMB, int list, int step_h0, int step_v0) +{ + if (currMB->mb_type == 1) + { + if ((currMB->b8pdir[0] == list || currMB->b8pdir[0]== BI_PRED))//has forward vector + { + int i4, j4, ii, jj; + short curr_mvd[2]; + MotionVector pred_mv, curr_mv; + short (*mvd)[4][2]; + //VideoParameters *p_Vid = currMB->p_Vid; + PicMotionParams **mv_info = currMB->p_Slice->dec_picture->mv_info; + PixelPos block[4]; // neighbor blocks + + currMB->subblock_x = 0; // position used for context determination + currMB->subblock_y = 0; // position used for context determination + i4 = currMB->block_x; + j4 = currMB->block_y; + mvd = &currMB->mvd [list][0]; + + get_neighbors(currMB, block, 0, 0, step_h0 << 2); + + // first get MV predictor + currMB->GetMVPredictor (currMB, block, &pred_mv, mv_info[j4][i4].ref_idx[list], mv_info, list, 0, 0, step_h0 << 2, step_v0 << 2); + + // X component +#if TRACE + trace_info(currSE, "mvd0_l", list); +#endif + currSE->value2 = list; // identifies the component; only used for context determination + dP->readSyntaxElement(currMB, currSE, dP); + curr_mvd[0] = (short) currSE->value1; + + // Y component +#if TRACE + trace_info(currSE, "mvd1_l", list); +#endif + currSE->value2 += 2; // identifies the component; only used for context determination + dP->readSyntaxElement(currMB, currSE, dP); + curr_mvd[1] = (short) currSE->value1; + + curr_mv.mv_x = (short)(curr_mvd[0] + pred_mv.mv_x); // compute motion vector x + curr_mv.mv_y = (short)(curr_mvd[1] + pred_mv.mv_y); // compute motion vector y + + for(jj = j4; jj < j4 + step_v0; ++jj) + { + PicMotionParams *mvinfo = mv_info[jj] + i4; + for(ii = i4; ii < i4 + step_h0; ++ii) + { + (mvinfo++)->mv[list] = curr_mv; + } + } + + // Init first line (mvd) + for(ii = 0; ii < step_h0; ++ii) + { + //*((int *) &mvd[0][ii][0]) = *((int *) curr_mvd); + mvd[0][ii][0] = curr_mvd[0]; + mvd[0][ii][1] = curr_mvd[1]; + } + + // now copy all other lines + for(jj = 1; jj < step_v0; ++jj) + { + memcpy(mvd[jj][0], mvd[0][0], 2 * step_h0 * sizeof(short)); + } + } + } + else + { + int i4, j4, ii, jj; + short curr_mvd[2]; + MotionVector pred_mv, curr_mv; + short (*mvd)[4][2]; + //VideoParameters *p_Vid = currMB->p_Vid; + PicMotionParams **mv_info = currMB->p_Slice->dec_picture->mv_info; + PixelPos block[4]; // neighbor blocks + + int i, j, i0, j0, kk, k; + for (j0=0; j0<4; j0+=step_v0) + { + for (i0=0; i0<4; i0+=step_h0) + { + kk = 2 * (j0 >> 1) + (i0 >> 1); + + if ((currMB->b8pdir[kk] == list || currMB->b8pdir[kk]== BI_PRED) && (currMB->b8mode[kk] != 0))//has forward vector + { + char cur_ref_idx = mv_info[currMB->block_y+j0][currMB->block_x+i0].ref_idx[list]; + int mv_mode = currMB->b8mode[kk]; + int step_h = BLOCK_STEP [mv_mode][0]; + int step_v = BLOCK_STEP [mv_mode][1]; + int step_h4 = step_h << 2; + int step_v4 = step_v << 2; + + for (j = j0; j < j0 + step_v0; j += step_v) + { + currMB->subblock_y = j << 2; // position used for context determination + j4 = currMB->block_y + j; + mvd = &currMB->mvd [list][j]; + + for (i = i0; i < i0 + step_h0; i += step_h) + { + currMB->subblock_x = i << 2; // position used for context determination + i4 = currMB->block_x + i; + + get_neighbors(currMB, block, BLOCK_SIZE * i, BLOCK_SIZE * j, step_h4); + + // first get MV predictor + currMB->GetMVPredictor (currMB, block, &pred_mv, cur_ref_idx, mv_info, list, BLOCK_SIZE * i, BLOCK_SIZE * j, step_h4, step_v4); + + for (k=0; k < 2; ++k) + { +#if TRACE + trace_info(currSE, "mvd_l", list); +#endif + currSE->value2 = (k << 1) + list; // identifies the component; only used for context determination + dP->readSyntaxElement(currMB, currSE, dP); + curr_mvd[k] = (short) currSE->value1; + } + + curr_mv.mv_x = (short)(curr_mvd[0] + pred_mv.mv_x); // compute motion vector + curr_mv.mv_y = (short)(curr_mvd[1] + pred_mv.mv_y); // compute motion vector + + for(jj = j4; jj < j4 + step_v; ++jj) + { + PicMotionParams *mvinfo = mv_info[jj] + i4; + for(ii = i4; ii < i4 + step_h; ++ii) + { + (mvinfo++)->mv[list] = curr_mv; + } + } + + // Init first line (mvd) + for(ii = i; ii < i + step_h; ++ii) + { + //*((int *) &mvd[0][ii][0]) = *((int *) curr_mvd); + mvd[0][ii][0] = curr_mvd[0]; + mvd[0][ii][1] = curr_mvd[1]; + } + + // now copy all other lines + for(jj = 1; jj < step_v; ++jj) + { + memcpy(&mvd[jj][i][0], &mvd[0][i][0], 2 * step_h * sizeof(short)); + } + } + } + } + } + } + } +} + +void invScaleCoeff(Macroblock *currMB, int level, int run, int qp_per, int i, int j, int i0, int j0, int coef_ctr, const byte (*pos_scan4x4)[2], int (*InvLevelScale4x4)[4]) +{ + if (level != 0) /* leave if level == 0 */ + { + coef_ctr += run + 1; + + i0 = pos_scan4x4[coef_ctr][0]; + j0 = pos_scan4x4[coef_ctr][1]; + + currMB->s_cbp[0].blk |= i64_power2((j << 2) + i) ; + currMB->p_Slice->cof[0][(j<<2) + j0][(i<<2) + i0]= rshift_rnd_sf((level * InvLevelScale4x4[j0][i0]) << qp_per, 4); + } +} + +static inline void setup_mb_pos_info(Macroblock *currMB) +{ + int mb_x = currMB->mb.x; + int mb_y = currMB->mb.y; + currMB->block_x = mb_x << BLOCK_SHIFT; /* horizontal block position */ + currMB->block_y = mb_y << BLOCK_SHIFT; /* vertical block position */ + currMB->block_y_aff = currMB->block_y; /* interlace relative vertical position */ + currMB->pix_x = mb_x << MB_BLOCK_SHIFT; /* horizontal luma pixel position */ + currMB->pix_y = mb_y << MB_BLOCK_SHIFT; /* vertical luma pixel position */ + currMB->pix_c_x = mb_x * currMB->p_Vid->mb_cr_size_x; /* horizontal chroma pixel position */ + currMB->pix_c_y = mb_y * currMB->p_Vid->mb_cr_size_y; /* vertical chroma pixel position */ +} + +/*! + ************************************************************************ + * \brief + * initializes the current macroblock + ************************************************************************ + */ +void start_macroblock(Slice *currSlice, Macroblock **currMB) +{ + VideoParameters *p_Vid = currSlice->p_Vid; + int mb_nr = currSlice->current_mb_nr; + + *currMB = &currSlice->mb_data[mb_nr]; + + (*currMB)->p_Slice = currSlice; + (*currMB)->p_Vid = p_Vid; + (*currMB)->mbAddrX = mb_nr; + + //assert (mb_nr < (int) p_Vid->PicSizeInMbs); + + /* Update coordinates of the current macroblock */ + if (currSlice->mb_aff_frame_flag) + { + (*currMB)->mb.x = (short) ( (mb_nr) % ((2*p_Vid->width) / MB_BLOCK_SIZE)); + (*currMB)->mb.y = (short) (2*((mb_nr) / ((2*p_Vid->width) / MB_BLOCK_SIZE))); + + (*currMB)->mb.y += ((*currMB)->mb.x & 0x01); + (*currMB)->mb.x >>= 1; + } + else + { + (*currMB)->mb = p_Vid->PicPos[mb_nr]; + } + + /* Define pixel/block positions */ + setup_mb_pos_info(*currMB); + + // reset intra mode + (*currMB)->is_intra_block = FALSE; + // reset mode info + (*currMB)->mb_type = 0; + (*currMB)->delta_quant = 0; + (*currMB)->cbp = 0; + (*currMB)->c_ipred_mode = DC_PRED_8; + + // Save the slice number of this macroblock. When the macroblock below + // is coded it will use this to decide if prediction for above is possible + (*currMB)->slice_nr = (short) currSlice->current_slice_nr; + + CheckAvailabilityOfNeighbors(*currMB); + + // Select appropriate MV predictor function + init_motion_vector_prediction(*currMB, currSlice->mb_aff_frame_flag); + + set_read_and_store_CBP(currMB, currSlice->active_sps->chroma_format_idc); + + // Reset syntax element entries in MB struct + + if (currSlice->slice_type != I_SLICE) + { + if (currSlice->slice_type != B_SLICE) + fast_memset((*currMB)->mvd[0][0][0], 0, MB_BLOCK_PARTITIONS * 2 * sizeof(short)); + else + fast_memset((*currMB)->mvd[0][0][0], 0, 2 * MB_BLOCK_PARTITIONS * 2 * sizeof(short)); + } + + fast_memset((*currMB)->s_cbp, 0, 3 * sizeof(CBPStructure)); + + // initialize currSlice->mb_rres + if (currSlice->is_reset_coeff == FALSE) + { + fast_memset_zero( currSlice->mb_rres[0][0], MB_PIXELS * sizeof(int)); + fast_memset_zero( currSlice->mb_rres[1][0], p_Vid->mb_cr_size * sizeof(int)); + fast_memset_zero( currSlice->mb_rres[2][0], p_Vid->mb_cr_size * sizeof(int)); + if (currSlice->is_reset_coeff_cr == FALSE) + { + fast_memset_zero( currSlice->cof[0][0], 3 * MB_PIXELS * sizeof(int)); + currSlice->is_reset_coeff_cr = TRUE; + } + else + { + fast_memset_zero( currSlice->cof[0][0], MB_PIXELS * sizeof(int)); + } + //fast_memset_zero( currSlice->cof[0][0], MB_PIXELS * sizeof(int)); + //fast_memset_zero( currSlice->cof[1][0], p_Vid->mb_cr_size * sizeof(int)); + //fast_memset_zero( currSlice->cof[2][0], p_Vid->mb_cr_size * sizeof(int)); + + //fast_memset(currSlice->fcf[0][0], 0, MB_PIXELS * sizeof(int)); // reset luma coeffs + //fast_memset(currSlice->fcf[1][0], 0, MB_PIXELS * sizeof(int)); + //fast_memset(currSlice->fcf[2][0], 0, MB_PIXELS * sizeof(int)); + + currSlice->is_reset_coeff = TRUE; + } + + // store filtering parameters for this MB + (*currMB)->DFDisableIdc = currSlice->DFDisableIdc; + (*currMB)->DFAlphaC0Offset = currSlice->DFAlphaC0Offset; + (*currMB)->DFBetaOffset = currSlice->DFBetaOffset; + (*currMB)->list_offset = 0; + (*currMB)->mixedModeEdgeFlag = 0; +} + +/*! + ************************************************************************ + * \brief + * set coordinates of the next macroblock + * check end_of_slice condition + ************************************************************************ + */ +Boolean exit_macroblock(Slice *currSlice, int eos_bit) +{ + VideoParameters *p_Vid = currSlice->p_Vid; + + //! The if() statement below resembles the original code, which tested + //! p_Vid->current_mb_nr == p_Vid->PicSizeInMbs. Both is, of course, nonsense + //! In an error prone environment, one can only be sure to have a new + //! picture by checking the tr of the next slice header! + +// printf ("exit_macroblock: FmoGetLastMBOfPicture %d, p_Vid->current_mb_nr %d\n", FmoGetLastMBOfPicture(), p_Vid->current_mb_nr); + ++(currSlice->num_dec_mb); + + if(currSlice->current_mb_nr == p_Vid->PicSizeInMbs - 1) //if (p_Vid->num_dec_mb == p_Vid->PicSizeInMbs) + { + return TRUE; + } + // ask for last mb in the slice CAVLC + else + { + + currSlice->current_mb_nr = FmoGetNextMBNr (p_Vid, currSlice->current_mb_nr); + + if (currSlice->current_mb_nr == -1) // End of Slice group, MUST be end of slice + { + assert (currSlice->nal_startcode_follows (currSlice, eos_bit) == TRUE); + return TRUE; + } + + if(currSlice->nal_startcode_follows(currSlice, eos_bit) == FALSE) + return FALSE; + + if(currSlice->slice_type == I_SLICE || currSlice->slice_type == SI_SLICE || p_Vid->active_pps->entropy_coding_mode_flag == (Boolean) CABAC) + return TRUE; + if(currSlice->cod_counter <= 0) + return TRUE; + return FALSE; + } +} + +/*! + ************************************************************************ + * \brief + * Interpret the mb mode for P-Frames + ************************************************************************ + */ +static void interpret_mb_mode_P(Macroblock *currMB) +{ + static const short ICBPTAB[6] = {0,16,32,15,31,47}; + short mbmode = currMB->mb_type; + + if(mbmode < 4) + { + currMB->mb_type = mbmode; + memset(currMB->b8mode, mbmode, 4 * sizeof(char)); + memset(currMB->b8pdir, 0, 4 * sizeof(char)); + } + else if((mbmode == 4 || mbmode == 5)) + { + currMB->mb_type = P8x8; + currMB->p_Slice->allrefzero = (mbmode == 5); + } + else if(mbmode == 6) + { + currMB->is_intra_block = TRUE; + currMB->mb_type = I4MB; + memset(currMB->b8mode, IBLOCK, 4 * sizeof(char)); + memset(currMB->b8pdir, -1, 4 * sizeof(char)); + } + else if(mbmode == 31) + { + currMB->is_intra_block = TRUE; + currMB->mb_type = IPCM; + currMB->cbp = -1; + currMB->i16mode = 0; + + memset(currMB->b8mode, 0, 4 * sizeof(char)); + memset(currMB->b8pdir,-1, 4 * sizeof(char)); + } + else + { + currMB->is_intra_block = TRUE; + currMB->mb_type = I16MB; + currMB->cbp = ICBPTAB[((mbmode-7))>>2]; + currMB->i16mode = ((mbmode-7)) & 0x03; + memset(currMB->b8mode, 0, 4 * sizeof(char)); + memset(currMB->b8pdir,-1, 4 * sizeof(char)); + } +} + +/*! + ************************************************************************ + * \brief + * Interpret the mb mode for I-Frames + ************************************************************************ + */ +static void interpret_mb_mode_I(Macroblock *currMB) +{ + static const short ICBPTAB[6] = {0,16,32,15,31,47}; + short mbmode = currMB->mb_type; + + if (mbmode == 0) + { + currMB->is_intra_block = TRUE; + currMB->mb_type = I4MB; + memset(currMB->b8mode,IBLOCK,4 * sizeof(char)); + memset(currMB->b8pdir,-1,4 * sizeof(char)); + } + else if(mbmode == 25) + { + currMB->is_intra_block = TRUE; + currMB->mb_type=IPCM; + currMB->cbp= -1; + currMB->i16mode = 0; + + memset(currMB->b8mode, 0,4 * sizeof(char)); + memset(currMB->b8pdir,-1,4 * sizeof(char)); + } + else + { + currMB->is_intra_block = TRUE; + currMB->mb_type = I16MB; + currMB->cbp= ICBPTAB[(mbmode-1)>>2]; + currMB->i16mode = (mbmode-1) & 0x03; + memset(currMB->b8mode, 0, 4 * sizeof(char)); + memset(currMB->b8pdir,-1, 4 * sizeof(char)); + } +} + +/*! + ************************************************************************ + * \brief + * Interpret the mb mode for B-Frames + ************************************************************************ + */ +static void interpret_mb_mode_B(Macroblock *currMB) +{ + static const char offset2pdir16x16[12] = {0, 0, 1, 2, 0,0,0,0,0,0,0,0}; + static const char offset2pdir16x8[22][2] = {{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{1,1},{0,0},{0,1},{0,0},{1,0}, + {0,0},{0,2},{0,0},{1,2},{0,0},{2,0},{0,0},{2,1},{0,0},{2,2},{0,0}}; + static const char offset2pdir8x16[22][2] = {{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{1,1},{0,0},{0,1},{0,0}, + {1,0},{0,0},{0,2},{0,0},{1,2},{0,0},{2,0},{0,0},{2,1},{0,0},{2,2}}; + + static const char ICBPTAB[6] = {0,16,32,15,31,47}; + + short i, mbmode; + short mbtype = currMB->mb_type; + + //--- set mbtype, b8type, and b8pdir --- + if (mbtype == 0) // direct + { + mbmode=0; + memset(currMB->b8mode, 0, 4 * sizeof(char)); + memset(currMB->b8pdir, 2, 4 * sizeof(char)); + } + else if (mbtype == 23) // intra4x4 + { + currMB->is_intra_block = TRUE; + mbmode=I4MB; + memset(currMB->b8mode, IBLOCK,4 * sizeof(char)); + memset(currMB->b8pdir, -1,4 * sizeof(char)); + } + else if ((mbtype > 23) && (mbtype < 48) ) // intra16x16 + { + currMB->is_intra_block = TRUE; + mbmode=I16MB; + memset(currMB->b8mode, 0, 4 * sizeof(char)); + memset(currMB->b8pdir, -1, 4 * sizeof(char)); + + currMB->cbp = (int) ICBPTAB[(mbtype-24)>>2]; + currMB->i16mode = (mbtype-24) & 0x03; + } + else if (mbtype == 22) // 8x8(+split) + { + mbmode=P8x8; // b8mode and pdir is transmitted in additional codewords + } + else if (mbtype < 4) // 16x16 + { + mbmode = 1; + memset(currMB->b8mode, 1,4 * sizeof(char)); + memset(currMB->b8pdir, offset2pdir16x16[mbtype], 4 * sizeof(char)); + } + else if(mbtype == 48) + { + currMB->is_intra_block = TRUE; + mbmode=IPCM; + memset(currMB->b8mode, 0,4 * sizeof(char)); + memset(currMB->b8pdir,-1,4 * sizeof(char)); + + currMB->cbp= -1; + currMB->i16mode = 0; + } + else if ((mbtype & 0x01) == 0) // 16x8 + { + mbmode = 2; + memset(currMB->b8mode, 2,4 * sizeof(char)); + for(i=0;i<4;++i) + { + currMB->b8pdir[i] = offset2pdir16x8 [mbtype][i>>1]; + } + } + else + { + mbmode=3; + memset(currMB->b8mode, 3,4 * sizeof(char)); + for(i=0;i<4; ++i) + { + currMB->b8pdir[i] = offset2pdir8x16 [mbtype][i&0x01]; + } + } + currMB->mb_type = mbmode; +} + +/*! + ************************************************************************ + * \brief + * Interpret the mb mode for SI-Frames + ************************************************************************ + */ +static void interpret_mb_mode_SI(Macroblock *currMB) +{ + //VideoParameters *p_Vid = currMB->p_Vid; + const int ICBPTAB[6] = {0,16,32,15,31,47}; + short mbmode = currMB->mb_type; + + if (mbmode == 0) + { + currMB->is_intra_block = TRUE; + currMB->mb_type = SI4MB; + memset(currMB->b8mode,IBLOCK,4 * sizeof(char)); + memset(currMB->b8pdir,-1,4 * sizeof(char)); + currMB->p_Slice->siblock[currMB->mb.y][currMB->mb.x]=1; + } + else if (mbmode == 1) + { + currMB->is_intra_block = TRUE; + currMB->mb_type = I4MB; + memset(currMB->b8mode,IBLOCK,4 * sizeof(char)); + memset(currMB->b8pdir,-1,4 * sizeof(char)); + } + else if(mbmode == 26) + { + currMB->is_intra_block = TRUE; + currMB->mb_type=IPCM; + currMB->cbp= -1; + currMB->i16mode = 0; + memset(currMB->b8mode,0,4 * sizeof(char)); + memset(currMB->b8pdir,-1,4 * sizeof(char)); + } + + else + { + currMB->is_intra_block = TRUE; + currMB->mb_type = I16MB; + currMB->cbp= ICBPTAB[(mbmode-2)>>2]; + currMB->i16mode = (mbmode-2) & 0x03; + memset(currMB->b8mode,0,4 * sizeof(char)); + memset(currMB->b8pdir,-1,4 * sizeof(char)); + } +} + + +/*! + ************************************************************************ + * \brief + * Set mode interpretation based on slice type + ************************************************************************ + */ +void setup_slice_methods(Slice *currSlice) +{ + setup_read_macroblock (currSlice); + switch (currSlice->slice_type) + { + case P_SLICE: + currSlice->interpret_mb_mode = interpret_mb_mode_P; + currSlice->read_motion_info_from_NAL = read_motion_info_from_NAL_p_slice; + currSlice->decode_one_component = decode_one_component_p_slice; + currSlice->update_direct_mv_info = NULL; +#if (MVC_EXTENSION_ENABLE) + currSlice->init_lists = currSlice->view_id ? init_lists_p_slice_mvc : init_lists_p_slice; +#else + currSlice->init_lists = init_lists_p_slice; +#endif + break; + case SP_SLICE: + currSlice->interpret_mb_mode = interpret_mb_mode_P; + currSlice->read_motion_info_from_NAL = read_motion_info_from_NAL_p_slice; + currSlice->decode_one_component = decode_one_component_sp_slice; + currSlice->update_direct_mv_info = NULL; +#if (MVC_EXTENSION_ENABLE) + currSlice->init_lists = currSlice->view_id ? init_lists_p_slice_mvc : init_lists_p_slice; +#else + currSlice->init_lists = init_lists_p_slice; +#endif + break; + case B_SLICE: + currSlice->interpret_mb_mode = interpret_mb_mode_B; + currSlice->read_motion_info_from_NAL = read_motion_info_from_NAL_b_slice; + currSlice->decode_one_component = decode_one_component_b_slice; + update_direct_types(currSlice); +#if (MVC_EXTENSION_ENABLE) + currSlice->init_lists = currSlice->view_id ? init_lists_b_slice_mvc : init_lists_b_slice; +#else + currSlice->init_lists = init_lists_b_slice; +#endif + break; + case I_SLICE: + currSlice->interpret_mb_mode = interpret_mb_mode_I; + currSlice->read_motion_info_from_NAL = NULL; + currSlice->decode_one_component = decode_one_component_i_slice; + currSlice->update_direct_mv_info = NULL; +#if (MVC_EXTENSION_ENABLE) + currSlice->init_lists = currSlice->view_id ? init_lists_i_slice_mvc : init_lists_i_slice; +#else + currSlice->init_lists = init_lists_i_slice; +#endif + break; + case SI_SLICE: + currSlice->interpret_mb_mode = interpret_mb_mode_SI; + currSlice->read_motion_info_from_NAL = NULL; + currSlice->decode_one_component = decode_one_component_i_slice; + currSlice->update_direct_mv_info = NULL; +#if (MVC_EXTENSION_ENABLE) + currSlice->init_lists = currSlice->view_id ? init_lists_i_slice_mvc : init_lists_i_slice; +#else + currSlice->init_lists = init_lists_i_slice; +#endif + break; + default: + printf("Unsupported slice type\n"); + break; + } + + set_intra_prediction_modes(currSlice); + + if ( currSlice->p_Vid->active_sps->chroma_format_idc==YUV444 && (currSlice->p_Vid->separate_colour_plane_flag == 0) ) + currSlice->read_coeff_4x4_CAVLC = read_coeff_4x4_CAVLC_444; + else + currSlice->read_coeff_4x4_CAVLC = read_coeff_4x4_CAVLC; + + switch(currSlice->p_Vid->active_pps->entropy_coding_mode_flag) + { + case CABAC: + set_read_CBP_and_coeffs_cabac(currSlice); + break; + case CAVLC: + set_read_CBP_and_coeffs_cavlc(currSlice); + break; + default: + printf("Unsupported entropy coding mode\n"); + break; + } +} + + +/*! + ************************************************************************ + * \brief + * Get current block spatial neighbors + ************************************************************************ + */ +void get_neighbors(Macroblock *currMB, // <-- current Macroblock + PixelPos *block, // <--> neighbor blocks + int mb_x, // <-- block x position + int mb_y, // <-- block y position + int blockshape_x // <-- block width + ) +{ + int *mb_size = currMB->p_Vid->mb_size[IS_LUMA]; + + get4x4Neighbour(currMB, mb_x - 1, mb_y , mb_size, block ); + get4x4Neighbour(currMB, mb_x, mb_y - 1, mb_size, block + 1); + get4x4Neighbour(currMB, mb_x + blockshape_x, mb_y - 1, mb_size, block + 2); + + if (mb_y > 0) + { + if (mb_x < 8) // first column of 8x8 blocks + { + if (mb_y == 8 ) + { + if (blockshape_x == MB_BLOCK_SIZE) + block[2].available = 0; + } + else if (mb_x + blockshape_x == 8) + { + block[2].available = 0; + } + } + else if (mb_x + blockshape_x == MB_BLOCK_SIZE) + { + block[2].available = 0; + } + } + + if (!block[2].available) + { + get4x4Neighbour(currMB, mb_x - 1, mb_y - 1, mb_size, block + 3); + block[2] = block[3]; + } +} + +/*! + ************************************************************************ + * \brief + * Read motion info + ************************************************************************ + */ +static void read_motion_info_from_NAL_p_slice (Macroblock *currMB) +{ + VideoParameters *p_Vid = currMB->p_Vid; + Slice *currSlice = currMB->p_Slice; + + SyntaxElement currSE; + DataPartition *dP = NULL; + const byte *partMap = assignSE2partition[currSlice->dp_mode]; + short partmode = ((currMB->mb_type == P8x8) ? 4 : currMB->mb_type); + int step_h0 = BLOCK_STEP [partmode][0]; + int step_v0 = BLOCK_STEP [partmode][1]; + + int j4; + StorablePicture *dec_picture = currSlice->dec_picture; + PicMotionParams *mv_info = NULL; + + int list_offset = currMB->list_offset; + StorablePicture **list0 = currSlice->listX[LIST_0 + list_offset]; + PicMotionParams **p_mv_info = &dec_picture->mv_info[currMB->block_y]; + + //===== READ REFERENCE PICTURE INDICES ===== + currSE.type = SE_REFFRAME; + dP = &(currSlice->partArr[partMap[SE_REFFRAME]]); + + // For LIST_0, if multiple ref. pictures, read LIST_0 reference picture indices for the MB *********** + prepareListforRefIdx (currMB, &currSE, dP, currSlice->num_ref_idx_active[LIST_0], (currMB->mb_type != P8x8) || (!currSlice->allrefzero)); + readMBRefPictureIdx (&currSE, dP, currMB, p_mv_info, LIST_0, step_v0, step_h0); + + //===== READ MOTION VECTORS ===== + currSE.type = SE_MVD; + dP = &(currSlice->partArr[partMap[SE_MVD]]); + + if (p_Vid->active_pps->entropy_coding_mode_flag == (Boolean) CAVLC || dP->bitstream->ei_flag) + currSE.mapping = linfo_se; + else + currSE.reading = currSlice->mb_aff_frame_flag ? read_mvd_CABAC_mbaff : read_MVD_CABAC; + + // LIST_0 Motion vectors + readMBMotionVectors (&currSE, dP, currMB, LIST_0, step_h0, step_v0); + + // record reference picture Ids for deblocking decisions + for(j4 = 0; j4 < 4;++j4) + { + mv_info = &p_mv_info[j4][currMB->block_x]; + mv_info->ref_pic[LIST_0] = list0[(short) mv_info->ref_idx[LIST_0]]; + mv_info++; + mv_info->ref_pic[LIST_0] = list0[(short) mv_info->ref_idx[LIST_0]]; + mv_info++; + mv_info->ref_pic[LIST_0] = list0[(short) mv_info->ref_idx[LIST_0]]; + mv_info++; + mv_info->ref_pic[LIST_0] = list0[(short) mv_info->ref_idx[LIST_0]]; + } +} + + +/*! +************************************************************************ +* \brief +* Read motion info +************************************************************************ +*/ +static void read_motion_info_from_NAL_b_slice (Macroblock *currMB) +{ + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + StorablePicture *dec_picture = currSlice->dec_picture; + SyntaxElement currSE; + DataPartition *dP = NULL; + const byte *partMap = assignSE2partition[currSlice->dp_mode]; + int partmode = ((currMB->mb_type == P8x8) ? 4 : currMB->mb_type); + int step_h0 = BLOCK_STEP [partmode][0]; + int step_v0 = BLOCK_STEP [partmode][1]; + + int j4, i4; + + int list_offset = currMB->list_offset; + StorablePicture **list0 = currSlice->listX[LIST_0 + list_offset]; + StorablePicture **list1 = currSlice->listX[LIST_1 + list_offset]; + PicMotionParams **p_mv_info = &dec_picture->mv_info[currMB->block_y]; + + if (currMB->mb_type == P8x8) + currSlice->update_direct_mv_info(currMB); + + //===== READ REFERENCE PICTURE INDICES ===== + currSE.type = SE_REFFRAME; + dP = &(currSlice->partArr[partMap[SE_REFFRAME]]); + + // For LIST_0, if multiple ref. pictures, read LIST_0 reference picture indices for the MB *********** + prepareListforRefIdx (currMB, &currSE, dP, currSlice->num_ref_idx_active[LIST_0], TRUE); + readMBRefPictureIdx (&currSE, dP, currMB, p_mv_info, LIST_0, step_v0, step_h0); + + // For LIST_1, if multiple ref. pictures, read LIST_1 reference picture indices for the MB *********** + prepareListforRefIdx (currMB, &currSE, dP, currSlice->num_ref_idx_active[LIST_1], TRUE); + readMBRefPictureIdx (&currSE, dP, currMB, p_mv_info, LIST_1, step_v0, step_h0); + + //===== READ MOTION VECTORS ===== + currSE.type = SE_MVD; + dP = &(currSlice->partArr[partMap[SE_MVD]]); + + if (p_Vid->active_pps->entropy_coding_mode_flag == (Boolean) CAVLC || dP->bitstream->ei_flag) + currSE.mapping = linfo_se; + else + currSE.reading = currSlice->mb_aff_frame_flag ? read_mvd_CABAC_mbaff : read_MVD_CABAC; + + // LIST_0 Motion vectors + readMBMotionVectors (&currSE, dP, currMB, LIST_0, step_h0, step_v0); + // LIST_1 Motion vectors + readMBMotionVectors (&currSE, dP, currMB, LIST_1, step_h0, step_v0); + + // record reference picture Ids for deblocking decisions + + for(j4 = 0; j4 < 4; ++j4) + { + for(i4 = currMB->block_x; i4 < (currMB->block_x + 4); ++i4) + { + PicMotionParams *mv_info = &p_mv_info[j4][i4]; + short ref_idx = mv_info->ref_idx[LIST_0]; + + mv_info->ref_pic[LIST_0] = (ref_idx >= 0) ? list0[ref_idx] : NULL; + ref_idx = mv_info->ref_idx[LIST_1]; + mv_info->ref_pic[LIST_1] = (ref_idx >= 0) ? list1[ref_idx] : NULL; + } + } +} + + +/*! + ************************************************************************ + * \brief + * Data partitioning: Check if neighboring macroblock is needed for + * CAVLC context decoding, and disable current MB if data partition + * is missing. + ************************************************************************ + */ +void check_dp_neighbors (Macroblock *currMB) +{ + VideoParameters *p_Vid = currMB->p_Vid; + PixelPos up, left; + + p_Vid->getNeighbour(currMB, -1, 0, p_Vid->mb_size[1], &left); + p_Vid->getNeighbour(currMB, 0, -1, p_Vid->mb_size[1], &up); + + if ((currMB->is_intra_block == FALSE) || (!(p_Vid->active_pps->constrained_intra_pred_flag)) ) + { + if (left.available) + { + currMB->dpl_flag |= p_Vid->mb_data[left.mb_addr].dpl_flag; + } + if (up.available) + { + currMB->dpl_flag |= p_Vid->mb_data[up.mb_addr].dpl_flag; + } + } +} + + +/*! + ************************************************************************ + * \brief + * decode one color component in an I slice + ************************************************************************ + */ + +static int decode_one_component_i_slice(Macroblock *currMB, ColorPlane curr_plane, imgpel **currImg, StorablePicture *dec_picture) +{ + //For residual DPCM + currMB->ipmode_DPCM = NO_INTRA_PMODE; + if(currMB->mb_type == IPCM) + mb_pred_ipcm(currMB); + else if (currMB->mb_type==I16MB) + mb_pred_intra16x16(currMB, curr_plane, dec_picture); + else if (currMB->mb_type == I4MB) + mb_pred_intra4x4(currMB, curr_plane, currImg, dec_picture); + else if (currMB->mb_type == I8MB) + mb_pred_intra8x8(currMB, curr_plane, currImg, dec_picture); + + return 1; +} + +/*! + ************************************************************************ + * \brief + * decode one color component for a p slice + ************************************************************************ + */ +static int decode_one_component_p_slice(Macroblock *currMB, ColorPlane curr_plane, imgpel **currImg, StorablePicture *dec_picture) +{ + //For residual DPCM + currMB->ipmode_DPCM = NO_INTRA_PMODE; + if(currMB->mb_type == IPCM) + mb_pred_ipcm(currMB); + else if (currMB->mb_type==I16MB) + mb_pred_intra16x16(currMB, curr_plane, dec_picture); + else if (currMB->mb_type == I4MB) + mb_pred_intra4x4(currMB, curr_plane, currImg, dec_picture); + else if (currMB->mb_type == I8MB) + mb_pred_intra8x8(currMB, curr_plane, currImg, dec_picture); + else if (currMB->mb_type == PSKIP) + mb_pred_skip(currMB, curr_plane, currImg, dec_picture); + else if (currMB->mb_type == P16x16) + mb_pred_p_inter16x16(currMB, curr_plane, dec_picture); + else if (currMB->mb_type == P16x8) + mb_pred_p_inter16x8(currMB, curr_plane, dec_picture); + else if (currMB->mb_type == P8x16) + mb_pred_p_inter8x16(currMB, curr_plane, dec_picture); + else + mb_pred_p_inter8x8(currMB, curr_plane, dec_picture); + + return 1; +} + + +/*! + ************************************************************************ + * \brief + * decode one color component for a sp slice + ************************************************************************ + */ +static int decode_one_component_sp_slice(Macroblock *currMB, ColorPlane curr_plane, imgpel **currImg, StorablePicture *dec_picture) +{ + //For residual DPCM + currMB->ipmode_DPCM = NO_INTRA_PMODE; + + if (currMB->mb_type == IPCM) + mb_pred_ipcm(currMB); + else if (currMB->mb_type==I16MB) + mb_pred_intra16x16(currMB, curr_plane, dec_picture); + else if (currMB->mb_type == I4MB) + mb_pred_intra4x4(currMB, curr_plane, currImg, dec_picture); + else if (currMB->mb_type == I8MB) + mb_pred_intra8x8(currMB, curr_plane, currImg, dec_picture); + else if (currMB->mb_type == PSKIP) + mb_pred_sp_skip(currMB, curr_plane, dec_picture); + else if (currMB->mb_type == P16x16) + mb_pred_p_inter16x16(currMB, curr_plane, dec_picture); + else if (currMB->mb_type == P16x8) + mb_pred_p_inter16x8(currMB, curr_plane, dec_picture); + else if (currMB->mb_type == P8x16) + mb_pred_p_inter8x16(currMB, curr_plane, dec_picture); + else + mb_pred_p_inter8x8(currMB, curr_plane, dec_picture); + + return 1; +} + + + +/*! + ************************************************************************ + * \brief + * decode one color component for a b slice + ************************************************************************ + */ + +static int decode_one_component_b_slice(Macroblock *currMB, ColorPlane curr_plane, imgpel **currImg, StorablePicture *dec_picture) +{ + //For residual DPCM + currMB->ipmode_DPCM = NO_INTRA_PMODE; + + if(currMB->mb_type == IPCM) + mb_pred_ipcm(currMB); + else if (currMB->mb_type==I16MB) + mb_pred_intra16x16(currMB, curr_plane, dec_picture); + else if (currMB->mb_type == I4MB) + mb_pred_intra4x4(currMB, curr_plane, currImg, dec_picture); + else if (currMB->mb_type == I8MB) + mb_pred_intra8x8(currMB, curr_plane, currImg, dec_picture); + else if (currMB->mb_type == P16x16) + mb_pred_p_inter16x16(currMB, curr_plane, dec_picture); + else if (currMB->mb_type == P16x8) + mb_pred_p_inter16x8(currMB, curr_plane, dec_picture); + else if (currMB->mb_type == P8x16) + mb_pred_p_inter8x16(currMB, curr_plane, dec_picture); + else if (currMB->mb_type == BSKIP_DIRECT) + { + Slice *currSlice = currMB->p_Slice; + //! Edited by Joao Carreira + currMB->p_Vid->n_SKIP++; + if (currSlice->direct_spatial_mv_pred_flag == 0) + { + if (currSlice->active_sps->direct_8x8_inference_flag) + mb_pred_b_d8x8temporal (currMB, curr_plane, currImg, dec_picture); + else + mb_pred_b_d4x4temporal (currMB, curr_plane, currImg, dec_picture); + } + else + { + if (currSlice->active_sps->direct_8x8_inference_flag) + mb_pred_b_d8x8spatial (currMB, curr_plane, currImg, dec_picture); + else + mb_pred_b_d4x4spatial (currMB, curr_plane, currImg, dec_picture); + } + } + else + mb_pred_b_inter8x8 (currMB, curr_plane, dec_picture); + + return 1; +} + +// probably a better way (or place) to do this, but I'm not sure what (where) it is [CJV] +// this is intended to make get_block_luma faster, but I'm still performing +// this at the MB level, and it really should be done at the slice level +static void init_cur_imgy(VideoParameters *p_Vid,Slice *currSlice,int pl) +{ + int i,j; + if ((p_Vid->separate_colour_plane_flag == 0)) + { + StorablePicture *vidref = p_Vid->no_reference_picture; + int noref = (currSlice->framepoc < p_Vid->recovery_poc); + if (pl==PLANE_Y) + { + for (j = 0; j < 6; j++) // for (j = 0; j < (currSlice->slice_type==B_SLICE?2:1); j++) + { + for (i = 0; i < currSlice->listXsize[j] ; i++) + { + StorablePicture *curr_ref = currSlice->listX[j][i]; + if (curr_ref) + { + curr_ref->no_ref = noref && (curr_ref == vidref); + curr_ref->cur_imgY = curr_ref->imgY; + } + } + } + } + else + { + for (j = 0; j < 6; j++) //for (j = 0; j < (currSlice->slice_type==B_SLICE?2:1); j++) + { + for (i = 0; i < currSlice->listXsize[j]; i++) + { + StorablePicture *curr_ref = currSlice->listX[j][i]; + if (curr_ref) + { + curr_ref->no_ref = noref && (curr_ref == vidref); + curr_ref->cur_imgY = curr_ref->imgUV[pl-1]; + } + } + } + } + } +} + + +/*! + ************************************************************************ + * \brief + * decode one macroblock + ************************************************************************ + */ + +int decode_one_macroblock(Macroblock *currMB, StorablePicture *dec_picture) +{ + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + + // macroblock decoding ************************************************** + if (currSlice->chroma444_not_separate) + { + if (!currMB->is_intra_block) + { + init_cur_imgy(p_Vid, currSlice, PLANE_Y); + currSlice->decode_one_component(currMB, PLANE_Y, dec_picture->imgY, dec_picture); + init_cur_imgy(p_Vid, currSlice, PLANE_U); + currSlice->decode_one_component(currMB, PLANE_U, dec_picture->imgUV[0], dec_picture); + init_cur_imgy(p_Vid, currSlice, PLANE_V); + currSlice->decode_one_component(currMB, PLANE_V, dec_picture->imgUV[1], dec_picture); + } + else + { + currSlice->decode_one_component(currMB, PLANE_Y, dec_picture->imgY, dec_picture); + currSlice->decode_one_component(currMB, PLANE_U, dec_picture->imgUV[0], dec_picture); + currSlice->decode_one_component(currMB, PLANE_V, dec_picture->imgUV[1], dec_picture); + } + currSlice->is_reset_coeff = FALSE; + currSlice->is_reset_coeff_cr = FALSE; + } + else + { + currSlice->decode_one_component(currMB, PLANE_Y, dec_picture->imgY, dec_picture); + } + + return 0; +} + + +/*! + ************************************************************************ + * \brief + * change target plane + * for 4:4:4 Independent mode + ************************************************************************ + */ +void change_plane_JV( VideoParameters *p_Vid, int nplane, Slice *pSlice) +{ + //Slice *currSlice = p_Vid->currentSlice; + //p_Vid->colour_plane_id = nplane; + p_Vid->mb_data = p_Vid->mb_data_JV[nplane]; + p_Vid->dec_picture = p_Vid->dec_picture_JV[nplane]; + p_Vid->siblock = p_Vid->siblock_JV[nplane]; + p_Vid->ipredmode = p_Vid->ipredmode_JV[nplane]; + p_Vid->intra_block = p_Vid->intra_block_JV[nplane]; + if(pSlice) + { + pSlice->mb_data = p_Vid->mb_data_JV[nplane]; + pSlice->dec_picture = p_Vid->dec_picture_JV[nplane]; + pSlice->siblock = p_Vid->siblock_JV[nplane]; + pSlice->ipredmode = p_Vid->ipredmode_JV[nplane]; + pSlice->intra_block = p_Vid->intra_block_JV[nplane]; + } +} + +/*! + ************************************************************************ + * \brief + * make frame picture from each plane data + * for 4:4:4 Independent mode + ************************************************************************ + */ +void make_frame_picture_JV(VideoParameters *p_Vid) +{ + int uv, line; + int nsize; + p_Vid->dec_picture = p_Vid->dec_picture_JV[0]; + //copy; + if(p_Vid->dec_picture->used_for_reference) + { + nsize = (p_Vid->dec_picture->size_y/BLOCK_SIZE)*(p_Vid->dec_picture->size_x/BLOCK_SIZE)*sizeof(PicMotionParams); + memcpy( &(p_Vid->dec_picture->JVmv_info[PLANE_Y][0][0]), &(p_Vid->dec_picture_JV[PLANE_Y]->mv_info[0][0]), nsize); + memcpy( &(p_Vid->dec_picture->JVmv_info[PLANE_U][0][0]), &(p_Vid->dec_picture_JV[PLANE_U]->mv_info[0][0]), nsize); + memcpy( &(p_Vid->dec_picture->JVmv_info[PLANE_V][0][0]), &(p_Vid->dec_picture_JV[PLANE_V]->mv_info[0][0]), nsize); + } + + // This could be done with pointers and seems not necessary + for( uv=0; uv<2; uv++ ) + { + for( line=0; lineheight; line++ ) + { + nsize = sizeof(imgpel) * p_Vid->width; + memcpy( p_Vid->dec_picture->imgUV[uv][line], p_Vid->dec_picture_JV[uv+1]->imgY[line], nsize ); + } + free_storable_picture(p_Vid->dec_picture_JV[uv+1]); + } +} + + diff --git a/ldecod/src/mb_access.c b/ldecod/src/mb_access.c new file mode 100644 index 0000000..1e51745 --- /dev/null +++ b/ldecod/src/mb_access.c @@ -0,0 +1,666 @@ + +/*! + ************************************************************************************* + * \file mb_access.c + * + * \brief + * Functions for macroblock neighborhoods + * + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * - Karsten Suehring + ************************************************************************************* + */ + +#include "global.h" +#include "mbuffer.h" +#include "mb_access.h" + +/*! + ************************************************************************ + * \brief + * returns 1 if the macroblock at the given address is available + ************************************************************************ + */ +Boolean mb_is_available(int mbAddr, Macroblock *currMB) +{ + Slice *currSlice = currMB->p_Slice; + if ((mbAddr < 0) || (mbAddr > ((int)currSlice->dec_picture->PicSizeInMbs - 1))) + return FALSE; + + // the following line checks both: slice number and if the mb has been decoded + if (!currMB->DeblockCall) + { + if (currSlice->mb_data[mbAddr].slice_nr != currMB->slice_nr) + return FALSE; + } + + return TRUE; +} + + +/*! + ************************************************************************ + * \brief + * Checks the availability of neighboring macroblocks of + * the current macroblock for prediction and context determination; + ************************************************************************ + */ +void CheckAvailabilityOfNeighbors(Macroblock *currMB) +{ + Slice *currSlice = currMB->p_Slice; + StorablePicture *dec_picture = currSlice->dec_picture; //p_Vid->dec_picture; + const int mb_nr = currMB->mbAddrX; + BlockPos *PicPos = currMB->p_Vid->PicPos; + + if (dec_picture->mb_aff_frame_flag) + { + int cur_mb_pair = mb_nr >> 1; + currMB->mbAddrA = 2 * (cur_mb_pair - 1); + currMB->mbAddrB = 2 * (cur_mb_pair - dec_picture->PicWidthInMbs); + currMB->mbAddrC = 2 * (cur_mb_pair - dec_picture->PicWidthInMbs + 1); + currMB->mbAddrD = 2 * (cur_mb_pair - dec_picture->PicWidthInMbs - 1); + + currMB->mbAvailA = (Boolean) (mb_is_available(currMB->mbAddrA, currMB) && ((PicPos[cur_mb_pair ].x)!=0)); + currMB->mbAvailB = (Boolean) (mb_is_available(currMB->mbAddrB, currMB)); + currMB->mbAvailC = (Boolean) (mb_is_available(currMB->mbAddrC, currMB) && ((PicPos[cur_mb_pair + 1].x)!=0)); + currMB->mbAvailD = (Boolean) (mb_is_available(currMB->mbAddrD, currMB) && ((PicPos[cur_mb_pair ].x)!=0)); + } + else + { + BlockPos *p_pic_pos = &PicPos[mb_nr ]; + currMB->mbAddrA = mb_nr - 1; + currMB->mbAddrD = currMB->mbAddrA - dec_picture->PicWidthInMbs; + currMB->mbAddrB = currMB->mbAddrD + 1; + currMB->mbAddrC = currMB->mbAddrB + 1; + + + currMB->mbAvailA = (Boolean) (mb_is_available(currMB->mbAddrA, currMB) && ((p_pic_pos->x)!=0)); + currMB->mbAvailD = (Boolean) (mb_is_available(currMB->mbAddrD, currMB) && ((p_pic_pos->x)!=0)); + currMB->mbAvailC = (Boolean) (mb_is_available(currMB->mbAddrC, currMB) && (((p_pic_pos + 1)->x)!=0)); + currMB->mbAvailB = (Boolean) (mb_is_available(currMB->mbAddrB, currMB)); + } + + currMB->mb_left = (currMB->mbAvailA) ? &(currSlice->mb_data[currMB->mbAddrA]) : NULL; + currMB->mb_up = (currMB->mbAvailB) ? &(currSlice->mb_data[currMB->mbAddrB]) : NULL; +} + +/*! + ************************************************************************ + * \brief + * Checks the availability of neighboring macroblocks of + * the current macroblock for prediction and context determination; + ************************************************************************ + */ +void CheckAvailabilityOfNeighborsNormal(Macroblock *currMB) +{ + Slice *currSlice = currMB->p_Slice; + StorablePicture *dec_picture = currSlice->dec_picture; //p_Vid->dec_picture; + const int mb_nr = currMB->mbAddrX; + BlockPos *PicPos = currMB->p_Vid->PicPos; + + BlockPos *p_pic_pos = &PicPos[mb_nr ]; + currMB->mbAddrA = mb_nr - 1; + currMB->mbAddrD = currMB->mbAddrA - dec_picture->PicWidthInMbs; + currMB->mbAddrB = currMB->mbAddrD + 1; + currMB->mbAddrC = currMB->mbAddrB + 1; + + + currMB->mbAvailA = (Boolean) (mb_is_available(currMB->mbAddrA, currMB) && ((p_pic_pos->x)!=0)); + currMB->mbAvailD = (Boolean) (mb_is_available(currMB->mbAddrD, currMB) && ((p_pic_pos->x)!=0)); + currMB->mbAvailC = (Boolean) (mb_is_available(currMB->mbAddrC, currMB) && (((p_pic_pos + 1)->x)!=0)); + currMB->mbAvailB = (Boolean) (mb_is_available(currMB->mbAddrB, currMB)); + + + currMB->mb_left = (currMB->mbAvailA) ? &(currSlice->mb_data[currMB->mbAddrA]) : NULL; + currMB->mb_up = (currMB->mbAvailB) ? &(currSlice->mb_data[currMB->mbAddrB]) : NULL; +} + +/*! + ************************************************************************ + * \brief + * Checks the availability of neighboring macroblocks of + * the current macroblock for prediction and context determination; + ************************************************************************ + */ +void CheckAvailabilityOfNeighborsMBAFF(Macroblock *currMB) +{ + Slice *currSlice = currMB->p_Slice; + StorablePicture *dec_picture = currSlice->dec_picture; //p_Vid->dec_picture; + const int mb_nr = currMB->mbAddrX; + BlockPos *PicPos = currMB->p_Vid->PicPos; + + int cur_mb_pair = mb_nr >> 1; + currMB->mbAddrA = 2 * (cur_mb_pair - 1); + currMB->mbAddrB = 2 * (cur_mb_pair - dec_picture->PicWidthInMbs); + currMB->mbAddrC = 2 * (cur_mb_pair - dec_picture->PicWidthInMbs + 1); + currMB->mbAddrD = 2 * (cur_mb_pair - dec_picture->PicWidthInMbs - 1); + + currMB->mbAvailA = (Boolean) (mb_is_available(currMB->mbAddrA, currMB) && ((PicPos[cur_mb_pair ].x)!=0)); + currMB->mbAvailB = (Boolean) (mb_is_available(currMB->mbAddrB, currMB)); + currMB->mbAvailC = (Boolean) (mb_is_available(currMB->mbAddrC, currMB) && ((PicPos[cur_mb_pair + 1].x)!=0)); + currMB->mbAvailD = (Boolean) (mb_is_available(currMB->mbAddrD, currMB) && ((PicPos[cur_mb_pair ].x)!=0)); + + currMB->mb_left = (currMB->mbAvailA) ? &(currSlice->mb_data[currMB->mbAddrA]) : NULL; + currMB->mb_up = (currMB->mbAvailB) ? &(currSlice->mb_data[currMB->mbAddrB]) : NULL; +} + + +/*! + ************************************************************************ + * \brief + * returns the x and y macroblock coordinates for a given MbAddress + ************************************************************************ + */ +void get_mb_block_pos_normal (BlockPos *PicPos, int mb_addr, short *x, short *y) +{ + BlockPos *pPos = &PicPos[ mb_addr ]; + *x = (short) pPos->x; + *y = (short) pPos->y; +} + +/*! + ************************************************************************ + * \brief + * returns the x and y macroblock coordinates for a given MbAddress + * for mbaff type slices + ************************************************************************ + */ +void get_mb_block_pos_mbaff (BlockPos *PicPos, int mb_addr, short *x, short *y) +{ + BlockPos *pPos = &PicPos[ mb_addr >> 1 ]; + *x = (short) pPos->x; + *y = (short) ((pPos->y << 1) + (mb_addr & 0x01)); +} + +/*! + ************************************************************************ + * \brief + * returns the x and y sample coordinates for a given MbAddress + ************************************************************************ + */ +void get_mb_pos (VideoParameters *p_Vid, int mb_addr, int mb_size[2], short *x, short *y) +{ + p_Vid->get_mb_block_pos(p_Vid->PicPos, mb_addr, x, y); + + (*x) = (short) ((*x) * mb_size[0]); + (*y) = (short) ((*y) * mb_size[1]); +} + + +/*! + ************************************************************************ + * \brief + * get neighbouring positions for non-aff coding + * \param currMB + * current macroblock + * \param xN + * input x position + * \param yN + * input y position + * \param mb_size + * Macroblock size in pixel (according to luma or chroma MB access) + * \param pix + * returns position informations + ************************************************************************ + */ +void getNonAffNeighbour(Macroblock *currMB, int xN, int yN, int mb_size[2], PixelPos *pix) +{ + int maxW = mb_size[0], maxH = mb_size[1]; + + if (xN < 0) + { + if (yN < 0) + { + pix->mb_addr = currMB->mbAddrD; + pix->available = currMB->mbAvailD; + } + else if (yN < maxH) + { + pix->mb_addr = currMB->mbAddrA; + pix->available = currMB->mbAvailA; + } + else + { + pix->available = FALSE; + } + } + else if (xN < maxW) + { + if (yN < 0) + { + pix->mb_addr = currMB->mbAddrB; + pix->available = currMB->mbAvailB; + } + else if (yN < maxH) + { + pix->mb_addr = currMB->mbAddrX; + pix->available = TRUE; + } + else + { + pix->available = FALSE; + } + } + else if ((xN >= maxW) && (yN < 0)) + { + pix->mb_addr = currMB->mbAddrC; + pix->available = currMB->mbAvailC; + } + else + { + pix->available = FALSE; + } + + if (pix->available || currMB->DeblockCall) + { + BlockPos *CurPos = &(currMB->p_Vid->PicPos[ pix->mb_addr ]); + pix->x = (short) (xN & (maxW - 1)); + pix->y = (short) (yN & (maxH - 1)); + pix->pos_x = (short) (pix->x + CurPos->x * maxW); + pix->pos_y = (short) (pix->y + CurPos->y * maxH); + } +} + +/*! + ************************************************************************ + * \brief + * get neighboring positions for aff coding + * \param currMB + * current macroblock + * \param xN + * input x position + * \param yN + * input y position + * \param mb_size + * Macroblock size in pixel (according to luma or chroma MB access) + * \param pix + * returns position informations + ************************************************************************ + */ +void getAffNeighbour(Macroblock *currMB, int xN, int yN, int mb_size[2], PixelPos *pix) +{ + VideoParameters *p_Vid = currMB->p_Vid; + int maxW, maxH; + int yM = -1; + + maxW = mb_size[0]; + maxH = mb_size[1]; + + // initialize to "not available" + pix->available = FALSE; + + if(yN > (maxH - 1)) + { + return; + } + if (xN > (maxW - 1) && yN >= 0 && yN < maxH) + { + return; + } + + if (xN < 0) + { + if (yN < 0) + { + if(!currMB->mb_field) + { + // frame + if ((currMB->mbAddrX & 0x01) == 0) + { + // top + pix->mb_addr = currMB->mbAddrD + 1; + pix->available = currMB->mbAvailD; + yM = yN; + } + else + { + // bottom + pix->mb_addr = currMB->mbAddrA; + pix->available = currMB->mbAvailA; + if (currMB->mbAvailA) + { + if(!p_Vid->mb_data[currMB->mbAddrA].mb_field) + { + yM = yN; + } + else + { + (pix->mb_addr)++; + yM = (yN + maxH) >> 1; + } + } + } + } + else + { + // field + if ((currMB->mbAddrX & 0x01) == 0) + { + // top + pix->mb_addr = currMB->mbAddrD; + pix->available = currMB->mbAvailD; + if (currMB->mbAvailD) + { + if(!p_Vid->mb_data[currMB->mbAddrD].mb_field) + { + (pix->mb_addr)++; + yM = 2 * yN; + } + else + { + yM = yN; + } + } + } + else + { + // bottom + pix->mb_addr = currMB->mbAddrD+1; + pix->available = currMB->mbAvailD; + yM = yN; + } + } + } + else + { // xN < 0 && yN >= 0 + if (yN >= 0 && yN mb_field) + { + // frame + if ((currMB->mbAddrX & 0x01) == 0) + { + // top + pix->mb_addr = currMB->mbAddrA; + pix->available = currMB->mbAvailA; + if (currMB->mbAvailA) + { + if(!p_Vid->mb_data[currMB->mbAddrA].mb_field) + { + yM = yN; + } + else + { + (pix->mb_addr)+= ((yN & 0x01) != 0); + yM = yN >> 1; + } + } + } + else + { + // bottom + pix->mb_addr = currMB->mbAddrA; + pix->available = currMB->mbAvailA; + if (currMB->mbAvailA) + { + if(!p_Vid->mb_data[currMB->mbAddrA].mb_field) + { + (pix->mb_addr)++; + yM = yN; + } + else + { + (pix->mb_addr)+= ((yN & 0x01) != 0); + yM = (yN + maxH) >> 1; + } + } + } + } + else + { + // field + if ((currMB->mbAddrX & 0x01) == 0) + { + // top + pix->mb_addr = currMB->mbAddrA; + pix->available = currMB->mbAvailA; + if (currMB->mbAvailA) + { + if(!p_Vid->mb_data[currMB->mbAddrA].mb_field) + { + if (yN < (maxH >> 1)) + { + yM = yN << 1; + } + else + { + (pix->mb_addr)++; + yM = (yN << 1 ) - maxH; + } + } + else + { + yM = yN; + } + } + } + else + { + // bottom + pix->mb_addr = currMB->mbAddrA; + pix->available = currMB->mbAvailA; + if (currMB->mbAvailA) + { + if(!p_Vid->mb_data[currMB->mbAddrA].mb_field) + { + if (yN < (maxH >> 1)) + { + yM = (yN << 1) + 1; + } + else + { + (pix->mb_addr)++; + yM = (yN << 1 ) + 1 - maxH; + } + } + else + { + (pix->mb_addr)++; + yM = yN; + } + } + } + } + } + } + } + else + { // xN >= 0 + if (xN >= 0 && xN < maxW) + { + if (yN<0) + { + if (!currMB->mb_field) + { + //frame + if ((currMB->mbAddrX & 0x01) == 0) + { + //top + pix->mb_addr = currMB->mbAddrB; + // for the deblocker if the current MB is a frame and the one above is a field + // then the neighbor is the top MB of the pair + if (currMB->mbAvailB) + { + if (!(currMB->DeblockCall == 1 && (p_Vid->mb_data[currMB->mbAddrB]).mb_field)) + pix->mb_addr += 1; + } + + pix->available = currMB->mbAvailB; + yM = yN; + } + else + { + // bottom + pix->mb_addr = currMB->mbAddrX - 1; + pix->available = TRUE; + yM = yN; + } + } + else + { + // field + if ((currMB->mbAddrX & 0x01) == 0) + { + // top + pix->mb_addr = currMB->mbAddrB; + pix->available = currMB->mbAvailB; + if (currMB->mbAvailB) + { + if(!p_Vid->mb_data[currMB->mbAddrB].mb_field) + { + (pix->mb_addr)++; + yM = 2* yN; + } + else + { + yM = yN; + } + } + } + else + { + // bottom + pix->mb_addr = currMB->mbAddrB + 1; + pix->available = currMB->mbAvailB; + yM = yN; + } + } + } + else + { + // yN >=0 + // for the deblocker if this is the extra edge then do this special stuff + if (yN == 0 && currMB->DeblockCall == 2) + { + pix->mb_addr = currMB->mbAddrB + 1; + pix->available = TRUE; + yM = yN - 1; + } + + else if ((yN >= 0) && (yN mb_addr = currMB->mbAddrX; + pix->available = TRUE; + yM = yN; + } + } + } + else + { // xN >= maxW + if(yN < 0) + { + if (!currMB->mb_field) + { + // frame + if ((currMB->mbAddrX & 0x01) == 0) + { + // top + pix->mb_addr = currMB->mbAddrC + 1; + pix->available = currMB->mbAvailC; + yM = yN; + } + else + { + // bottom + pix->available = FALSE; + } + } + else + { + // field + if ((currMB->mbAddrX & 0x01) == 0) + { + // top + pix->mb_addr = currMB->mbAddrC; + pix->available = currMB->mbAvailC; + if (currMB->mbAvailC) + { + if(!p_Vid->mb_data[currMB->mbAddrC].mb_field) + { + (pix->mb_addr)++; + yM = 2* yN; + } + else + { + yM = yN; + } + } + } + else + { + // bottom + pix->mb_addr = currMB->mbAddrC + 1; + pix->available = currMB->mbAvailC; + yM = yN; + } + } + } + } + } + if (pix->available || currMB->DeblockCall) + { + pix->x = (short) (xN & (maxW - 1)); + pix->y = (short) (yM & (maxH - 1)); + get_mb_pos(p_Vid, pix->mb_addr, mb_size, &(pix->pos_x), &(pix->pos_y)); + pix->pos_x = pix->pos_x + pix->x; + pix->pos_y = pix->pos_y + pix->y; + } +} + + +/*! + ************************************************************************ + * \brief + * get neighboring 4x4 block + * \param currMB + * current macroblock + * \param block_x + * input x block position + * \param block_y + * input y block position + * \param mb_size + * Macroblock size in pixel (according to luma or chroma MB access) + * \param pix + * returns position informations + ************************************************************************ + */ +void get4x4Neighbour (Macroblock *currMB, int block_x, int block_y, int mb_size[2], PixelPos *pix) +{ + currMB->p_Vid->getNeighbour(currMB, block_x, block_y, mb_size, pix); + + if (pix->available) + { + pix->x >>= 2; + pix->y >>= 2; + pix->pos_x >>= 2; + pix->pos_y >>= 2; + } +} + +/*! + ************************************************************************ + * \brief + * get neighboring 4x4 block + * \param currMB + * current macroblock + * \param block_x + * input x block position + * \param block_y + * input y block position + * \param mb_size + * Macroblock size in pixel (according to luma or chroma MB access) + * \param pix + * returns position informations + ************************************************************************ + */ +void get4x4NeighbourBase (Macroblock *currMB, int block_x, int block_y, int mb_size[2], PixelPos *pix) +{ + currMB->p_Vid->getNeighbour(currMB, block_x, block_y, mb_size, pix); + + if (pix->available) + { + pix->x >>= 2; + pix->y >>= 2; + } +} diff --git a/ldecod/src/mb_prediction.c b/ldecod/src/mb_prediction.c new file mode 100644 index 0000000..55dfc4f --- /dev/null +++ b/ldecod/src/mb_prediction.c @@ -0,0 +1,1300 @@ +/*! +************************************************************************************* +* \file mb_prediction.c +* +* \brief +* Macroblock prediction functions +* +* \author +* Main contributors (see contributors.h for copyright, address and affiliation details) +* - Alexis Michael Tourapis +************************************************************************************* +*/ + +#include "contributors.h" + +#include "block.h" +#include "global.h" +#include "mbuffer.h" +#include "elements.h" +#include "errorconcealment.h" +#include "macroblock.h" +#include "fmo.h" +#include "cabac.h" +#include "vlc.h" +#include "image.h" +#include "mb_access.h" +#include "biaridecod.h" +#include "transform8x8.h" +#include "transform.h" +#include "mc_prediction.h" +#include "quant.h" +#include "intra4x4_pred.h" +#include "intra8x8_pred.h" +#include "intra16x16_pred.h" +#include "mv_prediction.h" +#include "mb_prediction.h" + +extern int get_colocated_info_8x8 (Macroblock *currMB, StorablePicture *list1, int i, int j); +extern int get_colocated_info_4x4 (Macroblock *currMB, StorablePicture *list1, int i, int j); + + +int mb_pred_intra4x4(Macroblock *currMB, ColorPlane curr_plane, imgpel **currImg, StorablePicture *dec_picture) +{ + Slice *currSlice = currMB->p_Slice; + int yuv = dec_picture->chroma_format_idc - 1; + int i=0, j=0,k, j4=0,i4=0; + int j_pos, i_pos; + int ioff,joff; + int block8x8; // needed for ABT + currMB->itrans_4x4 = (currMB->is_lossless == FALSE) ? itrans4x4 : Inv_Residual_trans_4x4; + + for (block8x8 = 0; block8x8 < 4; block8x8++) + { + for (k = block8x8 * 4; k < block8x8 * 4 + 4; k ++) + { + i = (decode_block_scan[k] & 3); + j = ((decode_block_scan[k] >> 2) & 3); + + ioff = (i << 2); + joff = (j << 2); + i4 = currMB->block_x + i; + j4 = currMB->block_y + j; + j_pos = j4 * BLOCK_SIZE; + i_pos = i4 * BLOCK_SIZE; + + // PREDICTION + //===== INTRA PREDICTION ===== + if (currSlice->intra_pred_4x4(currMB, curr_plane, ioff,joff,i4,j4) == SEARCH_SYNC) /* make 4x4 prediction block mpr from given prediction p_Vid->mb_mode */ + return SEARCH_SYNC; /* bit error */ + // =============== 4x4 itrans ================ + // ------------------------------------------- + currMB->itrans_4x4 (currMB, curr_plane, ioff, joff); + + copy_image_data_4x4(&currImg[j_pos], &currSlice->mb_rec[curr_plane][joff], i_pos, ioff); + } + } + + // chroma decoding ******************************************************* + if ((dec_picture->chroma_format_idc != YUV400) && (dec_picture->chroma_format_idc != YUV444)) + { + intra_cr_decoding(currMB, yuv); + } + + if (currMB->cbp != 0) + currSlice->is_reset_coeff = FALSE; + return 1; +} + + +int mb_pred_intra16x16(Macroblock *currMB, ColorPlane curr_plane, StorablePicture *dec_picture) +{ + int yuv = dec_picture->chroma_format_idc - 1; + + currMB->p_Slice->intra_pred_16x16(currMB, curr_plane, currMB->i16mode); + currMB->ipmode_DPCM = (char) currMB->i16mode; //For residual DPCM + // =============== 4x4 itrans ================ + // ------------------------------------------- + iMBtrans4x4(currMB, curr_plane, 0); + + // chroma decoding ******************************************************* + if ((dec_picture->chroma_format_idc != YUV400) && (dec_picture->chroma_format_idc != YUV444)) + { + intra_cr_decoding(currMB, yuv); + } + + currMB->p_Slice->is_reset_coeff = FALSE; + return 1; +} + +int mb_pred_intra8x8(Macroblock *currMB, ColorPlane curr_plane, imgpel **currImg, StorablePicture *dec_picture) +{ + Slice *currSlice = currMB->p_Slice; + int yuv = dec_picture->chroma_format_idc - 1; + + int block8x8; // needed for ABT + currMB->itrans_8x8 = (currMB->is_lossless == FALSE) ? itrans8x8 : Inv_Residual_trans_8x8; + + for (block8x8 = 0; block8x8 < 4; block8x8++) + { + //=========== 8x8 BLOCK TYPE ============ + int ioff = (block8x8 & 0x01) << 3; + int joff = (block8x8 >> 1 ) << 3; + + //PREDICTION + currSlice->intra_pred_8x8(currMB, curr_plane, ioff, joff); + if (currMB->cbp & (1 << block8x8)) + currMB->itrans_8x8 (currMB, curr_plane, ioff,joff); // use inverse integer transform and make 8x8 block m7 from prediction block mpr + else + icopy8x8(currMB, curr_plane, ioff,joff); + + copy_image_data_8x8(&currImg[currMB->pix_y + joff], &currSlice->mb_rec[curr_plane][joff], currMB->pix_x + ioff, ioff); + } + // chroma decoding ******************************************************* + if ((dec_picture->chroma_format_idc != YUV400) && (dec_picture->chroma_format_idc != YUV444)) + { + intra_cr_decoding(currMB, yuv); + } + + if (currMB->cbp != 0) + currSlice->is_reset_coeff = FALSE; + return 1; +} + + +static void set_chroma_vector(Macroblock *currMB) +{ + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + + if (!currSlice->mb_aff_frame_flag) + { + if(currSlice->structure == TOP_FIELD) + { + int k,l; + for (l = LIST_0; l <= (LIST_1); l++) + { + for(k = 0; k < currSlice->listXsize[l]; k++) + { + if(p_Vid->structure != currSlice->listX[l][k]->structure) + currSlice->chroma_vector_adjustment[l][k] = -2; + else + currSlice->chroma_vector_adjustment[l][k] = 0; + } + } + } + else if(currSlice->structure == BOTTOM_FIELD) + { + int k,l; + for (l = LIST_0; l <= (LIST_1); l++) + { + for(k = 0; k < currSlice->listXsize[l]; k++) + { + if (p_Vid->structure != currSlice->listX[l][k]->structure) + currSlice->chroma_vector_adjustment[l][k] = 2; + else + currSlice->chroma_vector_adjustment[l][k] = 0; + } + } + } + else + { + int k,l; + for (l = LIST_0; l <= (LIST_1); l++) + { + for(k = 0; k < currSlice->listXsize[l]; k++) + { + currSlice->chroma_vector_adjustment[l][k] = 0; + } + } + } + } + else + { + int mb_nr = (currMB->mbAddrX & 0x01); + int k,l; + + ////////////////////////// + // find out the correct list offsets + if (currMB->mb_field) + { + int list_offset = currMB->list_offset; + + for (l = LIST_0 + list_offset; l <= (LIST_1 + list_offset); l++) + { + for(k = 0; k < currSlice->listXsize[l]; k++) + { + if(mb_nr == 0 && currSlice->listX[l][k]->structure == BOTTOM_FIELD) + currSlice->chroma_vector_adjustment[l][k] = -2; + else if(mb_nr == 1 && currSlice->listX[l][k]->structure == TOP_FIELD) + currSlice->chroma_vector_adjustment[l][k] = 2; + else + currSlice->chroma_vector_adjustment[l][k] = 0; + } + } + } + else + { + for (l = LIST_0; l <= (LIST_1); l++) + { + for(k = 0; k < currSlice->listXsize[l]; k++) + { + currSlice->chroma_vector_adjustment[l][k] = 0; + } + } + } + } + + currSlice->max_mb_vmv_r = (currSlice->structure != FRAME || ( currMB->mb_field )) ? p_Vid->max_vmv_r >> 1 : p_Vid->max_vmv_r; +} + +int mb_pred_skip(Macroblock *currMB, ColorPlane curr_plane, imgpel **currImg, StorablePicture *dec_picture) +{ + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + + //! Edited by Joao Carreira + currMB->p_Vid->n_SKIP++; + + set_chroma_vector(currMB); + + perform_mc(currMB, curr_plane, dec_picture, LIST_0, 0, 0, MB_BLOCK_SIZE, MB_BLOCK_SIZE); + + copy_image_data_16x16(&currImg[currMB->pix_y], currSlice->mb_pred[curr_plane], currMB->pix_x, 0); + + if ((dec_picture->chroma_format_idc != YUV400) && (dec_picture->chroma_format_idc != YUV444)) + { + + copy_image_data(&dec_picture->imgUV[0][currMB->pix_c_y], currSlice->mb_pred[1], currMB->pix_c_x, 0, p_Vid->mb_size[1][0], p_Vid->mb_size[1][1]); + copy_image_data(&dec_picture->imgUV[1][currMB->pix_c_y], currSlice->mb_pred[2], currMB->pix_c_x, 0, p_Vid->mb_size[1][0], p_Vid->mb_size[1][1]); + } + return 1; +} + +int mb_pred_sp_skip(Macroblock *currMB, ColorPlane curr_plane, StorablePicture *dec_picture) +{ + set_chroma_vector(currMB); + + perform_mc(currMB, curr_plane, dec_picture, LIST_0, 0, 0, MB_BLOCK_SIZE, MB_BLOCK_SIZE); + iTransform(currMB, curr_plane, 1); + return 1; +} + +int mb_pred_p_inter8x8(Macroblock *currMB, ColorPlane curr_plane, StorablePicture *dec_picture) +{ + int block8x8; // needed for ABT + int i=0, j=0,k; + + Slice *currSlice = currMB->p_Slice; + int smb = currSlice->slice_type == SP_SLICE && (currMB->is_intra_block == FALSE); + + set_chroma_vector(currMB); + + for (block8x8=0; block8x8<4; block8x8++) + { + int mv_mode = currMB->b8mode[block8x8]; + int pred_dir = currMB->b8pdir[block8x8]; + + int k_start = (block8x8 << 2); + int k_inc = (mv_mode == SMB8x4) ? 2 : 1; + int k_end = (mv_mode == SMB8x8) ? k_start + 1 : ((mv_mode == SMB4x4) ? k_start + 4 : k_start + k_inc + 1); + + int block_size_x = ( mv_mode == SMB8x4 || mv_mode == SMB8x8 ) ? SMB_BLOCK_SIZE : BLOCK_SIZE; + int block_size_y = ( mv_mode == SMB4x8 || mv_mode == SMB8x8 ) ? SMB_BLOCK_SIZE : BLOCK_SIZE; + + for (k = k_start; k < k_end; k += k_inc) + { + i = (decode_block_scan[k] & 3); + j = ((decode_block_scan[k] >> 2) & 3); + perform_mc(currMB, curr_plane, dec_picture, pred_dir, i, j, block_size_x, block_size_y); + } + } + + iTransform(currMB, curr_plane, smb); + + if (currMB->cbp != 0) + currSlice->is_reset_coeff = FALSE; + return 1; +} + +int mb_pred_p_inter16x16(Macroblock *currMB, ColorPlane curr_plane, StorablePicture *dec_picture) +{ + Slice *currSlice = currMB->p_Slice; + int smb = (currSlice->slice_type == SP_SLICE); + + set_chroma_vector(currMB); + perform_mc(currMB, curr_plane, dec_picture, currMB->b8pdir[0], 0, 0, MB_BLOCK_SIZE, MB_BLOCK_SIZE); + iTransform(currMB, curr_plane, smb); + + if (currMB->cbp != 0) + currSlice->is_reset_coeff = FALSE; + return 1; +} + +int mb_pred_p_inter16x8(Macroblock *currMB, ColorPlane curr_plane, StorablePicture *dec_picture) +{ + Slice *currSlice = currMB->p_Slice; + int smb = (currSlice->slice_type == SP_SLICE); + + set_chroma_vector(currMB); + + perform_mc(currMB, curr_plane, dec_picture, currMB->b8pdir[0], 0, 0, MB_BLOCK_SIZE, BLOCK_SIZE_8x8); + perform_mc(currMB, curr_plane, dec_picture, currMB->b8pdir[2], 0, 2, MB_BLOCK_SIZE, BLOCK_SIZE_8x8); + iTransform(currMB, curr_plane, smb); + + if (currMB->cbp != 0) + currSlice->is_reset_coeff = FALSE; + return 1; +} + +int mb_pred_p_inter8x16(Macroblock *currMB, ColorPlane curr_plane, StorablePicture *dec_picture) +{ + Slice *currSlice = currMB->p_Slice; + int smb = (currSlice->slice_type == SP_SLICE); + + set_chroma_vector(currMB); + + perform_mc(currMB, curr_plane, dec_picture, currMB->b8pdir[0], 0, 0, BLOCK_SIZE_8x8, MB_BLOCK_SIZE); + perform_mc(currMB, curr_plane, dec_picture, currMB->b8pdir[1], 2, 0, BLOCK_SIZE_8x8, MB_BLOCK_SIZE); + iTransform(currMB, curr_plane, smb); + + if (currMB->cbp != 0) + currSlice->is_reset_coeff = FALSE; + return 1; +} + +static inline void update_neighbor_mvs(PicMotionParams **motion, const PicMotionParams *mv_info, int i4) +{ + (*motion++)[i4 + 1] = *mv_info; + (*motion )[i4 ] = *mv_info; + (*motion )[i4 + 1] = *mv_info; +} + +int mb_pred_b_d8x8temporal(Macroblock *currMB, ColorPlane curr_plane, imgpel **currImg, StorablePicture *dec_picture) +{ + short ref_idx; + int refList; + + int k, i, j, i4, j4, j6; + int block8x8; // needed for ABT + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + PicMotionParams *mv_info = NULL, *colocated = NULL; + + int list_offset = currMB->list_offset; + StorablePicture **list0 = currSlice->listX[LIST_0 + list_offset]; + StorablePicture **list1 = currSlice->listX[LIST_1 + list_offset]; + + set_chroma_vector(currMB); + + //printf("currMB %d\n", currMB->mbAddrX); + for (block8x8=0; block8x8<4; block8x8++) + { + int pred_dir = currMB->b8pdir[block8x8]; + + int k_start = (block8x8 << 2); + int k_end = k_start + 1; + + for (k = k_start; k < k_start + BLOCK_MULTIPLE; k ++) + { + + i = (decode_block_scan[k] & 3); + j = ((decode_block_scan[k] >> 2) & 3); + i4 = currMB->block_x + i; + j4 = currMB->block_y + j; + j6 = currMB->block_y_aff + j; + mv_info = &dec_picture->mv_info[j4][i4]; + colocated = &list1[0]->mv_info[RSD(j6)][RSD(i4)]; + if(currMB->p_Vid->separate_colour_plane_flag && currMB->p_Vid->yuv_format==YUV444) + colocated = &list1[0]->JVmv_info[currMB->p_Slice->colour_plane_id][RSD(j6)][RSD(i4)]; + if(currSlice->mb_aff_frame_flag /*&& (!p_Vid->active_sps->frame_mbs_only_flag || p_Vid->active_sps->direct_8x8_inference_flag)*/) + { + assert(p_Vid->active_sps->direct_8x8_inference_flag); + if(!currMB->mb_field && ((currSlice->listX[LIST_1][0]->iCodingType==FRAME_MB_PAIR_CODING && currSlice->listX[LIST_1][0]->motion.mb_field[currMB->mbAddrX]) || + (currSlice->listX[LIST_1][0]->iCodingType==FIELD_CODING))) + { + if (iabs(dec_picture->poc - currSlice->listX[LIST_1+4][0]->poc)> iabs(dec_picture->poc -currSlice->listX[LIST_1+2][0]->poc) ) + { + colocated = p_Vid->active_sps->direct_8x8_inference_flag ? + &currSlice->listX[LIST_1+2][0]->mv_info[RSD(j6)>>1][RSD(i4)] : &currSlice->listX[LIST_1+2][0]->mv_info[j6>>1][i4]; + } + else + { + colocated = p_Vid->active_sps->direct_8x8_inference_flag ? + &currSlice->listX[LIST_1+4][0]->mv_info[RSD(j6)>>1][RSD(i4)] : &currSlice->listX[LIST_1+4][0]->mv_info[j6>>1][i4]; + } + } + } + else if(/*!currSlice->mb_aff_frame_flag &&*/ !p_Vid->active_sps->frame_mbs_only_flag && + (!currSlice->field_pic_flag && currSlice->listX[LIST_1][0]->iCodingType!=FRAME_CODING)) + { + if (iabs(dec_picture->poc - list1[0]->bottom_field->poc)> iabs(dec_picture->poc -list1[0]->top_field->poc) ) + { + colocated = p_Vid->active_sps->direct_8x8_inference_flag ? + &list1[0]->top_field->mv_info[RSD(j6)>>1][RSD(i4)] : &list1[0]->top_field->mv_info[j6>>1][i4]; + } + else + { + colocated = p_Vid->active_sps->direct_8x8_inference_flag ? + &list1[0]->bottom_field->mv_info[RSD(j6)>>1][RSD(i4)] : &list1[0]->bottom_field->mv_info[j6>>1][i4]; + } + } + else if(!p_Vid->active_sps->frame_mbs_only_flag && currSlice->field_pic_flag && currSlice->structure!=list1[0]->structure && list1[0]->coded_frame) + { + if (currSlice->structure == TOP_FIELD) + { + colocated = p_Vid->active_sps->direct_8x8_inference_flag ? + &list1[0]->frame->top_field->mv_info[RSD(j6)][RSD(i4)] : &list1[0]->frame->top_field->mv_info[j6][i4]; + } + else + { + colocated = p_Vid->active_sps->direct_8x8_inference_flag ? + &list1[0]->frame->bottom_field->mv_info[RSD(j6)][RSD(i4)] : &list1[0]->frame->bottom_field->mv_info[j6][i4]; + } + } + + + assert (pred_dir<=2); + + refList = (colocated->ref_idx[LIST_0]== -1 ? LIST_1 : LIST_0); + ref_idx = colocated->ref_idx[refList]; + + if( colocated->ref_pic[refList] == NULL ) + { + ref_idx = -1; + } + + if(ref_idx==-1) // co-located is intra mode + { + mv_info->mv[LIST_0] = zero_mv; + mv_info->mv[LIST_1] = zero_mv; + + mv_info->ref_idx[LIST_0] = 0; + mv_info->ref_idx[LIST_1] = 0; + } + else // co-located skip or inter mode + { + int mapped_idx=0; + int iref; + if( (currSlice->mb_aff_frame_flag && ( (currMB->mb_field && colocated->ref_pic[refList]->structure==FRAME) || + (!currMB->mb_field && colocated->ref_pic[refList]->structure!=FRAME))) || + (!currSlice->mb_aff_frame_flag && ((currSlice->field_pic_flag==0 && colocated->ref_pic[refList]->structure!=FRAME) + ||(currSlice->field_pic_flag==1 && colocated->ref_pic[refList]->structure==FRAME))) ) + { + for (iref = 0; iref < imin(currSlice->num_ref_idx_active[LIST_0], currSlice->listXsize[LIST_0 + list_offset]);iref++) + { + if(currSlice->listX[LIST_0 + list_offset][iref]->top_field == colocated->ref_pic[refList] || + currSlice->listX[LIST_0 + list_offset][iref]->bottom_field == colocated->ref_pic[refList] || + currSlice->listX[LIST_0 + list_offset][iref]->frame == colocated->ref_pic[refList]) + { + if ((currSlice->field_pic_flag==1) && (currSlice->listX[LIST_0 + list_offset][iref]->structure != currSlice->structure)) + { + mapped_idx=INVALIDINDEX; + } + else + { + mapped_idx = iref; + break; + } + } + else //! invalid index. Default to zero even though this case should not happen + { + mapped_idx=INVALIDINDEX; + } + } + } + else + { + for (iref = 0; iref < imin(currSlice->num_ref_idx_active[LIST_0], currSlice->listXsize[LIST_0 + list_offset]);iref++) + { + if(currSlice->listX[LIST_0 + list_offset][iref] == colocated->ref_pic[refList]) + { + mapped_idx = iref; + break; + } + else //! invalid index. Default to zero even though this case should not happen + { + mapped_idx=INVALIDINDEX; + } + } + } + + if(INVALIDINDEX != mapped_idx) + { + int mv_scale = currSlice->mvscale[LIST_0 + list_offset][mapped_idx]; + int mv_y = colocated->mv[refList].mv_y; + if((currSlice->mb_aff_frame_flag && !currMB->mb_field && colocated->ref_pic[refList]->structure!=FRAME) || + (!currSlice->mb_aff_frame_flag && currSlice->field_pic_flag==0 && colocated->ref_pic[refList]->structure!=FRAME) ) + mv_y *= 2; + else if((currSlice->mb_aff_frame_flag && currMB->mb_field && colocated->ref_pic[refList]->structure==FRAME) || + (!currSlice->mb_aff_frame_flag && currSlice->field_pic_flag==1 && colocated->ref_pic[refList]->structure==FRAME) ) + mv_y /= 2; + + //! In such case, an array is needed for each different reference. + if (mv_scale == 9999 || currSlice->listX[LIST_0 + list_offset][mapped_idx]->is_long_term) + { + mv_info->mv[LIST_0].mv_x = colocated->mv[refList].mv_x; + mv_info->mv[LIST_0].mv_y = (short) mv_y; + mv_info->mv[LIST_1] = zero_mv; + } + else + { + mv_info->mv[LIST_0].mv_x = (short) ((mv_scale * colocated->mv[refList].mv_x + 128 ) >> 8); + mv_info->mv[LIST_0].mv_y = (short) ((mv_scale * mv_y/*colocated->mv[refList].mv_y*/ + 128 ) >> 8); + + mv_info->mv[LIST_1].mv_x = (short) (mv_info->mv[LIST_0].mv_x - colocated->mv[refList].mv_x); + mv_info->mv[LIST_1].mv_y = (short) (mv_info->mv[LIST_0].mv_y - mv_y/*colocated->mv[refList].mv_y*/); + } + + mv_info->ref_idx[LIST_0] = (char) mapped_idx; //colocated->ref_idx[refList]; + mv_info->ref_idx[LIST_1] = 0; + } + else if (INVALIDINDEX == mapped_idx) + { + error("temporal direct error: colocated block has ref that is unavailable",-1111); + } + + } + // store reference picture ID determined by direct mode + mv_info->ref_pic[LIST_0] = list0[(short)mv_info->ref_idx[LIST_0]]; + mv_info->ref_pic[LIST_1] = list1[(short)mv_info->ref_idx[LIST_1]]; + } + + for (k = k_start; k < k_end; k ++) + { + int i = (decode_block_scan[k] & 3); + int j = ((decode_block_scan[k] >> 2) & 3); + perform_mc(currMB, curr_plane, dec_picture, pred_dir, i, j, SMB_BLOCK_SIZE, SMB_BLOCK_SIZE); + } + } + + if (currMB->cbp == 0) + { + copy_image_data_16x16(&currImg[currMB->pix_y], currSlice->mb_pred[curr_plane], currMB->pix_x, 0); + + if ((dec_picture->chroma_format_idc != YUV400) && (dec_picture->chroma_format_idc != YUV444)) + { + copy_image_data(&dec_picture->imgUV[0][currMB->pix_c_y], currSlice->mb_pred[1], currMB->pix_c_x, 0, p_Vid->mb_size[1][0], p_Vid->mb_size[1][1]); + copy_image_data(&dec_picture->imgUV[1][currMB->pix_c_y], currSlice->mb_pred[2], currMB->pix_c_x, 0, p_Vid->mb_size[1][0], p_Vid->mb_size[1][1]); + } + } + else + { + iTransform(currMB, curr_plane, 0); + currSlice->is_reset_coeff = FALSE; + } + return 1; +} + +int mb_pred_b_d4x4temporal(Macroblock *currMB, ColorPlane curr_plane, imgpel **currImg, StorablePicture *dec_picture) +{ + short ref_idx; + int refList; + + int k; + int block8x8; // needed for ABT + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + + int list_offset = currMB->list_offset; + StorablePicture **list0 = currSlice->listX[LIST_0 + list_offset]; + StorablePicture **list1 = currSlice->listX[LIST_1 + list_offset]; + + set_chroma_vector(currMB); + + for (block8x8=0; block8x8<4; block8x8++) + { + int pred_dir = currMB->b8pdir[block8x8]; + + int k_start = (block8x8 << 2); + int k_end = k_start + BLOCK_MULTIPLE; + + for (k = k_start; k < k_end; k ++) + { + + int i = (decode_block_scan[k] & 3); + int j = ((decode_block_scan[k] >> 2) & 3); + int i4 = currMB->block_x + i; + int j4 = currMB->block_y + j; + int j6 = currMB->block_y_aff + j; + PicMotionParams *mv_info = &dec_picture->mv_info[j4][i4]; + PicMotionParams *colocated = &list1[0]->mv_info[j6][i4]; + if(currMB->p_Vid->separate_colour_plane_flag && currMB->p_Vid->yuv_format==YUV444) + colocated = &list1[0]->JVmv_info[currMB->p_Slice->colour_plane_id][RSD(j6)][RSD(i4)]; + assert (pred_dir<=2); + + refList = (colocated->ref_idx[LIST_0]== -1 ? LIST_1 : LIST_0); + ref_idx = colocated->ref_idx[refList]; + + if(ref_idx==-1) // co-located is intra mode + { + mv_info->mv[LIST_0] = zero_mv; + mv_info->mv[LIST_1] = zero_mv; + + mv_info->ref_idx[LIST_0] = 0; + mv_info->ref_idx[LIST_1] = 0; + } + else // co-located skip or inter mode + { + int mapped_idx=0; + int iref; + + for (iref=0;irefnum_ref_idx_active[LIST_0], currSlice->listXsize[LIST_0 + list_offset]);iref++) + { + if(currSlice->listX[LIST_0 + list_offset][iref] == colocated->ref_pic[refList]) + { + mapped_idx=iref; + break; + } + else //! invalid index. Default to zero even though this case should not happen + { + mapped_idx=INVALIDINDEX; + } + } + if (INVALIDINDEX == mapped_idx) + { + error("temporal direct error: colocated block has ref that is unavailable",-1111); + } + else + { + int mv_scale = currSlice->mvscale[LIST_0 + list_offset][mapped_idx]; + + //! In such case, an array is needed for each different reference. + if (mv_scale == 9999 || currSlice->listX[LIST_0+list_offset][mapped_idx]->is_long_term) + { + mv_info->mv[LIST_0] = colocated->mv[refList]; + mv_info->mv[LIST_1] = zero_mv; + } + else + { + mv_info->mv[LIST_0].mv_x = (short) ((mv_scale * colocated->mv[refList].mv_x + 128 ) >> 8); + mv_info->mv[LIST_0].mv_y = (short) ((mv_scale * colocated->mv[refList].mv_y + 128 ) >> 8); + + mv_info->mv[LIST_1].mv_x = (short) (mv_info->mv[LIST_0].mv_x - colocated->mv[refList].mv_x); + mv_info->mv[LIST_1].mv_y = (short) (mv_info->mv[LIST_0].mv_y - colocated->mv[refList].mv_y); + } + + mv_info->ref_idx[LIST_0] = (char) mapped_idx; //colocated->ref_idx[refList]; + mv_info->ref_idx[LIST_1] = 0; + } + } + // store reference picture ID determined by direct mode + mv_info->ref_pic[LIST_0] = list0[(short)mv_info->ref_idx[LIST_0]]; + mv_info->ref_pic[LIST_1] = list1[(short)mv_info->ref_idx[LIST_1]]; + } + + for (k = k_start; k < k_end; k ++) + { + int i = (decode_block_scan[k] & 3); + int j = ((decode_block_scan[k] >> 2) & 3); + perform_mc(currMB, curr_plane, dec_picture, pred_dir, i, j, BLOCK_SIZE, BLOCK_SIZE); + + } + } + + if (currMB->cbp == 0) + { + copy_image_data_16x16(&currImg[currMB->pix_y], currSlice->mb_pred[curr_plane], currMB->pix_x, 0); + + if ((dec_picture->chroma_format_idc != YUV400) && (dec_picture->chroma_format_idc != YUV444)) + { + copy_image_data(&dec_picture->imgUV[0][currMB->pix_c_y], currSlice->mb_pred[1], currMB->pix_c_x, 0, p_Vid->mb_size[1][0], p_Vid->mb_size[1][1]); + copy_image_data(&dec_picture->imgUV[1][currMB->pix_c_y], currSlice->mb_pred[2], currMB->pix_c_x, 0, p_Vid->mb_size[1][0], p_Vid->mb_size[1][1]); + } + } + else + { + iTransform(currMB, curr_plane, 0); + currSlice->is_reset_coeff = FALSE; + } + + return 1; +} + + +int mb_pred_b_d8x8spatial(Macroblock *currMB, ColorPlane curr_plane, imgpel **currImg, StorablePicture *dec_picture) +{ + char l0_rFrame = -1, l1_rFrame = -1; + MotionVector pmvl0 = zero_mv, pmvl1 = zero_mv; + int i4, j4; + int block8x8; + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + + PicMotionParams *mv_info; + int list_offset = currMB->list_offset; + StorablePicture **list0 = currSlice->listX[LIST_0 + list_offset]; + StorablePicture **list1 = currSlice->listX[LIST_1 + list_offset]; + + int pred_dir = 0; + + set_chroma_vector(currMB); + + prepare_direct_params(currMB, dec_picture, &pmvl0, &pmvl1, &l0_rFrame, &l1_rFrame); + + if (l0_rFrame == 0 || l1_rFrame == 0) + { + int is_not_moving; + + for (block8x8 = 0; block8x8 < 4; block8x8++) + { + int k_start = (block8x8 << 2); + + int i = (decode_block_scan[k_start] & 3); + int j = ((decode_block_scan[k_start] >> 2) & 3); + i4 = currMB->block_x + i; + j4 = currMB->block_y + j; + + //Edited by Joao Carreira->Debug + if( currMB->p_Vid->dec_picture->poc==10 && currMB->p_Vid->dec_picture->view_id==1 && + i4 == 208 && j4 == 140) + { + int a = 0; + } + + + + is_not_moving = (get_colocated_info_8x8(currMB, list1[0], i4, currMB->block_y_aff + j) == 0); + + mv_info = &dec_picture->mv_info[j4][i4]; + + //===== DIRECT PREDICTION ===== + if (l1_rFrame == -1) + { + if (is_not_moving) + { + mv_info->ref_pic[LIST_0] = list0[0]; + mv_info->ref_pic[LIST_1] = NULL; + mv_info->mv[LIST_0] = zero_mv; + mv_info->mv[LIST_1] = zero_mv; + mv_info->ref_idx[LIST_0] = 0; + mv_info->ref_idx[LIST_1] = -1; + } + else + { + mv_info->ref_pic[LIST_0] = list0[(short) l0_rFrame]; + mv_info->ref_pic[LIST_1] = NULL; + mv_info->mv[LIST_0] = pmvl0; + mv_info->mv[LIST_1] = zero_mv; + mv_info->ref_idx[LIST_0] = l0_rFrame; + mv_info->ref_idx[LIST_1] = -1; + } + pred_dir = 0; + } + else if (l0_rFrame == -1) + { + if (is_not_moving) + { + mv_info->ref_pic[LIST_0] = NULL; + mv_info->ref_pic[LIST_1] = list1[0]; + mv_info->mv[LIST_0] = zero_mv; + mv_info->mv[LIST_1] = zero_mv; + mv_info->ref_idx[LIST_0] = -1; + mv_info->ref_idx[LIST_1] = 0; + } + else + { + mv_info->ref_pic[LIST_0] = NULL; + mv_info->ref_pic[LIST_1] = list1[(short) l1_rFrame]; + mv_info->mv[LIST_0] = zero_mv; + mv_info->mv[LIST_1] = pmvl1; + mv_info->ref_idx[LIST_0] = -1; + mv_info->ref_idx[LIST_1] = l1_rFrame; + } + pred_dir = 1; + } + else + { + if (l0_rFrame == 0 && ((is_not_moving))) + { + mv_info->ref_pic[LIST_0] = list0[0]; + mv_info->mv[LIST_0] = zero_mv; + mv_info->ref_idx[LIST_0] = 0; + } + else + { + mv_info->ref_pic[LIST_0] = list0[(short) l0_rFrame]; + mv_info->mv[LIST_0] = pmvl0; + mv_info->ref_idx[LIST_0] = l0_rFrame; + } + + if (l1_rFrame == 0 && ((is_not_moving))) + { + mv_info->ref_pic[LIST_1] = list1[0]; + mv_info->mv[LIST_1] = zero_mv; + mv_info->ref_idx[LIST_1] = 0; + } + else + { + mv_info->ref_pic[LIST_1] = list1[(short) l1_rFrame]; + mv_info->mv[LIST_1] = pmvl1; + mv_info->ref_idx[LIST_1] = l1_rFrame; + } + pred_dir = 2; + } + + update_neighbor_mvs(&dec_picture->mv_info[j4], mv_info, i4); + perform_mc(currMB, curr_plane, dec_picture, pred_dir, i, j, SMB_BLOCK_SIZE, SMB_BLOCK_SIZE); + } + } + else + { + //===== DIRECT PREDICTION ===== + if (l0_rFrame < 0 && l1_rFrame < 0) + { + pred_dir = 2; + for (j4 = currMB->block_y; j4 < currMB->block_y + BLOCK_MULTIPLE; j4 += 2) + { + for (i4 = currMB->block_x; i4 < currMB->block_x + BLOCK_MULTIPLE; i4 += 2) + { + mv_info = &dec_picture->mv_info[j4][i4]; + + mv_info->ref_pic[LIST_0] = list0[0]; + mv_info->ref_pic[LIST_1] = list1[0]; + mv_info->mv[LIST_0] = zero_mv; + mv_info->mv[LIST_1] = zero_mv; + mv_info->ref_idx[LIST_0] = 0; + mv_info->ref_idx[LIST_1] = 0; + + update_neighbor_mvs(&dec_picture->mv_info[j4], mv_info, i4); + } + } + } + else if (l1_rFrame == -1) + { + pred_dir = 0; + + for (j4 = currMB->block_y; j4 < currMB->block_y + BLOCK_MULTIPLE; j4 += 2) + { + for (i4 = currMB->block_x; i4 < currMB->block_x + BLOCK_MULTIPLE; i4 += 2) + { + mv_info = &dec_picture->mv_info[j4][i4]; + + mv_info->ref_pic[LIST_0] = list0[(short) l0_rFrame]; + mv_info->ref_pic[LIST_1] = NULL; + mv_info->mv[LIST_0] = pmvl0; + mv_info->mv[LIST_1] = zero_mv; + mv_info->ref_idx[LIST_0] = l0_rFrame; + mv_info->ref_idx[LIST_1] = -1; + + update_neighbor_mvs(&dec_picture->mv_info[j4], mv_info, i4); + } + } + } + else if (l0_rFrame == -1) + { + pred_dir = 1; + for (j4 = currMB->block_y; j4 < currMB->block_y + BLOCK_MULTIPLE; j4 += 2) + { + for (i4 = currMB->block_x; i4 < currMB->block_x + BLOCK_MULTIPLE; i4 += 2) + { + mv_info = &dec_picture->mv_info[j4][i4]; + + mv_info->ref_pic[LIST_0] = NULL; + mv_info->ref_pic[LIST_1] = list1[(short) l1_rFrame]; + mv_info->mv[LIST_0] = zero_mv; + mv_info->mv[LIST_1] = pmvl1; + mv_info->ref_idx[LIST_0] = -1; + mv_info->ref_idx[LIST_1] = l1_rFrame; + + update_neighbor_mvs(&dec_picture->mv_info[j4], mv_info, i4); + } + } + } + else + { + pred_dir = 2; + + for (j4 = currMB->block_y; j4 < currMB->block_y + BLOCK_MULTIPLE; j4 += 2) + { + for (i4 = currMB->block_x; i4 < currMB->block_x + BLOCK_MULTIPLE; i4 += 2) + { + mv_info = &dec_picture->mv_info[j4][i4]; + + mv_info->ref_pic[LIST_0] = list0[(short) l0_rFrame]; + mv_info->ref_pic[LIST_1] = list1[(short) l1_rFrame]; + mv_info->mv[LIST_0] = pmvl0; + mv_info->mv[LIST_1] = pmvl1; + mv_info->ref_idx[LIST_0] = l0_rFrame; + mv_info->ref_idx[LIST_1] = l1_rFrame; + + update_neighbor_mvs(&dec_picture->mv_info[j4], mv_info, i4); + } + } + } + // Now perform Motion Compensation + perform_mc(currMB, curr_plane, dec_picture, pred_dir, 0, 0, MB_BLOCK_SIZE, MB_BLOCK_SIZE); + } + + if (currMB->cbp == 0) + { + copy_image_data_16x16(&currImg[currMB->pix_y], currSlice->mb_pred[curr_plane], currMB->pix_x, 0); + + if ((dec_picture->chroma_format_idc != YUV400) && (dec_picture->chroma_format_idc != YUV444)) + { + copy_image_data(&dec_picture->imgUV[0][currMB->pix_c_y], currSlice->mb_pred[1], currMB->pix_c_x, 0, p_Vid->mb_size[1][0], p_Vid->mb_size[1][1]); + copy_image_data(&dec_picture->imgUV[1][currMB->pix_c_y], currSlice->mb_pred[2], currMB->pix_c_x, 0, p_Vid->mb_size[1][0], p_Vid->mb_size[1][1]); + } + } + else + { + iTransform(currMB, curr_plane, 0); + currSlice->is_reset_coeff = FALSE; + } + + return 1; +} + + +int mb_pred_b_d4x4spatial(Macroblock *currMB, ColorPlane curr_plane, imgpel **currImg, StorablePicture *dec_picture) +{ + char l0_rFrame = -1, l1_rFrame = -1; + MotionVector pmvl0 = zero_mv, pmvl1 = zero_mv; + int k; + int block8x8; + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + + PicMotionParams *mv_info; + int list_offset = currMB->list_offset; + StorablePicture **list0 = currSlice->listX[LIST_0 + list_offset]; + StorablePicture **list1 = currSlice->listX[LIST_1 + list_offset]; + + int pred_dir = 0; + + set_chroma_vector(currMB); + + prepare_direct_params(currMB, dec_picture, &pmvl0, &pmvl1, &l0_rFrame, &l1_rFrame); + + for (block8x8 = 0; block8x8 < 4; block8x8++) + { + int k_start = (block8x8 << 2); + int k_end = k_start + BLOCK_MULTIPLE; + + for (k = k_start; k < k_end; k ++) + { + int i = (decode_block_scan[k] & 3); + int j = ((decode_block_scan[k] >> 2) & 3); + int i4 = currMB->block_x + i; + int j4 = currMB->block_y + j; + + mv_info = &dec_picture->mv_info[j4][i4]; + //===== DIRECT PREDICTION ===== + if (l0_rFrame == 0 || l1_rFrame == 0) + { + int is_not_moving = (get_colocated_info_4x4(currMB, list1[0], i4, currMB->block_y_aff + j) == 0); + + if (l1_rFrame == -1) + { + if (is_not_moving) + { + mv_info->ref_pic[LIST_0] = list0[0]; + mv_info->ref_pic[LIST_1] = NULL; + mv_info->mv[LIST_0] = zero_mv; + mv_info->mv[LIST_1] = zero_mv; + mv_info->ref_idx[LIST_0] = 0; + mv_info->ref_idx[LIST_1] = -1; + } + else + { + mv_info->ref_pic[LIST_0] = list0[(short) l0_rFrame]; + mv_info->ref_pic[LIST_1] = NULL; + mv_info->mv[LIST_0] = pmvl0; + mv_info->mv[LIST_1] = zero_mv; + mv_info->ref_idx[LIST_0] = l0_rFrame; + mv_info->ref_idx[LIST_1] = -1; + } + pred_dir = 0; + } + else if (l0_rFrame == -1) + { + if (is_not_moving) + { + mv_info->ref_pic[LIST_0] = NULL; + mv_info->ref_pic[LIST_1] = list1[0]; + mv_info->mv[LIST_0] = zero_mv; + mv_info->mv[LIST_1] = zero_mv; + mv_info->ref_idx[LIST_0] = -1; + mv_info->ref_idx[LIST_1] = 0; + } + else + { + mv_info->ref_pic[LIST_0] = NULL; + mv_info->ref_pic[LIST_1] = list1[(short) l1_rFrame]; + mv_info->mv[LIST_0] = zero_mv; + mv_info->mv[LIST_1] = pmvl1; + mv_info->ref_idx[LIST_0] = -1; + mv_info->ref_idx[LIST_1] = l1_rFrame; + } + pred_dir = 1; + } + else + { + if (l0_rFrame == 0 && ((is_not_moving))) + { + mv_info->ref_pic[LIST_0] = list0[0]; + mv_info->mv[LIST_0] = zero_mv; + mv_info->ref_idx[LIST_0] = 0; + } + else + { + mv_info->ref_pic[LIST_0] = list0[(short) l0_rFrame]; + mv_info->mv[LIST_0] = pmvl0; + mv_info->ref_idx[LIST_0] = l0_rFrame; + } + + if (l1_rFrame == 0 && ((is_not_moving))) + { + mv_info->ref_pic[LIST_1] = list1[0]; + mv_info->mv[LIST_1] = zero_mv; + mv_info->ref_idx[LIST_1] = 0; + } + else + { + mv_info->ref_pic[LIST_1] = list1[(short) l1_rFrame]; + mv_info->mv[LIST_1] = pmvl1; + mv_info->ref_idx[LIST_1] = l1_rFrame; + } + pred_dir = 2; + } + } + else + { + mv_info = &dec_picture->mv_info[j4][i4]; + + if (l0_rFrame < 0 && l1_rFrame < 0) + { + pred_dir = 2; + mv_info->ref_pic[LIST_0] = list0[0]; + mv_info->ref_pic[LIST_1] = list1[0]; + mv_info->mv[LIST_0] = zero_mv; + mv_info->mv[LIST_1] = zero_mv; + mv_info->ref_idx[LIST_0] = 0; + mv_info->ref_idx[LIST_1] = 0; + } + else if (l1_rFrame == -1) + { + pred_dir = 0; + mv_info->ref_pic[LIST_0] = list0[(short) l0_rFrame]; + mv_info->ref_pic[LIST_1] = NULL; + mv_info->mv[LIST_0] = pmvl0; + mv_info->mv[LIST_1] = zero_mv; + mv_info->ref_idx[LIST_0] = l0_rFrame; + mv_info->ref_idx[LIST_1] = -1; + } + else if (l0_rFrame == -1) + { + pred_dir = 1; + mv_info->ref_pic[LIST_0] = NULL; + mv_info->ref_pic[LIST_1] = list1[(short) l1_rFrame]; + mv_info->mv[LIST_0] = zero_mv; + mv_info->mv[LIST_1] = pmvl1; + mv_info->ref_idx[LIST_0] = -1; + mv_info->ref_idx[LIST_1] = l1_rFrame; + } + else + { + pred_dir = 2; + mv_info->ref_pic[LIST_0] = list0[(short) l0_rFrame]; + mv_info->ref_pic[LIST_1] = list1[(short) l1_rFrame]; + mv_info->mv[LIST_0] = pmvl0; + mv_info->mv[LIST_1] = pmvl1; + mv_info->ref_idx[LIST_0] = l0_rFrame; + mv_info->ref_idx[LIST_1] = l1_rFrame; + } + } + } + + for (k = k_start; k < k_end; k ++) + { + int i = (decode_block_scan[k] & 3); + int j = ((decode_block_scan[k] >> 2) & 3); + + perform_mc(currMB, curr_plane, dec_picture, pred_dir, i, j, BLOCK_SIZE, BLOCK_SIZE); + } + } + + if (currMB->cbp == 0) + { + copy_image_data_16x16(&currImg[currMB->pix_y], currSlice->mb_pred[curr_plane], currMB->pix_x, 0); + + if ((dec_picture->chroma_format_idc != YUV400) && (dec_picture->chroma_format_idc != YUV444)) + { + copy_image_data(&dec_picture->imgUV[0][currMB->pix_c_y], currSlice->mb_pred[1], currMB->pix_c_x, 0, p_Vid->mb_size[1][0], p_Vid->mb_size[1][1]); + copy_image_data(&dec_picture->imgUV[1][currMB->pix_c_y], currSlice->mb_pred[2], currMB->pix_c_x, 0, p_Vid->mb_size[1][0], p_Vid->mb_size[1][1]); + } + } + else + { + iTransform(currMB, curr_plane, 0); + currSlice->is_reset_coeff = FALSE; + } + + return 1; +} + +int mb_pred_b_inter8x8(Macroblock *currMB, ColorPlane curr_plane, StorablePicture *dec_picture) +{ + char l0_rFrame = -1, l1_rFrame = -1; + MotionVector pmvl0 = zero_mv, pmvl1 = zero_mv; + int block_size_x, block_size_y; + int k; + int block8x8; // needed for ABT + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + + int list_offset = currMB->list_offset; + StorablePicture **list0 = currSlice->listX[LIST_0 + list_offset]; + StorablePicture **list1 = currSlice->listX[LIST_1 + list_offset]; + + set_chroma_vector(currMB); + + // prepare direct modes + if (currSlice->direct_spatial_mv_pred_flag && (!(currMB->b8mode[0] && currMB->b8mode[1] && currMB->b8mode[2] && currMB->b8mode[3]))) + prepare_direct_params(currMB, dec_picture, &pmvl0, &pmvl1, &l0_rFrame, &l1_rFrame); + + for (block8x8=0; block8x8<4; block8x8++) + { + int mv_mode = currMB->b8mode[block8x8]; + int pred_dir = currMB->b8pdir[block8x8]; + + if ( mv_mode != 0 ) + { + int k_start = (block8x8 << 2); + int k_inc = (mv_mode == SMB8x4) ? 2 : 1; + int k_end = (mv_mode == SMB8x8) ? k_start + 1 : ((mv_mode == SMB4x4) ? k_start + 4 : k_start + k_inc + 1); + + block_size_x = ( mv_mode == SMB8x4 || mv_mode == SMB8x8 ) ? SMB_BLOCK_SIZE : BLOCK_SIZE; + block_size_y = ( mv_mode == SMB4x8 || mv_mode == SMB8x8 ) ? SMB_BLOCK_SIZE : BLOCK_SIZE; + + for (k = k_start; k < k_end; k += k_inc) + { + int i = (decode_block_scan[k] & 3); + int j = ((decode_block_scan[k] >> 2) & 3); + perform_mc(currMB, curr_plane, dec_picture, pred_dir, i, j, block_size_x, block_size_y); + } + } + else + { + int k_start = (block8x8 << 2); + int k_end = k_start; + + if (p_Vid->active_sps->direct_8x8_inference_flag) + { + block_size_x = SMB_BLOCK_SIZE; + block_size_y = SMB_BLOCK_SIZE; + k_end ++; + } + else + { + block_size_x = BLOCK_SIZE; + block_size_y = BLOCK_SIZE; + k_end += BLOCK_MULTIPLE; + } + + // Prepare mvs (needed for deblocking and mv prediction + if (currSlice->direct_spatial_mv_pred_flag) + { + for (k = k_start; k < k_start + BLOCK_MULTIPLE; k ++) + { + int i = (decode_block_scan[k] & 3); + int j = ((decode_block_scan[k] >> 2) & 3); + int i4 = currMB->block_x + i; + int j4 = currMB->block_y + j; + PicMotionParams *mv_info = &dec_picture->mv_info[j4][i4]; + + assert (pred_dir<=2); + + //===== DIRECT PREDICTION ===== + // motion information should be already set + if (mv_info->ref_idx[LIST_1] == -1) + { + pred_dir = 0; + } + else if (mv_info->ref_idx[LIST_0] == -1) + { + pred_dir = 1; + } + else + { + pred_dir = 2; + } + } + } + else + { + for (k = k_start; k < k_start + BLOCK_MULTIPLE; k ++) + { + + int i = (decode_block_scan[k] & 3); + int j = ((decode_block_scan[k] >> 2) & 3); + int i4 = currMB->block_x + i; + int j4 = currMB->block_y + j; + PicMotionParams *mv_info = &dec_picture->mv_info[j4][i4]; + + assert (pred_dir<=2); + + // store reference picture ID determined by direct mode + mv_info->ref_pic[LIST_0] = list0[(short)mv_info->ref_idx[LIST_0]]; + mv_info->ref_pic[LIST_1] = list1[(short)mv_info->ref_idx[LIST_1]]; + } + } + + for (k = k_start; k < k_end; k ++) + { + int i = (decode_block_scan[k] & 3); + int j = ((decode_block_scan[k] >> 2) & 3); + perform_mc(currMB, curr_plane, dec_picture, pred_dir, i, j, block_size_x, block_size_y); + } + } + } + + iTransform(currMB, curr_plane, 0); + if (currMB->cbp != 0) + currSlice->is_reset_coeff = FALSE; + return 1; +} + +/*! +************************************************************************ +* \brief +* Copy IPCM coefficients to decoded picture buffer and set parameters for this MB +* (for IPCM CABAC and IPCM CAVLC 28/11/2003) +* +* \author +* Dong Wang +************************************************************************ +*/ + +int mb_pred_ipcm(Macroblock *currMB) +{ + int i, j, k; + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + StorablePicture *dec_picture = currSlice->dec_picture; + + //Copy coefficients to decoded picture buffer + //IPCM coefficients are stored in currSlice->cof which is set in function read_IPCM_coeffs_from_NAL() + + for(i = 0; i < MB_BLOCK_SIZE; ++i) + { + for(j = 0;j < MB_BLOCK_SIZE ; ++j) + { + dec_picture->imgY[currMB->pix_y + i][currMB->pix_x + j] = (imgpel) currSlice->cof[0][i][j]; + } + } + + if ((dec_picture->chroma_format_idc != YUV400) && (p_Vid->separate_colour_plane_flag == 0)) + { + for (k = 0; k < 2; ++k) + { + for(i = 0; i < p_Vid->mb_cr_size_y; ++i) + { + for(j = 0;j < p_Vid->mb_cr_size_x; ++j) + { + dec_picture->imgUV[k][currMB->pix_c_y+i][currMB->pix_c_x + j] = (imgpel) currSlice->cof[k + 1][i][j]; + } + } + } + } + + // for deblocking filter + update_qp(currMB, 0); + + // for CAVLC: Set the nz_coeff to 16. + // These parameters are to be used in CAVLC decoding of neighbour blocks + memset(p_Vid->nz_coeff[currMB->mbAddrX][0][0], 16, 3 * BLOCK_PIXELS * sizeof(byte)); + + // for CABAC decoding of MB skip flag + currMB->skip_flag = 0; + + //for deblocking filter CABAC + currMB->s_cbp[0].blk = 0xFFFF; + + //For CABAC decoding of Dquant + currSlice->last_dquant = 0; + currSlice->is_reset_coeff = FALSE; + currSlice->is_reset_coeff_cr = FALSE; + return 1; +} + diff --git a/ldecod/src/mb_read.c b/ldecod/src/mb_read.c new file mode 100644 index 0000000..569159e --- /dev/null +++ b/ldecod/src/mb_read.c @@ -0,0 +1,2230 @@ +/*! + *********************************************************************** + * \file macroblock.c + * + * \brief + * Decode a Macroblock + * + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * - Inge Lille-Langøy + * - Rickard Sjoberg + * - Jani Lainema + * - Sebastian Purreiter + * - Thomas Wedi + * - Detlev Marpe + * - Gabi Blaettermann + * - Ye-Kui Wang + * - Lowell Winger + * - Alexis Michael Tourapis + *********************************************************************** +*/ + +#include "contributors.h" + +#include + +#include "block.h" +#include "global.h" +#include "mbuffer.h" +#include "mbuffer_mvc.h" +#include "elements.h" +//#include "errorconcealment.h" +#include "macroblock.h" +#include "fmo.h" +#include "cabac.h" +#include "vlc.h" +#include "image.h" +#include "mb_access.h" +#include "biaridecod.h" +#include "transform.h" +#include "mc_prediction.h" +#include "quant.h" +#include "mv_prediction.h" +#include "mb_prediction.h" +#include "fast_memory.h" +#include "filehandle.h" + +#if TRACE +#define TRACE_STRING(s) strncpy(currSE.tracestring, s, TRACESTRING_SIZE) +#define TRACE_DECBITS(i) dectracebitcnt(1) +#define TRACE_printf(s) sprintf(type, "%s", s); +#define TRACE_STRING_P(s) strncpy(currSE->tracestring, s, TRACESTRING_SIZE) +#else +#define TRACE_STRING(s) +#define TRACE_DECBITS(i) +#define TRACE_printf(s) +#define TRACE_STRING_P(s) +#endif + +extern void set_read_comp_coeff_cabac (Macroblock *currMB); +extern void set_read_comp_coeff_cavlc (Macroblock *currMB); + +static inline void update_pixel_pos8(PixelPos *pos_block, const PixelPos *pos_mb, int pos) +{ + *pos_block = *pos_mb; + if (pos_block->available) + { + if (pos == 1) + { + pos_block->pos_x += 2; + } + else if (pos == 2) + { + pos_block->pos_y += 2; + } + else if (pos == 3) + { + pos_block->pos_y += 2; + pos_block->pos_x += 2; + } + } +} + +/*! + ************************************************************************ + * \brief + * Read Intra 8x8 Prediction modes + * + ************************************************************************ + */ +static void read_ipred_8x8_modes_mbaff(Macroblock *currMB) +{ + int b8, bi, bj, bx, by, dec; + SyntaxElement currSE; + DataPartition *dP; + Slice *currSlice = currMB->p_Slice; + const byte *partMap = assignSE2partition[currSlice->dp_mode]; + VideoParameters *p_Vid = currMB->p_Vid; + + int mostProbableIntraPredMode; + int upIntraPredMode; + int leftIntraPredMode; + + PixelPos left_block, top_block; + + currSE.type = SE_INTRAPREDMODE; + + TRACE_STRING("intra4x4_pred_mode"); + dP = &(currSlice->partArr[partMap[SE_INTRAPREDMODE]]); + + if (!(p_Vid->active_pps->entropy_coding_mode_flag == (Boolean) CAVLC || dP->bitstream->ei_flag)) + currSE.reading = readIntraPredMode_CABAC; + + for(b8 = 0; b8 < 4; ++b8) //loop 8x8 blocks + { + by = (b8 & 0x02); + bj = currMB->block_y + by; + + bx = ((b8 & 0x01) << 1); + bi = currMB->block_x + bx; + //get from stream + if (p_Vid->active_pps->entropy_coding_mode_flag == (Boolean) CAVLC || dP->bitstream->ei_flag) + readSyntaxElement_Intra4x4PredictionMode(&currSE, dP->bitstream); + else + { + currSE.context = (b8 << 2); + dP->readSyntaxElement(currMB, &currSE, dP); + } + + get4x4Neighbour(currMB, (bx << 2) - 1, (by << 2), p_Vid->mb_size[IS_LUMA], &left_block); + get4x4Neighbour(currMB, (bx << 2), (by << 2) - 1, p_Vid->mb_size[IS_LUMA], &top_block ); + + //get from array and decode + + if (p_Vid->active_pps->constrained_intra_pred_flag) + { + left_block.available = left_block.available ? currSlice->intra_block[left_block.mb_addr] : 0; + top_block.available = top_block.available ? currSlice->intra_block[top_block.mb_addr] : 0; + } + + upIntraPredMode = (top_block.available ) ? currSlice->ipredmode[top_block.pos_y ][top_block.pos_x ] : -1; + leftIntraPredMode = (left_block.available) ? currSlice->ipredmode[left_block.pos_y][left_block.pos_x] : -1; + + mostProbableIntraPredMode = (upIntraPredMode < 0 || leftIntraPredMode < 0) ? DC_PRED : upIntraPredMode < leftIntraPredMode ? upIntraPredMode : leftIntraPredMode; + + dec = (currSE.value1 == -1) ? mostProbableIntraPredMode : currSE.value1 + (currSE.value1 >= mostProbableIntraPredMode); + + //set + //loop 4x4s in the subblock for 8x8 prediction setting + currSlice->ipredmode[bj ][bi ] = (byte) dec; + currSlice->ipredmode[bj ][bi + 1] = (byte) dec; + currSlice->ipredmode[bj + 1][bi ] = (byte) dec; + currSlice->ipredmode[bj + 1][bi + 1] = (byte) dec; + } +} + +/*! + ************************************************************************ + * \brief + * Read Intra 8x8 Prediction modes + * + ************************************************************************ + */ +static void read_ipred_8x8_modes(Macroblock *currMB) +{ + int b8, bi, bj, bx, by, dec; + SyntaxElement currSE; + DataPartition *dP; + Slice *currSlice = currMB->p_Slice; + const byte *partMap = assignSE2partition[currSlice->dp_mode]; + VideoParameters *p_Vid = currMB->p_Vid; + + int mostProbableIntraPredMode; + int upIntraPredMode; + int leftIntraPredMode; + + PixelPos left_mb, top_mb; + PixelPos left_block, top_block; + + currSE.type = SE_INTRAPREDMODE; + + TRACE_STRING("intra4x4_pred_mode"); + dP = &(currSlice->partArr[partMap[SE_INTRAPREDMODE]]); + + if (!(p_Vid->active_pps->entropy_coding_mode_flag == (Boolean) CAVLC || dP->bitstream->ei_flag)) + currSE.reading = readIntraPredMode_CABAC; + + get4x4Neighbour(currMB, -1, 0, p_Vid->mb_size[IS_LUMA], &left_mb); + get4x4Neighbour(currMB, 0, -1, p_Vid->mb_size[IS_LUMA], &top_mb ); + + for(b8 = 0; b8 < 4; ++b8) //loop 8x8 blocks + { + + + by = (b8 & 0x02); + bj = currMB->block_y + by; + + bx = ((b8 & 0x01) << 1); + bi = currMB->block_x + bx; + + //get from stream + if (p_Vid->active_pps->entropy_coding_mode_flag == (Boolean) CAVLC || dP->bitstream->ei_flag) + readSyntaxElement_Intra4x4PredictionMode(&currSE, dP->bitstream); + else + { + currSE.context = (b8 << 2); + dP->readSyntaxElement(currMB, &currSE, dP); + } + + get4x4Neighbour(currMB, (bx<<2) - 1, (by<<2), p_Vid->mb_size[IS_LUMA], &left_block); + get4x4Neighbour(currMB, (bx<<2), (by<<2) - 1, p_Vid->mb_size[IS_LUMA], &top_block ); + + //get from array and decode + + if (p_Vid->active_pps->constrained_intra_pred_flag) + { + left_block.available = left_block.available ? currSlice->intra_block[left_block.mb_addr] : 0; + top_block.available = top_block.available ? currSlice->intra_block[top_block.mb_addr] : 0; + } + + upIntraPredMode = (top_block.available ) ? currSlice->ipredmode[top_block.pos_y ][top_block.pos_x ] : -1; + leftIntraPredMode = (left_block.available) ? currSlice->ipredmode[left_block.pos_y][left_block.pos_x] : -1; + + mostProbableIntraPredMode = (upIntraPredMode < 0 || leftIntraPredMode < 0) ? DC_PRED : upIntraPredMode < leftIntraPredMode ? upIntraPredMode : leftIntraPredMode; + + dec = (currSE.value1 == -1) ? mostProbableIntraPredMode : currSE.value1 + (currSE.value1 >= mostProbableIntraPredMode); + + //set + //loop 4x4s in the subblock for 8x8 prediction setting + currSlice->ipredmode[bj ][bi ] = (byte) dec; + currSlice->ipredmode[bj ][bi + 1] = (byte) dec; + currSlice->ipredmode[bj + 1][bi ] = (byte) dec; + currSlice->ipredmode[bj + 1][bi + 1] = (byte) dec; + } +} + +/*! + ************************************************************************ + * \brief + * Read Intra 4x4 Prediction modes + * + ************************************************************************ + */ +static void read_ipred_4x4_modes_mbaff(Macroblock *currMB) +{ + int b8,i,j,bi,bj,bx,by; + SyntaxElement currSE; + DataPartition *dP; + Slice *currSlice = currMB->p_Slice; + const byte *partMap = assignSE2partition[currSlice->dp_mode]; + VideoParameters *p_Vid = currMB->p_Vid; + BlockPos *PicPos = p_Vid->PicPos; + + int ts, ls; + int mostProbableIntraPredMode; + int upIntraPredMode; + int leftIntraPredMode; + + PixelPos left_block, top_block; + + currSE.type = SE_INTRAPREDMODE; + + TRACE_STRING("intra4x4_pred_mode"); + dP = &(currSlice->partArr[partMap[SE_INTRAPREDMODE]]); + + if (!(p_Vid->active_pps->entropy_coding_mode_flag == (Boolean) CAVLC || dP->bitstream->ei_flag)) + currSE.reading = readIntraPredMode_CABAC; + + for(b8 = 0; b8 < 4; ++b8) //loop 8x8 blocks + { + for(j = 0; j < 2; j++) //loop subblocks + { + by = (b8 & 0x02) + j; + bj = currMB->block_y + by; + + for(i = 0; i < 2; i++) + { + bx = ((b8 & 1) << 1) + i; + bi = currMB->block_x + bx; + //get from stream + if (p_Vid->active_pps->entropy_coding_mode_flag == (Boolean) CAVLC || dP->bitstream->ei_flag) + readSyntaxElement_Intra4x4PredictionMode(&currSE, dP->bitstream); + else + { + currSE.context=(b8<<2) + (j<<1) +i; + dP->readSyntaxElement(currMB, &currSE, dP); + } + + get4x4Neighbour(currMB, (bx<<2) - 1, (by<<2), p_Vid->mb_size[IS_LUMA], &left_block); + get4x4Neighbour(currMB, (bx<<2), (by<<2) - 1, p_Vid->mb_size[IS_LUMA], &top_block ); + + //get from array and decode + + if (p_Vid->active_pps->constrained_intra_pred_flag) + { + left_block.available = left_block.available ? currSlice->intra_block[left_block.mb_addr] : 0; + top_block.available = top_block.available ? currSlice->intra_block[top_block.mb_addr] : 0; + } + + // !! KS: not sure if the following is still correct... + ts = ls = 0; // Check to see if the neighboring block is SI + if (currSlice->slice_type == SI_SLICE) // need support for MBINTLC1 + { + if (left_block.available) + if (currSlice->siblock [PicPos[left_block.mb_addr].y][PicPos[left_block.mb_addr].x]) + ls=1; + + if (top_block.available) + if (currSlice->siblock [PicPos[top_block.mb_addr].y][PicPos[top_block.mb_addr].x]) + ts=1; + } + + upIntraPredMode = (top_block.available &&(ts == 0)) ? currSlice->ipredmode[top_block.pos_y ][top_block.pos_x ] : -1; + leftIntraPredMode = (left_block.available &&(ls == 0)) ? currSlice->ipredmode[left_block.pos_y][left_block.pos_x] : -1; + + mostProbableIntraPredMode = (upIntraPredMode < 0 || leftIntraPredMode < 0) ? DC_PRED : upIntraPredMode < leftIntraPredMode ? upIntraPredMode : leftIntraPredMode; + + currSlice->ipredmode[bj][bi] = (byte) ((currSE.value1 == -1) ? mostProbableIntraPredMode : currSE.value1 + (currSE.value1 >= mostProbableIntraPredMode)); + } + } + } +} + + +/*! + ************************************************************************ + * \brief + * Read Intra 4x4 Prediction modes + * + ************************************************************************ + */ +static void read_ipred_4x4_modes(Macroblock *currMB) +{ + int b8,i,j,bi,bj,bx,by; + SyntaxElement currSE; + DataPartition *dP; + Slice *currSlice = currMB->p_Slice; + const byte *partMap = assignSE2partition[currSlice->dp_mode]; + VideoParameters *p_Vid = currMB->p_Vid; + BlockPos *PicPos = p_Vid->PicPos; + + int ts, ls; + int mostProbableIntraPredMode; + int upIntraPredMode; + int leftIntraPredMode; + + PixelPos left_mb, top_mb; + PixelPos left_block, top_block; + + currSE.type = SE_INTRAPREDMODE; + + TRACE_STRING("intra4x4_pred_mode"); + dP = &(currSlice->partArr[partMap[SE_INTRAPREDMODE]]); + + if (!(p_Vid->active_pps->entropy_coding_mode_flag == (Boolean) CAVLC || dP->bitstream->ei_flag)) + currSE.reading = readIntraPredMode_CABAC; + + get4x4Neighbour(currMB, -1, 0, p_Vid->mb_size[IS_LUMA], &left_mb); + get4x4Neighbour(currMB, 0, -1, p_Vid->mb_size[IS_LUMA], &top_mb ); + + for(b8 = 0; b8 < 4; ++b8) //loop 8x8 blocks + { + for(j = 0; j < 2; j++) //loop subblocks + { + by = (b8 & 0x02) + j; + bj = currMB->block_y + by; + + for(i = 0; i < 2; i++) + { + bx = ((b8 & 1) << 1) + i; + bi = currMB->block_x + bx; + //get from stream + if (p_Vid->active_pps->entropy_coding_mode_flag == (Boolean) CAVLC || dP->bitstream->ei_flag) + readSyntaxElement_Intra4x4PredictionMode(&currSE, dP->bitstream); + else + { + currSE.context=(b8<<2) + (j<<1) +i; + dP->readSyntaxElement(currMB, &currSE, dP); + } + + get4x4Neighbour(currMB, (bx<<2) - 1, (by<<2), p_Vid->mb_size[IS_LUMA], &left_block); + get4x4Neighbour(currMB, (bx<<2), (by<<2) - 1, p_Vid->mb_size[IS_LUMA], &top_block ); + + //get from array and decode + + if (p_Vid->active_pps->constrained_intra_pred_flag) + { + left_block.available = left_block.available ? currSlice->intra_block[left_block.mb_addr] : 0; + top_block.available = top_block.available ? currSlice->intra_block[top_block.mb_addr] : 0; + } + + // !! KS: not sure if the following is still correct... + ts = ls = 0; // Check to see if the neighboring block is SI + if (currSlice->slice_type == SI_SLICE) // need support for MBINTLC1 + { + if (left_block.available) + if (currSlice->siblock [PicPos[left_block.mb_addr].y][PicPos[left_block.mb_addr].x]) + ls=1; + + if (top_block.available) + if (currSlice->siblock [PicPos[top_block.mb_addr].y][PicPos[top_block.mb_addr].x]) + ts=1; + } + + upIntraPredMode = (top_block.available &&(ts == 0)) ? currSlice->ipredmode[top_block.pos_y ][top_block.pos_x ] : -1; + leftIntraPredMode = (left_block.available &&(ls == 0)) ? currSlice->ipredmode[left_block.pos_y][left_block.pos_x] : -1; + + mostProbableIntraPredMode = (upIntraPredMode < 0 || leftIntraPredMode < 0) ? DC_PRED : upIntraPredMode < leftIntraPredMode ? upIntraPredMode : leftIntraPredMode; + + currSlice->ipredmode[bj][bi] = (byte) ((currSE.value1 == -1) ? mostProbableIntraPredMode : currSE.value1 + (currSE.value1 >= mostProbableIntraPredMode)); + } + } + } +} + + +/*! + ************************************************************************ + * \brief + * Read Intra Prediction modes + * + ************************************************************************ + */ +static void read_ipred_modes(Macroblock *currMB) +{ + Slice *currSlice = currMB->p_Slice; + StorablePicture *dec_picture = currSlice->dec_picture; + + if (currSlice->mb_aff_frame_flag) + { + if (currMB->mb_type == I8MB) + read_ipred_8x8_modes_mbaff(currMB); + else if (currMB->mb_type == I4MB) + read_ipred_4x4_modes_mbaff(currMB); + } + else + { + if (currMB->mb_type == I8MB) + read_ipred_8x8_modes(currMB); + else if (currMB->mb_type == I4MB) + read_ipred_4x4_modes(currMB); + } + + if ((dec_picture->chroma_format_idc != YUV400) && (dec_picture->chroma_format_idc != YUV444)) + { + SyntaxElement currSE; + DataPartition *dP; + const byte *partMap = assignSE2partition[currSlice->dp_mode]; + VideoParameters *p_Vid = currMB->p_Vid; + + currSE.type = SE_INTRAPREDMODE; + TRACE_STRING("intra_chroma_pred_mode"); + dP = &(currSlice->partArr[partMap[SE_INTRAPREDMODE]]); + + if (p_Vid->active_pps->entropy_coding_mode_flag == (Boolean) CAVLC || dP->bitstream->ei_flag) + currSE.mapping = linfo_ue; + else + currSE.reading = readCIPredMode_CABAC; + + dP->readSyntaxElement(currMB, &currSE, dP); + currMB->c_ipred_mode = (char) currSE.value1; + + if (currMB->c_ipred_mode < DC_PRED_8 || currMB->c_ipred_mode > PLANE_8) + { + error("illegal chroma intra pred mode!\n", 600); + } + } +} + + +static inline void reset_mbs(Macroblock *currMB) +{ + currMB->slice_nr = -1; + currMB->ei_flag = 1; + currMB->dpl_flag = 0; +} + +static inline void reset_mv_info(PicMotionParams *mv_info, int slice_no) +{ + mv_info->ref_pic[LIST_0] = NULL; + mv_info->ref_pic[LIST_1] = NULL; + mv_info->mv[LIST_0] = zero_mv; + mv_info->mv[LIST_1] = zero_mv; + mv_info->ref_idx[LIST_0] = -1; + mv_info->ref_idx[LIST_1] = -1; + mv_info->slice_no = slice_no; +} + +static inline void reset_mv_info_list(PicMotionParams *mv_info, int list, int slice_no) +{ + mv_info->ref_pic[list] = NULL; + mv_info->mv[list] = zero_mv; + mv_info->ref_idx[list] = -1; + mv_info->slice_no = slice_no; +} + +/*! + ************************************************************************ + * \brief + * init macroblock for skip mode. Only L1 info needs to be reset + ************************************************************************ + */ +static void init_macroblock_basic(Macroblock *currMB) +{ + int j, i; + PicMotionParams **mv_info = &currMB->p_Slice->dec_picture->mv_info[currMB->block_y]; //&p_Vid->dec_picture->mv_info[currMB->block_y]; + int slice_no = currMB->p_Slice->current_slice_nr; + // reset vectors and pred. modes + for(j = 0; j < BLOCK_SIZE; ++j) + { + i = currMB->block_x; + reset_mv_info_list(*mv_info + (i++), LIST_1, slice_no); + reset_mv_info_list(*mv_info + (i++), LIST_1, slice_no); + reset_mv_info_list(*mv_info + (i++), LIST_1, slice_no); + reset_mv_info_list(*(mv_info++) + i, LIST_1, slice_no); + } +} + +/*! + ************************************************************************ + * \brief + * init macroblock (direct) + ************************************************************************ + */ +static void init_macroblock_direct(Macroblock *currMB) +{ + int slice_no = currMB->p_Slice->current_slice_nr; + PicMotionParams **mv_info = &currMB->p_Slice->dec_picture->mv_info[currMB->block_y]; + int i, j; + + set_read_comp_coeff_cabac(currMB); + set_read_comp_coeff_cavlc(currMB); + i = currMB->block_x; + for(j = 0; j < BLOCK_SIZE; ++j) + { + (*mv_info+i)->slice_no = slice_no; + (*mv_info+i+1)->slice_no = slice_no; + (*mv_info+i+2)->slice_no = slice_no; + (*(mv_info++)+i+3)->slice_no = slice_no; + } +} + + +/*! + ************************************************************************ + * \brief + * init macroblock + ************************************************************************ + */ +static void init_macroblock(Macroblock *currMB) +{ + int j, i; + Slice *currSlice = currMB->p_Slice; + PicMotionParams **mv_info = &currSlice->dec_picture->mv_info[currMB->block_y]; + int slice_no = currSlice->current_slice_nr; + // reset vectors and pred. modes + + for(j = 0; j < BLOCK_SIZE; ++j) + { + i = currMB->block_x; + reset_mv_info(*mv_info + (i++), slice_no); + reset_mv_info(*mv_info + (i++), slice_no); + reset_mv_info(*mv_info + (i++), slice_no); + reset_mv_info(*(mv_info++) + i, slice_no); + } + + set_read_comp_coeff_cabac(currMB); + set_read_comp_coeff_cavlc(currMB); +} + +static void concealIPCMcoeffs(Macroblock *currMB) +{ + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + StorablePicture *dec_picture = currSlice->dec_picture; + int i, j, k; + + for(i=0;icof[0][i][j] = p_Vid->dc_pred_value_comp[0]; + //currSlice->fcf[0][i][j] = p_Vid->dc_pred_value_comp[0]; + } + } + + if ((dec_picture->chroma_format_idc != YUV400) && (p_Vid->separate_colour_plane_flag == 0)) + { + for (k = 0; k < 2; ++k) + { + for(i=0;imb_cr_size_y;++i) + { + for(j=0;jmb_cr_size_x;++j) + { + currSlice->cof[k][i][j] = p_Vid->dc_pred_value_comp[k]; + //currSlice->fcf[k][i][j] = p_Vid->dc_pred_value_comp[k]; + } + } + } + } +} + +/*! + ************************************************************************ + * \brief + * Initialize decoding engine after decoding an IPCM macroblock + * (for IPCM CABAC 28/11/2003) + * + * \author + * Dong Wang + ************************************************************************ + */ +static void init_decoding_engine_IPCM(Slice *currSlice) +{ + Bitstream *currStream; + int ByteStartPosition; + int PartitionNumber; + int i; + + if(currSlice->dp_mode==PAR_DP_1) + PartitionNumber=1; + else if(currSlice->dp_mode==PAR_DP_3) + PartitionNumber=3; + else + { + printf("Partition Mode is not supported\n"); + exit(1); + } + + for(i=0;ipartArr[i].bitstream; + ByteStartPosition = currStream->read_len; + + arideco_start_decoding (&currSlice->partArr[i].de_cabac, currStream->streamBuffer, ByteStartPosition, &currStream->read_len); + } +} + +/*! + ************************************************************************ + * \brief + * Read IPCM pcm_alignment_zero_bit and pcm_byte[i] from stream to currSlice->cof + * (for IPCM CABAC and IPCM CAVLC) + * + * \author + * Dong Wang + ************************************************************************ + */ +static void read_IPCM_coeffs_from_NAL(Slice *currSlice, struct datapartition_dec *dP) +{ + VideoParameters *p_Vid = currSlice->p_Vid; + + StorablePicture *dec_picture = currSlice->dec_picture; + SyntaxElement currSE; + int i,j; + + //For CABAC, we don't need to read bits to let stream byte aligned + // because we have variable for integer bytes position + if(p_Vid->active_pps->entropy_coding_mode_flag == (Boolean) CABAC) + { + readIPCM_CABAC(currSlice, dP); + init_decoding_engine_IPCM(currSlice); + } + else + { + //read bits to let stream byte aligned + + if(((dP->bitstream->frame_bitoffset) & 0x07) != 0) + { + TRACE_STRING("pcm_alignment_zero_bit"); + currSE.len = (8 - ((dP->bitstream->frame_bitoffset) & 0x07)); + readSyntaxElement_FLC(&currSE, dP->bitstream); + } + + //read luma and chroma IPCM coefficients + currSE.len=p_Vid->bitdepth_luma; + TRACE_STRING("pcm_sample_luma"); + + for(i=0;ibitstream); + currSlice->cof[0][i][j] = currSE.value1; + //currSlice->fcf[0][i][j] = currSE.value1; + } + } + currSE.len=p_Vid->bitdepth_chroma; + if ((dec_picture->chroma_format_idc != YUV400) && (p_Vid->separate_colour_plane_flag == 0)) + { + TRACE_STRING("pcm_sample_chroma (u)"); + for(i=0;imb_cr_size_y;++i) + { + for(j=0;jmb_cr_size_x;++j) + { + readSyntaxElement_FLC(&currSE, dP->bitstream); + currSlice->cof[1][i][j] = currSE.value1; + //currSlice->fcf[1][i][j] = currSE.value1; + } + } + TRACE_STRING("pcm_sample_chroma (v)"); + for(i=0;imb_cr_size_y;++i) + { + for(j=0;jmb_cr_size_x;++j) + { + readSyntaxElement_FLC(&currSE, dP->bitstream); + currSlice->cof[2][i][j] = currSE.value1; + //currSlice->fcf[2][i][j] = currSE.value1; + } + } + } + } +} + +/*! + ************************************************************************ + * \brief + * Sets mode for 8x8 block + ************************************************************************ + */ +static inline void SetB8Mode (Macroblock* currMB, int value, int i) +{ + Slice* currSlice = currMB->p_Slice; + static const char p_v2b8 [ 5] = {4, 5, 6, 7, IBLOCK}; + static const char p_v2pd [ 5] = {0, 0, 0, 0, -1}; + static const char b_v2b8 [14] = {0, 4, 4, 4, 5, 6, 5, 6, 5, 6, 7, 7, 7, IBLOCK}; + static const char b_v2pd [14] = {2, 0, 1, 2, 0, 0, 1, 1, 2, 2, 0, 1, 2, -1}; + + if (currSlice->slice_type==B_SLICE) + { + currMB->b8mode[i] = b_v2b8[value]; + currMB->b8pdir[i] = b_v2pd[value]; + } + else + { + currMB->b8mode[i] = p_v2b8[value]; + currMB->b8pdir[i] = p_v2pd[value]; + } +} + +static inline void reset_coeffs(Macroblock *currMB) +{ + VideoParameters *p_Vid = currMB->p_Vid; + + // CAVLC + if (p_Vid->active_pps->entropy_coding_mode_flag == (Boolean) CAVLC) + fast_memset(p_Vid->nz_coeff[currMB->mbAddrX][0][0], 0, 3 * BLOCK_PIXELS * sizeof(byte)); +} + +static inline void field_flag_inference(Macroblock *currMB) +{ + VideoParameters *p_Vid = currMB->p_Vid; + if (currMB->mbAvailA) + { + currMB->mb_field = p_Vid->mb_data[currMB->mbAddrA].mb_field; + } + else + { + // check top macroblock pair + currMB->mb_field = currMB->mbAvailB ? p_Vid->mb_data[currMB->mbAddrB].mb_field : FALSE; + } +} + + +void skip_macroblock(Macroblock *currMB) +{ + MotionVector pred_mv; + int zeroMotionAbove; + int zeroMotionLeft; + PixelPos mb[4]; // neighbor blocks + int i, j; + int a_mv_y = 0; + int a_ref_idx = 0; + int b_mv_y = 0; + int b_ref_idx = 0; + int img_block_y = currMB->block_y; + VideoParameters *p_Vid = currMB->p_Vid; + Slice *currSlice = currMB->p_Slice; + int list_offset = LIST_0 + currMB->list_offset; + StorablePicture *dec_picture = currSlice->dec_picture; + MotionVector *a_mv = NULL; + MotionVector *b_mv = NULL; + + get_neighbors(currMB, mb, 0, 0, MB_BLOCK_SIZE); + if (currSlice->mb_aff_frame_flag == 0) + { + if (mb[0].available) + { + a_mv = &dec_picture->mv_info[mb[0].pos_y][mb[0].pos_x].mv[LIST_0]; + a_mv_y = a_mv->mv_y; + a_ref_idx = dec_picture->mv_info[mb[0].pos_y][mb[0].pos_x].ref_idx[LIST_0]; + } + + if (mb[1].available) + { + b_mv = &dec_picture->mv_info[mb[1].pos_y][mb[1].pos_x].mv[LIST_0]; + b_mv_y = b_mv->mv_y; + b_ref_idx = dec_picture->mv_info[mb[1].pos_y][mb[1].pos_x].ref_idx[LIST_0]; + } + } + else + { + if (mb[0].available) + { + a_mv = &dec_picture->mv_info[mb[0].pos_y][mb[0].pos_x].mv[LIST_0]; + a_mv_y = a_mv->mv_y; + a_ref_idx = dec_picture->mv_info[mb[0].pos_y][mb[0].pos_x].ref_idx[LIST_0]; + + if (currMB->mb_field && !p_Vid->mb_data[mb[0].mb_addr].mb_field) + { + a_mv_y /=2; + a_ref_idx *=2; + } + if (!currMB->mb_field && p_Vid->mb_data[mb[0].mb_addr].mb_field) + { + a_mv_y *=2; + a_ref_idx >>=1; + } + } + + if (mb[1].available) + { + b_mv = &dec_picture->mv_info[mb[1].pos_y][mb[1].pos_x].mv[LIST_0]; + b_mv_y = b_mv->mv_y; + b_ref_idx = dec_picture->mv_info[mb[1].pos_y][mb[1].pos_x].ref_idx[LIST_0]; + + if (currMB->mb_field && !p_Vid->mb_data[mb[1].mb_addr].mb_field) + { + b_mv_y /=2; + b_ref_idx *=2; + } + if (!currMB->mb_field && p_Vid->mb_data[mb[1].mb_addr].mb_field) + { + b_mv_y *=2; + b_ref_idx >>=1; + } + } + } + + zeroMotionLeft = !mb[0].available ? 1 : a_ref_idx==0 && a_mv->mv_x == 0 && a_mv_y==0 ? 1 : 0; + zeroMotionAbove = !mb[1].available ? 1 : b_ref_idx==0 && b_mv->mv_x == 0 && b_mv_y==0 ? 1 : 0; + + currMB->cbp = 0; + reset_coeffs(currMB); + + if (zeroMotionAbove || zeroMotionLeft) + { + PicMotionParams **dec_mv_info = &dec_picture->mv_info[img_block_y]; + StorablePicture *cur_pic = currSlice->listX[list_offset][0]; + PicMotionParams *mv_info = NULL; + + for(j = 0; j < BLOCK_SIZE; ++j) + { + for(i = currMB->block_x; i < currMB->block_x + BLOCK_SIZE; ++i) + { + mv_info = &dec_mv_info[j][i]; + mv_info->ref_pic[LIST_0] = cur_pic; + mv_info->mv [LIST_0] = zero_mv; + mv_info->ref_idx[LIST_0] = 0; + } + } + } + else + { + PicMotionParams **dec_mv_info = &dec_picture->mv_info[img_block_y]; + PicMotionParams *mv_info = NULL; + StorablePicture *cur_pic = currSlice->listX[list_offset][0]; + currMB->GetMVPredictor (currMB, mb, &pred_mv, 0, dec_picture->mv_info, LIST_0, 0, 0, MB_BLOCK_SIZE, MB_BLOCK_SIZE); + + // Set first block line (position img_block_y) + for(j = 0; j < BLOCK_SIZE; ++j) + { + for(i = currMB->block_x; i < currMB->block_x + BLOCK_SIZE; ++i) + { + mv_info = &dec_mv_info[j][i]; + mv_info->ref_pic[LIST_0] = cur_pic; + mv_info->mv [LIST_0] = pred_mv; + mv_info->ref_idx[LIST_0] = 0; + } + } + } +} +/*! + ************************************************************************ + * \brief + * read and set skip macroblock information + ************************************************************************ + */ +static void read_skip_macroblock(Macroblock *currMB) +{ + currMB->luma_transform_size_8x8_flag = FALSE; + + if(currMB->p_Vid->active_pps->constrained_intra_pred_flag) + { + int mb_nr = currMB->mbAddrX; + currMB->p_Slice->intra_block[mb_nr] = 0; + } + + //--- init macroblock data --- + init_macroblock_basic(currMB); + + skip_macroblock(currMB); +} + +/*! + ************************************************************************ + * \brief + * read and set intra (other than 4x4/8x8) mode macroblock information + ************************************************************************ + */ +static void read_intra_macroblock(Macroblock *currMB) +{ + //init NoMbPartLessThan8x8Flag + currMB->NoMbPartLessThan8x8Flag = TRUE; + + //============= Transform Size Flag for INTRA MBs ============= + //------------------------------------------------------------- + //transform size flag for INTRA_4x4 and INTRA_8x8 modes + currMB->luma_transform_size_8x8_flag = FALSE; + + //--- init macroblock data --- + init_macroblock(currMB); + + // intra prediction modes for a macroblock 4x4 ********************************************** + read_ipred_modes(currMB); + + // read CBP and Coeffs *************************************************************** + currMB->p_Slice->read_CBP_and_coeffs_from_NAL (currMB); +} + + +/*! + ************************************************************************ + * \brief + * read and set intra (4x4/8x8) mode macroblock information (CAVLC) + ************************************************************************ + */ +static void read_intra4x4_macroblock_cavlc(Macroblock *currMB, const byte *partMap) +{ + Slice *currSlice = currMB->p_Slice; + //============= Transform Size Flag for INTRA MBs ============= + //------------------------------------------------------------- + //transform size flag for INTRA_4x4 and INTRA_8x8 modes + if (currSlice->Transform8x8Mode) + { + SyntaxElement currSE; + DataPartition *dP = &(currSlice->partArr[partMap[SE_HEADER]]); + currSE.type = SE_HEADER; + TRACE_STRING("transform_size_8x8_flag"); + + // read CAVLC transform_size_8x8_flag + currSE.len = (int64) 1; + readSyntaxElement_FLC(&currSE, dP->bitstream); + + currMB->luma_transform_size_8x8_flag = (Boolean) currSE.value1; + + if (currMB->luma_transform_size_8x8_flag) + { + currMB->mb_type = I8MB; + memset(&currMB->b8mode, I8MB, 4 * sizeof(char)); + memset(&currMB->b8pdir, -1, 4 * sizeof(char)); + } + } + else + { + currMB->luma_transform_size_8x8_flag = FALSE; + } + + //--- init macroblock data --- + init_macroblock(currMB); + + // intra prediction modes for a macroblock 4x4 ********************************************** + read_ipred_modes(currMB); + + // read CBP and Coeffs *************************************************************** + currSlice->read_CBP_and_coeffs_from_NAL (currMB); +} + +/*! + ************************************************************************ + * \brief + * read and set intra (4x4/8x8) mode macroblock information (CAVLC) + ************************************************************************ + */ +static void read_intra4x4_macroblock_cabac(Macroblock *currMB, const byte *partMap) +{ + Slice *currSlice = currMB->p_Slice; + //============= Transform Size Flag for INTRA MBs ============= + //------------------------------------------------------------- + //transform size flag for INTRA_4x4 and INTRA_8x8 modes + if (currSlice->Transform8x8Mode) + { + SyntaxElement currSE; + DataPartition *dP = &(currSlice->partArr[partMap[SE_HEADER]]); + currSE.type = SE_HEADER; + currSE.reading = readMB_transform_size_flag_CABAC; + TRACE_STRING("transform_size_8x8_flag"); + + // read CAVLC transform_size_8x8_flag + if (dP->bitstream->ei_flag) + { + currSE.len = (int64) 1; + readSyntaxElement_FLC(&currSE, dP->bitstream); + } + else + { + dP->readSyntaxElement(currMB, &currSE, dP); + } + + currMB->luma_transform_size_8x8_flag = (Boolean) currSE.value1; + + if (currMB->luma_transform_size_8x8_flag) + { + currMB->mb_type = I8MB; + memset(&currMB->b8mode, I8MB, 4 * sizeof(char)); + memset(&currMB->b8pdir, -1, 4 * sizeof(char)); + } + } + else + { + currMB->luma_transform_size_8x8_flag = FALSE; + } + + //--- init macroblock data --- + init_macroblock(currMB); + + // intra prediction modes for a macroblock 4x4 ********************************************** + read_ipred_modes(currMB); + + // read CBP and Coeffs *************************************************************** + currSlice->read_CBP_and_coeffs_from_NAL (currMB); +} + + +/*! + ************************************************************************ + * \brief + * read and set generic (non skip/direct and P8x8) inter + * mode macroblock information + ************************************************************************ + */ +static void read_inter_macroblock(Macroblock *currMB) +{ + Slice *currSlice = currMB->p_Slice; + //init NoMbPartLessThan8x8Flag + currMB->NoMbPartLessThan8x8Flag = TRUE; + currMB->luma_transform_size_8x8_flag = FALSE; + + if(currMB->p_Vid->active_pps->constrained_intra_pred_flag) + { + int mb_nr = currMB->mbAddrX; + currSlice->intra_block[mb_nr] = 0; + } + + //--- init macroblock data --- + init_macroblock(currMB); + + // read inter frame vector data ********************************************************* + currSlice->read_motion_info_from_NAL (currMB); + // read CBP and Coeffs *************************************************************** + currSlice->read_CBP_and_coeffs_from_NAL (currMB); +} + +/*! + ************************************************************************ + * \brief + * read and set I_PCM mode macroblock information + ************************************************************************ + */ +static void read_i_pcm_macroblock(Macroblock *currMB, const byte *partMap) +{ + Slice *currSlice = currMB->p_Slice; + currMB->NoMbPartLessThan8x8Flag = TRUE; + currMB->luma_transform_size_8x8_flag = FALSE; + + //--- init macroblock data --- + init_macroblock(currMB); + + //read pcm_alignment_zero_bit and pcm_byte[i] + + // here dP is assigned with the same dP as SE_MBTYPE, because IPCM syntax is in the + // same category as MBTYPE + if ( currSlice->dp_mode && currSlice->dpB_NotPresent ) + { + concealIPCMcoeffs(currMB); + } + else + { + DataPartition *dP = &(currSlice->partArr[partMap[SE_LUM_DC_INTRA]]); + read_IPCM_coeffs_from_NAL(currSlice, dP); + } +} + +/*! + ************************************************************************ + * \brief + * read and set P8x8 mode macroblock information + ************************************************************************ + */ +static void read_P8x8_macroblock(Macroblock *currMB, DataPartition *dP, SyntaxElement *currSE) +{ + int i; + Slice *currSlice = currMB->p_Slice; + //====== READ 8x8 SUB-PARTITION MODES (modes of 8x8 blocks) and Intra VBST block modes ====== + currMB->NoMbPartLessThan8x8Flag = TRUE; + currMB->luma_transform_size_8x8_flag = FALSE; + + for (i = 0; i < 4; ++i) + { + TRACE_STRING_P("sub_mb_type"); + dP->readSyntaxElement (currMB, currSE, dP); + SetB8Mode (currMB, currSE->value1, i); + + //set NoMbPartLessThan8x8Flag for P8x8 mode + currMB->NoMbPartLessThan8x8Flag &= (currMB->b8mode[i] == 0 && currSlice->active_sps->direct_8x8_inference_flag) || + (currMB->b8mode[i] == 4); + } + + //--- init macroblock data --- + init_macroblock (currMB); + currSlice->read_motion_info_from_NAL (currMB); + + if(currMB->p_Vid->active_pps->constrained_intra_pred_flag) + { + int mb_nr = currMB->mbAddrX; + currSlice->intra_block[mb_nr] = 0; + } + + // read CBP and Coeffs *************************************************************** + currSlice->read_CBP_and_coeffs_from_NAL (currMB); +} + +/*! + ************************************************************************ + * \brief + * Get the syntax elements from the NAL + ************************************************************************ + */ +static void read_one_macroblock_i_slice_cavlc(Macroblock *currMB) +{ + Slice *currSlice = currMB->p_Slice; + + SyntaxElement currSE; + int mb_nr = currMB->mbAddrX; + + DataPartition *dP; + const byte *partMap = assignSE2partition[currSlice->dp_mode]; + StorablePicture *dec_picture = currSlice->dec_picture; + PicMotionParamsOld *motion = &dec_picture->motion; + + currMB->mb_field = ((mb_nr&0x01) == 0)? FALSE : currSlice->mb_data[mb_nr-1].mb_field; + + update_qp(currMB, currSlice->qp); + currSE.type = SE_MBTYPE; + + // read MB mode ***************************************************************** + dP = &(currSlice->partArr[partMap[SE_MBTYPE]]); + + currSE.mapping = linfo_ue; + + // read MB aff + if (currSlice->mb_aff_frame_flag && (mb_nr&0x01)==0) + { + TRACE_STRING("mb_field_decoding_flag"); + currSE.len = (int64) 1; + readSyntaxElement_FLC(&currSE, dP->bitstream); + currMB->mb_field = (Boolean) currSE.value1; + } + + // read MB type + TRACE_STRING("mb_type"); + dP->readSyntaxElement(currMB, &currSE, dP); + + currMB->mb_type = (short) currSE.value1; + if(!dP->bitstream->ei_flag) + currMB->ei_flag = 0; + + motion->mb_field[mb_nr] = (byte) currMB->mb_field; + + currMB->block_y_aff = ((currSlice->mb_aff_frame_flag) && (currMB->mb_field)) ? (mb_nr&0x01) ? (currMB->block_y - 4)>>1 : currMB->block_y >> 1 : currMB->block_y; + + currSlice->siblock[currMB->mb.y][currMB->mb.x] = 0; + + currSlice->interpret_mb_mode(currMB); + + //init NoMbPartLessThan8x8Flag + currMB->NoMbPartLessThan8x8Flag = TRUE; + + if(currMB->mb_type == IPCM) + { + read_i_pcm_macroblock(currMB, partMap); + } + else if (currMB->mb_type == I4MB) + { + read_intra4x4_macroblock_cavlc(currMB, partMap); + } + else // all other modes + { + read_intra_macroblock(currMB); + } + return; +} + +/*! + ************************************************************************ + * \brief + * Get the syntax elements from the NAL + ************************************************************************ + */ +static void read_one_macroblock_i_slice_cabac(Macroblock *currMB) +{ + Slice *currSlice = currMB->p_Slice; + + SyntaxElement currSE; + int mb_nr = currMB->mbAddrX; + + DataPartition *dP; + const byte *partMap = assignSE2partition[currSlice->dp_mode]; + StorablePicture *dec_picture = currSlice->dec_picture; + PicMotionParamsOld *motion = &dec_picture->motion; + + currMB->mb_field = ((mb_nr&0x01) == 0)? FALSE : currSlice->mb_data[mb_nr-1].mb_field; + + update_qp(currMB, currSlice->qp); + currSE.type = SE_MBTYPE; + + // read MB mode ***************************************************************** + dP = &(currSlice->partArr[partMap[SE_MBTYPE]]); + + if (dP->bitstream->ei_flag) + currSE.mapping = linfo_ue; + + // read MB aff + if (currSlice->mb_aff_frame_flag && (mb_nr&0x01)==0) + { + TRACE_STRING("mb_field_decoding_flag"); + if (dP->bitstream->ei_flag) + { + currSE.len = (int64) 1; + readSyntaxElement_FLC(&currSE, dP->bitstream); + } + else + { + currSE.reading = readFieldModeInfo_CABAC; + dP->readSyntaxElement(currMB, &currSE, dP); + } + currMB->mb_field = (Boolean) currSE.value1; + } + + CheckAvailabilityOfNeighborsCABAC(currMB); + + // read MB type + TRACE_STRING("mb_type"); + currSE.reading = readMB_typeInfo_CABAC_i_slice; + dP->readSyntaxElement(currMB, &currSE, dP); + + currMB->mb_type = (short) currSE.value1; + if(!dP->bitstream->ei_flag) + currMB->ei_flag = 0; + + motion->mb_field[mb_nr] = (byte) currMB->mb_field; + + currMB->block_y_aff = ((currSlice->mb_aff_frame_flag) && (currMB->mb_field)) ? (mb_nr&0x01) ? (currMB->block_y - 4)>>1 : currMB->block_y >> 1 : currMB->block_y; + + currSlice->siblock[currMB->mb.y][currMB->mb.x] = 0; + + currSlice->interpret_mb_mode(currMB); + + //init NoMbPartLessThan8x8Flag + currMB->NoMbPartLessThan8x8Flag = TRUE; + + if(currMB->mb_type == IPCM) + { + read_i_pcm_macroblock(currMB, partMap); + } + else if (currMB->mb_type == I4MB) + { + //============= Transform Size Flag for INTRA MBs ============= + //------------------------------------------------------------- + //transform size flag for INTRA_4x4 and INTRA_8x8 modes + if (currSlice->Transform8x8Mode) + { + currSE.type = SE_HEADER; + dP = &(currSlice->partArr[partMap[SE_HEADER]]); + currSE.reading = readMB_transform_size_flag_CABAC; + TRACE_STRING("transform_size_8x8_flag"); + + // read CAVLC transform_size_8x8_flag + if (dP->bitstream->ei_flag) + { + currSE.len = (int64) 1; + readSyntaxElement_FLC(&currSE, dP->bitstream); + } + else + { + dP->readSyntaxElement(currMB, &currSE, dP); + } + + currMB->luma_transform_size_8x8_flag = (Boolean) currSE.value1; + + if (currMB->luma_transform_size_8x8_flag) + { + currMB->mb_type = I8MB; + memset(&currMB->b8mode, I8MB, 4 * sizeof(char)); + memset(&currMB->b8pdir, -1, 4 * sizeof(char)); + } + } + else + { + currMB->luma_transform_size_8x8_flag = FALSE; + } + + //--- init macroblock data --- + init_macroblock(currMB); + + // intra prediction modes for a macroblock 4x4 ********************************************** + read_ipred_modes(currMB); + + // read CBP and Coeffs *************************************************************** + currSlice->read_CBP_and_coeffs_from_NAL (currMB); + } + else // all other modes + { + read_intra_macroblock(currMB); + } + return; +} + +/*! + ************************************************************************ + * \brief + * Get the syntax elements from the NAL + ************************************************************************ + */ +static void read_one_macroblock_p_slice_cavlc(Macroblock *currMB) +{ + Slice *currSlice = currMB->p_Slice; + SyntaxElement currSE; + int mb_nr = currMB->mbAddrX; + + DataPartition *dP; + const byte *partMap = assignSE2partition[currSlice->dp_mode]; + + if (currSlice->mb_aff_frame_flag == 0) + { + StorablePicture *dec_picture = currSlice->dec_picture; + PicMotionParamsOld *motion = &dec_picture->motion; + + currMB->mb_field = FALSE; + + update_qp(currMB, currSlice->qp); + currSE.type = SE_MBTYPE; + + // read MB mode ***************************************************************** + dP = &(currSlice->partArr[partMap[SE_MBTYPE]]); + + currSE.mapping = linfo_ue; + + // VLC Non-Intra + if(currSlice->cod_counter == -1) + { + TRACE_STRING("mb_skip_run"); + dP->readSyntaxElement(currMB, &currSE, dP); + currSlice->cod_counter = currSE.value1; + } + + if (currSlice->cod_counter==0) + { + // read MB type + TRACE_STRING("mb_type"); + dP->readSyntaxElement(currMB, &currSE, dP); + ++(currSE.value1); + currMB->mb_type = (short) currSE.value1; + if(!dP->bitstream->ei_flag) + currMB->ei_flag = 0; + currSlice->cod_counter--; + currMB->skip_flag = 0; + } + else + { + currSlice->cod_counter--; + currMB->mb_type = 0; + currMB->ei_flag = 0; + currMB->skip_flag = 1; + } + //update the list offset; + currMB->list_offset = 0; + + motion->mb_field[mb_nr] = (byte) FALSE; + + currMB->block_y_aff = currMB->block_y; + + currSlice->siblock[currMB->mb.y][currMB->mb.x] = 0; + + currSlice->interpret_mb_mode(currMB); + } + else + { + VideoParameters *p_Vid = currMB->p_Vid; + + Macroblock *topMB = NULL; + int prevMbSkipped = 0; + StorablePicture *dec_picture = currSlice->dec_picture; + PicMotionParamsOld *motion = &dec_picture->motion; + + if (mb_nr&0x01) + { + topMB= &p_Vid->mb_data[mb_nr-1]; + prevMbSkipped = (topMB->mb_type == 0); + } + else + prevMbSkipped = 0; + + currMB->mb_field = ((mb_nr&0x01) == 0)? FALSE : p_Vid->mb_data[mb_nr-1].mb_field; + + update_qp(currMB, currSlice->qp); + currSE.type = SE_MBTYPE; + + // read MB mode ***************************************************************** + dP = &(currSlice->partArr[partMap[SE_MBTYPE]]); + + currSE.mapping = linfo_ue; + + // VLC Non-Intra + if(currSlice->cod_counter == -1) + { + TRACE_STRING("mb_skip_run"); + dP->readSyntaxElement(currMB, &currSE, dP); + currSlice->cod_counter = currSE.value1; + } + + if (currSlice->cod_counter==0) + { + // read MB aff + if ((((mb_nr&0x01)==0) || ((mb_nr&0x01) && prevMbSkipped))) + { + TRACE_STRING("mb_field_decoding_flag"); + currSE.len = (int64) 1; + readSyntaxElement_FLC(&currSE, dP->bitstream); + currMB->mb_field = (Boolean) currSE.value1; + } + + // read MB type + TRACE_STRING("mb_type"); + dP->readSyntaxElement(currMB, &currSE, dP); + ++(currSE.value1); + currMB->mb_type = (short) currSE.value1; + if(!dP->bitstream->ei_flag) + currMB->ei_flag = 0; + currSlice->cod_counter--; + currMB->skip_flag = 0; + } + else + { + currSlice->cod_counter--; + currMB->mb_type = 0; + currMB->ei_flag = 0; + currMB->skip_flag = 1; + + // read field flag of bottom block + if(currSlice->cod_counter == 0 && ((mb_nr&0x01) == 0)) + { + TRACE_STRING("mb_field_decoding_flag (of coded bottom mb)"); + currSE.len = (int64) 1; + readSyntaxElement_FLC(&currSE, dP->bitstream); + dP->bitstream->frame_bitoffset--; + TRACE_DECBITS(1); + currMB->mb_field = (Boolean) currSE.value1; + } + else if (currSlice->cod_counter > 0 && ((mb_nr & 0x01) == 0)) + { + // check left macroblock pair first + if (mb_is_available(mb_nr - 2, currMB) && ((mb_nr % (p_Vid->PicWidthInMbs * 2))!=0)) + { + currMB->mb_field = p_Vid->mb_data[mb_nr-2].mb_field; + } + else + { + // check top macroblock pair + if (mb_is_available(mb_nr - 2*p_Vid->PicWidthInMbs, currMB)) + { + currMB->mb_field = p_Vid->mb_data[mb_nr-2*p_Vid->PicWidthInMbs].mb_field; + } + else + currMB->mb_field = FALSE; + } + } + } + //update the list offset; + currMB->list_offset = (currMB->mb_field)? ((mb_nr&0x01)? 4: 2): 0; + + motion->mb_field[mb_nr] = (byte) currMB->mb_field; + + currMB->block_y_aff = (currMB->mb_field) ? (mb_nr&0x01) ? (currMB->block_y - 4)>>1 : currMB->block_y >> 1 : currMB->block_y; + + currSlice->siblock[currMB->mb.y][currMB->mb.x] = 0; + + currSlice->interpret_mb_mode(currMB); + + if(currMB->mb_field) + { + currSlice->num_ref_idx_active[LIST_0] <<=1; + currSlice->num_ref_idx_active[LIST_1] <<=1; + } + } + //init NoMbPartLessThan8x8Flag + currMB->NoMbPartLessThan8x8Flag = TRUE; + + if (currMB->mb_type == IPCM) // I_PCM mode + { + read_i_pcm_macroblock(currMB, partMap); + } + else if (currMB->mb_type == I4MB) + { + read_intra4x4_macroblock_cavlc(currMB, partMap); + } + else if (currMB->mb_type == P8x8) + { + currSE.type = SE_MBTYPE; + currSE.mapping = linfo_ue; + dP = &(currSlice->partArr[partMap[SE_MBTYPE]]); + + read_P8x8_macroblock(currMB, dP, &currSE); + } + else if (currMB->mb_type == PSKIP) + { + read_skip_macroblock(currMB); + } + else if (currMB->is_intra_block) // all other intra modes + { + read_intra_macroblock(currMB); + } + else // all other remaining modes + { + read_inter_macroblock(currMB); + } + + + return; +} + +/*! + ************************************************************************ + * \brief + * Get the syntax elements from the NAL + ************************************************************************ + */ +static void read_one_macroblock_p_slice_cabac(Macroblock *currMB) +{ + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + int mb_nr = currMB->mbAddrX; + SyntaxElement currSE; + DataPartition *dP; + const byte *partMap = assignSE2partition[currSlice->dp_mode]; + + if (currSlice->mb_aff_frame_flag == 0) + { + StorablePicture *dec_picture = currSlice->dec_picture; + PicMotionParamsOld *motion = &dec_picture->motion; + + currMB->mb_field = FALSE; + + update_qp(currMB, currSlice->qp); + currSE.type = SE_MBTYPE; + + // read MB mode ***************************************************************** + dP = &(currSlice->partArr[partMap[SE_MBTYPE]]); + + if (dP->bitstream->ei_flag) + currSE.mapping = linfo_ue; + + CheckAvailabilityOfNeighborsCABAC(currMB); + TRACE_STRING("mb_skip_flag"); + currSE.reading = read_skip_flag_CABAC_p_slice; + dP->readSyntaxElement(currMB, &currSE, dP); + + currMB->mb_type = (short) currSE.value1; + currMB->skip_flag = (char) (!(currSE.value1)); + + if (!dP->bitstream->ei_flag) + currMB->ei_flag = 0; + + // read MB type + if (currMB->mb_type != 0 ) + { + currSE.reading = readMB_typeInfo_CABAC_p_slice; + TRACE_STRING("mb_type"); + dP->readSyntaxElement(currMB, &currSE, dP); + currMB->mb_type = (short) currSE.value1; + if(!dP->bitstream->ei_flag) + currMB->ei_flag = 0; + } + + motion->mb_field[mb_nr] = (byte) FALSE; + currMB->block_y_aff = currMB->block_y; + currSlice->siblock[currMB->mb.y][currMB->mb.x] = 0; + currSlice->interpret_mb_mode(currMB); + } + else + { + Macroblock *topMB = NULL; + int prevMbSkipped = 0; + int check_bottom, read_bottom, read_top; + StorablePicture *dec_picture = currSlice->dec_picture; + PicMotionParamsOld *motion = &dec_picture->motion; + if (mb_nr&0x01) + { + topMB= &p_Vid->mb_data[mb_nr-1]; + prevMbSkipped = (topMB->mb_type == 0); + } + else + prevMbSkipped = 0; + + currMB->mb_field = ((mb_nr&0x01) == 0)? FALSE : p_Vid->mb_data[mb_nr-1].mb_field; + + update_qp(currMB, currSlice->qp); + currSE.type = SE_MBTYPE; + + // read MB mode ***************************************************************** + dP = &(currSlice->partArr[partMap[SE_MBTYPE]]); + + if (dP->bitstream->ei_flag) + currSE.mapping = linfo_ue; + + // read MB skip_flag + if (((mb_nr&0x01) == 0||prevMbSkipped)) + field_flag_inference(currMB); + + CheckAvailabilityOfNeighborsCABAC(currMB); + TRACE_STRING("mb_skip_flag"); + currSE.reading = read_skip_flag_CABAC_p_slice; + dP->readSyntaxElement(currMB, &currSE, dP); + + currMB->mb_type = (short) currSE.value1; + currMB->skip_flag = (char) (!(currSE.value1)); + + if (!dP->bitstream->ei_flag) + currMB->ei_flag = 0; + + // read MB AFF + check_bottom=read_bottom=read_top=0; + if ((mb_nr&0x01)==0) + { + check_bottom = currMB->skip_flag; + read_top = !check_bottom; + } + else + { + read_bottom = (topMB->skip_flag && (!currMB->skip_flag)); + } + + if (read_bottom || read_top) + { + TRACE_STRING("mb_field_decoding_flag"); + currSE.reading = readFieldModeInfo_CABAC; + dP->readSyntaxElement(currMB, &currSE, dP); + currMB->mb_field = (Boolean) currSE.value1; + } + + if (check_bottom) + check_next_mb_and_get_field_mode_CABAC_p_slice(currSlice, &currSE, dP); + + //update the list offset; + currMB->list_offset = (currMB->mb_field)? ((mb_nr&0x01)? 4: 2): 0; + + //if (currMB->mb_type != 0 ) + CheckAvailabilityOfNeighborsCABAC(currMB); + + // read MB type + if (currMB->mb_type != 0 ) + { + currSE.reading = readMB_typeInfo_CABAC_p_slice; + TRACE_STRING("mb_type"); + dP->readSyntaxElement(currMB, &currSE, dP); + currMB->mb_type = (short) currSE.value1; + if(!dP->bitstream->ei_flag) + currMB->ei_flag = 0; + } + + motion->mb_field[mb_nr] = (byte) currMB->mb_field; + + currMB->block_y_aff = (currMB->mb_field) ? (mb_nr&0x01) ? (currMB->block_y - 4)>>1 : currMB->block_y >> 1 : currMB->block_y; + + currSlice->siblock[currMB->mb.y][currMB->mb.x] = 0; + + currSlice->interpret_mb_mode(currMB); + + if(currMB->mb_field) + { + currSlice->num_ref_idx_active[LIST_0] <<=1; + currSlice->num_ref_idx_active[LIST_1] <<=1; + } + } + //init NoMbPartLessThan8x8Flag + currMB->NoMbPartLessThan8x8Flag = TRUE; + + if (currMB->mb_type == IPCM) // I_PCM mode + { + read_i_pcm_macroblock(currMB, partMap); + } + else if (currMB->mb_type == I4MB) + { + read_intra4x4_macroblock_cabac(currMB, partMap); + } + else if (currMB->mb_type == P8x8) + { + dP = &(currSlice->partArr[partMap[SE_MBTYPE]]); + currSE.type = SE_MBTYPE; + + if (dP->bitstream->ei_flag) + currSE.mapping = linfo_ue; + else + currSE.reading = readB8_typeInfo_CABAC_p_slice; + + read_P8x8_macroblock(currMB, dP, &currSE); + } + else if (currMB->mb_type == PSKIP) + { + read_skip_macroblock (currMB); + } + else if (currMB->is_intra_block == TRUE) // all other intra modes + { + read_intra_macroblock(currMB); + } + else // all other remaining modes + { + read_inter_macroblock(currMB); + } + + return; +} + +/*! + ************************************************************************ + * \brief + * Get the syntax elements from the NAL + ************************************************************************ + */ +static void read_one_macroblock_b_slice_cavlc(Macroblock *currMB) +{ + VideoParameters *p_Vid = currMB->p_Vid; + Slice *currSlice = currMB->p_Slice; + int mb_nr = currMB->mbAddrX; + DataPartition *dP; + SyntaxElement currSE; + const byte *partMap = assignSE2partition[currSlice->dp_mode]; + + if (currSlice->mb_aff_frame_flag == 0) + { + StorablePicture *dec_picture = currSlice->dec_picture; + PicMotionParamsOld *motion = &dec_picture->motion; + + currMB->mb_field = FALSE; + + update_qp(currMB, currSlice->qp); + currSE.type = SE_MBTYPE; + + // read MB mode ***************************************************************** + dP = &(currSlice->partArr[partMap[SE_MBTYPE]]); + + currSE.mapping = linfo_ue; + + if(currSlice->cod_counter == -1) + { + TRACE_STRING("mb_skip_run"); + dP->readSyntaxElement(currMB, &currSE, dP); + currSlice->cod_counter = currSE.value1; + } + if (currSlice->cod_counter==0) + { + // read MB type + TRACE_STRING("mb_type"); + dP->readSyntaxElement(currMB, &currSE, dP); + currMB->mb_type = (short) currSE.value1; + if(!dP->bitstream->ei_flag) + currMB->ei_flag = 0; + currSlice->cod_counter--; + currMB->skip_flag = 0; + } + else + { + currSlice->cod_counter--; + currMB->mb_type = 0; + currMB->ei_flag = 0; + currMB->skip_flag = 1; + + } + //update the list offset; + currMB->list_offset = 0; + + motion->mb_field[mb_nr] = FALSE; + currMB->block_y_aff = currMB->block_y; + currSlice->siblock[currMB->mb.y][currMB->mb.x] = 0; + + currSlice->interpret_mb_mode(currMB); + } + else + { + Macroblock *topMB = NULL; + int prevMbSkipped = 0; + StorablePicture *dec_picture = currSlice->dec_picture; + PicMotionParamsOld *motion = &dec_picture->motion; + + if (mb_nr&0x01) + { + topMB= &p_Vid->mb_data[mb_nr-1]; + prevMbSkipped = topMB->skip_flag; + } + else + prevMbSkipped = 0; + + currMB->mb_field = ((mb_nr&0x01) == 0)? FALSE : p_Vid->mb_data[mb_nr-1].mb_field; + + update_qp(currMB, currSlice->qp); + currSE.type = SE_MBTYPE; + + // read MB mode ***************************************************************** + dP = &(currSlice->partArr[partMap[SE_MBTYPE]]); + + currSE.mapping = linfo_ue; + + if(currSlice->cod_counter == -1) + { + TRACE_STRING("mb_skip_run"); + dP->readSyntaxElement(currMB, &currSE, dP); + currSlice->cod_counter = currSE.value1; + } + if (currSlice->cod_counter==0) + { + // read MB aff + if ((((mb_nr&0x01)==0) || ((mb_nr&0x01) && prevMbSkipped))) + { + TRACE_STRING("mb_field_decoding_flag"); + currSE.len = (int64) 1; + readSyntaxElement_FLC(&currSE, dP->bitstream); + currMB->mb_field = (Boolean) currSE.value1; + } + + // read MB type + TRACE_STRING("mb_type"); + dP->readSyntaxElement(currMB, &currSE, dP); + currMB->mb_type = (short) currSE.value1; + if(!dP->bitstream->ei_flag) + currMB->ei_flag = 0; + currSlice->cod_counter--; + currMB->skip_flag = 0; + } + else + { + currSlice->cod_counter--; + currMB->mb_type = 0; + currMB->ei_flag = 0; + currMB->skip_flag = 1; + + // read field flag of bottom block + if(currSlice->cod_counter == 0 && ((mb_nr&0x01) == 0)) + { + TRACE_STRING("mb_field_decoding_flag (of coded bottom mb)"); + currSE.len = (int64) 1; + readSyntaxElement_FLC(&currSE, dP->bitstream); + dP->bitstream->frame_bitoffset--; + TRACE_DECBITS(1); + currMB->mb_field = (Boolean) currSE.value1; + } + else if (currSlice->cod_counter > 0 && ((mb_nr & 0x01) == 0)) + { + // check left macroblock pair first + if (mb_is_available(mb_nr - 2, currMB) && ((mb_nr % (p_Vid->PicWidthInMbs * 2))!=0)) + { + currMB->mb_field = p_Vid->mb_data[mb_nr-2].mb_field; + } + else + { + // check top macroblock pair + if (mb_is_available(mb_nr - 2*p_Vid->PicWidthInMbs, currMB)) + { + currMB->mb_field = p_Vid->mb_data[mb_nr-2*p_Vid->PicWidthInMbs].mb_field; + } + else + currMB->mb_field = FALSE; + } + } + } + //update the list offset; + currMB->list_offset = (currMB->mb_field)? ((mb_nr&0x01)? 4: 2): 0; + + motion->mb_field[mb_nr] = (byte) currMB->mb_field; + + currMB->block_y_aff = (currMB->mb_field) ? (mb_nr&0x01) ? (currMB->block_y - 4)>>1 : currMB->block_y >> 1 : currMB->block_y; + + currSlice->siblock[currMB->mb.y][currMB->mb.x] = 0; + + currSlice->interpret_mb_mode(currMB); + + if(currSlice->mb_aff_frame_flag) + { + if(currMB->mb_field) + { + currSlice->num_ref_idx_active[LIST_0] <<=1; + currSlice->num_ref_idx_active[LIST_1] <<=1; + } + } + } + + if (currMB->mb_type == IPCM) + { + read_i_pcm_macroblock(currMB, partMap); + } + else if (currMB->mb_type == I4MB) + { + read_intra4x4_macroblock_cavlc(currMB, partMap); + } + else if (currMB->mb_type == P8x8) + { + dP = &(currSlice->partArr[partMap[SE_MBTYPE]]); + currSE.type = SE_MBTYPE; + currSE.mapping = linfo_ue; + + read_P8x8_macroblock(currMB, dP, &currSE); + } + else if (currMB->mb_type == BSKIP_DIRECT) + { + //init NoMbPartLessThan8x8Flag + currMB->NoMbPartLessThan8x8Flag = (!(currSlice->active_sps->direct_8x8_inference_flag))? FALSE: TRUE; + + currMB->luma_transform_size_8x8_flag = FALSE; + + if(p_Vid->active_pps->constrained_intra_pred_flag) + { + currSlice->intra_block[mb_nr] = 0; + } + + //--- init macroblock data --- + init_macroblock_direct(currMB); + + if (currSlice->cod_counter >= 0) + { + currMB->cbp = 0; + reset_coeffs(currMB); + } + else + { + // read CBP and Coeffs *************************************************************** + currSlice->read_CBP_and_coeffs_from_NAL (currMB); + } + } + else if (currMB->is_intra_block == TRUE) // all other intra modes + { + read_intra_macroblock(currMB); + } + else // all other remaining modes + { + read_inter_macroblock(currMB); + } + + return; +} + +/*! + ************************************************************************ + * \brief + * Get the syntax elements from the NAL + ************************************************************************ + */ +static void read_one_macroblock_b_slice_cabac(Macroblock *currMB) +{ + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + int mb_nr = currMB->mbAddrX; + SyntaxElement currSE; + + DataPartition *dP; + const byte *partMap = assignSE2partition[currSlice->dp_mode]; + + if (currSlice->mb_aff_frame_flag == 0) + { + StorablePicture *dec_picture = currSlice->dec_picture; + PicMotionParamsOld *motion = &dec_picture->motion; + + currMB->mb_field = FALSE; + + update_qp(currMB, currSlice->qp); + currSE.type = SE_MBTYPE; + + // read MB mode ***************************************************************** + dP = &(currSlice->partArr[partMap[SE_MBTYPE]]); + + if (dP->bitstream->ei_flag) + currSE.mapping = linfo_ue; + + CheckAvailabilityOfNeighborsCABAC(currMB); + TRACE_STRING("mb_skip_flag"); + currSE.reading = read_skip_flag_CABAC_b_slice; + dP->readSyntaxElement(currMB, &currSE, dP); + + currMB->mb_type = (short) currSE.value1; + currMB->skip_flag = (char) (!(currSE.value1)); + + currMB->cbp = currSE.value2; + + if (!dP->bitstream->ei_flag) + currMB->ei_flag = 0; + + if (currSE.value1 == 0 && currSE.value2 == 0) + currSlice->cod_counter=0; + + // read MB type + if (currMB->mb_type != 0 ) + { + currSE.reading = readMB_typeInfo_CABAC_b_slice; + TRACE_STRING("mb_type"); + dP->readSyntaxElement(currMB, &currSE, dP); + currMB->mb_type = (short) currSE.value1; + if(!dP->bitstream->ei_flag) + currMB->ei_flag = 0; + } + + motion->mb_field[mb_nr] = (byte) FALSE; + currMB->block_y_aff = currMB->block_y; + currSlice->siblock[currMB->mb.y][currMB->mb.x] = 0; + currSlice->interpret_mb_mode(currMB); + } + else + { + Macroblock *topMB = NULL; + int prevMbSkipped = 0; + int check_bottom, read_bottom, read_top; + StorablePicture *dec_picture = currSlice->dec_picture; + PicMotionParamsOld *motion = &dec_picture->motion; + + if (mb_nr&0x01) + { + topMB= &p_Vid->mb_data[mb_nr-1]; + prevMbSkipped = topMB->skip_flag; + } + else + prevMbSkipped = 0; + + currMB->mb_field = ((mb_nr&0x01) == 0)? FALSE : p_Vid->mb_data[mb_nr-1].mb_field; + + update_qp(currMB, currSlice->qp); + currSE.type = SE_MBTYPE; + + // read MB mode ***************************************************************** + dP = &(currSlice->partArr[partMap[SE_MBTYPE]]); + + if (dP->bitstream->ei_flag) + currSE.mapping = linfo_ue; + + // read MB skip_flag + if (((mb_nr&0x01) == 0||prevMbSkipped)) + field_flag_inference(currMB); + + CheckAvailabilityOfNeighborsCABAC(currMB); + TRACE_STRING("mb_skip_flag"); + currSE.reading = read_skip_flag_CABAC_b_slice; + dP->readSyntaxElement(currMB, &currSE, dP); + + currMB->mb_type = (short) currSE.value1; + currMB->skip_flag = (char) (!(currSE.value1)); + + currMB->cbp = currSE.value2; + + if (!dP->bitstream->ei_flag) + currMB->ei_flag = 0; + + if (currSE.value1 == 0 && currSE.value2 == 0) + currSlice->cod_counter=0; + + // read MB AFF + check_bottom=read_bottom=read_top=0; + if ((mb_nr & 0x01) == 0) + { + check_bottom = currMB->skip_flag; + read_top = !check_bottom; + } + else + { + read_bottom = (topMB->skip_flag && (!currMB->skip_flag)); + } + + if (read_bottom || read_top) + { + TRACE_STRING("mb_field_decoding_flag"); + currSE.reading = readFieldModeInfo_CABAC; + dP->readSyntaxElement(currMB, &currSE, dP); + currMB->mb_field = (Boolean) currSE.value1; + } + if (check_bottom) + check_next_mb_and_get_field_mode_CABAC_b_slice(currSlice, &currSE, dP); + + //update the list offset; + currMB->list_offset = (currMB->mb_field)? ((mb_nr&0x01)? 4: 2): 0; + //if (currMB->mb_type != 0 ) + CheckAvailabilityOfNeighborsCABAC(currMB); + + // read MB type + if (currMB->mb_type != 0 ) + { + currSE.reading = readMB_typeInfo_CABAC_b_slice; + TRACE_STRING("mb_type"); + dP->readSyntaxElement(currMB, &currSE, dP); + currMB->mb_type = (short) currSE.value1; + if(!dP->bitstream->ei_flag) + currMB->ei_flag = 0; + } + + + motion->mb_field[mb_nr] = (byte) currMB->mb_field; + + currMB->block_y_aff = (currMB->mb_field) ? (mb_nr&0x01) ? (currMB->block_y - 4)>>1 : currMB->block_y >> 1 : currMB->block_y; + + currSlice->siblock[currMB->mb.y][currMB->mb.x] = 0; + + currSlice->interpret_mb_mode(currMB); + + if(currMB->mb_field) + { + currSlice->num_ref_idx_active[LIST_0] <<=1; + currSlice->num_ref_idx_active[LIST_1] <<=1; + } + } + + if(currMB->mb_type == IPCM) + { + read_i_pcm_macroblock(currMB, partMap); + } + else if (currMB->mb_type == I4MB) + { + read_intra4x4_macroblock_cabac(currMB, partMap); + } + else if (currMB->mb_type == P8x8) + { + dP = &(currSlice->partArr[partMap[SE_MBTYPE]]); + currSE.type = SE_MBTYPE; + + if (dP->bitstream->ei_flag) + currSE.mapping = linfo_ue; + else + currSE.reading = readB8_typeInfo_CABAC_b_slice; + + read_P8x8_macroblock(currMB, dP, &currSE); + } + else if (currMB->mb_type == BSKIP_DIRECT) + { + //init NoMbPartLessThan8x8Flag + currMB->NoMbPartLessThan8x8Flag = (!(currSlice->active_sps->direct_8x8_inference_flag))? FALSE: TRUE; + + //============= Transform Size Flag for INTRA MBs ============= + //------------------------------------------------------------- + //transform size flag for INTRA_4x4 and INTRA_8x8 modes + currMB->luma_transform_size_8x8_flag = FALSE; + + if(p_Vid->active_pps->constrained_intra_pred_flag) + { + currSlice->intra_block[mb_nr] = 0; + } + + //--- init macroblock data --- + init_macroblock_direct(currMB); + + if (currSlice->cod_counter >= 0) + { + currSlice->is_reset_coeff = TRUE; + currMB->cbp = 0; + currSlice->cod_counter = -1; + } + else + { + // read CBP and Coeffs *************************************************************** + currSlice->read_CBP_and_coeffs_from_NAL (currMB); + } + } + else if (currMB->is_intra_block == TRUE) // all other intra modes + { + read_intra_macroblock(currMB); + } + else // all other remaining modes + { + read_inter_macroblock(currMB); + } + + return; +} + + +void setup_read_macroblock(Slice *currSlice) +{ + if (currSlice->p_Vid->active_pps->entropy_coding_mode_flag == (Boolean) CABAC) + { + switch (currSlice->slice_type) + { + case P_SLICE: + case SP_SLICE: + currSlice->read_one_macroblock = read_one_macroblock_p_slice_cabac; + break; + case B_SLICE: + currSlice->read_one_macroblock = read_one_macroblock_b_slice_cabac; + break; + case I_SLICE: + case SI_SLICE: + currSlice->read_one_macroblock = read_one_macroblock_i_slice_cabac; + break; + default: + printf("Unsupported slice type\n"); + break; + } + } + else + { + switch (currSlice->slice_type) + { + case P_SLICE: + case SP_SLICE: + currSlice->read_one_macroblock = read_one_macroblock_p_slice_cavlc; + break; + case B_SLICE: + currSlice->read_one_macroblock = read_one_macroblock_b_slice_cavlc; + break; + case I_SLICE: + case SI_SLICE: + currSlice->read_one_macroblock = read_one_macroblock_i_slice_cavlc; + break; + default: + printf("Unsupported slice type\n"); + break; + } + } +} diff --git a/ldecod/src/mbuffer.c b/ldecod/src/mbuffer.c new file mode 100644 index 0000000..6aea307 --- /dev/null +++ b/ldecod/src/mbuffer.c @@ -0,0 +1,3512 @@ + +/*! +*********************************************************************** +* \file +* mbuffer.c +* +* \brief +* Frame buffer functions +* +* \author +* Main contributors (see contributors.h for copyright, address and affiliation details) +* - Karsten Suehring +* - Alexis Tourapis +* - Jill Boyce +* - Saurav K Bandyopadhyay +* - Zhenyu Wu +* - Yuwen He +*********************************************************************** +*/ + +#include + +#include "global.h" +#include "erc_api.h" +#include "header.h" +#include "image.h" +#include "mbuffer.h" +#include "mbuffer_common.h" +#include "memalloc.h" +#include "output.h" +#include "mbuffer_mvc.h" +#include "fast_memory.h" +#include "input.h" + +#include "ferc_api.h" + +static void insert_picture_in_dpb (VideoParameters *p_Vid, FrameStore* fs, StorablePicture* p); +static int output_one_frame_from_dpb (DecodedPictureBuffer *p_Dpb); +static void gen_field_ref_ids (VideoParameters *p_Vid, StorablePicture *p); + +#define MAX_LIST_SIZE 33 + + + +void dump_ref_lists(Slice *currSlice) +{ + VideoParameters *p_Vid = currSlice->p_Vid; + unsigned int i; +#if (MVC_EXTENSION_ENABLE) + // print out for debug purpose + if((p_Vid->profile_idc == MVC_HIGH || p_Vid->profile_idc == STEREO_HIGH) && currSlice->current_slice_nr==0) + { + if((currSlice->listXsize[0]>0) || (currSlice->listXsize[1]>0)) + printf("\n *** Dump Ref Lists *** CurPOC: %d | ViewID:%d ***\n", currSlice->ThisPOC, currSlice->view_id); + if(currSlice->listXsize[0]>0) + { + printf(" ** %s Ref Pic List 0 ** \n", currSlice->structure==FRAME ? "FRM":(currSlice->structure==TOP_FIELD ? "TOP":"BOT")); + for(i=0; i<(unsigned int)(currSlice->listXsize[0]); i++) //ref list 0 + { + printf(" %2d -> POC: %4d PicNum: %4d ViewID: %d", i, currSlice->listX[0][i]->poc, currSlice->listX[0][i]->pic_num, GetViewIdx(p_Vid, currSlice->listX[0][i]->view_id) ); + if( currSlice->view_id != currSlice->listX[0][i]->view_id ) + printf("i"); + printf("\n"); + } + } + if(currSlice->listXsize[1]>0) + { + printf(" ** %s Ref Pic List 1 **\n", currSlice->structure==FRAME ? "FRM":(currSlice->structure==TOP_FIELD ? "TOP":"BOT")); + for(i=0; i<(unsigned int)(currSlice->listXsize[1]); i++) //ref list 1 + { + printf(" %2d -> POC: %4d PicNum: %4d ViewID: %d", i, currSlice->listX[1][i]->poc, currSlice->listX[1][i]->pic_num, GetViewIdx(p_Vid, currSlice->listX[1][i]->view_id) ); + if( currSlice->view_id != currSlice->listX[1][i]->view_id ) + printf("i"); + printf("\n"); + } + } + } +#endif + fflush(stdout); +} + +/*! +************************************************************************ +* \brief +* Print out list of pictures in DPB. Used for debug purposes. +************************************************************************ +*/ +void dump_dpb(DecodedPictureBuffer *p_Dpb) +{ + unsigned i; + + for (i=0; iused_size;i++) + { + printf("("); + printf("fn=%d ", p_Dpb->fs[i]->frame_num); + if (p_Dpb->fs[i]->is_used & 1) + { + if (p_Dpb->fs[i]->top_field) + printf("T: poc=%d ", p_Dpb->fs[i]->top_field->poc); + else + printf("T: poc=%d ", p_Dpb->fs[i]->frame->top_poc); + } + if (p_Dpb->fs[i]->is_used & 2) + { + if (p_Dpb->fs[i]->bottom_field) + printf("B: poc=%d ", p_Dpb->fs[i]->bottom_field->poc); + else + printf("B: poc=%d ", p_Dpb->fs[i]->frame->bottom_poc); + } + if (p_Dpb->fs[i]->is_used == 3) + printf("F: poc=%d ", p_Dpb->fs[i]->frame->poc); + printf("G: poc=%d) ", p_Dpb->fs[i]->poc); + if (p_Dpb->fs[i]->is_reference) printf ("ref (%d) ", p_Dpb->fs[i]->is_reference); + if (p_Dpb->fs[i]->is_long_term) printf ("lt_ref (%d) ", p_Dpb->fs[i]->is_reference); + if (p_Dpb->fs[i]->is_output) printf ("out "); + if (p_Dpb->fs[i]->is_used == 3) + { + if (p_Dpb->fs[i]->frame->non_existing) printf ("ne "); + } +#if (MVC_EXTENSION_ENABLE) + if (p_Dpb->fs[i]->is_reference) + printf ("view_id (%d) ", p_Dpb->fs[i]->view_id); +#endif + printf ("\n"); + } +} + +/*! +************************************************************************ +* \brief +* Returns the size of the dpb depending on level and picture size +* +* +************************************************************************ +*/ +int getDpbSize(VideoParameters *p_Vid, seq_parameter_set_rbsp_t *active_sps) +{ + int pic_size = (active_sps->pic_width_in_mbs_minus1 + 1) * (active_sps->pic_height_in_map_units_minus1 + 1) * (active_sps->frame_mbs_only_flag?1:2) * 384; + + int size = 0; + + switch (active_sps->level_idc) + { + case 9: + size = 152064; + break; + case 10: + size = 152064; + break; + case 11: + if (!is_FREXT_profile(active_sps->profile_idc) && (active_sps->constrained_set3_flag == 1)) + size = 152064; + else + size = 345600; + break; + case 12: + size = 912384; + break; + case 13: + size = 912384; + break; + case 20: + size = 912384; + break; + case 21: + size = 1824768; + break; + case 22: + size = 3110400; + break; + case 30: + size = 3110400; + break; + case 31: + size = 6912000; + break; + case 32: + size = 7864320; + break; + case 40: + size = 12582912; + break; + case 41: + size = 12582912; + break; + case 42: + size = 13369344; + break; + case 50: + size = 42393600; + break; + case 51: + size = 70778880; + break; + default: + error ("undefined level", 500); + break; + } + + size /= pic_size; + if(p_Vid->profile_idc == MVC_HIGH || p_Vid->profile_idc == STEREO_HIGH) + { + int num_views = p_Vid->active_subset_sps->num_views_minus1+1; + size = imin(2*size, imax(1, RoundLog2(num_views))*16)/num_views; + } + else + size = imin( size, 16); + + if (active_sps->vui_parameters_present_flag && active_sps->vui_seq_parameters.bitstream_restriction_flag) + { + int size_vui; + if ((int)active_sps->vui_seq_parameters.max_dec_frame_buffering > size) + { + error ("max_dec_frame_buffering larger than MaxDpbSize", 500); + } + size_vui = imax (1, active_sps->vui_seq_parameters.max_dec_frame_buffering); +#ifdef _DEBUG + if(size_vui < size) + { + printf("Warning: max_dec_frame_buffering(%d) is less than DPB size(%d) calculated from Profile/Level.\n", size_vui, size); + } +#endif + size = size_vui; + } + + return size; +} + +/*! +************************************************************************ +* \brief +* Check then number of frames marked "used for reference" and break +* if maximum is exceeded +* +************************************************************************ +*/ +void check_num_ref(DecodedPictureBuffer *p_Dpb) +{ + if ((int)(p_Dpb->ltref_frames_in_buffer + p_Dpb->ref_frames_in_buffer ) > imax(1, p_Dpb->num_ref_frames)) + { + error ("Max. number of reference frames exceeded. Invalid stream.", 500); + } +} + + +/*! +************************************************************************ +* \brief +* Allocate memory for decoded picture buffer and initialize with sane values. +* +************************************************************************ +*/ +void init_dpb(VideoParameters *p_Vid, DecodedPictureBuffer *p_Dpb, int type) +{ + unsigned i; + seq_parameter_set_rbsp_t *active_sps = p_Vid->active_sps; + + p_Dpb->p_Vid = p_Vid; + if (p_Dpb->init_done) + { + free_dpb(p_Dpb); + } + + p_Dpb->size = getDpbSize(p_Vid, active_sps) + p_Vid->p_Inp->dpb_plus[type==2? 1: 0]; + p_Dpb->num_ref_frames = active_sps->num_ref_frames; + +#if (MVC_EXTENSION_ENABLE) + if ((unsigned int)active_sps->max_dec_frame_buffering < active_sps->num_ref_frames) +#else + if (p_Dpb->size < active_sps->num_ref_frames) +#endif + { + error ("DPB size at specified level is smaller than the specified number of reference frames. This is not allowed.\n", 1000); + } + + p_Dpb->used_size = 0; + p_Dpb->last_picture = NULL; + + p_Dpb->ref_frames_in_buffer = 0; + p_Dpb->ltref_frames_in_buffer = 0; + + p_Dpb->fs = calloc(p_Dpb->size, sizeof (FrameStore*)); + if (NULL==p_Dpb->fs) + no_mem_exit("init_dpb: p_Dpb->fs"); + + p_Dpb->fs_ref = calloc(p_Dpb->size, sizeof (FrameStore*)); + if (NULL==p_Dpb->fs_ref) + no_mem_exit("init_dpb: p_Dpb->fs_ref"); + + p_Dpb->fs_ltref = calloc(p_Dpb->size, sizeof (FrameStore*)); + if (NULL==p_Dpb->fs_ltref) + no_mem_exit("init_dpb: p_Dpb->fs_ltref"); + +#if (MVC_EXTENSION_ENABLE) + //! Edited by Joao Carreira + if( p_Vid->p_Inp->DecodeAllLayers && p_Vid->active_subset_sps) + p_Dpb->size_ilref = p_Vid->active_subset_sps->num_views_minus1; + else + p_Dpb->size_ilref = 1; + + p_Dpb->fs_ilref = calloc(p_Dpb->size_ilref, sizeof (FrameStore*)); + if (NULL==p_Dpb->fs_ilref) + no_mem_exit("init_dpb: p_Dpb->fs_ilref"); + +#endif + +for (i = 0; i < p_Dpb->size; i++) +{ + p_Dpb->fs[i] = alloc_frame_store(); + p_Dpb->fs_ref[i] = NULL; + p_Dpb->fs_ltref[i] = NULL; + p_Dpb->fs[i]->layer_id = MVC_INIT_VIEW_ID; +#if (MVC_EXTENSION_ENABLE) + p_Dpb->fs[i]->view_id = MVC_INIT_VIEW_ID; + p_Dpb->fs[i]->inter_view_flag[0] = p_Dpb->fs[i]->inter_view_flag[1] = 0; + p_Dpb->fs[i]->anchor_pic_flag[0] = p_Dpb->fs[i]->anchor_pic_flag[1] = 0; +#endif +} +#if (MVC_EXTENSION_ENABLE) +//! Edited by Joao Carreira +if (type == 2) +{ + for (i=0; iactive_subset_sps->num_views_minus1+1-1; i++) + { + p_Dpb->fs_ilref[i] = alloc_frame_store(); + // These may need some cleanups + p_Dpb->fs_ilref[i]->view_id = MVC_INIT_VIEW_ID; + p_Dpb->fs_ilref[i]->inter_view_flag[0] = p_Dpb->fs_ilref[i]->inter_view_flag[1] = 0; + p_Dpb->fs_ilref[i]->anchor_pic_flag[0] = p_Dpb->fs_ilref[i]->anchor_pic_flag[1] = 0; + // given that this is in a different buffer, do we even need proc_flag anymore? + } +} +else + p_Dpb->fs_ilref[0] = NULL; +#endif + +/* +for (i = 0; i < 6; i++) +{ +currSlice->listX[i] = calloc(MAX_LIST_SIZE, sizeof (StorablePicture*)); // +1 for reordering +if (NULL==currSlice->listX[i]) +no_mem_exit("init_dpb: currSlice->listX[i]"); +} +*/ +/* allocate a dummy storable picture */ +if(!p_Vid->no_reference_picture) +{ + p_Vid->no_reference_picture = alloc_storable_picture (p_Vid, FRAME, p_Vid->width, p_Vid->height, p_Vid->width_cr, p_Vid->height_cr, 1); + p_Vid->no_reference_picture->top_field = p_Vid->no_reference_picture; + p_Vid->no_reference_picture->bottom_field = p_Vid->no_reference_picture; + p_Vid->no_reference_picture->frame = p_Vid->no_reference_picture; +} +p_Dpb->last_output_poc = INT_MIN; + +#if (MVC_EXTENSION_ENABLE) +p_Dpb->last_output_view_id = -1; +#endif + +p_Vid->last_has_mmco_5 = 0; + +p_Dpb->init_done = 1; + +} + +void re_init_dpb(VideoParameters *p_Vid, DecodedPictureBuffer *p_Dpb, int type) +{ + int i; + seq_parameter_set_rbsp_t *active_sps = p_Vid->active_sps; + int iDpbSize; + + iDpbSize = getDpbSize(p_Vid, active_sps)+p_Vid->p_Inp->dpb_plus[type==2? 1: 0]; + p_Dpb->num_ref_frames = active_sps->num_ref_frames; + if( iDpbSize > (int)p_Dpb->size) + { +#if (MVC_EXTENSION_ENABLE) + if ((unsigned int)active_sps->max_dec_frame_buffering < active_sps->num_ref_frames) +#else + if (p_Dpb->size < active_sps->num_ref_frames) +#endif + { + error ("DPB size at specified level is smaller than the specified number of reference frames. This is not allowed.\n", 1000); + } + + p_Dpb->fs = realloc(p_Dpb->fs, iDpbSize * sizeof (FrameStore*)); + if (NULL==p_Dpb->fs) + no_mem_exit("re_init_dpb: p_Dpb->fs"); + + p_Dpb->fs_ref = realloc(p_Dpb->fs_ref, iDpbSize * sizeof (FrameStore*)); + if (NULL==p_Dpb->fs_ref) + no_mem_exit("re_init_dpb: p_Dpb->fs_ref"); + + p_Dpb->fs_ltref = realloc(p_Dpb->fs_ltref, iDpbSize * sizeof (FrameStore*)); + if (NULL==p_Dpb->fs_ltref) + no_mem_exit("re_init_dpb: p_Dpb->fs_ltref"); + +#if (MVC_EXTENSION_ENABLE) + if(!p_Dpb->fs_ilref) + { + //! Edited by Joao Carreira + p_Dpb->size_ilref = p_Vid->active_subset_sps->num_views_minus1; + p_Dpb->fs_ilref = calloc(p_Dpb->size_ilref, sizeof (FrameStore*)); + if (NULL==p_Dpb->fs_ilref) + no_mem_exit("init_dpb: p_Dpb->fs_ilref"); + } +#endif + + for (i = p_Dpb->size; i < iDpbSize; i++) + { + p_Dpb->fs[i] = alloc_frame_store(); + p_Dpb->fs_ref[i] = NULL; + p_Dpb->fs_ltref[i] = NULL; +#if (MVC_EXTENSION_ENABLE) + p_Dpb->fs[i]->view_id = MVC_INIT_VIEW_ID; + p_Dpb->fs[i]->inter_view_flag[0] = p_Dpb->fs[i]->inter_view_flag[1] = 0; + p_Dpb->fs[i]->anchor_pic_flag[0] = p_Dpb->fs[i]->anchor_pic_flag[1] = 0; +#endif + } + + +#if (MVC_EXTENSION_ENABLE) + //! Edited by Joao Carreira + if (type == 2) + { + for (i=0; iactive_subset_sps->num_views_minus1; i++) + { + if(!p_Dpb->fs_ilref[i]) + { + p_Dpb->fs_ilref[i] = alloc_frame_store(); + // These may need some cleanups + p_Dpb->fs_ilref[i]->view_id = MVC_INIT_VIEW_ID; + p_Dpb->fs_ilref[i]->inter_view_flag[0] = p_Dpb->fs_ilref[i]->inter_view_flag[1] = 0; + p_Dpb->fs_ilref[i]->anchor_pic_flag[0] = p_Dpb->fs_ilref[i]->anchor_pic_flag[1] = 0; + // given that this is in a different buffer, do we even need proc_flag anymore? + } + } + } + else + p_Dpb->fs_ilref[0] = NULL; +#endif + + p_Dpb->size = iDpbSize; + p_Dpb->last_output_poc = INT_MIN; +#if (MVC_EXTENSION_ENABLE) + p_Dpb->last_output_view_id = -1; +#endif + p_Dpb->init_done = 1; + + } +} + +/*! +************************************************************************ +* \brief +* Free memory for decoded picture buffer. +************************************************************************ +*/ +void free_dpb(DecodedPictureBuffer *p_Dpb) +{ + VideoParameters *p_Vid = p_Dpb->p_Vid; + unsigned i; + if (p_Dpb->fs) + { + for (i=0; isize; i++) + { + free_frame_store(p_Dpb->fs[i]); + } + free (p_Dpb->fs); + p_Dpb->fs=NULL; + } + + if (p_Dpb->fs_ref) + { + free (p_Dpb->fs_ref); + } + if (p_Dpb->fs_ltref) + { + free (p_Dpb->fs_ltref); + } + +#if (MVC_EXTENSION_ENABLE) + if (p_Dpb->fs_ilref) + { + for (i=0; isize_ilref; i++) + { + if( p_Dpb->fs_ilref[i] ) + free_frame_store(p_Dpb->fs_ilref[i]); + } + free (p_Dpb->fs_ilref); + p_Dpb->fs_ilref=NULL; + } + + p_Dpb->last_output_view_id = -1; +#endif + + p_Dpb->last_output_poc = INT_MIN; + + p_Dpb->init_done = 0; + + if(p_Vid->no_reference_picture) + { + free_storable_picture(p_Vid->no_reference_picture); + p_Vid->no_reference_picture = NULL; + } +} + + +/*! +************************************************************************ +* \brief +* Allocate memory for decoded picture buffer frame stores and initialize with sane values. +* +* \return +* the allocated FrameStore structure +************************************************************************ +*/ +FrameStore* alloc_frame_store(void) +{ + FrameStore *f; + + f = calloc (1, sizeof(FrameStore)); + if (NULL==f) + no_mem_exit("alloc_frame_store: f"); + + f->is_used = 0; + f->is_reference = 0; + f->is_long_term = 0; + f->is_orig_reference = 0; + + f->is_output = 0; + + f->frame = NULL;; + f->top_field = NULL; + f->bottom_field = NULL; + + return f; +} + +void alloc_pic_motion(PicMotionParamsOld *motion, int size_y, int size_x) +{ + motion->mb_field = calloc (size_y * size_x, sizeof(byte)); + if (motion->mb_field == NULL) + no_mem_exit("alloc_storable_picture: motion->mb_field"); +} + +/*! +************************************************************************ +* \brief +* Allocate memory for a stored picture. +* +* \param p_Vid +* VideoParameters +* \param structure +* picture structure +* \param size_x +* horizontal luma size +* \param size_y +* vertical luma size +* \param size_x_cr +* horizontal chroma size +* \param size_y_cr +* vertical chroma size +* +* \return +* the allocated StorablePicture structure +************************************************************************ +*/ +StorablePicture* alloc_storable_picture(VideoParameters *p_Vid, PictureStructure structure, int size_x, int size_y, int size_x_cr, int size_y_cr, int is_output) +{ + seq_parameter_set_rbsp_t *active_sps = p_Vid->active_sps; + + StorablePicture *s; + int nplane; + + //printf ("Allocating (%s) picture (x=%d, y=%d, x_cr=%d, y_cr=%d)\n", (type == FRAME)?"FRAME":(type == TOP_FIELD)?"TOP_FIELD":"BOTTOM_FIELD", size_x, size_y, size_x_cr, size_y_cr); + + s = (StorablePicture *) calloc (1, sizeof(StorablePicture)); + if (NULL==s) + no_mem_exit("alloc_storable_picture: s"); + + if (structure!=FRAME) + { + size_y /= 2; + size_y_cr /= 2; + } + + s->PicSizeInMbs = (size_x*size_y)/256; + s->imgUV = NULL; + + get_mem2Dpel_pad (&(s->imgY), size_y, size_x, p_Vid->iLumaPadY, p_Vid->iLumaPadX); + s->iLumaStride = size_x+2*p_Vid->iLumaPadX; + s->iLumaExpandedHeight = size_y+2*p_Vid->iLumaPadY; + + if (active_sps->chroma_format_idc != YUV400) + { + get_mem3Dpel_pad(&(s->imgUV), 2, size_y_cr, size_x_cr, p_Vid->iChromaPadY, p_Vid->iChromaPadX); + } + + s->iChromaStride =size_x_cr + 2*p_Vid->iChromaPadX; + s->iChromaExpandedHeight = size_y_cr + 2*p_Vid->iChromaPadY; + s->iLumaPadY = p_Vid->iLumaPadY; + s->iLumaPadX = p_Vid->iLumaPadX; + s->iChromaPadY = p_Vid->iChromaPadY; + s->iChromaPadX = p_Vid->iChromaPadX; + + s->separate_colour_plane_flag = p_Vid->separate_colour_plane_flag; + + get_mem2Dmp ( &s->mv_info, (size_y >> BLOCK_SHIFT), (size_x >> BLOCK_SHIFT)); + alloc_pic_motion( &s->motion , (size_y >> BLOCK_SHIFT), (size_x >> BLOCK_SHIFT)); + + if( (p_Vid->separate_colour_plane_flag != 0) ) + { + for( nplane=0; nplaneJVmv_info[nplane], (size_y >> BLOCK_SHIFT), (size_x >> BLOCK_SHIFT)); + alloc_pic_motion(&s->JVmotion[nplane] , (size_y >> BLOCK_SHIFT), (size_x >> BLOCK_SHIFT)); + } + } + + s->pic_num = 0; + s->frame_num = 0; + s->long_term_frame_idx = 0; + s->long_term_pic_num = 0; + s->used_for_reference = 0; + s->is_long_term = 0; + s->non_existing = 0; + s->is_output = 0; + s->max_slice_id = 0; +#if (MVC_EXTENSION_ENABLE) + s->view_id = -1; +#endif + + s->structure=structure; + + s->size_x = size_x; + s->size_y = size_y; + s->size_x_cr = size_x_cr; + s->size_y_cr = size_y_cr; + s->size_x_m1 = size_x - 1; + s->size_y_m1 = size_y - 1; + s->size_x_cr_m1 = size_x_cr - 1; + s->size_y_cr_m1 = size_y_cr - 1; + + s->top_field = p_Vid->no_reference_picture; + s->bottom_field = p_Vid->no_reference_picture; + s->frame = p_Vid->no_reference_picture; + + s->dec_ref_pic_marking_buffer = NULL; + + //! Edited by Joao Carreira + s->PicSlice = NULL; + s->have_concealed_reference = 0; + s->disparity = NULL; + + s->coded_frame = 0; + s->mb_aff_frame_flag = 0; + + s->top_poc = s->bottom_poc = s->poc = 0; + s->seiHasTone_mapping = 0; + + if(!p_Vid->active_sps->frame_mbs_only_flag && structure != FRAME) + { + int i, j; + for(j = 0; j < MAX_NUM_SLICES; j++) + { + for (i = 0; i < 2; i++) + { + s->listX[j][i] = (StorablePicture **) calloc(MAX_LIST_SIZE, sizeof (StorablePicture*)); // +1 for reordering + if (NULL==s->listX[j][i]) + no_mem_exit("alloc_storable_picture: s->listX[i]"); + } + } + } + + s->disparity = NULL; + + return s; +} + +/*! +************************************************************************ +* \brief +* Free frame store memory. +* +* \param p_Vid +* VideoParameters +* \param f +* FrameStore to be freed +* +************************************************************************ +*/ +void free_frame_store(FrameStore* f) +{ + if (f) + { + if (f->frame) + { + free_storable_picture(f->frame); + f->frame=NULL; + } + if (f->top_field) + { + free_storable_picture(f->top_field); + f->top_field=NULL; + } + if (f->bottom_field) + { + free_storable_picture(f->bottom_field); + f->bottom_field=NULL; + } + free(f); + } +} + +void free_pic_motion(PicMotionParamsOld *motion) +{ + if (motion->mb_field) + { + free(motion->mb_field); + motion->mb_field = NULL; + } +} + + +/*! +************************************************************************ +* \brief +* Free picture memory. +* +* \param p +* Picture to be freed +* +************************************************************************ +*/ +void free_storable_picture(StorablePicture* p) +{ + int nplane; + if (p) + { + /* Edited by Joao Carreira */ + if( p->disparity ) + { + free_storable_picture( p->disparity ); + } + + if (p->mv_info) + { + free_mem2Dmp(p->mv_info); + p->mv_info = NULL; + } + free_pic_motion(&p->motion); + + if( (p->separate_colour_plane_flag != 0) ) + { + for( nplane=0; nplaneJVmv_info[nplane]) + { + free_mem2Dmp(p->JVmv_info[nplane]); + p->JVmv_info[nplane] = NULL; + } + free_pic_motion(&p->JVmotion[nplane]); + } + } + + if (p->imgY) + { + free_mem2Dpel_pad(p->imgY, p->iLumaPadY, p->iLumaPadX); + p->imgY = NULL; + } + + if (p->imgUV) + { + free_mem3Dpel_pad(p->imgUV, 2, p->iChromaPadY, p->iChromaPadX); + p->imgUV=NULL; + } + + + if (p->seiHasTone_mapping) + free(p->tone_mapping_lut); + + { + int i, j; + for(j = 0; j < MAX_NUM_SLICES; j++) + { + for(i=0; i<2; i++) + { + if(p->listX[j][i]) + { + free(p->listX[j][i]); + p->listX[j][i] = NULL; + } + } + } + } + free(p); + p = NULL; + } +} + +/*! +************************************************************************ +* \brief +* mark FrameStore unused for reference +* +************************************************************************ +*/ +void unmark_for_reference(FrameStore* fs) +{ + if (fs->is_used & 1) + { + if (fs->top_field) + { + fs->top_field->used_for_reference = 0; + } + } + if (fs->is_used & 2) + { + if (fs->bottom_field) + { + fs->bottom_field->used_for_reference = 0; + } + } + if (fs->is_used == 3) + { + if (fs->top_field && fs->bottom_field) + { + fs->top_field->used_for_reference = 0; + fs->bottom_field->used_for_reference = 0; + } + fs->frame->used_for_reference = 0; + } + + fs->is_reference = 0; + + if(fs->frame) + { + free_pic_motion(&fs->frame->motion); + } + + if (fs->top_field) + { + free_pic_motion(&fs->top_field->motion); + } + + if (fs->bottom_field) + { + free_pic_motion(&fs->bottom_field->motion); + } +} + + +/*! +************************************************************************ +* \brief +* mark FrameStore unused for reference and reset long term flags +* +************************************************************************ +*/ +void unmark_for_long_term_reference(FrameStore* fs) +{ + if (fs->is_used & 1) + { + if (fs->top_field) + { + fs->top_field->used_for_reference = 0; + fs->top_field->is_long_term = 0; + } + } + if (fs->is_used & 2) + { + if (fs->bottom_field) + { + fs->bottom_field->used_for_reference = 0; + fs->bottom_field->is_long_term = 0; + } + } + if (fs->is_used == 3) + { + if (fs->top_field && fs->bottom_field) + { + fs->top_field->used_for_reference = 0; + fs->top_field->is_long_term = 0; + fs->bottom_field->used_for_reference = 0; + fs->bottom_field->is_long_term = 0; + } + fs->frame->used_for_reference = 0; + fs->frame->is_long_term = 0; + } + + fs->is_reference = 0; + fs->is_long_term = 0; +} + + + +void update_pic_num(Slice *currSlice) +{ + unsigned int i; + VideoParameters *p_Vid = currSlice->p_Vid; + DecodedPictureBuffer *p_Dpb = currSlice->p_Dpb; + seq_parameter_set_rbsp_t *active_sps = p_Vid->active_sps; + + int add_top = 0, add_bottom = 0; + int max_frame_num = 1 << (active_sps->log2_max_frame_num_minus4 + 4); + + if (currSlice->structure == FRAME) + { + for (i=0; iref_frames_in_buffer; i++) + { + if ( p_Dpb->fs_ref[i]->is_used==3 ) + { + if ((p_Dpb->fs_ref[i]->frame->used_for_reference)&&(!p_Dpb->fs_ref[i]->frame->is_long_term)) + { + if( p_Dpb->fs_ref[i]->frame_num > currSlice->frame_num ) + { + p_Dpb->fs_ref[i]->frame_num_wrap = p_Dpb->fs_ref[i]->frame_num - max_frame_num; + } + else + { + p_Dpb->fs_ref[i]->frame_num_wrap = p_Dpb->fs_ref[i]->frame_num; + } + p_Dpb->fs_ref[i]->frame->pic_num = p_Dpb->fs_ref[i]->frame_num_wrap; + } + } + } + // update long_term_pic_num + for (i = 0; i < p_Dpb->ltref_frames_in_buffer; i++) + { + if (p_Dpb->fs_ltref[i]->is_used==3) + { + if (p_Dpb->fs_ltref[i]->frame->is_long_term) + { + p_Dpb->fs_ltref[i]->frame->long_term_pic_num = p_Dpb->fs_ltref[i]->frame->long_term_frame_idx; + } + } + } + } + else + { + if (currSlice->structure == TOP_FIELD) + { + add_top = 1; + add_bottom = 0; + } + else + { + add_top = 0; + add_bottom = 1; + } + + for (i=0; iref_frames_in_buffer; i++) + { + if (p_Dpb->fs_ref[i]->is_reference) + { + if( p_Dpb->fs_ref[i]->frame_num > currSlice->frame_num ) + { + p_Dpb->fs_ref[i]->frame_num_wrap = p_Dpb->fs_ref[i]->frame_num - max_frame_num; + } + else + { + p_Dpb->fs_ref[i]->frame_num_wrap = p_Dpb->fs_ref[i]->frame_num; + } + if (p_Dpb->fs_ref[i]->is_reference & 1) + { + p_Dpb->fs_ref[i]->top_field->pic_num = (2 * p_Dpb->fs_ref[i]->frame_num_wrap) + add_top; + } + if (p_Dpb->fs_ref[i]->is_reference & 2) + { + p_Dpb->fs_ref[i]->bottom_field->pic_num = (2 * p_Dpb->fs_ref[i]->frame_num_wrap) + add_bottom; + } + } + } + // update long_term_pic_num + for (i=0; iltref_frames_in_buffer; i++) + { + if (p_Dpb->fs_ltref[i]->is_long_term & 1) + { + p_Dpb->fs_ltref[i]->top_field->long_term_pic_num = 2 * p_Dpb->fs_ltref[i]->top_field->long_term_frame_idx + add_top; + } + if (p_Dpb->fs_ltref[i]->is_long_term & 2) + { + p_Dpb->fs_ltref[i]->bottom_field->long_term_pic_num = 2 * p_Dpb->fs_ltref[i]->bottom_field->long_term_frame_idx + add_bottom; + } + } + } +} +/*! +************************************************************************ +* \brief +* Initialize reference lists depending on current slice type +* +************************************************************************ +*/ +void init_lists_i_slice(Slice *currSlice) +{ + +#if (MVC_EXTENSION_ENABLE) + currSlice->listinterviewidx0 = 0; + currSlice->listinterviewidx1 = 0; +#endif + + currSlice->listXsize[0] = 0; + currSlice->listXsize[1] = 0; +} + +/*! +************************************************************************ +* \brief +* Initialize reference lists for a P Slice +* +************************************************************************ +*/ +void init_lists_p_slice(Slice *currSlice) +{ + VideoParameters *p_Vid = currSlice->p_Vid; + DecodedPictureBuffer *p_Dpb = currSlice->p_Dpb; + + unsigned int i; + + int list0idx = 0; + int listltidx = 0; + + FrameStore **fs_list0; + FrameStore **fs_listlt; + +#if (MVC_EXTENSION_ENABLE) + currSlice->listinterviewidx0 = 0; + currSlice->listinterviewidx1 = 0; +#endif + + if (currSlice->structure == FRAME) + { + for (i=0; iref_frames_in_buffer; i++) + { + if (p_Dpb->fs_ref[i]->is_used==3) + { + if ((p_Dpb->fs_ref[i]->frame->used_for_reference) && (!p_Dpb->fs_ref[i]->frame->is_long_term)) + { + currSlice->listX[0][list0idx++] = p_Dpb->fs_ref[i]->frame; + } + } + } + // order list 0 by PicNum + qsort((void *)currSlice->listX[0], list0idx, sizeof(StorablePicture*), compare_pic_by_pic_num_desc); + currSlice->listXsize[0] = (char) list0idx; + //printf("listX[0] (PicNum): "); for (i=0; ilistX[0][i]->pic_num);} printf("\n"); + + // long term handling + for (i=0; iltref_frames_in_buffer; i++) + { + if (p_Dpb->fs_ltref[i]->is_used==3) + { + if (p_Dpb->fs_ltref[i]->frame->is_long_term) + { + currSlice->listX[0][list0idx++] = p_Dpb->fs_ltref[i]->frame; + } + } + } + qsort((void *)&currSlice->listX[0][(short) currSlice->listXsize[0]], list0idx - currSlice->listXsize[0], sizeof(StorablePicture*), compare_pic_by_lt_pic_num_asc); + currSlice->listXsize[0] = (char) list0idx; + } + else + { + fs_list0 = calloc(p_Dpb->size, sizeof (FrameStore*)); + if (NULL==fs_list0) + no_mem_exit("init_lists: fs_list0"); + fs_listlt = calloc(p_Dpb->size, sizeof (FrameStore*)); + if (NULL==fs_listlt) + no_mem_exit("init_lists: fs_listlt"); + + for (i=0; iref_frames_in_buffer; i++) + { + if (p_Dpb->fs_ref[i]->is_reference) + { + fs_list0[list0idx++] = p_Dpb->fs_ref[i]; + } + } + + qsort((void *)fs_list0, list0idx, sizeof(FrameStore*), compare_fs_by_frame_num_desc); + + //printf("fs_list0 (FrameNum): "); for (i=0; iframe_num_wrap);} printf("\n"); + + currSlice->listXsize[0] = 0; + gen_pic_list_from_frame_list(currSlice->structure, fs_list0, list0idx, currSlice->listX[0], &currSlice->listXsize[0], 0); + + //printf("listX[0] (PicNum): "); for (i=0; i < currSlice->listXsize[0]; i++){printf ("%d ", currSlice->listX[0][i]->pic_num);} printf("\n"); + + // long term handling + for (i=0; iltref_frames_in_buffer; i++) + { + fs_listlt[listltidx++]=p_Dpb->fs_ltref[i]; + } + + qsort((void *)fs_listlt, listltidx, sizeof(FrameStore*), compare_fs_by_lt_pic_idx_asc); + + gen_pic_list_from_frame_list(currSlice->structure, fs_listlt, listltidx, currSlice->listX[0], &currSlice->listXsize[0], 1); + + free(fs_list0); + free(fs_listlt); + } + currSlice->listXsize[1] = 0; + + + // set max size + currSlice->listXsize[0] = (char) imin (currSlice->listXsize[0], currSlice->num_ref_idx_active[LIST_0]); + currSlice->listXsize[1] = (char) imin (currSlice->listXsize[1], currSlice->num_ref_idx_active[LIST_1]); + + // set the unused list entries to NULL + for (i=currSlice->listXsize[0]; i< (MAX_LIST_SIZE) ; i++) + { + currSlice->listX[0][i] = p_Vid->no_reference_picture; + } + for (i=currSlice->listXsize[1]; i< (MAX_LIST_SIZE) ; i++) + { + currSlice->listX[1][i] = p_Vid->no_reference_picture; + } + +#if PRINTREFLIST +#if (MVC_EXTENSION_ENABLE) + // print out for debug purpose + if((p_Vid->profile_idc == MVC_HIGH || p_Vid->profile_idc == STEREO_HIGH) && currSlice->current_slice_nr==0) + { + if(currSlice->listXsize[0]>0) + { + printf("\n"); + printf(" ** (CurViewID:%d %d) %s Ref Pic List 0 ****\n", currSlice->view_id, currSlice->ThisPOC, currSlice->structure==FRAME ? "FRM":(currSlice->structure==TOP_FIELD ? "TOP":"BOT")); + for(i=0; i<(unsigned int)(currSlice->listXsize[0]); i++) //ref list 0 + { + printf(" %2d -> POC: %4d PicNum: %4d ViewID: %d\n", i, currSlice->listX[0][i]->poc, currSlice->listX[0][i]->pic_num, currSlice->listX[0][i]->view_id); + } + } + } +#endif +#endif +} + + +/*! +************************************************************************ +* \brief +* Initialize reference lists for a B Slice +* +************************************************************************ +*/ +void init_lists_b_slice(Slice *currSlice) +{ + VideoParameters *p_Vid = currSlice->p_Vid; + DecodedPictureBuffer *p_Dpb = currSlice->p_Dpb; + + unsigned int i; + int j; + + int list0idx = 0; + int list0idx_1 = 0; + int listltidx = 0; + + FrameStore **fs_list0; + FrameStore **fs_list1; + FrameStore **fs_listlt; + +#if (MVC_EXTENSION_ENABLE) + currSlice->listinterviewidx0 = 0; + currSlice->listinterviewidx1 = 0; +#endif + + { + // B-Slice + if (currSlice->structure == FRAME) + { + for (i = 0; i < p_Dpb->ref_frames_in_buffer; i++) + { + if (p_Dpb->fs_ref[i]->is_used==3) + { + if ((p_Dpb->fs_ref[i]->frame->used_for_reference)&&(!p_Dpb->fs_ref[i]->frame->is_long_term)) + { + if (currSlice->framepoc >= p_Dpb->fs_ref[i]->frame->poc) //!KS use >= for error concealment + { + currSlice->listX[0][list0idx++] = p_Dpb->fs_ref[i]->frame; + } + } + } + } + qsort((void *)currSlice->listX[0], list0idx, sizeof(StorablePicture*), compare_pic_by_poc_desc); + + //get the backward reference picture (POC>current POC) in list0; + list0idx_1 = list0idx; + for (i=0; iref_frames_in_buffer; i++) + { + if (p_Dpb->fs_ref[i]->is_used==3) + { + if ((p_Dpb->fs_ref[i]->frame->used_for_reference)&&(!p_Dpb->fs_ref[i]->frame->is_long_term)) + { + if (currSlice->framepoc < p_Dpb->fs_ref[i]->frame->poc) + { + currSlice->listX[0][list0idx++] = p_Dpb->fs_ref[i]->frame; + } + } + } + } + qsort((void *)&currSlice->listX[0][list0idx_1], list0idx-list0idx_1, sizeof(StorablePicture*), compare_pic_by_poc_asc); + + for (j=0; jlistX[1][list0idx-list0idx_1+j]=currSlice->listX[0][j]; + } + for (j=list0idx_1; jlistX[1][j-list0idx_1]=currSlice->listX[0][j]; + } + + currSlice->listXsize[0] = currSlice->listXsize[1] = (char) list0idx; + + //printf("listX[0] (PicNum): "); for (i=0; ilistXsize[0]; i++){printf ("%d ", currSlice->listX[0][i]->pic_num);} printf("\n"); + //printf("listX[1] (PicNum): "); for (i=0; ilistXsize[1]; i++){printf ("%d ", currSlice->listX[1][i]->pic_num);} printf("\n"); + //printf("currSlice->listX[0] currPoc=%d (Poc): ", p_Vid->framepoc); for (i=0; ilistXsize[0]; i++){printf ("%d ", currSlice->listX[0][i]->poc);} printf("\n"); + //printf("currSlice->listX[1] currPoc=%d (Poc): ", p_Vid->framepoc); for (i=0; ilistXsize[1]; i++){printf ("%d ", currSlice->listX[1][i]->poc);} printf("\n"); + + // long term handling + for (i=0; iltref_frames_in_buffer; i++) + { + if (p_Dpb->fs_ltref[i]->is_used==3) + { + if (p_Dpb->fs_ltref[i]->frame->is_long_term) + { + currSlice->listX[0][list0idx] = p_Dpb->fs_ltref[i]->frame; + currSlice->listX[1][list0idx++] = p_Dpb->fs_ltref[i]->frame; + } + } + } + qsort((void *)&currSlice->listX[0][(short) currSlice->listXsize[0]], list0idx - currSlice->listXsize[0], sizeof(StorablePicture*), compare_pic_by_lt_pic_num_asc); + qsort((void *)&currSlice->listX[1][(short) currSlice->listXsize[0]], list0idx - currSlice->listXsize[0], sizeof(StorablePicture*), compare_pic_by_lt_pic_num_asc); + currSlice->listXsize[0] = currSlice->listXsize[1] = (char) list0idx; + } + else + { + fs_list0 = calloc(p_Dpb->size, sizeof (FrameStore*)); + if (NULL==fs_list0) + no_mem_exit("init_lists: fs_list0"); + fs_list1 = calloc(p_Dpb->size, sizeof (FrameStore*)); + if (NULL==fs_list1) + no_mem_exit("init_lists: fs_list1"); + fs_listlt = calloc(p_Dpb->size, sizeof (FrameStore*)); + if (NULL==fs_listlt) + no_mem_exit("init_lists: fs_listlt"); + + currSlice->listXsize[0] = 0; + currSlice->listXsize[1] = 1; + + for (i=0; iref_frames_in_buffer; i++) + { + if (p_Dpb->fs_ref[i]->is_used) + { + if (currSlice->ThisPOC >= p_Dpb->fs_ref[i]->poc) + { + fs_list0[list0idx++] = p_Dpb->fs_ref[i]; + } + } + } + qsort((void *)fs_list0, list0idx, sizeof(FrameStore*), compare_fs_by_poc_desc); + list0idx_1 = list0idx; + for (i=0; iref_frames_in_buffer; i++) + { + if (p_Dpb->fs_ref[i]->is_used) + { + if (currSlice->ThisPOC < p_Dpb->fs_ref[i]->poc) + { + fs_list0[list0idx++] = p_Dpb->fs_ref[i]; + } + } + } + qsort((void *)&fs_list0[list0idx_1], list0idx-list0idx_1, sizeof(FrameStore*), compare_fs_by_poc_asc); + + for (j=0; jThisPOC); for (i=0; ipoc);} printf("\n"); + //printf("fs_list1 currPoc=%d (Poc): ", currSlice->ThisPOC); for (i=0; ipoc);} printf("\n"); + + currSlice->listXsize[0] = 0; + currSlice->listXsize[1] = 0; + gen_pic_list_from_frame_list(currSlice->structure, fs_list0, list0idx, currSlice->listX[0], &currSlice->listXsize[0], 0); + gen_pic_list_from_frame_list(currSlice->structure, fs_list1, list0idx, currSlice->listX[1], &currSlice->listXsize[1], 0); + + //printf("currSlice->listX[0] currPoc=%d (Poc): ", p_Vid->framepoc); for (i=0; ilistXsize[0]; i++){printf ("%d ", currSlice->listX[0][i]->poc);} printf("\n"); + //printf("currSlice->listX[1] currPoc=%d (Poc): ", p_Vid->framepoc); for (i=0; ilistXsize[1]; i++){printf ("%d ", currSlice->listX[1][i]->poc);} printf("\n"); + + // long term handling + for (i=0; iltref_frames_in_buffer; i++) + { + fs_listlt[listltidx++]=p_Dpb->fs_ltref[i]; + } + + qsort((void *)fs_listlt, listltidx, sizeof(FrameStore*), compare_fs_by_lt_pic_idx_asc); + + gen_pic_list_from_frame_list(currSlice->structure, fs_listlt, listltidx, currSlice->listX[0], &currSlice->listXsize[0], 1); + gen_pic_list_from_frame_list(currSlice->structure, fs_listlt, listltidx, currSlice->listX[1], &currSlice->listXsize[1], 1); + + free(fs_list0); + free(fs_list1); + free(fs_listlt); + } + } + + if ((currSlice->listXsize[0] == currSlice->listXsize[1]) && (currSlice->listXsize[0] > 1)) + { + // check if lists are identical, if yes swap first two elements of currSlice->listX[1] + int diff=0; + for (j = 0; j< currSlice->listXsize[0]; j++) + { + if (currSlice->listX[0][j] != currSlice->listX[1][j]) + { + diff = 1; + break; + } + } + if (!diff) + { + StorablePicture *tmp_s = currSlice->listX[1][0]; + currSlice->listX[1][0]=currSlice->listX[1][1]; + currSlice->listX[1][1]=tmp_s; + } + } + + // set max size + currSlice->listXsize[0] = (char) imin (currSlice->listXsize[0], currSlice->num_ref_idx_active[LIST_0]); + currSlice->listXsize[1] = (char) imin (currSlice->listXsize[1], currSlice->num_ref_idx_active[LIST_1]); + + // set the unused list entries to NULL + for (i=currSlice->listXsize[0]; i< (MAX_LIST_SIZE) ; i++) + { + currSlice->listX[0][i] = p_Vid->no_reference_picture; + } + for (i=currSlice->listXsize[1]; i< (MAX_LIST_SIZE) ; i++) + { + currSlice->listX[1][i] = p_Vid->no_reference_picture; + } + +#if PRINTREFLIST +#if (MVC_EXTENSION_ENABLE) + // print out for debug purpose + if((p_Vid->profile_idc == MVC_HIGH || p_Vid->profile_idc == STEREO_HIGH) && currSlice->current_slice_nr==0) + { + if((currSlice->listXsize[0]>0) || (currSlice->listXsize[1]>0)) + printf("\n"); + if(currSlice->listXsize[0]>0) + { + printf(" ** (CurViewID:%d %d) %s Ref Pic List 0 ****\n", currSlice->view_id, currSlice->ThisPOC, currSlice->structure==FRAME ? "FRM":(currSlice->structure==TOP_FIELD ? "TOP":"BOT")); + for(i=0; i<(unsigned int)(currSlice->listXsize[0]); i++) //ref list 0 + { + printf(" %2d -> POC: %4d PicNum: %4d ViewID: %d\n", i, currSlice->listX[0][i]->poc, currSlice->listX[0][i]->pic_num, currSlice->listX[0][i]->view_id); + } + } + if(currSlice->listXsize[1]>0) + { + printf(" ** (CurViewID:%d %d) %s Ref Pic List 1 ****\n", currSlice->view_id, currSlice->ThisPOC, currSlice->structure==FRAME ? "FRM":(currSlice->structure==TOP_FIELD ? "TOP":"BOT")); + for(i=0; i<(unsigned int)(currSlice->listXsize[1]); i++) //ref list 1 + { + printf(" %2d -> POC: %4d PicNum: %4d ViewID: %d\n", i, currSlice->listX[1][i]->poc, currSlice->listX[1][i]->pic_num, currSlice->listX[1][i]->view_id); + } + } + } +#endif +#endif +} + +/*! +************************************************************************ +* \brief +* Initialize listX[2..5] from lists 0 and 1 +* listX[2]: list0 for current_field==top +* listX[3]: list1 for current_field==top +* listX[4]: list0 for current_field==bottom +* listX[5]: list1 for current_field==bottom +* +************************************************************************ +*/ +void init_mbaff_lists(VideoParameters *p_Vid, Slice *currSlice) +{ + unsigned j; + int i; + + for (i=2;i<6;i++) + { + for (j=0; jlistX[i][j] = p_Vid->no_reference_picture; + } + currSlice->listXsize[i]=0; + } + + for (i = 0; i < currSlice->listXsize[0]; i++) + { + currSlice->listX[2][2*i ] = currSlice->listX[0][i]->top_field; + currSlice->listX[2][2*i+1] = currSlice->listX[0][i]->bottom_field; + currSlice->listX[4][2*i ] = currSlice->listX[0][i]->bottom_field; + currSlice->listX[4][2*i+1] = currSlice->listX[0][i]->top_field; + } + currSlice->listXsize[2] = currSlice->listXsize[4] = currSlice->listXsize[0] * 2; + + for (i = 0; i < currSlice->listXsize[1]; i++) + { + currSlice->listX[3][2*i ] = currSlice->listX[1][i]->top_field; + currSlice->listX[3][2*i+1] = currSlice->listX[1][i]->bottom_field; + currSlice->listX[5][2*i ] = currSlice->listX[1][i]->bottom_field; + currSlice->listX[5][2*i+1] = currSlice->listX[1][i]->top_field; + } + currSlice->listXsize[3] = currSlice->listXsize[5] = currSlice->listXsize[1] * 2; +} + +/*! +************************************************************************ +* \brief +* Returns short term pic with given picNum +* +************************************************************************ +*/ +StorablePicture* get_short_term_pic(Slice *currSlice, DecodedPictureBuffer *p_Dpb, int picNum) +{ + unsigned i; + + for (i = 0; i < p_Dpb->ref_frames_in_buffer; i++) + { + if (currSlice->structure == FRAME) + { + if (p_Dpb->fs_ref[i]->is_reference == 3) + if ((!p_Dpb->fs_ref[i]->frame->is_long_term)&&(p_Dpb->fs_ref[i]->frame->pic_num == picNum)) + return p_Dpb->fs_ref[i]->frame; + } + else + { + if (p_Dpb->fs_ref[i]->is_reference & 1) + if ((!p_Dpb->fs_ref[i]->top_field->is_long_term)&&(p_Dpb->fs_ref[i]->top_field->pic_num == picNum)) + return p_Dpb->fs_ref[i]->top_field; + if (p_Dpb->fs_ref[i]->is_reference & 2) + if ((!p_Dpb->fs_ref[i]->bottom_field->is_long_term)&&(p_Dpb->fs_ref[i]->bottom_field->pic_num == picNum)) + return p_Dpb->fs_ref[i]->bottom_field; + } + } + + return currSlice->p_Vid->no_reference_picture; +} + + + +#if (!MVC_EXTENSION_ENABLE) +/*! +************************************************************************ +* \brief +* Reordering process for short-term reference pictures +* +************************************************************************ +*/ +static void reorder_short_term(Slice *currSlice, int cur_list, int num_ref_idx_lX_active_minus1, int picNumLX, int *refIdxLX) +{ + StorablePicture **RefPicListX = currSlice->listX[cur_list]; + int cIdx, nIdx; + + StorablePicture *picLX; + + picLX = get_short_term_pic(currSlice, currSlice->p_Dpb, picNumLX); + + for( cIdx = num_ref_idx_lX_active_minus1+1; cIdx > *refIdxLX; cIdx-- ) + RefPicListX[ cIdx ] = RefPicListX[ cIdx - 1]; + + RefPicListX[ (*refIdxLX)++ ] = picLX; + + nIdx = *refIdxLX; + + for( cIdx = *refIdxLX; cIdx <= num_ref_idx_lX_active_minus1+1; cIdx++ ) + { + if (RefPicListX[ cIdx ]) + if( (RefPicListX[ cIdx ]->is_long_term ) || (RefPicListX[ cIdx ]->pic_num != picNumLX )) + RefPicListX[ nIdx++ ] = RefPicListX[ cIdx ]; + } +} + + +/*! +************************************************************************ +* \brief +* Reordering process for long-term reference pictures +* +************************************************************************ +*/ +static void reorder_long_term(Slice *currSlice, StorablePicture **RefPicListX, int num_ref_idx_lX_active_minus1, int LongTermPicNum, int *refIdxLX) +{ + int cIdx, nIdx; + + StorablePicture *picLX; + + picLX = get_long_term_pic(currSlice, currSlice->p_Dpb, LongTermPicNum); + + for( cIdx = num_ref_idx_lX_active_minus1+1; cIdx > *refIdxLX; cIdx-- ) + RefPicListX[ cIdx ] = RefPicListX[ cIdx - 1]; + + RefPicListX[ (*refIdxLX)++ ] = picLX; + + nIdx = *refIdxLX; + + for( cIdx = *refIdxLX; cIdx <= num_ref_idx_lX_active_minus1+1; cIdx++ ) + { + if (RefPicListX[ cIdx ]) + { + if( (!RefPicListX[ cIdx ]->is_long_term ) || (RefPicListX[ cIdx ]->long_term_pic_num != LongTermPicNum )) + RefPicListX[ nIdx++ ] = RefPicListX[ cIdx ]; + } + } +} +#endif + +/*! +************************************************************************ +* \brief +* Reordering process for reference picture lists +* +************************************************************************ +*/ +void reorder_ref_pic_list(Slice *currSlice, int cur_list) +{ + int *modification_of_pic_nums_idc = currSlice->modification_of_pic_nums_idc[cur_list]; + int *abs_diff_pic_num_minus1 = currSlice->abs_diff_pic_num_minus1[cur_list]; + int *long_term_pic_idx = currSlice->long_term_pic_idx[cur_list]; + int num_ref_idx_lX_active_minus1 = currSlice->num_ref_idx_active[cur_list] - 1; + + VideoParameters *p_Vid = currSlice->p_Vid; + int i; + + int maxPicNum, currPicNum, picNumLXNoWrap, picNumLXPred, picNumLX; + int refIdxLX = 0; + + if (p_Vid->structure==FRAME) + { + maxPicNum = p_Vid->max_frame_num; + currPicNum = currSlice->frame_num; + } + else + { + maxPicNum = 2 * p_Vid->max_frame_num; + currPicNum = 2 * currSlice->frame_num + 1; + } + + picNumLXPred = currPicNum; + + for (i=0; modification_of_pic_nums_idc[i]!=3; i++) + { + if (modification_of_pic_nums_idc[i]>3) + error ("Invalid modification_of_pic_nums_idc command", 500); + + if (modification_of_pic_nums_idc[i] < 2) + { + if (modification_of_pic_nums_idc[i] == 0) + { + if( picNumLXPred - ( abs_diff_pic_num_minus1[i] + 1 ) < 0 ) + picNumLXNoWrap = picNumLXPred - ( abs_diff_pic_num_minus1[i] + 1 ) + maxPicNum; + else + picNumLXNoWrap = picNumLXPred - ( abs_diff_pic_num_minus1[i] + 1 ); + } + else // (modification_of_pic_nums_idc[i] == 1) + { + if( picNumLXPred + ( abs_diff_pic_num_minus1[i] + 1 ) >= maxPicNum ) + picNumLXNoWrap = picNumLXPred + ( abs_diff_pic_num_minus1[i] + 1 ) - maxPicNum; + else + picNumLXNoWrap = picNumLXPred + ( abs_diff_pic_num_minus1[i] + 1 ); + } + picNumLXPred = picNumLXNoWrap; + + if( picNumLXNoWrap > currPicNum ) + picNumLX = picNumLXNoWrap - maxPicNum; + else + picNumLX = picNumLXNoWrap; + +#if (MVC_EXTENSION_ENABLE) + reorder_short_term(currSlice, cur_list, num_ref_idx_lX_active_minus1, picNumLX, &refIdxLX, -1); +#else + reorder_short_term(currSlice, cur_list, num_ref_idx_lX_active_minus1, picNumLX, &refIdxLX); +#endif + } + else //(modification_of_pic_nums_idc[i] == 2) + { +#if (MVC_EXTENSION_ENABLE) + reorder_long_term(currSlice, currSlice->listX[cur_list], num_ref_idx_lX_active_minus1, long_term_pic_idx[i], &refIdxLX, -1); +#else + reorder_long_term(currSlice, currSlice->listX[cur_list], num_ref_idx_lX_active_minus1, long_term_pic_idx[i], &refIdxLX); +#endif + } + + } + // that's a definition + currSlice->listXsize[cur_list] = (char) (num_ref_idx_lX_active_minus1 + 1); +} + + + + +/*! +************************************************************************ +* \brief +* Perform Memory management for idr pictures +* +************************************************************************ +*/ +void idr_memory_management(DecodedPictureBuffer *p_Dpb, StorablePicture* p) +{ + uint32 i; + + if (p->no_output_of_prior_pics_flag) + { + // free all stored pictures + for (i=0; iused_size; i++) + { + // reset all reference settings + free_frame_store(p_Dpb->fs[i]); + p_Dpb->fs[i] = alloc_frame_store(); + } + for (i = 0; i < p_Dpb->ref_frames_in_buffer; i++) + { + p_Dpb->fs_ref[i]=NULL; + } + for (i=0; iltref_frames_in_buffer; i++) + { + p_Dpb->fs_ltref[i]=NULL; + } + p_Dpb->used_size=0; + } + else + { + flush_dpb(p_Dpb); + } + p_Dpb->last_picture = NULL; + + update_ref_list(p_Dpb); + update_ltref_list(p_Dpb); + p_Dpb->last_output_poc = INT_MIN; + + if (p->long_term_reference_flag) + { + p_Dpb->max_long_term_pic_idx = 0; + p->is_long_term = 1; + p->long_term_frame_idx = 0; + } + else + { + p_Dpb->max_long_term_pic_idx = -1; + p->is_long_term = 0; + } + +#if (MVC_EXTENSION_ENABLE) + p_Dpb->last_output_view_id = -1; +#endif + + //! Error Concealment Function + fercResetPOCInDpb(p_Dpb); + +} + +/*! +************************************************************************ +* \brief +* Perform Sliding window decoded reference picture marking process +* +************************************************************************ +*/ +void sliding_window_memory_management(DecodedPictureBuffer *p_Dpb, StorablePicture* p) +{ + uint32 i; + + assert (!p->idr_flag); + + // if this is a reference pic with sliding window, unmark first ref frame + if (p_Dpb->ref_frames_in_buffer == imax(1, p_Dpb->num_ref_frames) - p_Dpb->ltref_frames_in_buffer) + { + for (i = 0; i < p_Dpb->used_size; i++) + { + if (p_Dpb->fs[i]->is_reference && (!(p_Dpb->fs[i]->is_long_term))) + { + unmark_for_reference(p_Dpb->fs[i]); + update_ref_list(p_Dpb); + break; + } + } + } + + p->is_long_term = 0; +} + + +/*! +************************************************************************ +* \brief +* Perform Adaptive memory control decoded reference picture marking process +************************************************************************ +*/ +static void adaptive_memory_management(DecodedPictureBuffer *p_Dpb, StorablePicture* p) +{ + DecRefPicMarking_t *tmp_drpm; + VideoParameters *p_Vid = p_Dpb->p_Vid; + + p_Vid->last_has_mmco_5 = 0; + + assert (!p->idr_flag); + assert (p->adaptive_ref_pic_buffering_flag); + + while (p->dec_ref_pic_marking_buffer) + { + tmp_drpm = p->dec_ref_pic_marking_buffer; + switch (tmp_drpm->memory_management_control_operation) + { + case 0: + if (tmp_drpm->Next != NULL) + { + error ("memory_management_control_operation = 0 not last operation in buffer", 500); + } + break; + case 1: + mm_unmark_short_term_for_reference(p_Dpb, p, tmp_drpm->difference_of_pic_nums_minus1); + update_ref_list(p_Dpb); + break; + case 2: + mm_unmark_long_term_for_reference(p_Dpb, p, tmp_drpm->long_term_pic_num); + update_ltref_list(p_Dpb); + break; + case 3: + mm_assign_long_term_frame_idx(p_Dpb, p, tmp_drpm->difference_of_pic_nums_minus1, tmp_drpm->long_term_frame_idx); + update_ref_list(p_Dpb); + update_ltref_list(p_Dpb); + break; + case 4: + mm_update_max_long_term_frame_idx (p_Dpb, tmp_drpm->max_long_term_frame_idx_plus1); + update_ltref_list(p_Dpb); + break; + case 5: + mm_unmark_all_short_term_for_reference(p_Dpb); + mm_unmark_all_long_term_for_reference(p_Dpb); + p_Vid->last_has_mmco_5 = 1; + break; + case 6: + mm_mark_current_picture_long_term(p_Dpb, p, tmp_drpm->long_term_frame_idx); + check_num_ref(p_Dpb); + break; + default: + error ("invalid memory_management_control_operation in buffer", 500); + } + p->dec_ref_pic_marking_buffer = tmp_drpm->Next; + free (tmp_drpm); + } + if ( p_Vid->last_has_mmco_5 ) + { + p->pic_num = p->frame_num = 0; + + switch (p->structure) + { + case TOP_FIELD: + { + //p->poc = p->top_poc = p_Vid->toppoc =0; + p->poc = p->top_poc = 0; + break; + } + case BOTTOM_FIELD: + { + //p->poc = p->bottom_poc = p_Vid->bottompoc = 0; + p->poc = p->bottom_poc = 0; + break; + } + case FRAME: + { + p->top_poc -= p->poc; + p->bottom_poc -= p->poc; + + //p_Vid->toppoc = p->top_poc; + //p_Vid->bottompoc = p->bottom_poc; + + p->poc = imin (p->top_poc, p->bottom_poc); + //p_Vid->framepoc = p->poc; + break; + } + } + //currSlice->ThisPOC = p->poc; +#if (MVC_EXTENSION_ENABLE) + if(p->view_id == 0) + { + + flush_dpb(p_Vid->p_Dpb_layer[1]); + flush_dpb(p_Vid->p_Dpb_layer[0]); + } + else + { + flush_dpb(p_Dpb); + } +#else + flush_dpb(p_Dpb); +#endif + } +} + + +/*! +************************************************************************ +* \brief +* Store a picture in DPB. This includes cheking for space in DPB and +* flushing frames. +* If we received a frame, we need to check for a new store, if we +* got a field, check if it's the second field of an already allocated +* store. +* +* \param p_Vid +* VideoParameters +* \param p +* Picture to be stored +* +************************************************************************ +*/ +void store_picture_in_dpb(DecodedPictureBuffer *p_Dpb, StorablePicture* p) +{ + VideoParameters *p_Vid = p_Dpb->p_Vid; + unsigned i; + int poc, pos; + // picture error concealment + + // diagnostics + //printf ("Storing (%s) non-ref pic with frame_num #%d\n", (p->type == FRAME)?"FRAME":(p->type == TOP_FIELD)?"TOP_FIELD":"BOTTOM_FIELD", p->pic_num); + // if frame, check for new store, + assert (p!=NULL); + + p_Vid->last_has_mmco_5=0; + p_Vid->last_pic_bottom_field = (p->structure == BOTTOM_FIELD); + + if (p->idr_flag) + { +#if (MVC_EXTENSION_ENABLE) + if( p_Vid->p_Inp->DecodeAllLayers && p_Vid->conceal_mode != NONE_CONCEAL && p_Vid->active_subset_sps ) + { + int i; + for( i=1; i < p_Vid->active_subset_sps->num_views_minus1+1; i++ ) + { + if( p_Vid->p_Dpb_layer[i] ) + idr_memory_management(p_Vid->p_Dpb_layer[i], p); + } + } +#endif + idr_memory_management(p_Dpb, p); + } + else + { + // adaptive memory management + if (p->used_for_reference && (p->adaptive_ref_pic_buffering_flag)) + adaptive_memory_management(p_Dpb, p); + } + + if ((p->structure==TOP_FIELD)||(p->structure==BOTTOM_FIELD)) + { + // check for frame store with same pic_number + if (p_Dpb->last_picture) + { + if ((int)p_Dpb->last_picture->frame_num == p->pic_num) + { + if (((p->structure==TOP_FIELD)&&(p_Dpb->last_picture->is_used==2))||((p->structure==BOTTOM_FIELD)&&(p_Dpb->last_picture->is_used==1))) + { + if ((p->used_for_reference && (p_Dpb->last_picture->is_orig_reference!=0))|| + (!p->used_for_reference && (p_Dpb->last_picture->is_orig_reference==0))) + { + insert_picture_in_dpb(p_Vid, p_Dpb->last_picture, p); + update_ref_list(p_Dpb); + update_ltref_list(p_Dpb); +#if DUMP_DPB + dump_dpb(p_Dpb); +#endif + p_Dpb->last_picture = NULL; + return; + } + } + } + } + } + + // this is a frame or a field which has no stored complementary field + + // sliding window, if necessary + if ((!p->idr_flag)&&(p->used_for_reference && (!p->adaptive_ref_pic_buffering_flag))) + { + sliding_window_memory_management(p_Dpb, p); + } + + // first try to remove unused frames + if (p_Dpb->used_size==p_Dpb->size) + { + // picture error concealment + if( p_Vid->conceal_mode != NONE_CONCEAL) + fercConcealLostNonRefFrames(p_Dpb, 2); + + remove_unused_frame_from_dpb(p_Dpb); + } + + // then output frames until one can be removed + while (p_Dpb->used_size == p_Dpb->size) + { + // non-reference frames may be output directly + if (!p->used_for_reference) + { + get_smallest_poc(p_Dpb, &poc, &pos); + if ((-1==pos) || (p->poc < poc)) + { +#if (_DEBUG && MVC_EXTENSION_ENABLE) + if((p_Vid->profile_idc >= MVC_HIGH)) + printf("Display order might not be correct, %d, %d\n", p->view_id, p->poc); + dump_dpb(p_Dpb); +#endif +#if (MVC_EXTENSION_ENABLE) + direct_output(p_Vid, p, p_Vid->p_out_mvc[p_Dpb->layer_id]); +#else + direct_output(p_Vid, p, p_Vid->p_out); +#endif + return; + } + } + // flush a frame + output_one_frame_from_dpb(p_Dpb); + } + + // check for duplicate frame number in short term reference buffer + if ((p->used_for_reference)&&(!p->is_long_term)) + { + for (i=0; iref_frames_in_buffer; i++) + { +#if (MVC_EXTENSION_ENABLE) + if (p_Dpb->fs_ref[i]->frame_num == p->frame_num && p_Dpb->fs_ref[i]->view_id == p->view_id ) +#else + if (p_Dpb->fs_ref[i]->frame_num == p->frame_num ) +#endif + { + error("duplicate frame_num in short-term reference picture buffer", 500); + } + } + } + // store at end of buffer + insert_picture_in_dpb(p_Vid, p_Dpb->fs[p_Dpb->used_size],p); + + // picture error concealment + if (p->idr_flag) + { + p_Vid->earlier_missing_poc = INT_MAX; + } + + if (p->structure != FRAME) + { + p_Dpb->last_picture = p_Dpb->fs[p_Dpb->used_size]; + } + else + { + p_Dpb->last_picture = NULL; + } + + p_Dpb->used_size++; + + + //! Error Concealment Function + fercAddPOC2Dpb( p_Dpb , p->poc ); + + update_ref_list(p_Dpb); + update_ltref_list(p_Dpb); + + check_num_ref(p_Dpb); +#if DUMP_DPB + dump_dpb(p_Dpb); +#endif +} + + +/*! +************************************************************************ +* \brief +* Insert the picture into the DPB. A free DPB position is necessary +* for frames, . +* +* \param p_Vid +* VideoParameters +* \param fs +* FrameStore into which the picture will be inserted +* \param p +* StorablePicture to be inserted +* +************************************************************************ +*/ +static void insert_picture_in_dpb(VideoParameters *p_Vid, FrameStore* fs, StorablePicture* p) +{ + InputParameters *p_Inp = p_Vid->p_Inp; + // printf ("insert (%s) pic with frame_num #%d, poc %d\n", (p->structure == FRAME)?"FRAME":(p->structure == TOP_FIELD)?"TOP_FIELD":"BOTTOM_FIELD", p->pic_num, p->poc); + assert (p!=NULL); + assert (fs!=NULL); + switch (p->structure) + { + case FRAME: + fs->frame = p; + fs->is_used = 3; + if (p->used_for_reference) + { + fs->is_reference = 3; + fs->is_orig_reference = 3; + if (p->is_long_term) + { + fs->is_long_term = 3; + fs->long_term_frame_idx = p->long_term_frame_idx; + } + } + fs->layer_id = p->layer_id; +#if (MVC_EXTENSION_ENABLE) + fs->view_id = p->view_id; + fs->inter_view_flag[0] = fs->inter_view_flag[1] = p->inter_view_flag; + fs->anchor_pic_flag[0] = fs->anchor_pic_flag[1] = p->anchor_pic_flag; +#endif + // generate field views + dpb_split_field(p_Vid, fs); + break; + case TOP_FIELD: + fs->top_field = p; + fs->is_used |= 1; + fs->layer_id = p->layer_id; +#if (MVC_EXTENSION_ENABLE) + fs->view_id = p->view_id; + fs->inter_view_flag[0] = p->inter_view_flag; + fs->anchor_pic_flag[0] = p->anchor_pic_flag; +#endif + if (p->used_for_reference) + { + fs->is_reference |= 1; + fs->is_orig_reference |= 1; + if (p->is_long_term) + { + fs->is_long_term |= 1; + fs->long_term_frame_idx = p->long_term_frame_idx; + } + } + if (fs->is_used == 3) + { + // generate frame view + dpb_combine_field(p_Vid, fs); + } + else + { + fs->poc = p->poc; + } + gen_field_ref_ids(p_Vid, p); + break; + case BOTTOM_FIELD: + fs->bottom_field = p; + fs->is_used |= 2; + fs->layer_id = p->layer_id; +#if (MVC_EXTENSION_ENABLE) + fs->view_id = p->view_id; + fs->inter_view_flag[1] = p->inter_view_flag; + fs->anchor_pic_flag[1] = p->anchor_pic_flag; +#endif + if (p->used_for_reference) + { + fs->is_reference |= 2; + fs->is_orig_reference |= 2; + if (p->is_long_term) + { + fs->is_long_term |= 2; + fs->long_term_frame_idx = p->long_term_frame_idx; + } + } + if (fs->is_used == 3) + { + // generate frame view + dpb_combine_field(p_Vid, fs); + } + else + { + fs->poc = p->poc; + } + gen_field_ref_ids(p_Vid, p); + break; + } + //! Edited by Joao Carreira + fs->frame_no = p->frame_no; + fs->frame_num = p->pic_num; + fs->recovery_frame = p->recovery_frame; + + fs->is_output = p->is_output; + + /* + if (fs->is_used==3) + { + calculate_frame_no(p_Vid, p); + if (-1 != p_Vid->p_ref && !p_Inp->silent) + find_snr(p_Vid, fs->frame, &p_Vid->p_ref); + } + */ +} + + +/*! +************************************************************************ +* \brief +* remove one frame from DPB +************************************************************************ +*/ +void remove_frame_from_dpb(DecodedPictureBuffer *p_Dpb, int pos) +{ + FrameStore* fs = p_Dpb->fs[pos]; + FrameStore* tmp; + unsigned i; + + //! Error Concealment Function + fercSlindingWindowsOnceFrameRemoved( p_Dpb , fs->frame->poc ); + + //printf ("remove frame with frame_num #%d\n", fs->frame_num); + switch (fs->is_used) + { + case 3: + free_storable_picture(fs->frame); + free_storable_picture(fs->top_field); + free_storable_picture(fs->bottom_field); + fs->frame=NULL; + fs->top_field=NULL; + fs->bottom_field=NULL; + break; + case 2: + free_storable_picture(fs->bottom_field); + fs->bottom_field=NULL; + break; + case 1: + free_storable_picture(fs->top_field); + fs->top_field=NULL; + break; + case 0: + break; + default: + error("invalid frame store type",500); + } + fs->is_used = 0; + fs->is_long_term = 0; + fs->is_reference = 0; + fs->is_orig_reference = 0; + + // move empty framestore to end of buffer + tmp = p_Dpb->fs[pos]; + + for (i=pos; iused_size-1;i++) + { + p_Dpb->fs[i] = p_Dpb->fs[i+1]; + } + p_Dpb->fs[p_Dpb->used_size-1] = tmp; + p_Dpb->used_size--; +} + + + + +/*! +************************************************************************ +* \brief +* Output one picture stored in the DPB. +************************************************************************ +*/ +static int output_one_frame_from_dpb(DecodedPictureBuffer *p_Dpb) +{ + VideoParameters *p_Vid = p_Dpb->p_Vid; + int poc, pos; + //diagnostics + + if (p_Dpb->used_size < 1) + { + error("Cannot output frame, DPB empty.",150); + } + + // find smallest POC + get_smallest_poc(p_Dpb, &poc, &pos); + + if(pos==-1) + { + return 0; + } + + // call the output function + // printf ("output frame with frame_num #%d, poc %d (dpb. p_Dpb->size=%d, p_Dpb->used_size=%d)\n", p_Dpb->fs[pos]->frame_num, p_Dpb->fs[pos]->frame->poc, p_Dpb->size, p_Dpb->used_size); + + // picture error concealment + if(p_Vid->conceal_mode != 0) + { +#if (MVC_EXTENSION_ENABLE) + fercWriteConcealedLostNonRefFrames(p_Dpb, p_Dpb->fs[pos]->poc, p_Dpb->fs[pos]->view_id); +#else + fercWriteConcealedLostNonRefFrames(p_Dpb, poc, 0); +#endif + +#if( CONCEAL_REMAINING_FRAMES ) + if (p_Vid->p_Inp->IDR_Period != 0) + { //conceal the remaining frames + fercConcealRemainingLostFrames(p_Dpb, p_Dpb->fs[pos]); + } +#endif + } + + // JVT-P072 ends + +#if (MVC_EXTENSION_ENABLE) + write_stored_frame(p_Vid, p_Dpb->fs[pos], p_Vid->p_out_mvc[GetViewIdx(p_Vid, p_Dpb->layer_id)]); +#else + write_stored_frame(p_Vid, p_Dpb->fs[pos], p_Vid->p_out); +#endif + + + + // picture error concealment + if(p_Vid->conceal_mode == 0) + { + if (p_Dpb->last_output_poc >= poc) + { + error ("output POC must be in ascending order", 150); + } + } + + p_Dpb->last_output_poc = poc; + + // free frame store and move empty store to end of buffer + if (!is_used_for_reference(p_Dpb->fs[pos])) + { + remove_frame_from_dpb(p_Dpb, pos); + } + return 1; +} + + + +/*! +************************************************************************ +* \brief +* All stored picture are output. Should be called to empty the buffer +************************************************************************ +*/ +void flush_dpb(DecodedPictureBuffer *p_Dpb) +{ + VideoParameters *p_Vid = p_Dpb->p_Vid; + uint32 i; + + // diagnostics + // printf("Flush remaining frames from the dpb. p_Dpb->size=%d, p_Dpb->used_size=%d\n",p_Dpb->size,p_Dpb->used_size); + if(!p_Dpb->init_done) + return; + + if( p_Vid->conceal_mode != NONE_CONCEAL) + fercConcealLostNonRefFrames(p_Dpb, 0); + + // mark all frames unused + for (i=0; iused_size; i++) + { +#if MVC_EXTENSION_ENABLE + assert( p_Dpb->fs[i]->view_id == p_Dpb->layer_id); +#endif + unmark_for_reference (p_Dpb->fs[i]); + } + + while (remove_unused_frame_from_dpb(p_Dpb)) ; + + // output frames in POC order + while (p_Dpb->used_size && output_one_frame_from_dpb(p_Dpb)) ; + + p_Dpb->last_output_poc = INT_MIN; +} + +#if (MVC_EXTENSION_ENABLE) +void flush_dpbs(DecodedPictureBuffer **p_Dpb_layers, int nLayers) +{ + VideoParameters *p_Vid = p_Dpb_layers[0]->p_Vid; + DecodedPictureBuffer *p_Dpb; + int i, j, used_size; + + // diagnostics + // printf("Flush remaining frames from the dpb. p_Dpb->size=%d, p_Dpb->used_size=%d\n",p_Dpb->size,p_Dpb->used_size); + + // if(p_Vid->conceal_mode == 0) + + + // mark all frames unused + for(j=0; jconceal_mode != NONE_CONCEAL ) + fercConcealLostNonRefFrames(p_Dpb, 0); + + if(p_Dpb->init_done) + { + for (i=0; i<(int)p_Dpb->used_size; i++) + { + unmark_for_reference (p_Dpb->fs[i]); + } + while (remove_unused_frame_from_dpb(p_Dpb)) ; + } + } + // output frames in POC order + used_size = p_Dpb_layers[0]->used_size; + for(j=1; jinit_done) + { + if(p_Dpb->used_size && (p_Dpb->used_size != used_size)) + { + assert(!"DPB used_size is not equal!"); + } + if((int)p_Dpb->used_size > used_size) + { + used_size = (int)p_Dpb->used_size; + } + } + } + while (used_size) + { + for(j=0; jused_size) + output_one_frame_from_dpb(p_Dpb); + } + used_size--; + } + for(j=0; jlast_output_poc = INT_MIN; + } +} +#endif + +static void gen_field_ref_ids(VideoParameters *p_Vid, StorablePicture *p) +{ + int i,j; + //! Generate Frame parameters from field information. + + //copy the list; + for(j=0; jiSliceNumOfCurrPic; j++) + { + if(p->listX[j][LIST_0]) + { + p->listXsize[j][LIST_0] = p_Vid->ppSliceList[j]->listXsize[LIST_0]; + for(i=0; ilistXsize[j][LIST_0]; i++) + p->listX[j][LIST_0][i] = p_Vid->ppSliceList[j]->listX[LIST_0][i]; + } + if(p->listX[j][LIST_1]) + { + p->listXsize[j][LIST_1] = p_Vid->ppSliceList[j]->listXsize[LIST_1]; + for(i=0; ilistXsize[j][LIST_1]; i++) + p->listX[j][LIST_1][i] = p_Vid->ppSliceList[j]->listX[LIST_1][i]; + } + } +} + +/*! +************************************************************************ +* \brief +* Extract top field from a frame +************************************************************************ +*/ +void dpb_split_field(VideoParameters *p_Vid, FrameStore *fs) +{ + int i, j, ii, jj, jj4; + int idiv,jdiv; + int currentmb; + int twosz16 = 2 * (fs->frame->size_x >> 4); + StorablePicture *fs_top = NULL, *fs_btm = NULL; + StorablePicture *frame = fs->frame; + + fs->poc = frame->poc; + + if (!frame->frame_mbs_only_flag) + { + fs_top = fs->top_field = alloc_storable_picture(p_Vid, TOP_FIELD, frame->size_x, frame->size_y, frame->size_x_cr, frame->size_y_cr, 1); + fs_btm = fs->bottom_field = alloc_storable_picture(p_Vid, BOTTOM_FIELD, frame->size_x, frame->size_y, frame->size_x_cr, frame->size_y_cr, 1); + + for (i = 0; i < (frame->size_y >> 1); i++) + { + memcpy(fs_top->imgY[i], frame->imgY[i*2], frame->size_x*sizeof(imgpel)); + } + + for (i = 0; i< (frame->size_y_cr >> 1); i++) + { + memcpy(fs_top->imgUV[0][i], frame->imgUV[0][i*2], frame->size_x_cr*sizeof(imgpel)); + memcpy(fs_top->imgUV[1][i], frame->imgUV[1][i*2], frame->size_x_cr*sizeof(imgpel)); + } + + for (i = 0; i < (frame->size_y>>1); i++) + { + memcpy(fs_btm->imgY[i], frame->imgY[i*2 + 1], frame->size_x*sizeof(imgpel)); + } + + for (i = 0; i < (frame->size_y_cr>>1); i++) + { + memcpy(fs_btm->imgUV[0][i], frame->imgUV[0][i*2 + 1], frame->size_x_cr*sizeof(imgpel)); + memcpy(fs_btm->imgUV[1][i], frame->imgUV[1][i*2 + 1], frame->size_x_cr*sizeof(imgpel)); + } + + fs_top->poc = frame->top_poc; + fs_btm->poc = frame->bottom_poc; + +#if (MVC_EXTENSION_ENABLE) + fs_top->view_id = frame->view_id; + fs_btm->view_id = frame->view_id; +#endif + + fs_top->frame_poc = frame->frame_poc; + + fs_top->bottom_poc = fs_btm->bottom_poc = frame->bottom_poc; + fs_top->top_poc = fs_btm->top_poc = frame->top_poc; + fs_btm->frame_poc = frame->frame_poc; + + fs_top->used_for_reference = fs_btm->used_for_reference + = frame->used_for_reference; + fs_top->is_long_term = fs_btm->is_long_term + = frame->is_long_term; + fs->long_term_frame_idx = fs_top->long_term_frame_idx + = fs_btm->long_term_frame_idx + = frame->long_term_frame_idx; + + fs_top->coded_frame = fs_btm->coded_frame = 1; + fs_top->mb_aff_frame_flag = fs_btm->mb_aff_frame_flag + = frame->mb_aff_frame_flag; + + frame->top_field = fs_top; + frame->bottom_field = fs_btm; + frame->frame = frame; + fs_top->bottom_field = fs_btm; + fs_top->frame = frame; + fs_top->top_field = fs_top; + fs_btm->top_field = fs_top; + fs_btm->frame = frame; + fs_btm->bottom_field = fs_btm; + +#if (MVC_EXTENSION_ENABLE) + fs_top->view_id = fs_btm->view_id = fs->view_id; + fs_top->inter_view_flag = fs->inter_view_flag[0]; + fs_btm->inter_view_flag = fs->inter_view_flag[1]; +#endif + + fs_top->chroma_format_idc = fs_btm->chroma_format_idc = frame->chroma_format_idc; + fs_top->iCodingType = fs_btm->iCodingType = frame->iCodingType; + if(frame->used_for_reference) + { + pad_dec_picture(p_Vid, fs_top); + pad_dec_picture(p_Vid, fs_btm); + } + } + else + { + fs->top_field = NULL; + fs->bottom_field = NULL; + frame->top_field = NULL; + frame->bottom_field = NULL; + frame->frame = frame; + } + + if (!frame->frame_mbs_only_flag) + { + if (frame->mb_aff_frame_flag) + { + PicMotionParamsOld *frm_motion = &frame->motion; + for (j=0 ; j< (frame->size_y >> 3); j++) + { + jj = (j >> 2)*8 + (j & 0x03); + jj4 = jj + 4; + jdiv = (j >> 1); + for (i=0 ; i < (frame->size_x>>2); i++) + { + idiv = (i >> 2); + + currentmb = twosz16*(jdiv >> 1)+ (idiv)*2 + (jdiv & 0x01); + // Assign field mvs attached to MB-Frame buffer to the proper buffer + if (frm_motion->mb_field[currentmb]) + { + fs_btm->mv_info[j][i].mv[LIST_0] = frame->mv_info[jj4][i].mv[LIST_0]; + fs_btm->mv_info[j][i].mv[LIST_1] = frame->mv_info[jj4][i].mv[LIST_1]; + fs_btm->mv_info[j][i].ref_idx[LIST_0] = frame->mv_info[jj4][i].ref_idx[LIST_0]; + if(fs_btm->mv_info[j][i].ref_idx[LIST_0] >=0) + fs_btm->mv_info[j][i].ref_pic[LIST_0] = p_Vid->ppSliceList[frame->mv_info[jj4][i].slice_no]->listX[4][(short) fs_btm->mv_info[j][i].ref_idx[LIST_0]]; + else + fs_btm->mv_info[j][i].ref_pic[LIST_0] = NULL; + fs_btm->mv_info[j][i].ref_idx[LIST_1] = frame->mv_info[jj4][i].ref_idx[LIST_1]; + if(fs_btm->mv_info[j][i].ref_idx[LIST_1] >=0) + fs_btm->mv_info[j][i].ref_pic[LIST_1] = p_Vid->ppSliceList[frame->mv_info[jj4][i].slice_no]->listX[5][(short) fs_btm->mv_info[j][i].ref_idx[LIST_1]]; + else + fs_btm->mv_info[j][i].ref_pic[LIST_1] = NULL; + + fs_top->mv_info[j][i].mv[LIST_0] = frame->mv_info[jj][i].mv[LIST_0]; + fs_top->mv_info[j][i].mv[LIST_1] = frame->mv_info[jj][i].mv[LIST_1]; + fs_top->mv_info[j][i].ref_idx[LIST_0] = frame->mv_info[jj][i].ref_idx[LIST_0]; + if(fs_top->mv_info[j][i].ref_idx[LIST_0] >=0) + fs_top->mv_info[j][i].ref_pic[LIST_0] = p_Vid->ppSliceList[frame->mv_info[jj][i].slice_no]->listX[2][(short) fs_top->mv_info[j][i].ref_idx[LIST_0]]; + else + fs_top->mv_info[j][i].ref_pic[LIST_0] = NULL; + fs_top->mv_info[j][i].ref_idx[LIST_1] = frame->mv_info[jj][i].ref_idx[LIST_1]; + if(fs_top->mv_info[j][i].ref_idx[LIST_1] >=0) + fs_top->mv_info[j][i].ref_pic[LIST_1] = p_Vid->ppSliceList[frame->mv_info[jj][i].slice_no]->listX[3][(short) fs_top->mv_info[j][i].ref_idx[LIST_1]]; + else + fs_top->mv_info[j][i].ref_pic[LIST_1] = NULL; + } + } + } + } + + //! Generate field MVs from Frame MVs + for (j=0 ; j < (frame->size_y >> 3) ; j++) + { + jj = 2* RSD(j); + jdiv = (j >> 1); + for (i=0 ; i < (frame->size_x >> 2) ; i++) + { + ii = RSD(i); + idiv = (i >> 2); + + currentmb = twosz16 * (jdiv >> 1)+ (idiv)*2 + (jdiv & 0x01); + + if (!frame->mb_aff_frame_flag || !frame->motion.mb_field[currentmb]) + { + fs_top->mv_info[j][i].mv[LIST_0] = fs_btm->mv_info[j][i].mv[LIST_0] = frame->mv_info[jj][ii].mv[LIST_0]; + fs_top->mv_info[j][i].mv[LIST_1] = fs_btm->mv_info[j][i].mv[LIST_1] = frame->mv_info[jj][ii].mv[LIST_1]; + + // Scaling of references is done here since it will not affect spatial direct (2*0 =0) + if (frame->mv_info[jj][ii].ref_idx[LIST_0] == -1) + { + fs_top->mv_info[j][i].ref_idx[LIST_0] = fs_btm->mv_info[j][i].ref_idx[LIST_0] = - 1; + fs_top->mv_info[j][i].ref_pic[LIST_0] = fs_btm->mv_info[j][i].ref_pic[LIST_0] = NULL; + } + else + { + fs_top->mv_info[j][i].ref_idx[LIST_0] = fs_btm->mv_info[j][i].ref_idx[LIST_0] = frame->mv_info[jj][ii].ref_idx[LIST_0]; + fs_top->mv_info[j][i].ref_pic[LIST_0] = fs_btm->mv_info[j][i].ref_pic[LIST_0] = p_Vid->ppSliceList[frame->mv_info[jj][ii].slice_no]->listX[LIST_0][(short) frame->mv_info[jj][ii].ref_idx[LIST_0]]; + } + + if (frame->mv_info[jj][ii].ref_idx[LIST_1] == -1) + { + fs_top->mv_info[j][i].ref_idx[LIST_1] = fs_btm->mv_info[j][i].ref_idx[LIST_1] = - 1; + fs_top->mv_info[j][i].ref_pic[LIST_1] = fs_btm->mv_info[j][i].ref_pic[LIST_1] = NULL; + } + else + { + fs_top->mv_info[j][i].ref_idx[LIST_1] = fs_btm->mv_info[j][i].ref_idx[LIST_1] = frame->mv_info[jj][ii].ref_idx[LIST_1]; + fs_top->mv_info[j][i].ref_pic[LIST_1] = fs_btm->mv_info[j][i].ref_pic[LIST_1] = p_Vid->ppSliceList[frame->mv_info[jj][ii].slice_no]->listX[LIST_1][(short) frame->mv_info[jj][ii].ref_idx[LIST_1]]; + } + } + } + } + } +} + + +/*! +************************************************************************ +* \brief +* Generate a frame from top and bottom fields, +* YUV components and display information only +************************************************************************ +*/ +void dpb_combine_field_yuv(VideoParameters *p_Vid, FrameStore *fs) +{ + int i, j; + + if (!fs->frame) + { + fs->frame = alloc_storable_picture(p_Vid, FRAME, fs->top_field->size_x, fs->top_field->size_y*2, fs->top_field->size_x_cr, fs->top_field->size_y_cr*2, 1); + } + + for (i=0; itop_field->size_y; i++) + { + memcpy(fs->frame->imgY[i*2], fs->top_field->imgY[i] , fs->top_field->size_x * sizeof(imgpel)); // top field + memcpy(fs->frame->imgY[i*2 + 1], fs->bottom_field->imgY[i], fs->bottom_field->size_x * sizeof(imgpel)); // bottom field + } + + for (j = 0; j < 2; j++) + { + for (i=0; itop_field->size_y_cr; i++) + { + memcpy(fs->frame->imgUV[j][i*2], fs->top_field->imgUV[j][i], fs->top_field->size_x_cr*sizeof(imgpel)); + memcpy(fs->frame->imgUV[j][i*2 + 1], fs->bottom_field->imgUV[j][i], fs->bottom_field->size_x_cr*sizeof(imgpel)); + } + } + fs->poc=fs->frame->poc =fs->frame->frame_poc = imin (fs->top_field->poc, fs->bottom_field->poc); + + fs->bottom_field->frame_poc=fs->top_field->frame_poc=fs->frame->poc; + + fs->bottom_field->top_poc=fs->frame->top_poc=fs->top_field->poc; + fs->top_field->bottom_poc=fs->frame->bottom_poc=fs->bottom_field->poc; + + fs->frame->used_for_reference = (fs->top_field->used_for_reference && fs->bottom_field->used_for_reference ); + fs->frame->is_long_term = (fs->top_field->is_long_term && fs->bottom_field->is_long_term ); + + if (fs->frame->is_long_term) + fs->frame->long_term_frame_idx = fs->long_term_frame_idx; + + fs->frame->top_field = fs->top_field; + fs->frame->bottom_field = fs->bottom_field; + fs->frame->frame = fs->frame; + + fs->frame->coded_frame = 0; + + fs->frame->chroma_format_idc = fs->top_field->chroma_format_idc; + fs->frame->frame_cropping_flag = fs->top_field->frame_cropping_flag; + if (fs->frame->frame_cropping_flag) + { + fs->frame->frame_crop_top_offset = fs->top_field->frame_crop_top_offset; + fs->frame->frame_crop_bottom_offset = fs->top_field->frame_crop_bottom_offset; + fs->frame->frame_crop_left_offset = fs->top_field->frame_crop_left_offset; + fs->frame->frame_crop_right_offset = fs->top_field->frame_crop_right_offset; + } + + fs->top_field->frame = fs->bottom_field->frame = fs->frame; + fs->top_field->top_field = fs->top_field; + fs->top_field->bottom_field = fs->bottom_field; + fs->bottom_field->top_field = fs->top_field; + fs->bottom_field->bottom_field = fs->bottom_field; + if(fs->top_field->used_for_reference || fs->bottom_field->used_for_reference) + { + pad_dec_picture(p_Vid, fs->frame); + } + +} + + +/*! +************************************************************************ +* \brief +* Generate a frame from top and bottom fields +************************************************************************ +*/ +void dpb_combine_field(VideoParameters *p_Vid, FrameStore *fs) +{ + int i,j, jj, jj4, k, l; + + dpb_combine_field_yuv(p_Vid, fs); + +#if (MVC_EXTENSION_ENABLE) + fs->frame->view_id = fs->view_id; +#endif + fs->frame->iCodingType = fs->top_field->iCodingType; //FIELD_CODING; + //! Use inference flag to remap mvs/references + + //! Generate Frame parameters from field information. + + for (j=0 ; j < (fs->top_field->size_y >> 2) ; j++) + { + jj = (j<<1); + jj4 = jj + 1; + for (i=0 ; i< (fs->top_field->size_x >> 2) ; i++) + { + fs->frame->mv_info[jj][i].mv[LIST_0] = fs->top_field->mv_info[j][i].mv[LIST_0]; + fs->frame->mv_info[jj][i].mv[LIST_1] = fs->top_field->mv_info[j][i].mv[LIST_1]; + + fs->frame->mv_info[jj][i].ref_idx[LIST_0] = fs->top_field->mv_info[j][i].ref_idx[LIST_0]; + fs->frame->mv_info[jj][i].ref_idx[LIST_1] = fs->top_field->mv_info[j][i].ref_idx[LIST_1]; + + /* bug: top field list doesnot exist.*/ + l = fs->top_field->mv_info[j][i].slice_no; + k = fs->top_field->mv_info[j][i].ref_idx[LIST_0]; + fs->frame->mv_info[jj][i].ref_pic[LIST_0] = k>=0? fs->top_field->listX[l][LIST_0][k]: NULL; + k = fs->top_field->mv_info[j][i].ref_idx[LIST_1]; + fs->frame->mv_info[jj][i].ref_pic[LIST_1] = k>=0? fs->top_field->listX[l][LIST_1][k]: NULL; + + //! association with id already known for fields. + fs->frame->mv_info[jj4][i].mv[LIST_0] = fs->bottom_field->mv_info[j][i].mv[LIST_0]; + fs->frame->mv_info[jj4][i].mv[LIST_1] = fs->bottom_field->mv_info[j][i].mv[LIST_1]; + + fs->frame->mv_info[jj4][i].ref_idx[LIST_0] = fs->bottom_field->mv_info[j][i].ref_idx[LIST_0]; + fs->frame->mv_info[jj4][i].ref_idx[LIST_1] = fs->bottom_field->mv_info[j][i].ref_idx[LIST_1]; + l = fs->bottom_field->mv_info[j][i].slice_no; + + k = fs->bottom_field->mv_info[j][i].ref_idx[LIST_0]; + fs->frame->mv_info[jj4][i].ref_pic[LIST_0] = k>=0? fs->bottom_field->listX[l][LIST_0][k]: NULL; + k = fs->bottom_field->mv_info[j][i].ref_idx[LIST_1]; + fs->frame->mv_info[jj4][i].ref_pic[LIST_1] = k>=0? fs->bottom_field->listX[l][LIST_1][k]: NULL; + } + } +} + + +/*! +************************************************************************ +* \brief +* Allocate memory for buffering of reference picture reordering commands +************************************************************************ +*/ +void alloc_ref_pic_list_reordering_buffer(Slice *currSlice) +{ + if (currSlice->slice_type != I_SLICE && currSlice->slice_type != SI_SLICE) + { + int size = currSlice->num_ref_idx_active[LIST_0] + 1; + if ((currSlice->modification_of_pic_nums_idc[LIST_0] = calloc(size ,sizeof(int)))==NULL) + no_mem_exit("alloc_ref_pic_list_reordering_buffer: modification_of_pic_nums_idc_l0"); + if ((currSlice->abs_diff_pic_num_minus1[LIST_0] = calloc(size,sizeof(int)))==NULL) + no_mem_exit("alloc_ref_pic_list_reordering_buffer: abs_diff_pic_num_minus1_l0"); + if ((currSlice->long_term_pic_idx[LIST_0] = calloc(size,sizeof(int)))==NULL) + no_mem_exit("alloc_ref_pic_list_reordering_buffer: long_term_pic_idx_l0"); +#if (MVC_EXTENSION_ENABLE) + if ((currSlice->abs_diff_view_idx_minus1[LIST_0] = calloc(size,sizeof(int)))==NULL) + no_mem_exit("alloc_ref_pic_list_reordering_buffer: abs_diff_view_idx_minus1_l0"); +#endif + } + else + { + currSlice->modification_of_pic_nums_idc[LIST_0] = NULL; + currSlice->abs_diff_pic_num_minus1[LIST_0] = NULL; + currSlice->long_term_pic_idx[LIST_0] = NULL; +#if (MVC_EXTENSION_ENABLE) + currSlice->abs_diff_view_idx_minus1[LIST_0] = NULL; +#endif + } + + if (currSlice->slice_type == B_SLICE) + { + int size = currSlice->num_ref_idx_active[LIST_1] + 1; + if ((currSlice->modification_of_pic_nums_idc[LIST_1] = calloc(size,sizeof(int)))==NULL) + no_mem_exit("alloc_ref_pic_list_reordering_buffer: modification_of_pic_nums_idc_l1"); + if ((currSlice->abs_diff_pic_num_minus1[LIST_1] = calloc(size,sizeof(int)))==NULL) + no_mem_exit("alloc_ref_pic_list_reordering_buffer: abs_diff_pic_num_minus1_l1"); + if ((currSlice->long_term_pic_idx[LIST_1] = calloc(size,sizeof(int)))==NULL) + no_mem_exit("alloc_ref_pic_list_reordering_buffer: long_term_pic_idx_l1"); +#if (MVC_EXTENSION_ENABLE) + if ((currSlice->abs_diff_view_idx_minus1[LIST_1] = calloc(size,sizeof(int)))==NULL) + no_mem_exit("alloc_ref_pic_list_reordering_buffer: abs_diff_view_idx_minus1_l1"); +#endif + } + else + { + currSlice->modification_of_pic_nums_idc[LIST_1] = NULL; + currSlice->abs_diff_pic_num_minus1[LIST_1] = NULL; + currSlice->long_term_pic_idx[LIST_1] = NULL; +#if (MVC_EXTENSION_ENABLE) + currSlice->abs_diff_view_idx_minus1[LIST_1] = NULL; +#endif + } +} + + +/*! +************************************************************************ +* \brief +* Free memory for buffering of reference picture reordering commands +************************************************************************ +*/ +void free_ref_pic_list_reordering_buffer(Slice *currSlice) +{ + if (currSlice->modification_of_pic_nums_idc[LIST_0]) + free(currSlice->modification_of_pic_nums_idc[LIST_0]); + if (currSlice->abs_diff_pic_num_minus1[LIST_0]) + free(currSlice->abs_diff_pic_num_minus1[LIST_0]); + if (currSlice->long_term_pic_idx[LIST_0]) + free(currSlice->long_term_pic_idx[LIST_0]); + + currSlice->modification_of_pic_nums_idc[LIST_0] = NULL; + currSlice->abs_diff_pic_num_minus1[LIST_0] = NULL; + currSlice->long_term_pic_idx[LIST_0] = NULL; + + if (currSlice->modification_of_pic_nums_idc[LIST_1]) + free(currSlice->modification_of_pic_nums_idc[LIST_1]); + if (currSlice->abs_diff_pic_num_minus1[LIST_1]) + free(currSlice->abs_diff_pic_num_minus1[LIST_1]); + if (currSlice->long_term_pic_idx[LIST_1]) + free(currSlice->long_term_pic_idx[LIST_1]); + + currSlice->modification_of_pic_nums_idc[LIST_1] = NULL; + currSlice->abs_diff_pic_num_minus1[LIST_1] = NULL; + currSlice->long_term_pic_idx[LIST_1] = NULL; + +#if (MVC_EXTENSION_ENABLE) + if (currSlice->abs_diff_view_idx_minus1[LIST_0]) + free(currSlice->abs_diff_view_idx_minus1[LIST_0]); + currSlice->abs_diff_view_idx_minus1[LIST_0] = NULL; + if (currSlice->abs_diff_view_idx_minus1[LIST_1]) + free(currSlice->abs_diff_view_idx_minus1[LIST_1]); + currSlice->abs_diff_view_idx_minus1[LIST_1] = NULL; +#endif +} + +/*! +************************************************************************ +* \brief +* Tian Dong +* June 13, 2002, Modified on July 30, 2003 +* +* If a gap in frame_num is found, try to fill the gap +* \param p_Vid +* VideoParameters structure +* +************************************************************************ +*/ +void fill_frame_num_gap(VideoParameters *p_Vid, Slice *currSlice) +{ + seq_parameter_set_rbsp_t *active_sps = p_Vid->active_sps; + + int CurrFrameNum; + int UnusedShortTermFrameNum; + StorablePicture *picture = NULL; + int tmp1 = currSlice->delta_pic_order_cnt[0]; + int tmp2 = currSlice->delta_pic_order_cnt[1]; + currSlice->delta_pic_order_cnt[0] = currSlice->delta_pic_order_cnt[1] = 0; + + printf("A gap in frame number is found, try to fill it.\n"); + +#if (MVC_EXTENSION_ENABLE) + UnusedShortTermFrameNum = (p_Vid->pre_frame_num[currSlice->view_id] + 1) % p_Vid->max_frame_num; +#else + UnusedShortTermFrameNum = (p_Vid->pre_frame_num + 1) % p_Vid->max_frame_num; +#endif + CurrFrameNum = currSlice->frame_num; //p_Vid->frame_num; + + while (CurrFrameNum != UnusedShortTermFrameNum) + { + picture = alloc_storable_picture (p_Vid, FRAME, p_Vid->width, p_Vid->height, p_Vid->width_cr, p_Vid->height_cr, 1); + picture->coded_frame = 1; + picture->pic_num = UnusedShortTermFrameNum; + picture->frame_num = UnusedShortTermFrameNum; + picture->non_existing = 1; + picture->is_output = 1; + picture->used_for_reference = 1; + picture->adaptive_ref_pic_buffering_flag = 0; +#if (MVC_EXTENSION_ENABLE) + picture->view_id = currSlice->view_id; +#endif + + currSlice->frame_num = UnusedShortTermFrameNum; + if (active_sps->pic_order_cnt_type!=0) + { + decode_poc(p_Vid, p_Vid->ppSliceList[0]); + } + picture->top_poc = currSlice->toppoc; + picture->bottom_poc = currSlice->bottompoc; + picture->frame_poc = currSlice->framepoc; + picture->poc = currSlice->framepoc; + + store_picture_in_dpb(currSlice->p_Dpb, picture); + + picture=NULL; +#if (MVC_EXTENSION_ENABLE) + p_Vid->pre_frame_num[currSlice->view_id] = UnusedShortTermFrameNum; +#else + p_Vid->pre_frame_num = UnusedShortTermFrameNum; +#endif + UnusedShortTermFrameNum = (UnusedShortTermFrameNum + 1) % p_Vid->max_frame_num; + } + currSlice->delta_pic_order_cnt[0] = tmp1; + currSlice->delta_pic_order_cnt[1] = tmp2; + currSlice->frame_num = CurrFrameNum; +} + + +/*! +************************************************************************ +* \brief +* Compute co-located motion info +* +************************************************************************ +*/ +void compute_colocated (Slice *currSlice, StorablePicture **listX[6]) +{ + int i, j; + + VideoParameters *p_Vid = currSlice->p_Vid; + + if (currSlice->direct_spatial_mv_pred_flag == 0) + { + for (j = 0; j < 2 + (currSlice->mb_aff_frame_flag * 4); j += 2) + { + for (i = 0; i < currSlice->listXsize[j];i++) + { + int prescale, iTRb, iTRp; + + if (j==0) + { + iTRb = iClip3( -128, 127, p_Vid->dec_picture->poc - listX[LIST_0 + j][i]->poc ); + } + else if (j == 2) + { + iTRb = iClip3( -128, 127, p_Vid->dec_picture->top_poc - listX[LIST_0 + j][i]->poc ); + } + else + { + iTRb = iClip3( -128, 127, p_Vid->dec_picture->bottom_poc - listX[LIST_0 + j][i]->poc ); + } + + iTRp = iClip3( -128, 127, listX[LIST_1 + j][0]->poc - listX[LIST_0 + j][i]->poc); + + if (iTRp!=0) + { + prescale = ( 16384 + iabs( iTRp / 2 ) ) / iTRp; + currSlice->mvscale[j][i] = iClip3( -1024, 1023, ( iTRb * prescale + 32 ) >> 6 ) ; + } + else + { + currSlice->mvscale[j][i] = 9999; + } + } + } + } +} + + +#if (MVC_EXTENSION_ENABLE) +int GetMaxDecFrameBuffering(VideoParameters *p_Vid) +{ + int i, j, iMax, iMax_1 = 0, iMax_2 = 0; + subset_seq_parameter_set_rbsp_t *curr_subset_sps; + seq_parameter_set_rbsp_t *curr_sps; + + curr_subset_sps = p_Vid->SubsetSeqParSet; + curr_sps = p_Vid->SeqParSet; + for(i=0; iValid && curr_subset_sps->sps.seq_parameter_set_id < MAXSPS) + { + j = curr_subset_sps->sps.max_dec_frame_buffering; + + if (curr_subset_sps->sps.vui_parameters_present_flag && curr_subset_sps->sps.vui_seq_parameters.bitstream_restriction_flag) + { + if ((int)curr_subset_sps->sps.vui_seq_parameters.max_dec_frame_buffering > j) + { + error ("max_dec_frame_buffering larger than MaxDpbSize", 500); + } + j = imax (1, curr_subset_sps->sps.vui_seq_parameters.max_dec_frame_buffering); + } + + if(j > iMax_2) + iMax_2 = j; + } + + if(curr_sps->Valid) + { + j = curr_sps->max_dec_frame_buffering; + + if (curr_sps->vui_parameters_present_flag && curr_sps->vui_seq_parameters.bitstream_restriction_flag) + { + if ((int)curr_sps->vui_seq_parameters.max_dec_frame_buffering > j) + { + error ("max_dec_frame_buffering larger than MaxDpbSize", 500); + } + j = imax (1, curr_sps->vui_seq_parameters.max_dec_frame_buffering); + } + + if(j > iMax_1) + iMax_1 = j; + } + curr_subset_sps++; + curr_sps++; + } + + if (iMax_1 > 0 && iMax_2 > 0) + iMax = iMax_1 + iMax_2; + else + iMax = (iMax_1 >0? iMax_1*2 : iMax_2*2); + return iMax; +} + +static int is_view_id_in_ref_view_list(int view_id, int *ref_view_id, int num_ref_views) +{ + int i; + for(i=0; ip_Vid; + int iVOIdx = curr_view_id; + int pic_avail; + int poc = 0; + int fld_idx; + int num_ref_views, *ref_view_id; + FrameStore *fs = p_Dpb->fs_ilref[0]; + int i; + + if(iVOIdx <0) + printf("Error: iVOIdx: %d is not less than 0\n", iVOIdx); + + if(anchor_pic_flag) + { + num_ref_views = list_idx? p_Vid->active_subset_sps->num_anchor_refs_l1[iVOIdx] : p_Vid->active_subset_sps->num_anchor_refs_l0[iVOIdx]; + ref_view_id = list_idx? p_Vid->active_subset_sps->anchor_ref_l1[iVOIdx]:p_Vid->active_subset_sps->anchor_ref_l0[iVOIdx]; + } + else + { + num_ref_views = list_idx? p_Vid->active_subset_sps->num_non_anchor_refs_l1[iVOIdx] : p_Vid->active_subset_sps->num_non_anchor_refs_l0[iVOIdx]; + ref_view_id = list_idx? p_Vid->active_subset_sps->non_anchor_ref_l1[iVOIdx]:p_Vid->active_subset_sps->non_anchor_ref_l0[iVOIdx]; + } + + // if(num_ref_views <= 0) + // printf("Error: iNumOfRefViews: %d is not larger than 0\n", num_ref_views); + + if(currPicStructure == BOTTOM_FIELD) + fld_idx = 1; + else + fld_idx = 0; + + for(i=0;iused_size_il;i++) + { + FrameStore *fs = p_Dpb->fs_ilref[i]; + if(currPicStructure==FRAME) + { + pic_avail = (fs->is_used == 3); + if (pic_avail) + poc = fs->frame->poc; + } + else if(currPicStructure==TOP_FIELD) + { + pic_avail = fs->is_used & 1; + if (pic_avail) + poc = fs->top_field->poc; + } + else if(currPicStructure==BOTTOM_FIELD) + { + pic_avail = fs->is_used & 2; + if (pic_avail) + poc = fs->bottom_field->poc; + } + else + pic_avail =0; + + if(pic_avail && fs->inter_view_flag[fld_idx]) + { + if(poc == currPOC) + { + //int real_view = GetViewIdx(p_Vid, fs->view_id); + //! Edited by Joao Carreira - + if(is_view_id_in_ref_view_list( GetViewIdx(p_Vid, fs->view_id), ref_view_id, num_ref_views)) + { + //add one inter-view reference; + list[*listXsize] = fs; + //next; + (*listXsize)++; + } + } + } + } +} + +#endif + +void process_picture_in_dpb_s(VideoParameters *p_Vid, StorablePicture *p_pic) +{ + //InputParameters *p_Inp = p_Vid->p_Inp; + ImageData *p_img_out = &p_Vid->tempData3; + imgpel*** d_img; + int i; + + if(p_Vid->tempData3.frm_data[0] == NULL) + init_img_data( p_Vid, &(p_Vid->tempData3), p_Vid->active_sps); + + if (p_pic->structure == FRAME) + { + d_img = p_img_out->frm_data; + } + else //If reference picture is a field, then frm_data will actually contain field data and therefore top/bottom stride is set accordingly. + { + if (p_pic->structure == TOP_FIELD) + { + d_img = p_img_out->top_data; + } + else + { + d_img = p_img_out->bot_data; + } + } + + for(i=0; isize_y; i++) + memcpy(d_img[0][i], p_pic->imgY[i], p_pic->size_x*sizeof(imgpel)); + if (p_Vid->yuv_format != YUV400) + { + for(i=0; isize_y_cr; i++) + memcpy(d_img[1][i], p_pic->imgUV[0][i], p_pic->size_x_cr * sizeof(imgpel)); + for(i=0; isize_y_cr; i++) + memcpy(d_img[2][i], p_pic->imgUV[1][i], p_pic->size_x_cr * sizeof(imgpel)); + } +} + +int init_img_data(VideoParameters *p_Vid, ImageData *p_ImgData, seq_parameter_set_rbsp_t *sps) +{ + InputParameters *p_Inp = p_Vid->p_Inp; + int memory_size = 0; + int nplane; + + // allocate memory for reference frame buffers: p_ImgData->frm_data + p_ImgData->format = p_Inp->output; + p_ImgData->format.width[0] = p_Vid->width; + p_ImgData->format.width[1] = p_Vid->width_cr; + p_ImgData->format.width[2] = p_Vid->width_cr; + p_ImgData->format.height[0] = p_Vid->height; + p_ImgData->format.height[1] = p_Vid->height_cr; + p_ImgData->format.height[2] = p_Vid->height_cr; + p_ImgData->format.yuv_format = (ColorFormat) sps->chroma_format_idc; + p_ImgData->format.auto_crop_bottom = p_Inp->output.auto_crop_bottom; + p_ImgData->format.auto_crop_right = p_Inp->output.auto_crop_right; + p_ImgData->format.auto_crop_bottom_cr = p_Inp->output.auto_crop_bottom_cr; + p_ImgData->format.auto_crop_right_cr = p_Inp->output.auto_crop_right_cr; + p_ImgData->frm_stride[0] = p_Vid->width; + p_ImgData->frm_stride[1] = p_ImgData->frm_stride[2] = p_Vid->width_cr; + p_ImgData->top_stride[0] = p_ImgData->bot_stride[0] = p_ImgData->frm_stride[0] << 1; + p_ImgData->top_stride[1] = p_ImgData->top_stride[2] = p_ImgData->bot_stride[1] = p_ImgData->bot_stride[2] = p_ImgData->frm_stride[1] << 1; + + if( sps->separate_colour_plane_flag ) + { + for( nplane=0; nplane < MAX_PLANE; nplane++ ) + { + memory_size += get_mem2Dpel(&(p_ImgData->frm_data[nplane]), p_Vid->height, p_Vid->width); + } + } + else + { + memory_size += get_mem2Dpel(&(p_ImgData->frm_data[0]), p_Vid->height, p_Vid->width); + + if (p_Vid->yuv_format != YUV400) + { + int i, j, k; + memory_size += get_mem2Dpel(&(p_ImgData->frm_data[1]), p_Vid->height_cr, p_Vid->width_cr); + memory_size += get_mem2Dpel(&(p_ImgData->frm_data[2]), p_Vid->height_cr, p_Vid->width_cr); + + if (sizeof(imgpel) == sizeof(unsigned char)) + { + for (k = 1; k < 3; k++) + fast_memset(p_ImgData->frm_data[k][0], 128, p_Vid->height_cr * p_Vid->width_cr * sizeof(imgpel)); + } + else + { + imgpel mean_val; + + for (k = 1; k < 3; k++) + { + mean_val = (imgpel) ((p_Vid->max_pel_value_comp[k] + 1) >> 1); + + for (j = 0; j < p_Vid->height_cr; j++) + { + for (i = 0; i < p_Vid->width_cr; i++) + p_ImgData->frm_data[k][j][i] = mean_val; + } + } + } + } + } + + if (!p_Vid->active_sps->frame_mbs_only_flag) + { + // allocate memory for field reference frame buffers + memory_size += init_top_bot_planes(p_ImgData->frm_data[0], p_Vid->height, &(p_ImgData->top_data[0]), &(p_ImgData->bot_data[0])); + + if (p_Vid->yuv_format != YUV400) + { + memory_size += 4*(sizeof(imgpel**)); + + memory_size += init_top_bot_planes(p_ImgData->frm_data[1], p_Vid->height_cr, &(p_ImgData->top_data[1]), &(p_ImgData->bot_data[1])); + memory_size += init_top_bot_planes(p_ImgData->frm_data[2], p_Vid->height_cr, &(p_ImgData->top_data[2]), &(p_ImgData->bot_data[2])); + } + } + + return memory_size; +} + +void free_img_data(VideoParameters *p_Vid, ImageData *p_ImgData) +{ + if ( p_Vid->separate_colour_plane_flag ) + { + int nplane; + + for( nplane=0; nplanefrm_data[nplane]) + { + free_mem2Dpel(p_ImgData->frm_data[nplane]); // free ref frame buffers + p_ImgData->frm_data[nplane] = NULL; + } + } + } + else + { + if (p_ImgData->frm_data[0]) + { + free_mem2Dpel(p_ImgData->frm_data[0]); // free ref frame buffers + p_ImgData->frm_data[0] = NULL; + } + + if (p_ImgData->format.yuv_format != YUV400) + { + if (p_ImgData->frm_data[1]) + { + free_mem2Dpel(p_ImgData->frm_data[1]); + p_ImgData->frm_data[1] = NULL; + } + if (p_ImgData->frm_data[2]) + { + free_mem2Dpel(p_ImgData->frm_data[2]); + p_ImgData->frm_data[2] = NULL; + } + } + } + + if (!p_Vid->active_sps->frame_mbs_only_flag) + { + free_top_bot_planes(p_ImgData->top_data[0], p_ImgData->bot_data[0]); + + if (p_ImgData->format.yuv_format != YUV400) + { + free_top_bot_planes(p_ImgData->top_data[1], p_ImgData->bot_data[1]); + free_top_bot_planes(p_ImgData->top_data[2], p_ImgData->bot_data[2]); + } + } +} + +static inline void copy_img_data(imgpel *out_img, imgpel *in_img, int ostride, int istride, unsigned int size_y, unsigned int size_x) +{ + unsigned int i; + for(i = 0; i < size_y; i++) + { + fast_memcpy(out_img, in_img, size_x); + out_img += ostride; + in_img += istride; + } +} + +/*! +************************************************************************ +* \brief +* Remove a picture from DPB which is no longer needed. +************************************************************************ +*/ +#if !MVC_EXTENSION_ENABLE +int remove_unused_proc_pic_from_dpb(DecodedPictureBuffer *p_Dpb) +{ + assert(!"The function is not available\n"); + return 0; +} +#endif + +/*! +************************************************************************ +* \brief +* Store a processed picture in DPB. This includes cheking for space in DPB and +* flushing frames. +* If we received a frame, we need to check for a new store, if we +* got a field, check if it's the second field of an already allocated +* store. +* +* \param p_Vid +* VideoParameters +* \param p +* Picture to be stored +* +************************************************************************ +*/ +#if (MVC_EXTENSION_ENABLE) +void check_for_previous_interview_frames(DecodedPictureBuffer *p_Dpb, StorablePicture* p) +{ + int i; + FrameStore *fs; + if(p_Dpb->used_size_il > 0 ) + { + for(i=0;iused_size_il;i++) + { + fs = p_Dpb->fs_ilref[i]; + if( fs->is_used==3) + { + //checking; +#ifdef _DEBUG + if(p->structure==FRAME) + assert(fs->frame->frame_poc != p->poc); + else if(p->structure==TOP_FIELD) + assert(fs->top_field->top_poc != p->poc); + else if(p->structure==BOTTOM_FIELD) + assert(fs->bottom_field->bottom_poc != p->poc); +#endif + if(fs->frame) + { + free_storable_picture(fs->frame); + fs->frame = NULL; + } + if(fs->top_field) + { + free_storable_picture(fs->top_field); + fs->top_field = NULL; + } + if(fs->bottom_field) + { + free_storable_picture(fs->bottom_field); + fs->bottom_field = NULL; + } + fs->is_used = 0; + fs->is_reference = 0; + + } + + } + } + p_Dpb->used_size_il=0; +} +void store_proc_picture_in_dpb(DecodedPictureBuffer *p_Dpb, StorablePicture* p) +{ + VideoParameters *p_Vid = p_Dpb->p_Vid; + FrameStore *fs = p_Dpb->fs_ilref[p_Dpb->used_size_il]; + + if(p_Dpb->used_size_il == p_Vid->active_subset_sps->num_views_minus1 ) + { + fs = p_Dpb->fs_ilref[p_Dpb->used_size_il-1]; + if( fs->is_used==3) + { + //checking; +#ifdef _DEBUG + if(p->structure==FRAME) + assert(fs->frame->frame_poc != p->poc); + else if(p->structure==TOP_FIELD) + assert(fs->top_field->top_poc != p->poc); + else if(p->structure==BOTTOM_FIELD) + assert(fs->bottom_field->bottom_poc != p->poc); +#endif + if(fs->frame) + { + free_storable_picture(fs->frame); + fs->frame = NULL; + } + if(fs->top_field) + { + free_storable_picture(fs->top_field); + fs->top_field = NULL; + } + if(fs->bottom_field) + { + free_storable_picture(fs->bottom_field); + fs->bottom_field = NULL; + } + fs->is_used = 0; + fs->is_reference = 0; + p_Dpb->used_size_il--; + } + } +#ifdef _DEBUG + if(fs->is_used>0) + { + //checking; + if(p->structure==FRAME) + assert(fs->frame == NULL); + else if(p->structure==TOP_FIELD) + assert(fs->top_field == NULL); + else if(p->structure==BOTTOM_FIELD) + assert(fs->bottom_field == NULL); + } +#endif + + insert_picture_in_dpb(p_Vid, fs, p); + if((p->structure==FRAME && fs->is_used == 3) || (p->structure!=FRAME && fs->is_used && fs->is_used <3)) + p_Dpb->used_size_il++; +} + +/*! +************************************************************************ +* \brief +* Clone an encoded frame picture structure +************************************************************************ +*/ +StorablePicture * clone_storable_picture( VideoParameters *p_Vid, StorablePicture *p_pic ) +{ + int i, j; + int nplane; + int *istride = NULL; + int ostride[2]; + imgpel ***img_in = NULL; + + StorablePicture *p_stored_pic = alloc_storable_picture (p_Vid, (PictureStructure) p_Vid->structure, p_Vid->width, p_Vid->height, p_Vid->width_cr, p_Vid->height_cr, 0); + + p_stored_pic->pic_num = p_pic->pic_num; + p_stored_pic->frame_num = p_pic->frame_num; + p_stored_pic->long_term_frame_idx = p_pic->long_term_frame_idx; + p_stored_pic->long_term_pic_num = p_pic->long_term_pic_num; + //p_stored_pic->used_for_reference = p_pic->used_for_reference; + p_stored_pic->is_long_term = 0; + p_stored_pic->non_existing = p_pic->non_existing; + p_stored_pic->max_slice_id = p_pic->max_slice_id; + p_stored_pic->structure = p_pic->structure; + p_stored_pic->size_x = p_pic->size_x; + p_stored_pic->size_y = p_pic->size_y; + p_stored_pic->size_x_cr = p_pic->size_x_cr; + p_stored_pic->size_y_cr = p_pic->size_y_cr; + p_stored_pic->size_x_m1 = p_pic->size_x - 1; + p_stored_pic->size_y_m1 = p_pic->size_y - 1; + p_stored_pic->size_x_cr_m1 = p_pic->size_x_cr - 1; + p_stored_pic->size_y_cr_m1 = p_pic->size_y_cr - 1; + + p_stored_pic->mb_aff_frame_flag = p_pic->mb_aff_frame_flag; + p_stored_pic->seiHasTone_mapping = p_pic->seiHasTone_mapping; + p_stored_pic->frame_no = p_pic->frame_no; + p_stored_pic->poc = p_pic->poc; + p_stored_pic->top_poc = p_pic->top_poc; + p_stored_pic->bottom_poc = p_pic->bottom_poc; + p_stored_pic->frame_poc = p_pic->frame_poc; + p_stored_pic->pic_num = p_pic->pic_num; + p_stored_pic->frame_num = p_pic->frame_num; + p_stored_pic->coded_frame = 1; + p_stored_pic->qp = p_pic->qp; + p_stored_pic->slice_qp_delta = p_pic->slice_qp_delta; + p_stored_pic->chroma_qp_offset[0] = p_pic->chroma_qp_offset[0]; + p_stored_pic->chroma_qp_offset[1] = p_pic->chroma_qp_offset[1]; + + p_stored_pic->slice_type = p_pic->slice_type; + p_stored_pic->idr_flag = p_pic->idr_flag; + p_stored_pic->no_output_of_prior_pics_flag = p_pic->no_output_of_prior_pics_flag; + p_stored_pic->long_term_reference_flag = 0; + p_stored_pic->adaptive_ref_pic_buffering_flag = 0; + p_stored_pic->dec_ref_pic_marking_buffer = NULL; + p_stored_pic->PicWidthInMbs = p_pic->PicWidthInMbs; + p_stored_pic->recovery_frame = p_pic->recovery_frame; + p_stored_pic->chroma_format_idc = p_pic->chroma_format_idc; + p_stored_pic->frame_mbs_only_flag = p_pic->frame_mbs_only_flag; + p_stored_pic->frame_cropping_flag = p_pic->frame_cropping_flag; + + /* Edited by Joao Carreira */ + p_stored_pic->concealed_pic = p_pic->concealed_pic; + + + if (p_stored_pic->frame_cropping_flag) + { + p_stored_pic->frame_crop_left_offset = p_pic->frame_crop_left_offset; + p_stored_pic->frame_crop_right_offset = p_pic->frame_crop_right_offset; + p_stored_pic->frame_crop_top_offset = p_pic->frame_crop_top_offset; + p_stored_pic->frame_crop_bottom_offset = p_pic->frame_crop_bottom_offset; + } + + // store BL reconstruction + //memcpy((void *)p_stored_pic->imgY[0], (void *)p_Vid->tempData3.frm_data[0][0], p_pic->size_x * p_pic->size_y * sizeof(imgpel)); + + ostride[0] = p_stored_pic->iLumaStride; + ostride[1] = p_stored_pic->iChromaStride; + if (p_stored_pic->structure == FRAME) + { + istride = p_Vid->tempData3.frm_stride; + img_in = p_Vid->tempData3.frm_data; + } + else if (p_stored_pic->structure == TOP_FIELD) + { + istride = p_Vid->tempData3.top_stride; + img_in = p_Vid->tempData3.top_data; + } + else + { + istride = p_Vid->tempData3.bot_stride; + img_in = p_Vid->tempData3.bot_data; + } + + copy_img_data(&p_stored_pic->imgY[0][0], &img_in[0][0][0], ostride[0], istride[0], p_pic->size_y, p_pic->size_x * sizeof(imgpel)); + + pad_buf(*p_stored_pic->imgY, p_stored_pic->size_x, p_stored_pic->size_y, p_stored_pic->iLumaStride, p_Vid->iLumaPadX, p_Vid->iLumaPadY); + + if (p_Vid->active_sps->chroma_format_idc != YUV400) + { + //memcpy((void *)p_stored_pic->imgUV[0][0], (void *)p_Vid->tempData3.frm_data[1][0], p_pic->size_x_cr * p_pic->size_y_cr * sizeof(imgpel)); + //memcpy((void *)p_stored_pic->imgUV[1][0], (void *)p_Vid->tempData3.frm_data[2][0], p_pic->size_x_cr * p_pic->size_y_cr * sizeof(imgpel)); + copy_img_data(&p_stored_pic->imgUV[0][0][0], &img_in[1][0][0], ostride[1], istride[1], p_pic->size_y_cr, p_pic->size_x_cr*sizeof(imgpel)); + pad_buf(*p_stored_pic->imgUV[0], p_stored_pic->size_x_cr, p_stored_pic->size_y_cr, p_stored_pic->iChromaStride, p_Vid->iChromaPadX, p_Vid->iChromaPadY); + copy_img_data(&p_stored_pic->imgUV[1][0][0], &img_in[2][0][0], ostride[1], istride[2], p_pic->size_y_cr, p_pic->size_x_cr*sizeof(imgpel)); + pad_buf(*p_stored_pic->imgUV[1], p_stored_pic->size_x_cr, p_stored_pic->size_y_cr, p_stored_pic->iChromaStride, p_Vid->iChromaPadX, p_Vid->iChromaPadY); + } + + for (j = 0; j < (p_pic->size_y >> BLOCK_SHIFT); j++) + { + //PicMotionParams *mv_info = p_stored_pic->mv_info[j]; + char *ref_idx = p_stored_pic->mv_info[j][0].ref_idx; + for (i = 0; i < (p_pic->size_x >> BLOCK_SHIFT); i++) + { + //*((short *)&((mv_info++)->ref_idx[LIST_0])) = -1; + *((short *) ref_idx) = -1; + ref_idx += sizeof(PicMotionParams); + } + } + + if( (p_Vid->separate_colour_plane_flag != 0) ) + { + for( nplane=0; nplanesize_y >> BLOCK_SHIFT); j++) + { + for (i = 0; i < (p_pic->size_x >> BLOCK_SHIFT); i++) + { + p_stored_pic->JVmv_info[nplane][j][i].ref_idx[LIST_0] = -1; + p_stored_pic->JVmv_info[nplane][j][i].ref_idx[LIST_1] = -1; + } + } + } + } + + // MVC-related parameters + p_stored_pic->inter_view_flag = p_pic->inter_view_flag; + p_stored_pic->anchor_pic_flag = 0; + p_stored_pic->view_id = p_pic->view_id; + p_stored_pic->layer_id = p_pic->layer_id; + p_stored_pic->proc_flag = 1; + p_stored_pic->is_output = 1; + p_stored_pic->used_for_reference = 1; + + return p_stored_pic; +} +#endif + + diff --git a/ldecod/src/mbuffer_mvc.c b/ldecod/src/mbuffer_mvc.c new file mode 100644 index 0000000..a699f36 --- /dev/null +++ b/ldecod/src/mbuffer_mvc.c @@ -0,0 +1,859 @@ +/*! + *********************************************************************** + * \file + * mbuffer_mvc.c + * + * \brief + * Frame buffer functions + * + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * - Athanasios Leontaris + * - Karsten Suehring + * - Alexis Tourapis + * - Yuwen He + *********************************************************************** + */ + +#include + +#include "global.h" +#include "header.h" +#include "image.h" +#include "mbuffer.h" +#include "mbuffer_common.h" +#include "mbuffer_mvc.h" +#include "memalloc.h" +#include "output.h" + +#if (MVC_EXTENSION_ENABLE) + +/*! + ************************************************************************ + * \brief + * Reordering process for short-term reference pictures + * + ************************************************************************ + */ +void reorder_short_term(Slice *currSlice, int cur_list, int num_ref_idx_lX_active_minus1, int picNumLX, int *refIdxLX, int currViewID) +{ + StorablePicture **RefPicListX = currSlice->listX[cur_list]; + int cIdx, nIdx; + + StorablePicture *picLX; + + picLX = get_short_term_pic(currSlice, currSlice->p_Dpb, picNumLX); + + for( cIdx = num_ref_idx_lX_active_minus1+1; cIdx > *refIdxLX; cIdx-- ) + RefPicListX[ cIdx ] = RefPicListX[ cIdx - 1]; + + RefPicListX[ (*refIdxLX)++ ] = picLX; + + nIdx = *refIdxLX; + + for( cIdx = *refIdxLX; cIdx <= num_ref_idx_lX_active_minus1+1; cIdx++ ) + if (RefPicListX[ cIdx ]) + { + if( (RefPicListX[ cIdx ]->is_long_term ) || (RefPicListX[ cIdx ]->pic_num != picNumLX ) || + ( currViewID != -1 && RefPicListX[ cIdx ]->layer_id != currViewID ) + ) + RefPicListX[ nIdx++ ] = RefPicListX[ cIdx ]; + } +} + + +/*! + ************************************************************************ + * \brief + * Reordering process for long-term reference pictures + * + ************************************************************************ + */ +void reorder_long_term(Slice *currSlice, StorablePicture **RefPicListX, int num_ref_idx_lX_active_minus1, int LongTermPicNum, int *refIdxLX, int currViewID) +{ + int cIdx, nIdx; + + StorablePicture *picLX; + + picLX = get_long_term_pic(currSlice, currSlice->p_Dpb, LongTermPicNum); + + for( cIdx = num_ref_idx_lX_active_minus1+1; cIdx > *refIdxLX; cIdx-- ) + RefPicListX[ cIdx ] = RefPicListX[ cIdx - 1]; + + RefPicListX[ (*refIdxLX)++ ] = picLX; + + nIdx = *refIdxLX; + + for( cIdx = *refIdxLX; cIdx <= num_ref_idx_lX_active_minus1+1; cIdx++ ) + { + if (RefPicListX[ cIdx ]) + { + if( (!RefPicListX[ cIdx ]->is_long_term ) || (RefPicListX[ cIdx ]->long_term_pic_num != LongTermPicNum ) || + ( currViewID != -1 && RefPicListX[ cIdx ]->layer_id != currViewID ) + ) + RefPicListX[ nIdx++ ] = RefPicListX[ cIdx ]; + } + } +} + +/*! + ************************************************************************ + * \brief + * Returns inter-view prediction pic with given targetViewID + * + ************************************************************************ + */ +static StorablePicture* get_inter_view_pic(VideoParameters *p_Vid, Slice *currSlice, int targetViewID, int currPOC, int listidx) +{ + unsigned i; + unsigned int listinterview_size; + FrameStore **fs_listinterview; + + if (listidx == 0) + { + fs_listinterview = currSlice->fs_listinterview0; + listinterview_size = currSlice->listinterviewidx0; + } + else + { + fs_listinterview = currSlice->fs_listinterview1; + listinterview_size = currSlice->listinterviewidx1; + } + + for(i=0; ilayer_id == GetVOIdx( p_Vid, targetViewID )) + { + if(p_Vid->structure==FRAME && fs_listinterview[i]->frame->poc == currPOC) + { + return fs_listinterview[i]->frame; + } + else if(p_Vid->structure==TOP_FIELD && fs_listinterview[i]->top_field->poc == currPOC) + { + return fs_listinterview[i]->top_field; + } + else if(p_Vid->structure==BOTTOM_FIELD && fs_listinterview[i]->bottom_field->poc == currPOC) + { + return fs_listinterview[i]->bottom_field; + } + } + } + + return NULL; +} + +/*! + ************************************************************************ + * \brief + * Generates a alternating field list from a given FrameStore inter-view list + * + ************************************************************************ + */ +static void gen_pic_list_from_frame_interview_list(PictureStructure currStructure, FrameStore **fs_list, int list_idx, StorablePicture **list, char *list_size) +{ + int i; + + if (currStructure == TOP_FIELD) + { + for (i=0; itop_field; + (*list_size)++; + } + } + if (currStructure == BOTTOM_FIELD) + { + for (i=0; ibottom_field; + (*list_size)++; + } + } +} + + +/*! +************************************************************************ +* \brief +* Initialize reference lists depending on current slice type +* +************************************************************************ +*/ +void init_lists_i_slice_mvc(Slice *currSlice) +{ + //VideoParameters *p_Vid = currSlice->p_Vid; + + currSlice->listinterviewidx0 = 0; + currSlice->listinterviewidx1 = 0; + + currSlice->listXsize[0] = 0; + currSlice->listXsize[1] = 0; +} + +/*! +************************************************************************ +* \brief +* Initialize reference lists for a P Slice +* +************************************************************************ +*/ +void init_lists_p_slice_mvc(Slice *currSlice) +{ + VideoParameters *p_Vid = currSlice->p_Vid; + DecodedPictureBuffer *p_Dpb = currSlice->p_Dpb; + int view_id = currSlice->view_id; + unsigned int i; + + int list0idx = 0; + int listltidx = 0; + + FrameStore **fs_list0; + FrameStore **fs_listlt; + + int currPOC = currSlice->ThisPOC; + int anchor_pic_flag = currSlice->anchor_pic_flag; + + currSlice->listinterviewidx0 = 0; + currSlice->listinterviewidx1 = 0; + + if (currSlice->structure == FRAME) + { + for (i=0; iref_frames_in_buffer; i++) + { + if (p_Dpb->fs_ref[i]->is_used==3) + { + if ((p_Dpb->fs_ref[i]->frame->used_for_reference)&&(!p_Dpb->fs_ref[i]->frame->is_long_term)) + { + currSlice->listX[0][list0idx++] = p_Dpb->fs_ref[i]->frame; + } + } + } + // order list 0 by PicNum + qsort((void *)currSlice->listX[0], list0idx, sizeof(StorablePicture*), compare_pic_by_pic_num_desc); + currSlice->listXsize[0] = (char) list0idx; + //printf("listX[0] (PicNum): "); for (i=0; ilistX[0][i]->pic_num);} printf("\n"); + + // long term handling + for (i=0; iltref_frames_in_buffer; i++) + { + if (p_Dpb->fs_ltref[i]->is_used==3) + { + if (p_Dpb->fs_ltref[i]->frame->is_long_term) + { + currSlice->listX[0][list0idx++]=p_Dpb->fs_ltref[i]->frame; + } + } + } + qsort((void *)&currSlice->listX[0][(short) currSlice->listXsize[0]], list0idx - currSlice->listXsize[0], sizeof(StorablePicture*), compare_pic_by_lt_pic_num_asc); + currSlice->listXsize[0] = (char) list0idx; + } + else + { + fs_list0 = calloc(p_Dpb->size, sizeof (FrameStore*)); + if (NULL==fs_list0) + no_mem_exit("init_lists: fs_list0"); + fs_listlt = calloc(p_Dpb->size, sizeof (FrameStore*)); + if (NULL==fs_listlt) + no_mem_exit("init_lists: fs_listlt"); + + for (i=0; iref_frames_in_buffer; i++) + { + if (p_Dpb->fs_ref[i]->is_reference) + { + fs_list0[list0idx++] = p_Dpb->fs_ref[i]; + } + } + + qsort((void *)fs_list0, list0idx, sizeof(FrameStore*), compare_fs_by_frame_num_desc); + + //printf("fs_list0 (FrameNum): "); for (i=0; iframe_num_wrap);} printf("\n"); + + currSlice->listXsize[0] = 0; + gen_pic_list_from_frame_list(currSlice->structure, fs_list0, list0idx, currSlice->listX[0], &currSlice->listXsize[0], 0); + + //printf("listX[0] (PicNum): "); for (i=0; i < currSlice->listXsize[0]; i++){printf ("%d ", currSlice->listX[0][i]->pic_num);} printf("\n"); + + // long term handling + for (i=0; iltref_frames_in_buffer; i++) + { + fs_listlt[listltidx++]=p_Dpb->fs_ltref[i]; + } + + qsort((void *)fs_listlt, listltidx, sizeof(FrameStore*), compare_fs_by_lt_pic_idx_asc); + + gen_pic_list_from_frame_list(currSlice->structure, fs_listlt, listltidx, currSlice->listX[0], &currSlice->listXsize[0], 1); + + free(fs_list0); + free(fs_listlt); + } + + currSlice->listXsize[1] = 0; + +#if !SIMULCAST_ENABLE + if (currSlice->svc_extension_flag == 0) + { + int curr_view_id = currSlice->layer_id; + currSlice->fs_listinterview0 = calloc(p_Dpb->size, sizeof (FrameStore*)); + if (NULL==currSlice->fs_listinterview0) + no_mem_exit("init_lists: fs_listinterview0"); + list0idx = currSlice->listXsize[0]; + if (currSlice->structure == FRAME) + { + //append_interview_list(p_Vid->p_Dpb_layer[1], 0, 0, currSlice->fs_listinterview0, &currSlice->listinterviewidx0, currPOC, curr_view_id, anchor_pic_flag); + append_interview_list(p_Dpb, 0, 0, currSlice->fs_listinterview0, &currSlice->listinterviewidx0, currPOC, curr_view_id, anchor_pic_flag); + for (i=0; i<(unsigned int)currSlice->listinterviewidx0; i++) + { + currSlice->listX[0][list0idx++]=currSlice->fs_listinterview0[i]->frame; + } + currSlice->listXsize[0] = (char) list0idx; + } + else + { + //append_interview_list(p_Vid->p_Dpb_layer[1], currSlice->structure, 0, currSlice->fs_listinterview0, &currSlice->listinterviewidx0, currPOC, curr_view_id, anchor_pic_flag); + append_interview_list(p_Dpb, currSlice->structure, 0, currSlice->fs_listinterview0, &currSlice->listinterviewidx0, currPOC, curr_view_id, anchor_pic_flag); + gen_pic_list_from_frame_interview_list(currSlice->structure, currSlice->fs_listinterview0, currSlice->listinterviewidx0, currSlice->listX[0], &currSlice->listXsize[0]); + } + } +#endif + // set max size + currSlice->listXsize[0] = (char) imin (currSlice->listXsize[0], currSlice->num_ref_idx_active[LIST_0]); + currSlice->listXsize[1] = (char) imin (currSlice->listXsize[1], currSlice->num_ref_idx_active[LIST_1]); + + // set the unused list entries to NULL + for (i=currSlice->listXsize[0]; i< (MAX_LIST_SIZE) ; i++) + { + currSlice->listX[0][i] = p_Vid->no_reference_picture; + } + for (i=currSlice->listXsize[1]; i< (MAX_LIST_SIZE) ; i++) + { + currSlice->listX[1][i] = p_Vid->no_reference_picture; + } + + #if PRINTREFLIST + // print out for debug purpose + if((p_Vid->profile_idc == MVC_HIGH || p_Vid->profile_idc == STEREO_HIGH) && currSlice->current_slice_nr==0) + { + if(currSlice->listXsize[0]>0) + { + printf("\n"); + printf(" ** (CurViewID:%d %d) %s Ref Pic List 0 ****\n", currSlice->view_id, currSlice->ThisPOC, currSlice->structure==FRAME ? "FRM":(currSlice->structure==TOP_FIELD ? "TOP":"BOT")); + for(i=0; i<(unsigned int)(currSlice->listXsize[0]); i++) //ref list 0 + { + printf(" %2d -> POC: %4d PicNum: %4d ViewID: %d\n", i, currSlice->listX[0][i]->poc, currSlice->listX[0][i]->pic_num, currSlice->listX[0][i]->view_id); + } + } + } + #endif +} + +/*! + ************************************************************************ + * \brief + * Initialize reference lists for a B Slice + * + ************************************************************************ + */ +void init_lists_b_slice_mvc(Slice *currSlice) +{ + VideoParameters *p_Vid = currSlice->p_Vid; + DecodedPictureBuffer *p_Dpb = currSlice->p_Dpb; + + unsigned int i; + int j; + + int list0idx = 0; + int list0idx_1 = 0; + int listltidx = 0; + + FrameStore **fs_list0; + FrameStore **fs_list1; + FrameStore **fs_listlt; + + int currPOC = currSlice->ThisPOC; + int anchor_pic_flag = currSlice->anchor_pic_flag; + + currSlice->listinterviewidx0 = 0; + currSlice->listinterviewidx1 = 0; + + { + // B-Slice + if (currSlice->structure == FRAME) + { + for (i=0; iref_frames_in_buffer; i++) + { + if (p_Dpb->fs_ref[i]->is_used==3) + { + if ((p_Dpb->fs_ref[i]->frame->used_for_reference)&&(!p_Dpb->fs_ref[i]->frame->is_long_term)) + { + if (currSlice->framepoc >= p_Dpb->fs_ref[i]->frame->poc) //!KS use >= for error concealment + // if (p_Vid->framepoc > p_Dpb->fs_ref[i]->frame->poc) + { + currSlice->listX[0][list0idx++] = p_Dpb->fs_ref[i]->frame; + } + } + } + } + qsort((void *)currSlice->listX[0], list0idx, sizeof(StorablePicture*), compare_pic_by_poc_desc); + list0idx_1 = list0idx; + for (i=0; iref_frames_in_buffer; i++) + { + if (p_Dpb->fs_ref[i]->is_used==3) + { + if ((p_Dpb->fs_ref[i]->frame->used_for_reference)&&(!p_Dpb->fs_ref[i]->frame->is_long_term)) + { + if (currSlice->framepoc < p_Dpb->fs_ref[i]->frame->poc) + { + currSlice->listX[0][list0idx++] = p_Dpb->fs_ref[i]->frame; + } + } + } + } + qsort((void *)&currSlice->listX[0][list0idx_1], list0idx-list0idx_1, sizeof(StorablePicture*), compare_pic_by_poc_asc); + + for (j=0; jlistX[1][list0idx-list0idx_1+j]=currSlice->listX[0][j]; + } + for (j=list0idx_1; jlistX[1][j-list0idx_1]=currSlice->listX[0][j]; + } + + currSlice->listXsize[0] = currSlice->listXsize[1] = (char) list0idx; + + // printf("currSlice->listX[0] currPoc=%d (Poc): ", currSlice->framepoc); for (i=0; ilistXsize[0]; i++){printf ("%d ", currSlice->listX[0][i]->poc);} printf("\n"); + // printf("currSlice->listX[1] currPoc=%d (Poc): ", currSlice->framepoc); for (i=0; ilistXsize[1]; i++){printf ("%d ", currSlice->listX[1][i]->poc);} printf("\n"); + + // long term handling + for (i=0; iltref_frames_in_buffer; i++) + { + if (p_Dpb->fs_ltref[i]->is_used==3) + { + if (p_Dpb->fs_ltref[i]->frame->is_long_term) + { + currSlice->listX[0][list0idx] = p_Dpb->fs_ltref[i]->frame; + currSlice->listX[1][list0idx++] = p_Dpb->fs_ltref[i]->frame; + } + } + } + qsort((void *)&currSlice->listX[0][(short) currSlice->listXsize[0]], list0idx - currSlice->listXsize[0], sizeof(StorablePicture*), compare_pic_by_lt_pic_num_asc); + qsort((void *)&currSlice->listX[1][(short) currSlice->listXsize[0]], list0idx - currSlice->listXsize[0], sizeof(StorablePicture*), compare_pic_by_lt_pic_num_asc); + currSlice->listXsize[0] = currSlice->listXsize[1] = (char) list0idx; + } + else + { + fs_list0 = calloc(p_Dpb->size, sizeof (FrameStore*)); + if (NULL==fs_list0) + no_mem_exit("init_lists: fs_list0"); + fs_list1 = calloc(p_Dpb->size, sizeof (FrameStore*)); + if (NULL==fs_list1) + no_mem_exit("init_lists: fs_list1"); + fs_listlt = calloc(p_Dpb->size, sizeof (FrameStore*)); + if (NULL==fs_listlt) + no_mem_exit("init_lists: fs_listlt"); + + currSlice->listXsize[0] = 0; + currSlice->listXsize[1] = 1; + + for (i=0; iref_frames_in_buffer; i++) + { + if (p_Dpb->fs_ref[i]->is_used) + { + if (currSlice->ThisPOC >= p_Dpb->fs_ref[i]->poc) + { + fs_list0[list0idx++] = p_Dpb->fs_ref[i]; + } + } + } + qsort((void *)fs_list0, list0idx, sizeof(FrameStore*), compare_fs_by_poc_desc); + list0idx_1 = list0idx; + for (i=0; iref_frames_in_buffer; i++) + { + if (p_Dpb->fs_ref[i]->is_used) + { + if (currSlice->ThisPOC < p_Dpb->fs_ref[i]->poc) + { + fs_list0[list0idx++] = p_Dpb->fs_ref[i]; + } + } + } + qsort((void *)&fs_list0[list0idx_1], list0idx-list0idx_1, sizeof(FrameStore*), compare_fs_by_poc_asc); + + for (j=0; jThisPOC); for (i=0; ipoc);} printf("\n"); + // printf("fs_list1 currPoc=%d (Poc): ", currSlice->ThisPOC); for (i=0; ipoc);} printf("\n"); + + currSlice->listXsize[0] = 0; + currSlice->listXsize[1] = 0; + gen_pic_list_from_frame_list(currSlice->structure, fs_list0, list0idx, currSlice->listX[0], &currSlice->listXsize[0], 0); + gen_pic_list_from_frame_list(currSlice->structure, fs_list1, list0idx, currSlice->listX[1], &currSlice->listXsize[1], 0); + + // printf("currSlice->listX[0] currPoc=%d (Poc): ", currSlice->framepoc); for (i=0; ilistXsize[0]; i++){printf ("%d ", currSlice->listX[0][i]->poc);} printf("\n"); + // printf("currSlice->listX[1] currPoc=%d (Poc): ", currSlice->framepoc); for (i=0; ilistXsize[1]; i++){printf ("%d ", currSlice->listX[1][i]->poc);} printf("\n"); + + // long term handling + for (i=0; iltref_frames_in_buffer; i++) + { + fs_listlt[listltidx++]=p_Dpb->fs_ltref[i]; + } + + qsort((void *)fs_listlt, listltidx, sizeof(FrameStore*), compare_fs_by_lt_pic_idx_asc); + + gen_pic_list_from_frame_list(currSlice->structure, fs_listlt, listltidx, currSlice->listX[0], &currSlice->listXsize[0], 1); + gen_pic_list_from_frame_list(currSlice->structure, fs_listlt, listltidx, currSlice->listX[1], &currSlice->listXsize[1], 1); + + free(fs_list0); + free(fs_list1); + free(fs_listlt); + } + } + + if ((currSlice->listXsize[0] == currSlice->listXsize[1]) && (currSlice->listXsize[0] > 1)) + { + // check if lists are identical, if yes swap first two elements of currSlice->listX[1] + int diff=0; + for (j = 0; j< currSlice->listXsize[0]; j++) + { + if (currSlice->listX[0][j] != currSlice->listX[1][j]) + { + diff = 1; + break; + } + } + if (!diff) + { + StorablePicture *tmp_s = currSlice->listX[1][0]; + currSlice->listX[1][0]=currSlice->listX[1][1]; + currSlice->listX[1][1]=tmp_s; + } + } + // set max size + currSlice->listXsize[0] = (char) imin (currSlice->listXsize[0], currSlice->num_ref_idx_active[LIST_0]); + currSlice->listXsize[1] = (char) imin (currSlice->listXsize[1], currSlice->num_ref_idx_active[LIST_1]); + +#if !SIMULCAST_ENABLE + if (currSlice->svc_extension_flag == 0) + { + int curr_view_id = currSlice->view_id; + // B-Slice + currSlice->fs_listinterview0 = calloc(p_Dpb->size, sizeof (FrameStore*)); + if (NULL==currSlice->fs_listinterview0) + no_mem_exit("init_lists: fs_listinterview0"); + currSlice->fs_listinterview1 = calloc(p_Dpb->size, sizeof (FrameStore*)); + if (NULL==currSlice->fs_listinterview1) + no_mem_exit("init_lists: fs_listinterview1"); + list0idx = currSlice->listXsize[0]; + + if (currSlice->structure == FRAME) + { + append_interview_list( + p_Vid->p_Dpb_layer[curr_view_id], + 0, 0, currSlice->fs_listinterview0, &currSlice->listinterviewidx0, currPOC, curr_view_id, anchor_pic_flag); + append_interview_list( + p_Vid->p_Dpb_layer[curr_view_id], + 0, 1, currSlice->fs_listinterview1, &currSlice->listinterviewidx1, currPOC, curr_view_id, anchor_pic_flag); + for (i=0; i<(unsigned int)currSlice->listinterviewidx0; i++) + { + currSlice->listX[0][list0idx++]=currSlice->fs_listinterview0[i]->frame; + } + currSlice->listXsize[0] = (char) list0idx; + list0idx = currSlice->listXsize[1]; + for (i=0; i<(unsigned int)currSlice->listinterviewidx1; i++) + { + currSlice->listX[1][list0idx++] = currSlice->fs_listinterview1[i]->frame; + } + currSlice->listXsize[1] = (char) list0idx; + } + else + { + append_interview_list( + p_Vid->p_Dpb_layer[1], + currSlice->structure, 0, currSlice->fs_listinterview0, &currSlice->listinterviewidx0, currPOC, curr_view_id, anchor_pic_flag); + gen_pic_list_from_frame_interview_list(currSlice->structure, currSlice->fs_listinterview0, currSlice->listinterviewidx0, currSlice->listX[0], &currSlice->listXsize[0]); + append_interview_list( + p_Vid->p_Dpb_layer[1], + currSlice->structure, 1, currSlice->fs_listinterview1, &currSlice->listinterviewidx1, currPOC, curr_view_id, anchor_pic_flag); + gen_pic_list_from_frame_interview_list(currSlice->structure, currSlice->fs_listinterview1, currSlice->listinterviewidx1, currSlice->listX[1], &currSlice->listXsize[1]); + } + } +#endif + + + // set the unused list entries to NULL + for (i=currSlice->listXsize[0]; i< (MAX_LIST_SIZE) ; i++) + { + currSlice->listX[0][i] = p_Vid->no_reference_picture; + } + for (i=currSlice->listXsize[1]; i< (MAX_LIST_SIZE) ; i++) + { + currSlice->listX[1][i] = p_Vid->no_reference_picture; + } +#if PRINTREFLIST + // print out for debug purpose + if((p_Vid->profile_idc == MVC_HIGH || p_Vid->profile_idc == STEREO_HIGH) && currSlice->current_slice_nr==0) + { + if((currSlice->listXsize[0]>0) || (currSlice->listXsize[1]>0)) + printf("\n"); + if(currSlice->listXsize[0]>0) + { + printf(" ** (CurViewID:%d %d) %s Ref Pic List 0 ****\n", currSlice->view_id, currSlice->ThisPOC, currSlice->structure==FRAME ? "FRM":(currSlice->structure==TOP_FIELD ? "TOP":"BOT")); + for(i=0; i<(unsigned int)(currSlice->listXsize[0]); i++) //ref list 0 + { + printf(" %2d -> POC: %4d PicNum: %4d ViewID: %d\n", i, currSlice->listX[0][i]->poc, currSlice->listX[0][i]->pic_num, currSlice->listX[0][i]->view_id); + } + } + if(currSlice->listXsize[1]>0) + { + printf(" ** (CurViewID:%d %d) %s Ref Pic List 1 ****\n", currSlice->view_id, currSlice->ThisPOC, currSlice->structure==FRAME ? "FRM":(currSlice->structure==TOP_FIELD ? "TOP":"BOT")); + for(i=0; i<(unsigned int)(currSlice->listXsize[1]); i++) //ref list 1 + { + printf(" %2d -> POC: %4d PicNum: %4d ViewID: %d\n", i, currSlice->listX[1][i]->poc, currSlice->listX[1][i]->pic_num, currSlice->listX[1][i]->view_id); + } + } + } +#endif +} + + +/*! + ************************************************************************ + * \brief + * Reordering process for inter-view reference pictures + * + ************************************************************************ + */ +static void reorder_interview(VideoParameters *p_Vid, Slice *currSlice, StorablePicture **RefPicListX, int num_ref_idx_lX_active_minus1, int *refIdxLX, int targetViewID, int currPOC, int listidx) +{ + int cIdx, nIdx; + StorablePicture *picLX; + + picLX = get_inter_view_pic(p_Vid, currSlice, targetViewID, currPOC, listidx); + + if (picLX) + { + for( cIdx = num_ref_idx_lX_active_minus1+1; cIdx > *refIdxLX; cIdx-- ) + RefPicListX[ cIdx ] = RefPicListX[ cIdx - 1]; + + RefPicListX[ (*refIdxLX)++ ] = picLX; + + nIdx = *refIdxLX; + + for( cIdx = *refIdxLX; cIdx <= num_ref_idx_lX_active_minus1+1; cIdx++ ) + { + if((GetViewIdx( p_Vid, RefPicListX[cIdx]->view_id ) != targetViewID) || (RefPicListX[cIdx]->poc != currPOC)) + RefPicListX[ nIdx++ ] = RefPicListX[ cIdx ]; + } + } +} + +/*! + ************************************************************************ + * \brief + * Reordering process for MVC reference picture lists + * + ************************************************************************ + */ +void reorder_ref_pic_list_mvc(Slice *currSlice, int cur_list, int **anchor_ref, int **non_anchor_ref, int view_id, int anchor_pic_flag, int currPOC, int listidx) +{ + VideoParameters *p_Vid = currSlice->p_Vid; + int *modification_of_pic_nums_idc = currSlice->modification_of_pic_nums_idc[cur_list]; + int *abs_diff_pic_num_minus1 = currSlice->abs_diff_pic_num_minus1[cur_list]; + int *long_term_pic_idx = currSlice->long_term_pic_idx[cur_list]; + int num_ref_idx_lX_active_minus1 = currSlice->num_ref_idx_active[cur_list] - 1; + int *abs_diff_view_idx_minus1 = currSlice->abs_diff_view_idx_minus1[cur_list]; + + int i; + + int maxPicNum, currPicNum, picNumLXNoWrap, picNumLXPred, picNumLX; + int picViewIdxLX, targetViewID; + int refIdxLX = 0; + int maxViewIdx =0; + int curr_VOIdx = -1; + int picViewIdxLXPred=-1; + + if (p_Vid->structure==FRAME) + { + maxPicNum = p_Vid->max_frame_num; + currPicNum = currSlice->frame_num; + } + else + { + maxPicNum = 2 * p_Vid->max_frame_num; + currPicNum = 2 * currSlice->frame_num + 1; + } + + if(currSlice->svc_extension_flag==0) + { + curr_VOIdx = view_id; + maxViewIdx = get_maxViewIdx(p_Vid, view_id, anchor_pic_flag, 0); + picViewIdxLXPred=-1; + } + + picNumLXPred = currPicNum; + + for (i=0; modification_of_pic_nums_idc[i]!=3; i++) + { + if (modification_of_pic_nums_idc[i] > 5) + error ("Invalid modification_of_pic_nums_idc command", 500); + + if (modification_of_pic_nums_idc[i] < 2) + { + if (modification_of_pic_nums_idc[i] == 0) + { + if( picNumLXPred - ( abs_diff_pic_num_minus1[i] + 1 ) < 0 ) + picNumLXNoWrap = picNumLXPred - ( abs_diff_pic_num_minus1[i] + 1 ) + maxPicNum; + else + picNumLXNoWrap = picNumLXPred - ( abs_diff_pic_num_minus1[i] + 1 ); + } + else // (modification_of_pic_nums_idc[i] == 1) + { + if( picNumLXPred + ( abs_diff_pic_num_minus1[i] + 1 ) >= maxPicNum ) + picNumLXNoWrap = picNumLXPred + ( abs_diff_pic_num_minus1[i] + 1 ) - maxPicNum; + else + picNumLXNoWrap = picNumLXPred + ( abs_diff_pic_num_minus1[i] + 1 ); + } + picNumLXPred = picNumLXNoWrap; + + if( picNumLXNoWrap > currPicNum ) + picNumLX = picNumLXNoWrap - maxPicNum; + else + picNumLX = picNumLXNoWrap; + + reorder_short_term(currSlice, cur_list, num_ref_idx_lX_active_minus1, picNumLX, &refIdxLX, view_id); + } + else if (modification_of_pic_nums_idc[i] == 2) //(modification_of_pic_nums_idc[i] == 2) + { + reorder_long_term(currSlice, currSlice->listX[cur_list], num_ref_idx_lX_active_minus1, long_term_pic_idx[i], &refIdxLX, view_id); + } + else + { + if(modification_of_pic_nums_idc[i] == 4) //(modification_of_pic_nums_idc[i] == 4) + { + picViewIdxLX = picViewIdxLXPred - (abs_diff_view_idx_minus1[i] + 1); + if( picViewIdxLX <0) + picViewIdxLX += maxViewIdx; + } + else //(modification_of_pic_nums_idc[i] == 5) + { + picViewIdxLX = picViewIdxLXPred + (abs_diff_view_idx_minus1[i] + 1); + if( picViewIdxLX >= maxViewIdx) + picViewIdxLX -= maxViewIdx; + } + picViewIdxLXPred = picViewIdxLX; + + if (anchor_pic_flag) + targetViewID = anchor_ref[curr_VOIdx][picViewIdxLX]; + else + targetViewID = non_anchor_ref[curr_VOIdx][picViewIdxLX]; + + reorder_interview(p_Vid, currSlice, currSlice->listX[cur_list], num_ref_idx_lX_active_minus1, &refIdxLX, targetViewID, currPOC, listidx); + } + } + // that's a definition + currSlice->listXsize[cur_list] = (char) (num_ref_idx_lX_active_minus1 + 1); +} + +void reorder_lists_mvc(Slice * currSlice, int currPOC) +{ + VideoParameters *p_Vid = currSlice->p_Vid; + + if ((currSlice->slice_type != I_SLICE)&&(currSlice->slice_type != SI_SLICE)) + { + if (currSlice->ref_pic_list_reordering_flag[LIST_0]) + { + reorder_ref_pic_list_mvc(currSlice, LIST_0, + p_Vid->active_subset_sps->anchor_ref_l0, + p_Vid->active_subset_sps->non_anchor_ref_l0, + currSlice->view_id, currSlice->anchor_pic_flag, currPOC, 0); + } + if (p_Vid->no_reference_picture == currSlice->listX[0][currSlice->num_ref_idx_active[LIST_0]-1]) + { + if (p_Vid->non_conforming_stream) + printf("RefPicList0[ %d ] is equal to 'no reference picture'\n", currSlice->num_ref_idx_active[LIST_0] - 1); + else + error("RefPicList0[ num_ref_idx_l0_active_minus1 ] in MVC layer is equal to 'no reference picture', invalid bitstream",500); + } + // that's a definition + currSlice->listXsize[0] = (char)currSlice->num_ref_idx_active[LIST_0]; + } + if (currSlice->slice_type == B_SLICE) + { + if (currSlice->ref_pic_list_reordering_flag[LIST_1]) + { + reorder_ref_pic_list_mvc(currSlice, LIST_1, + p_Vid->active_subset_sps->anchor_ref_l1, + p_Vid->active_subset_sps->non_anchor_ref_l1, + currSlice->view_id, currSlice->anchor_pic_flag, currPOC, 1); + } + if (p_Vid->no_reference_picture == currSlice->listX[1][currSlice->num_ref_idx_active[LIST_1]-1]) + { + if (p_Vid->non_conforming_stream) + printf("RefPicList1[ %d ] is equal to 'no reference picture'\n", currSlice->num_ref_idx_active[LIST_1] - 1); + else + error("RefPicList1[ num_ref_idx_l1_active_minus1 ] is equal to 'no reference picture', invalid bitstream",500); + } + // that's a definition + currSlice->listXsize[1] = (char)currSlice->num_ref_idx_active[LIST_1]; + } + + free_ref_pic_list_reordering_buffer(currSlice); + + if ( currSlice->slice_type == P_SLICE ) + { +#if PRINTREFLIST + unsigned int i; + // print out for debug purpose + if((p_Vid->profile_idc == MVC_HIGH || p_Vid->profile_idc == STEREO_HIGH) && currSlice->current_slice_nr==0) + { + if(currSlice->listXsize[0]>0) + { + printf("\n"); + printf(" ** (FinalViewID:%d %d) %s Ref Pic List 0 ****\n", currSlice->view_id, currSlice->ThisPOC, currSlice->structure==FRAME ? "FRM":(currSlice->structure==TOP_FIELD ? "TOP":"BOT")); + for(i=0; i<(unsigned int)(currSlice->listXsize[0]); i++) //ref list 0 + { + printf(" %2d -> POC: %4d PicNum: %4d ViewID: %d\n", i, currSlice->listX[0][i]->poc, currSlice->listX[0][i]->pic_num, currSlice->listX[0][i]->view_id); + } + } + } +#endif + } + else if ( currSlice->slice_type == B_SLICE ) + { +#if PRINTREFLIST + unsigned int i; + // print out for debug purpose + if((p_Vid->profile_idc == MVC_HIGH || p_Vid->profile_idc == STEREO_HIGH) && currSlice->current_slice_nr==0) + { + if((currSlice->listXsize[0]>0) || (currSlice->listXsize[1]>0)) + printf("\n"); + if(currSlice->listXsize[0]>0) + { + printf(" ** (FinalViewID:%d) %s Ref Pic List 0 ****\n", currSlice->view_id, currSlice->structure==FRAME ? "FRM":(currSlice->structure==TOP_FIELD ? "TOP":"BOT")); + for(i=0; i<(unsigned int)(currSlice->listXsize[0]); i++) //ref list 0 + { + printf(" %2d -> POC: %4d PicNum: %4d ViewID: %d\n", i, currSlice->listX[0][i]->poc, currSlice->listX[0][i]->pic_num, currSlice->listX[0][i]->view_id); + } + } + if(currSlice->listXsize[1]>0) + { + printf(" ** (FinalViewID:%d) %s Ref Pic List 1 ****\n", currSlice->view_id, currSlice->structure==FRAME ? "FRM":(currSlice->structure==TOP_FIELD ? "TOP":"BOT")); + for(i=0; i<(unsigned int)(currSlice->listXsize[1]); i++) //ref list 1 + { + printf(" %2d -> POC: %4d PicNum: %4d ViewID: %d\n", i, currSlice->listX[1][i]->poc, currSlice->listX[1][i]->pic_num, currSlice->listX[1][i]->view_id); + } + } + } +#endif + } +} + +#endif + diff --git a/ldecod/src/mc_direct.c b/ldecod/src/mc_direct.c new file mode 100644 index 0000000..35b34cc --- /dev/null +++ b/ldecod/src/mc_direct.c @@ -0,0 +1,709 @@ + +/*! + ************************************************************************************* + * \file mc_direct.c + * + * \brief + * Direct Prediction functions + * + * \author + * Main contributors (see contributors.h for copyright, + * address and affiliation details) + * - Alexis Michael Tourapis + * - Yuwen He + * + ************************************************************************************* + */ +#include "global.h" +#include "block.h" +#include "mc_prediction.h" +#include "mbuffer.h" +#include "mb_access.h" +#include "macroblock.h" +#include "memalloc.h" + +static void update_direct_mv_info_temporal(Macroblock *currMB) +{ + VideoParameters *p_Vid = currMB->p_Vid; + Slice *currSlice = currMB->p_Slice; + int j,k; + int partmode = ((currMB->mb_type == P8x8) ? 4 : currMB->mb_type); + int step_h0 = BLOCK_STEP [partmode][0]; + int step_v0 = BLOCK_STEP [partmode][1]; + + int i0, j0, j6; + + int j4, i4; + StorablePicture *dec_picture = currSlice->dec_picture; + + int list_offset = currMB->list_offset; // ((currSlice->mb_aff_frame_flag)&&(currMB->mb_field))? (mb_nr&0x01) ? 4 : 2 : 0; + StorablePicture **list0 = currSlice->listX[LIST_0 + list_offset]; + StorablePicture **list1 = currSlice->listX[LIST_1 + list_offset]; + + Boolean has_direct = (currMB->b8mode[0] == 0) | (currMB->b8mode[1] == 0) | (currMB->b8mode[2] == 0) | (currMB->b8mode[3] == 0); + + if (has_direct) + { + int mv_scale = 0; + + for (k = 0; k < 4; ++k) // Scan all blocks + { + if (currMB->b8mode[k] == 0) + { + currMB->b8pdir[k] = 2; + for(j0 = 2 * (k >> 1); j0 < 2 * (k >> 1) + 2; j0 += step_v0) + { + for(i0 = currMB->block_x + 2*(k & 0x01); i0 < currMB->block_x + 2 * (k & 0x01)+2; i0 += step_h0) + { + PicMotionParams *colocated; + int refList; + int ref_idx; + int mapped_idx = -1, iref; + + colocated = p_Vid->active_sps->direct_8x8_inference_flag ? + &list1[0]->mv_info[RSD(currMB->block_y_aff + j0)][RSD(i0)] : &list1[0]->mv_info[currMB->block_y_aff + j0][i0]; + if(currSlice->mb_aff_frame_flag) + { + assert(p_Vid->active_sps->direct_8x8_inference_flag); + if(!currMB->mb_field && ((currSlice->listX[LIST_1][0]->iCodingType==FRAME_MB_PAIR_CODING && currSlice->listX[LIST_1][0]->motion.mb_field[currMB->mbAddrX]) || + (currSlice->listX[LIST_1][0]->iCodingType==FIELD_CODING))) + { + if (iabs(dec_picture->poc - currSlice->listX[LIST_1+4][0]->poc)> iabs(dec_picture->poc -currSlice->listX[LIST_1+2][0]->poc) ) + { + colocated = p_Vid->active_sps->direct_8x8_inference_flag ? + &currSlice->listX[LIST_1+2][0]->mv_info[RSD(currMB->block_y_aff + j0)>>1][RSD(i0)] : &currSlice->listX[LIST_1+2][0]->mv_info[(currMB->block_y_aff + j0)>>1][i0]; + } + else + { + colocated = p_Vid->active_sps->direct_8x8_inference_flag ? + &currSlice->listX[LIST_1+4][0]->mv_info[RSD(currMB->block_y_aff + j0)>>1][RSD(i0)] : &currSlice->listX[LIST_1+4][0]->mv_info[(currMB->block_y_aff + j0)>>1][i0]; + } + } + } + else if(!p_Vid->active_sps->frame_mbs_only_flag && !currSlice->field_pic_flag && currSlice->listX[LIST_1][0]->iCodingType != FRAME_CODING) + { + if (iabs(dec_picture->poc - list1[0]->bottom_field->poc)> iabs(dec_picture->poc -list1[0]->top_field->poc) ) + { + colocated = p_Vid->active_sps->direct_8x8_inference_flag ? + &list1[0]->top_field->mv_info[RSD(currMB->block_y_aff + j0)>>1][RSD(i0)] : &list1[0]->top_field->mv_info[(currMB->block_y_aff + j0)>>1][i0]; + } + else + { + colocated = p_Vid->active_sps->direct_8x8_inference_flag ? + &list1[0]->bottom_field->mv_info[RSD(currMB->block_y_aff + j0)>>1][RSD(i0)] : &list1[0]->bottom_field->mv_info[(currMB->block_y_aff + j0)>>1][i0]; + } + } + else if(!p_Vid->active_sps->frame_mbs_only_flag && currSlice->field_pic_flag && currSlice->structure!=list1[0]->structure && list1[0]->coded_frame) + { + if (currSlice->structure == TOP_FIELD) + { + colocated = p_Vid->active_sps->direct_8x8_inference_flag ? + &list1[0]->frame->top_field->mv_info[RSD(currMB->block_y_aff + j0)][RSD(i0)] : &list1[0]->frame->top_field->mv_info[currMB->block_y_aff + j0][i0]; + } + else + { + colocated = p_Vid->active_sps->direct_8x8_inference_flag ? + &list1[0]->frame->bottom_field->mv_info[RSD(currMB->block_y_aff + j0)][RSD(i0)] : &list1[0]->frame->bottom_field->mv_info[currMB->block_y_aff + j0][i0]; + } + } + + refList = colocated->ref_idx[LIST_0 ]== -1 ? LIST_1 : LIST_0; + ref_idx = colocated->ref_idx[refList]; + + if (colocated->ref_pic[refList] == NULL) + { + ref_idx = -1; + } + if (ref_idx == -1) + { + for (j4 = currMB->block_y + j0; j4 < currMB->block_y + j0 + step_v0; ++j4) + { + for (i4 = i0; i4 < i0 + step_h0; ++i4) + { + PicMotionParams *mv_info = &dec_picture->mv_info[j4][i4]; + mv_info->ref_pic[LIST_0] = list0[0]; + mv_info->ref_pic[LIST_1] = list1[0]; + mv_info->mv [LIST_0] = zero_mv; + mv_info->mv [LIST_1] = zero_mv; + mv_info->ref_idx [LIST_0] = 0; + mv_info->ref_idx [LIST_1] = 0; + } + } + } + else + { + if( (currSlice->mb_aff_frame_flag && ( (currMB->mb_field && colocated->ref_pic[refList]->structure==FRAME) || + (!currMB->mb_field && colocated->ref_pic[refList]->structure!=FRAME))) || + (!currSlice->mb_aff_frame_flag && ((currSlice->field_pic_flag==0 && colocated->ref_pic[refList]->structure!=FRAME)|| + (currSlice->field_pic_flag==1 && colocated->ref_pic[refList]->structure==FRAME))) ) + { + //! Frame with field co-located + for (iref = 0; iref < imin(currSlice->num_ref_idx_active[LIST_0], currSlice->listXsize[LIST_0 + list_offset]); ++iref) + { + if (currSlice->listX[LIST_0 + list_offset][iref]->top_field == colocated->ref_pic[refList] || + currSlice->listX[LIST_0 + list_offset][iref]->bottom_field == colocated->ref_pic[refList] || + currSlice->listX[LIST_0 + list_offset][iref]->frame == colocated->ref_pic[refList] ) + { + if ((currSlice->field_pic_flag==1) && (currSlice->listX[LIST_0 + list_offset][iref]->structure != currSlice->structure)) + { + mapped_idx=INVALIDINDEX; + } + else + { + mapped_idx = iref; + break; + } + } + else //! invalid index. Default to zero even though this case should not happen + mapped_idx=INVALIDINDEX; + } + } + else + { + for (iref = 0; iref < imin(currSlice->num_ref_idx_active[LIST_0], currSlice->listXsize[LIST_0 + list_offset]); ++iref) + { + if (currSlice->listX[LIST_0 + list_offset][iref] == colocated->ref_pic[refList]) + { + mapped_idx=iref; + break; + } + else //! invalid index. Default to zero even though this case should not happen + mapped_idx=INVALIDINDEX; + } + } + + if (mapped_idx != INVALIDINDEX) + { + for (j = j0; j < j0 + step_v0; ++j) + { + j4 = currMB->block_y + j; + j6 = currMB->block_y_aff + j; + + for (i4 = i0; i4 < i0 + step_h0; ++i4) + { + PicMotionParams *colocated = p_Vid->active_sps->direct_8x8_inference_flag ? + &list1[0]->mv_info[RSD(j6)][RSD(i4)] : &list1[0]->mv_info[j6][i4]; + PicMotionParams *mv_info = &dec_picture->mv_info[j4][i4]; + int mv_y; + if(currSlice->mb_aff_frame_flag /*&& (!p_Vid->active_sps->frame_mbs_only_flag || p_Vid->active_sps->direct_8x8_inference_flag)*/) + { + if(!currMB->mb_field && ((currSlice->listX[LIST_1][0]->iCodingType==FRAME_MB_PAIR_CODING && currSlice->listX[LIST_1][0]->motion.mb_field[currMB->mbAddrX]) || + (currSlice->listX[LIST_1][0]->iCodingType==FIELD_CODING))) + { + if (iabs(dec_picture->poc - currSlice->listX[LIST_1+4][0]->poc)> iabs(dec_picture->poc -currSlice->listX[LIST_1+2][0]->poc) ) + { + colocated = p_Vid->active_sps->direct_8x8_inference_flag ? + &currSlice->listX[LIST_1+2][0]->mv_info[RSD(j6)>>1][RSD(i4)] : &currSlice->listX[LIST_1+2][0]->mv_info[j6>>1][i4]; + } + else + { + colocated = p_Vid->active_sps->direct_8x8_inference_flag ? + &currSlice->listX[LIST_1+4][0]->mv_info[RSD(j6)>>1][RSD(i4)] : &currSlice->listX[LIST_1+4][0]->mv_info[j6>>1][i4]; + } + } + } + else if(/*!currSlice->mb_aff_frame_flag &&*/ !p_Vid->active_sps->frame_mbs_only_flag && !currSlice->field_pic_flag && currSlice->listX[LIST_1][0]->iCodingType!=FRAME_CODING) + { + if (iabs(dec_picture->poc - list1[0]->bottom_field->poc)> iabs(dec_picture->poc -list1[0]->top_field->poc) ) + { + colocated = p_Vid->active_sps->direct_8x8_inference_flag ? + &list1[0]->top_field->mv_info[RSD(j6)>>1][RSD(i4)] : &list1[0]->top_field->mv_info[(j6)>>1][i4]; + } + else + { + colocated = p_Vid->active_sps->direct_8x8_inference_flag ? + &list1[0]->bottom_field->mv_info[RSD(j6)>>1][RSD(i4)] : &list1[0]->bottom_field->mv_info[(j6)>>1][i4]; + } + } + else if(!p_Vid->active_sps->frame_mbs_only_flag && currSlice->field_pic_flag && currSlice->structure!=list1[0]->structure && list1[0]->coded_frame) + { + if (currSlice->structure == TOP_FIELD) + { + colocated = p_Vid->active_sps->direct_8x8_inference_flag ? + &list1[0]->frame->top_field->mv_info[RSD(j6)][RSD(i4)] : &list1[0]->frame->top_field->mv_info[j6][i4]; + } + else + { + colocated = p_Vid->active_sps->direct_8x8_inference_flag ? + &list1[0]->frame->bottom_field->mv_info[RSD(j6)][RSD(i4)] : &list1[0]->frame->bottom_field->mv_info[j6][i4]; + } + } + + mv_y = colocated->mv[refList].mv_y; + if((currSlice->mb_aff_frame_flag && !currMB->mb_field && colocated->ref_pic[refList]->structure!=FRAME) || + (!currSlice->mb_aff_frame_flag && currSlice->field_pic_flag==0 && colocated->ref_pic[refList]->structure!=FRAME)) + mv_y *= 2; + else if((currSlice->mb_aff_frame_flag && currMB->mb_field && colocated->ref_pic[refList]->structure==FRAME) || + (!currSlice->mb_aff_frame_flag && currSlice->field_pic_flag==1 && colocated->ref_pic[refList]->structure==FRAME)) + mv_y /= 2; + + mv_scale = currSlice->mvscale[LIST_0 + list_offset][mapped_idx]; + + mv_info->ref_idx [LIST_0] = (char) mapped_idx; + mv_info->ref_idx [LIST_1] = 0; + + mv_info->ref_pic[LIST_0] = list0[mapped_idx]; + mv_info->ref_pic[LIST_1] = list1[0]; + + if (mv_scale == 9999 || currSlice->listX[LIST_0+list_offset][mapped_idx]->is_long_term) + { + mv_info->mv[LIST_0].mv_x = colocated->mv[refList].mv_x; + mv_info->mv[LIST_0].mv_y = (short) mv_y; + mv_info->mv[LIST_1] = zero_mv; + } + else + { + mv_info->mv[LIST_0].mv_x = (short) ((mv_scale * colocated->mv[refList].mv_x + 128 ) >> 8); + mv_info->mv[LIST_0].mv_y = (short) ((mv_scale * mv_y/*colocated->mv[refList].mv_y*/ + 128 ) >> 8); + mv_info->mv[LIST_1].mv_x = (short) (mv_info->mv[LIST_0].mv_x - colocated->mv[refList].mv_x); + mv_info->mv[LIST_1].mv_y = (short) (mv_info->mv[LIST_0].mv_y - mv_y/*colocated->mv[refList].mv_y*/); + } + } + } + } + else if (INVALIDINDEX == mapped_idx) + { + error("temporal direct error: colocated block has ref that is unavailable",-1111); + } + } + } + } + } + } + } +} + + +static inline void update_neighbor_mvs(PicMotionParams **motion, const PicMotionParams *mv_info, int i4) +{ + (*motion++)[i4 + 1] = *mv_info; + (*motion )[i4 ] = *mv_info; + (*motion )[i4 + 1] = *mv_info; +} + +/*! +************************************************************************************* +* \brief +* Colocated info <= direct_inference is disabled. +************************************************************************************* +*/ +int get_colocated_info_4x4(Macroblock *currMB, StorablePicture *list1, int i, int j) +{ + if (list1->is_long_term) + return 1; + else + { + PicMotionParams *fs = &list1->mv_info[j][i]; + + int moving = !((( + (fs->ref_idx[LIST_0] == 0) + && (iabs(fs->mv[LIST_0].mv_x)>>1 == 0) + && (iabs(fs->mv[LIST_0].mv_y)>>1 == 0))) + || ((fs->ref_idx[LIST_0] == -1) + && (fs->ref_idx[LIST_1] == 0) + && (iabs(fs->mv[LIST_1].mv_x)>>1 == 0) + && (iabs(fs->mv[LIST_1].mv_y)>>1 == 0))); + + return moving; + } +} + +/*! +************************************************************************************* +* \brief +* Temporary function for colocated info when direct_inference is enabled. Will be replaced with +* function that will access directly motion information +************************************************************************************* +*/ +int get_colocated_info_8x8(Macroblock *currMB, StorablePicture *list1, int i, int j) +{ + if (list1->is_long_term) + return 1; + else + { + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + if( (currSlice->mb_aff_frame_flag) || + (!p_Vid->active_sps->frame_mbs_only_flag && ((!currSlice->structure && list1->iCodingType == FIELD_CODING)||(currSlice->structure!=list1->structure && list1->coded_frame)))) + { + int jj = RSD(j); + int ii = RSD(i); + int jdiv = (jj>>1); + int moving; + PicMotionParams *fs = &list1->mv_info[jj][ii]; + + if(currSlice->field_pic_flag && currSlice->structure!=list1->structure && list1->coded_frame) + { + if(currSlice->structure == TOP_FIELD) + fs = list1->top_field->mv_info[jj] + ii; + else + fs = list1->bottom_field->mv_info[jj] + ii; + } + else + { + if( (currSlice->mb_aff_frame_flag && ((!currMB->mb_field && list1->motion.mb_field[currMB->mbAddrX]) || + (!currMB->mb_field && list1->iCodingType == FIELD_CODING))) + || (!currSlice->mb_aff_frame_flag)) + { + if (iabs(currSlice->dec_picture->poc - list1->bottom_field->poc)> iabs(currSlice->dec_picture->poc -list1->top_field->poc) ) + { + fs = list1->top_field->mv_info[jdiv] + ii; + } + else + { + fs = list1->bottom_field->mv_info[jdiv] + ii; + } + } + } + moving = !((((fs->ref_idx[LIST_0] == 0) + && (iabs(fs->mv[LIST_0].mv_x)>>1 == 0) + && (iabs(fs->mv[LIST_0].mv_y)>>1 == 0))) + || ((fs->ref_idx[LIST_0] == -1) + && (fs->ref_idx[LIST_1] == 0) + && (iabs(fs->mv[LIST_1].mv_x)>>1 == 0) + && (iabs(fs->mv[LIST_1].mv_y)>>1 == 0))); + return moving; + } + else + { + PicMotionParams *fs = &list1->mv_info[RSD(j)][RSD(i)]; + int moving; + if(currMB->p_Vid->separate_colour_plane_flag && currMB->p_Vid->yuv_format==YUV444) + fs = &list1->JVmv_info[currMB->p_Slice->colour_plane_id][RSD(j)][RSD(i)]; + moving= !((((fs->ref_idx[LIST_0] == 0) + && (iabs(fs->mv[LIST_0].mv_x)>>1 == 0) + && (iabs(fs->mv[LIST_0].mv_y)>>1 == 0))) + || ((fs->ref_idx[LIST_0] == -1) + && (fs->ref_idx[LIST_1] == 0) + && (iabs(fs->mv[LIST_1].mv_x)>>1 == 0) + && (iabs(fs->mv[LIST_1].mv_y)>>1 == 0))); + return moving; + } + } +} + + +static void update_direct_mv_info_spatial_8x8(Macroblock *currMB) +{ + Boolean has_direct = (currMB->b8mode[0] == 0) | (currMB->b8mode[1] == 0) | (currMB->b8mode[2] == 0) | (currMB->b8mode[3] == 0); + + if (has_direct) + { + //VideoParameters *p_Vid = currMB->p_Vid; + Slice *currSlice = currMB->p_Slice; + int i,j,k; + + int j4, i4; + StorablePicture *dec_picture = currSlice->dec_picture; + + int list_offset = currMB->list_offset; // ((currSlice->mb_aff_frame_flag)&&(currMB->mb_field))? (mb_nr&0x01) ? 4 : 2 : 0; + StorablePicture **list0 = currSlice->listX[LIST_0 + list_offset]; + StorablePicture **list1 = currSlice->listX[LIST_1 + list_offset]; + + char l0_rFrame, l1_rFrame; + MotionVector pmvl0, pmvl1; + int is_not_moving; + PicMotionParams *mv_info = NULL; + + prepare_direct_params(currMB, dec_picture, &pmvl0, &pmvl1, &l0_rFrame, &l1_rFrame); + + for (k = 0; k < 4; ++k) + { + if (currMB->b8mode[k] == 0) + { + i = 2 * (k & 0x01); + j = 2 * (k >> 1); + + //j6 = currMB->block_y_aff + j; + j4 = currMB->block_y + j; + i4 = currMB->block_x + i; + + mv_info = &dec_picture->mv_info[j4][i4]; + + is_not_moving = (get_colocated_info_8x8(currMB, list1[0], i4, currMB->block_y_aff + j) == 0); + + if (is_not_moving && (l0_rFrame == 0 || l1_rFrame == 0)) + { + if (l1_rFrame == -1) + { + if (l0_rFrame == 0) + { + mv_info->ref_pic[LIST_0] = list0[0]; + mv_info->ref_pic[LIST_1] = list1[0]; + mv_info->mv[LIST_0] = zero_mv; + mv_info->mv[LIST_1] = zero_mv; + mv_info->ref_idx[LIST_0] = 0; + mv_info->ref_idx[LIST_1] = -1; + } + else + { + mv_info->ref_pic[LIST_0] = list0[(short) l0_rFrame]; + mv_info->ref_pic[LIST_1] = NULL; + mv_info->mv[LIST_0] = pmvl0; + mv_info->mv[LIST_1] = zero_mv; + mv_info->ref_idx[LIST_0] = l0_rFrame; + mv_info->ref_idx[LIST_1] = -1; + } + } + else if (l0_rFrame == -1) + { + if (l1_rFrame == 0) + { + mv_info->ref_pic[LIST_0] = NULL; + mv_info->ref_pic[LIST_1] = list1[0]; + mv_info->mv[LIST_0] = zero_mv; + mv_info->mv[LIST_1] = zero_mv; + mv_info->ref_idx[LIST_0] = -1; + mv_info->ref_idx[LIST_1] = 0; + } + else + { + mv_info->ref_pic[LIST_0] = NULL; + mv_info->ref_pic[LIST_1] = list1[(short) l1_rFrame]; + mv_info->mv[LIST_0] = zero_mv; + mv_info->mv[LIST_1] = pmvl1; + mv_info->ref_idx[LIST_0] = -1; + mv_info->ref_idx[LIST_1] = l1_rFrame; + } + } + else + { + if (l0_rFrame == 0) + { + mv_info->ref_pic[LIST_0] = list0[0]; + mv_info->mv[LIST_0] = zero_mv; + mv_info->ref_idx[LIST_0] = 0; + } + else + { + mv_info->ref_pic[LIST_1] = list1[(short) l0_rFrame]; + mv_info->mv[LIST_0] = pmvl0; + mv_info->ref_idx[LIST_0] = l0_rFrame; + } + + if (l1_rFrame == 0) + { + mv_info->ref_pic[LIST_1] = list1[0]; + mv_info->mv[LIST_1] = zero_mv; + mv_info->ref_idx[LIST_1] = 0; + } + else + { + mv_info->ref_pic[LIST_1] = list1[(short) l1_rFrame]; + mv_info->mv[LIST_1] = pmvl1; + mv_info->ref_idx[LIST_1] = l1_rFrame; + } + } + } + else + { + if (l0_rFrame < 0 && l1_rFrame < 0) + { + mv_info->ref_pic[LIST_0] = list0[0]; + mv_info->ref_pic[LIST_1] = list1[0]; + mv_info->mv[LIST_0] = zero_mv; + mv_info->mv[LIST_1] = zero_mv; + mv_info->ref_idx[LIST_0] = 0; + mv_info->ref_idx[LIST_1] = 0; + } + else if (l0_rFrame < 0) + { + mv_info->ref_pic[LIST_0] = NULL; + mv_info->ref_pic[LIST_1] = list1[(short) l1_rFrame]; + mv_info->mv[LIST_0] = zero_mv; + mv_info->mv[LIST_1] = pmvl1; + mv_info->ref_idx[LIST_0] = -1; + mv_info->ref_idx[LIST_1] = l1_rFrame; + } + else if (l1_rFrame < 0) + { + mv_info->ref_pic[LIST_0] = list0[(short) l0_rFrame]; + mv_info->ref_pic[LIST_1] = NULL; + + mv_info->mv[LIST_0] = pmvl0; + mv_info->mv[LIST_1] = zero_mv; + mv_info->ref_idx[LIST_0] = l0_rFrame; + mv_info->ref_idx[LIST_1] = -1; + } + else + { + mv_info->ref_pic[LIST_0] = list0[(short) l0_rFrame]; + mv_info->ref_pic[LIST_1] = list1[(short) l1_rFrame]; + mv_info->mv[LIST_0] = pmvl0; + mv_info->mv[LIST_1] = pmvl1; + mv_info->ref_idx[LIST_0] = l0_rFrame; + mv_info->ref_idx[LIST_1] = l1_rFrame; + } + } + update_neighbor_mvs(&dec_picture->mv_info[j4], mv_info, i4); + } + } + } +} + +static void update_direct_mv_info_spatial_4x4(Macroblock *currMB) +{ + Boolean has_direct = (currMB->b8mode[0] == 0) | (currMB->b8mode[1] == 0) | (currMB->b8mode[2] == 0) | (currMB->b8mode[3] == 0); + + if (has_direct) + { + VideoParameters *p_Vid = currMB->p_Vid; + Slice *currSlice = currMB->p_Slice; + int i,j,k; + + int j4, i4; + StorablePicture *dec_picture = p_Vid->dec_picture; + + int list_offset = currMB->list_offset; // ((currSlice->mb_aff_frame_flag)&&(currMB->mb_field))? (mb_nr&0x01) ? 4 : 2 : 0; + StorablePicture **list0 = currSlice->listX[LIST_0 + list_offset]; + StorablePicture **list1 = currSlice->listX[LIST_1 + list_offset]; + + char l0_rFrame, l1_rFrame; + MotionVector pmvl0, pmvl1; + + prepare_direct_params(currMB, dec_picture, &pmvl0, &pmvl1, &l0_rFrame, &l1_rFrame); + for (k = 0; k < 4; ++k) + { + if (currMB->b8mode[k] == 0) + { + + i = 2 * (k & 0x01); + for(j = 2 * (k >> 1); j < 2 * (k >> 1)+2;++j) + { + j4 = currMB->block_y + j; + + for(i4 = currMB->block_x + i; i4 < currMB->block_x + i + 2; ++i4) + { + PicMotionParams *mv_info = &dec_picture->mv_info[j4][i4]; + //===== DIRECT PREDICTION ===== + if (l0_rFrame == 0 || l1_rFrame == 0) + { + int is_not_moving = (get_colocated_info_4x4(currMB, list1[0], i4, currMB->block_y_aff + j) == 0); + + if (l1_rFrame == -1) + { + if (is_not_moving) + { + mv_info->ref_pic[LIST_0] = list0[0]; + mv_info->ref_pic[LIST_1] = NULL; + mv_info->mv[LIST_0] = zero_mv; + mv_info->mv[LIST_1] = zero_mv; + mv_info->ref_idx[LIST_0] = 0; + mv_info->ref_idx[LIST_1] = -1; + } + else + { + mv_info->ref_pic[LIST_0] = list0[(short) l0_rFrame]; + mv_info->ref_pic[LIST_1] = NULL; + mv_info->mv[LIST_0] = pmvl0; + mv_info->mv[LIST_1] = zero_mv; + mv_info->ref_idx[LIST_0] = l0_rFrame; + mv_info->ref_idx[LIST_1] = -1; + } + } + else if (l0_rFrame == -1) + { + if (is_not_moving) + { + mv_info->ref_pic[LIST_0] = NULL; + mv_info->ref_pic[LIST_1] = list1[0]; + mv_info->mv[LIST_0] = zero_mv; + mv_info->mv[LIST_1] = zero_mv; + mv_info->ref_idx[LIST_0] = -1; + mv_info->ref_idx[LIST_1] = 0; + } + else + { + mv_info->ref_pic[LIST_0] = NULL; + mv_info->ref_pic[LIST_1] = list1[(short) l1_rFrame]; + mv_info->mv[LIST_0] = zero_mv; + mv_info->mv[LIST_1] = pmvl1; + mv_info->ref_idx[LIST_0] = -1; + mv_info->ref_idx[LIST_1] = l1_rFrame; + } + } + else + { + if (l0_rFrame == 0 && ((is_not_moving))) + { + mv_info->ref_pic[LIST_0] = list0[0]; + mv_info->mv[LIST_0] = zero_mv; + mv_info->ref_idx[LIST_0] = 0; + } + else + { + mv_info->ref_pic[LIST_0] = list0[(short) l0_rFrame]; + mv_info->mv[LIST_0] = pmvl0; + mv_info->ref_idx[LIST_0] = l0_rFrame; + } + + if (l1_rFrame == 0 && ((is_not_moving))) + { + mv_info->ref_pic[LIST_1] = list1[0]; + mv_info->mv[LIST_1] = zero_mv; + mv_info->ref_idx[LIST_1] = 0; + } + else + { + mv_info->ref_pic[LIST_1] = list1[(short) l1_rFrame]; + mv_info->mv[LIST_1] = pmvl1; + mv_info->ref_idx[LIST_1] = l1_rFrame; + } + } + } + else + { + mv_info = &dec_picture->mv_info[j4][i4]; + + if (l0_rFrame < 0 && l1_rFrame < 0) + { + mv_info->ref_pic[LIST_0] = list0[0]; + mv_info->ref_pic[LIST_1] = list1[0]; + mv_info->mv[LIST_0] = zero_mv; + mv_info->mv[LIST_1] = zero_mv; + mv_info->ref_idx[LIST_0] = 0; + mv_info->ref_idx[LIST_1] = 0; + } + else if (l1_rFrame == -1) + { + mv_info->ref_pic[LIST_0] = list0[(short) l0_rFrame]; + mv_info->ref_pic[LIST_1] = NULL; + mv_info->mv[LIST_0] = pmvl0; + mv_info->mv[LIST_1] = zero_mv; + mv_info->ref_idx[LIST_0] = l0_rFrame; + mv_info->ref_idx[LIST_1] = -1; + } + else if (l0_rFrame == -1) + { + mv_info->ref_pic[LIST_0] = NULL; + mv_info->ref_pic[LIST_1] = list1[(short) l1_rFrame]; + mv_info->mv[LIST_0] = zero_mv; + mv_info->mv[LIST_1] = pmvl1; + mv_info->ref_idx[LIST_0] = -1; + mv_info->ref_idx[LIST_1] = l1_rFrame; + } + else + { + mv_info->ref_pic[LIST_0] = list0[(short) l0_rFrame]; + mv_info->ref_pic[LIST_1] = list1[(short) l1_rFrame]; + mv_info->mv[LIST_0] = pmvl0; + mv_info->mv[LIST_1] = pmvl1; + mv_info->ref_idx[LIST_0] = l0_rFrame; + mv_info->ref_idx[LIST_1] = l1_rFrame; + } + } + } + } + } + } + } +} + + +void update_direct_types(Slice *currSlice) +{ + if (currSlice->active_sps->direct_8x8_inference_flag) + currSlice->update_direct_mv_info = currSlice->direct_spatial_mv_pred_flag ? update_direct_mv_info_spatial_8x8 : update_direct_mv_info_temporal; + else + currSlice->update_direct_mv_info = currSlice->direct_spatial_mv_pred_flag ? update_direct_mv_info_spatial_4x4 : update_direct_mv_info_temporal; +} diff --git a/ldecod/src/mc_prediction.c b/ldecod/src/mc_prediction.c new file mode 100644 index 0000000..1fa4580 --- /dev/null +++ b/ldecod/src/mc_prediction.c @@ -0,0 +1,1802 @@ + +/*! +************************************************************************************* +* \file mc_prediction.c +* +* \brief +* Functions for motion compensated prediction +* +* \author +* Main contributors (see contributors.h for copyright, +* address and affiliation details) +* - Alexis Michael Tourapis +* - Chris Vogt +* +************************************************************************************* +*/ +#include "global.h" +#include "block.h" +#include "mc_prediction.h" +#include "mbuffer.h" +#include "mb_access.h" +#include "macroblock.h" +#include "memalloc.h" +#include "dec_statistics.h" + +#include "ferc_api.h" + + +int allocate_pred_mem(Slice *currSlice) +{ + int alloc_size = 0; + alloc_size += get_mem2Dpel(&currSlice->tmp_block_l0, MB_BLOCK_SIZE, MB_BLOCK_SIZE); + alloc_size += get_mem2Dpel(&currSlice->tmp_block_l1, MB_BLOCK_SIZE, MB_BLOCK_SIZE); + alloc_size += get_mem2Dpel(&currSlice->tmp_block_l2, MB_BLOCK_SIZE, MB_BLOCK_SIZE); + alloc_size += get_mem2Dpel(&currSlice->tmp_block_l3, MB_BLOCK_SIZE, MB_BLOCK_SIZE); + alloc_size += get_mem2Dint(&currSlice->tmp_res, MB_BLOCK_SIZE + 5, MB_BLOCK_SIZE + 5); + return (alloc_size); +} + +void free_pred_mem(Slice *currSlice) +{ + free_mem2Dint(currSlice->tmp_res); + free_mem2Dpel(currSlice->tmp_block_l0); + free_mem2Dpel(currSlice->tmp_block_l1); + free_mem2Dpel(currSlice->tmp_block_l2); + free_mem2Dpel(currSlice->tmp_block_l3); +} + +static const int COEF[6] = { 1, -5, 20, 20, -5, 1 }; + +/*! +************************************************************************ +* \brief +* block single list prediction +************************************************************************ +*/ +static void mc_prediction(imgpel **mb_pred, imgpel **block, int block_size_y, int block_size_x, int ioff) +{ + + int j; + + for (j = 0; j < block_size_y; j++) + { + memcpy(&mb_pred[j][ioff], block[j], block_size_x * sizeof(imgpel)); + } +} + +/*! +************************************************************************ +* \brief +* block single list weighted prediction +************************************************************************ +*/ +static void weighted_mc_prediction(imgpel **mb_pred, + imgpel **block, + int block_size_y, + int block_size_x, + int ioff, + int wp_scale, + int wp_offset, + int weight_denom, + int color_clip) +{ + int i, j; + int result; + + for(j = 0; j < block_size_y; j++) + { + for(i = 0; i < block_size_x; i++) + { + result = rshift_rnd((wp_scale * block[j][i]), weight_denom) + wp_offset; + mb_pred[j][i + ioff] = (imgpel)iClip3(0, color_clip, result); + } + } +} + + + +/*! +************************************************************************ +* \brief +* block bi-prediction +************************************************************************ +*/ +void bi_prediction(imgpel **mb_pred, imgpel **block_l0, imgpel **block_l1, + int block_size_y, int block_size_x, int ioff) +{ + imgpel *mpr = &mb_pred[0][ioff]; + imgpel *b0 = block_l0[0]; + imgpel *b1 = block_l1[0]; + int ii, jj; + int row_inc = MB_BLOCK_SIZE - block_size_x; + for(jj = 0;jj < block_size_y;jj++) + { + // unroll the loop + for(ii = 0; ii < block_size_x; ii += 2) + { + *(mpr++) = (imgpel)(((*(b0++) + *(b1++)) + 1) >> 1); + *(mpr++) = (imgpel)(((*(b0++) + *(b1++)) + 1) >> 1); + } + mpr += row_inc; + b0 += row_inc; + b1 += row_inc; + } +} + + + +/*! +************************************************************************ +* \brief +* block weighted biprediction +************************************************************************ +*/ +static void weighted_bi_prediction(imgpel *mb_pred, + imgpel *block_l0, + imgpel *block_l1, + int block_size_y, + int block_size_x, + int wp_scale_l0, + int wp_scale_l1, + int wp_offset, + int weight_denom, + int color_clip) +{ + int i, j, result; + int row_inc = MB_BLOCK_SIZE - block_size_x; + + for(j = 0; j < block_size_y; j++) + { + for(i = 0; i < block_size_x; i++) + { + result = rshift_rnd_sf((wp_scale_l0 * *(block_l0++) + wp_scale_l1 * *(block_l1++)), weight_denom); + + *(mb_pred++) = (imgpel) iClip1(color_clip, result + wp_offset); + } + mb_pred += row_inc; + block_l0 += row_inc; + block_l1 += row_inc; + } +} + +/*! +************************************************************************ +* \brief +* Integer positions +************************************************************************ +*/ +static void get_block_00(imgpel *block, imgpel *cur_img, int span, int block_size_y) +{ + // fastest to just move an entire block, since block is a temp block is a 256 byte block (16x16) + // writes 2 lines of 16 imgpel 1 to 8 times depending in block_size_y + int j; + + for (j = 0; j < block_size_y; j += 2) + { + memcpy(block, cur_img, MB_BLOCK_SIZE * sizeof(imgpel)); + block += MB_BLOCK_SIZE; + cur_img += span; + memcpy(block, cur_img, MB_BLOCK_SIZE * sizeof(imgpel)); + block += MB_BLOCK_SIZE; + cur_img += span; + } +} + + +/*! +************************************************************************ +* \brief +* Qpel (1,0) horizontal +************************************************************************ +*/ +static void get_luma_10(imgpel **block, imgpel **cur_imgY, int block_size_y, int block_size_x, int x_pos , int max_imgpel_value) +{ + imgpel *p0, *p1, *p2, *p3, *p4, *p5; + imgpel *orig_line, *cur_line; + int i, j; + int result; + + for (j = 0; j < block_size_y; j++) + { + cur_line = &(cur_imgY[j][x_pos]); + p0 = &cur_imgY[j][x_pos - 2]; + p1 = p0 + 1; + p2 = p1 + 1; + p3 = p2 + 1; + p4 = p3 + 1; + p5 = p4 + 1; + orig_line = block[j]; + + for (i = 0; i < block_size_x; i++) + { + result = (*(p0++) + *(p5++)) - 5 * (*(p1++) + *(p4++)) + 20 * (*(p2++) + *(p3++)); + + *orig_line = (imgpel) iClip1(max_imgpel_value, ((result + 16)>>5)); + *orig_line = (imgpel) ((*orig_line + *(cur_line++) + 1 ) >> 1); + orig_line++; + } + } +} + +/*! +************************************************************************ +* \brief +* Half horizontal +************************************************************************ +*/ +static void get_luma_20(imgpel **block, imgpel **cur_imgY, int block_size_y, int block_size_x, int x_pos , int max_imgpel_value) +{ + imgpel *p0, *p1, *p2, *p3, *p4, *p5; + imgpel *orig_line; + int i, j; + int result; + for (j = 0; j < block_size_y; j++) + { + p0 = &cur_imgY[j][x_pos - 2]; + p1 = p0 + 1; + p2 = p1 + 1; + p3 = p2 + 1; + p4 = p3 + 1; + p5 = p4 + 1; + orig_line = block[j]; + + for (i = 0; i < block_size_x; i++) + { + result = (*(p0++) + *(p5++)) - 5 * (*(p1++) + *(p4++)) + 20 * (*(p2++) + *(p3++)); + + *orig_line++ = (imgpel) iClip1(max_imgpel_value, ((result + 16)>>5)); + } + } +} + +/*! +************************************************************************ +* \brief +* Qpel (3,0) horizontal +************************************************************************ +*/ +static void get_luma_30(imgpel **block, imgpel **cur_imgY, int block_size_y, int block_size_x, int x_pos , int max_imgpel_value) +{ + imgpel *p0, *p1, *p2, *p3, *p4, *p5; + imgpel *orig_line, *cur_line; + int i, j; + int result; + + for (j = 0; j < block_size_y; j++) + { + cur_line = &(cur_imgY[j][x_pos + 1]); + p0 = &cur_imgY[j][x_pos - 2]; + p1 = p0 + 1; + p2 = p1 + 1; + p3 = p2 + 1; + p4 = p3 + 1; + p5 = p4 + 1; + orig_line = block[j]; + + for (i = 0; i < block_size_x; i++) + { + result = (*(p0++) + *(p5++)) - 5 * (*(p1++) + *(p4++)) + 20 * (*(p2++) + *(p3++)); + + *orig_line = (imgpel) iClip1(max_imgpel_value, ((result + 16)>>5)); + *orig_line = (imgpel) ((*orig_line + *(cur_line++) + 1 ) >> 1); + orig_line++; + } + } +} + +/*! +************************************************************************ +* \brief +* Qpel vertical (0, 1) +************************************************************************ +*/ +static void get_luma_01(imgpel **block, imgpel **cur_imgY, int block_size_y, int block_size_x, int x_pos, int shift_x, int max_imgpel_value) +{ + imgpel *p0, *p1, *p2, *p3, *p4, *p5; + imgpel *orig_line, *cur_line; + int i, j; + int result; + int jj = 0; + p0 = &(cur_imgY[ - 2][x_pos]); + for (j = 0; j < block_size_y; j++) + { + p1 = p0 + shift_x; + p2 = p1 + shift_x; + p3 = p2 + shift_x; + p4 = p3 + shift_x; + p5 = p4 + shift_x; + orig_line = block[j]; + cur_line = &(cur_imgY[jj++][x_pos]); + + for (i = 0; i < block_size_x; i++) + { + result = (*(p0++) + *(p5++)) - 5 * (*(p1++) + *(p4++)) + 20 * (*(p2++) + *(p3++)); + + *orig_line = (imgpel) iClip1(max_imgpel_value, ((result + 16)>>5)); + *orig_line = (imgpel) ((*orig_line + *(cur_line++) + 1 ) >> 1); + orig_line++; + } + p0 = p1 - block_size_x; + } +} + + +/*! +************************************************************************ +* \brief +* Half vertical +************************************************************************ +*/ +static void get_luma_02(imgpel **block, imgpel **cur_imgY, int block_size_y, int block_size_x, int x_pos, int shift_x, int max_imgpel_value) +{ + imgpel *p0, *p1, *p2, *p3, *p4, *p5; + imgpel *orig_line; + int i, j; + int result; + p0 = &(cur_imgY[ - 2][x_pos]); + for (j = 0; j < block_size_y; j++) + { + p1 = p0 + shift_x; + p2 = p1 + shift_x; + p3 = p2 + shift_x; + p4 = p3 + shift_x; + p5 = p4 + shift_x; + orig_line = block[j]; + + for (i = 0; i < block_size_x; i++) + { + result = (*(p0++) + *(p5++)) - 5 * (*(p1++) + *(p4++)) + 20 * (*(p2++) + *(p3++)); + + *orig_line++ = (imgpel) iClip1(max_imgpel_value, ((result + 16)>>5)); + } + p0 = p1 - block_size_x; + } +} + + +/*! +************************************************************************ +* \brief +* Qpel vertical (0, 3) +************************************************************************ +*/ +static void get_luma_03(imgpel **block, imgpel **cur_imgY, int block_size_y, int block_size_x, int x_pos, int shift_x, int max_imgpel_value) +{ + imgpel *p0, *p1, *p2, *p3, *p4, *p5; + imgpel *orig_line, *cur_line; + int i, j; + int result; + int jj = 1; + + p0 = &(cur_imgY[ -2][x_pos]); + for (j = 0; j < block_size_y; j++) + { + p1 = p0 + shift_x; + p2 = p1 + shift_x; + p3 = p2 + shift_x; + p4 = p3 + shift_x; + p5 = p4 + shift_x; + orig_line = block[j]; + cur_line = &(cur_imgY[jj++][x_pos]); + + for (i = 0; i < block_size_x; i++) + { + result = (*(p0++) + *(p5++)) - 5 * (*(p1++) + *(p4++)) + 20 * (*(p2++) + *(p3++)); + + *orig_line = (imgpel) iClip1(max_imgpel_value, ((result + 16)>>5)); + *orig_line = (imgpel) ((*orig_line + *(cur_line++) + 1 ) >> 1); + orig_line++; + } + p0 = p1 - block_size_x; + } +} + +/*! +************************************************************************ +* \brief +* Hpel horizontal, Qpel vertical (2, 1) +************************************************************************ +*/ +static void get_luma_21(imgpel **block, imgpel **cur_imgY, int **tmp_res, int block_size_y, int block_size_x, int x_pos, int max_imgpel_value) +{ + int i, j; + /* Vertical & horizontal interpolation */ + int *tmp_line; + imgpel *p0, *p1, *p2, *p3, *p4, *p5; + int *x0, *x1, *x2, *x3, *x4, *x5; + imgpel *orig_line; + int result; + + int jj = -2; + + for (j = 0; j < block_size_y + 5; j++) + { + p0 = &cur_imgY[jj++][x_pos - 2]; + p1 = p0 + 1; + p2 = p1 + 1; + p3 = p2 + 1; + p4 = p3 + 1; + p5 = p4 + 1; + tmp_line = tmp_res[j]; + + for (i = 0; i < block_size_x; i++) + { + *(tmp_line++) = (*(p0++) + *(p5++)) - 5 * (*(p1++) + *(p4++)) + 20 * (*(p2++) + *(p3++)); + } + } + + jj = 2; + for (j = 0; j < block_size_y; j++) + { + tmp_line = tmp_res[jj++]; + x0 = tmp_res[j ]; + x1 = tmp_res[j + 1]; + x2 = tmp_res[j + 2]; + x3 = tmp_res[j + 3]; + x4 = tmp_res[j + 4]; + x5 = tmp_res[j + 5]; + orig_line = block[j]; + + for (i = 0; i < block_size_x; i++) + { + result = (*x0++ + *x5++) - 5 * (*x1++ + *x4++) + 20 * (*x2++ + *x3++); + + *orig_line = (imgpel) iClip1(max_imgpel_value, ((result + 512)>>10)); + *orig_line = (imgpel) ((*orig_line + iClip1(max_imgpel_value, ((*(tmp_line++) + 16) >> 5)) + 1 )>> 1); + orig_line++; + } + } +} + +/*! +************************************************************************ +* \brief +* Hpel horizontal, Hpel vertical (2, 2) +************************************************************************ +*/ +static void get_luma_22(imgpel **block, imgpel **cur_imgY, int **tmp_res, int block_size_y, int block_size_x, int x_pos, int max_imgpel_value) +{ + int i, j; + /* Vertical & horizontal interpolation */ + int *tmp_line; + imgpel *p0, *p1, *p2, *p3, *p4, *p5; + int *x0, *x1, *x2, *x3, *x4, *x5; + imgpel *orig_line; + int result; + + int jj = - 2; + + for (j = 0; j < block_size_y + 5; j++) + { + p0 = &cur_imgY[jj++][x_pos - 2]; + p1 = p0 + 1; + p2 = p1 + 1; + p3 = p2 + 1; + p4 = p3 + 1; + p5 = p4 + 1; + tmp_line = tmp_res[j]; + + for (i = 0; i < block_size_x; i++) + { + *(tmp_line++) = (*(p0++) + *(p5++)) - 5 * (*(p1++) + *(p4++)) + 20 * (*(p2++) + *(p3++)); + } + } + + for (j = 0; j < block_size_y; j++) + { + x0 = tmp_res[j ]; + x1 = tmp_res[j + 1]; + x2 = tmp_res[j + 2]; + x3 = tmp_res[j + 3]; + x4 = tmp_res[j + 4]; + x5 = tmp_res[j + 5]; + orig_line = block[j]; + + for (i = 0; i < block_size_x; i++) + { + result = (*x0++ + *x5++) - 5 * (*x1++ + *x4++) + 20 * (*x2++ + *x3++); + + *(orig_line++) = (imgpel) iClip1(max_imgpel_value, ((result + 512)>>10)); + } + } +} + +/*! +************************************************************************ +* \brief +* Hpel horizontal, Qpel vertical (2, 3) +************************************************************************ +*/ +static void get_luma_23(imgpel **block, imgpel **cur_imgY, int **tmp_res, int block_size_y, int block_size_x, int x_pos, int max_imgpel_value) +{ + int i, j; + /* Vertical & horizontal interpolation */ + int *tmp_line; + imgpel *p0, *p1, *p2, *p3, *p4, *p5; + int *x0, *x1, *x2, *x3, *x4, *x5; + imgpel *orig_line; + int result; + + int jj = -2; + + for (j = 0; j < block_size_y + 5; j++) + { + p0 = &cur_imgY[jj++][x_pos - 2]; + p1 = p0 + 1; + p2 = p1 + 1; + p3 = p2 + 1; + p4 = p3 + 1; + p5 = p4 + 1; + tmp_line = tmp_res[j]; + + for (i = 0; i < block_size_x; i++) + { + *(tmp_line++) = (*(p0++) + *(p5++)) - 5 * (*(p1++) + *(p4++)) + 20 * (*(p2++) + *(p3++)); + } + } + + jj = 3; + for (j = 0; j < block_size_y; j++) + { + tmp_line = tmp_res[jj++]; + x0 = tmp_res[j ]; + x1 = tmp_res[j + 1]; + x2 = tmp_res[j + 2]; + x3 = tmp_res[j + 3]; + x4 = tmp_res[j + 4]; + x5 = tmp_res[j + 5]; + orig_line = block[j]; + + for (i = 0; i < block_size_x; i++) + { + result = (*x0++ + *x5++) - 5 * (*x1++ + *x4++) + 20 * (*x2++ + *x3++); + + *orig_line = (imgpel) iClip1(max_imgpel_value, ((result + 512)>>10)); + *orig_line = (imgpel) ((*orig_line + iClip1(max_imgpel_value, ((*(tmp_line++) + 16) >> 5)) + 1 )>> 1); + orig_line++; + } + } +} + +/*! +************************************************************************ +* \brief +* Qpel horizontal, Hpel vertical (1, 2) +************************************************************************ +*/ +static void get_luma_12(imgpel **block, imgpel **cur_imgY, int **tmp_res, int block_size_y, int block_size_x, int x_pos, int shift_x, int max_imgpel_value) +{ + int i, j; + int *tmp_line; + imgpel *p0, *p1, *p2, *p3, *p4, *p5; + int *x0, *x1, *x2, *x3, *x4, *x5; + imgpel *orig_line; + int result; + + p0 = &(cur_imgY[ -2][x_pos - 2]); + for (j = 0; j < block_size_y; j++) + { + p1 = p0 + shift_x; + p2 = p1 + shift_x; + p3 = p2 + shift_x; + p4 = p3 + shift_x; + p5 = p4 + shift_x; + tmp_line = tmp_res[j]; + + for (i = 0; i < block_size_x + 5; i++) + { + *(tmp_line++) = (*(p0++) + *(p5++)) - 5 * (*(p1++) + *(p4++)) + 20 * (*(p2++) + *(p3++)); + } + p0 = p1 - (block_size_x + 5); + } + + for (j = 0; j < block_size_y; j++) + { + tmp_line = &tmp_res[j][2]; + orig_line = block[j]; + x0 = tmp_res[j]; + x1 = x0 + 1; + x2 = x1 + 1; + x3 = x2 + 1; + x4 = x3 + 1; + x5 = x4 + 1; + + for (i = 0; i < block_size_x; i++) + { + result = (*(x0++) + *(x5++)) - 5 * (*(x1++) + *(x4++)) + 20 * (*(x2++) + *(x3++)); + + *orig_line = (imgpel) iClip1(max_imgpel_value, ((result + 512)>>10)); + *orig_line = (imgpel) ((*orig_line + iClip1(max_imgpel_value, ((*(tmp_line++) + 16)>>5))+1)>>1); + orig_line ++; + } + } +} + + +/*! +************************************************************************ +* \brief +* Qpel horizontal, Hpel vertical (3, 2) +************************************************************************ +*/ +static void get_luma_32(imgpel **block, imgpel **cur_imgY, int **tmp_res, int block_size_y, int block_size_x, int x_pos, int shift_x, int max_imgpel_value) +{ + int i, j; + int *tmp_line; + imgpel *p0, *p1, *p2, *p3, *p4, *p5; + int *x0, *x1, *x2, *x3, *x4, *x5; + imgpel *orig_line; + int result; + + p0 = &(cur_imgY[ -2][x_pos - 2]); + for (j = 0; j < block_size_y; j++) + { + p1 = p0 + shift_x; + p2 = p1 + shift_x; + p3 = p2 + shift_x; + p4 = p3 + shift_x; + p5 = p4 + shift_x; + tmp_line = tmp_res[j]; + + for (i = 0; i < block_size_x + 5; i++) + { + *(tmp_line++) = (*(p0++) + *(p5++)) - 5 * (*(p1++) + *(p4++)) + 20 * (*(p2++) + *(p3++)); + } + p0 = p1 - (block_size_x + 5); + } + + for (j = 0; j < block_size_y; j++) + { + tmp_line = &tmp_res[j][3]; + orig_line = block[j]; + x0 = tmp_res[j]; + x1 = x0 + 1; + x2 = x1 + 1; + x3 = x2 + 1; + x4 = x3 + 1; + x5 = x4 + 1; + + for (i = 0; i < block_size_x; i++) + { + result = (*(x0++) + *(x5++)) - 5 * (*(x1++) + *(x4++)) + 20 * (*(x2++) + *(x3++)); + + *orig_line = (imgpel) iClip1(max_imgpel_value, ((result + 512)>>10)); + *orig_line = (imgpel) ((*orig_line + iClip1(max_imgpel_value, ((*(tmp_line++) + 16)>>5))+1)>>1); + orig_line ++; + } + } +} + +/*! +************************************************************************ +* \brief +* Qpel horizontal, Qpel vertical (3, 3) +************************************************************************ +*/ +static void get_luma_33(imgpel **block, imgpel **cur_imgY, int block_size_y, int block_size_x, int x_pos, int shift_x, int max_imgpel_value) +{ + int i, j; + imgpel *p0, *p1, *p2, *p3, *p4, *p5; + imgpel *orig_line; + int result; + + int jj = 1; + + for (j = 0; j < block_size_y; j++) + { + p0 = &cur_imgY[jj++][x_pos - 2]; + p1 = p0 + 1; + p2 = p1 + 1; + p3 = p2 + 1; + p4 = p3 + 1; + p5 = p4 + 1; + + orig_line = block[j]; + + for (i = 0; i < block_size_x; i++) + { + result = (*(p0++) + *(p5++)) - 5 * (*(p1++) + *(p4++)) + 20 * (*(p2++) + *(p3++)); + + *(orig_line++) = (imgpel) iClip1(max_imgpel_value, ((result + 16)>>5)); + } + } + + p0 = &(cur_imgY[-2][x_pos + 1]); + for (j = 0; j < block_size_y; j++) + { + p1 = p0 + shift_x; + p2 = p1 + shift_x; + p3 = p2 + shift_x; + p4 = p3 + shift_x; + p5 = p4 + shift_x; + orig_line = block[j]; + + for (i = 0; i < block_size_x; i++) + { + result = (*(p0++) + *(p5++)) - 5 * (*(p1++) + *(p4++)) + 20 * (*(p2++) + *(p3++)); + + *orig_line = (imgpel) ((*orig_line + iClip1(max_imgpel_value, ((result + 16) >> 5)) + 1) >> 1); + orig_line++; + } + p0 = p1 - block_size_x ; + } +} + + + +/*! +************************************************************************ +* \brief +* Qpel horizontal, Qpel vertical (1, 1) +************************************************************************ +*/ +static void get_luma_11(imgpel **block, imgpel **cur_imgY, int block_size_y, int block_size_x, int x_pos, int shift_x, int max_imgpel_value) +{ + int i, j; + imgpel *p0, *p1, *p2, *p3, *p4, *p5; + imgpel *orig_line; + int result; + + int jj = 0; + + for (j = 0; j < block_size_y; j++) + { + p0 = &cur_imgY[jj++][x_pos - 2]; + p1 = p0 + 1; + p2 = p1 + 1; + p3 = p2 + 1; + p4 = p3 + 1; + p5 = p4 + 1; + + orig_line = block[j]; + + for (i = 0; i < block_size_x; i++) + { + result = (*(p0++) + *(p5++)) - 5 * (*(p1++) + *(p4++)) + 20 * (*(p2++) + *(p3++)); + + *(orig_line++) = (imgpel) iClip1(max_imgpel_value, ((result + 16)>>5)); + } + } + + p0 = &(cur_imgY[-2][x_pos]); + for (j = 0; j < block_size_y; j++) + { + p1 = p0 + shift_x; + p2 = p1 + shift_x; + p3 = p2 + shift_x; + p4 = p3 + shift_x; + p5 = p4 + shift_x; + orig_line = block[j]; + + for (i = 0; i < block_size_x; i++) + { + result = (*(p0++) + *(p5++)) - 5 * (*(p1++) + *(p4++)) + 20 * (*(p2++) + *(p3++)); + + *orig_line = (imgpel) ((*orig_line + iClip1(max_imgpel_value, ((result + 16) >> 5)) + 1) >> 1); + orig_line++; + } + p0 = p1 - block_size_x ; + } +} + +/*! +************************************************************************ +* \brief +* Qpel horizontal, Qpel vertical (1, 3) +************************************************************************ +*/ +static void get_luma_13(imgpel **block, imgpel **cur_imgY, int block_size_y, int block_size_x, int x_pos, int shift_x, int max_imgpel_value) +{ + /* Diagonal interpolation */ + int i, j; + imgpel *p0, *p1, *p2, *p3, *p4, *p5; + imgpel *orig_line; + int result; + + int jj = 1; + + for (j = 0; j < block_size_y; j++) + { + p0 = &cur_imgY[jj++][x_pos - 2]; + p1 = p0 + 1; + p2 = p1 + 1; + p3 = p2 + 1; + p4 = p3 + 1; + p5 = p4 + 1; + + orig_line = block[j]; + + for (i = 0; i < block_size_x; i++) + { + result = (*(p0++) + *(p5++)) - 5 * (*(p1++) + *(p4++)) + 20 * (*(p2++) + *(p3++)); + + *(orig_line++) = (imgpel) iClip1(max_imgpel_value, ((result + 16)>>5)); + } + } + + p0 = &(cur_imgY[-2][x_pos]); + for (j = 0; j < block_size_y; j++) + { + p1 = p0 + shift_x; + p2 = p1 + shift_x; + p3 = p2 + shift_x; + p4 = p3 + shift_x; + p5 = p4 + shift_x; + orig_line = block[j]; + + for (i = 0; i < block_size_x; i++) + { + result = (*(p0++) + *(p5++)) - 5 * (*(p1++) + *(p4++)) + 20 * (*(p2++) + *(p3++)); + + *orig_line = (imgpel) ((*orig_line + iClip1(max_imgpel_value, ((result + 16) >> 5)) + 1) >> 1); + orig_line++; + } + p0 = p1 - block_size_x ; + } +} + +/*! +************************************************************************ +* \brief +* Qpel horizontal, Qpel vertical (3, 1) +************************************************************************ +*/ +static void get_luma_31(imgpel **block, imgpel **cur_imgY, int block_size_y, int block_size_x, int x_pos, int shift_x, int max_imgpel_value) +{ + /* Diagonal interpolation */ + int i, j; + imgpel *p0, *p1, *p2, *p3, *p4, *p5; + imgpel *orig_line; + int result; + + int jj = 0; + + for (j = 0; j < block_size_y; j++) + { + p0 = &cur_imgY[jj++][x_pos - 2]; + p1 = p0 + 1; + p2 = p1 + 1; + p3 = p2 + 1; + p4 = p3 + 1; + p5 = p4 + 1; + + orig_line = block[j]; + + for (i = 0; i < block_size_x; i++) + { + result = (*(p0++) + *(p5++)) - 5 * (*(p1++) + *(p4++)) + 20 * (*(p2++) + *(p3++)); + + *(orig_line++) = (imgpel) iClip1(max_imgpel_value, ((result + 16)>>5)); + } + } + + p0 = &(cur_imgY[-2][x_pos + 1]); + for (j = 0; j < block_size_y; j++) + { + p1 = p0 + shift_x; + p2 = p1 + shift_x; + p3 = p2 + shift_x; + p4 = p3 + shift_x; + p5 = p4 + shift_x; + orig_line = block[j]; + + for (i = 0; i < block_size_x; i++) + { + result = (*(p0++) + *(p5++)) - 5 * (*(p1++) + *(p4++)) + 20 * (*(p2++) + *(p3++)); + + *orig_line = (imgpel) ((*orig_line + iClip1(max_imgpel_value, ((result + 16) >> 5)) + 1) >> 1); + orig_line++; + } + p0 = p1 - block_size_x ; + } +} + +/*! +************************************************************************ +* \brief +* Interpolation of 1/4 subpixel +************************************************************************ +*/ +void get_block_luma(StorablePicture *curr_ref, int x_pos, int y_pos, int block_size_x, int block_size_y, imgpel **block, + int shift_x, int maxold_x, int maxold_y, int **tmp_res, int max_imgpel_value, imgpel no_ref_value, Macroblock *currMB) +{ + if (curr_ref->no_ref) { + //printf("list[ref_frame] is equal to 'no reference picture' before RAP\n"); + memset(block[0],no_ref_value,block_size_y * block_size_x * sizeof(imgpel)); + } + else + { + imgpel **cur_imgY = (currMB->p_Vid->separate_colour_plane_flag && currMB->p_Slice->colour_plane_id>PLANE_Y)? curr_ref->imgUV[currMB->p_Slice->colour_plane_id-1] : curr_ref->cur_imgY; + int dx = (x_pos & 3); + int dy = (y_pos & 3); + x_pos >>= 2; + y_pos >>= 2; + x_pos = iClip3(-18, maxold_x+2, x_pos); + y_pos = iClip3(-10, maxold_y+2, y_pos); + + if (dx == 0 && dy == 0) + get_block_00(&block[0][0], &cur_imgY[y_pos][x_pos], curr_ref->iLumaStride, block_size_y); + else + { /* other positions */ + if (dy == 0) /* No vertical interpolation */ + { + if (dx == 1) + get_luma_10(block, &cur_imgY[ y_pos], block_size_y, block_size_x, x_pos, max_imgpel_value); + else if (dx == 2) + get_luma_20(block, &cur_imgY[ y_pos], block_size_y, block_size_x, x_pos, max_imgpel_value); + else + get_luma_30(block, &cur_imgY[ y_pos], block_size_y, block_size_x, x_pos, max_imgpel_value); + } + else if (dx == 0) /* No horizontal interpolation */ + { + if (dy == 1) + get_luma_01(block, &cur_imgY[y_pos], block_size_y, block_size_x, x_pos, shift_x, max_imgpel_value); + else if (dy == 2) + get_luma_02(block, &cur_imgY[ y_pos], block_size_y, block_size_x, x_pos, shift_x, max_imgpel_value); + else + get_luma_03(block, &cur_imgY[ y_pos], block_size_y, block_size_x, x_pos, shift_x, max_imgpel_value); + } + else if (dx == 2) /* Vertical & horizontal interpolation */ + { + if (dy == 1) + get_luma_21(block, &cur_imgY[ y_pos], tmp_res, block_size_y, block_size_x, x_pos, max_imgpel_value); + else if (dy == 2) + get_luma_22(block, &cur_imgY[ y_pos], tmp_res, block_size_y, block_size_x, x_pos, max_imgpel_value); + else + get_luma_23(block, &cur_imgY[ y_pos], tmp_res, block_size_y, block_size_x, x_pos, max_imgpel_value); + } + else if (dy == 2) + { + if (dx == 1) + get_luma_12(block, &cur_imgY[ y_pos], tmp_res, block_size_y, block_size_x, x_pos, shift_x, max_imgpel_value); + else + get_luma_32(block, &cur_imgY[ y_pos], tmp_res, block_size_y, block_size_x, x_pos, shift_x, max_imgpel_value); + } + else + { + if (dx == 1) + { + if (dy == 1) + get_luma_11(block, &cur_imgY[ y_pos], block_size_y, block_size_x, x_pos, shift_x, max_imgpel_value); + else + get_luma_13(block, &cur_imgY[ y_pos], block_size_y, block_size_x, x_pos, shift_x, max_imgpel_value); + } + else + { + if (dy == 1) + get_luma_31(block, &cur_imgY[ y_pos], block_size_y, block_size_x, x_pos, shift_x, max_imgpel_value); + else + get_luma_33(block, &cur_imgY[ y_pos], block_size_y, block_size_x, x_pos, shift_x, max_imgpel_value); + } + } + } + } +} + + +/*! +************************************************************************ +* \brief +* Chroma (0,X) +************************************************************************ +*/ +static void get_chroma_0X(imgpel *block, imgpel *cur_img, int span, int block_size_y, int block_size_x, int w00, int w01, int total_scale) +{ + imgpel *cur_row = cur_img; + imgpel *nxt_row = cur_img + span; + + + imgpel *cur_line, *cur_line_p1; + imgpel *blk_line; + int result; + int i, j; + for (j = 0; j < block_size_y; j++) + { + cur_line = cur_row; + cur_line_p1 = nxt_row; + blk_line = block; + block += 16; + cur_row = nxt_row; + nxt_row += span; + for (i = 0; i < block_size_x; i++) + { + result = (w00 * *cur_line++ + w01 * *cur_line_p1++); + *(blk_line++) = (imgpel) rshift_rnd_sf(result, total_scale); + } + } +} + + +/*! +************************************************************************ +* \brief +* Chroma (X,0) +************************************************************************ +*/ +static void get_chroma_X0(imgpel *block, imgpel *cur_img, int span, int block_size_y, int block_size_x, int w00, int w10, int total_scale) +{ + imgpel *cur_row = cur_img; + + + imgpel *cur_line, *cur_line_p1; + imgpel *blk_line; + int result; + int i, j; + for (j = 0; j < block_size_y; j++) + { + cur_line = cur_row; + cur_line_p1 = cur_line + 1; + blk_line = block; + block += 16; + cur_row += span; + for (i = 0; i < block_size_x; i++) + { + result = (w00 * *cur_line++ + w10 * *cur_line_p1++); + //*(blk_line++) = (imgpel) iClip1(max_imgpel_value, rshift_rnd_sf(result, total_scale)); + *(blk_line++) = (imgpel) rshift_rnd_sf(result, total_scale); + } + } +} + +/*! +************************************************************************ +* \brief +* Chroma (X,X) +************************************************************************ +*/ +static void get_chroma_XY(imgpel *block, imgpel *cur_img, int span, int block_size_y, int block_size_x, int w00, int w01, int w10, int w11, int total_scale) +{ + imgpel *cur_row = cur_img; + imgpel *nxt_row = cur_img + span; + + + { + imgpel *cur_line, *cur_line_p1; + imgpel *blk_line; + int result; + int i, j; + for (j = 0; j < block_size_y; j++) + { + cur_line = cur_row; + cur_line_p1 = nxt_row; + blk_line = block; + block += 16; + cur_row = nxt_row; + nxt_row += span; + for (i = 0; i < block_size_x; i++) + { + result = (w00 * *(cur_line++) + w01 * *(cur_line_p1++)); + result += (w10 * *(cur_line ) + w11 * *(cur_line_p1 )); + *(blk_line++) = (imgpel) rshift_rnd_sf(result, total_scale); + } + } + } +} + +static void get_block_chroma(StorablePicture *curr_ref, int x_pos, int y_pos, int subpel_x, int subpel_y, int maxold_x, int maxold_y, + int block_size_x, int vert_block_size, int shiftpel_x, int shiftpel_y, + imgpel *block1, imgpel *block2, int total_scale, imgpel no_ref_value, VideoParameters *p_Vid) +{ + imgpel *img1,*img2; + short dx,dy; + int span = curr_ref->iChromaStride; + if (curr_ref->no_ref) { + //printf("list[ref_frame] is equal to 'no reference picture' before RAP\n"); + memset(block1,no_ref_value,vert_block_size * block_size_x * sizeof(imgpel)); + memset(block2,no_ref_value,vert_block_size * block_size_x * sizeof(imgpel)); + } + else + { + dx = (short) (x_pos & subpel_x); + dy = (short) (y_pos & subpel_y); + x_pos = x_pos >> shiftpel_x; + y_pos = y_pos >> shiftpel_y; + //clip MV; + assert(vert_block_size <=p_Vid->iChromaPadY && block_size_x<=p_Vid->iChromaPadX); + x_pos = iClip3(-p_Vid->iChromaPadX, maxold_x, x_pos); //16 + y_pos = iClip3(-p_Vid->iChromaPadY, maxold_y, y_pos); //8 + img1 = &curr_ref->imgUV[0][y_pos][x_pos]; + img2 = &curr_ref->imgUV[1][y_pos][x_pos]; + + if (dx == 0 && dy == 0) + { + get_block_00(block1, img1, span, vert_block_size); + get_block_00(block2, img2, span, vert_block_size); + } + else + { + short dxcur = (short) (subpel_x + 1 - dx); + short dycur = (short) (subpel_y + 1 - dy); + short w00 = dxcur * dycur; + if (dx == 0) + { + short w01 = dxcur * dy; + get_chroma_0X(block1, img1, span, vert_block_size, block_size_x, w00, w01, total_scale); + get_chroma_0X(block2, img2, span, vert_block_size, block_size_x, w00, w01, total_scale); + } + else if (dy == 0) + { + short w10 = dx * dycur; + get_chroma_X0(block1, img1, span, vert_block_size, block_size_x, w00, w10, total_scale); + get_chroma_X0(block2, img2, span, vert_block_size, block_size_x, w00, w10, total_scale); + } + else + { + short w01 = dxcur * dy; + short w10 = dx * dycur; + short w11 = dx * dy; + get_chroma_XY(block1, img1, span, vert_block_size, block_size_x, w00, w01, w10, w11, total_scale); + get_chroma_XY(block2, img2, span, vert_block_size, block_size_x, w00, w01, w10, w11, total_scale); + } + } + } +} + +void intra_cr_decoding(Macroblock *currMB, int yuv) +{ + VideoParameters *p_Vid = currMB->p_Vid; + Slice *currSlice = currMB->p_Slice; + StorablePicture *dec_picture = currSlice->dec_picture; + imgpel **curUV; + int uv; + int b8,b4; + int ioff, joff; + int i,j; + + currSlice->intra_pred_chroma(currMB);// last argument is ignored, computes needed data for both uv channels + + for(uv = 0; uv < 2; uv++) + { + currMB->itrans_4x4 = (currMB->is_lossless == FALSE) ? itrans4x4 : itrans4x4_ls; + + curUV = dec_picture->imgUV[uv]; + + if(currMB->is_lossless) + { + if ((currMB->c_ipred_mode == VERT_PRED_8)||(currMB->c_ipred_mode == HOR_PRED_8)) + Inv_Residual_trans_Chroma(currMB, uv) ; + else + { + for(j=0;jmb_cr_size_y;j++) + for(i=0;imb_cr_size_x;i++) + currSlice->mb_rres [uv+1][j][i]=currSlice->cof[uv+1][j][i]; + } + } + + if ((!(currMB->mb_type == SI4MB) && (currMB->cbp >> 4)) ) + { + for (b8 = 0; b8 < (p_Vid->num_uv_blocks); b8++) + { + for(b4 = 0; b4 < 4; b4++) + { + joff = subblk_offset_y[yuv][b8][b4]; + ioff = subblk_offset_x[yuv][b8][b4]; + + currMB->itrans_4x4(currMB, (ColorPlane) (uv + 1), ioff, joff); + + copy_image_data_4x4(&curUV[currMB->pix_c_y + joff], &(currSlice->mb_rec[uv + 1][joff]), currMB->pix_c_x + ioff, ioff); + } + } + currSlice->is_reset_coeff_cr = FALSE; + } + else if (currMB->mb_type == SI4MB) + { + itrans_sp_cr(currMB, uv); + + for (joff = 0; joff < 8; joff += 4) + { + for(ioff = 0; ioff < 8;ioff+=4) + { + currMB->itrans_4x4(currMB, (ColorPlane) (uv + 1), ioff, joff); + + copy_image_data_4x4(&curUV[currMB->pix_c_y + joff], &(currSlice->mb_rec[uv + 1][joff]), currMB->pix_c_x + ioff, ioff); + } + } + currSlice->is_reset_coeff_cr = FALSE; + } + else + { + for (b8 = 0; b8 < (p_Vid->num_uv_blocks); b8++) + { + for(b4 = 0; b4 < 4; b4++) + { + joff = subblk_offset_y[yuv][b8][b4]; + ioff = subblk_offset_x[yuv][b8][b4]; + + copy_image_data_4x4(&curUV[currMB->pix_c_y + joff], &(currSlice->mb_pred[uv + 1][joff]), currMB->pix_c_x + ioff, ioff); + } + } + } + } +} + +static inline void set_direct_references(const PixelPos *mb, char *l0_rFrame, char *l1_rFrame, PicMotionParams **mv_info) +{ + if (mb->available) + { + char *ref_idx = mv_info[mb->pos_y][mb->pos_x].ref_idx; + *l0_rFrame = ref_idx[LIST_0]; + *l1_rFrame = ref_idx[LIST_1]; + } + else + { + *l0_rFrame = -1; + *l1_rFrame = -1; + } +} + + +static void set_direct_references_mb_field(const PixelPos *mb, char *l0_rFrame, char *l1_rFrame, PicMotionParams **mv_info, Macroblock *mb_data) +{ + if (mb->available) + { + char *ref_idx = mv_info[mb->pos_y][mb->pos_x].ref_idx; + if (mb_data[mb->mb_addr].mb_field) + { + *l0_rFrame = ref_idx[LIST_0]; + *l1_rFrame = ref_idx[LIST_1]; + } + else + { + *l0_rFrame = (ref_idx[LIST_0] < 0) ? ref_idx[LIST_0] : ref_idx[LIST_0] * 2; + *l1_rFrame = (ref_idx[LIST_1] < 0) ? ref_idx[LIST_1] : ref_idx[LIST_1] * 2; + } + } + else + { + *l0_rFrame = -1; + *l1_rFrame = -1; + } +} + +static void set_direct_references_mb_frame(const PixelPos *mb, char *l0_rFrame, char *l1_rFrame, PicMotionParams **mv_info, Macroblock *mb_data) +{ + if (mb->available) + { + char *ref_idx = mv_info[mb->pos_y][mb->pos_x].ref_idx; + if (mb_data[mb->mb_addr].mb_field) + { + *l0_rFrame = (ref_idx[LIST_0] >> 1); + *l1_rFrame = (ref_idx[LIST_1] >> 1); + } + else + { + *l0_rFrame = ref_idx[LIST_0]; + *l1_rFrame = ref_idx[LIST_1]; + } + } + else + { + *l0_rFrame = -1; + *l1_rFrame = -1; + } +} + +void prepare_direct_params(Macroblock *currMB, StorablePicture *dec_picture, MotionVector *pmvl0, MotionVector *pmvl1, char *l0_rFrame, char *l1_rFrame) +{ + Slice *currSlice = currMB->p_Slice; + char l0_refA, l0_refB, l0_refC; + char l1_refA, l1_refB, l1_refC; + PicMotionParams **mv_info = dec_picture->mv_info; + + PixelPos mb[4]; + + get_neighbors(currMB, mb, 0, 0, 16); + + if (!currSlice->mb_aff_frame_flag) + { + set_direct_references(&mb[0], &l0_refA, &l1_refA, mv_info); + set_direct_references(&mb[1], &l0_refB, &l1_refB, mv_info); + set_direct_references(&mb[2], &l0_refC, &l1_refC, mv_info); + } + else + { + VideoParameters *p_Vid = currMB->p_Vid; + if (currMB->mb_field) + { + set_direct_references_mb_field(&mb[0], &l0_refA, &l1_refA, mv_info, p_Vid->mb_data); + set_direct_references_mb_field(&mb[1], &l0_refB, &l1_refB, mv_info, p_Vid->mb_data); + set_direct_references_mb_field(&mb[2], &l0_refC, &l1_refC, mv_info, p_Vid->mb_data); + } + else + { + set_direct_references_mb_frame(&mb[0], &l0_refA, &l1_refA, mv_info, p_Vid->mb_data); + set_direct_references_mb_frame(&mb[1], &l0_refB, &l1_refB, mv_info, p_Vid->mb_data); + set_direct_references_mb_frame(&mb[2], &l0_refC, &l1_refC, mv_info, p_Vid->mb_data); + } + } + + *l0_rFrame = (char) imin(imin((unsigned char) l0_refA, (unsigned char) l0_refB), (unsigned char) l0_refC); + *l1_rFrame = (char) imin(imin((unsigned char) l1_refA, (unsigned char) l1_refB), (unsigned char) l1_refC); + + if (*l0_rFrame >=0) + currMB->GetMVPredictor (currMB, mb, pmvl0, *l0_rFrame, mv_info, LIST_0, 0, 0, 16, 16); + + if (*l1_rFrame >=0) + currMB->GetMVPredictor (currMB, mb, pmvl1, *l1_rFrame, mv_info, LIST_1, 0, 0, 16, 16); +} + +static void check_motion_vector_range(const MotionVector *mv, Slice *pSlice) +{ + if (mv->mv_x > 8191 || mv->mv_x < -8192) + { + printf("WARNING! Horizontal motion vector %d is out of allowed range {-8192, 8191} in picture %d, macroblock %d\n", mv->mv_x, pSlice->p_Vid->number, pSlice->current_mb_nr); + //error("invalid stream: too big horizontal motion vector", 500); + } + + if (mv->mv_y > (pSlice->max_mb_vmv_r - 1) || mv->mv_y < (-pSlice->max_mb_vmv_r)) + { + printf("WARNING! Vertical motion vector %d is out of allowed range {%d, %d} in picture %d, macroblock %d\n", mv->mv_y, (-pSlice->max_mb_vmv_r), (pSlice->max_mb_vmv_r - 1), pSlice->p_Vid->number, pSlice->current_mb_nr); + //error("invalid stream: too big vertical motion vector", 500); + } +} + +static inline int check_vert_mv(int llimit, int vec1_y,int rlimit) +{ + int y_pos = vec1_y >> 2; + if(y_pos < llimit || y_pos > rlimit) + return 1; + else + return 0; +} + +static void perform_mc_single_wp(Macroblock *currMB, ColorPlane pl, StorablePicture *dec_picture, int pred_dir, int i, int j, int block_size_x, int block_size_y) +{ + VideoParameters *p_Vid = currMB->p_Vid; + Slice *currSlice = currMB->p_Slice; + seq_parameter_set_rbsp_t *active_sps = currSlice->active_sps; + imgpel **tmp_block_l0 = currSlice->tmp_block_l0; + imgpel **tmp_block_l1 = currSlice->tmp_block_l1; + static const int mv_mul = 16; // 4 * 4 + int i4 = currMB->block_x + i; + int j4 = currMB->block_y + j; + int type = currSlice->slice_type; + int chroma_format_idc = dec_picture->chroma_format_idc; + //===== Single List Prediction ===== + int ioff = (i << 2); + int joff = (j << 2); + PicMotionParams *mv_info = &dec_picture->mv_info[j4][i4]; + short ref_idx = mv_info->ref_idx[pred_dir]; + short ref_idx_wp = ref_idx; + MotionVector *mv_array = &mv_info->mv[pred_dir]; + int list_offset = currMB->list_offset; + StorablePicture *list = currSlice->listX[list_offset + pred_dir][ref_idx]; + int vec1_x, vec1_y; + // vars for get_block_luma + int maxold_x = dec_picture->size_x_m1; + int maxold_y = (currMB->mb_field) ? (dec_picture->size_y >> 1) - 1 : dec_picture->size_y_m1; + int shift_x = dec_picture->iLumaStride; + int **tmp_res = currSlice->tmp_res; + int max_imgpel_value = p_Vid->max_pel_value_comp[pl]; + imgpel no_ref_value = (imgpel) p_Vid->dc_pred_value_comp[pl]; + // + +#if ENABLE_DEC_STATS + p_Vid->dec_stats->histogram_mv[LIST_0][0][mv_array->mv_x]++; + p_Vid->dec_stats->histogram_mv[LIST_0][1][mv_array->mv_y]++; +#endif + + check_motion_vector_range(mv_array, currSlice); + vec1_x = i4 * mv_mul + mv_array->mv_x; + vec1_y = (currMB->block_y_aff + j) * mv_mul + mv_array->mv_y; + if(block_size_y > (p_Vid->iLumaPadY-4) && CheckVertMV(currMB, vec1_y, block_size_y)) + { + get_block_luma(list, vec1_x, vec1_y, block_size_x, BLOCK_SIZE_8x8, tmp_block_l0, shift_x,maxold_x,maxold_y,tmp_res,max_imgpel_value,no_ref_value, currMB); + get_block_luma(list, vec1_x, vec1_y+BLOCK_SIZE_8x8_SP, block_size_x, block_size_y-BLOCK_SIZE_8x8, tmp_block_l0+BLOCK_SIZE_8x8, shift_x,maxold_x,maxold_y,tmp_res,max_imgpel_value,no_ref_value, currMB); + } + else + get_block_luma(list, vec1_x, vec1_y, block_size_x, block_size_y, tmp_block_l0,shift_x,maxold_x,maxold_y,tmp_res,max_imgpel_value,no_ref_value, currMB); + + + { + int alpha_l0, wp_offset, wp_denom; + if (currMB->mb_field && ((p_Vid->active_pps->weighted_pred_flag&&(type==P_SLICE|| type == SP_SLICE))||(p_Vid->active_pps->weighted_bipred_idc==1 && (type==B_SLICE)))) + ref_idx_wp >>=1; + alpha_l0 = currSlice->wp_weight[pred_dir][ref_idx_wp][pl]; + wp_offset = currSlice->wp_offset[pred_dir][ref_idx_wp][pl]; + wp_denom = pl > 0 ? currSlice->chroma_log2_weight_denom : currSlice->luma_log2_weight_denom; + weighted_mc_prediction(&currSlice->mb_pred[pl][joff], tmp_block_l0, block_size_y, block_size_x, ioff, alpha_l0, wp_offset, wp_denom, max_imgpel_value); + } + + if ((chroma_format_idc != YUV400) && (chroma_format_idc != YUV444) ) + { + int ioff_cr,joff_cr,block_size_x_cr,block_size_y_cr; + int vec1_y_cr = vec1_y + ((active_sps->chroma_format_idc == 1)? currSlice->chroma_vector_adjustment[list_offset + pred_dir][ref_idx] : 0); + int total_scale = p_Vid->total_scale; + int maxold_x = dec_picture->size_x_cr_m1; + int maxold_y = (currMB->mb_field) ? (dec_picture->size_y_cr >> 1) - 1 : dec_picture->size_y_cr_m1; + int chroma_log2_weight = currSlice->chroma_log2_weight_denom; + if (p_Vid->mb_cr_size_x == MB_BLOCK_SIZE) + { + ioff_cr = ioff; + block_size_x_cr = block_size_x; + } + else + { + ioff_cr = ioff >> 1; + block_size_x_cr = block_size_x >> 1; + } + if (p_Vid->mb_cr_size_y == MB_BLOCK_SIZE) + { + joff_cr = joff; + block_size_y_cr = block_size_y; + } + else + { + joff_cr = joff >> 1; + block_size_y_cr = block_size_y >> 1; + } + no_ref_value = (imgpel)p_Vid->dc_pred_value_comp[1]; + { + int *weight = currSlice->wp_weight[pred_dir][ref_idx_wp]; + int *offset = currSlice->wp_offset[pred_dir][ref_idx_wp]; + get_block_chroma(list,vec1_x,vec1_y_cr,p_Vid->subpel_x,p_Vid->subpel_y,maxold_x,maxold_y,block_size_x_cr,block_size_y_cr,p_Vid->shiftpel_x,p_Vid->shiftpel_y,&tmp_block_l0[0][0],&tmp_block_l1[0][0] ,total_scale,no_ref_value,p_Vid); + weighted_mc_prediction(&currSlice->mb_pred[1][joff_cr], tmp_block_l0, block_size_y_cr, block_size_x_cr, ioff_cr, weight[1], offset[1], chroma_log2_weight, p_Vid->max_pel_value_comp[1]); + weighted_mc_prediction(&currSlice->mb_pred[2][joff_cr], tmp_block_l1, block_size_y_cr, block_size_x_cr, ioff_cr, weight[2], offset[2], chroma_log2_weight, p_Vid->max_pel_value_comp[2]); + } + } +} + + +static void perform_mc_single(Macroblock *currMB, ColorPlane pl, StorablePicture *dec_picture, int pred_dir, int i, int j, int block_size_x, int block_size_y) +{ + VideoParameters *p_Vid = currMB->p_Vid; + Slice *currSlice = currMB->p_Slice; + seq_parameter_set_rbsp_t *active_sps = currSlice->active_sps; + imgpel **tmp_block_l0 = currSlice->tmp_block_l0; + imgpel **tmp_block_l1 = currSlice->tmp_block_l1; + static const int mv_mul = 16; // 4 * 4 + int i4 = currMB->block_x + i; + int j4 = currMB->block_y + j; + int chroma_format_idc = dec_picture->chroma_format_idc; + //===== Single List Prediction ===== + int ioff = (i << 2); + int joff = (j << 2); + PicMotionParams *mv_info = &dec_picture->mv_info[j4][i4]; + MotionVector *mv_array = &mv_info->mv[pred_dir]; + short ref_idx = mv_info->ref_idx[pred_dir]; + int list_offset = currMB->list_offset; + StorablePicture *list = currSlice->listX[list_offset + pred_dir][ref_idx]; + int vec1_x, vec1_y; + // vars for get_block_luma + int maxold_x = dec_picture->size_x_m1; + int maxold_y = (currMB->mb_field) ? (dec_picture->size_y >> 1) - 1 : dec_picture->size_y_m1; + int shift_x = dec_picture->iLumaStride; + int **tmp_res = currSlice->tmp_res; + int max_imgpel_value = p_Vid->max_pel_value_comp[pl]; + imgpel no_ref_value = (imgpel) p_Vid->dc_pred_value_comp[pl]; + // +#if ENABLE_DEC_STATS + p_Vid->dec_stats->histogram_mv[LIST_0][0][mv_array->mv_x]++; + p_Vid->dec_stats->histogram_mv[LIST_0][1][mv_array->mv_y]++; +#endif + + //if (iabs(mv_array->mv_x) > 4 * 126 || iabs(mv_array->mv_y) > 4 * 126) + //printf("motion vector %d %d\n", mv_array->mv_x, mv_array->mv_y); + + check_motion_vector_range(mv_array, currSlice); + vec1_x = i4 * mv_mul + mv_array->mv_x; + vec1_y = (currMB->block_y_aff + j) * mv_mul + mv_array->mv_y; + + if (block_size_y > (p_Vid->iLumaPadY-4) && CheckVertMV(currMB, vec1_y, block_size_y)) + { + get_block_luma(list, vec1_x, vec1_y, block_size_x, BLOCK_SIZE_8x8, tmp_block_l0, shift_x,maxold_x,maxold_y,tmp_res,max_imgpel_value,no_ref_value, currMB); + get_block_luma(list, vec1_x, vec1_y+BLOCK_SIZE_8x8_SP, block_size_x, block_size_y-BLOCK_SIZE_8x8, tmp_block_l0+BLOCK_SIZE_8x8, shift_x,maxold_x,maxold_y,tmp_res,max_imgpel_value,no_ref_value, currMB); + } + else + get_block_luma(list, vec1_x, vec1_y, block_size_x, block_size_y, tmp_block_l0,shift_x,maxold_x,maxold_y,tmp_res,max_imgpel_value,no_ref_value, currMB); + + mc_prediction(&currSlice->mb_pred[pl][joff], tmp_block_l0, block_size_y, block_size_x, ioff); + + if ((chroma_format_idc != YUV400) && (chroma_format_idc != YUV444) ) + { + int ioff_cr,joff_cr,block_size_x_cr,block_size_y_cr; + int vec1_y_cr = vec1_y + ((active_sps->chroma_format_idc == 1)? currSlice->chroma_vector_adjustment[list_offset + pred_dir][ref_idx] : 0); + int total_scale = p_Vid->total_scale; + int maxold_x = dec_picture->size_x_cr_m1; + int maxold_y = (currMB->mb_field) ? (dec_picture->size_y_cr >> 1) - 1 : dec_picture->size_y_cr_m1; + if (p_Vid->mb_cr_size_x == MB_BLOCK_SIZE) + { + ioff_cr = ioff; + block_size_x_cr = block_size_x; + } + else + { + ioff_cr = ioff >> 1; + block_size_x_cr = block_size_x >> 1; + } + if (p_Vid->mb_cr_size_y == MB_BLOCK_SIZE) + { + joff_cr = joff; + block_size_y_cr = block_size_y; + } + else + { + joff_cr = joff >> 1; + block_size_y_cr = block_size_y >> 1; + } + no_ref_value = (imgpel)p_Vid->dc_pred_value_comp[1]; + get_block_chroma(list,vec1_x,vec1_y_cr,p_Vid->subpel_x,p_Vid->subpel_y,maxold_x,maxold_y,block_size_x_cr,block_size_y_cr,p_Vid->shiftpel_x,p_Vid->shiftpel_y,&tmp_block_l0[0][0],&tmp_block_l1[0][0] ,total_scale,no_ref_value,p_Vid); + mc_prediction(&currSlice->mb_pred[1][joff_cr], tmp_block_l0, block_size_y_cr, block_size_x_cr, ioff_cr); + mc_prediction(&currSlice->mb_pred[2][joff_cr], tmp_block_l1, block_size_y_cr, block_size_x_cr, ioff_cr); + } +} + +static void perform_mc_bi_wp(Macroblock *currMB, ColorPlane pl, StorablePicture *dec_picture, int i, int j, int block_size_x, int block_size_y) +{ + static const int mv_mul = 16; + int vec1_x, vec1_y, vec2_x, vec2_y; + VideoParameters *p_Vid = currMB->p_Vid; + Slice *currSlice = currMB->p_Slice; + + int weighted_bipred_idc = p_Vid->active_pps->weighted_bipred_idc; + int block_y_aff = currMB->block_y_aff; + int i4 = currMB->block_x + i; + int j4 = currMB->block_y + j; + int ioff = (i << 2); + int joff = (j << 2); + int chroma_format_idc = dec_picture->chroma_format_idc; + int list_offset = currMB->list_offset; + PicMotionParams *mv_info = &dec_picture->mv_info[j4][i4]; + MotionVector *l0_mv_array = &mv_info->mv[LIST_0]; + MotionVector *l1_mv_array = &mv_info->mv[LIST_1]; + short l0_refframe = mv_info->ref_idx[LIST_0]; + short l1_refframe = mv_info->ref_idx[LIST_1]; + int l0_ref_idx = (currMB->mb_field && weighted_bipred_idc == 1) ? l0_refframe >> 1: l0_refframe; + int l1_ref_idx = (currMB->mb_field && weighted_bipred_idc == 1) ? l1_refframe >> 1: l1_refframe; + + + /// WP Parameters + int wt_list_offset = (weighted_bipred_idc==2)? list_offset : 0; + int *weight0 = currSlice->wbp_weight[LIST_0 + wt_list_offset][l0_ref_idx][l1_ref_idx]; + int *weight1 = currSlice->wbp_weight[LIST_1 + wt_list_offset][l0_ref_idx][l1_ref_idx]; + int *offset0 = currSlice->wp_offset[LIST_0 + wt_list_offset][l0_ref_idx]; + int *offset1 = currSlice->wp_offset[LIST_1 + wt_list_offset][l1_ref_idx]; + int maxold_y = (currMB->mb_field) ? (dec_picture->size_y >> 1) - 1 : dec_picture->size_y_m1; + int pady = p_Vid->iLumaPadY; + int rlimit = maxold_y + pady - block_size_y - 2; + int llimit = 2 - pady; + int big_blocky = block_size_y > (pady - 4); + StorablePicture *list0 = currSlice->listX[LIST_0 + list_offset][l0_refframe]; + StorablePicture *list1 = currSlice->listX[LIST_1 + list_offset][l1_refframe]; + imgpel **tmp_block_l0 = currSlice->tmp_block_l0; + imgpel *block0 = tmp_block_l0[0]; + imgpel **tmp_block_l1 = currSlice->tmp_block_l1; + imgpel *block1 = tmp_block_l1[0]; + imgpel **tmp_block_l2 = currSlice->tmp_block_l2; + imgpel *block2 = tmp_block_l2[0]; + imgpel **tmp_block_l3 = currSlice->tmp_block_l3; + imgpel *block3 = tmp_block_l3[0]; + int wp_offset; + int wp_denom; + + // vars for get_block_luma + int maxold_x = dec_picture->size_x_m1; + int shift_x = dec_picture->iLumaStride; + int **tmp_res = currSlice->tmp_res; + int max_imgpel_value = p_Vid->max_pel_value_comp[pl]; + imgpel no_ref_value = (imgpel) p_Vid->dc_pred_value_comp[pl]; + + check_motion_vector_range(l0_mv_array, currSlice); + check_motion_vector_range(l1_mv_array, currSlice); + vec1_x = i4 * mv_mul + l0_mv_array->mv_x; + vec2_x = i4 * mv_mul + l1_mv_array->mv_x; + vec1_y = (block_y_aff + j) * mv_mul + l0_mv_array->mv_y; + vec2_y = (block_y_aff + j) * mv_mul + l1_mv_array->mv_y; + + if (big_blocky && check_vert_mv(llimit, vec1_y, rlimit)) + { + get_block_luma(list0, vec1_x, vec1_y, block_size_x, BLOCK_SIZE_8x8, tmp_block_l0, shift_x,maxold_x,maxold_y,tmp_res,max_imgpel_value,no_ref_value, currMB); + get_block_luma(list0, vec1_x, vec1_y+BLOCK_SIZE_8x8_SP, block_size_x, block_size_y-BLOCK_SIZE_8x8, tmp_block_l0+BLOCK_SIZE_8x8, shift_x,maxold_x,maxold_y,tmp_res,max_imgpel_value,no_ref_value, currMB); + } + else + get_block_luma(list0, vec1_x, vec1_y, block_size_x, block_size_y, tmp_block_l0,shift_x,maxold_x,maxold_y,tmp_res,max_imgpel_value,no_ref_value, currMB); + if (big_blocky && check_vert_mv(llimit, vec2_y,rlimit)) + { + get_block_luma(list1, vec2_x, vec2_y, block_size_x, BLOCK_SIZE_8x8, tmp_block_l1, shift_x,maxold_x,maxold_y,tmp_res,max_imgpel_value,no_ref_value, currMB); + get_block_luma(list1, vec2_x, vec2_y+BLOCK_SIZE_8x8_SP, block_size_x, block_size_y-BLOCK_SIZE_8x8, tmp_block_l1 + BLOCK_SIZE_8x8, shift_x,maxold_x,maxold_y,tmp_res,max_imgpel_value,no_ref_value, currMB); + } + else + get_block_luma(list1, vec2_x, vec2_y, block_size_x, block_size_y, tmp_block_l1,shift_x,maxold_x,maxold_y,tmp_res,max_imgpel_value,no_ref_value, currMB); + + + wp_offset = ((offset0[pl] + offset1[pl] + 1) >>1); + wp_denom = pl > 0 ? currSlice->chroma_log2_weight_denom : currSlice->luma_log2_weight_denom; + weighted_bi_prediction(&currSlice->mb_pred[pl][joff][ioff], block0, block1, block_size_y, block_size_x, weight0[pl], weight1[pl], wp_offset, wp_denom + 1, max_imgpel_value); + + if ((chroma_format_idc != YUV400) && (chroma_format_idc != YUV444) ) + { + int ioff_cr, joff_cr,block_size_y_cr,block_size_x_cr,vec2_y_cr,vec1_y_cr; + int maxold_x = dec_picture->size_x_cr_m1; + int maxold_y = (currMB->mb_field) ? (dec_picture->size_y_cr >> 1) - 1 : dec_picture->size_y_cr_m1; + int shiftpel_x = p_Vid->shiftpel_x; + int shiftpel_y = p_Vid->shiftpel_y; + int subpel_x = p_Vid->subpel_x; + int subpel_y = p_Vid->subpel_y; + int total_scale = p_Vid->total_scale; + int chroma_log2 = currSlice->chroma_log2_weight_denom + 1; + + if (p_Vid->mb_cr_size_x == MB_BLOCK_SIZE) + { + ioff_cr = ioff; + block_size_x_cr = block_size_x; + } + else + { + ioff_cr = ioff >> 1; + block_size_x_cr = block_size_x >> 1; + } + + if (p_Vid->mb_cr_size_y == MB_BLOCK_SIZE) + { + joff_cr = joff; + block_size_y_cr = block_size_y; + } + else + { + joff_cr = joff >> 1; + block_size_y_cr = block_size_y >> 1; + } + if (chroma_format_idc == 1) + { + vec1_y_cr = vec1_y + currSlice->chroma_vector_adjustment[LIST_0 + list_offset][l0_refframe]; + vec2_y_cr = vec2_y + currSlice->chroma_vector_adjustment[LIST_1 + list_offset][l1_refframe]; + } + else + { + vec1_y_cr = vec1_y; + vec2_y_cr = vec2_y; + } + no_ref_value = (imgpel)p_Vid->dc_pred_value_comp[1]; + + wp_offset = ((offset0[1] + offset1[1] + 1) >>1); + get_block_chroma(list0,vec1_x,vec1_y_cr,subpel_x,subpel_y,maxold_x,maxold_y,block_size_x_cr,block_size_y_cr,shiftpel_x,shiftpel_y,block0,block2 ,total_scale,no_ref_value,p_Vid); + get_block_chroma(list1,vec2_x,vec2_y_cr,subpel_x,subpel_y,maxold_x,maxold_y,block_size_x_cr,block_size_y_cr,shiftpel_x,shiftpel_y,block1,block3 ,total_scale,no_ref_value,p_Vid); + weighted_bi_prediction(&currSlice->mb_pred[1][joff_cr][ioff_cr],block0,block1,block_size_y_cr,block_size_x_cr,weight0[1],weight1[1],wp_offset,chroma_log2,p_Vid->max_pel_value_comp[1]); + wp_offset = ((offset0[2] + offset1[2] + 1) >>1); + weighted_bi_prediction(&currSlice->mb_pred[2][joff_cr][ioff_cr],block2,block3,block_size_y_cr,block_size_x_cr,weight0[2],weight1[2],wp_offset,chroma_log2,p_Vid->max_pel_value_comp[2]); + } +} + +static void perform_mc_bi(Macroblock *currMB, ColorPlane pl, StorablePicture *dec_picture, int i, int j, int block_size_x, int block_size_y) +{ + static const int mv_mul = 16; + int vec1_x=0, vec1_y=0, vec2_x=0, vec2_y=0; + VideoParameters *p_Vid = currMB->p_Vid; + Slice *currSlice = currMB->p_Slice; + + int block_y_aff = currMB->block_y_aff; + int i4 = currMB->block_x + i; + int j4 = currMB->block_y + j; + int ioff = (i << 2); + int joff = (j << 2); + int chroma_format_idc = dec_picture->chroma_format_idc; + PicMotionParams *mv_info = &dec_picture->mv_info[j4][i4]; + MotionVector *l0_mv_array = &mv_info->mv[LIST_0]; + MotionVector *l1_mv_array = &mv_info->mv[LIST_1]; + short l0_refframe = mv_info->ref_idx[LIST_0]; + short l1_refframe = mv_info->ref_idx[LIST_1]; + int list_offset = currMB->list_offset; + + int maxold_y = (currMB->mb_field) ? (dec_picture->size_y >> 1) - 1 : dec_picture->size_y_m1; + int pady = p_Vid->iLumaPadY; + int rlimit = maxold_y + pady - block_size_y - 2; + int llimit = 2 - pady; + int big_blocky = block_size_y > (pady - 4); + StorablePicture *list0 = currSlice->listX[LIST_0 + list_offset][l0_refframe]; + StorablePicture *list1 = currSlice->listX[LIST_1 + list_offset][l1_refframe]; + imgpel **tmp_block_l0 = currSlice->tmp_block_l0; + imgpel *block0 = tmp_block_l0[0]; + imgpel **tmp_block_l1 = currSlice->tmp_block_l1; + imgpel *block1 = tmp_block_l1[0]; + imgpel **tmp_block_l2 = currSlice->tmp_block_l2; + imgpel *block2 = tmp_block_l2[0]; + imgpel **tmp_block_l3 = currSlice->tmp_block_l3; + imgpel *block3 = tmp_block_l3[0]; + // vars for get_block_luma + int maxold_x = dec_picture->size_x_m1; + int shift_x = dec_picture->iLumaStride; + int **tmp_res = currSlice->tmp_res; + int max_imgpel_value = p_Vid->max_pel_value_comp[pl]; + imgpel no_ref_value = (imgpel) p_Vid->dc_pred_value_comp[pl]; + check_motion_vector_range(l0_mv_array, currSlice); + check_motion_vector_range(l1_mv_array, currSlice); + vec1_x = i4 * mv_mul + l0_mv_array->mv_x; + vec2_x = i4 * mv_mul + l1_mv_array->mv_x; + vec1_y = (block_y_aff + j) * mv_mul + l0_mv_array->mv_y; + vec2_y = (block_y_aff + j) * mv_mul + l1_mv_array->mv_y; + if (big_blocky && check_vert_mv(llimit, vec1_y, rlimit)) + { + get_block_luma(list0, vec1_x, vec1_y, block_size_x, BLOCK_SIZE_8x8, tmp_block_l0, shift_x,maxold_x,maxold_y,tmp_res,max_imgpel_value,no_ref_value, currMB); + get_block_luma(list0, vec1_x, vec1_y+BLOCK_SIZE_8x8_SP, block_size_x, block_size_y-BLOCK_SIZE_8x8, tmp_block_l0+BLOCK_SIZE_8x8, shift_x,maxold_x,maxold_y,tmp_res,max_imgpel_value,no_ref_value, currMB); + } + else + get_block_luma(list0, vec1_x, vec1_y, block_size_x, block_size_y, tmp_block_l0,shift_x,maxold_x,maxold_y,tmp_res,max_imgpel_value,no_ref_value, currMB); + if (big_blocky && check_vert_mv(llimit, vec2_y,rlimit)) + { + get_block_luma(list1, vec2_x, vec2_y, block_size_x, BLOCK_SIZE_8x8, tmp_block_l1, shift_x,maxold_x,maxold_y,tmp_res,max_imgpel_value,no_ref_value, currMB); + get_block_luma(list1, vec2_x, vec2_y+BLOCK_SIZE_8x8_SP, block_size_x, block_size_y-BLOCK_SIZE_8x8, tmp_block_l1 + BLOCK_SIZE_8x8, shift_x,maxold_x,maxold_y,tmp_res,max_imgpel_value,no_ref_value, currMB); + } + else + get_block_luma(list1, vec2_x, vec2_y, block_size_x, block_size_y, tmp_block_l1,shift_x,maxold_x,maxold_y,tmp_res,max_imgpel_value,no_ref_value, currMB); + bi_prediction(&currSlice->mb_pred[pl][joff],tmp_block_l0,tmp_block_l1, block_size_y, block_size_x, ioff); + + if ((chroma_format_idc != YUV400) && (chroma_format_idc != YUV444) ) + { + int ioff_cr, joff_cr,block_size_y_cr,block_size_x_cr,vec2_y_cr,vec1_y_cr; + int chroma_format_idc = p_Vid->active_sps->chroma_format_idc; + int maxold_x = dec_picture->size_x_cr_m1; + int maxold_y = (currMB->mb_field) ? (dec_picture->size_y_cr >> 1) - 1 : dec_picture->size_y_cr_m1; + int shiftpel_x = p_Vid->shiftpel_x; + int shiftpel_y = p_Vid->shiftpel_y; + int subpel_x = p_Vid->subpel_x; + int subpel_y = p_Vid->subpel_y; + int total_scale = p_Vid->total_scale; + if (p_Vid->mb_cr_size_x == MB_BLOCK_SIZE) + { + ioff_cr = ioff; + block_size_x_cr = block_size_x; + } + else + { + ioff_cr = ioff >> 1; + block_size_x_cr = block_size_x >> 1; + } + if (p_Vid->mb_cr_size_y == MB_BLOCK_SIZE) + { + joff_cr = joff; + block_size_y_cr = block_size_y; + } + else + { + joff_cr = joff >> 1; + block_size_y_cr = block_size_y >> 1; + } + if (chroma_format_idc == 1) + { + vec1_y_cr = vec1_y + currSlice->chroma_vector_adjustment[LIST_0 + list_offset][l0_refframe]; + vec2_y_cr = vec2_y + currSlice->chroma_vector_adjustment[LIST_1 + list_offset][l1_refframe]; + } + else + { + vec1_y_cr = vec1_y; + vec2_y_cr = vec2_y; + } + no_ref_value = (imgpel)p_Vid->dc_pred_value_comp[1]; + get_block_chroma(list0,vec1_x,vec1_y_cr,subpel_x,subpel_y,maxold_x,maxold_y,block_size_x_cr,block_size_y_cr,shiftpel_x,shiftpel_y,block0,block2 ,total_scale,no_ref_value,p_Vid); + get_block_chroma(list1,vec2_x,vec2_y_cr,subpel_x,subpel_y,maxold_x,maxold_y,block_size_x_cr,block_size_y_cr,shiftpel_x,shiftpel_y,block1,block3 ,total_scale,no_ref_value,p_Vid); + bi_prediction(&currSlice->mb_pred[1][joff_cr],tmp_block_l0,tmp_block_l1, block_size_y_cr, block_size_x_cr, ioff_cr); + bi_prediction(&currSlice->mb_pred[2][joff_cr],tmp_block_l2,tmp_block_l3, block_size_y_cr, block_size_x_cr, ioff_cr); + } +} + +void perform_mc(Macroblock *currMB, ColorPlane pl, StorablePicture *dec_picture, int pred_dir, int i, int j, int block_size_x, int block_size_y) +{ + + Slice *currSlice = currMB->p_Slice; + assert (pred_dir<=2); + + /* Edited by joao Carreira */ + if( currMB->p_Vid->p_Inp->error_prop ) + ChangeInterViewPrediction(currMB, dec_picture, pred_dir, i, j, block_size_x, block_size_y ); + + if (pred_dir != 2) + { + if (currSlice->weighted_pred_flag) + perform_mc_single_wp(currMB, pl, dec_picture, pred_dir, i, j, block_size_x, block_size_y); + else + perform_mc_single(currMB, pl, dec_picture, pred_dir, i, j, block_size_x, block_size_y); + } + else + { + + if (currSlice->weighted_bipred_idc) + perform_mc_bi_wp(currMB, pl, dec_picture, i, j, block_size_x, block_size_y); + else + perform_mc_bi(currMB, pl, dec_picture, i, j, block_size_x, block_size_y); + } +} + + + diff --git a/ldecod/src/nal.c b/ldecod/src/nal.c new file mode 100644 index 0000000..ac9d00a --- /dev/null +++ b/ldecod/src/nal.c @@ -0,0 +1,120 @@ + +/*! + ************************************************************************ + * \file nal.c + * + * \brief + * Converts Encapsulated Byte Sequence Packets (EBSP) to Raw Byte + * Sequence Packets (RBSP), and then onto String Of Data Bits (SODB) + * + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * - Shankar L. Regunathan + * - Tobias Oelbaum +************************************************************************ + */ + +#include "contributors.h" +#include "global.h" + + /*! + ************************************************************************ + * \brief + * Converts RBSP to string of data bits + * \param streamBuffer + * pointer to buffer containing data + * \param last_byte_pos + * position of the last byte containing data. + * \return last_byte_pos + * position of the last byte pos. If the last-byte was entirely a stuffing byte, + * it is removed, and the last_byte_pos is updated. + * +************************************************************************/ + +int RBSPtoSODB(byte *streamBuffer, int last_byte_pos) +{ + int ctr_bit, bitoffset; + + bitoffset = 0; + //find trailing 1 + ctr_bit = (streamBuffer[last_byte_pos-1] & (0x01< 0x03)) + return -1; + //if cabac_zero_word is used, the final byte of this NAL unit(0x03) is discarded, and the last two bytes of RBSP must be 0x0000 + if(i == end_bytepos-1) + return j; + + ++i; + count = 0; + } + streamBuffer[j] = streamBuffer[i]; + if(streamBuffer[i] == 0x00) + ++count; + else + count = 0; + ++j; + } + + return j; +} diff --git a/ldecod/src/nalu.c b/ldecod/src/nalu.c new file mode 100644 index 0000000..983ba71 --- /dev/null +++ b/ldecod/src/nalu.c @@ -0,0 +1,187 @@ + +/*! +************************************************************************ +* \file nalu.c +* +* \brief +* Decoder NALU support functions +* +* \author +* Main contributors (see contributors.h for copyright, address and affiliation details) +* - Stephan Wenger +************************************************************************ +*/ + +#include "global.h" +#include "annexb.h" +#include "nalu.h" +#include "memalloc.h" +#include "rtp.h" +#if (MVC_EXTENSION_ENABLE) +#include "vlc.h" +#endif + +#include "stream_parser.h" + +/*! +************************************************************************************* +* \brief +* Converts a NALU to an RBSP +* +* \param +* nalu: nalu structure to be filled +* +* \return +* length of the RBSP in bytes +************************************************************************************* +*/ + +static int NALUtoRBSP (NALU_t *nalu) +{ + assert (nalu != NULL); + + nalu->len = EBSPtoRBSP (nalu->buf, nalu->len, 1) ; + + return nalu->len ; +} + +/*! +************************************************************************ +* \brief +* Read the next NAL unit (with error handling) +************************************************************************ +*/ +int read_next_nalu(VideoParameters *p_Vid, NALU_t *nalu) +{ + InputParameters *p_Inp = p_Vid->p_Inp; + int ret; + + do{ + switch( p_Inp->FileFormat ) + { + default: + case PAR_OF_ANNEXB: + ret = get_annex_b_NALU(p_Vid, nalu, p_Vid->annex_b); + break; + case PAR_OF_RTP: + ret = GetRTPNALU(p_Vid, nalu, p_Vid->BitStreamFile); + break; + } + + if (ret < 0) + { + snprintf(errortext, ET_SIZE, "Error while getting the NALU in file format %s, exit\n", p_Inp->FileFormat==PAR_OF_ANNEXB?"Annex B":"RTP"); + error (errortext, 601); + } + if (ret == 0) + { + //FreeNALU(nalu); + return 0; + } + + //In some cases, zero_byte shall be present. If current NALU is a VCL NALU, we can't tell + //whether it is the first VCL NALU at this point, so only non-VCL NAL unit is checked here. + CheckZeroByteNonVCL(p_Vid, nalu); + + ret = NALUtoRBSP(nalu); + + if (ret < 0) + error ("Invalid startcode emulation prevention found.", 602); + + // Got a NALU + if (nalu->forbidden_bit) + { + error ("Found NALU with forbidden_bit set, bit error?", 603); + } + + }while( !parse_nalu(p_Vid, nalu) ); + + return nalu->len; +} + +void CheckZeroByteNonVCL(VideoParameters *p_Vid, NALU_t *nalu) +{ + int CheckZeroByte=0; + + //This function deals only with non-VCL NAL units + if(nalu->nal_unit_type>=1&&nalu->nal_unit_type<=5) + return; + + //for SPS and PPS, zero_byte shall exist + if(nalu->nal_unit_type==NALU_TYPE_SPS || nalu->nal_unit_type==NALU_TYPE_PPS) + CheckZeroByte=1; + //check the possibility of the current NALU to be the start of a new access unit, according to 7.4.1.2.3 + if(nalu->nal_unit_type==NALU_TYPE_AUD || nalu->nal_unit_type==NALU_TYPE_SPS || + nalu->nal_unit_type==NALU_TYPE_PPS || nalu->nal_unit_type==NALU_TYPE_SEI || + (nalu->nal_unit_type>=13 && nalu->nal_unit_type<=18)) + { + if(p_Vid->LastAccessUnitExists) + { + p_Vid->LastAccessUnitExists=0; //deliver the last access unit to decoder + p_Vid->NALUCount=0; + } + } + p_Vid->NALUCount++; + //for the first NAL unit in an access unit, zero_byte shall exists + if(p_Vid->NALUCount==1) + CheckZeroByte=1; + if(CheckZeroByte && nalu->startcodeprefix_len==3) + { + printf("Warning: zero_byte shall exist\n"); + //because it is not a very serious problem, we do not exit here + } +} + +void CheckZeroByteVCL(VideoParameters *p_Vid, NALU_t *nalu) +{ + int CheckZeroByte=0; + + //This function deals only with VCL NAL units + if(!(nalu->nal_unit_type>=NALU_TYPE_SLICE && nalu->nal_unit_type <= NALU_TYPE_IDR)) + return; + + if(p_Vid->LastAccessUnitExists) + { + p_Vid->NALUCount=0; + } + p_Vid->NALUCount++; + //the first VCL NAL unit that is the first NAL unit after last VCL NAL unit indicates + //the start of a new access unit and hence the first NAL unit of the new access unit. (sounds like a tongue twister :-) + if(p_Vid->NALUCount == 1) + CheckZeroByte = 1; + p_Vid->LastAccessUnitExists = 1; + if(CheckZeroByte && nalu->startcodeprefix_len==3) + { + printf("warning: zero_byte shall exist\n"); + //because it is not a very serious problem, we do not exit here + } +} + +#if (MVC_EXTENSION_ENABLE) +void nal_unit_header_mvc_extension(NALUnitHeaderMVCExt_t *NaluHeaderMVCExt, Bitstream *s) +{ + //to be implemented; + NaluHeaderMVCExt->non_idr_flag = read_u_v (1, "non_idr_flag", s, &p_Dec->UsedBits); + NaluHeaderMVCExt->priority_id = read_u_v (6, "priority_id", s, &p_Dec->UsedBits); + NaluHeaderMVCExt->view_id = read_u_v (10, "view_id", s, &p_Dec->UsedBits); + NaluHeaderMVCExt->temporal_id = read_u_v (3, "temporal_id", s, &p_Dec->UsedBits); + NaluHeaderMVCExt->anchor_pic_flag = read_u_v (1, "anchor_pic_flag", s, &p_Dec->UsedBits); + NaluHeaderMVCExt->inter_view_flag = read_u_v (1, "inter_view_flag", s, &p_Dec->UsedBits); + NaluHeaderMVCExt->reserved_one_bit = read_u_v (1, "reserved_one_bit", s, &p_Dec->UsedBits); + if(NaluHeaderMVCExt->reserved_one_bit != 1) + { + printf("Nalu Header MVC Extension: reserved_one_bit is not 1!\n"); + } +} + +void nal_unit_header_svc_extension( void ) +{ + //to be implemented for Annex G; +} + +void prefix_nal_unit_svc( void ) +{ + //to be implemented for Annex G; +} + +#endif diff --git a/ldecod/src/output.c b/ldecod/src/output.c new file mode 100644 index 0000000..aa17fa3 --- /dev/null +++ b/ldecod/src/output.c @@ -0,0 +1,959 @@ + +/*! + ************************************************************************ + * \file output.c + * + * \brief + * Output an image and Trance support + * + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * - Karsten Suehring + ************************************************************************ + */ + +#include "contributors.h" + +#include "global.h" +#include "mbuffer.h" +#include "image.h" +#include "memalloc.h" +#include "sei.h" +#include "input.h" +#include "fast_memory.h" + +#if( DISPLAY_OUTPUTED_FRAMES ) +#include "debug_functions.h" +#endif + +static void write_out_picture(VideoParameters *p_Vid, StorablePicture *p, int p_out); +static void img2buf_byte (imgpel** imgX, unsigned char* buf, int size_x, int size_y, int symbol_size_in_bytes, int crop_left, int crop_right, int crop_top, int crop_bottom, int iOutStride); +static void img2buf_normal (imgpel** imgX, unsigned char* buf, int size_x, int size_y, int symbol_size_in_bytes, int crop_left, int crop_right, int crop_top, int crop_bottom, int iOutStride); +static void img2buf_endian (imgpel** imgX, unsigned char* buf, int size_x, int size_y, int symbol_size_in_bytes, int crop_left, int crop_right, int crop_top, int crop_bottom, int iOutStride); + + +/*! + ************************************************************************ + * \brief + * selects appropriate output function given system arch. and data + * \return + * + ************************************************************************ + */ +void init_output(CodingParameters *p_cps, int symbol_size_in_bytes) +{ + if (( sizeof(char) == sizeof (imgpel))) + { + if ( sizeof(char) == symbol_size_in_bytes) + p_cps->img2buf = img2buf_byte; + else + p_cps->img2buf = img2buf_normal; + } + else + { + if (testEndian()) + p_cps->img2buf = img2buf_endian; + else + p_cps->img2buf = img2buf_normal; + } +} + +/*! + ************************************************************************ + * \brief + * Convert image plane to temporary buffer for file writing + * \param imgX + * Pointer to image plane + * \param buf + * Buffer for file output + * \param size_x + * horizontal size + * \param size_y + * vertical size + * \param symbol_size_in_bytes + * number of bytes used per pel + * \param crop_left + * pixels to crop from left + * \param crop_right + * pixels to crop from right + * \param crop_top + * pixels to crop from top + * \param crop_bottom + * pixels to crop from bottom + ************************************************************************ + */ +static void img2buf_normal (imgpel** imgX, unsigned char* buf, int size_x, int size_y, int symbol_size_in_bytes, int crop_left, int crop_right, int crop_top, int crop_bottom, int iOutStride) +{ + int i,j; + + int twidth = size_x - crop_left - crop_right; + int theight = size_y - crop_top - crop_bottom; + + int size = 0; + + // sizeof (imgpel) > sizeof(char) + // little endian + if (sizeof (imgpel) < symbol_size_in_bytes) + { + // this should not happen. we should not have smaller imgpel than our source material. + size = sizeof (imgpel); + // clear buffer + for(j=0; j> 8) | ((tmp16&0xFF)<<8)); + memcpy(buf+((j-crop_left+((i-crop_top)*iOutStride))*2),&(ui16), 2); + } + break; + } + case 4: + { + for(i=crop_top;i>8) | ((tmp32&0xFF000000)>>24)); + memcpy(buf+((j-crop_left+((i-crop_top)*iOutStride))*4),&(ui32), 4); + } + break; + } + default: + { + error ("writing only to formats of 8, 16 or 32 bit allowed on big endian architecture", 500); + break; + } + } +} + + +#if (PAIR_FIELDS_IN_OUTPUT) + +void clear_picture(VideoParameters *p_Vid, StorablePicture *p); + +/*! + ************************************************************************ + * \brief + * output the pending frame buffer + * \param p_out + * Output file + ************************************************************************ + */ +void flush_pending_output(VideoParameters *p_Vid, int p_out) +{ + if (p_Vid->pending_output_state != FRAME) + { + write_out_picture(p_Vid, p_Vid->pending_output, p_out); + } + + if (p_Vid->pending_output->imgY) + { + free_mem2Dpel (p_Vid->pending_output->imgY); + p_Vid->pending_output->imgY=NULL; + } + if (p_Vid->pending_output->imgUV) + { + free_mem3Dpel (p_Vid->pending_output->imgUV); + p_Vid->pending_output->imgUV=NULL; + } + + p_Vid->pending_output_state = FRAME; +} + + +/*! + ************************************************************************ + * \brief + * Writes out a storable picture + * If the picture is a field, the output buffers the picture and tries + * to pair it with the next field. + * \param p + * Picture to be written + * \param p_out + * Output file + ************************************************************************ + */ +void write_picture(VideoParameters *p_Vid, StorablePicture *p, int p_out, int real_structure) +{ + int i, add; + + if (real_structure==FRAME) + { + + flush_pending_output(p_Vid, p_out); + write_out_picture(p_Vid, p, p_out); + return; + } + if (real_structure == p_Vid->pending_output_state) + { + flush_pending_output(p_Vid, p_out); + write_picture(p_Vid, p, p_out, real_structure); + return; + } + + if (p_Vid->pending_output_state == FRAME) + { + p_Vid->pending_output->size_x = p->size_x; + p_Vid->pending_output->size_y = p->size_y; + p_Vid->pending_output->size_x_cr = p->size_x_cr; + p_Vid->pending_output->size_y_cr = p->size_y_cr; + p_Vid->pending_output->chroma_format_idc = p->chroma_format_idc; + + p_Vid->pending_output->frame_mbs_only_flag = p->frame_mbs_only_flag; + p_Vid->pending_output->frame_cropping_flag = p->frame_cropping_flag; + if (p_Vid->pending_output->frame_cropping_flag) + { + p_Vid->pending_output->frame_crop_left_offset = p->frame_crop_left_offset; + p_Vid->pending_output->frame_crop_right_offset = p->frame_crop_right_offset; + p_Vid->pending_output->frame_crop_top_offset = p->frame_crop_top_offset; + p_Vid->pending_output->frame_crop_bottom_offset = p->frame_crop_bottom_offset; + } + + get_mem2Dpel (&(p_Vid->pending_output->imgY), p_Vid->pending_output->size_y, p_Vid->pending_output->size_x); + get_mem3Dpel (&(p_Vid->pending_output->imgUV), 2, p_Vid->pending_output->size_y_cr, p_Vid->pending_output->size_x_cr); + + clear_picture(p_Vid, p_Vid->pending_output); + + // copy first field + if (real_structure == TOP_FIELD) + { + add = 0; + } + else + { + add = 1; + } + + for (i=0; ipending_output->size_y; i+=2) + { + memcpy(p_Vid->pending_output->imgY[(i+add)], p->imgY[(i+add)], p->size_x * sizeof(imgpel)); + } + for (i=0; ipending_output->size_y_cr; i+=2) + { + memcpy(p_Vid->pending_output->imgUV[0][(i+add)], p->imgUV[0][(i+add)], p->size_x_cr * sizeof(imgpel)); + memcpy(p_Vid->pending_output->imgUV[1][(i+add)], p->imgUV[1][(i+add)], p->size_x_cr * sizeof(imgpel)); + } + p_Vid->pending_output_state = real_structure; + } + else + { + if ( (p_Vid->pending_output->size_x!=p->size_x) || (p_Vid->pending_output->size_y!= p->size_y) + || (p_Vid->pending_output->frame_mbs_only_flag != p->frame_mbs_only_flag) + || (p_Vid->pending_output->frame_cropping_flag != p->frame_cropping_flag) + || ( p_Vid->pending_output->frame_cropping_flag && + ( (p_Vid->pending_output->frame_crop_left_offset != p->frame_crop_left_offset) + ||(p_Vid->pending_output->frame_crop_right_offset != p->frame_crop_right_offset) + ||(p_Vid->pending_output->frame_crop_top_offset != p->frame_crop_top_offset) + ||(p_Vid->pending_output->frame_crop_bottom_offset != p->frame_crop_bottom_offset) + ) + ) + ) + { + flush_pending_output(p_Vid, p_out); + write_picture (p_Vid, p, p_out, real_structure); + return; + } + // copy second field + if (real_structure == TOP_FIELD) + { + add = 0; + } + else + { + add = 1; + } + + for (i=0; ipending_output->size_y; i+=2) + { + memcpy(p_Vid->pending_output->imgY[(i+add)], p->imgY[(i+add)], p->size_x * sizeof(imgpel)); + } + for (i=0; ipending_output->size_y_cr; i+=2) + { + memcpy(p_Vid->pending_output->imgUV[0][(i+add)], p->imgUV[0][(i+add)], p->size_x_cr * sizeof(imgpel)); + memcpy(p_Vid->pending_output->imgUV[1][(i+add)], p->imgUV[1][(i+add)], p->size_x_cr * sizeof(imgpel)); + } + + flush_pending_output(p_Vid, p_out); + } +} + +#else + +/*! + ************************************************************************ + * \brief + * Writes out a storable picture without doing any output modifications + * + * \param p_Vid + * image decoding parameters for current picture + * \param p + * Picture to be written + * \param p_out + * Output file + * \param real_structure + * real picture structure + ************************************************************************ + */ +void write_picture(VideoParameters *p_Vid, StorablePicture *p, int p_out, int real_structure) +{ +#if PRINT_OUTPUT_DPB_POC + if (!p_Vid->p_Inp->silent) + { + if (p->concealed_pic) + printf( "Output frame c%1d( frame_no: %2d poc: %3d view_id: %2d)\n", p->concealed_pic, p->frame_no , p->poc, p->view_id); + else + printf( "Output frame ( frame_no: %2d poc: %3d view_id: %2d)\n", p->frame_no , p->poc, p->view_id); + } +#endif + +#if( DISPLAY_OUTPUTED_FRAMES ) + showStorablePicture (p_Vid , p, 1, "Outputed Frame", 1000 / 30); +#endif + write_out_picture(p_Vid, p, p_out); +#if( MVC_EXTENSION_ENABLE ) + p_Vid->nFramesOut[p->view_id]++; +#else + p_Vid->nFramesOut++; +#endif +} + + +#endif + +static void allocate_p_dec_pic(VideoParameters *p_Vid, DecodedPicList *pDecPic, StorablePicture *p, int iLumaSize, int iFrameSize, int iLumaSizeX, int iLumaSizeY, int iChromaSizeX, int iChromaSizeY) +{ + int symbol_size_in_bytes = ((p_Vid->pic_unit_bitsize_on_disk+7) >> 3); + + if(pDecPic->pY) + mem_free(pDecPic->pY); + pDecPic->iBufSize = iFrameSize; + pDecPic->pY = mem_malloc(pDecPic->iBufSize); + pDecPic->pU = pDecPic->pY+iLumaSize; + pDecPic->pV = pDecPic->pU + ((iFrameSize-iLumaSize)>>1); + //init; + pDecPic->iYUVFormat = p->chroma_format_idc; + pDecPic->iYUVStorageFormat = 0; + pDecPic->iBitDepth = p_Vid->pic_unit_bitsize_on_disk; + pDecPic->iWidth = iLumaSizeX; //p->size_x; + pDecPic->iHeight = iLumaSizeY; //p->size_y; + pDecPic->iYBufStride = iLumaSizeX*symbol_size_in_bytes; //p->size_x *symbol_size_in_bytes; + pDecPic->iUVBufStride = iChromaSizeX*symbol_size_in_bytes; //p->size_x_cr*symbol_size_in_bytes; +} + +/*! +************************************************************************ +* \brief +* Writes out a storable picture +* +* \param p_Vid +* image decoding parameters for current picture +* \param p +* Picture to be written +* \param p_out +* Output file +************************************************************************ +*/ +static void write_out_picture(VideoParameters *p_Vid, StorablePicture *p, int p_out) +{ + InputParameters *p_Inp = p_Vid->p_Inp; + DecodedPicList *pDecPic; + + static const int SubWidthC [4]= { 1, 2, 2, 1}; + static const int SubHeightC [4]= { 1, 2, 1, 1}; + + int crop_left, crop_right, crop_top, crop_bottom; + int symbol_size_in_bytes = ((p_Vid->pic_unit_bitsize_on_disk+7) >> 3); + int rgb_output = p_Vid->p_EncodePar[p->layer_id]->rgb_output; //(p_Vid->active_sps->vui_seq_parameters.matrix_coefficients==0); + unsigned char *buf; + //int iPicSizeTab[4] = {2, 3, 4, 6}; + int iLumaSize, iFrameSize; + int iLumaSizeX, iLumaSizeY; + int iChromaSizeX, iChromaSizeY; + + int ret; + + if (p->non_existing) + return; + +#if (ENABLE_OUTPUT_TONEMAPPING) + // note: this tone-mapping is working for RGB format only. Sharp + if (p->seiHasTone_mapping && rgb_output) + { + //printf("output frame %d with tone model id %d\n", p->frame_num, p->tone_mapping_model_id); + symbol_size_in_bytes = (p->tonemapped_bit_depth>8)? 2 : 1; + tone_map(p->imgY, p->tone_mapping_lut, p->size_x, p->size_y); + tone_map(p->imgUV[0], p->tone_mapping_lut, p->size_x_cr, p->size_y_cr); + tone_map(p->imgUV[1], p->tone_mapping_lut, p->size_x_cr, p->size_y_cr); + } +#endif + + // should this be done only once? + if (p->frame_cropping_flag) + { + crop_left = SubWidthC [p->chroma_format_idc] * p->frame_crop_left_offset; + crop_right = SubWidthC [p->chroma_format_idc] * p->frame_crop_right_offset; + crop_top = SubHeightC[p->chroma_format_idc] * ( 2 - p->frame_mbs_only_flag ) * p->frame_crop_top_offset; + crop_bottom = SubHeightC[p->chroma_format_idc] * ( 2 - p->frame_mbs_only_flag ) * p->frame_crop_bottom_offset; + } + else + { + crop_left = crop_right = crop_top = crop_bottom = 0; + } + iChromaSizeX = p->size_x_cr- p->frame_crop_left_offset -p->frame_crop_right_offset; + iChromaSizeY = p->size_y_cr - ( 2 - p->frame_mbs_only_flag ) * p->frame_crop_top_offset -( 2 - p->frame_mbs_only_flag ) * p->frame_crop_bottom_offset; + iLumaSizeX = p->size_x - crop_left-crop_right; + iLumaSizeY = p->size_y - crop_top - crop_bottom; + iLumaSize = iLumaSizeX * iLumaSizeY * symbol_size_in_bytes; + iFrameSize = (iLumaSizeX * iLumaSizeY + 2 * (iChromaSizeX * iChromaSizeY)) * symbol_size_in_bytes; //iLumaSize*iPicSizeTab[p->chroma_format_idc]/2; + + //printf ("write frame size: %dx%d\n", p->size_x-crop_left-crop_right,p->size_y-crop_top-crop_bottom ); + + // We need to further cleanup this function + if (p_out == -1) + return; + + + + // KS: this buffer should actually be allocated only once, but this is still much faster than the previous version + pDecPic = get_one_avail_dec_pic_from_list(p_Vid->pDecOuputPic, 0, 0); + if( (pDecPic->pY == NULL) + || (pDecPic->iBufSize < iFrameSize) + ) + allocate_p_dec_pic(p_Vid, pDecPic, p, iLumaSize, iFrameSize, iLumaSizeX, iLumaSizeY, iChromaSizeX, iChromaSizeY); +#if (MVC_EXTENSION_ENABLE) + { + pDecPic->bValid = 1; + pDecPic->iViewId = p->view_id >=0 ? p->view_id : -1; + } +#else + pDecPic->bValid = 1; +#endif + + pDecPic->iPOC = p->frame_poc; + + if (NULL==pDecPic->pY) + { + no_mem_exit("write_out_picture: buf"); + } + + + if(rgb_output) + { + buf = malloc (p->size_x * p->size_y * symbol_size_in_bytes); + crop_left = p->frame_crop_left_offset; + crop_right = p->frame_crop_right_offset; + crop_top = ( 2 - p->frame_mbs_only_flag ) * p->frame_crop_top_offset; + crop_bottom = ( 2 - p->frame_mbs_only_flag ) * p->frame_crop_bottom_offset; + + p_Vid->img2buf (p->imgUV[1], buf, p->size_x_cr, p->size_y_cr, symbol_size_in_bytes, crop_left, crop_right, crop_top, crop_bottom, pDecPic->iYBufStride); + if (p_out >= 0) + { + ret = write(p_out, buf, (p->size_y_cr-crop_bottom-crop_top)*(p->size_x_cr-crop_right-crop_left)*symbol_size_in_bytes); + if (ret != ((p->size_y_cr-crop_bottom-crop_top)*(p->size_x_cr-crop_right-crop_left)*symbol_size_in_bytes)) + { + error ("write_out_picture: error writing to RGB file", 500); + } + } + + if (p->frame_cropping_flag) + { + crop_left = SubWidthC[p->chroma_format_idc] * p->frame_crop_left_offset; + crop_right = SubWidthC[p->chroma_format_idc] * p->frame_crop_right_offset; + crop_top = SubHeightC[p->chroma_format_idc]*( 2 - p->frame_mbs_only_flag ) * p->frame_crop_top_offset; + crop_bottom = SubHeightC[p->chroma_format_idc]*( 2 - p->frame_mbs_only_flag ) * p->frame_crop_bottom_offset; + } + else + { + crop_left = crop_right = crop_top = crop_bottom = 0; + } + if(buf) + free(buf); + } + + buf = (pDecPic->bValid==1)? pDecPic->pY: pDecPic->pY+iLumaSizeX*symbol_size_in_bytes; + + p_Vid->img2buf (p->imgY, buf, p->size_x, p->size_y, symbol_size_in_bytes, crop_left, crop_right, crop_top, crop_bottom, pDecPic->iYBufStride); + if(p_out >=0) + { + ret = write(p_out, buf, (p->size_y-crop_bottom-crop_top)*(p->size_x-crop_right-crop_left)*symbol_size_in_bytes); + if (ret != ((p->size_y-crop_bottom-crop_top)*(p->size_x-crop_right-crop_left)*symbol_size_in_bytes)) + { + error ("write_out_picture: error writing to YUV file", 500); + } + } + + if (p->chroma_format_idc!=YUV400) + { + crop_left = p->frame_crop_left_offset; + crop_right = p->frame_crop_right_offset; + crop_top = ( 2 - p->frame_mbs_only_flag ) * p->frame_crop_top_offset; + crop_bottom = ( 2 - p->frame_mbs_only_flag ) * p->frame_crop_bottom_offset; + buf = (pDecPic->bValid==1)? pDecPic->pU : pDecPic->pU + iChromaSizeX*symbol_size_in_bytes; + p_Vid->img2buf (p->imgUV[0], buf, p->size_x_cr, p->size_y_cr, symbol_size_in_bytes, crop_left, crop_right, crop_top, crop_bottom, pDecPic->iUVBufStride); + if(p_out >= 0) + { + ret = write(p_out, buf, (p->size_y_cr-crop_bottom-crop_top)*(p->size_x_cr-crop_right-crop_left)* symbol_size_in_bytes); + if (ret != ((p->size_y_cr-crop_bottom-crop_top)*(p->size_x_cr-crop_right-crop_left)* symbol_size_in_bytes)) + { + error ("write_out_picture: error writing to YUV file", 500); + } + } + + if (!rgb_output) + { + buf = (pDecPic->bValid==1)? pDecPic->pV : pDecPic->pV + iChromaSizeX*symbol_size_in_bytes; + p_Vid->img2buf (p->imgUV[1], buf, p->size_x_cr, p->size_y_cr, symbol_size_in_bytes, crop_left, crop_right, crop_top, crop_bottom, pDecPic->iUVBufStride); + + if(p_out >= 0) + { + ret = write(p_out, buf, (p->size_y_cr-crop_bottom-crop_top)*(p->size_x_cr-crop_right-crop_left)*symbol_size_in_bytes); + if (ret != ((p->size_y_cr-crop_bottom-crop_top)*(p->size_x_cr-crop_right-crop_left)*symbol_size_in_bytes)) + { + error ("write_out_picture: error writing to YUV file", 500); + } + } + } + } + else + { + if (p_Inp->write_uv) + { + int i,j; + imgpel cr_val = (imgpel) (1<<(p_Vid->bitdepth_luma - 1)); + + get_mem3Dpel (&(p->imgUV), 1, p->size_y/2, p->size_x/2); + + for (j=0; jsize_y/2; j++) + { + for (i=0; isize_x/2; i++) + { + p->imgUV[0][j][i]=cr_val; + } + } + + // fake out U=V=128 to make a YUV 4:2:0 stream + buf = malloc (p->size_x*p->size_y*symbol_size_in_bytes); + p_Vid->img2buf (p->imgUV[0], buf, p->size_x/2, p->size_y/2, symbol_size_in_bytes, crop_left/2, crop_right/2, crop_top/2, crop_bottom/2, pDecPic->iYBufStride/2); + + ret = write(p_out, buf, symbol_size_in_bytes * (p->size_y-crop_bottom-crop_top)/2 * (p->size_x-crop_right-crop_left)/2 ); + if (ret != (symbol_size_in_bytes * (p->size_y-crop_bottom-crop_top)/2 * (p->size_x-crop_right-crop_left)/2)) + { + error ("write_out_picture: error writing to YUV file", 500); + } + ret = write(p_out, buf, symbol_size_in_bytes * (p->size_y-crop_bottom-crop_top)/2 * (p->size_x-crop_right-crop_left)/2 ); + if (ret != (symbol_size_in_bytes * (p->size_y-crop_bottom-crop_top)/2 * (p->size_x-crop_right-crop_left)/2)) + { + error ("write_out_picture: error writing to YUV file", 500); + } + free(buf); + free_mem3Dpel(p->imgUV); + p->imgUV=NULL; + } + } + + //free(buf); + if(p_out >=0) + pDecPic->bValid = 0; + + // fsync(p_out); +} + +/*! + ************************************************************************ + * \brief + * Initialize output buffer for direct output + ************************************************************************ + */ +void init_out_buffer(VideoParameters *p_Vid) +{ + p_Vid->out_buffer = alloc_frame_store(); + +#if (PAIR_FIELDS_IN_OUTPUT) + p_Vid->pending_output = calloc (sizeof(StorablePicture), 1); + if (NULL==p_Vid->pending_output) no_mem_exit("init_out_buffer"); + p_Vid->pending_output->imgUV = NULL; + p_Vid->pending_output->imgY = NULL; +#endif +} + +/*! + ************************************************************************ + * \brief + * Uninitialize output buffer for direct output + ************************************************************************ + */ +void uninit_out_buffer(VideoParameters *p_Vid) +{ + free_frame_store(p_Vid->out_buffer); + p_Vid->out_buffer=NULL; +#if (PAIR_FIELDS_IN_OUTPUT) + flush_pending_output(p_Vid, p_Vid->p_out); + free (p_Vid->pending_output); +#endif +} + +/*! + ************************************************************************ + * \brief + * Initialize picture memory with (Y:0,U:128,V:128) + ************************************************************************ + */ +void clear_picture(VideoParameters *p_Vid, StorablePicture *p) +{ + int i,j; + + for(i=0;isize_y;i++) + { + for (j=0; jsize_x; j++) + p->imgY[i][j] = (imgpel) p_Vid->dc_pred_value_comp[0]; + } + for(i=0;isize_y_cr;i++) + { + for (j=0; jsize_x_cr; j++) + p->imgUV[0][i][j] = (imgpel) p_Vid->dc_pred_value_comp[1]; + } + for(i=0;isize_y_cr;i++) + { + for (j=0; jsize_x_cr; j++) + p->imgUV[1][i][j] = (imgpel) p_Vid->dc_pred_value_comp[2]; + } +} + +/*! + ************************************************************************ + * \brief + * Write out not paired direct output fields. A second empty field is generated + * and combined into the frame buffer. + * + * \param p_Vid + * image decoding parameters for current picture + * \param fs + * FrameStore that contains a single field + * \param p_out + * Output file + ************************************************************************ + */ +void write_unpaired_field(VideoParameters *p_Vid, FrameStore* fs, int p_out) +{ + StorablePicture *p; + assert (fs->is_used<3); + + if(fs->is_used & 0x01) + { + // we have a top field + // construct an empty bottom field + p = fs->top_field; + fs->bottom_field = alloc_storable_picture(p_Vid, BOTTOM_FIELD, p->size_x, 2*p->size_y, p->size_x_cr, 2*p->size_y_cr, 1); + fs->bottom_field->chroma_format_idc = p->chroma_format_idc; + clear_picture(p_Vid, fs->bottom_field); + dpb_combine_field_yuv(p_Vid, fs); +#if (MVC_EXTENSION_ENABLE) + fs->frame->view_id = fs->view_id; +#endif + write_picture (p_Vid, fs->frame, p_out, TOP_FIELD); + } + + if(fs->is_used & 0x02) + { + // we have a bottom field + // construct an empty top field + p = fs->bottom_field; + fs->top_field = alloc_storable_picture(p_Vid, TOP_FIELD, p->size_x, 2*p->size_y, p->size_x_cr, 2*p->size_y_cr, 1); + fs->top_field->chroma_format_idc = p->chroma_format_idc; + clear_picture(p_Vid, fs->top_field); + fs ->top_field->frame_cropping_flag = fs->bottom_field->frame_cropping_flag; + if(fs ->top_field->frame_cropping_flag) + { + fs ->top_field->frame_crop_top_offset = fs->bottom_field->frame_crop_top_offset; + fs ->top_field->frame_crop_bottom_offset = fs->bottom_field->frame_crop_bottom_offset; + fs ->top_field->frame_crop_left_offset = fs->bottom_field->frame_crop_left_offset; + fs ->top_field->frame_crop_right_offset = fs->bottom_field->frame_crop_right_offset; + } + dpb_combine_field_yuv(p_Vid, fs); +#if (MVC_EXTENSION_ENABLE) + fs->frame->view_id = fs->view_id; +#endif + write_picture (p_Vid, fs->frame, p_out, BOTTOM_FIELD); + } + + fs->is_used = 3; +} + +/*! + ************************************************************************ + * \brief + * Write out unpaired fields from output buffer. + * + * \param p_Vid + * image decoding parameters for current picture + * \param p_out + * Output file + ************************************************************************ + */ +void flush_direct_output(VideoParameters *p_Vid, int p_out) +{ + write_unpaired_field(p_Vid, p_Vid->out_buffer, p_out); + + free_storable_picture(p_Vid->out_buffer->frame); + p_Vid->out_buffer->frame = NULL; + free_storable_picture(p_Vid->out_buffer->top_field); + p_Vid->out_buffer->top_field = NULL; + free_storable_picture(p_Vid->out_buffer->bottom_field); + p_Vid->out_buffer->bottom_field = NULL; + p_Vid->out_buffer->is_used = 0; +} + + +/*! + ************************************************************************ + * \brief + * Write a frame (from FrameStore) + * + * \param p_Vid + * image decoding parameters for current picture + * \param fs + * FrameStore containing the frame + * \param p_out + * Output file + ************************************************************************ + */ +void write_stored_frame( VideoParameters *p_Vid, FrameStore *fs, int p_out) +{ + +#ifndef DISABLE_MB_DECODING + + if( fs->frame->frame_no >= INITAL_PFRAMES_LENGTH ) + { + if (fs->frame->concealed_pic > 0) + { + p_Vid->p_PFrames[fs->view_id][fs->frame->frame_no] = 0; + } + + if (fs->frame->have_concealed_reference == 1) + { + p_Vid->p_PFrames[fs->view_id][fs->frame->frame_no] = -1; + } + + if ( !(fs->frame->concealed_pic > 0 || fs->frame->have_concealed_reference == 1) ) + { + p_Vid->p_PFrames[fs->view_id][fs->frame->frame_no] = 1; + } + } + // make sure no direct output field is pending + flush_direct_output(p_Vid, p_out); + + if (fs->is_used<3) + { + write_unpaired_field(p_Vid, fs, p_out); + } + else + { + if (fs->recovery_frame) + p_Vid->recovery_flag = 1; + if ((!p_Vid->non_conforming_stream) || p_Vid->recovery_flag) + write_picture(p_Vid, fs->frame, p_out, FRAME); + } +#endif + + fs->is_output = 1; +} + +/*! + ************************************************************************ + * \brief + * Directly output a picture without storing it in the DPB. Fields + * are buffered before they are written to the file. + * + * \param p_Vid + * image decoding parameters for current picture + * \param p + * Picture for output + * \param p_out + * Output file + ************************************************************************ + */ +void direct_output(VideoParameters *p_Vid, StorablePicture *p, int p_out) +{ + InputParameters *p_Inp = p_Vid->p_Inp; + if (p->structure==FRAME) + { + // we have a frame (or complementary field pair) + // so output it directly + flush_direct_output(p_Vid, p_out); + write_picture (p_Vid, p, p_out, FRAME); + //calculate_frame_no(p_Vid, p); + //if (-1 != p_Vid->p_ref && !p_Inp->silent) + // find_snr(p_Vid, p, &p_Vid->p_ref); + free_storable_picture(p); + return; + } + + if (p->structure == TOP_FIELD) + { + if (p_Vid->out_buffer->is_used &1) + flush_direct_output(p_Vid, p_out); + p_Vid->out_buffer->top_field = p; + p_Vid->out_buffer->is_used |= 1; + } + + if (p->structure == BOTTOM_FIELD) + { + if (p_Vid->out_buffer->is_used &2) + flush_direct_output(p_Vid, p_out); + p_Vid->out_buffer->bottom_field = p; + p_Vid->out_buffer->is_used |= 2; + } + + if (p_Vid->out_buffer->is_used == 3) + { + // we have both fields, so output them + dpb_combine_field_yuv(p_Vid, p_Vid->out_buffer); +#if (MVC_EXTENSION_ENABLE) + p_Vid->out_buffer->frame->view_id = p_Vid->out_buffer->view_id; +#endif + write_picture (p_Vid, p_Vid->out_buffer->frame, p_out, FRAME); + + //calculate_frame_no(p_Vid, p); + //if (-1 != p_Vid->p_ref && !p_Inp->silent) + // find_snr(p_Vid, p_Vid->out_buffer->frame, &p_Vid->p_ref); + + free_storable_picture(p_Vid->out_buffer->frame); + p_Vid->out_buffer->frame = NULL; + free_storable_picture(p_Vid->out_buffer->top_field); + p_Vid->out_buffer->top_field = NULL; + free_storable_picture(p_Vid->out_buffer->bottom_field); + p_Vid->out_buffer->bottom_field = NULL; + p_Vid->out_buffer->is_used = 0; + } +} + diff --git a/ldecod/src/parset.c b/ldecod/src/parset.c new file mode 100644 index 0000000..e7fa538 --- /dev/null +++ b/ldecod/src/parset.c @@ -0,0 +1,1604 @@ + +/*! + ************************************************************************ + * \file + * parset.c + * \brief + * Parameter Sets + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * - Stephan Wenger + * + *********************************************************************** + */ + +#include "global.h" +#include "image.h" +#include "parsetcommon.h" +#include "parset.h" +#include "nalu.h" +#include "memalloc.h" +#include "fmo.h" +#include "cabac.h" +#include "vlc.h" +#include "mbuffer.h" +#include "erc_api.h" + +#if TRACE +#define SYMTRACESTRING(s) strncpy(sym->tracestring,s,TRACESTRING_SIZE) +#else +#define SYMTRACESTRING(s) // do nothing +#endif + +extern void init_frext(VideoParameters *p_Vid); + +// syntax for scaling list matrix values +void Scaling_List(int *scalingList, int sizeOfScalingList, Boolean *UseDefaultScalingMatrix, Bitstream *s) +{ + int j, scanj; + int delta_scale, lastScale, nextScale; + + lastScale = 8; + nextScale = 8; + + for(j=0; jUsedBits); + nextScale = (lastScale + delta_scale + 256) % 256; + *UseDefaultScalingMatrix = (Boolean) (scanj==0 && nextScale==0); + } + + scalingList[scanj] = (nextScale==0) ? lastScale:nextScale; + lastScale = scalingList[scanj]; + } +} +// fill sps with content of p + +int InterpretSPS (VideoParameters *p_Vid, DataPartition *p, seq_parameter_set_rbsp_t *sps) +{ + unsigned i; + unsigned n_ScalingList; + int reserved_zero; + Bitstream *s = p->bitstream; + + assert (p != NULL); + assert (p->bitstream != NULL); + assert (p->bitstream->streamBuffer != 0); + assert (sps != NULL); + + p_Dec->UsedBits = 0; + + sps->profile_idc = read_u_v (8, "SPS: profile_idc" , s, &p_Dec->UsedBits); + + if ((sps->profile_idc!=BASELINE ) && + (sps->profile_idc!=MAIN ) && + (sps->profile_idc!=EXTENDED ) && + (sps->profile_idc!=FREXT_HP ) && + (sps->profile_idc!=FREXT_Hi10P ) && + (sps->profile_idc!=FREXT_Hi422 ) && + (sps->profile_idc!=FREXT_Hi444 ) && + (sps->profile_idc!=FREXT_CAVLC444 ) +#if (MVC_EXTENSION_ENABLE) + && (sps->profile_idc!=MVC_HIGH) + && (sps->profile_idc!=STEREO_HIGH) +#endif + ) + { + printf("Invalid Profile IDC (%d) encountered. \n", sps->profile_idc); + return p_Dec->UsedBits; + } + + sps->constrained_set0_flag = read_u_1 ( "SPS: constrained_set0_flag" , s, &p_Dec->UsedBits); + sps->constrained_set1_flag = read_u_1 ( "SPS: constrained_set1_flag" , s, &p_Dec->UsedBits); + sps->constrained_set2_flag = read_u_1 ( "SPS: constrained_set2_flag" , s, &p_Dec->UsedBits); + sps->constrained_set3_flag = read_u_1 ( "SPS: constrained_set3_flag" , s, &p_Dec->UsedBits); +#if (MVC_EXTENSION_ENABLE) + sps->constrained_set4_flag = read_u_1 ( "SPS: constrained_set4_flag" , s, &p_Dec->UsedBits); + sps->constrained_set5_flag = read_u_1 ( "SPS: constrained_set5_flag" , s, &p_Dec->UsedBits); + reserved_zero = read_u_v (2, "SPS: reserved_zero_2bits" , s, &p_Dec->UsedBits); +#else + reserved_zero = read_u_v (4, "SPS: reserved_zero_4bits" , s, &p_Dec->UsedBits); +#endif + //assert (reserved_zero==0); + if (reserved_zero != 0) + { + printf("Warning, reserved_zero flag not equal to 0. Possibly new constrained_setX flag introduced.\n"); + } + + sps->level_idc = read_u_v (8, "SPS: level_idc" , s, &p_Dec->UsedBits); + + sps->seq_parameter_set_id = read_ue_v ("SPS: seq_parameter_set_id" , s, &p_Dec->UsedBits); + + // Fidelity Range Extensions stuff + sps->chroma_format_idc = 1; + sps->bit_depth_luma_minus8 = 0; + sps->bit_depth_chroma_minus8 = 0; + sps->lossless_qpprime_flag = 0; + sps->separate_colour_plane_flag = 0; + + if((sps->profile_idc==FREXT_HP ) || + (sps->profile_idc==FREXT_Hi10P) || + (sps->profile_idc==FREXT_Hi422) || + (sps->profile_idc==FREXT_Hi444) || + (sps->profile_idc==FREXT_CAVLC444) +#if (MVC_EXTENSION_ENABLE) + || (sps->profile_idc==MVC_HIGH) + || (sps->profile_idc==STEREO_HIGH) +#endif + ) + { + sps->chroma_format_idc = read_ue_v ("SPS: chroma_format_idc" , s, &p_Dec->UsedBits); + + if(sps->chroma_format_idc == YUV444) + { + sps->separate_colour_plane_flag = read_u_1 ("SPS: separate_colour_plane_flag" , s, &p_Dec->UsedBits); + } + + sps->bit_depth_luma_minus8 = read_ue_v ("SPS: bit_depth_luma_minus8" , s, &p_Dec->UsedBits); + sps->bit_depth_chroma_minus8 = read_ue_v ("SPS: bit_depth_chroma_minus8" , s, &p_Dec->UsedBits); + //checking; + if((sps->bit_depth_luma_minus8+8 > sizeof(imgpel)*8) || (sps->bit_depth_chroma_minus8+8> sizeof(imgpel)*8)) + error ("Source picture has higher bit depth than imgpel data type. \nPlease recompile with larger data type for imgpel.", 500); + + sps->lossless_qpprime_flag = read_u_1 ("SPS: lossless_qpprime_y_zero_flag" , s, &p_Dec->UsedBits); + + sps->seq_scaling_matrix_present_flag = read_u_1 ( "SPS: seq_scaling_matrix_present_flag" , s, &p_Dec->UsedBits); + + if(sps->seq_scaling_matrix_present_flag) + { + n_ScalingList = (sps->chroma_format_idc != YUV444) ? 8 : 12; + for(i=0; iseq_scaling_list_present_flag[i] = read_u_1 ( "SPS: seq_scaling_list_present_flag" , s, &p_Dec->UsedBits); + if(sps->seq_scaling_list_present_flag[i]) + { + if(i<6) + Scaling_List(sps->ScalingList4x4[i], 16, &sps->UseDefaultScalingMatrix4x4Flag[i], s); + else + Scaling_List(sps->ScalingList8x8[i-6], 64, &sps->UseDefaultScalingMatrix8x8Flag[i-6], s); + } + } + } + } + + sps->log2_max_frame_num_minus4 = read_ue_v ("SPS: log2_max_frame_num_minus4" , s, &p_Dec->UsedBits); + sps->pic_order_cnt_type = read_ue_v ("SPS: pic_order_cnt_type" , s, &p_Dec->UsedBits); + + if (sps->pic_order_cnt_type == 0) + sps->log2_max_pic_order_cnt_lsb_minus4 = read_ue_v ("SPS: log2_max_pic_order_cnt_lsb_minus4" , s, &p_Dec->UsedBits); + else if (sps->pic_order_cnt_type == 1) + { + sps->delta_pic_order_always_zero_flag = read_u_1 ("SPS: delta_pic_order_always_zero_flag" , s, &p_Dec->UsedBits); + sps->offset_for_non_ref_pic = read_se_v ("SPS: offset_for_non_ref_pic" , s, &p_Dec->UsedBits); + sps->offset_for_top_to_bottom_field = read_se_v ("SPS: offset_for_top_to_bottom_field" , s, &p_Dec->UsedBits); + sps->num_ref_frames_in_pic_order_cnt_cycle = read_ue_v ("SPS: num_ref_frames_in_pic_order_cnt_cycle" , s, &p_Dec->UsedBits); + for(i=0; inum_ref_frames_in_pic_order_cnt_cycle; i++) + sps->offset_for_ref_frame[i] = read_se_v ("SPS: offset_for_ref_frame[i]" , s, &p_Dec->UsedBits); + } + sps->num_ref_frames = read_ue_v ("SPS: num_ref_frames" , s, &p_Dec->UsedBits); + sps->gaps_in_frame_num_value_allowed_flag = read_u_1 ("SPS: gaps_in_frame_num_value_allowed_flag" , s, &p_Dec->UsedBits); + sps->pic_width_in_mbs_minus1 = read_ue_v ("SPS: pic_width_in_mbs_minus1" , s, &p_Dec->UsedBits); + sps->pic_height_in_map_units_minus1 = read_ue_v ("SPS: pic_height_in_map_units_minus1" , s, &p_Dec->UsedBits); + sps->frame_mbs_only_flag = read_u_1 ("SPS: frame_mbs_only_flag" , s, &p_Dec->UsedBits); + if (!sps->frame_mbs_only_flag) + { + sps->mb_adaptive_frame_field_flag = read_u_1 ("SPS: mb_adaptive_frame_field_flag" , s, &p_Dec->UsedBits); + } + //printf("interlace flags %d %d\n", sps->frame_mbs_only_flag, sps->mb_adaptive_frame_field_flag); + sps->direct_8x8_inference_flag = read_u_1 ("SPS: direct_8x8_inference_flag" , s, &p_Dec->UsedBits); + sps->frame_cropping_flag = read_u_1 ("SPS: frame_cropping_flag" , s, &p_Dec->UsedBits); + + if (sps->frame_cropping_flag) + { + sps->frame_crop_left_offset = read_ue_v ("SPS: frame_crop_left_offset" , s, &p_Dec->UsedBits); + sps->frame_crop_right_offset = read_ue_v ("SPS: frame_crop_right_offset" , s, &p_Dec->UsedBits); + sps->frame_crop_top_offset = read_ue_v ("SPS: frame_crop_top_offset" , s, &p_Dec->UsedBits); + sps->frame_crop_bottom_offset = read_ue_v ("SPS: frame_crop_bottom_offset" , s, &p_Dec->UsedBits); + } + sps->vui_parameters_present_flag = (Boolean) read_u_1 ("SPS: vui_parameters_present_flag" , s, &p_Dec->UsedBits); + + InitVUI(sps); + ReadVUI(p, sps); + + sps->Valid = TRUE; + return p_Dec->UsedBits; +} + +// fill subset_sps with content of p +#if (MVC_EXTENSION_ENABLE) +static int InterpretSubsetSPS (VideoParameters *p_Vid, DataPartition *p, int *curr_seq_set_id) +{ + subset_seq_parameter_set_rbsp_t *subset_sps; + unsigned int additional_extension2_flag; + Bitstream *s = p->bitstream; + seq_parameter_set_rbsp_t *sps = AllocSPS(); + + assert (p != NULL); + assert (p->bitstream != NULL); + assert (p->bitstream->streamBuffer != 0); + + InterpretSPS (p_Vid, p, sps); + get_max_dec_frame_buf_size(sps); + + *curr_seq_set_id = sps->seq_parameter_set_id; + subset_sps = p_Vid->SubsetSeqParSet + sps->seq_parameter_set_id; + if(subset_sps->Valid || subset_sps->num_views_minus1>=0) + { + if(memcmp(&subset_sps->sps, sps, sizeof (seq_parameter_set_rbsp_t)-sizeof(int))) + assert(0); + reset_subset_sps(subset_sps); + } + memcpy (&subset_sps->sps, sps, sizeof (seq_parameter_set_rbsp_t)); + + assert (subset_sps != NULL); + subset_sps->Valid = FALSE; + + /*if(subset_sps->sps.profile_idc == SCALABLE_BASELINE_PROFILE || subset_sps->sps.profile_idc == SCALABLE_HIGH_PROFILE) + { + printf("\nScalable profile is not supported yet!\n"); + } + else*/ + if( is_MVC_profile(subset_sps->sps.profile_idc)) + { + subset_sps->bit_equal_to_one = read_u_1("bit_equal_to_one", s, &p_Dec->UsedBits); + + if(subset_sps->bit_equal_to_one !=1 ) + { + printf("\nbit_equal_to_one is not equal to 1!\n"); + return p_Dec->UsedBits; + } + + seq_parameter_set_mvc_extension(subset_sps, s); + + subset_sps->mvc_vui_parameters_present_flag = read_u_1("mvc_vui_parameters_present_flag", s, &p_Dec->UsedBits); + if(subset_sps->mvc_vui_parameters_present_flag) + mvc_vui_parameters_extension(&(subset_sps->MVCVUIParams), s); + } + + additional_extension2_flag = read_u_1("additional_extension2_flag", s, &p_Dec->UsedBits); + if(additional_extension2_flag) + { + while (more_rbsp_data(s->streamBuffer, s->frame_bitoffset,s->bitstream_length)) + additional_extension2_flag = read_u_1("additional_extension2_flag", s, &p_Dec->UsedBits); + } + + if (subset_sps->sps.Valid) + subset_sps->Valid = TRUE; + + FreeSPS (sps); + return p_Dec->UsedBits; + +} +#endif + +void InitVUI(seq_parameter_set_rbsp_t *sps) +{ + sps->vui_seq_parameters.matrix_coefficients = 2; +} + + +int ReadVUI(DataPartition *p, seq_parameter_set_rbsp_t *sps) +{ + Bitstream *s = p->bitstream; + if (sps->vui_parameters_present_flag) + { + sps->vui_seq_parameters.aspect_ratio_info_present_flag = read_u_1 ("VUI: aspect_ratio_info_present_flag" , s, &p_Dec->UsedBits); + if (sps->vui_seq_parameters.aspect_ratio_info_present_flag) + { + sps->vui_seq_parameters.aspect_ratio_idc = read_u_v ( 8, "VUI: aspect_ratio_idc" , s, &p_Dec->UsedBits); + if (255==sps->vui_seq_parameters.aspect_ratio_idc) + { + sps->vui_seq_parameters.sar_width = (unsigned short) read_u_v (16, "VUI: sar_width" , s, &p_Dec->UsedBits); + sps->vui_seq_parameters.sar_height = (unsigned short) read_u_v (16, "VUI: sar_height" , s, &p_Dec->UsedBits); + } + } + + sps->vui_seq_parameters.overscan_info_present_flag = read_u_1 ("VUI: overscan_info_present_flag" , s, &p_Dec->UsedBits); + if (sps->vui_seq_parameters.overscan_info_present_flag) + { + sps->vui_seq_parameters.overscan_appropriate_flag = read_u_1 ("VUI: overscan_appropriate_flag" , s, &p_Dec->UsedBits); + } + + sps->vui_seq_parameters.video_signal_type_present_flag = read_u_1 ("VUI: video_signal_type_present_flag" , s, &p_Dec->UsedBits); + if (sps->vui_seq_parameters.video_signal_type_present_flag) + { + sps->vui_seq_parameters.video_format = read_u_v ( 3,"VUI: video_format" , s, &p_Dec->UsedBits); + sps->vui_seq_parameters.video_full_range_flag = read_u_1 ( "VUI: video_full_range_flag" , s, &p_Dec->UsedBits); + sps->vui_seq_parameters.colour_description_present_flag = read_u_1 ( "VUI: color_description_present_flag" , s, &p_Dec->UsedBits); + if(sps->vui_seq_parameters.colour_description_present_flag) + { + sps->vui_seq_parameters.colour_primaries = read_u_v ( 8,"VUI: colour_primaries" , s, &p_Dec->UsedBits); + sps->vui_seq_parameters.transfer_characteristics = read_u_v ( 8,"VUI: transfer_characteristics" , s, &p_Dec->UsedBits); + sps->vui_seq_parameters.matrix_coefficients = read_u_v ( 8,"VUI: matrix_coefficients" , s, &p_Dec->UsedBits); + } + } + sps->vui_seq_parameters.chroma_location_info_present_flag = read_u_1 ( "VUI: chroma_loc_info_present_flag" , s, &p_Dec->UsedBits); + if(sps->vui_seq_parameters.chroma_location_info_present_flag) + { + sps->vui_seq_parameters.chroma_sample_loc_type_top_field = read_ue_v ( "VUI: chroma_sample_loc_type_top_field" , s, &p_Dec->UsedBits); + sps->vui_seq_parameters.chroma_sample_loc_type_bottom_field = read_ue_v ( "VUI: chroma_sample_loc_type_bottom_field" , s, &p_Dec->UsedBits); + } + sps->vui_seq_parameters.timing_info_present_flag = read_u_1 ("VUI: timing_info_present_flag" , s, &p_Dec->UsedBits); + if (sps->vui_seq_parameters.timing_info_present_flag) + { + sps->vui_seq_parameters.num_units_in_tick = read_u_v (32,"VUI: num_units_in_tick" , s, &p_Dec->UsedBits); + sps->vui_seq_parameters.time_scale = read_u_v (32,"VUI: time_scale" , s, &p_Dec->UsedBits); + sps->vui_seq_parameters.fixed_frame_rate_flag = read_u_1 ( "VUI: fixed_frame_rate_flag" , s, &p_Dec->UsedBits); + } + sps->vui_seq_parameters.nal_hrd_parameters_present_flag = read_u_1 ("VUI: nal_hrd_parameters_present_flag" , s, &p_Dec->UsedBits); + if (sps->vui_seq_parameters.nal_hrd_parameters_present_flag) + { + ReadHRDParameters(p, &(sps->vui_seq_parameters.nal_hrd_parameters)); + } + sps->vui_seq_parameters.vcl_hrd_parameters_present_flag = read_u_1 ("VUI: vcl_hrd_parameters_present_flag" , s, &p_Dec->UsedBits); + if (sps->vui_seq_parameters.vcl_hrd_parameters_present_flag) + { + ReadHRDParameters(p, &(sps->vui_seq_parameters.vcl_hrd_parameters)); + } + if (sps->vui_seq_parameters.nal_hrd_parameters_present_flag || sps->vui_seq_parameters.vcl_hrd_parameters_present_flag) + { + sps->vui_seq_parameters.low_delay_hrd_flag = read_u_1 ("VUI: low_delay_hrd_flag" , s, &p_Dec->UsedBits); + } + sps->vui_seq_parameters.pic_struct_present_flag = read_u_1 ("VUI: pic_struct_present_flag " , s, &p_Dec->UsedBits); + sps->vui_seq_parameters.bitstream_restriction_flag = read_u_1 ("VUI: bitstream_restriction_flag" , s, &p_Dec->UsedBits); + if (sps->vui_seq_parameters.bitstream_restriction_flag) + { + sps->vui_seq_parameters.motion_vectors_over_pic_boundaries_flag = read_u_1 ("VUI: motion_vectors_over_pic_boundaries_flag", s, &p_Dec->UsedBits); + sps->vui_seq_parameters.max_bytes_per_pic_denom = read_ue_v ("VUI: max_bytes_per_pic_denom" , s, &p_Dec->UsedBits); + sps->vui_seq_parameters.max_bits_per_mb_denom = read_ue_v ("VUI: max_bits_per_mb_denom" , s, &p_Dec->UsedBits); + sps->vui_seq_parameters.log2_max_mv_length_horizontal = read_ue_v ("VUI: log2_max_mv_length_horizontal" , s, &p_Dec->UsedBits); + sps->vui_seq_parameters.log2_max_mv_length_vertical = read_ue_v ("VUI: log2_max_mv_length_vertical" , s, &p_Dec->UsedBits); + sps->vui_seq_parameters.num_reorder_frames = read_ue_v ("VUI: num_reorder_frames" , s, &p_Dec->UsedBits); + sps->vui_seq_parameters.max_dec_frame_buffering = read_ue_v ("VUI: max_dec_frame_buffering" , s, &p_Dec->UsedBits); + } + } + + return 0; +} + + +int ReadHRDParameters(DataPartition *p, hrd_parameters_t *hrd) +{ + Bitstream *s = p->bitstream; + unsigned int SchedSelIdx; + + hrd->cpb_cnt_minus1 = read_ue_v ( "VUI: cpb_cnt_minus1" , s, &p_Dec->UsedBits); + hrd->bit_rate_scale = read_u_v ( 4,"VUI: bit_rate_scale" , s, &p_Dec->UsedBits); + hrd->cpb_size_scale = read_u_v ( 4,"VUI: cpb_size_scale" , s, &p_Dec->UsedBits); + + for( SchedSelIdx = 0; SchedSelIdx <= hrd->cpb_cnt_minus1; SchedSelIdx++ ) + { + hrd->bit_rate_value_minus1[ SchedSelIdx ] = read_ue_v ( "VUI: bit_rate_value_minus1" , s, &p_Dec->UsedBits); + hrd->cpb_size_value_minus1[ SchedSelIdx ] = read_ue_v ( "VUI: cpb_size_value_minus1" , s, &p_Dec->UsedBits); + hrd->cbr_flag[ SchedSelIdx ] = read_u_1 ( "VUI: cbr_flag" , s, &p_Dec->UsedBits); + } + + hrd->initial_cpb_removal_delay_length_minus1 = read_u_v ( 5,"VUI: initial_cpb_removal_delay_length_minus1" , s, &p_Dec->UsedBits); + hrd->cpb_removal_delay_length_minus1 = read_u_v ( 5,"VUI: cpb_removal_delay_length_minus1" , s, &p_Dec->UsedBits); + hrd->dpb_output_delay_length_minus1 = read_u_v ( 5,"VUI: dpb_output_delay_length_minus1" , s, &p_Dec->UsedBits); + hrd->time_offset_length = read_u_v ( 5,"VUI: time_offset_length" , s, &p_Dec->UsedBits); + + return 0; +} + + +int InterpretPPS (VideoParameters *p_Vid, DataPartition *p, pic_parameter_set_rbsp_t *pps) +{ + unsigned i; + unsigned n_ScalingList; + int chroma_format_idc; + int NumberBitsPerSliceGroupId; + Bitstream *s = p->bitstream; + + assert (p != NULL); + assert (p->bitstream != NULL); + assert (p->bitstream->streamBuffer != 0); + assert (pps != NULL); + + p_Dec->UsedBits = 0; + + pps->pic_parameter_set_id = read_ue_v ("PPS: pic_parameter_set_id" , s, &p_Dec->UsedBits); + pps->seq_parameter_set_id = read_ue_v ("PPS: seq_parameter_set_id" , s, &p_Dec->UsedBits); + pps->entropy_coding_mode_flag = read_u_1 ("PPS: entropy_coding_mode_flag" , s, &p_Dec->UsedBits); + + //! Note: as per JVT-F078 the following bit is unconditional. If F078 is not accepted, then + //! one has to fetch the correct SPS to check whether the bit is present (hopefully there is + //! no consistency problem :-( + //! The current encoder code handles this in the same way. When you change this, don't forget + //! the encoder! StW, 12/8/02 + pps->bottom_field_pic_order_in_frame_present_flag = read_u_1 ("PPS: bottom_field_pic_order_in_frame_present_flag" , s, &p_Dec->UsedBits); + + pps->num_slice_groups_minus1 = read_ue_v ("PPS: num_slice_groups_minus1" , s, &p_Dec->UsedBits); + + // FMO stuff begins here + if (pps->num_slice_groups_minus1 > 0) + { + pps->slice_group_map_type = read_ue_v ("PPS: slice_group_map_type" , s, &p_Dec->UsedBits); + if (pps->slice_group_map_type == 0) + { + for (i=0; i<=pps->num_slice_groups_minus1; i++) + pps->run_length_minus1 [i] = read_ue_v ("PPS: run_length_minus1 [i]" , s, &p_Dec->UsedBits); + } + else if (pps->slice_group_map_type == 2) + { + for (i=0; inum_slice_groups_minus1; i++) + { + //! JVT-F078: avoid reference of SPS by using ue(v) instead of u(v) + pps->top_left [i] = read_ue_v ("PPS: top_left [i]" , s, &p_Dec->UsedBits); + pps->bottom_right [i] = read_ue_v ("PPS: bottom_right [i]" , s, &p_Dec->UsedBits); + } + } + else if (pps->slice_group_map_type == 3 || + pps->slice_group_map_type == 4 || + pps->slice_group_map_type == 5) + { + pps->slice_group_change_direction_flag = read_u_1 ("PPS: slice_group_change_direction_flag" , s, &p_Dec->UsedBits); + pps->slice_group_change_rate_minus1 = read_ue_v ("PPS: slice_group_change_rate_minus1" , s, &p_Dec->UsedBits); + } + else if (pps->slice_group_map_type == 6) + { + if (pps->num_slice_groups_minus1+1 >4) + NumberBitsPerSliceGroupId = 3; + else if (pps->num_slice_groups_minus1+1 > 2) + NumberBitsPerSliceGroupId = 2; + else + NumberBitsPerSliceGroupId = 1; + pps->pic_size_in_map_units_minus1 = read_ue_v ("PPS: pic_size_in_map_units_minus1" , s, &p_Dec->UsedBits); + if ((pps->slice_group_id = calloc (pps->pic_size_in_map_units_minus1+1, 1)) == NULL) + no_mem_exit ("InterpretPPS: slice_group_id"); + for (i=0; i<=pps->pic_size_in_map_units_minus1; i++) + pps->slice_group_id[i] = (byte) read_u_v (NumberBitsPerSliceGroupId, "slice_group_id[i]", s, &p_Dec->UsedBits); + } + } + + // End of FMO stuff + + pps->num_ref_idx_l0_default_active_minus1 = read_ue_v ("PPS: num_ref_idx_l0_default_active_minus1" , s, &p_Dec->UsedBits); + pps->num_ref_idx_l1_default_active_minus1 = read_ue_v ("PPS: num_ref_idx_l1_default_active_minus1" , s, &p_Dec->UsedBits); + pps->weighted_pred_flag = read_u_1 ("PPS: weighted_pred_flag" , s, &p_Dec->UsedBits); + pps->weighted_bipred_idc = read_u_v ( 2, "PPS: weighted_bipred_idc" , s, &p_Dec->UsedBits); + pps->pic_init_qp_minus26 = read_se_v ("PPS: pic_init_qp_minus26" , s, &p_Dec->UsedBits); + pps->pic_init_qs_minus26 = read_se_v ("PPS: pic_init_qs_minus26" , s, &p_Dec->UsedBits); + + pps->chroma_qp_index_offset = read_se_v ("PPS: chroma_qp_index_offset" , s, &p_Dec->UsedBits); + + pps->deblocking_filter_control_present_flag = read_u_1 ("PPS: deblocking_filter_control_present_flag" , s, &p_Dec->UsedBits); + pps->constrained_intra_pred_flag = read_u_1 ("PPS: constrained_intra_pred_flag" , s, &p_Dec->UsedBits); + pps->redundant_pic_cnt_present_flag = read_u_1 ("PPS: redundant_pic_cnt_present_flag" , s, &p_Dec->UsedBits); + + if(more_rbsp_data(s->streamBuffer, s->frame_bitoffset,s->bitstream_length)) // more_data_in_rbsp() + { + //Fidelity Range Extensions Stuff + pps->transform_8x8_mode_flag = read_u_1 ("PPS: transform_8x8_mode_flag" , s, &p_Dec->UsedBits); + pps->pic_scaling_matrix_present_flag = read_u_1 ("PPS: pic_scaling_matrix_present_flag" , s, &p_Dec->UsedBits); + + if(pps->pic_scaling_matrix_present_flag) + { + chroma_format_idc = p_Vid->SeqParSet[pps->seq_parameter_set_id].chroma_format_idc; + n_ScalingList = 6 + ((chroma_format_idc != YUV444) ? 2 : 6) * pps->transform_8x8_mode_flag; + for(i=0; ipic_scaling_list_present_flag[i]= read_u_1 ("PPS: pic_scaling_list_present_flag" , s, &p_Dec->UsedBits); + + if(pps->pic_scaling_list_present_flag[i]) + { + if(i<6) + Scaling_List(pps->ScalingList4x4[i], 16, &pps->UseDefaultScalingMatrix4x4Flag[i], s); + else + Scaling_List(pps->ScalingList8x8[i-6], 64, &pps->UseDefaultScalingMatrix8x8Flag[i-6], s); + } + } + } + pps->second_chroma_qp_index_offset = read_se_v ("PPS: second_chroma_qp_index_offset" , s, &p_Dec->UsedBits); + } + else + { + pps->second_chroma_qp_index_offset = pps->chroma_qp_index_offset; + } + + pps->Valid = TRUE; + return p_Dec->UsedBits; +} + + +void PPSConsistencyCheck (pic_parameter_set_rbsp_t *pps) +{ + printf ("Consistency checking a picture parset, to be implemented\n"); +// if (pps->seq_parameter_set_id invalid then do something) +} + +void SPSConsistencyCheck (seq_parameter_set_rbsp_t *sps) +{ + printf ("Consistency checking a sequence parset, to be implemented\n"); +} + +#if (MVC_EXTENSION_ENABLE) +void SubsetSPSConsistencyCheck (subset_seq_parameter_set_rbsp_t *subset_sps) +{ + printf ("Consistency checking a subset sequence parset, to be implemented\n"); +} +#endif + +void MakePPSavailable (VideoParameters *p_Vid, int id, pic_parameter_set_rbsp_t *pps) +{ + assert (pps->Valid == TRUE); + + if (p_Vid->PicParSet[id].Valid == TRUE && p_Vid->PicParSet[id].slice_group_id != NULL) + free (p_Vid->PicParSet[id].slice_group_id); + + memcpy (&p_Vid->PicParSet[id], pps, sizeof (pic_parameter_set_rbsp_t)); + + // we can simply use the memory provided with the pps. the PPS is destroyed after this function + // call and will not try to free if pps->slice_group_id == NULL + p_Vid->PicParSet[id].slice_group_id = pps->slice_group_id; + pps->slice_group_id = NULL; +} + +void CleanUpPPS(VideoParameters *p_Vid) +{ + int i; + + for (i=0; iPicParSet[i].Valid == TRUE && p_Vid->PicParSet[i].slice_group_id != NULL) + free (p_Vid->PicParSet[i].slice_group_id); + + p_Vid->PicParSet[i].Valid = FALSE; + } +} + + +void MakeSPSavailable (VideoParameters *p_Vid, int id, seq_parameter_set_rbsp_t *sps) +{ + assert (sps->Valid == TRUE); + memcpy (&p_Vid->SeqParSet[id], sps, sizeof (seq_parameter_set_rbsp_t)); +} + + +void ProcessSPS (VideoParameters *p_Vid, NALU_t *nalu) +{ + DataPartition *dp = AllocPartition(1); + seq_parameter_set_rbsp_t *sps = AllocSPS(); + + memcpy (dp->bitstream->streamBuffer, &nalu->buf[1], nalu->len-1); + dp->bitstream->code_len = dp->bitstream->bitstream_length = RBSPtoSODB (dp->bitstream->streamBuffer, nalu->len-1); + dp->bitstream->ei_flag = 0; + dp->bitstream->read_len = dp->bitstream->frame_bitoffset = 0; + + InterpretSPS (p_Vid, dp, sps); +#if (MVC_EXTENSION_ENABLE) + get_max_dec_frame_buf_size(sps); +#endif + + if (sps->Valid) + { + if (p_Vid->active_sps) + { + if (sps->seq_parameter_set_id == p_Vid->active_sps->seq_parameter_set_id) + { + if (!sps_is_equal(sps, p_Vid->active_sps)) + { + if (p_Vid->dec_picture) // && p_Vid->num_dec_mb == p_Vid->PicSizeInMbs) //? + { + // this may only happen on slice loss + exit_picture(p_Vid, &p_Vid->dec_picture); + } + p_Vid->active_sps=NULL; + } + } + } + // SPSConsistencyCheck (pps); + MakeSPSavailable (p_Vid, sps->seq_parameter_set_id, sps); +#if (MVC_EXTENSION_ENABLE) + if (p_Vid->profile_idc < (int) sps->profile_idc) + { + p_Vid->profile_idc = sps->profile_idc; + } +#else + p_Vid->profile_idc = sps->profile_idc; +#endif + p_Vid->separate_colour_plane_flag = sps->separate_colour_plane_flag; + if( p_Vid->separate_colour_plane_flag ) + { + p_Vid->ChromaArrayType = 0; + } + else + { + p_Vid->ChromaArrayType = sps->chroma_format_idc; + } + } + + FreePartition (dp, 1); + FreeSPS (sps); +} + +#if (MVC_EXTENSION_ENABLE) +void ProcessSubsetSPS (VideoParameters *p_Vid, NALU_t *nalu) +{ + DataPartition *dp = AllocPartition(1); + subset_seq_parameter_set_rbsp_t *subset_sps; + int curr_seq_set_id; + + memcpy (dp->bitstream->streamBuffer, &nalu->buf[1], nalu->len-1); + dp->bitstream->code_len = dp->bitstream->bitstream_length = RBSPtoSODB (dp->bitstream->streamBuffer, nalu->len-1); + dp->bitstream->ei_flag = 0; + dp->bitstream->read_len = dp->bitstream->frame_bitoffset = 0; + InterpretSubsetSPS (p_Vid, dp, &curr_seq_set_id); + + subset_sps = p_Vid->SubsetSeqParSet + curr_seq_set_id; + get_max_dec_frame_buf_size(&(subset_sps->sps)); + //check capability; + if(subset_sps->num_views_minus1 > MAX_VIEW_NUM ) + { + printf("Warning: num_views:%d is greater than %d, only decode baselayer!\n", subset_sps->num_views_minus1+1, MAX_VIEW_NUM); + subset_sps->Valid = 0; + subset_sps->sps.Valid = 0; + p_Vid->p_Inp->DecodeAllLayers = 0; + } + else if(subset_sps->num_views_minus1==1 && (subset_sps->view_id[0]!=0 || subset_sps->view_id[1]!=1)) + { + OpenOutputFiles(p_Vid, subset_sps->view_id[0], subset_sps->view_id[1]); + } + else + { + int layer; + for(layer=0; layernum_views_minus1+1; layer++) + { + OpenOutputFiles_per_layer(p_Vid, layer); + OpenRefFiles_per_layer(p_Vid, layer); + } + } + + if (subset_sps->Valid) + { + // SubsetSPSConsistencyCheck (subset_sps); + p_Vid->profile_idc = subset_sps->sps.profile_idc; + p_Vid->separate_colour_plane_flag = subset_sps->sps.separate_colour_plane_flag; + if( p_Vid->separate_colour_plane_flag ) + { + p_Vid->ChromaArrayType = 0; + } + else + { + p_Vid->ChromaArrayType = subset_sps->sps.chroma_format_idc; + } + } + + FreePartition (dp, 1); +} +#endif + +void ProcessPPS (VideoParameters *p_Vid, NALU_t *nalu) +{ + DataPartition *dp = AllocPartition(1); + pic_parameter_set_rbsp_t *pps = AllocPPS(); + + memcpy (dp->bitstream->streamBuffer, &nalu->buf[1], nalu->len-1); + dp->bitstream->code_len = dp->bitstream->bitstream_length = RBSPtoSODB (dp->bitstream->streamBuffer, nalu->len-1); + dp->bitstream->ei_flag = 0; + dp->bitstream->read_len = dp->bitstream->frame_bitoffset = 0; + InterpretPPS (p_Vid, dp, pps); + // PPSConsistencyCheck (pps); + if (p_Vid->active_pps) + { + if (pps->pic_parameter_set_id == p_Vid->active_pps->pic_parameter_set_id) + { + if(!pps_is_equal(pps, p_Vid->active_pps)) + { + //copy to next PPS; + memcpy(p_Vid->pNextPPS, p_Vid->active_pps, sizeof (pic_parameter_set_rbsp_t)); + { + if (p_Vid->dec_picture) // && p_Vid->num_dec_mb == p_Vid->PicSizeInMbs) + { + // this may only happen on slice loss + exit_picture(p_Vid, &p_Vid->dec_picture); + } + p_Vid->active_pps = NULL; + } + } + } + } + MakePPSavailable (p_Vid, pps->pic_parameter_set_id, pps); + FreePartition (dp, 1); + FreePPS (pps); +} + +/*! + ************************************************************************ + * \brief + * Updates images max values + * + ************************************************************************ + */ +static void updateMaxValue(FrameFormat *format) +{ + format->max_value[0] = (1 << format->bit_depth[0]) - 1; + format->max_value_sq[0] = format->max_value[0] * format->max_value[0]; + format->max_value[1] = (1 << format->bit_depth[1]) - 1; + format->max_value_sq[1] = format->max_value[1] * format->max_value[1]; + format->max_value[2] = (1 << format->bit_depth[2]) - 1; + format->max_value_sq[2] = format->max_value[2] * format->max_value[2]; +} + +/*! + ************************************************************************ + * \brief + * Reset format information + * + ************************************************************************ + */ +void reset_format_info(seq_parameter_set_rbsp_t *sps, VideoParameters *p_Vid, FrameFormat *source, FrameFormat *output) +{ + InputParameters *p_Inp = p_Vid->p_Inp; + static const int SubWidthC [4]= { 1, 2, 2, 1}; + static const int SubHeightC [4]= { 1, 2, 1, 1}; + + int crop_left, crop_right; + int crop_top, crop_bottom; + + // cropping for luma + if (sps->frame_cropping_flag) + { + crop_left = SubWidthC [sps->chroma_format_idc] * sps->frame_crop_left_offset; + crop_right = SubWidthC [sps->chroma_format_idc] * sps->frame_crop_right_offset; + crop_top = SubHeightC[sps->chroma_format_idc] * ( 2 - sps->frame_mbs_only_flag ) * sps->frame_crop_top_offset; + crop_bottom = SubHeightC[sps->chroma_format_idc] * ( 2 - sps->frame_mbs_only_flag ) * sps->frame_crop_bottom_offset; + } + else + { + crop_left = crop_right = crop_top = crop_bottom = 0; + } + + source->width[0] = p_Vid->width - crop_left - crop_right; + source->height[0] = p_Vid->height - crop_top - crop_bottom; + + // cropping for chroma + if (sps->frame_cropping_flag) + { + crop_left = sps->frame_crop_left_offset; + crop_right = sps->frame_crop_right_offset; + crop_top = ( 2 - sps->frame_mbs_only_flag ) * sps->frame_crop_top_offset; + crop_bottom = ( 2 - sps->frame_mbs_only_flag ) * sps->frame_crop_bottom_offset; + } + else + { + crop_left = crop_right = crop_top = crop_bottom = 0; + } + + if ((sps->chroma_format_idc==YUV400) && p_Inp->write_uv) + { + source->width[1] = (source->width[0] >> 1); + source->width[2] = source->width[1]; + source->height[1] = (source->height[0] >> 1); + source->height[2] = source->height[1]; + } + else + { + source->width[1] = p_Vid->width_cr - crop_left - crop_right; + source->width[2] = source->width[1]; + source->height[1] = p_Vid->height_cr - crop_top - crop_bottom; + source->height[2] = source->height[1]; + } + + output->width[0] = p_Vid->width; + source->width[1] = p_Vid->width_cr; + source->width[2] = p_Vid->width_cr; + output->height[0] = p_Vid->height; + output->height[1] = p_Vid->height_cr; + output->height[2] = p_Vid->height_cr; + + source->size_cmp[0] = source->width[0] * source->height[0]; + source->size_cmp[1] = source->width[1] * source->height[1]; + source->size_cmp[2] = source->size_cmp[1]; + source->size = source->size_cmp[0] + source->size_cmp[1] + source->size_cmp[2]; + source->mb_width = source->width[0] / MB_BLOCK_SIZE; + source->mb_height = source->height[0] / MB_BLOCK_SIZE; + + // output size (excluding padding) + output->size_cmp[0] = output->width[0] * output->height[0]; + output->size_cmp[1] = output->width[1] * output->height[1]; + output->size_cmp[2] = output->size_cmp[1]; + output->size = output->size_cmp[0] + output->size_cmp[1] + output->size_cmp[2]; + output->mb_width = output->width[0] / MB_BLOCK_SIZE; + output->mb_height = output->height[0] / MB_BLOCK_SIZE; + + + output->bit_depth[0] = source->bit_depth[0] = p_Vid->bitdepth_luma; + output->bit_depth[1] = source->bit_depth[1] = p_Vid->bitdepth_chroma; + output->bit_depth[2] = source->bit_depth[2] = p_Vid->bitdepth_chroma; + output->pic_unit_size_on_disk = (imax(output->bit_depth[0], output->bit_depth[1]) > 8) ? 16 : 8; + output->pic_unit_size_shift3 = output->pic_unit_size_on_disk >> 3; + + output->frame_rate = source->frame_rate; + output->color_model = source->color_model; + output->yuv_format = source->yuv_format = (ColorFormat) sps->chroma_format_idc; + + output->auto_crop_bottom = crop_bottom; + output->auto_crop_right = crop_right; + output->auto_crop_bottom_cr = (crop_bottom * p_Vid->mb_cr_size_y) / MB_BLOCK_SIZE; + output->auto_crop_right_cr = (crop_right * p_Vid->mb_cr_size_x) / MB_BLOCK_SIZE; + + source->auto_crop_bottom = output->auto_crop_bottom; + source->auto_crop_right = output->auto_crop_right; + source->auto_crop_bottom_cr = output->auto_crop_bottom_cr; + source->auto_crop_right_cr = output->auto_crop_right_cr; + + updateMaxValue(source); + updateMaxValue(output); + + if (p_Vid->first_sps == TRUE) { + p_Vid->first_sps = FALSE; + if(!p_Inp->bDisplayDecParams) { + printf("Image Format : %dx%d (%dx%d)\n", source->width[0], source->height[0], p_Vid->width, p_Vid->height); + if (p_Vid->yuv_format == YUV400) + printf("Color Format : 4:0:0 "); + else if (p_Vid->yuv_format == YUV420) + printf("Color Format : 4:2:0 "); + else if (p_Vid->yuv_format == YUV422) + printf("Color Format : 4:2:2 "); + else + printf("Color Format : 4:4:4 "); + + printf("(%d:%d:%d)\n", source->bit_depth[0], source->bit_depth[1], source->bit_depth[2]); + printf("--------------------------------------------------------------------------\n"); + } + if (!p_Inp->silent) + { + printf("POC must = frame# or field# for SNRs to be correct\n"); + printf("--------------------------------------------------------------------------\n"); + printf(" Frame POC Pic# QP SnrY SnrU SnrV Y:U:V Time(ms)\n"); + printf("--------------------------------------------------------------------------\n"); + } + } +} + +static void setup_layer_info(VideoParameters *p_Vid, seq_parameter_set_rbsp_t *sps, LayerParameters *p_Lps) +{ + int layer_id = p_Lps->layer_id; + p_Lps->p_Vid = p_Vid; + p_Lps->p_Cps = p_Vid->p_EncodePar[layer_id]; + p_Lps->p_SPS = sps; + p_Lps->p_Dpb = p_Vid->p_Dpb_layer[layer_id]; +} + +static void set_coding_par(seq_parameter_set_rbsp_t *sps, CodingParameters *cps) +{ + // maximum vertical motion vector range in luma quarter pixel units + cps->profile_idc = sps->profile_idc; + cps->lossless_qpprime_flag = sps->lossless_qpprime_flag; + if (sps->level_idc <= 10) + { + cps->max_vmv_r = 64 * 4; + } + else if (sps->level_idc <= 20) + { + cps->max_vmv_r = 128 * 4; + } + else if (sps->level_idc <= 30) + { + cps->max_vmv_r = 256 * 4; + } + else + { + cps->max_vmv_r = 512 * 4; // 512 pixels in quarter pixels + } + + // Fidelity Range Extensions stuff (part 1) + cps->bitdepth_chroma = 0; + cps->width_cr = 0; + cps->height_cr = 0; + cps->bitdepth_luma = (short) (sps->bit_depth_luma_minus8 + 8); + cps->bitdepth_scale[0] = 1 << sps->bit_depth_luma_minus8; + if (sps->chroma_format_idc != YUV400) + { + cps->bitdepth_chroma = (short) (sps->bit_depth_chroma_minus8 + 8); + cps->bitdepth_scale[1] = 1 << sps->bit_depth_chroma_minus8; + } + + cps->max_frame_num = 1<<(sps->log2_max_frame_num_minus4+4); + cps->PicWidthInMbs = (sps->pic_width_in_mbs_minus1 +1); + cps->PicHeightInMapUnits = (sps->pic_height_in_map_units_minus1 +1); + cps->FrameHeightInMbs = ( 2 - sps->frame_mbs_only_flag ) * cps->PicHeightInMapUnits; + cps->FrameSizeInMbs = cps->PicWidthInMbs * cps->FrameHeightInMbs; + + cps->yuv_format=sps->chroma_format_idc; + cps->separate_colour_plane_flag = sps->separate_colour_plane_flag; + if( cps->separate_colour_plane_flag ) + { + cps->ChromaArrayType = 0; + } + else + { + cps->ChromaArrayType = sps->chroma_format_idc; + } + + cps->width = cps->PicWidthInMbs * MB_BLOCK_SIZE; + cps->height = cps->FrameHeightInMbs * MB_BLOCK_SIZE; + + cps->iLumaPadX = MCBUF_LUMA_PAD_X; + cps->iLumaPadY = MCBUF_LUMA_PAD_Y; + cps->iChromaPadX = MCBUF_CHROMA_PAD_X; + cps->iChromaPadY = MCBUF_CHROMA_PAD_Y; + if (sps->chroma_format_idc == YUV420) + { + cps->width_cr = (cps->width >> 1); + cps->height_cr = (cps->height >> 1); + } + else if (sps->chroma_format_idc == YUV422) + { + cps->width_cr = (cps->width >> 1); + cps->height_cr = cps->height; + cps->iChromaPadY = MCBUF_CHROMA_PAD_Y*2; + } + else if (sps->chroma_format_idc == YUV444) + { + //YUV444 + cps->width_cr = cps->width; + cps->height_cr = cps->height; + cps->iChromaPadX = cps->iLumaPadX; + cps->iChromaPadY = cps->iLumaPadY; + } + //pel bitdepth init + cps->bitdepth_luma_qp_scale = 6 * (cps->bitdepth_luma - 8); + + if(cps->bitdepth_luma > cps->bitdepth_chroma || sps->chroma_format_idc == YUV400) + cps->pic_unit_bitsize_on_disk = (cps->bitdepth_luma > 8)? 16:8; + else + cps->pic_unit_bitsize_on_disk = (cps->bitdepth_chroma > 8)? 16:8; + cps->dc_pred_value_comp[0] = 1<<(cps->bitdepth_luma - 1); + cps->max_pel_value_comp[0] = (1<bitdepth_luma) - 1; + cps->mb_size[0][0] = cps->mb_size[0][1] = MB_BLOCK_SIZE; + + if (sps->chroma_format_idc != YUV400) + { + //for chrominance part + cps->bitdepth_chroma_qp_scale = 6 * (cps->bitdepth_chroma - 8); + cps->dc_pred_value_comp[1] = (1 << (cps->bitdepth_chroma - 1)); + cps->dc_pred_value_comp[2] = cps->dc_pred_value_comp[1]; + cps->max_pel_value_comp[1] = (1 << cps->bitdepth_chroma) - 1; + cps->max_pel_value_comp[2] = (1 << cps->bitdepth_chroma) - 1; + cps->num_blk8x8_uv = (1 << sps->chroma_format_idc) & (~(0x1)); + cps->num_uv_blocks = (cps->num_blk8x8_uv >> 1); + cps->num_cdc_coeff = (cps->num_blk8x8_uv << 1); + cps->mb_size[1][0] = cps->mb_size[2][0] = cps->mb_cr_size_x = (sps->chroma_format_idc==YUV420 || sps->chroma_format_idc==YUV422)? 8 : 16; + cps->mb_size[1][1] = cps->mb_size[2][1] = cps->mb_cr_size_y = (sps->chroma_format_idc==YUV444 || sps->chroma_format_idc==YUV422)? 16 : 8; + + cps->subpel_x = cps->mb_cr_size_x == 8 ? 7 : 3; + cps->subpel_y = cps->mb_cr_size_y == 8 ? 7 : 3; + cps->shiftpel_x = cps->mb_cr_size_x == 8 ? 3 : 2; + cps->shiftpel_y = cps->mb_cr_size_y == 8 ? 3 : 2; + cps->total_scale = cps->shiftpel_x + cps->shiftpel_y; + } + else + { + cps->bitdepth_chroma_qp_scale = 0; + cps->max_pel_value_comp[1] = 0; + cps->max_pel_value_comp[2] = 0; + cps->num_blk8x8_uv = 0; + cps->num_uv_blocks = 0; + cps->num_cdc_coeff = 0; + cps->mb_size[1][0] = cps->mb_size[2][0] = cps->mb_cr_size_x = 0; + cps->mb_size[1][1] = cps->mb_size[2][1] = cps->mb_cr_size_y = 0; + cps->subpel_x = 0; + cps->subpel_y = 0; + cps->shiftpel_x = 0; + cps->shiftpel_y = 0; + cps->total_scale = 0; + } + + cps->mb_cr_size = cps->mb_cr_size_x * cps->mb_cr_size_y; + cps->mb_size_blk[0][0] = cps->mb_size_blk[0][1] = cps->mb_size[0][0] >> 2; + cps->mb_size_blk[1][0] = cps->mb_size_blk[2][0] = cps->mb_size[1][0] >> 2; + cps->mb_size_blk[1][1] = cps->mb_size_blk[2][1] = cps->mb_size[1][1] >> 2; + + cps->mb_size_shift[0][0] = cps->mb_size_shift[0][1] = CeilLog2_sf (cps->mb_size[0][0]); + cps->mb_size_shift[1][0] = cps->mb_size_shift[2][0] = CeilLog2_sf (cps->mb_size[1][0]); + cps->mb_size_shift[1][1] = cps->mb_size_shift[2][1] = CeilLog2_sf (cps->mb_size[1][1]); + + cps->rgb_output = (sps->vui_seq_parameters.matrix_coefficients==0); +} + +/*! + ************************************************************************ + * \brief + * Activate Sequence Parameter Sets + * + ************************************************************************ + */ +void activate_sps (VideoParameters *p_Vid, seq_parameter_set_rbsp_t *sps) +{ + InputParameters *p_Inp = p_Vid->p_Inp; + int i; + if (p_Vid->active_sps != sps) + { + if (p_Vid->dec_picture) + { + // this may only happen on slice loss + exit_picture(p_Vid, &p_Vid->dec_picture); + } + p_Vid->active_sps = sps; + + if(p_Vid->dpb_layer_id==0 && is_BL_profile(sps->profile_idc) && !p_Vid->p_Dpb_layer[0]->init_done) + { + set_coding_par(sps, p_Vid->p_EncodePar[0]); + setup_layer_info( p_Vid, sps, p_Vid->p_LayerPar[0]); + } + else if(p_Vid->dpb_layer_id>=1 && is_EL_profile(sps->profile_idc) && !p_Vid->p_Dpb_layer[1]->init_done) + { + for(i=1;iactive_subset_sps->num_views_minus1+1;i++) + { + if( !p_Vid->p_Dpb_layer[i]->init_done ) + { + set_coding_par(sps, p_Vid->p_EncodePar[i]); + setup_layer_info(p_Vid, sps, p_Vid->p_LayerPar[i]); + } + } + //set_coding_par(sps, p_Vid->p_EncodePar[1]); + //setup_layer_info(p_Vid, sps, p_Vid->p_LayerPar[1]); + } + else if(p_Vid->dpb_layer_id==2 && is_EL_profile(sps->profile_idc) && !p_Vid->p_Dpb_layer[2]->init_done) + { + set_coding_par(sps, p_Vid->p_EncodePar[2]); + setup_layer_info(p_Vid, sps, p_Vid->p_LayerPar[2]); + } + +//to be removed in future; + set_global_coding_par(p_Vid, p_Vid->p_EncodePar[p_Vid->dpb_layer_id]); +//end; + +#if (MVC_EXTENSION_ENABLE) + //init_frext(p_Vid); + if (/*p_Vid->last_pic_width_in_mbs_minus1 != p_Vid->active_sps->pic_width_in_mbs_minus1 + || p_Vid->last_pic_height_in_map_units_minus1 != p_Vid->active_sps->pic_height_in_map_units_minus1 + || p_Vid->last_max_dec_frame_buffering != GetMaxDecFrameBuffering(p_Vid) + || */(p_Vid->last_profile_idc != p_Vid->active_sps->profile_idc && is_BL_profile(p_Vid->active_sps->profile_idc) && !p_Vid->p_Dpb_layer[0]->init_done /*&& is_BL_profile(p_Vid->last_profile_idc)*/)) + { + //init_frext(p_Vid); + init_global_buffers(p_Vid, 0); + + if (!p_Vid->no_output_of_prior_pics_flag) + { + flush_dpb(p_Vid->p_Dpb_layer[0]); + flush_dpb(p_Vid->p_Dpb_layer[1]); + } + init_dpb(p_Vid, p_Vid->p_Dpb_layer[0], 1); + } + else if(p_Vid->last_profile_idc != p_Vid->active_sps->profile_idc && ( + is_MVC_profile(p_Vid->last_profile_idc) || is_MVC_profile(p_Vid->active_sps->profile_idc) + )&& (!p_Vid->p_Dpb_layer[1]->init_done)) + { + assert(p_Vid->p_Dpb_layer[0]->init_done); + //init_frext(p_Vid); + if(p_Vid->p_Dpb_layer[0]->init_done) + { + free_dpb(p_Vid->p_Dpb_layer[0]); + init_dpb(p_Vid, p_Vid->p_Dpb_layer[0], 1); + } + + init_global_buffers(p_Vid, p_Vid->dpb_layer_id); + // for now lets re_init both buffers. Later, we should only re_init appropriate one + // Note that we seem to be doing this for every frame which seems not good. + //re_init_dpb(p_Vid, p_Vid->p_Dpb_layer[1], 2); +#if MVC_EXTENSION_ENABLE + for(i=1;iactive_subset_sps->num_views_minus1+1;i++) + { + init_global_buffers(p_Vid, i); + init_dpb(p_Vid, p_Vid->p_Dpb_layer[i], 2); + } + // init_dpb(p_Vid, p_Vid->p_Dpb_layer[p_Vid->dpb_layer_id], 2); +#endif + //p_Vid->last_profile_idc = p_Vid->active_sps->profile_idc; + } + //p_Vid->p_Dpb_layer[0]->num_ref_frames = p_Vid->active_sps->num_ref_frames; + //p_Vid->p_Dpb_layer[1]->num_ref_frames = p_Vid->active_sps->num_ref_frames; + p_Vid->last_pic_width_in_mbs_minus1 = p_Vid->active_sps->pic_width_in_mbs_minus1; + p_Vid->last_pic_height_in_map_units_minus1 = p_Vid->active_sps->pic_height_in_map_units_minus1; + p_Vid->last_max_dec_frame_buffering = GetMaxDecFrameBuffering(p_Vid); + p_Vid->last_profile_idc = p_Vid->active_sps->profile_idc; + +#else + //init_frext(p_Vid); + init_global_buffers(p_Vid, 0); + + if (!p_Vid->no_output_of_prior_pics_flag) + { + flush_dpb(p_Vid->p_Dpb_layer[0]); + } + init_dpb(p_Vid, p_Vid->p_Dpb_layer[0], 0); + // for now lets init both buffers. Later, we should only re_init appropriate one + //init_dpb(p_Vid, p_Vid->p_Dpb_layer[0], 1); + // obviously this is not needed her but just adding it for completeness + //init_dpb(p_Vid, p_Vid->p_Dpb_layer[1], 2); +#endif + +#if (DISABLE_ERC == 0) + ercInit(p_Vid, p_Vid->width, p_Vid->height, 1); + if(p_Vid->dec_picture) + { + ercReset(p_Vid->erc_errorVar, p_Vid->PicSizeInMbs, p_Vid->PicSizeInMbs, p_Vid->dec_picture->size_x); + p_Vid->erc_mvperMB = 0; + } +#endif + } + + reset_format_info(sps, p_Vid, &p_Inp->source, &p_Inp->output); +} + +void activate_pps(VideoParameters *p_Vid, pic_parameter_set_rbsp_t *pps) +{ + if (p_Vid->active_pps != pps) + { + if (p_Vid->dec_picture) // && p_Vid->num_dec_mb == p_Vid->pi) + { + // this may only happen on slice loss + exit_picture(p_Vid, &p_Vid->dec_picture); + } + + p_Vid->active_pps = pps; + } +} + +void UseParameterSet (Slice *currSlice) +{ + VideoParameters *p_Vid = currSlice->p_Vid; + int PicParsetId = currSlice->pic_parameter_set_id; + pic_parameter_set_rbsp_t *pps = &p_Vid->PicParSet[PicParsetId]; + seq_parameter_set_rbsp_t *sps = &p_Vid->SeqParSet[pps->seq_parameter_set_id]; + int i; + + if (pps->Valid != TRUE) + printf ("Trying to use an invalid (uninitialized) Picture Parameter Set with ID %d, expect the unexpected...\n", PicParsetId); +#if (MVC_EXTENSION_ENABLE) + if((currSlice->svc_extension_flag == -1)) + { + if (sps->Valid != TRUE) + printf ("PicParset %d references an invalid (uninitialized) Sequence Parameter Set with ID %d, expect the unexpected...\n", + PicParsetId, (int) pps->seq_parameter_set_id); + } + else + { + // Set SPS to the subset SPS parameters + p_Vid->active_subset_sps = p_Vid->SubsetSeqParSet + pps->seq_parameter_set_id; + sps = &(p_Vid->active_subset_sps->sps); + if (p_Vid->SubsetSeqParSet[pps->seq_parameter_set_id].Valid != TRUE) + printf ("PicParset %d references an invalid (uninitialized) Subset Sequence Parameter Set with ID %d, expect the unexpected...\n", + PicParsetId, (int) pps->seq_parameter_set_id); + } +#else + if (sps->Valid != TRUE) + printf ("PicParset %d references an invalid (uninitialized) Sequence Parameter Set with ID %d, expect the unexpected...\n", + PicParsetId, (int) pps->seq_parameter_set_id); +#endif + + // In theory, and with a well-designed software, the lines above + // are everything necessary. In practice, we need to patch many values + // in p_Vid-> (but no more in p_Inp-> -- these have been taken care of) + + // Set Sequence Parameter Stuff first + // printf ("Using Picture Parameter set %d and associated Sequence Parameter Set %d\n", PicParsetId, pps->seq_parameter_set_id); + if ((int) sps->pic_order_cnt_type < 0 || sps->pic_order_cnt_type > 2) // != 1 + { + printf ("invalid sps->pic_order_cnt_type = %d\n", (int) sps->pic_order_cnt_type); + error ("pic_order_cnt_type != 1", -1000); + } + + if (sps->pic_order_cnt_type == 1) + { + if(sps->num_ref_frames_in_pic_order_cnt_cycle >= MAXnum_ref_frames_in_pic_order_cnt_cycle) + { + error("num_ref_frames_in_pic_order_cnt_cycle too large",-1011); + } + } + p_Vid->dpb_layer_id = currSlice->layer_id; + activate_sps(p_Vid, sps); + activate_pps(p_Vid, pps); + + // currSlice->dp_mode is set by read_new_slice (NALU first byte available there) + if (pps->entropy_coding_mode_flag == (Boolean) CAVLC) + { + currSlice->nal_startcode_follows = uvlc_startcode_follows; + for (i=0; i<3; i++) + { + currSlice->partArr[i].readSyntaxElement = readSyntaxElement_UVLC; + } + } + else + { + currSlice->nal_startcode_follows = cabac_startcode_follows; + for (i=0; i<3; i++) + { + currSlice->partArr[i].readSyntaxElement = readSyntaxElement_CABAC; + } + } + p_Vid->type = currSlice->slice_type; +} + +#if (MVC_EXTENSION_ENABLE) +void seq_parameter_set_mvc_extension(subset_seq_parameter_set_rbsp_t *subset_sps, Bitstream *s) +{ + int i, j, num_views; + + subset_sps->num_views_minus1 = read_ue_v("num_views_minus1", s, &p_Dec->UsedBits); + num_views = 1+subset_sps->num_views_minus1; + if( num_views >0) + { + if ((subset_sps->view_id = (int*) calloc(num_views, sizeof(int))) == NULL) + no_mem_exit("init_subset_seq_parameter_set: subset_sps->view_id"); + if ((subset_sps->num_anchor_refs_l0 = (int*) calloc(num_views, sizeof(int))) == NULL) + no_mem_exit("init_subset_seq_parameter_set: subset_sps->num_anchor_refs_l0"); + if ((subset_sps->num_anchor_refs_l1 = (int*) calloc(num_views, sizeof(int))) == NULL) + no_mem_exit("init_subset_seq_parameter_set: subset_sps->num_anchor_refs_l1"); + if ((subset_sps->anchor_ref_l0 = (int**) calloc(num_views, sizeof(int*))) == NULL) + no_mem_exit("init_subset_seq_parameter_set: subset_sps->anchor_ref_l0"); + if ((subset_sps->anchor_ref_l1 = (int**) calloc(num_views, sizeof(int*))) == NULL) + no_mem_exit("init_subset_seq_parameter_set: subset_sps->anchor_ref_l1"); + if ((subset_sps->num_non_anchor_refs_l0 = (int*) calloc(num_views, sizeof(int))) == NULL) + no_mem_exit("init_subset_seq_parameter_set: subset_sps->num_non_anchor_refs_l0"); + if ((subset_sps->num_non_anchor_refs_l1 = (int*) calloc(num_views, sizeof(int))) == NULL) + no_mem_exit("init_subset_seq_parameter_set: subset_sps->num_non_anchor_refs_l1"); + if ((subset_sps->non_anchor_ref_l0 = (int**) calloc(num_views, sizeof(int*))) == NULL) + no_mem_exit("init_subset_seq_parameter_set: subset_sps->non_anchor_ref_l0"); + if ((subset_sps->non_anchor_ref_l1 = (int**) calloc(num_views, sizeof(int*))) == NULL) + no_mem_exit("init_subset_seq_parameter_set: subset_sps->non_anchor_ref_l1"); + } + for(i=0; iview_id[i] = read_ue_v("view_id", s, &p_Dec->UsedBits); + } + for(i=1; inum_anchor_refs_l0[i] = read_ue_v("num_anchor_refs_l0", s, &p_Dec->UsedBits); + if(subset_sps->num_anchor_refs_l0[i]>0) + { + if ((subset_sps->anchor_ref_l0[i] = (int*) calloc(subset_sps->num_anchor_refs_l0[i], sizeof(int))) == NULL) + no_mem_exit("init_subset_seq_parameter_set: subset_sps->anchor_ref_l0[i]"); + for(j=0; jnum_anchor_refs_l0[i]; j++) + subset_sps->anchor_ref_l0[i][j] = read_ue_v("anchor_ref_l0", s, &p_Dec->UsedBits); + } + + subset_sps->num_anchor_refs_l1[i] = read_ue_v("num_anchor_refs_l1", s, &p_Dec->UsedBits); + if(subset_sps->num_anchor_refs_l1[i]>0) + { + if ((subset_sps->anchor_ref_l1[i] = (int*) calloc(subset_sps->num_anchor_refs_l1[i], sizeof(int))) == NULL) + no_mem_exit("init_subset_seq_parameter_set: subset_sps->anchor_ref_l1[i]"); + for(j=0; jnum_anchor_refs_l1[i]; j++) + subset_sps->anchor_ref_l1[i][j] = read_ue_v("anchor_ref_l1", s, &p_Dec->UsedBits); + } + } + for(i=1; inum_non_anchor_refs_l0[i] = read_ue_v("num_non_anchor_refs_l0", s, &p_Dec->UsedBits); + if(subset_sps->num_non_anchor_refs_l0[i]>0) + { + if ((subset_sps->non_anchor_ref_l0[i] = (int*) calloc(subset_sps->num_non_anchor_refs_l0[i], sizeof(int))) == NULL) + no_mem_exit("init_subset_seq_parameter_set: subset_sps->non_anchor_ref_l0[i]"); + for(j=0; jnum_non_anchor_refs_l0[i]; j++) + subset_sps->non_anchor_ref_l0[i][j] = read_ue_v("non_anchor_ref_l0", s, &p_Dec->UsedBits); + } + subset_sps->num_non_anchor_refs_l1[i] = read_ue_v("num_non_anchor_refs_l1", s, &p_Dec->UsedBits); + if(subset_sps->num_non_anchor_refs_l1[i]>0) + { + if ((subset_sps->non_anchor_ref_l1[i] = (int*) calloc(subset_sps->num_non_anchor_refs_l1[i], sizeof(int))) == NULL) + no_mem_exit("init_subset_seq_parameter_set: subset_sps->non_anchor_ref_l1[i]"); + for(j=0; jnum_non_anchor_refs_l1[i]; j++) + subset_sps->non_anchor_ref_l1[i][j] = read_ue_v("non_anchor_ref_l1", s, &p_Dec->UsedBits); + } + } + subset_sps->num_level_values_signalled_minus1 = read_ue_v("num_level_values_signalled_minus1", s, &p_Dec->UsedBits); + if(subset_sps->num_level_values_signalled_minus1 >=0) + { + i = 1+ subset_sps->num_level_values_signalled_minus1; + if ((subset_sps->level_idc = (int*) calloc(i, sizeof(int))) == NULL) + no_mem_exit("init_subset_seq_parameter_set: subset_sps->level_idc"); + if ((subset_sps->num_applicable_ops_minus1 = (int*) calloc(i, sizeof(int))) == NULL) + no_mem_exit("init_subset_seq_parameter_set: subset_sps->num_applicable_ops_minus1"); + if ((subset_sps->applicable_op_temporal_id = (int**) calloc(i, sizeof(int*))) == NULL) + no_mem_exit("init_subset_seq_parameter_set: subset_sps->applicable_op_temporal_id"); + if ((subset_sps->applicable_op_num_target_views_minus1 = (int**) calloc(i, sizeof(int*))) == NULL) + no_mem_exit("init_subset_seq_parameter_set: subset_sps->applicable_op_num_target_views_minus1"); + if ((subset_sps->applicable_op_target_view_id = (int***) calloc(i, sizeof(int**))) == NULL) + no_mem_exit("init_subset_seq_parameter_set: subset_sps->applicable_op_target_view_id"); + if ((subset_sps->applicable_op_num_views_minus1 = (int**) calloc(i, sizeof(int*))) == NULL) + no_mem_exit("init_subset_seq_parameter_set: subset_sps->applicable_op_num_views_minus1"); + } + for(i=0; i<=subset_sps->num_level_values_signalled_minus1; i++) + { + subset_sps->level_idc[i] = read_u_v(8, "level_idc", s, &p_Dec->UsedBits); + subset_sps->num_applicable_ops_minus1[i] = read_ue_v("num_applicable_ops_minus1", s, &p_Dec->UsedBits); + if(subset_sps->num_applicable_ops_minus1[i]>=0) + { + if ((subset_sps->applicable_op_temporal_id[i] = (int*) calloc(1+subset_sps->num_applicable_ops_minus1[i], sizeof(int))) == NULL) + no_mem_exit("init_subset_seq_parameter_set: subset_sps->applicable_op_temporal_id[i]"); + if ((subset_sps->applicable_op_num_target_views_minus1[i] = (int*) calloc(1+subset_sps->num_applicable_ops_minus1[i], sizeof(int))) == NULL) + no_mem_exit("init_subset_seq_parameter_set: subset_sps->applicable_op_num_target_views_minus1[i]"); + if ((subset_sps->applicable_op_target_view_id[i] = (int**) calloc(1+subset_sps->num_applicable_ops_minus1[i], sizeof(int *))) == NULL) + no_mem_exit("init_subset_seq_parameter_set: subset_sps->applicable_op_target_view_id[i]"); + if ((subset_sps->applicable_op_num_views_minus1[i] = (int*) calloc(1+subset_sps->num_applicable_ops_minus1[i], sizeof(int))) == NULL) + no_mem_exit("init_subset_seq_parameter_set: subset_sps->applicable_op_num_views_minus1[i]"); + + for(j=0; j<=subset_sps->num_applicable_ops_minus1[i]; j++) + { + int k; + subset_sps->applicable_op_temporal_id[i][j] = read_u_v(3, "applicable_op_temporal_id", s, &p_Dec->UsedBits); + subset_sps->applicable_op_num_target_views_minus1[i][j] = read_ue_v("applicable_op_num_target_views_minus1", s, &p_Dec->UsedBits); + if(subset_sps->applicable_op_num_target_views_minus1[i][j]>=0) + { + if ((subset_sps->applicable_op_target_view_id[i][j] = (int*) calloc(1+subset_sps->applicable_op_num_target_views_minus1[i][j], sizeof(int))) == NULL) + no_mem_exit("init_subset_seq_parameter_set: subset_sps->applicable_op_target_view_id[i][j]"); + for(k = 0; k <= subset_sps->applicable_op_num_target_views_minus1[i][j]; k++) + subset_sps->applicable_op_target_view_id[i][j][k] = read_ue_v("applicable_op_target_view_id", s, &p_Dec->UsedBits); + } + subset_sps->applicable_op_num_views_minus1[i][j] = read_ue_v("applicable_op_num_views_minus1", s, &p_Dec->UsedBits); + } + } + } +} + +int MemAlloc1D(void** ppBuf, int iEleSize, int iNum) +{ + if(iEleSize*iNum <=0) + return 1; + + *ppBuf = calloc(iNum, iEleSize); + return (*ppBuf == NULL); +} + +void hrd_parameters(MVCVUI_t *pMVCVUI, Bitstream *s) +{ + int i; + + pMVCVUI->cpb_cnt_minus1 = (char) read_ue_v("cpb_cnt_minus1", s, &p_Dec->UsedBits); + assert(pMVCVUI->cpb_cnt_minus1<=31); + pMVCVUI->bit_rate_scale = (char) read_u_v(4, "bit_rate_scale", s, &p_Dec->UsedBits); + pMVCVUI->cpb_size_scale = (char) read_u_v(4, "cpb_size_scale", s, &p_Dec->UsedBits); + for(i=0; i<=pMVCVUI->cpb_cnt_minus1; i++) + { + pMVCVUI->bit_rate_value_minus1[i] = read_ue_v("bit_rate_value_minus1" , s, &p_Dec->UsedBits); + pMVCVUI->cpb_size_value_minus1[i] = read_ue_v("cpb_size_value_minus1" , s, &p_Dec->UsedBits); + pMVCVUI->cbr_flag[i] = (char) read_u_1 ("cbr_flag" , s, &p_Dec->UsedBits); + } + pMVCVUI->initial_cpb_removal_delay_length_minus1 = (char) read_u_v(5, "initial_cpb_removal_delay_length_minus1", s, &p_Dec->UsedBits); + pMVCVUI->cpb_removal_delay_length_minus1 = (char) read_u_v(5, "cpb_removal_delay_length_minus1", s, &p_Dec->UsedBits); + pMVCVUI->dpb_output_delay_length_minus1 = (char) read_u_v(5, "dpb_output_delay_length_minus1", s, &p_Dec->UsedBits); + pMVCVUI->time_offset_length = (char) read_u_v(5, "time_offset_length", s, &p_Dec->UsedBits); + +} + +void mvc_vui_parameters_extension(MVCVUI_t *pMVCVUI, Bitstream *s) +{ + int i, j, iNumOps; + + pMVCVUI->num_ops_minus1 = read_ue_v("vui_mvc_num_ops_minus1", s, &p_Dec->UsedBits); + iNumOps = 1+ pMVCVUI->num_ops_minus1; + if(iNumOps > 0) + { + MemAlloc1D((void **)&(pMVCVUI->temporal_id), sizeof(pMVCVUI->temporal_id[0]), iNumOps); + MemAlloc1D((void **)&(pMVCVUI->num_target_output_views_minus1), sizeof(pMVCVUI->num_target_output_views_minus1[0]), iNumOps); + if ((pMVCVUI->view_id = (int**) calloc(iNumOps, sizeof(int*))) == NULL) + no_mem_exit("mvc_vui_parameters_extension: pMVCVUI->view_id"); + MemAlloc1D((void **)&(pMVCVUI->timing_info_present_flag), sizeof(pMVCVUI->timing_info_present_flag[0]), iNumOps); + MemAlloc1D((void **)&(pMVCVUI->num_units_in_tick), sizeof(pMVCVUI->num_units_in_tick[0]), iNumOps); + MemAlloc1D((void **)&(pMVCVUI->time_scale), sizeof(pMVCVUI->time_scale[0]), iNumOps); + MemAlloc1D((void **)&(pMVCVUI->fixed_frame_rate_flag), sizeof(pMVCVUI->fixed_frame_rate_flag[0]), iNumOps); + MemAlloc1D((void **)&(pMVCVUI->nal_hrd_parameters_present_flag), sizeof(pMVCVUI->nal_hrd_parameters_present_flag[0]), iNumOps); + MemAlloc1D((void **)&(pMVCVUI->vcl_hrd_parameters_present_flag), sizeof(pMVCVUI->vcl_hrd_parameters_present_flag[0]), iNumOps); + MemAlloc1D((void **)&(pMVCVUI->low_delay_hrd_flag), sizeof(pMVCVUI->low_delay_hrd_flag[0]), iNumOps); + MemAlloc1D((void **)&(pMVCVUI->pic_struct_present_flag), sizeof(pMVCVUI->pic_struct_present_flag[0]), iNumOps); + + for(i=0; itemporal_id[i] = (char) read_u_v(3, "vui_mvc_temporal_id", s, &p_Dec->UsedBits); + pMVCVUI->num_target_output_views_minus1[i] = read_ue_v("vui_mvc_num_target_output_views_minus1", s, &p_Dec->UsedBits); + if(pMVCVUI->num_target_output_views_minus1[i] >= 0) + MemAlloc1D((void **)&(pMVCVUI->view_id[i]), sizeof(pMVCVUI->view_id[0][0]), pMVCVUI->num_target_output_views_minus1[i]+1); + for(j=0; j<=pMVCVUI->num_target_output_views_minus1[i]; j++) + pMVCVUI->view_id[i][j] = read_ue_v("vui_mvc_view_id", s, &p_Dec->UsedBits); + pMVCVUI->timing_info_present_flag[i] = (char) read_u_1("vui_mvc_timing_info_present_flag", s, &p_Dec->UsedBits); + if(pMVCVUI->timing_info_present_flag[i]) + { + pMVCVUI->num_units_in_tick[i] = read_u_v(32, "vui_mvc_num_units_in_tick", s, &p_Dec->UsedBits); + pMVCVUI->time_scale[i] = read_u_v(32, "vui_mvc_time_scale" , s, &p_Dec->UsedBits); + pMVCVUI->fixed_frame_rate_flag[i] = (char) read_u_1("vui_mvc_fixed_frame_rate_flag", s, &p_Dec->UsedBits); + } + pMVCVUI->nal_hrd_parameters_present_flag[i] = (char) read_u_1("vui_mvc_nal_hrd_parameters_present_flag", s, &p_Dec->UsedBits); + if(pMVCVUI->nal_hrd_parameters_present_flag[i]) + hrd_parameters(pMVCVUI, s); + pMVCVUI->vcl_hrd_parameters_present_flag[i] = (char) read_u_1("vcl_hrd_parameters_present_flag", s, &p_Dec->UsedBits); + if(pMVCVUI->vcl_hrd_parameters_present_flag[i]) + hrd_parameters(pMVCVUI, s); + if(pMVCVUI->nal_hrd_parameters_present_flag[i]||pMVCVUI->vcl_hrd_parameters_present_flag[i]) + pMVCVUI->low_delay_hrd_flag[i] = (char) read_u_1("vui_mvc_low_delay_hrd_flag", s, &p_Dec->UsedBits); + pMVCVUI->pic_struct_present_flag[i] = (char) read_u_1("vui_mvc_pic_struct_present_flag", s, &p_Dec->UsedBits); + } + } +} + +void init_subset_sps_list(subset_seq_parameter_set_rbsp_t *subset_sps_list, int iSize) +{ + int i; + memset(subset_sps_list, 0, iSize*sizeof(subset_sps_list[0])); + for(i=0; inum_views_minus1>=0) + { + subset_sps->sps.seq_parameter_set_id = (unsigned int) -1; + + free_pointer(subset_sps->view_id); + for(i=0; i<=subset_sps->num_views_minus1; i++) + { + free_pointer(subset_sps->anchor_ref_l0[i]); + free_pointer(subset_sps->anchor_ref_l1[i]); + } + free_pointer(subset_sps->anchor_ref_l0); + free_pointer(subset_sps->anchor_ref_l1); + free_pointer(subset_sps->num_anchor_refs_l0); + free_pointer(subset_sps->num_anchor_refs_l1); + + for(i=0; i<=subset_sps->num_views_minus1; i++) + { + free_pointer(subset_sps->non_anchor_ref_l0[i]); + free_pointer(subset_sps->non_anchor_ref_l1[i]); + } + free_pointer(subset_sps->non_anchor_ref_l0); + free_pointer(subset_sps->non_anchor_ref_l1); + free_pointer(subset_sps->num_non_anchor_refs_l0); + free_pointer(subset_sps->num_non_anchor_refs_l1); + + if(subset_sps->num_level_values_signalled_minus1 >= 0) + { + free_pointer(subset_sps->level_idc); + for(i=0; i<=subset_sps->num_level_values_signalled_minus1; i++) + { + for(j=0; j<=subset_sps->num_applicable_ops_minus1[i]; j++) + { + free_pointer(subset_sps->applicable_op_target_view_id[i][j]); + } + free_pointer(subset_sps->applicable_op_target_view_id[i]); + free_pointer(subset_sps->applicable_op_temporal_id[i]); + free_pointer(subset_sps->applicable_op_num_target_views_minus1[i]); + free_pointer(subset_sps->applicable_op_num_views_minus1[i]); + } + free_pointer(subset_sps->applicable_op_target_view_id); + free_pointer(subset_sps->applicable_op_temporal_id); + free_pointer(subset_sps->applicable_op_num_target_views_minus1); + free_pointer(subset_sps->applicable_op_num_views_minus1); + free_pointer(subset_sps->num_applicable_ops_minus1); + + subset_sps->num_level_values_signalled_minus1 = -1; + } + + //end; + subset_sps->num_views_minus1 = -1; + } + + if(subset_sps && subset_sps->mvc_vui_parameters_present_flag) + { + MVCVUI_t *pMVCVUI = &(subset_sps->MVCVUIParams); + if(pMVCVUI->num_ops_minus1 >=0) + { + free_pointer(pMVCVUI->temporal_id); + free_pointer(pMVCVUI->num_target_output_views_minus1); + for(i=0; i<=pMVCVUI->num_ops_minus1; i++) + free_pointer(pMVCVUI->view_id[i]); + free_pointer(pMVCVUI->view_id); + free_pointer(pMVCVUI->timing_info_present_flag); + free_pointer(pMVCVUI->num_units_in_tick); + free_pointer(pMVCVUI->time_scale); + free_pointer(pMVCVUI->fixed_frame_rate_flag); + free_pointer(pMVCVUI->nal_hrd_parameters_present_flag); + free_pointer(pMVCVUI->vcl_hrd_parameters_present_flag); + free_pointer(pMVCVUI->low_delay_hrd_flag); + free_pointer(pMVCVUI->pic_struct_present_flag); + + pMVCVUI->num_ops_minus1 = -1; + } + subset_sps->mvc_vui_parameters_present_flag = 0; + } +} + +int GetBaseViewId(VideoParameters *p_Vid, subset_seq_parameter_set_rbsp_t **subset_sps) +{ + subset_seq_parameter_set_rbsp_t *curr_subset_sps; + int i, iBaseViewId=0; //-1; + + *subset_sps = NULL; + curr_subset_sps = p_Vid->SubsetSeqParSet; + for(i=0; inum_views_minus1>=0 && curr_subset_sps->sps.Valid) // && curr_subset_sps->sps.seq_parameter_set_id < MAXSPS) + { + iBaseViewId = curr_subset_sps->view_id[BASE_VIEW_IDX]; + break; + } + curr_subset_sps++; + } + + if(ipic_width_in_mbs_minus1 + 1) * (sps->pic_height_in_map_units_minus1 + 1) * (sps->frame_mbs_only_flag?1:2) * 384; + + int size = 0; + + switch (sps->level_idc) + { + case 9: + size = 152064; + break; + case 10: + size = 152064; + break; + case 11: + if (!is_FREXT_profile(sps->profile_idc) && (sps->constrained_set3_flag == 1)) + size = 152064; + else + size = 345600; + break; + case 12: + size = 912384; + break; + case 13: + size = 912384; + break; + case 20: + size = 912384; + break; + case 21: + size = 1824768; + break; + case 22: + size = 3110400; + break; + case 30: + size = 3110400; + break; + case 31: + size = 6912000; + break; + case 32: + size = 7864320; + break; + case 40: + size = 12582912; + break; + case 41: + size = 12582912; + break; + case 42: + size = 13369344; + break; + case 50: + size = 42393600; + break; + case 51: + size = 70778880; + break; + default: + error ("undefined level", 500); + break; + } + + size /= pic_size; + size = imin( size, 16); + sps->max_dec_frame_buffering = size; +} +#endif diff --git a/ldecod/src/quant.c b/ldecod/src/quant.c new file mode 100644 index 0000000..5fa80e2 --- /dev/null +++ b/ldecod/src/quant.c @@ -0,0 +1,353 @@ + +/*! + *********************************************************************** + * \file + * quant.c + * + * \brief + * Quantization functions + * + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * + *********************************************************************** + */ + +#include "contributors.h" + +#include "global.h" +#include "memalloc.h" +#include "block.h" +#include "image.h" +#include "mb_access.h" +#include "transform.h" +#include "quant.h" + +int quant_intra_default[16] = { + 6,13,20,28, + 13,20,28,32, + 20,28,32,37, + 28,32,37,42 +}; + +int quant_inter_default[16] = { + 10,14,20,24, + 14,20,24,27, + 20,24,27,30, + 24,27,30,34 +}; + +int quant8_intra_default[64] = { + 6,10,13,16,18,23,25,27, +10,11,16,18,23,25,27,29, +13,16,18,23,25,27,29,31, +16,18,23,25,27,29,31,33, +18,23,25,27,29,31,33,36, +23,25,27,29,31,33,36,38, +25,27,29,31,33,36,38,40, +27,29,31,33,36,38,40,42 +}; + +int quant8_inter_default[64] = { + 9,13,15,17,19,21,22,24, +13,13,17,19,21,22,24,25, +15,17,19,21,22,24,25,27, +17,19,21,22,24,25,27,28, +19,21,22,24,25,27,28,30, +21,22,24,25,27,28,30,32, +22,24,25,27,28,30,32,33, +24,25,27,28,30,32,33,35 +}; + +int quant_org[16] = { //to be use if no q matrix is chosen +16,16,16,16, +16,16,16,16, +16,16,16,16, +16,16,16,16 +}; + +int quant8_org[64] = { //to be use if no q matrix is chosen +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 +}; + +/*! + *********************************************************************** + * \brief + * Initiate quantization process arrays + *********************************************************************** + */ +void init_qp_process(CodingParameters *cps) +{ + int bitdepth_qp_scale = imax(cps->bitdepth_luma_qp_scale, cps->bitdepth_chroma_qp_scale); + int i; + + // We should allocate memory outside of this process since maybe we will have a change of SPS + // and we may need to recreate these. Currently should only support same bitdepth + if (cps->qp_per_matrix == NULL) + if ((cps->qp_per_matrix = (int*)malloc((MAX_QP + 1 + bitdepth_qp_scale)*sizeof(int))) == NULL) + no_mem_exit("init_qp_process: cps->qp_per_matrix"); + + if (cps->qp_rem_matrix == NULL) + if ((cps->qp_rem_matrix = (int*)malloc((MAX_QP + 1 + bitdepth_qp_scale)*sizeof(int))) == NULL) + no_mem_exit("init_qp_process: cps->qp_rem_matrix"); + + for (i = 0; i < MAX_QP + bitdepth_qp_scale + 1; i++) + { + cps->qp_per_matrix[i] = i / 6; + cps->qp_rem_matrix[i] = i % 6; + } +} + +void free_qp_matrices(CodingParameters *cps) +{ + if (cps->qp_per_matrix != NULL) + { + free (cps->qp_per_matrix); + cps->qp_per_matrix = NULL; + } + + if (cps->qp_rem_matrix != NULL) + { + free (cps->qp_rem_matrix); + cps->qp_rem_matrix = NULL; + } +} + +/*! + ************************************************************************ + * \brief + * For mapping the q-matrix to the active id and calculate quantisation values + * + * \param currSlice + * Slice pointer + * \param pps + * Picture parameter set + * \param sps + * Sequence parameter set + * + ************************************************************************ + */ +void assign_quant_params(Slice *currSlice) +{ + seq_parameter_set_rbsp_t* sps = currSlice->active_sps; + pic_parameter_set_rbsp_t* pps = currSlice->active_pps; + int i; + int n_ScalingList; + + if(!pps->pic_scaling_matrix_present_flag && !sps->seq_scaling_matrix_present_flag) + { + for(i=0; i<12; i++) + currSlice->qmatrix[i] = (i < 6) ? quant_org : quant8_org; + } + else + { + n_ScalingList = (sps->chroma_format_idc != YUV444) ? 8 : 12; + if(sps->seq_scaling_matrix_present_flag) // check sps first + { + for(i=0; iseq_scaling_list_present_flag[i]) // fall-back rule A + { + if(i==0) + currSlice->qmatrix[i] = quant_intra_default; + else if(i==3) + currSlice->qmatrix[i] = quant_inter_default; + else + currSlice->qmatrix[i] = currSlice->qmatrix[i-1]; + } + else + { + if(sps->UseDefaultScalingMatrix4x4Flag[i]) + currSlice->qmatrix[i] = (i<3) ? quant_intra_default : quant_inter_default; + else + currSlice->qmatrix[i] = sps->ScalingList4x4[i]; + } + } + else + { + if(!sps->seq_scaling_list_present_flag[i]) // fall-back rule A + { + if(i==6) + currSlice->qmatrix[i] = quant8_intra_default; + else if(i==7) + currSlice->qmatrix[i] = quant8_inter_default; + else + currSlice->qmatrix[i] = currSlice->qmatrix[i-2]; + } + else + { + if(sps->UseDefaultScalingMatrix8x8Flag[i-6]) + currSlice->qmatrix[i] = (i==6 || i==8 || i==10) ? quant8_intra_default:quant8_inter_default; + else + currSlice->qmatrix[i] = sps->ScalingList8x8[i-6]; + } + } + } + } + + if(pps->pic_scaling_matrix_present_flag) // then check pps + { + for(i=0; ipic_scaling_list_present_flag[i]) // fall-back rule B + { + if (i==0) + { + if(!sps->seq_scaling_matrix_present_flag) + currSlice->qmatrix[i] = quant_intra_default; + } + else if (i==3) + { + if(!sps->seq_scaling_matrix_present_flag) + currSlice->qmatrix[i] = quant_inter_default; + } + else + currSlice->qmatrix[i] = currSlice->qmatrix[i-1]; + } + else + { + if(pps->UseDefaultScalingMatrix4x4Flag[i]) + currSlice->qmatrix[i] = (i<3) ? quant_intra_default:quant_inter_default; + else + currSlice->qmatrix[i] = pps->ScalingList4x4[i]; + } + } + else + { + if(!pps->pic_scaling_list_present_flag[i]) // fall-back rule B + { + if (i==6) + { + if(!sps->seq_scaling_matrix_present_flag) + currSlice->qmatrix[i] = quant8_intra_default; + } + else if(i==7) + { + if(!sps->seq_scaling_matrix_present_flag) + currSlice->qmatrix[i] = quant8_inter_default; + } + else + currSlice->qmatrix[i] = currSlice->qmatrix[i-2]; + } + else + { + if(pps->UseDefaultScalingMatrix8x8Flag[i-6]) + currSlice->qmatrix[i] = (i==6 || i==8 || i==10) ? quant8_intra_default:quant8_inter_default; + else + currSlice->qmatrix[i] = pps->ScalingList8x8[i-6]; + } + } + } + } + } + + CalculateQuant4x4Param(currSlice); + if(pps->transform_8x8_mode_flag) + CalculateQuant8x8Param(currSlice); +} + +static void set_dequant4x4(int (*InvLevelScale4x4)[4], const int (*dequant)[4], int *qmatrix) +{ + int j; + for(j=0; j<4; j++) + { + *(*InvLevelScale4x4 ) = *(*dequant ) * *qmatrix++; + *(*InvLevelScale4x4 + 1) = *(*dequant + 1) * *qmatrix++; + *(*InvLevelScale4x4 + 2) = *(*dequant + 2) * *qmatrix++; + *(*InvLevelScale4x4++ + 3) = *(*dequant++ + 3) * *qmatrix++; + } +} + +static void set_dequant8x8(int (*InvLevelScale8x8)[8], const int (*dequant)[8], int *qmatrix) +{ + int j; + for(j = 0; j < 8; j++) + { + *(*InvLevelScale8x8 ) = *(*dequant ) * *qmatrix++; + *(*InvLevelScale8x8 + 1) = *(*dequant + 1) * *qmatrix++; + *(*InvLevelScale8x8 + 2) = *(*dequant + 2) * *qmatrix++; + *(*InvLevelScale8x8 + 3) = *(*dequant + 3) * *qmatrix++; + *(*InvLevelScale8x8 + 4) = *(*dequant + 4) * *qmatrix++; + *(*InvLevelScale8x8 + 5) = *(*dequant + 5) * *qmatrix++; + *(*InvLevelScale8x8 + 6) = *(*dequant + 6) * *qmatrix++; + *(*InvLevelScale8x8++ + 7) = *(*dequant++ + 7) * *qmatrix++; + } +} + +/*! + ************************************************************************ + * \brief + * For calculating the quantisation values at frame level + * + ************************************************************************ + */ +void CalculateQuant4x4Param(Slice *currSlice) +{ + int k; + const int (*p_dequant_coef)[4][4] = dequant_coef; + int (*InvLevelScale4x4_Intra_0)[4][4] = currSlice->InvLevelScale4x4_Intra[0]; + int (*InvLevelScale4x4_Intra_1)[4][4] = currSlice->InvLevelScale4x4_Intra[1]; + int (*InvLevelScale4x4_Intra_2)[4][4] = currSlice->InvLevelScale4x4_Intra[2]; + int (*InvLevelScale4x4_Inter_0)[4][4] = currSlice->InvLevelScale4x4_Inter[0]; + int (*InvLevelScale4x4_Inter_1)[4][4] = currSlice->InvLevelScale4x4_Inter[1]; + int (*InvLevelScale4x4_Inter_2)[4][4] = currSlice->InvLevelScale4x4_Inter[2]; + + + for(k=0; k<6; k++) + { + set_dequant4x4(*InvLevelScale4x4_Intra_0++, *p_dequant_coef , currSlice->qmatrix[0]); + set_dequant4x4(*InvLevelScale4x4_Intra_1++, *p_dequant_coef , currSlice->qmatrix[1]); + set_dequant4x4(*InvLevelScale4x4_Intra_2++, *p_dequant_coef , currSlice->qmatrix[2]); + set_dequant4x4(*InvLevelScale4x4_Inter_0++, *p_dequant_coef , currSlice->qmatrix[3]); + set_dequant4x4(*InvLevelScale4x4_Inter_1++, *p_dequant_coef , currSlice->qmatrix[4]); + set_dequant4x4(*InvLevelScale4x4_Inter_2++, *p_dequant_coef++, currSlice->qmatrix[5]); + } +} + +/*! + ************************************************************************ + * \brief + * Calculate the quantisation and inverse quantisation parameters + * + ************************************************************************ + */ +void CalculateQuant8x8Param(Slice *currSlice) +{ + int k; + const int (*p_dequant_coef)[8][8] = dequant_coef8; + int (*InvLevelScale8x8_Intra_0)[8][8] = currSlice->InvLevelScale8x8_Intra[0]; + int (*InvLevelScale8x8_Intra_1)[8][8] = currSlice->InvLevelScale8x8_Intra[1]; + int (*InvLevelScale8x8_Intra_2)[8][8] = currSlice->InvLevelScale8x8_Intra[2]; + int (*InvLevelScale8x8_Inter_0)[8][8] = currSlice->InvLevelScale8x8_Inter[0]; + int (*InvLevelScale8x8_Inter_1)[8][8] = currSlice->InvLevelScale8x8_Inter[1]; + int (*InvLevelScale8x8_Inter_2)[8][8] = currSlice->InvLevelScale8x8_Inter[2]; + + for(k=0; k<6; k++) + { + set_dequant8x8(*InvLevelScale8x8_Intra_0++, *p_dequant_coef , currSlice->qmatrix[6]); + set_dequant8x8(*InvLevelScale8x8_Inter_0++, *p_dequant_coef++, currSlice->qmatrix[7]); + } + + p_dequant_coef = dequant_coef8; + if( currSlice->active_sps->chroma_format_idc == 3 ) // 4:4:4 + { + for(k=0; k<6; k++) + { + set_dequant8x8(*InvLevelScale8x8_Intra_1++, *p_dequant_coef , currSlice->qmatrix[8]); + set_dequant8x8(*InvLevelScale8x8_Inter_1++, *p_dequant_coef , currSlice->qmatrix[9]); + set_dequant8x8(*InvLevelScale8x8_Intra_2++, *p_dequant_coef , currSlice->qmatrix[10]); + set_dequant8x8(*InvLevelScale8x8_Inter_2++, *p_dequant_coef++, currSlice->qmatrix[11]); + } + } +} diff --git a/ldecod/src/read_comp_cabac.c b/ldecod/src/read_comp_cabac.c new file mode 100644 index 0000000..de3bdc6 --- /dev/null +++ b/ldecod/src/read_comp_cabac.c @@ -0,0 +1,1942 @@ +/*! + *********************************************************************** + * \file read_comp_cabac.c + * + * \brief + * Read Coefficient Components + * + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * - Alexis Michael Tourapis + *********************************************************************** +*/ + +#include "contributors.h" + +#include "global.h" +#include "elements.h" +#include "macroblock.h" +#include "cabac.h" +#include "vlc.h" +#include "transform.h" + +#if TRACE +#define TRACE_STRING(s) strncpy(currSE.tracestring, s, TRACESTRING_SIZE) +#define TRACE_DECBITS(i) dectracebitcnt(1) +#define TRACE_printf(s) sprintf(type, "%s", s); +#define TRACE_STRING_P(s) strncpy(currSE->tracestring, s, TRACESTRING_SIZE) +#else +#define TRACE_STRING(s) +#define TRACE_DECBITS(i) +#define TRACE_printf(s) +#define TRACE_STRING_P(s) +#endif + +extern void check_dp_neighbors (Macroblock *currMB); +extern void read_delta_quant (SyntaxElement *currSE, DataPartition *dP, Macroblock *currMB, const byte *partMap, int type); + +/*! +************************************************************************ +* \brief +* Get coefficients (run/level) of 4x4 blocks in a SMB +* from the NAL (CABAC Mode) +************************************************************************ +*/ +static void read_comp_coeff_4x4_smb_CABAC (Macroblock *currMB, SyntaxElement *currSE, ColorPlane pl, int block_y, int block_x, int start_scan, int64 *cbp_blk) +{ + int i,j,k; + int i0, j0; + int level = 1; + DataPartition *dP; + VideoParameters *p_Vid = currMB->p_Vid; + Slice *currSlice = currMB->p_Slice; + const byte *partMap = assignSE2partition[currSlice->dp_mode]; + + const byte (*pos_scan4x4)[2] = ((p_Vid->structure == FRAME) && (!currMB->mb_field)) ? SNGL_SCAN : FIELD_SCAN; + const byte *pos_scan_4x4 = pos_scan4x4[0]; + int **cof = currSlice->cof[pl]; + + for (j = block_y; j < block_y + BLOCK_SIZE_8x8; j += 4) + { + currMB->subblock_y = j; // position for coeff_count ctx + + for (i = block_x; i < block_x + BLOCK_SIZE_8x8; i += 4) + { + currMB->subblock_x = i; // position for coeff_count ctx + pos_scan_4x4 = pos_scan4x4[start_scan]; + level = 1; + + if (start_scan == 0) + { + /* + * make distinction between INTRA and INTER coded + * luminance coefficients + */ + currSE->type = (currMB->is_intra_block ? SE_LUM_DC_INTRA : SE_LUM_DC_INTER); + dP = &(currSlice->partArr[partMap[currSE->type]]); + if (dP->bitstream->ei_flag) + currSE->mapping = linfo_levrun_inter; + else + currSE->reading = readRunLevel_CABAC; + +#if TRACE + if (pl == PLANE_Y) + sprintf(currSE->tracestring, "Luma sng "); + else if (pl == PLANE_U) + sprintf(currSE->tracestring, "Cb sng "); + else + sprintf(currSE->tracestring, "Cr sng "); +#endif + + dP->readSyntaxElement(currMB, currSE, dP); + level = currSE->value1; + + if (level != 0) /* leave if level == 0 */ + { + pos_scan_4x4 += 2 * currSE->value2; + + i0 = *pos_scan_4x4++; + j0 = *pos_scan_4x4++; + + *cbp_blk |= i64_power2(j + (i >> 2)) ; + //cof[j + j0][i + i0]= rshift_rnd_sf((level * InvLevelScale4x4[j0][i0]) << qp_per, 4); + cof[j + j0][i + i0]= level; + //currSlice->fcf[pl][j + j0][i + i0]= level; + } + } + + if (level != 0) + { + // make distinction between INTRA and INTER coded luminance coefficients + currSE->type = (currMB->is_intra_block ? SE_LUM_AC_INTRA : SE_LUM_AC_INTER); + dP = &(currSlice->partArr[partMap[currSE->type]]); + + if (dP->bitstream->ei_flag) + currSE->mapping = linfo_levrun_inter; + else + currSE->reading = readRunLevel_CABAC; + + for(k = 1; (k < 17) && (level != 0); ++k) + { +#if TRACE + if (pl == PLANE_Y) + sprintf(currSE->tracestring, "Luma sng "); + else if (pl == PLANE_U) + sprintf(currSE->tracestring, "Cb sng "); + else + sprintf(currSE->tracestring, "Cr sng "); +#endif + + dP->readSyntaxElement(currMB, currSE, dP); + level = currSE->value1; + + if (level != 0) /* leave if level == 0 */ + { + pos_scan_4x4 += 2 * currSE->value2; + + i0 = *pos_scan_4x4++; + j0 = *pos_scan_4x4++; + + cof[j + j0][i + i0] = level; + //currSlice->fcf[pl][j + j0][i + i0]= level; + } + } + } + } + } +} + +/*! +************************************************************************ +* \brief +* Get coefficients (run/level) of all 4x4 blocks in a MB +* from the NAL (CABAC Mode) +************************************************************************ +*/ +static void read_comp_coeff_4x4_CABAC (Macroblock *currMB, SyntaxElement *currSE, ColorPlane pl, int (*InvLevelScale4x4)[4], int qp_per, int cbp) +{ + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + int start_scan = IS_I16MB (currMB)? 1 : 0; + int block_y, block_x; + int i, j; + int64 *cbp_blk = &currMB->s_cbp[pl].blk; + + if( pl == PLANE_Y || (p_Vid->separate_colour_plane_flag != 0) ) + currSE->context = (IS_I16MB(currMB) ? LUMA_16AC: LUMA_4x4); + else if (pl == PLANE_U) + currSE->context = (IS_I16MB(currMB) ? CB_16AC: CB_4x4); + else + currSE->context = (IS_I16MB(currMB) ? CR_16AC: CR_4x4); + + for (block_y = 0; block_y < MB_BLOCK_SIZE; block_y += BLOCK_SIZE_8x8) /* all modes */ + { + int **cof = &currSlice->cof[pl][block_y]; + for (block_x = 0; block_x < MB_BLOCK_SIZE; block_x += BLOCK_SIZE_8x8) + { + if (cbp & (1 << ((block_y >> 2) + (block_x >> 3)))) // are there any coeff in current block at all + { + read_comp_coeff_4x4_smb_CABAC (currMB, currSE, pl, block_y, block_x, start_scan, cbp_blk); + + if (start_scan == 0) + { + for (j = 0; j < BLOCK_SIZE_8x8; ++j) + { + int *coef = &cof[j][block_x]; + int jj = j & 0x03; + for (i = 0; i < BLOCK_SIZE_8x8; i+=4) + { + if (*coef) + *coef = rshift_rnd_sf((*coef * InvLevelScale4x4[jj][0]) << qp_per, 4); + coef++; + if (*coef) + *coef = rshift_rnd_sf((*coef * InvLevelScale4x4[jj][1]) << qp_per, 4); + coef++; + if (*coef) + *coef = rshift_rnd_sf((*coef * InvLevelScale4x4[jj][2]) << qp_per, 4); + coef++; + if (*coef) + *coef = rshift_rnd_sf((*coef * InvLevelScale4x4[jj][3]) << qp_per, 4); + coef++; + } + } + } + else + { + for (j = 0; j < BLOCK_SIZE_8x8; ++j) + { + int *coef = &cof[j][block_x]; + int jj = j & 0x03; + for (i = 0; i < BLOCK_SIZE_8x8; i += 4) + { + if ((jj != 0) && *coef) + *coef= rshift_rnd_sf((*coef * InvLevelScale4x4[jj][0]) << qp_per, 4); + coef++; + if (*coef) + *coef= rshift_rnd_sf((*coef * InvLevelScale4x4[jj][1]) << qp_per, 4); + coef++; + if (*coef) + *coef= rshift_rnd_sf((*coef * InvLevelScale4x4[jj][2]) << qp_per, 4); + coef++; + if (*coef) + *coef= rshift_rnd_sf((*coef * InvLevelScale4x4[jj][3]) << qp_per, 4); + coef++; + } + } + } + } + } + } +} + + +/*! +************************************************************************ +* \brief +* Get coefficients (run/level) of all 4x4 blocks in a MB +* from the NAL (CABAC Mode) +************************************************************************ +*/ +static void read_comp_coeff_4x4_CABAC_ls (Macroblock *currMB, SyntaxElement *currSE, ColorPlane pl, int (*InvLevelScale4x4)[4], int qp_per, int cbp) +{ + VideoParameters *p_Vid = currMB->p_Vid; + int start_scan = IS_I16MB (currMB)? 1 : 0; + int block_y, block_x; + int64 *cbp_blk = &currMB->s_cbp[pl].blk; + + if( pl == PLANE_Y || (p_Vid->separate_colour_plane_flag != 0) ) + currSE->context = (IS_I16MB(currMB) ? LUMA_16AC: LUMA_4x4); + else if (pl == PLANE_U) + currSE->context = (IS_I16MB(currMB) ? CB_16AC: CB_4x4); + else + currSE->context = (IS_I16MB(currMB) ? CR_16AC: CR_4x4); + + for (block_y = 0; block_y < MB_BLOCK_SIZE; block_y += BLOCK_SIZE_8x8) /* all modes */ + { + for (block_x = 0; block_x < MB_BLOCK_SIZE; block_x += BLOCK_SIZE_8x8) + { + if (cbp & (1 << ((block_y >> 2) + (block_x >> 3)))) // are there any coeff in current block at all + { + read_comp_coeff_4x4_smb_CABAC (currMB, currSE, pl, block_y, block_x, start_scan, cbp_blk); + } + } + } +} + + +/*! +************************************************************************ +* \brief +* Get coefficients (run/level) of one 8x8 block +* from the NAL (CABAC Mode) +************************************************************************ +*/ +static void readCompCoeff8x8_CABAC (Macroblock *currMB, SyntaxElement *currSE, ColorPlane pl, int b8) +{ + if (currMB->cbp & (1<p_Vid; + int transform_pl = (p_Vid->separate_colour_plane_flag != 0) ? currMB->p_Slice->colour_plane_id : pl; + + int **tcoeffs; + int i,j,k; + int level = 1; + + DataPartition *dP; + Slice *currSlice = currMB->p_Slice; + const byte *partMap = assignSE2partition[currSlice->dp_mode]; + int boff_x, boff_y; + + int64 cbp_mask = (int64) 51 << (4 * b8 - 2 * (b8 & 0x01)); // corresponds to 110011, as if all four 4x4 blocks contain coeff, shifted to block position + int64 *cur_cbp = &currMB->s_cbp[pl].blk; + + // select scan type + const byte (*pos_scan8x8) = ((p_Vid->structure == FRAME) && (!currMB->mb_field)) ? SNGL_SCAN8x8[0] : FIELD_SCAN8x8[0]; + + int qp_per = p_Vid->qp_per_matrix[ currMB->qp_scaled[pl] ]; + int qp_rem = p_Vid->qp_rem_matrix[ currMB->qp_scaled[pl] ]; + + int (*InvLevelScale8x8)[8] = (currMB->is_intra_block == TRUE) ? currSlice->InvLevelScale8x8_Intra[transform_pl][qp_rem] : currSlice->InvLevelScale8x8_Inter[transform_pl][qp_rem]; + + // === set offset in current macroblock === + boff_x = (b8&0x01) << 3; + boff_y = (b8 >> 1) << 3; + tcoeffs = &currSlice->mb_rres[pl][boff_y]; + + currMB->subblock_x = boff_x; // position for coeff_count ctx + currMB->subblock_y = boff_y; // position for coeff_count ctx + + if (pl==PLANE_Y || (p_Vid->separate_colour_plane_flag != 0)) + currSE->context = LUMA_8x8; + else if (pl==PLANE_U) + currSE->context = CB_8x8; + else + currSE->context = CR_8x8; + + currSE->reading = readRunLevel_CABAC; + + // Read DC + currSE->type = ((currMB->is_intra_block == 1) ? SE_LUM_DC_INTRA : SE_LUM_DC_INTER ); // Intra or Inter? + dP = &(currSlice->partArr[partMap[currSE->type]]); + +#if TRACE + if (pl==PLANE_Y) + sprintf(currSE->tracestring, "Luma8x8 DC sng "); + else if (pl==PLANE_U) + sprintf(currSE->tracestring, "Cb 8x8 DC sng "); + else + sprintf(currSE->tracestring, "Cr 8x8 DC sng "); +#endif + + dP->readSyntaxElement(currMB, currSE, dP); + level = currSE->value1; + + //============ decode ============= + if (level != 0) /* leave if level == 0 */ + { + *cur_cbp |= cbp_mask; + + pos_scan8x8 += 2 * (currSE->value2); + + i = *pos_scan8x8++; + j = *pos_scan8x8++; + + tcoeffs[j][boff_x + i] = rshift_rnd_sf((level * InvLevelScale8x8[j][i]) << qp_per, 6); // dequantization + //tcoeffs[ j][boff_x + i] = level; + + // AC coefficients + currSE->type = ((currMB->is_intra_block == 1) ? SE_LUM_AC_INTRA : SE_LUM_AC_INTER); + dP = &(currSlice->partArr[partMap[currSE->type]]); + + for(k = 1;(k < 65) && (level != 0);++k) + { +#if TRACE + if (pl==PLANE_Y) + sprintf(currSE->tracestring, "Luma8x8 sng "); + else if (pl==PLANE_U) + sprintf(currSE->tracestring, "Cb 8x8 sng "); + else + sprintf(currSE->tracestring, "Cr 8x8 sng "); +#endif + + dP->readSyntaxElement(currMB, currSE, dP); + level = currSE->value1; + + //============ decode ============= + if (level != 0) /* leave if level == 0 */ + { + pos_scan8x8 += 2 * (currSE->value2); + + i = *pos_scan8x8++; + j = *pos_scan8x8++; + + tcoeffs[ j][boff_x + i] = rshift_rnd_sf((level * InvLevelScale8x8[j][i]) << qp_per, 6); // dequantization + //tcoeffs[ j][boff_x + i] = level; + } + } + /* + for (j = 0; j < 8; j++) + { + for (i = 0; i < 8; i++) + { + if (tcoeffs[ j][boff_x + i]) + tcoeffs[ j][boff_x + i] = rshift_rnd_sf((tcoeffs[ j][boff_x + i] * InvLevelScale8x8[j][i]) << qp_per, 6); // dequantization + } + } + */ + } + } +} + +/*! +************************************************************************ +* \brief +* Get coefficients (run/level) of one 8x8 block +* from the NAL (CABAC Mode - lossless) +************************************************************************ +*/ +static void readCompCoeff8x8_CABAC_lossless (Macroblock *currMB, SyntaxElement *currSE, ColorPlane pl, int b8) +{ + if (currMB->cbp & (1<p_Vid; + + int **tcoeffs; + int i,j,k; + int level = 1; + + DataPartition *dP; + Slice *currSlice = currMB->p_Slice; + const byte *partMap = assignSE2partition[currSlice->dp_mode]; + int boff_x, boff_y; + + int64 cbp_mask = (int64) 51 << (4 * b8 - 2 * (b8 & 0x01)); // corresponds to 110011, as if all four 4x4 blocks contain coeff, shifted to block position + int64 *cur_cbp = &currMB->s_cbp[pl].blk; + + // select scan type + const byte (*pos_scan8x8) = ((p_Vid->structure == FRAME) && (!currMB->mb_field)) ? SNGL_SCAN8x8[0] : FIELD_SCAN8x8[0]; + + // === set offset in current macroblock === + boff_x = (b8&0x01) << 3; + boff_y = (b8 >> 1) << 3; + tcoeffs = &currSlice->mb_rres[pl][boff_y]; + + currMB->subblock_x = boff_x; // position for coeff_count ctx + currMB->subblock_y = boff_y; // position for coeff_count ctx + + if (pl==PLANE_Y || (p_Vid->separate_colour_plane_flag != 0)) + currSE->context = LUMA_8x8; + else if (pl==PLANE_U) + currSE->context = CB_8x8; + else + currSE->context = CR_8x8; + + currSE->reading = readRunLevel_CABAC; + + for(k=0; (k < 65) && (level != 0);++k) + { + //============ read ============= + /* + * make distinction between INTRA and INTER coded + * luminance coefficients + */ + + currSE->type = ((currMB->is_intra_block == 1) + ? (k==0 ? SE_LUM_DC_INTRA : SE_LUM_AC_INTRA) + : (k==0 ? SE_LUM_DC_INTER : SE_LUM_AC_INTER)); + +#if TRACE + if (pl==PLANE_Y) + sprintf(currSE->tracestring, "Luma8x8 sng "); + else if (pl==PLANE_U) + sprintf(currSE->tracestring, "Cb 8x8 sng "); + else + sprintf(currSE->tracestring, "Cr 8x8 sng "); +#endif + + dP = &(currSlice->partArr[partMap[currSE->type]]); + currSE->reading = readRunLevel_CABAC; + + dP->readSyntaxElement(currMB, currSE, dP); + level = currSE->value1; + + //============ decode ============= + if (level != 0) /* leave if level == 0 */ + { + pos_scan8x8 += 2 * (currSE->value2); + + i = *pos_scan8x8++; + j = *pos_scan8x8++; + + *cur_cbp |= cbp_mask; + + tcoeffs[j][boff_x + i] = level; + } + } + } +} + + +/*! +************************************************************************ +* \brief +* Get coefficients (run/level) of 8x8 blocks in a MB +* from the NAL (CABAC Mode) +************************************************************************ +*/ +static void read_comp_coeff_8x8_MB_CABAC (Macroblock *currMB, SyntaxElement *currSE, ColorPlane pl) +{ + //======= 8x8 transform size & CABAC ======== + readCompCoeff8x8_CABAC (currMB, currSE, pl, 0); + readCompCoeff8x8_CABAC (currMB, currSE, pl, 1); + readCompCoeff8x8_CABAC (currMB, currSE, pl, 2); + readCompCoeff8x8_CABAC (currMB, currSE, pl, 3); +} + + +/*! +************************************************************************ +* \brief +* Get coefficients (run/level) of 8x8 blocks in a MB +* from the NAL (CABAC Mode) +************************************************************************ +*/ +static void read_comp_coeff_8x8_MB_CABAC_ls (Macroblock *currMB, SyntaxElement *currSE, ColorPlane pl) +{ + //======= 8x8 transform size & CABAC ======== + readCompCoeff8x8_CABAC_lossless (currMB, currSE, pl, 0); + readCompCoeff8x8_CABAC_lossless (currMB, currSE, pl, 1); + readCompCoeff8x8_CABAC_lossless (currMB, currSE, pl, 2); + readCompCoeff8x8_CABAC_lossless (currMB, currSE, pl, 3); +} + + +/*! + ************************************************************************ + * \brief + * Get coded block pattern and coefficients (run/level) + * from the NAL + ************************************************************************ + */ +static void read_CBP_and_coeffs_from_NAL_CABAC_420(Macroblock *currMB) +{ + int i,j; + int level; + int cbp; + SyntaxElement currSE; + DataPartition *dP = NULL; + Slice *currSlice = currMB->p_Slice; + const byte *partMap = assignSE2partition[currSlice->dp_mode]; + int i0, j0; + + int qp_per, qp_rem; + VideoParameters *p_Vid = currMB->p_Vid; + int smb = ((p_Vid->type==SP_SLICE) && (currMB->is_intra_block == FALSE)) || (p_Vid->type == SI_SLICE && currMB->mb_type == SI4MB); + + int qp_per_uv[2]; + int qp_rem_uv[2]; + + int intra = (currMB->is_intra_block == TRUE); + + StorablePicture *dec_picture = currSlice->dec_picture; + int yuv = dec_picture->chroma_format_idc - 1; + + int (*InvLevelScale4x4)[4] = NULL; + + // select scan type + const byte (*pos_scan4x4)[2] = ((p_Vid->structure == FRAME) && (!currMB->mb_field)) ? SNGL_SCAN : FIELD_SCAN; + const byte *pos_scan_4x4 = pos_scan4x4[0]; + + if (!IS_I16MB (currMB)) + { + int need_transform_size_flag; + //===== C B P ===== + //--------------------- + currSE.type = (currMB->mb_type == I4MB || currMB->mb_type == SI4MB || currMB->mb_type == I8MB) + ? SE_CBP_INTRA + : SE_CBP_INTER; + + dP = &(currSlice->partArr[partMap[currSE.type]]); + + if (dP->bitstream->ei_flag) + { + currSE.mapping = (currMB->mb_type == I4MB || currMB->mb_type == SI4MB || currMB->mb_type == I8MB) + ? currSlice->linfo_cbp_intra + : currSlice->linfo_cbp_inter; + } + else + { + currSE.reading = read_CBP_CABAC; + } + + TRACE_STRING("coded_block_pattern"); + dP->readSyntaxElement(currMB, &currSE, dP); + currMB->cbp = cbp = currSE.value1; + + //============= Transform size flag for INTER MBs ============= + //------------------------------------------------------------- + need_transform_size_flag = (((currMB->mb_type >= 1 && currMB->mb_type <= 3)|| + (IS_DIRECT(currMB) && p_Vid->active_sps->direct_8x8_inference_flag) || + (currMB->NoMbPartLessThan8x8Flag)) + && currMB->mb_type != I8MB && currMB->mb_type != I4MB + && (currMB->cbp&15) + && currSlice->Transform8x8Mode); + + if (need_transform_size_flag) + { + currSE.type = SE_HEADER; + dP = &(currSlice->partArr[partMap[SE_HEADER]]); + currSE.reading = readMB_transform_size_flag_CABAC; + TRACE_STRING("transform_size_8x8_flag"); + + // read CAVLC transform_size_8x8_flag + if (dP->bitstream->ei_flag) + { + currSE.len = 1; + readSyntaxElement_FLC(&currSE, dP->bitstream); + } + else + { + dP->readSyntaxElement(currMB, &currSE, dP); + } + currMB->luma_transform_size_8x8_flag = (Boolean) currSE.value1; + } + + //===== DQUANT ===== + //---------------------- + // Delta quant only if nonzero coeffs + if (cbp !=0) + { + read_delta_quant(&currSE, dP, currMB, partMap, ((currMB->is_intra_block == FALSE)) ? SE_DELTA_QUANT_INTER : SE_DELTA_QUANT_INTRA); + + if (currSlice->dp_mode) + { + if ((currMB->is_intra_block == FALSE) && currSlice->dpC_NotPresent ) + currMB->dpl_flag = 1; + + if( intra && currSlice->dpB_NotPresent ) + { + currMB->ei_flag = 1; + currMB->dpl_flag = 1; + } + + // check for prediction from neighbours + check_dp_neighbors (currMB); + if (currMB->dpl_flag) + { + cbp = 0; + currMB->cbp = cbp; + } + } + } + } + else // read DC coeffs for new intra modes + { + cbp = currMB->cbp; + + read_delta_quant(&currSE, dP, currMB, partMap, SE_DELTA_QUANT_INTRA); + + if (currSlice->dp_mode) + { + if (currSlice->dpB_NotPresent) + { + currMB->ei_flag = 1; + currMB->dpl_flag = 1; + } + check_dp_neighbors (currMB); + if (currMB->dpl_flag) + { + currMB->cbp = cbp = 0; + } + } + + if (!currMB->dpl_flag) + { + int **cof = currSlice->cof[0]; + int k; + pos_scan_4x4 = pos_scan4x4[0]; + + currSE.type = SE_LUM_DC_INTRA; + dP = &(currSlice->partArr[partMap[currSE.type]]); + + currSE.context = LUMA_16DC; + currSE.type = SE_LUM_DC_INTRA; + + if (dP->bitstream->ei_flag) + { + currSE.mapping = linfo_levrun_inter; + } + else + { + currSE.reading = readRunLevel_CABAC; + } + + level = 1; // just to get inside the loop + + for(k = 0; (k < 17) && (level != 0); ++k) + { +#if TRACE + snprintf(currSE.tracestring, TRACESTRING_SIZE, "DC luma 16x16 "); +#endif + dP->readSyntaxElement(currMB, &currSE, dP); + level = currSE.value1; + + if (level != 0) /* leave if level == 0 */ + { + pos_scan_4x4 += (2 * currSE.value2); + + i0 = ((*pos_scan_4x4++) << 2); + j0 = ((*pos_scan_4x4++) << 2); + + cof[j0][i0] = level;// add new intra DC coeff + //currSlice->fcf[0][j0][i0] = level;// add new intra DC coeff + } + } + + if(currMB->is_lossless == FALSE) + itrans_2(currMB, (ColorPlane) currSlice->colour_plane_id);// transform new intra DC + } + } + + update_qp(currMB, currSlice->qp); + + qp_per = p_Vid->qp_per_matrix[ currMB->qp_scaled[currSlice->colour_plane_id] ]; + qp_rem = p_Vid->qp_rem_matrix[ currMB->qp_scaled[currSlice->colour_plane_id] ]; + + // luma coefficients + //======= Other Modes & CABAC ======== + //------------------------------------ + if (cbp) + { + if(currMB->luma_transform_size_8x8_flag) + { + //======= 8x8 transform size & CABAC ======== + currMB->read_comp_coeff_8x8_CABAC (currMB, &currSE, PLANE_Y); + } + else + { + InvLevelScale4x4 = intra? currSlice->InvLevelScale4x4_Intra[currSlice->colour_plane_id][qp_rem] : currSlice->InvLevelScale4x4_Inter[currSlice->colour_plane_id][qp_rem]; + currMB->read_comp_coeff_4x4_CABAC (currMB, &currSE, PLANE_Y, InvLevelScale4x4, qp_per, cbp); + } + } + + //init quant parameters for chroma + for(i=0; i < 2; ++i) + { + qp_per_uv[i] = p_Vid->qp_per_matrix[ currMB->qp_scaled[i + 1] ]; + qp_rem_uv[i] = p_Vid->qp_rem_matrix[ currMB->qp_scaled[i + 1] ]; + } + + //========================== CHROMA DC ============================ + //----------------------------------------------------------------- + // chroma DC coeff + if(cbp>15) + { + CBPStructure *s_cbp = &currMB->s_cbp[0]; + int uv, ll, k, coef_ctr; + + for (ll = 0; ll < 3; ll += 2) + { + uv = ll >> 1; + + InvLevelScale4x4 = intra ? currSlice->InvLevelScale4x4_Intra[uv + 1][qp_rem_uv[uv]] : currSlice->InvLevelScale4x4_Inter[uv + 1][qp_rem_uv[uv]]; + //===================== CHROMA DC YUV420 ====================== + memset(currSlice->cofu, 0, 4 *sizeof(int)); + coef_ctr=-1; + + level = 1; + currMB->is_v_block = ll; + currSE.context = CHROMA_DC; + currSE.type = (intra ? SE_CHR_DC_INTRA : SE_CHR_DC_INTER); + + dP = &(currSlice->partArr[partMap[currSE.type]]); + + if (dP->bitstream->ei_flag) + currSE.mapping = linfo_levrun_c2x2; + else + currSE.reading = readRunLevel_CABAC; + + for(k = 0; (k < (p_Vid->num_cdc_coeff + 1))&&(level!=0);++k) + { +#if TRACE + snprintf(currSE.tracestring, TRACESTRING_SIZE, "2x2 DC Chroma "); +#endif + + dP->readSyntaxElement(currMB, &currSE, dP); + level = currSE.value1; + + if (level != 0) + { + s_cbp->blk |= 0xf0000 << (ll<<1) ; + coef_ctr += currSE.value2 + 1; + + // Bug: currSlice->cofu has only 4 entries, hence coef_ctr MUST be <4 (which is + // caught by the assert(). If it is bigger than 4, it starts patching the + // p_Vid->predmode pointer, which leads to bugs later on. + // + // This assert() should be left in the code, because it captures a very likely + // bug early when testing in error prone environments (or when testing NAL + // functionality). + + assert (coef_ctr < p_Vid->num_cdc_coeff); + currSlice->cofu[coef_ctr] = level; + } + } + + + if (smb || (currMB->is_lossless == TRUE)) // check to see if MB type is SPred or SIntra4x4 + { + currSlice->cof[uv + 1][0][0] = currSlice->cofu[0]; + currSlice->cof[uv + 1][0][4] = currSlice->cofu[1]; + currSlice->cof[uv + 1][4][0] = currSlice->cofu[2]; + currSlice->cof[uv + 1][4][4] = currSlice->cofu[3]; + //currSlice->fcf[uv + 1][0][0] = currSlice->cofu[0]; + //currSlice->fcf[uv + 1][4][0] = currSlice->cofu[1]; + //currSlice->fcf[uv + 1][0][4] = currSlice->cofu[2]; + //currSlice->fcf[uv + 1][4][4] = currSlice->cofu[3]; + } + else + { + int temp[4]; + int scale_dc = InvLevelScale4x4[0][0]; + int **cof = currSlice->cof[uv + 1]; + + ihadamard2x2(currSlice->cofu, temp); + + //currSlice->fcf[uv + 1][0][0] = temp[0]; + //currSlice->fcf[uv + 1][0][4] = temp[1]; + //currSlice->fcf[uv + 1][4][0] = temp[2]; + //currSlice->fcf[uv + 1][4][4] = temp[3]; + + cof[0][0] = (((temp[0] * scale_dc) << qp_per_uv[uv]) >> 5); + cof[0][4] = (((temp[1] * scale_dc) << qp_per_uv[uv]) >> 5); + cof[4][0] = (((temp[2] * scale_dc) << qp_per_uv[uv]) >> 5); + cof[4][4] = (((temp[3] * scale_dc) << qp_per_uv[uv]) >> 5); + } + } + } + + //========================== CHROMA AC ============================ + //----------------------------------------------------------------- + // chroma AC coeff, all zero fram start_scan + if (cbp >31) + { + currSE.context = CHROMA_AC; + currSE.type = (currMB->is_intra_block ? SE_CHR_AC_INTRA : SE_CHR_AC_INTER); + + dP = &(currSlice->partArr[partMap[currSE.type]]); + + if (dP->bitstream->ei_flag) + currSE.mapping = linfo_levrun_inter; + else + currSE.reading = readRunLevel_CABAC; + + if(currMB->is_lossless == FALSE) + { + int b4, b8, uv, k; + int **cof; + CBPStructure *s_cbp = &currMB->s_cbp[0]; + for (b8=0; b8 < p_Vid->num_blk8x8_uv; ++b8) + { + currMB->is_v_block = uv = (b8 > ((p_Vid->num_uv_blocks) - 1 )); + InvLevelScale4x4 = intra ? currSlice->InvLevelScale4x4_Intra[uv + 1][qp_rem_uv[uv]] : currSlice->InvLevelScale4x4_Inter[uv + 1][qp_rem_uv[uv]]; + cof = currSlice->cof[uv + 1]; + + for (b4 = 0; b4 < 4; ++b4) + { + i = cofuv_blk_x[yuv][b8][b4]; + j = cofuv_blk_y[yuv][b8][b4]; + + currMB->subblock_y = subblk_offset_y[yuv][b8][b4]; + currMB->subblock_x = subblk_offset_x[yuv][b8][b4]; + + pos_scan_4x4 = pos_scan4x4[1]; + level = 1; + + for(k = 0; (k < 16) && (level != 0);++k) + { +#if TRACE + snprintf(currSE.tracestring, TRACESTRING_SIZE, "AC Chroma "); +#endif + + dP->readSyntaxElement(currMB, &currSE, dP); + level = currSE.value1; + + if (level != 0) + { + s_cbp->blk |= i64_power2(cbp_blk_chroma[b8][b4]); + pos_scan_4x4 += (currSE.value2 << 1); + + i0 = *pos_scan_4x4++; + j0 = *pos_scan_4x4++; + + cof[(j<<2) + j0][(i<<2) + i0] = rshift_rnd_sf((level * InvLevelScale4x4[j0][i0])<fcf[uv + 1][(j<<2) + j0][(i<<2) + i0] = level; + } + } //for(k=0;(k<16)&&(level!=0);++k) + } + } + } + else + { + CBPStructure *s_cbp = &currMB->s_cbp[0]; + int b4, b8, k; + int uv; + for (b8=0; b8 < p_Vid->num_blk8x8_uv; ++b8) + { + currMB->is_v_block = uv = (b8 > ((p_Vid->num_uv_blocks) - 1 )); + + for (b4=0; b4 < 4; ++b4) + { + i = cofuv_blk_x[yuv][b8][b4]; + j = cofuv_blk_y[yuv][b8][b4]; + + pos_scan_4x4 = pos_scan4x4[1]; + level=1; + + currMB->subblock_y = subblk_offset_y[yuv][b8][b4]; + currMB->subblock_x = subblk_offset_x[yuv][b8][b4]; + + for(k=0;(k<16)&&(level!=0);++k) + { +#if TRACE + snprintf(currSE.tracestring, TRACESTRING_SIZE, "AC Chroma "); +#endif + dP->readSyntaxElement(currMB, &currSE, dP); + level = currSE.value1; + + if (level != 0) + { + s_cbp->blk |= i64_power2(cbp_blk_chroma[b8][b4]); + pos_scan_4x4 += (currSE.value2 << 1); + + i0 = *pos_scan_4x4++; + j0 = *pos_scan_4x4++; + + currSlice->cof[uv + 1][(j<<2) + j0][(i<<2) + i0] = level; + //currSlice->fcf[uv + 1][(j<<2) + j0][(i<<2) + i0] = level; + } + } + } + } + } //for (b4=0; b4 < 4; b4++) + } +} + + +/*! + ************************************************************************ + * \brief + * Get coded block pattern and coefficients (run/level) + * from the NAL + ************************************************************************ + */ +static void read_CBP_and_coeffs_from_NAL_CABAC_400(Macroblock *currMB) +{ + int k; + int level; + int cbp; + SyntaxElement currSE; + DataPartition *dP = NULL; + Slice *currSlice = currMB->p_Slice; + const byte *partMap = assignSE2partition[currSlice->dp_mode]; + int i0, j0; + + int qp_per, qp_rem; + VideoParameters *p_Vid = currMB->p_Vid; + + int intra = (currMB->is_intra_block == TRUE); + + int need_transform_size_flag; + + int (*InvLevelScale4x4)[4] = NULL; + // select scan type + const byte (*pos_scan4x4)[2] = ((p_Vid->structure == FRAME) && (!currMB->mb_field)) ? SNGL_SCAN : FIELD_SCAN; + const byte *pos_scan_4x4 = pos_scan4x4[0]; + + + // read CBP if not new intra mode + if (!IS_I16MB (currMB)) + { + //===== C B P ===== + //--------------------- + currSE.type = (currMB->mb_type == I4MB || currMB->mb_type == SI4MB || currMB->mb_type == I8MB) + ? SE_CBP_INTRA + : SE_CBP_INTER; + + dP = &(currSlice->partArr[partMap[currSE.type]]); + + if (dP->bitstream->ei_flag) + { + currSE.mapping = (currMB->mb_type == I4MB || currMB->mb_type == SI4MB || currMB->mb_type == I8MB) + ? currSlice->linfo_cbp_intra + : currSlice->linfo_cbp_inter; + } + else + { + currSE.reading = read_CBP_CABAC; + } + + TRACE_STRING("coded_block_pattern"); + dP->readSyntaxElement(currMB, &currSE, dP); + currMB->cbp = cbp = currSE.value1; + + + //============= Transform size flag for INTER MBs ============= + //------------------------------------------------------------- + need_transform_size_flag = (((currMB->mb_type >= 1 && currMB->mb_type <= 3)|| + (IS_DIRECT(currMB) && p_Vid->active_sps->direct_8x8_inference_flag) || + (currMB->NoMbPartLessThan8x8Flag)) + && currMB->mb_type != I8MB && currMB->mb_type != I4MB + && (currMB->cbp&15) + && currSlice->Transform8x8Mode); + + if (need_transform_size_flag) + { + currSE.type = SE_HEADER; + dP = &(currSlice->partArr[partMap[SE_HEADER]]); + currSE.reading = readMB_transform_size_flag_CABAC; + TRACE_STRING("transform_size_8x8_flag"); + + // read CAVLC transform_size_8x8_flag + if (dP->bitstream->ei_flag) + { + currSE.len = 1; + readSyntaxElement_FLC(&currSE, dP->bitstream); + } + else + { + dP->readSyntaxElement(currMB, &currSE, dP); + } + currMB->luma_transform_size_8x8_flag = (Boolean) currSE.value1; + } + + //===== DQUANT ===== + //---------------------- + // Delta quant only if nonzero coeffs + if (cbp !=0) + { + read_delta_quant(&currSE, dP, currMB, partMap, ((currMB->is_intra_block == FALSE)) ? SE_DELTA_QUANT_INTER : SE_DELTA_QUANT_INTRA); + + if (currSlice->dp_mode) + { + if ((currMB->is_intra_block == FALSE) && currSlice->dpC_NotPresent ) + currMB->dpl_flag = 1; + + if( intra && currSlice->dpB_NotPresent ) + { + currMB->ei_flag = 1; + currMB->dpl_flag = 1; + } + + // check for prediction from neighbours + check_dp_neighbors (currMB); + if (currMB->dpl_flag) + { + cbp = 0; + currMB->cbp = cbp; + } + } + } + } + else // read DC coeffs for new intra modes + { + cbp = currMB->cbp; + read_delta_quant(&currSE, dP, currMB, partMap, SE_DELTA_QUANT_INTRA); + + if (currSlice->dp_mode) + { + if (currSlice->dpB_NotPresent) + { + currMB->ei_flag = 1; + currMB->dpl_flag = 1; + } + check_dp_neighbors (currMB); + if (currMB->dpl_flag) + { + currMB->cbp = cbp = 0; + } + } + + if (!currMB->dpl_flag) + { + pos_scan_4x4 = pos_scan4x4[0]; + + { + currSE.type = SE_LUM_DC_INTRA; + dP = &(currSlice->partArr[partMap[currSE.type]]); + + currSE.context = LUMA_16DC; + currSE.type = SE_LUM_DC_INTRA; + + if (dP->bitstream->ei_flag) + { + currSE.mapping = linfo_levrun_inter; + } + else + { + currSE.reading = readRunLevel_CABAC; + } + + level = 1; // just to get inside the loop + + for(k = 0; (k < 17) && (level != 0); ++k) + { +#if TRACE + snprintf(currSE.tracestring, TRACESTRING_SIZE, "DC luma 16x16 "); +#endif + dP->readSyntaxElement(currMB, &currSE, dP); + level = currSE.value1; + + if (level != 0) /* leave if level == 0 */ + { + pos_scan_4x4 += (2 * currSE.value2); + + i0 = ((*pos_scan_4x4++) << 2); + j0 = ((*pos_scan_4x4++) << 2); + + currSlice->cof[0][j0][i0] = level;// add new intra DC coeff + //currSlice->fcf[0][j0][i0] = level;// add new intra DC coeff + } + } + } + + if(currMB->is_lossless == FALSE) + itrans_2(currMB, (ColorPlane) currSlice->colour_plane_id);// transform new intra DC + } + } + + update_qp(currMB, currSlice->qp); + + qp_per = p_Vid->qp_per_matrix[ currMB->qp_scaled[PLANE_Y] ]; + qp_rem = p_Vid->qp_rem_matrix[ currMB->qp_scaled[PLANE_Y] ]; + + //======= Other Modes & CABAC ======== + //------------------------------------ + if (cbp) + { + if(currMB->luma_transform_size_8x8_flag) + { + //======= 8x8 transform size & CABAC ======== + currMB->read_comp_coeff_8x8_CABAC (currMB, &currSE, PLANE_Y); + } + else + { + InvLevelScale4x4 = intra? currSlice->InvLevelScale4x4_Intra[currSlice->colour_plane_id][qp_rem] : currSlice->InvLevelScale4x4_Inter[currSlice->colour_plane_id][qp_rem]; + currMB->read_comp_coeff_4x4_CABAC (currMB, &currSE, PLANE_Y, InvLevelScale4x4, qp_per, cbp); + } + } +} + +/*! + ************************************************************************ + * \brief + * Get coded block pattern and coefficients (run/level) + * from the NAL + ************************************************************************ + */ +static void read_CBP_and_coeffs_from_NAL_CABAC_444(Macroblock *currMB) +{ + int i, k; + int level; + int cbp; + SyntaxElement currSE; + DataPartition *dP = NULL; + Slice *currSlice = currMB->p_Slice; + const byte *partMap = assignSE2partition[currSlice->dp_mode]; + int coef_ctr, i0, j0; + + int qp_per, qp_rem; + VideoParameters *p_Vid = currMB->p_Vid; + + int uv; + int qp_per_uv[2]; + int qp_rem_uv[2]; + + int intra = (currMB->is_intra_block == TRUE); + + + int need_transform_size_flag; + + int (*InvLevelScale4x4)[4] = NULL; + // select scan type + const byte (*pos_scan4x4)[2] = ((p_Vid->structure == FRAME) && (!currMB->mb_field)) ? SNGL_SCAN : FIELD_SCAN; + const byte *pos_scan_4x4 = pos_scan4x4[0]; + + // QPI + //init constants for every chroma qp offset + for (i=0; i<2; ++i) + { + qp_per_uv[i] = p_Vid->qp_per_matrix[ currMB->qp_scaled[i + 1] ]; + qp_rem_uv[i] = p_Vid->qp_rem_matrix[ currMB->qp_scaled[i + 1] ]; + } + + + // read CBP if not new intra mode + if (!IS_I16MB (currMB)) + { + //===== C B P ===== + //--------------------- + currSE.type = (currMB->mb_type == I4MB || currMB->mb_type == SI4MB || currMB->mb_type == I8MB) + ? SE_CBP_INTRA + : SE_CBP_INTER; + + dP = &(currSlice->partArr[partMap[currSE.type]]); + + if (dP->bitstream->ei_flag) + { + currSE.mapping = (currMB->mb_type == I4MB || currMB->mb_type == SI4MB || currMB->mb_type == I8MB) + ? currSlice->linfo_cbp_intra + : currSlice->linfo_cbp_inter; + } + else + { + currSE.reading = read_CBP_CABAC; + } + + TRACE_STRING("coded_block_pattern"); + dP->readSyntaxElement(currMB, &currSE, dP); + currMB->cbp = cbp = currSE.value1; + + + //============= Transform size flag for INTER MBs ============= + //------------------------------------------------------------- + need_transform_size_flag = (((currMB->mb_type >= 1 && currMB->mb_type <= 3)|| + (IS_DIRECT(currMB) && p_Vid->active_sps->direct_8x8_inference_flag) || + (currMB->NoMbPartLessThan8x8Flag)) + && currMB->mb_type != I8MB && currMB->mb_type != I4MB + && (currMB->cbp&15) + && currSlice->Transform8x8Mode); + + if (need_transform_size_flag) + { + currSE.type = SE_HEADER; + dP = &(currSlice->partArr[partMap[SE_HEADER]]); + currSE.reading = readMB_transform_size_flag_CABAC; + TRACE_STRING("transform_size_8x8_flag"); + + // read CAVLC transform_size_8x8_flag + if (dP->bitstream->ei_flag) + { + currSE.len = 1; + readSyntaxElement_FLC(&currSE, dP->bitstream); + } + else + { + dP->readSyntaxElement(currMB, &currSE, dP); + } + currMB->luma_transform_size_8x8_flag = (Boolean) currSE.value1; + } + + //===== DQUANT ===== + //---------------------- + // Delta quant only if nonzero coeffs + if (cbp !=0) + { + read_delta_quant(&currSE, dP, currMB, partMap, ((currMB->is_intra_block == FALSE)) ? SE_DELTA_QUANT_INTER : SE_DELTA_QUANT_INTRA); + + if (currSlice->dp_mode) + { + if ((currMB->is_intra_block == FALSE) && currSlice->dpC_NotPresent ) + currMB->dpl_flag = 1; + + if( intra && currSlice->dpB_NotPresent ) + { + currMB->ei_flag = 1; + currMB->dpl_flag = 1; + } + + // check for prediction from neighbours + check_dp_neighbors (currMB); + if (currMB->dpl_flag) + { + cbp = 0; + currMB->cbp = cbp; + } + } + } + } + else // read DC coeffs for new intra modes + { + cbp = currMB->cbp; + + read_delta_quant(&currSE, dP, currMB, partMap, SE_DELTA_QUANT_INTRA); + + if (currSlice->dp_mode) + { + if (currSlice->dpB_NotPresent) + { + currMB->ei_flag = 1; + currMB->dpl_flag = 1; + } + check_dp_neighbors (currMB); + if (currMB->dpl_flag) + { + currMB->cbp = cbp = 0; + } + } + + if (!currMB->dpl_flag) + { + pos_scan_4x4 = pos_scan4x4[0]; + + { + currSE.type = SE_LUM_DC_INTRA; + dP = &(currSlice->partArr[partMap[currSE.type]]); + + currSE.context = LUMA_16DC; + currSE.type = SE_LUM_DC_INTRA; + + if (dP->bitstream->ei_flag) + { + currSE.mapping = linfo_levrun_inter; + } + else + { + currSE.reading = readRunLevel_CABAC; + } + + level = 1; // just to get inside the loop + + for(k = 0; (k < 17) && (level != 0); ++k) + { +#if TRACE + snprintf(currSE.tracestring, TRACESTRING_SIZE, "DC luma 16x16 "); +#endif + dP->readSyntaxElement(currMB, &currSE, dP); + level = currSE.value1; + + if (level != 0) /* leave if level == 0 */ + { + pos_scan_4x4 += (2 * currSE.value2); + + i0 = ((*pos_scan_4x4++) << 2); + j0 = ((*pos_scan_4x4++) << 2); + + currSlice->cof[0][j0][i0] = level;// add new intra DC coeff + //currSlice->fcf[0][j0][i0] = level;// add new intra DC coeff + } + } + } + + if(currMB->is_lossless == FALSE) + itrans_2(currMB, (ColorPlane) currSlice->colour_plane_id);// transform new intra DC + } + } + + update_qp(currMB, currSlice->qp); + + qp_per = p_Vid->qp_per_matrix[ currMB->qp_scaled[currSlice->colour_plane_id] ]; + qp_rem = p_Vid->qp_rem_matrix[ currMB->qp_scaled[currSlice->colour_plane_id] ]; + + //init quant parameters for chroma + for(i=0; i < 2; ++i) + { + qp_per_uv[i] = p_Vid->qp_per_matrix[ currMB->qp_scaled[i + 1] ]; + qp_rem_uv[i] = p_Vid->qp_rem_matrix[ currMB->qp_scaled[i + 1] ]; + } + + + InvLevelScale4x4 = intra? currSlice->InvLevelScale4x4_Intra[currSlice->colour_plane_id][qp_rem] : currSlice->InvLevelScale4x4_Inter[currSlice->colour_plane_id][qp_rem]; + + // luma coefficients + { + //======= Other Modes & CABAC ======== + //------------------------------------ + if (cbp) + { + if(currMB->luma_transform_size_8x8_flag) + { + //======= 8x8 transform size & CABAC ======== + currMB->read_comp_coeff_8x8_CABAC (currMB, &currSE, PLANE_Y); + } + else + { + currMB->read_comp_coeff_4x4_CABAC (currMB, &currSE, PLANE_Y, InvLevelScale4x4, qp_per, cbp); + } + } + } + + for (uv = 0; uv < 2; ++uv ) + { + /*----------------------16x16DC Luma_Add----------------------*/ + if (IS_I16MB (currMB)) // read DC coeffs for new intra modes + { + { + currSE.type = SE_LUM_DC_INTRA; + dP = &(currSlice->partArr[partMap[currSE.type]]); + + if( (p_Vid->separate_colour_plane_flag != 0) ) + currSE.context = LUMA_16DC; + else + currSE.context = (uv==0) ? CB_16DC : CR_16DC; + + if (dP->bitstream->ei_flag) + { + currSE.mapping = linfo_levrun_inter; + } + else + { + currSE.reading = readRunLevel_CABAC; + } + + coef_ctr = -1; + level = 1; // just to get inside the loop + + for(k=0;(k<17) && (level!=0);++k) + { +#if TRACE + if (uv == 0) + snprintf(currSE.tracestring, TRACESTRING_SIZE, "DC Cb 16x16 "); + else + snprintf(currSE.tracestring, TRACESTRING_SIZE, "DC Cr 16x16 "); +#endif + + dP->readSyntaxElement(currMB, &currSE, dP); + level = currSE.value1; + + if (level != 0) // leave if level == 0 + { + coef_ctr += currSE.value2 + 1; + + i0 = pos_scan4x4[coef_ctr][0]; + j0 = pos_scan4x4[coef_ctr][1]; + currSlice->cof[uv + 1][j0<<2][i0<<2] = level; + //currSlice->fcf[uv + 1][j0<<2][i0<<2] = level; + } + } //k loop + } // else CAVLC + + if(currMB->is_lossless == FALSE) + { + itrans_2(currMB, (ColorPlane) (uv + 1)); // transform new intra DC + } + } //IS_I16MB + + update_qp(currMB, currSlice->qp); + + qp_per = p_Vid->qp_per_matrix[ (currSlice->qp + p_Vid->bitdepth_luma_qp_scale) ]; + qp_rem = p_Vid->qp_rem_matrix[ (currSlice->qp + p_Vid->bitdepth_luma_qp_scale) ]; + + //init constants for every chroma qp offset + qp_per_uv[uv] = p_Vid->qp_per_matrix[ (currMB->qpc[uv] + p_Vid->bitdepth_chroma_qp_scale) ]; + qp_rem_uv[uv] = p_Vid->qp_rem_matrix[ (currMB->qpc[uv] + p_Vid->bitdepth_chroma_qp_scale) ]; + + InvLevelScale4x4 = intra? currSlice->InvLevelScale4x4_Intra[uv + 1][qp_rem_uv[uv]] : currSlice->InvLevelScale4x4_Inter[uv + 1][qp_rem_uv[uv]]; + + { + if (cbp) + { + if(currMB->luma_transform_size_8x8_flag) + { + //======= 8x8 transform size & CABAC ======== + currMB->read_comp_coeff_8x8_CABAC (currMB, &currSE, (ColorPlane) (PLANE_U + uv)); + } + else //4x4 + { + currMB->read_comp_coeff_4x4_CABAC (currMB, &currSE, (ColorPlane) (PLANE_U + uv), InvLevelScale4x4, qp_per_uv[uv], cbp); + } + } + } + } +} + +/*! + ************************************************************************ + * \brief + * Get coded block pattern and coefficients (run/level) + * from the NAL + ************************************************************************ + */ +static void read_CBP_and_coeffs_from_NAL_CABAC_422(Macroblock *currMB) +{ + int i,j,k; + int level; + int cbp; + SyntaxElement currSE; + DataPartition *dP = NULL; + Slice *currSlice = currMB->p_Slice; + const byte *partMap = assignSE2partition[currSlice->dp_mode]; + int coef_ctr, i0, j0, b8; + int ll; + + int qp_per, qp_rem; + VideoParameters *p_Vid = currMB->p_Vid; + + int uv; + int qp_per_uv[2]; + int qp_rem_uv[2]; + + int intra = (currMB->is_intra_block == TRUE); + + int b4; + StorablePicture *dec_picture = currSlice->dec_picture; + int yuv = dec_picture->chroma_format_idc - 1; + int m6[4]; + + int need_transform_size_flag; + + int (*InvLevelScale4x4)[4] = NULL; + // select scan type + const byte (*pos_scan4x4)[2] = ((p_Vid->structure == FRAME) && (!currMB->mb_field)) ? SNGL_SCAN : FIELD_SCAN; + const byte *pos_scan_4x4 = pos_scan4x4[0]; + + // QPI + //init constants for every chroma qp offset + for (i=0; i<2; ++i) + { + qp_per_uv[i] = p_Vid->qp_per_matrix[ currMB->qp_scaled[i + 1] ]; + qp_rem_uv[i] = p_Vid->qp_rem_matrix[ currMB->qp_scaled[i + 1] ]; + } + + // read CBP if not new intra mode + if (!IS_I16MB (currMB)) + { + //===== C B P ===== + //--------------------- + currSE.type = (currMB->mb_type == I4MB || currMB->mb_type == SI4MB || currMB->mb_type == I8MB) + ? SE_CBP_INTRA + : SE_CBP_INTER; + + dP = &(currSlice->partArr[partMap[currSE.type]]); + + if (dP->bitstream->ei_flag) + { + currSE.mapping = (currMB->mb_type == I4MB || currMB->mb_type == SI4MB || currMB->mb_type == I8MB) + ? currSlice->linfo_cbp_intra + : currSlice->linfo_cbp_inter; + } + else + { + currSE.reading = read_CBP_CABAC; + } + + TRACE_STRING("coded_block_pattern"); + dP->readSyntaxElement(currMB, &currSE, dP); + currMB->cbp = cbp = currSE.value1; + + + //============= Transform size flag for INTER MBs ============= + //------------------------------------------------------------- + need_transform_size_flag = (((currMB->mb_type >= 1 && currMB->mb_type <= 3)|| + (IS_DIRECT(currMB) && p_Vid->active_sps->direct_8x8_inference_flag) || + (currMB->NoMbPartLessThan8x8Flag)) + && currMB->mb_type != I8MB && currMB->mb_type != I4MB + && (currMB->cbp&15) + && currSlice->Transform8x8Mode); + + if (need_transform_size_flag) + { + currSE.type = SE_HEADER; + dP = &(currSlice->partArr[partMap[SE_HEADER]]); + currSE.reading = readMB_transform_size_flag_CABAC; + TRACE_STRING("transform_size_8x8_flag"); + + // read CAVLC transform_size_8x8_flag + if (dP->bitstream->ei_flag) + { + currSE.len = 1; + readSyntaxElement_FLC(&currSE, dP->bitstream); + } + else + { + dP->readSyntaxElement(currMB, &currSE, dP); + } + currMB->luma_transform_size_8x8_flag = (Boolean) currSE.value1; + } + + //===== DQUANT ===== + //---------------------- + // Delta quant only if nonzero coeffs + if (cbp !=0) + { + read_delta_quant(&currSE, dP, currMB, partMap, ((currMB->is_intra_block == FALSE)) ? SE_DELTA_QUANT_INTER : SE_DELTA_QUANT_INTRA); + + if (currSlice->dp_mode) + { + if ((currMB->is_intra_block == FALSE) && currSlice->dpC_NotPresent ) + currMB->dpl_flag = 1; + + if( intra && currSlice->dpB_NotPresent ) + { + currMB->ei_flag = 1; + currMB->dpl_flag = 1; + } + + // check for prediction from neighbours + check_dp_neighbors (currMB); + if (currMB->dpl_flag) + { + cbp = 0; + currMB->cbp = cbp; + } + } + } + } + else // read DC coeffs for new intra modes + { + cbp = currMB->cbp; + + read_delta_quant(&currSE, dP, currMB, partMap, SE_DELTA_QUANT_INTRA); + + if (currSlice->dp_mode) + { + if (currSlice->dpB_NotPresent) + { + currMB->ei_flag = 1; + currMB->dpl_flag = 1; + } + check_dp_neighbors (currMB); + if (currMB->dpl_flag) + { + currMB->cbp = cbp = 0; + } + } + + if (!currMB->dpl_flag) + { + pos_scan_4x4 = pos_scan4x4[0]; + + { + currSE.type = SE_LUM_DC_INTRA; + dP = &(currSlice->partArr[partMap[currSE.type]]); + + currSE.context = LUMA_16DC; + currSE.type = SE_LUM_DC_INTRA; + + if (dP->bitstream->ei_flag) + { + currSE.mapping = linfo_levrun_inter; + } + else + { + currSE.reading = readRunLevel_CABAC; + } + + level = 1; // just to get inside the loop + + for(k = 0; (k < 17) && (level != 0); ++k) + { +#if TRACE + snprintf(currSE.tracestring, TRACESTRING_SIZE, "DC luma 16x16 "); +#endif + dP->readSyntaxElement(currMB, &currSE, dP); + level = currSE.value1; + + if (level != 0) /* leave if level == 0 */ + { + pos_scan_4x4 += (2 * currSE.value2); + + i0 = ((*pos_scan_4x4++) << 2); + j0 = ((*pos_scan_4x4++) << 2); + + currSlice->cof[0][j0][i0] = level;// add new intra DC coeff + //currSlice->fcf[0][j0][i0] = level;// add new intra DC coeff + } + } + } + + if(currMB->is_lossless == FALSE) + itrans_2(currMB, (ColorPlane) currSlice->colour_plane_id);// transform new intra DC + } + } + + update_qp(currMB, currSlice->qp); + + qp_per = p_Vid->qp_per_matrix[ currMB->qp_scaled[currSlice->colour_plane_id] ]; + qp_rem = p_Vid->qp_rem_matrix[ currMB->qp_scaled[currSlice->colour_plane_id] ]; + + //init quant parameters for chroma + for(i=0; i < 2; ++i) + { + qp_per_uv[i] = p_Vid->qp_per_matrix[ currMB->qp_scaled[i + 1] ]; + qp_rem_uv[i] = p_Vid->qp_rem_matrix[ currMB->qp_scaled[i + 1] ]; + } + + InvLevelScale4x4 = intra? currSlice->InvLevelScale4x4_Intra[currSlice->colour_plane_id][qp_rem] : currSlice->InvLevelScale4x4_Inter[currSlice->colour_plane_id][qp_rem]; + + // luma coefficients + { + //======= Other Modes & CABAC ======== + //------------------------------------ + if (cbp) + { + if(currMB->luma_transform_size_8x8_flag) + { + //======= 8x8 transform size & CABAC ======== + currMB->read_comp_coeff_8x8_CABAC (currMB, &currSE, PLANE_Y); + } + else + { + currMB->read_comp_coeff_4x4_CABAC (currMB, &currSE, PLANE_Y, InvLevelScale4x4, qp_per, cbp); + } + } + } + + //========================== CHROMA DC ============================ + //----------------------------------------------------------------- + // chroma DC coeff + if(cbp>15) + { + for (ll=0;ll<3;ll+=2) + { + int (*InvLevelScale4x4)[4] = NULL; + uv = ll>>1; + { + int **imgcof = currSlice->cof[uv + 1]; + int m3[2][4] = {{0,0,0,0},{0,0,0,0}}; + int m4[2][4] = {{0,0,0,0},{0,0,0,0}}; + int qp_per_uv_dc = p_Vid->qp_per_matrix[ (currMB->qpc[uv] + 3 + p_Vid->bitdepth_chroma_qp_scale) ]; //for YUV422 only + int qp_rem_uv_dc = p_Vid->qp_rem_matrix[ (currMB->qpc[uv] + 3 + p_Vid->bitdepth_chroma_qp_scale) ]; //for YUV422 only + if (intra) + InvLevelScale4x4 = currSlice->InvLevelScale4x4_Intra[uv + 1][qp_rem_uv_dc]; + else + InvLevelScale4x4 = currSlice->InvLevelScale4x4_Inter[uv + 1][qp_rem_uv_dc]; + + + //===================== CHROMA DC YUV422 ====================== + { + CBPStructure *s_cbp = &currMB->s_cbp[0]; + coef_ctr=-1; + level=1; + for(k=0;(k<9)&&(level!=0);++k) + { + currSE.context = CHROMA_DC_2x4; + currSE.type = ((currMB->is_intra_block == TRUE) ? SE_CHR_DC_INTRA : SE_CHR_DC_INTER); + currMB->is_v_block = ll; + +#if TRACE + snprintf(currSE.tracestring, TRACESTRING_SIZE, "2x4 DC Chroma "); +#endif + dP = &(currSlice->partArr[partMap[currSE.type]]); + + if (dP->bitstream->ei_flag) + currSE.mapping = linfo_levrun_c2x2; + else + currSE.reading = readRunLevel_CABAC; + + dP->readSyntaxElement(currMB, &currSE, dP); + + level = currSE.value1; + + if (level != 0) + { + s_cbp->blk |= ((int64)0xff0000) << (ll<<2) ; + coef_ctr += currSE.value2 + 1; + assert (coef_ctr < p_Vid->num_cdc_coeff); + i0=SCAN_YUV422[coef_ctr][0]; + j0=SCAN_YUV422[coef_ctr][1]; + + m3[i0][j0]=level; + } + } + } + // inverse CHROMA DC YUV422 transform + // horizontal + if(currMB->is_lossless == FALSE) + { + m4[0][0] = m3[0][0] + m3[1][0]; + m4[0][1] = m3[0][1] + m3[1][1]; + m4[0][2] = m3[0][2] + m3[1][2]; + m4[0][3] = m3[0][3] + m3[1][3]; + + m4[1][0] = m3[0][0] - m3[1][0]; + m4[1][1] = m3[0][1] - m3[1][1]; + m4[1][2] = m3[0][2] - m3[1][2]; + m4[1][3] = m3[0][3] - m3[1][3]; + + for (i = 0; i < 2; ++i) + { + m6[0] = m4[i][0] + m4[i][2]; + m6[1] = m4[i][0] - m4[i][2]; + m6[2] = m4[i][1] - m4[i][3]; + m6[3] = m4[i][1] + m4[i][3]; + + imgcof[ 0][i<<2] = m6[0] + m6[3]; + imgcof[ 4][i<<2] = m6[1] + m6[2]; + imgcof[ 8][i<<2] = m6[1] - m6[2]; + imgcof[12][i<<2] = m6[0] - m6[3]; + }//for (i=0;i<2;++i) + + for(j = 0;j < p_Vid->mb_cr_size_y; j += BLOCK_SIZE) + { + for(i=0;i < p_Vid->mb_cr_size_x;i+=BLOCK_SIZE) + { + imgcof[j][i] = rshift_rnd_sf((imgcof[j][i] * InvLevelScale4x4[0][0]) << qp_per_uv_dc, 6); + } + } + } + else + { + for(j=0;j<4;++j) + { + for(i=0;i<2;++i) + { + currSlice->cof[uv + 1][j<<2][i<<2] = m3[i][j]; + //currSlice->fcf[uv + 1][j<<2][i<<2] = m3[i][j]; + } + } + } + + } + }//for (ll=0;ll<3;ll+=2) + } + + //========================== CHROMA AC ============================ + //----------------------------------------------------------------- + // chroma AC coeff, all zero fram start_scan + if (cbp<=31) + { + } + else + { + { + currSE.context = CHROMA_AC; + currSE.type = (currMB->is_intra_block ? SE_CHR_AC_INTRA : SE_CHR_AC_INTER); + + dP = &(currSlice->partArr[partMap[currSE.type]]); + + if (dP->bitstream->ei_flag) + currSE.mapping = linfo_levrun_inter; + else + currSE.reading = readRunLevel_CABAC; + + if(currMB->is_lossless == FALSE) + { + CBPStructure *s_cbp = &currMB->s_cbp[0]; + for (b8=0; b8 < p_Vid->num_blk8x8_uv; ++b8) + { + currMB->is_v_block = uv = (b8 > ((p_Vid->num_uv_blocks) - 1 )); + InvLevelScale4x4 = intra ? currSlice->InvLevelScale4x4_Intra[uv + 1][qp_rem_uv[uv]] : currSlice->InvLevelScale4x4_Inter[uv + 1][qp_rem_uv[uv]]; + + for (b4 = 0; b4 < 4; ++b4) + { + i = cofuv_blk_x[yuv][b8][b4]; + j = cofuv_blk_y[yuv][b8][b4]; + + currMB->subblock_y = subblk_offset_y[yuv][b8][b4]; + currMB->subblock_x = subblk_offset_x[yuv][b8][b4]; + + pos_scan_4x4 = pos_scan4x4[1]; + level=1; + + for(k = 0; (k < 16) && (level != 0);++k) + { +#if TRACE + snprintf(currSE.tracestring, TRACESTRING_SIZE, "AC Chroma "); +#endif + + dP->readSyntaxElement(currMB, &currSE, dP); + level = currSE.value1; + + if (level != 0) + { + s_cbp->blk |= i64_power2(cbp_blk_chroma[b8][b4]); + pos_scan_4x4 += (currSE.value2 << 1); + + i0 = *pos_scan_4x4++; + j0 = *pos_scan_4x4++; + + currSlice->cof[uv + 1][(j<<2) + j0][(i<<2) + i0] = rshift_rnd_sf((level * InvLevelScale4x4[j0][i0])<fcf[uv + 1][(j<<2) + j0][(i<<2) + i0] = level; + } + } //for(k=0;(k<16)&&(level!=0);++k) + } + } + } + else + { + CBPStructure *s_cbp = &currMB->s_cbp[0]; + for (b8=0; b8 < p_Vid->num_blk8x8_uv; ++b8) + { + currMB->is_v_block = uv = (b8 > ((p_Vid->num_uv_blocks) - 1 )); + + for (b4=0; b4 < 4; ++b4) + { + i = cofuv_blk_x[yuv][b8][b4]; + j = cofuv_blk_y[yuv][b8][b4]; + + pos_scan_4x4 = pos_scan4x4[1]; + level=1; + + currMB->subblock_y = subblk_offset_y[yuv][b8][b4]; + currMB->subblock_x = subblk_offset_x[yuv][b8][b4]; + + for(k=0;(k<16)&&(level!=0);++k) + { +#if TRACE + snprintf(currSE.tracestring, TRACESTRING_SIZE, "AC Chroma "); +#endif + dP->readSyntaxElement(currMB, &currSE, dP); + level = currSE.value1; + + if (level != 0) + { + s_cbp->blk |= i64_power2(cbp_blk_chroma[b8][b4]); + pos_scan_4x4 += (currSE.value2 << 1); + + i0 = *pos_scan_4x4++; + j0 = *pos_scan_4x4++; + + currSlice->cof[uv + 1][(j<<2) + j0][(i<<2) + i0] = level; + //currSlice->fcf[uv + 1][(j<<2) + j0][(i<<2) + i0] = level; + } + } + } + } + } //for (b4=0; b4 < 4; b4++) + } //for (b8=0; b8 < p_Vid->num_blk8x8_uv; b8++) + } //if (dec_picture->chroma_format_idc != YUV400) +} + +void set_read_CBP_and_coeffs_cabac(Slice *currSlice) +{ + switch (currSlice->p_Vid->active_sps->chroma_format_idc) + { + case YUV444: + if (currSlice->p_Vid->separate_colour_plane_flag == 0) + { + currSlice->read_CBP_and_coeffs_from_NAL = read_CBP_and_coeffs_from_NAL_CABAC_444; + } + else + { + currSlice->read_CBP_and_coeffs_from_NAL = read_CBP_and_coeffs_from_NAL_CABAC_400; + } + break; + case YUV422: + currSlice->read_CBP_and_coeffs_from_NAL = read_CBP_and_coeffs_from_NAL_CABAC_422; + break; + case YUV420: + currSlice->read_CBP_and_coeffs_from_NAL = read_CBP_and_coeffs_from_NAL_CABAC_420; + break; + case YUV400: + currSlice->read_CBP_and_coeffs_from_NAL = read_CBP_and_coeffs_from_NAL_CABAC_400; + break; + default: + assert (1); + currSlice->read_CBP_and_coeffs_from_NAL = NULL; + break; + } +} + + +/*! +************************************************************************ +* \brief +* setup coefficient reading functions for CABAC +* +************************************************************************ +*/ +void set_read_comp_coeff_cabac(Macroblock *currMB) +{ + if (currMB->is_lossless == FALSE) + { + currMB->read_comp_coeff_4x4_CABAC = read_comp_coeff_4x4_CABAC; + currMB->read_comp_coeff_8x8_CABAC = read_comp_coeff_8x8_MB_CABAC; + } + else + { + currMB->read_comp_coeff_4x4_CABAC = read_comp_coeff_4x4_CABAC_ls; + currMB->read_comp_coeff_8x8_CABAC = read_comp_coeff_8x8_MB_CABAC_ls; + } +} \ No newline at end of file diff --git a/ldecod/src/read_comp_cavlc.c b/ldecod/src/read_comp_cavlc.c new file mode 100644 index 0000000..8dd923d --- /dev/null +++ b/ldecod/src/read_comp_cavlc.c @@ -0,0 +1,2156 @@ +/*! + *********************************************************************** + * \file read_comp_cavlc.c + * + * \brief + * Read Coefficient Components (CAVLC version) + * + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * - Alexis Michael Tourapis + *********************************************************************** +*/ + +#include "contributors.h" + +#include "global.h" +#include "elements.h" +#include "macroblock.h" +#include "vlc.h" +#include "fast_memory.h" +#include "transform.h" +#include "mb_access.h" + +#if TRACE +#define TRACE_STRING(s) strncpy(currSE.tracestring, s, TRACESTRING_SIZE) +#define TRACE_DECBITS(i) dectracebitcnt(1) +#define TRACE_printf(s) sprintf(type, "%s", s); +#define TRACE_STRING_P(s) strncpy(currSE->tracestring, s, TRACESTRING_SIZE) +#else +#define TRACE_STRING(s) +#define TRACE_DECBITS(i) +#define TRACE_printf(s) +#define TRACE_STRING_P(s) +#endif + +extern void check_dp_neighbors (Macroblock *currMB); +extern void read_delta_quant (SyntaxElement *currSE, DataPartition *dP, Macroblock *currMB, const byte *partMap, int type); + +/*! + ************************************************************************ + * \brief + * Get the Prediction from the Neighboring Blocks for Number of + * Nonzero Coefficients + * + * Luma Blocks + ************************************************************************ + */ +static int predict_nnz(Macroblock *currMB, int block_type, int i,int j) +{ + VideoParameters *p_Vid = currMB->p_Vid; + Slice *currSlice = currMB->p_Slice; + + PixelPos pix; + + int pred_nnz = 0; + int cnt = 0; + + // left block + get4x4Neighbour(currMB, i - 1, j, p_Vid->mb_size[IS_LUMA], &pix); + + if ((currMB->is_intra_block == TRUE) && pix.available && p_Vid->active_pps->constrained_intra_pred_flag && (currSlice->dp_mode == PAR_DP_3)) + { + pix.available &= currSlice->intra_block[pix.mb_addr]; + if (!pix.available) + ++cnt; + } + + if (pix.available) + { + switch (block_type) + { + case LUMA: + pred_nnz = p_Vid->nz_coeff [pix.mb_addr ][0][pix.y][pix.x]; + ++cnt; + break; + case CB: + pred_nnz = p_Vid->nz_coeff [pix.mb_addr ][1][pix.y][pix.x]; + ++cnt; + break; + case CR: + pred_nnz = p_Vid->nz_coeff [pix.mb_addr ][2][pix.y][pix.x]; + ++cnt; + break; + default: + error("writeCoeff4x4_CAVLC: Invalid block type", 600); + break; + } + } + + // top block + get4x4Neighbour(currMB, i, j - 1, p_Vid->mb_size[IS_LUMA], &pix); + + if ((currMB->is_intra_block == TRUE) && pix.available && p_Vid->active_pps->constrained_intra_pred_flag && (currSlice->dp_mode==PAR_DP_3)) + { + pix.available &= currSlice->intra_block[pix.mb_addr]; + if (!pix.available) + ++cnt; + } + + if (pix.available) + { + switch (block_type) + { + case LUMA: + pred_nnz += p_Vid->nz_coeff [pix.mb_addr ][0][pix.y][pix.x]; + ++cnt; + break; + case CB: + pred_nnz += p_Vid->nz_coeff [pix.mb_addr ][1][pix.y][pix.x]; + ++cnt; + break; + case CR: + pred_nnz += p_Vid->nz_coeff [pix.mb_addr ][2][pix.y][pix.x]; + ++cnt; + break; + default: + error("writeCoeff4x4_CAVLC: Invalid block type", 600); + break; + } + } + + if (cnt==2) + { + ++pred_nnz; + pred_nnz >>= 1; + } + + return pred_nnz; +} + + +/*! + ************************************************************************ + * \brief + * Get the Prediction from the Neighboring Blocks for Number of + * Nonzero Coefficients + * + * Chroma Blocks + ************************************************************************ + */ +static int predict_nnz_chroma(Macroblock *currMB, int i,int j) +{ + StorablePicture *dec_picture = currMB->p_Slice->dec_picture; + + if (dec_picture->chroma_format_idc != YUV444) + { + VideoParameters *p_Vid = currMB->p_Vid; + Slice *currSlice = currMB->p_Slice; + PixelPos pix; + int pred_nnz = 0; + int cnt = 0; + + //YUV420 and YUV422 + // left block + get4x4Neighbour(currMB, ((i&0x01)<<2) - 1, j, p_Vid->mb_size[IS_CHROMA], &pix); + + if ((currMB->is_intra_block == TRUE) && pix.available && p_Vid->active_pps->constrained_intra_pred_flag && (currSlice->dp_mode==PAR_DP_3)) + { + pix.available &= currSlice->intra_block[pix.mb_addr]; + if (!pix.available) + ++cnt; + } + + if (pix.available) + { + pred_nnz = p_Vid->nz_coeff [pix.mb_addr ][1][pix.y][2 * (i>>1) + pix.x]; + ++cnt; + } + + // top block + get4x4Neighbour(currMB, ((i&0x01)<<2), j - 1, p_Vid->mb_size[IS_CHROMA], &pix); + + if ((currMB->is_intra_block == TRUE) && pix.available && p_Vid->active_pps->constrained_intra_pred_flag && (currSlice->dp_mode==PAR_DP_3)) + { + pix.available &= currSlice->intra_block[pix.mb_addr]; + if (!pix.available) + ++cnt; + } + + if (pix.available) + { + pred_nnz += p_Vid->nz_coeff [pix.mb_addr ][1][pix.y][2 * (i>>1) + pix.x]; + ++cnt; + } + + if (cnt==2) + { + ++pred_nnz; + pred_nnz >>= 1; + } + return pred_nnz; + } + else + return 0; +} + +/*! + ************************************************************************ + * \brief + * Reads coeff of an 4x4 block (CAVLC) + * + * \author + * Karl Lillevold + * contributions by James Au + ************************************************************************ + */ +void read_coeff_4x4_CAVLC (Macroblock *currMB, + int block_type, + int i, int j, int levarr[16], int runarr[16], + int *number_coefficients) +{ + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + int mb_nr = currMB->mbAddrX; + SyntaxElement currSE; + DataPartition *dP; + const byte *partMap = assignSE2partition[currSlice->dp_mode]; + Bitstream *currStream; + + int k, code, vlcnum; + int numcoeff = 0, numtrailingones; + int level_two_or_higher; + int numones, totzeros, abslevel, cdc=0, cac=0; + int zerosleft, ntr, dptype = 0; + int max_coeff_num = 0, nnz; + char type[15]; + static const int incVlc[] = {0, 3, 6, 12, 24, 48, 32768}; // maximum vlc = 6 + + switch (block_type) + { + case LUMA: + max_coeff_num = 16; + TRACE_printf("Luma"); + dptype = (currMB->is_intra_block == TRUE) ? SE_LUM_AC_INTRA : SE_LUM_AC_INTER; + p_Vid->nz_coeff[mb_nr][0][j][i] = 0; + break; + case LUMA_INTRA16x16DC: + max_coeff_num = 16; + TRACE_printf("Lum16DC"); + dptype = SE_LUM_DC_INTRA; + p_Vid->nz_coeff[mb_nr][0][j][i] = 0; + break; + case LUMA_INTRA16x16AC: + max_coeff_num = 15; + TRACE_printf("Lum16AC"); + dptype = SE_LUM_AC_INTRA; + p_Vid->nz_coeff[mb_nr][0][j][i] = 0; + break; + case CHROMA_DC: + max_coeff_num = p_Vid->num_cdc_coeff; + cdc = 1; + TRACE_printf("ChrDC"); + dptype = (currMB->is_intra_block == TRUE) ? SE_CHR_DC_INTRA : SE_CHR_DC_INTER; + p_Vid->nz_coeff[mb_nr][0][j][i] = 0; + break; + case CHROMA_AC: + max_coeff_num = 15; + cac = 1; + TRACE_printf("ChrAC"); + dptype = (currMB->is_intra_block == TRUE) ? SE_CHR_AC_INTRA : SE_CHR_AC_INTER; + p_Vid->nz_coeff[mb_nr][0][j][i] = 0; + break; + default: + error ("read_coeff_4x4_CAVLC: invalid block type", 600); + p_Vid->nz_coeff[mb_nr][0][j][i] = 0; + break; + } + + currSE.type = dptype; + dP = &(currSlice->partArr[partMap[dptype]]); + currStream = dP->bitstream; + + if (!cdc) + { + // luma or chroma AC + nnz = (!cac) ? predict_nnz(currMB, LUMA, i<<2, j<<2) : predict_nnz_chroma(currMB, i, ((j-4)<<2)); + + currSE.value1 = (nnz < 2) ? 0 : ((nnz < 4) ? 1 : ((nnz < 8) ? 2 : 3)); + + readSyntaxElement_NumCoeffTrailingOnes(&currSE, currStream, type); + + numcoeff = currSE.value1; + numtrailingones = currSE.value2; + + p_Vid->nz_coeff[mb_nr][0][j][i] = (byte) numcoeff; + } + else + { + // chroma DC + readSyntaxElement_NumCoeffTrailingOnesChromaDC(p_Vid, &currSE, currStream); + + numcoeff = currSE.value1; + numtrailingones = currSE.value2; + } + + memset(levarr, 0, max_coeff_num * sizeof(int)); + memset(runarr, 0, max_coeff_num * sizeof(int)); + + numones = numtrailingones; + *number_coefficients = numcoeff; + + if (numcoeff) + { + if (numtrailingones) + { + currSE.len = numtrailingones; + +#if TRACE + snprintf(currSE.tracestring, + TRACESTRING_SIZE, "%s trailing ones sign (%d,%d)", type, i, j); +#endif + + readSyntaxElement_FLC (&currSE, currStream); + + code = currSE.inf; + ntr = numtrailingones; + for (k = numcoeff - 1; k > numcoeff - 1 - numtrailingones; k--) + { + ntr --; + levarr[k] = (code>>ntr)&1 ? -1 : 1; + } + } + + // decode levels + level_two_or_higher = (numcoeff > 3 && numtrailingones == 3)? 0 : 1; + vlcnum = (numcoeff > 10 && numtrailingones < 3) ? 1 : 0; + + for (k = numcoeff - 1 - numtrailingones; k >= 0; k--) + { + +#if TRACE + snprintf(currSE.tracestring, + TRACESTRING_SIZE, "%s lev (%d,%d) k=%d vlc=%d ", type, i, j, k, vlcnum); +#endif + + if (vlcnum == 0) + readSyntaxElement_Level_VLC0(&currSE, currStream); + else + readSyntaxElement_Level_VLCN(&currSE, vlcnum, currStream); + + if (level_two_or_higher) + { + currSE.inf += (currSE.inf > 0) ? 1 : -1; + level_two_or_higher = 0; + } + + levarr[k] = currSE.inf; + abslevel = iabs(levarr[k]); + if (abslevel == 1) + ++numones; + + // update VLC table + if (abslevel > incVlc[vlcnum]) + ++vlcnum; + + if (k == numcoeff - 1 - numtrailingones && abslevel >3) + vlcnum = 2; + } + + if (numcoeff < max_coeff_num) + { + // decode total run + vlcnum = numcoeff - 1; + currSE.value1 = vlcnum; + +#if TRACE + snprintf(currSE.tracestring, + TRACESTRING_SIZE, "%s totalrun (%d,%d) vlc=%d ", type, i,j, vlcnum); +#endif + if (cdc) + readSyntaxElement_TotalZerosChromaDC(p_Vid, &currSE, currStream); + else + readSyntaxElement_TotalZeros(&currSE, currStream); + + totzeros = currSE.value1; + } + else + { + totzeros = 0; + } + + // decode run before each coefficient + zerosleft = totzeros; + i = numcoeff - 1; + + if (zerosleft > 0 && i > 0) + { + do + { + // select VLC for runbefore + vlcnum = imin(zerosleft - 1, RUNBEFORE_NUM_M1); + + currSE.value1 = vlcnum; +#if TRACE + snprintf(currSE.tracestring, + TRACESTRING_SIZE, "%s run (%d,%d) k=%d vlc=%d ", + type, i, j, i, vlcnum); +#endif + + readSyntaxElement_Run(&currSE, currStream); + runarr[i] = currSE.value1; + + zerosleft -= runarr[i]; + i --; + } while (zerosleft != 0 && i != 0); + } + runarr[i] = zerosleft; + } // if numcoeff +} + +/*! + ************************************************************************ + * \brief + * Reads coeff of an 4x4 block (CAVLC) + * + * \author + * Karl Lillevold + * contributions by James Au + ************************************************************************ + */ +void read_coeff_4x4_CAVLC_444 (Macroblock *currMB, + int block_type, + int i, int j, int levarr[16], int runarr[16], + int *number_coefficients) +{ + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + int mb_nr = currMB->mbAddrX; + SyntaxElement currSE; + DataPartition *dP; + const byte *partMap = assignSE2partition[currSlice->dp_mode]; + Bitstream *currStream; + + int k, code, vlcnum; + int numcoeff = 0, numtrailingones; + int level_two_or_higher; + int numones, totzeros, abslevel, cdc=0, cac=0; + int zerosleft, ntr, dptype = 0; + int max_coeff_num = 0, nnz; + char type[15]; + static const int incVlc[] = {0, 3, 6, 12, 24, 48, 32768}; // maximum vlc = 6 + + switch (block_type) + { + case LUMA: + max_coeff_num = 16; + TRACE_printf("Luma"); + dptype = (currMB->is_intra_block == TRUE) ? SE_LUM_AC_INTRA : SE_LUM_AC_INTER; + p_Vid->nz_coeff[mb_nr][0][j][i] = 0; + break; + case LUMA_INTRA16x16DC: + max_coeff_num = 16; + TRACE_printf("Lum16DC"); + dptype = SE_LUM_DC_INTRA; + p_Vid->nz_coeff[mb_nr][0][j][i] = 0; + break; + case LUMA_INTRA16x16AC: + max_coeff_num = 15; + TRACE_printf("Lum16AC"); + dptype = SE_LUM_AC_INTRA; + p_Vid->nz_coeff[mb_nr][0][j][i] = 0; + break; + case CB: + max_coeff_num = 16; + TRACE_printf("Luma_add1"); + dptype = ((currMB->is_intra_block == TRUE)) ? SE_LUM_AC_INTRA : SE_LUM_AC_INTER; + p_Vid->nz_coeff[mb_nr][1][j][i] = 0; + break; + case CB_INTRA16x16DC: + max_coeff_num = 16; + TRACE_printf("Luma_add1_16DC"); + dptype = SE_LUM_DC_INTRA; + p_Vid->nz_coeff[mb_nr][1][j][i] = 0; + break; + case CB_INTRA16x16AC: + max_coeff_num = 15; + TRACE_printf("Luma_add1_16AC"); + dptype = SE_LUM_AC_INTRA; + p_Vid->nz_coeff[mb_nr][1][j][i] = 0; + break; + case CR: + max_coeff_num = 16; + TRACE_printf("Luma_add2"); + dptype = ((currMB->is_intra_block == TRUE)) ? SE_LUM_AC_INTRA : SE_LUM_AC_INTER; + p_Vid->nz_coeff[mb_nr][2][j][i] = 0; + break; + case CR_INTRA16x16DC: + max_coeff_num = 16; + TRACE_printf("Luma_add2_16DC"); + dptype = SE_LUM_DC_INTRA; + p_Vid->nz_coeff[mb_nr][2][j][i] = 0; + break; + case CR_INTRA16x16AC: + max_coeff_num = 15; + TRACE_printf("Luma_add1_16AC"); + dptype = SE_LUM_AC_INTRA; + p_Vid->nz_coeff[mb_nr][2][j][i] = 0; + break; + case CHROMA_DC: + max_coeff_num = p_Vid->num_cdc_coeff; + cdc = 1; + TRACE_printf("ChrDC"); + dptype = (currMB->is_intra_block == TRUE) ? SE_CHR_DC_INTRA : SE_CHR_DC_INTER; + p_Vid->nz_coeff[mb_nr][0][j][i] = 0; + break; + case CHROMA_AC: + max_coeff_num = 15; + cac = 1; + TRACE_printf("ChrAC"); + dptype = (currMB->is_intra_block == TRUE) ? SE_CHR_AC_INTRA : SE_CHR_AC_INTER; + p_Vid->nz_coeff[mb_nr][0][j][i] = 0; + break; + default: + error ("read_coeff_4x4_CAVLC: invalid block type", 600); + p_Vid->nz_coeff[mb_nr][0][j][i] = 0; + break; + } + + currSE.type = dptype; + dP = &(currSlice->partArr[partMap[dptype]]); + currStream = dP->bitstream; + + if (!cdc) + { + // luma or chroma AC + if(block_type==LUMA || block_type==LUMA_INTRA16x16DC || block_type==LUMA_INTRA16x16AC ||block_type==CHROMA_AC) + { + nnz = (!cac) ? predict_nnz(currMB, LUMA, i<<2, j<<2) : predict_nnz_chroma(currMB, i, ((j-4)<<2)); + } + else if (block_type==CB || block_type==CB_INTRA16x16DC || block_type==CB_INTRA16x16AC) + { + nnz = predict_nnz(currMB, CB, i<<2, j<<2); + } + else + { + nnz = predict_nnz(currMB, CR, i<<2, j<<2); + } + + currSE.value1 = (nnz < 2) ? 0 : ((nnz < 4) ? 1 : ((nnz < 8) ? 2 : 3)); + + readSyntaxElement_NumCoeffTrailingOnes(&currSE, currStream, type); + + numcoeff = currSE.value1; + numtrailingones = currSE.value2; + + if(block_type==LUMA || block_type==LUMA_INTRA16x16DC || block_type==LUMA_INTRA16x16AC ||block_type==CHROMA_AC) + p_Vid->nz_coeff[mb_nr][0][j][i] = (byte) numcoeff; + else if (block_type==CB || block_type==CB_INTRA16x16DC || block_type==CB_INTRA16x16AC) + p_Vid->nz_coeff[mb_nr][1][j][i] = (byte) numcoeff; + else + p_Vid->nz_coeff[mb_nr][2][j][i] = (byte) numcoeff; + } + else + { + // chroma DC + readSyntaxElement_NumCoeffTrailingOnesChromaDC(p_Vid, &currSE, currStream); + + numcoeff = currSE.value1; + numtrailingones = currSE.value2; + } + + memset(levarr, 0, max_coeff_num * sizeof(int)); + memset(runarr, 0, max_coeff_num * sizeof(int)); + + numones = numtrailingones; + *number_coefficients = numcoeff; + + if (numcoeff) + { + if (numtrailingones) + { + currSE.len = numtrailingones; + +#if TRACE + snprintf(currSE.tracestring, + TRACESTRING_SIZE, "%s trailing ones sign (%d,%d)", type, i, j); +#endif + + readSyntaxElement_FLC (&currSE, currStream); + + code = currSE.inf; + ntr = numtrailingones; + for (k = numcoeff - 1; k > numcoeff - 1 - numtrailingones; k--) + { + ntr --; + levarr[k] = (code>>ntr)&1 ? -1 : 1; + } + } + + // decode levels + level_two_or_higher = (numcoeff > 3 && numtrailingones == 3)? 0 : 1; + vlcnum = (numcoeff > 10 && numtrailingones < 3) ? 1 : 0; + + for (k = numcoeff - 1 - numtrailingones; k >= 0; k--) + { + +#if TRACE + snprintf(currSE.tracestring, + TRACESTRING_SIZE, "%s lev (%d,%d) k=%d vlc=%d ", type, i, j, k, vlcnum); +#endif + + if (vlcnum == 0) + readSyntaxElement_Level_VLC0(&currSE, currStream); + else + readSyntaxElement_Level_VLCN(&currSE, vlcnum, currStream); + + if (level_two_or_higher) + { + currSE.inf += (currSE.inf > 0) ? 1 : -1; + level_two_or_higher = 0; + } + + levarr[k] = currSE.inf; + abslevel = iabs(levarr[k]); + if (abslevel == 1) + ++numones; + + // update VLC table + if (abslevel > incVlc[vlcnum]) + ++vlcnum; + + if (k == numcoeff - 1 - numtrailingones && abslevel >3) + vlcnum = 2; + } + + if (numcoeff < max_coeff_num) + { + // decode total run + vlcnum = numcoeff - 1; + currSE.value1 = vlcnum; + +#if TRACE + snprintf(currSE.tracestring, + TRACESTRING_SIZE, "%s totalrun (%d,%d) vlc=%d ", type, i,j, vlcnum); +#endif + if (cdc) + readSyntaxElement_TotalZerosChromaDC(p_Vid, &currSE, currStream); + else + readSyntaxElement_TotalZeros(&currSE, currStream); + + totzeros = currSE.value1; + } + else + { + totzeros = 0; + } + + // decode run before each coefficient + zerosleft = totzeros; + i = numcoeff - 1; + + if (zerosleft > 0 && i > 0) + { + do + { + // select VLC for runbefore + vlcnum = imin(zerosleft - 1, RUNBEFORE_NUM_M1); + + currSE.value1 = vlcnum; +#if TRACE + snprintf(currSE.tracestring, + TRACESTRING_SIZE, "%s run (%d,%d) k=%d vlc=%d ", + type, i, j, i, vlcnum); +#endif + + readSyntaxElement_Run(&currSE, currStream); + runarr[i] = currSE.value1; + + zerosleft -= runarr[i]; + i --; + } while (zerosleft != 0 && i != 0); + } + runarr[i] = zerosleft; + } // if numcoeff +} + +/*! +************************************************************************ +* \brief +* Get coefficients (run/level) of 4x4 blocks in a MB +* from the NAL (CABAC Mode) +************************************************************************ +*/ +static void read_comp_coeff_4x4_CAVLC (Macroblock *currMB, ColorPlane pl, int (*InvLevelScale4x4)[4], int qp_per, int cbp, byte **nzcoeff) +{ + int block_y, block_x, b8; + int i, j, k; + int i0, j0; + int levarr[16] = {0}, runarr[16] = {0}, numcoeff; + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + const byte (*pos_scan4x4)[2] = ((p_Vid->structure == FRAME) && (!currMB->mb_field)) ? SNGL_SCAN : FIELD_SCAN; + const byte *pos_scan_4x4 = pos_scan4x4[0]; + int start_scan = IS_I16MB(currMB) ? 1 : 0; + int64 *cur_cbp = &currMB->s_cbp[pl].blk; + int cur_context; + int block_y4, block_x4; + + if (IS_I16MB(currMB)) + { + if (pl == PLANE_Y) + cur_context = LUMA_INTRA16x16AC; + else if (pl == PLANE_U) + cur_context = CB_INTRA16x16AC; + else + cur_context = CR_INTRA16x16AC; + } + else + { + if (pl == PLANE_Y) + cur_context = LUMA; + else if (pl == PLANE_U) + cur_context = CB; + else + cur_context = CR; + } + + + for (block_y = 0; block_y < 4; block_y += 2) /* all modes */ + { + block_y4 = block_y << 2; + for (block_x = 0; block_x < 4; block_x += 2) + { + block_x4 = block_x << 2; + b8 = (block_y + (block_x >> 1)); + + if (cbp & (1 << b8)) // test if the block contains any coefficients + { + for (j = block_y4; j < block_y4 + 8; j += BLOCK_SIZE) + { + for (i = block_x4; i < block_x4 + 8; i += BLOCK_SIZE) + { + currSlice->read_coeff_4x4_CAVLC(currMB, cur_context, i >> 2, j >> 2, levarr, runarr, &numcoeff); + pos_scan_4x4 = pos_scan4x4[start_scan]; + + for (k = 0; k < numcoeff; ++k) + { + if (levarr[k] != 0) + { + pos_scan_4x4 += (runarr[k] << 1); + + i0 = *pos_scan_4x4++; + j0 = *pos_scan_4x4++; + + // inverse quant for 4x4 transform only + *cur_cbp |= i64_power2(j + (i >> 2)); + + currSlice->cof[pl][j + j0][i + i0]= rshift_rnd_sf((levarr[k] * InvLevelScale4x4[j0][i0])<fcf[pl][j + j0][i + i0]= levarr[k]; + } + } + } + } + } + else + { + nzcoeff[block_y ][block_x ] = 0; + nzcoeff[block_y ][block_x + 1] = 0; + nzcoeff[block_y + 1][block_x ] = 0; + nzcoeff[block_y + 1][block_x + 1] = 0; + } + } + } +} + +/*! +************************************************************************ +* \brief +* Get coefficients (run/level) of 4x4 blocks in a MB +* from the NAL (CAVLC Lossless Mode) +************************************************************************ +*/ +static void read_comp_coeff_4x4_CAVLC_ls (Macroblock *currMB, ColorPlane pl, int (*InvLevelScale4x4)[4], int qp_per, int cbp, byte **nzcoeff) +{ + int block_y, block_x, b8; + int i, j, k; + int i0, j0; + int levarr[16] = {0}, runarr[16] = {0}, numcoeff; + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + const byte (*pos_scan4x4)[2] = ((p_Vid->structure == FRAME) && (!currMB->mb_field)) ? SNGL_SCAN : FIELD_SCAN; + int start_scan = IS_I16MB(currMB) ? 1 : 0; + int64 *cur_cbp = &currMB->s_cbp[pl].blk; + int coef_ctr, cur_context; + + if (IS_I16MB(currMB)) + { + if (pl == PLANE_Y) + cur_context = LUMA_INTRA16x16AC; + else if (pl == PLANE_U) + cur_context = CB_INTRA16x16AC; + else + cur_context = CR_INTRA16x16AC; + } + else + { + if (pl == PLANE_Y) + cur_context = LUMA; + else if (pl == PLANE_U) + cur_context = CB; + else + cur_context = CR; + } + + for (block_y=0; block_y < 4; block_y += 2) /* all modes */ + { + for (block_x=0; block_x < 4; block_x += 2) + { + b8 = 2*(block_y>>1) + (block_x>>1); + + if (cbp & (1<read_coeff_4x4_CAVLC(currMB, cur_context, i, j, levarr, runarr, &numcoeff); + + coef_ctr = start_scan - 1; + + for (k = 0; k < numcoeff; ++k) + { + if (levarr[k] != 0) + { + coef_ctr += runarr[k]+1; + + i0=pos_scan4x4[coef_ctr][0]; + j0=pos_scan4x4[coef_ctr][1]; + + *cur_cbp |= i64_power2((j<<2) + i); + currSlice->cof[pl][(j<<2) + j0][(i<<2) + i0]= levarr[k]; + //currSlice->fcf[pl][(j<<2) + j0][(i<<2) + i0]= levarr[k]; + } + } + } + } + } + else + { + nzcoeff[block_y ][block_x ] = 0; + nzcoeff[block_y ][block_x + 1] = 0; + nzcoeff[block_y + 1][block_x ] = 0; + nzcoeff[block_y + 1][block_x + 1] = 0; + } + } + } +} + +/*! +************************************************************************ +* \brief +* Get coefficients (run/level) of 4x4 blocks in a MB +* from the NAL (CABAC Mode) +************************************************************************ +*/ +static void read_comp_coeff_8x8_CAVLC (Macroblock *currMB, ColorPlane pl, int (*InvLevelScale8x8)[8], int qp_per, int cbp, byte **nzcoeff) +{ + int block_y, block_x, b4, b8; + int block_y4, block_x4; + int i, j, k; + int i0, j0; + int levarr[16] = {0}, runarr[16] = {0}, numcoeff; + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + const byte (*pos_scan8x8)[2] = ((p_Vid->structure == FRAME) && (!currMB->mb_field)) ? SNGL_SCAN8x8 : FIELD_SCAN8x8; + int start_scan = IS_I16MB(currMB) ? 1 : 0; + int64 *cur_cbp = &currMB->s_cbp[pl].blk; + int coef_ctr, cur_context; + + if (IS_I16MB(currMB)) + { + if (pl == PLANE_Y) + cur_context = LUMA_INTRA16x16AC; + else if (pl == PLANE_U) + cur_context = CB_INTRA16x16AC; + else + cur_context = CR_INTRA16x16AC; + } + else + { + if (pl == PLANE_Y) + cur_context = LUMA; + else if (pl == PLANE_U) + cur_context = CB; + else + cur_context = CR; + } + + for (block_y = 0; block_y < 4; block_y += 2) /* all modes */ + { + block_y4 = block_y << 2; + + for (block_x = 0; block_x < 4; block_x += 2) + { + block_x4 = block_x << 2; + b8 = block_y + (block_x>>1); + + if (cbp & (1<read_coeff_4x4_CAVLC(currMB, cur_context, i, j, levarr, runarr, &numcoeff); + + coef_ctr = start_scan - 1; + + for (k = 0; k < numcoeff; ++k) + { + if (levarr[k] != 0) + { + coef_ctr += runarr[k] + 1; + + // do same as CABAC for deblocking: any coeff in the 8x8 marks all the 4x4s + //as containing coefficients + *cur_cbp |= 51 << (block_y4 + block_x); + + b4 = (coef_ctr << 2) + 2*(j - block_y) + (i - block_x); + + i0 = pos_scan8x8[b4][0]; + j0 = pos_scan8x8[b4][1]; + + currSlice->mb_rres[pl][block_y4 +j0][block_x4 +i0] = rshift_rnd_sf((levarr[k] * InvLevelScale8x8[j0][i0])<luma_transform_size_8x8_flag) + } + } + } + else + { + nzcoeff[block_y ][block_x ] = 0; + nzcoeff[block_y ][block_x + 1] = 0; + nzcoeff[block_y + 1][block_x ] = 0; + nzcoeff[block_y + 1][block_x + 1] = 0; + } + } + } +} + +/*! +************************************************************************ +* \brief +* Get coefficients (run/level) of 8x8 blocks in a MB +* from the NAL (CAVLC Lossless Mode) +************************************************************************ +*/ +static void read_comp_coeff_8x8_CAVLC_ls (Macroblock *currMB, ColorPlane pl, int (*InvLevelScale8x8)[8], int qp_per, int cbp, byte **nzcoeff) +{ + int block_y, block_x, b4, b8; + int i, j, k; + int levarr[16] = {0}, runarr[16] = {0}, numcoeff; + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + const byte (*pos_scan8x8)[2] = ((p_Vid->structure == FRAME) && (!currMB->mb_field)) ? SNGL_SCAN8x8 : FIELD_SCAN8x8; + int start_scan = IS_I16MB(currMB) ? 1 : 0; + int64 *cur_cbp = &currMB->s_cbp[pl].blk; + int coef_ctr, cur_context; + + if (IS_I16MB(currMB)) + { + if (pl == PLANE_Y) + cur_context = LUMA_INTRA16x16AC; + else if (pl == PLANE_U) + cur_context = CB_INTRA16x16AC; + else + cur_context = CR_INTRA16x16AC; + } + else + { + if (pl == PLANE_Y) + cur_context = LUMA; + else if (pl == PLANE_U) + cur_context = CB; + else + cur_context = CR; + } + + for (block_y=0; block_y < 4; block_y += 2) /* all modes */ + { + for (block_x=0; block_x < 4; block_x += 2) + { + b8 = 2*(block_y>>1) + (block_x>>1); + + if (cbp & (1<read_coeff_4x4_CAVLC(currMB, cur_context, i, j, levarr, runarr, &numcoeff); + + coef_ctr = start_scan - 1; + + for (k = 0; k < numcoeff; ++k) + { + if (levarr[k] != 0) + { + coef_ctr += runarr[k]+1; + + // do same as CABAC for deblocking: any coeff in the 8x8 marks all the 4x4s + //as containing coefficients + *cur_cbp |= 51 << ((block_y<<2) + block_x); + + b4 = 2*(j-block_y)+(i-block_x); + + iz=pos_scan8x8[coef_ctr*4+b4][0]; + jz=pos_scan8x8[coef_ctr*4+b4][1]; + + currSlice->mb_rres[pl][block_y*4 +jz][block_x*4 +iz] = levarr[k]; + } + } + } + } + } + else + { + nzcoeff[block_y ][block_x ] = 0; + nzcoeff[block_y ][block_x + 1] = 0; + nzcoeff[block_y + 1][block_x ] = 0; + nzcoeff[block_y + 1][block_x + 1] = 0; + } + } + } +} + +/*! + ************************************************************************ + * \brief + * Get coded block pattern and coefficients (run/level) + * from the NAL + ************************************************************************ + */ +static void read_CBP_and_coeffs_from_NAL_CAVLC_400(Macroblock *currMB) +{ + int k; + int mb_nr = currMB->mbAddrX; + int cbp; + SyntaxElement currSE; + DataPartition *dP = NULL; + Slice *currSlice = currMB->p_Slice; + const byte *partMap = assignSE2partition[currSlice->dp_mode]; + int i0, j0; + + int levarr[16], runarr[16], numcoeff; + + int qp_per, qp_rem; + VideoParameters *p_Vid = currMB->p_Vid; + + int intra = (currMB->is_intra_block == TRUE); + + int need_transform_size_flag; + + int (*InvLevelScale4x4)[4] = NULL; + int (*InvLevelScale8x8)[8] = NULL; + // select scan type + const byte (*pos_scan4x4)[2] = ((p_Vid->structure == FRAME) && (!currMB->mb_field)) ? SNGL_SCAN : FIELD_SCAN; + const byte *pos_scan_4x4 = pos_scan4x4[0]; + + + // read CBP if not new intra mode + if (!IS_I16MB (currMB)) + { + //===== C B P ===== + //--------------------- + currSE.type = (currMB->mb_type == I4MB || currMB->mb_type == SI4MB || currMB->mb_type == I8MB) + ? SE_CBP_INTRA + : SE_CBP_INTER; + + dP = &(currSlice->partArr[partMap[currSE.type]]); + + currSE.mapping = (currMB->mb_type == I4MB || currMB->mb_type == SI4MB || currMB->mb_type == I8MB) + ? currSlice->linfo_cbp_intra + : currSlice->linfo_cbp_inter; + + TRACE_STRING("coded_block_pattern"); + dP->readSyntaxElement(currMB, &currSE, dP); + currMB->cbp = cbp = currSE.value1; + + + //============= Transform size flag for INTER MBs ============= + //------------------------------------------------------------- + need_transform_size_flag = (((currMB->mb_type >= 1 && currMB->mb_type <= 3)|| + (IS_DIRECT(currMB) && p_Vid->active_sps->direct_8x8_inference_flag) || + (currMB->NoMbPartLessThan8x8Flag)) + && currMB->mb_type != I8MB && currMB->mb_type != I4MB + && (currMB->cbp&15) + && currSlice->Transform8x8Mode); + + if (need_transform_size_flag) + { + currSE.type = SE_HEADER; + dP = &(currSlice->partArr[partMap[SE_HEADER]]); + TRACE_STRING("transform_size_8x8_flag"); + + // read CAVLC transform_size_8x8_flag + currSE.len = 1; + readSyntaxElement_FLC(&currSE, dP->bitstream); + + currMB->luma_transform_size_8x8_flag = (Boolean) currSE.value1; + } + + //===== DQUANT ===== + //---------------------- + // Delta quant only if nonzero coeffs + if (cbp !=0) + { + read_delta_quant(&currSE, dP, currMB, partMap, ((currMB->is_intra_block == FALSE)) ? SE_DELTA_QUANT_INTER : SE_DELTA_QUANT_INTRA); + + if (currSlice->dp_mode) + { + if ((currMB->is_intra_block == FALSE) && currSlice->dpC_NotPresent ) + currMB->dpl_flag = 1; + + if( intra && currSlice->dpB_NotPresent ) + { + currMB->ei_flag = 1; + currMB->dpl_flag = 1; + } + + // check for prediction from neighbours + check_dp_neighbors (currMB); + if (currMB->dpl_flag) + { + cbp = 0; + currMB->cbp = cbp; + } + } + } + } + else // read DC coeffs for new intra modes + { + cbp = currMB->cbp; + + read_delta_quant(&currSE, dP, currMB, partMap, SE_DELTA_QUANT_INTRA); + + if (currSlice->dp_mode) + { + if (currSlice->dpB_NotPresent) + { + currMB->ei_flag = 1; + currMB->dpl_flag = 1; + } + check_dp_neighbors (currMB); + if (currMB->dpl_flag) + { + currMB->cbp = cbp = 0; + } + } + + if (!currMB->dpl_flag) + { + pos_scan_4x4 = pos_scan4x4[0]; + + currSlice->read_coeff_4x4_CAVLC(currMB, LUMA_INTRA16x16DC, 0, 0, levarr, runarr, &numcoeff); + + for(k = 0; k < numcoeff; ++k) + { + if (levarr[k] != 0) // leave if level == 0 + { + pos_scan_4x4 += 2 * runarr[k]; + + i0 = ((*pos_scan_4x4++) << 2); + j0 = ((*pos_scan_4x4++) << 2); + + currSlice->cof[0][j0][i0] = levarr[k];// add new intra DC coeff + //currSlice->fcf[0][j0][i0] = levarr[k];// add new intra DC coeff + } + } + + + if(currMB->is_lossless == FALSE) + itrans_2(currMB, (ColorPlane) currSlice->colour_plane_id);// transform new intra DC + } + } + + update_qp(currMB, currSlice->qp); + + qp_per = p_Vid->qp_per_matrix[ currMB->qp_scaled[PLANE_Y] ]; + qp_rem = p_Vid->qp_rem_matrix[ currMB->qp_scaled[PLANE_Y] ]; + + InvLevelScale4x4 = intra? currSlice->InvLevelScale4x4_Intra[currSlice->colour_plane_id][qp_rem] : currSlice->InvLevelScale4x4_Inter[currSlice->colour_plane_id][qp_rem]; + InvLevelScale8x8 = intra? currSlice->InvLevelScale8x8_Intra[currSlice->colour_plane_id][qp_rem] : currSlice->InvLevelScale8x8_Inter[currSlice->colour_plane_id][qp_rem]; + + // luma coefficients + if (cbp) + { + if (!currMB->luma_transform_size_8x8_flag) // 4x4 transform + { + currMB->read_comp_coeff_4x4_CAVLC (currMB, PLANE_Y, InvLevelScale4x4, qp_per, cbp, p_Vid->nz_coeff[mb_nr][PLANE_Y]); + } + else // 8x8 transform + { + currMB->read_comp_coeff_8x8_CAVLC (currMB, PLANE_Y, InvLevelScale8x8, qp_per, cbp, p_Vid->nz_coeff[mb_nr][PLANE_Y]); + } + } + else + { + fast_memset(p_Vid->nz_coeff[mb_nr][0][0], 0, BLOCK_PIXELS * sizeof(byte)); + } +} + +/*! + ************************************************************************ + * \brief + * Get coded block pattern and coefficients (run/level) + * from the NAL + ************************************************************************ + */ +static void read_CBP_and_coeffs_from_NAL_CAVLC_422(Macroblock *currMB) +{ + int i,j,k; + int mb_nr = currMB->mbAddrX; + int cbp; + SyntaxElement currSE; + DataPartition *dP = NULL; + Slice *currSlice = currMB->p_Slice; + const byte *partMap = assignSE2partition[currSlice->dp_mode]; + int coef_ctr, i0, j0, b8; + int ll; + int levarr[16], runarr[16], numcoeff; + + int qp_per, qp_rem; + VideoParameters *p_Vid = currMB->p_Vid; + + int uv; + int qp_per_uv[2]; + int qp_rem_uv[2]; + + int intra = (currMB->is_intra_block == TRUE); + + int b4; + //StorablePicture *dec_picture = currSlice->dec_picture; + int m6[4]; + + int need_transform_size_flag; + + int (*InvLevelScale4x4)[4] = NULL; + int (*InvLevelScale8x8)[8] = NULL; + // select scan type + const byte (*pos_scan4x4)[2] = ((p_Vid->structure == FRAME) && (!currMB->mb_field)) ? SNGL_SCAN : FIELD_SCAN; + const byte *pos_scan_4x4 = pos_scan4x4[0]; + + + // read CBP if not new intra mode + if (!IS_I16MB (currMB)) + { + //===== C B P ===== + //--------------------- + currSE.type = (currMB->mb_type == I4MB || currMB->mb_type == SI4MB || currMB->mb_type == I8MB) + ? SE_CBP_INTRA + : SE_CBP_INTER; + + dP = &(currSlice->partArr[partMap[currSE.type]]); + + currSE.mapping = (currMB->mb_type == I4MB || currMB->mb_type == SI4MB || currMB->mb_type == I8MB) + ? currSlice->linfo_cbp_intra + : currSlice->linfo_cbp_inter; + + TRACE_STRING("coded_block_pattern"); + dP->readSyntaxElement(currMB, &currSE, dP); + currMB->cbp = cbp = currSE.value1; + + + //============= Transform size flag for INTER MBs ============= + //------------------------------------------------------------- + need_transform_size_flag = (((currMB->mb_type >= 1 && currMB->mb_type <= 3)|| + (IS_DIRECT(currMB) && p_Vid->active_sps->direct_8x8_inference_flag) || + (currMB->NoMbPartLessThan8x8Flag)) + && currMB->mb_type != I8MB && currMB->mb_type != I4MB + && (currMB->cbp&15) + && currSlice->Transform8x8Mode); + + if (need_transform_size_flag) + { + currSE.type = SE_HEADER; + dP = &(currSlice->partArr[partMap[SE_HEADER]]); + TRACE_STRING("transform_size_8x8_flag"); + + // read CAVLC transform_size_8x8_flag + currSE.len = 1; + readSyntaxElement_FLC(&currSE, dP->bitstream); + + currMB->luma_transform_size_8x8_flag = (Boolean) currSE.value1; + } + + //===== DQUANT ===== + //---------------------- + // Delta quant only if nonzero coeffs + if (cbp !=0) + { + read_delta_quant(&currSE, dP, currMB, partMap, ((currMB->is_intra_block == FALSE)) ? SE_DELTA_QUANT_INTER : SE_DELTA_QUANT_INTRA); + + if (currSlice->dp_mode) + { + if ((currMB->is_intra_block == FALSE) && currSlice->dpC_NotPresent ) + currMB->dpl_flag = 1; + + if( intra && currSlice->dpB_NotPresent ) + { + currMB->ei_flag = 1; + currMB->dpl_flag = 1; + } + + // check for prediction from neighbours + check_dp_neighbors (currMB); + if (currMB->dpl_flag) + { + cbp = 0; + currMB->cbp = cbp; + } + } + } + } + else // read DC coeffs for new intra modes + { + cbp = currMB->cbp; + + read_delta_quant(&currSE, dP, currMB, partMap, SE_DELTA_QUANT_INTRA); + + if (currSlice->dp_mode) + { + if (currSlice->dpB_NotPresent) + { + currMB->ei_flag = 1; + currMB->dpl_flag = 1; + } + check_dp_neighbors (currMB); + if (currMB->dpl_flag) + { + currMB->cbp = cbp = 0; + } + } + + if (!currMB->dpl_flag) + { + pos_scan_4x4 = pos_scan4x4[0]; + + currSlice->read_coeff_4x4_CAVLC(currMB, LUMA_INTRA16x16DC, 0, 0, levarr, runarr, &numcoeff); + + for(k = 0; k < numcoeff; ++k) + { + if (levarr[k] != 0) // leave if level == 0 + { + pos_scan_4x4 += 2 * runarr[k]; + + i0 = ((*pos_scan_4x4++) << 2); + j0 = ((*pos_scan_4x4++) << 2); + + currSlice->cof[0][j0][i0] = levarr[k];// add new intra DC coeff + //currSlice->fcf[0][j0][i0] = levarr[k];// add new intra DC coeff + } + } + + + if(currMB->is_lossless == FALSE) + itrans_2(currMB, (ColorPlane) currSlice->colour_plane_id);// transform new intra DC + } + } + + update_qp(currMB, currSlice->qp); + + qp_per = p_Vid->qp_per_matrix[ currMB->qp_scaled[currSlice->colour_plane_id] ]; + qp_rem = p_Vid->qp_rem_matrix[ currMB->qp_scaled[currSlice->colour_plane_id] ]; + + //init quant parameters for chroma + for(i=0; i < 2; ++i) + { + qp_per_uv[i] = p_Vid->qp_per_matrix[ currMB->qp_scaled[i + 1] ]; + qp_rem_uv[i] = p_Vid->qp_rem_matrix[ currMB->qp_scaled[i + 1] ]; + } + + InvLevelScale4x4 = intra? currSlice->InvLevelScale4x4_Intra[currSlice->colour_plane_id][qp_rem] : currSlice->InvLevelScale4x4_Inter[currSlice->colour_plane_id][qp_rem]; + InvLevelScale8x8 = intra? currSlice->InvLevelScale8x8_Intra[currSlice->colour_plane_id][qp_rem] : currSlice->InvLevelScale8x8_Inter[currSlice->colour_plane_id][qp_rem]; + + // luma coefficients + if (cbp) + { + if (!currMB->luma_transform_size_8x8_flag) // 4x4 transform + { + currMB->read_comp_coeff_4x4_CAVLC (currMB, PLANE_Y, InvLevelScale4x4, qp_per, cbp, p_Vid->nz_coeff[mb_nr][PLANE_Y]); + } + else // 8x8 transform + { + currMB->read_comp_coeff_8x8_CAVLC (currMB, PLANE_Y, InvLevelScale8x8, qp_per, cbp, p_Vid->nz_coeff[mb_nr][PLANE_Y]); + } + } + else + { + fast_memset(p_Vid->nz_coeff[mb_nr][0][0], 0, BLOCK_PIXELS * sizeof(byte)); + } + + //========================== CHROMA DC ============================ + //----------------------------------------------------------------- + // chroma DC coeff + if(cbp>15) + { + for (ll=0;ll<3;ll+=2) + { + int (*InvLevelScale4x4)[4] = NULL; + uv = ll>>1; + { + int **imgcof = currSlice->cof[PLANE_U + uv]; + int m3[2][4] = {{0,0,0,0},{0,0,0,0}}; + int m4[2][4] = {{0,0,0,0},{0,0,0,0}}; + int qp_per_uv_dc = p_Vid->qp_per_matrix[ (currMB->qpc[uv] + 3 + p_Vid->bitdepth_chroma_qp_scale) ]; //for YUV422 only + int qp_rem_uv_dc = p_Vid->qp_rem_matrix[ (currMB->qpc[uv] + 3 + p_Vid->bitdepth_chroma_qp_scale) ]; //for YUV422 only + if (intra) + InvLevelScale4x4 = currSlice->InvLevelScale4x4_Intra[PLANE_U + uv][qp_rem_uv_dc]; + else + InvLevelScale4x4 = currSlice->InvLevelScale4x4_Inter[PLANE_U + uv][qp_rem_uv_dc]; + + + //===================== CHROMA DC YUV422 ====================== + currSlice->read_coeff_4x4_CAVLC(currMB, CHROMA_DC, 0, 0, levarr, runarr, &numcoeff); + coef_ctr=-1; + for(k = 0; k < numcoeff; ++k) + { + if (levarr[k] != 0) + { + currMB->s_cbp[0].blk |= ((int64)0xff0000) << (ll<<2); + coef_ctr += runarr[k]+1; + i0 = SCAN_YUV422[coef_ctr][0]; + j0 = SCAN_YUV422[coef_ctr][1]; + + m3[i0][j0]=levarr[k]; + } + } + + // inverse CHROMA DC YUV422 transform + // horizontal + if(currMB->is_lossless == FALSE) + { + m4[0][0] = m3[0][0] + m3[1][0]; + m4[0][1] = m3[0][1] + m3[1][1]; + m4[0][2] = m3[0][2] + m3[1][2]; + m4[0][3] = m3[0][3] + m3[1][3]; + + m4[1][0] = m3[0][0] - m3[1][0]; + m4[1][1] = m3[0][1] - m3[1][1]; + m4[1][2] = m3[0][2] - m3[1][2]; + m4[1][3] = m3[0][3] - m3[1][3]; + + for (i = 0; i < 2; ++i) + { + m6[0] = m4[i][0] + m4[i][2]; + m6[1] = m4[i][0] - m4[i][2]; + m6[2] = m4[i][1] - m4[i][3]; + m6[3] = m4[i][1] + m4[i][3]; + + imgcof[ 0][i<<2] = m6[0] + m6[3]; + imgcof[ 4][i<<2] = m6[1] + m6[2]; + imgcof[ 8][i<<2] = m6[1] - m6[2]; + imgcof[12][i<<2] = m6[0] - m6[3]; + }//for (i=0;i<2;++i) + + for(j = 0;j < p_Vid->mb_cr_size_y; j += BLOCK_SIZE) + { + for(i=0;i < p_Vid->mb_cr_size_x;i+=BLOCK_SIZE) + { + imgcof[j][i] = rshift_rnd_sf((imgcof[j][i] * InvLevelScale4x4[0][0]) << qp_per_uv_dc, 6); + } + } + } + else + { + for(j=0;j<4;++j) + { + currSlice->cof[PLANE_U + uv][j<<2][0] = m3[0][j]; + currSlice->cof[PLANE_U + uv][j<<2][4] = m3[1][j]; + } + } + + } + }//for (ll=0;ll<3;ll+=2) + } + + //========================== CHROMA AC ============================ + //----------------------------------------------------------------- + // chroma AC coeff, all zero fram start_scan + if (cbp<=31) + { + fast_memset(p_Vid->nz_coeff [mb_nr ][1][0], 0, 2 * BLOCK_PIXELS * sizeof(byte)); + } + else + { + if(currMB->is_lossless == FALSE) + { + for (b8=0; b8 < p_Vid->num_blk8x8_uv; ++b8) + { + currMB->is_v_block = uv = (b8 > ((p_Vid->num_uv_blocks) - 1 )); + InvLevelScale4x4 = intra ? currSlice->InvLevelScale4x4_Intra[PLANE_U + uv][qp_rem_uv[uv]] : currSlice->InvLevelScale4x4_Inter[PLANE_U + uv][qp_rem_uv[uv]]; + + for (b4=0; b4 < 4; ++b4) + { + i = cofuv_blk_x[1][b8][b4]; + j = cofuv_blk_y[1][b8][b4]; + + currSlice->read_coeff_4x4_CAVLC(currMB, CHROMA_AC, i + 2*uv, j + 4, levarr, runarr, &numcoeff); + coef_ctr = 0; + + for(k = 0; k < numcoeff;++k) + { + if (levarr[k] != 0) + { + currMB->s_cbp[0].blk |= i64_power2(cbp_blk_chroma[b8][b4]); + coef_ctr += runarr[k] + 1; + + i0=pos_scan4x4[coef_ctr][0]; + j0=pos_scan4x4[coef_ctr][1]; + + currSlice->cof[PLANE_U + uv][(j<<2) + j0][(i<<2) + i0] = rshift_rnd_sf((levarr[k] * InvLevelScale4x4[j0][i0])<fcf[PLANE_U + uv][(j<<2) + j0][(i<<2) + i0] = levarr[k]; + } + } + } + } + } + else + { + for (b8=0; b8 < p_Vid->num_blk8x8_uv; ++b8) + { + currMB->is_v_block = uv = (b8 > ((p_Vid->num_uv_blocks) - 1 )); + + for (b4=0; b4 < 4; ++b4) + { + i = cofuv_blk_x[1][b8][b4]; + j = cofuv_blk_y[1][b8][b4]; + + currSlice->read_coeff_4x4_CAVLC(currMB, CHROMA_AC, i + 2*uv, j + 4, levarr, runarr, &numcoeff); + coef_ctr = 0; + + for(k = 0; k < numcoeff;++k) + { + if (levarr[k] != 0) + { + currMB->s_cbp[0].blk |= i64_power2(cbp_blk_chroma[b8][b4]); + coef_ctr += runarr[k] + 1; + + i0=pos_scan4x4[coef_ctr][0]; + j0=pos_scan4x4[coef_ctr][1]; + + currSlice->cof[PLANE_U + uv][(j<<2) + j0][(i<<2) + i0] = levarr[k]; + } + } + } + } + } + } //if (dec_picture->chroma_format_idc != YUV400) +} + +/*! + ************************************************************************ + * \brief + * Get coded block pattern and coefficients (run/level) + * from the NAL + ************************************************************************ + */ +static void read_CBP_and_coeffs_from_NAL_CAVLC_444(Macroblock *currMB) +{ + int i,k; + int mb_nr = currMB->mbAddrX; + int cbp; + SyntaxElement currSE; + DataPartition *dP = NULL; + Slice *currSlice = currMB->p_Slice; + const byte *partMap = assignSE2partition[currSlice->dp_mode]; + int coef_ctr, i0, j0; + int levarr[16], runarr[16], numcoeff; + + int qp_per, qp_rem; + VideoParameters *p_Vid = currMB->p_Vid; + + int uv; + int qp_per_uv[3]; + int qp_rem_uv[3]; + + int intra = (currMB->is_intra_block == TRUE); + + int need_transform_size_flag; + + int (*InvLevelScale4x4)[4] = NULL; + int (*InvLevelScale8x8)[8] = NULL; + // select scan type + const byte (*pos_scan4x4)[2] = ((p_Vid->structure == FRAME) && (!currMB->mb_field)) ? SNGL_SCAN : FIELD_SCAN; + const byte *pos_scan_4x4 = pos_scan4x4[0]; + + // read CBP if not new intra mode + if (!IS_I16MB (currMB)) + { + //===== C B P ===== + //--------------------- + currSE.type = (currMB->mb_type == I4MB || currMB->mb_type == SI4MB || currMB->mb_type == I8MB) + ? SE_CBP_INTRA + : SE_CBP_INTER; + + dP = &(currSlice->partArr[partMap[currSE.type]]); + + currSE.mapping = (currMB->mb_type == I4MB || currMB->mb_type == SI4MB || currMB->mb_type == I8MB) + ? currSlice->linfo_cbp_intra + : currSlice->linfo_cbp_inter; + + TRACE_STRING("coded_block_pattern"); + dP->readSyntaxElement(currMB, &currSE, dP); + currMB->cbp = cbp = currSE.value1; + + + //============= Transform size flag for INTER MBs ============= + //------------------------------------------------------------- + need_transform_size_flag = (((currMB->mb_type >= 1 && currMB->mb_type <= 3)|| + (IS_DIRECT(currMB) && p_Vid->active_sps->direct_8x8_inference_flag) || + (currMB->NoMbPartLessThan8x8Flag)) + && currMB->mb_type != I8MB && currMB->mb_type != I4MB + && (currMB->cbp&15) + && currSlice->Transform8x8Mode); + + if (need_transform_size_flag) + { + currSE.type = SE_HEADER; + dP = &(currSlice->partArr[partMap[SE_HEADER]]); + TRACE_STRING("transform_size_8x8_flag"); + + // read CAVLC transform_size_8x8_flag + currSE.len = 1; + readSyntaxElement_FLC(&currSE, dP->bitstream); + + currMB->luma_transform_size_8x8_flag = (Boolean) currSE.value1; + } + + //===== DQUANT ===== + //---------------------- + // Delta quant only if nonzero coeffs + if (cbp !=0) + { + read_delta_quant(&currSE, dP, currMB, partMap, ((currMB->is_intra_block == FALSE)) ? SE_DELTA_QUANT_INTER : SE_DELTA_QUANT_INTRA); + + if (currSlice->dp_mode) + { + if ((currMB->is_intra_block == FALSE) && currSlice->dpC_NotPresent ) + currMB->dpl_flag = 1; + + if( intra && currSlice->dpB_NotPresent ) + { + currMB->ei_flag = 1; + currMB->dpl_flag = 1; + } + + // check for prediction from neighbours + check_dp_neighbors (currMB); + if (currMB->dpl_flag) + { + cbp = 0; + currMB->cbp = cbp; + } + } + } + } + else // read DC coeffs for new intra modes + { + cbp = currMB->cbp; + + read_delta_quant(&currSE, dP, currMB, partMap, SE_DELTA_QUANT_INTRA); + + if (currSlice->dp_mode) + { + if (currSlice->dpB_NotPresent) + { + currMB->ei_flag = 1; + currMB->dpl_flag = 1; + } + check_dp_neighbors (currMB); + if (currMB->dpl_flag) + { + currMB->cbp = cbp = 0; + } + } + + if (!currMB->dpl_flag) + { + pos_scan_4x4 = pos_scan4x4[0]; + + currSlice->read_coeff_4x4_CAVLC(currMB, LUMA_INTRA16x16DC, 0, 0, levarr, runarr, &numcoeff); + + for(k = 0; k < numcoeff; ++k) + { + if (levarr[k] != 0) // leave if level == 0 + { + pos_scan_4x4 += 2 * runarr[k]; + + i0 = ((*pos_scan_4x4++) << 2); + j0 = ((*pos_scan_4x4++) << 2); + + currSlice->cof[0][j0][i0] = levarr[k];// add new intra DC coeff + //currSlice->fcf[0][j0][i0] = levarr[k];// add new intra DC coeff + } + } + + + if(currMB->is_lossless == FALSE) + itrans_2(currMB, (ColorPlane) currSlice->colour_plane_id);// transform new intra DC + } + } + + update_qp(currMB, currSlice->qp); + + qp_per = p_Vid->qp_per_matrix[ currMB->qp_scaled[currSlice->colour_plane_id] ]; + qp_rem = p_Vid->qp_rem_matrix[ currMB->qp_scaled[currSlice->colour_plane_id] ]; + + //init quant parameters for chroma + for(i=PLANE_U; i <= PLANE_V; ++i) + { + qp_per_uv[i] = p_Vid->qp_per_matrix[ currMB->qp_scaled[i] ]; + qp_rem_uv[i] = p_Vid->qp_rem_matrix[ currMB->qp_scaled[i] ]; + } + + InvLevelScale4x4 = intra? currSlice->InvLevelScale4x4_Intra[currSlice->colour_plane_id][qp_rem] : currSlice->InvLevelScale4x4_Inter[currSlice->colour_plane_id][qp_rem]; + InvLevelScale8x8 = intra? currSlice->InvLevelScale8x8_Intra[currSlice->colour_plane_id][qp_rem] : currSlice->InvLevelScale8x8_Inter[currSlice->colour_plane_id][qp_rem]; + + // luma coefficients + if (cbp) + { + if (!currMB->luma_transform_size_8x8_flag) // 4x4 transform + { + currMB->read_comp_coeff_4x4_CAVLC (currMB, PLANE_Y, InvLevelScale4x4, qp_per, cbp, p_Vid->nz_coeff[mb_nr][PLANE_Y]); + } + else // 8x8 transform + { + currMB->read_comp_coeff_8x8_CAVLC (currMB, PLANE_Y, InvLevelScale8x8, qp_per, cbp, p_Vid->nz_coeff[mb_nr][PLANE_Y]); + } + } + else + { + fast_memset(p_Vid->nz_coeff[mb_nr][0][0], 0, BLOCK_PIXELS * sizeof(byte)); + } + + for (uv = PLANE_U; uv <= PLANE_V; ++uv ) + { + /*----------------------16x16DC Luma_Add----------------------*/ + if (IS_I16MB (currMB)) // read DC coeffs for new intra modes + { + if (uv == PLANE_U) + currSlice->read_coeff_4x4_CAVLC(currMB, CB_INTRA16x16DC, 0, 0, levarr, runarr, &numcoeff); + else + currSlice->read_coeff_4x4_CAVLC(currMB, CR_INTRA16x16DC, 0, 0, levarr, runarr, &numcoeff); + + coef_ctr=-1; + + for(k = 0; k < numcoeff; ++k) + { + if (levarr[k] != 0) // leave if level == 0 + { + coef_ctr += runarr[k] + 1; + + i0 = pos_scan4x4[coef_ctr][0]; + j0 = pos_scan4x4[coef_ctr][1]; + currSlice->cof[uv][j0<<2][i0<<2] = levarr[k];// add new intra DC coeff + //currSlice->fcf[uv][j0<<2][i0<<2] = levarr[k];// add new intra DC coeff + } //if leavarr[k] + } //k loop + + if(currMB->is_lossless == FALSE) + { + itrans_2(currMB, (ColorPlane) (uv)); // transform new intra DC + } + } //IS_I16MB + + update_qp(currMB, currSlice->qp); + + //init constants for every chroma qp offset + qp_per_uv[uv] = p_Vid->qp_per_matrix[ currMB->qp_scaled[uv] ]; + qp_rem_uv[uv] = p_Vid->qp_rem_matrix[ currMB->qp_scaled[uv] ]; + + InvLevelScale4x4 = intra? currSlice->InvLevelScale4x4_Intra[uv][qp_rem_uv[uv]] : currSlice->InvLevelScale4x4_Inter[uv][qp_rem_uv[uv]]; + InvLevelScale8x8 = intra? currSlice->InvLevelScale8x8_Intra[uv][qp_rem_uv[uv]] : currSlice->InvLevelScale8x8_Inter[uv][qp_rem_uv[uv]]; + + if (!currMB->luma_transform_size_8x8_flag) // 4x4 transform + { + currMB->read_comp_coeff_4x4_CAVLC (currMB, (ColorPlane) (uv), InvLevelScale4x4, qp_per_uv[uv], cbp, p_Vid->nz_coeff[mb_nr][uv]); + } + else // 8x8 transform + { + currMB->read_comp_coeff_8x8_CAVLC (currMB, (ColorPlane) (uv), InvLevelScale8x8, qp_per_uv[uv], cbp, p_Vid->nz_coeff[mb_nr][uv]); + } + } +} + +/*! + ************************************************************************ + * \brief + * Get coded block pattern and coefficients (run/level) + * from the NAL + ************************************************************************ + */ +static void read_CBP_and_coeffs_from_NAL_CAVLC_420(Macroblock *currMB) +{ + int i,j,k; + int mb_nr = currMB->mbAddrX; + int cbp; + SyntaxElement currSE; + DataPartition *dP = NULL; + Slice *currSlice = currMB->p_Slice; + const byte *partMap = assignSE2partition[currSlice->dp_mode]; + int coef_ctr, i0, j0, b8; + int ll; + int levarr[16], runarr[16], numcoeff; + + int qp_per, qp_rem; + VideoParameters *p_Vid = currMB->p_Vid; + int smb = ((p_Vid->type==SP_SLICE) && (currMB->is_intra_block == FALSE)) || (p_Vid->type == SI_SLICE && currMB->mb_type == SI4MB); + + int uv; + int qp_per_uv[2]; + int qp_rem_uv[2]; + + int intra = (currMB->is_intra_block == TRUE); + int temp[4]; + + int b4; + //StorablePicture *dec_picture = currSlice->dec_picture; + + int need_transform_size_flag; + + int (*InvLevelScale4x4)[4] = NULL; + int (*InvLevelScale8x8)[8] = NULL; + // select scan type + const byte (*pos_scan4x4)[2] = ((p_Vid->structure == FRAME) && (!currMB->mb_field)) ? SNGL_SCAN : FIELD_SCAN; + const byte *pos_scan_4x4 = pos_scan4x4[0]; + + // read CBP if not new intra mode + if (!IS_I16MB (currMB)) + { + //===== C B P ===== + //--------------------- + currSE.type = (currMB->mb_type == I4MB || currMB->mb_type == SI4MB || currMB->mb_type == I8MB) + ? SE_CBP_INTRA + : SE_CBP_INTER; + + dP = &(currSlice->partArr[partMap[currSE.type]]); + + currSE.mapping = (currMB->mb_type == I4MB || currMB->mb_type == SI4MB || currMB->mb_type == I8MB) + ? currSlice->linfo_cbp_intra + : currSlice->linfo_cbp_inter; + + TRACE_STRING("coded_block_pattern"); + dP->readSyntaxElement(currMB, &currSE, dP); + currMB->cbp = cbp = currSE.value1; + + //============= Transform size flag for INTER MBs ============= + //------------------------------------------------------------- + need_transform_size_flag = (((currMB->mb_type >= 1 && currMB->mb_type <= 3)|| + (IS_DIRECT(currMB) && p_Vid->active_sps->direct_8x8_inference_flag) || + (currMB->NoMbPartLessThan8x8Flag)) + && currMB->mb_type != I8MB && currMB->mb_type != I4MB + && (currMB->cbp&15) + && currSlice->Transform8x8Mode); + + if (need_transform_size_flag) + { + currSE.type = SE_HEADER; + dP = &(currSlice->partArr[partMap[SE_HEADER]]); + TRACE_STRING("transform_size_8x8_flag"); + + // read CAVLC transform_size_8x8_flag + currSE.len = 1; + readSyntaxElement_FLC(&currSE, dP->bitstream); + + currMB->luma_transform_size_8x8_flag = (Boolean) currSE.value1; + } + + //===== DQUANT ===== + //---------------------- + // Delta quant only if nonzero coeffs + if (cbp !=0) + { + read_delta_quant(&currSE, dP, currMB, partMap, ((currMB->is_intra_block == FALSE)) ? SE_DELTA_QUANT_INTER : SE_DELTA_QUANT_INTRA); + + if (currSlice->dp_mode) + { + if ((currMB->is_intra_block == FALSE) && currSlice->dpC_NotPresent ) + currMB->dpl_flag = 1; + + if( intra && currSlice->dpB_NotPresent ) + { + currMB->ei_flag = 1; + currMB->dpl_flag = 1; + } + + // check for prediction from neighbours + check_dp_neighbors (currMB); + if (currMB->dpl_flag) + { + cbp = 0; + currMB->cbp = cbp; + } + } + } + } + else + { + cbp = currMB->cbp; + read_delta_quant(&currSE, dP, currMB, partMap, SE_DELTA_QUANT_INTRA); + + if (currSlice->dp_mode) + { + if (currSlice->dpB_NotPresent) + { + currMB->ei_flag = 1; + currMB->dpl_flag = 1; + } + check_dp_neighbors (currMB); + if (currMB->dpl_flag) + { + currMB->cbp = cbp = 0; + } + } + + if (!currMB->dpl_flag) + { + pos_scan_4x4 = pos_scan4x4[0]; + + currSlice->read_coeff_4x4_CAVLC(currMB, LUMA_INTRA16x16DC, 0, 0, levarr, runarr, &numcoeff); + + for(k = 0; k < numcoeff; ++k) + { + if (levarr[k] != 0) // leave if level == 0 + { + pos_scan_4x4 += 2 * runarr[k]; + + i0 = ((*pos_scan_4x4++) << 2); + j0 = ((*pos_scan_4x4++) << 2); + + currSlice->cof[0][j0][i0] = levarr[k];// add new intra DC coeff + //currSlice->fcf[0][j0][i0] = levarr[k];// add new intra DC coeff + } + } + + + if(currMB->is_lossless == FALSE) + itrans_2(currMB, (ColorPlane) currSlice->colour_plane_id);// transform new intra DC + } + } + + update_qp(currMB, currSlice->qp); + + qp_per = p_Vid->qp_per_matrix[ currMB->qp_scaled[currSlice->colour_plane_id] ]; + qp_rem = p_Vid->qp_rem_matrix[ currMB->qp_scaled[currSlice->colour_plane_id] ]; + + //init quant parameters for chroma + for(i=0; i < 2; ++i) + { + qp_per_uv[i] = p_Vid->qp_per_matrix[ currMB->qp_scaled[i + 1] ]; + qp_rem_uv[i] = p_Vid->qp_rem_matrix[ currMB->qp_scaled[i + 1] ]; + } + + InvLevelScale4x4 = intra? currSlice->InvLevelScale4x4_Intra[currSlice->colour_plane_id][qp_rem] : currSlice->InvLevelScale4x4_Inter[currSlice->colour_plane_id][qp_rem]; + InvLevelScale8x8 = intra? currSlice->InvLevelScale8x8_Intra[currSlice->colour_plane_id][qp_rem] : currSlice->InvLevelScale8x8_Inter[currSlice->colour_plane_id][qp_rem]; + + // luma coefficients + if (cbp) + { + if (!currMB->luma_transform_size_8x8_flag) // 4x4 transform + { + currMB->read_comp_coeff_4x4_CAVLC (currMB, PLANE_Y, InvLevelScale4x4, qp_per, cbp, p_Vid->nz_coeff[mb_nr][PLANE_Y]); + } + else // 8x8 transform + { + currMB->read_comp_coeff_8x8_CAVLC (currMB, PLANE_Y, InvLevelScale8x8, qp_per, cbp, p_Vid->nz_coeff[mb_nr][PLANE_Y]); + } + } + else + { + fast_memset(p_Vid->nz_coeff[mb_nr][0][0], 0, BLOCK_PIXELS * sizeof(byte)); + } + + //========================== CHROMA DC ============================ + //----------------------------------------------------------------- + // chroma DC coeff + if(cbp>15) + { + for (ll=0;ll<3;ll+=2) + { + uv = ll>>1; + + InvLevelScale4x4 = intra ? currSlice->InvLevelScale4x4_Intra[PLANE_U + uv][qp_rem_uv[uv]] : currSlice->InvLevelScale4x4_Inter[PLANE_U + uv][qp_rem_uv[uv]]; + //===================== CHROMA DC YUV420 ====================== + memset(currSlice->cofu, 0, 4 *sizeof(int)); + coef_ctr=-1; + + currSlice->read_coeff_4x4_CAVLC(currMB, CHROMA_DC, 0, 0, levarr, runarr, &numcoeff); + + for(k = 0; k < numcoeff; ++k) + { + if (levarr[k] != 0) + { + currMB->s_cbp[0].blk |= 0xf0000 << (ll<<1) ; + coef_ctr += runarr[k] + 1; + currSlice->cofu[coef_ctr]=levarr[k]; + } + } + + + if (smb || (currMB->is_lossless == TRUE)) // check to see if MB type is SPred or SIntra4x4 + { + currSlice->cof[PLANE_U + uv][0][0] = currSlice->cofu[0]; + currSlice->cof[PLANE_U + uv][4][0] = currSlice->cofu[1]; + currSlice->cof[PLANE_U + uv][0][4] = currSlice->cofu[2]; + currSlice->cof[PLANE_U + uv][4][4] = currSlice->cofu[3]; + //currSlice->fcf[PLANE_U + uv][0][0] = currSlice->cofu[0]; + //currSlice->fcf[PLANE_U + uv][4][0] = currSlice->cofu[1]; + //currSlice->fcf[PLANE_U + uv][0][4] = currSlice->cofu[2]; + //currSlice->fcf[PLANE_U + uv][4][4] = currSlice->cofu[3]; + } + else + { + ihadamard2x2(currSlice->cofu, temp); + //currSlice->fcf[PLANE_U + uv][0][0] = temp[0]; + //currSlice->fcf[PLANE_U + uv][0][4] = temp[1]; + //currSlice->fcf[PLANE_U + uv][4][0] = temp[2]; + //currSlice->fcf[PLANE_U + uv][4][4] = temp[3]; + + currSlice->cof[PLANE_U + uv][0][0] = (((temp[0] * InvLevelScale4x4[0][0])<>5); + currSlice->cof[PLANE_U + uv][0][4] = (((temp[1] * InvLevelScale4x4[0][0])<>5); + currSlice->cof[PLANE_U + uv][4][0] = (((temp[2] * InvLevelScale4x4[0][0])<>5); + currSlice->cof[PLANE_U + uv][4][4] = (((temp[3] * InvLevelScale4x4[0][0])<>5); + } + } + } + + //========================== CHROMA AC ============================ + //----------------------------------------------------------------- + // chroma AC coeff, all zero fram start_scan + if (cbp<=31) + { + fast_memset(p_Vid->nz_coeff [mb_nr ][1][0], 0, 2 * BLOCK_PIXELS * sizeof(byte)); + } + else + { + if(currMB->is_lossless == FALSE) + { + for (b8=0; b8 < p_Vid->num_blk8x8_uv; ++b8) + { + currMB->is_v_block = uv = (b8 > ((p_Vid->num_uv_blocks) - 1 )); + InvLevelScale4x4 = intra ? currSlice->InvLevelScale4x4_Intra[PLANE_U + uv][qp_rem_uv[uv]] : currSlice->InvLevelScale4x4_Inter[PLANE_U + uv][qp_rem_uv[uv]]; + + for (b4=0; b4 < 4; ++b4) + { + i = cofuv_blk_x[0][b8][b4]; + j = cofuv_blk_y[0][b8][b4]; + + currSlice->read_coeff_4x4_CAVLC(currMB, CHROMA_AC, i + 2*uv, j + 4, levarr, runarr, &numcoeff); + coef_ctr = 0; + + for(k = 0; k < numcoeff;++k) + { + if (levarr[k] != 0) + { + currMB->s_cbp[0].blk |= i64_power2(cbp_blk_chroma[b8][b4]); + coef_ctr += runarr[k] + 1; + + i0=pos_scan4x4[coef_ctr][0]; + j0=pos_scan4x4[coef_ctr][1]; + + currSlice->cof[PLANE_U + uv][(j<<2) + j0][(i<<2) + i0] = rshift_rnd_sf((levarr[k] * InvLevelScale4x4[j0][i0])<fcf[PLANE_U + uv][(j<<2) + j0][(i<<2) + i0] = levarr[k]; + } + } + } + } + } + else + { + for (b8=0; b8 < p_Vid->num_blk8x8_uv; ++b8) + { + currMB->is_v_block = uv = (b8 > ((p_Vid->num_uv_blocks) - 1 )); + + for (b4=0; b4 < 4; ++b4) + { + i = cofuv_blk_x[0][b8][b4]; + j = cofuv_blk_y[0][b8][b4]; + + currSlice->read_coeff_4x4_CAVLC(currMB, CHROMA_AC, i + 2*uv, j + 4, levarr, runarr, &numcoeff); + coef_ctr = 0; + + for(k = 0; k < numcoeff;++k) + { + if (levarr[k] != 0) + { + currMB->s_cbp[0].blk |= i64_power2(cbp_blk_chroma[b8][b4]); + coef_ctr += runarr[k] + 1; + + i0=pos_scan4x4[coef_ctr][0]; + j0=pos_scan4x4[coef_ctr][1]; + + currSlice->cof[PLANE_U + uv][(j<<2) + j0][(i<<2) + i0] = levarr[k]; + } + } + } + } + } + } +} + + +/*! +************************************************************************ +* \brief +* setup coefficient reading functions for CAVLC +* +************************************************************************ +*/ +void set_read_comp_coeff_cavlc(Macroblock *currMB) +{ + if (currMB->is_lossless == FALSE) + { + currMB->read_comp_coeff_4x4_CAVLC = read_comp_coeff_4x4_CAVLC; + currMB->read_comp_coeff_8x8_CAVLC = read_comp_coeff_8x8_CAVLC; + } + else + { + currMB->read_comp_coeff_4x4_CAVLC = read_comp_coeff_4x4_CAVLC_ls; + currMB->read_comp_coeff_8x8_CAVLC = read_comp_coeff_8x8_CAVLC_ls; + } +} + + +void set_read_CBP_and_coeffs_cavlc(Slice *currSlice) +{ + switch (currSlice->p_Vid->active_sps->chroma_format_idc) + { + case YUV444: + if (currSlice->p_Vid->separate_colour_plane_flag == 0) + { + currSlice->read_CBP_and_coeffs_from_NAL = read_CBP_and_coeffs_from_NAL_CAVLC_444; + } + else + { + currSlice->read_CBP_and_coeffs_from_NAL = read_CBP_and_coeffs_from_NAL_CAVLC_400; + } + break; + case YUV422: + currSlice->read_CBP_and_coeffs_from_NAL = read_CBP_and_coeffs_from_NAL_CAVLC_422; + break; + case YUV420: + currSlice->read_CBP_and_coeffs_from_NAL = read_CBP_and_coeffs_from_NAL_CAVLC_420; + break; + case YUV400: + currSlice->read_CBP_and_coeffs_from_NAL = read_CBP_and_coeffs_from_NAL_CAVLC_400; + break; + default: + assert (1); + currSlice->read_CBP_and_coeffs_from_NAL = NULL; + break; + } +} diff --git a/ldecod/src/report_dec.c b/ldecod/src/report_dec.c new file mode 100644 index 0000000..5781b30 --- /dev/null +++ b/ldecod/src/report_dec.c @@ -0,0 +1,566 @@ + +/*! + ************************************************************************ + * \file report_dec.c + * + * \brief + * functions for frame format related information + * + * \author + * Joao Carreira + ************************************************************************ + */ + +#include +#include "global.h" +#include "report_dec.h" + +#include "image.h" + +#define LOGFILE "log.dec" +#define DATADECFILE "dataDec.txt" +#define TRACEFILE "trace_dec.txt" + +void init_report (InputParameters *p_Inp) +{ + if(!p_Inp->bDisplayDecParams) + { + fprintf(stdout,"-------------------------------- Decoder Params -------------------------------\n"); + fprintf(stdout," Input H.264 bitstream : %s \n",p_Inp->infile); + fprintf(stdout," Output Decoded YUV : %s[VIEW] \n",p_Inp->outfile); + if(p_Inp->reffile[0]) + fprintf(stdout," Input reference file : %s \n",p_Inp->reffile); + if(p_Inp->reffile[0] || p_Inp->conceal_mode) + fprintf(stdout," IDRPeriod : %d \n",p_Inp->IDR_Period); + if (p_Inp->conceal_mode) + { + fprintf(stdout," ConcealMode : %d \n",p_Inp->conceal_mode); + fprintf(stdout," SearchModeRef : %d \n",p_Inp->search_mode_ref); + fprintf(stdout," SearchModeNonRef : %d \n",p_Inp->search_mode_nonref); + } + +#ifdef _LEAKYBUCKET_ + fprintf(stdout," Rate_decoder : %8ld \n",p_Inp->R_decoder); + fprintf(stdout," B_decoder : %8ld \n",p_Inp->B_decoder); + fprintf(stdout," F_decoder : %8ld \n",p_Inp->F_decoder); + // Leaky Bucket Param file + fprintf(stdout," LeakyBucketParamFile: %s \n",p_Inp->LeakyBucketParamFile); + calc_buffer(p_Inp); + fprintf(stdout,"-------------------------------------------------------------------------------\n"); +#endif + + } + + if (!p_Inp->silent) + { + //fprintf(stdout,"POC must = frame# or field# for SNRs to be correct\n"); + fprintf(stdout,"-------------------------------------------------------------------------------\n"); + fprintf(stdout," Frame POC View Pic# QP SnrY SnrU SnrV Y:U:V Time(ms)\n"); + fprintf(stdout,"-------------------------------------------------------------------------------\n"); + } +} + + +void FinalReport (VideoParameters *p_Vid) +{ + InputParameters *p_Inp = p_Vid->p_Inp; + int i; + int nFramesDec = 0, nFramesConceal = 0, nFramesOut = 0; + int n_views; + + // normalize time + p_Vid->tot_time = timenorm(p_Vid->tot_time); + + if( p_Vid->p_Inp->statsfile[0] ) + report_stats(p_Vid); + + for (i=0;inFramesDecoded[i]; + nFramesConceal += p_Vid->nFramesConceal[i]; + nFramesOut += p_Vid->nFramesOut[i]; + } + + if( p_Vid->active_subset_sps ) + n_views = p_Vid->active_subset_sps->num_views_minus1+1; + else + n_views = 1; + + if(p_Inp->reffile[0]) + { + fprintf(stdout,"------------------------- Average SNR all frames -------------------------- \n"); + for(i=0;isnr[GetViewIdx(p_Vid, i)]; + fprintf(stdout," SNR View %2d Y|U|V(dB) : %5.2f | %5.2f | %5.2f\n", GetViewIdx(p_Vid, i), snr->snra[0], snr->snra[1], snr->snra[2]); + } + + } + fprintf(stdout,"------------------------------ Final Report ------------------------------- \n"); + + fprintf(stdout," Frames Decoded = %4d ( %3d", nFramesDec, p_Vid->nFramesDecoded[0] ); + for(i=1;inFramesDecoded[GetViewIdx(p_Vid, i)]); + } + fprintf(stdout," )\n"); + + fprintf(stdout," Frames Concealed = %4d ( %3d", nFramesConceal, p_Vid->nFramesConceal[0] ); + for(i=1;inFramesConceal[i]); + } + fprintf(stdout," )\n"); + + fprintf(stdout," Frames Outputed = %4d ( %3d", nFramesOut, p_Vid->nFramesOut[0] ); + for(i=1;inFramesOut[i]); + } + fprintf(stdout," )\n"); + + fprintf(stdout," Total decoding time : %.3f sec (%.3f fps)[%d frm/%" FORMAT_OFF_T " ms]\n",p_Vid->tot_time*0.001,(nFramesDec + nFramesConceal) * 1000.0 / p_Vid->tot_time, (nFramesDec + nFramesConceal), p_Vid->tot_time); + +} + +#if( MVC_EXTENSION_ENABLE == 0) +/*! + ************************************************************************ + * \brief + * Reports the gathered information to appropriate outputs + * + * \par Input: + * InputParameters *p_Inp, + * VideoParameters *p_Vid, + * struct snr_par *stat + * + * \par Output: + * None + ************************************************************************ + */ +void report_to_logfile(VideoParameters *p_Vid) +{ + InputParameters *p_Inp = p_Vid->p_Inp; + SNRParameters *snr = p_Vid->snr; + pic_parameter_set_rbsp_t *active_pps = p_Vid->active_pps; +#define OUTSTRING_SIZE 255 + char string[OUTSTRING_SIZE]; + FILE *p_log; +#ifndef WIN32 + time_t now; + struct tm *l_time; +#else + char timebuf[128]; +#endif + static const char yuv_formats[4][4]= { {"400"}, {"420"}, {"422"}, {"444"} }; + + // write to log file + fprintf(stdout," Output status file : %s \n",LOGFILE); + snprintf(string, OUTSTRING_SIZE, "%s", LOGFILE); + + if ((p_log=fopen(string,"r"))==0) // check if file exist + { + if ((p_log=fopen(string,"a"))==0) + { + snprintf(errortext, ET_SIZE, "Error open file %s for appending",string); + error(errortext, 500); + } + else // Create header to new file + { + fprintf(p_log," -------------------------------------------------------------------------------------------------------------------\n"); + fprintf(p_log,"| Decoder statistics. This file is made first time, later runs are appended |\n"); + fprintf(p_log," ------------------------------------------------------------------------------------------------------------------- \n"); + fprintf(p_log,"| ver | Date | Time | Sequence |#Img| Format | YUV |Coding|SNRY 1|SNRU 1|SNRV 1|SNRY N|SNRU N|SNRV N|\n"); + fprintf(p_log," -------------------------------------------------------------------------------------------------------------------\n"); + } + } + else + { + fclose(p_log); + p_log=fopen(string,"a"); // File exist,just open for appending + } + + fprintf(p_log,"|%s/%-4s", VERSION, EXT_VERSION); + +#ifdef WIN32 + _strdate( timebuf ); + fprintf(p_log,"| %1.5s |",timebuf ); + + _strtime( timebuf); + fprintf(p_log," % 1.5s |",timebuf); +#else + now = time ((time_t *) NULL); // Get the system time and put it into 'now' as 'calender time' + time (&now); + l_time = localtime (&now); + strftime (string, sizeof string, "%d-%b-%Y", l_time); + fprintf(p_log,"| %1.5s |",string ); + + strftime (string, sizeof string, "%H:%M:%S", l_time); + fprintf(p_log,"| %1.5s |",string ); +#endif + + fprintf(p_log,"%20.20s|",p_Inp->infile); + + fprintf(p_log,"%3d |",p_Vid->number); + fprintf(p_log,"%4dx%-4d|", p_Vid->width, p_Vid->height); + fprintf(p_log," %s |", &(yuv_formats[p_Vid->yuv_format][0])); + + if (active_pps) + { + if (active_pps->entropy_coding_mode_flag == CAVLC) + fprintf(p_log," CAVLC|"); + else + fprintf(p_log," CABAC|"); + } + + fprintf(p_log,"%6.3f|",snr->snr1[0]); + fprintf(p_log,"%6.3f|",snr->snr1[1]); + fprintf(p_log,"%6.3f|",snr->snr1[2]); + fprintf(p_log,"%6.3f|",snr->snra[0][0]); + fprintf(p_log,"%6.3f|",snr->snra[0][1]); + fprintf(p_log,"%6.3f|",snr->snra[0][2]); + fprintf(p_log,"\n"); + fclose(p_log); + + //snprintf(string, OUTSTRING_SIZE,"%s", DATADECFILE); + //p_log=fopen(string,"a"); + + //if(p_Vid->Bframe_ctr != 0) // B picture used + //{ + // fprintf(p_log, "%3d %2d %2d %2.2f %2.2f %2.2f %5d " + // "%2.2f %2.2f %2.2f %5d " + // "%2.2f %2.2f %2.2f %5d %.3f\n", + // p_Vid->number, 0, p_Vid->ppSliceList[0]->qp, + // snr->snr1[0], + // snr->snr1[1], + // snr->snr1[2], + // 0, + // 0.0, + // 0.0, + // 0.0, + // 0, + // snr->snra[0], + // snr->snra[1], + // snr->snra[2], + // 0, + // (double)0.001*p_Vid->tot_time/(p_Vid->number + p_Vid->Bframe_ctr - 1)); + //} + //else + //{ + // fprintf(p_log, "%3d %2d %2d %2.2f %2.2f %2.2f %5d " + // "%2.2f %2.2f %2.2f %5d " + // "%2.2f %2.2f %2.2f %5d %.3f\n", + // p_Vid->number, 0, p_Vid->ppSliceList[0]? p_Vid->ppSliceList[0]->qp: 0, + // snr->snr1[0], + // snr->snr1[1], + // snr->snr1[2], + // 0, + // 0.0, + // 0.0, + // 0.0, + // 0, + // snr->snra[0], + // snr->snra[1], + // snr->snra[2], + // 0, + // p_Vid->number ? ((double)0.001*p_Vid->tot_time/p_Vid->number) : 0.0); + //} + //fclose(p_log); +} + + +#endif +/*! +************************************************************************ +* \brief +* calculate the average value of a vector +* +************************************************************************ +*/ +static inline double average (int* elem, unsigned int n_elem) +{ + unsigned int y; + double x; + + for(x = 0, y = 0; n_elem > y; y++) //y is the # that cycles through the array + { + x += elem[y]; //adds everything to x + } + + return(x/n_elem);//divides it by how many numbers they entered +} +/*! +************************************************************************ +* \brief +* calculate the variance of a vector +* +************************************************************************ +*/ +static inline double variance (int* elem, unsigned int n_elem, double average) +{ + unsigned int i; + double n; + double average2; + double variance; + + for (n=0, i=0;i *(int *)arg2) + return -1; + else + return 0; +} + +void calculate_stats (VideoParameters *p_Vid) +{ + int view, i, j; + int *burst_elem, n_burst_elem; + + burst_elem = (int*) malloc (p_Vid->nFramesOut[0] * sizeof(int)); + n_burst_elem = p_Vid->nFramesOut[0]; + + // Loop all views + for (view=0;viewp_PFrames[view]; + + if (!p_Vid->p_PFrames[view]) + continue; + + if (p_Vid->nFramesOut[view] > n_burst_elem ) + { + free(burst_elem); + burst_elem = (int*) malloc (p_Vid->nFramesOut[view] * sizeof(int)); + n_burst_elem = p_Vid->nFramesOut[view]; + } + + /************** Calculate Lost Frames ****************/ + memset(burst_elem,0,n_burst_elem * sizeof(int)); + for (i=0, j=0;inFramesOut[view]-1;i++) + { + if (!Vector[i]) + { + p_Vid->LostFrames[view].total_frames++; + ///burst_elem[j]++; + + if(!Vector[i] && !Vector[i+1]) + { + burst_elem[j]++; + } + else + { + burst_elem[j]++; + j++; + } + } + } + if (!Vector[i]) + { + p_Vid->LostFrames[view].total_frames++; + } + qsort(burst_elem,j,sizeof(int),sort_by_value); + for (j=0; ;j++) + if (!burst_elem[j]) + break; + if (j != 0) + { + p_Vid->LostFrames[view].burst_average = (float) (average (burst_elem, j)); + p_Vid->LostFrames[view].burst_variance = (float)( variance(burst_elem, j, p_Vid->LostFrames[view].burst_average)); + } + else + { + p_Vid->LostFrames[view].burst_average = 0; + p_Vid->LostFrames[view].burst_variance = 0; + } + /************** Calculate Afected Frames *************/ + memset(burst_elem,0,n_burst_elem * sizeof(int)); + for (i=0, j=0;inFramesOut[view]-1;i++) + { + if (Vector[i] <= 0) + { + p_Vid->AfectedFrames[view].total_frames++; + if( (Vector[i] <=0) && (Vector[i+1] <=0) ) + { + burst_elem[j]++; + } + else + { + burst_elem[j]++; + j++; + } + } + } + if (Vector[i] <= 0) + { + p_Vid->AfectedFrames[view].total_frames++; + } + qsort(burst_elem,j,sizeof(int),sort_by_value); + for (j=0; ;j++) + if (!burst_elem[j]) + break; + if (j != 0) + { + p_Vid->AfectedFrames[view].burst_average = (float)( average (burst_elem, j) ); + p_Vid->AfectedFrames[view].burst_variance = (float)( variance(burst_elem, j, p_Vid->AfectedFrames[view].burst_average)); + } + else + { + p_Vid->AfectedFrames[view].burst_average = 0; + p_Vid->AfectedFrames[view].burst_variance = 0; + } + } + free(burst_elem); + +} + +void report_stats (VideoParameters *p_Vid) +{ + InputParameters *p_Inp = p_Vid->p_Inp; + FILE *f_stats=NULL; + char out_Stats[FILE_NAME_SIZE], chBuf[FILE_NAME_SIZE], *pch; + int view; + + calculate_stats(p_Vid); + + if (p_Inp->statsfile[0]) + { + for (view=0;viewp_PFrames[view] != NULL) + { + strcpy(chBuf, p_Inp->statsfile); + pch = strrchr(chBuf, '.'); + if(pch) + *pch = '\0'; + sprintf(out_Stats, "%s_ViewId0%1d.%s", chBuf, view, pch+1); + if ((f_stats=fopen(out_Stats,"r+"))==NULL) + { + /*First Time*/ + if ((f_stats=fopen(out_Stats,"w"))==NULL) + { + snprintf(errortext, ET_SIZE, "Error open file %s ", out_Stats); + error(errortext, 500); + } + n_Loops = 1; + /* Write Header */ + fprintf(f_stats, "NLoops; %4d;\n", n_Loops); + fprintf(f_stats, "Mean Received Frames; Mean Lost Frames; Mean Lost Burst Average; Mean Lost Burst Variance; Mean Affected Frames; Mean Affected Burst Average; Mean Affected Burst Variance; Mean PSNR Y;\n"); + fprintf(f_stats, "%4.3f;" , (float) p_Vid->nFramesDecoded[view]); + fprintf(f_stats, "%4.3f;" , (float) p_Vid->LostFrames[view].total_frames); + fprintf(f_stats, "%4.3f;" , p_Vid->LostFrames[view].burst_average); + fprintf(f_stats, "%4.3f;" , p_Vid->LostFrames[view].burst_variance); + fprintf(f_stats, "%4.3f;" , (float) p_Vid->AfectedFrames[view].total_frames); + fprintf(f_stats, "%4.3f;" , p_Vid->AfectedFrames[view].burst_average); + fprintf(f_stats, "%4.3f;" , p_Vid->AfectedFrames[view].burst_variance); + fprintf(f_stats, "%4.3f;\n", (float)p_Vid->snr[view]->snra[0]); + fprintf(f_stats, "Received Frames; Lost Frames; Lost Burst Average; Lost Burst Variance; Affected Frames; Affected Burst Average; Affected Burst Variance; PSNR Y\n"); + } + else + { + fseek(f_stats,0L,SEEK_SET); + fscanf(f_stats, "NLoops; %d;\n", &n_Loops); + fscanf(f_stats, "Mean Received Frames; Mean Lost Frames; Mean Lost Burst Average; Mean Lost Burst Variance; Mean Affected Frames; Mean Affected Burst Average; Mean Affected Burst Variance; Mean PSNR Y;\n"); + fscanf(f_stats, "%f;" , &prev_mean_dec_frames); + fscanf(f_stats, "%f;" , &prev_mean_total_frames_LostFrames); + fscanf(f_stats, "%f;" , &prev_mean_burst_average_LostFrames); + fscanf(f_stats, "%f;" , &prev_mean_burst_variance_LostFrames); + fscanf(f_stats, "%f;" , &prev_mean_total_frames_AfectedFrames); + fscanf(f_stats, "%f;" , &prev_mean_burst_average_AfectedFrames); + fscanf(f_stats, "%f;" , &prev_mean_burst_variance_AfectedFrames); + fscanf(f_stats, "%f;" , &prev_mean_psnr_y); + + /* Calculate new mean values */ + prev_mean_dec_frames = ((prev_mean_dec_frames*((float)n_Loops))+p_Vid->nFramesDecoded[view])/((float)(n_Loops+1)); + prev_mean_total_frames_LostFrames = ((prev_mean_total_frames_LostFrames*((float)n_Loops))+p_Vid->LostFrames[view].total_frames)/((float)(n_Loops+1)); + prev_mean_burst_average_LostFrames = ((prev_mean_burst_average_LostFrames*((float)n_Loops))+p_Vid->LostFrames[view].burst_average)/((float)(n_Loops+1)); + prev_mean_burst_variance_LostFrames = ((prev_mean_burst_variance_LostFrames*((float)n_Loops))+p_Vid->LostFrames[view].burst_variance)/((float)(n_Loops+1)); + prev_mean_total_frames_AfectedFrames = ((prev_mean_total_frames_AfectedFrames*((float)n_Loops))+p_Vid->AfectedFrames[view].total_frames)/((float)(n_Loops+1)); + prev_mean_burst_average_AfectedFrames = ((prev_mean_burst_average_AfectedFrames*((float)n_Loops))+p_Vid->AfectedFrames[view].burst_average)/((float)(n_Loops+1)); + prev_mean_burst_variance_AfectedFrames = ((prev_mean_burst_variance_AfectedFrames*((float)n_Loops))+p_Vid->AfectedFrames[view].burst_variance)/((float)(n_Loops+1)); + prev_mean_psnr_y = ((prev_mean_psnr_y*((float)n_Loops))+((float)p_Vid->snr[view]->snra[0]))/((float)(n_Loops+1)); + + n_Loops++; + + /* Write new mean values */ + fseek(f_stats,0L,SEEK_SET); + fprintf(f_stats, "NLoops; %4d;\n", n_Loops); + fprintf(f_stats, "Mean Received Frames; Mean Lost Frames; Mean Lost Burst Average; Mean Lost Burst Variance; Mean Affected Frames; Mean Affected Burst Average; Mean Affected Burst Variance; Mean PSNR Y;\n"); + fprintf(f_stats, "%4.3f;" , prev_mean_dec_frames); + fprintf(f_stats, "%4.3f;" , prev_mean_total_frames_LostFrames); + fprintf(f_stats, "%4.3f;" , prev_mean_burst_average_LostFrames); + fprintf(f_stats, "%4.3f;" , prev_mean_burst_variance_LostFrames); + fprintf(f_stats, "%4.3f;" , prev_mean_total_frames_AfectedFrames); + fprintf(f_stats, "%4.3f;" , prev_mean_burst_average_AfectedFrames); + fprintf(f_stats, "%4.3f;" , prev_mean_burst_variance_AfectedFrames); + fprintf(f_stats, "%4.3f;\n", prev_mean_psnr_y); + } + + fseek(f_stats,0L,SEEK_END); + fprintf(f_stats, "%4d; ", p_Vid->nFramesDecoded[view]); + fprintf(f_stats, "%4d;%4.3f;%4.3f;", p_Vid->LostFrames[view].total_frames, + p_Vid->LostFrames[view].burst_average, + p_Vid->LostFrames[view].burst_variance); + fprintf(f_stats, "%4d;%4.3f;%4.3f;", p_Vid->AfectedFrames[view].total_frames, + p_Vid->AfectedFrames[view].burst_average, + p_Vid->AfectedFrames[view].burst_variance); + fprintf(f_stats, "%4.3f;", p_Vid->snr[view]->snra[0]); + fprintf(f_stats, "\n"); + fclose(f_stats); + } + } + } + + for (view=0;viewp_PFrames[view] != NULL) + { + int i; + strcpy(chBuf, p_Inp->outfile); + pch = strrchr(chBuf, '.'); + if(pch) + *pch = '\0'; + sprintf(out_Stats, "%s_ViewId0%1d_FRAMEERRORPATTERN.dat", chBuf, view); + if ((f_stats=fopen(out_Stats,"w"))==NULL) + { + snprintf(errortext, ET_SIZE, "Error open file %s ", out_Stats); + error(errortext, 500); + } + for (i=0;inFramesOut[view];i++) + { + fprintf(f_stats,"%d,",p_Vid->p_PFrames[view][i]); + } + fclose(f_stats); + } + } + +} diff --git a/ldecod/src/rtp.c b/ldecod/src/rtp.c new file mode 100644 index 0000000..7317e50 --- /dev/null +++ b/ldecod/src/rtp.c @@ -0,0 +1,388 @@ + +/*! + ************************************************************************ + * \file rtp.c + * + * \brief + * Network Adaptation layer for RTP packets + * + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * - Stephan Wenger + ************************************************************************ + */ + + +/*! + + A quick guide to the basics of the RTP decoder implementation + + This module contains the RTP packetization, de-packetization, and the + handling of Parameter Sets, see VCEG-N52 and accompanying documents. + Note: Compound packets are not yet implemented! + + The interface between every NAL (including the RTP NAL) and the VCL is + based on Slices. The slice data structure on which the VCL is working + is defined in the type Slice (in defines.h). This type contains the + various fields of the slice header and a partition array, which itself + contains the data partitions the slice consists of. When data + partitioning is not used, then the whole slice bit string is stored + in partition #0. When individual partitions are missing, this is + indicated by the size of the bit strings in the partition array. + A complete missing slice (e.g. if a Full Slice packet was lost) is + indicated in a similar way. + + part of the slice structure is the error indication (ei-flag). The + Ei-flag is set in such cases in which at least one partition of a slice + is damaged or missing.When data partitioning is used, it can happen that + one partition does not contain any symbols but the ei_flag is cleared, + which indicates the intentional missing of symbols of that partition. + A typical example for this behaviour is the Intra Slice, which does not + have symnbols in its type C partition. + + The VCL requests new data to work on through the call of readSliceRTP(). + This function calls the main state machine of this module in ReadRTPpaacket(). + + ReadRTPpacket assumes, when called, that in an error free environment + a complete slice, consisting of one Full Slice RTP packet, or three Partition + packets of types A, B, C with consecutive sequence numbers, can be read. + It first interprets any trailing SUPP and Parameter Update (Header) packets. + Then it reads one video data packet. Two cases have to be distinguished: + + 1. Type A, or Full Slice packet + In this case, the PictureID and the macroblock mumbers are used to + identify the potential loss of a slice. A slice is lost, when the + StartMB of the newly read slice header is not equal to the current + state of the decoder + 1.1 Loss detected + In this case the last packet is unread (fseek back), and a dummy slice + containing the missing macroblocks is conveyed to the VCL. At the next + call of the NAL, the same packet is read again, but this time no packet + loss is detected by the above algorithm, + 1.2. No loss + In this case it is checked whether a Full Slice packet or a type A data + partition was read + 1.2.1 Full Slice + The Full Slice packet is conveyed to the NAL + 1.2.2 Type A Partition + The function RTPReadDataPartitionedSlice() is called, which collects + the remaining type B, C partitions and handles them appropriately. + + Paraneter Update Packets (aka Header packets) are in an SDP-like syntax + and are interpreted by a simple parser in the function + RTPInterpretParameterSetPacket() + + Each Slice header contaions the information on which parameter set to be used. + The function RTPSetImgInp() copies the information of the relevant parameter + set in the VCL's global variables p_Vid-> and p_Inp-> IMPORTANT: any changes + in the semantics of the p_Vid-> and p_Inp-> structure members must be represented + in this function as well! + + A note to the stream-buffer data structure: The stream buffer always contains + only the contents of the partition in question, and not the slice/partition + header. Decoding has to start at bitoffset 0 (CAVLC) or bytreoffset 0 (CABAC). + + The remaining functions should be self-explanatory. + +*/ + +#if defined(WIN32) || defined(WIN64) +#include +#else +#include +#endif + +#include "contributors.h" +#include "global.h" +#include "errorconcealment.h" +#include "rtp.h" +#include "fmo.h" +#include "sei.h" +#include "memalloc.h" + +int RTPReadPacket (RTPpacket_t *p, int bitstream); + +/*! + ************************************************************************ + * \brief + * Opens the bit stream file named fn + * \return + * none + ************************************************************************ + */ +void OpenRTPFile (char *fn, int *p_BitStreamFile) +{ + if (((*p_BitStreamFile) = open(fn, OPENFLAGS_READ)) == -1) + { + snprintf(errortext, ET_SIZE, "Cannot open RTP file '%s'", fn); + error(errortext,500); + } +} + + +/*! + ************************************************************************ + * \brief + * Closes the bit stream file + ************************************************************************ + */ +void CloseRTPFile(int *p_BitStreamFile) +{ + if ((*p_BitStreamFile) != -1) + { + close(*p_BitStreamFile); + (*p_BitStreamFile) = - 1; + } +} + + +/*! + ************************************************************************ + * \brief + * Fills nalu->buf and nalu->len with the payload of an RTP packet. + * Other fields in nalu-> remain uninitialized (will be taken care of + * by NALUtoRBSP. + * + * \return + * 4 in case of ok (for compatibility with get_annex_b_NALU) + * 0 if there is nothing any more to read (EOF) + * -1 in case of any error + * + ************************************************************************ + */ + +int GetRTPNALU (VideoParameters *p_Vid, NALU_t *nalu, int BitStreamFile) +{ + static uint16 first_call = 1; //!< triggers sequence number initialization on first call + static uint16 old_seq = 0; //!< store the last RTP sequence number for loss detection + + RTPpacket_t *p; + int ret; + + if ((p=malloc (sizeof (RTPpacket_t)))== NULL) + no_mem_exit ("GetRTPNALU-1"); + if ((p->packet=malloc (MAXRTPPACKETSIZE))== NULL) + no_mem_exit ("GetRTPNALU-2"); + if ((p->payload=malloc (MAXRTPPACKETSIZE))== NULL) + no_mem_exit ("GetRTPNALU-3"); + + ret = RTPReadPacket (p, BitStreamFile); + nalu->forbidden_bit = 1; + nalu->len = 0; + + if (ret > 0) // we got a packet ( -1=error, 0=end of file ) + { + if (first_call) + { + first_call = 0; + old_seq = (uint16) (p->seq - 1); + } + + nalu->lost_packets = (uint16) ( p->seq - (old_seq + 1) ); + old_seq = p->seq; + + assert (p->paylen < nalu->max_size); + + nalu->len = p->paylen; + memcpy (nalu->buf, p->payload, p->paylen); + nalu->forbidden_bit = (nalu->buf[0]>>7) & 1; + nalu->nal_reference_idc = (NalRefIdc) ((nalu->buf[0]>>5) & 3); + nalu->nal_unit_type = (NaluType) ((nalu->buf[0]) & 0x1f); + if (nalu->lost_packets) + { + printf ("Warning: RTP sequence number discontinuity detected\n"); + } + } + + // free memory + free (p->payload); + free (p->packet); + free (p); + +// printf ("Got an RTP NALU, len %d, first byte %x\n", nalu->len, nalu->buf[0]); + + if (ret>0) + // length of packet + return nalu->len; + else + // error code + return ret; +} + + + +/*! + ***************************************************************************** + * + * \brief + * DecomposeRTPpacket interprets the RTP packet and writes the various + * structure members of the RTPpacket_t structure + * + * \return + * 0 in case of success + * negative error code in case of failure + * + * \param p + * Caller is responsible to allocate enough memory for the generated payload + * in parameter->payload. Typically a malloc of paclen-12 bytes is sufficient + * + * \par Side effects + * none + * + * \date + * 30 Spetember 2001 + * + * \author + * Stephan Wenger stewe@cs.tu-berlin.de + *****************************************************************************/ + +int DecomposeRTPpacket (RTPpacket_t *p) + +{ + // consistency check + assert (p->packlen < 65536 - 28); // IP, UDP headers + assert (p->packlen >= 12); // at least a complete RTP header + assert (p->payload != NULL); + assert (p->packet != NULL); + + // Extract header information + + p->v = (p->packet[0] >> 6) & 0x03; + p->p = (p->packet[0] >> 5) & 0x01; + p->x = (p->packet[0] >> 4) & 0x01; + p->cc = (p->packet[0] >> 0) & 0x0F; + + p->m = (p->packet[1] >> 7) & 0x01; + p->pt = (p->packet[1] >> 0) & 0x7F; + + memcpy (&p->seq, &p->packet[2], 2); + p->seq = ntohs((uint16)p->seq); + + memcpy (&p->timestamp, &p->packet[4], 4);// change to shifts for unified byte sex + p->timestamp = ntohl(p->timestamp); + memcpy (&p->ssrc, &p->packet[8], 4);// change to shifts for unified byte sex + p->ssrc = ntohl(p->ssrc); + + // header consistency checks + if ( (p->v != 2) + || (p->p != 0) + || (p->x != 0) + || (p->cc != 0) ) + { + printf ("DecomposeRTPpacket, RTP header consistency problem, header follows\n"); + DumpRTPHeader (p); + return -1; + } + p->paylen = p->packlen-12; + memcpy (p->payload, &p->packet[12], p->paylen); + return 0; +} + +/*! + ***************************************************************************** + * + * \brief + * DumpRTPHeader is a debug tool that dumps a human-readable interpretation + * of the RTP header + * + * \return + * n.a. + * \param p + * the RTP packet to be dumped, after DecompositeRTPpacket() + * + * \par Side effects + * Debug output to stdout + * + * \date + * 30 Spetember 2001 + * + * \author + * Stephan Wenger stewe@cs.tu-berlin.de + *****************************************************************************/ + +void DumpRTPHeader (RTPpacket_t *p) + +{ + int i; + for (i=0; i< 30; i++) + printf ("%02x ", p->packet[i]); + printf ("Version (V): %d\n", (int) p->v); + printf ("Padding (P): %d\n", (int) p->p); + printf ("Extension (X): %d\n", (int) p->x); + printf ("CSRC count (CC): %d\n", (int) p->cc); + printf ("Marker bit (M): %d\n", (int) p->m); + printf ("Payload Type (PT): %d\n", (int) p->pt); + printf ("Sequence Number: %d\n", (int) p->seq); + printf ("Timestamp: %d\n", (int) p->timestamp); + printf ("SSRC: %d\n", (int) p->ssrc); +} + + +/*! + ***************************************************************************** + * + * \brief + * RTPReadPacket reads one packet from file + * + * \return + * 0: EOF + * negative: error + * positive: size of RTP packet in bytes + * + * \param p + * packet data structure, with memory for p->packet allocated + * + * \param bitstream + * target file + * + * \par Side effects: + * - File pointer in bits moved + * - p->xxx filled by reading and Decomposepacket() + * + * \date + * 04 November, 2001 + * + * \author + * Stephan Wenger, stewe@cs.tu-berlin.de + *****************************************************************************/ +int RTPReadPacket (RTPpacket_t *p, int bitstream) +{ + int64 Filepos; + int intime; + + assert (p != NULL); + assert (p->packet != NULL); + assert (p->payload != NULL); + + Filepos = tell (bitstream); + if (4 != read (bitstream, &p->packlen, 4)) + { + return 0; + } + if (4 != read (bitstream, &intime, 4)) + { + lseek (bitstream, Filepos, SEEK_SET); + printf ("RTPReadPacket: File corruption, could not read Timestamp, exit\n"); + exit (-1); + } + + assert (p->packlen < MAXRTPPACKETSIZE); + + if (p->packlen != (unsigned int) read (bitstream, p->packet, p->packlen)) + { + printf ("RTPReadPacket: File corruption, could not read %d bytes\n", (int) p->packlen); + exit (-1); // EOF inidication + } + + if (DecomposeRTPpacket (p) < 0) + { + // this should never happen, hence exit() is ok. We probably do not want to attempt + // to decode a packet that obviously wasn't generated by RTP + printf ("Errors reported by DecomposePacket(), exit\n"); + exit (-700); + } + assert (p->pt == H264PAYLOADTYPE); + assert (p->ssrc == H264SSRC); + return p->packlen; +} + diff --git a/ldecod/src/sei.c b/ldecod/src/sei.c new file mode 100644 index 0000000..61c6648 --- /dev/null +++ b/ldecod/src/sei.c @@ -0,0 +1,2236 @@ +/*! + ************************************************************************ + * \file sei.c + * + * \brief + * Functions to implement SEI messages + * + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * - Dong Tian + * - Karsten Suehring + ************************************************************************ + */ + +#include "contributors.h" + +#include +#include "global.h" +#include "memalloc.h" +#include "sei.h" +#include "vlc.h" +#include "header.h" +#include "mbuffer.h" +#include "parset.h" + + +// #define PRINT_BUFFERING_PERIOD_INFO // uncomment to print buffering period SEI info +// #define PRINT_PICTURE_TIMING_INFO // uncomment to print picture timing SEI info +// #define WRITE_MAP_IMAGE // uncomment to write spare picture map +// #define PRINT_SUBSEQUENCE_INFO // uncomment to print sub-sequence SEI info +// #define PRINT_SUBSEQUENCE_LAYER_CHAR // uncomment to print sub-sequence layer characteristics SEI info +// #define PRINT_SUBSEQUENCE_CHAR // uncomment to print sub-sequence characteristics SEI info +// #define PRINT_SCENE_INFORMATION // uncomment to print scene information SEI info +// #define PRINT_PAN_SCAN_RECT // uncomment to print pan-scan rectangle SEI info +// #define PRINT_RECOVERY_POINT // uncomment to print random access point SEI info +// #define PRINT_FILLER_PAYLOAD_INFO // uncomment to print filler payload SEI info +// #define PRINT_DEC_REF_PIC_MARKING // uncomment to print decoded picture buffer management repetition SEI info +// #define PRINT_RESERVED_INFO // uncomment to print reserved SEI info +// #define PRINT_USER_DATA_UNREGISTERED_INFO // uncomment to print unregistered user data SEI info +// #define PRINT_USER_DATA_REGISTERED_ITU_T_T35_INFO // uncomment to print ITU-T T.35 user data SEI info +// #define PRINT_FULL_FRAME_FREEZE_INFO // uncomment to print full-frame freeze SEI info +// #define PRINT_FULL_FRAME_FREEZE_RELEASE_INFO // uncomment to print full-frame freeze release SEI info +// #define PRINT_FULL_FRAME_SNAPSHOT_INFO // uncomment to print full-frame snapshot SEI info +// #define PRINT_PROGRESSIVE_REFINEMENT_END_INFO // uncomment to print Progressive refinement segment start SEI info +// #define PRINT_PROGRESSIVE_REFINEMENT_END_INFO // uncomment to print Progressive refinement segment end SEI info +// #define PRINT_MOTION_CONST_SLICE_GROUP_SET_INFO // uncomment to print Motion-constrained slice group set SEI info +// #define PRINT_FILM_GRAIN_CHARACTERISTICS_INFO // uncomment to print Film grain characteristics SEI info +// #define PRINT_DEBLOCKING_FILTER_DISPLAY_PREFERENCE_INFO // uncomment to print deblocking filter display preference SEI info +// #define PRINT_STEREO_VIDEO_INFO_INFO // uncomment to print stereo video SEI info +// #define PRINT_TONE_MAPPING // uncomment to print tone-mapping SEI info +// #define PRINT_POST_FILTER_HINT_INFO // uncomment to print post-filter hint SEI info +// #define PRINT_FRAME_PACKING_ARRANGEMENT_INFO // uncomment to print frame packing arrangement SEI info +/*! + ************************************************************************ + * \brief + * Interpret the SEI rbsp + * \param msg + * a pointer that point to the sei message. + * \param size + * the size of the sei message + * \param p_Vid + * the image pointer + * + ************************************************************************ + */ +void InterpretSEIMessage(byte* msg, int size, VideoParameters *p_Vid, Slice *pSlice) +{ + int payload_type = 0; + int payload_size = 0; + int offset = 1; + byte tmp_byte; + + do + { + // sei_message(); + payload_type = 0; + tmp_byte = msg[offset++]; + while (tmp_byte == 0xFF) + { + payload_type += 255; + tmp_byte = msg[offset++]; + } + payload_type += tmp_byte; // this is the last byte + + payload_size = 0; + tmp_byte = msg[offset++]; + while (tmp_byte == 0xFF) + { + payload_size += 255; + tmp_byte = msg[offset++]; + } + payload_size += tmp_byte; // this is the last byte + + switch ( payload_type ) // sei_payload( type, size ); + { + case SEI_BUFFERING_PERIOD: + interpret_buffering_period_info( msg+offset, payload_size, p_Vid ); + break; + case SEI_PIC_TIMING: + interpret_picture_timing_info( msg+offset, payload_size, p_Vid ); + break; + case SEI_PAN_SCAN_RECT: + interpret_pan_scan_rect_info( msg+offset, payload_size, p_Vid ); + break; + case SEI_FILLER_PAYLOAD: + interpret_filler_payload_info( msg+offset, payload_size, p_Vid ); + break; + case SEI_USER_DATA_REGISTERED_ITU_T_T35: + interpret_user_data_registered_itu_t_t35_info( msg+offset, payload_size, p_Vid ); + break; + case SEI_USER_DATA_UNREGISTERED: + interpret_user_data_unregistered_info( msg+offset, payload_size, p_Vid ); + break; + case SEI_RECOVERY_POINT: + interpret_recovery_point_info( msg+offset, payload_size, p_Vid ); + break; + case SEI_DEC_REF_PIC_MARKING_REPETITION: + interpret_dec_ref_pic_marking_repetition_info( msg+offset, payload_size, p_Vid, pSlice ); + break; + case SEI_SPARE_PIC: + interpret_spare_pic( msg+offset, payload_size, p_Vid ); + break; + case SEI_SCENE_INFO: + interpret_scene_information( msg+offset, payload_size, p_Vid ); + break; + case SEI_SUB_SEQ_INFO: + interpret_subsequence_info( msg+offset, payload_size, p_Vid ); + break; + case SEI_SUB_SEQ_LAYER_CHARACTERISTICS: + interpret_subsequence_layer_characteristics_info( msg+offset, payload_size, p_Vid ); + break; + case SEI_SUB_SEQ_CHARACTERISTICS: + interpret_subsequence_characteristics_info( msg+offset, payload_size, p_Vid ); + break; + case SEI_FULL_FRAME_FREEZE: + interpret_full_frame_freeze_info( msg+offset, payload_size, p_Vid ); + break; + case SEI_FULL_FRAME_FREEZE_RELEASE: + interpret_full_frame_freeze_release_info( msg+offset, payload_size, p_Vid ); + break; + case SEI_FULL_FRAME_SNAPSHOT: + interpret_full_frame_snapshot_info( msg+offset, payload_size, p_Vid ); + break; + case SEI_PROGRESSIVE_REFINEMENT_SEGMENT_START: + interpret_progressive_refinement_start_info( msg+offset, payload_size, p_Vid ); + break; + case SEI_PROGRESSIVE_REFINEMENT_SEGMENT_END: + interpret_progressive_refinement_end_info( msg+offset, payload_size, p_Vid ); + break; + case SEI_MOTION_CONSTRAINED_SLICE_GROUP_SET: + interpret_motion_constrained_slice_group_set_info( msg+offset, payload_size, p_Vid ); + case SEI_FILM_GRAIN_CHARACTERISTICS: + interpret_film_grain_characteristics_info ( msg+offset, payload_size, p_Vid ); + break; + case SEI_DEBLOCKING_FILTER_DISPLAY_PREFERENCE: + interpret_deblocking_filter_display_preference_info ( msg+offset, payload_size, p_Vid ); + break; + case SEI_STEREO_VIDEO_INFO: + interpret_stereo_video_info_info ( msg+offset, payload_size, p_Vid ); + break; + case SEI_TONE_MAPPING: + interpret_tone_mapping( msg+offset, payload_size, p_Vid ); + break; + case SEI_POST_FILTER_HINTS: + interpret_post_filter_hints_info ( msg+offset, payload_size, p_Vid ); + break; + case SEI_FRAME_PACKING_ARRANGEMENT: + interpret_frame_packing_arrangement_info( msg+offset, payload_size, p_Vid ); + break; + default: + interpret_reserved_info( msg+offset, payload_size, p_Vid ); + break; + } + offset += payload_size; + + } while( msg[offset] != 0x80 ); // more_rbsp_data() msg[offset] != 0x80 + // ignore the trailing bits rbsp_trailing_bits(); + assert(msg[offset] == 0x80); // this is the trailing bits + assert( offset+1 == size ); +} + + +/*! +************************************************************************ +* \brief +* Interpret the spare picture SEI message +* \param payload +* a pointer that point to the sei payload +* \param size +* the size of the sei message +* \param p_Vid +* the image pointer +* +************************************************************************ +*/ +void interpret_spare_pic( byte* payload, int size, VideoParameters *p_Vid ) +{ + int i,x,y; + Bitstream* buf; + int bit0, bit1, bitc, no_bit0; + int target_frame_num = 0; + int num_spare_pics; + int delta_spare_frame_num, CandidateSpareFrameNum, SpareFrameNum = 0; + int ref_area_indicator; + + int m, n, left, right, top, bottom,directx, directy; + byte ***map; + +#ifdef WRITE_MAP_IMAGE + int symbol_size_in_bytes = p_Vid->pic_unit_bitsize_on_disk/8; + int j, k, i0, j0, tmp, kk; + char filename[20] = "map_dec.yuv"; + FILE *fp; + imgpel** Y; + static int old_pn=-1; + static int first = 1; + + printf("Spare picture SEI message\n"); +#endif + + p_Dec->UsedBits = 0; + + assert( payload!=NULL); + assert( p_Vid!=NULL); + + buf = malloc(sizeof(Bitstream)); + buf->bitstream_length = size; + buf->streamBuffer = payload; + buf->frame_bitoffset = 0; + + target_frame_num = read_ue_v("SEI: target_frame_num", buf, &p_Dec->UsedBits); + +#ifdef WRITE_MAP_IMAGE + printf( "target_frame_num is %d\n", target_frame_num ); +#endif + + num_spare_pics = 1 + read_ue_v("SEI: num_spare_pics_minus1", buf, &p_Dec->UsedBits); + +#ifdef WRITE_MAP_IMAGE + printf( "num_spare_pics is %d\n", num_spare_pics ); +#endif + + get_mem3D(&map, num_spare_pics, p_Vid->height >> 4, p_Vid->width >> 4); + + for (i=0; iUsedBits); + + SpareFrameNum = CandidateSpareFrameNum - delta_spare_frame_num; + if( SpareFrameNum < 0 ) + SpareFrameNum = MAX_FN + SpareFrameNum; + + ref_area_indicator = read_ue_v("SEI: ref_area_indicator", buf, &p_Dec->UsedBits); + + switch ( ref_area_indicator ) + { + case 0: // The whole frame can serve as spare picture + for (y=0; yheight >> 4; y++) + for (x=0; xwidth >> 4; x++) + map[i][y][x] = 0; + break; + case 1: // The map is not compressed + for (y=0; yheight >> 4; y++) + for (x=0; xwidth >> 4; x++) + { + map[i][y][x] = (byte) read_u_1("SEI: ref_mb_indicator", buf, &p_Dec->UsedBits); + } + break; + case 2: // The map is compressed + //!KS: could not check this function, description is unclear (as stated in Ed. Note) + bit0 = 0; + bit1 = 1; + bitc = bit0; + no_bit0 = -1; + + x = ( (p_Vid->width >> 4) - 1 ) / 2; + y = ( (p_Vid->height >> 4) - 1 ) / 2; + left = right = x; + top = bottom = y; + directx = 0; + directy = 1; + + for (m=0; mheight >> 4; m++) + for (n=0; nwidth >> 4; n++) + { + + if (no_bit0<0) + { + no_bit0 = read_ue_v("SEI: zero_run_length", buf, &p_Dec->UsedBits); + } + if (no_bit0>0) + map[i][y][x] = (byte) bit0; + else + map[i][y][x] = (byte) bit1; + no_bit0--; + + // go to the next mb: + if ( directx == -1 && directy == 0 ) + { + if (x > left) x--; + else if (x == 0) + { + y = bottom + 1; + bottom++; + directx = 1; + directy = 0; + } + else if (x == left) + { + x--; + left--; + directx = 0; + directy = 1; + } + } + else if ( directx == 1 && directy == 0 ) + { + if (x < right) x++; + else if (x == (p_Vid->width >> 4) - 1) + { + y = top - 1; + top--; + directx = -1; + directy = 0; + } + else if (x == right) + { + x++; + right++; + directx = 0; + directy = -1; + } + } + else if ( directx == 0 && directy == -1 ) + { + if ( y > top) y--; + else if (y == 0) + { + x = left - 1; + left--; + directx = 0; + directy = 1; + } + else if (y == top) + { + y--; + top--; + directx = -1; + directy = 0; + } + } + else if ( directx == 0 && directy == 1 ) + { + if (y < bottom) y++; + else if (y == (p_Vid->height >> 4) - 1) + { + x = right+1; + right++; + directx = 0; + directy = -1; + } + else if (y == bottom) + { + y++; + bottom++; + directx = 1; + directy = 0; + } + } + + + } + break; + default: + printf( "Wrong ref_area_indicator %d!\n", ref_area_indicator ); + exit(0); + break; + } + + } // end of num_spare_pics + +#ifdef WRITE_MAP_IMAGE + // begin to write map seq + if ( old_pn != p_Vid->number ) + { + old_pn = p_Vid->number; + get_mem2Dpel(&Y, p_Vid->height, p_Vid->width); + if (first) + { + fp = fopen( filename, "wb" ); + first = 0; + } + else + fp = fopen( filename, "ab" ); + assert( fp != NULL ); + for (kk=0; kkheight >> 4; i++) + for (j=0; j < p_Vid->width >> 4; j++) + { + tmp=map[kk][i][j]==0? p_Vid->max_pel_value_comp[0] : 0; + for (i0=0; i0<16; i0++) + for (j0=0; j0<16; j0++) + Y[i*16+i0][j*16+j0]=tmp; + } + + // write the map image + for (i=0; i < p_Vid->height; i++) + for (j=0; j < p_Vid->width; j++) + fwrite(&(Y[i][j]), symbol_size_in_bytes, 1, p_out); + + for (k=0; k < 2; k++) + for (i=0; i < p_Vid->height>>1; i++) + for (j=0; j < p_Vid->width>>1; j++) + fwrite(&(p_Vid->dc_pred_value_comp[1]), symbol_size_in_bytes, 1, p_out); + } + fclose( fp ); + free_mem2Dpel( Y ); + } + // end of writing map image +#undef WRITE_MAP_IMAGE +#endif + + free_mem3D( map ); + + free(buf); +} + + +/*! + ************************************************************************ + * \brief + * Interpret the Sub-sequence information SEI message + * \param payload + * a pointer that point to the sei payload + * \param size + * the size of the sei message + * \param p_Vid + * the image pointer + * + ************************************************************************ + */ +void interpret_subsequence_info( byte* payload, int size, VideoParameters *p_Vid ) +{ + Bitstream* buf; + int sub_seq_layer_num, sub_seq_id, first_ref_pic_flag, leading_non_ref_pic_flag, last_pic_flag, + sub_seq_frame_num_flag, sub_seq_frame_num; + + buf = malloc(sizeof(Bitstream)); + buf->bitstream_length = size; + buf->streamBuffer = payload; + buf->frame_bitoffset = 0; + + p_Dec->UsedBits = 0; + + sub_seq_layer_num = read_ue_v("SEI: sub_seq_layer_num" , buf, &p_Dec->UsedBits); + sub_seq_id = read_ue_v("SEI: sub_seq_id" , buf, &p_Dec->UsedBits); + first_ref_pic_flag = read_u_1 ("SEI: first_ref_pic_flag" , buf, &p_Dec->UsedBits); + leading_non_ref_pic_flag = read_u_1 ("SEI: leading_non_ref_pic_flag", buf, &p_Dec->UsedBits); + last_pic_flag = read_u_1 ("SEI: last_pic_flag" , buf, &p_Dec->UsedBits); + sub_seq_frame_num_flag = read_u_1 ("SEI: sub_seq_frame_num_flag" , buf, &p_Dec->UsedBits); + if (sub_seq_frame_num_flag) + { + sub_seq_frame_num = read_ue_v("SEI: sub_seq_frame_num" , buf, &p_Dec->UsedBits); + } + +#ifdef PRINT_SUBSEQUENCE_INFO + printf("Sub-sequence information SEI message\n"); + printf("sub_seq_layer_num = %d\n", sub_seq_layer_num ); + printf("sub_seq_id = %d\n", sub_seq_id); + printf("first_ref_pic_flag = %d\n", first_ref_pic_flag); + printf("leading_non_ref_pic_flag = %d\n", leading_non_ref_pic_flag); + printf("last_pic_flag = %d\n", last_pic_flag); + printf("sub_seq_frame_num_flag = %d\n", sub_seq_frame_num_flag); + if (sub_seq_frame_num_flag) + { + printf("sub_seq_frame_num = %d\n", sub_seq_frame_num); + } +#endif + + free(buf); +#ifdef PRINT_SUBSEQUENCE_INFO +#undef PRINT_SUBSEQUENCE_INFO +#endif +} + +/*! + ************************************************************************ + * \brief + * Interpret the Sub-sequence layer characteristics SEI message + * \param payload + * a pointer that point to the sei payload + * \param size + * the size of the sei message + * \param p_Vid + * the image pointer + * + ************************************************************************ + */ +void interpret_subsequence_layer_characteristics_info( byte* payload, int size, VideoParameters *p_Vid ) +{ + Bitstream* buf; + long num_sub_layers, accurate_statistics_flag, average_bit_rate, average_frame_rate; + int i; + + buf = malloc(sizeof(Bitstream)); + buf->bitstream_length = size; + buf->streamBuffer = payload; + buf->frame_bitoffset = 0; + + p_Dec->UsedBits = 0; + + num_sub_layers = 1 + read_ue_v("SEI: num_sub_layers_minus1", buf, &p_Dec->UsedBits); + +#ifdef PRINT_SUBSEQUENCE_LAYER_CHAR + printf("Sub-sequence layer characteristics SEI message\n"); + printf("num_sub_layers_minus1 = %d\n", num_sub_layers - 1); +#endif + + for (i=0; iUsedBits); + average_bit_rate = read_u_v(16,"SEI: average_bit_rate" , buf, &p_Dec->UsedBits); + average_frame_rate = read_u_v(16,"SEI: average_frame_rate" , buf, &p_Dec->UsedBits); + +#ifdef PRINT_SUBSEQUENCE_LAYER_CHAR + printf("layer %d: accurate_statistics_flag = %ld \n", i, accurate_statistics_flag); + printf("layer %d: average_bit_rate = %ld \n", i, average_bit_rate); + printf("layer %d: average_frame_rate = %ld \n", i, average_frame_rate); +#endif + } + free (buf); +} + + +/*! + ************************************************************************ + * \brief + * Interpret the Sub-sequence characteristics SEI message + * \param payload + * a pointer that point to the sei payload + * \param size + * the size of the sei message + * \param p_Vid + * the image pointer + * + ************************************************************************ + */ +void interpret_subsequence_characteristics_info( byte* payload, int size, VideoParameters *p_Vid ) +{ + Bitstream* buf; + int i; + int sub_seq_layer_num, sub_seq_id, duration_flag, average_rate_flag, accurate_statistics_flag; + unsigned long sub_seq_duration, average_bit_rate, average_frame_rate; + int num_referenced_subseqs, ref_sub_seq_layer_num, ref_sub_seq_id, ref_sub_seq_direction; + + buf = malloc(sizeof(Bitstream)); + buf->bitstream_length = size; + buf->streamBuffer = payload; + buf->frame_bitoffset = 0; + + p_Dec->UsedBits = 0; + + sub_seq_layer_num = read_ue_v("SEI: sub_seq_layer_num", buf, &p_Dec->UsedBits); + sub_seq_id = read_ue_v("SEI: sub_seq_id", buf, &p_Dec->UsedBits); + duration_flag = read_u_1 ("SEI: duration_flag", buf, &p_Dec->UsedBits); + +#ifdef PRINT_SUBSEQUENCE_CHAR + printf("Sub-sequence characteristics SEI message\n"); + printf("sub_seq_layer_num = %d\n", sub_seq_layer_num ); + printf("sub_seq_id = %d\n", sub_seq_id); + printf("duration_flag = %d\n", duration_flag); +#endif + + if ( duration_flag ) + { + sub_seq_duration = read_u_v (32, "SEI: duration_flag", buf, &p_Dec->UsedBits); +#ifdef PRINT_SUBSEQUENCE_CHAR + printf("sub_seq_duration = %ld\n", sub_seq_duration); +#endif + } + + average_rate_flag = read_u_1 ("SEI: average_rate_flag", buf, &p_Dec->UsedBits); + +#ifdef PRINT_SUBSEQUENCE_CHAR + printf("average_rate_flag = %d\n", average_rate_flag); +#endif + + if ( average_rate_flag ) + { + accurate_statistics_flag = read_u_1 ( "SEI: accurate_statistics_flag", buf, &p_Dec->UsedBits); + average_bit_rate = read_u_v (16, "SEI: average_bit_rate", buf, &p_Dec->UsedBits); + average_frame_rate = read_u_v (16, "SEI: average_frame_rate", buf, &p_Dec->UsedBits); + +#ifdef PRINT_SUBSEQUENCE_CHAR + printf("accurate_statistics_flag = %d\n", accurate_statistics_flag); + printf("average_bit_rate = %ld\n", average_bit_rate); + printf("average_frame_rate = %ld\n", average_frame_rate); +#endif + } + + num_referenced_subseqs = read_ue_v("SEI: num_referenced_subseqs", buf, &p_Dec->UsedBits); + +#ifdef PRINT_SUBSEQUENCE_CHAR + printf("num_referenced_subseqs = %d\n", num_referenced_subseqs); +#endif + + for (i=0; iUsedBits); + ref_sub_seq_id = read_ue_v("SEI: ref_sub_seq_id", buf, &p_Dec->UsedBits); + ref_sub_seq_direction = read_u_1 ("SEI: ref_sub_seq_direction", buf, &p_Dec->UsedBits); + +#ifdef PRINT_SUBSEQUENCE_CHAR + printf("ref_sub_seq_layer_num = %d\n", ref_sub_seq_layer_num); + printf("ref_sub_seq_id = %d\n", ref_sub_seq_id); + printf("ref_sub_seq_direction = %d\n", ref_sub_seq_direction); +#endif + } + + free( buf ); +#ifdef PRINT_SUBSEQUENCE_CHAR +#undef PRINT_SUBSEQUENCE_CHAR +#endif +} + + +/*! + ************************************************************************ + * \brief + * Interpret the Scene information SEI message + * \param payload + * a pointer that point to the sei payload + * \param size + * the size of the sei message + * \param p_Vid + * the image pointer + * + ************************************************************************ + */ +void interpret_scene_information( byte* payload, int size, VideoParameters *p_Vid ) +{ + Bitstream* buf; + int scene_id, scene_transition_type, second_scene_id; + + buf = malloc(sizeof(Bitstream)); + buf->bitstream_length = size; + buf->streamBuffer = payload; + buf->frame_bitoffset = 0; + + p_Dec->UsedBits = 0; + + scene_id = read_ue_v("SEI: scene_id" , buf, &p_Dec->UsedBits); + scene_transition_type = read_ue_v("SEI: scene_transition_type", buf, &p_Dec->UsedBits); + if ( scene_transition_type > 3 ) + { + second_scene_id = read_ue_v("SEI: scene_transition_type", buf, &p_Dec->UsedBits); + } + +#ifdef PRINT_SCENE_INFORMATION + printf("Scene information SEI message\n"); + printf("scene_transition_type = %d\n", scene_transition_type); + printf("scene_id = %d\n", scene_id); + if ( scene_transition_type > 3 ) + { + printf("second_scene_id = %d\n", second_scene_id); + } +#endif + free( buf ); +#ifdef PRINT_SCENE_INFORMATION +#undef PRINT_SCENE_INFORMATION +#endif +} + + +/*! + ************************************************************************ + * \brief + * Interpret the Filler payload SEI message + * \param payload + * a pointer that point to the sei payload + * \param size + * the size of the sei message + * \param p_Vid + * the image pointer + * + ************************************************************************ + */ +void interpret_filler_payload_info( byte* payload, int size, VideoParameters *p_Vid ) +{ + int payload_cnt = 0; + + while (payload_cnt=16); + + for (offset = 0; offset < 16; offset++) + { +#ifdef PRINT_USER_DATA_UNREGISTERED_INFO + printf("%02x",payload[offset]); +#endif + } + +#ifdef PRINT_USER_DATA_UNREGISTERED_INFO + printf("\n"); +#endif + + while (offset < size) + { + payload_byte = payload[offset]; + offset ++; +#ifdef PRINT_USER_DATA_UNREGISTERED_INFO + printf("Unreg data payload_byte = %d\n", payload_byte); +#endif + } +#ifdef PRINT_USER_DATA_UNREGISTERED_INFO +#undef PRINT_USER_DATA_UNREGISTERED_INFO +#endif +} + + +/*! + ************************************************************************ + * \brief + * Interpret the User data registered by ITU-T T.35 SEI message + * \param payload + * a pointer that point to the sei payload + * \param size + * the size of the sei message + * \param p_Vid + * the image pointer + * + ************************************************************************ + */ +void interpret_user_data_registered_itu_t_t35_info( byte* payload, int size, VideoParameters *p_Vid ) +{ + int offset = 0; + byte itu_t_t35_country_code, itu_t_t35_country_code_extension_byte, payload_byte; + + itu_t_t35_country_code = payload[offset]; + offset++; +#ifdef PRINT_USER_DATA_REGISTERED_ITU_T_T35_INFO + printf("User data registered by ITU-T T.35 SEI message\n"); + printf(" itu_t_t35_country_code = %d \n", itu_t_t35_country_code); +#endif + if(itu_t_t35_country_code == 0xFF) + { + itu_t_t35_country_code_extension_byte = payload[offset]; + offset++; +#ifdef PRINT_USER_DATA_REGISTERED_ITU_T_T35_INFO + printf(" ITU_T_T35_COUNTRY_CODE_EXTENSION_BYTE %d \n", itu_t_t35_country_code_extension_byte); +#endif + } + while (offset < size) + { + payload_byte = payload[offset]; + offset ++; +#ifdef PRINT_USER_DATA_REGISTERED_ITU_T_T35_INFO + printf("itu_t_t35 payload_byte = %d\n", payload_byte); +#endif + } +#ifdef PRINT_USER_DATA_REGISTERED_ITU_T_T35_INFO +#undef PRINT_USER_DATA_REGISTERED_ITU_T_T35_INFO +#endif +} + + +/*! + ************************************************************************ + * \brief + * Interpret the Pan scan rectangle SEI message + * \param payload + * a pointer that point to the sei payload + * \param size + * the size of the sei message + * \param p_Vid + * the image pointer + * + ************************************************************************ + */ +void interpret_pan_scan_rect_info( byte* payload, int size, VideoParameters *p_Vid ) +{ + int pan_scan_rect_cancel_flag; + int pan_scan_cnt_minus1, i; + int pan_scan_rect_repetition_period; + int pan_scan_rect_id, pan_scan_rect_left_offset, pan_scan_rect_right_offset; + int pan_scan_rect_top_offset, pan_scan_rect_bottom_offset; + + Bitstream* buf; + + buf = malloc(sizeof(Bitstream)); + buf->bitstream_length = size; + buf->streamBuffer = payload; + buf->frame_bitoffset = 0; + + p_Dec->UsedBits = 0; + + pan_scan_rect_id = read_ue_v("SEI: pan_scan_rect_id", buf, &p_Dec->UsedBits); + + pan_scan_rect_cancel_flag = read_u_1("SEI: pan_scan_rect_cancel_flag", buf, &p_Dec->UsedBits); + if (!pan_scan_rect_cancel_flag) + { + pan_scan_cnt_minus1 = read_ue_v("SEI: pan_scan_cnt_minus1", buf, &p_Dec->UsedBits); + for (i = 0; i <= pan_scan_cnt_minus1; i++) + { + pan_scan_rect_left_offset = read_se_v("SEI: pan_scan_rect_left_offset" , buf, &p_Dec->UsedBits); + pan_scan_rect_right_offset = read_se_v("SEI: pan_scan_rect_right_offset" , buf, &p_Dec->UsedBits); + pan_scan_rect_top_offset = read_se_v("SEI: pan_scan_rect_top_offset" , buf, &p_Dec->UsedBits); + pan_scan_rect_bottom_offset = read_se_v("SEI: pan_scan_rect_bottom_offset", buf, &p_Dec->UsedBits); +#ifdef PRINT_PAN_SCAN_RECT + printf("Pan scan rectangle SEI message %d/%d\n", i, pan_scan_cnt_minus1); + printf("pan_scan_rect_id = %d\n", pan_scan_rect_id); + printf("pan_scan_rect_left_offset = %d\n", pan_scan_rect_left_offset); + printf("pan_scan_rect_right_offset = %d\n", pan_scan_rect_right_offset); + printf("pan_scan_rect_top_offset = %d\n", pan_scan_rect_top_offset); + printf("pan_scan_rect_bottom_offset = %d\n", pan_scan_rect_bottom_offset); +#endif + } + pan_scan_rect_repetition_period = read_ue_v("SEI: pan_scan_rect_repetition_period", buf, &p_Dec->UsedBits); + } + + free (buf); +#ifdef PRINT_PAN_SCAN_RECT +#undef PRINT_PAN_SCAN_RECT +#endif +} + + +/*! + ************************************************************************ + * \brief + * Interpret the Random access point SEI message + * \param payload + * a pointer that point to the sei payload + * \param size + * the size of the sei message + * \param p_Vid + * the image pointer + * + ************************************************************************ + */ +void interpret_recovery_point_info( byte* payload, int size, VideoParameters *p_Vid ) +{ + int recovery_frame_cnt, exact_match_flag, broken_link_flag, changing_slice_group_idc; + + + Bitstream* buf; + + + buf = malloc(sizeof(Bitstream)); + buf->bitstream_length = size; + buf->streamBuffer = payload; + buf->frame_bitoffset = 0; + + p_Dec->UsedBits = 0; + + recovery_frame_cnt = read_ue_v( "SEI: recovery_frame_cnt" , buf, &p_Dec->UsedBits); + exact_match_flag = read_u_1 ( "SEI: exact_match_flag" , buf, &p_Dec->UsedBits); + broken_link_flag = read_u_1 ( "SEI: broken_link_flag" , buf, &p_Dec->UsedBits); + changing_slice_group_idc = read_u_v ( 2, "SEI: changing_slice_group_idc", buf, &p_Dec->UsedBits); + + p_Vid->recovery_point = 1; + p_Vid->recovery_frame_cnt = recovery_frame_cnt; + +#ifdef PRINT_RECOVERY_POINT + printf("Recovery point SEI message\n"); + printf("recovery_frame_cnt = %d\n", recovery_frame_cnt); + printf("exact_match_flag = %d\n", exact_match_flag); + printf("broken_link_flag = %d\n", broken_link_flag); + printf("changing_slice_group_idc = %d\n", changing_slice_group_idc); +#endif + free (buf); +#ifdef PRINT_RECOVERY_POINT +#undef PRINT_RECOVERY_POINT +#endif +} + + +/*! + ************************************************************************ + * \brief + * Interpret the Decoded Picture Buffer Management Repetition SEI message + * \param payload + * a pointer that point to the sei payload + * \param size + * the size of the sei message + * \param p_Vid + * the image pointer + * + ************************************************************************ + */ +void interpret_dec_ref_pic_marking_repetition_info( byte* payload, int size, VideoParameters *p_Vid, Slice *pSlice ) +{ + int original_idr_flag, original_frame_num; + int original_field_pic_flag, original_bottom_field_flag; + + DecRefPicMarking_t *tmp_drpm; + DecRefPicMarking_t *old_drpm; + int old_idr_flag, old_no_output_of_prior_pics_flag, old_long_term_reference_flag , old_adaptive_ref_pic_buffering_flag; + + Bitstream* buf; + + buf = malloc(sizeof(Bitstream)); + buf->bitstream_length = size; + buf->streamBuffer = payload; + buf->frame_bitoffset = 0; + + p_Dec->UsedBits = 0; + + original_idr_flag = read_u_1 ( "SEI: original_idr_flag" , buf, &p_Dec->UsedBits); + original_frame_num = read_ue_v( "SEI: original_frame_num" , buf, &p_Dec->UsedBits); + + if ( !p_Vid->active_sps->frame_mbs_only_flag ) + { + original_field_pic_flag = read_u_1 ( "SEI: original_field_pic_flag", buf, &p_Dec->UsedBits); + if ( original_field_pic_flag ) + { + original_bottom_field_flag = read_u_1 ( "SEI: original_bottom_field_flag", buf, &p_Dec->UsedBits); + } + } + +#ifdef PRINT_DEC_REF_PIC_MARKING + printf("Decoded Picture Buffer Management Repetition SEI message\n"); + printf("original_idr_flag = %d\n", original_idr_flag); + printf("original_frame_num = %d\n", original_frame_num); + if ( active_sps->frame_mbs_only_flag ) + { + printf("original_field_pic_flag = %d\n", original_field_pic_flag); + if ( original_field_pic_flag ) + { + printf("original_bottom_field_flag = %d\n", original_bottom_field_flag); + } + } +#endif + + // we need to save everything that is probably overwritten in dec_ref_pic_marking() + old_drpm = pSlice->dec_ref_pic_marking_buffer; + old_idr_flag = pSlice->idr_flag; //p_Vid->idr_flag; + + old_no_output_of_prior_pics_flag = pSlice->no_output_of_prior_pics_flag; //p_Vid->no_output_of_prior_pics_flag; + old_long_term_reference_flag = pSlice->long_term_reference_flag; + old_adaptive_ref_pic_buffering_flag = pSlice->adaptive_ref_pic_buffering_flag; + + // set new initial values + //p_Vid->idr_flag = original_idr_flag; + pSlice->idr_flag = original_idr_flag; + pSlice->dec_ref_pic_marking_buffer = NULL; + + dec_ref_pic_marking(p_Vid, buf, pSlice); + + // print out decoded values +#ifdef PRINT_DEC_REF_PIC_MARKING + if (p_Vid->idr_flag) + { + printf("no_output_of_prior_pics_flag = %d\n", p_Vid->no_output_of_prior_pics_flag); + printf("long_term_reference_flag = %d\n", p_Vid->long_term_reference_flag); + } + else + { + printf("adaptive_ref_pic_buffering_flag = %d\n", p_Vid->adaptive_ref_pic_buffering_flag); + if (p_Vid->adaptive_ref_pic_buffering_flag) + { + tmp_drpm=p_Vid->dec_ref_pic_marking_buffer; + while (tmp_drpm != NULL) + { + printf("memory_management_control_operation = %d\n", tmp_drpm->memory_management_control_operation); + + if ((tmp_drpm->memory_management_control_operation==1)||(tmp_drpm->memory_management_control_operation==3)) + { + printf("difference_of_pic_nums_minus1 = %d\n", tmp_drpm->difference_of_pic_nums_minus1); + } + if (tmp_drpm->memory_management_control_operation==2) + { + printf("long_term_pic_num = %d\n", tmp_drpm->long_term_pic_num); + } + if ((tmp_drpm->memory_management_control_operation==3)||(tmp_drpm->memory_management_control_operation==6)) + { + printf("long_term_frame_idx = %d\n", tmp_drpm->long_term_frame_idx); + } + if (tmp_drpm->memory_management_control_operation==4) + { + printf("max_long_term_pic_idx_plus1 = %d\n", tmp_drpm->max_long_term_frame_idx_plus1); + } + tmp_drpm = tmp_drpm->Next; + } + } + } +#endif + + while (pSlice->dec_ref_pic_marking_buffer) + { + tmp_drpm=pSlice->dec_ref_pic_marking_buffer; + + pSlice->dec_ref_pic_marking_buffer=tmp_drpm->Next; + free (tmp_drpm); + } + + // restore old values in p_Vid + pSlice->dec_ref_pic_marking_buffer = old_drpm; + pSlice->idr_flag = old_idr_flag; + pSlice->no_output_of_prior_pics_flag = old_no_output_of_prior_pics_flag; + p_Vid->no_output_of_prior_pics_flag = pSlice->no_output_of_prior_pics_flag; + pSlice->long_term_reference_flag = old_long_term_reference_flag; + pSlice->adaptive_ref_pic_buffering_flag = old_adaptive_ref_pic_buffering_flag; + + free (buf); +#ifdef PRINT_DEC_REF_PIC_MARKING +#undef PRINT_DEC_REF_PIC_MARKING +#endif +} + +/*! + ************************************************************************ + * \brief + * Interpret the Full-frame freeze SEI message + * \param payload + * a pointer that point to the sei payload + * \param size + * the size of the sei message + * \param p_Vid + * the image pointer + * + ************************************************************************ + */ +void interpret_full_frame_freeze_info( byte* payload, int size, VideoParameters *p_Vid ) +{ + int full_frame_freeze_repetition_period; + Bitstream* buf; + + buf = malloc(sizeof(Bitstream)); + buf->bitstream_length = size; + buf->streamBuffer = payload; + buf->frame_bitoffset = 0; + + full_frame_freeze_repetition_period = read_ue_v( "SEI: full_frame_freeze_repetition_period" , buf, &p_Dec->UsedBits); + +#ifdef PRINT_FULL_FRAME_FREEZE_INFO + printf("full_frame_freeze_repetition_period = %d\n", full_frame_freeze_repetition_period); +#endif + + free (buf); +#ifdef PRINT_FULL_FRAME_FREEZE_INFO +#undef PRINT_FULL_FRAME_FREEZE_INFO +#endif +} + + +/*! + ************************************************************************ + * \brief + * Interpret the Full-frame freeze release SEI message + * \param payload + * a pointer that point to the sei payload + * \param size + * the size of the sei message + * \param p_Vid + * the image pointer + * + ************************************************************************ + */ +void interpret_full_frame_freeze_release_info( byte* payload, int size, VideoParameters *p_Vid ) +{ +#ifdef PRINT_FULL_FRAME_FREEZE_RELEASE_INFO + printf("Full-frame freeze release SEI message\n"); + if (size) + { + printf("payload size of this message should be zero, but is %d bytes.\n", size); + } +#endif + +#ifdef PRINT_FULL_FRAME_FREEZE_RELEASE_INFO +#undef PRINT_FULL_FRAME_FREEZE_RELEASE_INFO +#endif +} + +/*! + ************************************************************************ + * \brief + * Interpret the Full-frame snapshot SEI message + * \param payload + * a pointer that point to the sei payload + * \param size + * the size of the sei message + * \param p_Vid + * the image pointer + * + ************************************************************************ + */ +void interpret_full_frame_snapshot_info( byte* payload, int size, VideoParameters *p_Vid ) +{ + int snapshot_id; + + Bitstream* buf; + + buf = malloc(sizeof(Bitstream)); + buf->bitstream_length = size; + buf->streamBuffer = payload; + buf->frame_bitoffset = 0; + + p_Dec->UsedBits = 0; + + snapshot_id = read_ue_v("SEI: snapshot_id", buf, &p_Dec->UsedBits); + +#ifdef PRINT_FULL_FRAME_SNAPSHOT_INFO + printf("Full-frame snapshot SEI message\n"); + printf("snapshot_id = %d\n", snapshot_id); +#endif + free (buf); +#ifdef PRINT_FULL_FRAME_SNAPSHOT_INFO +#undef PRINT_FULL_FRAME_SNAPSHOT_INFO +#endif +} + +/*! + ************************************************************************ + * \brief + * Interpret the Progressive refinement segment start SEI message + * \param payload + * a pointer that point to the sei payload + * \param size + * the size of the sei message + * \param p_Vid + * the image pointer + * + ************************************************************************ + */ +void interpret_progressive_refinement_start_info( byte* payload, int size, VideoParameters *p_Vid ) +{ + int progressive_refinement_id, num_refinement_steps_minus1; + + Bitstream* buf; + + buf = malloc(sizeof(Bitstream)); + buf->bitstream_length = size; + buf->streamBuffer = payload; + buf->frame_bitoffset = 0; + + p_Dec->UsedBits = 0; + + progressive_refinement_id = read_ue_v("SEI: progressive_refinement_id" , buf, &p_Dec->UsedBits); + num_refinement_steps_minus1 = read_ue_v("SEI: num_refinement_steps_minus1", buf, &p_Dec->UsedBits); + +#ifdef PRINT_PROGRESSIVE_REFINEMENT_START_INFO + printf("Progressive refinement segment start SEI message\n"); + printf("progressive_refinement_id = %d\n", progressive_refinement_id); + printf("num_refinement_steps_minus1 = %d\n", num_refinement_steps_minus1); +#endif + free (buf); +#ifdef PRINT_PROGRESSIVE_REFINEMENT_START_INFO +#undef PRINT_PROGRESSIVE_REFINEMENT_START_INFO +#endif +} + + +/*! + ************************************************************************ + * \brief + * Interpret the Progressive refinement segment end SEI message + * \param payload + * a pointer that point to the sei payload + * \param size + * the size of the sei message + * \param p_Vid + * the image pointer + * + ************************************************************************ + */ +void interpret_progressive_refinement_end_info( byte* payload, int size, VideoParameters *p_Vid ) +{ + int progressive_refinement_id; + + Bitstream* buf; + + buf = malloc(sizeof(Bitstream)); + buf->bitstream_length = size; + buf->streamBuffer = payload; + buf->frame_bitoffset = 0; + + p_Dec->UsedBits = 0; + + progressive_refinement_id = read_ue_v("SEI: progressive_refinement_id" , buf, &p_Dec->UsedBits); + +#ifdef PRINT_PROGRESSIVE_REFINEMENT_END_INFO + printf("Progressive refinement segment end SEI message\n"); + printf("progressive_refinement_id = %d\n", progressive_refinement_id); +#endif + free (buf); +#ifdef PRINT_PROGRESSIVE_REFINEMENT_END_INFO +#undef PRINT_PROGRESSIVE_REFINEMENT_END_INFO +#endif +} + + +/*! + ************************************************************************ + * \brief + * Interpret the Motion-constrained slice group set SEI message + * \param payload + * a pointer that point to the sei payload + * \param size + * the size of the sei message + * \param p_Vid + * the image pointer + * + ************************************************************************ + */ +void interpret_motion_constrained_slice_group_set_info( byte* payload, int size, VideoParameters *p_Vid ) +{ + int num_slice_groups_minus1, slice_group_id, exact_match_flag, pan_scan_rect_flag, pan_scan_rect_id; + int i; + int sliceGroupSize; + + Bitstream* buf; + + buf = malloc(sizeof(Bitstream)); + buf->bitstream_length = size; + buf->streamBuffer = payload; + buf->frame_bitoffset = 0; + + p_Dec->UsedBits = 0; + + num_slice_groups_minus1 = read_ue_v("SEI: num_slice_groups_minus1" , buf, &p_Dec->UsedBits); + sliceGroupSize = CeilLog2( num_slice_groups_minus1 + 1 ); +#ifdef PRINT_MOTION_CONST_SLICE_GROUP_SET_INFO + printf("Motion-constrained slice group set SEI message\n"); + printf("num_slice_groups_minus1 = %d\n", num_slice_groups_minus1); +#endif + + for (i=0; i<=num_slice_groups_minus1;i++) + { + + slice_group_id = read_u_v (sliceGroupSize, "SEI: slice_group_id" , buf, &p_Dec->UsedBits); +#ifdef PRINT_MOTION_CONST_SLICE_GROUP_SET_INFO + printf("slice_group_id = %d\n", slice_group_id); +#endif + } + + exact_match_flag = read_u_1("SEI: exact_match_flag" , buf, &p_Dec->UsedBits); + pan_scan_rect_flag = read_u_1("SEI: pan_scan_rect_flag" , buf, &p_Dec->UsedBits); + +#ifdef PRINT_MOTION_CONST_SLICE_GROUP_SET_INFO + printf("exact_match_flag = %d\n", exact_match_flag); + printf("pan_scan_rect_flag = %d\n", pan_scan_rect_flag); +#endif + + if (pan_scan_rect_flag) + { + pan_scan_rect_id = read_ue_v("SEI: pan_scan_rect_id" , buf, &p_Dec->UsedBits); +#ifdef PRINT_MOTION_CONST_SLICE_GROUP_SET_INFO + printf("pan_scan_rect_id = %d\n", pan_scan_rect_id); +#endif + } + + free (buf); +#ifdef PRINT_MOTION_CONST_SLICE_GROUP_SET_INFO +#undef PRINT_MOTION_CONST_SLICE_GROUP_SET_INFO +#endif +} + +/*! + ************************************************************************ + * \brief + * Interpret the film grain characteristics SEI message + * \param payload + * a pointer that point to the sei payload + * \param size + * the size of the sei message + * \param p_Vid + * the image pointer + * + ************************************************************************ + */ +void interpret_film_grain_characteristics_info( byte* payload, int size, VideoParameters *p_Vid ) +{ + int film_grain_characteristics_cancel_flag; + int model_id, separate_colour_description_present_flag; + int film_grain_bit_depth_luma_minus8, film_grain_bit_depth_chroma_minus8, film_grain_full_range_flag, film_grain_colour_primaries, film_grain_transfer_characteristics, film_grain_matrix_coefficients; + int blending_mode_id, log2_scale_factor, comp_model_present_flag[3]; + int num_intensity_intervals_minus1, num_model_values_minus1; + int intensity_interval_lower_bound, intensity_interval_upper_bound; + int comp_model_value; + int film_grain_characteristics_repetition_period; + + int c, i, j; + + Bitstream* buf; + + buf = malloc(sizeof(Bitstream)); + buf->bitstream_length = size; + buf->streamBuffer = payload; + buf->frame_bitoffset = 0; + + film_grain_characteristics_cancel_flag = read_u_1("SEI: film_grain_characteristics_cancel_flag", buf, &p_Dec->UsedBits); +#ifdef PRINT_FILM_GRAIN_CHARACTERISTICS_INFO + printf("film_grain_characteristics_cancel_flag = %d\n", film_grain_characteristics_cancel_flag); +#endif + if(!film_grain_characteristics_cancel_flag) + { + + model_id = read_u_v(2, "SEI: model_id", buf, &p_Dec->UsedBits); + separate_colour_description_present_flag = read_u_1("SEI: separate_colour_description_present_flag", buf, &p_Dec->UsedBits); +#ifdef PRINT_FILM_GRAIN_CHARACTERISTICS_INFO + printf("model_id = %d\n", model_id); + printf("separate_colour_description_present_flag = %d\n", separate_colour_description_present_flag); +#endif + if (separate_colour_description_present_flag) + { + film_grain_bit_depth_luma_minus8 = read_u_v(3, "SEI: film_grain_bit_depth_luma_minus8", buf, &p_Dec->UsedBits); + film_grain_bit_depth_chroma_minus8 = read_u_v(3, "SEI: film_grain_bit_depth_chroma_minus8", buf, &p_Dec->UsedBits); + film_grain_full_range_flag = read_u_v(1, "SEI: film_grain_full_range_flag", buf, &p_Dec->UsedBits); + film_grain_colour_primaries = read_u_v(8, "SEI: film_grain_colour_primaries", buf, &p_Dec->UsedBits); + film_grain_transfer_characteristics = read_u_v(8, "SEI: film_grain_transfer_characteristics", buf, &p_Dec->UsedBits); + film_grain_matrix_coefficients = read_u_v(8, "SEI: film_grain_matrix_coefficients", buf, &p_Dec->UsedBits); +#ifdef PRINT_FILM_GRAIN_CHARACTERISTICS_INFO + printf("film_grain_bit_depth_luma_minus8 = %d\n", film_grain_bit_depth_luma_minus8); + printf("film_grain_bit_depth_chroma_minus8 = %d\n", film_grain_bit_depth_chroma_minus8); + printf("film_grain_full_range_flag = %d\n", film_grain_full_range_flag); + printf("film_grain_colour_primaries = %d\n", film_grain_colour_primaries); + printf("film_grain_transfer_characteristics = %d\n", film_grain_transfer_characteristics); + printf("film_grain_matrix_coefficients = %d\n", film_grain_matrix_coefficients); +#endif + } + blending_mode_id = read_u_v(2, "SEI: blending_mode_id", buf, &p_Dec->UsedBits); + log2_scale_factor = read_u_v(4, "SEI: log2_scale_factor", buf, &p_Dec->UsedBits); +#ifdef PRINT_FILM_GRAIN_CHARACTERISTICS_INFO + printf("blending_mode_id = %d\n", blending_mode_id); + printf("log2_scale_factor = %d\n", log2_scale_factor); +#endif + for (c = 0; c < 3; c ++) + { + comp_model_present_flag[c] = read_u_1("SEI: comp_model_present_flag", buf, &p_Dec->UsedBits); +#ifdef PRINT_FILM_GRAIN_CHARACTERISTICS_INFO + printf("comp_model_present_flag = %d\n", comp_model_present_flag[c]); +#endif + } + for (c = 0; c < 3; c ++) + if (comp_model_present_flag[c]) + { + num_intensity_intervals_minus1 = read_u_v(8, "SEI: num_intensity_intervals_minus1", buf, &p_Dec->UsedBits); + num_model_values_minus1 = read_u_v(3, "SEI: num_model_values_minus1", buf, &p_Dec->UsedBits); +#ifdef PRINT_FILM_GRAIN_CHARACTERISTICS_INFO + printf("num_intensity_intervals_minus1 = %d\n", num_intensity_intervals_minus1); + printf("num_model_values_minus1 = %d\n", num_model_values_minus1); +#endif + for (i = 0; i <= num_intensity_intervals_minus1; i ++) + { + intensity_interval_lower_bound = read_u_v(8, "SEI: intensity_interval_lower_bound", buf, &p_Dec->UsedBits); + intensity_interval_upper_bound = read_u_v(8, "SEI: intensity_interval_upper_bound", buf, &p_Dec->UsedBits); +#ifdef PRINT_FILM_GRAIN_CHARACTERISTICS_INFO + printf("intensity_interval_lower_bound = %d\n", intensity_interval_lower_bound); + printf("intensity_interval_upper_bound = %d\n", intensity_interval_upper_bound); +#endif + for (j = 0; j <= num_model_values_minus1; j++) + { + comp_model_value = read_se_v("SEI: comp_model_value", buf, &p_Dec->UsedBits); +#ifdef PRINT_FILM_GRAIN_CHARACTERISTICS_INFO + printf("comp_model_value = %d\n", comp_model_value); +#endif + } + } + } + film_grain_characteristics_repetition_period = read_ue_v("SEI: film_grain_characteristics_repetition_period", buf, &p_Dec->UsedBits); +#ifdef PRINT_FILM_GRAIN_CHARACTERISTICS_INFO + printf("film_grain_characteristics_repetition_period = %d\n", film_grain_characteristics_repetition_period); +#endif + } + + free (buf); +#ifdef PRINT_FILM_GRAIN_CHARACTERISTICS_INFO +#undef PRINT_FILM_GRAIN_CHARACTERISTICS_INFO +#endif +} + +/*! + ************************************************************************ + * \brief + * Interpret the deblocking filter display preference SEI message + * \param payload + * a pointer that point to the sei payload + * \param size + * the size of the sei message + * \param p_Vid + * the image pointer + * + ************************************************************************ + */ +void interpret_deblocking_filter_display_preference_info( byte* payload, int size, VideoParameters *p_Vid ) +{ + int deblocking_display_preference_cancel_flag; + int display_prior_to_deblocking_preferred_flag, dec_frame_buffering_constraint_flag, deblocking_display_preference_repetition_period; + + Bitstream* buf; + + buf = malloc(sizeof(Bitstream)); + buf->bitstream_length = size; + buf->streamBuffer = payload; + buf->frame_bitoffset = 0; + + deblocking_display_preference_cancel_flag = read_u_1("SEI: deblocking_display_preference_cancel_flag", buf, &p_Dec->UsedBits); +#ifdef PRINT_DEBLOCKING_FILTER_DISPLAY_PREFERENCE_INFO + printf("deblocking_display_preference_cancel_flag = %d\n", deblocking_display_preference_cancel_flag); +#endif + if(!deblocking_display_preference_cancel_flag) + { + display_prior_to_deblocking_preferred_flag = read_u_1("SEI: display_prior_to_deblocking_preferred_flag", buf, &p_Dec->UsedBits); + dec_frame_buffering_constraint_flag = read_u_1("SEI: dec_frame_buffering_constraint_flag", buf, &p_Dec->UsedBits); + deblocking_display_preference_repetition_period = read_ue_v("SEI: deblocking_display_preference_repetition_period", buf, &p_Dec->UsedBits); +#ifdef PRINT_DEBLOCKING_FILTER_DISPLAY_PREFERENCE_INFO + printf("display_prior_to_deblocking_preferred_flag = %d\n", display_prior_to_deblocking_preferred_flag); + printf("dec_frame_buffering_constraint_flag = %d\n", dec_frame_buffering_constraint_flag); + printf("deblocking_display_preference_repetition_period = %d\n", deblocking_display_preference_repetition_period); +#endif + } + + free (buf); +#ifdef PRINT_DEBLOCKING_FILTER_DISPLAY_PREFERENCE_INFO +#undef PRINT_DEBLOCKING_FILTER_DISPLAY_PREFERENCE_INFO +#endif +} + +/*! + ************************************************************************ + * \brief + * Interpret the stereo video info SEI message + * \param payload + * a pointer that point to the sei payload + * \param size + * the size of the sei message + * \param p_Vid + * the image pointer + * + ************************************************************************ + */ +void interpret_stereo_video_info_info( byte* payload, int size, VideoParameters *p_Vid ) +{ + int field_views_flags; + int top_field_is_left_view_flag, current_frame_is_left_view_flag, next_frame_is_second_view_flag; + int left_view_self_contained_flag; + int right_view_self_contained_flag; + + Bitstream* buf; + + buf = malloc(sizeof(Bitstream)); + buf->bitstream_length = size; + buf->streamBuffer = payload; + buf->frame_bitoffset = 0; + + field_views_flags = read_u_1("SEI: field_views_flags", buf, &p_Dec->UsedBits); +#ifdef PRINT_STEREO_VIDEO_INFO_INFO + printf("field_views_flags = %d\n", field_views_flags); +#endif + if (field_views_flags) + { + top_field_is_left_view_flag = read_u_1("SEI: top_field_is_left_view_flag", buf, &p_Dec->UsedBits); +#ifdef PRINT_STEREO_VIDEO_INFO_INFO + printf("top_field_is_left_view_flag = %d\n", top_field_is_left_view_flag); +#endif + } + else + { + current_frame_is_left_view_flag = read_u_1("SEI: current_frame_is_left_view_flag", buf, &p_Dec->UsedBits); + next_frame_is_second_view_flag = read_u_1("SEI: next_frame_is_second_view_flag", buf, &p_Dec->UsedBits); +#ifdef PRINT_STEREO_VIDEO_INFO_INFO + printf("current_frame_is_left_view_flag = %d\n", current_frame_is_left_view_flag); + printf("next_frame_is_second_view_flag = %d\n", next_frame_is_second_view_flag); +#endif + } + + left_view_self_contained_flag = read_u_1("SEI: left_view_self_contained_flag", buf, &p_Dec->UsedBits); + right_view_self_contained_flag = read_u_1("SEI: right_view_self_contained_flag", buf, &p_Dec->UsedBits); +#ifdef PRINT_STEREO_VIDEO_INFO_INFO + printf("left_view_self_contained_flag = %d\n", left_view_self_contained_flag); + printf("right_view_self_contained_flag = %d\n", right_view_self_contained_flag); +#endif + + free (buf); +#ifdef PRINT_STEREO_VIDEO_INFO_INFO +#undef PRINT_STEREO_VIDEO_INFO_INFO +#endif +} + +/*! + ************************************************************************ + * \brief + * Interpret the Reserved SEI message + * \param payload + * a pointer that point to the sei payload + * \param size + * the size of the sei message + * \param p_Vid + * the image pointer + * + ************************************************************************ + */ +void interpret_reserved_info( byte* payload, int size, VideoParameters *p_Vid ) +{ + int offset = 0; + byte payload_byte; + +#ifdef PRINT_RESERVED_INFO + printf("Reserved SEI message\n"); +#endif + + while (offset < size) + { + payload_byte = payload[offset]; + offset ++; +#ifdef PRINT_RESERVED_INFO + printf("reserved_sei_message_payload_byte = %d\n", payload_byte); +#endif + } +#ifdef PRINT_RESERVED_INFO +#undef PRINT_RESERVED_INFO +#endif +} + + +/*! + ************************************************************************ + * \brief + * Interpret the Buffering period SEI message + * \param payload + * a pointer that point to the sei payload + * \param size + * the size of the sei message + * \param p_Vid + * the image pointer + * + ************************************************************************ + */ +void interpret_buffering_period_info( byte* payload, int size, VideoParameters *p_Vid ) +{ + int seq_parameter_set_id, initial_cpb_removal_delay, initial_cpb_removal_delay_offset; + unsigned int k; + + Bitstream* buf; + seq_parameter_set_rbsp_t *sps; + + buf = malloc(sizeof(Bitstream)); + buf->bitstream_length = size; + buf->streamBuffer = payload; + buf->frame_bitoffset = 0; + + p_Dec->UsedBits = 0; + + seq_parameter_set_id = read_ue_v("SEI: seq_parameter_set_id" , buf, &p_Dec->UsedBits); + + sps = &p_Vid->SeqParSet[seq_parameter_set_id]; + + activate_sps(p_Vid, sps); + +#ifdef PRINT_BUFFERING_PERIOD_INFO + printf("Buffering period SEI message\n"); + printf("seq_parameter_set_id = %d\n", seq_parameter_set_id); +#endif + + // Note: NalHrdBpPresentFlag and CpbDpbDelaysPresentFlag can also be set "by some means not specified in this Recommendation | International Standard" + if (sps->vui_parameters_present_flag) + { + + if (sps->vui_seq_parameters.nal_hrd_parameters_present_flag) + { + for (k=0; kvui_seq_parameters.nal_hrd_parameters.cpb_cnt_minus1+1; k++) + { + initial_cpb_removal_delay = read_u_v(sps->vui_seq_parameters.nal_hrd_parameters.initial_cpb_removal_delay_length_minus1+1, "SEI: initial_cpb_removal_delay" , buf, &p_Dec->UsedBits); + initial_cpb_removal_delay_offset = read_u_v(sps->vui_seq_parameters.nal_hrd_parameters.initial_cpb_removal_delay_length_minus1+1, "SEI: initial_cpb_removal_delay_offset" , buf, &p_Dec->UsedBits); + +#ifdef PRINT_BUFFERING_PERIOD_INFO + printf("nal initial_cpb_removal_delay[%d] = %d\n", k, initial_cpb_removal_delay); + printf("nal initial_cpb_removal_delay_offset[%d] = %d\n", k, initial_cpb_removal_delay_offset); +#endif + } + } + + if (sps->vui_seq_parameters.vcl_hrd_parameters_present_flag) + { + for (k=0; kvui_seq_parameters.vcl_hrd_parameters.cpb_cnt_minus1+1; k++) + { + initial_cpb_removal_delay = read_u_v(sps->vui_seq_parameters.vcl_hrd_parameters.initial_cpb_removal_delay_length_minus1+1, "SEI: initial_cpb_removal_delay" , buf, &p_Dec->UsedBits); + initial_cpb_removal_delay_offset = read_u_v(sps->vui_seq_parameters.vcl_hrd_parameters.initial_cpb_removal_delay_length_minus1+1, "SEI: initial_cpb_removal_delay_offset" , buf, &p_Dec->UsedBits); + +#ifdef PRINT_BUFFERING_PERIOD_INFO + printf("vcl initial_cpb_removal_delay[%d] = %d\n", k, initial_cpb_removal_delay); + printf("vcl initial_cpb_removal_delay_offset[%d] = %d\n", k, initial_cpb_removal_delay_offset); +#endif + } + } + } + + free (buf); +#ifdef PRINT_BUFFERING_PERIOD_INFO +#undef PRINT_BUFFERING_PERIOD_INFO +#endif +} + + +/*! + ************************************************************************ + * \brief + * Interpret the Picture timing SEI message + * \param payload + * a pointer that point to the sei payload + * \param size + * the size of the sei message + * \param p_Vid + * the image pointer + * + ************************************************************************ + */ +void interpret_picture_timing_info( byte* payload, int size, VideoParameters *p_Vid ) +{ + seq_parameter_set_rbsp_t *active_sps = p_Vid->active_sps; + + int cpb_removal_delay, dpb_output_delay, pic_struct_present_flag, pic_struct; + int clock_timestamp_flag; + int ct_type, nuit_field_based_flag, counting_type, full_timestamp_flag, discontinuity_flag, cnt_dropped_flag, nframes; + int seconds_value, minutes_value, hours_value, seconds_flag, minutes_flag, hours_flag, time_offset; + int NumClockTs = 0; + int i; + + int cpb_removal_len = 24; + int dpb_output_len = 24; + + Boolean CpbDpbDelaysPresentFlag; + + Bitstream* buf; + + if (NULL==active_sps) + { + fprintf (stdout, "Warning: no active SPS, timing SEI cannot be parsed\n"); + return; + } + + buf = malloc(sizeof(Bitstream)); + buf->bitstream_length = size; + buf->streamBuffer = payload; + buf->frame_bitoffset = 0; + + p_Dec->UsedBits = 0; + + +#ifdef PRINT_PICTURE_TIMING_INFO + printf("Picture timing SEI message\n"); +#endif + + // CpbDpbDelaysPresentFlag can also be set "by some means not specified in this Recommendation | International Standard" + CpbDpbDelaysPresentFlag = (Boolean) (active_sps->vui_parameters_present_flag + && ( (active_sps->vui_seq_parameters.nal_hrd_parameters_present_flag != 0) + ||(active_sps->vui_seq_parameters.vcl_hrd_parameters_present_flag != 0))); + + if (CpbDpbDelaysPresentFlag ) + { + if (active_sps->vui_parameters_present_flag) + { + if (active_sps->vui_seq_parameters.nal_hrd_parameters_present_flag) + { + cpb_removal_len = active_sps->vui_seq_parameters.nal_hrd_parameters.cpb_removal_delay_length_minus1 + 1; + dpb_output_len = active_sps->vui_seq_parameters.nal_hrd_parameters.dpb_output_delay_length_minus1 + 1; + } + else if (active_sps->vui_seq_parameters.vcl_hrd_parameters_present_flag) + { + cpb_removal_len = active_sps->vui_seq_parameters.vcl_hrd_parameters.cpb_removal_delay_length_minus1 + 1; + dpb_output_len = active_sps->vui_seq_parameters.vcl_hrd_parameters.dpb_output_delay_length_minus1 + 1; + } + } + + if ((active_sps->vui_seq_parameters.nal_hrd_parameters_present_flag)|| + (active_sps->vui_seq_parameters.vcl_hrd_parameters_present_flag)) + { + cpb_removal_delay = read_u_v(cpb_removal_len, "SEI: cpb_removal_delay" , buf, &p_Dec->UsedBits); + dpb_output_delay = read_u_v(dpb_output_len, "SEI: dpb_output_delay" , buf, &p_Dec->UsedBits); +#ifdef PRINT_PICTURE_TIMING_INFO + printf("cpb_removal_delay = %d\n",cpb_removal_delay); + printf("dpb_output_delay = %d\n",dpb_output_delay); +#endif + } + } + + if (!active_sps->vui_parameters_present_flag) + { + pic_struct_present_flag = 0; + } + else + { + pic_struct_present_flag = active_sps->vui_seq_parameters.pic_struct_present_flag; + } + + if (pic_struct_present_flag) + { + pic_struct = read_u_v(4, "SEI: pic_struct" , buf, &p_Dec->UsedBits); +#ifdef PRINT_PICTURE_TIMING_INFO + printf("pic_struct = %d\n",pic_struct); +#endif + switch (pic_struct) + { + case 0: + case 1: + case 2: + NumClockTs = 1; + break; + case 3: + case 4: + case 7: + NumClockTs = 2; + break; + case 5: + case 6: + case 8: + NumClockTs = 3; + break; + default: + error("reserved pic_struct used (can't determine NumClockTs)", 500); + } + for (i=0; iUsedBits); +#ifdef PRINT_PICTURE_TIMING_INFO + printf("clock_timestamp_flag = %d\n",clock_timestamp_flag); +#endif + if (clock_timestamp_flag) + { + ct_type = read_u_v(2, "SEI: ct_type" , buf, &p_Dec->UsedBits); + nuit_field_based_flag = read_u_1( "SEI: nuit_field_based_flag" , buf, &p_Dec->UsedBits); + counting_type = read_u_v(5, "SEI: counting_type" , buf, &p_Dec->UsedBits); + full_timestamp_flag = read_u_1( "SEI: full_timestamp_flag" , buf, &p_Dec->UsedBits); + discontinuity_flag = read_u_1( "SEI: discontinuity_flag" , buf, &p_Dec->UsedBits); + cnt_dropped_flag = read_u_1( "SEI: cnt_dropped_flag" , buf, &p_Dec->UsedBits); + nframes = read_u_v(8, "SEI: nframes" , buf, &p_Dec->UsedBits); + +#ifdef PRINT_PICTURE_TIMING_INFO + printf("ct_type = %d\n",ct_type); + printf("nuit_field_based_flag = %d\n",nuit_field_based_flag); + printf("full_timestamp_flag = %d\n",full_timestamp_flag); + printf("discontinuity_flag = %d\n",discontinuity_flag); + printf("cnt_dropped_flag = %d\n",cnt_dropped_flag); + printf("nframes = %d\n",nframes); +#endif + if (full_timestamp_flag) + { + seconds_value = read_u_v(6, "SEI: seconds_value" , buf, &p_Dec->UsedBits); + minutes_value = read_u_v(6, "SEI: minutes_value" , buf, &p_Dec->UsedBits); + hours_value = read_u_v(5, "SEI: hours_value" , buf, &p_Dec->UsedBits); +#ifdef PRINT_PICTURE_TIMING_INFO + printf("seconds_value = %d\n",seconds_value); + printf("minutes_value = %d\n",minutes_value); + printf("hours_value = %d\n",hours_value); +#endif + } + else + { + seconds_flag = read_u_1( "SEI: seconds_flag" , buf, &p_Dec->UsedBits); +#ifdef PRINT_PICTURE_TIMING_INFO + printf("seconds_flag = %d\n",seconds_flag); +#endif + if (seconds_flag) + { + seconds_value = read_u_v(6, "SEI: seconds_value" , buf, &p_Dec->UsedBits); + minutes_flag = read_u_1( "SEI: minutes_flag" , buf, &p_Dec->UsedBits); +#ifdef PRINT_PICTURE_TIMING_INFO + printf("seconds_value = %d\n",seconds_value); + printf("minutes_flag = %d\n",minutes_flag); +#endif + if(minutes_flag) + { + minutes_value = read_u_v(6, "SEI: minutes_value" , buf, &p_Dec->UsedBits); + hours_flag = read_u_1( "SEI: hours_flag" , buf, &p_Dec->UsedBits); +#ifdef PRINT_PICTURE_TIMING_INFO + printf("minutes_value = %d\n",minutes_value); + printf("hours_flag = %d\n",hours_flag); +#endif + if(hours_flag) + { + hours_value = read_u_v(5, "SEI: hours_value" , buf, &p_Dec->UsedBits); +#ifdef PRINT_PICTURE_TIMING_INFO + printf("hours_value = %d\n",hours_value); +#endif + } + } + } + } + { + int time_offset_length; + if (active_sps->vui_seq_parameters.vcl_hrd_parameters_present_flag) + time_offset_length = active_sps->vui_seq_parameters.vcl_hrd_parameters.time_offset_length; + else if (active_sps->vui_seq_parameters.nal_hrd_parameters_present_flag) + time_offset_length = active_sps->vui_seq_parameters.nal_hrd_parameters.time_offset_length; + else + time_offset_length = 24; + if (time_offset_length) + time_offset = read_i_v(time_offset_length, "SEI: time_offset" , buf, &p_Dec->UsedBits); + else + time_offset = 0; +#ifdef PRINT_PICTURE_TIMING_INFO + printf("time_offset = %d\n",time_offset); +#endif + } + } + } + } + + free (buf); +#ifdef PRINT_PICTURE_TIMING_INFO +#undef PRINT_PICTURE_TIMING_INFO +#endif +} + +/*! + ************************************************************************ + * \brief + * Interpret the Frame Packing Arrangement SEI message + * \param payload + * a pointer that point to the sei payload + * \param size + * the size of the sei message + * \param p_Vid + * the image pointer + ************************************************************************ + */ +void interpret_frame_packing_arrangement_info( byte* payload, int size, VideoParameters *p_Vid ) +{ + frame_packing_arrangement_information_struct seiFramePackingArrangement; + Bitstream* buf; + + buf = malloc(sizeof(Bitstream)); + buf->bitstream_length = size; + buf->streamBuffer = payload; + buf->frame_bitoffset = 0; + + p_Dec->UsedBits = 0; + +#ifdef PRINT_FRAME_PACKING_ARRANGEMENT_INFO + printf("Frame packing arrangement SEI message\n"); +#endif + + seiFramePackingArrangement.frame_packing_arrangement_id = (unsigned int)read_ue_v( "SEI: frame_packing_arrangement_id", buf, &p_Dec->UsedBits ); + seiFramePackingArrangement.frame_packing_arrangement_cancel_flag = read_u_1( "SEI: frame_packing_arrangement_cancel_flag", buf, &p_Dec->UsedBits ); +#ifdef PRINT_FRAME_PACKING_ARRANGEMENT_INFO + printf("frame_packing_arrangement_id = %d\n", seiFramePackingArrangement.frame_packing_arrangement_id); + printf("frame_packing_arrangement_cancel_flag = %d\n", seiFramePackingArrangement.frame_packing_arrangement_cancel_flag); +#endif + if ( seiFramePackingArrangement.frame_packing_arrangement_cancel_flag == FALSE ) + { + seiFramePackingArrangement.frame_packing_arrangement_type = (unsigned char)read_u_v( 7, "SEI: frame_packing_arrangement_type", buf, &p_Dec->UsedBits ); + seiFramePackingArrangement.quincunx_sampling_flag = read_u_1( "SEI: quincunx_sampling_flag", buf, &p_Dec->UsedBits ); + seiFramePackingArrangement.content_interpretation_type = (unsigned char)read_u_v( 6, "SEI: content_interpretation_type", buf, &p_Dec->UsedBits ); + seiFramePackingArrangement.spatial_flipping_flag = read_u_1( "SEI: spatial_flipping_flag", buf, &p_Dec->UsedBits ); + seiFramePackingArrangement.frame0_flipped_flag = read_u_1( "SEI: frame0_flipped_flag", buf, &p_Dec->UsedBits ); + seiFramePackingArrangement.field_views_flag = read_u_1( "SEI: field_views_flag", buf, &p_Dec->UsedBits ); + seiFramePackingArrangement.current_frame_is_frame0_flag = read_u_1( "SEI: current_frame_is_frame0_flag", buf, &p_Dec->UsedBits ); + seiFramePackingArrangement.frame0_self_contained_flag = read_u_1( "SEI: frame0_self_contained_flag", buf, &p_Dec->UsedBits ); + seiFramePackingArrangement.frame1_self_contained_flag = read_u_1( "SEI: frame1_self_contained_flag", buf, &p_Dec->UsedBits ); +#ifdef PRINT_FRAME_PACKING_ARRANGEMENT_INFO + printf("frame_packing_arrangement_type = %d\n", seiFramePackingArrangement.frame_packing_arrangement_type); + printf("quincunx_sampling_flag = %d\n", seiFramePackingArrangement.quincunx_sampling_flag); + printf("content_interpretation_type = %d\n", seiFramePackingArrangement.content_interpretation_type); + printf("spatial_flipping_flag = %d\n", seiFramePackingArrangement.spatial_flipping_flag); + printf("frame0_flipped_flag = %d\n", seiFramePackingArrangement.frame0_flipped_flag); + printf("field_views_flag = %d\n", seiFramePackingArrangement.field_views_flag); + printf("current_frame_is_frame0_flag = %d\n", seiFramePackingArrangement.current_frame_is_frame0_flag); + printf("frame0_self_contained_flag = %d\n", seiFramePackingArrangement.frame0_self_contained_flag); + printf("frame1_self_contained_flag = %d\n", seiFramePackingArrangement.frame1_self_contained_flag); +#endif + if ( seiFramePackingArrangement.quincunx_sampling_flag == FALSE && seiFramePackingArrangement.frame_packing_arrangement_type != 5 ) + { + seiFramePackingArrangement.frame0_grid_position_x = (unsigned char)read_u_v( 4, "SEI: frame0_grid_position_x", buf, &p_Dec->UsedBits ); + seiFramePackingArrangement.frame0_grid_position_y = (unsigned char)read_u_v( 4, "SEI: frame0_grid_position_y", buf, &p_Dec->UsedBits ); + seiFramePackingArrangement.frame1_grid_position_x = (unsigned char)read_u_v( 4, "SEI: frame1_grid_position_x", buf, &p_Dec->UsedBits ); + seiFramePackingArrangement.frame1_grid_position_y = (unsigned char)read_u_v( 4, "SEI: frame1_grid_position_y", buf, &p_Dec->UsedBits ); +#ifdef PRINT_FRAME_PACKING_ARRANGEMENT_INFO + printf("frame0_grid_position_x = %d\n", seiFramePackingArrangement.frame0_grid_position_x); + printf("frame0_grid_position_y = %d\n", seiFramePackingArrangement.frame0_grid_position_y); + printf("frame1_grid_position_x = %d\n", seiFramePackingArrangement.frame1_grid_position_x); + printf("frame1_grid_position_y = %d\n", seiFramePackingArrangement.frame1_grid_position_y); +#endif + } + seiFramePackingArrangement.frame_packing_arrangement_reserved_byte = (unsigned char)read_u_v( 8, "SEI: frame_packing_arrangement_reserved_byte", buf, &p_Dec->UsedBits ); + seiFramePackingArrangement.frame_packing_arrangement_repetition_period = (unsigned int)read_ue_v( "SEI: frame_packing_arrangement_repetition_period", buf, &p_Dec->UsedBits ); +#ifdef PRINT_FRAME_PACKING_ARRANGEMENT_INFO + printf("frame_packing_arrangement_reserved_byte = %d\n", seiFramePackingArrangement.frame_packing_arrangement_reserved_byte); + printf("frame_packing_arrangement_repetition_period = %d\n", seiFramePackingArrangement.frame_packing_arrangement_repetition_period); +#endif + } + seiFramePackingArrangement.frame_packing_arrangement_extension_flag = read_u_1( "SEI: frame_packing_arrangement_extension_flag", buf, &p_Dec->UsedBits ); +#ifdef PRINT_FRAME_PACKING_ARRANGEMENT_INFO + printf("frame_packing_arrangement_extension_flag = %d\n", seiFramePackingArrangement.frame_packing_arrangement_extension_flag); +#endif + + free (buf); +#ifdef PRINT_FRAME_PACKING_ARRANGEMENT_INFO +#undef PRINT_FRAME_PACKING_ARRANGEMENT_INFO +#endif +} + +/*! + ************************************************************************ + * \brief + * Interpret the HDR tone-mapping SEI message (JVT-T060) + * \param payload + * a pointer that point to the sei payload + * \param size + * the size of the sei message + * \param p_Vid + * the image pointer + * + ************************************************************************ + */ +typedef struct +{ + unsigned int tone_map_id; + unsigned char tone_map_cancel_flag; + unsigned int tone_map_repetition_period; + unsigned char coded_data_bit_depth; + unsigned char sei_bit_depth; + unsigned int model_id; + // variables for model 0 + int min_value; + int max_value; + // variables for model 1 + int sigmoid_midpoint; + int sigmoid_width; + // variables for model 2 + int start_of_coded_interval[1<bitstream_length = size; + buf->streamBuffer = payload; + buf->frame_bitoffset = 0; + + seiToneMappingTmp.tone_map_id = read_ue_v("SEI: tone_map_id", buf, &p_Dec->UsedBits); + seiToneMappingTmp.tone_map_cancel_flag = (unsigned char) read_u_1("SEI: tone_map_cancel_flag", buf, &p_Dec->UsedBits); + +#ifdef PRINT_TONE_MAPPING + printf("Tone-mapping SEI message\n"); + printf("tone_map_id = %d\n", seiToneMappingTmp.tone_map_id); + + if (seiToneMappingTmp.tone_map_id != 0) + printf("WARNING! Tone_map_id != 0, print the SEI message info only. The tone mapping is actually applied only when Tone_map_id==0\n\n"); + printf("tone_map_cancel_flag = %d\n", seiToneMappingTmp.tone_map_cancel_flag); +#endif + + if (!seiToneMappingTmp.tone_map_cancel_flag) + { + seiToneMappingTmp.tone_map_repetition_period = read_ue_v( "SEI: tone_map_repetition_period", buf, &p_Dec->UsedBits); + seiToneMappingTmp.coded_data_bit_depth = (unsigned char)read_u_v (8,"SEI: coded_data_bit_depth" , buf, &p_Dec->UsedBits); + seiToneMappingTmp.sei_bit_depth = (unsigned char)read_u_v (8,"SEI: sei_bit_depth" , buf, &p_Dec->UsedBits); + seiToneMappingTmp.model_id = read_ue_v( "SEI: model_id" , buf, &p_Dec->UsedBits); + +#ifdef PRINT_TONE_MAPPING + printf("tone_map_repetition_period = %d\n", seiToneMappingTmp.tone_map_repetition_period); + printf("coded_data_bit_depth = %d\n", seiToneMappingTmp.coded_data_bit_depth); + printf("sei_bit_depth = %d\n", seiToneMappingTmp.sei_bit_depth); + printf("model_id = %d\n", seiToneMappingTmp.model_id); +#endif + + max_coded_num = 1<UsedBits); + seiToneMappingTmp.max_value = read_u_v (32,"SEI: min_value", buf, &p_Dec->UsedBits); +#ifdef PRINT_TONE_MAPPING + printf("min_value = %d, max_value = %d\n", seiToneMappingTmp.min_value, seiToneMappingTmp.max_value); +#endif + } + else if (seiToneMappingTmp.model_id == 1) + { // sigmoidal mapping + seiToneMappingTmp.sigmoid_midpoint = read_u_v (32,"SEI: sigmoid_midpoint", buf, &p_Dec->UsedBits); + seiToneMappingTmp.sigmoid_width = read_u_v (32,"SEI: sigmoid_width", buf, &p_Dec->UsedBits); +#ifdef PRINT_TONE_MAPPING + printf("sigmoid_midpoint = %d, sigmoid_width = %d\n", seiToneMappingTmp.sigmoid_midpoint, seiToneMappingTmp.sigmoid_width); +#endif + } + else if (seiToneMappingTmp.model_id == 2) + { // user defined table mapping + for (i=0; i>3)<<3), "SEI: start_of_coded_interval" , buf, &p_Dec->UsedBits); +#ifdef PRINT_TONE_MAPPING // too long to print + //printf("start_of_coded_interval[%d] = %d\n", i, seiToneMappingTmp.start_of_coded_interval[i]); +#endif + } + } + else if (seiToneMappingTmp.model_id == 3) + { // piece-wise linear mapping + seiToneMappingTmp.num_pivots = read_u_v (16,"SEI: num_pivots", buf, &p_Dec->UsedBits); +#ifdef PRINT_TONE_MAPPING + printf("num_pivots = %d\n", seiToneMappingTmp.num_pivots); +#endif + seiToneMappingTmp.coded_pivot_value[0] = 0; + seiToneMappingTmp.sei_pivot_value[0] = 0; + seiToneMappingTmp.coded_pivot_value[seiToneMappingTmp.num_pivots+1] = max_coded_num-1; + seiToneMappingTmp.sei_pivot_value[seiToneMappingTmp.num_pivots+1] = max_output_num-1; + + for (i=1; i < seiToneMappingTmp.num_pivots+1; i++) + { + seiToneMappingTmp.coded_pivot_value[i] = read_u_v( (((seiToneMappingTmp.coded_data_bit_depth+7)>>3)<<3), "SEI: coded_pivot_value", buf, &p_Dec->UsedBits); + seiToneMappingTmp.sei_pivot_value[i] = read_u_v( (((seiToneMappingTmp.sei_bit_depth+7)>>3)<<3), "SEI: sei_pivot_value", buf, &p_Dec->UsedBits); +#ifdef PRINT_TONE_MAPPING + printf("coded_pivot_value[%d] = %d, sei_pivot_value[%d] = %d\n", i, seiToneMappingTmp.coded_pivot_value[i], i, seiToneMappingTmp.sei_pivot_value[i]); +#endif + } + } + +#if (ENABLE_OUTPUT_TONEMAPPING) + // Currently, only when the map_id == 0, the tone-mapping is actually applied. + if (seiToneMappingTmp.tone_map_id== 0) + { + int j; + p_Vid->seiToneMapping->seiHasTone_mapping = TRUE; + p_Vid->seiToneMapping->tone_map_repetition_period = seiToneMappingTmp.tone_map_repetition_period; + p_Vid->seiToneMapping->coded_data_bit_depth = seiToneMappingTmp.coded_data_bit_depth; + p_Vid->seiToneMapping->sei_bit_depth = seiToneMappingTmp.sei_bit_depth; + p_Vid->seiToneMapping->model_id = seiToneMappingTmp.model_id; + p_Vid->seiToneMapping->count = 0; + + // generate the look up table of tone mapping + switch(seiToneMappingTmp.model_id) + { + case 0: // linear mapping with clipping + for (i=0; i<=seiToneMappingTmp.min_value; i++) + p_Vid->seiToneMapping->lut[i] = 0; + + for (i=seiToneMappingTmp.min_value+1; i < seiToneMappingTmp.max_value; i++) + p_Vid->seiToneMapping->lut[i] = (imgpel) ((i-seiToneMappingTmp.min_value) * (max_output_num-1)/(seiToneMappingTmp.max_value- seiToneMappingTmp.min_value)); + + for (i=seiToneMappingTmp.max_value; iseiToneMapping->lut[i] = (imgpel) (max_output_num - 1); + break; + case 1: // sigmoid mapping + + for (i=0; i < max_coded_num; i++) + { + double tmp = 1.0 + exp( -6*(double)(i-seiToneMappingTmp.sigmoid_midpoint)/seiToneMappingTmp.sigmoid_width); + p_Vid->seiToneMapping->lut[i] = (imgpel)( (double)(max_output_num-1)/ tmp + 0.5); + } + break; + case 2: // user defined table + if (0 < max_output_num-1) + { + for (j=0; jseiToneMapping->lut[i] = (imgpel) j; + } + } + p_Vid->seiToneMapping->lut[i] = (imgpel) (max_output_num - 1); + } + break; + case 3: // piecewise linear mapping + for (j=0; jseiToneMapping->lut[i] = (imgpel) (seiToneMappingTmp.sei_pivot_value[j] + (int)(( (i - seiToneMappingTmp.coded_pivot_value[j]) * slope))); + } + } + break; + + default: + break; + } // end switch + } +#endif + } // end !tone_map_cancel_flag + free (buf); +} + +#if (ENABLE_OUTPUT_TONEMAPPING) +// tone map using the look-up-table generated according to SEI tone mapping message +void tone_map (imgpel** imgX, imgpel* lut, int size_x, int size_y) +{ + int i, j; + + for(i=0;iseiHasTone_mapping = FALSE; + seiToneMapping->count = 0; +} + +void update_tone_mapping_sei(ToneMappingSEI *seiToneMapping) +{ + + if(seiToneMapping->tone_map_repetition_period == 0) + { + seiToneMapping->seiHasTone_mapping = FALSE; + seiToneMapping->count = 0; + } + else if (seiToneMapping->tone_map_repetition_period>1) + { + seiToneMapping->count++; + if (seiToneMapping->count>=seiToneMapping->tone_map_repetition_period) + { + seiToneMapping->seiHasTone_mapping = FALSE; + seiToneMapping->count = 0; + } + } +} +#endif + +/*! + ************************************************************************ + * \brief + * Interpret the post filter hints SEI message (JVT-U035) + * \param payload + * a pointer that point to the sei payload + * \param size + * the size of the sei message + * \param p_Vid + * the image pointer + * + ************************************************************************ + */ +void interpret_post_filter_hints_info( byte* payload, int size, VideoParameters *p_Vid ) +{ + Bitstream* buf; + unsigned int filter_hint_size_y, filter_hint_size_x, filter_hint_type, color_component, cx, cy, additional_extension_flag; + int ***filter_hint; + + buf = malloc(sizeof(Bitstream)); + buf->bitstream_length = size; + buf->streamBuffer = payload; + buf->frame_bitoffset = 0; + + p_Dec->UsedBits = 0; + + filter_hint_size_y = read_ue_v("SEI: filter_hint_size_y", buf, &p_Dec->UsedBits); // interpret post-filter hint SEI here + filter_hint_size_x = read_ue_v("SEI: filter_hint_size_x", buf, &p_Dec->UsedBits); // interpret post-filter hint SEI here + filter_hint_type = read_u_v(2, "SEI: filter_hint_type", buf, &p_Dec->UsedBits); // interpret post-filter hint SEI here + + get_mem3Dint (&filter_hint, 3, filter_hint_size_y, filter_hint_size_x); + + for (color_component = 0; color_component < 3; color_component ++) + for (cy = 0; cy < filter_hint_size_y; cy ++) + for (cx = 0; cx < filter_hint_size_x; cx ++) + filter_hint[color_component][cy][cx] = read_se_v("SEI: filter_hint", buf, &p_Dec->UsedBits); // interpret post-filter hint SEI here + + additional_extension_flag = read_u_1("SEI: additional_extension_flag", buf, &p_Dec->UsedBits); // interpret post-filter hint SEI here + +#ifdef PRINT_POST_FILTER_HINT_INFO + printf(" Post-filter hint SEI message\n"); + printf(" post_filter_hint_size_y %d \n", filter_hint_size_y); + printf(" post_filter_hint_size_x %d \n", filter_hint_size_x); + printf(" post_filter_hint_type %d \n", filter_hint_type); + for (color_component = 0; color_component < 3; color_component ++) + for (cy = 0; cy < filter_hint_size_y; cy ++) + for (cx = 0; cx < filter_hint_size_x; cx ++) + printf(" post_filter_hint[%d][%d][%d] %d \n", color_component, cy, cx, filter_hint[color_component][cy][cx]); + + printf(" additional_extension_flag %d \n", additional_extension_flag); + +#undef PRINT_POST_FILTER_HINT_INFO +#endif + + free_mem3Dint (filter_hint); + free( buf ); +} diff --git a/ldecod/src/stream_parser.c b/ldecod/src/stream_parser.c new file mode 100644 index 0000000..8675f14 --- /dev/null +++ b/ldecod/src/stream_parser.c @@ -0,0 +1,546 @@ +/*! + ************************************************************************************* + * \file stream_parser.c + * + * \brief + * Functions for dump NALU information and simluation NAL loss + * Functions to parser the decoded h264 stream + * \date + * 29.11.2011 + * + * \version + * 1.0 + * + * \author + * Jo�o Carreira + * + ************************************************************************************* + */ + +#include "global.h" +#include "nalucommon.h" +#include "memalloc.h" +#include "mbuffer.h" + +#include "ifunctions.h" + +//! NAL unit structure + +typedef struct nalu_parser_t +{ + int need_parse; + int *lost_nalu; + int n_NALU; +#if (MVC_EXTENSION_ENABLE) + int view_id; + int frame_no[MAX_VIEW_NUM]; +#endif + FILE *p_FileDumpNAL; + +} NALU_PARSER_t; + +/*! + ************************************************************************ + * \brief + * Alloc NALU Parser + * @return: 0 if no errors + ************************************************************************ + */ +void init_parse_nalu( VideoParameters *p_Vid) +{ + NALU_PARSER_t *p_NAlParser; + + if( (p_NAlParser = (NALU_PARSER_t*) calloc( 1, sizeof(NALU_PARSER_t))) == NULL ) + no_mem_exit( "alloc_video_params: p_Vid->p_ParserNAL"); + + memset( p_NAlParser, 0, sizeof(NALU_PARSER_t)); + + p_NAlParser->n_NALU = 0; + p_NAlParser->need_parse = 1; + + p_NAlParser->p_FileDumpNAL = NULL; + if( p_Vid->p_Inp->dump_nalu ) + { + char FileName[FILE_NAME_SIZE], chBuf[FILE_NAME_SIZE], *pch; + strcpy( chBuf, p_Vid->p_Inp->infile); + pch = strrchr( chBuf, '.'); + if( pch ) + *pch = '\0'; + if( strcmp( "nul", chBuf) ) + { + sprintf( FileName, "%s_NALUDebug.txt", chBuf); + p_NAlParser->p_FileDumpNAL = fopen( FileName, "w"); + } + } + if( p_NAlParser->p_FileDumpNAL ) + { + fprintf( p_NAlParser->p_FileDumpNAL, " Number | Type | Length |\n"); + fflush( p_NAlParser->p_FileDumpNAL); + } + + if( p_NAlParser->lost_nalu == 0 ) // frst time + { + int i = 0, str_len = strlen( p_Vid->p_Inp->drop_nalu); + int n_num = 0; + char *p_cur_init, *p_cur_end; + + if( str_len ) + { + p_NAlParser->lost_nalu = (int *) malloc( str_len * sizeof(int)); + /* Parser the vector with Losted NALU */ + p_cur_end = p_cur_init = p_Vid->p_Inp->drop_nalu; + while( i < str_len ) + { + if( *p_cur_end == '_' ) + { + *p_cur_end = '\0'; + p_NAlParser->lost_nalu[n_num] = atoi( p_cur_init); + p_cur_init = p_cur_end = p_cur_end + 1; + + n_num++; + } + p_cur_end++; + i++; + } + + *p_cur_end = '\0'; + p_NAlParser->lost_nalu[n_num] = atoi( p_cur_init); + p_cur_init = p_cur_end = p_cur_end + 1; + n_num++; + p_NAlParser->lost_nalu[n_num] = -1; + n_num++; + p_NAlParser->lost_nalu = (int *) realloc( p_NAlParser->lost_nalu, n_num * sizeof(int)); + } + } + + p_Vid->p_ParserNAL = p_NAlParser; + return; +} + +/*! + ************************************************************************ + * \brief + * Free NALU Parser + * @return: void + ************************************************************************ + */ +void free_parse_nalu( VideoParameters *p_Vid) +{ + NALU_PARSER_t *p_NAlParser = (NALU_PARSER_t *) p_Vid->p_ParserNAL; + + if( p_NAlParser->p_FileDumpNAL ) + { + fclose( p_NAlParser->p_FileDumpNAL); + } + + free( p_NAlParser->lost_nalu); + free( p_NAlParser); +} + +static int check_nal( int *lost_nalu, int n_nalu) +{ + + do + { + if( *lost_nalu == n_nalu ) + if( *(lost_nalu + 1) == -1 ) + return -1; + else + return 0; + lost_nalu++; + } while( *lost_nalu != -1 ); + return 1; +} + +/*! + ************************************************************************ + * \brief + * Return the status of current NALU + * Dump to a file the found NALU + * @return: 1 for error free and 0 for an erroneous NALU + ************************************************************************ + */ +int parse_nalu( VideoParameters *p_Vid, NALU_t *nalu) +{ + NALU_PARSER_t *p_NAlParser = (NALU_PARSER_t *) p_Vid->p_ParserNAL; + int iRet = 1; + int view_id = 0; + if( p_NAlParser->p_FileDumpNAL ) + { + +#if( MVC_EXTENSION_ENABLE ) + if( nalu->nal_unit_type == 5 || nalu->nal_unit_type == 1 ) + { + p_NAlParser->frame_no[0]++; + view_id = 0; + } + else if( nalu->nal_unit_type == 20 ) + { + //p_NAlParser->frame_no[nalu->view_id]++; + //view_id = 1; + } + fprintf( p_NAlParser->p_FileDumpNAL, " %3d | %2d | %5d | %5d |\n", p_NAlParser->n_NALU, nalu->nal_unit_type, nalu->len, + p_NAlParser->frame_no[view_id]); + +#else + fprintf(p_NAlParser->p_FileDumpNAL, " %3d | %2d | %5d | %5d |\n", p_NAlParser->n_NALU, nalu->nal_unit_type, nalu->len, 0 ); +#endif + fflush( p_NAlParser->p_FileDumpNAL); + } + + if( p_NAlParser->need_parse && p_NAlParser->lost_nalu ) + { + if( (iRet = check_nal( p_NAlParser->lost_nalu, p_NAlParser->n_NALU)) <= 0 ) + { + if( iRet == -1 ) + p_NAlParser->need_parse = 0; + + printf( "[NALU Parser] Lost NAL Unit: %d\n", p_NAlParser->n_NALU); + iRet = 0; + } + else + { + iRet = 1; + } + } + + p_NAlParser->n_NALU++; + + return iRet; +} + +/*! + ************************************************************************ + * \brief + * Prediction Parser + ************************************************************************ + */ + +typedef struct prediction_parser_t +{ + FILE *p_File[MAX_VIEW_NUM]; + +} PREDICTION_PARSER; + +void init_prediction_parser( VideoParameters *p_Vid) +{ + PREDICTION_PARSER *PredParser; + char FileName[FILE_NAME_SIZE], chBuf[FILE_NAME_SIZE], *pch; + + p_Vid->p_PredictionParser = NULL; + + if( p_Vid->p_Inp->PredictionParser ) + { + + p_Vid->p_PredictionParser = malloc( sizeof(PREDICTION_PARSER)); + memset( p_Vid->p_PredictionParser, 0, sizeof(PREDICTION_PARSER)); + PredParser = (PREDICTION_PARSER *) p_Vid->p_PredictionParser; + + strcpy( chBuf, p_Vid->p_Inp->infile); + pch = strrchr( chBuf, '.'); + if( pch ) + *pch = '\0'; + if( strcmp( "nul", chBuf) ) + { + sprintf( FileName, "%s_ViewID00_PredictionParser.txt", chBuf); + PredParser->p_File[0] = fopen( FileName, "w"); + sprintf( FileName, "%s_ViewID01_PredictionParser.txt", chBuf); + PredParser->p_File[1] = fopen( FileName, "w"); + } + } +} +void free_prediction_parser( VideoParameters *p_Vid) +{ + int i; + PREDICTION_PARSER *PredParser = (PREDICTION_PARSER *) p_Vid->p_PredictionParser; + + if( PredParser != NULL ) + { + + for( i = 0; i < MAX_VIEW_NUM; i++ ) + { + if( PredParser->p_File[i] ) + fclose( PredParser->p_File[i]); + } + free( PredParser); + } +} +void parse_prediction( VideoParameters *p_Vid, Slice *CurrSlice, StorablePicture *CurrPic) +{ + PREDICTION_PARSER *PredParser = (PREDICTION_PARSER *) p_Vid->p_PredictionParser; + FILE *pFile = PredParser->p_File[CurrPic->view_id]; + int FrameNo = CurrPic->frame_no; + int yBlock4x4, xBlock4x4; + int mb_width = CurrPic->PicWidthInMbs; + int mb_height = (CurrPic->PicSizeInMbs) / (CurrPic->PicWidthInMbs); + short mv_x, mv_y; + //Count variables + int countInterView[2] = + { 0, 0 }, countIntraView[2] = + { 0, 0 }, countIntraFrame = + { 0 }, countSkipBlocks = 0, countBiPred = 0; + unsigned int sum_mv_temporal_one = 0, sum_mv_temporal = 0; + + for( yBlock4x4 = 0; yBlock4x4 < mb_height * MB_BLOCK_SIZE / BLOCK_SIZE; yBlock4x4++ ) + { + for( xBlock4x4 = 0; xBlock4x4 < mb_width * MB_BLOCK_SIZE / BLOCK_SIZE; xBlock4x4++ ) + { + // get position + int yPel = yBlock4x4 * BLOCK_SIZE; + int xPel = xBlock4x4 * BLOCK_SIZE; + // get block 8x8 cordinates within a MB + int idx_8x8 = (xPel % MB_BLOCK_SIZE) / SMB_BLOCK_SIZE + 2 * ((yPel % MB_BLOCK_SIZE) / SMB_BLOCK_SIZE); + //get references idx + char ref_idx_l0 = CurrPic->mv_info[yBlock4x4][xBlock4x4].ref_idx[LIST_0]; + char ref_idx_l1 = CurrPic->mv_info[yBlock4x4][xBlock4x4].ref_idx[LIST_1]; + + int idx_mb = (yPel / MB_BLOCK_SIZE) * mb_width + (xPel / MB_BLOCK_SIZE); + Macroblock *currMB = &(p_Vid->mb_data[idx_mb]); + + // Check References + if( ref_idx_l0 < 0 && ref_idx_l1 < 0 ) + { + countIntraFrame++; + } + else + { + + if( ref_idx_l0 >= 0 ) + { // LIST_0 + if( CurrSlice->view_id != CurrSlice->listX[LIST_0][ref_idx_l0]->view_id ) + { + countInterView[LIST_0]++; + } + else + { + countIntraView[LIST_0]++; + mv_x = CurrPic->mv_info[yBlock4x4][xBlock4x4].mv[LIST_0].mv_x; + mv_y = CurrPic->mv_info[yBlock4x4][xBlock4x4].mv[LIST_0].mv_y; + if( mv_x / 4 > 0 || mv_y / 4 > 0 ) + sum_mv_temporal_one++; + sum_mv_temporal++; + } + } + if( ref_idx_l1 >= 0 ) + { // LIST_1 + if( CurrSlice->view_id != CurrSlice->listX[LIST_0][ref_idx_l1]->view_id ) + { + countInterView[LIST_1]++; + } + else + { + countIntraView[LIST_1]++; + mv_x = CurrPic->mv_info[yBlock4x4][xBlock4x4].mv[LIST_0].mv_x; + mv_y = CurrPic->mv_info[yBlock4x4][xBlock4x4].mv[LIST_0].mv_y; + if( abs_ivec( mv_x / 4, mv_y / 4) > 0 ) + sum_mv_temporal_one++; + sum_mv_temporal++; + } + } + if( ref_idx_l0 >= 0 && ref_idx_l1 >= 0 ) + countBiPred++; + } + + } + } + + // Dump to file + fprintf( pFile, "%4d;%8d;%8d;%8d;%8d;%8d;%8d\n", FrameNo, countIntraFrame, countIntraView[LIST_0], countIntraView[LIST_1], countInterView[LIST_0], + countInterView[LIST_1], sum_mv_temporal_one); +} + +void parse_mv_prediction( VideoParameters *p_Vid, Slice *pCurrSlice, StorablePicture *pDecPic) +{ + + //Count variables + int countInterView[2] = + { 0, 0 }, countIntraView[2] = + { 0, 0 }, countIntraFrame = + { 0 }, countSkipBlocks = 0, countBiPred = 0; + + // if( pCurrSlice->view_id == 0) + //return; + + if( pDecPic->slice_type == I_SLICE ) + return; + + { + int yBlock4x4, xBlock4x4; + int mb_width = pDecPic->PicWidthInMbs; + int mb_height = (pDecPic->PicSizeInMbs) / (pDecPic->PicWidthInMbs); + + for( yBlock4x4 = 0; yBlock4x4 < mb_height * MB_BLOCK_SIZE / BLOCK_SIZE; yBlock4x4++ ) + { + for( xBlock4x4 = 0; xBlock4x4 < mb_width * MB_BLOCK_SIZE / BLOCK_SIZE; xBlock4x4++ ) + { + // get position + int yPel = yBlock4x4 * BLOCK_SIZE; + int xPel = xBlock4x4 * BLOCK_SIZE; + // get block 8x8 cordinates within a MB + int idx_8x8 = (xPel % MB_BLOCK_SIZE) / SMB_BLOCK_SIZE + 2 * ((yPel % MB_BLOCK_SIZE) / SMB_BLOCK_SIZE); + //get references idx + char ref_idx_l0 = pDecPic->mv_info[yBlock4x4][xBlock4x4].ref_idx[LIST_0]; + char ref_idx_l1 = pDecPic->mv_info[yBlock4x4][xBlock4x4].ref_idx[LIST_1]; + + int idx_mb = (yPel / MB_BLOCK_SIZE) * mb_width + (xPel / MB_BLOCK_SIZE); + Macroblock *currMB = &(p_Vid->mb_data[idx_mb]); + + // Check References + if( ref_idx_l0 < 0 && ref_idx_l1 < 0 ) + { + countIntraFrame++; + } + else + { + if( ref_idx_l0 >= 0 ) + { // LIST_0 + if( pCurrSlice->view_id != pCurrSlice->listX[LIST_0][ref_idx_l0]->view_id ) + countInterView[LIST_0]++; + else + countIntraView[LIST_0]++; + } + if( ref_idx_l1 >= 0 ) + { // LIST_1 + if( pCurrSlice->view_id != pCurrSlice->listX[LIST_1][ref_idx_l1]->view_id ) + countInterView[LIST_1]++; + else + countIntraView[LIST_1]++; + } + if( ref_idx_l0 >= 0 && ref_idx_l1 >= 0 ) + countBiPred++; + + } + + /* switch( currMB->b8mode[idx_8x8] ) + { + case PSKIP: + countSkipBlocks++; + break; + }*/ + } + } + } + // Verbose + //printf("== Debugging Tool == frame of view %d\n", pDecPic->view_id); + printf( "[Stream Parser] Pred: IntraFrame %d | Intraview %d + %d | Interview %d + %d\n", countIntraFrame, countIntraView[LIST_0], countIntraView[LIST_1], + countInterView[LIST_0], countInterView[LIST_1]); + printf( "[Stream Parser] Number of Bi Pred Blocks: %d \n", countBiPred); + printf( "[Stream Parser] Number of SKIP Blocks: %d \n", p_Vid->n_SKIP); + +} + +void parse_mv_values( VideoParameters *p_Vid, Slice *pCurrSlice, StorablePicture *p_Pic, int ReportToFile) +{ + int yBlock4x4, xBlock4x4, iList; + int mb_width = p_Pic->PicWidthInMbs; + int mb_height = (p_Pic->PicSizeInMbs) / (p_Pic->PicWidthInMbs); + + double sum_mv_temporal_one = 0, sum_mv_temporal_one_total = 0; + double sum_mv_temporal = 0, mv_average_temporal = 0, mv_abs; + double sum_mv_spatial = 0, mv_average_spatial = 0; + unsigned int n_elem_temp = 0, n_elem_spatial = 0; + double sum_intra_blocks = 0; + int mv_x, mv_y, mv_ref; + int scale_factor; + FILE *pReportFile = NULL, *pReportFile_Disp = NULL, *pReportFile_Temp = NULL; + char out_ViewFileName[FILE_NAME_SIZE], chBuf[FILE_NAME_SIZE], *pch; + int FinalList = LIST_0; + + p_Vid->NoZeroMVsCount = 0; + + if( p_Pic->slice_type == I_SLICE ) + return; + + //if( ReportToFile ) + //{ + // strcpy( chBuf , p_Vid->p_Inp->outfile ); + // pch = strrchr( chBuf , '.' ); + // if(pch) + // *pch = '\0'; + // if( !strcmp("nul", chBuf) ) + // return; + // sprintf(out_ViewFileName, "%s-Motion4x4Report_ViewId%02d.txt", chBuf, p_Pic->view_id); + // pReportFile = fopen( out_ViewFileName , "wt" ); + // if( !pReportFile ) + // return; + //} + if( ReportToFile ) + { + strcpy( chBuf, p_Vid->p_Inp->outfile); + pch = strrchr( chBuf, '.'); + if( pch ) + *pch = '\0'; + if( !strcmp( "nul", chBuf) ) + return; + sprintf( out_ViewFileName, "%s_DVs_F%d_V%02d.txt", chBuf, p_Pic->poc, p_Pic->view_id); + pReportFile_Disp = fopen( out_ViewFileName, "wt"); + sprintf( out_ViewFileName, "%s_MVs_F%d_V%02d.txt", chBuf, p_Pic->poc, p_Pic->view_id); + pReportFile_Temp = fopen( out_ViewFileName, "wt"); + } + + FinalList = pCurrSlice->slice_type == B_SLICE ? LIST_1 : LIST_0; + + for( iList = LIST_0; iList <= FinalList; iList++ ) + { + for( yBlock4x4 = 0; yBlock4x4 < mb_height * MB_BLOCK_SIZE / BLOCK_SIZE; yBlock4x4++ ) + { + for( xBlock4x4 = 0; xBlock4x4 < mb_width * MB_BLOCK_SIZE / BLOCK_SIZE; xBlock4x4++ ) + { + mv_x = p_Pic->mv_info[yBlock4x4][xBlock4x4].mv[iList].mv_x; + mv_y = p_Pic->mv_info[yBlock4x4][xBlock4x4].mv[iList].mv_y; + mv_ref = p_Pic->mv_info[yBlock4x4][xBlock4x4].ref_idx[iList]; + + if( mv_ref >= 0 ) + { // Valid motion vector + scale_factor = iabs( p_Pic->poc - pCurrSlice->listX[iList][mv_ref]->poc); + if( scale_factor ) + { + mv_abs = (abs_ivec( mv_x, mv_y) / scale_factor); + if( mv_x / 4 > 0 || mv_y / 4 > 0 ) + sum_mv_temporal_one++; + sum_mv_temporal_one_total++; + sum_mv_temporal += mv_abs; + n_elem_temp++; + if( pReportFile_Temp ) + fprintf( pReportFile_Temp, "%d;%d;%d;%d;\n", xBlock4x4, yBlock4x4, mv_x, mv_y); + } + else + { + mv_abs = abs_ivec( mv_x, mv_y); + sum_mv_spatial += mv_abs; + n_elem_spatial++; + if( pReportFile_Disp ) + fprintf( pReportFile_Disp, "%d;%d;%d;%d;\n", xBlock4x4, yBlock4x4, mv_x, mv_y); + } + } + else + { + sum_intra_blocks++; + } + } + } + } + + if( pReportFile ) + fclose( pReportFile); + if( pReportFile_Temp ) + fclose( pReportFile_Temp); + if( pReportFile_Disp ) + fclose( pReportFile_Disp); + + sum_intra_blocks = sum_intra_blocks / ((double) (mb_width * mb_height * MB_BLOCK_SIZE / BLOCK_SIZE * MB_BLOCK_SIZE / BLOCK_SIZE)); + + mv_average_temporal = n_elem_temp ? (sum_mv_temporal / n_elem_temp / 4) : 0; + mv_average_spatial = n_elem_spatial ? (sum_mv_spatial / n_elem_spatial / 4) : 0; + + // Verbose + //printf("== Debugging Tool == Frame of view %d\n", p_Pic->view_id); + + printf( "[Stream Parser] Motion Vectors values - Temporal: %2.4f \\ Spatial: %2.4f\n", mv_average_temporal, mv_average_spatial); + printf( "[Stream Parser] INTRA blocks: %2.4f\n", sum_intra_blocks); + printf( "[Stream Parser] Motion Vectors greater that one: %f\n", sum_mv_temporal_one / sum_mv_temporal_one_total); + + p_Vid->NoZeroMVsCount = sum_mv_temporal_one * 100 / sum_mv_temporal_one_total; + p_Vid->IntraBlocks = sum_intra_blocks * 100; + return; +} diff --git a/ldecod/src/transcode_dec.c b/ldecod/src/transcode_dec.c new file mode 100644 index 0000000..47f9aad --- /dev/null +++ b/ldecod/src/transcode_dec.c @@ -0,0 +1,104 @@ +/*! +************************************************************************ +* \file transcode_dec.c +* +* \brief +* Functions to save transcoding information in the decoder +* +* \author +* - João Carreira +* +************************************************************************ +*/ + +#include "transcode_dec.h" +#include "transcode_common.h" +#include "memalloc.h" + +typedef struct trans_t +{ + int p_fileout[MAX_VIEW_NUM]; + +} TRANS_DEC_T; + +void initTranscodeModule( VideoParameters *p_Vid ) +{ + TRANS_DEC_T *TranscodeModule = NULL; + InputParameters *p_Inp = p_Vid->p_Inp; + char out_ViewFileName[FILE_NAME_SIZE], chBuf[FILE_NAME_SIZE], *pch; + int view_id, number_of_views; + + if ( ( TranscodeModule = (TRANS_DEC_T*)calloc(1, sizeof(TRANS_DEC_T)) ) == NULL ) + no_mem_exit("alloc_video_params: p_Vid->p_Transcode"); + + memset(TranscodeModule,0,sizeof(TRANS_DEC_T)); + + memset(TranscodeModule->p_fileout,-1,MAX_VIEW_NUM*sizeof(int)); + + if( p_Vid->p_Inp->DecodeAllLayers && p_Vid->active_subset_sps) + number_of_views = p_Vid->active_subset_sps->num_views_minus1+1; + else + number_of_views = 1; + + for(view_id=0; view_idp_Inp->TranscodeFile, "\"\"")!=0) && (strlen(p_Vid->p_Inp->TranscodeFile)>0)) + { + strcpy(chBuf, p_Vid->p_Inp->TranscodeFile); + pch = strrchr(chBuf, '.'); + if(pch) + *pch = '\0'; + if (strcmp("nul", chBuf)) + { + sprintf(out_ViewFileName, "%s_ViewId%02d.dat", chBuf, view_id); + + if ((TranscodeModule->p_fileout[view_id]=open(out_ViewFileName, OPENFLAGS_WRITE, OPEN_PERMISSIONS))==-1) + { + snprintf(errortext, ET_SIZE, "Error open file %s ", out_ViewFileName); + error(errortext,500); + } + } + } + } + + p_Vid->p_Transcode = TranscodeModule; +} + +void closeTranscodeModule( VideoParameters *p_Vid ) +{ + TRANS_DEC_T *TranscodeModule = (TRANS_DEC_T *) p_Vid->p_Transcode; + int i; + for(i=0; ip_fileout[i] != -1 ) + { + close( TranscodeModule->p_fileout[i] ); + } + } + + free(TranscodeModule); + p_Vid->p_Transcode = NULL; + +} + + +void saveTranscodingInfo( VideoParameters *p_Vid, StorablePicture *p_Decoded ) +{ + TRANS_DEC_T *TranscodeModule = (TRANS_DEC_T *) p_Vid->p_Transcode; + TRANS_FRAME_NODE_T node; + int pfile = TranscodeModule->p_fileout[p_Decoded->view_id]; + + if( p_Decoded->idr_flag ) + return; + + if( pfile >= 0 ) + { + node.frame_height = p_Decoded->size_y; + node.frame_width = p_Decoded->size_x; + node.poc = p_Decoded->poc; + node.view_id = p_Decoded->view_id; + node.pp_mv_info = p_Decoded->mv_info; + + write_trans_node( pfile, node); + } +} \ No newline at end of file diff --git a/ldecod/src/transform8x8.c b/ldecod/src/transform8x8.c new file mode 100644 index 0000000..3d7964b --- /dev/null +++ b/ldecod/src/transform8x8.c @@ -0,0 +1,115 @@ + +/*! + *************************************************************************** + * \file transform8x8.c + * + * \brief + * 8x8 transform functions + * + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * - Yuri Vatis + * - Jan Muenster + * + * \date + * 12. October 2003 + ************************************************************************** + */ + +#include "global.h" + +#include "image.h" +#include "mb_access.h" +#include "elements.h" +#include "transform8x8.h" +#include "transform.h" +#include "quant.h" + +static void recon8x8(int **m7, imgpel **mb_rec, imgpel **mpr, int max_imgpel_value, int ioff) +{ + int j; + int *m_tr = NULL; + imgpel *m_rec = NULL; + imgpel *m_prd = NULL; + + for( j = 0; j < 8; j++) + { + m_tr = (*m7++) + ioff; + m_rec = (*mb_rec++) + ioff; + m_prd = (*mpr++) + ioff; + + *m_rec++ = (imgpel) iClip1(max_imgpel_value, (*m_prd++) + rshift_rnd_sf(*m_tr++, DQ_BITS_8)); + *m_rec++ = (imgpel) iClip1(max_imgpel_value, (*m_prd++) + rshift_rnd_sf(*m_tr++, DQ_BITS_8)); + *m_rec++ = (imgpel) iClip1(max_imgpel_value, (*m_prd++) + rshift_rnd_sf(*m_tr++, DQ_BITS_8)); + *m_rec++ = (imgpel) iClip1(max_imgpel_value, (*m_prd++) + rshift_rnd_sf(*m_tr++, DQ_BITS_8)); + *m_rec++ = (imgpel) iClip1(max_imgpel_value, (*m_prd++) + rshift_rnd_sf(*m_tr++, DQ_BITS_8)); + *m_rec++ = (imgpel) iClip1(max_imgpel_value, (*m_prd++) + rshift_rnd_sf(*m_tr++, DQ_BITS_8)); + *m_rec++ = (imgpel) iClip1(max_imgpel_value, (*m_prd++) + rshift_rnd_sf(*m_tr++, DQ_BITS_8)); + *m_rec = (imgpel) iClip1(max_imgpel_value, (*m_prd ) + rshift_rnd_sf(*m_tr , DQ_BITS_8)); + } +} + +static void copy8x8(imgpel **mb_rec, imgpel **mpr, int ioff) +{ + int j; + + for( j = 0; j < 8; j++) + { + memcpy((*mb_rec++) + ioff, (*mpr++) + ioff, 8 * sizeof(imgpel)); + } +} + +static void recon8x8_lossless(int **m7, imgpel **mb_rec, imgpel **mpr, int max_imgpel_value, int ioff) +{ + int i, j; + for( j = 0; j < 8; j++) + { + for( i = ioff; i < ioff + 8; i++) + (*mb_rec)[i] = (imgpel) iClip1(max_imgpel_value, ((*m7)[i] + (long)(*mpr)[i])); + mb_rec++; + m7++; + mpr++; + } +} + +/*! + *********************************************************************** + * \brief + * Inverse 8x8 transformation + *********************************************************************** + */ +void itrans8x8(Macroblock *currMB, //!< current macroblock + ColorPlane pl, //!< used color plane + int ioff, //!< index to 4x4 block + int joff) //!< index to 4x4 block +{ + Slice *currSlice = currMB->p_Slice; + + int **m7 = currSlice->mb_rres[pl]; + + if (currMB->is_lossless == TRUE) + { + recon8x8_lossless(&m7[joff], &currSlice->mb_rec[pl][joff], &currSlice->mb_pred[pl][joff], currMB->p_Vid->max_pel_value_comp[pl], ioff); + } + else + { + inverse8x8(&m7[joff], &m7[joff], ioff); + recon8x8 (&m7[joff], &currSlice->mb_rec[pl][joff], &currSlice->mb_pred[pl][joff], currMB->p_Vid->max_pel_value_comp[pl], ioff); + } +} + +/*! + *********************************************************************** + * \brief + * Inverse 8x8 transformation + *********************************************************************** + */ +void icopy8x8(Macroblock *currMB, //!< current macroblock + ColorPlane pl, //!< used color plane + int ioff, //!< index to 4x4 block + int joff) //!< index to 4x4 block +{ + Slice *currSlice = currMB->p_Slice; + + copy8x8 (&currSlice->mb_rec[pl][joff], &currSlice->mb_pred[pl][joff], ioff); +} diff --git a/ldecod/src/vlc.c b/ldecod/src/vlc.c new file mode 100644 index 0000000..789de49 --- /dev/null +++ b/ldecod/src/vlc.c @@ -0,0 +1,1289 @@ +/*! + ************************************************************************ + * \file vlc.c + * + * \brief + * VLC support functions + * + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * - Inge Lille-Langøy + * - Detlev Marpe + * - Gabi Blaettermann + ************************************************************************ + */ +#include "contributors.h" + +#include "global.h" +#include "vlc.h" +#include "elements.h" + + +// A little trick to avoid those horrible #if TRACE all over the source code +#if TRACE +#define SYMTRACESTRING(s) strncpy(symbol.tracestring,s,TRACESTRING_SIZE) +#else +#define SYMTRACESTRING(s) // do nothing +#endif + +// Note that all NA values are filled with 0 + +/*! + ************************************************************************************* + * \brief + * read_ue_v, reads an ue(v) syntax element, the length in bits is stored in + * the global p_Dec->UsedBits variable + * + * \param tracestring + * the string for the trace file + * + * \param bitstream + * the stream to be read from + * + * \return + * the value of the coded syntax element + * + ************************************************************************************* + */ +int read_ue_v (char *tracestring, Bitstream *bitstream, int *used_bits) +{ + SyntaxElement symbol; + + //assert (bitstream->streamBuffer != NULL); + symbol.type = SE_HEADER; + symbol.mapping = linfo_ue; // Mapping rule + SYMTRACESTRING(tracestring); + readSyntaxElement_VLC (&symbol, bitstream); + *used_bits+=symbol.len; + return symbol.value1; +} + + +/*! + ************************************************************************************* + * \brief + * read_ue_v, reads an se(v) syntax element, the length in bits is stored in + * the global p_Dec->UsedBits variable + * + * \param tracestring + * the string for the trace file + * + * \param bitstream + * the stream to be read from + * + * \return + * the value of the coded syntax element + * + ************************************************************************************* + */ +int read_se_v (char *tracestring, Bitstream *bitstream, int *used_bits) +{ + SyntaxElement symbol; + + //assert (bitstream->streamBuffer != NULL); + symbol.type = SE_HEADER; + symbol.mapping = linfo_se; // Mapping rule: signed integer + SYMTRACESTRING(tracestring); + readSyntaxElement_VLC (&symbol, bitstream); + *used_bits+=symbol.len; + return symbol.value1; +} + + +/*! + ************************************************************************************* + * \brief + * read_ue_v, reads an u(v) syntax element, the length in bits is stored in + * the global p_Dec->UsedBits variable + * + * \param LenInBits + * length of the syntax element + * + * \param tracestring + * the string for the trace file + * + * \param bitstream + * the stream to be read from + * + * \return + * the value of the coded syntax element + * + ************************************************************************************* + */ +int read_u_v (int LenInBits, char*tracestring, Bitstream *bitstream, int *used_bits) +{ + SyntaxElement symbol; + symbol.inf = 0; + + //assert (bitstream->streamBuffer != NULL); + symbol.type = SE_HEADER; + symbol.mapping = linfo_ue; // Mapping rule + symbol.len = LenInBits; + SYMTRACESTRING(tracestring); + readSyntaxElement_FLC (&symbol, bitstream); + *used_bits+=symbol.len; + + return symbol.inf; +} + +/*! + ************************************************************************************* + * \brief + * read_i_v, reads an i(v) syntax element, the length in bits is stored in + * the global p_Dec->UsedBits variable + * + * \param LenInBits + * length of the syntax element + * + * \param tracestring + * the string for the trace file + * + * \param bitstream + * the stream to be read from + * + * \return + * the value of the coded syntax element + * + ************************************************************************************* + */ +int read_i_v (int LenInBits, char*tracestring, Bitstream *bitstream, int *used_bits) +{ + SyntaxElement symbol; + symbol.inf = 0; + + //assert (bitstream->streamBuffer != NULL); + symbol.type = SE_HEADER; + symbol.mapping = linfo_ue; // Mapping rule + symbol.len = LenInBits; + SYMTRACESTRING(tracestring); + readSyntaxElement_FLC (&symbol, bitstream); + *used_bits+=symbol.len; + + // can be negative + symbol.inf = -( symbol.inf & (1 << (LenInBits - 1)) ) | symbol.inf; + + return symbol.inf; +} + + +/*! + ************************************************************************************* + * \brief + * read_ue_v, reads an u(1) syntax element, the length in bits is stored in + * the global p_Dec->UsedBits variable + * + * \param tracestring + * the string for the trace file + * + * \param bitstream + * the stream to be read from + * + * \return + * the value of the coded syntax element + * + ************************************************************************************* + */ +Boolean read_u_1 (char *tracestring, Bitstream *bitstream, int *used_bits) +{ + return (Boolean) read_u_v (1, tracestring, bitstream, used_bits); +} + + + +/*! + ************************************************************************ + * \brief + * mapping rule for ue(v) syntax elements + * \par Input: + * lenght and info + * \par Output: + * number in the code table + ************************************************************************ + */ +void linfo_ue(int len, int info, int *value1, int *dummy) +{ + //assert ((len >> 1) < 32); + *value1 = (int) (((unsigned int) 1 << (len >> 1)) + (unsigned int) (info) - 1); +} + +/*! + ************************************************************************ + * \brief + * mapping rule for se(v) syntax elements + * \par Input: + * lenght and info + * \par Output: + * signed mvd + ************************************************************************ + */ +void linfo_se(int len, int info, int *value1, int *dummy) +{ + //assert ((len >> 1) < 32); + unsigned int n = ((unsigned int) 1 << (len >> 1)) + (unsigned int) info - 1; + *value1 = (n + 1) >> 1; + if((n & 0x01) == 0) // lsb is signed bit + *value1 = -*value1; +} + + +/*! + ************************************************************************ + * \par Input: + * length and info + * \par Output: + * cbp (intra) + ************************************************************************ + */ +void linfo_cbp_intra_normal(int len,int info,int *cbp, int *dummy) +{ + int cbp_idx; + + linfo_ue(len, info, &cbp_idx, dummy); + *cbp=NCBP[1][cbp_idx][0]; +} + + +/*! + ************************************************************************ + * \par Input: + * length and info + * \par Output: + * cbp (intra) + ************************************************************************ + */ +void linfo_cbp_intra_other(int len,int info,int *cbp, int *dummy) +{ + int cbp_idx; + + linfo_ue(len, info, &cbp_idx, dummy); + *cbp=NCBP[0][cbp_idx][0]; +} + +/*! + ************************************************************************ + * \par Input: + * length and info + * \par Output: + * cbp (inter) + ************************************************************************ + */ +void linfo_cbp_inter_normal(int len,int info,int *cbp, int *dummy) +{ + int cbp_idx; + + linfo_ue(len, info, &cbp_idx, dummy); + *cbp=NCBP[1][cbp_idx][1]; +} + +/*! + ************************************************************************ + * \par Input: + * length and info + * \par Output: + * cbp (inter) + ************************************************************************ + */ +void linfo_cbp_inter_other(int len,int info,int *cbp, int *dummy) +{ + int cbp_idx; + + linfo_ue(len, info, &cbp_idx, dummy); + *cbp=NCBP[0][cbp_idx][1]; +} + +/*! + ************************************************************************ + * \par Input: + * length and info + * \par Output: + * level, run + ************************************************************************ + */ +void linfo_levrun_inter(int len, int info, int *level, int *irun) +{ + //assert (((len >> 1) - 5) < 32); + + if (len <= 9) + { + int l2 = imax(0,(len >> 1)-1); + int inf = info >> 1; + + *level = NTAB1[l2][inf][0]; + *irun = NTAB1[l2][inf][1]; + if ((info & 0x01) == 1) + *level = -*level; // make sign + } + else // if len > 9, skip using the array + { + *irun = (info & 0x1e) >> 1; + *level = LEVRUN1[*irun] + (info >> 5) + ( 1 << ((len >> 1) - 5)); + if ((info & 0x01) == 1) + *level = -*level; + } + + if (len == 1) // EOB + *level = 0; +} + + +/*! + ************************************************************************ + * \par Input: + * length and info + * \par Output: + * level, run + ************************************************************************ + */ +void linfo_levrun_c2x2(int len, int info, int *level, int *irun) +{ + if (len<=5) + { + int l2 = imax(0, (len >> 1) - 1); + int inf = info >> 1; + *level = NTAB3[l2][inf][0]; + *irun = NTAB3[l2][inf][1]; + if ((info & 0x01) == 1) + *level = -*level; // make sign + } + else // if len > 5, skip using the array + { + *irun = (info & 0x06) >> 1; + *level = LEVRUN3[*irun] + (info >> 3) + (1 << ((len >> 1) - 3)); + if ((info & 0x01) == 1) + *level = -*level; + } + + if (len == 1) // EOB + *level = 0; +} + +/*! + ************************************************************************ + * \brief + * read next UVLC codeword from UVLC-partition and + * map it to the corresponding syntax element + ************************************************************************ + */ +int readSyntaxElement_VLC(SyntaxElement *sym, Bitstream *currStream) +{ + + sym->len = GetVLCSymbol (currStream->streamBuffer, currStream->frame_bitoffset, &(sym->inf), currStream->bitstream_length); + if (sym->len == -1) + return -1; + + currStream->frame_bitoffset += sym->len; + sym->mapping(sym->len, sym->inf, &(sym->value1), &(sym->value2)); + +#if TRACE + tracebits(sym->tracestring, sym->len, sym->inf, sym->value1); +#endif + + return 1; +} + + +/*! + ************************************************************************ + * \brief + * read next UVLC codeword from UVLC-partition and + * map it to the corresponding syntax element + ************************************************************************ + */ +int readSyntaxElement_UVLC(Macroblock *currMB, SyntaxElement *sym, struct datapartition_dec *dP) +{ + return (readSyntaxElement_VLC(sym, dP->bitstream)); +} + +/*! + ************************************************************************ + * \brief + * read next VLC codeword for 4x4 Intra Prediction Mode and + * map it to the corresponding Intra Prediction Direction + ************************************************************************ + */ +int readSyntaxElement_Intra4x4PredictionMode(SyntaxElement *sym, Bitstream *currStream) +{ + sym->len = GetVLCSymbol_IntraMode (currStream->streamBuffer, currStream->frame_bitoffset, &(sym->inf), currStream->bitstream_length); + + if (sym->len == -1) + return -1; + + currStream->frame_bitoffset += sym->len; + sym->value1 = (sym->len == 1) ? -1 : sym->inf; + +#if TRACE + tracebits2(sym->tracestring, sym->len, sym->value1); +#endif + + return 1; +} + +int GetVLCSymbol_IntraMode (byte buffer[],int totbitoffset,int *info, int bytecount) +{ + int byteoffset = (totbitoffset >> 3); // byte from start of buffer + int bitoffset = (7 - (totbitoffset & 0x07)); // bit from start of byte + byte *cur_byte = &(buffer[byteoffset]); + int ctr_bit = (*cur_byte & (0x01 << bitoffset)); // control bit for current bit posision + + //First bit + if (ctr_bit) + { + *info = 0; + return 1; + } + + if (byteoffset >= bytecount) + { + return -1; + } + else + { + int inf = (*(cur_byte) << 8) + *(cur_byte + 1); + inf <<= (sizeof(byte) * 8) - bitoffset; + inf = inf & 0xFFFF; + inf >>= (sizeof(byte) * 8) * 2 - 3; + + *info = inf; + return 4; // return absolute offset in bit from start of frame + } +} + + +/*! + ************************************************************************ + * \brief + * test if bit buffer contains only stop bit + * + * \param buffer + * buffer containing VLC-coded data bits + * \param totbitoffset + * bit offset from start of partition + * \param bytecount + * buffer length + * \return + * true if more bits available + ************************************************************************ + */ +int more_rbsp_data (byte buffer[],int totbitoffset,int bytecount) +{ + long byteoffset = (totbitoffset >> 3); // byte from start of buffer + // there is more until we're in the last byte + if (byteoffset < (bytecount - 1)) + return TRUE; + else + { + int bitoffset = (7 - (totbitoffset & 0x07)); // bit from start of byte + byte *cur_byte = &(buffer[byteoffset]); + // read one bit + int ctr_bit = ctr_bit = ((*cur_byte)>> (bitoffset--)) & 0x01; // control bit for current bit posision + + //assert (byteoffset=0 && !cnt) + { + cnt |= ((*cur_byte)>> (bitoffset--)) & 0x01; // set up control bit + } + + return (cnt); + } + } +} + + +/*! + ************************************************************************ + * \brief + * Check if there are symbols for the next MB + ************************************************************************ + */ +int uvlc_startcode_follows(Slice *currSlice, int dummy) +{ + byte dp_Nr = assignSE2partition[currSlice->dp_mode][SE_MBTYPE]; + DataPartition *dP = &(currSlice->partArr[dp_Nr]); + Bitstream *currStream = dP->bitstream; + byte *buf = currStream->streamBuffer; + + return (!(more_rbsp_data(buf, currStream->frame_bitoffset,currStream->bitstream_length))); +} + + + +/*! + ************************************************************************ + * \brief + * read one exp-golomb VLC symbol + * + * \param buffer + * containing VLC-coded data bits + * \param totbitoffset + * bit offset from start of partition + * \param info + * returns the value of the symbol + * \param bytecount + * buffer length + * \return + * bits read + ************************************************************************ + */ +int GetVLCSymbol (byte buffer[],int totbitoffset,int *info, int bytecount) +{ + long byteoffset = (totbitoffset >> 3); // byte from start of buffer + int bitoffset = (7 - (totbitoffset & 0x07)); // bit from start of byte + int bitcounter = 1; + int len = 0; + byte *cur_byte = &(buffer[byteoffset]); + int ctr_bit = ((*cur_byte) >> (bitoffset)) & 0x01; // control bit for current bit posision + + while (ctr_bit == 0) + { // find leading 1 bit + len++; + bitcounter++; + bitoffset--; + bitoffset &= 0x07; + cur_byte += (bitoffset == 7); + byteoffset+= (bitoffset == 7); + ctr_bit = ((*cur_byte) >> (bitoffset)) & 0x01; + } + + if (byteoffset + ((len + 7) >> 3) > bytecount) + return -1; + else + { + // make infoword + int inf = 0; // shortest possible code is 1, then info is always 0 + + while (len--) + { + bitoffset --; + bitoffset &= 0x07; + cur_byte += (bitoffset == 7); + bitcounter++; + inf <<= 1; + inf |= ((*cur_byte) >> (bitoffset)) & 0x01; + } + + *info = inf; + return bitcounter; // return absolute offset in bit from start of frame + } +} + + +/*! + ************************************************************************ + * \brief + * Reads bits from the bitstream buffer (Threshold based) + * + * \param inf + * bytes to extract numbits from with bitoffset already applied + * \param numbits + * number of bits to read + * + ************************************************************************ + */ + +//static inline int ShowBitsThres (int inf, int bitcount, int numbits) +static inline int ShowBitsThres (int inf, int numbits) +{ + return ((inf) >> ((sizeof(byte) * 24) - (numbits))); + /* + if ((numbits + 7) > bitcount) + { + return -1; + } + else + { + //Worst case scenario is that we will need to traverse 3 bytes + inf >>= (sizeof(byte)*8)*3 - numbits; + } + + return inf; //Will be a small unsigned integer so will not need any conversion when returning as int + */ +} + + +/*! + ************************************************************************ + * \brief + * code from bitstream (2d tables) + ************************************************************************ + */ + +static int code_from_bitstream_2d(SyntaxElement *sym, + Bitstream *currStream, + const byte *lentab, + const byte *codtab, + int tabwidth, + int tabheight, + int *code) +{ + int i, j; + const byte *len = &lentab[0], *cod = &codtab[0]; + + int *frame_bitoffset = &currStream->frame_bitoffset; + byte *buf = &currStream->streamBuffer[*frame_bitoffset >> 3]; + + //Apply bitoffset to three bytes (maximum that may be traversed by ShowBitsThres) + unsigned int inf = ((*buf) << 16) + (*(buf + 1) << 8) + *(buf + 2); //Even at the end of a stream we will still be pulling out of allocated memory as alloc is done by MAX_CODED_FRAME_SIZE + inf <<= (*frame_bitoffset & 0x07); //Offset is constant so apply before extracting different numbers of bits + inf &= 0xFFFFFF; //Arithmetic shift so wipe any sign which may be extended inside ShowBitsThres + + // this VLC decoding method is not optimized for speed + for (j = 0; j < tabheight; j++) + { + for (i = 0; i < tabwidth; i++) + { + if ((*len == 0) || (ShowBitsThres(inf, (int) *len) != *cod)) + { + ++len; + ++cod; + } + else + { + sym->len = *len; + *frame_bitoffset += *len; // move bitstream pointer + *code = *cod; + sym->value1 = i; + sym->value2 = j; + return 0; // found code and return + } + } + } + return -1; // failed to find code +} + + +/*! + ************************************************************************ + * \brief + * read FLC codeword from UVLC-partition + ************************************************************************ + */ +int readSyntaxElement_FLC(SyntaxElement *sym, Bitstream *currStream) +{ + int BitstreamLengthInBits = (currStream->bitstream_length << 3) + 7; + + if ((GetBits(currStream->streamBuffer, currStream->frame_bitoffset, &(sym->inf), BitstreamLengthInBits, sym->len)) < 0) + return -1; + + sym->value1 = sym->inf; + currStream->frame_bitoffset += sym->len; // move bitstream pointer + +#if TRACE + tracebits2(sym->tracestring, sym->len, sym->inf); +#endif + + return 1; +} + + + +/*! + ************************************************************************ + * \brief + * read NumCoeff/TrailingOnes codeword from UVLC-partition + ************************************************************************ + */ + +int readSyntaxElement_NumCoeffTrailingOnes(SyntaxElement *sym, + Bitstream *currStream, + char *type) +{ + int frame_bitoffset = currStream->frame_bitoffset; + int BitstreamLengthInBytes = currStream->bitstream_length; + int BitstreamLengthInBits = (BitstreamLengthInBytes << 3) + 7; + byte *buf = currStream->streamBuffer; + + static const byte lentab[3][4][17] = + { + { // 0702 + { 1, 6, 8, 9,10,11,13,13,13,14,14,15,15,16,16,16,16}, + { 0, 2, 6, 8, 9,10,11,13,13,14,14,15,15,15,16,16,16}, + { 0, 0, 3, 7, 8, 9,10,11,13,13,14,14,15,15,16,16,16}, + { 0, 0, 0, 5, 6, 7, 8, 9,10,11,13,14,14,15,15,16,16}, + }, + { + { 2, 6, 6, 7, 8, 8, 9,11,11,12,12,12,13,13,13,14,14}, + { 0, 2, 5, 6, 6, 7, 8, 9,11,11,12,12,13,13,14,14,14}, + { 0, 0, 3, 6, 6, 7, 8, 9,11,11,12,12,13,13,13,14,14}, + { 0, 0, 0, 4, 4, 5, 6, 6, 7, 9,11,11,12,13,13,13,14}, + }, + { + { 4, 6, 6, 6, 7, 7, 7, 7, 8, 8, 9, 9, 9,10,10,10,10}, + { 0, 4, 5, 5, 5, 5, 6, 6, 7, 8, 8, 9, 9, 9,10,10,10}, + { 0, 0, 4, 5, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9,10,10,10}, + { 0, 0, 0, 4, 4, 4, 4, 4, 5, 6, 7, 8, 8, 9,10,10,10}, + }, + }; + + static const byte codtab[3][4][17] = + { + { + { 1, 5, 7, 7, 7, 7,15,11, 8,15,11,15,11,15,11, 7,4}, + { 0, 1, 4, 6, 6, 6, 6,14,10,14,10,14,10, 1,14,10,6}, + { 0, 0, 1, 5, 5, 5, 5, 5,13, 9,13, 9,13, 9,13, 9,5}, + { 0, 0, 0, 3, 3, 4, 4, 4, 4, 4,12,12, 8,12, 8,12,8}, + }, + { + { 3,11, 7, 7, 7, 4, 7,15,11,15,11, 8,15,11, 7, 9,7}, + { 0, 2, 7,10, 6, 6, 6, 6,14,10,14,10,14,10,11, 8,6}, + { 0, 0, 3, 9, 5, 5, 5, 5,13, 9,13, 9,13, 9, 6,10,5}, + { 0, 0, 0, 5, 4, 6, 8, 4, 4, 4,12, 8,12,12, 8, 1,4}, + }, + { + {15,15,11, 8,15,11, 9, 8,15,11,15,11, 8,13, 9, 5,1}, + { 0,14,15,12,10, 8,14,10,14,14,10,14,10, 7,12, 8,4}, + { 0, 0,13,14,11, 9,13, 9,13,10,13, 9,13, 9,11, 7,3}, + { 0, 0, 0,12,11,10, 9, 8,13,12,12,12, 8,12,10, 6,2}, + }, + }; + + int retval = 0, code; + int vlcnum = sym->value1; + // vlcnum is the index of Table used to code coeff_token + // vlcnum==3 means (8<=nC) which uses 6bit FLC + + if (vlcnum == 3) + { + // read 6 bit FLC + //code = ShowBits(buf, frame_bitoffset, BitstreamLengthInBytes, 6); + code = ShowBits(buf, frame_bitoffset, BitstreamLengthInBits, 6); + currStream->frame_bitoffset += 6; + sym->value2 = (code & 3); + sym->value1 = (code >> 2); + + if (!sym->value1 && sym->value2 == 3) + { + // #c = 0, #t1 = 3 => #c = 0 + sym->value2 = 0; + } + else + sym->value1++; + + sym->len = 6; + } + else + { + //retval = code_from_bitstream_2d(sym, currStream, &lentab[vlcnum][0][0], &codtab[vlcnum][0][0], 17, 4, &code); + retval = code_from_bitstream_2d(sym, currStream, lentab[vlcnum][0], codtab[vlcnum][0], 17, 4, &code); + if (retval) + { + printf("ERROR: failed to find NumCoeff/TrailingOnes\n"); + exit(-1); + } + } + +#if TRACE + snprintf(sym->tracestring, TRACESTRING_SIZE, "%s # c & tr.1s vlc=%d #c=%d #t1=%d", + type, vlcnum, sym->value1, sym->value2); + tracebits2(sym->tracestring, sym->len, code); +#endif + + return retval; +} + + +/*! + ************************************************************************ + * \brief + * read NumCoeff/TrailingOnes codeword from UVLC-partition ChromaDC + ************************************************************************ + */ +int readSyntaxElement_NumCoeffTrailingOnesChromaDC(VideoParameters *p_Vid, SyntaxElement *sym, Bitstream *currStream) +{ + static const byte lentab[3][4][17] = + { + //YUV420 + {{ 2, 6, 6, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + { 0, 1, 6, 7, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + { 0, 0, 3, 7, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + { 0, 0, 0, 6, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, + //YUV422 + {{ 1, 7, 7, 9, 9,10,11,12,13, 0, 0, 0, 0, 0, 0, 0, 0}, + { 0, 2, 7, 7, 9,10,11,12,12, 0, 0, 0, 0, 0, 0, 0, 0}, + { 0, 0, 3, 7, 7, 9,10,11,12, 0, 0, 0, 0, 0, 0, 0, 0}, + { 0, 0, 0, 5, 6, 7, 7,10,11, 0, 0, 0, 0, 0, 0, 0, 0}}, + //YUV444 + {{ 1, 6, 8, 9,10,11,13,13,13,14,14,15,15,16,16,16,16}, + { 0, 2, 6, 8, 9,10,11,13,13,14,14,15,15,15,16,16,16}, + { 0, 0, 3, 7, 8, 9,10,11,13,13,14,14,15,15,16,16,16}, + { 0, 0, 0, 5, 6, 7, 8, 9,10,11,13,14,14,15,15,16,16}} + }; + + static const byte codtab[3][4][17] = + { + //YUV420 + {{ 1, 7, 4, 3, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + { 0, 1, 6, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + { 0, 0, 1, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + { 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, + //YUV422 + {{ 1,15,14, 7, 6, 7, 7, 7, 7, 0, 0, 0, 0, 0, 0, 0, 0}, + { 0, 1,13,12, 5, 6, 6, 6, 5, 0, 0, 0, 0, 0, 0, 0, 0}, + { 0, 0, 1,11,10, 4, 5, 5, 4, 0, 0, 0, 0, 0, 0, 0, 0}, + { 0, 0, 0, 1, 1, 9, 8, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0}}, + //YUV444 + {{ 1, 5, 7, 7, 7, 7,15,11, 8,15,11,15,11,15,11, 7, 4}, + { 0, 1, 4, 6, 6, 6, 6,14,10,14,10,14,10, 1,14,10, 6}, + { 0, 0, 1, 5, 5, 5, 5, 5,13, 9,13, 9,13, 9,13, 9, 5}, + { 0, 0, 0, 3, 3, 4, 4, 4, 4, 4,12,12, 8,12, 8,12, 8}} + + }; + + int code; + int yuv = p_Vid->active_sps->chroma_format_idc - 1; + int retval = code_from_bitstream_2d(sym, currStream, &lentab[yuv][0][0], &codtab[yuv][0][0], 17, 4, &code); + + if (retval) + { + printf("ERROR: failed to find NumCoeff/TrailingOnes ChromaDC\n"); + exit(-1); + } + +#if TRACE + snprintf(sym->tracestring, TRACESTRING_SIZE, "ChrDC # c & tr.1s #c=%d #t1=%d", + sym->value1, sym->value2); + tracebits2(sym->tracestring, sym->len, code); + +#endif + + return retval; +} + +/*! + ************************************************************************ + * \brief + * read Level VLC0 codeword from UVLC-partition + ************************************************************************ + */ +int readSyntaxElement_Level_VLC0(SyntaxElement *sym, Bitstream *currStream) +{ + int frame_bitoffset = currStream->frame_bitoffset; + int BitstreamLengthInBytes = currStream->bitstream_length; + int BitstreamLengthInBits = (BitstreamLengthInBytes << 3) + 7; + byte *buf = currStream->streamBuffer; + int len = 1, sign = 0, level = 0, code = 1; + + while (!ShowBits(buf, frame_bitoffset++, BitstreamLengthInBits, 1)) + len++; + + if (len < 15) + { + sign = (len - 1) & 1; + level = ((len - 1) >> 1) + 1; + } + else if (len == 15) + { + // escape code + code <<= 4; + code |= ShowBits(buf, frame_bitoffset, BitstreamLengthInBits, 4); + len += 4; + frame_bitoffset += 4; + sign = (code & 0x01); + level = ((code >> 1) & 0x07) + 8; + } + else if (len >= 16) + { + // escape code + int addbit = (len - 16); + int offset = (2048 << addbit) - 2032; + len -= 4; + code = ShowBits(buf, frame_bitoffset, BitstreamLengthInBits, len); + sign = (code & 0x01); + frame_bitoffset += len; + level = (code >> 1) + offset; + + code |= (1 << (len)); // for display purpose only + len += addbit + 16; + } + + sym->inf = (sign) ? -level : level ; + sym->len = len; + +#if TRACE + tracebits2(sym->tracestring, sym->len, code); +#endif + + currStream->frame_bitoffset = frame_bitoffset; + return 0; +} + +/*! + ************************************************************************ + * \brief + * read Level VLC codeword from UVLC-partition + ************************************************************************ + */ +int readSyntaxElement_Level_VLCN(SyntaxElement *sym, int vlc, Bitstream *currStream) +{ + int frame_bitoffset = currStream->frame_bitoffset; + int BitstreamLengthInBytes = currStream->bitstream_length; + int BitstreamLengthInBits = (BitstreamLengthInBytes << 3) + 7; + byte *buf = currStream->streamBuffer; + + int levabs, sign; + int len = 1; + int code = 1, sb; + + int shift = vlc - 1; + + // read pre zeros + while (!ShowBits(buf, frame_bitoffset ++, BitstreamLengthInBits, 1)) + len++; + + frame_bitoffset -= len; + + if (len < 16) + { + levabs = ((len - 1) << shift) + 1; + + // read (vlc-1) bits -> suffix + if (shift) + { + sb = ShowBits(buf, frame_bitoffset + len, BitstreamLengthInBits, shift); + code = (code << (shift) )| sb; + levabs += sb; + len += (shift); + } + + // read 1 bit -> sign + sign = ShowBits(buf, frame_bitoffset + len, BitstreamLengthInBits, 1); + code = (code << 1)| sign; + len ++; + } + else // escape + { + int addbit = len - 5; + int offset = (1 << addbit) + (15 << shift) - 2047; + + sb = ShowBits(buf, frame_bitoffset + len, BitstreamLengthInBits, addbit); + code = (code << addbit ) | sb; + len += addbit; + + levabs = sb + offset; + + // read 1 bit -> sign + sign = ShowBits(buf, frame_bitoffset + len, BitstreamLengthInBits, 1); + + code = (code << 1)| sign; + + len++; + } + + sym->inf = (sign)? -levabs : levabs; + sym->len = len; + + currStream->frame_bitoffset = frame_bitoffset + len; + +#if TRACE + tracebits2(sym->tracestring, sym->len, code); +#endif + + return 0; +} + +/*! + ************************************************************************ + * \brief + * read Total Zeros codeword from UVLC-partition + ************************************************************************ + */ +int readSyntaxElement_TotalZeros(SyntaxElement *sym, Bitstream *currStream) +{ + static const byte lentab[TOTRUN_NUM][16] = + { + + { 1,3,3,4,4,5,5,6,6,7,7,8,8,9,9,9}, + { 3,3,3,3,3,4,4,4,4,5,5,6,6,6,6}, + { 4,3,3,3,4,4,3,3,4,5,5,6,5,6}, + { 5,3,4,4,3,3,3,4,3,4,5,5,5}, + { 4,4,4,3,3,3,3,3,4,5,4,5}, + { 6,5,3,3,3,3,3,3,4,3,6}, + { 6,5,3,3,3,2,3,4,3,6}, + { 6,4,5,3,2,2,3,3,6}, + { 6,6,4,2,2,3,2,5}, + { 5,5,3,2,2,2,4}, + { 4,4,3,3,1,3}, + { 4,4,2,1,3}, + { 3,3,1,2}, + { 2,2,1}, + { 1,1}, + }; + + static const byte codtab[TOTRUN_NUM][16] = + { + {1,3,2,3,2,3,2,3,2,3,2,3,2,3,2,1}, + {7,6,5,4,3,5,4,3,2,3,2,3,2,1,0}, + {5,7,6,5,4,3,4,3,2,3,2,1,1,0}, + {3,7,5,4,6,5,4,3,3,2,2,1,0}, + {5,4,3,7,6,5,4,3,2,1,1,0}, + {1,1,7,6,5,4,3,2,1,1,0}, + {1,1,5,4,3,3,2,1,1,0}, + {1,1,1,3,3,2,2,1,0}, + {1,0,1,3,2,1,1,1,}, + {1,0,1,3,2,1,1,}, + {0,1,1,2,1,3}, + {0,1,1,1,1}, + {0,1,1,1}, + {0,1,1}, + {0,1}, + }; + + int code; + int vlcnum = sym->value1; + int retval = code_from_bitstream_2d(sym, currStream, &lentab[vlcnum][0], &codtab[vlcnum][0], 16, 1, &code); + + if (retval) + { + printf("ERROR: failed to find Total Zeros !cdc\n"); + exit(-1); + } + +#if TRACE + tracebits2(sym->tracestring, sym->len, code); +#endif + + return retval; +} + +/*! + ************************************************************************ + * \brief + * read Total Zeros Chroma DC codeword from UVLC-partition + ************************************************************************ + */ +int readSyntaxElement_TotalZerosChromaDC(VideoParameters *p_Vid, SyntaxElement *sym, Bitstream *currStream) +{ + static const byte lentab[3][TOTRUN_NUM][16] = + { + //YUV420 + {{ 1,2,3,3}, + { 1,2,2}, + { 1,1}}, + //YUV422 + {{ 1,3,3,4,4,4,5,5}, + { 3,2,3,3,3,3,3}, + { 3,3,2,2,3,3}, + { 3,2,2,2,3}, + { 2,2,2,2}, + { 2,2,1}, + { 1,1}}, + //YUV444 + {{ 1,3,3,4,4,5,5,6,6,7,7,8,8,9,9,9}, + { 3,3,3,3,3,4,4,4,4,5,5,6,6,6,6}, + { 4,3,3,3,4,4,3,3,4,5,5,6,5,6}, + { 5,3,4,4,3,3,3,4,3,4,5,5,5}, + { 4,4,4,3,3,3,3,3,4,5,4,5}, + { 6,5,3,3,3,3,3,3,4,3,6}, + { 6,5,3,3,3,2,3,4,3,6}, + { 6,4,5,3,2,2,3,3,6}, + { 6,6,4,2,2,3,2,5}, + { 5,5,3,2,2,2,4}, + { 4,4,3,3,1,3}, + { 4,4,2,1,3}, + { 3,3,1,2}, + { 2,2,1}, + { 1,1}} + }; + + static const byte codtab[3][TOTRUN_NUM][16] = + { + //YUV420 + {{ 1,1,1,0}, + { 1,1,0}, + { 1,0}}, + //YUV422 + {{ 1,2,3,2,3,1,1,0}, + { 0,1,1,4,5,6,7}, + { 0,1,1,2,6,7}, + { 6,0,1,2,7}, + { 0,1,2,3}, + { 0,1,1}, + { 0,1}}, + //YUV444 + {{1,3,2,3,2,3,2,3,2,3,2,3,2,3,2,1}, + {7,6,5,4,3,5,4,3,2,3,2,3,2,1,0}, + {5,7,6,5,4,3,4,3,2,3,2,1,1,0}, + {3,7,5,4,6,5,4,3,3,2,2,1,0}, + {5,4,3,7,6,5,4,3,2,1,1,0}, + {1,1,7,6,5,4,3,2,1,1,0}, + {1,1,5,4,3,3,2,1,1,0}, + {1,1,1,3,3,2,2,1,0}, + {1,0,1,3,2,1,1,1,}, + {1,0,1,3,2,1,1,}, + {0,1,1,2,1,3}, + {0,1,1,1,1}, + {0,1,1,1}, + {0,1,1}, + {0,1}} + }; + + int code; + int yuv = p_Vid->active_sps->chroma_format_idc - 1; + int vlcnum = sym->value1; + int retval = code_from_bitstream_2d(sym, currStream, &lentab[yuv][vlcnum][0], &codtab[yuv][vlcnum][0], 16, 1, &code); + + if (retval) + { + printf("ERROR: failed to find Total Zeros\n"); + exit(-1); + } + +#if TRACE + tracebits2(sym->tracestring, sym->len, code); +#endif + + return retval; +} + + +/*! + ************************************************************************ + * \brief + * read Run codeword from UVLC-partition + ************************************************************************ + */ +int readSyntaxElement_Run(SyntaxElement *sym, Bitstream *currStream) +{ + static const byte lentab[TOTRUN_NUM][16] = + { + {1,1}, + {1,2,2}, + {2,2,2,2}, + {2,2,2,3,3}, + {2,2,3,3,3,3}, + {2,3,3,3,3,3,3}, + {3,3,3,3,3,3,3,4,5,6,7,8,9,10,11}, + }; + + static const byte codtab[TOTRUN_NUM][16] = + { + {1,0}, + {1,1,0}, + {3,2,1,0}, + {3,2,1,1,0}, + {3,2,3,2,1,0}, + {3,0,1,3,2,5,4}, + {7,6,5,4,3,2,1,1,1,1,1,1,1,1,1}, + }; + int code; + int vlcnum = sym->value1; + int retval = code_from_bitstream_2d(sym, currStream, &lentab[vlcnum][0], &codtab[vlcnum][0], 16, 1, &code); + + if (retval) + { + printf("ERROR: failed to find Run\n"); + exit(-1); + } + +#if TRACE + tracebits2(sym->tracestring, sym->len, code); +#endif + + return retval; +} + + +/*! + ************************************************************************ + * \brief + * Reads bits from the bitstream buffer + * + * \param buffer + * containing VLC-coded data bits + * \param totbitoffset + * bit offset from start of partition + * \param info + * returns value of the read bits + * \param bitcount + * total bytes in bitstream + * \param numbits + * number of bits to read + * + ************************************************************************ + */ +int GetBits (byte buffer[],int totbitoffset,int *info, int bitcount, + int numbits) +{ + if ((totbitoffset + numbits ) > bitcount) + { + return -1; + } + else + { + int bitoffset = 7 - (totbitoffset & 0x07); // bit from start of byte + int byteoffset = (totbitoffset >> 3); // byte from start of buffer + int bitcounter = numbits; + byte *curbyte = &(buffer[byteoffset]); + int inf = 0; + + while (numbits--) + { + inf <<=1; + inf |= ((*curbyte)>> (bitoffset--)) & 0x01; + if (bitoffset == -1 ) + { //Move onto next byte to get all of numbits + curbyte++; + bitoffset = 7; + } + // Above conditional could also be avoided using the following: + // curbyte -= (bitoffset >> 3); + // bitoffset &= 0x07; + } + *info = inf; + + return bitcounter; // return absolute offset in bit from start of frame + } +} + +/*! + ************************************************************************ + * \brief + * Reads bits from the bitstream buffer + * + * \param buffer + * buffer containing VLC-coded data bits + * \param totbitoffset + * bit offset from start of partition + * \param bitcount + * total bytes in bitstream + * \param numbits + * number of bits to read + * + ************************************************************************ + */ + +int ShowBits (byte buffer[],int totbitoffset,int bitcount, int numbits) +{ + if ((totbitoffset + numbits ) > bitcount) + { + return -1; + } + else + { + int bitoffset = 7 - (totbitoffset & 0x07); // bit from start of byte + int byteoffset = (totbitoffset >> 3); // byte from start of buffer + byte *curbyte = &(buffer[byteoffset]); + int inf = 0; + + while (numbits--) + { + inf <<=1; + inf |= ((*curbyte)>> (bitoffset--)) & 0x01; + + if (bitoffset == -1 ) + { //Move onto next byte to get all of numbits + curbyte++; + bitoffset = 7; + } + } + return inf; // return absolute offset in bit from start of frame + } +} + diff --git a/ldecod/use_opencv_release.props b/ldecod/use_opencv_release.props new file mode 100644 index 0000000..1ae09b8 --- /dev/null +++ b/ldecod/use_opencv_release.props @@ -0,0 +1,17 @@ + + + + + + + + $(OPENCV_DIR)\lib\; + opencv_calib3d246.lib;opencv_contrib246.lib;opencv_core246.lib;opencv_features2d246.lib;opencv_flann246.lib;opencv_gpu246.lib;opencv_highgui246.lib;opencv_imgproc246.lib;opencv_legacy246.lib;opencv_ml246.lib;opencv_nonfree246.lib;opencv_objdetect246.lib;opencv_photo246.lib;opencv_stitching246.lib;opencv_superres246.lib;opencv_ts246.lib;opencv_video246.lib;opencv_videostab246.lib;%(AdditionalDependencies) + + + $(OPENCV_DIR)\include\;%(AdditionalIncludeDirectories) + HAVE_OPENCV;%(PreprocessorDefinitions) + + + + \ No newline at end of file diff --git a/ldecod/use_opencv_x86_release.props b/ldecod/use_opencv_x86_release.props new file mode 100644 index 0000000..1fe1406 --- /dev/null +++ b/ldecod/use_opencv_x86_release.props @@ -0,0 +1,17 @@ + + + + + + + + $(OPENCV_X86_DIR)\lib\; + opencv_calib3d233.lib;opencv_contrib233.lib;opencv_core233.lib;opencv_features2d233.lib;opencv_flann233.lib;opencv_gpu233.lib;opencv_highgui233.lib;opencv_imgproc233.lib;opencv_legacy233.lib;opencv_ml233.lib;opencv_objdetect233.lib;opencv_stitching233.lib;opencv_ts233.lib;opencv_video233.lib;%(AdditionalDependencies) + + + $(OPENCV_X86_DIR)\include\;%(AdditionalIncludeDirectories) + HAVE_OPENCV;%(PreprocessorDefinitions) + + + + \ No newline at end of file diff --git a/ldecod_vc10.vcxproj b/ldecod_vc10.vcxproj new file mode 100644 index 0000000..45ec315 --- /dev/null +++ b/ldecod_vc10.vcxproj @@ -0,0 +1,565 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + release_parser + Win32 + + + release_parser + x64 + + + Release + Win32 + + + Release + x64 + + + + ldecod + {5499B067-CF32-4141-A757-E0A29866994A} + ldecod + + + + Application + MultiByte + + + Application + MultiByte + + + Application + MultiByte + + + Application + MultiByte + + + Application + MultiByte + + + Application + MultiByte + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <_ProjectFileVersion>10.0.30319.1 + .\bin\ + .\bin\ + .\ldecod\$(Configuration)_$(Platform)_vc10\ + .\ldecod\$(Configuration)_$(Platform)_vc10\ + true + true + .\bin\ + .\bin\ + .\bin\ + .\bin\ + .\ldecod\$(Configuration)_$(Platform)_vc10\ + .\ldecod\$(Configuration)_$(Platform)_vc10\ + .\ldecod\$(Configuration)_$(Platform)_vc10\ + .\ldecod\$(Configuration)_$(Platform)_vc10\ + false + false + false + false + + + + .\ldecod/$(Configuration)_$(Platform)_vc10/ldecod.tlb + + + + + /MP %(AdditionalOptions) + Disabled + WIN32;_DEBUG;_CONSOLE;_CRT_SECURE_NO_DEPRECATE;%(PreprocessorDefinitions) + EnableFastChecks + MultiThreadedDebug + false + + + .\ldecod/$(Configuration)_$(Platform)_vc10/ldecod.pch + .\ldecod\$(Configuration)_$(Platform)_vc10\ + .\ldecod\$(Configuration)_$(Platform)_vc10\ + .\ldecod\$(Configuration)_$(Platform)_vc10\ + true + Level3 + true + EditAndContinue + Default + + + + + _DEBUG;%(PreprocessorDefinitions) + 0x0409 + + + $(OutDir)$(TargetName)$(TargetExt) + true + true + .\ldecod/$(Configuration)_$(Platform)_vc10/ldecod.pdb + false + Console + false + + + MachineX86 + + + + + .\ldecod/$(Configuration)_$(Platform)_vc10/ldecod.tlb + + + + + /MP %(AdditionalOptions) + Disabled + WIN32;_DEBUG;_CONSOLE;_CRT_SECURE_NO_DEPRECATE;%(PreprocessorDefinitions) + EnableFastChecks + MultiThreadedDebug + true + + + .\ldecod/$(Configuration)_$(Platform)_vc10/ldecod.pch + .\ldecod/$(Configuration)_$(Platform)_vc10/ + .\ldecod/$(Configuration)_$(Platform)_vc10/ + .\ldecod/$(Configuration)_$(Platform)_vc10/ + true + Level3 + true + ProgramDatabase + Default + + + _DEBUG;%(PreprocessorDefinitions) + 0x0409 + + + $(OutDir)$(TargetName)$(TargetExt) + true + true + .\ldecod/$(Configuration)_$(Platform)_vc10/ldecod.pdb + false + Console + false + + + + + + + .\ldecod\$(Configuration)_$(Platform)_vc10/ldecod.tlb + + + + + %(AdditionalOptions) + MaxSpeed + AnySuitable + WIN32;NDEBUG;_CRT_SECURE_NO_DEPRECATE;%(PreprocessorDefinitions) + true + MultiThreaded + true + true + false + + + .\ldecod\$(Configuration)_$(Platform)_vc10/ldecod.pch + .\ldecod\$(Configuration)_$(Platform)_vc10\ + .\ldecod\$(Configuration)_$(Platform)_vc10\ + .\ldecod\$(Configuration)_$(Platform)_vc10\ + true + .\ldecod\$(Configuration)_$(Platform)_vc10/ + Level3 + true + ProgramDatabase + Cdecl + Default + + + Neither + + + NDEBUG;%(PreprocessorDefinitions) + 0x0409 + + + /FIXED:NO %(AdditionalOptions) + $(OutDir)$(TargetName)$(TargetExt) + true + true + .\ldecod\$(Configuration)_$(Platform)_vc10/ldecod.pdb + false + Console + false + + + MachineX86 + + + + + .\ldecod\$(Configuration)_$(Platform)_vc10/ldecod.tlb + + + + + %(AdditionalOptions) + MaxSpeed + AnySuitable + WIN32;NDEBUG;_CRT_SECURE_NO_DEPRECATE;%(PreprocessorDefinitions) + true + MultiThreaded + true + true + false + + + .\ldecod\$(Configuration)_$(Platform)_vc10/ldecod.pch + .\ldecod\$(Configuration)_$(Platform)_vc10\ + .\ldecod\$(Configuration)_$(Platform)_vc10\ + .\ldecod\$(Configuration)_$(Platform)_vc10\ + true + .\ldecod\$(Configuration)_$(Platform)_vc10/ + Level3 + true + ProgramDatabase + Cdecl + Default + + + Neither + + + NDEBUG;%(PreprocessorDefinitions) + 0x0409 + + + /FIXED:NO %(AdditionalOptions) + $(OutDir)$(TargetName)$(TargetExt) + true + true + .\ldecod\$(Configuration)_$(Platform)_vc10/ldecod.pdb + false + Console + false + + + MachineX86 + + + + + .\ldecod\$(Configuration)_$(Platform)_vc10/ldecod.tlb + + + + + %(AdditionalOptions) + MaxSpeed + AnySuitable + WIN32;NDEBUG;_CRT_SECURE_NO_DEPRECATE;%(PreprocessorDefinitions) + true + MultiThreaded + true + true + false + + + .\ldecod\$(Configuration)_$(Platform)_vc10/ldecod.pch + .\ldecod\$(Configuration)_$(Platform)_vc10/ + .\ldecod\$(Configuration)_$(Platform)_vc10/ + .\ldecod\$(Configuration)_$(Platform)_vc10/ + true + .\ldecod\$(Configuration)_$(Platform)_vc10/ + Level3 + true + ProgramDatabase + Cdecl + Default + Neither + false + StreamingSIMDExtensions2 + + + NDEBUG;%(PreprocessorDefinitions) + 0x0409 + + + /FIXED:NO %(AdditionalOptions) + Ws2_32.lib;%(AdditionalDependencies) + $(OutDir)$(TargetName)$(TargetExt) + true + true + .\ldecod\$(Configuration)_$(Platform)_vc10/ldecod.pdb + false + Console + false + + + Default + + + + + .\ldecod\$(Configuration)_$(Platform)_vc10/ldecod.tlb + + + + + %(AdditionalOptions) + MaxSpeed + AnySuitable + WIN32;NDEBUG;_CRT_SECURE_NO_DEPRECATE;%(PreprocessorDefinitions) + true + MultiThreaded + true + true + false + + + .\ldecod\$(Configuration)_$(Platform)_vc10/ldecod.pch + .\ldecod\$(Configuration)_$(Platform)_vc10/ + .\ldecod\$(Configuration)_$(Platform)_vc10/ + .\ldecod\$(Configuration)_$(Platform)_vc10/ + true + .\ldecod\$(Configuration)_$(Platform)_vc10/ + Level3 + true + ProgramDatabase + Cdecl + Default + Neither + + + NDEBUG;%(PreprocessorDefinitions) + 0x0409 + + + /FIXED:NO %(AdditionalOptions) + Ws2_32.lib;%(AdditionalDependencies) + $(OutDir)$(TargetName)_parser$(TargetExt) + true + true + .\ldecod\$(Configuration)_$(Platform)_vc10/ldecod.pdb + false + Console + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ldecod_vc10.vcxproj.filters b/ldecod_vc10.vcxproj.filters new file mode 100644 index 0000000..ba55b40 --- /dev/null +++ b/ldecod_vc10.vcxproj.filters @@ -0,0 +1,496 @@ + + + + + + + + + {45079de0-5d68-41ce-9d18-5d46f22c6853} + + + {9c7796bb-c64f-4d78-a028-bd13e8177aab} + + + {3952ab50-719a-4c3a-b5ec-1209d4d9e084} + + + {d91cfc9d-ed81-4e7f-a92b-cbabd1227627} + + + {04deefe3-6346-4041-9dc6-b021cafb3b82} + + + {0325f21c-1221-46e4-9a20-2a1e3ece8319} + + + + + Common Header Files + + + Common Header Files + + + Common Header Files + + + Common Header Files + + + Common Header Files + + + Common Header Files + + + Common Header Files + + + Common Header Files + + + Common Header Files + + + Common Header Files + + + Common Header Files + + + Common Header Files + + + Common Header Files + + + Common Header Files + + + Common Header Files + + + Common Header Files + + + Common Header Files + + + Common Header Files + + + Common Header Files + + + Common Header Files + + + Common Header Files + + + Common Header Files + + + Common Header Files + + + Common Header Files + + + Common Header Files + + + Common Header Files + + + Common Header Files + + + Common Header Files + + + Common Header Files + + + Common Header Files + + + Common Header Files + + + Project Header Files + + + Project Header Files + + + Project Header Files + + + Project Header Files + + + Project Header Files + + + Project Header Files + + + Project Header Files + + + Project Header Files + + + Project Header Files + + + Project Header Files + + + Project Header Files + + + Project Header Files + + + Project Header Files + + + Project Header Files + + + Project Header Files + + + Project Header Files + + + Project Header Files + + + Project Header Files + + + Project Header Files + + + Project Header Files + + + Project Header Files + + + Project Header Files + + + Project Header Files + + + Project Header Files + + + Project Header Files + + + Project Header Files + + + Project Header Files + + + Project Header Files + + + Project Header Files + + + Project Header Files + + + Project Header Files + + + Project Header Files + + + Project Header Files + + + Project Header Files + + + Project Header Files + + + Project Header Files + + + Project Header Files + + + Project Header Files + + + Project Header Files + + + Project Header Files + + + Project Header Files + + + Project Header Files + + + Project Header Files\ferc + + + Project Header Files\ferc + + + Project Header Files\ferc + + + Project Header Files\ferc + + + + + Common Source Files + + + Common Source Files + + + Common Source Files + + + Common Source Files + + + Common Source Files + + + Common Source Files + + + Common Source Files + + + Common Source Files + + + Common Source Files + + + Common Source Files + + + Common Source Files + + + Common Source Files + + + Common Source Files + + + Common Source Files + + + Common Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files\ferc + + + Project Source Files\ferc + + + Project Source Files\ferc + + + Project Source Files\ferc + + + Project Source Files\ferc + + + Project Source Files\ferc + + + Project Source Files\ferc + + + Project Source Files\ferc + + + \ No newline at end of file diff --git a/ldecod_vc10.vcxproj.user b/ldecod_vc10.vcxproj.user new file mode 100644 index 0000000..f225daa --- /dev/null +++ b/ldecod_vc10.vcxproj.user @@ -0,0 +1,15 @@ + + + + -d zdecoder_config.cfg + + + ..\jm_debug + WindowsLocalDebugger + + + ..\jm_debug + WindowsLocalDebugger + -d zdecoder_config.cfg + + \ No newline at end of file diff --git a/ldecod_vc7.vcproj b/ldecod_vc7.vcproj new file mode 100644 index 0000000..b7b93f0 --- /dev/null +++ b/ldecod_vc7.vcproj @@ -0,0 +1,853 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ldecod_vc8.vcproj b/ldecod_vc8.vcproj new file mode 100644 index 0000000..8e2d9e7 --- /dev/null +++ b/ldecod_vc8.vcproj @@ -0,0 +1,1347 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ldecod_vc9.vcproj b/ldecod_vc9.vcproj new file mode 100644 index 0000000..19d8610 --- /dev/null +++ b/ldecod_vc9.vcproj @@ -0,0 +1,956 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lencod/Makefile b/lencod/Makefile new file mode 100644 index 0000000..0660748 --- /dev/null +++ b/lencod/Makefile @@ -0,0 +1,132 @@ +### +### Makefile for H.264/AVC encoder +### +### generated for UNIX/LINUX environments +### by H. Schwarz, Limin Wang +### + + + +NAME= lencod + +### include debug information: 1=yes, 0=no +DBG?= 0 +### Generate 32 bit executable : 1=yes, 0=no +M32?= 0 +### include O level optimization : 0-3 +OPT?= 3 +### Static Compilation +STC?= 0 +### OPENMP support : 1=yes, 0=no +OPENMP?= 1 + +DEPEND= dependencies + +BINDIR= ../bin +INCDIR= inc +SRCDIR= src +OBJDIR= obj + +ADDSRCDIR= ../lcommon/src +ADDINCDIR= ../lcommon/inc + +ifeq ($(STC),1) +ifeq ($(DBG),1) ### Do not use static compilation for Debug mode +STC=0 +STATIC= +else +STATIC= -static +endif +else +STATIC= +endif + +LIBS= -lm $(STATIC) +CFLAGS= -std=gnu99 -pedantic -ffloat-store -fno-strict-aliasing -fsigned-char $(STATIC) +FLAGS= $(CFLAGS) -Wall -I$(INCDIR) -I$(ADDINCDIR) -D __USE_LARGEFILE64 -D _FILE_OFFSET_BITS=64 + +ifeq ($(M32),1) +FLAGS+=-m32 +endif + +ifeq ($(OPENMP),1) +FLAGS+=-fopenmp +endif + + +OPT_FLAG = -O$(OPT) +ifeq ($(DBG),1) +SUFFIX= .dbg +FLAGS+= -g +else +SUFFIX= +FLAGS+= $(OPT_FLAG) +endif + +OBJSUF= .o$(SUFFIX) + +SRC= $(wildcard $(SRCDIR)/*.c) +ADDSRC= $(wildcard $(ADDSRCDIR)/*.c) +OBJ= $(SRC:$(SRCDIR)/%.c=$(OBJDIR)/%.o$(SUFFIX)) $(ADDSRC:$(ADDSRCDIR)/%.c=$(OBJDIR)/%.o$(SUFFIX)) +BIN= $(BINDIR)/$(NAME)$(SUFFIX).exe + +.PHONY: default distclean clean tags depend + +default: messages objdir_mk depend bin + +messages: +ifeq ($(M32),1) + @echo 'Compiling with M32 support...' +endif +ifeq ($(DBG),1) + @echo 'Compiling with Debug support...' + @echo 'Note static compilation not supported in this mode.' +endif +ifeq ($(STC),1) + @echo 'Compiling with -static support...' +endif +ifeq ($(OPENMP),1) + @echo 'Compiling with -fopenmp support...' +endif + +clean: + @echo remove all objects + @rm -rf $(OBJDIR) + +distclean: clean + @rm -f $(DEPEND) tags + @rm -f $(BIN) + +tags: + @echo update tag table + @ctags inc/*.h src/*.c + +bin: $(OBJ) + @echo + @echo 'creating binary "$(BIN)"' + @$(CC) $(FLAGS) -o $(BIN) $(OBJ) $(LIBS) + @echo '... done' + @echo + +depend: + @echo + @echo 'checking dependencies' + @$(SHELL) -ec '$(CC) $(FLAGS) -MM $(CFLAGS) -I$(INCDIR) -I$(ADDINCDIR) $(SRC) $(ADDSRC) \ + | sed '\''s@\(.*\)\.o[ :]@$(OBJDIR)/\1.o$(SUFFIX):@g'\'' \ + >$(DEPEND)' + @echo + +$(OBJDIR)/%.o$(SUFFIX): $(SRCDIR)/%.c + @echo 'compiling object file "$@" ...' + @$(CC) -c -o $@ $(FLAGS) $< + +$(OBJDIR)/%.o$(SUFFIX): $(ADDSRCDIR)/%.c + @echo 'compiling object file "$@" ...' + @$(CC) -c -o $@ $(FLAGS) $< + +objdir_mk: + @echo 'Creating $(OBJDIR) ...' + @mkdir -p $(OBJDIR) + +-include $(DEPEND) + diff --git a/lencod/inc/annexb.h b/lencod/inc/annexb.h new file mode 100644 index 0000000..f033bf1 --- /dev/null +++ b/lencod/inc/annexb.h @@ -0,0 +1,25 @@ +/*! + ************************************************************************************** + * \file + * annexb.h + * \brief + * Byte stream operations support + * + * \date 7 December 2002 + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * - Stephan Wenger + *************************************************************************************** + */ + +#ifndef _ANNEXB_H_ +#define _ANNEXB_H_ + +#include "nalucommon.h" + +extern int WriteAnnexbNALU (VideoParameters *p_Vid, NALU_t *n, FILE **f_annexb); +extern void OpenAnnexbFile (char *fn, FILE **f_annexb); +extern void CloseAnnexbFile(FILE *f_annexb); + + +#endif //_ANNEXB_H_ diff --git a/lencod/inc/annexb_split.h b/lencod/inc/annexb_split.h new file mode 100644 index 0000000..e435f4f --- /dev/null +++ b/lencod/inc/annexb_split.h @@ -0,0 +1,34 @@ +/*! +************************************************************************************* +* \file annexb_split.h +* +* \brief +* +* \date +* 03-02-2012 +* +* \version +* 1.0 +* +* \author +* João Carreira +* +************************************************************************************* +*/ + +#ifndef _ANNEXB_SPLIT_H_ +#define _ANNEXB_SPLIT_H_ + +#include "global.h" +#include "nalucommon.h" + +extern void split_annexb_files( VideoParameters *p_Vid, NaluType nalu_type, void *data_pointer, size_t length ); + +extern void OpenAnnexbFileBaseView (VideoParameters *p_Vid, char *Filename); +extern void OpenAnnexbFileAuxView (VideoParameters *p_Vid, char *Filename); + +extern void CloseAnnexbFileBaseView(VideoParameters *p_Vid); +extern void CloseAnnexbFileAuxView(VideoParameters *p_Vid); + +extern void fflush_split_files( VideoParameters *p_Vid ); +#endif \ No newline at end of file diff --git a/lencod/inc/biariencode.h b/lencod/inc/biariencode.h new file mode 100644 index 0000000..0db5459 --- /dev/null +++ b/lencod/inc/biariencode.h @@ -0,0 +1,69 @@ + +/*! + *************************************************************************** + * \file + * biariencode.h + * + * \brief + * Headerfile for binary arithmetic encoding routines + * + * \author + * - Detlev Marpe + * - Gabi Blaettermann + * - Gunnar Marten + * + * Copyright (C) 2000 HEINRICH HERTZ INSTITUTE All Rights Reserved. + * + * \date + * 21. Oct 2000 + ************************************************************************** + */ + + +#ifndef _BIARIENCOD_H_ +#define _BIARIENCOD_H_ + + +/************************************************************************ + * D e f i n i t i o n s + *********************************************************************** + */ + +// some definitions to increase the readability of the source code + +#define B_BITS 10 // Number of bits to represent the whole coding interval +#define BITS_TO_LOAD 16 +#define MAX_BITS 26 //(B_BITS + BITS_TO_LOAD) +#define MASK_BITS 18 //(MAX_BITS - 8) +#define ONE 0x04000000 //(1 << MAX_BITS) +#define ONE_M1 0x03FFFFFF //(ONE - 1) +#define HALF 0x01FE //(1 << (B_BITS-1)) - 2 +#define QUARTER 0x0100 //(1 << (B_BITS-2)) +#define MIN_BITS_TO_GO 0 +#define B_LOAD_MASK 0xFFFF // ((1<Ecodestrm_len) + eep->Epbuf + 1) << 3) + (eep->Echunks_outstanding * BITS_TO_LOAD) + BITS_TO_LOAD - eep->Ebits_to_go; +} + +#endif // BIARIENCOD_H + diff --git a/lencod/inc/block.h b/lencod/inc/block.h new file mode 100644 index 0000000..25bcbb6 --- /dev/null +++ b/lencod/inc/block.h @@ -0,0 +1,103 @@ + +/*! + ************************************************************************ + * \file block.h + * + * \brief + * constant arrays for single block processing + * + * \author + * Inge Lille-Langoy \n + * Telenor Satellite Services \n + * P.O.Box 6914 St.Olavs plass \n + * N-0130 Oslo, Norway + * + ************************************************************************ + */ + +#ifndef _BLOCK_H_ +#define _BLOCK_H_ + + +#include "global.h" + +extern int residual_transform_quant_luma_4x4(Macroblock *currMB, ColorPlane pl, int block_x,int block_y, int *coeff_cost, int intra); + +static const byte QP_SCALE_CR[52]= +{ + 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,32,33,34,34,35,35,36,36,37,37, 37,38,38,38,39,39,39,39 +}; + +//! look up tables for FRExt_chroma support +static const unsigned char subblk_offset_x[3][8][4] = +{ + { + {0, 4, 0, 4}, + {0, 4, 0, 4}, + {0, 0, 0, 0}, + {0, 0, 0, 0}, + {0, 0, 0, 0}, + {0, 0, 0, 0}, + {0, 0, 0, 0}, + {0, 0, 0, 0}, + }, + { + {0, 4, 0, 4}, + {0, 4, 0, 4}, + {0, 4, 0, 4}, + {0, 4, 0, 4}, + {0, 0, 0, 0}, + {0, 0, 0, 0}, + {0, 0, 0, 0}, + {0, 0, 0, 0}, + }, + { + {0, 4, 0, 4}, + {8,12, 8,12}, + {0, 4, 0, 4}, + {8,12, 8,12}, + {0, 4, 0, 4}, + {8,12, 8,12}, + {0, 4, 0, 4}, + {8,12, 8,12} + } +}; + + +static const unsigned char subblk_offset_y[3][8][4] = +{ + { + {0, 0, 4, 4}, + {0, 0, 4, 4}, + {0, 0, 0, 0}, + {0, 0, 0, 0}, + {0, 0, 0, 0}, + {0, 0, 0, 0}, + {0, 0, 0, 0}, + {0, 0, 0, 0} + }, + { + {0, 0, 4, 4}, + {8, 8,12,12}, + {0, 0, 4, 4}, + {8, 8,12,12}, + {0, 0, 0, 0}, + {0, 0, 0, 0}, + {0, 0, 0, 0}, + {0, 0, 0, 0} + }, + { + {0, 0, 4, 4}, + {0, 0, 4, 4}, + {8, 8,12,12}, + {8, 8,12,12}, + {0, 0, 4, 4}, + {0, 0, 4, 4}, + {8, 8,12,12}, + {8, 8,12,12} + } +}; + +#endif + diff --git a/lencod/inc/cabac.h b/lencod/inc/cabac.h new file mode 100644 index 0000000..52e5f3b --- /dev/null +++ b/lencod/inc/cabac.h @@ -0,0 +1,73 @@ + +/*! + *************************************************************************** + * \file + * cabac.h + * + * \brief + * Headerfile for entropy coding routines + * + * \author + * Detlev Marpe \n + * Copyright (C) 2000 HEINRICH HERTZ INSTITUTE All Rights Reserved. + * + * \date + * 21. Oct 2000 (Changes by Tobias Oelbaum 28.08.2001) + *************************************************************************** + */ + + +#ifndef _CABAC_H_ +#define _CABAC_H_ + +static const byte maxpos [] = {15, 14, 63, 31, 31, 15, 3, 14, 7, 15, 15, 14, 63, 31, 31, 15, 15, 14, 63, 31, 31, 15}; +static const byte c1isdc [] = { 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1}; + +static const byte type2ctx_bcbp[] = { 0, 1, 2, 3, 3, 4, 5, 6, 5, 5, 10, 11, 12, 13, 13, 14, 16, 17, 18, 19, 19, 20}; +static const byte type2ctx_map [] = { 0, 1, 2, 3, 4, 5, 6, 7, 6, 6, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21}; // 8 +static const byte type2ctx_last[] = { 0, 1, 2, 3, 4, 5, 6, 7, 6, 6, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21}; // 8 +static const byte type2ctx_one [] = { 0, 1, 2, 3, 3, 4, 5, 6, 5, 5, 10, 11, 12, 13, 13, 14, 16, 17, 18, 19, 19, 20}; // 7 +static const byte type2ctx_abs [] = { 0, 1, 2, 3, 3, 4, 5, 6, 5, 5, 10, 11, 12, 13, 13, 14, 16, 17, 18, 19, 19, 20}; // 7 +static const byte max_c2 [] = { 4, 4, 4, 4, 4, 4, 3, 4, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4}; // 9 + + +extern const byte* pos2ctx_map []; +extern const byte* pos2ctx_map_int []; +extern const byte* pos2ctx_last []; + +// CABAC +extern MotionInfoContexts* create_contexts_MotionInfo (void); +extern TextureInfoContexts* create_contexts_TextureInfo(void); + +extern void delete_contexts_MotionInfo (MotionInfoContexts *enco_ctx); +extern void delete_contexts_TextureInfo (TextureInfoContexts *enco_ctx); +extern void writeMB_I_typeInfo_CABAC (Macroblock *currMB, SyntaxElement *se, DataPartition *dp); +extern void writeMB_B_typeInfo_CABAC (Macroblock *currMB, SyntaxElement *se, DataPartition *dp); +extern void writeMB_P_typeInfo_CABAC (Macroblock *currMB, SyntaxElement *se, DataPartition *dp); +extern void writeIntraPredMode_CABAC (SyntaxElement *se, DataPartition *dp); +extern void writeB8_typeInfo_CABAC (SyntaxElement *se, DataPartition *dp); +extern void writeB8_B_typeInfo_CABAC (SyntaxElement *se, DataPartition *dp); + +extern void writeRefPic_B_CABAC (Macroblock *currMB, SyntaxElement *se, DataPartition *dp); +extern void writeRefPic_P_CABAC (Macroblock *currMB, SyntaxElement *se, DataPartition *dp); + +extern void writeMVD_CABAC (Macroblock *currMB, SyntaxElement *se, DataPartition *dp); +extern void writeCBP_CABAC (Macroblock *currMB, SyntaxElement *se, DataPartition *dp); +extern void writeDquant_CABAC (Macroblock *currMB, SyntaxElement *se, DataPartition *dp); +extern void writeRunLevel_CABAC (Macroblock* currMB, SyntaxElement *se, DataPartition *dp); +extern void writeCIPredMode_CABAC (Macroblock* currMB, SyntaxElement *se, DataPartition *dp); +extern void print_ctx_TextureInfo (TextureInfoContexts *enco_ctx); +extern void writeMB_Pskip_flagInfo_CABAC(Macroblock *currMB, SyntaxElement *se, DataPartition *dp); +extern void writeMB_Bskip_flagInfo_CABAC(Macroblock *currMB, SyntaxElement *se, DataPartition *dp); +extern void writeFieldModeInfo_CABAC (Macroblock *currMB, SyntaxElement *se, DataPartition *dp); //GB +extern void writeCBP_BIT_CABAC (Macroblock *currMB, int b8, int bit, int cbp, EncodingEnvironmentPtr eep_dp, TextureInfoContexts *ctx); + +extern void CheckAvailabilityOfNeighborsCABAC (Macroblock* currMB); +extern void writeMB_transform_size_CABAC(Macroblock *currMB, SyntaxElement *se, DataPartition *dp); + +extern void write_and_store_CBP_block_bit_444 (Macroblock* currMB, EncodingEnvironmentPtr eep_dp, int type, int cbp_bit, TextureInfoContexts* tex_ctx); +extern void write_and_store_CBP_block_bit (Macroblock* currMB, EncodingEnvironmentPtr eep_dp, int type, int cbp_bit, TextureInfoContexts* tex_ctx); + + +#endif // CABAC_H + diff --git a/lencod/inc/cconv_yuv2rgb.h b/lencod/inc/cconv_yuv2rgb.h new file mode 100644 index 0000000..5b801d9 --- /dev/null +++ b/lencod/inc/cconv_yuv2rgb.h @@ -0,0 +1,28 @@ +/*! + *************************************************************************** + * \file + * cconv_yuv2rgb.h + * + * \author + * Woo-Shik Kim + * + * \date + * 29 May 2008 + * + * \brief + * Headerfile for YUV to RGB color conversion + ************************************************************************** + */ + +#ifndef _CCONV_YUV2RGB_H_ +#define _CCONV_YUV2RGB_H_ + +#include "img_distortion.h" + +extern void init_YUVtoRGB (VideoParameters *p_Vid, InputParameters *p_Inp); +extern void YUVtoRGB (VideoParameters *p_Vid, ImageStructure *YUV, ImageStructure *RGB); +extern int create_RGB_memory(VideoParameters *p_Vid); +extern void delete_RGB_memory(VideoParameters *p_Vid); + +#endif + diff --git a/lencod/inc/configfile.h b/lencod/inc/configfile.h new file mode 100644 index 0000000..396474e --- /dev/null +++ b/lencod/inc/configfile.h @@ -0,0 +1,642 @@ + +/*! + *********************************************************************** + * \file + * configfile.h + * \brief + * Prototypes for configfile.c and definitions of used structures. + *********************************************************************** + */ + +#include "fmo.h" + +#ifndef _CONFIGFILE_H_ +#define _CONFIGFILE_H_ + +#include "config_common.h" + +#define DEFAULTCONFIGFILENAME "encoder.cfg" + +#define PROFILE_IDC 88 +#define LEVEL_IDC 21 + +InputParameters cfgparams; + + +#ifdef INCLUDED_BY_CONFIGFILE_C +// Mapping_Map Syntax: +// {NAMEinConfigFile, &cfgparams.VariableName, Type, InitialValue, LimitType, MinLimit, MaxLimit, CharSize} +// Types : {0:int, 1:text, 2: double} +// LimitType: {0:none, 1:both, 2:minimum, 3: QP based} +// We could separate this based on types to make it more flexible and allow also defaults for text types. +Mapping Map[] = { + //! Edited by Joao Carreira + {"TranscodeFileBase", &cfgparams.TranscodeFile[0], 1, 0.0, 0, 0.0, 0.0, FILE_NAME_SIZE }, + {"List1NotUsed", &cfgparams.List1NotUsed, 0, 0.0, 1, 0.0, 2.0, }, + + {"ProfileIDC", &cfgparams.ProfileIDC, 0, (double) PROFILE_IDC, 0, 0.0, 0.0, }, + {"IntraProfile", &cfgparams.IntraProfile, 0, 0.0, 1, 0.0, 1.0, }, + {"LevelIDC", &cfgparams.LevelIDC, 0, (double) LEVEL_IDC, 0, 0.0, 0.0, }, + {"FrameRate", &cfgparams.source.frame_rate, 2, (double) INIT_FRAME_RATE, 1, 0.0, 480.0, }, + {"Enable32Pulldown", &cfgparams.enable_32_pulldown, 0, 0.0, 1, 0.0, 2.0, }, + {"ResendSPS", &cfgparams.ResendSPS, 0, 0.0, 1, 0.0, 3.0, }, + {"StartFrame", &cfgparams.start_frame, 0, 0.0, 2, 0.0, 0.0, }, + {"IntraPeriod", &cfgparams.intra_period, 0, 0.0, 2, 0.0, 0.0, }, + {"IDRPeriod", &cfgparams.idr_period, 0, 0.0, 2, 0.0, 0.0, }, + {"IntraDelay", &cfgparams.intra_delay, 0, 0.0, 2, 0.0, 0.0, }, + {"AdaptiveIntraPeriod", &cfgparams.adaptive_intra_period, 0, 0.0, 1, 0.0, 1.0, }, + {"AdaptiveIDRPeriod", &cfgparams.adaptive_idr_period, 0, 0.0, 1, 0.0, 2.0, }, + {"EnableOpenGOP", &cfgparams.EnableOpenGOP, 0, 0.0, 1, 0.0, 1.0, }, + {"EnableIDRGOP", &cfgparams.EnableIDRGOP, 0, 0.0, 1, 0.0, 1.0, }, + {"FramesToBeEncoded", &cfgparams.no_frames, 0, 1.0, 2, -1.0, 0.0, }, + {"QPISlice", &cfgparams.qp[I_SLICE], 0, 24.0, 3, (double) MIN_QP, (double) MAX_QP, }, + {"QPPSlice", &cfgparams.qp[P_SLICE], 0, 24.0, 3, (double) MIN_QP, (double) MAX_QP, }, + {"QPBSlice", &cfgparams.qp[B_SLICE], 0, 24.0, 3, (double) MIN_QP, (double) MAX_QP, }, + {"QPSPSlice", &cfgparams.qp[SP_SLICE], 0, 24.0, 3, (double) MIN_QP, (double) MAX_QP, }, + {"QPSISlice", &cfgparams.qp[SI_SLICE], 0, 24.0, 3, (double) MIN_QP, (double) MAX_QP, }, + {"ChangeQPFrame", &cfgparams.qp2frame, 0, 0.0, 2, 0.0, 0.0, }, + {"ChangeQPI", &cfgparams.qp2off[I_SLICE], 0, 0.0, 0, (double) -MAX_QP, (double) MAX_QP, }, + {"ChangeQPP", &cfgparams.qp2off[P_SLICE], 0, 0.0, 0, (double) -MAX_QP, (double) MAX_QP, }, + {"ChangeQPB", &cfgparams.qp2off[B_SLICE], 0, 0.0, 0, (double) -MAX_QP, (double) MAX_QP, }, + {"ChangeQPSP", &cfgparams.qp2off[SP_SLICE], 0, 0.0, 0, (double) -MAX_QP, (double) MAX_QP, }, + {"ChangeQPSI", &cfgparams.qp2off[SI_SLICE], 0, 0.0, 0, (double) -MAX_QP, (double) MAX_QP, }, + {"QPSP2Slice", &cfgparams.qpsp, 0, 0.0, 3, (double) MIN_QP, (double) MAX_QP, }, + {"FrameSkip", &cfgparams.frame_skip, 0, 0.0, 2, 0.0, 0.0, }, + {"DisableSubpelME", &cfgparams.DisableSubpelME[0], 0, 0.0, 1, 0.0, 1.0, }, + {"SearchRange", &cfgparams.search_range[0], 0, 16.0, 2, 0.0, 0.0, }, + {"NumberReferenceFrames", &cfgparams.num_ref_frames, 0, 1.0, 1, 0.0, 16.0, }, + {"PList0References", &cfgparams.P_List0_refs[0], 0, 0.0, 1, 0.0, 16.0, }, + {"BList0References", &cfgparams.B_List0_refs[0], 0, 0.0, 1, 0.0, 16.0, }, + {"BList1References", &cfgparams.B_List1_refs[0], 0, 1.0, 1, 0.0, 16.0, }, + {"NumberOfViews", &cfgparams.num_of_views, 0, 1.0, 1, 1.0, 2.0, }, +#if (MVC_EXTENSION_ENABLE) + {"View1ConfigFile", &cfgparams.View1ConfigName, 1, 0.0, 0, 0.0, 0.0, FILE_NAME_SIZE, }, + //! Edit by João Carreira + {"OutputBaseView", &cfgparams.outfile_base, 1, 0.0, 0, 0.0, 0.0, FILE_NAME_SIZE }, + {"OutputAuxView", &cfgparams.outfile_aux, 1, 0.0, 0, 0.0, 0.0, FILE_NAME_SIZE }, +#endif + {"Log2MaxFNumMinus4", &cfgparams.Log2MaxFNumMinus4, 0, 0.0, 1, -1.0, 12.0, }, + {"Log2MaxPOCLsbMinus4", &cfgparams.Log2MaxPOCLsbMinus4, 0, 2.0, 1, -1.0, 12.0, }, + {"GenerateMultiplePPS", &cfgparams.GenerateMultiplePPS, 0, 0.0, 1, 0.0, 1.0, }, + {"ResendPPS", &cfgparams.ResendPPS, 0, 0.0, 1, 0.0, 1.0, }, + {"SendAUD", &cfgparams.SendAUD, 0, 0.0, 1, 0.0, 3.0, }, + {"SourceWidth", &cfgparams.source.width[0], 0, 176.0, 2, 0.0, 0.0, }, + {"SourceHeight", &cfgparams.source.height[0], 0, 144.0, 2, 0.0, 0.0, }, + {"SourceResize", &cfgparams.src_resize, 0, 0.0, 1, 0.0, 1.0, }, + {"OutputWidth", &cfgparams.output.width[0], 0, 176.0, 2, 16.0, 0.0, }, + {"OutputHeight", &cfgparams.output.height[0], 0, 144.0, 2, 16.0, 0.0, }, + {"Grayscale", &cfgparams.grayscale, 0, 0.0, 0, 0.0, 1.0, }, + {"MbLineIntraUpdate", &cfgparams.intra_upd, 0, 0.0, 1, 0.0, 1.0, }, + {"SliceMode", &cfgparams.slice_mode, 0, 0.0, 1, 0.0, 3.0, }, + {"SliceArgument", &cfgparams.slice_argument, 0, 1.0, 2, 1.0, 1.0, }, + {"UseConstrainedIntraPred", &cfgparams.UseConstrainedIntraPred, 0, 0.0, 1, 0.0, 1.0, }, + {"InputFile", &cfgparams.input_file1.fname, 1, 0.0, 0, 0.0, 0.0, FILE_NAME_SIZE, }, + {"InputHeaderLength", &cfgparams.infile_header, 0, 0.0, 2, 0.0, 1.0, }, + {"OutputFile", &cfgparams.outfile, 1, 0.0, 0, 0.0, 0.0, FILE_NAME_SIZE, }, + {"ReconFile", &cfgparams.ReconFile, 1, 0.0, 0, 0.0, 0.0, FILE_NAME_SIZE, }, + {"TraceFile", &cfgparams.TraceFile, 1, 0.0, 0, 0.0, 0.0, FILE_NAME_SIZE, }, + {"StatsFile", &cfgparams.StatsFile, 1, 0.0, 0, 0.0, 0.0, FILE_NAME_SIZE, }, + {"DisposableP", &cfgparams.DisposableP, 0, 0.0, 1, 0.0, 1.0, }, + {"SetFirstAsLongTerm", &cfgparams.SetFirstAsLongTerm, 0, 0.0, 1, 0.0, 1.0, }, + {"MultiSourceData", &cfgparams.MultiSourceData, 0, 0.0, 0, 0.0, 2.0, }, + {"InputFile3", &cfgparams.input_file3.fname, 1, 0.0, 0, 0.0, 0.0, FILE_NAME_SIZE, }, + {"SEIVUI32Pulldown", &cfgparams.SEIVUI32Pulldown, 0, 0.0, 1, 0.0, 5.0, }, + + {"ProcessInput", &cfgparams.ProcessInput, 0, 0.0, 1, 0.0, 4.0, }, + {"DispPQPOffset", &cfgparams.DispPQPOffset, 0, 0.0, 0,-51.0, 51.0, }, + {"NumberBFrames", &cfgparams.NumberBFrames, 0, 0.0, 2, 0.0, 0.0, }, + {"PReplaceBSlice", &cfgparams.PReplaceBSlice, 0, 0.0, 1, 0.0, 1.0, }, + {"BRefPicQPOffset", &cfgparams.qpBRSOffset, 0, 0.0, 0,-51.0, 51.0, }, + {"DirectModeType", &cfgparams.direct_spatial_mv_pred_flag, 0, 0.0, 1, 0.0, 1.0, }, + {"DirectInferenceFlag", &cfgparams.directInferenceFlag, 0, 1.0, 1, 0.0, 1.0, }, + {"SPPicturePeriodicity", &cfgparams.sp_periodicity, 0, 0.0, 2, 0.0, 0.0, }, + {"SI_FRAMES", &cfgparams.si_frame_indicator, 0, 0.0, 1, 0.0, 1.0, }, + {"SP_output", &cfgparams.sp_output_indicator, 0, 0.0, 1, 0.0, 1.0, }, + {"SP_output_name", &cfgparams.sp_output_filename, 1, 0.0, 0, 0.0, 0.0, FILE_NAME_SIZE, }, + {"SPSwitchPeriod", &cfgparams.sp_switch_period, 0, 0.0, 2, 0.0, 0.0, }, + {"SP2_FRAMES", &cfgparams.sp2_frame_indicator, 0, 0.0, 1, 0.0, 1.0, }, + {"SP2_input_name1", &cfgparams.sp2_input_filename1, 1, 0.0, 0, 0.0, 0.0, FILE_NAME_SIZE, }, + {"SP2_input_name2", &cfgparams.sp2_input_filename2, 1, 0.0, 0, 0.0, 0.0, FILE_NAME_SIZE, }, + {"SymbolMode", &cfgparams.symbol_mode, 0, 0.0, 1, (double) CAVLC, (double) CABAC, }, + {"OutFileMode", &cfgparams.of_mode, 0, 0.0, 1, 0.0, 1.0, }, + {"PartitionMode", &cfgparams.partition_mode, 0, 0.0, 1, 0.0, 1.0, }, + {"PSliceSkip", &cfgparams.InterSearch[0][0][0], 0, 1.0, 1, 0.0, 1.0, }, + {"PSliceSearch16x16", &cfgparams.InterSearch[0][0][1], 0, 1.0, 1, 0.0, 1.0, }, + {"PSliceSearch16x8", &cfgparams.InterSearch[0][0][2], 0, 1.0, 1, 0.0, 1.0, }, + {"PSliceSearch8x16", &cfgparams.InterSearch[0][0][3], 0, 1.0, 1, 0.0, 1.0, }, + {"PSliceSearch8x8", &cfgparams.InterSearch[0][0][4], 0, 1.0, 1, 0.0, 1.0, }, + {"PSliceSearch8x4", &cfgparams.InterSearch[0][0][5], 0, 1.0, 1, 0.0, 1.0, }, + {"PSliceSearch4x8", &cfgparams.InterSearch[0][0][6], 0, 1.0, 1, 0.0, 1.0, }, + {"PSliceSearch4x4", &cfgparams.InterSearch[0][0][7], 0, 1.0, 1, 0.0, 1.0, }, + // B slice partition modes. + {"BSliceDirect", &cfgparams.InterSearch[0][1][0], 0, 1.0, 1, 0.0, 1.0, }, + {"BSliceSearch16x16", &cfgparams.InterSearch[0][1][1], 0, 1.0, 1, 0.0, 1.0, }, + {"BSliceSearch16x8", &cfgparams.InterSearch[0][1][2], 0, 1.0, 1, 0.0, 1.0, }, + {"BSliceSearch8x16", &cfgparams.InterSearch[0][1][3], 0, 1.0, 1, 0.0, 1.0, }, + {"BSliceSearch8x8", &cfgparams.InterSearch[0][1][4], 0, 1.0, 1, 0.0, 1.0, }, + {"BSliceSearch8x4", &cfgparams.InterSearch[0][1][5], 0, 1.0, 1, 0.0, 1.0, }, + {"BSliceSearch4x8", &cfgparams.InterSearch[0][1][6], 0, 1.0, 1, 0.0, 1.0, }, + {"BSliceSearch4x4", &cfgparams.InterSearch[0][1][7], 0, 1.0, 1, 0.0, 1.0, }, + //Bipredicting Motion Estimation parameters + {"BiPredMotionEstimation", &cfgparams.BiPredMotionEstimation, 0, 0.0, 1, 0.0, 1.0, }, + {"BiPredSearch16x16", &cfgparams.BiPredSearch[0], 0, 1.0, 1, 0.0, 1.0, }, + {"BiPredSearch16x8", &cfgparams.BiPredSearch[1], 0, 0.0, 1, 0.0, 1.0, }, + {"BiPredSearch8x16", &cfgparams.BiPredSearch[2], 0, 0.0, 1, 0.0, 1.0, }, + {"BiPredSearch8x8", &cfgparams.BiPredSearch[3], 0, 0.0, 1, 0.0, 1.0, }, + {"BiPredMERefinements", &cfgparams.BiPredMERefinements, 0, 0.0, 1, 0.0, 5.0, }, + {"BiPredMESearchRange", &cfgparams.BiPredMESearchRange[0], 0, 8.0, 2, 0.0, 0.0, }, + {"BiPredMESubPel", &cfgparams.BiPredMESubPel, 0, 1.0, 1, 0.0, 2.0, }, + {"DisableIntraInInter", &cfgparams.DisableIntraInInter[0], 0, 0.0, 1, 0.0, 1.0, }, + {"IntraDisableInterOnly", &cfgparams.IntraDisableInterOnly, 0, 0.0, 1, 0.0, 1.0, }, + {"DisableIntra4x4", &cfgparams.DisableIntra4x4, 0, 0.0, 1, 0.0, 1.0, }, + {"DisableIntra16x16", &cfgparams.DisableIntra16x16, 0, 0.0, 1, 0.0, 1.0, }, + {"Intra4x4ParDisable", &cfgparams.Intra4x4ParDisable, 0, 0.0, 1, 0.0, 1.0, }, + {"Intra4x4DiagDisable", &cfgparams.Intra4x4DiagDisable, 0, 0.0, 1, 0.0, 1.0, }, + {"Intra4x4DirDisable", &cfgparams.Intra4x4DirDisable, 0, 0.0, 1, 0.0, 1.0, }, + {"Intra16x16ParDisable", &cfgparams.Intra16x16ParDisable, 0, 0.0, 1, 0.0, 1.0, }, + {"Intra16x16PlaneDisable", &cfgparams.Intra16x16PlaneDisable, 0, 0.0, 1, 0.0, 1.0, }, + {"EnableIPCM", &cfgparams.EnableIPCM, 0, 0.0, 1, 0.0, 2.0, }, + {"ChromaIntraDisable", &cfgparams.ChromaIntraDisable, 0, 0.0, 1, 0.0, 1.0, }, + {"RDOptimization", &cfgparams.rdopt, 0, 0.0, 1, 0.0, 3.0, }, + + {"DistortionEstimation", &cfgparams.de, 0, 1.0, 2, 0.0, 8.0, }, + {"SubMBCodingState", &cfgparams.subMBCodingState, 0, 2.0, 1, 0.0, 2.0, }, + {"I16RDOpt", &cfgparams.I16rdo, 0, 0.0, 1, 0.0, 1.0, }, + {"MDReference", &cfgparams.MDReference[0], 0, 0.0, 1, 0.0, 1.0, }, + {"EnhLayerMDReference", &cfgparams.MDReference[1], 0, 0.0, 1, 0.0, 1.0, }, + {"DistortionSSIM", &cfgparams.Distortion[SSIM], 0, 0.0, 1, 0.0, 1.0, }, + {"DistortionMS_SSIM", &cfgparams.Distortion[MS_SSIM], 0, 0.0, 1, 0.0, 1.0, }, + {"SSIMOverlapSize", &cfgparams.SSIMOverlapSize, 0, 1.0, 2, 1.0, 1.0, }, + {"DistortionYUVtoRGB", &cfgparams.DistortionYUVtoRGB, 0, 0.0, 1, 0.0, 1.0, }, + {"CtxAdptLagrangeMult", &cfgparams.CtxAdptLagrangeMult, 0, 0.0, 1, 0.0, 1.0, }, + {"FastCrIntraDecision", &cfgparams.FastCrIntraDecision, 0, 0.0, 1, 0.0, 1.0, }, + {"DisableThresholding", &cfgparams.disthres, 0, 0.0, 1, 0.0, 1.0, }, + {"DisableBSkipRDO", &cfgparams.nobskip, 0, 0.0, 1, 0.0, 1.0, }, + {"BiasSkipRDO", &cfgparams.BiasSkipRDO, 0, 0.0, 1, 0.0, 1.0, }, + {"ForceTrueRateRDO", &cfgparams.ForceTrueRateRDO, 0, 0.0, 1, 0.0, 2.0, }, + {"LossRateA", &cfgparams.LossRateA, 2, 0.0, 2, 0.0, 0.0, }, + {"LossRateB", &cfgparams.LossRateB, 2, 0.0, 2, 0.0, 0.0, }, + {"LossRateC", &cfgparams.LossRateC, 2, 0.0, 2, 0.0, 0.0, }, + {"FirstFrameCorrect", &cfgparams.FirstFrameCorrect, 0, 0.0, 2, 0.0, 0.0, }, + {"NumberOfDecoders", &cfgparams.NoOfDecoders, 0, 0.0, 2, 0.0, 0.0, }, + {"ErrorConcealment", &cfgparams.ErrorConcealment, 0, 0.0, 2, 0.0, 0.0, }, + {"RestrictRefFrames", &cfgparams.RestrictRef , 0, 0.0, 1, 0.0, 1.0, }, +#ifdef _LEAKYBUCKET_ + {"NumberofLeakyBuckets", &cfgparams.NumberLeakyBuckets, 0, 2.0, 1, 2.0, 255.0, }, + {"LeakyBucketRateFile", &cfgparams.LeakyBucketRateFile, 1, 0.0, 0, 0.0, 0.0, FILE_NAME_SIZE, }, + {"LeakyBucketParamFile", &cfgparams.LeakyBucketParamFile, 1, 0.0, 0, 0.0, 0.0, FILE_NAME_SIZE, }, +#endif + {"PicInterlace", &cfgparams.PicInterlace, 0, 0.0, 1, 0.0, 3.0, }, + {"MbInterlace", &cfgparams.MbInterlace, 0, 0.0, 1, 0.0, 3.0, }, + + {"IntraBottom", &cfgparams.IntraBottom, 0, 0.0, 1, 0.0, 1.0, }, + + {"NumFramesInELayerSubSeq", &cfgparams.NumFramesInELSubSeq, 0, 0.0, 2, 0.0, 0.0, }, + {"RandomIntraMBRefresh", &cfgparams.RandomIntraMBRefresh, 0, 0.0, 2, 0.0, 0.0, }, + {"WeightedPrediction", &cfgparams.WeightedPrediction, 0, 0.0, 1, 0.0, 1.0, }, + {"WeightedBiprediction", &cfgparams.WeightedBiprediction, 0, 0.0, 1, 0.0, 2.0, }, + {"WPMethod", &cfgparams.WPMethod, 0, 0.0, 1, 0.0, 1.0, }, + {"WPIterMC", &cfgparams.WPIterMC, 0, 0.0, 1, 0.0, 1.0, }, + {"ChromaWeightSupport", &cfgparams.ChromaWeightSupport, 0, 0.0, 1, 0.0, 1.0, }, + {"EnhancedBWeightSupport", &cfgparams.EnhancedBWeightSupport, 0, 0.0, 1, 0.0, 2.0, }, + {"UseWeightedReferenceME", &cfgparams.UseWeightedReferenceME, 0, 0.0, 1, 0.0, 1.0, }, + {"RDPictureDecision", &cfgparams.RDPictureDecision, 0, 0.0, 1, 0.0, 1.0, }, + {"RDPSliceBTest", &cfgparams.RDPSliceBTest, 0, 0.0, 1, 0.0, 1.0, }, + {"RDPSliceITest", &cfgparams.RDPSliceITest, 0, 1.0, 1, 0.0, 1.0, }, + {"RDPictureMaxPassISlice", &cfgparams.RDPictureMaxPassISlice, 0, 1.0, 1, 1.0, 3.0, }, + {"RDPictureMaxPassPSlice", &cfgparams.RDPictureMaxPassPSlice, 0, 2.0, 1, 1.0, 6.0, }, + {"RDPictureMaxPassBSlice", &cfgparams.RDPictureMaxPassBSlice, 0, 3.0, 1, 1.0, 6.0, }, + {"RDPictureDeblocking", &cfgparams.RDPictureDeblocking, 0, 0.0, 1, 0.0, 1.0, }, + {"RDPictureDirectMode", &cfgparams.RDPictureDirectMode, 0, 0.0, 1, 0.0, 1.0, }, + {"RDPictureFrameQPPSlice", &cfgparams.RDPictureFrameQPPSlice, 0, 0.0, 1, 0.0, 1.0, }, + {"RDPictureFrameQPBSlice", &cfgparams.RDPictureFrameQPBSlice, 0, 0.0, 1, 0.0, 1.0, }, + {"SkipIntraInInterSlices", &cfgparams.SkipIntraInInterSlices, 0, 0.0, 1, 0.0, 1.0, }, + {"BReferencePictures", &cfgparams.BRefPictures, 0, 0.0, 1, 0.0, 2.0, }, + {"HierarchicalCoding", &cfgparams.HierarchicalCoding, 0, 0.0, 1, 0.0, 3.0, }, + {"HierarchyLevelQPEnable", &cfgparams.HierarchyLevelQPEnable, 0, 0.0, 1, 0.0, 1.0, }, + {"ExplicitHierarchyFormat", &cfgparams.ExplicitHierarchyFormat, 1, 0.0, 0, 0.0, 0.0, INPUT_TEXT_SIZE,}, + {"ExplicitSeqCoding", &cfgparams.ExplicitSeqCoding, 0, 0.0, 1, 0.0, 3.0, }, + {"ExplicitSeqFile", &cfgparams.ExplicitSeqFile, 1, 0.0, 0, 0.0, 0.0, FILE_NAME_SIZE, }, + {"LowDelay", &cfgparams.LowDelay, 0, 0.0, 1, 0.0, 1.0, }, + {"ReferenceReorder", &cfgparams.ReferenceReorder, 0, 0.0, 1, 0.0, 2.0, }, + {"PocMemoryManagement", &cfgparams.PocMemoryManagement, 0, 0.0, 1, 0.0, 2.0, }, + + + {"DFParametersFlag", &cfgparams.DFSendParameters, 0, 0.0, 1, 0.0, 1.0, }, + {"DFDisableRefISlice", &cfgparams.DFDisableIdc[1][I_SLICE], 0, 0.0, 1, 0.0, 2.0, }, + {"DFDisableNRefISlice", &cfgparams.DFDisableIdc[0][I_SLICE], 0, 0.0, 1, 0.0, 2.0, }, + {"DFDisableRefPSlice", &cfgparams.DFDisableIdc[1][P_SLICE], 0, 0.0, 1, 0.0, 2.0, }, + {"DFDisableNRefPSlice", &cfgparams.DFDisableIdc[0][P_SLICE], 0, 0.0, 1, 0.0, 2.0, }, + {"DFDisableRefBSlice", &cfgparams.DFDisableIdc[1][B_SLICE], 0, 0.0, 1, 0.0, 2.0, }, + {"DFDisableNRefBSlice", &cfgparams.DFDisableIdc[0][B_SLICE], 0, 0.0, 1, 0.0, 2.0, }, + {"DFDisableRefSPSlice", &cfgparams.DFDisableIdc[1][SP_SLICE], 0, 0.0, 1, 0.0, 2.0, }, + {"DFDisableNRefSPSlice", &cfgparams.DFDisableIdc[0][SP_SLICE], 0, 0.0, 1, 0.0, 2.0, }, + {"DFDisableRefSISlice", &cfgparams.DFDisableIdc[1][SI_SLICE], 0, 0.0, 1, 0.0, 2.0, }, + {"DFDisableNRefSISlice", &cfgparams.DFDisableIdc[0][SI_SLICE], 0, 0.0, 1, 0.0, 2.0, }, + {"DFAlphaRefISlice", &cfgparams.DFAlpha[1][I_SLICE], 0, 0.0, 1, -6.0, 6.0, }, + {"DFAlphaNRefISlice", &cfgparams.DFAlpha[0][I_SLICE], 0, 0.0, 1, -6.0, 6.0, }, + {"DFAlphaRefPSlice", &cfgparams.DFAlpha[1][P_SLICE], 0, 0.0, 1, -6.0, 6.0, }, + {"DFAlphaNRefPSlice", &cfgparams.DFAlpha[0][P_SLICE], 0, 0.0, 1, -6.0, 6.0, }, + {"DFAlphaRefBSlice", &cfgparams.DFAlpha[1][B_SLICE], 0, 0.0, 1, -6.0, 6.0, }, + {"DFAlphaNRefBSlice", &cfgparams.DFAlpha[0][B_SLICE], 0, 0.0, 1, -6.0, 6.0, }, + {"DFAlphaRefSPSlice", &cfgparams.DFAlpha[1][SP_SLICE], 0, 0.0, 1, -6.0, 6.0, }, + {"DFAlphaNRefSPSlice", &cfgparams.DFAlpha[0][SP_SLICE], 0, 0.0, 1, -6.0, 6.0, }, + {"DFAlphaRefSISlice", &cfgparams.DFAlpha[1][SI_SLICE], 0, 0.0, 1, -6.0, 6.0, }, + {"DFAlphaNRefSISlice", &cfgparams.DFAlpha[0][SI_SLICE], 0, 0.0, 1, -6.0, 6.0, }, + {"DFBetaRefISlice", &cfgparams.DFBeta[1][I_SLICE], 0, 0.0, 1, -6.0, 6.0, }, + {"DFBetaNRefISlice", &cfgparams.DFBeta[0][I_SLICE], 0, 0.0, 1, -6.0, 6.0, }, + {"DFBetaRefPSlice", &cfgparams.DFBeta[1][P_SLICE], 0, 0.0, 1, -6.0, 6.0, }, + {"DFBetaNRefPSlice", &cfgparams.DFBeta[0][P_SLICE], 0, 0.0, 1, -6.0, 6.0, }, + {"DFBetaRefBSlice", &cfgparams.DFBeta[1][B_SLICE], 0, 0.0, 1, -6.0, 6.0, }, + {"DFBetaNRefBSlice", &cfgparams.DFBeta[0][B_SLICE], 0, 0.0, 1, -6.0, 6.0, }, + {"DFBetaRefSPSlice", &cfgparams.DFBeta[1][SP_SLICE], 0, 0.0, 1, -6.0, 6.0, }, + {"DFBetaNRefSPSlice", &cfgparams.DFBeta[0][SP_SLICE], 0, 0.0, 1, -6.0, 6.0, }, + {"DFBetaRefSISlice", &cfgparams.DFBeta[1][SI_SLICE], 0, 0.0, 1, -6.0, 6.0, }, + {"DFBetaNRefSISlice", &cfgparams.DFBeta[0][SI_SLICE], 0, 0.0, 1, -6.0, 6.0, }, + {"SparePictureOption", &cfgparams.SparePictureOption, 0, 0.0, 1, 0.0, 1.0, }, + {"SparePictureDetectionThr", &cfgparams.SPDetectionThreshold, 0, 0.0, 2, 0.0, 0.0, }, + {"SparePicturePercentageThr",&cfgparams.SPPercentageThreshold, 0, 0.0, 2, 0.0, 100.0, }, + + {"num_slice_groups_minus1", &cfgparams.num_slice_groups_minus1, 0, 0.0, 1, 0.0, (double)MAXSLICEGROUPIDS - 1 }, + {"slice_group_map_type", &cfgparams.slice_group_map_type, 0, 0.0, 1, 0.0, 6.0, }, + {"slice_group_change_direction_flag", &cfgparams.slice_group_change_direction_flag, 0, 0.0, 1, 0.0, 2.0, }, + {"slice_group_change_rate_minus1", &cfgparams.slice_group_change_rate_minus1, 0, 0.0, 2, 0.0, 1.0, }, + {"SliceGroupConfigFileName", &cfgparams.SliceGroupConfigFileName, 1, 0.0, 0, 0.0, 0.0, FILE_NAME_SIZE, }, + + {"UseRedundantPicture", &cfgparams.redundant_pic_flag, 0, 0.0, 1, 0.0, 1.0, }, + {"NumRedundantHierarchy", &cfgparams.NumRedundantHierarchy, 0, 0.0, 1, 0.0, 4.0, }, + {"PrimaryGOPLength", &cfgparams.PrimaryGOPLength, 0, 1.0, 1, 1.0, 16.0, }, + {"NumRefPrimary", &cfgparams.NumRefPrimary, 0, 1.0, 1, 1.0, 16.0, }, + + {"PicOrderCntType", &cfgparams.pic_order_cnt_type, 0, 0.0, 1, 0.0, 2.0, }, + + {"ContextInitMethod", &cfgparams.context_init_method, 0, 0.0, 1, 0.0, 1.0, }, + {"FixedModelNumber", &cfgparams.model_number, 0, 0.0, 1, 0.0, 2.0, }, + + {"ReportFrameStats", &cfgparams.ReportFrameStats, 0, 0.0, 1, 0.0, 1.0, }, + {"DisplayEncParams", &cfgparams.DisplayEncParams, 0, 0.0, 1, 0.0, 1.0, }, + {"Verbose", &cfgparams.Verbose, 0, 1.0, 1, 0.0, 4.0, }, + {"SkipGlobalStats", &cfgparams.skip_gl_stats, 0, 0.0, 1, 0.0, 1.0, }, + {"OnTheFlyFractMCP", &cfgparams.OnTheFlyFractMCP, 0, 0.0, 1, 0.0, 3.0, }, + {"ChromaMCBuffer", &cfgparams.ChromaMCBuffer, 0, 0.0, 1, 0.0, 1.0, }, + {"ChromaMEEnable", &cfgparams.ChromaMEEnable, 0, 0.0, 1, 0.0, 2.0, }, + {"ChromaMEWeight", &cfgparams.ChromaMEWeight, 0, 1.0, 2, 1.0, 0.0, }, + {"MESoftenSSEMetric", &cfgparams.MESoftenSSEMetric, 0, 0.0, 1, 0.0, 1.0, }, + {"MEDistortionFPel", &cfgparams.MEErrorMetric[F_PEL], 0, 0.0, 1, 0.0, 3.0, }, + {"MEDistortionHPel", &cfgparams.MEErrorMetric[H_PEL], 0, 0.0, 1, 0.0, 3.0, }, + {"MEDistortionQPel", &cfgparams.MEErrorMetric[Q_PEL], 0, 2.0, 1, 0.0, 3.0, }, + {"MDDistortion", &cfgparams.ModeDecisionMetric, 0, 2.0, 1, 0.0, 2.0, }, + {"SkipDeBlockNonRef", &cfgparams.SkipDeBlockNonRef, 0, 0.0, 1, 0.0, 1.0, }, + + // Rate Control + {"RateControlEnable", &cfgparams.RCEnable, 0, 0.0, 1, 0.0, 1.0, }, + {"Bitrate", &cfgparams.bit_rate, 0, 0.0, 2, 0.0, 0.0, }, + {"InitialQP", &cfgparams.SeinitialQP, 0, 0.0, 3, (double) MIN_QP, (double) MAX_QP, }, + {"BasicUnit", &cfgparams.basicunit, 0, 0.0, 2, 0.0, 0.0, }, + {"ChannelType", &cfgparams.channel_type, 0, 0.0, 1, 0.0, 1.0, }, + {"RCUpdateMode", &cfgparams.RCUpdateMode, 0, 0.0, 1, 0.0, 4.0, }, + {"RCISliceBitRatio", &cfgparams.RCISliceBitRatio, 2, 1.0, 1, 0.0, 20.0, }, + {"RCBSliceBitRatio0", &cfgparams.RCBSliceBitRatio[0], 2, 0.5, 1, 0.0, 20.0, }, + {"RCBSliceBitRatio1", &cfgparams.RCBSliceBitRatio[1], 2, 0.25, 1, 0.0, 20.0, }, + {"RCBSliceBitRatio2", &cfgparams.RCBSliceBitRatio[2], 2, 0.25, 1, 0.0, 20.0, }, + {"RCBSliceBitRatio3", &cfgparams.RCBSliceBitRatio[3], 2, 0.25, 1, 0.0, 20.0, }, + {"RCBSliceBitRatio4", &cfgparams.RCBSliceBitRatio[4], 2, 0.25, 1, 0.0, 20.0, }, + {"RCBoverPRatio", &cfgparams.RCBoverPRatio, 2, 0.45, 1, 0.0, 1000.0, }, + {"RCIoverPRatio", &cfgparams.RCIoverPRatio, 2, 3.80, 1, 0.0, 1000.0, }, + {"RCMinQPPSlice", &cfgparams.RCMinQP[P_SLICE], 0, (double) MIN_QP, 3, (double) MIN_QP, (double) MAX_QP, }, + {"RCMaxQPPSlice", &cfgparams.RCMaxQP[P_SLICE], 0, (double) MAX_QP, 3, (double) MIN_QP, (double) MAX_QP, }, + {"RCMinQPBSlice", &cfgparams.RCMinQP[B_SLICE], 0, (double) MIN_QP, 3, (double) MIN_QP, (double) MAX_QP, }, + {"RCMaxQPBSlice", &cfgparams.RCMaxQP[B_SLICE], 0, (double) MAX_QP, 3, (double) MIN_QP, (double) MAX_QP, }, + {"RCMinQPISlice", &cfgparams.RCMinQP[I_SLICE], 0, (double) MIN_QP, 3, (double) MIN_QP, (double) MAX_QP, }, + {"RCMaxQPISlice", &cfgparams.RCMaxQP[I_SLICE], 0, (double) MAX_QP, 3, (double) MIN_QP, (double) MAX_QP, }, + {"RCMinQPSPSlice", &cfgparams.RCMinQP[SP_SLICE], 0, (double) MIN_QP, 3, (double) MIN_QP, (double) MAX_QP, }, + {"RCMaxQPSPSlice", &cfgparams.RCMaxQP[SP_SLICE], 0, (double) MAX_QP, 3, (double) MIN_QP, (double) MAX_QP, }, + {"RCMinQPSISlice", &cfgparams.RCMinQP[SI_SLICE], 0, (double) MIN_QP, 3, (double) MIN_QP, (double) MAX_QP, }, + {"RCMaxQPSISlice", &cfgparams.RCMaxQP[SI_SLICE], 0, (double) MAX_QP, 3, (double) MIN_QP, (double) MAX_QP, }, + {"RCMaxQPChange", &cfgparams.RCMaxQPChange, 0, 4.0, 1, 0.0, 51.0, }, + + // Q_Matrix + {"QmatrixFile", &cfgparams.QmatrixFile, 1, 0.0, 0, 0.0, 0.0, FILE_NAME_SIZE, }, + {"ScalingMatrixPresentFlag", &cfgparams.ScalingMatrixPresentFlag, 0, 0.0, 1, 0.0, 3.0, }, + {"ScalingListPresentFlag0", &cfgparams.ScalingListPresentFlag[0], 0, 0.0, 1, 0.0, 3.0, }, + {"ScalingListPresentFlag1", &cfgparams.ScalingListPresentFlag[1], 0, 0.0, 1, 0.0, 3.0, }, + {"ScalingListPresentFlag2", &cfgparams.ScalingListPresentFlag[2], 0, 0.0, 1, 0.0, 3.0, }, + {"ScalingListPresentFlag3", &cfgparams.ScalingListPresentFlag[3], 0, 0.0, 1, 0.0, 3.0, }, + {"ScalingListPresentFlag4", &cfgparams.ScalingListPresentFlag[4], 0, 0.0, 1, 0.0, 3.0, }, + {"ScalingListPresentFlag5", &cfgparams.ScalingListPresentFlag[5], 0, 0.0, 1, 0.0, 3.0, }, + {"ScalingListPresentFlag6", &cfgparams.ScalingListPresentFlag[6], 0, 0.0, 1, 0.0, 3.0, }, + {"ScalingListPresentFlag7", &cfgparams.ScalingListPresentFlag[7], 0, 0.0, 1, 0.0, 3.0, }, + {"ScalingListPresentFlag8", &cfgparams.ScalingListPresentFlag[8], 0, 0.0, 1, 0.0, 3.0, }, + {"ScalingListPresentFlag9", &cfgparams.ScalingListPresentFlag[9], 0, 0.0, 1, 0.0, 3.0, }, + {"ScalingListPresentFlag10", &cfgparams.ScalingListPresentFlag[10], 0, 0.0, 1, 0.0, 3.0, }, + {"ScalingListPresentFlag11", &cfgparams.ScalingListPresentFlag[11], 0, 0.0, 1, 0.0, 3.0, }, + + // Chroma QP Offset + {"ChromaQPOffset", &cfgparams.chroma_qp_index_offset, 0, 0.0, 1,-51.0, 51.0, }, + + // Fidelity Range Extensions +// New Input bit-depth entries + {"SourceBitDepthLuma", &cfgparams.source.bit_depth[0], 0, 8.0, 1, 8.0, 14.0, }, + {"SourceBitDepthChroma", &cfgparams.source.bit_depth[1], 0, 8.0, 1, 8.0, 14.0, }, + {"SourceBitDepthRescale", &cfgparams.src_BitDepthRescale, 0, 0.0, 1, 0.0, 1.0, }, +// Old variables replaced from BitDepthLuma and BitDepthChroma. Considered only if SourceBitDepthRescale is 1. + {"OutputBitDepthLuma", &cfgparams.output.bit_depth[0], 0, 8.0, 1, 8.0, 14.0, }, + {"OutputBitDepthChroma", &cfgparams.output.bit_depth[1], 0, 8.0, 1, 8.0, 14.0, }, + + {"YUVFormat", &cfgparams.yuv_format, 0, 1.0, 1, 0.0, 3.0, }, + {"RGBInput", &cfgparams.source.color_model, 0, 0.0, 1, 0.0, 1.0, }, + {"PixelFormat", &cfgparams.source.pixel_format, 0, 0.0, 1, 0.0, 4.0, }, + {"Interleaved", &cfgparams.input_file1.is_interleaved , 0, 0.0, 1, 0.0, 1.0, }, + {"StandardRange", &cfgparams.stdRange, 0, 0.0, 1, 0.0, 1.0, }, + {"VideoCode", &cfgparams.videoCode, 0, 1.0, 1, 0.0, 8.0, }, + {"CbQPOffset", &cfgparams.cb_qp_index_offset, 0, 0.0, 1,-51.0, 51.0, }, + {"CrQPOffset", &cfgparams.cr_qp_index_offset, 0, 0.0, 1,-51.0, 51.0, }, + {"Transform8x8Mode", &cfgparams.Transform8x8Mode, 0, 0.0, 1, 0.0, 2.0, }, + // Lossless Coding + {"LosslessCoding", &cfgparams.LosslessCoding, 0, 0.0, 1, 0.0, 1.0, }, + + // Explicit Lambda Parameters for RDO + {"UseExplicitLambdaParams", &cfgparams.UseExplicitLambdaParams, 0, 0.0, 1, 0.0, 3.0, }, + {"UpdateLambdaChromaME", &cfgparams.UpdateLambdaChromaME, 0, 0.0, 1, 0.0, 3.0, }, + {"FixedLambdaPSlice", &cfgparams.FixedLambda[P_SLICE], 2, 0.1, 2, 0.0, 0.0, }, + {"FixedLambdaBSlice", &cfgparams.FixedLambda[B_SLICE], 2, 0.1, 2, 0.0, 0.0, }, + {"FixedLambdaISlice", &cfgparams.FixedLambda[I_SLICE], 2, 0.1, 2, 0.0, 0.0, }, + {"FixedLambdaSPSlice", &cfgparams.FixedLambda[SP_SLICE], 2, 0.1, 2, 0.0, 0.0, }, + {"FixedLambdaSISlice", &cfgparams.FixedLambda[SI_SLICE], 2, 0.1, 2, 0.0, 0.0, }, + {"FixedLambdaRefBSlice", &cfgparams.FixedLambda[5], 2, 0.1, 2, 0.0, 0.0, }, + {"LambdaWeightPSlice", &cfgparams.LambdaWeight[P_SLICE], 2, 0.68, 2, 0.0, 0.0, }, + {"LambdaWeightBSlice", &cfgparams.LambdaWeight[B_SLICE], 2, 2.00, 2, 0.0, 0.0, }, + {"LambdaWeightISlice", &cfgparams.LambdaWeight[I_SLICE], 2, 0.65, 2, 0.0, 0.0, }, + {"LambdaWeightSPSlice", &cfgparams.LambdaWeight[SP_SLICE], 2, 1.50, 2, 0.0, 0.0, }, + {"LambdaWeightSISlice", &cfgparams.LambdaWeight[SI_SLICE], 2, 0.65, 2, 0.0, 0.0, }, + {"LambdaWeightRefBSlice", &cfgparams.LambdaWeight[5], 2, 1.50, 2, 0.0, 0.0, }, + // Usage of explicit "initial" offsets for quantization + {"QOffsetMatrixFile", &cfgparams.QOffsetMatrixFile, 1, 0.0, 0, 0.0, 0.0, FILE_NAME_SIZE, }, + {"OffsetMatrixPresentFlag", &cfgparams.OffsetMatrixPresentFlag, 0, 0.0, 1, 0.0, 1.0, }, + + // Adaptive rounding technique based on JVT-N011 + {"AdaptiveRounding", &cfgparams.AdaptiveRounding, 0, 0.0, 1, 0.0, 1.0, }, + {"AdaptRoundingFixed", &cfgparams.AdaptRoundingFixed, 0, 1.0, 1, 0.0, 1.0, }, + {"AdaptRndPeriod", &cfgparams.AdaptRndPeriod, 0, 16.0, 2, 1.0, 0.0, }, + {"AdaptRndChroma", &cfgparams.AdaptRndChroma, 0, 0.0, 1, 0.0, 1.0, }, + {"AdaptRndWFactorIRef", &cfgparams.AdaptRndWFactor[1][I_SLICE], 0, 4.0, 1, 0.0, 4096.0, }, + {"AdaptRndWFactorPRef", &cfgparams.AdaptRndWFactor[1][P_SLICE], 0, 4.0, 1, 0.0, 4096.0, }, + {"AdaptRndWFactorBRef", &cfgparams.AdaptRndWFactor[1][B_SLICE], 0, 4.0, 1, 0.0, 4096.0, }, + {"AdaptRndWFactorINRef", &cfgparams.AdaptRndWFactor[0][I_SLICE], 0, 4.0, 1, 0.0, 4096.0, }, + {"AdaptRndWFactorPNRef", &cfgparams.AdaptRndWFactor[0][P_SLICE], 0, 4.0, 1, 0.0, 4096.0, }, + {"AdaptRndWFactorBNRef", &cfgparams.AdaptRndWFactor[0][B_SLICE], 0, 4.0, 1, 0.0, 4096.0, }, + + {"AdaptRndCrWFactorIRef", &cfgparams.AdaptRndCrWFactor[1][I_SLICE],0, 4.0, 1, 0.0, 4096.0, }, + {"AdaptRndCrWFactorPRef", &cfgparams.AdaptRndCrWFactor[1][P_SLICE],0, 4.0, 1, 0.0, 4096.0, }, + {"AdaptRndCrWFactorBRef", &cfgparams.AdaptRndCrWFactor[1][B_SLICE],0, 4.0, 1, 0.0, 4096.0, }, + {"AdaptRndCrWFactorINRef", &cfgparams.AdaptRndCrWFactor[0][I_SLICE],0, 4.0, 1, 0.0, 4096.0, }, + {"AdaptRndCrWFactorPNRef", &cfgparams.AdaptRndCrWFactor[0][P_SLICE],0, 4.0, 1, 0.0, 4096.0, }, + {"AdaptRndCrWFactorBNRef", &cfgparams.AdaptRndCrWFactor[0][B_SLICE],0, 4.0, 1, 0.0, 4096.0, }, + + // Prediction Structure + {"PreferDispOrder", &cfgparams.PreferDispOrder, 0, 1.0, 1, 0.0, 1.0, }, + {"PreferPowerOfTwo", &cfgparams.PreferPowerOfTwo, 0, 0.0, 1, 0.0, 1.0, }, + {"FrmStructBufferLength", &cfgparams.FrmStructBufferLength, 0, 16.0, 1, 1.0, 128.0, }, + + // Fast Mode Decision + {"EarlySkipEnable", &cfgparams.EarlySkipEnable, 0, 0.0, 1, 0.0, 1.0, }, + {"SelectiveIntraEnable", &cfgparams.SelectiveIntraEnable, 0, 0.0, 1, 0.0, 1.0, }, + + //================================ + // Motion Estimation (ME) Parameters + //================================ + {"RestrictSearchRange", &cfgparams.full_search, 0, 2.0, 1, 0.0, 2.0, }, + // ME Limits + {"UseMVLimits", &cfgparams.UseMVLimits, 0, 0.0, 1, 0.0, 1.0, }, + {"SetMVXLimit", &cfgparams.SetMVXLimit, 0, 0.0, 1, 0.0, 2048.0, }, + {"SetMVYLimit", &cfgparams.SetMVYLimit, 0, 0.0, 1, 0.0, 512.0, }, + // Fast ME enable + {"SearchMode", &cfgparams.SearchMode[0], 0, 0.0, 1, -1.0, 3.0, }, + // Parameters for UMHEX control + {"UMHexDSR", &cfgparams.UMHexDSR, 0, 1.0, 1, 0.0, 1.0, }, + {"UMHexScale", &cfgparams.UMHexScale, 0, 1.0, 0, 0.0, 0.0, }, + // Parameters for EPZS control + {"EPZSPattern", &cfgparams.EPZSPattern, 0, 2.0, 1, 0.0, 5.0, }, + {"EPZSDualRefinement", &cfgparams.EPZSDual, 0, 3.0, 1, 0.0, 6.0, }, + {"EPZSFixedPredictors", &cfgparams.EPZSFixed, 0, 3.0, 1, 0.0, 3.0, }, +#if (MVC_EXTENSION_ENABLE) + {"EPZSTemporal", &cfgparams.EPZSTemporal[0], 0, 1.0, 1, 0.0, 1.0, }, +#else + {"EPZSTemporal", &cfgparams.EPZSTemporal, 0, 1.0, 1, 0.0, 1.0, }, +#endif + {"EPZSSpatialMem", &cfgparams.EPZSSpatialMem, 0, 1.0, 1, 0.0, 1.0, }, + {"EPZSBlockType", &cfgparams.EPZSBlockType, 0, 1.0, 1, 0.0, 1.0, }, +#if (MVC_EXTENSION_ENABLE) + {"EPZSMinThresScale", &cfgparams.EPZSMinThresScale[0], 0, 0.0, 0, 0.0, 0.0, }, + {"EPZSMaxThresScale", &cfgparams.EPZSMaxThresScale[0], 0, 2.0, 0, 0.0, 0.0, }, + {"EPZSMedThresScale", &cfgparams.EPZSMedThresScale[0], 0, 1.0, 0, 0.0, 0.0, }, + {"EPZSSubPelThresScale", &cfgparams.EPZSSubPelThresScale[0], 0, 1.0, 0, 0.0, 0.0, }, +#else + {"EPZSMinThresScale", &cfgparams.EPZSMinThresScale, 0, 0.0, 0, 0.0, 0.0, }, + {"EPZSMaxThresScale", &cfgparams.EPZSMaxThresScale, 0, 2.0, 0, 0.0, 0.0, }, + {"EPZSMedThresScale", &cfgparams.EPZSMedThresScale, 0, 1.0, 0, 0.0, 0.0, }, + {"EPZSSubPelThresScale", &cfgparams.EPZSSubPelThresScale, 0, 2.0, 0, 0.0, 0.0, }, +#endif + {"EPZSSubPelME", &cfgparams.EPZSSubPelME, 0, 1.0, 1, 0.0, 2.0, }, + {"EPZSSubPelMEBiPred", &cfgparams.EPZSSubPelMEBiPred, 0, 1.0, 1, 0.0, 2.0, }, + {"EPZSSubPelGrid", &cfgparams.EPZSSubPelGrid, 0, 0.0, 1, 0.0, 1.0, }, + {"EPZSSubPelThresScale", &cfgparams.EPZSSubPelThresScale, 0, 1.0, 0, 0.0, 0.0, }, + {"DisableMEPrediction", &cfgparams.DisableMEPrediction, 0, 0.0, 1, 0.0, 1.0, }, + // Tone mapping SEI cfg file + {"ToneMappingSEIPresentFlag",&cfgparams.ToneMappingSEIPresentFlag, 0, 0.0, 1, 0.0, 1.0, }, + {"ToneMappingFile", &cfgparams.ToneMappingFile, 1, 0.0, 0, 0.0, 0.0, FILE_NAME_SIZE, }, + + {"SeparateColourPlane", &cfgparams.separate_colour_plane_flag, 0, 0.0, 1, 0.0, 1.0, }, + {"WeightY", &cfgparams.WeightY, 2, 1.00, 1, 0.0, 4.0, }, + {"WeightCb", &cfgparams.WeightCb, 2, 1.00, 1, 0.0, 4.0, }, + {"WeightCr", &cfgparams.WeightCr, 2, 1.00, 1, 0.0, 4.0, }, + {"WPMCPrecision", &cfgparams.WPMCPrecision, 0, 0.0, 1, 0.0, 2.0, }, + {"WPMCPrecFullRef", &cfgparams.WPMCPrecFullRef, 0, 0.0, 1, 0.0, 1.0, }, + {"WPMCPrecBSlice", &cfgparams.WPMCPrecBSlice, 0, 1.0, 1, 0.0, 2.0, }, + {"RCCpbSize", &cfgparams.rc_cpb_size, 0,1024.0, 2, 0.0, 0.0, }, + {"MinIDRDistance", &cfgparams.MinIDRDistance, 0, 10.0, 1, 0.0, 128.0, }, + // Trellis based quantization + {"UseRDOQuant", &cfgparams.UseRDOQuant, 0, 0.0, 1, 0.0, 1.0, }, + {"RDOQ_DC", &cfgparams.RDOQ_DC, 0, 1.0, 1, 0.0, 1.0, }, + {"RDOQ_CR", &cfgparams.RDOQ_CR, 0, 1.0, 1, 0.0, 1.0, }, + {"RDOQ_DC_CR", &cfgparams.RDOQ_DC_CR, 0, 1.0, 1, 0.0, 1.0, }, + {"RDOQ_QP_Num", &cfgparams.RDOQ_QP_Num, 0, 1.0, 1, 1.0, 9.0, }, + {"RDOQ_CP_Mode", &cfgparams.RDOQ_CP_Mode, 0, 0.0, 1, 0.0, 1.0, }, + {"RDOQ_CP_MV", &cfgparams.RDOQ_CP_MV, 0, 0.0, 1, 0.0, 1.0, }, + {"RDOQ_Fast", &cfgparams.RDOQ_Fast, 0, 0.0, 1, 0.0, 1.0, }, + // VUI parameters + {"GenerateSEIMessage", &cfgparams.GenerateSEIMessage, 0, 0.0, 1, 0.0, 1.0, }, + {"EnableVUISupport", &cfgparams.EnableVUISupport, 0, 0.0, 1, 0.0, 1.0, }, + {"VUI_aspect_ratio_info_present_flag", &cfgparams.VUI.aspect_ratio_info_present_flag, 0, 0.0, 1, 0.0, 1.0, }, + {"VUI_aspect_ratio_idc", &cfgparams.VUI.aspect_ratio_idc, 0, 0.0, 1, 0.0, 255.0, }, + {"VUI_sar_width", &cfgparams.VUI.sar_width, 0, 0.0, 2, 0.0, 0.0, }, + {"VUI_sar_height", &cfgparams.VUI.sar_height, 0, 0.0, 2, 0.0, 0.0, }, + {"VUI_overscan_info_present_flag", &cfgparams.VUI.overscan_info_present_flag, 0, 0.0, 1, 0.0, 1.0, }, + {"VUI_overscan_appropriate_flag", &cfgparams.VUI.overscan_appropriate_flag, 0, 0.0, 1, 0.0, 1.0, }, + {"VUI_video_signal_type_present_flag", &cfgparams.VUI.video_signal_type_present_flag, 0, 0.0, 1, 0.0, 1.0, }, + {"VUI_video_format", &cfgparams.VUI.video_format, 0, 5.0, 1, 0.0, 7.0, }, + {"VUI_video_full_range_flag", &cfgparams.VUI.video_full_range_flag, 0, 0.0, 1, 0.0, 1.0, }, + {"VUI_colour_description_present_flag", &cfgparams.VUI.colour_description_present_flag, 0, 0.0, 1, 0.0, 1.0, }, + {"VUI_colour_primaries", &cfgparams.VUI.colour_primaries, 0, 2.0, 1, 0.0, 255.0, }, + {"VUI_transfer_characteristics", &cfgparams.VUI.transfer_characteristics, 0, 2.0, 1, 0.0, 255.0, }, + {"VUI_matrix_coefficients", &cfgparams.VUI.matrix_coefficients, 0, 2.0, 1, 0.0, 255.0, }, + {"VUI_chroma_location_info_present_flag", &cfgparams.VUI.chroma_location_info_present_flag, 0, 0.0, 1, 0.0, 1.0, }, + {"VUI_chroma_sample_loc_type_top_field", &cfgparams.VUI.chroma_sample_loc_type_top_field, 0, 0.0, 1, 0.0, 5.0, }, + {"VUI_chroma_sample_loc_type_bottom_field",&cfgparams.VUI.chroma_sample_loc_type_bottom_field,0, 0.0, 1, 0.0, 5.0, }, + {"VUI_timing_info_present_flag", &cfgparams.VUI.timing_info_present_flag, 0, 0.0, 1, 0.0, 1.0, }, + {"VUI_num_units_in_tick", &cfgparams.VUI.num_units_in_tick, 0,1000.0, 2, 0.0, 0.0, }, + {"VUI_time_scale", &cfgparams.VUI.time_scale, 0,60000.0, 2, 0.0, 0.0, }, + {"VUI_fixed_frame_rate_flag", &cfgparams.VUI.fixed_frame_rate_flag, 0, 0.0, 1, 0.0, 1.0, }, + {"VUI_nal_hrd_parameters_present_flag", &cfgparams.VUI.nal_hrd_parameters_present_flag, 0, 0.0, 1, 0.0, 1.0, }, + {"VUI_nal_cpb_cnt_minus1", &cfgparams.VUI.nal_cpb_cnt_minus1, 0, 0.0, 1, 0.0, 31.0, }, + {"VUI_nal_bit_rate_scale", &cfgparams.VUI.nal_bit_rate_scale, 0, 0.0, 2, 0.0, 0.0, }, + {"VUI_nal_cpb_size_scale", &cfgparams.VUI.nal_cpb_size_scale, 0, 0.0, 2, 0.0, 0.0, }, + {"VUI_nal_bit_rate_value_minus1", &cfgparams.VUI.nal_bit_rate_value_minus1, 0, 0.0, 2, 0.0, 0.0, }, + {"VUI_nal_cpb_size_value_minus1", &cfgparams.VUI.nal_cpb_size_value_minus1, 0, 0.0, 2, 0.0, 0.0, }, + {"VUI_nal_vbr_cbr_flag", &cfgparams.VUI.nal_vbr_cbr_flag, 0, 1.0, 1, 0.0, 1.0, }, + {"VUI_nal_initial_cpb_removal_delay_length_minus1", &cfgparams.VUI.nal_initial_cpb_removal_delay_length_minus1, 0, 23.0, 2, 0.0, 0.0, }, + {"VUI_nal_cpb_removal_delay_length_minus1",&cfgparams.VUI.nal_cpb_removal_delay_length_minus1,0, 23.0, 2, 0.0, 0.0, }, + {"VUI_nal_dpb_output_delay_length_minus1", &cfgparams.VUI.nal_dpb_output_delay_length_minus1, 0, 23.0, 2, 0.0, 0.0, }, + {"VUI_nal_time_offset_length", &cfgparams.VUI.nal_time_offset_length, 0, 24.0, 2, 0.0, 0.0, }, + {"VUI_vcl_hrd_parameters_present_flag", &cfgparams.VUI.vcl_hrd_parameters_present_flag, 0, 0.0, 1, 0.0, 1.0, }, + {"VUI_vcl_cpb_cnt_minus1", &cfgparams.VUI.vcl_cpb_cnt_minus1, 0, 0.0, 1, 0.0, 31.0, }, + {"VUI_vcl_bit_rate_scale", &cfgparams.VUI.vcl_bit_rate_scale, 0, 0.0, 2, 0.0, 0.0, }, + {"VUI_vcl_cpb_size_scale", &cfgparams.VUI.vcl_cpb_size_scale, 0, 0.0, 2, 0.0, 0.0, }, + {"VUI_vcl_bit_rate_value_minus1", &cfgparams.VUI.vcl_bit_rate_value_minus1, 0, 0.0, 2, 0.0, 0.0, }, + {"VUI_vcl_cpb_size_value_minus1", &cfgparams.VUI.vcl_cpb_size_value_minus1, 0, 0.0, 2, 0.0, 0.0, }, + {"VUI_vcl_vbr_cbr_flag", &cfgparams.VUI.vcl_vbr_cbr_flag, 0, 0.0, 1, 0.0, 1.0, }, + {"VUI_vcl_initial_cpb_removal_delay_length_minus1", &cfgparams.VUI.vcl_initial_cpb_removal_delay_length_minus1, 0, 23.0, 2, 0.0, 0.0, }, + {"VUI_vcl_cpb_removal_delay_length_minus1",&cfgparams.VUI.vcl_cpb_removal_delay_length_minus1,0, 23.0, 2, 0.0, 0.0, }, + {"VUI_vcl_dpb_output_delay_length_minus1", &cfgparams.VUI.vcl_dpb_output_delay_length_minus1, 0, 23.0, 2, 0.0, 0.0, }, + {"VUI_vcl_time_offset_length", &cfgparams.VUI.vcl_time_offset_length, 0, 24.0, 2, 0.0, 0.0, }, + {"VUI_low_delay_hrd_flag", &cfgparams.VUI.low_delay_hrd_flag, 0, 0.0, 1, 0.0, 1.0, }, + {"VUI_pic_struct_present_flag", &cfgparams.VUI.pic_struct_present_flag, 0, 0.0, 1, 0.0, 1.0, }, + {"VUI_bitstream_restriction_flag", &cfgparams.VUI.bitstream_restriction_flag, 0, 0.0, 1, 0.0, 1.0, }, + {"VUI_motion_vectors_over_pic_boundaries_flag", &cfgparams.VUI.motion_vectors_over_pic_boundaries_flag, 0, 1.0, 1, 0.0, 1.0, }, + {"VUI_max_bytes_per_pic_denom", &cfgparams.VUI.max_bytes_per_pic_denom, 0, 2.0, 1, 0.0, 16.0, }, + {"VUI_max_bits_per_mb_denom", &cfgparams.VUI.max_bits_per_mb_denom, 0, 1.0, 1, 0.0, 16.0, }, + {"VUI_log2_max_mv_length_vertical", &cfgparams.VUI.log2_max_mv_length_vertical, 0, 16.0, 1, 0.0, 16.0, }, + {"VUI_log2_max_mv_length_horizontal", &cfgparams.VUI.log2_max_mv_length_horizontal, 0, 16.0, 1, 0.0, 16.0, }, + {"VUI_num_reorder_frames", &cfgparams.VUI.num_reorder_frames, 0, 16.0, 1, 0.0, 16.0, }, + {"VUI_max_dec_frame_buffering", &cfgparams.VUI.max_dec_frame_buffering, 0, 16.0, 1, 0.0, 16.0, }, + {"SEIMessageText", &cfgparams.SEIMessageText, 1, 0.0, 0, 0.0, 0.0, INPUT_TEXT_SIZE,}, + // {NAMEinConfigFile, &cfgparams.VariableName, Type,InitialValue, LimitType, MinLimit, MaxLimit, CharSize} +#if B0_MORE_REF + {"BLevel0MoreRef", &cfgparams.BLevel0MoreRef, 0, 1.0, 1, 0.0, 1.0, }, +#endif +#if KEEP_B_SAME_LIST + {"BIdenticalList", &cfgparams.BIdenticalList, 0, 1.0, 1, 0.0, 2.0, }, +#endif +#if CRA + {"CRA", &cfgparams.useCRA, 0, 1.0, 1, 0.0, 1.0, }, +#endif +#if HM50_LIKE_MMCO + {"HM50RefStructure", &cfgparams.HM50LikeMMCO, 0, 1.0, 1, 0.0, 1.0, }, +#endif +#if LD_REF_SETTING + {"LDRefSetting", &cfgparams.useF701RefForLD, 0, 1.0, 1, 0.0, 1.0, }, +#endif + {NULL, NULL, -1, 0.0, 0, 0.0, 0.0, }, +}; + + +#if (MVC_EXTENSION_ENABLE) +Mapping MapView1[] = { + //! Edited by Joao Carreira + {"TranscodeFileAux", &cfgparams.TranscodeFile[1], 1, 0.0, 0, 0.0, 0.0, FILE_NAME_SIZE }, + + {"InputFile", &cfgparams.input_file2.fname, 1, 0.0, 0, 0.0, 0.0, FILE_NAME_SIZE, }, + {"ReconFile", &cfgparams.ReconFile2, 1, 0.0, 0, 0.0, 0.0, FILE_NAME_SIZE, }, + {"SearchRange", &cfgparams.search_range[1], 0, 16.0, 2, 0.0, 0.0, }, + {"DisableSubpelME", &cfgparams.DisableSubpelME[1], 0, 0.0, 1, 0.0, 1.0, }, + {"DisableIntraInInter", &cfgparams.DisableIntraInInter[1], 0, 0.0, 1, 0.0, 1.0, }, + {"MVCInterViewReorder", &cfgparams.MVCInterViewReorder, 0, 0.0, 1, 0.0, 2.0, }, + //! Edited by Joao Carreira + {"PFramesOnlyView1", &cfgparams.PFramesOnlyView1, 0, 0.0, 1, 0.0, 2.0, }, + {"Force_InterViewPred", &cfgparams.Force_InterView, 0, 0.0, 1, 0.0, 2.0, }, + {"Force_BiPredMultiview", &cfgparams.Force_BiPredMultiview, 0, 0.0, 1, 0.0, 2.0, }, + + {"MVCFlipViews", &cfgparams.MVCFlipViews, 0, 0.0, 1, 0.0, 1.0, }, + {"QPOffset", &cfgparams.View1QPOffset, 0, 0.0, 0, (double) -MAX_QP, (double) MAX_QP, }, + {"MVCInterViewForceB", &cfgparams.MVCInterViewForceB, 0, 0.0, 1, 0.0, 1.0, }, + {"MVCEnableInterViewFlag", &cfgparams.enable_inter_view_flag, 0, 1.0, 1, 0.0, 1.0, }, + {"SepViewInterSearch", &cfgparams.SepViewInterSearch, 0, 0.0, 1, 0.0, 1.0, }, + {"NoResidueRDO", &cfgparams.View1NoResidueRDO, 0, 1.0, 1, 0.0, 1.0, }, + {"PList0References", &cfgparams.P_List0_refs[1], 0, 0.0, 1, 0.0, 16.0, }, + {"BList0References", &cfgparams.B_List0_refs[1], 0, 0.0, 1, 0.0, 16.0, }, + {"BList1References", &cfgparams.B_List1_refs[1], 0, 1.0, 1, 0.0, 16.0, }, + {"LambdaMultiplier", &cfgparams.enh_layer_lambda_multiplier, 2, 1.0, 2, 0.0, 0.0, }, + {"MELambdaMultiplier", &cfgparams.enh_layer_me_lambda_multiplier, 2, 1.0, 2, 0.0, 0.0, }, + {"PSliceSkip", &cfgparams.InterSearch[1][0][0], 0, 1.0, 1, 0.0, 1.0, }, + {"PSliceSearch16x16", &cfgparams.InterSearch[1][0][1], 0, 1.0, 1, 0.0, 1.0, }, + {"PSliceSearch16x8", &cfgparams.InterSearch[1][0][2], 0, 1.0, 1, 0.0, 1.0, }, + {"PSliceSearch8x16", &cfgparams.InterSearch[1][0][3], 0, 1.0, 1, 0.0, 1.0, }, + {"PSliceSearch8x8", &cfgparams.InterSearch[1][0][4], 0, 1.0, 1, 0.0, 1.0, }, + {"PSliceSearch8x4", &cfgparams.InterSearch[1][0][5], 0, 1.0, 1, 0.0, 1.0, }, + {"PSliceSearch4x8", &cfgparams.InterSearch[1][0][6], 0, 1.0, 1, 0.0, 1.0, }, + {"PSliceSearch4x4", &cfgparams.InterSearch[1][0][7], 0, 1.0, 1, 0.0, 1.0, }, + // B slice partition modes. + {"BSliceDirect", &cfgparams.InterSearch[1][1][0], 0, 1.0, 1, 0.0, 1.0, }, + {"BSliceSearch16x16", &cfgparams.InterSearch[1][1][1], 0, 1.0, 1, 0.0, 1.0, }, + {"BSliceSearch16x8", &cfgparams.InterSearch[1][1][2], 0, 1.0, 1, 0.0, 1.0, }, + {"BSliceSearch8x16", &cfgparams.InterSearch[1][1][3], 0, 1.0, 1, 0.0, 1.0, }, + {"BSliceSearch8x8", &cfgparams.InterSearch[1][1][4], 0, 1.0, 1, 0.0, 1.0, }, + {"BSliceSearch8x4", &cfgparams.InterSearch[1][1][5], 0, 1.0, 1, 0.0, 1.0, }, + {"BSliceSearch4x8", &cfgparams.InterSearch[1][1][6], 0, 1.0, 1, 0.0, 1.0, }, + {"BSliceSearch4x4", &cfgparams.InterSearch[1][1][7], 0, 1.0, 1, 0.0, 1.0, }, + {"BiPredMESearchRange", &cfgparams.BiPredMESearchRange[1], 0, 8.0, 2, 0.0, 0.0, }, + {"MDReference", &cfgparams.MDReference[1], 0, 0.0, 1, 0.0, 1.0, }, + {"DFDisableRefISlice", &cfgparams.EnhLayerDFDisableIdc[1][I_SLICE], 0, 0.0, 1, 0.0, 2.0, }, + {"DFDisableNRefISlice", &cfgparams.EnhLayerDFDisableIdc[0][I_SLICE], 0, 0.0, 1, 0.0, 2.0, }, + {"DFDisableRefPSlice", &cfgparams.EnhLayerDFDisableIdc[1][P_SLICE], 0, 0.0, 1, 0.0, 2.0, }, + {"DFDisableNRefPSlice", &cfgparams.EnhLayerDFDisableIdc[0][P_SLICE], 0, 0.0, 1, 0.0, 2.0, }, + {"DFDisableRefBSlice", &cfgparams.EnhLayerDFDisableIdc[1][B_SLICE], 0, 0.0, 1, 0.0, 2.0, }, + {"DFDisableNRefBSlice", &cfgparams.EnhLayerDFDisableIdc[0][B_SLICE], 0, 0.0, 1, 0.0, 2.0, }, + {"DFDisableRefSPSlice", &cfgparams.EnhLayerDFDisableIdc[1][SP_SLICE], 0, 0.0, 1, 0.0, 2.0, }, + {"DFDisableNRefSPSlice", &cfgparams.EnhLayerDFDisableIdc[0][SP_SLICE], 0, 0.0, 1, 0.0, 2.0, }, + {"DFDisableRefSISlice", &cfgparams.EnhLayerDFDisableIdc[1][SI_SLICE], 0, 0.0, 1, 0.0, 2.0, }, + {"DFDisableNRefSISlice", &cfgparams.EnhLayerDFDisableIdc[0][SI_SLICE], 0, 0.0, 1, 0.0, 2.0, }, + {"DFAlphaRefISlice", &cfgparams.EnhLayerDFAlpha[1][I_SLICE], 0, 0.0, 1, -6.0, 6.0, }, + {"DFAlphaNRefISlice", &cfgparams.EnhLayerDFAlpha[0][I_SLICE], 0, 0.0, 1, -6.0, 6.0, }, + {"DFAlphaRefPSlice", &cfgparams.EnhLayerDFAlpha[1][P_SLICE], 0, 0.0, 1, -6.0, 6.0, }, + {"DFAlphaNRefPSlice", &cfgparams.EnhLayerDFAlpha[0][P_SLICE], 0, 0.0, 1, -6.0, 6.0, }, + {"DFAlphaRefBSlice", &cfgparams.EnhLayerDFAlpha[1][B_SLICE], 0, 0.0, 1, -6.0, 6.0, }, + {"DFAlphaNRefBSlice", &cfgparams.EnhLayerDFAlpha[0][B_SLICE], 0, 0.0, 1, -6.0, 6.0, }, + {"DFAlphaRefSPSlice", &cfgparams.EnhLayerDFAlpha[1][SP_SLICE], 0, 0.0, 1, -6.0, 6.0, }, + {"DFAlphaNRefSPSlice", &cfgparams.EnhLayerDFAlpha[0][SP_SLICE], 0, 0.0, 1, -6.0, 6.0, }, + {"DFAlphaRefSISlice", &cfgparams.EnhLayerDFAlpha[1][SI_SLICE], 0, 0.0, 1, -6.0, 6.0, }, + {"DFAlphaNRefSISlice", &cfgparams.EnhLayerDFAlpha[0][SI_SLICE], 0, 0.0, 1, -6.0, 6.0, }, + {"DFBetaRefISlice", &cfgparams.EnhLayerDFBeta[1][I_SLICE], 0, 0.0, 1, -6.0, 6.0, }, + {"DFBetaNRefISlice", &cfgparams.EnhLayerDFBeta[0][I_SLICE], 0, 0.0, 1, -6.0, 6.0, }, + {"DFBetaRefPSlice", &cfgparams.EnhLayerDFBeta[1][P_SLICE], 0, 0.0, 1, -6.0, 6.0, }, + {"DFBetaNRefPSlice", &cfgparams.EnhLayerDFBeta[0][P_SLICE], 0, 0.0, 1, -6.0, 6.0, }, + {"DFBetaRefBSlice", &cfgparams.EnhLayerDFBeta[1][B_SLICE], 0, 0.0, 1, -6.0, 6.0, }, + {"DFBetaNRefBSlice", &cfgparams.EnhLayerDFBeta[0][B_SLICE], 0, 0.0, 1, -6.0, 6.0, }, + {"DFBetaRefSPSlice", &cfgparams.EnhLayerDFBeta[1][SP_SLICE], 0, 0.0, 1, -6.0, 6.0, }, + {"DFBetaNRefSPSlice", &cfgparams.EnhLayerDFBeta[0][SP_SLICE], 0, 0.0, 1, -6.0, 6.0, }, + {"DFBetaRefSISlice", &cfgparams.EnhLayerDFBeta[1][SI_SLICE], 0, 0.0, 1, -6.0, 6.0, }, + {"DFBetaNRefSISlice", &cfgparams.EnhLayerDFBeta[0][SI_SLICE], 0, 0.0, 1, -6.0, 6.0, }, + {"SearchMode", &cfgparams.SearchMode[1], 0, 0.0, 1, -1.0, 3.0, }, + {"EPZSTemporal", &cfgparams.EPZSTemporal[1], 0, 0.0, 1, 0.0, 1.0, }, + {"EnableEPZSScalers", &cfgparams.EnableEnhLayerEPZSScalers, 0, 0.0, 1, 0.0, 1.0, }, + {"EPZSMinThresScale", &cfgparams.EPZSMinThresScale[1], 0, 0.0, 0, 0.0, 0.0, }, + {"EPZSMaxThresScale", &cfgparams.EPZSMaxThresScale[1], 0, 2.0, 0, 0.0, 0.0, }, + {"EPZSMedThresScale", &cfgparams.EPZSMedThresScale[1], 0, 1.0, 0, 0.0, 0.0, }, + {"EPZSSubPelThresScale", &cfgparams.EPZSSubPelThresScale[1], 0, 1.0, 0, 0.0, 0.0, }, + {NULL, NULL, -1, 0.0, 0, 0.0, 0.0, }, +}; +#endif + +#endif + +#ifndef INCLUDED_BY_CONFIGFILE_C +extern Mapping Map[]; +extern Mapping MapView1[]; +#endif + +extern void Configure (VideoParameters *p_Vid, InputParameters *p_Inp, int ac, char *av[]); +extern void get_number_of_frames (InputParameters *p_Inp, VideoDataFile *input_file); +extern void read_slice_group_info(VideoParameters *p_Vid, InputParameters *p_Inp); + +#endif + diff --git a/lencod/inc/conformance.h b/lencod/inc/conformance.h new file mode 100644 index 0000000..1e25565 --- /dev/null +++ b/lencod/inc/conformance.h @@ -0,0 +1,32 @@ + +/*! + ************************************************************************ + * \file conformance.h + * + * \brief + * Level & Profile Related definitions + * + * \author + * Alexis Michael Tourapis \n + * + ************************************************************************ + */ + +#ifndef _CONFORMANCE_H_ +#define _CONFORMANCE_H_ + +extern void profile_check (InputParameters *p_Inp); +extern void level_check (VideoParameters *p_Vid, InputParameters *p_Inp); +extern void update_mv_limits (VideoParameters *p_Vid, byte is_field); +extern void clip_mv_range (VideoParameters *p_Vid, int search_range, MotionVector *mv, int res); +extern int out_of_bounds_mvs (VideoParameters *p_Vid, const MotionVector *mv); +extern void test_clip_mvs (VideoParameters *p_Vid, MotionVector *mv, Boolean write_mb); +extern Boolean CheckPredictionParams(Macroblock *currMB, Block8x8Info *b8x8info, int mode); + +extern unsigned int getMaxMBPS(unsigned int levelIdc); +extern unsigned int getMinCR (unsigned int levelIdc); +extern unsigned int getMaxBR (unsigned int levelIdc); +extern unsigned int getMaxCPB (unsigned int levelIdc); + +#endif + diff --git a/lencod/inc/context_ini.h b/lencod/inc/context_ini.h new file mode 100644 index 0000000..e624711 --- /dev/null +++ b/lencod/inc/context_ini.h @@ -0,0 +1,27 @@ + +/*! + ************************************************************************************* + * \file context_ini.h + * + * \brief + * CABAC context initializations + * + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * - Detlev Marpe + * - Heiko Schwarz + ************************************************************************************** + */ + +#ifndef _CONTEXT_INI_ +#define _CONTEXT_INI_ + +extern void create_context_memory (VideoParameters *p_Vid, InputParameters *p_Inp); +extern void free_context_memory (VideoParameters *p_Vid); +extern void update_field_frame_contexts (VideoParameters *p_Vid, int); +extern void SetCtxModelNumber (Slice *currSlice); +extern void init_contexts (Slice *currSlice); +extern void store_contexts (Slice *currSlice); + +#endif + diff --git a/lencod/inc/contributors.h b/lencod/inc/contributors.h new file mode 100644 index 0000000..8f83ce4 --- /dev/null +++ b/lencod/inc/contributors.h @@ -0,0 +1,217 @@ + +/*! \file + * contributors.h + * \brief + * List of contributors and copyright information. + * + * \par Copyright statements + \verbatim + MPEG-4 AVC/H.264 JM coder/decoder + + Copyright (C) 2000/2009 by + Apple Computer, Inc. + Dolby Laboratories, Inc., USA + Ericsson Radio Systems, Sweden + Fraunhofer-Institute for Telecommunications Heinrich-Hertz-Institut (HHI), Germany + LSI Logic, Canada + Microsoft Corp., USA + Motorola Inc., USA + Nokia Corporation, Finland + Nokia Inc., USA + RealNetworks, Inc., USA + Sejong Univ., Digital Media System Lab., Korea + Siemens AG, Germany + Telenor Broadband Services, Norway + TELES AG, Germany + Thomson, Inc., USA + University of Hannover, Institut of Communication Theory and Signal Processing,Germany + Videolocus, Canada + + \endverbatim + \par Full Contact Information + \verbatim + + Lowell Winger + Guy Côté + Michael Gallant + VideoLocus Inc. + 97 Randall Dr. + Waterloo, ON, Canada N2V1C5 + + Inge Lille-Langøy + Telenor Broadband Services + P.O.Box 6914 St.Olavs plass + N-0130 Oslo, Norway + + Rickard Sjoberg + Ericsson Radio Systems + KI/ERA/T/VV + 164 80 Stockholm, Sweden + + Stephan Wenger + TU Berlin / TELES AG + Sekr. FR 6-3 + Franklinstr. 28-29 + D-10587 Berlin, Germany + + Jani Lainema + Ragip Kurceren + Nokia Inc. / Nokia Research Center + 6000 Connection Drive + Irving, TX 75039, USA + + Miska M. Hannuksela + Nokia Corporation / Nokia Mobile Phones + P.O. Box 88 + 33721 Tampere, Finland + + Sebastian Purreiter + Siemens AG + ICM MD MP RD MCH 83 + P.O.Box 80 17 07 + D-81617 Munich, Germany + + Thomas Wedi + University of Hannover + Institut of Communication Theory and Signal Processing + Appelstr. 9a + 30167 Hannover + + Thomas Stockhammer + Tobias Oelbaum (TO) + Institute for Communications Engineering + Munich University of Technology + 80290 Munich + Germany + + Yann Le Maguet + Philips Research France + + Dong Tian + Ye-Kui Wang + Tampere University of Technology + Tampere International Center for Signal Processing + 33720 Tampere, Finland + + Karsten Suehring + Heiko Schwarz + Detlev Marpe + Guido Heising + Fraunhofer-Institute for Telecommunications + Heinrich-Hertz-Institut (HHI) + Einsteinufer 37 + 10587 Berlin + Germany + + Limin Wang + Krit Panusopone + Rajeev Gandhi + Yue Yu + Motorola Inc. + 6450 Sequence Drive + San Diego, CA 92121 USA + + Feng Wu + Xiaoyan Sun + Microsoft Research Asia + 3/F, Beijing Sigma Center + No.49, Zhichun Road, Hai Dian District, + Beijing China 100080 + + Mathias Wien + Achim Dahlhoff + Institut und Lehrstuhl für Nachrichtentechnik + RWTH Aachen University + 52072 Aachen + Germany + + Yoshihiro Kikuchi + Takeshi Chujoh + Toshiba Corporation + Research and Development Center + Kawasaki 212-8582, Japan + + Shinya Kadono + Matsushita Electric Industrial Co., Ltd. + 1006 Kadoma, Kadoma + Osaka 663-8113, Japan + + Dzung Hoang + 10533 Roy Butler Dr. + Austin, TX 78717 + + Eric Viscito + eV Consulting + 52 Tracy Ln + Shelburne, VT 05482 USA + + Dzung Hoang + Xilient Inc. + 10181 Bubb Rd. + Cupertino, CA 95014 + + Barry Haskell + Apple Computer, Inc. + 2 Infinite Loop + Cupertino, California 95014 + + Greg Conklin + RealNetworks, Inc. + 2601 Elliott Ave + Seattle, WA 98101 + + Jill Boyce + Cristina Gomila + Thomson Inc + 2 Independence Way + Princeton, NJ 08540 + + Haoping Yu + Corporate Research + Thomson Inc + 101 West 103rd Street + Indianapolis, IN 46290 + + Siwei Ma + Institute of Computing Technology + Chinese Academy of Sciences + Kexueyuan South Road 6 + Haidian District + Beijing, China + + Zhibo Chen + JianFeng Xu + Wenfang Fu + Dept.of E&E, Tsinghua Univ + Haidian District + Beijing China, 100084 + + Alexis Michael Tourapis + Athanasios Leontaris + Peshala Pahalawatta + Yuwen He + Yan Ye + Dolby Laboratories Inc. + 3601 West Alameda Ave. + Burbank, CA 91505 + + Xiaoquan Yi + Jun Zhang + Dept. of C.E. Santa Clara Univ. + 500 El Camino Real + Santa Clara, CA 95053 + + Shun-ichi Sekiguchi + Information Technology R&D Center, + Mitsubishi Electric Corporation + 5-1-1, Ofuna, Kamakura, Japan + + Yung-Lyul Lee + Ki-Hun Han + Department of Computer Engineering, + Sejong University + 98 Kunja-Dong, Kwangjin-Gu, Seoul 143-747, Korea + + \endverbatim + */ + diff --git a/lencod/inc/defines.h b/lencod/inc/defines.h new file mode 100644 index 0000000..bb8344f --- /dev/null +++ b/lencod/inc/defines.h @@ -0,0 +1,372 @@ +/*! + ************************************************************************** + * \file defines.h + * + * \brief + * Header file containing some useful global definitions + * + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * - Detlev Marpe + * - Karsten Suehring + * - Alexis Michael Tourapis + * + * + * \date + * 21. March 2001 + ************************************************************************** + */ + + +#ifndef _DEFINES_H_ +#define _DEFINES_H_ + +#if defined _DEBUG +# define TRACE 0 //!< 0:Trace off 1:Trace on 2:detailed CABAC context information +#else +# define TRACE 0 //!< 0:Trace off 1:Trace on 2:detailed CABAC context information +#endif + +#define JM "18 (FRExt)" +#define VERSION "18.2" +#define EXT_VERSION "(FRExt)" + +//! Edited by Joao Carreira +#define MAX_TRANSCODED_FRAMES 100 + +#define GET_METIME 1 //!< Enables or disables ME computation time +#define DUMP_DPB 0 //!< Dump DPB info for debug purposes +#define PRINTREFLIST 1 //!< Print ref list info for debug purposes +#define IMGTYPE 0 //!< Define imgpel size type. 0 implies byte (cannot handle >8 bit depths) and 1 implies unsigned short +#define ENABLE_FIELD_CTX 1 //!< Enables field context types for CABAC. If disabled, results in speedup for progressive content. +#define ENABLE_HIGH444_CTX 1 //!< Enables High 444 context types for CABAC. If disabled, results in speedup of non High444 profile encodings. +#define DEBUG_BITDEPTH 0 //!< Ensures that > 8 bit content have no values that would result in out of range results +#define ALLOW_GRAYSCALE 1 //!< Allow encoding in grayscale +#define ZEROSNR 1 //!< PSNR computation method +#define USE_RND_COST 0 //!< Perform ME RD decision using a rounding estimate of the motion cost +#define JM_INT_DIVIDE 1 +#define JM_MEM_DISTORTION 0 +#define JCOST_CALC_SCALEUP 1 //!< 1: J = (D<>LAMBDA_ACCURACY_BITS) +#define INTRA_RDCOSTCALC_ET 1 //!< Early termination +#define INTRA_RDCOSTCALC_NNZ 1 //1: to recover block's nzn after rdcost calculation; +#define JCOST_OVERFLOWCHECK 0 //!<1: to check the J cost if it is overflow> +#define JM_PARALLEL_DEBLOCK 0 //!< Enables Parallel Deblocking +#define SIMULCAST_ENABLE 0 + +#define MVC_EXTENSION_ENABLE 1 //!< enable support for the Multiview High Profile +//! Added by Joao Carreira +#define MAX_VIEW_NUM 2 +#define BASE_VIEW_IDX 0 + +#define EOS_OUTPUT 0 + +#define EPZSREF 1 + +#define MAX_RC_MODE 3 +#define RC_MAX_TEMPORAL_LEVELS 5 + +#define SSE_MEMORY_ALIGNMENT 16 +#define MAX_NUM_DPB_LAYERS 2 +//#define BEST_NZ_COEFF 1 // yuwen 2005.11.03 => for high complexity mode decision (CAVLC, #TotalCoeff) + +// defines for creating similar coding structures like HM +#define OUTPUT_REF_LIST 0 //!< for debug purpose, output the reference picture list. do not support field + +#define B0_MORE_REF 0 ///!< used for random access setting, treat POC(HM like poc, =JM poc/2)%(NumberOfBFrames+1)==0 as B0, i.e. use more references at temporal level 0 +#define KEEP_B_SAME_LIST 1 ///!< keep B pictures using two identical reference picture lists +#define CRA 1 ///!< used for random access setting, HM CRA like random access point + ///!< using open GOP is not enough, for example, 32 is an Intra picture, when encoding 40, everything is OK. + ///!< but when encoding 36, there is one reference picture (32) in list0; actually, there should be two (32, 40) as HM. + ///!< setting number of reference frame to 1 at picture 40, using RPLR to let list1 use 32 as reference, + ///!< and marking all pictures unused for reference except 32 to solve this problem +#define HM50_LIKE_MMCO 1 ///!< use the HM-5.0 like MMCO, keep the identical referencing structure as HM-5.0 +#define LD_REF_SETTING 1 ///!< used for low delay setting, 1+X referencing structure, as proposed in JCTVC-F701 + + +//AVC Profile IDC definitions +enum { + FREXT_CAVLC444 = 44, //!< YUV 4:4:4/14 "CAVLC 4:4:4" + BASELINE = 66, //!< YUV 4:2:0/8 "Baseline" + MAIN = 77, //!< YUV 4:2:0/8 "Main" + EXTENDED = 88, //!< YUV 4:2:0/8 "Extended" + FREXT_HP = 100, //!< YUV 4:2:0/8 "High" + FREXT_Hi10P = 110, //!< YUV 4:2:0/10 "High 10" + FREXT_Hi422 = 122, //!< YUV 4:2:2/10 "High 4:2:2" + FREXT_Hi444 = 244, //!< YUV 4:4:4/14 "High 4:4:4" + MULTIVIEW_HIGH = 118, //!< YUV 4:2:0/8 "Multiview High" + STEREO_HIGH = 128 //!< YUV 4:2:0/8 "Stereo High" +} ProfileIDC; + +// Some typedefs used in the software +#include "types.h" + +#define FILE_NAME_SIZE 255 +#define INPUT_TEXT_SIZE 1024 + +#define CAVLC_LEVEL_LIMIT 2063 + +#if (ENABLE_HIGH444_CTX == 1) +# define NUM_BLOCK_TYPES 22 +#else +# define NUM_BLOCK_TYPES 10 +#endif + +#define _LEAKYBUCKET_ + +// --------------------------------------------------------------------------------- +// FLAGS and DEFINES for new chroma intra prediction, Dzung Hoang +// Threshold values to zero out quantized transform coefficients. +// Recommend that _CHROMA_COEFF_COST_ be low to improve chroma quality +#define _LUMA_COEFF_COST_ 4 //!< threshold for luma coeffs +#define _CHROMA_COEFF_COST_ 4 //!< threshold for chroma coeffs, used to be 7 +#define _LUMA_MB_COEFF_COST_ 5 //!< threshold for luma coeffs of inter Macroblocks +#define _LUMA_8x8_COEFF_COST_ 5 //!< threshold for luma coeffs of 8x8 Inter Partition + +//#define IMG_PAD_SIZE 20 //!< Number of pixels padded around the reference frame (>=4) +//#define IMG_PAD_SIZE_TIMES4 80 //!< Number of pixels padded around the reference frame in subpel units(>=16) +#define IMG_PAD_SIZE_X 32 //!< Number of pixels padded around the reference frame (>=4) +#define IMG_PAD_SIZE_Y 20 //!< Number of pixels padded around the reference frame (>=4) + +#define MAX_VALUE 999999 //!< used for start value for some variables +#define INVALIDINDEX (-135792468) + +#define DUMMY 14 +#define ET_SIZE 300 //!< size of error text buffer + +#define LAMBDA_ACCURACY_BITS 5 +#define LAMBDA_FACTOR(lambda) ((int)((double)(1 << LAMBDA_ACCURACY_BITS) * lambda + 0.5)) + +#if (IMGTYPE == 0) +#define DISTBLK_MAX INT_MAX +#else +#define DISTBLK_MAX ((distblk) INT_MAX << LAMBDA_ACCURACY_BITS) +#endif + +#define MAXSLICEPERPICTURE 500 +#define MAX_REFERENCE_PICTURES 32 + +#define BLOCK_SHIFT 2 +#define BLOCK_SIZE 4 +#define BLOCK_SIZE_8x8 8 +#define SMB_BLOCK_SIZE 8 +#define BLOCK_PIXELS 16 +#define MB_BLOCK_SIZE 16 +#define MB_PIXELS 256 // MB_BLOCK_SIZE * MB_BLOCK_SIZE +#define MB_PIXELS_SHIFT 8 // log2(MB_BLOCK_SIZE * MB_BLOCK_SIZE) +#define MB_BLOCK_SHIFT 4 +#define BLOCK_MULTIPLE 4 // (MB_BLOCK_SIZE/BLOCK_SIZE) +#define MB_BLOCK_PARTITIONS 16 // (BLOCK_MULTIPLE * BLOCK_MULTIPLE) +#define BLOCK_CONTEXT 64 // (4 * MB_BLOCK_PARTITIONS) +// These variables relate to the subpel accuracy supported by the software (1/4) +#define BLOCK_SIZE_SP 16 // BLOCK_SIZE << 2 +#define BLOCK_SIZE_8x8_SP 32 // BLOCK_SIZE8x8 << 2 + +// wavelet based weighted PSNR wavelet levels +#define NUM_WAVELET_LEVEL 4 + +// RDOQ +#define MAX_PREC_COEFF 25 + +// Joao Carreira - Useful information +// Available MB modes +enum { + PSKIP = 0, + BSKIP_DIRECT = 0, + P16x16 = 1, + P16x8 = 2, + P8x16 = 3, + SMB8x8 = 4, + SMB8x4 = 5, + SMB4x8 = 6, + SMB4x4 = 7, + P8x8 = 8, + I4MB = 9, + I16MB = 10, + IBLOCK = 11, + SI4MB = 12, + I8MB = 13, + IPCM = 14, + MAXMODE = 15 +} MBModeTypes; + +// number of intra prediction modes +#define NO_INTRA_PMODE 9 + +// Direct Mode types +enum { + DIR_TEMPORAL = 0, //!< Temporal Direct Mode + DIR_SPATIAL = 1 //!< Spatial Direct Mode +} DirectModes; + +// CAVLC block types +enum { + LUMA = 0, + LUMA_INTRA16x16DC = 1, + LUMA_INTRA16x16AC = 2, + CB = 3, + CB_INTRA16x16DC = 4, + CB_INTRA16x16AC = 5, + CR = 8, + CR_INTRA16x16DC = 9, + CR_INTRA16x16AC = 10 +} CAVLCBlockTypes; + +// CABAC block types +enum { + LUMA_16DC = 0, + LUMA_16AC = 1, + LUMA_8x8 = 2, + LUMA_8x4 = 3, + LUMA_4x8 = 4, + LUMA_4x4 = 5, + CHROMA_DC = 6, + CHROMA_AC = 7, + CHROMA_DC_2x4 = 8, + CHROMA_DC_4x4 = 9, + CB_16DC = 10, + CB_16AC = 11, + CB_8x8 = 12, + CB_8x4 = 13, + CB_4x8 = 14, + CB_4x4 = 15, + CR_16DC = 16, + CR_16AC = 17, + CR_8x8 = 18, + CR_8x4 = 19, + CR_4x8 = 20, + CR_4x4 = 21 +} CABACBlockTypes; + +// Color components +enum { + Y_COMP = 0, // Y Component + U_COMP = 1, // U Component + V_COMP = 2, // V Component + R_COMP = 3, // R Component + G_COMP = 4, // G Component + B_COMP = 5, // B Component + T_COMP = 6 +} ColorComponent; + + +#define IS_INTRA(MB) ((MB)->mb_type==SI4MB || (MB)->mb_type==I4MB || (MB)->mb_type==I16MB || (MB)->mb_type==I8MB || (MB)->mb_type==IPCM) + +#define LEVEL_NUM 6 +#define TOTRUN_NUM 15 +#define RUNBEFORE_NUM 7 +#define RUNBEFORE_NUM_M1 6 + +// Quantization parameter range +#define MIN_QP 0 +#define MAX_QP 51 +#define SHIFT_QP 12 + +// 4x4 intra prediction modes +enum { + VERT_PRED = 0, + HOR_PRED = 1, + DC_PRED = 2, + DIAG_DOWN_LEFT_PRED = 3, + DIAG_DOWN_RIGHT_PRED = 4, + VERT_RIGHT_PRED = 5, + HOR_DOWN_PRED = 6, + VERT_LEFT_PRED = 7, + HOR_UP_PRED = 8 +} I4x4PredModes; + +// 16x16 intra prediction modes +enum { + VERT_PRED_16 = 0, + HOR_PRED_16 = 1, + DC_PRED_16 = 2, + PLANE_16 = 3 +} I16x16PredModes; + +// 8x8 chroma intra prediction modes +enum { + DC_PRED_8 = 0, + HOR_PRED_8 = 1, + VERT_PRED_8 = 2, + PLANE_8 = 3 +} I8x8PredModes; + +#define INIT_FRAME_RATE 30 +enum { + EOS = 1, //!< End Of Sequence + SOP = 2, //!< Start Of Picture + SOS = 3 //!< Start Of Slice +}; + +// MV Prediction types +enum { + MVPRED_MEDIAN = 0, + MVPRED_L = 1, + MVPRED_U = 2, + MVPRED_UR = 3 +} MVPredTypes; + +#define MAX_SYMBOLS_PER_MB 1200 //!< Maximum number of different syntax elements for one MB + // CAVLC needs more symbols per MB + + +#define MAX_PART_NR 3 /*!< Maximum number of different data partitions. + Some reasonable number which should reflect + what is currently defined in the SE2Partition map (elements.h) */ + +//Start code and Emulation Prevention need this to be defined in identical manner at encoder and decoder +#define ZEROBYTES_SHORTSTARTCODE 2 //indicates the number of zero bytes in the short start-code prefix + +#define Q_BITS 15 +#define DQ_BITS 6 + +#define Q_BITS_8 16 +#define DQ_BITS_8 6 + +// Context Adaptive Lagrange Multiplier (CALM) +#define CALM_MF_FACTOR_THRESHOLD 512.0 + +#define MAX_PLANE 3 + +#define MAXSLICEGROUPIDS 8 + + +#define NUM_MB_TYPE_CTX 11 +#define NUM_B8_TYPE_CTX 9 +#define NUM_MV_RES_CTX 10 +#define NUM_REF_NO_CTX 6 +#define NUM_DELTA_QP_CTX 4 +#define NUM_MB_AFF_CTX 4 + +#define NUM_TRANSFORM_SIZE_CTX 3 + +#define NUM_IPR_CTX 2 +#define NUM_CIPR_CTX 4 +#define NUM_CBP_CTX 4 +#define NUM_BCBP_CTX 4 +#define NUM_MAP_CTX 15 +#define NUM_LAST_CTX 15 +#define NUM_ONE_CTX 5 +#define NUM_ABS_CTX 5 + +enum // JLT : on-the-fly levels/modes +{ + OTF_L0 = 0, // Disable, interpolate & store all positions + OTF_L1 = 1, // Store full pel & interpolated 1/2 pel positions; 1/4 pel positions interpolate on-the-fly + OTF_L2 = 2 // Store only full pell positions; 1/2 & 1/4 pel positions interpolate on-the-fly +} OTFMode; + +enum +{ + OTF_ME = 0, + OTF_MC = 1 +} OTFFunction; + +#if IMGTYPE +#if JM_MEM_DISTORTION +#pragma message("JM_MEM_DISTORTION must be zero!") +#endif +#endif + + +#endif + diff --git a/lencod/inc/elements.h b/lencod/inc/elements.h new file mode 100644 index 0000000..0733424 --- /dev/null +++ b/lencod/inc/elements.h @@ -0,0 +1,89 @@ + +/*! + ************************************************************************** + * \file elements.h + * \brief Header file for elements in H.264 streams + * \date 6.10.2000, + * \version + * 1.1 + * + * \note + * Version 1.0 included three partition modes, no DP, 2 partitionsper slice + * and 4 partitions per slice. As per document VCEG-N72 this is changed + * in version 1.1 to only two partition modes, one without DP and one with + * 3 partition per slice + * + * \author Sebastian Purreiter + * \author Stephan Wenger + * + ************************************************************************** + */ + +#ifndef _ELEMENTS_H_ +#define _ELEMENTS_H_ + +/*! + * definition of H.264 syntax elements + * order of elements follow dependencies for picture reconstruction + */ + +#define MAXPARTITIONMODES 2 //!< maximum possible partition modes as defined in assignSE2partition[][] + +/*! + * \brief lookup-table to assign different elements to partition + * + * \note here we defined up to 6 different partitions similar to + * document Q15-k-18 described in the PROGFRAMEMODE. + * The Sliceheader contains the PSYNC information. \par + * + * Elements inside a partition are not ordered. They are + * ordered by occurence in the stream. + * Assumption: Only partitionlosses are considered. \par + * + * The texture elements luminance and chrominance are + * not ordered in the progressive form + * This may be changed in image.c \par + * + * -IMPORTANT: + * Picture- or Sliceheaders must be assigned to partition 0. \par + * Furthermore partitions must follow syntax dependencies as + * outlined in document Q15-J-23. + */ + + +// A note on this table: +// +// While the assignment of values in enum data types is specified in C, it is not +// very ood style to have an "elementnumber", not even as a comment. +// +// Hence a copy of the relevant structure from global.h here +/* +typedef enum { + 0 SE_HEADER, + 1 SE_PTYPE, + 2 SE_MBTYPE, + 3 SE_REFFRAME, + 4 SE_INTRAPREDMODE, + 5 SE_MVD, + 6 SE_CBP + 7 SE_LUM_DC_INTRA, + 8 SE_CHR_DC_INTRA, + 9 SE_LUM_AC_INTRA, +10 SE_CHR_AC_INTRA, +12 SE_LUM_DC_INTER, +13 SE_CHR_DC_INTER, +14 SE_LUM_AC_INTER, +15 SE_CHR_AC_INTER, +16 SE_DELTA_QUANT, +18 SE_BFRAME, +19 SE_EOS, +20 SE_MAX_ELEMENTS */ // number of maximum syntax elements +//} SE_type; + + +extern const int * assignSE2partition[2]; +extern const int assignSE2partition_NoDP[SE_MAX_ELEMENTS]; +extern const int assignSE2partition_DP[SE_MAX_ELEMENTS]; + +#endif + diff --git a/lencod/inc/errdo.h b/lencod/inc/errdo.h new file mode 100644 index 0000000..efe56a8 --- /dev/null +++ b/lencod/inc/errdo.h @@ -0,0 +1,123 @@ +/*! + ************************************************************************** + * \file errdo.h + * \brief Header file for error resilient RDO (name of file should change) + * + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * - Peshala Pahalawatta + * - Zhifeng Chen + * - Alexis Michael Tourapis + * + ************************************************************************** + */ + +#ifndef _ERRDO_H_ +#define _ERRDO_H_ + + +typedef enum +{ + LLN = 0, + ROPE = 1, + RMPC = 2, +} DE_Type; + +struct distortion_estimation +{ + //To support slice data partitioing, residual and motion vector should use their own buffers (used if rdopt==3) + int ** res_con_diff_Y; //!< for storing the residual concealment difference for component Y in rmpc algorithm + int *** res_con_diff_UV; //!< for storing the residual concealment difference for component UV in rmpc algorithm + int ** p_res_con_diff[MAX_PLANE]; //!< pointer array for accessing res_con_error_Y / res_con_error_UV[] + int ** MV_con_diff_Y; //!< for storing the MV concealment difference for component Y in rmpc algorithm + int *** MV_con_diff_UV; //!< for storing the MV concealment difference for component UV in rmpc algorithm + int ** p_MV_con_diff[MAX_PLANE]; //!< pointer array for accessing MV_con_diff_Y / MV_con_diff_UV[] + byte ** error_sign_flag_Y; //!< to indicate the sign of transmission error for component Y in rmpc algorithm + byte ***error_sign_flag_UV; //!< to indicate the sign of transmission error for component UV in rmpc algorithm + byte ** p_error_sign_flag[MAX_PLANE]; //!< pointer array for accessing error_sign_flag_Y / error_sign_flag_UV[] + int ** transmission_dist_Y; //!< for storing the transmission distortion for component Y in rmpc algorithm + int *** transmission_dist_UV; //!< for storing the transmission distortion for component UV in rmpc algorithm + int ** p_transmission_dist[MAX_PLANE]; //!< pointer array for accessing transmission_dist_Y / transmission_dist_UV[] + int ** transmission_err_Y; //!< for storing the transmission distortion for component Y in rmpc algorithm + int *** transmission_err_UV; //!< for storing the transmission distortion for component UV in rmpc algorithm + int ** p_transmission_err[MAX_PLANE]; //!< pointer array for accessing transmission_dist_Y / transmission_dist_UV[] + + //Multiple Decoder Buffers (used if rdopt==3) + imgpel *** dec_imgY; //!< Decoded Y component in multiple hypothetical decoders + imgpel **** dec_imgUV; //!< Decoded U and V components in multiple hypothetical decoders + imgpel *** p_dec_img[MAX_PLANE]; //!< pointer array for accessing decoded pictures in hypothetical decoders + byte *** mb_error_map; //!< Map of macroblock errors in hypothetical decoders. + + //(used if rdopt==3) + imgpel ** first_moment_Y; //!< for storing the estimated first moment for component Y in rope algorithm + imgpel *** first_moment_UV; //!< for storing the estimated first moment for component Y in rope algorithm + imgpel ** p_first_moment[MAX_PLANE]; //!< pointer array for accessing first_moment_Y / first_moment_UV[] + uint16 ** second_moment_Y; //!< for storing the estimated second moment for component Y in rope algorithm + uint16 *** second_moment_UV; //!< for storing the estimated second moment for component Y in rope algorithm + uint16 ** p_second_moment[MAX_PLANE]; //!< pointer array for accessing second_moment_Y / first_moment_UV[] +}; + + +//! Info for the "decoders-in-the-encoder" used for rdoptimization with packet losses +struct decoders +{ + int ***res_img; //!< Residual values for macroblock + int ***res_mb_best8x8; //!< Residual values for the best b8x8 to be used in P8x8 mode decision + + //for RMPC algorithm + int **RCD_bestY_mb; + int ***RCD_bestY_b8x8; + int **MVCD_bestY_mb; + int ***MVCD_bestY_b8x8; + byte **flag_bestY_mb; + byte ***flag_bestY_b8x8; + byte **flag_wo_res; + byte ***flag_wo_res_bestY_b8x8; + int **trans_dist_bestY_mb; + int ***trans_dist_bestY_b8x8; + int **trans_dist_wo_res; + int ***trans_dist_wo_res_bestY_b8x8; + int **trans_err_bestY_mb; + int ***trans_err_bestY_b8x8; + int **trans_err_wo_res; + int ***trans_err_wo_res_bestY_b8x8; + + //for LLN algorithm + imgpel ***dec_mbY_best; //!< Best reconstructed macroblock pixel values + imgpel ****dec_mbY_best8x8; //!< Best reconstructed 8x8 mode pixel values + imgpel ****dec_mb_pred_best8x8; //!< Predicted pixel values for best 8x8 modes + imgpel ***dec_mb_pred; //!< Predicted pixel values for macroblock + int rec_type; + + //for ROPE algorithm + imgpel **first_moment_bestY_mb; + imgpel ***first_moment_bestY_b8x8; + imgpel ***first_moment_pred_bestY_b8x8; + imgpel **first_moment_pred; + uint16 **second_moment_bestY_mb; + uint16 ***second_moment_bestY_b8x8; + uint16 ***second_moment_pred_bestY_b8x8; + uint16 **second_moment_pred; +}; + +typedef struct decoders Decoders; + +//============= rate-distortion opt with packet losses =========== + +extern void init_error_conceal (VideoParameters *p_Vid, int concealment_type); +extern void errdo_compute_residue (Macroblock *currMB, imgpel **imgY, int **res_img, imgpel **mb_pred, int b8block, int block_size); +extern int allocate_errdo_mem (VideoParameters *p_Vid, InputParameters *p_Inp); +extern void free_errdo_mem (VideoParameters *p_Vid); + +extern void init_distortion_estimation (VideoParameters *p_Vid, int de_algorithm); +extern void errdo_store_best_b8x8(Macroblock *currMB, int transform8x8, int block); +extern void errdo_get_best_b8x8(Macroblock *currMB, int transform8x8, int block); +extern void errdo_store_best_MB(Macroblock *currMB); +extern void errdo_get_best_MB(Macroblock *currMB); +extern void errdo_get_best_P8x8(Macroblock *currMB, int transform8x8); +extern void errdo_alloc_storable_picture(StorablePicture *s, VideoParameters *p_Vid, InputParameters *p_Inp, int size_x, int size_y, int size_x_cr, int size_y_cr); +extern void errdo_free_storable_picture(StorablePicture* p); +extern StorablePicture* find_nearest_ref_picture(DecodedPictureBuffer *p_Dpb, int poc); + +#endif + diff --git a/lencod/inc/errdo_dist_mhyp.h b/lencod/inc/errdo_dist_mhyp.h new file mode 100644 index 0000000..8a64f60 --- /dev/null +++ b/lencod/inc/errdo_dist_mhyp.h @@ -0,0 +1,21 @@ +/*! + ************************************************************************** + * \file errdo_dist_mhyp.h + * \brief + * Header file for distortion estimation with multiple decoder based method + * + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * - Zhifeng Chen + * + ************************************************************************** + */ + +#ifndef _ERRDO_DIST_MHYP_H_ +#define _ERRDO_DIST_MHYP_H_ + +extern void errdo_store_best_block_multihyp (InputParameters *p_Inp, imgpel*** mbY, imgpel*** dec_img, int block, int img_i, int img_j, int block_size); +extern void errdo_get_best_block_multihyp (Macroblock *currMB, imgpel*** dec_img, imgpel*** mbY, int block, int block_size); +extern distblk errdo_distortion_estimation_multihyp(Macroblock *currMB, int block, int block_size, short mode, short pdir, distblk min_rdcost); +extern void copy_conceal_picture (VideoParameters *p_Vid, StorablePicture *enc_pic, int decoder); +#endif diff --git a/lencod/inc/explicit_gop.h b/lencod/inc/explicit_gop.h new file mode 100644 index 0000000..06150b8 --- /dev/null +++ b/lencod/inc/explicit_gop.h @@ -0,0 +1,23 @@ + +/*! + ************************************************************************************* + * \file explicit_gop.h + * + * \brief + * Functions for explicit gop and hierarchy support + * + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * - Alexis Michael Tourapis + ************************************************************************************* + */ + +#ifndef _EXPLICIT_GOP_H_ +#define _EXPLICIT_GOP_H_ + +// GOP Hierarchy +extern void init_gop_structure (VideoParameters *p_Vid, InputParameters *p_Inp); +extern void interpret_gop_structure(VideoParameters *p_Vid, InputParameters *p_Inp); +extern void clear_gop_structure (VideoParameters *p_Vid); + +#endif diff --git a/lencod/inc/explicit_seq.h b/lencod/inc/explicit_seq.h new file mode 100644 index 0000000..18baba1 --- /dev/null +++ b/lencod/inc/explicit_seq.h @@ -0,0 +1,39 @@ + +/*! + ************************************************************************************* + * \file explicit_seq.h + * + * \brief + * Functions for explicit sequence support + * + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * - Alexis Michael Tourapis + ************************************************************************************* + */ + +#ifndef _EXPLICIT_SEQ_H_ +#define _EXPLICIT_SEQ_H_ + +// Definition of structures used for explicit sequence representation +typedef struct +{ + int seq_number; + int slice_type; + int is_idr; + int reference_idc; + int frame_qp; + int deblocking; + int is_field; +} ExpFrameInfo; + +typedef struct exp_seq_info +{ + int no_frames; + ExpFrameInfo *info; +} ExpSeqInfo; + +extern void ReadExplicitSeqFile (ExpSeqInfo *seq_info, FILE *exp_file, int coding_index); +extern void OpenExplicitSeqFile (VideoParameters *p_Vid, InputParameters *p_Inp); +extern void CloseExplicitSeqFile (VideoParameters *p_Vid); +#endif diff --git a/lencod/inc/filehandle.h b/lencod/inc/filehandle.h new file mode 100644 index 0000000..922587a --- /dev/null +++ b/lencod/inc/filehandle.h @@ -0,0 +1,25 @@ + +/*! + *************************************************************************** + * + * \file filehandle.h + * + * \brief + * + * \date + * + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * + **************************************************************************/ + +#ifndef _FILEHANDLE_H_ +#define _FILEHANDLE_H_ + +extern int rewrite_paramsets (VideoParameters *p_Vid); +extern int start_sequence (VideoParameters *p_Vid, InputParameters *p_Inp); +extern int terminate_sequence(VideoParameters *p_Vid, InputParameters *p_Inp); +extern int write_PPS (VideoParameters *p_Vid, int len, int PPS_id); +extern int end_of_stream (VideoParameters *p_Vid); + +#endif diff --git a/lencod/inc/fmo.h b/lencod/inc/fmo.h new file mode 100644 index 0000000..273b052 --- /dev/null +++ b/lencod/inc/fmo.h @@ -0,0 +1,35 @@ + +/*! + *************************************************************************** + * + * \file fmo.h + * + * \brief + * Support for Flexible Macroblock Ordering + * + * \date + * 16 June 2002 + * + * \author + * Stephan Wenger stewe@cs.tu-berlin.de + **************************************************************************/ + +#ifndef _FMO_H_ +#define _FMO_H_ + +extern int FmoInit (VideoParameters * p_Vid, pic_parameter_set_rbsp_t * pps, seq_parameter_set_rbsp_t * sps); +extern void FmoUninit (VideoParameters *p_Vid); +extern int FmoFinit (seq_parameter_set_rbsp_t * sps); +extern int FmoMB2SliceGroup (VideoParameters *p_Vid, int mb); +extern int FmoGetFirstMBOfSliceGroup (VideoParameters *p_Vid, int SliceGroupID); +extern int FmoGetFirstMacroblockInSlice (VideoParameters *p_Vid, int SliceGroup); +extern int FmoGetNextMBNr (VideoParameters *p_Vid, int CurrentMbNr); +extern int FmoGetPreviousMBNr (VideoParameters *p_Vid, int CurrentMbNr); +extern int FmoGetLastCodedMBOfSliceGroup (VideoParameters *p_Vid, int SliceGroupID); +extern int FmoStartPicture (VideoParameters *p_Vid); +extern int FmoEndPicture (void); +extern int FmoSliceGroupCompletelyCoded (VideoParameters *p_Vid, int SliceGroupID); +extern void FmoSetLastMacroblockInSlice (VideoParameters *p_Vid, int mb); + + +#endif diff --git a/lencod/inc/get_block_otf.h b/lencod/inc/get_block_otf.h new file mode 100644 index 0000000..e6a24ab --- /dev/null +++ b/lencod/inc/get_block_otf.h @@ -0,0 +1,95 @@ +/*! + ************************************************************************************* + * \file get_block_otf.h + * + * \brief + * get predicted (chroma|luma) block with subpel interpolation computes on-the-fly + * + * + ************************************************************************************* + */ + + +#ifndef _GET_BLOCK_OTF_H_ +#define _GET_BLOCK_OTF_H_ + +void get_block_luma_otf_L2( VideoParameters *p_Vid, //!< video encoding parameters for current picture + imgpel* mpred, //!< array of prediction values (row by row) + int* tmp_pred, //!< array of temporary prediction values (row by row), used for some hal-pel interpolations + int pic_pix_x, //!< motion shifted horizontal coordinate of block + int pic_pix_y, //!< motion shifted vertical coordinate of block + int block_size_x, //!< horizontal block size + int block_size_y, //!< vertical block size + StorablePicture *ref, //!< reference picture list + int pl //!< plane + ); + +void get_block_luma_otf_L1( VideoParameters *p_Vid, //!< video encoding parameters for current picture + imgpel* mpred, //!< array of prediction values (row by row) + int* tmp_pred, //!< array of temporary prediction values (row by row), used for some hal-pel interpolations + int pic_pix_x, //!< motion shifted horizontal coordinate of block + int pic_pix_y, //!< motion shifted vertical coordinate of block + int block_size_x, //!< horizontal block size + int block_size_y, //!< vertical block size + StorablePicture *ref, //!< reference picture list + int pl //!< plane + ) ; + + +void get_block_chroma_otf_L2 ( VideoParameters *p_Vid, //!< video encoding parameters for current picture + imgpel* mpred, //!< array to store prediction values + int* tmp_pred, + int pic_pix_x, //!< motion shifted horizontal coordinate of block + int pic_pix_y, //!< motion shifted vertical block + int block_size_x, //!< horizontal block size + int block_size_y, //!< vertical block size + StorablePicture *ref, //!< reference picture list + int uv //!< chroma component + ) ; + +void me_get_block_chroma_otf_L1 ( VideoParameters *p_Vid, //!< video encoding parameters for current picture + imgpel* mpred, //!< array to store prediction values + int* tmp_pred, + int pic_pix_x, //!< motion shifted horizontal coordinate of block + int pic_pix_y, //!< motion shifted vertical block + int block_size_x, //!< horizontal block size + int block_size_y, //!< vertical block size + StorablePicture *ref, //!< reference picture list + int uv //!< chroma component + ) ; + +void mc_get_block_chroma_otf_L1 ( VideoParameters *p_Vid, //!< video encoding parameters for current picture + imgpel* mpred, //!< array to store prediction values + int* tmp_pred, + int pic_pix_x, //!< motion shifted horizontal coordinate of block + int pic_pix_y, //!< motion shifted vertical block + int block_size_x, //!< horizontal block size + int block_size_y, //!< vertical block size + StorablePicture *ref, //!< reference picture list + int uv //!< chroma component + ) ; + + + + +//static inline int is_pel( int x, int y, int bx, int by ) +//{ +// return ( (((x & bx)==0) && ((y & by)==0)) ? (1):(0) ); +//} +// +//static inline int is_hpel( int x, int y, int bx, int by ) +//{ +// return ( ((( (x & bx)==2)&&((y & by)==2)) || (((x & bx)==2)&&((y & by)==0)) || (((x & bx)==0)&&((y & by)==2)) ) ? (1):(0) ); +//} +// +//static inline int is_qpel( int x, int y, int bx, int by ) +//{ +// return ( !is_pel(x,y,bx,by) && !is_hpel(x,y,bx,by) ); +//} + +static inline int is_qpel( int x, int y, int bx, int by ) +{ + return ( ((x&bx)%2) || ((y&by)%2) ) ? (1):(0) ; +} + +#endif \ No newline at end of file diff --git a/lencod/inc/global.h b/lencod/inc/global.h new file mode 100644 index 0000000..c1228bc --- /dev/null +++ b/lencod/inc/global.h @@ -0,0 +1,1635 @@ +/*! + ************************************************************************ + * \file + * global.h + * + * \brief + * global definitions for H.264 encoder. + * + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * + ************************************************************************ + */ +#ifndef _GLOBAL_H_ +#define _GLOBAL_H_ + +#include "defines.h" +#include "win32.h" +#include "typedefs.h" +#include "parsetcommon.h" +#include "ifunctions.h" +#include "frame.h" +#include "io_video.h" +#include "io_image.h" +#include "nalucommon.h" +#include "params.h" +#include "distortion.h" +#include "lagrangian.h" +#include "quant_params.h" +#include "rc_types.h" + +typedef struct bit_stream_enc Bitstream; + +#include "pred_struct_types.h" + + +/*********************************************************************** + * D a t a t y p e s f o r C A B A C + *********************************************************************** + */ +typedef struct inp_par_enc InputParameters; + +// structures that will be declared somewhere else +struct storable_picture; +struct coding_state; +struct pic_motion_params_old; +struct pic_motion_params; + +typedef struct image_structure ImageStructure; +typedef struct info_8x8 Info8x8; +typedef struct search_window SearchWindow; +typedef struct block_pos BlockPos; +typedef struct bit_counter BitCounter; +typedef struct encoding_environment EncodingEnvironment; +typedef EncodingEnvironment *EncodingEnvironmentPtr; +typedef struct bi_context_type BiContextType; +typedef BiContextType *BiContextTypePtr; + +struct image_structure +{ + FrameFormat format; //!< ImageStructure format Information + imgpel **data[3]; //!< ImageStructure pixel data +}; + +//! block 8x8 temporary RD info +struct info_8x8 +{ + char mode; + char pdir; + char ref[2]; + char bipred; +}; + +struct search_window { + int min_x; + int max_x; + int min_y; + int max_y; +}; + +struct block_pos +{ + short x; + short y; +}; + +//! bit counter for a macroblock. Note that it seems safe to change all to unsigned short for 16x16 MBs. May be an issue if MB > 32x32 +struct bit_counter +{ + int mb_total; + unsigned short mb_mode; + unsigned short mb_inter; + unsigned short mb_cbp; + unsigned short mb_delta_quant; + int mb_y_coeff; + int mb_uv_coeff; + int mb_cb_coeff; + int mb_cr_coeff; + int mb_stuffing; +}; + +//! struct to characterize the state of the arithmetic coding engine +struct encoding_environment +{ + struct video_par *p_Vid; + unsigned int Elow, Erange; + unsigned int Ebuffer; + unsigned int Ebits_to_go; + unsigned int Echunks_outstanding; + int Epbuf; + byte *Ecodestrm; + int *Ecodestrm_len; + int C; + int E; +}; + +//! struct for context management +struct bi_context_type +{ + unsigned long count; + byte state; //uint16 state; // index into state-table CP + unsigned char MPS; // Least Probable Symbol 0/1 CP +}; + + + +/********************************************************************** + * C O N T E X T S F O R T M L S Y N T A X E L E M E N T S + ********************************************************************** + */ + +typedef struct +{ + BiContextType mb_type_contexts [3][NUM_MB_TYPE_CTX]; + BiContextType b8_type_contexts [2][NUM_B8_TYPE_CTX]; + BiContextType mv_res_contexts [2][NUM_MV_RES_CTX]; + BiContextType ref_no_contexts [2][NUM_REF_NO_CTX]; +} MotionInfoContexts; + +typedef struct +{ + BiContextType transform_size_contexts [NUM_TRANSFORM_SIZE_CTX]; + BiContextType ipr_contexts [NUM_IPR_CTX]; + BiContextType cipr_contexts[NUM_CIPR_CTX]; + BiContextType cbp_contexts [3][NUM_CBP_CTX]; + BiContextType bcbp_contexts[NUM_BLOCK_TYPES][NUM_BCBP_CTX]; + BiContextType delta_qp_contexts [NUM_DELTA_QP_CTX]; + BiContextType one_contexts [NUM_BLOCK_TYPES][NUM_ONE_CTX]; + BiContextType abs_contexts [NUM_BLOCK_TYPES][NUM_ABS_CTX]; +#if ENABLE_FIELD_CTX + BiContextType mb_aff_contexts [NUM_MB_AFF_CTX]; + BiContextType map_contexts [2][NUM_BLOCK_TYPES][NUM_MAP_CTX]; + BiContextType last_contexts[2][NUM_BLOCK_TYPES][NUM_LAST_CTX]; +#else + BiContextType map_contexts [1][NUM_BLOCK_TYPES][NUM_MAP_CTX]; + BiContextType last_contexts[1][NUM_BLOCK_TYPES][NUM_LAST_CTX]; +#endif +} TextureInfoContexts; + +//*********************** end of data type definition for CABAC ******************* + +//! Pixel position for checking neighbors +typedef struct pix_pos +{ + int available; + int mb_addr; + short x; + short y; + short pos_x; + short pos_y; +} PixelPos; + +//! Motion Vector structure +typedef struct +{ + short mv_x; + short mv_y; +} MotionVector; + +//! Bipred Motion Vector structure +typedef struct +{ + short refIdx[2]; + MotionVector mv[2]; + distblk dist; + distblk sad; +} BiPredMVInfo; + +static const MotionVector zero_mv = {0, 0}; + +typedef struct rd_8x8_data +{ + distblk mb_p8x8_cost; + distblk smb_p8x8_cost[4]; + distblk smb_p8x8_rdcost[4]; + Info8x8 part[4]; + + int cbp8x8; + int cbp_blk8x8; + int cnt_nonz_8x8; + + int **lrec; // transform and quantized coefficients will be stored here for SP frames + imgpel **mpr8x8; + imgpel ***mpr8x8CbCr; + imgpel **rec_mbY8x8; + imgpel ***rec_mb8x8_cr; +} RD_8x8DATA; + +typedef struct rd_params +{ + double lambda_md; //!< Mode decision Lambda +//#if JCOST_CALC_SCALEUP + int lambda_mdfp; //!< Fixed point mode decision lambda; +//#endif + double lambda_me[3]; //!< Motion Estimation Lambda + int lambda_mf[3]; //!< Integer formatted Motion Estimation Lambda + int best_mcost[2]; + + short valid[MAXMODE]; + short curr_mb_field; +} RD_PARAMS; + + +//! Set Explicit GOP Parameters. +//! Currently only supports Enhancement GOP but could be easily extended +typedef struct +{ + int slice_type; //! Slice type + int display_no; //! GOP Display order + int reference_idc; //! Is reference? + int slice_qp_off; //! Assigned QP offset + int hierarchy_layer; //! Hierarchy layer (used with GOP Hierarchy option 2) + int hierarchyPocDelta; //! Currently unused + int temporal_layer; //! Temporal layer; + // This information is used for hierarchical coding with temporal scalability feature + // If ReferenceReorder = 2, then + // reference reordering is applied based on POC and then frames of higher temporal layer are prevented from being referenced + // If PocMemoryManagement = 2, then + // after encoding a reference frame with temporal layer T, a list of MMCO commands to delete higher temporal layer frames is created + +} GOP_DATA; + +//! Buffer structure for decoded reference picture marking commands +typedef struct DecRefPicMarking_s +{ + int memory_management_control_operation; + int difference_of_pic_nums_minus1; + int long_term_pic_num; + int long_term_frame_idx; + int max_long_term_frame_idx_plus1; + struct DecRefPicMarking_s *Next; +} DecRefPicMarking_t; + +typedef struct me_block +{ + struct video_par *p_Vid; + + short pos_x_padded; //!< position x in image + short pos_y_padded; //!< position y in image + + short blocktype; + short blocksize_x; //!< block size x dimension + short blocksize_y; //!< block size y dimension + short blocksize_cr_x; //!< block size x dimension + short blocksize_cr_y; //!< block size y dimension + + short block_x; //!< position x in MB (subpartitions) + short block_y; //!< position y in MB + + short pos_x; //!< position x in image + short pos_y; //!< position y in image + short pos_x2; //!< position x >> 2 in image + short pos_y2; //!< position y >> 2 in image + + short pos_cr_x; //!< position x in image + short pos_cr_y; //!< position y in image + + short hme_level; //!< level for HME search; + int hme_ref_size_y_pad; + int hme_ref_size_x_pad; + int hme_ref_size_y_max; + int hme_ref_size_x_max; + char list; //!< current list (list 0 or list 1). This is needed for bipredictive ME + char ref_idx; //!< current reference index + MotionVector mv[2]; //!< motion vectors (L0/L1) + PixelPos block[4]; + struct slice *p_Slice; + struct search_window searchRange; + int cost; //!< Rate Distortion cost + imgpel **orig_pic; //!< Block Data + int ChromaMEEnable; + int ChromaMEWeight; + // use weighted prediction based ME + int apply_weights; + int apply_bi_weights; + // 8x8 transform + int test8x8; + // Single List Prediction weights + short weight_luma; + short weight_cr[2]; + short offset_luma; + short offset_cr[2]; + + // Biprediction weights + short weight1; + short weight2; + short offsetBi; + short weight1_cr[2]; + short weight2_cr[2]; + short offsetBi_cr[2]; + int search_pos2; // <-- search positions for half-pel search (default: 9) + int search_pos4; // <-- search positions for quarter-pel search (default: 9) + int iteration_no; // <-- bi pred iteration number + + // functions + distblk (*computePredFPel) (struct storable_picture *, struct me_block *, distblk , MotionVector * ); + distblk (*computePredHPel) (struct storable_picture *, struct me_block *, distblk , MotionVector * ); + distblk (*computePredQPel) (struct storable_picture *, struct me_block *, distblk , MotionVector * ); + distblk (*computeBiPredFPel) (struct storable_picture *, struct storable_picture *, struct me_block *, distblk , MotionVector *, MotionVector *); + distblk (*computeBiPredHPel) (struct storable_picture *, struct storable_picture *, struct me_block *, distblk , MotionVector *, MotionVector *); + distblk (*computeBiPredQPel) (struct storable_picture *, struct storable_picture *, struct me_block *, distblk , MotionVector *, MotionVector *); +} MEBlock; + +//! Syntax Element +typedef struct syntaxelement_enc +{ + int type; //!< type of syntax element for data part. + int value1; //!< numerical value of syntax element + int value2; //!< for blocked symbols, e.g. run/level + int len; //!< length of code + int inf; //!< info part of UVLC code + unsigned int bitpattern; //!< UVLC bitpattern + int context; //!< CABAC context + +#if TRACE + #define TRACESTRING_SIZE 100 //!< size of trace string + char tracestring[TRACESTRING_SIZE]; //!< trace string +#endif + + //!< for mapping of syntaxElement to UVLC + void (*mapping)(int value1, int value2, int* len_ptr, int* info_ptr); + +} SyntaxElement; + +typedef struct wp_params +{ + short weight[3]; + short offset[3]; +} WPParams; + +//! Macroblock +typedef struct macroblock_enc +{ + struct slice *p_Slice; //!< pointer to the current slice + struct video_par *p_Vid; //!< pointer to VideoParameters + InputParameters *p_Inp; + int mbAddrX; //!< current MB address + short mb_type; //!< current MB mode type + short slice_nr; //!< current MB slice id + short mb_x; //!< current MB horizontal + short mb_y; //!< current MB vertical + short block_x; //!< current block horizontal + short block_y; //!< current block vertical + + short pix_x; //!< current pixel horizontal + short pix_y; //!< current pixel vertical + short pix_c_x; //!< current pixel chroma horizontal + short pix_c_y; //!< current pixel chroma vertical + + short opix_y; //!< current original picture pixel vertical + short opix_c_y; //!< current original picture pixel chroma vertical + + short subblock_x; //!< current subblock horizontal + short subblock_y; //!< current subblock vertical + + short list_offset; + Boolean prev_recode_mb; + Boolean DeblockCall; + + int mbAddrA, mbAddrB, mbAddrC, mbAddrD; + byte mbAvailA, mbAvailB, mbAvailC, mbAvailD; + + short qp; //!< QP luma + short qpc[2]; //!< QP chroma + short qp_scaled[MAX_PLANE]; //!< QP scaled for all comps. + short qpsp; + int cbp; + short prev_qp; + short prev_dqp; + int prev_cbp; + int cr_cbp[3]; // 444. Should be added in an external structure + int c_nzCbCr[3]; + + short i16offset; + + BitCounter bits; + + short ar_mode; //!< mb type to store adaptive rounding parameters + short mvd[2][BLOCK_MULTIPLE][BLOCK_MULTIPLE][2]; //!< indices correspond to [list][block_y][block_x][x,y] + char c_ipred_mode; //!< chroma intra prediction mode + char i16mode; + + Info8x8 b8x8[4]; + //imgpel intra4x4_pred[16][17]; //!< 4x4 Intra prediction samples + imgpel **intra4x4_pred; //[3][17]; //!< 4x4 Intra prediction samples + imgpel **intra4x4_pred_buf[2]; //[3][17]; //!< 4x4 Intra prediction samples + imgpel **intra8x8_pred; //[3][25]; //!< 8x8 Intra prediction samples + imgpel **intra8x8_pred_buf[2]; //[3][25]; //!< 8x8 Intra prediction samples + imgpel **intra16x16_pred; //[3][33]; //!< 8x8 Intra prediction samples + imgpel **intra16x16_pred_buf[2]; //[3][33]; //!< 8x8 Intra prediction samples + + char IntraChromaPredModeFlag; + byte mb_field; + byte is_field_mode; + byte luma_transform_size_8x8_flag; + byte temp_transform_size_8x8_flag; + byte NoMbPartLessThan8x8Flag; + byte valid_8x8; + byte valid_4x4; + byte write_mb; + byte is_intra_block; + + char DFDisableIdc; + char DFAlphaC0Offset; + char DFBetaOffset; + + int skip_flag; + + char intra_pred_modes [MB_BLOCK_PARTITIONS]; + char intra_pred_modes8x8[MB_BLOCK_PARTITIONS]; //!< four 8x8 blocks in a macroblock + + int64 cbp_blk ; //!< 1 bit set for every 4x4 block with coefs (not implemented for INTRA) + int64 cbp_bits[3]; + int64 cbp_bits_8x8[3]; + + distblk min_rdcost; + distblk min_dcost; + distblk min_rate; + int min_bits; + + short best_mode; + short best_i16offset; + char best_i16mode; + char best_c_imode; + int best_cbp; + + //For residual DPCM + short ipmode_DPCM; + + + + struct macroblock_enc *mb_up; //!< pointer to neighboring MB (CABAC) + struct macroblock_enc *mb_left; //!< pointer to neighboring MB (CABAC) + struct macroblock_enc *PrevMB; + + // Functions + void (*GetMVPredictor) (struct macroblock_enc *currMB, PixelPos *block, MotionVector *pmv, short ref_frame, + struct pic_motion_params **mv_info, int list, int mb_x, int mb_y, int blockshape_x, int blockshape_y); + distblk (*IntPelME) (struct macroblock_enc *currMB, MotionVector *, struct me_block *mv_block, distblk, int); + distblk (*SubPelBiPredME) (struct macroblock_enc *currMB, struct me_block *, int list, + MotionVector *pred_mv1, MotionVector *pred_mv2, MotionVector *mv1, MotionVector *mv2, distblk min_mcost, int* lambda_factor); + distblk (*SubPelME) (struct macroblock_enc *currMB, + MotionVector *pred_mv, struct me_block *mv_block, distblk min_mcost, int* lambda_factor); + distblk (*BiPredME) (struct macroblock_enc *currMB, int, MotionVector *, MotionVector *, MotionVector *, MotionVector *, struct me_block *, int, distblk, int); + int (*residual_transform_quant_luma_4x4) (struct macroblock_enc *currMB, ColorPlane pl, int block_x, int block_y, int *coeff_cost, int intra); + int (*residual_transform_quant_luma_16x16) (struct macroblock_enc *currMB, ColorPlane pl); + int (*residual_transform_quant_luma_8x8) (struct macroblock_enc *currMB, ColorPlane pl, int b8, int *coeff_cost, int intra); + int (*residual_transform_quant_chroma_4x4[2])(struct macroblock_enc *currMB, int uv,int i11); + void (*p_SetupFastFullPelSearch) (struct macroblock_enc *, MEBlock *, int ) ; + + void (*cbp_linfo_intra)(int cbp, int dummy, int *len,int *info); + void (*cbp_linfo_inter)(int cbp, int dummy, int *len,int *info); +} Macroblock; + +//! Bitstream +struct bit_stream_enc +{ + int buffer_size; //!< Buffer size + int byte_pos; //!< current position in bitstream; + int bits_to_go; //!< current bitcounter + + int stored_byte_pos; //!< storage for position in bitstream; + int stored_bits_to_go; //!< storage for bitcounter + int byte_pos_skip; //!< storage for position in bitstream; + int bits_to_go_skip; //!< storage for bitcounter + int write_flag; //!< Bitstream contains data and needs to be written + + byte byte_buf; //!< current buffer for last written byte + byte stored_byte_buf; //!< storage for buffer of last written byte + byte byte_buf_skip; //!< current buffer for last written byte + byte *streamBuffer; //!< actual buffer for written bytes + +#if TRACE + Boolean trace_enabled; +#endif +}; + + +//! DataPartition +typedef struct datapartition_enc +{ + struct slice *p_Slice; + struct video_par *p_Vid; + InputParameters *p_Inp; //!< pointer to the input parameters + Bitstream *bitstream; + NALU_t *nal_unit; + EncodingEnvironment ee_cabac; + EncodingEnvironment ee_recode; +} DataPartition; + +/*! For MB level field/frame coding tools + temporary structure to store MB data for field/frame coding */ +typedef struct rd_data +{ + double min_rdcost; + double min_dcost; + double min_rate; + + imgpel ***rec_mb; //!< hold the components of reconstructed MB + int ****cofAC; + int ***cofDC; + int *****cofAC_new; + short mb_type; + int64 cbp_blk; + int cbp; + int prev_cbp; + int mode; + short i16offset; + char i16mode; + Boolean NoMbPartLessThan8x8Flag; + + char c_ipred_mode; + short qp; + short prev_qp; + short prev_dqp; + byte luma_transform_size_8x8_flag; + Info8x8 block; + Info8x8 b8x8[4]; + + MotionVector *****all_mv; //!< all modes motion vectors + MotionVector ******bipred_mv; //!< Bipredictive motion vectors + + char intra_pred_modes[16]; + char intra_pred_modes8x8[16]; + char **ipredmode; + char ***refar; //!< reference frame array [list][y][x] +} RD_DATA; + + //! Slice +typedef struct slice +{ + struct video_par *p_Vid; // pointer to the original video structure + InputParameters *p_Inp; // pointer to the input parameters + pic_parameter_set_rbsp_t *active_pps; + seq_parameter_set_rbsp_t *active_sps; + + int flag_Force_InterView; + + struct decoded_picture_buffer *p_Dpb; + + int layer_id; + int picture_id; + int qp; + int qs; + short slice_type; //!< picture type + unsigned int frame_num; + unsigned int max_frame_num; + signed int framepoc; //!< min (toppoc, bottompoc) + signed int ThisPOC; //!< current picture POC + short slice_nr; + int model_number; + int colour_plane_id; //!< colour plane id for 4:4:4 profile + int lossless_qpprime_flag; + int P444_joined; + int disthres; + int Transform8x8Mode; + int rdoq_motion_copy; + // RDOQ at the slice level (note this allows us to reduce passed parameters, + // but also could enable RDOQ control at the slice level. + int UseRDOQuant; + int RDOQ_QP_Num; + + char num_ref_idx_active[2]; + int ref_pic_list_reordering_flag[2]; + int *modification_of_pic_nums_idc[2]; + int *abs_diff_pic_num_minus1[2]; + int *long_term_pic_idx[2]; +#if (MVC_EXTENSION_ENABLE) + int *abs_diff_view_idx_minus1[2]; +#endif + int view_id; + int width_blk; //!< Number of columns in blocks + int height_blk; //!< Number of lines in blocks + + PictureStructure structure; + Boolean mb_aff_frame_flag; + + int start_mb_nr; + int max_part_nr; //!< number of different partitions + int num_mb; //!< number of MBs in the slice + + int cmp_cbp[3]; + int curr_cbp[2]; + char symbol_mode; + short NoResidueDirect; + short partition_mode; + short idr_flag; + int frame_no; + unsigned int PicSizeInMbs; + int num_blk8x8_uv; + int nal_reference_idc; //!< nal_reference_idc from NAL unit + + // Bit depth related information + short bitdepth_luma; + short bitdepth_chroma; + int bitdepth_scale[2]; + int bitdepth_luma_qp_scale; + int bitdepth_chroma_qp_scale; + int bitdepth_lambda_scale; + + DataPartition *partArr; //!< array of partitions + MotionInfoContexts *mot_ctx; //!< pointer to struct of context models for use in CABAC + TextureInfoContexts *tex_ctx; //!< pointer to struct of context models for use in CABAC + + int mvscale[6][MAX_REFERENCE_PICTURES]; + char direct_spatial_mv_pred_flag; //!< Direct Mode type to be used (0: Temporal, 1: Spatial) + // Deblocking filter parameters + char DFDisableIdc; //!< Deblocking Filter Disable indicator + char DFAlphaC0Offset; //!< Deblocking Filter Alpha offset + char DFBetaOffset; //!< Deblocking Filter Beta offset + + byte weighted_prediction; //!< Use of weighted prediction + byte weighted_bipred_idc; //!< Use of weighted biprediction (note that weighted_pred_flag is probably not needed here) + + short luma_log_weight_denom; + short chroma_log_weight_denom; + short wp_luma_round; + short wp_chroma_round; + + short max_num_references; //!< maximum number of reference pictures that may occur + // Motion vectors for a macroblock + // These need to be changed to MotionVector parameters + MotionVector *****all_mv; //!< replaces local all_mv + MotionVector ******bipred_mv; //!< Biprediction MVs + //Weighted prediction + short ***wp_weight; //!< weight in [list][index][component] order + short ***wp_offset; //!< offset in [list][index][component] order + short ****wbp_weight; //!< weight in [list][fwd_index][bwd_idx][component] order + + int *****cofAC_new; //!< AC coefficients [comp][8x8block][4x4block][level/run][scan_pos] + + int ****cofAC; //!< AC coefficients [8x8block][4x4block][level/run][scan_pos] + int *** cofDC; //!< DC coefficients [yuv][level/run][scan_pos] + + // For rate control + int diffy[16][16]; + + int64 cur_cbp_blk[MAX_PLANE]; + int coeff_cost_cr[MAX_PLANE]; + + + int **tblk16x16; //!< Transform related array + int **tblk4x4; //!< Transform related array + int ****i16blk4x4; + + RD_DATA *rddata; + // RD_DATA data. Moved here to enable parallelization at the slice level + // of RDOQ + RD_DATA rddata_trellis_best; + RD_DATA rddata_trellis_curr; + //!< For MB level field/frame coding tools + RD_DATA rddata_top_frame_mb; + RD_DATA rddata_bot_frame_mb; + RD_DATA rddata_top_field_mb; + RD_DATA rddata_bot_field_mb; + + Boolean si_frame_indicator; + Boolean sp2_frame_indicator; + + char ***direct_ref_idx; //!< direct mode reference index buffer + char **direct_pdir; //!< direct mode direction buffer + + MotionVector ****tmp_mv8; + MotionVector ****tmp_mv4; + + distblk ***motion_cost8; + distblk ***motion_cost4; + int deltaQPTable[9]; + + // RDOQ + struct est_bits_cabac *estBitsCabac; // [NUM_BLOCK_TYPES] + double norm_factor_4x4; + double norm_factor_8x8; + int norm_shift_4x4; + int norm_shift_8x8; + + imgpel ****mpr_4x4; //!< prediction samples for 4x4 intra prediction modes + imgpel ****mpr_8x8; //!< prediction samples for 8x8 intra prediction modes + imgpel ****mpr_16x16; //!< prediction samples for 16x16 intra prediction modes (and chroma) + imgpel ***mb_pred; //!< current best prediction mode + int ***mb_rres; //!< the diff pixel values between the original macroblock/block and its prediction (reconstructed) + int ***mb_ores; //!< the diff pixel values between the original macroblock/block and its prediction (original) + + // Residue Color Transform + char b8_ipredmode8x8[4][4]; + char b8_intra_pred_modes8x8[16]; + char b4_ipredmode[16]; + char b4_intra_pred_modes[16]; + + struct rdo_structure *p_RDO; + struct epzs_params *p_EPZS; + + // This should be the right location for this + struct storable_picture **listX[6]; + char listXsize[6]; + + // Some Cabac related parameters (could be put in a different structure so we can dynamically allocate them when needed) + int coeff[64]; + int coeff_ctr; + int pos; + + int default_pic_num[2][MAX_REFERENCE_PICTURES]; + int default_view_id[2][MAX_REFERENCE_PICTURES]; + + // Function pointers + int (*mode_decision_for_I16x16_MB) (Macroblock *currMB, int lambda); + int (*mode_decision_for_I4x4_blocks) (Macroblock *currMB, int b8, int b4, int lambda, distblk* min_cost); + int (*mode_decision_for_I8x8_blocks) (Macroblock *currMB, int b8, int lambda, distblk *min_cost); + distblk (*rdcost_for_4x4_intra_blocks) (Macroblock *currMB, int *c_nz, int b8, int b4, int ipmode, int lambda, int mostProbableMode, distblk min_rdcost); + distblk (*rdcost_for_8x8_intra_blocks) (Macroblock *currMB, int *c_nz, int b8, int ipmode, int lambda, distblk min_rdcost, int mostProbableMode); + void (*submacroblock_mode_decision) (Macroblock *currMB, RD_PARAMS *, RD_8x8DATA *, int ****, int, distblk *); + void (*intra_chroma_RD_decision) (Macroblock *currMB, RD_PARAMS *); + + void (*set_intrapred_4x4) (Macroblock *currMB, ColorPlane pl, int img_x,int img_y, int *left_available, int *up_available, int *all_available); + void (*set_intrapred_8x8) (Macroblock *currMB, ColorPlane pl, int img_x,int img_y, int *left_available, int *up_available, int *all_available); + void (*set_intrapred_16x16) (Macroblock *currMB, ColorPlane pl, int *left_available, int *up_available, int *all_available); + void (*intra_chroma_prediction) (Macroblock *currMB, int*, int*, int*); + void (*rdo_low_intra_chroma_decision) (Macroblock *currMB, int mb_available_up, int mb_available_left[2], int mb_available_up_left); + + void (*Get_Direct_Motion_Vectors) (Macroblock *currMB); + Boolean (*slice_too_big) (int bits_slice); //!< for use of callback functions + void (*set_motion_vectors_mb) (Macroblock *currMB); + void (*encode_one_macroblock) (Macroblock *currMB); + void (*set_stored_mb_parameters) (Macroblock *currMB); + void (*set_ref_and_motion_vectors) (Macroblock *currMB, struct pic_motion_params **motion, Info8x8 *part, int block); + + void (*luma_residual_coding) (Macroblock *currMB); + int (*luma_residual_coding_8x8) (Macroblock* currMB, int*, int64*, int, short, int[2], char *); + void (*chroma_residual_coding) (Macroblock *currMB); + + distblk (*compute_cost8x8) (struct video_par *p_Vid, imgpel **cur_img, imgpel **prd_img, int pic_opix_x, distblk min_cost); + distblk (*compute_cost4x4) (struct video_par *p_Vid, imgpel **cur_img, imgpel **prd_img, int pic_opix_x, distblk min_cost); + distblk (*find_sad_16x16) (Macroblock *currMB); + distblk (*distI16x16) (Macroblock *currMB, imgpel **img_org, imgpel **pred_img, distblk min_cost); + + void (*set_lagrangian_multipliers) (struct slice *currSlice); + + void (*set_modes_and_refs_for_blocks) (Macroblock *currMB, short mode); + void (*set_coeff_and_recon_8x8 ) (Macroblock *currMB); + // Write MB info function pointers + void (*writeMB_typeInfo) (Macroblock *currMB, SyntaxElement *se, DataPartition *dP); + void (*writeMB_Skip) (Macroblock *currMB, SyntaxElement *se, DataPartition *dP); + void (*writeIntraPredMode) (SyntaxElement *se, DataPartition *dP); + void (*writeB8_typeInfo) (SyntaxElement *se, DataPartition *dP); + void (*writeRefFrame[6]) (Macroblock* currMB, SyntaxElement *se, DataPartition *dP); + int (*writeMotionInfo2NAL) (Macroblock* currMB); + int (*write_MB_layer) (Macroblock *currMB, int rdopt, int *coeff_rate); + void (*writeMVD) (Macroblock *currMB, SyntaxElement *se, DataPartition *dP); + void (*writeCBP) (Macroblock* currMB, SyntaxElement *se, DataPartition *dP); + void (*writeDquant) (Macroblock* currMB, SyntaxElement *se, DataPartition *dP); + void (*writeCIPredMode) (Macroblock *currMB, SyntaxElement *se, DataPartition *dP); + void (*writeFieldModeInfo) (Macroblock *currMB, SyntaxElement *se, DataPartition *dP); + void (*writeMB_transform_size)(Macroblock *currMB, SyntaxElement *se, DataPartition *dP); + int (*writeCoeff16x16) (Macroblock* currMB, ColorPlane); + int (*writeCoeff4x4_CAVLC) (Macroblock* currMB, int block_type, int b8, int b4, int param); + void (*write_and_store_CBP_block_bit) (Macroblock* currMB, EncodingEnvironmentPtr eep_dp, int type, int cbp_bit, TextureInfoContexts* tex_ctx); + + // Coding state + void (*reset_coding_state) (Macroblock *currMB, struct coding_state *); + void (*store_coding_state) (Macroblock *currMB, struct coding_state *); + + // List initialization / reordering + void (*init_lists ) (struct slice *currSlice); + void (*reorder_lists ) (struct slice *currSlice); + void (*poc_ref_pic_reorder_frame) (struct slice *currSlice, unsigned num_ref_idx_lX_active, int list_no ); + + + // Quantization + int (*quant_4x4) (Macroblock *currMB, int **tblock, struct quant_methods *q_method); + int (*quant_ac4x4cr) (Macroblock *currMB, int **tblock, struct quant_methods *q_method); + int (*quant_dc4x4) (Macroblock *currMB, int **tblock, int qp, int* DCLevel, int* DCRun, LevelQuantParams *q_params_4x4, const byte (*pos_scan)[2]); + int (*quant_ac4x4) (Macroblock *currMB, int **tblock, struct quant_methods *q_method); + int (*quant_8x8) (Macroblock *currMB, int **tblock, struct quant_methods *q_method); + int (*quant_8x8cavlc)(Macroblock *currMB, int **tblock, struct quant_methods *q_method, int*** cofAC); + + int (*quant_dc_cr) (Macroblock *currMB, int **tblock, int qp, int* DCLevel, int* DCRun, + LevelQuantParams *q_params_4x4, int **fadjust, const byte (*pos_scan)[2]); + void (*rdoq_4x4) (Macroblock *currMB, int **tblock, struct quant_methods *q_method, int levelTrellis[16]); + + void (*rdoq_dc) (Macroblock *currMB, int **tblock, int qp_per, int qp_rem, LevelQuantParams *q_params_4x4, + const byte (*pos_scan)[2], int levelTrellis[16], int type); + + void (*rdoq_ac4x4) (Macroblock *currMB, int **tblock , struct quant_methods *q_method, int levelTrellis[16]); + + void (*rdoq_dc_cr) (Macroblock *currMB, int **tblock, int qp_per, int qp_rem, LevelQuantParams *q_params_4x4, + const byte (*pos_scan)[2], int levelTrellis[16], int type); + + void (*set_modes_and_reframe) (Macroblock *currMB, int b8, short* p_dir, int list_mode[2], char *list_ref_idx); + void (*store_8x8_motion_vectors) (struct slice *currSlice, int dir, int block8x8, Info8x8 *B8x8Info); + distblk (*getDistortion) ( Macroblock *currMB ); + +} Slice; + +#if (MVC_EXTENSION_ENABLE) +typedef struct prev_coding_stats +{ + int frm_no_in_file; + char pic_type[256]; + int view_id; + int cur_bits; + int wp_method; + int AverageFrameQP; + int lambda; + float psnr_value[3]; + float ssim_value[3]; + int tmp_time; + int me_time; + int fld_flag; + int intras; + int direct_mode; + int num_ref_idx_l0_active; + int num_ref_idx_l1_active; + int rd_pass; + int nal_reference_idc; + +} PrevCodingStats; +#endif + +//! DistortionParams +typedef struct distortion_metric +{ + float value[3]; //!< current frame distortion + float average[3]; //!< average frame distortion + float avslice[NUM_SLICE_TYPES][3]; //!< average frame type distortion +} DistMetric; + +typedef struct distortion_params +{ + int frame_ctr; //!< number of coded frames + DistMetric metric[TOTAL_DIST_TYPES]; //!< Distortion metrics +#if (MVC_EXTENSION_ENABLE) + int frame_ctr_v[2]; //!< number of coded frames for each view + DistMetric metric_v[2][TOTAL_DIST_TYPES]; //!< Distortion metrics for each view +#endif +} DistortionParams; + +typedef struct picture +{ + int no_slices; + int bits_per_picture; + struct slice *slices[MAXSLICEPERPICTURE]; + + DistMetric distortion; + byte idr_flag; +} Picture; + +//! block 8x8 temporary RD info +typedef struct block_8x8_info +{ + Info8x8 best[MAXMODE][4]; +} Block8x8Info; + +typedef struct coding_par +{ + int layer_id; + ColorFormat yuv_format; + int P444_joined; + + short bitdepth_luma; + short bitdepth_chroma; + int bitdepth_scale[2]; + int bitdepth_luma_qp_scale; + int bitdepth_chroma_qp_scale; + int bitdepth_lambda_scale; + + uint32 dc_pred_value_comp[MAX_PLANE]; //!< component value for DC prediction (depends on component pel bit depth) + int max_pel_value_comp [MAX_PLANE]; //!< max value that one picture element (pixel) can take (depends on pic_unit_bitdepth) + int max_imgpel_value_comp_sq [MAX_PLANE]; //!< max value that one picture element (pixel) can take (depends on pic_unit_bitdepth) + + int num_blk8x8_uv; + int num_cdc_coeff; + + short mb_cr_size_x; + short mb_cr_size_y; + int mb_size[MAX_PLANE][2]; + + int max_bitCount; + int width; //!< Number of pels + int width_padded; //!< Width in pels of padded picture + int width_blk; //!< Number of columns in blocks + int width_cr; //!< Number of pels chroma + int height; //!< Number of lines + int height_padded; //!< Number in lines of padded picture + int height_blk; //!< Number of lines in blocks + int height_cr; //!< Number of lines chroma + int height_cr_frame; //!< Number of lines chroma frame + int size; //!< Luma Picture size in pels + int size_cr; //!< Chroma Picture size in pels + + int padded_size_x; + int padded_size_x_m8x8; + int padded_size_x_m4x4; + int cr_padded_size_x; + int cr_padded_size_x_m8; + int cr_padded_size_x2; + int cr_padded_size_x4; + + int pad_size_uv_x; + int pad_size_uv_y; + unsigned char chroma_mask_mv_y; + unsigned char chroma_mask_mv_x; + int chroma_shift_x; + int chroma_shift_y; + int shift_cr_x; + int shift_cr_y; + + unsigned int PicWidthInMbs; + unsigned int PicHeightInMapUnits; + unsigned int FrameHeightInMbs; + unsigned int FrameSizeInMbs; + + unsigned int frame_num; + unsigned int prevFrameNum; + unsigned int FrameNumOffset; + unsigned int prevFrameNumOffset; + int last_ref_idc; + + //uint32 dc_pred_value; //!< DC prediction value for current component + //short max_imgpel_value; //!< max value that one picture element (pixel) can take (depends on pic_unit_bitdepth) + char md5String[2][33]; +}CodingParameters; + +//! VideoParameters +typedef struct video_par +{ + InputParameters *p_Inp; + pic_parameter_set_rbsp_t *active_pps; + seq_parameter_set_rbsp_t *active_sps; + struct sei_params *p_SEI; + struct decoders *p_decs; + CodingParameters *p_CurrEncodePar; + CodingParameters *p_EncodePar[MAX_NUM_DPB_LAYERS]; + + int number; //!< current image number to be encoded (in first layer) + int LevelIndex; //!< mapped level idc + int MaxVmvR[6]; //!< maximum vertical motion vector + int MaxHmvR[6]; //!< maximum horizontal motion vector + int current_mb_nr; + short current_slice_nr; + short type; + PictureStructure structure; //!< picture structure + int base_dist; + int num_ref_frames; //!< number of reference frames to be used + int max_num_references; //!< maximum number of reference pictures that may occur + int masterQP; //!< Master quantization parameter + int qp; //!< quant for the current frame + int qpsp; //!< quant for the prediction frame of SP-frame + + int prev_frame_no; // POC200301 + int consecutive_non_reference_pictures; // POC200301 + + // prediction structure + int frm_struct_buffer; //!< length of the frame struct buffer (this may also be a define) + int last_idr_code_order; + int last_idr_disp_order; + int last_mmco_5_code_order; //!< it is a good idea to re-initialize POCs after such a frame: while seemingly not required it is critical for good implicit WP performance (I *THINK*) since POCs are set internally to zero after an MMCO=5 command. + int last_mmco_5_disp_order; + int curr_frm_idx; //!< frame we wish to code in coding order (points also to p_frm) + int FrameNumOffset; //!< POC type 1 + int prevFrameNumOffset; //!< POC type 1 + unsigned int prevFrameNum; //!< POC type 1 + SeqStructure *p_pred; + FrameUnitStruct *p_curr_frm_struct; + PicStructure *p_curr_pic; + SliceStructure *p_curr_slice; + int pic_struct; + struct search_window searchRange; + ImageData imgData; //!< Image data to be encoded + ImageData imgData0; //!< Input Image Data + ImageData imgData1; + ImageData imgData2; + ImageData imgData3; + // Data needed for 3:2 pulldown or temporal interleaving + ImageData imgData32; //!< Image data to be encoded + ImageData imgData4; + ImageData imgData5; + ImageData imgData6; + ImageData imgRefData; + ImageData origData0; + ImageData origData1; + + ImageData imgDataOpt1; + ImageData imgDataOpt2; + + + int num_slices_wp; + int wp_parameters_set; + short ****wp_weights; // explicit WP weights + short ****wp_offsets; // explicit WP offsets + short *****wbp_weight; // implicit WP weights + + struct image_structure imgSRC; + struct image_structure imgREF; + struct image_structure imgREF0; + struct image_structure imgREF1; + struct image_structure imgRGB_src; + struct image_structure imgRGB_ref; + int **imgY_sub_tmp; //!< Y picture temporary component (Quarter pel) + imgpel **imgY_com; //!< Encoded luma images + imgpel **imgUV_com[2]; //!< Encoded croma images + + imgpel **imgY_com_buf[2]; //!< Encoded luma images + imgpel **imgUV_com_buf[2][2]; //!< Encoded croma images + + imgpel **pCurImg; //!< Reference image. Luma for other profiles, can be any component for 4:4:4 + // global picture format dependend buffers, mem allocation in image.c + imgpel **pImgOrg[MAX_PLANE]; + + imgpel **pCurImgRef; + imgpel **pImgOrgRef[MAX_PLANE]; + + Picture *p_frame_pic; + Picture **frame_pic; +#if (MVC_EXTENSION_ENABLE) + Picture **field_pic_ptr; + Picture **field_pic1; + Picture **field_pic2; +#else + Picture **field_pic; +#endif + + Picture *frame_pic_si; + + byte *MapUnitToSliceGroupMap; + + byte *buf; + byte *ibuf; + +#ifdef _LEAKYBUCKET_ + long *Bit_Buffer; + unsigned long total_frame_buffer; +#endif + + unsigned int log2_max_frame_num_minus4; + unsigned int log2_max_pic_order_cnt_lsb_minus4; + unsigned int max_frame_num; + unsigned int max_pic_order_cnt_lsb; + + int64 me_tot_time; + int64 tot_time; + int64 me_time; + + byte mixedModeEdgeFlag; + + int *RefreshPattern; + int *IntraMBs; + int WalkAround; + int NumberOfMBs; + int NumberIntraPerPicture; + + short start_me_refinement_hp; //!< if set then recheck the center position when doing half-pel motion refinement + short start_me_refinement_qp; //!< if set then recheck the center position when doing quarter-pel motion refinement + + // Motion Estimation + struct umhex_struct *p_UMHex; + struct umhex_smp_struct *p_UMHexSMP; + struct me_full_fast *p_ffast_me; + + struct search_window *p_search_window; + + // EPZS + struct epzs_struct *sdiamond; + struct epzs_struct *square; + struct epzs_struct *ediamond; + struct epzs_struct *ldiamond; + struct epzs_struct *sbdiamond; + struct epzs_struct *pmvfast; + struct epzs_struct *search_rpu_square; + struct epzs_struct *SP_M2DRPU; + struct epzs_struct *SP_M2DRPU4MM; + + // RDOQ + int precalcUnaryLevelTab[128][MAX_PREC_COEFF]; + int AdaptRndWeight; + int AdaptRndCrWeight; + + + ////////////////////////////////////////////////////////////////////////// + // B pictures + // motion vector : forward, backward, direct + byte MBPairIsField; //!< For MB level field/frame coding tools + + // Buffers for rd optimization with packet losses, Dim. Kontopodis + byte **pixel_map; //!< Shows the latest reference frame that is reliable for each pixel + byte **refresh_map; //!< Stores the new values for pixel_map + int intras; //!< Counts the intra updates in each frame. + int iInterViewMBs; + int RCMinQP; + int RCMaxQP; + + float framerate; + + int is_v_block; + int mb_y_upd; + int mb_y_intra; //!< which GOB to intra code + char **ipredmode; //!< intra prediction mode + char **ipredmode8x8; //!< help storage for 8x8 modes, inserted by YV + //fast intra prediction; + char **ipredmode4x4_line; //!< intra prediction mode + char **ipredmode8x8_line; //!< help storage for 8x8 modes, inserted by YV + + int cod_counter; //!< Current count of number of skipped macroblocks in a row + int ***nz_coeff; //!< number of coefficients per block (CAVLC) + int ***nz_coeff_buf[2]; + int pix_x; //!< current pixel horizontal + int pix_y; //!< current pixel vertical + + + imgpel min_IPCM_value; + // Cabac related parameters (we can put these in a structure) + int pic_bin_count; + + // Adaptive rounding + int ****ARCofAdj4x4; //!< Transform coefficients for 4x4 luma/chroma. + int ****ARCofAdj8x8; //!< Transform coefficients for 4x4 luma/chroma. + + ImageData tempData1, tempData2, tempData3, tempData4; + imgpel **tempImg; + + Picture *currentPicture; //!< The coded picture currently in the works (typically p_frame_pic, p_Vid->field_pic[0], or p_Vid->field_pic[1]) + struct slice *currentSlice; //!< pointer to current Slice data struct + Macroblock *mb_data; //!< array containing all MBs of a whole frame + Block8x8Info *b8x8info; //!< block 8x8 information for RDopt + + //FAST_REFPIC_DECISION + int mb_refpic_used; //=FREXT_HP || profile_idc == FREXT_CAVLC444 ); +} + + +static inline int is_MVC_profile(unsigned int profile_idc) +{ + return ( (0) +#if (MVC_EXTENSION_ENABLE) + || (profile_idc == MULTIVIEW_HIGH) || (profile_idc == STEREO_HIGH) +#endif + ); +} + + +#endif + diff --git a/lencod/inc/header.h b/lencod/inc/header.h new file mode 100644 index 0000000..bc896bb --- /dev/null +++ b/lencod/inc/header.h @@ -0,0 +1,20 @@ + +/*! + ************************************************************************************* + * \file header.h + * + * \brief + * Prototypes for header.c + ************************************************************************************* + */ + +#ifndef _HEADER_H_ +#define _HEADER_H_ + +extern int SliceHeader (Slice* currSlice); +extern int Partition_BC_Header(Slice *currSlice, int PartNo); +extern int writeERPS (SyntaxElement *sym, DataPartition *partition); +extern int dec_ref_pic_marking(Bitstream *bitstream, DecRefPicMarking_t *p_drpm, int idr_flag, int no_output_of_prior_pics_flag, int long_term_reference_flag ); + +#endif + diff --git a/lencod/inc/image.h b/lencod/inc/image.h new file mode 100644 index 0000000..7972745 --- /dev/null +++ b/lencod/inc/image.h @@ -0,0 +1,59 @@ + +/*! + ************************************************************************ + * \file image.h + * + * \brief + * headers for image processing + * + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * - Karsten Suehring + * - Inge Lille-Langoy + * - Alexis Michael Tourapis + * + ************************************************************************ + */ +#ifndef _IMAGE_H_ +#define _IMAGE_H_ + +#include "mbuffer.h" + +typedef struct coding_info { + short type; + int intras; + int sumFrameQP; + int num_ref_idx_l0; + int num_ref_idx_l1; + pic_parameter_set_rbsp_t *active_pps; +} CodingInfo; + +extern int encode_one_frame ( VideoParameters *p_Vid, InputParameters *p_Inp); +extern Boolean dummy_slice_too_big ( int bits_slice); +extern void copy_rdopt_data ( Macroblock *currMB); // For MB level field/frame coding tools +extern void UnifiedOneForthPix ( VideoParameters *p_Vid, StorablePicture *s); +// For 4:4:4 independent mode +extern void UnifiedOneForthPix_JV ( VideoParameters *p_Vid, int nplane, StorablePicture *s); +extern void frame_picture ( VideoParameters *p_Vid, Picture *frame, ImageData *imgData, int rd_pass); +extern byte get_idr_flag ( VideoParameters *p_Vid ); +extern byte get_random_access_flag( VideoParameters *p_Vid ); +extern void write_non_vcl_nalu ( VideoParameters *p_Vid); +extern void write_non_vcl_nalu_bot_fld( VideoParameters *p_Vid ); +#if (MVC_EXTENSION_ENABLE) +extern void write_non_vcl_nalu_mvc( VideoParameters *p_Vid); +#endif + +extern void frame_picture_mp ( VideoParameters *p_Vid, InputParameters *p_Inp ); +extern void restore_coding_info ( VideoParameters *p_Vid, CodingInfo *coding_info ); +extern void store_coding_info ( VideoParameters *p_Vid, CodingInfo *coding_info ); +extern void store_coding_and_rc_info( VideoParameters *p_Vid, CodingInfo *coding_info ); +extern void swap_frame_buffer ( VideoParameters *p_Vid, int a, int b ); +extern void frame_picture_mp_exit ( VideoParameters *p_Vid, CodingInfo *coding_info ); + +extern void GenerateImagePyramid(VideoParameters *p_Vid, int size_x, int size_y, imgpel ***p_hme_int_img, int offset_x, int offset_y); +extern void OutputImage(char *pcPrefix, int iFrameNo, int iLevel, imgpel **pImg, int iWidth, int iHeight, int iXOffset, int iYOffset); +extern void copy_params(VideoParameters *p_Vid, StorablePicture *enc_picture, seq_parameter_set_rbsp_t *active_sps); +extern void OtfCompatibility_copyWithPadding ( imgpel **dstImg, imgpel **srcImg, int size_x, int size_y, int padding_x, int padding_y ); // JLT + +#endif + diff --git a/lencod/inc/img_chroma.h b/lencod/inc/img_chroma.h new file mode 100644 index 0000000..8f3774d --- /dev/null +++ b/lencod/inc/img_chroma.h @@ -0,0 +1,23 @@ +/*! + *************************************************************************** + * \file + * img_chroma.h + * + * \author + * Athanasios Leontaris + * Alexis Michael Tourapis + * + * \date + * 4. October 2006 + * + * \brief + * Headerfile for luma interpolation functions + ************************************************************************** + */ + +#ifndef _IMG_CHROMA_H_ +#define _IMG_CHROMA_H_ + +extern void getSubImagesChroma( VideoParameters *p_Vid, StorablePicture *s ); + +#endif // _IMG_CHROMA_H_ diff --git a/lencod/inc/img_dist_ms_ssim.h b/lencod/inc/img_dist_ms_ssim.h new file mode 100644 index 0000000..31fc6a7 --- /dev/null +++ b/lencod/inc/img_dist_ms_ssim.h @@ -0,0 +1,25 @@ +/*! + *************************************************************************** + * \file + * img_dist_ms_ssim.h + * + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * - Woo-Shik Kim + * - Alexis Michael Tourapis + * + * \date + * 18 July 2008 + * + * \brief + * Headerfile to calculate multi-scale structural similarity (SSIM) index + ************************************************************************** + */ + +#ifndef _IMG_DIST_MS_SSIM_H_ +#define _IMG_DIST_MS_SSIM_H_ +#include "img_distortion.h" + +extern void find_ms_ssim (VideoParameters *p_Vid, InputParameters *p_Inp, ImageStructure *imgREF, ImageStructure *imgSRC, DistMetric metricMS_SSIM[3]); + +#endif diff --git a/lencod/inc/img_dist_snr.h b/lencod/inc/img_dist_snr.h new file mode 100644 index 0000000..2b89497 --- /dev/null +++ b/lencod/inc/img_dist_snr.h @@ -0,0 +1,24 @@ +/*! + *************************************************************************** + * \file + * img_dist_snr.h + * + * \author + * Woo-Shik Kim + * + * \date + * 29 May 2008 + * + * \brief + * Headerfile to calculate signal to noise ratio (SNR) + ************************************************************************** + */ + +#ifndef _IMG_DIST_SNR_H_ +#define _IMG_DIST_SNR_H_ +#include "img_distortion.h" + +extern void find_snr(VideoParameters *p_Vid, ImageStructure *imgREF, ImageStructure *imgSRC, DistMetric metricSSE[3], DistMetric metricPSNR[3]); + +#endif + diff --git a/lencod/inc/img_dist_ssim.h b/lencod/inc/img_dist_ssim.h new file mode 100644 index 0000000..52111b9 --- /dev/null +++ b/lencod/inc/img_dist_ssim.h @@ -0,0 +1,26 @@ +/*! + *************************************************************************** + * \file + * img_dist_ssim.h + * + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * - Woo-Shik Kim + * - Alexis Michael Tourapis + * + * \date + * 29 May 2008 + * + * \brief + * Headerfile to calculate structural similarity (SSIM) index + ************************************************************************** + */ + +#ifndef _IMG_DIST_SSIM_H_ +#define _IMG_DIST_SSIM_H_ +#include "img_distortion.h" + +extern void find_ssim (VideoParameters *p_Vid, InputParameters *p_Inp, ImageStructure *imgREF, ImageStructure *imgSRC, DistMetric metricSSIM[3]); + +#endif + diff --git a/lencod/inc/img_distortion.h b/lencod/inc/img_distortion.h new file mode 100644 index 0000000..6a6fbdb --- /dev/null +++ b/lencod/inc/img_distortion.h @@ -0,0 +1,27 @@ + +/*! + ************************************************************************ + * \file img_distortion.h + * + * \brief + * Distortion related definitions + * + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * - Alexis Michael Tourapis + * - Woo-Shik Kim + * + ************************************************************************ + */ + +#ifndef _IMG_DISTORTION_H_ +#define _IMG_DISTORTION_H_ + +extern void accumulate_avslice(DistMetric metric[3], int slice_type, int frames); +extern void accumulate_average(DistMetric metric[3], int frames); +extern void find_distortion (VideoParameters *p_Vid, ImageData *imgData); +extern void select_img (VideoParameters *p_Vid, ImageStructure *imgSRC, ImageStructure *imgREF, ImageData *imgData); +extern void compute_distortion(VideoParameters *p_Vid, ImageData *imgData); + +#endif + diff --git a/lencod/inc/img_luma.h b/lencod/inc/img_luma.h new file mode 100644 index 0000000..91a7d6a --- /dev/null +++ b/lencod/inc/img_luma.h @@ -0,0 +1,39 @@ +/*! + *************************************************************************** + * \file + * img_luma.h + * + * \author + * Athanasios Leontaris + * Alexis Michael Tourapis + * + * \date + * 4. October 2006 + * + * \brief + * Headerfile for luma interpolation functions + ************************************************************************** + */ + +#ifndef _IMG_LUMA_H_ +#define _IMG_LUMA_H_ + +static const int ONE_FOURTH_TAP[2][3] = +{ + {20, -5, 1}, // AVC Interpolation taps + {20,-4, 0}, // Experimental - not valid +}; + +extern void getSubImagesLuma ( VideoParameters *p_Vid, StorablePicture *s ); +/* +extern void getSubImageInteger ( StorablePicture *s, imgpel **dstImg, imgpel **srcImg); +extern void getSubImageInteger_s ( StorablePicture *s, imgpel **dstImg, imgpel **srcImg); +extern void getHorSubImageSixTap ( VideoParameters *p_Vid, StorablePicture *s, imgpel **dst_imgY, imgpel **ref_imgY); +extern void getVerSubImageSixTap ( VideoParameters *p_Vid, StorablePicture *s, imgpel **dst_imgY, imgpel **ref_imgY); +extern void getVerSubImageSixTapTmp( VideoParameters *p_Vid, StorablePicture *s, imgpel **dst_imgY); +extern void getSubImageBiLinear ( StorablePicture *s, imgpel **dstImg, imgpel **srcImgL, imgpel **srcImgR); +extern void getHorSubImageBiLinear ( StorablePicture *s, imgpel **dstImg, imgpel **srcImgL, imgpel **srcImgR); +extern void getVerSubImageBiLinear ( StorablePicture *s, imgpel **dstImg, imgpel **srcImgT, imgpel **srcImgB); +extern void getDiagSubImageBiLinear( StorablePicture *s, imgpel **dstImg, imgpel **srcImgT, imgpel **srcImgB); +*/ +#endif // _IMG_LUMA_H_ diff --git a/lencod/inc/intra16x16.h b/lencod/inc/intra16x16.h new file mode 100644 index 0000000..fb174b0 --- /dev/null +++ b/lencod/inc/intra16x16.h @@ -0,0 +1,30 @@ +/*! + *************************************************************************** + * + * \file intra16x16.h + * + * \brief + * prototypes of 16x16 intra prediction + * + * \date + * 26 September 2009 + * + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * Alexis Michael Tourapis + **************************************************************************/ + +#ifndef _INTRA16X16_H_ +#define _INTRA16X16_H_ + +extern distblk distI16x16_sad (Macroblock *currMB, imgpel **img_org, imgpel **pred_img, distblk min_cost); +extern distblk distI16x16_sse (Macroblock *currMB, imgpel **img_org, imgpel **pred_img, distblk min_cost); +extern distblk distI16x16_satd (Macroblock *currMB, imgpel **img_org, imgpel **pred_img, distblk min_cost); + +extern distblk find_sad_16x16_JM (Macroblock *currMB); + +extern void set_intrapred_16x16_mbaff (Macroblock *currMB, ColorPlane pl, int *left_available, int *up_available, int *all_available); +extern void set_intrapred_16x16 (Macroblock *currMB, ColorPlane pl, int *left_available, int *up_available, int *all_available); +extern void get_intrapred_16x16 (Macroblock *currMB, ColorPlane pl, int i16x16_mode, int left_available, int up_available); + +#endif //_INTRA16X16_H_ diff --git a/lencod/inc/intra4x4.h b/lencod/inc/intra4x4.h new file mode 100644 index 0000000..612abbf --- /dev/null +++ b/lencod/inc/intra4x4.h @@ -0,0 +1,26 @@ + +/*! + ************************************************************************ + * \file intra4x4.h + * + * \brief + * intra 4x4 functions + * + * \author + * - Alexis Michael Tourapis + * + ************************************************************************ + */ + +#ifndef _INTRA4x4_H_ +#define _INTRA4x4_H_ + +// intra 4x4 generation function +extern void set_intrapred_4x4_mbaff(Macroblock *currMB, ColorPlane pl, int img_x, int img_y, int *left_available, int *up_available, int *all_available); +extern void set_intrapred_4x4 (Macroblock *currMB, ColorPlane pl, int img_x, int img_y, int *left_available, int *up_available, int *all_available); +extern void get_intrapred_4x4 (Macroblock *currMB, ColorPlane pl, int i4x4_mode, int img_x, int img_y, int left_available, int up_available); + +// 4x4 residual generation +extern void generate_pred_error_4x4(imgpel **cur_img, imgpel **prd_img, imgpel **cur_prd, int **mb_rres, int pic_opix_x, int block_x); +#endif + diff --git a/lencod/inc/intra8x8.h b/lencod/inc/intra8x8.h new file mode 100644 index 0000000..383d04e --- /dev/null +++ b/lencod/inc/intra8x8.h @@ -0,0 +1,27 @@ +/*! + *************************************************************************** + * + * \file intra8x8.h + * + * \brief +* prototypes of 8x8 intra prediction + * + * \date + * 5. February 2009 + * + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * Alexis Michael Tourapis + **************************************************************************/ + +#ifndef _INTRA8X8_H_ +#define _INTRA8X8_H_ + +extern void set_intrapred_8x8_mbaff (Macroblock *currMB, ColorPlane pl, int img_x,int img_y, int *left_available, int *up_available, int *up_left_available); +extern void set_intrapred_8x8 (Macroblock *currMB, ColorPlane pl, int img_x,int img_y, int *left_available, int *up_available, int *up_left_available); +extern void get_intrapred_8x8 (Macroblock *currMB, ColorPlane pl, int i8x8_mode, int left_available, int up_available); +extern void LowPassForIntra8x8Pred (imgpel *PredPel, int block_up_left, int block_up, int block_left); + +extern void generate_pred_error_8x8 (imgpel **cur_img, imgpel **prd_img, imgpel **cur_prd, int **mb_rres, int pic_opix_x, int block_x); + +#endif //_TRANSFORM8X8_H_ diff --git a/lencod/inc/intrarefresh.h b/lencod/inc/intrarefresh.h new file mode 100644 index 0000000..eb6a422 --- /dev/null +++ b/lencod/inc/intrarefresh.h @@ -0,0 +1,26 @@ + +/*! + *************************************************************************** + * + * \file intrarefresh.h + * + * \brief + * Pseudo-Raqndom Intra macroblock refresh support + * + * \date + * 16 June 2002 + * + * \author + * Stephan Wenger stewe@cs.tu-berlin.de + **************************************************************************/ + +#ifndef _INTRAREFRESH_H_ +#define _INTRAREFRESH_H_ + +extern void RandomIntraInit(VideoParameters *p_Vid, int xsize, int ysize, int refresh); +extern void RandomIntraUninit(VideoParameters *p_Vid); +extern int RandomIntra (VideoParameters *p_Vid, int mb); //! returns 1 for MBs that need forced Intra +extern void RandomIntraNewPicture (VideoParameters *p_Vid); //! to be called once per picture + + +#endif //_INTRAREFRESH_H_ diff --git a/lencod/inc/lambda.h b/lencod/inc/lambda.h new file mode 100644 index 0000000..97358ee --- /dev/null +++ b/lencod/inc/lambda.h @@ -0,0 +1,31 @@ +/*! + *************************************************************************** + * \file + * lambda.h + * + * \date + * 13 September 2009 + * + * \brief + * Headerfile for lagrangian lambda related computations + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * - Alexis Michael Tourapis + + ************************************************************************** + */ + +#ifndef _LAMBDA_H_ +#define _LAMBDA_H_ + +#include "global.h" + +extern void get_implicit_lambda_p_slice (Slice *currSlice); +extern void get_implicit_lambda_b_slice (Slice *currSlice); +extern void get_implicit_lambda_i_slice (Slice *currSlice); +extern void get_implicit_lambda_sp_slice(Slice *currSlice); +extern void get_explicit_lambda (Slice *currSlice); +extern void get_fixed_lambda (Slice *currSlice); +extern void set_rdoq_lambda (Slice *currSlice); + +#endif diff --git a/lencod/inc/leaky_bucket.h b/lencod/inc/leaky_bucket.h new file mode 100644 index 0000000..60d0691 --- /dev/null +++ b/lencod/inc/leaky_bucket.h @@ -0,0 +1,29 @@ + +/*! + *************************************************************************** + * + * \file leaky_bucket.h + * + * \brief + * Header for Leaky Buffer parameters + * + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * - Shankar Regunathan + **************************************************************************/ + +#ifndef _LEAKY_BUCKET_H_ +#define _LEAKY_BUCKET_H_ + + +/* Leaky Bucket Parameter Optimization */ +#ifdef _LEAKYBUCKET_ +extern int get_LeakyBucketRate(InputParameters *p_Inp, unsigned long NumberLeakyBuckets, unsigned long *Rmin); +extern void PutBigDoubleWord (unsigned long dw, FILE *fp); +extern void write_buffer (InputParameters *p_Inp, unsigned long NumberLeakyBuckets, unsigned long Rmin[], unsigned long Bmin[], unsigned long Fmin[]); +extern void Sort (unsigned long NumberLeakyBuckets, unsigned long *Rmin); +extern void calc_buffer (VideoParameters *p_Vid, InputParameters *p_Inp); +#endif + +#endif + diff --git a/lencod/inc/list_reorder.h b/lencod/inc/list_reorder.h new file mode 100644 index 0000000..9af6f50 --- /dev/null +++ b/lencod/inc/list_reorder.h @@ -0,0 +1,39 @@ +/*! + *************************************************************************** + * \file + * list_reorder.h + * + * \date + * 25 Feb 2009 + * + * \brief + * Headerfile for slice-related functions + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * - Athanasios Leontaris + * - Karsten Suehring + * - Alexis Michael Tourapis + ************************************************************************** + */ + +#ifndef _LIST_REORDER_H_ +#define _LIST_REORDER_H_ + +#include "global.h" +#include "mbuffer.h" + +extern void init_ref_pic_list_reordering( Slice *currSlice, int refReorderMethod ); +extern void reorder_lists ( Slice *currSlice ); +extern void wp_mcprec_reorder_lists ( Slice *currSlice ); + + +extern void poc_ref_pic_reorder_frame_default( Slice *currSlice, unsigned num_ref_idx_lX_active, int list_no ); +extern void poc_ref_pic_reorder_field ( Slice *currSlice, unsigned num_ref_idx_lX_active, int list_no ); +extern void poc_ref_pic_reorder_field_enh ( Slice *currSlice, unsigned num_ref_idx_lX_active, int list_no ); + +extern void tlyr_ref_pic_reorder_frame_default( Slice *currSlice, unsigned num_ref_idx_lX_active, int list_no ); +extern void reorder_against_default_ref_pic_lists( Slice *currSlice, int cur_list ); +extern void poc_ref_pic_reorder_frame_enh( Slice *currSlice, unsigned num_ref_idx_lX_active, int list_no ); +extern void set_default_ref_pic_lists( Slice *currSlice ); + +#endif diff --git a/lencod/inc/lln_mc_prediction.h b/lencod/inc/lln_mc_prediction.h new file mode 100644 index 0000000..52aa62e --- /dev/null +++ b/lencod/inc/lln_mc_prediction.h @@ -0,0 +1,35 @@ +/*! + ************************************************************************************* + * \file lln_mc_prediction.h + * + * \brief + * definitions for motion compensated prediction + * + * \author + * Main contributors (see contributors.h for copyright, + * address and affiliation details) + * - Alexis Michael Tourapis + * - Modified for use in hypothetical decoders at encoder + * by Peshala V. Pahalawatta + * + ************************************************************************************* + */ + +#ifndef _LLN_MC_PREDICTION_H_ +#define _LLN_MC_PREDICTION_H_ + +#include "global.h" +#include "mbuffer.h" + + +extern void get_block_luma (Macroblock *currMB, int decoder, ColorPlane pl, StorablePicture *dec_picture, StorablePicture *list, int x_pos, int y_pos, int block_size_y, int block_size_x, imgpel block[MB_BLOCK_SIZE][MB_BLOCK_SIZE]); +extern void get_block_chroma(Macroblock *currMb, int decoder, int uv, StorablePicture *dec_picture, StorablePicture *list, int x_pos, int y_pos, int block_size_x, int block_size_y, imgpel block[MB_BLOCK_SIZE][MB_BLOCK_SIZE]); + +//extern void intra_cr_decoding(Macroblock *currMB, int yuv, int smb); +//extern void prepare_direct_params(Macroblock *currMB, StorablePicture *dec_picture, MotionVector *pmvl0, MotionVector *pmvl1,char *l0_rFrame, char *l1_rFrame); + +extern void perform_mc (Macroblock* currMB, int decoder, ColorPlane pl, StorablePicture *dec_picture, int pred_dir, int l0_mode, int l1_mode, PicMotionParams **mv_info, int i, int j, int block_size_x, int block_size_y, short bipred_me); +extern void perform_mc_concealment(Macroblock* currMB, int decoder, ColorPlane pl, StorablePicture *dec_picture, int pred_dir, int l0_mode, int l1_mode, PicMotionParams **mv_info, int i, int j, int block_size_x, int block_size_y); + +#endif + diff --git a/lencod/inc/loop_filter.h b/lencod/inc/loop_filter.h new file mode 100644 index 0000000..a093b01 --- /dev/null +++ b/lencod/inc/loop_filter.h @@ -0,0 +1,67 @@ +/*! + *************************************************************************** + * \file + * loop_filter.h + * + * \date + * 25 April 2010 + * + * \brief + * Headerfile for loopfilter processing + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * - Alexis Michael Tourapis + + ************************************************************************** + */ + +#ifndef _LOOP_FILTER_H_ +#define _LOOP_FILTER_H_ + +#include "global.h" + +#define GROUP_SIZE 1 + +/*********************************************************************************************************/ + +// NOTE: In principle, the alpha and beta tables are calculated with the formulas below +// Alpha( qp ) = 0.8 * (2^(qp/6) - 1) +// Beta ( qp ) = 0.5 * qp - 7 + +// The tables actually used have been "hand optimized" though (by Anthony Joch). So, the +// table values might be a little different to formula-generated values. Also, the first +// few values of both tables is set to zero to force the filter off at low qp’s + +static const byte ALPHA_TABLE[52] = {0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,4,4,5,6, 7,8,9,10,12,13,15,17, 20,22,25,28,32,36,40,45, 50,56,63,71,80,90,101,113, 127,144,162,182,203,226,255,255} ; +static const byte BETA_TABLE[52] = {0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,2,2,2,3, 3,3,3, 4, 4, 4, 6, 6, 7, 7, 8, 8, 9, 9,10,10, 11,11,12,12,13,13, 14, 14, 15, 15, 16, 16, 17, 17, 18, 18} ; +static const byte CLIP_TAB[52][5] = +{ + { 0, 0, 0, 0, 0},{ 0, 0, 0, 0, 0},{ 0, 0, 0, 0, 0},{ 0, 0, 0, 0, 0},{ 0, 0, 0, 0, 0},{ 0, 0, 0, 0, 0},{ 0, 0, 0, 0, 0},{ 0, 0, 0, 0, 0}, + { 0, 0, 0, 0, 0},{ 0, 0, 0, 0, 0},{ 0, 0, 0, 0, 0},{ 0, 0, 0, 0, 0},{ 0, 0, 0, 0, 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},{ 0, 0, 0, 1, 1},{ 0, 0, 0, 1, 1},{ 0, 0, 0, 1, 1},{ 0, 0, 1, 1, 1},{ 0, 0, 1, 1, 1},{ 0, 1, 1, 1, 1}, + { 0, 1, 1, 1, 1},{ 0, 1, 1, 1, 1},{ 0, 1, 1, 1, 1},{ 0, 1, 1, 2, 2},{ 0, 1, 1, 2, 2},{ 0, 1, 1, 2, 2},{ 0, 1, 1, 2, 2},{ 0, 1, 2, 3, 3}, + { 0, 1, 2, 3, 3},{ 0, 2, 2, 3, 3},{ 0, 2, 2, 4, 4},{ 0, 2, 3, 4, 4},{ 0, 2, 3, 4, 4},{ 0, 3, 3, 5, 5},{ 0, 3, 4, 6, 6},{ 0, 3, 4, 6, 6}, + { 0, 4, 5, 7, 7},{ 0, 4, 5, 8, 8},{ 0, 4, 6, 9, 9},{ 0, 5, 7,10,10},{ 0, 6, 8,11,11},{ 0, 6, 8,13,13},{ 0, 7,10,14,14},{ 0, 8,11,16,16}, + { 0, 9,12,18,18},{ 0,10,13,20,20},{ 0,11,15,23,23},{ 0,13,17,25,25} +} ; + +static const char chroma_edge[2][4][4] = //[dir][edge][yuv_format] +{ { {-4, 0, 0, 0}, + {-4,-4,-4, 4}, + {-4, 4, 4, 8}, + {-4,-4,-4, 12}}, + + { {-4, 0, 0, 0}, + {-4,-4, 4, 4}, + {-4, 4, 8, 8}, + {-4,-4, 12, 12}}}; + +static const int pelnum_cr[2][4] = {{0,8,16,16}, {0,8, 8,16}}; //[dir:0=vert, 1=hor.][yuv_format] + + +static inline int compare_mvs(const MotionVector *mv0, const MotionVector *mv1, int mvlimit) +{ + return ((iabs( mv0->mv_x - mv1->mv_x) >= 4) | (iabs( mv0->mv_y - mv1->mv_y) >= mvlimit)); +} + +#endif diff --git a/lencod/inc/macroblock.h b/lencod/inc/macroblock.h new file mode 100644 index 0000000..0e7ab05 --- /dev/null +++ b/lencod/inc/macroblock.h @@ -0,0 +1,128 @@ + +/*! + ************************************************************************ + * \file + * macroblock.h + * + * \brief + * Arrays for macroblock processing + * + * \author + * Inge Lille-Langoy \n + * Telenor Satellite Services \n + * P.O.Box 6914 St.Olavs plass \n + * N-0130 Oslo, Norway + * + ************************************************************************/ + +#ifndef _MACROBLOCK_H_ +#define _MACROBLOCK_H_ + +#include "block.h" +#include "macroblock_p444.h" + +static const int block8x8_idx[3][4][4] = +{ + { + {0, 1, 0, 0}, + {2, 3, 0, 0}, + {0, 0, 0, 0}, + {0, 0, 0, 0} + }, + { + {0, 1, 0, 0}, + {0, 1, 0, 0}, + {2, 3, 0, 0}, + {2, 3, 0, 0} + }, + { + {0, 0, 1, 1}, + {0, 0, 1, 1}, + {2, 2, 3, 3}, + {2, 2, 3, 3} + } +}; + +static const short part_size[8][2] = +{ + {4, 4}, + {4, 4}, + {4, 2}, + {2, 4}, + {2, 2}, + {2, 1}, + {1, 2}, + {1, 1} +}; + +static const short block_size[8][2] = +{ + {16, 16}, + {16, 16}, + {16, 8}, + {8, 16}, + {8, 8}, + {8, 4}, + {4, 8}, + {4, 4} +}; + +extern void next_macroblock (Macroblock* currMB); +extern void start_macroblock (Slice *currSlice, Macroblock** currMB, int mb_addr, Boolean mb_field); +extern void reset_macroblock (Macroblock *currMB); +extern void end_macroblock (Macroblock* currMB, Boolean *end_of_slice, Boolean *recode_macroblock); +extern void write_macroblock (Macroblock* currMB, int eos_bit); + + +extern int reset_block(Macroblock* currMB, int *cbp, int64 *cbp_blk, int block8x8); + +extern int luma_residual_coding_16x16 (Macroblock *, int, int[2]); +extern int luma_residual_coding_8x8 (Macroblock *, int*, int64*, int, short, int[2], char *); +extern void luma_residual_coding (Macroblock *); +extern void luma_residual_coding_sp (Macroblock *); +extern void chroma_residual_coding (Macroblock *); +extern void chroma_residual_coding_sp (Macroblock *); +extern void chroma_residual_coding_si (Macroblock *); +extern void intra_chroma_RD_decision (Macroblock *, RD_PARAMS *); +extern void intra_chroma_RD_decision_mbaff (Macroblock *, RD_PARAMS *); + +extern byte transform_decision (Macroblock *, int, distblk*); +extern int B8Mode2Value (Slice *, short, short); + +extern int write_p_slice_MB_layer (Macroblock *, int , int *); +extern int write_b_slice_MB_layer (Macroblock *, int , int *); +extern int write_i_slice_MB_layer (Macroblock *, int , int *); + +extern void write_terminating_bit (Slice* currSlice, int bit); + +extern int writeMotionInfo2NAL (Macroblock* currMB); +extern int write_p_slice_motion_info_to_NAL (Macroblock* currMB); +extern int write_b_slice_motion_info_to_NAL (Macroblock* currMB); +extern int write_p_slice_motion_info_to_NAL (Macroblock* currMB); +extern int writeReferenceFrame (Macroblock *currMB, int i, int j, int list_idx, int ref); +extern int writeMotionVector8x8 (Macroblock *currMB, int i0, int j0, int i1, int j1, int refframe, int list_idx, int mv_mode, short bipred_me); + +extern int writeCoeff4x4_CABAC (Macroblock *currMB, ColorPlane, int, int, int); +extern int writeCoeff8x8_CABAC (Macroblock* currMB, ColorPlane, int, int); +extern int writeCoeff8x8 (Macroblock* currMB, ColorPlane, int, int, int); +extern int writeCoeff16x16_CABAC (Macroblock* currMB, ColorPlane); +extern int writeCoeff16x16_CAVLC (Macroblock* currMB, ColorPlane); + +extern int writeCoeff4x4_CAVLC_normal (Macroblock* currMB, int block_type, int b8, int b4, int param); +extern int writeCoeff4x4_CAVLC_444 (Macroblock* currMB, int block_type, int b8, int b4, int param); + +extern int predict_nnz (Macroblock *currMB, int block_type, int i,int j); +extern int predict_nnz_chroma(Macroblock *currMB, int i,int j); + +extern void set_modes_and_reframe (Macroblock* currMB, int b8, short* p_dir, int list_mode[2], char *list_ref_idx); +extern void set_modes_and_reframe_i_slice (Macroblock* currMB, int b8, short* p_dir, int list_mode[2], char *list_ref_idx); +extern void set_modes_and_reframe_b_slice (Macroblock* currMB, int b8, short* p_dir, int list_mode[2], char *list_ref_idx); +extern void set_modes_and_reframe_p_slice (Macroblock* currMB, int b8, short* p_dir, int list_mode[2], char *list_ref_idx); +extern Macroblock *alloc_mbs(VideoParameters *p_Vid, int mb_num, int layers); +extern void free_mbs(Macroblock *pMBs, int mb_num); +extern void setup_mbs(Macroblock *pMBs, int mb_num, int layer_id); + + + +#endif + diff --git a/lencod/inc/macroblock_p444.h b/lencod/inc/macroblock_p444.h new file mode 100644 index 0000000..2d11003 --- /dev/null +++ b/lencod/inc/macroblock_p444.h @@ -0,0 +1,24 @@ + +/*! + ************************************************************************ + * \file + * macroblock_p444.h + * + * \brief + * Arrays for macroblock processing (P444 mode) + * + * \author + * + ************************************************************************/ + +#ifndef _MACROBLOCK_P444_H_ +#define _MACROBLOCK_P444_H_ + +#include "block.h" + +extern int luma_residual_coding_p444_16x16 (Macroblock* currMB, int, short, int[2], char *); +extern int luma_residual_coding_p444_8x8 (Macroblock* currMB, int*, int64*, int, short, int[2], char *); +extern void luma_residual_coding_p444 (Macroblock *currMB); + +#endif + diff --git a/lencod/inc/mbuffer.h b/lencod/inc/mbuffer.h new file mode 100644 index 0000000..c54028e --- /dev/null +++ b/lencod/inc/mbuffer.h @@ -0,0 +1,258 @@ + +/*! + *********************************************************************** + * \file + * mbuffer.h + * + * \brief + * Frame buffer functions + * + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * - Karsten Suehring + * - Alexis Michael Tourapis + * - Yuwen He + *********************************************************************** + */ +#ifndef _MBUFFERENC_H_ +#define _MBUFFERENC_H_ + +#include "global.h" +#include "enc_statistics.h" + +#define MAX_LIST_SIZE 33 + +typedef struct frame_store FrameStore; +typedef struct distortion_estimation Dist_Estm; +typedef struct picture_stats PictureStats; +typedef struct pic_motion_params_old PicMotionParamsOld; + +struct picture_stats +{ + double dsum[3]; + double dvar[3]; +}; + + +//! definition of pic motion parameters +struct pic_motion_params_old +{ + byte * mb_field; //!< field macroblock indicator +}; + + +//! definition of pic motion parameters +typedef struct pic_motion_params +{ + struct storable_picture *ref_pic[2]; //!< referrence picture pointer + char ref_idx[2]; //!< reference picture [list][subblock_y][subblock_x] + MotionVector mv[2]; //!< motion vector + byte field_frame; //!< indicates if co_located is field or frame. Will be removed at some point +} PicMotionParams; + + +//! definition a picture (field or frame) +typedef struct storable_picture +{ + PictureStructure structure; + + int poc; + int top_poc; + int bottom_poc; + int frame_poc; + int order_num; + unsigned frame_num; + int pic_num; + int long_term_pic_num; + int long_term_frame_idx; + int temporal_layer; + + byte is_long_term; + int used_for_reference; + int is_output; + int non_existing; + + int size_x, size_y, size_x_cr, size_y_cr; + int size_x_padded, size_y_padded; + int size_x_pad, size_y_pad; + int size_x_cr_pad, size_y_cr_pad; + int pad_size_uv_y, pad_size_uv_x; + int chroma_vector_adjustment; + int coded_frame; + int mb_aff_frame_flag; + + imgpel ** imgY; //!< Y picture component + imgpel **** imgY_sub; //!< Y picture component upsampled (Quarter pel) + imgpel *** imgUV; //!< U and V picture components + imgpel *****imgUV_sub; //!< UV picture component upsampled (Quarter/One-Eighth pel) + + imgpel *** p_dec_img[MAX_PLANE]; //!< pointer array for accessing decoded pictures in hypothetical decoders + + imgpel ** p_img[MAX_PLANE]; //!< pointer array for accessing imgY/imgUV[] + imgpel **** p_img_sub[MAX_PLANE]; //!< pointer array for storing top address of imgY_sub/imgUV_sub[] + imgpel ** p_curr_img; //!< current int-pel ref. picture area to be used for motion estimation + imgpel **** p_curr_img_sub; //!< current sub-pel ref. picture area to be used for motion estimation + //hme; + imgpel *** p_hme_int_img; //!< [level][y][x]; + imgpel ***** p_hme_sub_img; //!< [level][y_frac][x_frac][y][x]; + + Dist_Estm * de_mem; + + PicMotionParams **mv_info; //!< Motion info + PicMotionParams **JVmv_info[MAX_PLANE]; //!< Motion info for 4:4:4 independent coding + PicMotionParamsOld motion; //!< Motion info + PicMotionParamsOld JVmotion[MAX_PLANE]; //!< Motion info for 4:4:4 independent coding + + int colour_plane_id; //!< colour_plane_id to be used for 4:4:4 independent mode encoding + + struct storable_picture *top_field; // for mb aff, if frame for referencing the top field + struct storable_picture *bottom_field; // for mb aff, if frame for referencing the bottom field + struct storable_picture *frame; // for mb aff, if field for referencing the combined frame + + int chroma_format_idc; + int chroma_mask_mv_x; + int chroma_mask_mv_y; + int chroma_shift_y; + int chroma_shift_x; + int frame_mbs_only_flag; + int frame_cropping_flag; + int frame_crop_left_offset; + int frame_crop_right_offset; + int frame_crop_top_offset; + int frame_crop_bottom_offset; + + PictureStats p_stats; + StatParameters stats; + + int type; + +#if (MVC_EXTENSION_ENABLE) + int view_id; + int inter_view_flag[2]; + int anchor_pic_flag[2]; +#endif + + int bInterpolated; + int ref_pic_na[6]; + int otf_flag; + //int separate_colour_plane_flag; +} StorablePicture; + +typedef StorablePicture *StorablePicturePtr; + +//! Frame Stores for Decoded Picture Buffer +struct frame_store +{ + int is_used; //!< 0=empty; 1=top; 2=bottom; 3=both fields (or frame) + int is_reference; //!< 0=not used for ref; 1=top used; 2=bottom used; 3=both fields (or frame) used + int is_long_term; //!< 0=not used for ref; 1=top used; 2=bottom used; 3=both fields (or frame) used + int is_orig_reference; //!< original marking by nal_ref_idc: 0=not used for ref; 1=top used; 2=bottom used; 3=both fields (or frame) used + + int is_non_existent; + + unsigned frame_num; + int frame_num_wrap; + int long_term_frame_idx; + int is_output; + int poc; + + StorablePicture *frame; + StorablePicture *top_field; + StorablePicture *bottom_field; + + Boolean is_inter_layer; + +#if (MVC_EXTENSION_ENABLE) + int view_id; + int inter_view_flag[2]; + int anchor_pic_flag[2]; +#endif +}; + + + +//! Decoded Picture Buffer +typedef struct decoded_picture_buffer +{ + VideoParameters *p_Vid; + InputParameters *p_Inp; + FrameStore **fs; + FrameStore **fs_ref; + FrameStore **fs_ltref; + FrameStore **fs_ilref; + int num_ref_frames; + unsigned size; + unsigned used_size; + unsigned ref_frames_in_buffer; + unsigned ltref_frames_in_buffer; + int last_output_poc; +#if (MVC_EXTENSION_ENABLE) + int last_output_view_id; +#endif + int max_long_term_pic_idx; + + int init_done; + + FrameStore *last_picture; + int layer_id; + unsigned used_size_il; + + FrameFormat storage_format; + + void (*pf_luma_prediction) ( Macroblock* currMB, int, int, int, int, int, int[2], char *, short ); + void (*pf_luma_prediction_bi) ( Macroblock* currMB, int, int, int, int, int, int, short, short, int ); + void (*pf_chroma_prediction) ( Macroblock* currMB, int, int, int, int, int, int, int, int, short, short, short ); + void (*pf_get_block_luma) ( struct video_par *, imgpel*, int*, int, int, int, int, struct storable_picture*, int ); + void (*pf_get_block_chroma[2]) ( struct video_par *, imgpel*, int*, int, int, int, int, struct storable_picture*, int ); + void (*pf_OneComponentChromaPrediction4x4_regenerate)(Macroblock *currMB, imgpel* , int , int , MotionVector ** , StorablePicture *listX, int ); + void (*pf_OneComponentChromaPrediction4x4_retrieve) (Macroblock *currMB, imgpel* , int , int , MotionVector ** , StorablePicture *listX, int ); + distblk (*pf_computeSAD) (StorablePicture *ref1, MEBlock*, distblk, MotionVector *); + distblk (*pf_computeSADWP) (StorablePicture *ref1, MEBlock*, distblk, MotionVector *); + distblk (*pf_computeSATD) (StorablePicture *ref1, MEBlock*, distblk, MotionVector *); + distblk (*pf_computeSATDWP) (StorablePicture *ref1, MEBlock*, distblk, MotionVector *); + distblk (*pf_computeBiPredSAD1)(StorablePicture *ref1, StorablePicture *ref2, MEBlock*, distblk, MotionVector *, MotionVector *); + distblk (*pf_computeBiPredSAD2)(StorablePicture *ref1, StorablePicture *ref2, MEBlock*, distblk, MotionVector *, MotionVector *); + distblk (*pf_computeBiPredSATD1) (StorablePicture *ref1, StorablePicture *ref2, MEBlock*, distblk, MotionVector *, MotionVector *); + distblk (*pf_computeBiPredSATD2) (StorablePicture *ref1, StorablePicture *ref2, MEBlock*, distblk, MotionVector *, MotionVector *); + distblk (*pf_computeSSE) (StorablePicture *ref1, MEBlock*, distblk, MotionVector *); + distblk (*pf_computeSSEWP) (StorablePicture *ref1, MEBlock*, distblk, MotionVector *); + distblk (*pf_computeBiPredSSE1) (StorablePicture *ref1, StorablePicture *ref2, MEBlock*, distblk, MotionVector *, MotionVector *); + distblk (*pf_computeBiPredSSE2) (StorablePicture *ref1, StorablePicture *ref2, MEBlock*, distblk, MotionVector *, MotionVector *); +}DecodedPictureBuffer; + +extern void init_dpb (VideoParameters *p_Vid, DecodedPictureBuffer *dpb); +extern void free_dpb (DecodedPictureBuffer *p_Dpb); +extern FrameStore* alloc_frame_store(void); +extern void free_frame_store (VideoParameters *p_Vid, FrameStore* f); +extern StorablePicture* alloc_storable_picture (VideoParameters *p_Vid, PictureStructure type, int size_x, int size_y, int size_x_cr, int size_y_cr); +extern void free_storable_picture (VideoParameters *p_Vid, StorablePicture* p); +extern void store_picture_in_dpb (DecodedPictureBuffer *p_Dpb, StorablePicture* p, FrameFormat *output); +extern void replace_top_pic_with_frame(DecodedPictureBuffer *p_Dpb, StorablePicture* p, FrameFormat *output); +extern void flush_dpb (DecodedPictureBuffer *p_Dpb, FrameFormat *output); +extern void dpb_split_field (VideoParameters *p_Vid, FrameStore *fs); +extern void dpb_combine_field (VideoParameters *p_Vid, FrameStore *fs); +extern void dpb_combine_field_yuv (VideoParameters *p_Vid, FrameStore *fs); +extern void init_lists_p_slice (Slice *currSlice); +extern void init_lists_b_slice (Slice *currSlice); +extern void init_lists_i_slice (Slice *currSlice); +extern void update_pic_num (Slice *currSlice); +extern void reorder_ref_pic_list (Slice *currSlice, int cur_list); +extern void init_mbaff_lists (Slice *currSlice); +extern void alloc_ref_pic_list_reordering_buffer (Slice *currSlice); +extern void free_ref_pic_list_reordering_buffer (Slice *currSlice); +extern void fill_frame_num_gap (VideoParameters *p_Vid, FrameFormat *output); +extern void compute_colocated (Slice *currSlice, StorablePicture **listX[6]); +extern void reorder_short_term(Slice *currSlice, DecodedPictureBuffer *p_Dpb, int cur_list, int picNumLX, int *refIdxLX); + +extern void unmark_for_reference(FrameStore* fs); +extern void unmark_for_long_term_reference(FrameStore* fs); +extern void remove_frame_from_dpb(DecodedPictureBuffer *p_Dpb, int pos); + +#if (MVC_EXTENSION_ENABLE) +void check_num_ref(DecodedPictureBuffer *p_Dpb); +extern void replace_top_proc_pic_with_frame(DecodedPictureBuffer *p_Dpb, StorablePicture* p); +extern void store_proc_picture_in_dpb(DecodedPictureBuffer *p_Dpb, StorablePicture* p, FrameFormat *output); +#endif + +#endif + diff --git a/lencod/inc/mc_prediction.h b/lencod/inc/mc_prediction.h new file mode 100644 index 0000000..6b41dd9 --- /dev/null +++ b/lencod/inc/mc_prediction.h @@ -0,0 +1,38 @@ + +/*! + ************************************************************************ + * \file + * mc_prediction.h + * + * \brief + * motion compensation header + * + * \author + * Main contributors (see contributors.h for copyright, + * address and affiliation details) + * - Alexis Michael Tourapis + * + ************************************************************************************* + */ + +#ifndef _MC_PREDICTION_H_ +#define _MC_PREDICTION_H_ +#include "mbuffer.h" + +extern void luma_prediction ( Macroblock* currMB, int, int, int, int, int, int[2], char *, short ); +extern void luma_prediction_bi ( Macroblock* currMB, int, int, int, int, int, int, short, short, int ); +extern void chroma_prediction ( Macroblock* currMB, int, int, int, int, int, int, int, int, short, short, short ); +extern void chroma_prediction_4x4 ( Macroblock* currMB, int, int, int, int, int, int, short, short, short); + +extern void rdo_low_intra_chroma_decision (Macroblock *currMB, int mb_available_up, int mb_available_left[2], int mb_available_up_left); +extern void rdo_low_intra_chroma_decision_mbaff (Macroblock *currMB, int mb_available_up, int mb_available_left[2], int mb_available_up_left); +extern void OneComponentChromaPrediction4x4_regenerate (Macroblock *currMB, imgpel* , int , int , MotionVector ** , StorablePicture *listX, int ); +extern void OneComponentChromaPrediction4x4_retrieve (Macroblock *currMB, imgpel* , int , int , MotionVector ** , StorablePicture *listX, int ); + +extern void intra_chroma_prediction_mbaff(Macroblock *currMB, int*, int*, int*); +extern void intra_chroma_prediction (Macroblock *currMB, int*, int*, int*); +extern void IntraChromaPrediction4x4 (Macroblock* currMB, int uv, int block_x, int block_y); +extern void get_difference_4x4(imgpel **src, imgpel **prd, short *diff, int pos_x, int block_x); + +#endif + diff --git a/lencod/inc/mc_prediction_otf.h b/lencod/inc/mc_prediction_otf.h new file mode 100644 index 0000000..337bdc9 --- /dev/null +++ b/lencod/inc/mc_prediction_otf.h @@ -0,0 +1,43 @@ +#ifndef _MC_PREDICTION_OTF_H_ +#define _MC_PREDICTION_OTF_H_ + +#include "global.h" + +void luma_prediction_otf ( Macroblock* currMB, //!< Current Macroblock + int block_x, //!< relative horizontal block coordinate of block + int block_y, //!< relative vertical block coordinate of block + int block_size_x, //!< relative horizontal block coordinate of block + int block_size_y, //!< relative vertical block coordinate of block + int p_dir, //!< prediction direction (0=list0, 1=list1, 2=bipred) + int list_mode[2], //!< list prediction mode (1-7, 0=DIRECT) + char *ref_idx, //!< reference pictures + short bipred_me //!< use bi prediction mv (0=no bipred, 1 = use set 1, 2 = use set 2) + ) ; + +void luma_prediction_bi_otf ( Macroblock* currMB, //!< Current Macroblock + int block_x, //!< relative horizontal block coordinate of 4x4 block + int block_y, //!< relative vertical block coordinate of 4x4 block + int block_size_x, //!< horizontal block size + int block_size_y, //!< vertical block size + int l0_mode, //!< list0 prediction mode (1-7, 0=DIRECT if l1_mode=0) + int l1_mode, //!< list1 prediction mode (1-7, 0=DIRECT if l0_mode=0) + short l0_ref_idx, //!< reference frame for list0 prediction (-1: Intra4x4 pred. with l0_mode) + short l1_ref_idx, //!< reference frame for list1 prediction + int list //!< current list for prediction. + ); + +void chroma_prediction_otf ( Macroblock* currMB, // <-- Current Macroblock + int uv, // <-- colour component + int block_x, // <-- relative horizontal block coordinate of block + int block_y, // <-- relative vertical block coordinate of block + int block_size_x, // <-- relative horizontal block coordinate of block + int block_size_y, // <-- relative vertical block coordinate of block + int p_dir, // <-- prediction direction (0=list0, 1=list1, 2=bipred) + int l0_mode, // <-- list0 prediction mode (1-7, 0=DIRECT if l1_mode=0) + int l1_mode, // <-- list1 prediction mode (1-7, 0=DIRECT if l0_mode=0) + short l0_ref_idx, // <-- reference frame for list0 prediction (if (<0) -> intra prediction) + short l1_ref_idx, // <-- reference frame for list1 prediction + short bipred_me // <-- use bi prediction mv (0=no bipred, 1 = use set 1, 2 = use set 2) + ) ; + +#endif \ No newline at end of file diff --git a/lencod/inc/md_common.h b/lencod/inc/md_common.h new file mode 100644 index 0000000..2ac12d5 --- /dev/null +++ b/lencod/inc/md_common.h @@ -0,0 +1,38 @@ +/*! + *************************************************************************** + * \file + * md_common.h + * + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * - Alexis Michael Tourapis + * + * \date + * 4. October 2008 + * + * \brief + * Headerfile for common mode functions + ************************************************************************** + */ + +#ifndef _MD_COMMON_H_ +#define _MD_COMMON_H_ +#include "mv_search.h" +#include "me_distortion.h" + +extern void SetMotionVectorsMBPSlice (Macroblock* currMB); +extern void SetMotionVectorsMBBSlice (Macroblock* currMB); +extern void SetMotionVectorsMBISlice (Macroblock* currMB); +extern void copy_image_data (imgpel **imgBuf1, imgpel **imgBuf2, int off1, int off2, int width, int height); +extern void copy_image_data_16x16 (imgpel **imgBuf1, imgpel **imgBuf2, int off1, int off2); +extern void copy_image_data_8x8 (imgpel **imgBuf1, imgpel **imgBuf2, int off1, int off2); +extern void copy_image_data_4x4 (imgpel **imgBuf1, imgpel **imgBuf2, int off1, int off2); +extern void ResetRD8x8Data (VideoParameters *p_Vid, RD_8x8DATA *rd_data); +extern void set_chroma_pred_mode (Macroblock *currMB, RD_PARAMS enc_mb, int *mb_available, char chroma_pred_mode_range[2]); + + +extern Info8x8 init_best (void); + + +#endif + diff --git a/lencod/inc/md_distortion.h b/lencod/inc/md_distortion.h new file mode 100644 index 0000000..9762bb6 --- /dev/null +++ b/lencod/inc/md_distortion.h @@ -0,0 +1,31 @@ +/*! + *************************************************************************** + * \file + * md_distortion.h + * + * \author + * Alexis Michael Tourapis + * + * \date + * 7. February 2009 + * + * \brief + * Headerfile for distortion functions + ************************************************************************** + */ + +#ifndef _MD_DISTORTION_H_ +#define _MD_DISTORTION_H_ + +#include "global.h" + +// Functions +extern void setupDistortion (Slice *currSlice); +extern int64 compute_SSE (imgpel **imgRef, imgpel **imgSrc, int xRef, int xSrc, int ySize, int xSize); +extern distblk compute_SSE_cr (imgpel **imgRef, imgpel **imgSrc, int xRef, int xSrc, int ySize, int xSize); +extern distblk compute_SSE16x16(imgpel **imgRef, imgpel **imgSrc, int xRef, int xSrc); +extern distblk compute_SSE8x8 (imgpel **imgRef, imgpel **imgSrc, int xRef, int xSrc); +extern distblk compute_SSE4x4 (imgpel **imgRef, imgpel **imgSrc, int xRef, int xSrc); +extern distblk compute_SSE16x16_thres(imgpel **imgRef, imgpel **imgSrc, int xRef, int xSrc, distblk min_cost); + +#endif diff --git a/lencod/inc/me_distortion.h b/lencod/inc/me_distortion.h new file mode 100644 index 0000000..8ab5407 --- /dev/null +++ b/lencod/inc/me_distortion.h @@ -0,0 +1,92 @@ +/*! + *************************************************************************** + * \file + * me_distortion.h + * + * \author + * Alexis Michael Tourapis + * Athanasios Leontaris + * + * \date + * 11. August 2006 + * + * \brief + * Headerfile for motion estimation distortion + ************************************************************************** + */ + +#ifndef _ME_DISTORTION_H_ +#define _ME_DISTORTION_H_ + +extern distblk distortion4x4SAD(short* diff, distblk min_mcost); +extern distblk distortion4x4SSE(short* diff, distblk min_mcost); +extern distblk distortion4x4SATD(short* diff, distblk min_cost); +extern distblk distortion8x8SAD(short* diff, distblk min_mcost); +extern distblk distortion8x8SSE(short* diff, distblk min_mcost); +extern distblk distortion8x8SATD(short* diff, distblk min_cost); + +extern int HadamardSAD4x4(short* diff); +extern int HadamardSAD8x8(short* diff); +// SAD functions +extern distblk computeSAD (StorablePicture *ref1, MEBlock*, distblk, MotionVector *); +extern distblk computeSAD16x16 (StorablePicture *ref1, MEBlock*, distblk, MotionVector *); +extern distblk computeSAD16x8 (StorablePicture *ref1, MEBlock*, distblk, MotionVector *); +extern distblk computeSAD8x16 (StorablePicture *ref1, MEBlock*, distblk, MotionVector *); +extern distblk computeSAD8x8 (StorablePicture *ref1, MEBlock*, distblk, MotionVector *); +extern distblk computeSAD8x4 (StorablePicture *ref1, MEBlock*, distblk, MotionVector *); +extern distblk computeSAD4x8 (StorablePicture *ref1, MEBlock*, distblk, MotionVector *); +extern distblk computeSAD4x4 (StorablePicture *ref1, MEBlock*, distblk, MotionVector *); + +// Weighted Prediction SAD functions +extern distblk computeSADWP (StorablePicture *ref1, MEBlock*, distblk, MotionVector *); +extern distblk computeSADWP16x16 (StorablePicture *ref1, MEBlock*, distblk, MotionVector *); +extern distblk computeSADWP16x8 (StorablePicture *ref1, MEBlock*, distblk, MotionVector *); +extern distblk computeSADWP8x16 (StorablePicture *ref1, MEBlock*, distblk, MotionVector *); +extern distblk computeSADWP8x8 (StorablePicture *ref1, MEBlock*, distblk, MotionVector *); +extern distblk computeSADWP8x4 (StorablePicture *ref1, MEBlock*, distblk, MotionVector *); +extern distblk computeSADWP4x8 (StorablePicture *ref1, MEBlock*, distblk, MotionVector *); +extern distblk computeSADWP4x4 (StorablePicture *ref1, MEBlock*, distblk, MotionVector *); + +// SATD +extern distblk computeSATD (StorablePicture *ref1, MEBlock*, distblk, MotionVector *); +extern distblk computeSATDWP (StorablePicture *ref1, MEBlock*, distblk, MotionVector *); +extern distblk computeSAT8x8D16x16(StorablePicture *ref1, MEBlock*, distblk, MotionVector *); +extern distblk computeSAT4x4D16x16(StorablePicture *ref1, MEBlock*, distblk, MotionVector *); +extern distblk computeSAT8x8D16x8 (StorablePicture *ref1, MEBlock*, distblk, MotionVector *); +extern distblk computeSAT4x4D16x8 (StorablePicture *ref1, MEBlock*, distblk, MotionVector *); +extern distblk computeSAT8x8D8x16 (StorablePicture *ref1, MEBlock*, distblk, MotionVector *); +extern distblk computeSAT4x4D8x16 (StorablePicture *ref1, MEBlock*, distblk, MotionVector *); +extern distblk computeSAT8x8D (StorablePicture *ref1, MEBlock*, distblk, MotionVector *); +extern distblk computeSAT4x4D (StorablePicture *ref1, MEBlock*, distblk, MotionVector *); + +// SSE +extern distblk computeSSE (StorablePicture *ref1, MEBlock*, distblk, MotionVector *); +extern distblk computeSSEWP (StorablePicture *ref1, MEBlock*, distblk, MotionVector *); + +// Bipred SAD +extern distblk computeBiPredSAD1 (StorablePicture *ref1, StorablePicture *ref2, MEBlock*, distblk, MotionVector *, MotionVector *); +extern distblk computeBiPred16x16SAD1 (StorablePicture *ref1, StorablePicture *ref2, MEBlock*, distblk, MotionVector *, MotionVector *); +extern distblk computeBiPred16x8SAD1 (StorablePicture *ref1, StorablePicture *ref2, MEBlock*, distblk, MotionVector *, MotionVector *); +extern distblk computeBiPred8x16SAD1 (StorablePicture *ref1, StorablePicture *ref2, MEBlock*, distblk, MotionVector *, MotionVector *); +extern distblk computeBiPred8x8SAD1 (StorablePicture *ref1, StorablePicture *ref2, MEBlock*, distblk, MotionVector *, MotionVector *); +extern distblk computeBiPred8x4SAD1 (StorablePicture *ref1, StorablePicture *ref2, MEBlock*, distblk, MotionVector *, MotionVector *); +extern distblk computeBiPred4x8SAD1 (StorablePicture *ref1, StorablePicture *ref2, MEBlock*, distblk, MotionVector *, MotionVector *); +extern distblk computeBiPred4x4SAD1 (StorablePicture *ref1, StorablePicture *ref2, MEBlock*, distblk, MotionVector *, MotionVector *); +extern distblk computeBiPredSAD2 (StorablePicture *ref1, StorablePicture *ref2, MEBlock*, distblk, MotionVector *, MotionVector *); + +// Bipred SATD +extern distblk computeBiPredSATD1 (StorablePicture *ref1, StorablePicture *ref2, MEBlock*, distblk, MotionVector *, MotionVector *); +extern distblk computeBiPredSATD2 (StorablePicture *ref1, StorablePicture *ref2, MEBlock*, distblk, MotionVector *, MotionVector *); +extern distblk computeBiPred16x16SATD1(StorablePicture *ref1, StorablePicture *ref2, MEBlock*, distblk, MotionVector *, MotionVector *); +extern distblk computeBiPred16x8SATD1 (StorablePicture *ref1, StorablePicture *ref2, MEBlock*, distblk, MotionVector *, MotionVector *); +extern distblk computeBiPred8x8SATD1 (StorablePicture *ref1, StorablePicture *ref2, MEBlock*, distblk, MotionVector *, MotionVector *); +extern distblk computeBiPred8x16SATD1 (StorablePicture *ref1, StorablePicture *ref2, MEBlock*, distblk, MotionVector *, MotionVector *); + +// Bipred SSE +extern distblk computeBiPredSSE1 (StorablePicture *ref1, StorablePicture *ref2, MEBlock*, distblk, MotionVector *, MotionVector *); +extern distblk computeBiPredSSE2 (StorablePicture *ref1, StorablePicture *ref2, MEBlock*, distblk, MotionVector *, MotionVector *); + +extern void select_distortion (VideoParameters *p_Vid, InputParameters *p_Inp); + +extern void calcDifference(imgpel **origImg, int ox, int oy, imgpel **predImg, int px, int py, int width, int height, short *diff); +#endif diff --git a/lencod/inc/me_distortion_otf.h b/lencod/inc/me_distortion_otf.h new file mode 100644 index 0000000..3656387 --- /dev/null +++ b/lencod/inc/me_distortion_otf.h @@ -0,0 +1,40 @@ +/*! + ****************************************************************************************** + * \file + * me_distortion_otf.h + * + * \brief + * Headerfile for motion estimation distortion with subpel interpolation on-the-fly + ****************************************************************************************** + */ + +#ifndef _ME_DISTORTION_OTF_H_ +#define _ME_DISTORTION_OTF_H_ + +// SAD functions +extern distblk computeSAD_otf (StorablePicture *ref1, MEBlock*, distblk, MotionVector *); + +// Weighted Prediction SAD functions +extern distblk computeSADWP_otf (StorablePicture *ref1, MEBlock*, distblk, MotionVector *); + +// SATD +extern distblk computeSATD_otf (StorablePicture *ref1, MEBlock*, distblk, MotionVector *); +extern distblk computeSATDWP_otf (StorablePicture *ref1, MEBlock*, distblk, MotionVector *); + +// SSE +extern distblk computeSSE_otf (StorablePicture *ref1, MEBlock*, distblk, MotionVector *); +extern distblk computeSSEWP_otf (StorablePicture *ref1, MEBlock*, distblk, MotionVector *); + +// Bipred SAD +extern distblk computeBiPredSAD1_otf (StorablePicture *ref1, StorablePicture *ref2, MEBlock*, distblk, MotionVector *, MotionVector *); +extern distblk computeBiPredSAD2_otf (StorablePicture *ref1, StorablePicture *ref2, MEBlock*, distblk, MotionVector *, MotionVector *); + +// Bipred SATD +extern distblk computeBiPredSATD1_otf (StorablePicture *ref1, StorablePicture *ref2, MEBlock*, distblk, MotionVector *, MotionVector *); +extern distblk computeBiPredSATD2_otf (StorablePicture *ref1, StorablePicture *ref2, MEBlock*, distblk, MotionVector *, MotionVector *); + +// Bipred SSE +extern distblk computeBiPredSSE1_otf (StorablePicture *ref1, StorablePicture *ref2, MEBlock*, distblk, MotionVector *, MotionVector *); +extern distblk computeBiPredSSE2_otf (StorablePicture *ref1, StorablePicture *ref2, MEBlock*, distblk, MotionVector *, MotionVector *); + +#endif diff --git a/lencod/inc/me_epzs.h b/lencod/inc/me_epzs.h new file mode 100644 index 0000000..bbde6b0 --- /dev/null +++ b/lencod/inc/me_epzs.h @@ -0,0 +1,53 @@ + +/*! +************************************************************************ +* \file +* me_epzs.h +* +* \author +* Alexis Michael Tourapis +* +* \date +* 11. August 2006 +* +* \brief +* Headerfile for EPZS motion estimation +************************************************************************** +*/ + + +#ifndef _ME_EPZS_H_ +#define _ME_EPZS_H_ +#include "me_epzs_common.h" + +static const int next_start_pos[5][5] = +{ + {0, 8, 5, 6, 7}, + {8, 0, 5, 8, 8}, + {5, 5, 0, 6, 5}, + {6, 6, 6, 0, 7}, + {7, 8, 7, 7, 0} +}; + +static const int next_end_pos[5][5] = +{ + {0, 10, 7, 8, 9}, + {10, 0, 6, 10, 9}, + {7, 6, 0, 7, 7}, + {8, 8, 7, 0, 8}, + {9, 9, 9, 8, 0} +}; + +static const MotionVector search_point_hp[10] = {{0,0},{-2,0}, {0,2}, {2,0}, {0,-2}, {-2,2}, {2,2}, {2,-2}, {-2,-2}, {-2,2}}; +static const MotionVector search_point_qp[10] = {{0,0},{-1,0}, {0,1}, {1,0}, {0,-1}, {-1,1}, {1,1}, {1,-1}, {-1,-1}, {-1,1}}; + +// Functions +extern distblk EPZS_motion_estimation (Macroblock *, MotionVector *, MEBlock *, distblk, int); +extern distblk EPZS_subMB_motion_estimation (Macroblock *, MotionVector *, MEBlock *, distblk, int); +extern distblk EPZS_sub_pel_motion_estimation (Macroblock *, MotionVector *, MEBlock *, distblk, int*); +extern distblk EPZS_sub_pel_bipred_motion_estimation (Macroblock *, MEBlock *, int list, MotionVector *, MotionVector *, MotionVector *, MotionVector *, distblk, int *); +extern distblk EPZS_bipred_motion_estimation (Macroblock *, int, MotionVector *, MotionVector *, MotionVector *, MotionVector *, MEBlock *, int, distblk, int); + + +#endif + diff --git a/lencod/inc/me_epzs_common.h b/lencod/inc/me_epzs_common.h new file mode 100644 index 0000000..533be01 --- /dev/null +++ b/lencod/inc/me_epzs_common.h @@ -0,0 +1,155 @@ + +/*! +************************************************************************ +* \file +* me_epzs_common.h +* +* \author +* Alexis Michael Tourapis +* +* \date +* 11. August 2006 +* +* \brief +* Headerfile for common functions for the EPZS motion estimation +************************************************************************** +*/ + + +#ifndef _ME_EPZS_COMMON_H_ +#define _ME_EPZS_COMMON_H_ + + +// Structure definitions +typedef struct +{ + int mb_adaptive_frame_field_flag; + int size_x, size_y; + + // Frame + MotionVector ***frame; //!< motion vector [list][subblock_x][subblock_y] + // Top field + MotionVector ***top; //!< motion vector [list][subblock_x][subblock_y] + // Bottom field + MotionVector ***bot; //!< motion vector [list][subblock_x][subblock_y] +} EPZSColocParams; + +typedef struct +{ + MotionVector motion; + int start_nmbr; + int next_points; +} +SPoint; + +struct epzs_struct +{ + int searchPoints; + SPoint *point; + int stopSearch; + int nextLast; + struct epzs_struct *nextpattern; +}; + +typedef struct epzs_struct EPZSStructure; + +typedef enum +{ + SDIAMOND = 0, + SQUARE = 1, + EDIAMOND = 2, + LDIAMOND = 3, + SBDIAMOND = 4 +} EPZSPatterns; + +typedef struct epzs_params { + VideoParameters *p_Vid; + uint16 BlkCount; + int searcharray; + + distblk medthres[8]; + distblk maxthres[8]; + distblk minthres[8]; + distblk subthres[8]; + + int mv_scale [6][MAX_REFERENCE_PICTURES][MAX_REFERENCE_PICTURES]; + int mv_scale_update[6][MAX_REFERENCE_PICTURES][MAX_REFERENCE_PICTURES]; + + uint16 **EPZSMap; //!< Memory Map definition + +#if EPZSREF + MotionVector *****p_motion; //!< Array for storing Motion Vectors +#else + MotionVector ****p_motion; //!< Array for storing Motion Vectors +#endif + distblk ***distortion; //!< Array for storing SAD Values + distblk ***bi_distortion; //!< Array for storing SAD Values + distblk ***distortion_hpel; //!< Array for storing SAD Values + + // + EPZSStructure *searchPattern; + EPZSStructure *searchPatternD; + EPZSStructure *predictor; + // Window predictors + EPZSStructure *window_predictor; + EPZSStructure *window_predictor_ext; + // Co-located helper + EPZSColocParams *p_colocated; +} EPZSParameters; + + +extern void EPZSBlockTypePredictorsMB (Slice *currSlice, MEBlock *mv_block, SPoint *point, int *prednum); +extern short EPZS_spatial_predictors (EPZSParameters *p_EPZS, MEBlock *mv_block, int list, int list_offset, short ref, struct pic_motion_params **mv_info); +extern void EPZS_temporal_predictors (Macroblock *currMB, StorablePicture *ref_picture, EPZSParameters *p_EPZS, MEBlock *mv_block, int *prednum, distblk stopCriterion, distblk min_mcost); +extern distblk EPZSDetermineStopCriterion(EPZSParameters *p_EPZS, distblk* prevSad, MEBlock *mv_block ,distblk lambda_dist); +extern void EPZSBlockTypePredictors (Slice *currSlice, MEBlock *mv_block, SPoint *point, int *prednum); +extern void EPZSWindowPredictors (MotionVector *mv, EPZSStructure *predictor, int *prednum, EPZSStructure *windowPred); +extern void EPZS_spatial_memory_predictors (EPZSParameters *p_EPZS, MEBlock *mv_block, int list, int *prednum, int img_width); + +extern void EPZSDelete (VideoParameters *p_Vid); +extern void EPZSStructDelete (Slice *currSlice); +extern void EPZSSliceInit (Slice *currSlice); +extern int EPZSInit (VideoParameters *p_Vid); +extern int EPZSStructInit (Slice *currSlice); +extern void EPZSOutputStats (InputParameters *p_Inp, FILE * stat, short stats_file); +extern void EPZS_setup_engine (Macroblock *, InputParameters *); +/*! +*********************************************************************** +* \brief +* Add Predictor/normalize function +* Returns if succesfully added (i.e. if not 0) +* +* +*********************************************************************** +*/ +static inline int add_predictor(MotionVector *cur_mv, MotionVector prd_mv, int mvScale, int shift_mv) +{ + *cur_mv = prd_mv; + cur_mv->mv_x = (short) rshift_rnd_sf((mvScale * cur_mv->mv_x), shift_mv); + cur_mv->mv_y = (short) rshift_rnd_sf((mvScale * cur_mv->mv_y), shift_mv); + return (*((int*) cur_mv) != 0); +} + +static inline void scale_mv(MotionVector *out_mv, int scale, const MotionVector *mv, int shift_mv) +{ + out_mv->mv_x = (short) rshift_rnd_sf((scale * mv->mv_x), shift_mv); + out_mv->mv_y = (short) rshift_rnd_sf((scale * mv->mv_y), shift_mv); +} + +static inline void scale_mv_xy(MotionVector *out_mv, int *scale, const MotionVector *mv, int shift_mv) +{ + out_mv->mv_x = (short) rshift_rnd_sf((scale[0] * mv->mv_x), shift_mv); + out_mv->mv_y = (short) rshift_rnd_sf((scale[1] * mv->mv_y), shift_mv); +} + +static inline void compute_scaled(MotionVector *MotionVector0, MotionVector *MotionVector1, int tempmv_scale[2], const MotionVector *mv, int invmv_precision) +{ + MotionVector0->mv_x = (short) iClip3 (-32768, 32767, rshift_rnd_sf((tempmv_scale[LIST_0] * mv->mv_x), invmv_precision)); + MotionVector0->mv_y = (short) iClip3 (-32768, 32767, rshift_rnd_sf((tempmv_scale[LIST_0] * mv->mv_y), invmv_precision)); + MotionVector1->mv_x = (short) iClip3 (-32768, 32767, rshift_rnd_sf((tempmv_scale[LIST_1] * mv->mv_x), invmv_precision)); + MotionVector1->mv_y = (short) iClip3 (-32768, 32767, rshift_rnd_sf((tempmv_scale[LIST_1] * mv->mv_y), invmv_precision)); +} + +#endif + + diff --git a/lencod/inc/me_epzs_int.h b/lencod/inc/me_epzs_int.h new file mode 100644 index 0000000..772474d --- /dev/null +++ b/lencod/inc/me_epzs_int.h @@ -0,0 +1,29 @@ + +/*! + ************************************************************************ + * \file + * me_epzs_int.h + * + * \author + * Alexis Michael Tourapis + * + * \date + * 11. August 2006 + * + * \brief + * Headerfile for EPZS motion estimation + ************************************************************************** + */ + + +#ifndef _ME_EPZS_INT_H_ +#define _ME_EPZS_INT_H_ + +#include "me_epzs.h" + +// Functions +extern distblk EPZS_integer_motion_estimation (Macroblock *, MotionVector *, MEBlock *, distblk, int); +extern distblk EPZS_integer_subMB_motion_estimation (Macroblock *, MotionVector *, MEBlock *, distblk, int); +extern distblk EPZS_integer_bipred_motion_estimation (Macroblock *, int, MotionVector *, MotionVector *, MotionVector *, MotionVector *, MEBlock *, int, distblk, int); +#endif + diff --git a/lencod/inc/me_fullfast.h b/lencod/inc/me_fullfast.h new file mode 100644 index 0000000..fdb2981 --- /dev/null +++ b/lencod/inc/me_fullfast.h @@ -0,0 +1,39 @@ + +/*! + ************************************************************************ + * \file + * me_fullfast.h + * + * \author + * Alexis Michael Tourapis + * + * \date + * 9 September 2006 + * + * \brief + * Headerfile for Fast Full Search motion estimation + ************************************************************************** + */ + + +#ifndef _ME_FULLFAST_H_ +#define _ME_FULLFAST_H_ +typedef struct me_full_fast +{ + int **search_setup_done; //!< flag if all block SAD's have been calculated yet + MotionVector **search_center; //!< absolute search center for fast full motion search + MotionVector **search_center_padded; //!< absolute search center for fast full motion search + int **pos_00; //!< position of (0,0) vector + distpel *****BlockSAD; //!< SAD for all blocksize, ref. frames and motion vectors + int **max_search_range; +} MEFullFast; + +extern distblk fast_full_search_motion_estimation ( Macroblock *, MotionVector *, MEBlock *, distblk, int ); +extern void initialize_fast_full_search ( VideoParameters *, InputParameters * ); +extern void reset_fast_full_search ( VideoParameters * ); +extern void clear_fast_full_search ( VideoParameters * ); +extern void setup_fast_full_search ( Macroblock *, MEBlock *, int ); +extern void update_full_search_large_blocks ( MEFullFast *, int, int, int ); + +#endif + diff --git a/lencod/inc/me_fullfast_otf.h b/lencod/inc/me_fullfast_otf.h new file mode 100644 index 0000000..096b055 --- /dev/null +++ b/lencod/inc/me_fullfast_otf.h @@ -0,0 +1,17 @@ +/*! + ***************************************************************************************** + * \file + * me_fullfast_otf.h + * + * \brief + * Headerfile for Fast Full Search motion estimation extention on-the-fly interpolation + ***************************************************************************************** + */ + + +#ifndef _ME_FULLFAST_OTF_H_ +#define _ME_FULLFAST_OTF_H_ + +void SetupFastFullPelSearch_otf (Macroblock *, MEBlock *, int ) ; + +#endif \ No newline at end of file diff --git a/lencod/inc/me_fullsearch.h b/lencod/inc/me_fullsearch.h new file mode 100644 index 0000000..ccf1caa --- /dev/null +++ b/lencod/inc/me_fullsearch.h @@ -0,0 +1,28 @@ + +/*! + ************************************************************************ + * \file + * me_fullsearch.h + * + * \author + * Alexis Michael Tourapis + * + * \date + * 9 September 2006 + * + * \brief + * Headerfile for Full Search motion estimation + ************************************************************************** + */ + + +#ifndef _ME_FULLSEARCH_H_ +#define _ME_FULLSEARCH_H_ +extern distblk full_search_motion_estimation (Macroblock *, MotionVector *, MEBlock *, distblk, int ); +extern distblk full_search_bipred_motion_estimation (Macroblock *, int, MotionVector *, MotionVector *, MotionVector *, MotionVector *, MEBlock *, int, distblk, int ); +extern distblk sub_pel_motion_estimation (Macroblock *, MotionVector *, MEBlock *, distblk, int *); +extern distblk sub_pel_bipred_motion_estimation (Macroblock *, MEBlock *, int, MotionVector *, MotionVector *, MotionVector *, MotionVector *, distblk, int *); +extern distblk full_sub_pel_motion_estimation (Macroblock *, MotionVector *, MEBlock *, distblk, int *); +extern distblk full_sub_pel_bipred_motion_estimation (Macroblock *, MEBlock *, int, MotionVector *, MotionVector *, MotionVector *, MotionVector *, distblk, int *); +#endif + diff --git a/lencod/inc/me_umhex.h b/lencod/inc/me_umhex.h new file mode 100644 index 0000000..3f0295e --- /dev/null +++ b/lencod/inc/me_umhex.h @@ -0,0 +1,147 @@ + +/*! + ************************************************************************ + * + * \file me_umhex.h + * + * \brief + * Macro definitions and global variables for UMHEX fast + * integer pel motion estimation and fractional pel motion estimation + * + * \author + * Main contributors: (see contributors.h for copyright, address and affiliation details) + * - Zhibo Chen + * - JianFeng Xu + * - Wenfang Fu + * - Xiaozhong Xu + * + * \date + * 2006.1 + ************************************************************************ + */ + +#ifndef _ME_UMHEX_H_ +#define _ME_UMHEX_H_ + +#include "mbuffer.h" + +struct umhex_struct { + float AlphaFourth_1[8]; + float AlphaFourth_2[8]; + // for bipred mode + int pred_MV_ref_flag; + int BlockType_LUT[4][4]; + distblk Median_Pred_Thd_MB[8]; + distblk Big_Hexagon_Thd_MB[8]; + distblk Multi_Ref_Thd_MB[8]; + distblk Threshold_DSR_MB[8]; //!< Threshold for usage of DSR. DSR refer to JVT-Q088 + byte **McostState; //!< state for integer pel search + byte **SearchState; //!< state for fractional pel search + distblk ****fastme_ref_cost; //!< store SAD information needed for forward ref-frame prediction + distblk ***fastme_l0_cost; //!< store SAD information needed for forward median and uplayer prediction + distblk ***fastme_l1_cost; //!< store SAD information needed for backward median and uplayer prediction + distblk **fastme_best_cost; //!< for multi ref early termination threshold + distblk ***fastme_l0_cost_bipred; //!< store SAD information for bipred mode + distblk ***fastme_l1_cost_bipred; //!< store SAD information for bipred mode + distblk pred_SAD; //!< SAD prediction in use. + distblk SAD_a,SAD_b,SAD_c,SAD_d; + int bipred_flag; //!< flag for bipred + int pred_MV_ref[2], pred_MV_uplayer[2]; //!< pred motion vector by space or temporal correlation,Median is provided + + int UMHEX_blocktype; //!< blocktype for UMHEX SetMotionVectorPredictor + int predict_point[5][2]; + //for early termination + float Bsize[8]; + + byte *flag_intra; + int flag_intra_SAD; +}; + +typedef struct umhex_struct UMHexStruct; + +#define EARLY_TERMINATION \ + if ((min_mcost - p_UMHex->pred_SAD)pred_SAD * betaFourth_2) \ + goto fourth_2_step; \ + else if((min_mcost - p_UMHex->pred_SAD) < p_UMHex->pred_SAD * betaFourth_1) \ + goto fourth_1_step; + +#define SEARCH_ONE_PIXEL \ + if((iabs(cand.mv_x - center.mv_x)>>2) < search_range && (iabs(cand.mv_y - center.mv_y)>>2)< search_range)\ + { \ + if(!p_UMHex->McostState[((cand.mv_y - center.mv_y) >> 2)+ search_range][((cand.mv_x-center.mv_x)>>2)+search_range]) \ + { \ + mcost = mv_cost (p_Vid, lambda_factor, &cand, &pred); \ + if(mcostcomputePredFPel(ref_picture, mv_block, \ + min_mcost - mcost, &cand); \ + p_UMHex->McostState[((cand.mv_y - center.mv_y) >> 2) + search_range][((cand.mv_x - center.mv_x) >> 2) + search_range] = 1; \ + if (mcost < min_mcost) \ + { \ + best = cand; \ + min_mcost = mcost; \ + } \ + } \ + } \ + } + +#define SEARCH_ONE_PIXEL_BIPRED \ +if((iabs(cand.mv_x - center2.mv_x) >> 2) < search_range && (iabs(cand.mv_y - center2.mv_y) >> 2) < search_range) \ +{ \ + if(!p_UMHex->McostState[((cand.mv_y - center2.mv_y) >> 2) + search_range][((cand.mv_x-center2.mv_x) >> 2)+search_range]) \ + { \ + mcost = mv_cost (p_Vid, lambda_factor, ¢er1, &pred1); \ + mcost += mv_cost (p_Vid, lambda_factor, &cand, &pred2); \ + if(mcostcomputeBiPredFPel(ref_picture1, ref_picture2, \ + mv_block, min_mcost - mcost, ¢er1, &cand); \ + p_UMHex->McostState[((cand.mv_y - center2.mv_y) >> 2) + search_range][((cand.mv_x - center2.mv_x) >> 2) + search_range] = 1; \ + if (mcost < min_mcost) \ + { \ + best = cand; \ + min_mcost = mcost; \ + } \ + } \ + } \ +} \ + + +extern void UMHEX_DefineThreshold (VideoParameters *p_Vid); +extern void UMHEX_DefineThresholdMB(VideoParameters *p_Vid, InputParameters *p_Inp); +extern int UMHEX_get_mem (VideoParameters *p_Vid, InputParameters *p_Inp); +extern void UMHEX_free_mem (VideoParameters *p_Vid, InputParameters *p_Inp); + +extern void UMHEX_decide_intrabk_SAD(Macroblock *currMB); +extern void UMHEX_skip_intrabk_SAD (Macroblock *currMB, int ref_max); +extern void UMHEX_setup (Macroblock *currMB, short ref, int list, int block_y, int block_x, int blocktype, MotionVector *****all_mv); + +extern distblk // ==> minimum motion cost after search +UMHEXIntegerPelBlockMotionSearch (Macroblock *currMB, // <-- current Macroblock + MotionVector *pred, // <-- motion vector predictor (x) in sub-pel units + MEBlock *mv_block, + distblk min_mcost, // <-- minimum motion cost (cost for center or huge value) + int lambda_factor // <-- lagrangian parameter for determining motion cost + ); +extern distblk UMHEXSubPelBlockME ( // ==> minimum motion cost after search + Macroblock *currMB, // <-- Current Macroblock + MotionVector *pred, // <-- motion vector predictor (x) in sub-pel units + MEBlock *mv_block, + distblk min_mcost, // <-- minimum motion cost (cost for center or huge value) + int* lambda // <-- lagrangian parameter for determining motion cost + ); +extern distblk UMHEXSubPelBlockMotionSearch ( // ==> minimum motion cost after search + Macroblock *currMB, // <-- current Macroblock + MotionVector *pred_mv, // < <-- motion vector predictor (x|y) in sub-pel units + MEBlock *mv_block, + distblk min_mcost, // <-- minimum motion cost (cost for center or huge value) + int lambda_factor // <-- lagrangian parameter for determining motion cost + ); +extern distblk UMHEXBipredIntegerPelBlockMotionSearch (Macroblock *, int, + MotionVector *, MotionVector *, MotionVector *, MotionVector *, + MEBlock *, int, distblk, int); + +extern void UMHEXSetMotionVectorPredictor (Macroblock *currMB, MotionVector *pmv, struct pic_motion_params **mv_info, + short ref_frame, int list, int mb_x, int mb_y, int bl_x, int bl_y, MEBlock *mv_block); + +#endif diff --git a/lencod/inc/me_umhexsmp.h b/lencod/inc/me_umhexsmp.h new file mode 100644 index 0000000..5e2da48 --- /dev/null +++ b/lencod/inc/me_umhexsmp.h @@ -0,0 +1,61 @@ + +/*! + ************************************************************************************* + * + * \file me_umhexsmp.h + * + * \brief + * Fast integer pixel and sub pixel motion estimation + * Improved and simplified from the original UMHexagonS algorithms + * See JVT-P021 for details + * + * \author + * Main contributors: (see contributors.h for copyright, address and affiliation details) + * - Zhibo Chen + * - JianFeng Xu + * - Wenfang Fu + * + * - Xiaoquan Yi + * - Jun Zhang + * + * \date + * 6. Nov. 2006 + ************************************************************************************* + */ + +#ifndef _ME_UMHEXSMP_H_ +#define _ME_UMHEXSMP_H_ + +#include "mbuffer.h" + +struct umhex_smp_struct { + distblk SymmetricalCrossSearchThreshold1; + distblk SymmetricalCrossSearchThreshold2; + distblk ConvergeThreshold; + distblk SubPelThreshold1; + distblk SubPelThreshold3; + byte **SearchState; //state for fractional pel search + distblk ***l0_cost; //store SAD information needed for forward median and uplayer prediction + distblk ***l1_cost; //store SAD information needed for backward median and uplayer prediction + distblk pred_SAD_uplayer; // Up layer SAD prediction + byte *flag_intra; + int flag_intra_SAD; + + short pred_MV_uplayer_X; // Up layer MV predictor X-component + short pred_MV_uplayer_Y; // Up layer MV predictor Y-component +}; + +typedef struct umhex_smp_struct UMHexSMPStruct; + +extern void smpUMHEX_init (VideoParameters *p_Vid); +extern int smpUMHEX_get_mem (VideoParameters *p_Vid); +extern void smpUMHEX_free_mem (VideoParameters *p_Vid); +extern void smpUMHEX_decide_intrabk_SAD(Macroblock *currMB); +extern void smpUMHEX_skip_intrabk_SAD (Macroblock *currMB); +extern void smpUMHEX_setup (Macroblock *currMB, short, int, int, int, int, MotionVector *****); +extern distblk smpUMHEXBipredIntegerPelBlockMotionSearch (Macroblock *, int, MotionVector *, MotionVector *, MotionVector *, MotionVector *, MEBlock *, int, distblk, int); +extern distblk smpUMHEXIntegerPelBlockMotionSearch (Macroblock *currMB, MotionVector *pred_mv, MEBlock *mv_block, distblk min_mcost, int lambda_factor); +extern distblk smpUMHEXSubPelBlockMotionSearch (Macroblock *currMB, MotionVector *pred_mv, MEBlock *mv_block, distblk min_mcost, int lambda_factor); +extern distblk smpUMHEXFullSubPelBlockMotionSearch (Macroblock *currMB, MotionVector *pred_mv, MEBlock *mv_block, distblk min_mcost, int lambda_factor); +extern distblk smpUMHEXSubPelBlockME (Macroblock *currMB, MotionVector *pred_mv, MEBlock *mv_block, distblk min_mcost, int *lambda); +#endif diff --git a/lencod/inc/mmco.h b/lencod/inc/mmco.h new file mode 100644 index 0000000..7b57565 --- /dev/null +++ b/lencod/inc/mmco.h @@ -0,0 +1,32 @@ +/*! + ************************************************************************************** + * \file + * mmco.h + * \brief + * MMCO example operations. + * + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * - Alexis Michael Tourapis + * - Athanasios Leontaris + *************************************************************************************** + */ + +#ifndef _MMCO_H_ +#define _MMCO_H_ + +extern void mmco_long_term(VideoParameters *p_Vid, int current_pic_num); +extern void poc_based_ref_management_frame_pic(DecodedPictureBuffer *p_Dpb, int current_pic_num); +#if CRA +extern void cra_ref_management_frame_pic(DecodedPictureBuffer *p_Dpb, int current_pic_num); +#endif +#if HM50_LIKE_MMCO +extern void hm50_ref_management_frame_pic(DecodedPictureBuffer *p_Dpb, int current_pic_num); +#endif +#if LD_REF_SETTING +extern void low_delay_ref_management_frame_pic(DecodedPictureBuffer *p_Dpb, int current_pic_num); +#endif +extern void poc_based_ref_management_field_pic(DecodedPictureBuffer *p_Dpb, int current_pic_num); +extern void tlyr_based_ref_management_frame_pic(VideoParameters *p_Vid, int current_pic_num); + +#endif diff --git a/lencod/inc/mode_decision.h b/lencod/inc/mode_decision.h new file mode 100644 index 0000000..be322e9 --- /dev/null +++ b/lencod/inc/mode_decision.h @@ -0,0 +1,64 @@ +/*! + *************************************************************************** + * \file + * mode_decision.h + * + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * - Alexis Michael Tourapis + * + * \date + * 21. February 2005 + * + * \brief + * Headerfile for mode decision + ************************************************************************** + */ + +#ifndef _MODE_DECISION_H_ +#define _MODE_DECISION_H_ + +//==== MODULE PARAMETERS ==== +static const int b8_mode_table[6] = {0, 4, 5, 6, 7}; // DO NOT CHANGE ORDER !!! +static const char mb_mode_table[10] = {0, 1, 2, 3, P8x8, I16MB, I4MB, I8MB, IPCM, SI4MB}; // DO NOT CHANGE ORDER !!! +extern distblk BIDPartitionCost (Macroblock *currMB, int, int, char[2], int); +extern distblk BPredPartitionCost (Macroblock *currMB, int, int, short, short, int, int); +extern void StoreMV8x8 (Slice *currSlice, int dir); +extern void RestoreMV8x8 (Slice *currSlice, int dir); +extern void store_macroblock_parameters (Macroblock *currMB, int); +extern void StoreNewMotionVectorsBlock8x8 (Slice *currSlice, int, int, Info8x8 *); +extern void assign_enc_picture_params (Macroblock *currMB, int mode, Info8x8 *best, int block); +extern void set_subblock8x8_info (Block8x8Info*, int, int, RD_8x8DATA*); +extern void set_block8x8_info (Block8x8Info*, int, int, Info8x8 *best); +extern void update_refresh_map (Macroblock *currMB, int intra, int intra1); +extern void get_bipred_cost (Macroblock *currMB, int mode, int block, int i, int j, Info8x8 *best, RD_PARAMS *enc_mb, distblk bmcost[5]); +extern short I16Offset (int, short); +extern int CheckReliabilityOfRef (Macroblock *currMB, int block, int list_idx, int ref, int mode); +extern int RDCost_for_macroblocks (Macroblock *currMB, int, short); +extern int mode_decision_for_I4x4_MB (Macroblock *currMB, int, distblk*); +extern distblk rdcost_for_8x8blocks (Macroblock *currMB, RD_8x8DATA *dataTr, int*, int64*, int, int, short, Info8x8 *, distblk); + +extern void rc_store_diff (int diff[16][16], imgpel **p_curImg, int cpix_x,imgpel **prediction); + +extern void init_enc_mb_params (Macroblock* currMB, RD_PARAMS *enc_mb, int intra); +extern void list_prediction_cost (Macroblock *currMB, int list, int block, int mode, RD_PARAMS *enc_mb, distblk bmcost[5], char best_ref[2]); +extern void determine_prediction_list (distblk [5], Info8x8 *, distblk *); +extern void compute_mode_RD_cost (Macroblock *currMB, RD_PARAMS *enc_mb, short mode, short *inter_skip); + +extern int transform_termination_control (Macroblock* currMB, int mode); +extern int bslice_16x16_termination_control(InputParameters *p_Inp, Block8x8Info *b8x8info, int *ctr16x16, int mode, int bslice); + +distblk distblkminarray ( distblk arr[], int size, int *minind ); +extern int iminarray ( int arr[], int size, int *minind ); + +extern void update_lambda_costs(Macroblock *currMB, RD_PARAMS *enc_mb, int lambda_mf[3]); + +extern void get_initial_mb16x16_cost(Macroblock *currMB); +extern void adjust_mb16x16_cost (Macroblock *currMB, distblk cost); +extern void reset_adaptive_rounding(VideoParameters *p_Vid); + +extern distblk distortionSSE ( Macroblock *currMB ); + + +#endif + diff --git a/lencod/inc/mode_decision_p8x8.h b/lencod/inc/mode_decision_p8x8.h new file mode 100644 index 0000000..ae5c43e --- /dev/null +++ b/lencod/inc/mode_decision_p8x8.h @@ -0,0 +1,31 @@ +/*! + *************************************************************************** + * \file + * mode_decision_p8x8.h + * + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * - Alexis Michael Tourapis + * + * \date + * 1 September 2009 + * + * \brief + * Headerfile for 8x8 mode decision + ************************************************************************** + */ + +#ifndef _MODE_DECISION_P8x8_H_ +#define _MODE_DECISION_P8x8_H_ + +//==== MODULE PARAMETERS ==== +//static const int b8_mode_table[6] = {0, 4, 5, 6, 7}; // DO NOT CHANGE ORDER !!! +//static const char mb_mode_table[10] = {0, 1, 2, 3, P8x8, I16MB, I4MB, I8MB, IPCM, SI4MB}; // DO NOT CHANGE ORDER !!! + +extern void submacroblock_mode_decision_p_slice (Macroblock *currMB, RD_PARAMS *, RD_8x8DATA *, int ****, int, distblk *); +extern void submacroblock_mode_decision_b_slice (Macroblock *currMB, RD_PARAMS *, RD_8x8DATA *, int ****, int, distblk *); +extern void submacroblock_mode_decision_low(Macroblock *currMB, RD_PARAMS *, RD_8x8DATA *, int ****, int *, int, distblk *, distblk *, distblk *, int); +extern void copy_part_info(Info8x8 *b8x8, Info8x8 *part); + +#endif + diff --git a/lencod/inc/mv_search.h b/lencod/inc/mv_search.h new file mode 100644 index 0000000..58908bb --- /dev/null +++ b/lencod/inc/mv_search.h @@ -0,0 +1,140 @@ + +/*! + ************************************************************************ + * \file mv_search.h + * + * \brief + * array definition for motion search + * + * \author + * Inge Lille-Langoy \n + * Alexis Michael Tourapis \n + * + ************************************************************************ + */ + +#ifndef _MV_SEARCH_H_ +#define _MV_SEARCH_H_ + +#if 1 +#define dist_scale_f(x) (min_mcost) +#else +#define dist_scale_f(x) dist_scale(x) +#endif + + +extern void get_neighbors(Macroblock *currMB, PixelPos *block, int mb_x, int mb_y, int blockshape_x); +extern void set_access_method(int *access_method, MotionVector *blk, int min_x, int min_y, int max_x, int max_y); + +extern void PrepareMEParams (Slice *currSlice, MEBlock *mv_block, int ChromaMEEnable, int list, int ref); +extern void PrepareBiPredMEParams(Slice *currSlice, MEBlock *mv_block, int ChromaMEEnable, int list, int list_offset, int ref); + +extern void init_motion_search_module (VideoParameters *p_Vid, InputParameters *p_Inp); +extern void clear_motion_search_module (VideoParameters *p_Vid, InputParameters *p_Inp); + +extern void PartitionMotionSearch (Macroblock *currMB, int, int, int*); +extern void SubPartitionMotionSearch (Macroblock *currMB, int, int, int*); + +extern void Get_Direct_MV_Spatial_MBAFF (Macroblock *currMB); +extern void Get_Direct_MV_Spatial_Normal (Macroblock *currMB); +extern void Get_Direct_MV_Temporal (Macroblock *currMB); + +extern void FindSkipModeMotionVector (Macroblock *currMB); + +extern void init_ME_engine (Macroblock *currMB); +extern distblk BlockMotionSearch (Macroblock *currMB, MEBlock *mv_block, int,int, int*); +extern void init_mv_block (Macroblock *currMB, MEBlock *mv_block, short blocktype, int list, char ref_idx, short mb_x, short mb_y); +extern void get_original_block(VideoParameters *p_Vid, MEBlock *mv_block); +extern void free_mv_block (MEBlock *mv_block); +extern void update_mv_block (Macroblock *currMB, MEBlock *mv_block, int h, int v); +extern void get_search_range(MEBlock *mv_block, InputParameters *p_Inp, short ref, int blocktype); + +static inline void add_mvs(MotionVector *mv0, const MotionVector *mv1) +{ + mv0->mv_x = (short) (mv0->mv_x + mv1->mv_x); + mv0->mv_y = (short) (mv0->mv_y + mv1->mv_y); +} + +static inline MotionVector add_MVs(MotionVector mv0, const MotionVector *mv1) +{ + mv0.mv_x = (short) (mv0.mv_x + mv1->mv_x); + mv0.mv_y = (short) (mv0.mv_y + mv1->mv_y); + + return (mv0); +} + +static inline MotionVector pad_MVs(MotionVector mv0, MEBlock *mv_block) +{ + mv0.mv_x = (short) (mv0.mv_x + mv_block->pos_x_padded); + mv0.mv_y = (short) (mv0.mv_y + mv_block->pos_y_padded); + + return (mv0); +} + +static inline int64 overflow_weight_cost(int lambda, int bits) +{ +#if JCOST_CALC_SCALEUP + return ( ((int64)lambda) * ((int64)(bits)) ); +#else +#if (USE_RND_COST) + return (i64_rshift_rnd_sf(((int64)lambda) * ((int64)(bits)) , LAMBDA_ACCURACY_BITS)); +#else + return (((int64)lambda) * ((int64)(bits)) >> LAMBDA_ACCURACY_BITS); +#endif +#endif +} + +static inline distblk weight_cost(int lambda, int bits) +{ +#if JCOST_CALC_SCALEUP + return ( ((distblk)lambda) * ((distblk)(bits)) ); +#else +#if (USE_RND_COST) + return (rshift_rnd_sf((lambda) * (bits), LAMBDA_ACCURACY_BITS)); +#else + return (((lambda) * (bits)) >> LAMBDA_ACCURACY_BITS); +#endif +#endif +} + +static inline distblk mv_cost(const VideoParameters *p_Vid, int lambda, const MotionVector *mv, const MotionVector *pmv) +{ +#if JCOST_CALC_SCALEUP + int *mvbits = p_Vid->mvbits; + return ( ((distblk)lambda) *((distblk)(mvbits[mv->mv_x - pmv->mv_x] + mvbits[mv->mv_y - pmv->mv_y])) ); +#else +#if (USE_RND_COST) + return (rshift_rnd_sf((lambda *(p_Vid->mvbits[mv->mv_x - pmv->mv_x] + p_Vid->mvbits[mv->mv_y - pmv->mv_y])), LAMBDA_ACCURACY_BITS)); +#else + return ((lambda *(p_Vid->mvbits[mv->mv_x - pmv->mv_x] + p_Vid->mvbits[mv->mv_y - pmv->mv_y]))>> LAMBDA_ACCURACY_BITS); +#endif +#endif +} + +static inline distblk ref_cost(const Slice *currSlice, int lambda, short ref, int list_offset) +{ + if (currSlice->listXsize[list_offset] <= 1) + return 0; + else + { + VideoParameters *p_Vid = currSlice->p_Vid; +#if JCOST_CALC_SCALEUP + return ( ((distblk)lambda) *((distblk)(p_Vid->refbits[(ref)])) ); +#else +#if (USE_RND_COST) + return (rshift_rnd_sf((lambda) * (p_Vid->refbits[(ref)]), LAMBDA_ACCURACY_BITS)); +#else + return ((lambda *(p_Vid->refbits[(ref)]))>> LAMBDA_ACCURACY_BITS); +#endif +#endif + } +} + +static inline int GetMaxMVD(MotionVector *pMV, MotionVector *pRefMV) +{ + int i32MVx = iabs(pMV->mv_x - pRefMV->mv_x); + int i32MVy = iabs(pMV->mv_y - pRefMV->mv_y); + return imax(i32MVx, i32MVy); +} +#endif + diff --git a/lencod/inc/nal.h b/lencod/inc/nal.h new file mode 100644 index 0000000..47237b2 --- /dev/null +++ b/lencod/inc/nal.h @@ -0,0 +1,28 @@ + +/*! + ************************************************************************************** + * \file + * nal.h + * \brief + * NAL related headers + * + * \date 4 Agust 2008 + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * - Karsten Suehring + * - Alexis Michael Tourapis + *************************************************************************************** + */ + + +#ifndef _NAL_H_ +#define _NAL_H_ + +#include "nalucommon.h" +#include "enc_statistics.h" + +extern int addCabacZeroWords(VideoParameters *p_Vid, NALU_t *nalu, StatParameters *cur_stats); +extern void SODBtoRBSP (Bitstream *currStream); +extern int RBSPtoEBSP(byte *NaluBuffer, unsigned char *rbsp, int rbsp_size); + +#endif diff --git a/lencod/inc/nalu.h b/lencod/inc/nalu.h new file mode 100644 index 0000000..9ae854e --- /dev/null +++ b/lencod/inc/nalu.h @@ -0,0 +1,26 @@ + +/*! + ************************************************************************************** + * \file + * nalu.h + * \brief + * Common NALU support functions + * + * \date 25 November 2002 + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * - Stephan Wenger + *************************************************************************************** + */ + + +#ifndef _NALU_H_ +#define _NALU_H_ + +#include "nalucommon.h" + +extern int RBSPtoNALU (unsigned char *rbsp, NALU_t *nalu, int rbsp_size, int nal_unit_type, int nal_reference_idc, int UseAnnexbLongStartcode); +extern int Write_AUD_NALU( VideoParameters *p_Vid ); +extern int Write_Filler_Data_NALU( VideoParameters *p_Vid, int num_bytes ); + +#endif diff --git a/lencod/inc/output.h b/lencod/inc/output.h new file mode 100644 index 0000000..867ff8f --- /dev/null +++ b/lencod/inc/output.h @@ -0,0 +1,26 @@ + +/*! + ************************************************************************************** + * \file + * output.h + * \brief + * Picture writing routine headers + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * - Karsten Suehring + *************************************************************************************** + */ + +#ifndef _OUTPUT_H_ +#define _OUTPUT_H_ + +extern void flush_direct_output(VideoParameters *p_Vid, FrameFormat *output, int p_out); +extern void write_out_picture ( StorablePicture *p, FrameFormat *output, int p_out); +extern void write_stored_frame (VideoParameters *p_Vid, FrameStore *fs, FrameFormat *output, int p_out); +extern void direct_output (VideoParameters *p_Vid, StorablePicture *p, FrameFormat *output, int p_out); +extern void direct_output_paff (VideoParameters *p_Vid, StorablePicture *p, FrameFormat *output, int p_out); +extern void init_out_buffer (VideoParameters *p_Vid); +extern void uninit_out_buffer (VideoParameters *p_Vid); + + +#endif //_OUTPUT_H_ diff --git a/lencod/inc/params.h b/lencod/inc/params.h new file mode 100644 index 0000000..4ca3372 --- /dev/null +++ b/lencod/inc/params.h @@ -0,0 +1,453 @@ +/*! +************************************************************************ +* \file params.h +* +* \brief +* Input parameters related definitions +* +* \author +* +************************************************************************ +*/ + +#ifndef _PARAMS_H_ +#define _PARAMS_H_ + +#include "defines.h" +#include "types.h" +#include "vui_params.h" +#include "frame.h" +#include "io_video.h" + +//! all input parameters +struct inp_par_enc +{ + int ProfileIDC; //!< value of syntax element profile_idc + int LevelIDC; //!< value of syntax element level_idc + int IntraProfile; //!< Enable Intra profiles + + int no_frames; //!< number of frames to be encoded + int qp[NUM_SLICE_TYPES]; //!< QP values for all slice types + int qp2frame; //!< frame in display order from which to apply the Change QP offsets + int qp2off[NUM_SLICE_TYPES]; //!< Change QP offset values for all slice types + int qpsp; //!< QPSP quantization value + int frame_skip; //!< number of frames to skip in input sequence (e.g 2 takes frame 0,3,6,9...) + int jumpd; /*!< number of frames to skip in input sequence including intermediate pictures + (e.g 2 takes frame 0,3,6,9...) */ + int DisableSubpelME[2]; //!< Disable sub-pixel motion estimation + int search_range[2]; /*!< search range - integer pel search and 16x16 blocks. The search window is + generally around the predicted vector. Max vector is 2xmcrange. */ + int num_ref_frames; //!< number of reference frames to be used + int P_List0_refs[2]; //!< number of reference picture in list 0 in P pictures + int B_List0_refs[2]; //!< number of reference picture in list 0 in B pictures + int B_List1_refs[2]; //!< number of reference picture in list 1 in B pictures + int num_ref_frames_org; + int P_List0_refs_org[2]; //!< number of reference picture in list 0 in P pictures + int B_List0_refs_org[2]; //!< number of reference picture in list 0 in B pictures + int B_List1_refs_org[2]; //!< number of reference picture in list 1 in B pictures + int Log2MaxFNumMinus4; //!< value of syntax element log2_max_frame_num + int Log2MaxPOCLsbMinus4; //!< value of syntax element log2_max_pic_order_cnt_lsb_minus4 + + // Input/output sequence format related variables + FrameFormat source; //!< source related information + FrameFormat output; //!< output related information + int is_interleaved; + int src_resize; //!< Control if input sequence will be resized (currently only cropping is supported) + int src_BitDepthRescale; //!< Control if input sequence bitdepth should be adjusted + int yuv_format; //!< YUV format (0=4:0:0, 1=4:2:0, 2=4:2:2, 3=4:4:4) + int intra_upd; /*!< For error robustness. 0: no special action. 1: One GOB/frame is intra coded + as regular 'update'. 2: One GOB every 2 frames is intra coded etc. + In connection with this intra update, restrictions is put on motion vectors + to prevent errors to propagate from the past */ + + int slice_mode; //!< Indicate what algorithm to use for setting slices + int slice_argument; //!< Argument to the specified slice algorithm + int UseConstrainedIntraPred; //!< 0: Inter MB pixels are allowed for intra prediction 1: Not allowed + int SetFirstAsLongTerm; //!< Support for temporal considerations for CB plus encoding + int infile_header; //!< If input file has a header set this to the length of the header + int MultiSourceData; + VideoDataFile input_file2; //!< Input video file2 + VideoDataFile input_file3; //!< Input video file3 + int num_of_views; //!< number of views to encode (1=1view, 2=2views) +#if (MVC_EXTENSION_ENABLE) + char View1ConfigName[FILE_NAME_SIZE]; //! + *************************************************************************************** + */ + + +#ifndef _PARSET_H_ +#define _PARSET_H_ + +#include "parsetcommon.h" +#include "nalu.h" +#include "sei.h" + +extern void generate_parameter_sets (VideoParameters *p_Vid); +extern void FreeParameterSets (VideoParameters *p_Vid); + +extern NALU_t *GenerateSeq_parameter_set_NALU (VideoParameters *p_Vid); +extern NALU_t *GeneratePic_parameter_set_NALU (VideoParameters *p_Vid, int); +extern NALU_t *GenerateSEImessage_NALU(InputParameters *p_Inp); +#if (MVC_EXTENSION_ENABLE) +extern NALU_t *GenerateSubsetSeq_parameter_set_NALU (VideoParameters *p_Vid); +#endif + +// The following are local helpers, but may come handy in the future, hence public +extern void GenerateSequenceParameterSet(seq_parameter_set_rbsp_t *sps, VideoParameters *p_Vid, int SPS_id); +extern void GeneratePictureParameterSet( pic_parameter_set_rbsp_t *pps, seq_parameter_set_rbsp_t *sps, + VideoParameters *p_Vid, + InputParameters *p_Inp, int PPS_id, + int WeightedPrediction, int WeightedBiprediction, + int cb_qp_index_offset, int cr_qp_index_offset); + +extern int Scaling_List(short *scalingListinput, short *scalingList, int sizeOfScalingList, short *UseDefaultScalingMatrix, Bitstream *bitstream); +#if (MVC_EXTENSION_ENABLE) +extern int GenerateSeq_parameter_set_rbsp (VideoParameters *p_Vid, seq_parameter_set_rbsp_t *sps, byte *buf, short Is_Subset); +#else +extern int GenerateSeq_parameter_set_rbsp (VideoParameters *p_Vid, seq_parameter_set_rbsp_t *sps, byte *buf); +#endif +extern int GeneratePic_parameter_set_rbsp (VideoParameters *p_Vid, pic_parameter_set_rbsp_t *pps, byte *buf); +extern int GenerateSEImessage_rbsp (InputParameters *p_Inp, int id, byte *buf); +extern void FreeSPS (seq_parameter_set_rbsp_t *sps); +extern void FreePPS (pic_parameter_set_rbsp_t *pps); + +extern int WriteHRDParameters(seq_parameter_set_rbsp_t *sps, Bitstream *bitstream); +extern void GenerateVUIParameters(seq_parameter_set_rbsp_t *sps, InputParameters *p_Inp); + +extern pic_parameter_set_rbsp_t *AllocPPS (void); +extern seq_parameter_set_rbsp_t *AllocSPS (void); + + +#endif diff --git a/lencod/inc/pred_struct.h b/lencod/inc/pred_struct.h new file mode 100644 index 0000000..111e2dd --- /dev/null +++ b/lencod/inc/pred_struct.h @@ -0,0 +1,35 @@ +/*! + *************************************************************************** + * \file + * pred_struct.h + * + * \author + * Athanasios Leontaris + * + * \date + * June 8, 2009 + * + * \brief + * Header file for prediction structure function headers + ************************************************************************** + */ + +#ifndef _PRED_STRUCT_H_ +#define _PRED_STRUCT_H_ + +#include "global.h" +#include "pred_struct_types.h" +#include "explicit_seq.h" + +extern void get_poc_type_zero( VideoParameters *p_Vid, InputParameters *p_Inp, FrameUnitStruct *p_frm_struct ); +extern void get_poc_type_one( VideoParameters *p_Vid, InputParameters *p_Inp, FrameUnitStruct *p_frm_struct ); +extern void init_poc(VideoParameters *p_Vid); +extern SeqStructure * init_seq_structure( VideoParameters *p_Vid, InputParameters *p_Inp, int *memory_size ); +extern void free_seq_structure( SeqStructure *p_seq_struct ); +extern void populate_frm_struct( VideoParameters *p_Vid, InputParameters *p_Inp, SeqStructure *p_seq_struct, int num_to_populate, int init_frames_to_code ); +extern void populate_frame_explicit( ExpFrameInfo *info, InputParameters *p_Inp, FrameUnitStruct *p_frm_struct, int num_slices ); +extern void populate_frame_slice_type( InputParameters *p_Inp, FrameUnitStruct *p_frm_struct, int slice_type, int num_slices ); +extern void populate_reg_pic( InputParameters *p_Inp, PicStructure *p_pic, FrameUnitStruct *p_frm_struct, int num_slices, int is_bot_fld ); +extern void populate_frm_struct_mvc( VideoParameters *p_Vid, InputParameters *p_Inp, SeqStructure *p_seq_struct, int start, int end ); + +#endif diff --git a/lencod/inc/pred_struct_adapt.h b/lencod/inc/pred_struct_adapt.h new file mode 100644 index 0000000..8ead686 --- /dev/null +++ b/lencod/inc/pred_struct_adapt.h @@ -0,0 +1,23 @@ +/*! + *************************************************************************** + * \file + * pred_struct_adapt.h + * + * \author + * Athanasios Leontaris + * + * \date + * July 21, 2009 + * + * \brief + * Header file for adaptive prediction structure function headers + ************************************************************************** + */ + +#ifndef _PRED_STRUCT_ADAPT_H_ +#define _PRED_STRUCT_ADAPT_H_ + +#include "global.h" + + +#endif diff --git a/lencod/inc/pred_struct_adapt_types.h b/lencod/inc/pred_struct_adapt_types.h new file mode 100644 index 0000000..83c8044 --- /dev/null +++ b/lencod/inc/pred_struct_adapt_types.h @@ -0,0 +1,22 @@ +/*! + *************************************************************************** + * \file + * pred_struct_adapt_types.h + * + * \author + * Athanasios Leontaris + * + * \date + * July 21, 2009 + * + * \brief + * Header file for adaptive prediction structure types + ************************************************************************** + */ + +#ifndef _PRED_STRUCT_ADAPT_TYPES_H_ +#define _PRED_STRUCT_ADAPT_TYPES_H_ + + + +#endif diff --git a/lencod/inc/pred_struct_types.h b/lencod/inc/pred_struct_types.h new file mode 100644 index 0000000..565de23 --- /dev/null +++ b/lencod/inc/pred_struct_types.h @@ -0,0 +1,143 @@ +/*! + *************************************************************************** + * \file + * pred_struct_types.h + * + * \author + * Athanasios Leontaris + * + * \date + * June 8, 2009 + * + * \brief + * Header file for prediction structure types + ************************************************************************** + */ + +#ifndef _PRED_STRUCT_TYPES_H_ +#define _PRED_STRUCT_TYPES_H_ + +// default coding structures +// these are utilized to compress the current sequence when they are not pre-defined +// sizes are 1 to 4 frames: if a length 4 structure does not *fit* then we try a length 3, 2, or 1 structure. +// One can define also structures > length 4. If these structures do not fit then we attempt to replace them 4, 3, 2, or 1. +// We do not define default structures > 4 since the coding gains are questionable and the increase in memory and +// computational compleity is usually prohibitive (e.g. Blu-Ray can do up to length 4 in HD resolution). + +// After a prediction structure atom has been coded and the coding order of next frame to code is "code_order" +// then all frames with frame_no lesser or equal to "code_order" times (frame_skip + 1) have been coded. + +// non idr atoms +// idr atoms (for intra_delay) + +typedef enum +{ + POP_IDR = 1, + POP_INTRA = 2, + POP_SP = 3 +} SliceTypeToPop; + +// prediction frame structure +typedef struct pred_struct_frm +{ + int disp_offset; + int slice_type; + int nal_ref_idc; + int layer; // the lower the higher the priority + int slice_qp_off; + int random_access; + int temporal_layer; +} PredStructFrm; + +// prediction structure +typedef struct pred_struct_atom +{ + int length; + int gop_levels; + PredStructFrm *p_frm; +} PredStructAtom; + +// slice structure +typedef struct slice_struct +{ + int type; + int qp; + int deblock; + int weighted_pred; + int num_refs; +} SliceStructure; + +// picture structure (frame, top field, bottom field) +typedef struct pic_struct +{ + int idr_flag; + int nal_ref_idc; + int random_access; // to support open GOPs + int num_slices; + SliceStructure *p_Slice; +} PicStructure; + +// frame structure +typedef struct frame_struct +{ + int type; // dominant + int idr_flag; + int nal_ref_idc; + int frame_no; + int field_pic_flag; + int mod_qp; // QP modifier with respect to the slice type default QP + int qp; // QP used to code this frame unit + int layer; + int num_refs; + int random_access; // random access point (IDR or Intra-coded picture that precludes future pictures in display order to reference pictures decoded prior to this picture) + int atom_idx; // index in the prediction structure pointed to be *p_atom + int temporal_layer; +#if (MVC_EXTENSION_ENABLE) + int view_id; +#endif + PicStructure *p_frame_pic; + PicStructure *p_top_fld_pic; + PicStructure *p_bot_fld_pic; + PredStructAtom *p_atom; // pointer to the prediction structure type to which this frame belongs +} FrameUnitStruct; // here "frame" is as in the real thing: the displayed frame; not as "frame" in "frame picture" in H.264 + +// sequence structure +typedef struct seq_struct +{ + int num_frames; + int num_prds; + int num_gops; + int num_intra_gops; + + // frame indices based on *coding* order + int last_random_access_frame; + int last_idr_frame; + int last_intra_frame; + int last_mmco_5_frame; // values during building the structure: have *not* happened yet! + int last_sp_frame; + // frame indices based on *display* order + int last_rand_access_disp; + int last_idr_disp; + int last_intra_disp; + int last_sp_disp; + int last_bl_frm_disposable; // check whether the last frame with layer == 0 (highest priority) + + int curr_num_to_populate; // number of future frames in coding order to populate with each iteration of function populate_frm_struct (note that there are cases that fewer than "num_to_populate" frames may be populated + int pop_start_frame; // frame index (in coding order) from which frame structure population commences + + int max_num_slices; + int pop_flag; + +#if (MVC_EXTENSION_ENABLE) + int num_frames_mvc; + FrameUnitStruct *p_frm_mvc; // frame struct store for num_views > 1 +#endif + + FrameUnitStruct *p_frm; + + PredStructAtom *p_prd; // regular prediction structure + PredStructAtom *p_gop; // IDR GOPs + PredStructAtom *p_intra_gop; // Intra GOPs +} SeqStructure; + +#endif diff --git a/lencod/inc/q_around.h b/lencod/inc/q_around.h new file mode 100644 index 0000000..f0c9d18 --- /dev/null +++ b/lencod/inc/q_around.h @@ -0,0 +1,26 @@ +/*! + *************************************************************************** + * \file + * q_around.h + * + * \author + * Alexis Michael Tourapis + * + * \brief + * Headerfile for Quantization Adaptive Rounding + ************************************************************************** + */ + +#ifndef _Q_AROUND_H_ +#define _Q_AROUND_H_ + +extern void update_adaptive_rounding_8x8 (VideoParameters *p_Vid, InputParameters *p_Inp, RD_8x8DATA* dataTr, int**** ARCofAdj); +extern void store_adaptive_rounding_4x4 (VideoParameters *p_Vid, int****ARCofAdj, int mode, int block_y, int block_x); +extern void update_adaptive_rounding_4x4 (VideoParameters *p_Vid, int****ARCofAdj , int mode, int block_y, int block_x); +extern void update_adaptive_rounding_16x16(VideoParameters *p_Vid, int****ARCofAdj , int mode); +extern void store_adaptive_rounding_16x16 (VideoParameters *p_Vid, int****ARCofAdj, int mode); +extern void reset_adaptive_rounding_direct(VideoParameters *p_Vid); +extern void update_offset_params (Macroblock *currMB, int mode, byte luma_transform_size_8x8_flag); + +#endif + diff --git a/lencod/inc/q_matrix.h b/lencod/inc/q_matrix.h new file mode 100644 index 0000000..e7e8fef --- /dev/null +++ b/lencod/inc/q_matrix.h @@ -0,0 +1,33 @@ + +/*! + *************************************************************************** + * \file + * q_matrix.h + * + * \brief + * Headerfile for q_matrix array + * + * \date + * 07. Apr 2004 + *************************************************************************** + */ + +#ifndef _Q_MATRIX_H_ +#define _Q_MATRIX_H_ + +struct scaling_list { + short ScalingList4x4input[6][16]; + short ScalingList8x8input[6][64]; + short ScalingList4x4[6][16]; + short ScalingList8x8[6][64]; + + short UseDefaultScalingMatrix4x4Flag[6]; + short UseDefaultScalingMatrix8x8Flag[6]; +}; + +extern void init_qmatrix (VideoParameters *p_Vid, InputParameters *p_Inp); +extern void CalculateQuant4x4Param (VideoParameters *p_Vid); +extern void CalculateQuant8x8Param (VideoParameters *p_Vid); +extern void free_QMatrix(QuantParameters *p_Quant); + +#endif diff --git a/lencod/inc/q_offsets.h b/lencod/inc/q_offsets.h new file mode 100644 index 0000000..d566e6b --- /dev/null +++ b/lencod/inc/q_offsets.h @@ -0,0 +1,25 @@ + +/*! + *************************************************************************** + * \file + * q_offsets.h + * + * \brief + * Headerfile for q_offsets array + * + * \date + * 18. Nov 2004 + *************************************************************************** + */ + +#ifndef _Q_OFFSETS_H_ +#define _Q_OFFSETS_H_ + +static const int OffsetBits = 11; + +extern void init_qoffset (VideoParameters *p_Vid); +extern void CalculateOffset4x4Param (VideoParameters *p_Vid); +extern void CalculateOffset8x8Param (VideoParameters *p_Vid); +extern void free_QOffsets (QuantParameters *p_Quant, InputParameters *p_Inp); +extern void InitOffsetParam (QuantParameters *p_Quant, InputParameters *p_Inp); +#endif diff --git a/lencod/inc/quant4x4.h b/lencod/inc/quant4x4.h new file mode 100644 index 0000000..fcdfdb4 --- /dev/null +++ b/lencod/inc/quant4x4.h @@ -0,0 +1,53 @@ + +/*! +************************************************************************ +* \file quant4x4.h +* +* \brief +* Quantization process header file +* +* \author +* Limin Liu +* Alexis Michael Tourapis +* +************************************************************************ +*/ + +#ifndef _QUANT4x4_H_ +#define _QUANT4x4_H_ + +extern void init_quant_4x4 (Slice *currSlice); + +extern int quant_4x4_normal (Macroblock *currMB, int **tblock, struct quant_methods *q_method); +extern int quant_4x4_around (Macroblock *currMB, int **tblock, struct quant_methods *q_method); +extern int quant_4x4_trellis(Macroblock *currMB, int **tblock, struct quant_methods *q_method); +extern int quant_dc4x4_normal (Macroblock *currMB, int **tblock, int qp, int* DCLevel, int* DCRun, + LevelQuantParams *q_params_4x4, const byte (*pos_scan)[2]); + +extern int quant_dc4x4_around (Macroblock *currMB, int **tblock, int qp, int* DCLevel, int* DCRun, + LevelQuantParams *q_params_4x4, const byte (*pos_scan)[2]); + +extern int quant_dc4x4_trellis(Macroblock *currMB, int **tblock, int qp, int* DCLevel, int* DCRun, + LevelQuantParams *q_params_4x4, const byte (*pos_scan)[2]); + +extern int quant_ac4x4_normal (Macroblock *currMB, int **tblock, struct quant_methods *q_method); +extern int quant_ac4x4_around (Macroblock *currMB, int **tblock, struct quant_methods *q_method); +extern int quant_ac4x4_trellis(Macroblock *currMB, int **tblock, struct quant_methods *q_method); + +extern void rdoq_4x4_CAVLC (Macroblock *currMB, int **tblock, struct quant_methods *q_method, int levelTrellis[16]); + +extern void rdoq_4x4_CABAC (Macroblock *currMB, int **tblock, struct quant_methods *q_method, int levelTrellis[16]); + +extern void rdoq_dc_CAVLC (Macroblock *currMB, int **tblock, int qp_per, int qp_rem, LevelQuantParams *q_params_4x4, + const byte (*pos_scan)[2], int levelTrellis[16], int type); + +extern void rdoq_dc_CABAC (Macroblock *currMB, int **tblock, int qp_per, int qp_rem, LevelQuantParams *q_params_4x4, + const byte (*pos_scan)[2], int levelTrellis[16], int type); + +extern void rdoq_ac4x4_CAVLC (Macroblock *currMB, int **tblock, struct quant_methods *q_method, int levelTrellis[16]); + +extern void rdoq_ac4x4_CABAC (Macroblock *currMB, int **tblock , struct quant_methods *q_method, int levelTrellis[16]); + + +#endif + diff --git a/lencod/inc/quant8x8.h b/lencod/inc/quant8x8.h new file mode 100644 index 0000000..1f05fba --- /dev/null +++ b/lencod/inc/quant8x8.h @@ -0,0 +1,29 @@ + +/*! +************************************************************************ +* \file quant8x8.h +* +* \brief +* Quantization process header file +* +* \author +* Alexis Michael Tourapis +* +************************************************************************ +*/ + +#ifndef _QUANT8x8_H_ +#define _QUANT8x8_H_ + +extern void init_quant_8x8 (Slice *currSlice); + +extern int quant_8x8_normal (Macroblock *currMB, int **tblock, struct quant_methods *q_method); +extern int quant_8x8_around (Macroblock *currMB, int **tblock, struct quant_methods *q_method); +extern int quant_8x8_trellis(Macroblock *currMB, int **tblock, struct quant_methods *q_method); + +extern int quant_8x8cavlc_around (Macroblock *currMB, int **tblock, struct quant_methods *q_method, int*** cofAC); +extern int quant_8x8cavlc_normal (Macroblock *currMB, int **tblock, struct quant_methods *q_method, int*** cofAC); +extern int quant_8x8cavlc_trellis(Macroblock *currMB, int **tblock, struct quant_methods *q_method, int*** cofAC); + +#endif + diff --git a/lencod/inc/quantChroma.h b/lencod/inc/quantChroma.h new file mode 100644 index 0000000..18105fa --- /dev/null +++ b/lencod/inc/quantChroma.h @@ -0,0 +1,47 @@ + +/*! +************************************************************************ +* \file quantChroma.h +* +* \brief +* Quantization process for chroma header file +* +* \author +* Limin Liu +* Alexis Michael Tourapis +* +************************************************************************ +*/ + +#ifndef _QUANT_CR_H_ +#define _QUANT_CR_H_ + +extern void init_quant_Chroma(Slice *currSlice); + +extern int quant_dc2x2_normal (Macroblock *currMB, int **tblock, int qp, int* DCLevel, int* DCRun, + LevelQuantParams *q_params_4x4, int **fadjust2x2, const byte (*pos_scan)[2]); + +extern int quant_dc2x2_around (Macroblock *currMB, int **tblock, int qp, int* DCLevel, int* DCRun, + LevelQuantParams *q_params_4x4, int **fadjust2x2, const byte (*pos_scan)[2]); + +extern int quant_dc2x2_trellis(Macroblock *currMB, int **tblock, int qp, int* DCLevel, int* DCRun, + LevelQuantParams *q_params_4x4, int **fadjust, const byte (*pos_scan)[2]); + +extern int quant_dc4x2_normal (Macroblock *currMB, int **tblock, int qp, int* DCLevel, int* DCRun, + LevelQuantParams *q_params_4x4, int **fadjust, const byte (*pos_scan)[2]); + +extern int quant_dc4x2_around (Macroblock *currMB, int **tblock, int qp, int* DCLevel, int* DCRun, + LevelQuantParams *q_params_4x4, int **fadjust, const byte (*pos_scan)[2]); + +extern int quant_dc4x2_trellis(Macroblock *currMB, int **tblock, int qp, int* DCLevel, int* DCRun, + LevelQuantParams *q_params_4x4, int **fadjust, const byte (*pos_scan)[2]); + +extern void rdoq_dc_cr_CAVLC (Macroblock *currMB, int **tblock, int qp_per, int qp_rem, + LevelQuantParams *q_params_4x4, + const byte (*pos_scan)[2], int levelTrellis[16], int type); + +extern void rdoq_dc_cr_CABAC (Macroblock *currMB, int **tblock, int qp_per, int qp_rem, + LevelQuantParams *q_params_4x4, const byte (*pos_scan)[2], int levelTrellis[16], int type); + +#endif + diff --git a/lencod/inc/ratectl.h b/lencod/inc/ratectl.h new file mode 100644 index 0000000..21b9891 --- /dev/null +++ b/lencod/inc/ratectl.h @@ -0,0 +1,44 @@ + +/*! + *************************************************************************** + * \file + * ratectl.h + * + * \author + * Zhengguo LI + * Athanasios Leontaris + * + * \date + * 14 Jan 2003 + * + * \brief + * Headerfile for rate control + ************************************************************************** + */ + +#ifndef _RATE_CTL_H_ +#define _RATE_CTL_H_ + +#include "global.h" +#include "rc_quadratic.h" + + +// generic functions +extern int Qstep2QP ( double Qstep, int qp_offset ); +extern double QP2Qstep ( int QP ); +extern int ComputeMBMAD ( int diff[16][16] ); +extern double ComputeFrameMAD ( VideoParameters *p_Vid ); +extern void rc_store_mad ( Macroblock *currMB ); + +// rate control functions +// init/copy +extern void rc_alloc_generic ( VideoParameters *p_Vid, RCGeneric **p_quad ); +extern void rc_free_generic ( RCGeneric **p_quad ); +extern void rc_copy_generic ( VideoParameters *p_Vid, RCGeneric *dst, RCGeneric *src ); +extern void rc_init_gop_params ( VideoParameters *p_Vid, InputParameters *p_Inp ); +extern void rc_init_frame ( VideoParameters *p_Vid, InputParameters *p_Inp); +extern void rc_init_sequence ( VideoParameters *p_Vid, InputParameters *p_Inp); +extern void rc_store_slice_header_bits ( VideoParameters *p_Vid, InputParameters *p_Inp, int len); + +#endif + diff --git a/lencod/inc/rc_quadratic.h b/lencod/inc/rc_quadratic.h new file mode 100644 index 0000000..1672b2f --- /dev/null +++ b/lencod/inc/rc_quadratic.h @@ -0,0 +1,76 @@ + +/*! + *************************************************************************** + * \file + * rc_quadratic.h + * + * \author + * Zhengguo LI + * Athanasios Leontaris + * + * \date + * 14 Jan 2003 + * + * \brief + * Headerfile for rate control + ************************************************************************** + */ + +#ifndef _RC_QUADRATIC_H_ +#define _RC_QUADRATIC_H_ + +#include "rc_types.h" + +// rate control functions +// init/copy +extern void rc_alloc_quadratic( VideoParameters *p_Vid, InputParameters *p_Inp, RCQuadratic **p_quad ); +extern void rc_free_quadratic ( RCQuadratic **p_quad ); +extern void rc_copy_quadratic ( VideoParameters *p_Vid, InputParameters *p_Inp, RCQuadratic *dst, RCQuadratic *src ); + +// rate control (externally visible) +extern void rc_init_seq (VideoParameters *p_Vid, InputParameters *p_Inp, RCQuadratic *p_quad, RCGeneric *p_gen); +extern void rc_init_GOP (VideoParameters *p_Vid, InputParameters *p_Inp, RCQuadratic *p_quad, RCGeneric *p_gen, int np, int nb); +extern void rc_update_pict_frame (VideoParameters *p_Vid, InputParameters *p_Inp, RCQuadratic *p_quad, RCGeneric *p_gen, int nbits); +extern void rc_init_pict (VideoParameters *p_Vid, InputParameters *p_Inp, + RCQuadratic *p_quad, RCGeneric *p_gen, int fieldpic, int topfield, int targetcomputation, float mult); +extern void rc_update_pict (VideoParameters *p_Vid, InputParameters *p_Inp, RCQuadratic *p_quad, RCGeneric *p_gen, int nbits); +extern void rc_update_picture (VideoParameters *p_Vid, InputParameters *p_Inp, int bits); + +extern int updateQPRC0(VideoParameters *p_Vid, InputParameters *p_Inp, RCQuadratic *p_quad, RCGeneric *p_gen, int topfield); +extern int updateQPRC1(VideoParameters *p_Vid, InputParameters *p_Inp, RCQuadratic *p_quad, RCGeneric *p_gen, int topfield); +extern int updateQPRC2(VideoParameters *p_Vid, InputParameters *p_Inp, RCQuadratic *p_quad, RCGeneric *p_gen, int topfield); +extern int updateQPRC3(VideoParameters *p_Vid, InputParameters *p_Inp, RCQuadratic *p_quad, RCGeneric *p_gen, int topfield); + +// internal functions +extern void updateQPInterlace ( RCQuadratic *p_quad, RCGeneric *p_gen ); +extern void updateQPNonPicAFF ( seq_parameter_set_rbsp_t *active_sps, RCQuadratic *p_quad ); +extern void updateBottomField ( InputParameters *p_Inp, RCQuadratic *p_quad ); +extern int updateFirstP ( VideoParameters *p_Vid, InputParameters *p_Inp, RCQuadratic *p_quad, RCGeneric *p_gen, int topfield ); +extern int updateNegativeTarget( VideoParameters *p_Vid, InputParameters *p_Inp, RCQuadratic *p_quad, RCGeneric *p_gen, int topfield, int m_Qp ); +extern int updateFirstBU ( VideoParameters *p_Vid, InputParameters *p_Inp, RCQuadratic *p_quad, RCGeneric *p_gen, int topfield ); +extern void updateLastBU ( VideoParameters *p_Vid, InputParameters *p_Inp, RCQuadratic *p_quad, RCGeneric *p_gen, int topfield ); +extern void predictCurrPicMAD ( InputParameters *p_Inp, RCQuadratic *p_quad, RCGeneric *p_gen ); +extern void updateModelQPBU ( VideoParameters *p_Vid, InputParameters *p_Inp, RCQuadratic *p_quad, int m_Qp ); +extern void updateQPInterlaceBU ( RCQuadratic *p_quad, RCGeneric *p_gen ); +extern void updateModelQPFrame ( RCQuadratic *p_quad, int m_Bits ); + +extern void updateRCModel (VideoParameters *p_Vid, InputParameters *p_Inp, RCQuadratic *p_quad, RCGeneric *p_gen); +extern void updateMADModel (VideoParameters *p_Vid, InputParameters *p_Inp, RCQuadratic *p_quad, RCGeneric *p_gen); +extern void RCModelEstimator (VideoParameters *p_Vid, InputParameters *p_Inp, RCQuadratic *p_quad, int n_windowSize, Boolean *m_rgRejected); +extern void MADModelEstimator(VideoParameters *p_Vid, InputParameters *p_Inp, RCQuadratic *p_quad, int n_windowSize, Boolean *PictureRejected); +extern int updateComplexity (VideoParameters *p_Vid, RCQuadratic *p_quad, RCGeneric *p_gen, Boolean is_updated, int nbits ); +extern void updatePparams (RCQuadratic *p_quad, RCGeneric *p_gen, int complexity ); +extern void updateBparams (RCQuadratic *p_quad, RCGeneric *p_gen, int complexity ); + +// external generic functions +extern int rc_handle_mb ( Macroblock *currMB, int prev_mb); +extern void rc_init_top_field ( VideoParameters *p_Vid, InputParameters *p_Inp ); +extern void rc_init_bottom_field ( VideoParameters *p_Vid, InputParameters *p_Inp, int TopFieldBits ); +extern void rc_init_frame_rdpic ( VideoParameters *p_Vid, InputParameters *p_Inp, float rateRatio ); +extern void rc_allocate_memory ( VideoParameters *p_Vid, InputParameters *p_Inp ); +extern void rc_free_memory ( VideoParameters *p_Vid, InputParameters *p_Inp ); +extern void rc_update_mb_stats ( Macroblock *currMB); +extern void rc_save_state ( VideoParameters *p_Vid, InputParameters *p_Inp ); +extern void rc_restore_state ( VideoParameters *p_Vid, InputParameters *p_Inp ); + +#endif diff --git a/lencod/inc/rc_types.h b/lencod/inc/rc_types.h new file mode 100644 index 0000000..9394f49 --- /dev/null +++ b/lencod/inc/rc_types.h @@ -0,0 +1,156 @@ + +/*! + *************************************************************************** + * \file + * rc_types.h + * + * \author + * Zhengguo LI + * Athanasios Leontaris + * + * \date + * 14 Jan 2003 + * 27 Apr 2009 + * + * \brief + * Headerfile for basic rate control structures + ************************************************************************** + */ + +#ifndef _RC_TYPES_H_ +#define _RC_TYPES_H_ + +#define RC_MODEL_HISTORY 21 + +/* generic rate control variables */ +typedef struct rc_generic +{ + // RC flags + int TopFieldFlag; + int FieldControl; + int FieldFrame; + int NoGranularFieldRC; + // bits stats + int NumberofHeaderBits; + int NumberofTextureBits; + int NumberofBasicUnitHeaderBits; + int NumberofBasicUnitTextureBits; + // frame stats + int NumberofGOP; + int NumberofCodedBFrame; + // MAD stats + int64 TotalMADBasicUnit; + int *MADofMB; + // buffer and budget + int64 CurrentBufferFullness; //LIZG 25/10/2002 + int64 RemainingBits; + // bit allocations for RC_MODE_3 + int RCPSliceBits; + int RCISliceBits; + int RCBSliceBits[RC_MAX_TEMPORAL_LEVELS]; + int temporal_levels; + int hierNb[RC_MAX_TEMPORAL_LEVELS]; + int NPSlice; + int NISlice; +} RCGeneric; + +typedef struct rc_quadratic +{ + float bit_rate; + float frame_rate; + float PrevBitRate; //LIZG 25/10/2002 + double GAMMAP; //LIZG, JVT019r1 + double BETAP; //LIZG, JVT019r1 + double GOPTargetBufferLevel; + double TargetBufferLevel; //LIZG 25/10/2002 + double AveWp; + double AveWb; + int MyInitialQp; + int PAverageQp; + double PreviousPictureMAD; + double MADPictureC1; + double MADPictureC2; + double PMADPictureC1; + double PMADPictureC2; + double PPictureMAD [RC_MODEL_HISTORY]; + double PictureMAD [RC_MODEL_HISTORY]; + double ReferenceMAD[RC_MODEL_HISTORY]; + double m_rgQp [RC_MODEL_HISTORY]; + double m_rgRp [RC_MODEL_HISTORY]; + double Pm_rgQp [RC_MODEL_HISTORY]; + double Pm_rgRp [RC_MODEL_HISTORY]; + + double m_X1; + double m_X2; + double Pm_X1; + double Pm_X2; + int Pm_Qp; + int Pm_Hp; + + int MADm_windowSize; + int m_windowSize; + int m_Qc; + + int PPreHeader; + int PrevLastQP; // QP of the second-to-last coded frame in the primary layer + int CurrLastQP; // QP of the last coded frame in the primary layer + int NumberofBFrames; + // basic unit layer rate control + int TotalFrameQP; + int NumberofBasicUnit; + int PAveHeaderBits1; + int PAveHeaderBits2; + int PAveHeaderBits3; + int PAveFrameQP; + int TotalNumberofBasicUnit; + int CodedBasicUnit; + + int NumberofCodedPFrame; + int TotalQpforPPicture; + int NumberofPPicture; + + double CurrentFrameMAD; + double CurrentBUMAD; + double TotalBUMAD; + double PreviousFrameMAD; + double PreviousWholeFrameMAD; + + int DDquant; + unsigned int MBPerRow; + int QPLastPFrame; + int QPLastGOP; + + // adaptive field/frame coding + int FieldQPBuffer; + int FrameQPBuffer; + int FrameAveHeaderBits; + int FieldAveHeaderBits; + double *BUPFMAD; + double *BUCFMAD; + double *FCBUCFMAD; + double *FCBUPFMAD; + + Boolean GOPOverdue; + int64 Pprev_bits; + + // rate control variables + int Xp, Xb; + int Target; + int TargetField; + int Np, Nb, bits_topfield; + // HRD consideration + int UpperBound1, UpperBound2, LowerBound; + double Wp, Wb; // complexity weights + double DeltaP; + int TotalPFrame; + int PMaxQpChange; + + int bitdepth_qp_scale; // support negative QPs (bitdepth > 8-bits per component) + + // simple encoder buffer simulation + int enc_buf_curr; + +} RCQuadratic; + +#endif + diff --git a/lencod/inc/rd_intra_jm.h b/lencod/inc/rd_intra_jm.h new file mode 100644 index 0000000..d480e82 --- /dev/null +++ b/lencod/inc/rd_intra_jm.h @@ -0,0 +1,27 @@ +/*! + *************************************************************************** + * \file + * rd_intra_jm.h + * + * \author + * Alexis Michael Tourapis + * + * \date + * 2 January 2008 + * + * \brief + * Headerfile for JM rd based intra mode decision + ************************************************************************** + */ + +#ifndef _RD_INTRA_JM_H_ +#define _RD_INTRA_JM_H_ + +extern int mode_decision_for_I16x16_MB_RDO (Macroblock *currMB, int lambda); +extern int mode_decision_for_I16x16_MB (Macroblock *currMB, int lambda); +extern int mode_decision_for_I4x4_blocks_JM_High (Macroblock *currMB, int b8, int b4, int lambda, distblk* min_cost); +extern int mode_decision_for_I4x4_blocks_JM_Low (Macroblock *currMB, int b8, int b4, int lambda, distblk* min_cost); +extern int find_best_mode_I16x16_MB (Macroblock *currMB, int lambda, distblk min_cost); + +#endif + diff --git a/lencod/inc/rd_intra_jm444.h b/lencod/inc/rd_intra_jm444.h new file mode 100644 index 0000000..e43c7af --- /dev/null +++ b/lencod/inc/rd_intra_jm444.h @@ -0,0 +1,24 @@ +/*! + *************************************************************************** + * \file + * rd_intra_jm444.h + * + * \author + * Alexis Michael Tourapis + * + * \date + * 2 January 2008 + * + * \brief + * Headerfile for JM rd based intra mode decision (High444) + ************************************************************************** + */ + +#ifndef _RD_INTRA_JM444_H_ +#define _RD_INTRA_JM444_H_ + +extern int mode_decision_for_I16x16_MB_444 (Macroblock *currMB, int lambda); +extern int mode_decision_for_I4x4_blocks_JM_High444 (Macroblock *currMB, int b8, int b4, int lambda, distblk* min_cost); +extern int mode_decision_for_I4x4_blocks_JM_Low444 (Macroblock *currMB, int b8, int b4, int lambda, distblk* min_cost); +#endif + diff --git a/lencod/inc/rdopt.h b/lencod/inc/rdopt.h new file mode 100644 index 0000000..d3bb79c --- /dev/null +++ b/lencod/inc/rdopt.h @@ -0,0 +1,132 @@ +/*! + *************************************************************************** + * \file + * rdopt.h + * + * \author + * Alexis Michael Tourapis + * + * \date + * 2 January 2008 + * + * \brief + * Headerfile for RDO + ************************************************************************** + */ + +#ifndef _RDO_H_ +#define _RDO_H_ + +#include "global.h" +#include "rdopt_coding_state.h" + +// Motion Vector structure +typedef struct +{ + double rdcost; + double dcost; + double rate; // why is rate a double? Could we use int64 or even simply int given that this is for a MB? Could a MB require int64 bits (answer is no)? + int cbp; + distblk cost; + short c_imode; + short i16offset; + byte mode; +} BestMode; + +struct rdo_structure +{ + RD_8x8DATA *tr4x4; + RD_8x8DATA *tr8x8; + imgpel ***rec8x8; + imgpel ***rec4x4; + imgpel **pred; + imgpel ***rec_mb; + int **lrec_rec; + int ***lrec_rec_uv; + + int ****cofAC; + int ***cofDC; + int **cofAC4x4; + int ****cofAC4x4intern; + + MotionVector ****all_mv8x8; + + int *****cofAC4x4CbCrintern; + int *****cofAC8x8ts; // [plane][8x8block][4x4block][level/run][scan_pos] + int *****coefAC8x8; + int *****coefAC8x8intra; + int **cofAC4x4CbCr[2]; + + + int cbp; + char **l0_refframe; + char **l1_refframe; + + Info8x8 best8x8[4]; + + CSobj *cs_mb; + CSobj *cs_b8; + CSobj *cs_cm; + CSobj *cs_tmp; + + BestMode mode_best; + +// adaptive langrangian parameters + double lambda_mf_factor; +}; + + +extern byte field_flag_inference (Macroblock *currMB); +extern int valid_intra_mode(Slice *currSlice, int ipmode); + +extern void init_md_best(BestMode *best); + +//============= rate-distortion optimization =================== +extern void clear_rdopt (Slice *currSlice); +extern void init_rdopt (Slice *currSlice); + +extern void UpdatePixelMap(VideoParameters *p_Vid, InputParameters *p_Inp); + +extern void update_qp_cbp (Macroblock *currMB); +extern void update_qp_cbp_tmp (Macroblock *currMB, int cbp); + +extern void alloc_rd8x8data (RD_8x8DATA *rd_data); +extern void free_rd8x8data (RD_8x8DATA *rd_data); + +extern void restore_nz_coeff(Macroblock *currMB); + +extern void end_encode_one_macroblock(Macroblock *currMB); + +extern void encode_one_macroblock_low (Macroblock *currMB); +extern void encode_one_macroblock_high (Macroblock *currMB); +extern void encode_one_macroblock_highfast (Macroblock *currMB); +extern void encode_one_macroblock_highloss (Macroblock *currMB); + +extern void store_8x8_motion_vectors_p_slice (Slice *currSlice, int dir, int block8x8, Info8x8 *B8x8Info); +extern void store_8x8_motion_vectors_b_slice (Slice *currSlice, int dir, int block8x8, Info8x8 *B8x8Info); + +extern void set_modes_and_refs_for_blocks_p_slice(Macroblock *currMB, short mode); +extern void set_modes_and_refs_for_blocks_b_slice(Macroblock *currMB, short mode); +extern void set_modes_and_refs_for_blocks_i_slice(Macroblock *currMB, short mode); + +extern void set_coeff_and_recon_8x8_p_slice (Macroblock* currMB); +extern void set_coeff_and_recon_8x8_b_slice (Macroblock* currMB); + +extern Info8x8 init_info_8x8_struct(void); + +/*! + ************************************************************************************* + * \brief + * copy data in iblock to oblock + ************************************************************************************* + */ +static inline void copy_4x4block(imgpel **oblock, imgpel **iblock, int o_xoffset, int i_xoffset) +{ + int y; + for (y = 0; y < BLOCK_SIZE; y++) + { + memcpy(&oblock[y][o_xoffset],&iblock[y][i_xoffset], BLOCK_SIZE * sizeof(imgpel)); + } +} +#endif + diff --git a/lencod/inc/rdopt_coding_state.h b/lencod/inc/rdopt_coding_state.h new file mode 100644 index 0000000..b028f75 --- /dev/null +++ b/lencod/inc/rdopt_coding_state.h @@ -0,0 +1,53 @@ + +/*! + *************************************************************************** + * \file + * rdopt_coding_state.h + * + * \author + * Heiko Schwarz + * + * \date + * 17. April 2001 + * + * \brief + * Headerfile for storing/restoring coding state + * (for rd-optimized mode decision) + ************************************************************************** + */ + +#ifndef _RD_OPT_CS_H_ +#define _RD_OPT_CS_H_ + +struct coding_state { + + // important variables of data partition array + int no_part; + Bitstream *bitstream; + EncodingEnvironment *encenv; + + // contexts for binary arithmetic coding + MotionInfoContexts *mot_ctx; + TextureInfoContexts *tex_ctx; + + // bit counter + BitCounter bits; + + // elements of current macroblock + short mvd[2][BLOCK_MULTIPLE][BLOCK_MULTIPLE][2]; + int64 cbp_bits[3]; + int64 *cbp_bits_8x8; +}; + +typedef struct coding_state CSobj; + +extern void delete_coding_state (CSobj *); //!< delete structure +extern CSobj *create_coding_state (InputParameters *p_Inp); //!< create structure + +extern void init_coding_state_methods(Slice *currSlice); //!< Init methods given entropy coding + +extern void store_coding_state_cavlc (Macroblock *currMB, CSobj *cs); +extern void reset_coding_state_cavlc (Macroblock *currMB, CSobj *cs); + +#endif + diff --git a/lencod/inc/rdoq.h b/lencod/inc/rdoq.h new file mode 100644 index 0000000..8a5bcf7 --- /dev/null +++ b/lencod/inc/rdoq.h @@ -0,0 +1,202 @@ + +/*! + *************************************************************************** + * \file + * rdoq.h + * + * \brief + * Headerfile for trellis based mode decision + * + * \author + * Limin Liu + * Alexis Michael Tourapis + *************************************************************************** + */ + + +#ifndef _RDOQ_H_ +#define _RDOQ_H_ + +#include + + +#define SIGN_BITS 1 + +static const int estErr4x4[6][4][4] = +{ + { + {25600, 27040, 25600, 27040}, + {27040, 25600, 27040, 25600}, + {25600, 27040, 25600, 27040}, + {27040, 25600, 27040, 25600} + }, + { + {30976, 31360, 30976, 31360}, + {31360, 32400, 31360, 32400}, + {30976, 31360, 30976, 31360}, + {31360, 32400, 31360, 32400} + }, + { + {43264, 40960, 43264, 40960}, + {40960, 40000, 40960, 40000}, + {43264, 40960, 43264, 40960}, + {40960, 40000, 40960, 40000} + }, + { + {50176, 51840, 50176, 51840}, + {51840, 52900, 51840, 52900}, + {50176, 51840, 50176, 51840}, + {51840, 52900, 51840, 52900} + }, + { + {65536, 64000, 65536, 64000}, + {64000, 62500, 64000, 62500}, + {65536, 64000, 65536, 64000}, + {64000, 62500, 64000, 62500} + }, + { + {82944, 84640, 82944, 84640}, + {84640, 84100, 84640, 84100}, + {82944, 84640, 82944, 84640}, + {84640, 84100, 84640, 84100} + } +}; + +static const int estErr8x8[6][8][8]={ + { + {6553600, 6677056, 6400000, 6677056, 6553600, 6677056, 6400000, 6677056}, + {6677056, 6765201, 6658560, 6765201, 6677056, 6765201, 6658560, 6765201}, + {6400000, 6658560, 6553600, 6658560, 6400000, 6658560, 6553600, 6658560}, + {6677056, 6765201, 6658560, 6765201, 6677056, 6765201, 6658560, 6765201}, + {6553600, 6677056, 6400000, 6677056, 6553600, 6677056, 6400000, 6677056}, + {6677056, 6765201, 6658560, 6765201, 6677056, 6765201, 6658560, 6765201}, + {6400000, 6658560, 6553600, 6658560, 6400000, 6658560, 6553600, 6658560}, + {6677056, 6765201, 6658560, 6765201, 6677056, 6765201, 6658560, 6765201} + }, + { + {7929856, 8156736, 8028160, 8156736, 7929856, 8156736, 8028160, 8156736}, + {8156736, 7537770, 7814560, 7537770, 8156736, 7537770, 7814560, 7537770}, + {8028160, 7814560, 7840000, 7814560, 8028160, 7814560, 7840000, 7814560}, + {8156736, 7537770, 7814560, 7537770, 8156736, 7537770, 7814560, 7537770}, + {7929856, 8156736, 8028160, 8156736, 7929856, 8156736, 8028160, 8156736}, + {8156736, 7537770, 7814560, 7537770, 8156736, 7537770, 7814560, 7537770}, + {8028160, 7814560, 7840000, 7814560, 8028160, 7814560, 7840000, 7814560}, + {8156736, 7537770, 7814560, 7537770, 8156736, 7537770, 7814560, 7537770} + }, + { + {11075584, 10653696, 11151360, 10653696, 11075584, 10653696, 11151360, 10653696}, + {10653696, 11045652, 11109160, 11045652, 10653696, 11045652, 11109160, 11045652}, + {11151360, 11109160, 11289600, 11109160, 11151360, 11109160, 11289600, 11109160}, + {10653696, 11045652, 11109160, 11045652, 10653696, 11045652, 11109160, 11045652}, + {11075584, 10653696, 11151360, 10653696, 11075584, 10653696, 11151360, 10653696}, + {10653696, 11045652, 11109160, 11045652, 10653696, 11045652, 11109160, 11045652}, + {11151360, 11109160, 11289600, 11109160, 11151360, 11109160, 11289600, 11109160}, + {10653696, 11045652, 11109160, 11045652, 10653696, 11045652, 11109160, 11045652} + }, + { + {12845056, 12503296, 12544000, 12503296, 12845056, 12503296, 12544000, 12503296}, + {12503296, 13050156, 12588840, 13050156, 12503296, 13050156, 12588840, 13050156}, + {12544000, 12588840, 12960000, 12588840, 12544000, 12588840, 12960000, 12588840}, + {12503296, 13050156, 12588840, 13050156, 12503296, 13050156, 12588840, 13050156}, + {12845056, 12503296, 12544000, 12503296, 12845056, 12503296, 12544000, 12503296}, + {12503296, 13050156, 12588840, 13050156, 12503296, 13050156, 12588840, 13050156}, + {12544000, 12588840, 12960000, 12588840, 12544000, 12588840, 12960000, 12588840}, + {12503296, 13050156, 12588840, 13050156, 12503296, 13050156, 12588840, 13050156} + }, + { + {16777216, 16646400, 16384000, 16646400, 16777216, 16646400, 16384000, 16646400}, + {16646400, 16370116, 16692640, 16370116, 16646400, 16370116, 16692640, 16370116}, + {16384000, 16692640, 16646400, 16692640, 16384000, 16692640, 16646400, 16692640}, + {16646400, 16370116, 16692640, 16370116, 16646400, 16370116, 16692640, 16370116}, + {16777216, 16646400, 16384000, 16646400, 16777216, 16646400, 16384000, 16646400}, + {16646400, 16370116, 16692640, 16370116, 16646400, 16370116, 16692640, 16370116}, + {16384000, 16692640, 16646400, 16692640, 16384000, 16692640, 16646400, 16692640}, + {16646400, 16370116, 16692640, 16370116, 16646400, 16370116, 16692640, 16370116} + }, + { + {21233664, 21381376, 21667840, 21381376, 21233664, 21381376, 21667840, 21381376}, + {21381376, 21381376, 21374440, 21381376, 21381376, 21381376, 21374440, 21381376}, + {21667840, 21374440, 21529600, 21374440, 21667840, 21374440, 21529600, 21374440}, + {21381376, 21381376, 21374440, 21381376, 21381376, 21381376, 21374440, 21381376}, + {21233664, 21381376, 21667840, 21381376, 21233664, 21381376, 21667840, 21381376}, + {21381376, 21381376, 21374440, 21381376, 21381376, 21381376, 21374440, 21381376}, + {21667840, 21374440, 21529600, 21374440, 21667840, 21374440, 21529600, 21374440}, + {21381376, 21381376, 21374440, 21381376, 21381376, 21381376, 21374440, 21381376} + } +}; + + +struct est_bits_cabac +{ + int significantBits[16][2]; + int lastBits[16][2]; + int greaterOneBits[2][5][2]; // c1 and c2 + int greaterOneState[5]; + int blockCbpBits[4][2]; // c1 and c2 +}; +typedef struct est_bits_cabac estBitsCabacStruct; + +typedef struct level_data_struct +{ + int level[3]; + int levelDouble; + double errLevel[3]; + int noLevels; + int coeff_ctr; + int pre_level; + int sign; +} levelDataStruct; + + +extern void init_rdoq_slice(Slice *currSlice); + +/*----------CAVLC related functions----------*/ +extern void est_RunLevel_CAVLC(Macroblock *currMB, levelDataStruct *levelData, int *levelTrellis, int block_type, + int b8, int b4, int coeff_num, double lambda); +extern int est_CAVLC_bits (VideoParameters *p_Vid, int level_to_enc[16], int sign_to_enc[16], int nnz, int block_type); + +/*----------CABAC related functions----------*/ +extern void precalculate_unary_exp_golomb_level(VideoParameters *p_Vid); +extern int est_unary_exp_golomb_level_bits(unsigned int symbol, int bits0, int bits1); +extern int est_exp_golomb_encode_eq_prob (unsigned int symbol); + +extern void estRunLevel_CABAC (Macroblock* currMB, int context); + + + +extern int est_write_and_store_CBP_block_bit(Macroblock* currMB, int type); +extern void est_writeRunLevel_CABAC(Macroblock *currMB, levelDataStruct levelData[], int levelTabMin[], int type, double lambda, int kStart, + int kStop, int noCoeff, int estCBP); +extern void init_trellis_data_4x4_CAVLC(Macroblock *currMB, int **tblock, int block_x, int qp_per, int qp_rem, + LevelQuantParams **q_params_4x4, const byte *p_scan, + levelDataStruct *dataLevel, int type); +extern int init_trellis_data_4x4_CABAC(Macroblock *currMB, int **tblock, + struct quant_methods *q_method, const byte *p_scan, + levelDataStruct *dataLevel, int* kStart, int* kStop, int type); +extern void init_trellis_data_8x8_CAVLC(Macroblock *currMB, int **tblock, int block_x, int qp_per, int qp_rem, + LevelQuantParams **q_params_8x8, const byte *p_scan, + levelDataStruct levelData[4][16]); +extern int init_trellis_data_8x8_CABAC(Macroblock *currMB, int **tblock, int block_x, int qp_per, int qp_rem, + LevelQuantParams **q_params_8x8, const byte *p_scan, + levelDataStruct *dataLevel, int* kStart, int* kStop); +extern void init_trellis_data_DC_CAVLC(Macroblock *currMB, int **tblock, int qp_per, int qp_rem, + LevelQuantParams *q_params_4x4, const byte *p_scan, + levelDataStruct *dataLevel); +extern int init_trellis_data_DC_CABAC(Macroblock *currMB, int **tblock, int qp_per, int qp_rem, + LevelQuantParams *q_params_4x4, const byte *p_scan, + levelDataStruct *dataLevel, int* kStart, int* kStop); +extern int init_trellis_data_DC_cr_CAVLC(Macroblock *currMB, int **tblock, int qp_per, int qp_rem, + LevelQuantParams *q_params_4x4, const byte *p_scan, + levelDataStruct *dataLevel); +extern int init_trellis_data_DC_cr_CABAC(Macroblock *currMB, int **tblock, int qp_per, int qp_rem, + LevelQuantParams *q_params_4x4, const byte *p_scan, + levelDataStruct *dataLevel, int* kStart, int* kStop); + +extern void RDOQ_update_mode (Slice *currSlice, RD_PARAMS *enc_mb); +extern void copy_rddata_trellis (Macroblock *currMB, RD_DATA *dest, RD_DATA *src); +extern void updateMV_mp (Macroblock *currMB, distblk *m_cost, short ref, int list, int h, int v, int blocktype, int block8x8); +extern void trellis_coding (Macroblock *currMB); +extern void get_dQP_table (Slice *currSlice); + +#endif // _RDOQ_H_ + diff --git a/lencod/inc/refbuf.h b/lencod/inc/refbuf.h new file mode 100644 index 0000000..9a1aad5 --- /dev/null +++ b/lencod/inc/refbuf.h @@ -0,0 +1,82 @@ + +/*! + ************************************************************************ + * \file refbuf.h + * + * \brief + * Declarations of the reference frame buffer types and functions + ************************************************************************ + */ +#ifndef _REBUF_H_ +#define _REBUF_H_ + +#include "mbuffer.h" + +/*! + ************************************************************************ + * \brief + * Yields a pel line _pointer_ from one of the 16 sub-images + * Input does not require subpixel image indices + ************************************************************************ + */ +static inline imgpel *UMVLine4X (StorablePicture *ref, int y, int x) +{ + //return &(ref->p_curr_img_sub[(y & 0x03)][(x & 0x03)][iClip3( 0, ref->size_y_pad, y >> 2)][iClip3( 0, ref->size_x_pad, x >> 2)]); + return &(ref->p_curr_img_sub[(y & 0x03)][(x & 0x03)][iClip3( -IMG_PAD_SIZE_Y, ref->size_y_pad, y >> 2)][iClip3(-IMG_PAD_SIZE_X, ref->size_x_pad, x >> 2)]); +} + +/*! + ************************************************************************ + * \brief + * Yields a pel line _pointer_ from one of the 16 sub-images + * Input does not require subpixel image indices + ************************************************************************ + */ +static inline imgpel *UMVLine4Xcr (StorablePicture *ref, int cmp, int y, int x) +{ + //return &(ref->p_img_sub[cmp][(y & 0x03)][(x & 0x03)][iClip3( 0, ref->size_y_cr_pad, y >> 2)][iClip3( 0, ref->size_x_cr_pad, x >> 2)]); + return &(ref->p_img_sub[cmp][(y & 0x03)][(x & 0x03)][iClip3(-ref->pad_size_uv_y, ref->size_y_cr_pad, y >> 2)][iClip3(-ref->pad_size_uv_x, ref->size_x_cr_pad, x >> 2)]); +} + +/*! + ************************************************************************ + * \brief + * Yields a pel line _pointer_ from one of the 16 sub-images + * Input does not require subpixel image indices + ************************************************************************ + */ +static inline imgpel *FastLine4X (StorablePicture *ref, int y, int x) +{ + return &(ref->p_curr_img_sub[(y & 0x03)][(x & 0x03)][y >> 2][x >> 2]); +} + +/*! + ************************************************************************ + * \brief + * Yields a pel line _pointer_ from one of the 16 (4:4:4), 32 (4:2:2), + * or 64 (4:2:0) sub-images + * Input does not require subpixel image indices + ************************************************************************ + */ +static inline imgpel *UMVLine8X_chroma (StorablePicture *ref, int cmp, int y, int x) +{ + //return &(ref->p_img_sub[cmp][y & ref->chroma_mask_mv_y][x & ref->chroma_mask_mv_x][iClip3 (0, ref->size_y_cr_pad, y >> ref->chroma_shift_y)][iClip3 (0, ref->size_x_cr_pad, x >> ref->chroma_shift_x)]); + return &(ref->p_img_sub[cmp][y & ref->chroma_mask_mv_y][x & ref->chroma_mask_mv_x][iClip3 (-ref->pad_size_uv_y, ref->size_y_cr_pad, y >> ref->chroma_shift_y)][iClip3 (-ref->pad_size_uv_x, ref->size_x_cr_pad, x >> ref->chroma_shift_x)]); +} + +/*! + ************************************************************************ + * \brief + * Yields a pel line _pointer_ from one of the 16 (4:4:4), 32 (4:2:2), + * or 64 (4:2:0) sub-images + * Input does not require subpixel image indices + ************************************************************************ + */ +static inline imgpel *FastLine8X_chroma (StorablePicture *ref, int cmp, int y, int x) +{ + return &(ref->p_img_sub[cmp][y & ref->chroma_mask_mv_y][x & ref->chroma_mask_mv_x][y >> ref->chroma_shift_y][x >> ref->chroma_shift_x]); +} + + +#endif + diff --git a/lencod/inc/refbuf_otf.h b/lencod/inc/refbuf_otf.h new file mode 100644 index 0000000..e89d4a4 --- /dev/null +++ b/lencod/inc/refbuf_otf.h @@ -0,0 +1,32 @@ + +/*! + ************************************************************************ + * \file refbuf_otf.h + * + * \brief + * Declarations of the reference frame buffer types and functions for on-the-fly interpolations + ************************************************************************ + */ +#ifndef _REBUF_OTF_H_ +#define _REBUF_OTF_H_ + +#include "mbuffer.h" + +static inline imgpel *UMVLine4X_otf (StorablePicture *ref, int y, int x ) +{ + return &(ref->p_curr_img_sub[(y & 0x03)>>1][(x & 0x03)>>1][iClip3( -IMG_PAD_SIZE_Y, ref->size_y_pad, y >> 2)][iClip3(-IMG_PAD_SIZE_X, ref->size_x_pad, x >> 2)]); +} + +static inline imgpel *UMVLine4Xcr_otf (StorablePicture *ref, int cmp, int y, int x) +{ + return &(ref->p_img_sub[cmp][(y & 0x03)>>1][(x & 0x03)>>1][iClip3(-ref->pad_size_uv_y, ref->size_y_cr_pad, y >> 2)][iClip3(-ref->pad_size_uv_x, ref->size_x_cr_pad, x >> 2)]); +} + +static inline imgpel *UMVLine8X_chroma_otf (StorablePicture *ref, int cmp, int y, int x) +{ + return &(ref->p_img_sub[cmp][(y & ref->chroma_mask_mv_y)>>1][(x & ref->chroma_mask_mv_x)>>1][iClip3 (-ref->pad_size_uv_y, ref->size_y_cr_pad, y >> ref->chroma_shift_y)][iClip3 (-ref->pad_size_uv_x, ref->size_x_cr_pad, x >> ref->chroma_shift_x)]); +} + + +#endif + diff --git a/lencod/inc/rtp.h b/lencod/inc/rtp.h new file mode 100644 index 0000000..cbb344b --- /dev/null +++ b/lencod/inc/rtp.h @@ -0,0 +1,56 @@ + +/*! + *************************************************************************** + * + * \file rtp.h + * + * \brief + * Definition of structures and functions to handle RTP headers. For a + * description of RTP see RFC1889 on http://www.ietf.org + * + * \date + * 30 September 2001 + * + * \author + * Stephan Wenger stewe@cs.tu-berlin.de + **************************************************************************/ + +#ifndef _RTP_H_ +#define _RTP_H_ + +#include "nalu.h" + +#define MAXRTPPAYLOADLEN (65536 - 40) //!< Maximum payload size of an RTP packet +#define MAXRTPPACKETSIZE (65536 - 28) //!< Maximum size of an RTP packet incl. header +#define H264PAYLOADTYPE 105 //!< RTP paylaod type fixed here for simplicity +#define H264SSRC 0x12345678 //!< SSRC, chosen to simplify debugging +#define RTP_TR_TIMESTAMP_MULT 1000 //!< should be something like 27 Mhz / 29.97 Hz + +typedef struct +{ + unsigned int v; //!< Version, 2 bits, MUST be 0x2 + unsigned int p; //!< Padding bit, Padding MUST NOT be used + unsigned int x; //!< Extension, MUST be zero + unsigned int cc; /*!< CSRC count, normally 0 in the absence + of RTP mixers */ + unsigned int m; //!< Marker bit + unsigned int pt; //!< 7 bits, Payload Type, dynamically established + unsigned int seq; /*!< RTP sequence number, incremented by one for + each sent packet */ + unsigned int timestamp; //!< timestamp, 27 MHz for H.264 + unsigned int ssrc; //!< Synchronization Source, chosen randomly + byte * payload; //!< the payload including payload headers + unsigned int paylen; //!< length of payload in bytes + byte * packet; //!< complete packet including header and payload + unsigned int packlen; //!< length of packet, typically paylen+12 +} RTPpacket_t; + +extern void RTPUpdateTimestamp (VideoParameters *p_Vid, int tr); +extern void OpenRTPFile (char *Filename, FILE **f_rtp); +extern void CloseRTPFile (FILE *f_rtp); +extern int WriteRTPNALU (VideoParameters *p_Vid, NALU_t *n, FILE **f_rtp); + + + +#endif + diff --git a/lencod/inc/sei.h b/lencod/inc/sei.h new file mode 100644 index 0000000..8fb6cba --- /dev/null +++ b/lencod/inc/sei.h @@ -0,0 +1,433 @@ + +/*! + ************************************************************************ + * \file + * sei.h + * \brief + * definitions for Supplemental Enhanced Information + * \author(s) + * - Dong Tian + * - TBD + * + * ************************************************************************ + */ + +#ifndef SEI_H +#define SEI_H + +#define MAX_LAYER_NUMBER 2 +#define MAX_DEPENDENT_SUBSEQ 5 +// tone mapping information +#define MAX_CODED_BIT_DEPTH 12 +#define MAX_SEI_BIT_DEPTH 12 +#define MAX_NUM_PIVOTS (1< + * - Karsten Suehring + * - Alexis Michael Tourapis + + ************************************************************************** + */ + +#ifndef _SLICE_H_ +#define _SLICE_H_ + +#include "global.h" +#include "mbuffer.h" +#include "rdopt_coding_state.h" + +static const int QP2QUANT[40]= +{ + 1, 1, 1, 1, 2, 2, 2, 2, + 3, 3, 3, 4, 4, 4, 5, 6, + 6, 7, 8, 9,10,11,13,14, + 16,18,20,23,25,29,32,36, + 40,45,51,57,64,72,81,91 +}; + + +extern int encode_one_slice ( VideoParameters *p_Vid, int SliceGroupId, int TotalCodedMBs ); +extern int encode_one_slice_MBAFF ( VideoParameters *p_Vid, int SliceGroupId, int TotalCodedMBs ); +extern void init_slice ( VideoParameters *p_Vid, Slice **currSlice, int start_mb_addr ); +extern void init_slice_lite ( VideoParameters *p_Vid, Slice **currSlice, int start_mb_addr ); +extern void free_slice_list ( Picture *currPic ); + +extern void SetLagrangianMultipliersOn (Slice *currSlice); +extern void SetLagrangianMultipliersOff(Slice *currSlice); +extern void free_slice (Slice *currSlice); + + +#endif diff --git a/lencod/inc/symbol.h b/lencod/inc/symbol.h new file mode 100644 index 0000000..313539e --- /dev/null +++ b/lencod/inc/symbol.h @@ -0,0 +1,27 @@ + +/*! +*************************************************************************** +* +* \file symbol.h +* +* \brief +* Generic Symbol writing interface +* +* \date +* 18 Jan 2006 +* +* \author +* Karsten Suehring +**************************************************************************/ +struct writeMB { + void (*writeMB_typeInfo) (Macroblock *currMB, SyntaxElement *se, DataPartition *dP); + void (*writeIntraPredMode) (SyntaxElement *se, DataPartition *dP); + void (*writeB8_typeInfo) (SyntaxElement *se, DataPartition *dP); + void (*writeRefFrame[6]) (Macroblock *currMB, SyntaxElement *se, DataPartition *dP); + void (*writeMVD) (Macroblock *currMB, SyntaxElement *se, DataPartition *dP); + void (*writeCBP) (Macroblock* currMB, SyntaxElement *se, DataPartition *dP); + void (*writeDquant) (Macroblock* currMB, SyntaxElement *se, DataPartition *dP); + void (*writeCIPredMode) (Macroblock* currMB, SyntaxElement *se, DataPartition *dP); + void (*writeFieldModeInfo) (Macroblock *currMB, SyntaxElement *se, DataPartition *dP); + void (*writeMB_transform_size)(Macroblock *currMB, SyntaxElement *se, DataPartition *dP); +}; diff --git a/lencod/inc/transcode_enc.h b/lencod/inc/transcode_enc.h new file mode 100644 index 0000000..7956191 --- /dev/null +++ b/lencod/inc/transcode_enc.h @@ -0,0 +1,25 @@ +/*! +************************************************************************ +* \file transcode_enc.h +* +* \brief +* Functions to use trancoding information from decoder +* +* \author +* - João Carreira +* +************************************************************************ +*/ + +#ifndef _TRANSCODE_ENC_H +#define _TRANSCODE_ENC_H + +#include "global.h" +#include "mbuffer.h" + +extern void initTranscodeModule( VideoParameters *p_Vid ); +extern void closeTranscodeModule( VideoParameters *p_Vid ); + +extern void readTranscodingInfo( VideoParameters *p_Vid ); + +#endif \ No newline at end of file diff --git a/lencod/inc/transform8x8.h b/lencod/inc/transform8x8.h new file mode 100644 index 0000000..b081471 --- /dev/null +++ b/lencod/inc/transform8x8.h @@ -0,0 +1,37 @@ + +/*! + *************************************************************************** + * + * \file transform8x8.h + * + * \brief + * prototypes of 8x8 transform functions + * + * \date + * 9. October 2003 + * + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * Yuri Vatis + **************************************************************************/ + +#ifndef _TRANSFORM8X8_H_ +#define _TRANSFORM8X8_H_ +extern int mode_decision_for_I8x8_MB (Macroblock *currMB, int lambda, distblk *min_cost); +extern int mode_decision_for_I8x8_blocks_JM_Low (Macroblock *currMB, int b8, int lambda, distblk *min_cost); +extern int mode_decision_for_I8x8_blocks_JM_High (Macroblock *currMB, int b8, int lambda, distblk *min_cost); +extern int mode_decision_for_I8x8_blocks_JM_Low444 (Macroblock *currMB, int b8, int lambda, distblk *min_cost); +extern int mode_decision_for_I8x8_blocks_JM_High444(Macroblock *currMB, int b8, int lambda, distblk *min_cost); + +extern distblk rdcost_for_8x8_intra_blocks (Macroblock *currMB, int *c_nz, int b8, int ipmode, int lambda, distblk min_rdcost, int mostProbableMode); +extern distblk rdcost_for_8x8_intra_blocks_444 (Macroblock *currMB, int *c_nz, int b8, int ipmode, int lambda, distblk min_rdcost, int mostProbableMode); +extern distblk compute_satd8x8_cost (VideoParameters *p_Vid, imgpel **cur_img, imgpel **mpr8x8, int pic_opix_x, distblk min_cost); +extern distblk compute_sse8x8_cost (VideoParameters *p_Vid, imgpel **cur_img, imgpel **mpr8x8, int pic_opix_x, distblk min_cost); +extern distblk compute_sad8x8_cost (VideoParameters *p_Vid, imgpel **cur_img, imgpel **mpr8x8, int pic_opix_x, distblk min_cost); +extern distblk compute_comp8x8_cost (VideoParameters *p_Vid, imgpel **cur_img, imgpel **mpr8x8, int pic_opix_x, distblk min_cost); + +extern int residual_transform_quant_luma_8x8 (Macroblock *currMB, ColorPlane pl, int b8, int *coeff_cost, int intra); +extern int residual_transform_quant_luma_8x8_cavlc (Macroblock *currMB, ColorPlane pl, int b8, int *coeff_cost, int intra); +extern int residual_transform_quant_luma_8x8_ls (Macroblock *currMB, ColorPlane pl, int b8, int *coeff_cost, int intra); + +#endif //_TRANSFORM8X8_H_ diff --git a/lencod/inc/vlc.h b/lencod/inc/vlc.h new file mode 100644 index 0000000..d0647f9 --- /dev/null +++ b/lencod/inc/vlc.h @@ -0,0 +1,72 @@ + +/*! + ************************************************************************************* + * \file vlc.h + * + * \brief + * Prototypes for VLC coding funtions + * \author + * Karsten Suehring + ************************************************************************************* + */ + +#ifndef _VLC_H_ +#define _VLC_H_ + +#include "enc_statistics.h" + +extern Boolean write_u_1 (char *tracestring, int value, Bitstream *bitstream); +extern int write_se_v (char *tracestring, int value, Bitstream *bitstream); +extern int write_ue_v (char *tracestring, int value, Bitstream *bitstream); +extern int write_u_v (int n, char *tracestring, int value, Bitstream *bitstream); + +extern void levrun_linfo_c2x2(int level,int run,int *len,int *info); +extern void levrun_linfo_inter(int level,int run,int *len,int *info); + +extern void writeSE_Fix (SyntaxElement *se, Bitstream *bitstream); +extern void writeSE_UVLC (SyntaxElement *se, DataPartition *dp); +extern void writeSE_SVLC (SyntaxElement *se, DataPartition *dp); +extern void writeSE_Flag (SyntaxElement *se, DataPartition *dp); +extern void writeSE_invFlag (SyntaxElement *se, DataPartition *dp); +extern void writeSE_Dummy (SyntaxElement *se, DataPartition *dp); + +extern void writeRefPic_NRef_CAVLC (Macroblock *currMB, SyntaxElement *se, DataPartition *dp); +extern void writeRefPic_2Ref_CAVLC (Macroblock *currMB, SyntaxElement *se, DataPartition *dp); +extern void writeRefPic_Dummy (Macroblock *currMB, SyntaxElement *se, DataPartition *dp); + +extern void writeCBP_VLC (Macroblock *currMB, SyntaxElement *se, DataPartition *dp); +extern void writeIntraPredMode_CAVLC (SyntaxElement *se, DataPartition *dp); +extern int writeSyntaxElement2Buf_UVLC (SyntaxElement *se, Bitstream* this_streamBuffer ); +extern void writeUVLC2buffer (SyntaxElement *se, Bitstream *currStream); +extern void writeVlcByteAlign (VideoParameters *p_Vid, Bitstream* currStream, StatParameters *cur_stats); +extern int writeSyntaxElement2Buf_Fixed(SyntaxElement *se, Bitstream* this_streamBuffer ); +extern int symbol2uvlc (SyntaxElement *se); +extern void ue_linfo (int n, int dummy, int *len,int *info); +extern void se_linfo (int mvd, int dummy, int *len,int *info); +extern void cbp_linfo_intra_normal(int cbp, int dummy, int *len,int *info); +extern void cbp_linfo_intra_other (int cbp, int dummy, int *len,int *info); +extern void cbp_linfo_inter_normal(int cbp, int dummy, int *len,int *info); +extern void cbp_linfo_inter_other (int cbp, int dummy, int *len,int *info); + +extern int writeSyntaxElement_VLC(SyntaxElement *se, DataPartition *this_dataPart); +extern int writeSyntaxElement_TotalZeros(SyntaxElement *se, DataPartition *this_dataPart); +extern int writeSyntaxElement_TotalZerosChromaDC(VideoParameters *p_Vid, SyntaxElement *se, DataPartition *this_dataPart); +extern int writeSyntaxElement_Run(SyntaxElement *se, DataPartition *this_dataPart); +extern int writeSyntaxElement_NumCoeffTrailingOnes(SyntaxElement *se, DataPartition *this_dataPart); +extern int writeSyntaxElement_NumCoeffTrailingOnesChromaDC(VideoParameters *p_Vid, SyntaxElement *se, DataPartition *this_dataPart); +extern int writeSyntaxElement_Level_VLC1(SyntaxElement *se, DataPartition *this_dataPart, int profile_idc); +extern int writeSyntaxElement_Level_VLCN(SyntaxElement *se, int vlc, DataPartition *this_dataPart, int profile_idc); + +extern void writeUVLC_CAVLC (Macroblock *currMB, SyntaxElement *se, DataPartition *dp); +extern void writeSVLC_CAVLC (Macroblock *currMB, SyntaxElement *se, DataPartition *dp); +extern void writeFlag_CAVLC (Macroblock *currMB, SyntaxElement *se, DataPartition *dp); + +extern void reset_mb_nz_coeff(VideoParameters *p_Vid, int mb_number); +extern int bs_bitlength(Bitstream *bitstream); +extern int GetBits(byte *buffer, + int totbitoffset, + int *info, + int bitcount, + int numbits); +#endif + diff --git a/lencod/inc/wp.h b/lencod/inc/wp.h new file mode 100644 index 0000000..636bf8f --- /dev/null +++ b/lencod/inc/wp.h @@ -0,0 +1,56 @@ +/*! + *************************************************************************** + * \file + * wp.h + * + * \author + * Alexis Michael Tourapis + * + * \date + * 22. February 2008 + * + * \brief + * Headerfile for weighted prediction support + ************************************************************************** + */ + +#ifndef _WP_H_ +#define _WP_H_ + +#include "wp_lms.h" +#include "wp_mcprec.h" +#include "wp_mciter.h" + +#define DEBUG_WP 0 + +void InitWP (VideoParameters *p_Vid, InputParameters *p_Inp, int force_wp_method); +void ResetWP (VideoParameters *p_Vid, InputParameters *p_Inp); + +extern void EstimateWPBSliceAlg0 (Slice *currSlice); +extern void EstimateWPPSliceAlg0 (Slice *currSlice, int offset); +extern int TestWPPSliceAlg0 (Slice *currSlice, int offset); +extern int TestWPBSliceAlg0 (Slice *currSlice, int method); +extern double ComputeImgSum (imgpel **CurrentImage, int height, int width); +extern void ComputeImgSumBlockBased(imgpel **CurrentImage, int height_in_blk, int width_in_blk, int blk_size_y, int blk_size_x, int start_blk, int end_blk, double *dc); +extern int64 ComputeSumBlockBased (imgpel **CurrentImage, int height_in_blk, int width_in_blk, int blk_size_y, int blk_size_x, int start_blk, int end_blk); + +extern void ComputeImplicitWeights (Slice *currSlice, + short default_weight[3], + short im_weight[6][MAX_REFERENCE_PICTURES][MAX_REFERENCE_PICTURES][3]); +extern void ComputeExplicitWPParamsLMS(Slice *currSlice, + int select_offset, + int start_mb, + int end_mb, + short default_weight[3], + short weight[6][MAX_REFERENCE_PICTURES][3], + short offset[6][MAX_REFERENCE_PICTURES][3]); +extern void ComputeExplicitWPParamsJNT(Slice *currSlice, + int start_mb, + int end_mb, + short default_weight[3], + short weight[6][MAX_REFERENCE_PICTURES][3], + short offset[6][MAX_REFERENCE_PICTURES][3]); + + +#endif + diff --git a/lencod/inc/wp_lms.h b/lencod/inc/wp_lms.h new file mode 100644 index 0000000..c584be3 --- /dev/null +++ b/lencod/inc/wp_lms.h @@ -0,0 +1,26 @@ +/*! + *************************************************************************** + * \file + * wp_lms.h + * + * \author + * Alexis Michael Tourapis + * + * \date + * 22. February 2008 + * + * \brief + * Headerfile for weighted prediction support using LMS + ************************************************************************** + */ + +#ifndef _WP_LMS_H_ +#define _WP_LMS_H_ + +extern void EstimateWPPSliceAlg1(Slice *currSlice, int offset); +extern void EstimateWPBSliceAlg1(Slice *currSlice); +extern int TestWPPSliceAlg1 (Slice *currSlice, int offset); +extern int TestWPBSliceAlg1 (Slice *currSlice, int method); + +#endif + diff --git a/lencod/inc/wp_mciter.h b/lencod/inc/wp_mciter.h new file mode 100644 index 0000000..bbf72bc --- /dev/null +++ b/lencod/inc/wp_mciter.h @@ -0,0 +1,29 @@ +/*! + *************************************************************************** + * \file + * wp_mciter.h + * + * \author + * Alexis Michael Tourapis + * + * \date + * 22. February 2008 + * + * \brief + * Headerfile for weighted prediction support + ************************************************************************** + */ + +#ifndef _WP_MCITERM_H_ +#define _WP_MCITERM_H_ + + +extern void EstimateWPBSliceAlg2(Slice *currSlice); +extern void EstimateWPPSliceAlg2(Slice *currSlice, int offset); +extern int TestWPPSliceAlg2 (Slice *currSlice, int offset); +extern int TestWPBSliceAlg2 (Slice *currSlice, int method); + +extern void compute_offset (Slice *currSlice); + +#endif + diff --git a/lencod/inc/wp_mcprec.h b/lencod/inc/wp_mcprec.h new file mode 100644 index 0000000..64f5719 --- /dev/null +++ b/lencod/inc/wp_mcprec.h @@ -0,0 +1,52 @@ +/*! + *************************************************************************** + * \file + * wp_mcprec.h + * + * \brief + * Headerfile for Improved Motion Compensatation Precision Scheme using Weighted Prediction + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * - Athanasios Leontaris + * + * \date + * 16 July 2008 + * + ************************************************************************** + */ + +#ifndef _WP_MCPREC_H_ +#define _WP_MCPREC_H_ + +typedef struct +{ + int PicNum; // PicNum/FrameNum + int POCNum; // POC +} +WeightedPredRefX; + +typedef struct +{ + int algorithm; +} +WPXPass; + +typedef struct wpx_object +{ + int num_wp_ref_list[2]; // num of elements in each of the above matrices [LIST] + WeightedPredRefX *wp_ref_list[2]; // structure with reordering and WP information for ref frames [LIST] + WPXPass *curr_wp_rd_pass; + WPXPass wp_rd_passes[3]; // frame_picture [0...4] (MultiRefWeightedPred == 2) +} +WPXObject; + +extern void wpxInitWPXObject( VideoParameters *p_Vid ); +extern void wpxFreeWPXObject( VideoParameters *p_Vid ); +extern void wpxInitWPXPasses( VideoParameters *p_Vid, InputParameters *p_Inp ); +extern void wpxModifyRefPicList( Slice *currSlice ); +// Note that at some point, InputParameters p_Inp contents should be copied into VideoParameters *p_Vid. +// This would eliminate need of having to use both structures +extern int wpxDetermineWP( Slice *currSlice, int clist, int n ); +extern void wpxAdaptRefNum( Slice *currSlice ); + +#endif diff --git a/lencod/src/annexb.c b/lencod/src/annexb.c new file mode 100644 index 0000000..d662ca3 --- /dev/null +++ b/lencod/src/annexb.c @@ -0,0 +1,199 @@ + +/*! +************************************************************************************* +* \file annexb.c +* +* \brief +* Annex B Byte Stream format NAL Unit writing routines +* +* \author +* Main contributors (see contributors.h for copyright, address and affiliation details) +* - Stephan Wenger +************************************************************************************* +*/ + +#include "global.h" +#include "nalucommon.h" + +#include "annexb_split.h" + +/*! +******************************************************************************************** +* \brief +* Writes a NALU to the Annex B Byte Stream +* +* \return +* number of bits written +* +******************************************************************************************** +*/ +int WriteAnnexbNALU (VideoParameters *p_Vid, NALU_t *n, FILE **f_annexb) +{ + int BitsWritten = 0; + int offset = 0; + int length = 4; + static const byte startcode[] = {0,0,0,1}; + byte first_byte; + + assert (n != NULL); + assert (n->forbidden_bit == 0); + assert ((*f_annexb) != NULL); + assert (n->startcodeprefix_len == 3 || n->startcodeprefix_len == 4); + + // printf ("WriteAnnexbNALU: writing %d bytes w/ startcode_len %d\n", n->len+1, n->startcodeprefix_len); + if (n->startcodeprefix_len < 4) + { + offset = 1; + length = 3; + } + + if ( length != (int) fwrite (startcode+offset, 1, length, *f_annexb)) + { + printf ("Fatal: cannot write %d bytes to bitstream file, exit (-1)\n", length); + exit (-1); + } + BitsWritten = (length) << 3; + + //! Edited by Joao Carreira + split_annexb_files( p_Vid, n->nal_unit_type, (void*)(startcode+offset), length ); + + + first_byte = (unsigned char) ((n->forbidden_bit << 7) | (n->nal_reference_idc << 5) | n->nal_unit_type); + + if ( 1 != fwrite (&first_byte, 1, 1, *f_annexb)) + { + printf ("Fatal: cannot write %d bytes to bitstream file, exit (-1)\n", 1); + exit (-1); + } + BitsWritten += 8; + + //! Edited by Joao Carreira + split_annexb_files( p_Vid, n->nal_unit_type, &first_byte, 1 ); + + + // printf ("First Byte %x, nal_ref_idc %x, nal_unit_type %d\n", first_byte, n->nal_reference_idc, n->nal_unit_type); +#if (MVC_EXTENSION_ENABLE) + if(n->nal_unit_type==NALU_TYPE_PREFIX || n->nal_unit_type==NALU_TYPE_SLC_EXT) + { + int view_id = p_Vid->p_Inp->MVCFlipViews ? !(n->view_id) : n->view_id; + + first_byte = (unsigned char) ((n->svc_extension_flag << 7) | (n->non_idr_flag << 6) | n->priority_id); + if ( 1 != fwrite (&first_byte, 1, 1, *f_annexb)) + { + printf ("Fatal: cannot write %d bytes to bitstream file, exit (-1)\n", 1); + exit (-1); + } + BitsWritten += 8; + + //! Edited by Joao Carreira + split_annexb_files( p_Vid, n->nal_unit_type, &first_byte, 1 ); + + first_byte = (unsigned char) (view_id >> 2); + if ( 1 != fwrite (&first_byte, 1, 1, *f_annexb)) + { + printf ("Fatal: cannot write %d bytes to bitstream file, exit (-1)\n", 1); + exit (-1); + } + BitsWritten += 8; + + //! Edited by Joao Carreira + split_annexb_files( p_Vid, n->nal_unit_type, &first_byte, 1 ); + + first_byte = (unsigned char) (((view_id&3) << 6) | (n->temporal_id << 3) | (n->anchor_pic_flag << 2) | (n->inter_view_flag << 1) | n->reserved_one_bit); + if ( 1 != fwrite (&first_byte, 1, 1, *f_annexb)) + { + printf ("Fatal: cannot write %d bytes to bitstream file, exit (-1)\n", 1); + exit (-1); + } + BitsWritten += 8; + + //! Edited by Joao Carreira + split_annexb_files( p_Vid, n->nal_unit_type, &first_byte, 1 ); + } +#endif + + if (n->len != fwrite (n->buf, 1, n->len, *f_annexb)) + { + printf ("Fatal: cannot write %d bytes to bitstream file, exit (-1)\n", n->len); + exit (-1); + } + BitsWritten += n->len * 8; + + //! Edited by Joao Carreira + split_annexb_files( p_Vid, n->nal_unit_type, n->buf, n->len ); + + //! Edited by Joao Carreira + fflush_split_files( p_Vid ); + + fflush (*f_annexb); +#if TRACE + //fprintf (p_Enc->p_trace, "\nAnnex B NALU w/ %s startcode, len %d, forbidden_bit %d, nal_reference_idc %d, nal_unit_type %d\n\n\n", + // n->startcodeprefix_len == 4?"long":"short", n->len + 1, n->forbidden_bit, n->nal_reference_idc, n->nal_unit_type); + fprintf (p_Enc->p_trace, "\nAnnex B NALU w/ %s startcode, len %d,", n->startcodeprefix_len == 4?"long":"short", n->len + 1); + fprintf (p_Enc->p_trace, "\n forbidden_bit %d,", n->forbidden_bit); + fprintf (p_Enc->p_trace, "\n nal_reference_idc %d,", n->nal_reference_idc); + fprintf (p_Enc->p_trace, "\n nal_unit_type %d ", n->nal_unit_type); +#if (MVC_EXTENSION_ENABLE) + if(n->nal_unit_type==NALU_TYPE_PREFIX || n->nal_unit_type==NALU_TYPE_SLC_EXT) + { + fprintf (p_Enc->p_trace, "\n svc_extension_flag %d ", n->svc_extension_flag); + fprintf (p_Enc->p_trace, "\n non_idr_flag %d ", n->non_idr_flag); + fprintf (p_Enc->p_trace, "\n priority_id %d ", n->priority_id); + + fprintf (p_Enc->p_trace, "\n view_id %d ", p_Vid->p_Inp->MVCFlipViews ? !(n->view_id) : n->view_id); + fprintf (p_Enc->p_trace, "\n temporal_id %d ", n->temporal_id); + fprintf (p_Enc->p_trace, "\n anchor_pic_flag %d ", n->anchor_pic_flag); + fprintf (p_Enc->p_trace, "\n inter_view_flag %d ", n->inter_view_flag); + fprintf (p_Enc->p_trace, "\n reserved_one_bit %d ", n->reserved_one_bit); + } +#endif + fprintf (p_Enc->p_trace, "\n----------------------------------------------------------------------------\n\n\n"); + fflush (p_Enc->p_trace); +#endif + return BitsWritten; +} + + +/*! +******************************************************************************************** +* \brief +* Opens the output file for the bytestream +* +* \param p_Vid +* VideoParameters structure +* \param Filename +* The filename of the file to be opened +* +* \return +* none. Function terminates the program in case of an error +* +******************************************************************************************** +*/ +void OpenAnnexbFile (char *Filename, FILE **f_annexb) +{ + if (((*f_annexb) = fopen (Filename, "wb")) == NULL) + { + printf ("Fatal: cannot open Annex B bytestream file '%s', exit (-1)\n", Filename); + exit (-1); + } +} + + +/*! +******************************************************************************************** +* \brief +* Closes the output bit stream file +* +* \return +* none. Function terminates the program in case of an error +******************************************************************************************** +*/ +void CloseAnnexbFile(FILE *f_annexb) +{ + if (fclose (f_annexb)) + { + printf ("Fatal: cannot close Annex B bytestream file, exit (-1)\n"); + exit (-1); + } +} + diff --git a/lencod/src/annexb_split.c b/lencod/src/annexb_split.c new file mode 100644 index 0000000..b04b118 --- /dev/null +++ b/lencod/src/annexb_split.c @@ -0,0 +1,197 @@ +/*! +************************************************************************************* +* \file annexb_split.c +* +* \brief +* +* \date +* 03-02-2012 +* +* \version +* 1.0 +* +* \author +* João Carreira +* +************************************************************************************* +*/ + +#include "global.h" +#include "nalucommon.h" +#include "memalloc.h" +#include "mbuffer.h" +#include "ifunctions.h" + + +//Define wich nal units are write on each stream +#define NALU_COMMON NALU_TYPE_SPS, NALU_TYPE_PPS, NALU_TYPE_SUB_SPS, NALU_TYPE_AUD, NALU_TYPE_EOSEQ, NALU_TYPE_EOSTREAM, NALU_TYPE_FILL +#define NALU_BASE_VIEW {NALU_COMMON, NALU_TYPE_IDR, NALU_TYPE_SLICE} +#define NALU_AUX_VIEW {NALU_COMMON, NALU_TYPE_IDR, NALU_TYPE_PREFIX, NALU_TYPE_SLC_EXT, NALU_TYPE_VDRD} + + +static inline int WriteNALUBaseView (NaluType nalu_type) +{ + NaluType Write_NAL [] = NALU_BASE_VIEW; + int n_nalu = sizeof (Write_NAL)/sizeof (NaluType); + int i; + + for (i=0; if_annexb_base && p_Vid->f_annexb_aux) + { + if ( WriteNALUBaseView(nalu_type) ) + if ( length != (int) fwrite (data_pointer, 1, length, p_Vid->f_annexb_base)) + { + printf ("Fatal: cannot write %d bytes to bitstream file, exit (-1)\n", length); + exit (-1); + } + if ( WriteNALUAuxView(nalu_type) ) + if ( length != (int) fwrite (data_pointer, 1, length, p_Vid->f_annexb_aux )) + { + printf ("Fatal: cannot write %d bytes to bitstream file, exit (-1)\n", length); + exit (-1); + } + } +} + +/*! +******************************************************************************************** +* \brief +* Flush both bitstreams after the writing operations +* +******************************************************************************************** +*/ +void fflush_split_files( VideoParameters *p_Vid ) +{ + if (p_Vid->f_annexb_base ) + { + fflush(p_Vid->f_annexb_base); + } + if( p_Vid->f_annexb_aux) + { + fflush(p_Vid->f_annexb_aux); + } +} + +/*! +******************************************************************************************** +* \brief +* Opens the output file for the bytestream separatly for base and auxiliary views +* +* \param p_Vid +* VideoParameters structure +* \param Filename +* The filename of the file to be opened +* +* \return +* none. Function terminates the program in case of an error +* +******************************************************************************************** +*/ +void OpenAnnexbFileBaseView (VideoParameters *p_Vid, char *Filename) +{ + if (Filename[0] != 0) + { + if ((p_Vid->f_annexb_base = fopen (Filename, "wb")) == NULL) + { + printf ("Fatal: cannot open Annex B bytestream file '%s', exit (-1)\n", Filename); + exit (-1); + } + } +} +void OpenAnnexbFileAuxView (VideoParameters *p_Vid, char *Filename) +{ + if (Filename[0] != 0) + { + if ((p_Vid->f_annexb_aux = fopen (Filename, "wb")) == NULL) + { + printf ("Fatal: cannot open Annex B bytestream file '%s', exit (-1)\n", Filename); + exit (-1); + } + } +} + +/*! +******************************************************************************************** +* \brief +* Closes the output bit stream file for the base and auxiliary view +* +* \return +* none. Function terminates the program in case of an error +******************************************************************************************** +*/ +void CloseAnnexbFileBaseView(VideoParameters *p_Vid) +{ + if (p_Vid->f_annexb_base) + { + if (fclose (p_Vid->f_annexb_base)) + { + printf ("Fatal: cannot close Annex B bytestream file, exit (-1)\n"); + exit (-1); + } + } +} + +void CloseAnnexbFileAuxView(VideoParameters *p_Vid) +{ + if (p_Vid->f_annexb_aux) + { + if (fclose (p_Vid->f_annexb_aux)) + { + printf ("Fatal: cannot close Annex B bytestream file, exit (-1)\n"); + exit (-1); + } + } +} + + + + +#else +void split_annexb_files( VideoParameters *p_Vid, NaluType nalu_type, void *data_pointer, size_t length ) +{ + return; +} + +void fflush_split_files( VideoParameters *p_Vid ) +{ + return; +} +#endif \ No newline at end of file diff --git a/lencod/src/biariencode.c b/lencod/src/biariencode.c new file mode 100644 index 0000000..a851a9e --- /dev/null +++ b/lencod/src/biariencode.c @@ -0,0 +1,572 @@ + +/*! + ************************************************************************************* + * \file biariencode.c + * + * \brief + * Routines for binary arithmetic encoding. + * + * This modified implementation of the M Coder is based on JVT-U084 + * with the choice of M_BITS = 16. + * + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * - Detlev Marpe + * - Gabi Blaettermann + * - Gunnar Marten + ************************************************************************************* + */ + +#include "global.h" +#include "biariencode.h" + +// Range table for LPS +static const byte renorm_table_32[32]={6,5,4,4,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,1,1}; + + +void reset_pic_bin_count(VideoParameters *p_Vid) +{ + p_Vid->pic_bin_count = 0; +} + +int get_pic_bin_count(VideoParameters *p_Vid) +{ + return p_Vid->pic_bin_count; +} + +/*! + ************************************************************************ + * \brief + * Allocates memory for the EncodingEnvironment struct + ************************************************************************ + */ +EncodingEnvironmentPtr arienco_create_encoding_environment(void) +{ + EncodingEnvironmentPtr eep; + + if ( (eep = (EncodingEnvironmentPtr) calloc(1,sizeof(EncodingEnvironment))) == NULL) + no_mem_exit("arienco_create_encoding_environment: eep"); + + return eep; +} + +/*! + ************************************************************************ + * \brief + * Frees memory of the EncodingEnvironment struct + ************************************************************************ + */ +void arienco_delete_encoding_environment(EncodingEnvironmentPtr eep) +{ + free_pointer(eep); +} + +/*! + ************************************************************************ + * inline functions for writing bytes of code + *********************************************************************** + */ + +static inline void put_one_byte_final(EncodingEnvironmentPtr eep, unsigned int b) +{ + eep->Ecodestrm[(*eep->Ecodestrm_len)++] = (byte) b; +} + +static forceinline void put_buffer(EncodingEnvironmentPtr eep) +{ + while(eep->Epbuf>=0) + { + eep->Ecodestrm[(*eep->Ecodestrm_len)++] = (byte) ((eep->Ebuffer>>((eep->Epbuf--)<<3))&0xFF); + } + while(eep->C > 7) + { + eep->C-=8; + ++(eep->E); + } + eep->Ebuffer = 0; +} + +static inline void put_one_byte(EncodingEnvironmentPtr eep, int b) +{ + if(eep->Epbuf == 3) + { + put_buffer(eep); + } + eep->Ebuffer <<= 8; + eep->Ebuffer += (b); + + ++(eep->Epbuf); +} + +static inline void put_one_word(EncodingEnvironmentPtr eep, int b) +{ + if (eep->Epbuf >= 3) + { + put_buffer(eep); + } + eep->Ebuffer <<= 16; + eep->Ebuffer += (b); + + eep->Epbuf += 2; +} + +static forceinline void propagate_carry(EncodingEnvironmentPtr eep) +{ + ++(eep->Ebuffer); + while (eep->Echunks_outstanding > 0) + { + put_one_word(eep, 0); + --(eep->Echunks_outstanding); + } +} + +static inline void put_last_chunk_plus_outstanding(EncodingEnvironmentPtr eep, unsigned int l) +{ + while (eep->Echunks_outstanding > 0) + { + put_one_word(eep, 0xFFFF); + --(eep->Echunks_outstanding); + } + put_one_word(eep, l); +} + +static inline void put_last_chunk_plus_outstanding_final(EncodingEnvironmentPtr eep, unsigned int l) +{ + while (eep->Echunks_outstanding > 0) + { + put_one_word(eep, 0xFFFF); + --(eep->Echunks_outstanding); + } + put_one_byte(eep, l); +} + +/*! +************************************************************************ +* \brief +* Initializes the EncodingEnvironment E and C values to zero +************************************************************************ +*/ +void arienco_reset_EC(EncodingEnvironmentPtr eep) +{ + eep->E = 0; + eep->C = 0; +} + +/*! + ************************************************************************ + * \brief + * Initializes the EncodingEnvironment for the arithmetic coder + ************************************************************************ + */ +void arienco_start_encoding(EncodingEnvironmentPtr eep, + unsigned char *code_buffer, + int *code_len ) +{ + eep->Elow = 0; + eep->Echunks_outstanding = 0; + eep->Ebuffer = 0; + eep->Epbuf = -1; // to remove redundant chunk ^^ + eep->Ebits_to_go = BITS_TO_LOAD + 1; // to swallow first redundant bit + + eep->Ecodestrm = code_buffer; + eep->Ecodestrm_len = code_len; + + eep->Erange = HALF; +} + +/*! +************************************************************************ +* \brief +* add slice bin number to picture bin counter +* should be only used when slice is terminated +************************************************************************ +*/ +void set_pic_bin_count(VideoParameters *p_Vid, EncodingEnvironmentPtr eep) +{ + p_Vid->pic_bin_count += (eep->E << 3) + eep->C; // no of processed bins +} + +/*! + ************************************************************************ + * \brief + * Terminates the arithmetic codeword, writes stop bit and stuffing bytes (if any) + ************************************************************************ + */ +void arienco_done_encoding(Macroblock *currMB, EncodingEnvironmentPtr eep) +{ + unsigned int low = eep->Elow; + int remaining_bits = BITS_TO_LOAD - eep->Ebits_to_go; // output (2 + remaining) bits for terminating the codeword + one stop bit + unsigned char mask; + BitCounter *mbBits = &currMB->bits; + + //p_Vid->pic_bin_count += eep->E*8 + eep->C; // no of processed bins + + if (remaining_bits <= 5) // one terminating byte + { + mbBits->mb_stuffing += (5 - remaining_bits); + mask = (unsigned char) (255 - ((1 << (6 - remaining_bits)) - 1)); + low = (low >> (MASK_BITS)) & mask; // mask out the (2+remaining_bits) MSBs + low += (32 >> remaining_bits); // put the terminating stop bit '1' + + put_last_chunk_plus_outstanding_final(eep, low); + put_buffer(eep); + } + else if(remaining_bits <= 13) // two terminating bytes + { + mbBits->mb_stuffing += (13 - remaining_bits); + put_last_chunk_plus_outstanding_final(eep, ((low >> (MASK_BITS)) & 0xFF)); // mask out the 8 MSBs for output + + put_buffer(eep); + if (remaining_bits > 6) + { + mask = (unsigned char) (255 - ((1 << (14 - remaining_bits)) - 1)); + low = (low >> (B_BITS)) & mask; + low += (0x2000 >> remaining_bits); // put the terminating stop bit '1' + put_one_byte_final(eep, low); + } + else + { + put_one_byte_final(eep, 128); // second byte contains terminating stop bit '1' only + } + } + else // three terminating bytes + { + put_last_chunk_plus_outstanding(eep, ((low >> B_BITS) & B_LOAD_MASK)); // mask out the 16 MSBs for output + put_buffer(eep); + mbBits->mb_stuffing += (21 - remaining_bits); + + if (remaining_bits > 14) + { + mask = (unsigned char) (255 - ((1 << (22 - remaining_bits)) - 1)); + low = (low >> (MAX_BITS - 24)) & mask; + low += (0x200000 >> remaining_bits); // put the terminating stop bit '1' + put_one_byte_final(eep, low); + } + else + { + put_one_byte_final(eep, 128); // third byte contains terminating stop bit '1' only + } + } + eep->Ebits_to_go = 8; +} + + +/*! + ************************************************************************ + * \brief + * Actually arithmetic encoding of one binary symbol by using + * the probability estimate of its associated context model + ************************************************************************ + */ +void biari_encode_symbol(EncodingEnvironmentPtr eep, int symbol, BiContextTypePtr bi_ct ) +{ + static const byte rLPS_table_64x4[64][4]= + { + { 128, 176, 208, 240}, + { 128, 167, 197, 227}, + { 128, 158, 187, 216}, + { 123, 150, 178, 205}, + { 116, 142, 169, 195}, + { 111, 135, 160, 185}, + { 105, 128, 152, 175}, + { 100, 122, 144, 166}, + { 95, 116, 137, 158}, + { 90, 110, 130, 150}, + { 85, 104, 123, 142}, + { 81, 99, 117, 135}, + { 77, 94, 111, 128}, + { 73, 89, 105, 122}, + { 69, 85, 100, 116}, + { 66, 80, 95, 110}, + { 62, 76, 90, 104}, + { 59, 72, 86, 99}, + { 56, 69, 81, 94}, + { 53, 65, 77, 89}, + { 51, 62, 73, 85}, + { 48, 59, 69, 80}, + { 46, 56, 66, 76}, + { 43, 53, 63, 72}, + { 41, 50, 59, 69}, + { 39, 48, 56, 65}, + { 37, 45, 54, 62}, + { 35, 43, 51, 59}, + { 33, 41, 48, 56}, + { 32, 39, 46, 53}, + { 30, 37, 43, 50}, + { 29, 35, 41, 48}, + { 27, 33, 39, 45}, + { 26, 31, 37, 43}, + { 24, 30, 35, 41}, + { 23, 28, 33, 39}, + { 22, 27, 32, 37}, + { 21, 26, 30, 35}, + { 20, 24, 29, 33}, + { 19, 23, 27, 31}, + { 18, 22, 26, 30}, + { 17, 21, 25, 28}, + { 16, 20, 23, 27}, + { 15, 19, 22, 25}, + { 14, 18, 21, 24}, + { 14, 17, 20, 23}, + { 13, 16, 19, 22}, + { 12, 15, 18, 21}, + { 12, 14, 17, 20}, + { 11, 14, 16, 19}, + { 11, 13, 15, 18}, + { 10, 12, 15, 17}, + { 10, 12, 14, 16}, + { 9, 11, 13, 15}, + { 9, 11, 12, 14}, + { 8, 10, 12, 14}, + { 8, 9, 11, 13}, + { 7, 9, 11, 12}, + { 7, 9, 10, 12}, + { 7, 8, 10, 11}, + { 6, 8, 9, 11}, + { 6, 7, 9, 10}, + { 6, 7, 8, 9}, + { 2, 2, 2, 2} + }; + static const byte AC_next_state_MPS_64[64] = + { + 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,62,63 + }; + + static const byte AC_next_state_LPS_64[64] = + { + 0, 0, 1, 2, 2, 4, 4, 5, 6, 7, + 8, 9, 9,11,11,12,13,13,15,15, + 16,16,18,18,19,19,21,21,22,22, + 23,24,24,25,26,26,27,27,28,29, + 29,30,30,30,31,32,32,33,33,33, + 34,34,35,35,35,36,36,36,37,37, + 37,38,38,63 + }; + + unsigned int low = eep->Elow; + unsigned int range = eep->Erange; + int bl = eep->Ebits_to_go; + unsigned int rLPS = rLPS_table_64x4[bi_ct->state][(range>>6) & 3]; + + range -= rLPS; + + ++(eep->C); + bi_ct->count += eep->p_Vid->cabac_encoding; + + /* covers all cases where code does not bother to shift down symbol to be + * either 0 or 1, e.g. in some cases for cbp, mb_Type etc the code simply + * masks off the bit position and passes in the resulting value */ + //symbol = (short) (symbol != 0); + + if ((symbol != 0) == bi_ct->MPS) //MPS + { + bi_ct->state = AC_next_state_MPS_64[bi_ct->state]; // next state + + if( range >= QUARTER ) // no renorm + { + eep->Erange = range; + return; + } + else + { + range<<=1; + if( --bl > MIN_BITS_TO_GO ) // renorm once, no output + { + eep->Erange = range; + eep->Ebits_to_go = bl; + return; + } + } + } + else //LPS + { + unsigned int renorm = renorm_table_32[(rLPS >> 3) & 0x1F]; + + low += range << bl; + range = (rLPS << renorm); + bl -= renorm; + + if (!bi_ct->state) + bi_ct->MPS ^= 0x01; // switch MPS if necessary + + bi_ct->state = AC_next_state_LPS_64[bi_ct->state]; // next state + + if (low >= ONE) // output of carry needed + { + low -= ONE; + propagate_carry(eep); + } + + if( bl > MIN_BITS_TO_GO ) + { + eep->Elow = low; + eep->Erange = range; + eep->Ebits_to_go = bl; + return; + } + } + + //renorm needed + eep->Elow = (low << BITS_TO_LOAD )& (ONE_M1); + low = (low >> B_BITS) & B_LOAD_MASK; // mask out the 8/16 MSBs for output + + if (low < B_LOAD_MASK) // no carry possible, output now + { + put_last_chunk_plus_outstanding(eep, low); + } + else // low == "FF.."; keep it, may affect future carry + { + ++(eep->Echunks_outstanding); + } + eep->Erange = range; + eep->Ebits_to_go = bl + BITS_TO_LOAD; +} + +/*! + ************************************************************************ + * \brief + * Arithmetic encoding of one binary symbol assuming + * a fixed prob. distribution with p(symbol) = 0.5 + ************************************************************************ + */ +void biari_encode_symbol_eq_prob(EncodingEnvironmentPtr eep, int symbol) +{ + unsigned int low = eep->Elow; + --(eep->Ebits_to_go); + ++(eep->C); + + if (symbol != 0) + { + low += eep->Erange << eep->Ebits_to_go; + if (low >= ONE) // output of carry needed + { + low -= ONE; + propagate_carry(eep); + } + } + if(eep->Ebits_to_go == MIN_BITS_TO_GO) // renorm needed + { + eep->Elow = (low << BITS_TO_LOAD )& (ONE_M1); + low = (low >> B_BITS) & B_LOAD_MASK; // mask out the 8/16 MSBs for output + if (low < B_LOAD_MASK) // no carry possible, output now + { + put_last_chunk_plus_outstanding(eep, low);} + else // low == "FF"; keep it, may affect future carry + { + ++(eep->Echunks_outstanding); + } + + eep->Ebits_to_go = BITS_TO_LOAD; + return; + } + else // no renorm needed + { + eep->Elow = low; + return; + } +} + +/*! + ************************************************************************ + * \brief + * Arithmetic encoding for last symbol before termination + ************************************************************************ + */ +void biari_encode_symbol_final(EncodingEnvironmentPtr eep, int symbol) +{ + unsigned int range = eep->Erange - 2; + unsigned int low = eep->Elow; + int bl = eep->Ebits_to_go; + + ++(eep->C); + + if (symbol == 0) // MPS + { + if( range >= QUARTER ) // no renorm + { + eep->Erange = range; + return; + } + else + { + range<<=1; + if( --bl > MIN_BITS_TO_GO ) // renorm once, no output + { + eep->Erange = range; + eep->Ebits_to_go = bl; + return; + } + } + } + else // LPS + { + low += (range << bl); + range = 2; + + if (low >= ONE) // output of carry needed + { + low -= ONE; // remove MSB, i.e., carry bit + propagate_carry(eep); + } + bl -= 7; // 7 left shifts needed to renormalize + + range <<= 7; + if( bl > MIN_BITS_TO_GO ) + { + eep->Erange = range; + eep->Elow = low; + eep->Ebits_to_go = bl; + return; + } + } + + + //renorm needed + eep->Elow = (low << BITS_TO_LOAD ) & (ONE_M1); + low = (low >> B_BITS) & B_LOAD_MASK; // mask out the 8/16 MSBs + if (low < B_LOAD_MASK) + { // no carry possible, output now + put_last_chunk_plus_outstanding(eep, low); + } + else + { // low == "FF"; keep it, may affect future carry + ++(eep->Echunks_outstanding); + } + + eep->Erange = range; + bl += BITS_TO_LOAD; + eep->Ebits_to_go = bl; +} + +/*! + ************************************************************************ + * \brief + * Initializes a given context with some pre-defined probability state + ************************************************************************ + */ +void biari_init_context (int qp, BiContextTypePtr ctx, const char* ini) +{ + int pstate = ((ini[0]* qp) >> 4) + ini[1]; + + if ( pstate >= 64 ) + { + pstate = imin(126, pstate); + ctx->state = (uint8) (pstate - 64); + ctx->MPS = 1; + } + else + { + pstate = imax(1, pstate); + ctx->state = (uint8) (63 - pstate); + ctx->MPS = 0; + } + + ctx->count = 0; +} + diff --git a/lencod/src/block.c b/lencod/src/block.c new file mode 100644 index 0000000..2388296 --- /dev/null +++ b/lencod/src/block.c @@ -0,0 +1,2463 @@ + +/*! + ************************************************************************************* + * \file block.c + * + * \brief + * Process one block + * + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * - Inge Lille-Langoy + * - Rickard Sjoberg + * - Stephan Wenger + * - Jani Lainema + * - Detlev Marpe + * - Thomas Wedi + * - Ragip Kurceren + * - Greg Conklin + * - Alexis Michael Tourapis + ************************************************************************************* + */ + +#include "contributors.h" + +#include + +#include "global.h" +#include "blk_prediction.h" +#include "enc_statistics.h" +#include "memalloc.h" +#include "image.h" +#include "mb_access.h" +#include "block.h" +#include "vlc.h" +#include "transform.h" +#include "mc_prediction.h" +#include "q_offsets.h" +#include "q_matrix.h" +#include "quant4x4.h" +#include "quantChroma.h" +#include "md_common.h" +#include "transform8x8.h" + + +// Notation for comments regarding prediction and predictors. +// The pels of the 4x4 block are labelled a..p. The predictor pels above +// are labelled A..H, from the left I..P, and from above left X, as follows: +// +// X A B C D E F G H +// I a b c d +// J e f g h +// K i j k l +// L m n o p +// + +// Predictor array index definitions +#define P_X (PredPel[0]) +#define P_A (PredPel[1]) +#define P_B (PredPel[2]) +#define P_C (PredPel[3]) +#define P_D (PredPel[4]) +#define P_E (PredPel[5]) +#define P_F (PredPel[6]) +#define P_G (PredPel[7]) +#define P_H (PredPel[8]) +#define P_I (PredPel[9]) +#define P_J (PredPel[10]) +#define P_K (PredPel[11]) +#define P_L (PredPel[12]) + +//! array used to find expensive coefficients +static const byte COEFF_COST4x4[2][16] = +{ + {3,2,2,1,1,1,0,0,0,0,0,0,0,0,0,0}, + {9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9} +}; + +static const byte SCAN_YUV420 [4][2] = +{ + {0,0}, + {0,1}, + {0,2}, + {0,3} +}; + +//! single scan pattern +static const byte SCAN_YUV422 [8][2] = +{ + {0,0},{0,1}, + {1,0},{0,2}, + {0,3},{1,1}, + {1,2},{1,3} +}; + +//! look up tables for FRExt-chroma support +static const unsigned char hor_offset[4][4][4] = { + { + {0, 0, 0, 0}, + {0, 0, 0, 0}, + {0, 0, 0, 0}, + {0, 0, 0, 0} + }, + { + {0, 4, 0, 4}, + {0, 0, 0, 0}, + {0, 0, 0, 0}, + {0, 0, 0, 0} + }, + { + {0, 4, 0, 4}, + {0, 4, 0, 4}, + {0, 0, 0, 0}, + {0, 0, 0, 0} + }, + { + {0, 4, 0, 4}, + {8,12, 8,12}, + {0, 4, 0, 4}, + {8,12, 8,12} + } +}; + +static const unsigned char ver_offset[4][4][4] = { + { + {0, 0, 0, 0}, + {0, 0, 0, 0}, + {0, 0, 0, 0}, + {0, 0, 0, 0} + }, + { + {0, 0, 4, 4}, + {0, 0, 0, 0}, + {0, 0, 0, 0}, + {0, 0, 0, 0} + }, + { + {0, 0, 4, 4}, + {8, 8,12,12}, + {0, 0, 0, 0}, + {0, 0, 0, 0} + }, + { + {0, 0, 4, 4}, + {0, 0, 4, 4}, + {8, 8,12,12}, + {8, 8,12,12} + } +}; + +static const int A[4][4] = { + { 16, 20, 16, 20}, + { 20, 25, 20, 25}, + { 16, 20, 16, 20}, + { 20, 25, 20, 25} +}; + +static const unsigned char cbp_blk_chroma[8][4] = { + {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} +}; + +//! single scan pattern +static const byte SNGL_SCAN[16][2] = +{ + {0,0},{1,0},{0,1},{0,2}, + {1,1},{2,0},{3,0},{2,1}, + {1,2},{0,3},{1,3},{2,2}, + {3,1},{3,2},{2,3},{3,3} +}; + +//! field scan pattern +static const byte FIELD_SCAN[16][2] = +{ + {0,0},{0,1},{1,0},{0,2}, + {0,3},{1,1},{1,2},{1,3}, + {2,0},{2,1},{2,2},{2,3}, + {3,0},{3,1},{3,2},{3,3} +}; + + +//For residual DPCM +static int Residual_DPCM_16x16(int **mb_ores, int ipmode); +static int Inv_Residual_DPCM_16x16(int **mb_ores, int ipmode); +static int Residual_DPCM_4x4(int ipmode, int **mb_ores, int **mb_rres, int block_y, int block_x); +static int Inv_Residual_DPCM_4x4 (Macroblock *currMB, int **m7, int block_y, int block_x); +static int Residual_DPCM_Chroma(int ipmode, int **ores, int **rres, int width, int height); +static int Inv_Residual_DPCM_Chroma(int ipmode, int **m7, int width, int height); + +/*! + ************************************************************************ + * \brief + * For new intra pred routines + * + * \par Input: + * Image par, 16x16 based intra mode + * + * \par Output: + * none + ************************************************************************ + */ +int residual_transform_quant_luma_16x16(Macroblock *currMB, ColorPlane pl) +{ + int i,j; + int ii,jj; + + int ac_coef = 0; + imgpel *img_Y, *predY; + int nonzero = FALSE; + + int jpos, ipos; + int b8, b4; + + //begin the changes + int pl_off = pl<<2; + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currSlice->p_Vid; + QuantParameters *p_Quant = p_Vid->p_Quant; + + int* DCLevel = currSlice->cofDC[pl][0]; + int* DCRun = currSlice->cofDC[pl][1]; + int ****cofAC = &currSlice->cofAC[pl_off]; + int coeff_cost; + int new_intra_mode = currMB->i16mode; + imgpel **img_enc = p_Vid->enc_picture->p_curr_img; + int max_imgpel_value = p_Vid->max_imgpel_value; + int qp = (p_Vid->yuv_format==YUV444 && !currSlice->P444_joined)? currMB->qp_scaled[(int)(p_Vid->colour_plane_id)]: currMB->qp_scaled[pl]; //currMB->qp_scaled[pl]; + const byte (*pos_scan)[2] = currMB->is_field_mode ? FIELD_SCAN : SNGL_SCAN; + imgpel ***curr_mpr_16x16 = currSlice->mpr_16x16[pl]; + + int qp_per = p_Quant->qp_per_matrix[qp]; + + // select scaling parameters + // needs a fix for 444 modes (pl should be current color plane) + + QuantMethods quant_methods; + // set quantization parameters + + quant_methods.qp = qp; + quant_methods.q_params = p_Quant->q_params_4x4[pl][1][qp]; + quant_methods.fadjust = p_Vid->AdaptiveRounding ? (&p_Vid->ARCofAdj4x4[pl][I16MB][0]): NULL; + quant_methods.pos_scan = currMB->is_field_mode ? FIELD_SCAN : SNGL_SCAN; + quant_methods.c_cost = COEFF_COST4x4[currSlice->disthres]; + quant_methods.coeff_cost = &coeff_cost; + quant_methods.type = LUMA_16AC; + + + for (j = 0; j < 16; ++j) + { + predY = curr_mpr_16x16[new_intra_mode][j]; + img_Y = &p_Vid->pCurImg[currMB->opix_y + j][currMB->pix_x]; + for (i = 0; i < 16; ++i) + { + currSlice->tblk16x16[j][i] = img_Y[i] - predY[i]; + } + } + + // forward 4x4 integer transform + for (j = 0; j < 16; j+=4) + { + for (i = 0;i < 16; i+=4) + { + forward4x4(currSlice->tblk16x16, currSlice->tblk16x16, j, i); + } + } + + // pick out DC coeff + for (j = 0; j < 4; ++j) + for (i = 0; i < 4; ++i) + currSlice->tblk4x4[j][i]= currSlice->tblk16x16[j << 2][i << 2]; + + // hadamard of DC coefficients + hadamard4x4(currSlice->tblk4x4, currSlice->tblk4x4); + + nonzero = currSlice->quant_dc4x4(currMB, &currSlice->tblk4x4[0], qp, DCLevel, DCRun, &quant_methods.q_params[0][0], pos_scan); + + + // inverse DC transform + if (nonzero) + { + ihadamard4x4(currSlice->tblk4x4, currSlice->tblk4x4); + + // inverse quantization for the DC coefficients + for (j = 0; j < MB_BLOCK_SIZE; j += BLOCK_SIZE) + { + for (i = 0; i < MB_BLOCK_SIZE;i += BLOCK_SIZE) + { + currSlice->tblk16x16[j][i] = rshift_rnd_sf(((currSlice->tblk4x4[j>>2][i>>2]) * quant_methods.q_params[0][0].InvScaleComp) << qp_per, 6); + } + } + } + else // All DC equal to 0. + { + for (j = 0; j < MB_BLOCK_SIZE; j += BLOCK_SIZE) + { + for (i = 0; i < MB_BLOCK_SIZE; i += BLOCK_SIZE) + { + currSlice->tblk16x16[j][i] = 0; + } + } + } + + // AC processing for MB + for (jj=0;jj<4;++jj) + { + jpos = (jj << 2); + currMB->subblock_y = (short) jpos; + for (ii=0;ii<4;++ii) + { + ipos = (ii << 2); + currMB->subblock_x = (short) ipos; + + b8 = 2*(jj >> 1) + (ii >> 1); + b4 = 2*(jj & 0x01) + (ii & 0x01); + quant_methods.block_x = ipos; + quant_methods.block_y = jpos; + + quant_methods.ACLevel = cofAC[b8][b4][0]; + quant_methods.ACRun = cofAC[b8][b4][1]; + + // Quantization process + nonzero = currSlice->quant_ac4x4(currMB, &currSlice->tblk16x16[jpos], &quant_methods); + + if (nonzero) + ac_coef = 15; + + //inverse transform + if (currSlice->tblk16x16[jpos][ipos]!= 0 || nonzero) + inverse4x4(currSlice->tblk16x16, currSlice->tblk16x16, jpos, ipos); + } + } + + + // Reconstruct samples + sample_reconstruct (&img_enc[currMB->pix_y], curr_mpr_16x16[new_intra_mode], currSlice->tblk16x16, 0, currMB->pix_x, 16, 16, max_imgpel_value, DQ_BITS); + + if(currSlice->slice_type == SP_SLICE || currSlice->slice_type == SI_SLICE) + { + for (j = currMB->pix_y; j < currMB->pix_y + 16;++j) + for (i = currMB->pix_x; i < currMB->pix_x + 16;++i) + p_Vid->lrec[j][i]=-16; //signals an I16 block in the SP frame + } + + return ac_coef; +} + +static int Residual_DPCM_16x16(int **m7, int ipmode) +{ + int i,j; + int temp[16][16]; + + if(ipmode==VERT_PRED_16) + { + for (i=1; i<16; ++i) + for (j=0; j<16; ++j) + temp[i][j] = m7[i][j] - m7[i-1][j]; + + for (i=1; i<16; ++i) + for (j=0; j<16; ++j) + m7[i][j] = temp[i][j]; + } + else //HOR_PRED_16 + { + for (i=0; i<16; ++i) + for (j=1; j<16; ++j) + temp[i][j] = m7[i][j] - m7[i][j-1]; + + for (i=0; i<16; ++i) + for (j=1; j<16; ++j) + m7[i][j] = temp[i][j]; + } + return 0; +} + +static int Inv_Residual_DPCM_16x16(int **m7, int ipmode) +{ + int i; + int temp[16][16]; + + if(ipmode==VERT_PRED_16) + { + for (i=0; i<16; ++i) + { + temp[0][i] = m7[0][i]; + temp[1][i] = m7[1][i] + temp[0][i]; + temp[2][i] = m7[2][i] + temp[1][i]; + temp[3][i] = m7[3][i] + temp[2][i]; + temp[4][i] = m7[4][i] + temp[3][i]; + temp[5][i] = m7[5][i] + temp[4][i]; + temp[6][i] = m7[6][i] + temp[5][i]; + temp[7][i] = m7[7][i] + temp[6][i]; + temp[8][i] = m7[8][i] + temp[7][i]; + temp[9][i] = m7[9][i] + temp[8][i]; + temp[10][i] = m7[10][i] + temp[9][i]; + temp[11][i] = m7[11][i] + temp[10][i]; + temp[12][i] = m7[12][i] + temp[11][i]; + temp[13][i] = m7[13][i] + temp[12][i]; + temp[14][i] = m7[14][i] + temp[13][i]; + temp[15][i] = m7[15][i] + temp[14][i]; + } + // These could now just use a memcpy + for (i=0; i<16; ++i) + { + m7[1][i] = temp[1][i]; + m7[2][i] = temp[2][i]; + m7[3][i] = temp[3][i]; + m7[4][i] = temp[4][i]; + m7[5][i] = temp[5][i]; + m7[6][i] = temp[6][i]; + m7[7][i] = temp[7][i]; + m7[8][i] = temp[8][i]; + m7[9][i] = temp[9][i]; + m7[10][i] = temp[10][i]; + m7[11][i] = temp[11][i]; + m7[12][i] = temp[12][i]; + m7[13][i] = temp[13][i]; + m7[14][i] = temp[14][i]; + m7[15][i] = temp[15][i]; + } + } + else //HOR_PRED_16 + { + for(i=0; i<16; ++i) + { + temp[i][0] = m7[i][0]; + temp[i][1] = m7[i][1] + temp[i][0]; + temp[i][2] = m7[i][2] + temp[i][1]; + temp[i][3] = m7[i][3] + temp[i][2]; + temp[i][4] = m7[i][4] + temp[i][3]; + temp[i][5] = m7[i][5] + temp[i][4]; + temp[i][6] = m7[i][6] + temp[i][5]; + temp[i][7] = m7[i][7] + temp[i][6]; + temp[i][8] = m7[i][8] + temp[i][7]; + temp[i][9] = m7[i][9] + temp[i][8]; + temp[i][10] = m7[i][10] + temp[i][9]; + temp[i][11] = m7[i][11] + temp[i][10]; + temp[i][12] = m7[i][12] + temp[i][11]; + temp[i][13] = m7[i][13] + temp[i][12]; + temp[i][14] = m7[i][14] + temp[i][13]; + temp[i][15] = m7[i][15] + temp[i][14]; + } + for (i=0; i<16; ++i) + { + m7[i][1] = temp[i][1]; + m7[i][2] = temp[i][2]; + m7[i][3] = temp[i][3]; + m7[i][4] = temp[i][4]; + m7[i][5] = temp[i][5]; + m7[i][6] = temp[i][6]; + m7[i][7] = temp[i][7]; + m7[i][8] = temp[i][8]; + m7[i][9] = temp[i][9]; + m7[i][10] = temp[i][10]; + m7[i][11] = temp[i][11]; + m7[i][12] = temp[i][12]; + m7[i][13] = temp[i][13]; + m7[i][14] = temp[i][14]; + m7[i][15] = temp[i][15]; + } + } + return 0; +} + + +/*! + ************************************************************************ + * \brief + * For new intra pred routines + * + * \par Input: + * Image par, 16x16 based intra mode + * + * \par Output: + * none + ************************************************************************ + */ +int residual_transform_quant_luma_16x16_ls(Macroblock *currMB, ColorPlane pl) +{ + int i,j; + int ii,jj; + + int run,scan_pos,coeff_ctr; + int ac_coef = 0; + imgpel *img_Y, *predY; + + int b8, b4; + + //begin the changes + int pl_off = pl<<2; + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currSlice->p_Vid; + Boolean is_cavlc = (Boolean) (currSlice->symbol_mode == CAVLC); + int* DCLevel = currSlice->cofDC[pl][0]; + int* DCRun = currSlice->cofDC[pl][1]; + int* ACLevel; + int* ACRun; + imgpel **img_enc = p_Vid->enc_picture->p_curr_img; + int *m7; + int new_intra_mode = currMB->i16mode; + + const byte (*pos_scan)[2] = currMB->is_field_mode ? FIELD_SCAN : SNGL_SCAN; + imgpel ***curr_mpr_16x16 = currSlice->mpr_16x16[pl]; + + for (j = 0; j < 16; ++j) + { + predY = curr_mpr_16x16[new_intra_mode][j]; + img_Y = &p_Vid->pCurImg[currMB->opix_y + j][currMB->pix_x]; + for (i = 0; i < 16; ++i) + { + currSlice->tblk16x16[j][i] = img_Y[i] - predY[i]; + } + } + + if (new_intra_mode < 2) + { + Residual_DPCM_16x16(currSlice->tblk16x16, new_intra_mode); + } + + // pick out DC coeff + for (j = 0; j < 4;++j) + for (i = 0; i < 4;++i) + currSlice->tblk4x4[j][i]= currSlice->tblk16x16[j << 2][i << 2]; + + run=-1; + scan_pos=0; + + for (coeff_ctr=0;coeff_ctr<16;++coeff_ctr) + { + i=pos_scan[coeff_ctr][0]; + j=pos_scan[coeff_ctr][1]; + + ++run; + + m7 = &currSlice->tblk4x4[j][i]; + + if (*m7 != 0) + { + if (is_cavlc) + *m7 = iClip3(-CAVLC_LEVEL_LIMIT, CAVLC_LEVEL_LIMIT, *m7); + + DCLevel[scan_pos ] = *m7; + DCRun [scan_pos++] = run; + run=-1; + } + } + DCLevel[scan_pos]=0; + + // replace DC coeff. This is needed in case of out of limits for CAVLC. Could be done only for CAVLC + for (j = 0; j < 4;++j) + for (i = 0; i < 4;++i) + currSlice->tblk16x16[j << 2][i << 2] = currSlice->tblk4x4[j][i]; + + // AC inverse trans/quant for MB + for (jj = 0; jj < 4; ++jj) + { + for (ii = 0; ii < 4; ++ii) + { + for (j=0;j<4;++j) + { + memcpy(currSlice->tblk4x4[j],&currSlice->tblk16x16[(jj<<2)+j][(ii<<2)], BLOCK_SIZE * sizeof(int)); + } + + run = -1; + scan_pos = 0; + b8 = 2*(jj >> 1) + (ii >> 1); + b4 = 2*(jj & 0x01) + (ii & 0x01); + ACLevel = currSlice->cofAC [b8+pl_off][b4][0]; + ACRun = currSlice->cofAC [b8+pl_off][b4][1]; + + for (coeff_ctr=1;coeff_ctr<16;coeff_ctr++) // set in AC coeff + { + i=pos_scan[coeff_ctr][0]; + j=pos_scan[coeff_ctr][1]; + + run++; + m7 = &currSlice->tblk4x4[j][i]; + + if (*m7 != 0) + { + if (is_cavlc) + *m7 = iClip3(-CAVLC_LEVEL_LIMIT, CAVLC_LEVEL_LIMIT, *m7); + + ac_coef = 15; + ACLevel[scan_pos ] = *m7; + ACRun [scan_pos++] = run; + run=-1; + } + // set adaptive rounding p_Inp to 0 since process is not meaningful here. + } + ACLevel[scan_pos] = 0; + + for (j=0;j<4;++j) + memcpy(&currSlice->tblk16x16[(jj<<2)+j][(ii<<2)],currSlice->tblk4x4[j], BLOCK_SIZE * sizeof(int)); + } + } + + if (new_intra_mode < 2) + { + Inv_Residual_DPCM_16x16(currSlice->tblk16x16, new_intra_mode); + } + + + for (j = 0; j < 16; ++j) + { + img_Y = &img_enc[currMB->pix_y + j][currMB->pix_x]; + predY = curr_mpr_16x16[new_intra_mode][j]; + for (i = 0; i < 16; ++i) + img_Y[i]=(imgpel)(currSlice->tblk16x16[j][i] + predY[i]); + } + + if(currSlice->slice_type == SP_SLICE || currSlice->slice_type == SI_SLICE) + { + for (j = currMB->pix_y; j < currMB->pix_y + 16;++j) + for (i = currMB->pix_x; i < currMB->pix_x + 16;++i) + p_Vid->lrec[j][i]=-16; //signals an I16 block in the SP frame + } + + return ac_coef; +} + +static inline int check_zero(int **mb_ores, int block_x) +{ + int i, j, k = 0; + + for (j = 0; (j < BLOCK_SIZE) && (k == 0); ++j) + { + for (i = block_x; (i< block_x + BLOCK_SIZE) && (k == 0); ++i) + { + //k |= (mb_ores[j][i] != 0); + k |= mb_ores[j][i]; + } + } + return k; +} + +/*! +************************************************************************ +* \brief +* The routine performs transform,quantization,inverse transform, +* adds the diff to the prediction and writes the result to the +* decoded luma frame. +* +* \par Input: +* currMB: Current macroblock. +* pl: Color plane for 4:4:4 coding. +* block_x,block_y: Block position inside a macro block (0,4,8,12). +* intra: Intra block indicator. +* +* \par Output_ +* nonzero: 0 if no levels are nonzero. \n +* 1 if there are nonzero levels.\n +* coeff_cost: Coeff coding cost for thresholding consideration.\n +************************************************************************ +*/ +int residual_transform_quant_luma_4x4(Macroblock *currMB, ColorPlane pl, int block_x,int block_y, int *coeff_cost, int intra) +{ + int nonzero = FALSE; + + int pos_x = block_x >> BLOCK_SHIFT; + int pos_y = block_y >> BLOCK_SHIFT; + int b8 = 2*(pos_y >> 1) + (pos_x >> 1) + (pl<<2); + int b4 = 2*(pos_y & 0x01) + (pos_x & 0x01); + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currSlice->p_Vid; + + imgpel **img_enc = p_Vid->enc_picture->p_curr_img; + imgpel **mb_pred = currSlice->mb_pred[pl]; + int **mb_ores = currSlice->mb_ores[pl]; + + if (check_zero(&mb_ores[block_y], block_x) != 0) // check if any coefficients in block + { + int **mb_rres = currSlice->mb_rres[pl]; + int max_imgpel_value = p_Vid->max_imgpel_value; + int qp = (p_Vid->yuv_format==YUV444 && !currSlice->P444_joined)? currMB->qp_scaled[(int)(p_Vid->colour_plane_id)]: currMB->qp_scaled[pl]; + QuantParameters *p_Quant = p_Vid->p_Quant; + QuantMethods quant_methods; + quant_methods.ACLevel = currSlice->cofAC[b8][b4][0]; + quant_methods.ACRun = currSlice->cofAC[b8][b4][1]; + + quant_methods.block_x = block_x; + quant_methods.block_y = block_y; + quant_methods.qp = qp; + quant_methods.q_params = p_Quant->q_params_4x4[pl][intra][qp]; + quant_methods.fadjust = p_Vid->AdaptiveRounding ? (&p_Vid->ARCofAdj4x4[pl][currMB->ar_mode][block_y]) : NULL; + quant_methods.coeff_cost = coeff_cost; + quant_methods.pos_scan = currMB->is_field_mode ? FIELD_SCAN : SNGL_SCAN; + quant_methods.c_cost = COEFF_COST4x4[currSlice->disthres]; + + currMB->subblock_x = ((b8&0x1)==0) ? (((b4&0x1)==0)? 0: 4) : (((b4&0x1)==0)? 8: 12); // horiz. position for coeff_count context + currMB->subblock_y = (b8<2) ? ((b4<2) ? 0: 4) : ((b4<2) ? 8: 12); // vert. position for coeff_count context + + // Forward 4x4 transform + forward4x4(mb_ores, currSlice->tblk16x16, block_y, block_x); + + // Quantization process + nonzero = currSlice->quant_4x4(currMB, &currSlice->tblk16x16[block_y], &quant_methods); + + // Decoded block moved to frame memory + if (nonzero) + { + // Inverse 4x4 transform + inverse4x4(currSlice->tblk16x16, mb_rres, block_y, block_x); + + // generate final block + sample_reconstruct (&img_enc[currMB->pix_y + block_y], &mb_pred[block_y], &mb_rres[block_y], block_x, currMB->pix_x + block_x, BLOCK_SIZE, BLOCK_SIZE, max_imgpel_value, DQ_BITS); + } + else // if (nonzero) => No transformed residual. Just use prediction. + { + copy_image_data_4x4(&img_enc[currMB->pix_y + block_y], &mb_pred[block_y], currMB->pix_x + block_x, block_x); + } + } + else + { + currSlice->cofAC[b8][b4][0][0] = 0; + copy_image_data_4x4(&img_enc[currMB->pix_y + block_y], &mb_pred[block_y], currMB->pix_x + block_x, block_x); + } + + return nonzero; +} + + + +/*! +************************************************************************ +* \brief +* Process for lossless coding of coefficients. +* The routine performs transform, quantization,inverse transform, +* adds the diff to the prediction and writes the result to the +* decoded luma frame. +* +* \par Input: +* currMB: Current macroblock. +* pl: Color plane for 4:4:4 coding. +* block_x,block_y: Block position inside a macro block (0,4,8,12). +* intra: Intra block indicator. +* +* \par Output_ +* nonzero: 0 if no levels are nonzero. \n +* 1 if there are nonzero levels.\n +* coeff_cost: Coeff coding cost for thresholding consideration.\n +************************************************************************ +*/ +int residual_transform_quant_luma_4x4_ls(Macroblock *currMB, ColorPlane pl, int block_x,int block_y,int *coeff_cost, int intra) +{ + int i,j, coeff_ctr; + int run = -1; + int nonzero = FALSE; + + int pos_x = block_x >> BLOCK_SHIFT; + int pos_y = block_y >> BLOCK_SHIFT; + int b8 = 2*(pos_y >> 1) + (pos_x >> 1) + (pl<<2); + int b4 = 2*(pos_y & 0x01) + (pos_x & 0x01); + + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currSlice->p_Vid; + Boolean is_cavlc = (Boolean) (currSlice->symbol_mode == CAVLC); + int* ACL = &currSlice->cofAC[b8][b4][0][0]; + int* ACR = &currSlice->cofAC[b8][b4][1][0]; + + int pix_y, pix_x; + imgpel **img_enc = p_Vid->enc_picture->p_curr_img; + imgpel **mb_pred = currSlice->mb_pred[pl]; + int **mb_ores = currSlice->mb_ores[pl]; + int **mb_rres = currSlice->mb_rres[pl]; + int *m7; + + const byte *p_scan = currMB->is_field_mode ? &FIELD_SCAN[0][0] : &SNGL_SCAN[0][0]; + + // select scaling parameters + int **fadjust4x4 = p_Vid->AdaptiveRounding ? (&p_Vid->ARCofAdj4x4[pl][currMB->ar_mode][block_y]) : NULL; + + if( (currMB->ipmode_DPCM < 2) && (intra)) + { + Residual_DPCM_4x4(currMB->ipmode_DPCM, mb_ores, mb_rres, block_y, block_x); + } + else + { + for (j=block_y; j < block_y + BLOCK_SIZE; ++j) + for (i=block_x; i < block_x + BLOCK_SIZE; ++i) + mb_rres[j][i] = mb_ores[j][i]; + } + + for (coeff_ctr=0;coeff_ctr < 16;coeff_ctr++) + { + i = *p_scan++; + j = *p_scan++; + + run++; + + m7 = &mb_rres[block_y + j][block_x + i]; + + if (p_Vid->AdaptiveRounding) + fadjust4x4[j][block_x+i] = 0; + + if (*m7 != 0) + { + if (is_cavlc) + *m7 = iClip3(-CAVLC_LEVEL_LIMIT, CAVLC_LEVEL_LIMIT, *m7); + + nonzero=TRUE; + *coeff_cost += MAX_VALUE; + *ACL++ = *m7; + *ACR++ = run; + run=-1; // reset zero level counter + } + } + *ACL = 0; + + if( (currMB->ipmode_DPCM < 2) && (intra)) + { + Inv_Residual_DPCM_4x4(currMB, mb_rres, block_y, block_x); + } + + for (j=0; j < BLOCK_SIZE; ++j) + { + pix_y = currMB->pix_y + block_y + j; + pix_x = currMB->pix_x+block_x; + for (i=0; i < BLOCK_SIZE; ++i) + { + img_enc[pix_y][pix_x+i] = (imgpel) (mb_rres[j+block_y][i+block_x] + mb_pred[j+block_y][i+block_x]); + } + } + + return nonzero; +} + +/*! +************************************************************************ +* \brief +* Residual DPCM for Intra lossless coding +* +* \par Input: +* block_x,block_y: Block position inside a macro block (0,4,8,12). +************************************************************************ +*/ +static int Residual_DPCM_4x4(int ipmode, int **mb_ores, int **mb_rres, int block_y, int block_x) +{ + int i; + int temp[4][4]; + + if(ipmode==VERT_PRED) + { + for (i=0; i<4; ++i) + { + temp[0][i] = mb_ores[block_y + 0][block_x + i]; + temp[1][i] = mb_ores[block_y + 1][block_x + i] - mb_ores[block_y ][block_x + i]; + temp[2][i] = mb_ores[block_y + 2][block_x + i] - mb_ores[block_y + 1][block_x + i]; + temp[3][i] = mb_ores[block_y + 3][block_x + i] - mb_ores[block_y + 2][block_x + i]; + } + + for (i = 0; i < 4; ++i) + { + mb_rres[block_y + 0][block_x + i] = temp[0][i]; + mb_rres[block_y + 1][block_x + i] = temp[1][i]; + mb_rres[block_y + 2][block_x + i] = temp[2][i]; + mb_rres[block_y + 3][block_x + i] = temp[3][i]; + } + } + else //HOR_PRED + { + for (i=0; i<4; ++i) + { + temp[i][0] = mb_ores[block_y + i][block_x]; + temp[i][1] = mb_ores[block_y + i][block_x + 1] - mb_ores[block_y + i][block_x ]; + temp[i][2] = mb_ores[block_y + i][block_x + 2] - mb_ores[block_y + i][block_x + 1]; + temp[i][3] = mb_ores[block_y + i][block_x + 3] - mb_ores[block_y + i][block_x + 2]; + } + + for (i=0; i<4; ++i) + { + mb_rres[block_y + i][block_x + 0] = temp[i][0]; + mb_rres[block_y + i][block_x + 1] = temp[i][1]; + mb_rres[block_y + i][block_x + 2] = temp[i][2]; + mb_rres[block_y + i][block_x + 3] = temp[i][3]; + } + } + return 0; +} + +/*! +************************************************************************ +* \brief +* Inverse residual DPCM for Intra lossless coding +* +* \par Input: +* block_x,block_y: Block position inside a macro block (0,4,8,12). +************************************************************************ +*/ +//For residual DPCM +static int Inv_Residual_DPCM_4x4(Macroblock *currMB, int **m7, int block_y, int block_x) +{ + int i; + int temp[4][4]; + + if(currMB->ipmode_DPCM == VERT_PRED) + { + for(i=0; i<4; ++i) + { + temp[0][i] = m7[block_y + 0][block_x + i]; + temp[1][i] = m7[block_y + 1][block_x + i] + temp[0][i]; + temp[2][i] = m7[block_y + 2][block_x + i] + temp[1][i]; + temp[3][i] = m7[block_y + 3][block_x + i] + temp[2][i]; + } + for(i=0; i<4; ++i) + { + m7[block_y + 0][block_x + i] = temp[0][i]; + m7[block_y + 1][block_x + i] = temp[1][i]; + m7[block_y + 2][block_x + i] = temp[2][i]; + m7[block_y + 3][block_x + i] = temp[3][i]; + } + } + else //HOR_PRED + { + for(i=0; i<4; ++i) + { + temp[i][0] = m7[block_y + i][block_x + 0]; + temp[i][1] = m7[block_y + i][block_x + 1] + temp[i][0]; + temp[i][2] = m7[block_y + i][block_x + 2] + temp[i][1]; + temp[i][3] = m7[block_y + i][block_x + 3] + temp[i][2]; + } + for(i=0; i<4; ++i) + { + m7[block_y+i][block_x ] = temp[i][0]; + m7[block_y+i][block_x+1] = temp[i][1]; + m7[block_y+i][block_x+2] = temp[i][2]; + m7[block_y+i][block_x+3] = temp[i][3]; + } + } + return 0; +} + +/*! + ************************************************************************ + * \brief + * Transform,quantization,inverse transform for chroma. + * The main reason why this is done in a separate routine is the + * additional 2x2 transform of DC-coeffs. This routine is called + * once for each of the chroma components. + * + * \par Input: + * uv : Make difference between the U and V chroma component \n + * cr_cbp: chroma coded block pattern + * + * \par Output: + * cr_cbp: Updated chroma coded block pattern. + ************************************************************************ + */ +int residual_transform_quant_chroma_4x4(Macroblock *currMB, int uv, int cr_cbp) +{ + int i, j, n2, n1, coeff_ctr; + int *m1; + int coeff_cost = 0; + int cr_cbp_tmp = 0; + int DCzero = FALSE; + int nonzero[4][4] = {{FALSE}}; + int nonezero = FALSE; + //int empty_block = TRUE; + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currSlice->p_Vid; + QuantParameters *p_Quant = p_Vid->p_Quant; + + int b4; + int* DCLevel = currSlice->cofDC[uv+1][0]; + int* DCRun = currSlice->cofDC[uv+1][1]; + int intra = IS_INTRA (currMB); + int uv_scale = uv * (p_Vid->num_blk8x8_uv >> 1); + + //FRExt + static const int64 cbpblk_pattern[4]={0, 0xf0000, 0xff0000, 0xffff0000}; + int yuv = p_Vid->yuv_format; + int b8; + + const byte (*pos_scan)[2] = currMB->is_field_mode ? FIELD_SCAN : SNGL_SCAN; + int cur_qp = currMB->qpc[uv] + currSlice->bitdepth_chroma_qp_scale; + + int max_imgpel_value_uv = p_Vid->max_pel_value_comp[uv + 1]; + + int **mb_rres = currSlice->mb_rres[uv + 1]; + int **mb_ores = currSlice->mb_ores[uv + 1]; + imgpel **mb_pred = currSlice->mb_pred[uv + 1]; + + QuantMethods quant_methods; + // set quantization parameters + quant_methods.qp = cur_qp; + quant_methods.q_params = p_Quant->q_params_4x4[uv + 1][intra][cur_qp]; + quant_methods.type = CHROMA_AC; + if (currMB->mb_type == P8x8 && currMB->luma_transform_size_8x8_flag) + { + //P8x8, transform 8x8 chroma adjustments must be stored in a different array to avoid conflict with transform 4x4 + quant_methods.fadjust = p_Vid->AdaptiveRounding ? p_Vid->ARCofAdj4x4[uv + 1][4] : NULL; + } + else + { + quant_methods.fadjust = p_Vid->AdaptiveRounding ? p_Vid->ARCofAdj4x4[uv + 1][currMB->ar_mode] : NULL; + } + + quant_methods.pos_scan = currMB->is_field_mode ? FIELD_SCAN : SNGL_SCAN; + quant_methods.c_cost = COEFF_COST4x4[currSlice->disthres]; + quant_methods.coeff_cost = &coeff_cost; + + + p_Vid->is_v_block = uv; + + //============= integer transform =============== + for (n2=0; n2 < p_Vid->mb_cr_size_y; n2 += BLOCK_SIZE) + { + for (n1=0; n1 < p_Vid->mb_cr_size_x; n1 += BLOCK_SIZE) + { + if (check_zero(&mb_ores[n2], n1) == 0) // check if any coefficients in block + { + for (j = n2; j < n2 + BLOCK_SIZE; j++) + for (i = n1; i < n1 + BLOCK_SIZE; i++) + mb_rres[j][i] = 0; + } + else + { + forward4x4(mb_ores, mb_rres, n2, n1); + //empty_block = FALSE; + } + } + } + + if (yuv == YUV420) + { + int **fadjust2x2 = NULL; + + m1 = currSlice->tblk4x4[0]; + //================== CHROMA DC YUV420 =================== + + // forward 2x2 hadamard + hadamard2x2(mb_rres, m1); + + // Quantization process of chroma 2X2 hadamard transformed DC coeffs. + DCzero = currSlice->quant_dc_cr(currMB, &m1, cur_qp, DCLevel, DCRun, &quant_methods.q_params[0][0], fadjust2x2, SCAN_YUV420); + + if (DCzero) + { + currMB->cbp_blk |= 0xf0000 << (uv << 2) ; // if one of the 2x2-DC levels is != 0 set the + cr_cbp=imax(1,cr_cbp); // coded-bit all 4 4x4 blocks (bit 16-19 or 20-23) + } + + // Inverse transform of 2x2 DC levels + ihadamard2x2(m1, m1); + + mb_rres[0][0] = m1[0] >> 5; + mb_rres[0][4] = m1[1] >> 5; + mb_rres[4][0] = m1[2] >> 5; + mb_rres[4][4] = m1[3] >> 5; + } + else if (yuv == YUV422) + { + int **fadjust4x2 = NULL; // note that this is in fact 2x4 but the coefficients have been transposed for better memory access + //for YUV422 only + int cur_qp_dc = currMB->qpc[uv] + 3 + currSlice->bitdepth_chroma_qp_scale; + + LevelQuantParams **quant_paramsDC = p_Quant->q_params_4x4[uv + 1][intra][cur_qp_dc]; + + //================== CHROMA DC YUV422 =================== + //pick out DC coeff + for (j=0; j < p_Vid->mb_cr_size_y; j+=BLOCK_SIZE) + { + for (i=0; i < p_Vid->mb_cr_size_x; i+=BLOCK_SIZE) + currSlice->tblk4x4[i>>2][j>>2]= mb_rres[j][i]; + } + + // forward hadamard transform. Note that coeffs have been transposed (4x2 instead of 2x4) which makes transform a bit faster + hadamard4x2(currSlice->tblk4x4, currSlice->tblk4x4); + + // Quantization process of chroma transformed DC coeffs. + DCzero = currSlice->quant_dc_cr(currMB, currSlice->tblk4x4, cur_qp_dc, DCLevel, DCRun, &quant_paramsDC[0][0], fadjust4x2, SCAN_YUV422); + + if (DCzero) + { + currMB->cbp_blk |= 0xff0000 << (uv << 3) ; // if one of the DC levels is != 0 set the + cr_cbp=imax(1,cr_cbp); // coded-bit all 4 4x4 blocks (bit 16-31 or 32-47) //YUV444 + } + + //inverse DC transform. Note that now currSlice->tblk4x4 is transposed back + ihadamard4x2(currSlice->tblk4x4, currSlice->tblk4x4); + + // This code assumes sizeof(int) > 16. Therefore, no need to have conditional + for (j = 0; j < 4; ++j) + { + mb_rres[j << 2 ][0] = rshift_rnd_sf(currSlice->tblk4x4[j][0], 6); + mb_rres[j << 2 ][4] = rshift_rnd_sf(currSlice->tblk4x4[j][1], 6); + } + } + + // Quant of chroma AC-coeffs. + for (b8=0; b8 < (p_Vid->num_blk8x8_uv >> 1); b8++) + { + for (b4=0; b4 < 4; b4++) + { + int64 uv_cbpblk = ((int64)1) << cbp_blk_chroma[b8 + uv_scale][b4]; + n1 = hor_offset[yuv][b8][b4]; + n2 = ver_offset[yuv][b8][b4]; + quant_methods.ACLevel = currSlice->cofAC[4 + b8 + uv_scale][b4][0]; + quant_methods.ACRun = currSlice->cofAC[4 + b8 + uv_scale][b4][1]; + quant_methods.block_x = n1; + quant_methods.block_y = n2; + + currMB->subblock_y = subblk_offset_y[p_Vid->yuv_format - 1][b8][b4]; + currMB->subblock_x = subblk_offset_x[p_Vid->yuv_format - 1][b8][b4]; + if (p_Vid->AdaptiveRounding) + { + if (currMB->mb_type == P8x8 && currMB->luma_transform_size_8x8_flag) + { + //P8x8, transform 8x8 chroma adjustments must be stored in a different array to avoid conflict with transform 4x4 + quant_methods.fadjust = &p_Vid->ARCofAdj4x4[uv + 1][4][n2]; + } + else + { + quant_methods.fadjust = &p_Vid->ARCofAdj4x4[uv + 1][currMB->ar_mode][n2]; + } + } + else + { + quant_methods.fadjust = NULL; + } + + // Quantization process + nonzero[n2>>2][n1>>2] = currSlice->quant_ac4x4cr(currMB, &mb_rres[n2], &quant_methods); + + if (nonzero[n2>>2][n1>>2]) + { + currMB->cbp_blk |= uv_cbpblk; + cr_cbp_tmp = 2; + nonezero = TRUE; + } + } + } + + // Perform thresholding + // * reset chroma coeffs + if(nonezero && coeff_cost < _CHROMA_COEFF_COST_) + { + int64 uv_cbpblk = ((int64)cbpblk_pattern[yuv] << (uv << (1+yuv))); + cr_cbp_tmp = 0; + + for (b8 = 0; b8 < (p_Vid->num_blk8x8_uv >> 1); b8++) + { + for (b4 = 0; b4 < 4; b4++) + { + n1 = hor_offset[yuv][b8][b4]; + n2 = ver_offset[yuv][b8][b4]; + if (nonzero[n2>>2][n1>>2] == TRUE) + { + nonzero[n2>>2][n1>>2] = FALSE; + quant_methods.ACLevel = currSlice->cofAC[4 + b8 + uv_scale][b4][0]; + quant_methods.ACRun = currSlice->cofAC[4 + b8 + uv_scale][b4][1]; + + if (DCzero == 0) + currMB->cbp_blk &= ~(uv_cbpblk); // if no chroma DC's: then reset coded-bits of this chroma subblock + + quant_methods.ACLevel[0] = 0; + + for (coeff_ctr=1; coeff_ctr < 16; coeff_ctr++)// ac coeff + { + mb_rres[n2 + pos_scan[coeff_ctr][1]][n1 + pos_scan[coeff_ctr][0]] = 0; + quant_methods.ACLevel[coeff_ctr] = 0; + } + } + } + } + } + + // inverse transform. + // Horizontal. + if(cr_cbp_tmp == 2) + cr_cbp = 2; + + nonezero = FALSE; + for (n2=0; n2 < p_Vid->mb_cr_size_y; n2 += BLOCK_SIZE) + { + for (n1=0; n1 < p_Vid->mb_cr_size_x; n1 += BLOCK_SIZE) + { + if (mb_rres[n2][n1] != 0 || nonzero[n2>>2][n1>>2] == TRUE) + { + inverse4x4(mb_rres, mb_rres, n2, n1); + nonezero = TRUE; + } + } + } + + // Decoded block moved to memory + if (nonezero == TRUE) + { + sample_reconstruct (&p_Vid->enc_picture->imgUV[uv][currMB->pix_c_y], mb_pred, mb_rres, 0, currMB->pix_c_x, p_Vid->mb_cr_size_x, p_Vid->mb_cr_size_y, max_imgpel_value_uv, DQ_BITS); + } + else + { + copy_image_data(&p_Vid->enc_picture->imgUV[uv][currMB->pix_c_y], mb_pred, currMB->pix_c_x, 0, p_Vid->mb_cr_size_x, p_Vid->mb_cr_size_y); + } + + return cr_cbp; +} + +static int Residual_DPCM_Chroma(int ipmode, int **ores, int **rres, int width, int height) +{ + int i,j; + int temp[16][16]; + + if(ipmode==VERT_PRED_8) + { + for (j=0; jp_Slice; + VideoParameters *p_Vid = currSlice->p_Vid; + Boolean is_cavlc = (Boolean) (currSlice->symbol_mode == CAVLC); + int* DCLevel = currSlice->cofDC[uv+1][0]; + int* DCRun = currSlice->cofDC[uv+1][1]; + int* ACLevel; + int* ACRun; + int uv_scale = uv * (p_Vid->num_blk8x8_uv >> 1); + + //FRExt + int yuv = p_Vid->yuv_format; + int b8; + int *m7; + int m3[4][4]; + + const byte (*pos_scan)[2] = currMB->is_field_mode ? FIELD_SCAN : SNGL_SCAN; + int **mb_rres = currSlice->mb_rres[uv + 1]; + int **mb_ores = currSlice->mb_ores[uv + 1]; + imgpel **mb_pred = currSlice->mb_pred[uv + 1]; + int **fadjust4x4; + + int intra = ((currMB->mb_type == I4MB) || (currMB->mb_type == I8MB) || (currMB->mb_type == I16MB)); + + + if (currMB->mb_type == P8x8 && currMB->luma_transform_size_8x8_flag) + { + //P8x8, transform 8x8 chroma adjustments must be stored in a different array to avoid conflict with transform 4x4 + fadjust4x4 = p_Vid->AdaptiveRounding ? p_Vid->ARCofAdj4x4[uv + 1][4] : NULL; + } + else + { + fadjust4x4 = p_Vid->AdaptiveRounding ? p_Vid->ARCofAdj4x4[uv + 1][currMB->ar_mode] : NULL; + } + + if((currMB->c_ipred_mode == HOR_PRED_8 || currMB->c_ipred_mode == VERT_PRED_8) && (intra)) + { + Residual_DPCM_Chroma(currMB->c_ipred_mode, mb_ores, mb_rres, p_Vid->mb_cr_size_x, p_Vid->mb_cr_size_y); + } + else + { + for (j=0; j < p_Vid->mb_cr_size_y; j++) + { + for (i=0; i < p_Vid->mb_cr_size_x; i++) + { + mb_rres[j][i] = mb_ores[j][i]; + } + } + } + + if (yuv == YUV420) + { + //================== CHROMA DC YUV420 =================== + // 2X2 transform of DC coeffs. + run=-1; + scan_pos=0; + m1[0] = mb_rres[0][0] ; + m1[1] = mb_rres[0][4] ; + m1[2] = mb_rres[4][0] ; + m1[3] = mb_rres[4][4] ; + + for (coeff_ctr=0; coeff_ctr < 4; coeff_ctr++) + { + run++; + + level =iabs(m1[coeff_ctr]); + + if (level != 0) + { + if (is_cavlc) + level = imin(level, CAVLC_LEVEL_LIMIT); + + currMB->cbp_blk |= 0xf0000 << (uv << 2) ; // if one of the 2x2-DC levels is != 0 set the + cr_cbp=imax(1, cr_cbp); // coded-bit all 4 4x4 blocks (bit 16-19 or 20-23) + level = isignab(level, m1[coeff_ctr]); + DCLevel[scan_pos ] = level; + DCRun [scan_pos++] = run; + run=-1; + } + } + DCLevel[scan_pos] = 0; + } + else if(yuv == YUV422) + { + //================== CHROMA DC YUV422 =================== + //transform DC coeff + //horizontal + + //pick out DC coeff + for (j=0; j < p_Vid->mb_cr_size_y; j+=BLOCK_SIZE) + { + for (i=0; i < p_Vid->mb_cr_size_x; i+=BLOCK_SIZE) + { + m3[i>>2][j>>2] = mb_rres[j][i]; + } + } + + + run=-1; + scan_pos=0; + + //quant of chroma DC-coeffs + for (coeff_ctr=0;coeff_ctr<8;coeff_ctr++) + { + i=SCAN_YUV422[coeff_ctr][0]; + j=SCAN_YUV422[coeff_ctr][1]; + + run++; + + level = iabs(m3[i][j]); + currSlice->tblk4x4[i][j]=m3[i][j]; + + if (level != 0) + { + //YUV422 + currMB->cbp_blk |= 0xff0000 << (uv << 3) ; // if one of the DC levels is != 0 set the + cr_cbp=imax(1,cr_cbp); // coded-bit all 4 4x4 blocks (bit 16-31 or 32-47) //YUV444 + + DCLevel[scan_pos ] = isignab(level,currSlice->tblk4x4[i][j]); + DCRun [scan_pos++] = run; + run=-1; + } + } + DCLevel[scan_pos]=0; + + //inverse DC transform + //horizontal + } + + // Quant of chroma AC-coeffs. + coeff_cost=0; + + for (b8=0; b8 < (p_Vid->num_blk8x8_uv >> 1); b8++) + { + for (b4=0; b4 < 4; b4++) + { + int64 uv_cbpblk = ((int64)1) << cbp_blk_chroma[b8 + uv_scale][b4]; + n1 = hor_offset[yuv][b8][b4]; + n2 = ver_offset[yuv][b8][b4]; + ACLevel = currSlice->cofAC[4 + b8 + uv_scale][b4][0]; + ACRun = currSlice->cofAC[4 + b8 + uv_scale][b4][1]; + run=-1; + scan_pos=0; + + for (coeff_ctr=1; coeff_ctr < 16; coeff_ctr++)// start change rd_quant + { + i=pos_scan[coeff_ctr][0]; + j=pos_scan[coeff_ctr][1]; + + ++run; + + level = iabs(mb_rres[n2+j][n1+i]); + + if (p_Vid->AdaptiveRounding) + { + fadjust4x4[n2+j][n1+i] = 0; + } + + if (level != 0) + { + cr_cbp=imax(2, cr_cbp); + + currMB->cbp_blk |= uv_cbpblk; + coeff_cost += MAX_VALUE; // set high cost, shall not be discarded + + ACLevel[scan_pos ] = isignab(level, mb_rres[n2+j][n1+i]); + ACRun [scan_pos++] = run; + run=-1; + + level = isignab(level, mb_rres[n2+j][n1+i]); + } + } + ACLevel[scan_pos] = 0; + } + } + + if((currMB->c_ipred_mode == HOR_PRED_8 || currMB->c_ipred_mode == VERT_PRED_8) && intra) + { + Inv_Residual_DPCM_Chroma(currMB->c_ipred_mode, mb_rres, p_Vid->mb_cr_size_x, p_Vid->mb_cr_size_y) ; + } + + for (j=0; j < p_Vid->mb_cr_size_y; ++j) + { + orig_img = &p_Vid->enc_picture->imgUV[uv][currMB->pix_c_y + j][currMB->pix_c_x]; + m7 = mb_rres[j]; + pred_img = mb_pred[j]; + for (i=0; i < p_Vid->mb_cr_size_x; ++i) + { + orig_img[i] = (imgpel) m7[i] + pred_img[i]; + } + } + + return cr_cbp; +} + +/*! + ************************************************************************ + * \brief + * The routine performs transform,quantization,inverse transform, adds the diff. + * to the prediction and writes the result to the decoded luma frame. Includes the + * RD constrained quantization also. + * + * \par Input: + * block_x,block_y: Block position inside a macro block (0,4,8,12). + * + * \par Output: + * nonzero: 0 if no levels are nonzero. 1 if there are nonzero levels. \n + * coeff_cost: Counter for nonzero coefficients, used to discard expensive levels. + * + * + ************************************************************************ + */ +int residual_transform_quant_luma_4x4_sp(Macroblock *currMB, ColorPlane pl, int block_x,int block_y,int *coeff_cost, int intra) +{ + int i,j,coeff_ctr; + int qp_const,ilev, level,scan_pos = 0,run = -1; + int nonzero = FALSE; + VideoParameters *p_Vid = currMB->p_Vid; + InputParameters *p_Inp = currMB->p_Inp; + Slice *currSlice = currMB->p_Slice; + + imgpel **img_enc = p_Vid->enc_picture->p_curr_img; + imgpel **mb_pred = currSlice->mb_pred[pl]; + int **mb_rres = currSlice->mb_rres[pl]; + int **mb_ores = currSlice->mb_ores[pl]; + int c_err,qp_const2; + + int qp = currMB->qp_scaled[pl]; + int qp_sp = (currMB->qpsp); + + const byte *c_cost = COEFF_COST4x4[p_Inp->disthres]; + const byte (*pos_scan)[2] = currMB->is_field_mode ? FIELD_SCAN : SNGL_SCAN; + + int pos_x = block_x >> BLOCK_SHIFT; + int pos_y = block_y >> BLOCK_SHIFT; + int b8 = 2*(pos_y >> 1) + (pos_x >> 1); + int b4 = 2*(pos_y & 0x01) + (pos_x & 0x01); + int* ACLevel = currSlice->cofAC[b8][b4][0]; + int* ACRun = currSlice->cofAC[b8][b4][1]; + + // For encoding optimization + int c_err1, c_err2, level1, level2; + double D_dis1, D_dis2; + int len, info; + double lambda_mode = 0.85 * pow (2, (qp - SHIFT_QP)/3.0) * 4; + + QuantParameters *p_Quant = p_Vid->p_Quant; + + int qp_per = p_Quant->qp_per_matrix[qp]; + int q_bits = Q_BITS + qp_per; + int qp_per_sp = p_Quant->qp_per_matrix[qp_sp]; + int q_bits_sp = Q_BITS + qp_per_sp; + + LevelQuantParams **q_params_4x4 = p_Quant->q_params_4x4[pl][intra][qp]; + LevelQuantParams **quant_params_sp = p_Quant->q_params_4x4[pl][intra][qp_sp]; + + //QuantMethods quant_methods; + //quant_methods.ACLevel = currSlice->cofAC[b8][b4][0]; + //quant_methods.ACRun = currSlice->cofAC[b8][b4][1]; + + //quant_methods.block_x = block_x; + //quant_methods.block_y = block_y; + //quant_methods.qp = qp; + + //quant_methods.q_params = p_Quant->q_params_4x4[pl][intra][qp]; + //quant_methods.fadjust = p_Vid->AdaptiveRounding ? (&p_Vid->ARCofAdj4x4[pl][currMB->ar_mode][block_y]) : NULL; + //quant_methods.coeff_cost = coeff_cost; + //quant_methods.pos_scan = currMB->is_field_mode ? FIELD_SCAN : SNGL_SCAN; + //quant_methods.c_cost = COEFF_COST4x4[currSlice->disthres]; + + qp_const = (1<>1; //sp_pred + + // Horizontal transform + for (j=block_y; j< block_y + BLOCK_SIZE; ++j) + { + for (i=block_x; i< block_x + BLOCK_SIZE; ++i) + { + mb_rres[j][i] = mb_ores[j][i]; + mb_rres[j][i]+=mb_pred[j][i]; + currSlice->tblk16x16[j][i] = mb_pred[j][i]; + } + } + + // 4x4 transform + forward4x4(mb_rres, mb_rres, block_y, block_x); + forward4x4(currSlice->tblk16x16, currSlice->tblk16x16, block_y, block_x); + + for (coeff_ctr = 0;coeff_ctr < 16;coeff_ctr++) + { + i = pos_scan[coeff_ctr][0]; + j = pos_scan[coeff_ctr][1]; + + run++; + ilev=0; + + // decide prediction + + // case 1 + level1 = (iabs (currSlice->tblk16x16[j+block_y][i+block_x]) * quant_params_sp[j][i].ScaleComp + qp_const2) >> q_bits_sp; + level1 = (level1 << q_bits_sp) / quant_params_sp[j][i].ScaleComp; + c_err1 = mb_rres[j+block_y][i+block_x] - isignab(level1, currSlice->tblk16x16[j+block_y][i+block_x]); + level1 = (iabs (c_err1) * q_params_4x4[j][i].ScaleComp + qp_const) >> q_bits; + + // case 2 + c_err2 = mb_rres[j+block_y][i+block_x] - currSlice->tblk16x16[j+block_y][i+block_x]; + level2 = (iabs (c_err2) * q_params_4x4[j][i].ScaleComp + qp_const) >> q_bits; + + // select prediction + if ((level1 != level2) && (level1 != 0) && (level2 != 0)) + { + D_dis1 = mb_rres[j+block_y][i+block_x] - + ((isignab(level1,c_err1) * (q_params_4x4[j][i].InvScaleComp >> 4) * A[j][i]<< qp_per) >>6) + - currSlice->tblk16x16[j+block_y][i+block_x]; + levrun_linfo_inter(level1, run, &len, &info); + D_dis1 = D_dis1 * D_dis1 + lambda_mode * len; + + D_dis2 = mb_rres[j+block_y][i+block_x] - + ((isignab(level2,c_err2)* (q_params_4x4[j][i].InvScaleComp >> 4)* A[j][i]<< qp_per) >>6) + - currSlice->tblk16x16[j+block_y][i+block_x]; + levrun_linfo_inter(level2, run, &len, &info); + D_dis2 = D_dis2 * D_dis2 + lambda_mode * len; + + if (D_dis1 == D_dis2) + level = (iabs(level1) < iabs(level2)) ? level1 : level2; + else if (D_dis1 < D_dis2) + level = level1; + else + level = level2; + + c_err = (level == level1) ? c_err1 : c_err2; + } + else if (level1 == level2) + { + level = level1; + c_err = c_err1; + } + else + { + level = (level1 == 0) ? level1 : level2; + c_err = (level1 == 0) ? c_err1 : c_err2; + } + + if (level != 0) + { + nonzero = TRUE; + + *coeff_cost += (level > 1) ? MAX_VALUE : c_cost[run]; + + level = isignab(level,c_err); + ACLevel[scan_pos] = level; + ACRun [scan_pos] = run; + ++scan_pos; + run=-1; // reset zero level counter + ilev=((level * (q_params_4x4[j][i].InvScaleComp >> 4)* A[j][i] << qp_per) >>6); + } + + ilev += currSlice->tblk16x16[j+block_y][i+block_x]; + + if(currSlice->slice_type != SI_SLICE && !p_Vid->sp2_frame_indicator)//stores the SP frame coefficients in p_Vid->lrec, will be useful to encode these and create SI or SP switching frame + { + p_Vid->lrec[currMB->pix_y+block_y+j][currMB->pix_x+block_x+i]= + isignab((iabs(ilev) * quant_params_sp[j][i].ScaleComp + qp_const2) >> q_bits_sp, ilev); + } + + // Yrec Qs and DeQs + mb_rres[j+block_y][i+block_x] = isignab((iabs(ilev) * quant_params_sp[j][i].ScaleComp + qp_const2)>> q_bits_sp, ilev) * (quant_params_sp[j][i].InvScaleComp >> 4) << qp_per_sp; + } + ACLevel[scan_pos] = 0; + + // inverse transform + inverse4x4(mb_rres, mb_rres, block_y, block_x); + // inverse4x4(currSlice->tblk16x16, mb_rres, 0, 0); + + + for (j=block_y; j < block_y+BLOCK_SIZE; ++j) + { + for (i=block_x; i < block_x+BLOCK_SIZE; ++i) + { + //printf("%d ",mb_rres[j][i]); + mb_rres[j][i] = iClip1 (p_Vid->max_imgpel_value, rshift_rnd_sf(mb_rres[j][i], DQ_BITS)); + //printf("%d\n",mb_rres[j][i]); + } + } + + // Decoded block moved to frame memory + for (j=0; j < BLOCK_SIZE; ++j) + { + for (i=0; i < BLOCK_SIZE; ++i) + { + img_enc[currMB->pix_y+block_y+j][currMB->pix_x+block_x+i]= (imgpel) mb_rres[block_y+j][block_x+i]; + + //printf("%d\n",mb_rres[j][i]); + } + } + + return nonzero; +} + +/*! + ************************************************************************ + * \brief + * Transform,quantization,inverse transform for chroma. + * The main reason why this is done in a separate routine is the + * additional 2x2 transform of DC-coeffs. This routine is called + * once for each of the chroma components. + * + * \par Input: + * uv : Make difference between the U and V chroma component + * cr_cbp: chroma coded block pattern + * + * \par Output: + * cr_cbp: Updated chroma coded block pattern. + ************************************************************************ + */ +int residual_transform_quant_chroma_4x4_sp(Macroblock *currMB, int uv,int cr_cbp) +{ + int i,j,ilev,n2,n1,coeff_ctr,c_err,level ,scan_pos,run; + int m1[BLOCK_SIZE]; + int coeff_cost; + int cr_cbp_tmp; + int mp1[BLOCK_SIZE]; + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currSlice->p_Vid; + Boolean is_cavlc = (Boolean) (currSlice->symbol_mode == CAVLC); + const byte *c_cost = COEFF_COST4x4[currSlice->disthres]; + const byte (*pos_scan)[2] = currMB->is_field_mode ? FIELD_SCAN : SNGL_SCAN; + + int intra = IS_INTRA (currMB); + + int b4; + int* DCLevel = currSlice->cofDC[uv+1][0]; + int* DCRun = currSlice->cofDC[uv+1][1]; + + int c_err1, c_err2, level1, level2; + int len, info; + double D_dis1, D_dis2; + double lambda_mode = 0.85 * pow (2, (currMB->qp -SHIFT_QP)/3.0) * 4; + int max_imgpel_value_uv = p_Vid->max_pel_value_comp[1]; + + int qpChroma = currMB->qpc[uv] + currSlice->bitdepth_chroma_qp_scale; + int qpc_sp = iClip3(-currSlice->bitdepth_chroma_qp_scale, 51, currMB->qpsp + p_Vid->active_pps->chroma_qp_index_offset); + int qpChromaSP = qpc_sp < 0 ? qpc_sp : QP_SCALE_CR[qpc_sp]; + + int **mb_rres = currSlice->mb_rres[uv + 1]; + int **mb_ores = currSlice->mb_ores[uv + 1]; + imgpel **mb_pred = currSlice->mb_pred[uv + 1]; + + QuantMethods quant_methods; + QuantParameters *p_Quant = p_Vid->p_Quant; + + int qp_per = p_Quant->qp_per_matrix[qpChroma]; + int q_bits = Q_BITS + qp_per; + int qp_const = (1<qp_per_matrix[qpChromaSP]; + int q_bits_sp = Q_BITS + qp_per_sp; + int qp_const2 = (1<>1; //sp_pred + + LevelQuantParams **q_params_4x4 = p_Quant->q_params_4x4[uv + 1][intra][qpChroma]; + + LevelQuantParams **quant_params_sp = p_Quant->q_params_4x4[uv + 1][intra][qpChromaSP]; + + quant_methods.type = CHROMA_AC; + + for (j=0; j < p_Vid->mb_cr_size_y; ++j) + { + for (i=0; i < p_Vid->mb_cr_size_x; ++i) + { + mb_rres[j][i] = mb_ores[j][i]; + mb_rres[j][i] += mb_pred[j][i]; + currSlice->tblk16x16[j][i] = mb_pred[j][i]; + } + } + + for (n2=0; n2 < p_Vid->mb_cr_size_y; n2 += BLOCK_SIZE) + { + for (n1=0; n1 < p_Vid->mb_cr_size_x; n1 += BLOCK_SIZE) + { + forward4x4(mb_rres, mb_rres, n2, n1); + forward4x4(currSlice->tblk16x16, currSlice->tblk16x16, n2, n1); + } + } + + // 2X2 transform of DC coeffs. + hadamard2x2(mb_rres, m1); + hadamard2x2(currSlice->tblk16x16, mp1); + + run=-1; + scan_pos=0; + + for (coeff_ctr = 0; coeff_ctr < 4; coeff_ctr++) + { + run++; + ilev=0; + + // case 1 + c_err1 = (iabs (mp1[coeff_ctr]) * quant_params_sp[0][0].ScaleComp + 2 * qp_const2) >> (q_bits_sp + 1); + c_err1 = (c_err1 << (q_bits_sp + 1)) / quant_params_sp[0][0].ScaleComp; + c_err1 = m1[coeff_ctr] - isignab(c_err1, mp1[coeff_ctr]); + level1 = (iabs(c_err1) * q_params_4x4[0][0].ScaleComp + 2 * qp_const) >> (q_bits+1); + + // case 2 + c_err2 = m1[coeff_ctr] - mp1[coeff_ctr]; + level2 = (iabs(c_err2) * q_params_4x4[0][0].ScaleComp + 2 * qp_const) >> (q_bits+1); + + if (level1 != level2 && level1 != 0 && level2 != 0) + { + D_dis1 = m1[coeff_ctr] - ((isignab(level1,c_err1)* (q_params_4x4[0][0].InvScaleComp >> 4) * A[0][0]<< qp_per) >>5)- mp1[coeff_ctr]; + levrun_linfo_c2x2(level1, run, &len, &info); + D_dis1 = D_dis1 * D_dis1 + lambda_mode * len; + + D_dis2 = m1[coeff_ctr] - ((isignab(level2,c_err2)* (q_params_4x4[0][0].InvScaleComp >> 4) * A[0][0]<< qp_per) >>5)- mp1[coeff_ctr]; + levrun_linfo_c2x2(level2, run, &len, &info); + D_dis2 = D_dis2 * D_dis2 + lambda_mode * len; + + if (D_dis1 == D_dis2) + level = (iabs(level1) < iabs(level2)) ? level1 : level2; + else if (D_dis1 < D_dis2) + level = level1; + else + level = level2; + + c_err = (level == level1) ? c_err1 : c_err2; + } + else if (level1 == level2) + { + level = level1; + c_err = c_err1; + } + else + { + level = (level1 == 0) ? level1 : level2; + c_err = (level1 == 0) ? c_err1 : c_err2; + } + + if (level != 0) + { + if (is_cavlc) + level = imin(level, CAVLC_LEVEL_LIMIT); + + currMB->cbp_blk |= 0xf0000 << (uv << 2) ; // if one of the 2x2-DC levels is != 0 the coded-bit + cr_cbp = imax(1, cr_cbp); + DCLevel[scan_pos ] = isignab(level ,c_err); + DCRun [scan_pos++] = run; + run=-1; + ilev=((isignab(level,c_err)* (q_params_4x4[0][0].InvScaleComp*A[0][0] >> 4) << qp_per) >>5); + } + + ilev+= mp1[coeff_ctr]; + m1[coeff_ctr]=isignab((iabs(ilev) * quant_params_sp[0][0].ScaleComp + 2 * qp_const2) >> (q_bits_sp+1), ilev) * (quant_params_sp[0][0].InvScaleComp >> 4) << qp_per_sp; + if(currSlice->slice_type != SI_SLICE && !p_Vid->sp2_frame_indicator) + p_Vid->lrec_uv[uv][currMB->pix_c_y + 4*(coeff_ctr & 0x01)][currMB->pix_c_x + 4*(coeff_ctr>>1)]=isignab((iabs(ilev) * quant_params_sp[0][0].ScaleComp + 2 * qp_const2) >> (q_bits_sp+1), ilev);// stores the SP frames coefficients, will be useful to encode SI or switching SP frame + } + DCLevel[scan_pos] = 0; + + // Inverse transform of 2x2 DC levels + ihadamard2x2(m1, m1); + + mb_rres[0][0]=(m1[0])>>1; + mb_rres[0][4]=(m1[1])>>1; + mb_rres[4][0]=(m1[2])>>1; + mb_rres[4][4]=(m1[3])>>1; + + // Quant of chroma AC-coeffs. + coeff_cost=0; + cr_cbp_tmp=0; + + for (n2=0; n2 <= BLOCK_SIZE; n2 += BLOCK_SIZE) + { + for (n1=0; n1 <= BLOCK_SIZE; n1 += BLOCK_SIZE) + { + b4 = 2*(n2 >> 2) + (n1 >> 2); + quant_methods.ACLevel = currSlice->cofAC[uv+4][b4][0]; + quant_methods.ACRun = currSlice->cofAC[uv+4][b4][1]; + + run = -1; + scan_pos = 0; + + for (coeff_ctr=1; coeff_ctr < 16; coeff_ctr++)// start change rd_quant + { + i=pos_scan[coeff_ctr][0]; + j=pos_scan[coeff_ctr][1]; + + ++run; + ilev=0; + + // quantization on prediction + c_err1 = (iabs(currSlice->tblk16x16[n2+j][n1+i]) * quant_params_sp[j][i].ScaleComp + qp_const2) >> q_bits_sp; + c_err1 = (c_err1 << q_bits_sp) / quant_params_sp[j][i].ScaleComp; + c_err1 = mb_rres[n2+j][n1+i] - isignab(c_err1, currSlice->tblk16x16[n2+j][n1+i]); + level1 = (iabs(c_err1) * q_params_4x4[j][i].ScaleComp + qp_const) >> q_bits; + + // no quantization on prediction + c_err2 = mb_rres[n2+j][n1+i] - currSlice->tblk16x16[n2+j][n1+i]; + level2 = (iabs(c_err2) * q_params_4x4[j][i].ScaleComp + qp_const) >> q_bits; + + if (level1 != level2 && level1 != 0 && level2 != 0) + { + D_dis1 = mb_rres[n2+j][n1+i] - ((isignab(level1,c_err1) * (q_params_4x4[j][i].InvScaleComp >> 4) * A[j][i]<< qp_per) >>6) - currSlice->tblk16x16[n2+j][n1+i]; + + levrun_linfo_inter(level1, run, &len, &info); + D_dis1 = D_dis1 * D_dis1 + lambda_mode * len; + + D_dis2 = mb_rres[n2+j][n1+i] - ((isignab(level2,c_err2)* (q_params_4x4[j][i].InvScaleComp*A[j][i] >> 4) << qp_per) >>6) - currSlice->tblk16x16[n2+j][n1+i]; + levrun_linfo_inter(level2, run, &len, &info); + D_dis2 = D_dis2 * D_dis2 + lambda_mode * len; + + if (D_dis1 == D_dis2) + level = (iabs(level1) < iabs(level2)) ? level1 : level2; + else + { + if (D_dis1 < D_dis2) + level = level1; + else + level = level2; + } + c_err = (level == level1) ? c_err1 : c_err2; + } + else if (level1 == level2) + { + level = level1; + c_err = c_err1; + } + else + { + level = (level1 == 0) ? level1 : level2; + c_err = (level1 == 0) ? c_err1 : c_err2; + } + + if (level != 0) + { + currMB->cbp_blk |= (int64)1 << (16 + (uv << 2) + ((n2 >> 1) + (n1 >> 2))) ; + + coeff_cost += (level > 1) ? MAX_VALUE : c_cost[run]; // set high cost, shall not be discarded + + cr_cbp_tmp=2; + level = isignab(level,c_err); + quant_methods.ACLevel[scan_pos] = level; + quant_methods.ACRun [scan_pos] = run; + ++scan_pos; + run=-1; + ilev=((level * (q_params_4x4[j][i].InvScaleComp*A[j][i] >> 4) << qp_per) >>6); + } + ilev+=currSlice->tblk16x16[n2+j][n1+i]; + if(currSlice->slice_type != SI_SLICE && !p_Vid->sp2_frame_indicator) + if(!( ((n2+j) & 0x03)==0 && ((n1+i) & 0x03) ==0 )) + p_Vid->lrec_uv[uv][currMB->pix_c_y + n2+j][currMB->pix_c_x + n1 + i]=isignab((iabs(ilev) * quant_params_sp[j][i].ScaleComp + qp_const2) >> q_bits_sp,ilev);//stores the SP frames coefficients, will be useful to encode SI or switching SP frame + mb_rres[n2+j][n1+i] = isignab((iabs(ilev) * quant_params_sp[j][i].ScaleComp + qp_const2) >> q_bits_sp,ilev) * (quant_params_sp[j][i].InvScaleComp >> 4) << qp_per_sp; + } + quant_methods.ACLevel[scan_pos] = 0; + } + } + + // * reset chroma coeffs + + if(cr_cbp_tmp==2) + cr_cbp=2; + + // inverse transform. + // Horizontal. + for (n2=0; n2 <= BLOCK_SIZE; n2 += BLOCK_SIZE) + { + for (n1=0; n1 <= BLOCK_SIZE; n1 += BLOCK_SIZE) + { + inverse4x4(mb_rres, mb_rres, n2, n1); + + for (j=0; j < BLOCK_SIZE; ++j) + for (i=0; i < BLOCK_SIZE; ++i) + { + mb_rres[n2+j][n1+i] = iClip1 (max_imgpel_value_uv,rshift_rnd_sf(mb_rres[n2+j][n1+i], DQ_BITS)); + } + } + } + + // Decoded block moved to memory + for (j=0; j < BLOCK_SIZE*2; ++j) + for (i=0; i < BLOCK_SIZE*2; ++i) + { + p_Vid->enc_picture->imgUV[uv][currMB->pix_c_y + j][currMB->pix_c_x + i]= (imgpel) mb_rres[j][i]; + } + + return cr_cbp; +} + + +/*! + ************************************************************************ + * \brief + * The routine performs transform,quantization,inverse transform, adds the diff. + * to the prediction and writes the result to the decoded luma frame. Includes the + * RD constrained quantization also. + * + * \par Input: + * block_x,block_y: Block position inside a macro block (0,4,8,12). + * + * \par Output: + * nonzero: 0 if no levels are nonzero. 1 if there are nonzero levels. \n + * coeff_cost: Counter for nonzero coefficients, used to discard expensive levels. + ************************************************************************ + */ +void copyblock_sp(Macroblock *currMB, ColorPlane pl, int block_x,int block_y) +{ + int i, j; + + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currSlice->p_Vid; + QuantParameters *p_Quant = p_Vid->p_Quant; + int cur_qp = currMB->qpsp + p_Vid->bitdepth_luma_qp_scale; + int qp_per = p_Quant->qp_per_matrix[cur_qp]; + int q_bits = Q_BITS + qp_per; + int qp_const2 = (1 << q_bits) >> 1; //sp_pred + imgpel **img_enc = p_Vid->enc_picture->p_curr_img; + imgpel **mb_pred = currSlice->mb_pred[pl]; + int **mb_ores = currSlice->mb_ores[pl]; + int **mb_rres = currSlice->mb_rres[pl]; + + LevelQuantParams **q_params_4x4 = p_Quant->q_params_4x4[pl][0][cur_qp]; + + // Horizontal transform + for (j=0; j< BLOCK_SIZE; ++j) + { + for (i=0; i< BLOCK_SIZE; ++i) + { + mb_rres[j][i] = mb_ores[j+block_y][i+block_x]; + currSlice->tblk16x16[j][i]=mb_pred[j+block_y][i+block_x]; + } + } + + forward4x4(currSlice->tblk16x16, currSlice->tblk16x16, 0, 0); + + // Quant + for (j=0;j < BLOCK_SIZE; ++j) + { + for (i=0; i < BLOCK_SIZE; ++i) + { + mb_rres[j][i]=isignab((iabs(currSlice->tblk16x16[j][i])* q_params_4x4[j][i].ScaleComp+qp_const2)>> q_bits, + currSlice->tblk16x16[j][i]) * (q_params_4x4[j][i].InvScaleComp >> 4) <slice_type != SI_SLICE && !p_Vid->sp2_frame_indicator) + { + p_Vid->lrec[currMB->pix_y+block_y+j][currMB->pix_x+block_x+i] = + isignab((iabs(currSlice->tblk16x16[j][i]) * q_params_4x4[j][i].ScaleComp + qp_const2) >> q_bits, currSlice->tblk16x16[j][i]);// stores the SP frames coefficients, will be useful to encode SI or switching SP frame + } + } + } + + // inverse transform. + // horizontal + inverse4x4(mb_rres, mb_rres, 0, 0); + + // Decoded block moved to frame memory + for (j=0; j < BLOCK_SIZE; ++j) + { + for (i=0; i < BLOCK_SIZE; ++i) + { + mb_rres[j][i] = iClip1 (p_Vid->max_imgpel_value, rshift_rnd_sf(mb_rres[j][i], DQ_BITS)); + img_enc[currMB->pix_y+block_y+j][currMB->pix_x+block_x+i]=(imgpel) mb_rres[j][i]; + } + } + +} + +/*! + ************************************************************************ + * \brief Eric Setton + * Encoding of a secondary SP / SI frame. + * For an SI frame the predicted block should only come from spatial pred. + * The original image signal is the error coefficients of a primary SP in the raw data stream + * the difference with the primary SP are : + * - the prediction signal is transformed and quantized (qpsp) but not dequantized + * - only one kind of prediction is considered and not two + * - the resulting error coefficients are not quantized before being sent to the VLC + * + * \par Input: + * block_x,block_y: Block position inside a macro block (0,4,8,12). + * + * \par Output: + * nonzero: 0 if no levels are nonzero. 1 if there are nonzero levels. + * coeff_cost: Counter for nonzero coefficients, used to discard expensive levels. + * + * + ************************************************************************ + */ + +int residual_transform_quant_luma_4x4_sp2(Macroblock *currMB, ColorPlane pl, int block_x,int block_y,int *coeff_cost, int intra) +{ + int i,j,ilev,coeff_ctr; + int level,scan_pos = 0,run = -1; + int nonzero = FALSE; + + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currSlice->p_Vid; + QuantParameters *p_Quant = p_Vid->p_Quant; + imgpel **img_enc = p_Vid->enc_picture->p_curr_img; + imgpel **mb_pred = currSlice->mb_pred[pl]; +// int **mb_ores = currSlice->mb_ores[pl]; + int **mb_rres = currSlice->mb_rres[pl]; + int c_err,qp_const2; + + int pos_x = block_x >> BLOCK_SHIFT; + int pos_y = block_y >> BLOCK_SHIFT; + int b8 = 2*(pos_y >> 1) + (pos_x >> 1); + int b4 = 2*(pos_y & 0x01) + (pos_x & 0x01); + const byte *c_cost = COEFF_COST4x4[currSlice->disthres]; + const byte (*pos_scan)[2] = currMB->is_field_mode ? FIELD_SCAN : SNGL_SCAN; + + int level1; + + //int qp = (currMB->qpsp); // should double check spec why these are equal + int qp_sp = (currMB->qpsp); + + //int qp_per = p_Quant->qp_per_matrix[qp]; + //int q_bits = Q_BITS + qp_per; + int qp_per_sp = p_Quant->qp_per_matrix[qp_sp]; + int q_bits_sp = Q_BITS + qp_per_sp; + + //LevelQuantParams **q_params_4x4 = p_Quant->q_params_4x4[pl][intra][qp]; + LevelQuantParams **quant_params_sp = p_Quant->q_params_4x4[pl][intra][qp_sp]; + QuantMethods quant_methods; + + quant_methods.ACLevel = currSlice->cofAC[b8][b4][0]; + quant_methods.ACRun = currSlice->cofAC[b8][b4][1]; + + //qp_const=(1<>1; //sp_pred + + for (j=0; j< BLOCK_SIZE; ++j) + { + for (i=0; i< BLOCK_SIZE; ++i) + { + //Coefficients obtained from the prior encoding of the SP frame + mb_rres[j][i] = p_Vid->lrec[currMB->pix_y+block_y+j][currMB->pix_x+block_x+i]; + //Predicted block + currSlice->tblk16x16[j][i]=mb_pred[j+block_y][i+block_x]; + } + } + // forward transform + forward4x4(currSlice->tblk16x16, currSlice->tblk16x16, 0, 0); + + for (coeff_ctr=0;coeff_ctr < 16;coeff_ctr++) // 8 times if double scan, 16 normal scan + { + + i=pos_scan[coeff_ctr][0]; + j=pos_scan[coeff_ctr][1]; + + run++; + ilev=0; + + //quantization of the predicted block + level1 = (iabs (currSlice->tblk16x16[j][i]) * quant_params_sp[j][i].ScaleComp + qp_const2) >> q_bits_sp; + //substracted from p_Vid->lrec + c_err = mb_rres[j][i]-isignab(level1, currSlice->tblk16x16[j][i]); //substracting the predicted block + + level = iabs(c_err); + if (level != 0) + { + nonzero=TRUE; + + *coeff_cost += (level > 1) ? MAX_VALUE : c_cost[run]; + + quant_methods.ACLevel[scan_pos] = isignab(level,c_err); + quant_methods.ACRun [scan_pos] = run; + ++scan_pos; + run=-1; // reset zero level counter + } + //from now on we are in decoder land + ilev=c_err + isignab(level1,currSlice->tblk16x16[j][i]) ; // adding the quantized predicted block + mb_rres[j][i] = ilev * (quant_params_sp[j][i].InvScaleComp >> 4) << qp_per_sp; + } + quant_methods.ACLevel[scan_pos] = 0; + + // Inverse transform + inverse4x4(mb_rres, mb_rres, 0, 0); + + for (j=0; j < BLOCK_SIZE; ++j) + { + for (i=0; i < BLOCK_SIZE; ++i) + { + img_enc[currMB->pix_y+block_y+j][currMB->pix_x+block_x+i] = (imgpel) iClip3 (0, p_Vid->max_imgpel_value,rshift_rnd_sf(mb_rres[j][i], DQ_BITS)); + } + } + + return nonzero; +} + + +/*! + ************************************************************************ + * \brief Eric Setton + * Encoding of the chroma of a secondary SP / SI frame. + * For an SI frame the predicted block should only come from spatial pred. + * The original image signal is the error coefficients of a primary SP in the raw data stream + * the difference with the primary SP are : + * - the prediction signal is transformed and quantized (qpsp) but not dequantized + * - the resulting error coefficients are not quantized before being sent to the VLC + * + * \par Input: + * uv : Make difference between the U and V chroma component + * cr_cbp: chroma coded block pattern + * + * \par Output: + * cr_cbp: Updated chroma coded block pattern. + * + ************************************************************************ + */ +int residual_transform_quant_chroma_4x4_sp2(Macroblock *currMB, int uv,int cr_cbp) +{ + int i,j,ilev,n2,n1,coeff_ctr,c_err,level ,scan_pos = 0,run = -1; + int m1[BLOCK_SIZE]; + int coeff_cost; + int cr_cbp_tmp; + int mp1[BLOCK_SIZE]; + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currSlice->p_Vid; + QuantParameters *p_Quant = p_Vid->p_Quant; + QuantMethods quant_methods; + const byte *c_cost = COEFF_COST4x4[currSlice->disthres]; + const byte (*pos_scan)[2] = currMB->is_field_mode ? FIELD_SCAN : SNGL_SCAN; + + int b4; + int* DCLevel = currSlice->cofDC[uv+1][0]; + int* DCRun = currSlice->cofDC[uv+1][1]; + int level1; + int **mb_rres = currSlice->mb_rres[uv + 1]; + //int **mb_ores = currSlice->mb_ores[uv + 1]; + imgpel **mb_pred = currSlice->mb_pred[uv + 1]; + int intra = IS_INTRA (currMB); + + int qpChroma = currMB->qpc[uv] + currSlice->bitdepth_chroma_qp_scale; + + int qpc_sp = iClip3(-currSlice->bitdepth_chroma_qp_scale, 51, currMB->qpsp + p_Vid->active_pps->chroma_qp_index_offset); + + int qpChromaSP = qpc_sp < 0 ? qpc_sp : QP_SCALE_CR[qpc_sp]; + + int qp_per = p_Quant->qp_per_matrix[qpChroma]; + //int qp_rem = p_Quant->qp_rem_matrix[qpChroma]; + + int qp_per_sp = p_Quant->qp_per_matrix[qpChromaSP]; + int q_bits_sp = Q_BITS + qp_per; + int qp_const2 = (1 << q_bits_sp)>>1; //sp_pred + + //LevelQuantParams **q_params_4x4 = p_Quant->q_params_4x4[uv + 1][intra][qpChroma]; + LevelQuantParams **quant_params_sp = p_Quant->q_params_4x4[uv + 1][intra][qpChromaSP]; + + for (j=0; j < MB_BLOCK_SIZE>>1; ++j) + { + for (i=0; i < MB_BLOCK_SIZE>>1; ++i) + { + currSlice->tblk16x16[j][i]=mb_pred[j][i]; + mb_rres[j][i]=p_Vid->lrec_uv[uv][currMB->pix_c_y + j][currMB->pix_c_x + i]; + } + } + + + for (n2=0; n2 <= BLOCK_SIZE; n2 += BLOCK_SIZE) + { + for (n1=0; n1 <= BLOCK_SIZE; n1 += BLOCK_SIZE) + { + forward4x4(currSlice->tblk16x16, currSlice->tblk16x16, n2, n1); + } + } + + // DC coefficients already transformed and quantized + m1[0]= mb_rres[0][0]; + m1[1]= mb_rres[0][4]; + m1[2]= mb_rres[4][0]; + m1[3]= mb_rres[4][4]; + + // 2X2 transform of predicted DC coeffs. + hadamard2x2(currSlice->tblk16x16, mp1); + + for (coeff_ctr=0; coeff_ctr < 4; coeff_ctr++) + { + run++; + ilev=0; + + //quantization of predicted DC coeff + level1 = (iabs (mp1[coeff_ctr]) * quant_params_sp[0][0].ScaleComp + 2 * qp_const2) >> (q_bits_sp + 1); + //substratcted from lrecUV + c_err = m1[coeff_ctr] - isignab(level1, mp1[coeff_ctr]); + level = iabs(c_err); + + if (level != 0) + { + currMB->cbp_blk |= 0xf0000 << (uv << 2) ; // if one of the 2x2-DC levels is != 0 the coded-bit + cr_cbp=imax(1,cr_cbp); + DCLevel[scan_pos] = isignab(level ,c_err); + DCRun [scan_pos] = run; + scan_pos++; + run=-1; + } + + //from now on decoder world + ilev = c_err + isignab(level1,mp1[coeff_ctr]) ; // we have perfect reconstruction here + + m1[coeff_ctr]= ilev * (quant_params_sp[0][0].InvScaleComp >> 4) << qp_per_sp; + + } + DCLevel[scan_pos] = 0; + + // Inverse transform of 2x2 DC levels + ihadamard2x2(m1, m1); + + mb_rres[0][0]=m1[0]>>1; + mb_rres[0][4]=m1[1]>>1; + mb_rres[4][0]=m1[2]>>1; + mb_rres[4][4]=m1[3]>>1; + + // Quant of chroma AC-coeffs. + coeff_cost=0; + cr_cbp_tmp=0; + + for (n2=0; n2 <= BLOCK_SIZE; n2 += BLOCK_SIZE) + { + for (n1=0; n1 <= BLOCK_SIZE; n1 += BLOCK_SIZE) + { + b4 = 2*(n2 >> 2) + (n1 >> 2); + quant_methods.ACLevel = currSlice->cofAC[uv+4][b4][0]; + quant_methods.ACRun = currSlice->cofAC[uv+4][b4][1]; + + run = -1; + scan_pos = 0; + + for (coeff_ctr=1; coeff_ctr < 16; coeff_ctr++)// start change rd_quant + { + + i=pos_scan[coeff_ctr][0]; + j=pos_scan[coeff_ctr][1]; + + ++run; + ilev=0; + // quantization on prediction + level1 = (iabs(currSlice->tblk16x16[n2+j][n1+i]) * quant_params_sp[j][i].ScaleComp + qp_const2) >> q_bits_sp; + //substracted from p_Vid->lrec + c_err = mb_rres[n2+j][n1+i] - isignab(level1, currSlice->tblk16x16[n2+j][n1+i]); + level = iabs(c_err) ; + + if (level != 0) + { + currMB->cbp_blk |= (int64)1 << (16 + (uv << 2) + ((n2 >> 1) + (n1 >> 2))) ; + + coeff_cost += (level > 1) ? MAX_VALUE : c_cost[run]; + + cr_cbp_tmp=2; + quant_methods.ACLevel[scan_pos] = isignab(level,c_err); + quant_methods.ACRun [scan_pos] = run; + ++scan_pos; + run=-1; + } + + //from now on decoder land + ilev=c_err + isignab(level1,currSlice->tblk16x16[n2+j][n1+i]); + mb_rres[n2+j][n1+i] = ilev * (quant_params_sp[j][i].InvScaleComp >> 4) << qp_per_sp; + } + quant_methods.ACLevel[scan_pos] = 0; + } + } + // * reset chroma coeffs + + if(cr_cbp_tmp==2) + cr_cbp=2; + + // inverse transform. + // Horizontal. + for (n2=0; n2 <= BLOCK_SIZE; n2 += BLOCK_SIZE) + { + for (n1=0; n1 <= BLOCK_SIZE; n1 += BLOCK_SIZE) + { + inverse4x4(mb_rres, mb_rres, n2, n1); + + // Vertical. + for (j=0; j < BLOCK_SIZE; ++j) + { + for (i=0; i < BLOCK_SIZE; ++i) + { + p_Vid->enc_picture->imgUV[uv][currMB->pix_c_y + j + n2][currMB->pix_c_x + i + n1 ] = + (imgpel) iClip3 (0, p_Vid->max_imgpel_value,rshift_rnd_sf(mb_rres[n2+j][n1+i], DQ_BITS)); + } + } + } + } + + return cr_cbp; +} + + +void select_transform(Macroblock *currMB) +{ + Slice *currSlice = currMB->p_Slice; + + if (currSlice->slice_type != SP_SLICE && currSlice->slice_type != SI_SLICE) + { + VideoParameters *p_Vid = currMB->p_Vid; + if (p_Vid->active_sps->lossless_qpprime_flag == 1) + { + if (currMB->qp_scaled[(short) p_Vid->colour_plane_id] == 0) + { + currMB->residual_transform_quant_luma_4x4 = residual_transform_quant_luma_4x4_ls; + currMB->residual_transform_quant_luma_16x16 = residual_transform_quant_luma_16x16_ls; + currMB->residual_transform_quant_luma_8x8 = residual_transform_quant_luma_8x8_ls; + } + else + { + currMB->residual_transform_quant_luma_4x4 = residual_transform_quant_luma_4x4; + currMB->residual_transform_quant_luma_16x16 = residual_transform_quant_luma_16x16; + if (currSlice->symbol_mode == CAVLC) + currMB->residual_transform_quant_luma_8x8 = residual_transform_quant_luma_8x8_cavlc; + else + currMB->residual_transform_quant_luma_8x8 = residual_transform_quant_luma_8x8; + currMB->residual_transform_quant_chroma_4x4[0] = residual_transform_quant_chroma_4x4; + currMB->residual_transform_quant_chroma_4x4[1] = residual_transform_quant_chroma_4x4; + } + + if (currMB->qp_scaled[PLANE_U] == 0) + { + currMB->residual_transform_quant_chroma_4x4[0] = residual_transform_quant_chroma_4x4_ls; + } + if (currMB->qp_scaled[PLANE_V] == 0) + { + currMB->residual_transform_quant_chroma_4x4[1] = residual_transform_quant_chroma_4x4_ls; + } + } + else + { + currMB->residual_transform_quant_luma_4x4 = residual_transform_quant_luma_4x4; + currMB->residual_transform_quant_luma_16x16 = residual_transform_quant_luma_16x16; + if (currSlice->symbol_mode == CAVLC) + currMB->residual_transform_quant_luma_8x8 = residual_transform_quant_luma_8x8_cavlc; + else + currMB->residual_transform_quant_luma_8x8 = residual_transform_quant_luma_8x8; + + currMB->residual_transform_quant_chroma_4x4[0] = residual_transform_quant_chroma_4x4; + currMB->residual_transform_quant_chroma_4x4[1] = residual_transform_quant_chroma_4x4; + } + } + else if(currSlice->slice_type != SI_SLICE && !currSlice->sp2_frame_indicator) + { + currMB->residual_transform_quant_luma_4x4 = residual_transform_quant_luma_4x4_sp; + currMB->residual_transform_quant_luma_16x16 = residual_transform_quant_luma_16x16; + if (currSlice->symbol_mode == CAVLC) + currMB->residual_transform_quant_luma_8x8 = residual_transform_quant_luma_8x8_cavlc; + else + currMB->residual_transform_quant_luma_8x8 = residual_transform_quant_luma_8x8; + + currMB->residual_transform_quant_chroma_4x4[0] = residual_transform_quant_chroma_4x4_sp; + currMB->residual_transform_quant_chroma_4x4[1] = residual_transform_quant_chroma_4x4_sp; + } + else + { + currMB->residual_transform_quant_luma_4x4 = residual_transform_quant_luma_4x4_sp2; + currMB->residual_transform_quant_luma_16x16 = residual_transform_quant_luma_16x16; + if (currSlice->symbol_mode == CAVLC) + currMB->residual_transform_quant_luma_8x8 = residual_transform_quant_luma_8x8_cavlc; + else + currMB->residual_transform_quant_luma_8x8 = residual_transform_quant_luma_8x8; + + currMB->residual_transform_quant_chroma_4x4[0] = residual_transform_quant_chroma_4x4_sp2; + currMB->residual_transform_quant_chroma_4x4[1] = residual_transform_quant_chroma_4x4_sp2; + } +} diff --git a/lencod/src/cabac.c b/lencod/src/cabac.c new file mode 100644 index 0000000..9aa78d9 --- /dev/null +++ b/lencod/src/cabac.c @@ -0,0 +1,1879 @@ +/*! + ************************************************************************************* + * \file cabac.c + * + * \brief + * CABAC entropy coding routines + * + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * - Detlev Marpe + ************************************************************************************** + */ + +#include "global.h" + +#include "cabac.h" +#include "biariencode.h" +#include "image.h" +#include "mb_access.h" + +#if TRACE + #define CABAC_TRACE if (dp->bitstream->trace_enabled) trace2out_cabac (se) +#else + #define CABAC_TRACE +#endif + +/*********************************************************************** + Constant declarations +*********************************************************************** +*/ + + + +//===== position -> ctx for MAP ===== +//--- zig-zag scan ---- +static const byte pos2ctx_map8x8 [] = { + 0, 1, 2, 3, 4, 5, 5, 4, 4, 3, 3, 4, 4, 4, 5, 5, + 4, 4, 4, 4, 3, 3, 6, 7, 7, 7, 8, 9, 10, 9, 8, 7, + 7, 6, 11, 12, 13, 11, 6, 7, 8, 9, 14, 10, 9, 8, 6, 11, + 12, 13, 11, 6, 9, 14, 10, 9, 11, 12, 13, 11 ,14, 10, 12, 14 +}; // 15 CTX + +static const byte pos2ctx_map8x4 [] = { + 0, 1, 2, 3, 4, 5, 7, 8, 9, 10, 11, 9, 8, 6, 7, 8, + 9, 10, 11, 9, 8, 6, 12, 8, 9, 10, 11, 9, 13, 13, 14, 14 +}; // 15 CTX + +static const byte pos2ctx_map4x4 [] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 14 +}; // 15 CTX + +static const byte pos2ctx_map2x4c[] = { + 0, 0, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 +}; // 15 CTX + +static const byte pos2ctx_map4x4c[] = { + 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2 +}; // 15 CTX + +const byte* pos2ctx_map [] = { + pos2ctx_map4x4, pos2ctx_map4x4, pos2ctx_map8x8, pos2ctx_map8x4, + pos2ctx_map8x4, pos2ctx_map4x4, pos2ctx_map4x4, pos2ctx_map4x4, + pos2ctx_map2x4c, pos2ctx_map4x4c, + pos2ctx_map4x4, pos2ctx_map4x4, pos2ctx_map8x8,pos2ctx_map8x4, + pos2ctx_map8x4, pos2ctx_map4x4, //Cb component + pos2ctx_map4x4, pos2ctx_map4x4, pos2ctx_map8x8,pos2ctx_map8x4, + pos2ctx_map8x4,pos2ctx_map4x4 //Cr component +}; + +//--- interlace scan ---- +//Taken from ABT +static const byte pos2ctx_map8x8i[] = { + 0, 1, 1, 2, 2, 3, 3, 4, 5, 6, 7, 7, 7, 8, 4, 5, + 6, 9, 10, 10, 8, 11, 12, 11, 9, 9, 10, 10, 8, 11, 12, 11, + 9, 9, 10, 10, 8, 11, 12, 11, 9, 9, 10, 10, 8, 13, 13, 9, + 9, 10, 10, 8, 13, 13, 9, 9, 10, 10, 14, 14, 14, 14, 14, 14 +}; // 15 CTX + +static const byte pos2ctx_map8x4i[] = { + 0, 1, 2, 3, 4, 5, 6, 3, 4, 5, 6, 3, 4, 7, 6, 8, + 9, 7, 6, 8, 9, 10, 11, 12, 12, 10, 11, 13, 13, 14, 14, 14 +}; // 15 CTX + +static const byte pos2ctx_map4x8i[] = { + 0, 1, 1, 1, 2, 3, 3, 4, 4, 4, 5, 6, 2, 7, 7, 8, + 8, 8, 5, 6, 9, 10, 10, 11, 11, 11, 12, 13, 13, 14, 14, 14 +}; // 15 CTX + +const byte* pos2ctx_map_int[] = { + pos2ctx_map4x4, pos2ctx_map4x4, pos2ctx_map8x8i,pos2ctx_map8x4i, + pos2ctx_map4x8i,pos2ctx_map4x4, pos2ctx_map4x4, pos2ctx_map4x4, + pos2ctx_map2x4c, pos2ctx_map4x4c, + //444_TEMP_NOTE: the followings are addded for the 4:4:4 common mode}; + pos2ctx_map4x4, pos2ctx_map4x4, pos2ctx_map8x8i,pos2ctx_map8x4i, + pos2ctx_map8x4i,pos2ctx_map4x4, //Cb component + pos2ctx_map4x4, pos2ctx_map4x4, pos2ctx_map8x8i,pos2ctx_map8x4i, + pos2ctx_map8x4i,pos2ctx_map4x4 //Cr component} +}; + + +//===== position -> ctx for LAST ===== +static const byte pos2ctx_last8x8 [] = { + 0, 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, + 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, + 5, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8 +}; // 9 CTX + +static const byte pos2ctx_last8x4 [] = { + 0, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, + 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8 +}; // 9 CTX + +static const byte pos2ctx_last4x4 [] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 +}; // 15 CTX + +static const byte pos2ctx_last2x4c[] = { + 0, 0, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 +}; // 15 CTX + +static const byte pos2ctx_last4x4c[] = { + 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2 +}; // 15 CTX + +const byte* pos2ctx_last [] = { + pos2ctx_last4x4, pos2ctx_last4x4, pos2ctx_last8x8, pos2ctx_last8x4, + pos2ctx_last8x4, pos2ctx_last4x4, pos2ctx_last4x4, pos2ctx_last4x4, + pos2ctx_last2x4c, pos2ctx_last4x4c, + pos2ctx_last4x4, pos2ctx_last4x4, pos2ctx_last8x8,pos2ctx_last8x4, + pos2ctx_last8x4, pos2ctx_last4x4, //Cb component + pos2ctx_last4x4, pos2ctx_last4x4, pos2ctx_last8x8,pos2ctx_last8x4, + pos2ctx_last8x4, pos2ctx_last4x4 //Cr component +}; + +/*********************************************************************** + * L O C A L L Y D E F I N E D F U N C T I O N P R O T O T Y P E S + *********************************************************************** + */ + + +/*! + ************************************************************************ + * \brief + * Exp Golomb binarization and encoding + ************************************************************************ + */ +static void exp_golomb_encode_eq_prob( EncodingEnvironmentPtr eep_dp, + unsigned int symbol, + int k) +{ + for(;;) + { + if (symbol >= (unsigned int)(1<>k)&1)); + break; + } + } +} + +/*! + ************************************************************************ + * \brief + * Unary binarization and encoding of a symbol by using + * one or two distinct models for the first two and all + * remaining bins + * + ************************************************************************/ +static void unary_bin_encode(EncodingEnvironmentPtr eep_dp, + unsigned int symbol, + BiContextTypePtr ctx, + int ctx_offset) +{ + if (symbol==0) + { + biari_encode_symbol(eep_dp, 0, ctx ); + return; + } + else + { + biari_encode_symbol(eep_dp, 1, ctx ); + ctx += ctx_offset; + while ((--symbol) > 0) + biari_encode_symbol(eep_dp, 1, ctx); + biari_encode_symbol(eep_dp, 0, ctx); + } +} + +/*! + ************************************************************************ + * \brief + * Unary binarization and encoding of a symbol by using + * one or two distinct models for the first two and all + * remaining bins; no terminating "0" for max_symbol + * (finite symbol alphabet) + ************************************************************************ + */ +static void unary_bin_max_encode(EncodingEnvironmentPtr eep_dp, + unsigned int symbol, + BiContextTypePtr ctx, + int ctx_offset, + unsigned int max_symbol) +{ + if (symbol==0) + { + biari_encode_symbol(eep_dp, 0, ctx ); + return; + } + else + { + unsigned int l = symbol; + biari_encode_symbol(eep_dp, 1, ctx ); + + ctx += ctx_offset; + while ((--l)>0) + biari_encode_symbol(eep_dp, 1, ctx); + if (symbol < max_symbol) + biari_encode_symbol(eep_dp, 0, ctx); + } +} + +/*! + ************************************************************************ + * \brief + * Exp-Golomb for Level Encoding +* +************************************************************************/ +static void unary_exp_golomb_level_encode( EncodingEnvironmentPtr eep_dp, + unsigned int symbol, + BiContextTypePtr ctx) +{ + if (symbol==0) + { + biari_encode_symbol(eep_dp, 0, ctx ); + return; + } + else + { + unsigned int l=symbol; + unsigned int k = 1; + + biari_encode_symbol(eep_dp, 1, ctx ); + while (((--l)>0) && (++k <= 13)) + biari_encode_symbol(eep_dp, 1, ctx); + if (symbol < 13) + biari_encode_symbol(eep_dp, 0, ctx); + else + exp_golomb_encode_eq_prob(eep_dp,symbol - 13, 0); + } +} + + +/*! + ************************************************************************ + * \brief + * Exp-Golomb for MV Encoding +* +************************************************************************/ +static void unary_exp_golomb_mv_encode(EncodingEnvironmentPtr eep_dp, + unsigned int symbol, + BiContextTypePtr ctx, + unsigned int max_bin) +{ + if (symbol==0) + { + biari_encode_symbol(eep_dp, 0, ctx ); + return; + } + else + { + unsigned int bin = 1; + unsigned int l = symbol, k = 1; + biari_encode_symbol(eep_dp, 1, ctx++ ); + + while (((--l)>0) && (++k <= 8)) + { + biari_encode_symbol(eep_dp, 1, ctx ); + if ((++bin) == 2) + ++ctx; + if (bin == max_bin) + ++ctx; + } + if (symbol < 8) + biari_encode_symbol(eep_dp, 0, ctx); + else + exp_golomb_encode_eq_prob(eep_dp, symbol - 8, 3); + } +} + +/*! + ************************************************************************ + * \brief + * Check for available neighbouring blocks + * and set pointers in current macroblock + ************************************************************************ + */ +void CheckAvailabilityOfNeighborsCABAC(Macroblock *currMB) +{ + VideoParameters *p_Vid = currMB->p_Vid; + PixelPos up, left; + int *mb_size = p_Vid->mb_size[IS_LUMA]; + + p_Vid->getNeighbour(currMB, -1, 0, mb_size, &left); + p_Vid->getNeighbour(currMB, 0, -1, mb_size, &up); + + if (up.available) + currMB->mb_up = &p_Vid->mb_data[up.mb_addr]; + else + currMB->mb_up = NULL; + + if (left.available) + currMB->mb_left = &p_Vid->mb_data[left.mb_addr]; + else + currMB->mb_left = NULL; +} + +/*! + ************************************************************************ + * \brief + * Allocation of contexts models for the motion info + * used for arithmetic encoding + ************************************************************************ + */ +MotionInfoContexts* create_contexts_MotionInfo(void) +{ + MotionInfoContexts* enco_ctx; + + enco_ctx = (MotionInfoContexts*) calloc(1, sizeof(MotionInfoContexts) ); + if( enco_ctx == NULL ) + no_mem_exit("create_contexts_MotionInfo: enco_ctx"); + + return enco_ctx; +} + + +/*! + ************************************************************************ + * \brief + * Allocates of contexts models for the texture info + * used for arithmetic encoding + ************************************************************************ + */ +TextureInfoContexts* create_contexts_TextureInfo(void) +{ + TextureInfoContexts* enco_ctx = (TextureInfoContexts*) calloc(1, sizeof(TextureInfoContexts) ); + if( enco_ctx == NULL ) + no_mem_exit("create_contexts_TextureInfo: enco_ctx"); + + return enco_ctx; +} + +/*! + ************************************************************************ + * \brief + * Frees the memory of the contexts models + * used for arithmetic encoding of the motion info. + ************************************************************************ + */ +void delete_contexts_MotionInfo(MotionInfoContexts *enco_ctx) +{ + free_pointer( enco_ctx ); +} + +/*! + ************************************************************************ + * \brief + * Frees the memory of the contexts models + * used for arithmetic encoding of the texture info. + ************************************************************************ + */ +void delete_contexts_TextureInfo(TextureInfoContexts *enco_ctx) +{ + free_pointer( enco_ctx ); +} + + +/*! + *************************************************************************** + * \brief + * This function is used to arithmetically encode the field + * mode info of a given MB in the case of mb-based frame/field decision + *************************************************************************** + */ +void writeFieldModeInfo_CABAC(Macroblock *currMB, SyntaxElement *se, DataPartition *dp) +{ + EncodingEnvironmentPtr eep_dp = &(dp->ee_cabac); + int curr_len = arienco_bits_written(eep_dp); + VideoParameters *p_Vid = currMB->p_Vid; + Macroblock *mb_data = p_Vid->mb_data; + + int a = currMB->mbAvailA ? mb_data[currMB->mbAddrA].mb_field : 0; + int b = currMB->mbAvailB ? mb_data[currMB->mbAddrB].mb_field : 0; + + int act_ctx = a + b; + +#if ENABLE_FIELD_CTX + TextureInfoContexts *ctx = dp->p_Slice->tex_ctx; + + biari_encode_symbol(eep_dp, (se->value1 != 0), &ctx->mb_aff_contexts[act_ctx]); +#endif + + se->context = act_ctx; + + dp->bitstream->write_flag = 1; + se->len = (arienco_bits_written(eep_dp) - curr_len); + CABAC_TRACE; +} + +/*! +*************************************************************************** +* \brief +* This function is used to arithmetically encode the mb_skip_flag. +*************************************************************************** +*/ +void writeMB_Pskip_flagInfo_CABAC(Macroblock *currMB, SyntaxElement *se, DataPartition *dp) +{ + Slice *currSlice = currMB->p_Slice; + EncodingEnvironmentPtr eep_dp = &(dp->ee_cabac); + int curr_len = arienco_bits_written(eep_dp); + MotionInfoContexts *ctx = currSlice->mot_ctx; + int curr_mb_type = se->value1; + int a = (currMB->mb_left != NULL && (currMB->mb_left->skip_flag == 0)) ? 1 : 0; + int b = (currMB->mb_up != NULL && (currMB->mb_up ->skip_flag == 0)) ? 1 : 0; + int act_ctx = a + b; + + if (curr_mb_type==0) // SKIP + biari_encode_symbol(eep_dp, 1, &ctx->mb_type_contexts[1][act_ctx]); + else + biari_encode_symbol(eep_dp, 0, &ctx->mb_type_contexts[1][act_ctx]); + + currMB->skip_flag = (curr_mb_type==0) ? 1 : 0; + + se->context = act_ctx; + se->value1 = 1 - currMB->skip_flag; + + dp->bitstream->write_flag = 1; + se->len = (arienco_bits_written(eep_dp) - curr_len); + CABAC_TRACE; +} + +/*! +*************************************************************************** +* \brief +* This function is used to arithmetically encode the mb_skip_flag for B slices. +*************************************************************************** +*/ +void writeMB_Bskip_flagInfo_CABAC(Macroblock *currMB, SyntaxElement *se, DataPartition *dp) +{ + Slice *currSlice = currMB->p_Slice; + EncodingEnvironmentPtr eep_dp = &(dp->ee_cabac); + int curr_len = arienco_bits_written(eep_dp); + MotionInfoContexts *ctx = currSlice->mot_ctx; + int a = (currMB->mb_left != NULL && (currMB->mb_left->skip_flag == 0)) ? 1 : 0; + int b = (currMB->mb_up != NULL && (currMB->mb_up ->skip_flag == 0)) ? 1 : 0; + int act_ctx = a + b; + + act_ctx += 7; + + if (se->value1==0 && se->value2==0) // DIRECT mode, no coefficients + biari_encode_symbol (eep_dp, 1, &ctx->mb_type_contexts[2][act_ctx]); + else + biari_encode_symbol (eep_dp, 0, &ctx->mb_type_contexts[2][act_ctx]); + + currMB->skip_flag = (se->value1==0 && se->value2==0) ? 1 : 0; + + se->context = act_ctx; + se->value1 = 1 - currMB->skip_flag; + + dp->bitstream->write_flag = 1; + se->len = (arienco_bits_written(eep_dp) - curr_len); + CABAC_TRACE; +} + +/*! +*************************************************************************** +* \brief +* This function is used to arithmetically encode the macroblock +* intra_pred_size flag info of a given MB. +*************************************************************************** +*/ + +void writeMB_transform_size_CABAC(Macroblock *currMB, SyntaxElement *se, DataPartition *dp) +{ + EncodingEnvironmentPtr eep_dp = &(dp->ee_cabac); + TextureInfoContexts *ctx = (dp->p_Slice)->tex_ctx; + + int curr_len = arienco_bits_written(eep_dp); + int act_sym = currMB->luma_transform_size_8x8_flag; + + + int b = (currMB->mb_up == NULL) ? 0 : currMB->mb_up->luma_transform_size_8x8_flag; + int a = (currMB->mb_left == NULL) ? 0 :currMB->mb_left->luma_transform_size_8x8_flag; + int act_ctx = a + b; + + se->context = act_ctx; // store context + biari_encode_symbol(eep_dp, (act_sym != 0), ctx->transform_size_contexts + act_ctx ); + + dp->bitstream->write_flag = 1; + se->len = (arienco_bits_written(eep_dp) - curr_len); + CABAC_TRACE; +} + +/*! + *************************************************************************** + * \brief + * This function is used to arithmetically encode the macroblock + * type info of a given MB. + *************************************************************************** + */ + +void writeMB_P_typeInfo_CABAC(Macroblock *currMB, SyntaxElement *se, DataPartition *dp) +{ + EncodingEnvironmentPtr eep_dp = &(dp->ee_cabac); + int curr_len = arienco_bits_written(eep_dp); + + BiContextType *mb_type_contexts = dp->p_Slice->mot_ctx->mb_type_contexts[1]; + + int act_ctx = 0; + int mode_sym = 0; + int act_sym = se->value1; + int mode16x16 = 7; + + if (act_sym >= mode16x16) + { + mode_sym = act_sym - mode16x16; + act_sym = mode16x16; // 16x16 mode info + } + + switch (act_sym) + { + case 0: + break; + case 1: + biari_encode_symbol (eep_dp, 0, &mb_type_contexts[4]); + biari_encode_symbol (eep_dp, 0, &mb_type_contexts[5]); + biari_encode_symbol (eep_dp, 0, &mb_type_contexts[6]); + break; + case 2: + biari_encode_symbol (eep_dp, 0, &mb_type_contexts[4]); + biari_encode_symbol (eep_dp, 1, &mb_type_contexts[5]); + biari_encode_symbol (eep_dp, 1, &mb_type_contexts[7]); + break; + case 3: + biari_encode_symbol (eep_dp, 0, &mb_type_contexts[4]); + biari_encode_symbol (eep_dp, 1, &mb_type_contexts[5]); + biari_encode_symbol (eep_dp, 0, &mb_type_contexts[7]); + break; + case 4: + case 5: + biari_encode_symbol (eep_dp, 0, &mb_type_contexts[4]); + biari_encode_symbol (eep_dp, 0, &mb_type_contexts[5]); + biari_encode_symbol (eep_dp, 1, &mb_type_contexts[6]); + break; + case 6: + biari_encode_symbol (eep_dp, 1, &mb_type_contexts[4]); + biari_encode_symbol (eep_dp, 0, &mb_type_contexts[7]); + break; + case 7: + biari_encode_symbol (eep_dp, 1, &mb_type_contexts[4]); + biari_encode_symbol (eep_dp, 1, &mb_type_contexts[7]); + break; + default: + printf ("Unsupported MB-MODE in writeMB_I_typeInfo_CABAC!\n"); + exit (1); + } + + if(act_sym == mode16x16) // additional info for 16x16 Intra-mode + { + if( mode_sym == 24 ) + { + biari_encode_symbol_final(eep_dp, 1 ); + dp->bitstream->write_flag = 1; + se->len = (arienco_bits_written(eep_dp) - curr_len); + CABAC_TRACE; + return; + } + biari_encode_symbol_final(eep_dp, 0 ); + + act_ctx = 8; + act_sym = mode_sym/12; + biari_encode_symbol(eep_dp, act_sym, mb_type_contexts + act_ctx ); // coding of AC/no AC + mode_sym = mode_sym % 12; + + act_sym = mode_sym >> 2; // coding of cbp: 0,1,2 + act_ctx = 9; + if (act_sym==0) + { + biari_encode_symbol(eep_dp, 0, mb_type_contexts + act_ctx ); + } + else + { + biari_encode_symbol(eep_dp, 1, mb_type_contexts + act_ctx ); + biari_encode_symbol(eep_dp, (act_sym!=1), mb_type_contexts + act_ctx ); + } + + mode_sym = mode_sym & 0x03; // coding of I pred-mode: 0,1,2,3 + act_ctx = 10; + act_sym = mode_sym >> 1; + biari_encode_symbol(eep_dp, act_sym, mb_type_contexts + act_ctx ); + act_sym = (mode_sym & 0x01); + biari_encode_symbol(eep_dp, act_sym, mb_type_contexts + act_ctx ); + } + + dp->bitstream->write_flag = 1; + se->len = (arienco_bits_written(eep_dp) - curr_len); + CABAC_TRACE; +} + +/*! + *************************************************************************** + * \brief + * This function is used to arithmetically encode the macroblock + * type info of a given MB in a B slice. + *************************************************************************** + */ + +void writeMB_B_typeInfo_CABAC(Macroblock *currMB, SyntaxElement *se, DataPartition *dp) +{ + EncodingEnvironmentPtr eep_dp = &(dp->ee_cabac); + int curr_len = arienco_bits_written(eep_dp); + + int csym; + int act_sym = se->value1; + + int mode_sym = 0; + int mode16x16 = 24; + + MotionInfoContexts *ctx = dp->p_Slice->mot_ctx; + BiContextType *mb_type_contexts = ctx->mb_type_contexts[2]; + + int a = (currMB->mb_left == NULL) ? 0 : ((currMB->mb_left->mb_type != 0) ? 1 : 0 ); + int b = (currMB->mb_up == NULL) ? 0 : ((currMB->mb_up->mb_type != 0) ? 1 : 0 ); + int act_ctx = a + b; + se->context = act_ctx; // store context + + if (act_sym >= mode16x16) + { + mode_sym = act_sym - mode16x16; + act_sym = mode16x16; // 16x16 mode info + } + + if (act_sym==0) + { + biari_encode_symbol (eep_dp, 0, &mb_type_contexts[act_ctx]); + } + else if (act_sym<=2) + { + biari_encode_symbol (eep_dp, 1, &mb_type_contexts[act_ctx]); + biari_encode_symbol (eep_dp, 0, &mb_type_contexts[4]); + csym = (act_sym-1 != 0); + biari_encode_symbol (eep_dp, csym, &mb_type_contexts[6]); + } + else if (act_sym<=10) + { + int temp_sym = act_sym - 3; + biari_encode_symbol (eep_dp, 1, &mb_type_contexts[act_ctx]); + biari_encode_symbol (eep_dp, 1, &mb_type_contexts[4]); + biari_encode_symbol (eep_dp, 0, &mb_type_contexts[5]); + csym = ((temp_sym >> 2)&0x01) != 0; + biari_encode_symbol (eep_dp, csym, &mb_type_contexts[6]); + csym = ((temp_sym >> 1)&0x01) != 0; + biari_encode_symbol (eep_dp, csym, &mb_type_contexts[6]); + csym = (temp_sym & 0x01) != 0; + biari_encode_symbol (eep_dp, csym, &mb_type_contexts[6]); + } + else if (act_sym==11 || act_sym==22) + { + biari_encode_symbol (eep_dp, 1, &mb_type_contexts[act_ctx]); + biari_encode_symbol (eep_dp, 1, &mb_type_contexts[4]); + biari_encode_symbol (eep_dp, 1, &mb_type_contexts[5]); + biari_encode_symbol (eep_dp, 1, &mb_type_contexts[6]); + biari_encode_symbol (eep_dp, 1, &mb_type_contexts[6]); + csym = (act_sym != 11); + biari_encode_symbol (eep_dp, csym, &mb_type_contexts[6]); + } + else + { + int temp_sym = (act_sym > 22) ? act_sym - 13 : act_sym - 12; + biari_encode_symbol (eep_dp, 1, &mb_type_contexts[act_ctx]); + biari_encode_symbol (eep_dp, 1, &mb_type_contexts[4]); + biari_encode_symbol (eep_dp, 1, &mb_type_contexts[5]); + csym = ((temp_sym >> 3)&0x01) != 0; + biari_encode_symbol (eep_dp, csym, &mb_type_contexts[6]); + csym = ((temp_sym >> 2)&0x01) != 0; + biari_encode_symbol (eep_dp, csym, &mb_type_contexts[6]); + csym = ((temp_sym >> 1)&0x01) != 0; + biari_encode_symbol (eep_dp, csym, &mb_type_contexts[6]); + csym = (temp_sym & 0x01) != 0; + biari_encode_symbol (eep_dp, csym, &mb_type_contexts[6]); + } + + + if(act_sym == mode16x16) // additional info for 16x16 Intra-mode + { + mb_type_contexts = ctx->mb_type_contexts[1]; + if( mode_sym == 24 ) + { + biari_encode_symbol_final(eep_dp, 1 ); + dp->bitstream->write_flag = 1; + se->len = (arienco_bits_written(eep_dp) - curr_len); + CABAC_TRACE; + return; + } + biari_encode_symbol_final(eep_dp, 0 ); + + act_ctx = 8; + act_sym = mode_sym/12; + biari_encode_symbol(eep_dp, act_sym, mb_type_contexts + act_ctx ); // coding of AC/no AC + mode_sym = mode_sym % 12; + + act_sym = mode_sym >> 2; // coding of cbp: 0,1,2 + act_ctx = 9; + if (act_sym==0) + { + biari_encode_symbol(eep_dp, 0, mb_type_contexts + act_ctx ); + } + else + { + biari_encode_symbol(eep_dp, 1, mb_type_contexts + act_ctx ); + biari_encode_symbol(eep_dp, (act_sym!=1), mb_type_contexts + act_ctx ); + } + + mode_sym = mode_sym & 0x03; // coding of I pred-mode: 0,1,2,3 + act_ctx = 10; + act_sym = mode_sym >> 1; + biari_encode_symbol(eep_dp, act_sym, mb_type_contexts + act_ctx ); + act_sym = (mode_sym & 0x01); + biari_encode_symbol(eep_dp, act_sym, mb_type_contexts + act_ctx ); + } + + dp->bitstream->write_flag = 1; + se->len = (arienco_bits_written(eep_dp) - curr_len); + CABAC_TRACE; +} + +/*! + *************************************************************************** + * \brief + * This function is used to arithmetically encode the macroblock + * type info of a given MB in an I Slice. + *************************************************************************** + */ +void writeMB_I_typeInfo_CABAC(Macroblock *currMB, SyntaxElement *se, DataPartition *dp) +{ + EncodingEnvironmentPtr eep_dp = &(dp->ee_cabac); + int curr_len = arienco_bits_written(eep_dp); + int a, b; + int act_ctx = 0; + int act_sym; + Slice *currSlice = dp->p_Slice; + int mode_sym = 0; + + MotionInfoContexts *ctx = currSlice->mot_ctx; + + if (currMB->mb_up == NULL) + b = 0; + else + b = ((currMB->mb_up->mb_type != I4MB && currMB->mb_up->mb_type != I8MB) ? 1 : 0 ); + + if (currMB->mb_left == NULL) + a = 0; + else + a = ((currMB->mb_left->mb_type != I4MB && currMB->mb_left->mb_type != I8MB) ? 1 : 0 ); + + act_ctx = a + b; + act_sym = se->value1; + se->context = act_ctx; // store context + + if (act_sym==0) // 4x4 Intra + { + biari_encode_symbol(eep_dp, 0, ctx->mb_type_contexts[0] + act_ctx ); + } + else if( act_sym == 25 ) // PCM-MODE + { + biari_encode_symbol(eep_dp, 1, ctx->mb_type_contexts[0] + act_ctx ); + biari_encode_symbol_final(eep_dp, 1); + } + else // 16x16 Intra + { + biari_encode_symbol(eep_dp, 1, ctx->mb_type_contexts[0] + act_ctx ); + + biari_encode_symbol_final(eep_dp, 0); + + mode_sym = act_sym-1; // Values in the range of 0...23 + act_ctx = 4; + act_sym = mode_sym/12; + biari_encode_symbol(eep_dp, act_sym, ctx->mb_type_contexts[0] + act_ctx ); // coding of AC/no AC + mode_sym = mode_sym % 12; + act_sym = mode_sym / 4; // coding of cbp: 0,1,2 + act_ctx = 5; + if (act_sym==0) + { + biari_encode_symbol(eep_dp, 0, ctx->mb_type_contexts[0] + act_ctx ); + } + else + { + biari_encode_symbol(eep_dp, 1, ctx->mb_type_contexts[0] + act_ctx ); + act_ctx=6; + biari_encode_symbol(eep_dp, (act_sym!=1), ctx->mb_type_contexts[0] + act_ctx ); + } + mode_sym = mode_sym & 0x03; // coding of I pred-mode: 0,1,2,3 + act_sym = mode_sym >> 1; + act_ctx = 7; + biari_encode_symbol(eep_dp, act_sym, ctx->mb_type_contexts[0] + act_ctx ); + act_ctx = 8; + act_sym = mode_sym & 0x01; + biari_encode_symbol(eep_dp, act_sym, ctx->mb_type_contexts[0] + act_ctx ); + } + + dp->bitstream->write_flag = 1; + se->len = (arienco_bits_written(eep_dp) - curr_len); + CABAC_TRACE; +} + +/*! + *************************************************************************** + * \brief + * This function is used to arithmetically encode the 8x8 block + * type info + *************************************************************************** + */ +void writeB8_B_typeInfo_CABAC(SyntaxElement *se, DataPartition *dp) +{ + EncodingEnvironmentPtr eep_dp = &(dp->ee_cabac); + BiContextType *b8_type_contexts = dp->p_Slice->mot_ctx->b8_type_contexts[1]; + int curr_len = arienco_bits_written(eep_dp); + int act_sym = se->value1; + signed short csym; + + if (act_sym==0) + { + biari_encode_symbol (eep_dp, 0, &b8_type_contexts[0]); + dp->bitstream->write_flag = 1; + se->len = (arienco_bits_written(eep_dp) - curr_len); + CABAC_TRACE; + return; + } + else + { + biari_encode_symbol (eep_dp, 1, &b8_type_contexts[0]); + act_sym--; + } + + if (act_sym < 2) + { + biari_encode_symbol (eep_dp, 0, &b8_type_contexts[1]); + biari_encode_symbol (eep_dp, (act_sym!=0), &b8_type_contexts[3]); + } + else if (act_sym < 6) + { + biari_encode_symbol (eep_dp, 1, &b8_type_contexts[1]); + biari_encode_symbol (eep_dp, 0, &b8_type_contexts[2]); + csym = (short) (((act_sym - 2) >> 1) & 0x01) != 0; + biari_encode_symbol (eep_dp, csym, &b8_type_contexts[3]); + csym = (short) ((act_sym - 2) & 0x01) != 0; + biari_encode_symbol (eep_dp, csym, &b8_type_contexts[3]); + } + else + { + biari_encode_symbol (eep_dp, 1, &b8_type_contexts[1]); + biari_encode_symbol (eep_dp, 1, &b8_type_contexts[2]); + csym = (short) (((act_sym - 6)>> 2) & 0x01); + if (csym) + { + biari_encode_symbol (eep_dp, 1, &b8_type_contexts[3]); + csym = (short) ((act_sym - 6) & 0x01) != 0; + biari_encode_symbol (eep_dp, csym, &b8_type_contexts[3]); + } + else + { + biari_encode_symbol (eep_dp, 0, &b8_type_contexts[3]); + csym = (short) (((act_sym - 6) >> 1) & 0x01) != 0; + biari_encode_symbol (eep_dp, csym, &b8_type_contexts[3]); + csym = (short) ((act_sym - 6) & 0x01) != 0; + biari_encode_symbol (eep_dp, csym, &b8_type_contexts[3]); + } + } + + dp->bitstream->write_flag = 1; + se->len = (arienco_bits_written(eep_dp) - curr_len); + CABAC_TRACE; +} + +/*! + *************************************************************************** + * \brief + * This function is used to arithmetically encode the 8x8 block + * type info + *************************************************************************** + */ +void writeB8_typeInfo_CABAC(SyntaxElement *se, DataPartition *dp) +{ + EncodingEnvironmentPtr eep_dp = &(dp->ee_cabac); + BiContextType (*b8_type_contexts) = dp->p_Slice->mot_ctx->b8_type_contexts[0]; + int curr_len = arienco_bits_written(eep_dp); + + switch (se->value1) + { + case 0: + biari_encode_symbol (eep_dp, 1, &b8_type_contexts[1]); + break; + case 1: + biari_encode_symbol (eep_dp, 0, &b8_type_contexts[1]); + biari_encode_symbol (eep_dp, 0, &b8_type_contexts[3]); + break; + case 2: + biari_encode_symbol (eep_dp, 0, &b8_type_contexts[1]); + biari_encode_symbol (eep_dp, 1, &b8_type_contexts[3]); + biari_encode_symbol (eep_dp, 1, &b8_type_contexts[4]); + break; + case 3: + biari_encode_symbol (eep_dp, 0, &b8_type_contexts[1]); + biari_encode_symbol (eep_dp, 1, &b8_type_contexts[3]); + biari_encode_symbol (eep_dp, 0, &b8_type_contexts[4]); + break; + } + + dp->bitstream->write_flag = 1; + se->len = (arienco_bits_written(eep_dp) - curr_len); + CABAC_TRACE; +} + +/*! + **************************************************************************** + * \brief + * This function is used to arithmetically encode a pair of + * intra prediction modes of a given MB. + **************************************************************************** + */ +void writeIntraPredMode_CABAC(SyntaxElement *se, DataPartition *dp) +{ + EncodingEnvironmentPtr eep_dp = &(dp->ee_cabac); + int curr_len = arienco_bits_written(eep_dp); + BiContextType *ipr_contexts = (dp->p_Slice)->tex_ctx->ipr_contexts; + + // use_most_probable_mode + if (se->value1 == -1) + biari_encode_symbol(eep_dp, 1, ipr_contexts); + else + { + biari_encode_symbol(eep_dp, 0, ipr_contexts); + + // remaining_mode_selector + biari_encode_symbol(eep_dp, ( se->value1 & 0x1 ), ipr_contexts+1); + biari_encode_symbol(eep_dp, ((se->value1 & 0x2)>>1), ipr_contexts+1); + biari_encode_symbol(eep_dp, ((se->value1 & 0x4)>>2), ipr_contexts+1); + } + + dp->bitstream->write_flag = 1; + se->len = (arienco_bits_written(eep_dp) - curr_len); + CABAC_TRACE; +} + +/*! + **************************************************************************** + * \brief + * This function is used to arithmetically encode the reference + * parameter of a given MB. + **************************************************************************** + */ +void writeRefPic_P_CABAC(Macroblock *currMB, SyntaxElement *se, DataPartition *dp) +{ + EncodingEnvironmentPtr eep_dp = &(dp->ee_cabac); + int curr_len = arienco_bits_written(eep_dp); + Slice *currSlice = dp->p_Slice; + VideoParameters *p_Vid = dp->p_Vid; + PicMotionParams **motion = p_Vid->enc_picture->mv_info; + MotionInfoContexts *ctx = currSlice->mot_ctx; + + int addctx = 0; + + int a = 0, b = 0; + int act_ctx; + int act_sym; + int list = se->value2; + + PixelPos block_a, block_b; + int *mb_size = p_Vid->mb_size[IS_LUMA]; + + get4x4Neighbour(currMB, currMB->subblock_x - 1, currMB->subblock_y , mb_size, &block_a); + get4x4Neighbour(currMB, currMB->subblock_x, currMB->subblock_y - 1, mb_size, &block_b); + + if (block_b.available) + { + if (p_Vid->mb_aff_frame_flag && (currMB->mb_field == FALSE) && (p_Vid->mb_data[block_b.mb_addr].mb_field == TRUE)) + b = (motion[block_b.pos_y][block_b.pos_x].ref_idx[list] > 1 ? 2 : 0); + else + b = (motion[block_b.pos_y][block_b.pos_x].ref_idx[list] > 0 ? 2 : 0); + } + + if (block_a.available) + { + if (p_Vid->mb_aff_frame_flag && (currMB->mb_field == FALSE) && (p_Vid->mb_data[block_a.mb_addr].mb_field == TRUE)) + a = (motion[block_a.pos_y][block_a.pos_x].ref_idx[list] > 1 ? 1 : 0); + else + a = (motion[block_a.pos_y][block_a.pos_x].ref_idx[list] > 0 ? 1 : 0); + } + + act_ctx = a + b; + se->context = act_ctx; // store context + act_sym = se->value1; + + if (act_sym==0) + { + biari_encode_symbol(eep_dp, 0, ctx->ref_no_contexts[addctx] + act_ctx ); + } + else + { + biari_encode_symbol(eep_dp, 1, ctx->ref_no_contexts[addctx] + act_ctx); + act_sym--; + act_ctx=4; + unary_bin_encode(eep_dp, act_sym, ctx->ref_no_contexts[addctx] + act_ctx, 1); + } + + dp->bitstream->write_flag = 1; + se->len = (arienco_bits_written(eep_dp) - curr_len); + CABAC_TRACE; +} + +/*! + **************************************************************************** + * \brief + * This function is used to arithmetically encode the reference + * parameter of a given MB. + **************************************************************************** + */ +void writeRefPic_B_CABAC(Macroblock *currMB, SyntaxElement *se, DataPartition *dp) +{ + EncodingEnvironmentPtr eep_dp = &(dp->ee_cabac); + int curr_len = arienco_bits_written(eep_dp); + Slice *currSlice = dp->p_Slice; + VideoParameters *p_Vid = dp->p_Vid; + PicMotionParams **motion = p_Vid->enc_picture->mv_info; + MotionInfoContexts *ctx = currSlice->mot_ctx; + int addctx = 0; + + int a = 0, b = 0; + int act_ctx; + int act_sym; + int list = se->value2; + + int b8a, b8b; + + PixelPos block_a, block_b; + int *mb_size = p_Vid->mb_size[IS_LUMA]; + + get4x4Neighbour(currMB, currMB->subblock_x - 1, currMB->subblock_y , mb_size, &block_a); + get4x4Neighbour(currMB, currMB->subblock_x, currMB->subblock_y - 1, mb_size, &block_b); + + if (block_b.available) + { + b8b=((block_b.x >> 1) & 0x01)+2*((block_b.y >> 1) & 0x01); + + if (((p_Vid->mb_data[block_b.mb_addr].mb_type == 0) && !p_Vid->giRDOpt_B8OnlyFlag) || (p_Vid->mb_data[block_b.mb_addr].b8x8[b8b].mode==0)) + b=0; + else + { + if (p_Vid->mb_aff_frame_flag && (currMB->mb_field == FALSE) && (p_Vid->mb_data[block_b.mb_addr].mb_field == TRUE)) + b = (motion[block_b.pos_y][block_b.pos_x].ref_idx[list] > 1 ? 1 : 0); + else + b = (motion[block_b.pos_y][block_b.pos_x].ref_idx[list] > 0 ? 1 : 0); + } + } + + if (block_a.available) + { + b8a=((block_a.x >> 1) & 0x01)+2*((block_a.y >> 1) & 0x01); + if (((p_Vid->mb_data[block_a.mb_addr].mb_type == 0) && !p_Vid->giRDOpt_B8OnlyFlag) || (p_Vid->mb_data[block_a.mb_addr].b8x8[b8a].mode ==0)) + a=0; + else + { + if (p_Vid->mb_aff_frame_flag && (currMB->mb_field == FALSE) && (p_Vid->mb_data[block_a.mb_addr].mb_field == TRUE)) + a = (motion[block_a.pos_y][block_a.pos_x].ref_idx[list] > 1 ? 1 : 0); + else + a = (motion[block_a.pos_y][block_a.pos_x].ref_idx[list] > 0 ? 1 : 0); + } + } + + act_ctx = a + 2*b; + se->context = act_ctx; // store context + act_sym = se->value1; + + if (act_sym==0) + { + biari_encode_symbol(eep_dp, 0, ctx->ref_no_contexts[addctx] + act_ctx ); + } + else + { + biari_encode_symbol(eep_dp, 1, ctx->ref_no_contexts[addctx] + act_ctx); + act_sym--; + act_ctx=4; + unary_bin_encode(eep_dp, act_sym, ctx->ref_no_contexts[addctx] + act_ctx, 1); + } + + dp->bitstream->write_flag = 1; + se->len = (arienco_bits_written(eep_dp) - curr_len); + CABAC_TRACE; +} + +/*! + **************************************************************************** + * \brief + * This function is used to arithmetically encode the coded + * block pattern of a given delta quant. + **************************************************************************** + */ +void writeDquant_CABAC(Macroblock *currMB, SyntaxElement *se, DataPartition *dp) +{ + EncodingEnvironmentPtr eep_dp = &(dp->ee_cabac); + int curr_len = arienco_bits_written(eep_dp); + + TextureInfoContexts *ctx = dp->p_Slice->tex_ctx; + + int dquant = se->value1; + int sign = (dquant <= 0) ? 0 : -1; + int act_sym = (iabs(dquant) << 1) + sign; + int act_ctx = ((currMB->prev_dqp != 0) ? 1 : 0); + + if (act_sym == 0) + { + biari_encode_symbol(eep_dp, 0, ctx->delta_qp_contexts + act_ctx ); + } + else + { + biari_encode_symbol(eep_dp, 1, ctx->delta_qp_contexts + act_ctx); + act_ctx=2; + act_sym--; + unary_bin_encode(eep_dp, act_sym,ctx->delta_qp_contexts+act_ctx,1); + } + + dp->bitstream->write_flag = 1; + se->len = (arienco_bits_written(eep_dp) - curr_len); + CABAC_TRACE; +} + +/*! + **************************************************************************** + * \brief + * This function is used to arithmetically encode the motion + * vector data of a B-frame MB. + **************************************************************************** + */ +void writeMVD_CABAC(Macroblock *currMB, SyntaxElement *se, DataPartition *dp) +{ + VideoParameters *p_Vid = currMB->p_Vid; + EncodingEnvironmentPtr eep_dp = &(dp->ee_cabac); + + int curr_len = arienco_bits_written(eep_dp); + MotionInfoContexts *ctx = dp->p_Slice->mot_ctx; + + int i = currMB->subblock_x; + int j = currMB->subblock_y; + int a, b; + int act_ctx; + int act_sym; + int mv_pred_res; + int mv_local_err; + int mv_sign; + int list_idx = se->value2 & 0x01; + int k = (se->value2>>1); // MVD component + + PixelPos block_a, block_b; + + int *mb_size = p_Vid->mb_size[IS_LUMA]; + + get4x4Neighbour(currMB, i - 1, j , mb_size, &block_a); + get4x4Neighbour(currMB, i , j - 1, mb_size, &block_b); + + if (block_b.available) + { + b = iabs(p_Vid->mb_data[block_b.mb_addr].mvd[list_idx][block_b.y][block_b.x][k]); + if (p_Vid->mb_aff_frame_flag && (k==1)) + { + if ((currMB->mb_field==0) && (p_Vid->mb_data[block_b.mb_addr].mb_field==1)) + b *= 2; + else if ((currMB->mb_field==1) && (p_Vid->mb_data[block_b.mb_addr].mb_field==0)) + b /= 2; + } + } + else + b=0; + + if (block_a.available) + { + a = iabs(p_Vid->mb_data[block_a.mb_addr].mvd[list_idx][block_a.y][block_a.x][k]); + if (p_Vid->mb_aff_frame_flag && (k==1)) + { + if ((currMB->mb_field==0) && (p_Vid->mb_data[block_a.mb_addr].mb_field==1)) + a *= 2; + else if ((currMB->mb_field==1) && (p_Vid->mb_data[block_a.mb_addr].mb_field==0)) + a /= 2; + } + } + else + a = 0; + + if ((mv_local_err = a + b)<3) + act_ctx = 5 * k; + else + { + if (mv_local_err>32) + act_ctx = 5 * k + 3; + else + act_ctx = 5 * k + 2; + } + + mv_pred_res = se->value1; + se->context = act_ctx; + + act_sym = iabs(mv_pred_res); + + if (act_sym == 0) + biari_encode_symbol(eep_dp, 0, &ctx->mv_res_contexts[0][act_ctx] ); + else + { + biari_encode_symbol(eep_dp, 1, &ctx->mv_res_contexts[0][act_ctx] ); + act_sym--; + act_ctx=5*k; + unary_exp_golomb_mv_encode(eep_dp,act_sym,ctx->mv_res_contexts[1]+act_ctx,3); + mv_sign = (mv_pred_res<0) ? 1: 0; + biari_encode_symbol_eq_prob(eep_dp, mv_sign); + } + + dp->bitstream->write_flag = 1; + se->len = (arienco_bits_written(eep_dp) - curr_len); + CABAC_TRACE; +} + + +/*! + **************************************************************************** + * \brief + * This function is used to arithmetically encode the chroma + * intra prediction mode of an 8x8 block + **************************************************************************** + */ +void writeCIPredMode_CABAC(Macroblock *currMB, SyntaxElement *se, DataPartition *dp) +{ + EncodingEnvironmentPtr eep_dp = &(dp->ee_cabac); + int curr_len = arienco_bits_written(eep_dp); + TextureInfoContexts *ctx = dp->p_Slice->tex_ctx; + + int act_sym = se->value1; + + Macroblock *MbUp = currMB->mb_up; + Macroblock *MbLeft = currMB->mb_left; + + int b = (MbUp != NULL) ? (((MbUp->c_ipred_mode != 0) && (MbUp->mb_type != IPCM)) ? 1 : 0) : 0; + int a = (MbLeft != NULL) ? (((MbLeft->c_ipred_mode != 0) && (MbLeft->mb_type != IPCM)) ? 1 : 0) : 0; + + int act_ctx = a + b; + + if (act_sym==0) + biari_encode_symbol(eep_dp, 0, ctx->cipr_contexts + act_ctx ); + else + { + biari_encode_symbol(eep_dp, 1, ctx->cipr_contexts + act_ctx ); + unary_bin_max_encode(eep_dp,(unsigned int) (act_sym-1),ctx->cipr_contexts + 3,0,2); + } + + dp->bitstream->write_flag = 1; + se->len = (arienco_bits_written(eep_dp) - curr_len); + CABAC_TRACE; +} + + +/*! + **************************************************************************** + * \brief + * This function is used to arithmetically encode the coded + * block pattern of an 8x8 block + **************************************************************************** + */ +void writeCBP_BIT_CABAC (Macroblock* currMB, int b8, int bit, int cbp, EncodingEnvironmentPtr eep_dp, TextureInfoContexts *ctx) +{ + VideoParameters *p_Vid = currMB->p_Vid; + PixelPos block_a; + int a = 0, b = 0; + + int mb_x=(b8 & 0x01)<<1; + int mb_y=(b8 >> 1)<<1; + + if (mb_y == 0) + { + if (!((currMB->mb_up == NULL) || ((currMB->mb_up)->mb_type==IPCM))) + { + b = (( ((currMB->mb_up)->cbp & (1<<(2+(mb_x>>1)))) == 0) ? 1 : 0); //VG-ADD + } + + } + else + b = ( ((cbp & (1<<(mb_x >> 1))) == 0) ? 1: 0); + + if (mb_x == 0) + { + get4x4Neighbour(currMB, (mb_x << 2) - 1, (mb_y << 2), p_Vid->mb_size[IS_LUMA], &block_a); + + if (block_a.available && (!(p_Vid->mb_data[block_a.mb_addr].mb_type==IPCM))) + { + a = (( (p_Vid->mb_data[block_a.mb_addr].cbp & (1<<(2*(block_a.y>>1)+1))) == 0) ? 1 : 0); //VG-ADD + } + } + else + a = ( ((cbp & (1<cbp_contexts[0] + a+2*b); +} + +/*! +**************************************************************************** +* \brief +* This function is used to arithmetically encode the coded +* block pattern of a macroblock +**************************************************************************** +*/ +void writeCBP_CABAC(Macroblock *currMB, SyntaxElement *se, DataPartition *dp) +{ + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currSlice->p_Vid; + EncodingEnvironmentPtr eep_dp = &(dp->ee_cabac); + int curr_len = arienco_bits_written(eep_dp); + TextureInfoContexts *ctx = currSlice->tex_ctx; + + int a0 = 0, a1 = 0, b0 = 0, b1 = 0; + int curr_cbp_ctx; + int cbp = se->value1; // symbol to encode + int cbp_bit; + int b8; + + for (b8=0; b8<4; ++b8) + { + writeCBP_BIT_CABAC (currMB, b8, cbp&(1<yuv_format != YUV400) && (p_Vid->yuv_format != YUV444) ) + { + // coding of chroma part + if (currMB->mb_up != NULL) + { + if((currMB->mb_up)->mb_type == IPCM || ((currMB->mb_up)->cbp > 15)) + b0 = 2; + } + + if (currMB->mb_left != NULL) + { + if((currMB->mb_left)->mb_type==IPCM || ((currMB->mb_left)->cbp > 15)) + a0 = 1; + } + + curr_cbp_ctx = a0 + b0; + cbp_bit = (cbp > 15 ) ? 1 : 0; + biari_encode_symbol(eep_dp, cbp_bit, ctx->cbp_contexts[1] + curr_cbp_ctx ); + + if (cbp > 15) + { + if (currMB->mb_up != NULL) + { + if((currMB->mb_up)->mb_type == IPCM || + (((currMB->mb_up)->cbp > 15) && ( ((currMB->mb_up)->cbp >> 4) == 2))) + b1 = 2; + } + + if (currMB->mb_left != NULL) + { + if((currMB->mb_left)->mb_type==IPCM || + (((currMB->mb_left)->cbp > 15) && ( ((currMB->mb_left)->cbp >> 4) == 2))) + a1 = 1; + } + + curr_cbp_ctx = a1 + b1; + cbp_bit = ((cbp>>4) == 2) ? 1 : 0; + biari_encode_symbol(eep_dp, cbp_bit, ctx->cbp_contexts[2] + curr_cbp_ctx ); + } + } + + dp->bitstream->write_flag = 1; + se->len = (arienco_bits_written(eep_dp) - curr_len); + CABAC_TRACE; +} + +/*! + **************************************************************************** + * \brief + * Write CBP4-BIT (for 444 formats) + **************************************************************************** + */ +void write_and_store_CBP_block_bit_444 (Macroblock* currMB, EncodingEnvironmentPtr eep_dp, int type, int cbp_bit, TextureInfoContexts* tex_ctx) +{ + VideoParameters *p_Vid = currMB->p_Vid; + Macroblock *mb_data = p_Vid->mb_data; + + int y_ac = (type==LUMA_16AC || type==LUMA_8x8 || type==LUMA_8x4 || type==LUMA_4x8 || type==LUMA_4x4 + || type==CB_16AC || type==CB_8x8 || type==CB_8x4 || type==CB_4x8 || type==CB_4x4 + || type==CR_16AC || type==CR_8x8 || type==CR_8x4 || type==CR_4x8 || type==CR_4x4); + int y_dc = (type==LUMA_16DC || type==CB_16DC || type==CR_16DC); + int u_ac = (type==CHROMA_AC && !p_Vid->is_v_block); + int v_ac = (type==CHROMA_AC && p_Vid->is_v_block); + int chroma_dc = (type==CHROMA_DC || type==CHROMA_DC_2x4 || type==CHROMA_DC_4x4); + int u_dc = (chroma_dc && !p_Vid->is_v_block); + int v_dc = (chroma_dc && p_Vid->is_v_block); + int j = (y_ac || u_ac || v_ac ? currMB->subblock_y : 0); + int i = (y_ac || u_ac || v_ac ? currMB->subblock_x : 0); + int bit = (y_dc ? 0 : y_ac ? 1 : u_dc ? 17 : v_dc ? 18 : u_ac ? 19 : 23); + int default_bit = (currMB->is_intra_block ? 1 : 0); + int upper_bit = default_bit; + int left_bit = default_bit; + int ctx; + + int bit_pos_a = 0; + int bit_pos_b = 0; + + PixelPos block_a, block_b; + + if (y_ac || y_dc) + { + get4x4Neighbour(currMB, i - 1, j , p_Vid->mb_size[IS_LUMA], &block_a); + get4x4Neighbour(currMB, i, j -1, p_Vid->mb_size[IS_LUMA], &block_b); + if (y_ac) + { + if (block_a.available) + bit_pos_a = (block_a.y << 2) + block_a.x; + if (block_b.available) + bit_pos_b = (block_b.y << 2) + block_b.x; + } + } + else + { + get4x4Neighbour(currMB, i - 1, j , p_Vid->mb_size[IS_CHROMA], &block_a); + get4x4Neighbour(currMB, i, j - 1, p_Vid->mb_size[IS_CHROMA], &block_b); + if (u_ac||v_ac) + { + if (block_a.available) + bit_pos_a = (block_a.y << 2) + block_a.x; + if (block_b.available) + bit_pos_b = (block_b.y << 2) + block_b.x; + } + } + + bit = (y_dc ? 0 : y_ac ? 1 + j + (i >> 2) : u_dc ? 17 : v_dc ? 18 : u_ac ? 19 + j + (i >> 2): 35 + j + (i >> 2)); + //--- set bits for current block --- + if (cbp_bit) + { + if (type==LUMA_8x8) + { + currMB->cbp_bits[0] |= ((int64) 0x33 << bit); + + if (p_Vid->enc_picture->chroma_format_idc == YUV444) + { + currMB->cbp_bits_8x8[0] |= ((int64) 0x33 << bit); + } + } + else if (type==CB_8x8) + { + currMB->cbp_bits_8x8[1] |= ((int64) 0x33 << bit); + currMB->cbp_bits[1] |= ((int64) 0x33 << bit); + } + else if (type==CR_8x8) + { + currMB->cbp_bits_8x8[2] |= ((int64) 0x33 << bit); + currMB->cbp_bits[2] |= ((int64) 0x33 << bit); + } + else if (type==LUMA_8x4) + { + currMB->cbp_bits[0] |= ((int64) 0x03 << bit); + } + else if (type==LUMA_4x8) + { + currMB->cbp_bits[0] |= ((int64) 0x11 << bit); + } + else if (type==CB_8x4) + { + currMB->cbp_bits[1] |= ((int64) 0x03 << bit); + } + else if (type==CR_8x4) + { + currMB->cbp_bits[2] |= ((int64) 0x03 << bit); + } + else if (type==CB_4x8) + { + currMB->cbp_bits[1] |= ((int64) 0x11 << bit); + } + else if (type==CR_4x8) + { + currMB->cbp_bits[2] |= ((int64) 0x11 << bit); + } + else if ((type==CB_4x4)||(type==CB_16AC)||(type==CB_16DC)) + { + currMB->cbp_bits[1] |= ((int64) 0x01 << bit); + } + else if ((type == CR_4x4)||(type == CR_16AC)||(type == CR_16DC)) + { + currMB->cbp_bits[2] |= ((int64) 0x01 << bit); + } + else + { + currMB->cbp_bits[0] |= ((int64) 0x01 << bit); + } + } + + bit = (y_dc ? 0 : y_ac ? 1 : u_dc ? 17 : v_dc ? 18 : u_ac ? 19 : 35); + + if (p_Vid->enc_picture->chroma_format_idc!=YUV444) + { + if (type!=LUMA_8x8) + { + if (block_b.available) + { + if(mb_data[block_b.mb_addr].mb_type==IPCM) + upper_bit = 1; + else + upper_bit = get_bit(mb_data[block_b.mb_addr].cbp_bits[0],bit+bit_pos_b); + } + + + if (block_a.available) + { + if(mb_data[block_a.mb_addr].mb_type == IPCM) + left_bit = 1; + else + left_bit = get_bit(mb_data[block_a.mb_addr].cbp_bits[0], bit + bit_pos_a); + } + + ctx = (upper_bit << 1) + left_bit; + + //===== encode symbol ===== + biari_encode_symbol (eep_dp, cbp_bit, tex_ctx->bcbp_contexts[type2ctx_bcbp[type]] + ctx); + } + } + else if( (currMB->p_Inp->separate_colour_plane_flag != 0) ) + { + if (type!=LUMA_8x8) + { + if (block_b.available) + { + if(mb_data[block_b.mb_addr].mb_type==IPCM) + upper_bit = 1; + else + upper_bit = get_bit(mb_data[block_b.mb_addr].cbp_bits[0],bit+bit_pos_b); + } + + + if (block_a.available) + { + if(mb_data[block_a.mb_addr].mb_type == IPCM) + left_bit = 1; + else + left_bit = get_bit(mb_data[block_a.mb_addr].cbp_bits[0], bit + bit_pos_a); + } + + ctx = (upper_bit << 1) + left_bit; + + //===== encode symbol ===== + biari_encode_symbol (eep_dp, cbp_bit, tex_ctx->bcbp_contexts[type2ctx_bcbp[type]] + ctx); + } + } + else + { + if (block_b.available) + { + if((type==LUMA_8x8 || type==CB_8x8 || type==CR_8x8) && + !mb_data[block_b.mb_addr].luma_transform_size_8x8_flag) + {} + else if(mb_data[block_b.mb_addr].mb_type == IPCM) + upper_bit=1; + else + { + if(type==LUMA_8x8) + upper_bit = get_bit(mb_data[block_b.mb_addr].cbp_bits_8x8[0], bit + bit_pos_b); + else if (type==CB_8x8) + upper_bit = get_bit(mb_data[block_b.mb_addr].cbp_bits_8x8[1], bit + bit_pos_b); + else if (type==CR_8x8) + upper_bit = get_bit(mb_data[block_b.mb_addr].cbp_bits_8x8[2], bit + bit_pos_b); + else if ((type==CB_4x4)||(type==CB_4x8)||(type==CB_8x4)||(type==CB_16AC)||(type==CB_16DC)) + upper_bit = get_bit(mb_data[block_b.mb_addr].cbp_bits[1], bit + bit_pos_b); + else if ((type==CR_4x4)||(type==CR_4x8)||(type==CR_8x4)||(type==CR_16AC)||(type==CR_16DC)) + upper_bit = get_bit(mb_data[block_b.mb_addr].cbp_bits[2], bit + bit_pos_b); + else + upper_bit = get_bit(mb_data[block_b.mb_addr].cbp_bits[0], bit + bit_pos_b); + } + } + + if (block_a.available) + { + if((type==LUMA_8x8 || type==CB_8x8 || type==CR_8x8) && + !mb_data[block_a.mb_addr].luma_transform_size_8x8_flag) + {} + else if(mb_data[block_a.mb_addr].mb_type==IPCM) + left_bit = 1; + else + { + if(type==LUMA_8x8) + left_bit = get_bit(mb_data[block_a.mb_addr].cbp_bits_8x8[0],bit + bit_pos_a); + else if (type==CB_8x8) + left_bit = get_bit(mb_data[block_a.mb_addr].cbp_bits_8x8[1],bit + bit_pos_a); + else if (type==CR_8x8) + left_bit = get_bit(mb_data[block_a.mb_addr].cbp_bits_8x8[2],bit + bit_pos_a); + else if ((type==CB_4x4)||(type==CB_4x8)||(type==CB_8x4)||(type==CB_16AC)||(type==CB_16DC)) + left_bit = get_bit(mb_data[block_a.mb_addr].cbp_bits[1],bit + bit_pos_a); + else if ((type==CR_4x4)||(type==CR_4x8)||(type==CR_8x4)||(type==CR_16AC)||(type==CR_16DC)) + left_bit = get_bit(mb_data[block_a.mb_addr].cbp_bits[2],bit + bit_pos_a); + else + left_bit = get_bit(mb_data[block_a.mb_addr].cbp_bits[0],bit + bit_pos_a); + } + } + + ctx = 2*upper_bit+left_bit; + + //===== encode symbol ===== + biari_encode_symbol (eep_dp, cbp_bit, tex_ctx->bcbp_contexts[type2ctx_bcbp[type]]+ctx); + } +} + + +/*! + **************************************************************************** + * \brief + * Write CBP4-BIT + **************************************************************************** + */ +void write_and_store_CBP_block_bit (Macroblock* currMB, EncodingEnvironmentPtr eep_dp, int type, int cbp_bit, TextureInfoContexts* tex_ctx) +{ + VideoParameters *p_Vid = currMB->p_Vid; + Macroblock *mb_data = p_Vid->mb_data; + int y_ac = (type==LUMA_16AC || type==LUMA_8x8 || type==LUMA_8x4 || type==LUMA_4x8 || type==LUMA_4x4); + int y_dc = (type==LUMA_16DC); + int u_ac = (type==CHROMA_AC && !p_Vid->is_v_block); + int v_ac = (type==CHROMA_AC && p_Vid->is_v_block); + int chroma_dc = (type==CHROMA_DC || type==CHROMA_DC_2x4 || type==CHROMA_DC_4x4); + int u_dc = (chroma_dc && !p_Vid->is_v_block); + int v_dc = (chroma_dc && p_Vid->is_v_block); + int j = (y_ac || u_ac || v_ac ? currMB->subblock_y : 0); + int i = (y_ac || u_ac || v_ac ? currMB->subblock_x : 0); + int bit = (y_dc ? 0 : y_ac ? 1 : u_dc ? 17 : v_dc ? 18 : u_ac ? 19 : 23); + int default_bit = (currMB->is_intra_block ? 1 : 0); + int upper_bit = default_bit; + int left_bit = default_bit; + int ctx; + + int bit_pos_a = 0; + int bit_pos_b = 0; + + PixelPos block_a, block_b; + + if (y_ac || y_dc) + { + get4x4Neighbour(currMB, i - 1, j , p_Vid->mb_size[IS_LUMA], &block_a); + get4x4Neighbour(currMB, i, j -1, p_Vid->mb_size[IS_LUMA], &block_b); + if (y_ac) + { + if (block_a.available) + bit_pos_a = 4*block_a.y + block_a.x; + if (block_b.available) + bit_pos_b = 4*block_b.y + block_b.x; + } + } + else + { + get4x4Neighbour(currMB, i - 1, j , p_Vid->mb_size[IS_CHROMA], &block_a); + get4x4Neighbour(currMB, i, j - 1, p_Vid->mb_size[IS_CHROMA], &block_b); + if (u_ac||v_ac) + { + if (block_a.available) + bit_pos_a = (block_a.y << 2) + block_a.x; + if (block_b.available) + bit_pos_b = (block_b.y << 2) + block_b.x; + } + } + + bit = (y_dc ? 0 : y_ac ? 1 + j + (i >> 2): u_dc ? 17 : v_dc ? 18 : u_ac ? 19 + j + (i >> 2): 35 + j + (i >> 2)); + //--- set bits for current block --- + if (cbp_bit) + { + if (type==LUMA_8x8) + { + currMB->cbp_bits[0] |= ((int64) 0x33 << bit); + } + else if (type==LUMA_8x4) + { + currMB->cbp_bits[0] |= ((int64) 0x03 << bit); + } + else if (type==LUMA_4x8) + { + currMB->cbp_bits[0] |= ((int64) 0x11 << bit); + } + else + { + currMB->cbp_bits[0] |= ((int64) 0x01 << bit); + } + } + + bit = (y_dc ? 0 : y_ac ? 1 : u_dc ? 17 : v_dc ? 18 : u_ac ? 19 : 35); + + if (type!=LUMA_8x8) + { + if (block_b.available) + { + if(mb_data[block_b.mb_addr].mb_type==IPCM) + upper_bit = 1; + else + upper_bit = get_bit(mb_data[block_b.mb_addr].cbp_bits[0],bit+bit_pos_b); + } + + + if (block_a.available) + { + if(mb_data[block_a.mb_addr].mb_type == IPCM) + left_bit = 1; + else + left_bit = get_bit(mb_data[block_a.mb_addr].cbp_bits[0], bit + bit_pos_a); + } + + ctx = (upper_bit << 1) + left_bit; + + //===== encode symbol ===== + biari_encode_symbol (eep_dp, cbp_bit, tex_ctx->bcbp_contexts[type2ctx_bcbp[type]] + ctx); + } +} + +/*! +**************************************************************************** +* \brief +* Write Significance MAP +**************************************************************************** +*/ +void write_significance_map (Macroblock* currMB, EncodingEnvironmentPtr eep_dp, int type, int coeff[], int coeff_ctr, TextureInfoContexts* tex_ctx) +{ + int k; + int sig, last; + int k0 = 0; + int k1 = maxpos[type]; + +#if ENABLE_FIELD_CTX + VideoParameters *p_Vid = currMB->p_Vid; + int fld = ( p_Vid->structure!=FRAME || currMB->mb_field ); +#else + int fld = 0; +#endif + BiContextTypePtr map_ctx = tex_ctx->map_contexts [fld][type2ctx_map [type]]; + BiContextTypePtr last_ctx = tex_ctx->last_contexts[fld][type2ctx_last[type]]; + const byte *pos2ctxmap = fld ? pos2ctx_map_int [type] : pos2ctx_map [type]; + const byte *pos2ctxlast = pos2ctx_last[type]; + + if (!c1isdc[type]) + { + ++k0; + ++k1; + --coeff; + } + + for (k=k0; kone_contexts[type2ctx_one[type]]; + BiContextType *abs_contexts = tex_ctx->abs_contexts[type2ctx_abs[type]]; + int absLevel; + int ctx; + short sign; + int greater_one; + int c1 = 1; + int c2 = 0; + int i; + + for (i=maxpos[type]; (i>=0) && (coeff_cnt); i--) + { + if (coeff[i]!=0) + { + coeff_cnt--; + + if (coeff[i] > 0) + { + absLevel = coeff[i]; + sign = 0; + } + else + { + absLevel = -coeff[i]; + sign = 1; + } + + greater_one = (absLevel > 1); + + //--- if coefficient is one --- + ctx = imin(c1, 4); + biari_encode_symbol (eep_dp, greater_one, one_contexts + ctx); + + if (greater_one) + { + ctx = imin(c2++, max_c2[type]); + unary_exp_golomb_level_encode(eep_dp, absLevel - 2, abs_contexts + ctx); + c1 = 0; + } + else if (c1) + { + c1++; + } + biari_encode_symbol_eq_prob (eep_dp, sign); + } + } +} + + + +/*! + **************************************************************************** + * \brief + * Write Block-Transform Coefficients + **************************************************************************** + */ +void writeRunLevel_CABAC (Macroblock* currMB, SyntaxElement *se, DataPartition *dp) +{ + Slice *currSlice = currMB->p_Slice; + EncodingEnvironmentPtr eep_dp = &(dp->ee_cabac); + int curr_len = arienco_bits_written(eep_dp); + + //--- accumulate run-level information --- + if (se->value1 != 0) + { + currSlice->pos += se->value2; + currSlice->coeff[currSlice->pos++] = se->value1; + ++currSlice->coeff_ctr; + } + else + { + TextureInfoContexts *tex_ctx = currSlice->tex_ctx; + //===== encode CBP-BIT ===== + if (currSlice->coeff_ctr > 0) + { + currSlice->write_and_store_CBP_block_bit (currMB, eep_dp, se->context, 1, tex_ctx); + //===== encode significance map ===== + write_significance_map (currMB, eep_dp, se->context, currSlice->coeff, currSlice->coeff_ctr, tex_ctx); + //===== encode significant coefficients ===== + write_significant_coefficients (eep_dp, se->context, currSlice->coeff, tex_ctx, currSlice->coeff_ctr); + + memset(currSlice->coeff, 0 , 64 * sizeof(int)); + } + else + currSlice->write_and_store_CBP_block_bit (currMB, eep_dp, se->context, 0, tex_ctx); + + //--- reset counters --- + currSlice->pos = currSlice->coeff_ctr = 0; + } + + dp->bitstream->write_flag = 1; + se->len = (arienco_bits_written(eep_dp) - curr_len); + CABAC_TRACE; +} + + diff --git a/lencod/src/cconv_yuv2rgb.c b/lencod/src/cconv_yuv2rgb.c new file mode 100644 index 0000000..00f2648 --- /dev/null +++ b/lencod/src/cconv_yuv2rgb.c @@ -0,0 +1,149 @@ + +/*! + ************************************************************************************* + * \file cconv_yuv2rgb.c + * + * \brief + * YUV to RGB color conversion + * + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * - Woo-Shik Kim + ************************************************************************************* + */ +#include "contributors.h" +#include "global.h" +#include "memalloc.h" +#include "img_distortion.h" + +#define YUV2RGB_YOFFSET + +//YUV to RGB conversion +#ifdef YUV2RGB_YOFFSET +#define OFFSET_Y 16 +static const float K0 = 1.164f; +static const float K1 = 1.596f; +static const float K2 = 0.391f; +static const float K3 = 0.813f; +static const float K4 = 2.018f; +#else +static const float K0 = 1.000f; +static const float K1 = 1.402f; +static const float K2 = 0.34414f; +static const float K3 = 0.71414f; +static const float K4 = 1.772f; +#endif + +int create_RGB_memory(VideoParameters *p_Vid) +{ + int memory_size = 0; + int j; + for( j = 0; j < 3; j++ ) + { + memory_size += get_mem2Dpel (&p_Vid->imgRGB_src.data[j], p_Vid->height, p_Vid->width); + } + for( j = 0; j < 3; j++ ) + { + memory_size += get_mem2Dpel (&p_Vid->imgRGB_ref.data[j], p_Vid->height, p_Vid->width); + } + + return memory_size; +} + +void delete_RGB_memory(VideoParameters *p_Vid) +{ + int i; + for( i = 0; i < 3; i++ ) + { + free_mem2Dpel(p_Vid->imgRGB_src.data[i]); + } + for( i = 0; i < 3; i++ ) + { + free_mem2Dpel(p_Vid->imgRGB_ref.data[i]); + } +} + +void init_YUVtoRGB(VideoParameters *p_Vid, InputParameters *p_Inp) +{ + float conv_scale = (float) (65536.0f); + + p_Vid->wka0 = float2int( conv_scale * K0); + p_Vid->wka1 = float2int( conv_scale * K1); + p_Vid->wka2 = float2int( -conv_scale * K2); + p_Vid->wka3 = float2int( -conv_scale * K3); + p_Vid->wka4 = float2int( conv_scale * K4); + +#ifdef YUV2RGB_YOFFSET + p_Vid->offset_y = OFFSET_Y << (p_Inp->output.bit_depth[0] - 8); + p_Vid->offset_cr = 1 << (p_Inp->output.bit_depth[0] - 1); +#endif +} + +/*! +************************************************************************************* +* \brief +* YUV to RGB conversion +* ITU 601 with and without offset consideration +* Upsampling by repetition of chroma samples in case of 4:2:0 and 4:2:2 +* Method not support for 4:0:0 content +************************************************************************************* +*/ +void YUVtoRGB(VideoParameters *p_Vid, ImageStructure *YUV, ImageStructure *RGB) +{ + int i, j, j_cr, i_cr; + int sy, su, sv; + int wbuv, wguv, wruv; + imgpel *Y, *U, *V, *R, *G, *B; + FrameFormat format = YUV->format; + int width = format.width[0]; + int height = format.height[0]; + int max_value = format.max_value[0]; + + // Color conversion + for (j = 0; j < height; j++) + { + j_cr = j >> p_Vid->shift_cr_y; + Y = YUV->data[0][j]; + U = YUV->data[1][j_cr]; + V = YUV->data[2][j_cr]; + R = RGB->data[0][j]; + G = RGB->data[1][j]; + B = RGB->data[2][j]; + + for (i = 0; i < width; i++) + { + i_cr = i >> p_Vid->shift_cr_x; + + su = U[i_cr] - p_Vid->offset_cr; + sv = V[i_cr] - p_Vid->offset_cr; + + wruv = p_Vid->wka1 * sv; + wguv = p_Vid->wka2 * su + p_Vid->wka3 * sv; + wbuv = p_Vid->wka4 * su; + +#ifdef YUV2RGB_YOFFSET // Y offset value of 16 is considered + sy = p_Vid->wka0 * (Y[i] - p_Vid->offset_y); +#else + sy = p_Vid->wka0 * Y[i]; +#endif + + R[i] = (imgpel) iClip1( max_value, rshift_rnd(sy + wruv, 16)); + G[i] = (imgpel) iClip1( max_value, rshift_rnd(sy + wguv, 16)); + B[i] = (imgpel) iClip1( max_value, rshift_rnd(sy + wbuv, 16)); + } + } + // Setting RGB FrameFormat + RGB->format = format; // copy format information from YUV to RGB + RGB->format.yuv_format = YUV444; + RGB->format.color_model = CM_RGB; + RGB->format.pixel_format = BGR; + RGB->format.height[1] = format.height[0]; + RGB->format.width[1] = format.width[0]; + for (i = 1; i < 3; i++) + { + RGB->format.size_cmp[i] = format.size_cmp[0]; + RGB->format.bit_depth[i] = format.bit_depth[0]; + RGB->format.max_value[i] = max_value; + RGB->format.max_value_sq[i] = format.max_value_sq[0]; + } +} diff --git a/lencod/src/configfile.c b/lencod/src/configfile.c new file mode 100644 index 0000000..8fd28c1 --- /dev/null +++ b/lencod/src/configfile.c @@ -0,0 +1,1529 @@ + +/*! + *********************************************************************** + * \file + * configfile.c + * \brief + * Configuration handling. + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * - Stephan Wenger + * \note + * In the future this module should hide the Parameters and offer only + * Functions for their access. Modules which make frequent use of some parameters + * (e.g. picture size in macroblocks) are free to buffer them on local variables. + * This will not only avoid global variable and make the code more readable, but also + * speed it up. It will also greatly facilitate future enhancements such as the + * handling of different picture sizes in the same sequence. \n + * \n + * For now, everything is just copied to the inp_par structure (gulp) + * + ************************************************************************************** + * \par Configuration File Format + ************************************************************************************** + * Format is line oriented, maximum of one parameter per line \n + * \n + * Lines have the following format: \n + * \ = \ # Comments \\n \n + * Whitespace is space and \\t + * \par + * \ are the predefined names for Parameters and are case sensitive. + * See configfile.h for the definition of those names and their mapping to + * cfgparams->values. + * \par + * \ are either integers [0..9]* or strings. + * Integers must fit into the wordlengths, signed values are generally assumed. + * Strings containing no whitespace characters can be used directly. Strings containing + * whitespace characters are to be inclosed in double quotes ("string with whitespace") + * The double quote character is forbidden (may want to implement something smarter here). + * \par + * Any Parameters whose ParameterName is undefined lead to the termination of the program + * with an error message. + * + * \par Known bug/Shortcoming: + * zero-length strings (i.e. to signal an non-existing file + * have to be coded as "". + * + * \par Rules for using command files + * \n + * All Parameters are initially taken from DEFAULTCONFIGFILENAME, defined in configfile.h. + * If an -f \ parameter is present in the command line then this file is used to + * update the defaults of DEFAULTCONFIGFILENAME. There can be more than one -f parameters + * present. If -p parameters are present then these + * override the default and the additional config file's settings, and are themselves + * overridden by future -p parameters. There must be whitespace between -f and -p commands + * and their respective parameters + *********************************************************************** + */ + +#define INCLUDED_BY_CONFIGFILE_C + +#include + +#include "global.h" +#include "config_common.h" +#include "configfile.h" +#include "fmo.h" +#include "conformance.h" +#include "mc_prediction.h" +#include "mv_search.h" +#include "img_io.h" +#include "ratectl.h" + +static void PatchInp (VideoParameters *p_Vid, InputParameters *p_Inp); +static int TestEncoderParams (Mapping *Map, int bitdepth_qp_scale[3]); +static int DisplayEncoderParams (Mapping *Map); + +static const int mb_width_cr[4] = {0,8, 8,16}; +static const int mb_height_cr[4]= {0,8,16,16}; + +#define MAX_ITEMS_TO_PARSE 10000 + +#include "sei.h" +static void SetVUIScaleAndTicks(InputParameters *p_Inp, double frame_rate); + +/*! + *********************************************************************** + * \brief + * Set JM VUI parameters + *********************************************************************** + */ + +static void set_jm_vui_params( InputParameters *p_Inp ) +{ + SetVUIScaleAndTicks(p_Inp, 1.25 * p_Inp->output.frame_rate); + + p_Inp->EnableVUISupport = 1; + + p_Inp->VUI.nal_hrd_parameters_present_flag = 0; + p_Inp->VUI.vcl_hrd_parameters_present_flag = 0; + p_Inp->VUI.timing_info_present_flag = 1; + p_Inp->VUI.pic_struct_present_flag = 1; + p_Inp->VUI.fixed_frame_rate_flag = 1; +} + +/*! + *********************************************************************** + * \brief + * print help message and exit + *********************************************************************** + */ +void JMHelpExit (void) +{ + fprintf( stdout, "\n lencod [-h] [-d defenc.cfg] {[-f curenc1.cfg]...[-f curencN.cfg]}" + " {[-p EncParam1=EncValue1]..[-p EncParamM=EncValueM]}\n\n" + "## Parameters\n\n" + + "## Options\n" + " -h : prints function usage\n" + " -d : use as default file for parameter initializations.\n" + " If not used then file defaults to encoder.cfg in local directory.\n" + " -f : read for reseting selected encoder parameters.\n" + " Multiple files could be used that set different parameters\n" + " -p : Set parameter to .\n" + " See default encoder.cfg file for description of all parameters.\n\n" + + "## Supported video file formats\n" + " RAW: .yuv.,rgb -> P444 - Planar, 4:4:4 \n" + " P422 - Planar, 4:2:2 \n" + " P420 - Planar, 4:2:0 \n" + " P400 - Planar, 4:0:0 \n" + " I444 - Packed, 4:4:4 \n" + " I422 - Packed, 4:2:2 \n" + " I420 - Packed, 4:2:0 \n" + " IYUV/YV12 - Planar, 4:2:0 \n" + " IYU1 - Packed, 4:2:0 (UYYVYY) \n" + " IYU2 - Packed, 4:4:4 (UYV) \n" + " YUY2 - Packed, 4:2:2 (YUYV) \n" + " YUV - Packed, 4:4:4 (YUV) \n\n" + + "## Examples of usage:\n" + " lencod\n" + " lencod -h\n" + " lencod -d default.cfg\n" + " lencod -f curenc1.cfg\n" + " lencod -f curenc1.cfg -p InputFile=\"e:\\data\\container_qcif_30.yuv\" -p SourceWidth=176 -p SourceHeight=144\n" + " lencod -f curenc1.cfg -p FramesToBeEncoded=30 -p QPISlice=28 -p QPPSlice=28 -p QPBSlice=30\n"); + + exit(-1); +} + +/*! + ************************************************************************ + * \brief + * Reads Input File Size + * + ************************************************************************ + */ +int64 getVideoFileSize(int video_file) +{ + int64 fsize; + + lseek(video_file, 0, SEEK_END); + fsize = tell((int) video_file); + lseek(video_file, 0, SEEK_SET); + + return fsize; +} + +/*! + ************************************************************************ + * \brief + * Updates the number of frames to encode based on the file size + * + ************************************************************************ + */ +void get_number_of_frames (InputParameters *p_Inp, VideoDataFile *input_file) +{ + int64 fsize = getVideoFileSize(input_file->f_num); + + int64 isize = (int64) p_Inp->source.size; + int maxBitDepth = imax(p_Inp->source.bit_depth[0], p_Inp->source.bit_depth[1]); + + isize <<= (maxBitDepth > 8)? 1: 0; + p_Inp->no_frames = (int) (((fsize - p_Inp->infile_header)/ isize) - p_Inp->start_frame); +} + +/*! + ************************************************************************ + * \brief + * Updates images max values + * + ************************************************************************ + */ +static void updateMaxValue(FrameFormat *format) +{ + format->max_value[0] = (1 << format->bit_depth[0]) - 1; + format->max_value_sq[0] = format->max_value[0] * format->max_value[0]; + format->max_value[1] = (1 << format->bit_depth[1]) - 1; + format->max_value_sq[1] = format->max_value[1] * format->max_value[1]; + format->max_value[2] = (1 << format->bit_depth[2]) - 1; + format->max_value_sq[2] = format->max_value[2] * format->max_value[2]; +} + +/*! + ************************************************************************ + * \brief + * Update output format parameters (resolution & bit-depth) given input + * + ************************************************************************ + */ +static void updateOutFormat(InputParameters *p_Inp) +{ + FrameFormat *output = &p_Inp->output; + FrameFormat *source = &p_Inp->source; + output->yuv_format = (ColorFormat) p_Inp->yuv_format; + source->yuv_format = (ColorFormat) p_Inp->yuv_format; + + if (p_Inp->src_resize == 0) + { + output->width[0] = source->width[0]; + output->height[0] = source->height[0]; + } + + if (p_Inp->yuv_format == YUV400) // reset bitdepth of chroma for 400 content + { + source->bit_depth[1] = 8; + output->bit_depth[1] = 8; + source->width[1] = 0; + source->width[2] = 0; + source->height[1] = 0; + source->height[2] = 0; + output->width[1] = 0; + output->width[2] = 0; + output->height[1] = 0; + output->height[2] = 0; + } + else + { + source->width[1] = (source->width[0] * mb_width_cr [output->yuv_format]) >> 4; + source->width[2] = source->width[1]; + source->height[1] = (source->height[0] * mb_height_cr[output->yuv_format]) >> 4; + source->height[2] = source->height[1]; + output->width[1] = (output->width[0] * mb_width_cr [output->yuv_format]) >> 4; + output->width[2] = output->width[1]; + output->height[1] = (output->height[0] * mb_height_cr[output->yuv_format]) >> 4; + output->height[2] = output->height[1]; + } + + // source size + source->size_cmp[0] = source->width[0] * source->height[0]; + source->size_cmp[1] = source->width[1] * source->height[1]; + source->size_cmp[2] = source->size_cmp[1]; + source->size = source->size_cmp[0] + source->size_cmp[1] + source->size_cmp[2]; + source->mb_width = source->width[0] / MB_BLOCK_SIZE; + source->mb_height = source->height[0] / MB_BLOCK_SIZE; + source->pic_unit_size_on_disk = (imax(source->bit_depth[0], source->bit_depth[1]) > 8) ? 16 : 8; + source->pic_unit_size_shift3 = source->pic_unit_size_on_disk >> 3; + + + // output size (excluding padding) + output->size_cmp[0] = output->width[0] * output->height[0]; + output->size_cmp[1] = output->width[1] * output->height[1]; + output->size_cmp[2] = output->size_cmp[1]; + output->size = output->size_cmp[0] + output->size_cmp[1] + output->size_cmp[2]; + output->mb_width = output->width[0] / MB_BLOCK_SIZE; + output->mb_height = output->height[0] / MB_BLOCK_SIZE; + + + // both chroma components have the same bitdepth + source->bit_depth[2] = source->bit_depth[1]; + output->bit_depth[2] = output->bit_depth[1]; + + // if no bitdepth rescale ensure bitdepth is same + if (p_Inp->src_BitDepthRescale == 0) + { + output->bit_depth[0] = source->bit_depth[0]; + output->bit_depth[1] = source->bit_depth[1]; + output->bit_depth[2] = source->bit_depth[2]; + } + output->pic_unit_size_on_disk = (imax(output->bit_depth[0], output->bit_depth[1]) > 8) ? 16 : 8; + output->pic_unit_size_shift3 = output->pic_unit_size_on_disk >> 3; + + if (p_Inp->enable_32_pulldown) + { + source->frame_rate = source->frame_rate * 5 / 4; + p_Inp->idr_period = p_Inp->idr_period * 5 / 4; + p_Inp->intra_period = p_Inp->intra_period * 5 / 4; + p_Inp->no_frames = p_Inp->no_frames * 5 / 4; + } + + output->frame_rate = source->frame_rate / (p_Inp->frame_skip + 1); + output->color_model = source->color_model; + output->pixel_format = source->pixel_format; + + updateMaxValue(source); + updateMaxValue(output); +} + + +/*! + *********************************************************************** + * \brief + * Parse the command line parameters and read the config files. + * \param p_Vid + * VideoParameters structure for encoding + * \param p_Inp + * InputParameters structure as input configuration + * \param ac + * number of command line parameters + * \param av + * command line parameters + *********************************************************************** + */ +void Configure (VideoParameters *p_Vid, InputParameters *p_Inp, int ac, char *av[]) +{ + char *content = NULL; + int CLcount, ContentLen, NumberParams; + char *filename=DEFAULTCONFIGFILENAME; + + if (ac==2) + { + if (0 == strncmp (av[1], "-v", 2)) + { + printf("JM-" VERSION "\n"); + exit(0); + } + if (0 == strncmp (av[1], "-V", 2)) + { + printf("JM " JM ": compiled " __DATE__ " " __TIME__ "\n"); +#if ( IMGTYPE == 0 ) + printf("support for more than 8 bits/pel disabled\n"); +#endif +#if ( ENABLE_FIELD_CTX == 0 ) + printf("CABAC field coding disabled\n"); +#endif +#if ( ENABLE_HIGH444_CTX == 0 ) + printf("CABAC High 4:4:4 profile coding disabled\n"); +#endif + exit(0); + } + + if (0 == strncmp (av[1], "-h", 2)) + { + JMHelpExit(); + } + } + + memset (&cfgparams, 0, sizeof (InputParameters)); + //Set default parameters. + printf ("Setting Default Parameters...\n"); + InitParams(Map); + + // Process default config file + CLcount = 1; + + if (ac>=3) + { + if (0 == strncmp (av[1], "-d", 2)) + { + filename=av[2]; + CLcount = 3; + } + if (0 == strncmp (av[1], "-h", 2)) + { + JMHelpExit(); + } + } + printf ("Parsing Configfile %s", filename); + content = GetConfigFileContent (filename); + if (NULL==content) + error (errortext, 300); + ParseContent (p_Inp, Map, content, (int) strlen(content)); + printf ("\n"); + free (content); + + // Parse the command line + + while (CLcount < ac) + { + if (0 == strncmp (av[CLcount], "-h", 2)) + { + JMHelpExit(); + } + + if (0 == strncasecmp (av[CLcount], "-f", 2)) // A file parameter? + { + content = GetConfigFileContent (av[CLcount+1]); + if (NULL==content) + error (errortext, 300); + printf ("Parsing Configfile %s", av[CLcount+1]); + ParseContent (p_Inp, Map, content, (int) strlen (content)); + printf ("\n"); + free (content); + CLcount += 2; + } + else + { + if (0 == strncasecmp (av[CLcount], "-p", 2)) // A config change? + { + // Collect all data until next parameter (starting with - (x is any character)), + // put it into content, and parse content. + + ++CLcount; + ContentLen = 0; + NumberParams = CLcount; + + // determine the necessary size for content + while (NumberParams < ac && av[NumberParams][0] != '-') + ContentLen += (int) strlen (av[NumberParams++]); // Space for all the strings + ContentLen += 1000; // Additional 1000 bytes for spaces and \0s + + + if ((content = malloc (ContentLen))==NULL) no_mem_exit("Configure: content");; + content[0] = '\0'; + + // concatenate all parameters identified before + + while (CLcount < NumberParams) + { + char *source = &av[CLcount][0]; + char *destin = &content[(int) strlen (content)]; + + while (*source != '\0') + { + if (*source == '=') // The Parser expects whitespace before and after '=' + { + *destin++=' '; *destin++='='; *destin++=' '; // Hence make sure we add it + } + else + *destin++=*source; + source++; + } + *destin = '\0'; + CLcount++; + } + printf ("Parsing command line string '%s'", content); + ParseContent (p_Inp, Map, content, (int) strlen(content)); + free (content); + printf ("\n"); + } + else + { + snprintf (errortext, ET_SIZE, "Error in command line, ac %d, around string '%s', missing -f or -p parameters?", CLcount, av[CLcount]); + error (errortext, 300); + } + } + } + printf ("\n"); + +#if (MVC_EXTENSION_ENABLE) + if (p_Inp->num_of_views > 1) + { + printf ("Parsing Second View Configfile %s", p_Inp->View1ConfigName ); + content = GetConfigFileContent ( p_Inp->View1ConfigName ); + if (NULL==content) + error (errortext, 300); + ParseContent (p_Inp, MapView1, content, (int) strlen(content)); + printf ("\n"); + free (content); + } +#endif + + PatchInp(p_Vid, p_Inp); + + cfgparams = *p_Inp; + + if (p_Inp->DisplayEncParams) + DisplayEncoderParams(Map); +} + + + + +/*! + *********************************************************************** + * \brief + * Validates encoding parameters. + * \return + * -1 for error + *********************************************************************** + */ +static int TestEncoderParams(Mapping *Map, int bitdepth_qp_scale[3]) +{ + int i = 0; + + while (Map[i].TokenName != NULL) + { + if (Map[i].param_limits == 1) + { + if (Map[i].Type == 0) + { + if ( * (int *) (Map[i].Place) < (int) Map[i].min_limit || * (int *) (Map[i].Place) > (int) Map[i].max_limit ) + { + snprintf(errortext, ET_SIZE, "Error in input parameter %s. Check configuration file. Value should be in [%d, %d] range.", Map[i].TokenName, (int) Map[i].min_limit,(int)Map[i].max_limit ); + error (errortext, 400); + } + + } + else if (Map[i].Type == 2) + { + if ( * (double *) (Map[i].Place) < Map[i].min_limit || * (double *) (Map[i].Place) > Map[i].max_limit ) + { + snprintf(errortext, ET_SIZE, "Error in input parameter %s. Check configuration file. Value should be in [%.2f, %.2f] range.", Map[i].TokenName,Map[i].min_limit ,Map[i].max_limit ); + error (errortext, 400); + } + } + } + else if (Map[i].param_limits == 2) + { + if (Map[i].Type == 0) + { + if ( * (int *) (Map[i].Place) < (int) Map[i].min_limit ) + { + snprintf(errortext, ET_SIZE, "Error in input parameter %s. Check configuration file. Value should not be smaller than %d.", Map[i].TokenName, (int) Map[i].min_limit); + error (errortext, 400); + } + } + else if (Map[i].Type == 2) + { + if ( * (double *) (Map[i].Place) < Map[i].min_limit ) + { + snprintf(errortext, ET_SIZE, "Error in input parameter %s. Check configuration file. Value should not be smaller than %2.f.", Map[i].TokenName,Map[i].min_limit); + error (errortext, 400); + } + } + } + else if (Map[i].param_limits == 3) // Only used for QPs + { + + if (Map[i].Type == 0) + { + int cur_qp = * (int *) (Map[i].Place); + int min_qp = (int) (Map[i].min_limit - bitdepth_qp_scale[0]); + int max_qp = (int) Map[i].max_limit; + + if (( cur_qp < min_qp ) || ( cur_qp > max_qp )) + { + snprintf(errortext, ET_SIZE, "Error in input parameter %s. Check configuration file. Value should be in [%d, %d] range.", Map[i].TokenName, min_qp, max_qp ); + error (errortext, 400); + } + } + } + + i++; + } + return -1; +} + + + +/*! + *********************************************************************** + * \brief + * Outputs encoding parameters. + * \return + * -1 for error + *********************************************************************** + */ +static int DisplayEncoderParams(Mapping *Map) +{ + int i = 0; + + printf("******************************************************\n"); + printf("* Encoder Parameters *\n"); + printf("******************************************************\n"); + while (Map[i].TokenName != NULL) + { + if (Map[i].Type == 0) + printf("Parameter %s = %d\n",Map[i].TokenName,* (int *) (Map[i].Place)); + else if (Map[i].Type == 1) + printf("Parameter %s = ""%s""\n",Map[i].TokenName,(char *) (Map[i].Place)); + else if (Map[i].Type == 2) + printf("Parameter %s = %.2f\n",Map[i].TokenName,* (double *) (Map[i].Place)); + i++; + } + printf("******************************************************\n"); + return -1; +} + +/*! + ************************************************************************ + * \brief + * read the slice group configuration file. Returns without action + * if type is not 0, 2 or 6 + ************************************************************************ + */ +void read_slice_group_info(VideoParameters *p_Vid, InputParameters *p_Inp) +{ + FILE * sgfile=NULL; + int i; + int ret; + unsigned int PicSizeInMapUnits; + + if ((p_Inp->slice_group_map_type != 0) && (p_Inp->slice_group_map_type != 2) && (p_Inp->slice_group_map_type != 6)) + { + // nothing to do + return; + } + + // do we have a file name (not only NULL character) + if ((int) strlen (p_Inp->SliceGroupConfigFileName) <= 1) + error ("No slice group config file name specified", 500); + + // open file + sgfile = fopen(p_Inp->SliceGroupConfigFileName,"r"); + + if ( NULL==sgfile ) + { + snprintf(errortext, ET_SIZE, "Error opening slice group file %s", p_Inp->SliceGroupConfigFileName); + error (errortext, 500); + } + + switch (p_Inp->slice_group_map_type) + { + case 0: + p_Inp->run_length_minus1=(int *)malloc(sizeof(int)*(p_Inp->num_slice_groups_minus1+1)); + if ( NULL==p_Inp->run_length_minus1 ) + { + fclose(sgfile); + no_mem_exit("read_slice_group_info: p_Inp->run_length_minus1"); + } + + // each line contains one 'run_length_minus1' value + for(i=0; i <= p_Inp->num_slice_groups_minus1;i++) + { + ret = fscanf(sgfile,"%d",(p_Inp->run_length_minus1+i)); + if ( 1!=ret ) + { + fclose(sgfile); + snprintf(errortext, ET_SIZE, "Error while reading slice group config file (line %d)", i+1); + error (errortext, 500); + } + // scan remaining line + ret = fscanf(sgfile,"%*[^\n]"); + } + break; + + case 2: + // determine expected frame size in map units + PicSizeInMapUnits = (p_Inp->output.width[0] >> 4) * (p_Inp->output.height[0] >> 4); + if (p_Inp->MbInterlace||p_Inp->PicInterlace) + PicSizeInMapUnits >>= 1; + + p_Inp->top_left = (unsigned *)malloc(sizeof(unsigned)*p_Inp->num_slice_groups_minus1); + p_Inp->bottom_right = (unsigned *)malloc(sizeof(unsigned)*p_Inp->num_slice_groups_minus1); + + if (NULL==p_Inp->top_left) + { + fclose(sgfile); + no_mem_exit("PatchInp: p_Inp->top_left"); + } + + if (NULL==p_Inp->bottom_right) + { + fclose(sgfile); + no_mem_exit("PatchInp: p_Inp->bottom_right"); + } + + // every two lines contain 'top_left' and 'bottom_right' value + for(i=0;inum_slice_groups_minus1;i++) + { + ret = fscanf(sgfile,"%ud",(p_Inp->top_left+i)); + if ( 1!=ret ) + { + fclose(sgfile); + snprintf(errortext, ET_SIZE, "Error while reading slice group config file (line %d)", 2*i +1); + error (errortext, 500); + } + if (p_Inp->top_left[i] > PicSizeInMapUnits) + { + fprintf(stdout, "Warning: slice group # %d top_left exceeds picture size (will be clipped)\n", i); + } + // scan remaining line + ret = fscanf(sgfile,"%*[^\n]"); + ret = fscanf(sgfile,"%ud",(p_Inp->bottom_right+i)); + if ( 1!=ret ) + { + fclose(sgfile); + snprintf(errortext, ET_SIZE, "Error while reading slice group config file (line %d)", 2*i + 2); + error (errortext, 500); + } + if (p_Inp->bottom_right[i] > PicSizeInMapUnits) + { + fprintf(stdout, "Warning: slice group # %d bottom_right exceeds picture size (will be clipped)\n", i); + } + // scan remaining line + ret = fscanf(sgfile,"%*[^\n]"); + } + break; + case 6: + { + int tmp; + int frame_mb_only; + int mb_width, mb_height, mapunit_height; + + frame_mb_only = !(p_Inp->PicInterlace || p_Inp->MbInterlace); + mb_width = (p_Inp->output.width[0] + p_Vid->auto_crop_right)>>4; + mb_height = (p_Inp->output.height[0] + p_Vid->auto_crop_bottom)>>4; + mapunit_height = mb_height / (2-frame_mb_only); + + p_Inp->slice_group_id=(byte * ) malloc(sizeof(byte)*mapunit_height*mb_width); + if (NULL==p_Inp->slice_group_id) + { + fclose(sgfile); + no_mem_exit("PatchInp: p_Inp->slice_group_id"); + } + + // each line contains slice_group_id for one Macroblock + for (i=0;islice_group_id[i]= (byte) tmp; + if ( 1!=ret ) + { + fclose(sgfile); + snprintf(errortext, ET_SIZE, "Error while reading slice group config file (line %d)", i + 1); + error (errortext, 500); + } + if ( *(p_Inp->slice_group_id+i) > p_Inp->num_slice_groups_minus1 ) + { + fclose(sgfile); + snprintf(errortext, ET_SIZE, "Error while reading slice group config file: slice_group_id not allowed (line %d)", i + 1); + error (errortext, 500); + } + // scan remaining line + ret = fscanf(sgfile,"%*[^\n]"); + } + } + break; + default: + // we should not get here + error ("Wrong slice group type while reading config file", 500); + break; + } + + // close file again + fclose(sgfile); +} + +/*! + *********************************************************************** + * \brief + * Checks the input parameters for consistency. + *********************************************************************** + */ +static void PatchInp (VideoParameters *p_Vid, InputParameters *p_Inp) +{ + int i; + int bitdepth_qp_scale[3]; + + if (p_Inp->src_BitDepthRescale) + { + bitdepth_qp_scale [0] = 6*(p_Inp->output.bit_depth[0] - 8); + bitdepth_qp_scale [1] = 6*(p_Inp->output.bit_depth[1] - 8); + bitdepth_qp_scale [2] = 6*(p_Inp->output.bit_depth[2] - 8); + } + else + { + bitdepth_qp_scale [0] = 6*(p_Inp->source.bit_depth[0] - 8); + bitdepth_qp_scale [1] = 6*(p_Inp->source.bit_depth[1] - 8); + bitdepth_qp_scale [2] = 6*(p_Inp->source.bit_depth[2] - 8); + } + + TestEncoderParams(Map, bitdepth_qp_scale); + + if (p_Inp->source.frame_rate == 0.0) + p_Inp->source.frame_rate = (double) INIT_FRAME_RATE; + + ParseVideoType(&p_Inp->input_file1); + ParseFrameNoFormatFromString (&p_Inp->input_file1); + +#if (MVC_EXTENSION_ENABLE) + if ( p_Inp->num_of_views == 2 ) + { + ParseVideoType(&p_Inp->input_file2); + ParseFrameNoFormatFromString (&p_Inp->input_file2); + } +#endif + + if(p_Inp->source.bit_depth[0] >8) + { + if(!IMGTYPE) + { + error("IMGTYPE should be 1 when bitdepth is greater than 8", 600); + } + } + // Read resolution from file name + if (p_Inp->source.width[0] == 0 || p_Inp->source.height[0] == 0) + { + if (ParseSizeFromString (&p_Inp->input_file1, &(p_Inp->source.width[0]), &(p_Inp->source.height[0]), &(p_Inp->source.frame_rate)) == 0) + { + snprintf(errortext, ET_SIZE, "File name does not contain resolution information."); + error (errortext, 500); + } + } + +#if (!ENABLE_FIELD_CTX) + if ( (p_Inp->PicInterlace || p_Inp->MbInterlace) && p_Inp->symbol_mode ) + { + snprintf(errortext, ET_SIZE, "Recompile with ENABLE_FIELD_CTX set to one to enable interlaced coding with CABAC."); + error (errortext, 500); + } +#endif + +#if (!ENABLE_HIGH444_CTX) + if ( p_Inp->ProfileIDC == 244 && p_Inp->symbol_mode ) + { + snprintf(errortext, ET_SIZE, "Recompile with ENABLE_HIGH444_CTX set to one to enable the High 4:4:4 Profile with CABAC."); + error (errortext, 500); + } +#endif + + // Currently to simplify things, lets copy everything (overwrites yuv_format) + p_Inp->input_file1.format = p_Inp->source; + + + if (p_Inp->idr_period && p_Inp->intra_delay && p_Inp->idr_period <= p_Inp->intra_delay) + { + snprintf(errortext, ET_SIZE, " IntraDelay cannot be larger than or equal to IDRPeriod."); + error (errortext, 500); + } + + // some contraints for IntraPeriod, IDRPeriod, EnableIDRGOP, and NumberBFrames + if ( p_Inp->idr_period > 0 && (p_Inp->NumberBFrames + 1 + p_Inp->intra_delay) > p_Inp->idr_period ) + { + snprintf(errortext, ET_SIZE, " Setting NumberBFrames = IDRPeriod - 1 - IntraDelay."); + p_Inp->NumberBFrames = p_Inp->idr_period - 1 - p_Inp->intra_delay; + if ( p_Inp->NumberBFrames < 0 ) + { + p_Inp->NumberBFrames = p_Inp->idr_period - 1; + p_Inp->intra_delay = 0; + } + if ( p_Inp->HierarchicalCoding ) + { + p_Inp->HierarchicalCoding = 2; + } + } + if ( p_Inp->intra_period > 0 && (p_Inp->NumberBFrames + 1 + p_Inp->intra_delay) > p_Inp->intra_period ) + { + snprintf(errortext, ET_SIZE, " Setting NumberBFrames = IntraPeriod - 1 - IntraDelay."); + p_Inp->NumberBFrames = p_Inp->intra_period - 1 - p_Inp->intra_delay; + if ( p_Inp->NumberBFrames < 0 ) + { + p_Inp->NumberBFrames = p_Inp->intra_period - 1; + p_Inp->intra_delay = 0; + } + if ( p_Inp->HierarchicalCoding ) + { + p_Inp->HierarchicalCoding = 2; + } + } + // Added to avoid encoding problems with List 0 being empty + if (p_Inp->intra_delay && p_Inp->NumberBFrames > 0 && p_Inp->EnableOpenGOP) + p_Inp->PocMemoryManagement = 1; + + // Let us set up p_Inp->jumpd from frame_skip and NumberBFrames + p_Inp->jumpd = (p_Inp->NumberBFrames + 1) * (p_Inp->frame_skip + 1) - 1; + + + updateOutFormat(p_Inp); + // SEI/VUI 3:2 pulldown support + if ( p_Inp->SEIVUI32Pulldown && p_Inp->enable_32_pulldown ) + { + snprintf(errortext, ET_SIZE, "SEIVUI32Pulldown and Enable32Pulldown cannot be set at the same time."); + error (errortext, 500); + } + if ( p_Inp->SEIVUI32Pulldown ) + { + set_jm_vui_params( p_Inp ); + } + + if (p_Inp->no_frames == -1) + { + OpenFiles(&p_Inp->input_file1); + get_number_of_frames (p_Inp, &p_Inp->input_file1); + CloseFiles(&p_Inp->input_file1); + } + + if (p_Inp->no_frames < 1) + { + snprintf(errortext, ET_SIZE, "Not enough frames to encode (%d)", p_Inp->no_frames); + error (errortext, 500); + } + + // Check IntraProfile settings + if (p_Inp->IntraProfile) + { + if(p_Inp->NumberBFrames) + { + snprintf(errortext, ET_SIZE, "Use of B slices not supported with Intra Profiles. Set NumberBFrames to 0."); + error (errortext, 400); + } + + if (p_Inp->PocMemoryManagement) + { + printf("Warning: PocMemoryManagement not supported for Intra Profiles. Process Disabled.\n"); + p_Inp->PocMemoryManagement = 0; + } + if (p_Inp->ReferenceReorder) + { + printf("Warning: ReferenceReorder not supported for Intra Profiles. Process Disabled.\n"); + p_Inp->ReferenceReorder = 0; + } + } + + // Direct Mode consistency check + if(p_Inp->NumberBFrames && p_Inp->direct_spatial_mv_pred_flag != DIR_SPATIAL && p_Inp->direct_spatial_mv_pred_flag != DIR_TEMPORAL) + { + snprintf(errortext, ET_SIZE, "Unsupported direct mode=%d, use TEMPORAL=0 or SPATIAL=1", p_Inp->direct_spatial_mv_pred_flag); + error (errortext, 400); + } + + if (p_Inp->PicInterlace>0 || p_Inp->MbInterlace>0) + { + if (p_Inp->directInferenceFlag==0) + printf("\nWarning: DirectInferenceFlag set to 1 due to interlace coding."); + p_Inp->directInferenceFlag = 1; + } + + if(p_Inp->PicInterlace > 0 || p_Inp->MbInterlace > 0) + { + printf("\nWarning: WPMCPredicions is set to 0 due to interlace coding.\n"); + p_Inp->WPMCPrecision = 0; + } +#if TRACE + if ((int) strlen (p_Inp->TraceFile) > 0 && (p_Enc->p_trace = fopen(p_Inp->TraceFile,"w"))==NULL) + { + snprintf(errortext, ET_SIZE, "Error open file %s", p_Inp->TraceFile); + error (errortext, 500); + } +#endif + + + + if ((p_Inp->slice_mode == 1)&&(p_Inp->MbInterlace != 0)) + { + if ((p_Inp->slice_argument & 0x01)!=0) + { + fprintf ( stdout, "Warning: slice border within macroblock pair. "); + if (p_Inp->slice_argument > 1) + { + p_Inp->slice_argument--; + } + else + { + p_Inp->slice_argument++; + } + fprintf ( stdout, "Using %d MBs per slice.\n", p_Inp->slice_argument); + } + } + + if (p_Inp->WPMCPrecision && (p_Inp->RDPictureDecision != 1 || p_Inp->GenerateMultiplePPS != 1) ) + { + snprintf(errortext, ET_SIZE, "WPMCPrecision requires both RDPictureDecision=1 and GenerateMultiplePPS=1.\n"); + error (errortext, 400); + } + p_Inp->num_ref_frames_org = p_Inp->num_ref_frames; + p_Inp->P_List0_refs_org[0] = p_Inp->P_List0_refs[0]; + p_Inp->B_List0_refs_org[0] = p_Inp->B_List0_refs[0]; + p_Inp->B_List1_refs_org[0] = p_Inp->B_List1_refs[0]; + p_Inp->P_List0_refs_org[1] = p_Inp->P_List0_refs[1]; + p_Inp->B_List0_refs_org[1] = p_Inp->B_List0_refs[1]; + p_Inp->B_List1_refs_org[1] = p_Inp->B_List1_refs[1]; + + if (p_Inp->WPMCPrecision && p_Inp->WPMCPrecFullRef && p_Inp->num_ref_frames < 16 ) + { + p_Inp->num_ref_frames++; + if ( p_Inp->P_List0_refs[0] ) + { + p_Inp->P_List0_refs[0]++; + } + else + { + p_Inp->P_List0_refs[0] = p_Inp->num_ref_frames; + } + if ( p_Inp->B_List0_refs[0] ) + { + p_Inp->B_List0_refs[0]++; + } + else + { + p_Inp->B_List0_refs[0] = p_Inp->num_ref_frames; + } + if ( p_Inp->B_List1_refs[0] ) + { + p_Inp->B_List1_refs[0]++; + } + else + { + p_Inp->B_List1_refs[0] = p_Inp->num_ref_frames; + } + if ( p_Inp->SepViewInterSearch ) + { + if ( p_Inp->P_List0_refs[1] ) + { + p_Inp->P_List0_refs[1]++; + } + else + { + p_Inp->P_List0_refs[1] = p_Inp->num_ref_frames; + } + if ( p_Inp->B_List0_refs[1] ) + { + p_Inp->B_List0_refs[1]++; + } + else + { + p_Inp->B_List0_refs[1] = p_Inp->num_ref_frames; + } + if ( p_Inp->B_List1_refs[1] ) + { + p_Inp->B_List1_refs[1]++; + } + else + { + p_Inp->B_List1_refs[1] = p_Inp->num_ref_frames; + } + } + } + else if ( p_Inp->WPMCPrecision && p_Inp->WPMCPrecFullRef ) + { + snprintf(errortext, ET_SIZE, "WPMCPrecFullRef requires NumberReferenceFrames < 16.\n"); + error (errortext, 400); + } + + if (p_Inp->PicInterlace) + { + if (p_Inp->ReferenceReorder == 2) + { + snprintf(errortext, ET_SIZE, "ReferenceReorder = 2 is not supported with field encoding\n"); + error (errortext, 400); + } + if (p_Inp->ReferenceReorder == 2) + { + snprintf(errortext, ET_SIZE, "PocMemoryManagement = 2 is not supported with field encoding\n"); + error (errortext, 400); + } + } + + if (p_Inp->ReferenceReorder && p_Inp->MbInterlace ) + { + snprintf(errortext, ET_SIZE, "ReferenceReorder is not supported with MBAFF\n"); + error (errortext, 400); + } + + if (p_Inp->SetFirstAsLongTerm && (p_Inp->ReferenceReorder == 1 || p_Inp->ReferenceReorder == 2)) + { + printf("SetFirstAsLongTerm is set. ReferenceReorder is not supported and therefore disabled. \n"); + p_Inp->ReferenceReorder = 0; + } + + if ( is_MVC_profile(p_Inp->ProfileIDC) ) + { + if (p_Inp->ReferenceReorder > 1) + { + snprintf(errortext, ET_SIZE, "ReferenceReorder > 1 is not supported with the Multiview (118) or Stereo High (128) profiles and is therefore disabled. \n"); + p_Inp->ReferenceReorder = 0; + } + if ( (p_Inp->PocMemoryManagement) && (p_Inp->PicInterlace > 0) ) + { + snprintf(errortext, ET_SIZE, "PocMemoryManagement>0 is not supported with the Multiview (118) or Stereo High (128) profiles and is therefore disabled. \n"); + p_Inp->PocMemoryManagement = 0; + } + } + + + if (p_Inp->PocMemoryManagement && p_Inp->MbInterlace ) + { + snprintf(errortext, ET_SIZE, "PocMemoryManagement is not supported with MBAFF\n"); + error (errortext, 400); + } + + if(p_Inp->MbInterlace && p_Inp->RDPictureDecision && p_Inp->GenerateMultiplePPS) + { + snprintf(errortext, ET_SIZE, "RDPictureDecision+GenerateMultiplePPS not supported with MBAFF. RDPictureDecision therefore disabled\n"); + p_Inp->RDPictureDecision = 0; + } + + if ((!p_Inp->rdopt)&&(p_Inp->MbInterlace==2)) + { + snprintf(errortext, ET_SIZE, "MB AFF is not compatible with non-rd-optimized coding."); + error (errortext, 500); + } + + // check RDoptimization mode and profile. FMD does not support Frex Profiles. + if (p_Inp->rdopt==2 && ( p_Inp->ProfileIDC>=FREXT_HP || p_Inp->ProfileIDC==FREXT_CAVLC444 )) + { + snprintf(errortext, ET_SIZE, "Fast Mode Decision methods not supported in FREX Profiles"); + error (errortext, 500); + } + + // Tian Dong: May 31, 2002 + // The number of frames in one sub-seq in enhanced layer should not exceed + // the number of reference frame number. + if ( p_Inp->NumFramesInELSubSeq > p_Inp->num_ref_frames || p_Inp->NumFramesInELSubSeq < 0 ) + { + snprintf(errortext, ET_SIZE, "NumFramesInELSubSeq (%d) is out of range [0,%d).", p_Inp->NumFramesInELSubSeq, p_Inp->num_ref_frames); + error (errortext, 500); + } + // Tian Dong: Enhanced GOP is not supported in bitstream mode. September, 2002 + if ( p_Inp->NumFramesInELSubSeq > 0 ) + { + snprintf(errortext, ET_SIZE, "Enhanced GOP is not properly supported yet."); + error (errortext, 500); + } + // Tian Dong (Sept 2002) + // The AFF is not compatible with spare picture for the time being. + if ((p_Inp->PicInterlace || p_Inp->MbInterlace) && p_Inp->SparePictureOption == TRUE) + { + snprintf(errortext, ET_SIZE, "AFF is not compatible with spare picture."); + error (errortext, 500); + } + + // Only the RTP mode is compatible with spare picture for the time being. + if (p_Inp->of_mode != PAR_OF_RTP && p_Inp->SparePictureOption == TRUE) + { + snprintf(errortext, ET_SIZE, "Only RTP output mode is compatible with spare picture features."); + error (errortext, 500); + } + + // RTP is not defined for MVC yet + if (p_Inp->of_mode == PAR_OF_RTP && (( p_Inp->ProfileIDC == STEREO_HIGH ) || (p_Inp->ProfileIDC == MULTIVIEW_HIGH) )) + { + snprintf(errortext, ET_SIZE, "RTP output mode is not compatible with MVC profiles."); + error (errortext, 500); + } + + if( (p_Inp->WeightedPrediction > 0 || p_Inp->WeightedBiprediction > 0) && (p_Inp->MbInterlace)) + { + snprintf(errortext, ET_SIZE, "Weighted prediction coding is not supported for MB AFF currently."); + error (errortext, 500); + } + if( (p_Inp->WeightedPrediction > 0 || p_Inp->WeightedBiprediction > 0) && (p_Inp->yuv_format==3)) + { + snprintf(errortext, ET_SIZE, "Weighted prediction coding is not supported for 4:4:4 encoding."); + error (errortext, 500); + } + if ( p_Inp->NumFramesInELSubSeq > 0 && p_Inp->WeightedPrediction > 0) + { + snprintf(errortext, ET_SIZE, "Enhanced GOP is not supported in weighted prediction coding mode yet."); + error (errortext, 500); + } + + // Rate control + if(p_Inp->RCEnable) + { + if ( p_Inp->RCUpdateMode == RC_MODE_1 && + !( (p_Inp->intra_period == 1 || p_Inp->idr_period == 1 || p_Inp->BRefPictures == 2 ) && !p_Inp->NumberBFrames ) ) + { + snprintf(errortext, ET_SIZE, "Use RCUpdateMode = 1 only for all intra or all B-slice coding."); + error (errortext, 500); + } + if ( (p_Inp->RCUpdateMode == RC_MODE_0 || p_Inp->RCUpdateMode == RC_MODE_2 || p_Inp->RCUpdateMode == RC_MODE_3) && + (p_Inp->PReplaceBSlice && p_Inp->NumberBFrames ) ) + { + snprintf(errortext, ET_SIZE, "PReplaceBSlice is not supported with RCUpdateMode=0,2,3."); + error (errortext, 500); + } + + if ( p_Inp->BRefPictures == 2 && p_Inp->intra_period == 0 && p_Inp->RCUpdateMode != RC_MODE_1 ) + { + snprintf(errortext, ET_SIZE, "Use RCUpdateMode = 1 for all B-slice coding."); + error (errortext, 500); + } + + if ( p_Inp->HierarchicalCoding && p_Inp->RCUpdateMode != RC_MODE_2 && p_Inp->RCUpdateMode != RC_MODE_3 ) + { + snprintf(errortext, ET_SIZE, "Use RCUpdateMode = 2 or 3 for hierarchical B-picture coding."); + error (errortext, 500); + } + if ( (p_Inp->RCUpdateMode != RC_MODE_1) && (p_Inp->intra_period == 1) ) + { + snprintf(errortext, ET_SIZE, "Use RCUpdateMode = 1 for all intra coding."); + error (errortext, 500); + } + } + + if ((p_Inp->NumberBFrames)&&(p_Inp->BRefPictures)&&(p_Inp->idr_period)&&(p_Inp->pic_order_cnt_type!=0)) + { + error("Stored B pictures combined with IDR pictures only supported in Picture Order Count type 0\n",-1000); + } + + if( !p_Inp->direct_spatial_mv_pred_flag && p_Inp->num_ref_frames<2 && p_Inp->NumberBFrames >0) + error("temporal direct needs at least 2 ref frames\n",-1000); + +#if (MVC_EXTENSION_ENABLE) + if (p_Inp->SepViewInterSearch && p_Inp->SearchMode[1] == FAST_FULL_SEARCH && p_Inp->MEErrorMetric[F_PEL] > ERROR_SSE) + { + snprintf(errortext, ET_SIZE, "\nOnly SAD and SSE distortion computation supported with Fast Full Search."); + error (errortext, 500); + } + else +#endif + if (p_Inp->SearchMode[0] == FAST_FULL_SEARCH && p_Inp->MEErrorMetric[F_PEL] > ERROR_SSE) + { + snprintf(errortext, ET_SIZE, "\nOnly SAD and SSE distortion computation supported with Fast Full Search."); + error (errortext, 500); + } + + if (p_Inp->rdopt == 0) + { + if (p_Inp->DisableSubpelME[0] || p_Inp->DisableSubpelME[1]) + { + if (p_Inp->MEErrorMetric[F_PEL] != p_Inp->ModeDecisionMetric) + { + snprintf(errortext, ET_SIZE, "\nLast refinement level (FPel) distortion not the same as Mode decision distortion.\nPlease update MEDistortionFPel (%d) and/or MDDistortion(%d).", p_Inp->MEErrorMetric[F_PEL], p_Inp->ModeDecisionMetric); + error (errortext, 500); + } + } + else if (p_Inp->MEErrorMetric[Q_PEL] != p_Inp->ModeDecisionMetric) + { + snprintf(errortext, ET_SIZE, "\nLast refinement level (QPel) distortion not the same as Mode decision distortion.\nPlease update MEDistortionQPel (%d) and/or MDDistortion(%d).", p_Inp->MEErrorMetric[Q_PEL], p_Inp->ModeDecisionMetric); + error (errortext, 500); + } + } + // frext + if(p_Inp->Transform8x8Mode && p_Inp->sp_periodicity /*SP-frames*/) + { + snprintf(errortext, ET_SIZE, "\nThe new 8x8 mode is not implemented for sp-frames."); + error (errortext, 500); + } + + if(p_Inp->Transform8x8Mode && ( p_Inp->ProfileIDCProfileIDC!=FREXT_CAVLC444 )) + { + snprintf(errortext, ET_SIZE, "\nTransform8x8Mode may be used only with ProfileIDC %d to %d.", FREXT_HP, FREXT_Hi444); + error (errortext, 500); + } + + if (p_Inp->DisableIntra4x4 == 1 && p_Inp->DisableIntra16x16 == 1 && p_Inp->EnableIPCM == 0 && p_Inp->Transform8x8Mode == 0) + { + snprintf(errortext, ET_SIZE, "\nAt least one intra prediction mode needs to be enabled."); + error (errortext, 500); + } + + if(p_Inp->ScalingMatrixPresentFlag && ( p_Inp->ProfileIDCProfileIDC!=FREXT_CAVLC444 )) + { + snprintf(errortext, ET_SIZE, "\nScalingMatrixPresentFlag may be used only with ProfileIDC %d to %d.", FREXT_HP, FREXT_Hi444); + error (errortext, 500); + } + + if(p_Inp->yuv_format==YUV422 && ( p_Inp->ProfileIDC < FREXT_Hi422 && p_Inp->ProfileIDC!=FREXT_CAVLC444 )) + { + snprintf(errortext, ET_SIZE, "\nFRExt Profile(YUV Format) Error!\nYUV422 can be used only with ProfileIDC %d or %d\n",FREXT_Hi422, FREXT_Hi444); + error (errortext, 500); + } + if(p_Inp->yuv_format==YUV444 && ( p_Inp->ProfileIDC < FREXT_Hi444 && p_Inp->ProfileIDC!=FREXT_CAVLC444 )) + { + snprintf(errortext, ET_SIZE, "\nFRExt Profile(YUV Format) Error!\nYUV444 can be used only with ProfileIDC %d.\n",FREXT_Hi444); + error (errortext, 500); + } + + if (p_Inp->NumberBFrames && ((p_Inp->BiPredMotionEstimation) && (p_Inp->search_range[0] < p_Inp->BiPredMESearchRange[0]))) + { + snprintf(errortext, ET_SIZE, "\nBiPredMESearchRange must be smaller or equal SearchRange."); + error (errortext, 500); + } +#if (MVC_EXTENSION_ENABLE) + if ( p_Inp->SepViewInterSearch ) + { + if (p_Inp->NumberBFrames && ((p_Inp->BiPredMotionEstimation) && (p_Inp->search_range[1] < p_Inp->BiPredMESearchRange[1]))) + { + snprintf(errortext, ET_SIZE, "\nView1BiPredMESearchRange must be smaller or equal View1SearchRange."); + error (errortext, 500); + } + } +#endif + + if (p_Inp->BiPredMotionEstimation) + { + p_Inp->BiPredMotionEstimation = 0; + for (i = 0 ; i < 4; i++) + p_Inp->BiPredMotionEstimation |= p_Inp->BiPredSearch[i]; + } + else + { + for (i = 0 ; i < 4; i++) + p_Inp->BiPredSearch[i] = 0; + } + + // check consistency + if ( p_Inp->ChromaMEEnable && !(p_Inp->ChromaMCBuffer) ) + { + snprintf(errortext, ET_SIZE, "\nChromaMCBuffer must be set to 1 if ChromaMEEnable is set."); + error (errortext, 500); + } + + if ( p_Inp->ChromaMEEnable && p_Inp->yuv_format == YUV400) + { + fprintf(stdout, "Warning: ChromaMEEnable cannot be used with monochrome color format, disabling ChromaMEEnable.\n"); + p_Inp->ChromaMEEnable = FALSE; + } + + if ( (p_Inp->ChromaMCBuffer == 0) && (( p_Inp->yuv_format == YUV444) && (!p_Inp->separate_colour_plane_flag)) ) + { + fprintf(stdout, "Warning: Enabling ChromaMCBuffer for 4:4:4 combined color coding.\n"); + p_Inp->ChromaMCBuffer = 1; + } + + if (p_Inp->EnableOpenGOP && p_Inp->ReferenceReorder == 2) + { + printf("If OpenGOP is enabled than ReferenceReorder is set to 1. \n"); + } + + if (p_Inp->EnableOpenGOP) + p_Inp->ReferenceReorder = 1; + +#if (MVC_EXTENSION_ENABLE) + if((p_Inp->ProfileIDC==MULTIVIEW_HIGH || p_Inp->ProfileIDC==STEREO_HIGH) && p_Inp->num_of_views != 2) + { + snprintf(errortext, ET_SIZE, "NumberOfViews must be two if ProfileIDC is set to 118 (Multiview High Profile). Otherwise (for a single view) please select a non-multiview profile such as 100."); + error (errortext, 500); + } + +// if (p_Inp->PicInterlace == 2 && p_Inp->MVCInterViewReorder != 0) +// { +// snprintf(errortext, ET_SIZE, "MVCInterViewReorder not supported with Adaptive Frame Field Coding"); +// error (errortext, 500); +// } + + if(p_Inp->MVCInterViewReorder) + { + if ( !is_MVC_profile(p_Inp->ProfileIDC) ) + { + snprintf(errortext, ET_SIZE, "ProfileIDC must be 118, 128, 134, or 135 to use MVCInterViewReorder=1."); + error (errortext, 500); + } + } +#endif + + if (p_Inp->SearchMode[0] != EPZS +#if (MVC_EXTENSION_ENABLE) + && (!p_Inp->SepViewInterSearch || p_Inp->SearchMode[1] != EPZS ) +#endif + ) + p_Inp->EPZSSubPelGrid = 0; + + if (p_Inp->redundant_pic_flag) + { + if (p_Inp->PicInterlace || p_Inp->MbInterlace) + { + snprintf(errortext, ET_SIZE, "Redundant pictures cannot be used with interlaced tools."); + error (errortext, 500); + } + if (p_Inp->RDPictureDecision) + { + snprintf(errortext, ET_SIZE, "Redundant pictures cannot be used with RDPictureDecision."); + error (errortext, 500); + } + if (p_Inp->NumberBFrames) + { + snprintf(errortext, ET_SIZE, "Redundant pictures cannot be used with B frames."); + error (errortext, 500); + } + if (p_Inp->PrimaryGOPLength < (1 << p_Inp->NumRedundantHierarchy)) + { + snprintf(errortext, ET_SIZE, "PrimaryGOPLength must be equal or greater than 2^NumRedundantHierarchy."); + error (errortext, 500); + } + if (p_Inp->num_ref_frames < p_Inp->PrimaryGOPLength) + { + snprintf(errortext, ET_SIZE, "NumberReferenceFrames must be greater than or equal to PrimaryGOPLength."); + error (errortext, 500); + } + } + + if (p_Inp->num_ref_frames == 1 && p_Inp->NumberBFrames) + { + fprintf( stdout, "\nWarning: B slices used but only one reference allocated within reference buffer.\n"); + fprintf( stdout, " Performance may be considerably compromised! \n"); + fprintf( stdout, " 2 or more references recommended for use with B slices.\n"); + } + if ((p_Inp->HierarchicalCoding || p_Inp->BRefPictures) && p_Inp->NumberBFrames) + { + fprintf( stdout, "\nWarning: Hierarchical coding or Referenced B slices used.\n"); + fprintf( stdout, " Make sure that you have allocated enough references\n"); + fprintf( stdout, " in reference buffer to achieve best performance.\n"); + } + + if (p_Inp->FastMDEnable == 0) + { + p_Inp->FastIntraMD = 0; + p_Inp->FastIntra16x16 = 0; + p_Inp->FastIntra4x4 = 0; + p_Inp->FastIntra8x8 = 0; + p_Inp->FastIntraChroma = 0; + } + + // Init RDOQuant + if (p_Inp->UseRDOQuant == 1) + { + if (p_Inp->rdopt == 0) + { + snprintf(errortext, ET_SIZE, "RDO Quantization not supported with low complexity RDO."); + error (errortext, 500); + } + + if (p_Inp->MbInterlace != 0) + { + printf("RDO Quantization currently not supported with MBAFF. Option disabled.\n"); + p_Inp->UseRDOQuant = 0; + p_Inp->RDOQ_QP_Num = 1; + p_Inp->RDOQ_CP_MV = 0; + p_Inp->RDOQ_CP_Mode = 0; + } + else + { + p_Inp->AdaptiveRounding = 0; + printf("AdaptiveRounding is disabled when RDO Quantization is used\n"); + if (p_Inp->RDOQ_QP_Num < 2) + { + p_Inp->RDOQ_CP_MV = 0; + p_Inp->RDOQ_CP_Mode = 0; + } + } + } + else + { + p_Inp->RDOQ_QP_Num = 1; + p_Inp->RDOQ_CP_MV = 0; + p_Inp->RDOQ_CP_Mode = 0; + } + + if(p_Inp->num_slice_groups_minus1 > 0 && (p_Inp->GenerateMultiplePPS ==1 && p_Inp->RDPictureDecision == 1)) + { + printf("Warning: Weighted Prediction may not function correctly for multiple slices\n"); + } + +#if KEEP_B_SAME_LIST + if ( p_Inp->BIdenticalList > 0 && p_Inp->ReferenceReorder != 1 ) + { + snprintf(errortext, ET_SIZE, "Set two lists of B picture identical can only be used when ReferenceReorder is 1"); + error (errortext, 500); + } +#endif + +#if CRA + if ( p_Inp->useCRA == 1 && p_Inp->intra_period == 0 ) + { + printf("Warning: CRA can only be used for IntraPeriod > 0, set CRA to 0\n"); + p_Inp->useCRA = 0; + } + if ( p_Inp->useCRA == 1 && p_Inp->LowDelay == 1 ) + { + snprintf(errortext, ET_SIZE, "CRA cannot be used when LowDelay is 1"); + error (errortext, 500); + } +#endif + +#if HM50_LIKE_MMCO + if ( p_Inp->HM50LikeMMCO == 1 ) + { + printf("Warning: HM50LikeMMCO can only be used for random access case with GOP size equal to 8 and HM-5.0 coding order\n"); + printf(" If it used for other coding structure, the performance may loss\n"); + } + if ( p_Inp->HM50LikeMMCO == 1 && p_Inp->NumberBFrames != 7 ) + { + printf("Warning: HM50LikeMMCO can only be used for random access case with NumberBFrames equal to 7, turn it off\n"); + p_Inp->HM50LikeMMCO = 0; + } + if ( p_Inp->HM50LikeMMCO == 1 && p_Inp->LowDelay == 1 ) + { + printf("Warning: HM50LikeMMCO cannot be used for low delay, turn it off\n"); + p_Inp->HM50LikeMMCO = 0; + } +#endif + +#if LD_REF_SETTING + if ( p_Inp->useF701RefForLD == 1 ) + { + printf("Warning: useF701RefForLD can only be used for low delay case with GOP size equal to 4 and 4 reference frames as HM-5.0 low delay\n"); + printf(" If other GOP size or number of reference frames are used, the performance may be loss with useF701RefForLD\n"); + } + if ( p_Inp->useF701RefForLD == 1 && p_Inp->LowDelay == 0 ) + { + snprintf(errortext, ET_SIZE, "useF701RefForLD can only be used when LowDelay is 1"); + error (errortext, 500); + } +#endif + + profile_check(p_Inp); + + if(!p_Inp->RDPictureDecision) + { + p_Inp->RDPictureMaxPassISlice = 1; + p_Inp->RDPictureMaxPassPSlice = 1; + p_Inp->RDPictureMaxPassBSlice = 1; + p_Inp->RDPictureDeblocking = 0; + p_Inp->RDPictureDirectMode = 0; + p_Inp->RDPictureFrameQPPSlice = 0; + p_Inp->RDPictureFrameQPBSlice = 0; + } +} + +/*! + ************************************************************************ + * \brief + * Set VUI time scale and number of clock ticks given input frame rate + ************************************************************************ +*/ + +static void SetVUIScaleAndTicks(InputParameters *p_Inp, double frame_rate) +{ + double frame_rate_integer = (double)ceil( frame_rate ); + + if ( frame_rate_integer != frame_rate ) + { + // frame rate is a floating-point number + // check whether multiplying it with 1001/1000=1.001 brings it closer to frame_rate_integer + double new_frame_rate = 1.001 * frame_rate; + + if ( dabs( new_frame_rate - frame_rate_integer ) < dabs( frame_rate - frame_rate_integer ) ) + { + p_Inp->VUI.num_units_in_tick = 1001; + p_Inp->VUI.time_scale = (int)floor( frame_rate_integer * (1000 << 1) ); // two ticks per frame + } + else + { + p_Inp->VUI.num_units_in_tick = 1000; + p_Inp->VUI.time_scale = (int)floor( frame_rate * (p_Inp->VUI.num_units_in_tick << 1) + 0.5 ); // two ticks per frame + } + } + else + { + // frame rate is an integer + p_Inp->VUI.num_units_in_tick = 1000; + p_Inp->VUI.time_scale = (int)floor( frame_rate * (p_Inp->VUI.num_units_in_tick << 1) ); // two ticks per frame + } +} + diff --git a/lencod/src/conformance.c b/lencod/src/conformance.c new file mode 100644 index 0000000..52cf1a8 --- /dev/null +++ b/lencod/src/conformance.c @@ -0,0 +1,633 @@ +/*! + ************************************************************************************** + * \file + * conformance.c + * \brief + * Level & Profile related conformance functions + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * - Karsten Suehring + * - Alexis Michael Tourapis + * \note + * + ************************************************************************************** + */ + +#include "global.h" +#include "conformance.h" + +// Max Frame Size Limit +// Level Limit - - - - - - - - - 1b 10 11 12 13 - - - - - - 20 21 22 - - - - - - - +static const unsigned int MaxFs [] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 99, 99, 396, 396, 396, 0, 0, 0, 0, 0, 0, 396, 792, 1620, 0, 0, 0, 0, 0, 0, 0, +// 30 31 32 - - - - - - - 40 41 42 - - - - - - - 50 51 + 1620, 3600, 5120, 0, 0, 0, 0, 0, 0, 0, 8192, 8192, 8704, 0, 0, 0, 0, 0, 0, 0, 22080, 36864 }; +static const unsigned int MinCR [] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, + 2, 4, 4, 0, 0, 0, 0, 0, 0, 0, 4, 2, 2, 0, 0, 0, 0, 0, 0, 0, 2, 2 }; +static const unsigned int MaxBR [] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 64,128, 192, 384, 768, 0, 0, 0, 0, 0, 0,2000,4000,4000, 0, 0, 0, 0, 0, 0, 0, + 10000,14000,20000, 0, 0, 0, 0, 0, 0, 0,20000,50000,50000, 0, 0, 0, 0, 0, 0, 0,135000,240000 }; +static const unsigned int MaxCPB[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0,175,350, 500,1000,2000, 0, 0, 0, 0, 0, 0,2000,4000,4000, 0, 0, 0, 0, 0, 0, 0, + 10000,14000,20000, 0, 0, 0, 0, 0, 0, 0,25000,62500,62500, 0, 0, 0, 0, 0, 0, 0,135000,240000 }; +// Max macroblock processing rate +static const unsigned int MaxMBPS[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1485, 1485, 3000, 6000, 11880, 0, 0, 0, 0, 0, 0, 11880, 19800, 20250, 0, 0, 0, 0, 0, 0, 0, + 40500, 108000, 216000, 0, 0, 0, 0, 0, 0, 0, 245760, 245760, 522240, 0, 0, 0, 0, 0, 0, 0, 589824, 983040 }; +// Vertical MV Limits (integer/halfpel/quarterpel) +// Currently only Integer Pel restrictions are used, +// since the way values are specified +// (i.e. mvlowbound = (levelmvlowbound + 1) and the way +// Subpel ME is performed, subpel will always be within range. +static const int LEVELVMVLIMIT[17][6] = +{ + { -63, 63, -128, 127, -256, 255}, + { -63, 63, -128, 127, -256, 255}, + { -127, 127, -256, 255, -512, 511}, + { -127, 127, -256, 255, -512, 511}, + { -127, 127, -256, 255, -512, 511}, + { -127, 127, -256, 255, -512, 511}, + { -255, 255, -512, 511, -1024, 1023}, + { -255, 255, -512, 511, -1024, 1023}, + { -255, 255, -512, 511, -1024, 1023}, + { -511, 511, -1024, 1023, -2048, 2047}, + { -511, 511, -1024, 1023, -2048, 2047}, + { -511, 511, -1024, 1023, -2048, 2047}, + { -511, 511, -1024, 1023, -2048, 2047}, + { -511, 511, -1024, 1023, -2048, 2047}, + { -511, 511, -1024, 1023, -2048, 2047}, + { -511, 511, -1024, 1023, -2048, 2047}, + { -511, 511, -1024, 1023, -2048, 2047} +}; + +const int LEVELHMVLIMIT[6] = { -2047, 2047, -4096, 4095, -8192, 8191}; + +/*! + *********************************************************************** + * \brief + * Get maximum frame size (in MBs) supported given a level + *********************************************************************** + */ +unsigned int getMaxFs (unsigned int levelIdc) +{ + unsigned int ret; + + if ( (levelIdc < 9) || (levelIdc > 51)) + error ("getMaxFs: Unknown LevelIdc", 500); + + // in Baseline, Main and Extended: Level 1b is specified with LevelIdc==11 and constrained_set3_flag == 1 + + ret = MaxFs[levelIdc]; + + if ( 0 == ret ) + error ("getMaxFs: Unknown LevelIdc", 500); + + return ret; +} + +/*! + *********************************************************************** + * \brief + * Get maximum processing rate (in MB/s) supported given a level + *********************************************************************** + */ +unsigned int getMaxMBPS (unsigned int levelIdc) +{ + unsigned int ret; + + if ( (levelIdc < 9) || (levelIdc > 51)) + error ("getMaxMBPS: Unknown LevelIdc", 500); + + // in Baseline, Main and Extended: Level 1b is specified with LevelIdc==11 and constrained_set3_flag == 1 + + ret = MaxMBPS[levelIdc]; + + if ( 0 == ret ) + error ("getMaxMBPS: Unknown LevelIdc", 500); + + return ret; +} + +/*! + *********************************************************************** + * \brief + * Get minimum compression ratio supported given a level + *********************************************************************** + */ +unsigned int getMinCR (unsigned int levelIdc) +{ + unsigned int ret; + + if ( (levelIdc < 9) || (levelIdc > 51)) + error ("getMinCR: Unknown LevelIdc", 500); + + // in Baseline, Main and Extended: Level 1b is specified with LevelIdc==11 and constrained_set3_flag == 1 + + ret = MinCR[levelIdc]; + + if ( 0 == ret ) + error ("getMinCR: Unknown LevelIdc", 500); + + return ret; +} + +/*! + *********************************************************************** + * \brief + * Get maximum bit rate (in bits/s) supported given a level + *********************************************************************** + */ +unsigned int getMaxBR (unsigned int levelIdc) +{ + unsigned int ret; + + if ( (levelIdc < 9) || (levelIdc > 51)) + error ("getMaxBR: Unknown LevelIdc", 500); + + // in Baseline, Main and Extended: Level 1b is specified with LevelIdc==11 and constrained_set3_flag == 1 + + ret = MaxBR[levelIdc]; + + if ( 0 == ret ) + error ("getMaxBR: Unknown LevelIdc", 500); + + return ret; +} + +/*! + *********************************************************************** + * \brief + * Get maximum coded buffer size (in bits) supported given a level + *********************************************************************** + */ +unsigned int getMaxCPB (unsigned int levelIdc) +{ + unsigned int ret; + + if ( (levelIdc < 9) || (levelIdc > 51)) + error ("getMaxCPB: Unknown LevelIdc", 500); + + // in Baseline, Main and Extended: Level 1b is specified with LevelIdc==11 and constrained_set3_flag == 1 + + ret = MaxCPB[levelIdc]; + + if ( 0 == ret ) + error ("getMaxCPB: Unknown LevelIdc", 500); + + return ret; +} + +/*! + *********************************************************************** + * \brief + * Check Profile conformance + *********************************************************************** + */ +void profile_check(InputParameters *p_Inp) +{ + if((p_Inp->ProfileIDC != BASELINE ) && + (p_Inp->ProfileIDC != MAIN ) && + (p_Inp->ProfileIDC != EXTENDED ) && + (p_Inp->ProfileIDC != FREXT_HP ) && + (p_Inp->ProfileIDC != FREXT_Hi10P ) && +#if (MVC_EXTENSION_ENABLE) + (p_Inp->ProfileIDC != MULTIVIEW_HIGH ) && // MVC multiview high profile + (p_Inp->ProfileIDC != STEREO_HIGH ) && // MVC stereo high profile +#endif + (p_Inp->ProfileIDC != FREXT_Hi422 ) && + (p_Inp->ProfileIDC != FREXT_Hi444 ) && + (p_Inp->ProfileIDC != FREXT_CAVLC444 )) + { +#if (MVC_EXTENSION_ENABLE) + snprintf(errortext, ET_SIZE, "Profile must be in\n\n 66 (Baseline),\n 77 (Main),\n 88 (Extended),\n 100 (High),\n 110 (High 10 or High 10 Intra)\n" + " 122 (High 4:2:2 or High 4:2:2 Intra),\n 244 (High 4:4:4 predictive or High 4:4:4 Intra),\n 44 (CAVLC 4:4:4 Intra)\n 118 (MVC profile)\n"); +#else + snprintf(errortext, ET_SIZE, "Profile must be in\n\n 66 (Baseline),\n 77 (Main),\n 88 (Extended),\n 100 (High),\n 110 (High 10 or High 10 Intra)\n" + " 122 (High 4:2:2 or High 4:2:2 Intra),\n 244 (High 4:4:4 predictive or High 4:4:4 Intra),\n 44 (CAVLC 4:4:4 Intra)\n"); +#endif + error (errortext, 500); + } + + if ((p_Inp->partition_mode) && (p_Inp->symbol_mode==CABAC)) + { + snprintf(errortext, ET_SIZE, "Data partitioning and CABAC is not supported in any profile."); + error (errortext, 500); + } + + if (p_Inp->redundant_pic_flag) + { + if (p_Inp->ProfileIDC != BASELINE) + { + snprintf(errortext, ET_SIZE, "Redundant pictures are only allowed in Baseline profile (ProfileIDC = 66)."); + error (errortext, 500); + } + } + + if ((p_Inp->partition_mode) && (p_Inp->ProfileIDC!=EXTENDED)) + { + snprintf(errortext, ET_SIZE, "Data partitioning is only allowed in Extended profile (ProfileIDC = 88)."); + error (errortext, 500); + } + + if (p_Inp->ChromaIntraDisable && p_Inp->FastCrIntraDecision) + { + fprintf( stdout, "\n Warning: ChromaIntraDisable and FastCrIntraDecision cannot be combined together.\n Using only Chroma Intra DC mode.\n"); + p_Inp->FastCrIntraDecision=0; + } + + if ((p_Inp->sp_periodicity) && (p_Inp->ProfileIDC != EXTENDED )) + { + snprintf(errortext, ET_SIZE, "SP pictures are only allowed in Extended profile (ProfileIDC = 88)."); + error (errortext, 500); + } + + // baseline + if (p_Inp->ProfileIDC == BASELINE ) + { + if ((p_Inp->NumberBFrames || p_Inp->BRefPictures==2) && p_Inp->PReplaceBSlice == 0) + { + snprintf(errortext, ET_SIZE, "B slices are not allowed in Baseline profile (ProfileIDC = 66)."); + error (errortext, 500); + } + if (p_Inp->WeightedPrediction) + { + snprintf(errortext, ET_SIZE, "Weighted prediction is not allowed in Baseline profile (ProfileIDC = 66)."); + error (errortext, 500); + } + if (p_Inp->WeightedBiprediction) + { + snprintf(errortext, ET_SIZE, "Weighted prediction is not allowed in Baseline profile (ProfileIDC = 66)."); + error (errortext, 500); + } + if (p_Inp->symbol_mode == CABAC) + { + snprintf(errortext, ET_SIZE, "CABAC is not allowed in Baseline profile (ProfileIDC = 66)."); + error (errortext, 500); + } + if ((p_Inp->PicInterlace) ||(p_Inp->MbInterlace)) + { + snprintf(errortext, ET_SIZE, "Interlace tools are not allowed in Baseline profile (ProfileIDC = 66)."); + error (errortext, 500); + } + if (p_Inp->GenerateMultiplePPS != 0) + { + snprintf(errortext, ET_SIZE, "GenerateMultiplePPS is not supported for Baseline profile because it requires enabling Weighted prediction.\n"); + error (errortext, 400); + } + } + + // main + if (p_Inp->ProfileIDC == MAIN ) + { + if (p_Inp->num_slice_groups_minus1) + { + snprintf(errortext, ET_SIZE, "num_slice_groups_minus1>0 (FMO) is not allowed in Main profile (ProfileIDC = 77)."); + error (errortext, 500); + } + } + + // extended + if (p_Inp->ProfileIDC == EXTENDED ) + { + if (!p_Inp->directInferenceFlag) + { + snprintf(errortext, ET_SIZE, "direct_8x8_inference flag must be equal to 1 in Extended profile (ProfileIDC = 88)."); + error (errortext, 500); + } + + if (p_Inp->symbol_mode == CABAC) + { + snprintf(errortext, ET_SIZE, "CABAC is not allowed in Extended profile (ProfileIDC = 88)."); + error (errortext, 500); + } + } + + //FRExt + if ( p_Inp->separate_colour_plane_flag ) + { + if( p_Inp->yuv_format!=3 ) + { + fprintf( stdout, "\nWarning: SeparateColourPlane has only effect in 4:4:4 chroma mode (YUVFormat=3),\n disabling SeparateColourPlane."); + p_Inp->separate_colour_plane_flag = 0; + } + + if ( p_Inp->ChromaMEEnable ) + { + snprintf(errortext, ET_SIZE, "\nChromaMEEnable is not allowed when SeparateColourPlane is enabled."); + error (errortext, 500); + } + } + + // CAVLC 4:4:4 Intra + if ( p_Inp->ProfileIDC == FREXT_CAVLC444 ) + { + if ( p_Inp->symbol_mode != CAVLC ) + { + snprintf(errortext, ET_SIZE, "\nCABAC is not allowed in CAVLC 4:4:4 Intra profile (ProfileIDC = 44)."); + error (errortext, 500); + } + if ( !p_Inp->IntraProfile ) + { + fprintf (stdout, "\nWarning: ProfileIDC equal to 44 implies an Intra only profile, setting IntraProfile = 1."); + p_Inp->IntraProfile = 1; + } + } + + // Intra only profiles + if (p_Inp->IntraProfile && ( p_Inp->ProfileIDCProfileIDC!=FREXT_CAVLC444 )) + { + snprintf(errortext, ET_SIZE, "\nIntraProfile is allowed only with ProfileIDC %d to %d.", FREXT_HP, FREXT_Hi444); + error (errortext, 500); + } + + if (p_Inp->IntraProfile && !p_Inp->idr_period) + { + snprintf(errortext, ET_SIZE, "\nIntraProfile requires IDRPeriod >= 1."); + error (errortext, 500); + } + + if (p_Inp->IntraProfile && p_Inp->intra_period != 1) + { + snprintf(errortext, ET_SIZE, "\nIntraProfile requires IntraPeriod equal 1."); + error (errortext, 500); + } + + if (p_Inp->IntraProfile && p_Inp->num_ref_frames) + { + fprintf( stdout, "\nWarning: Setting NumberReferenceFrames to 0 in IntraProfile."); + p_Inp->num_ref_frames = 0; + } + + if (p_Inp->IntraProfile == 0 && p_Inp->num_ref_frames == 0) + { + snprintf(errortext, ET_SIZE, "\nProfiles other than IntraProfile require NumberReferenceFrames > 0."); + error (errortext, 500); + } +} + +/*! + *********************************************************************** + * \brief + * Check if Level constraints are satisfied + *********************************************************************** + */ +void level_check(VideoParameters *p_Vid, InputParameters *p_Inp) +{ + unsigned int PicSizeInMbs = ( (p_Inp->output.width[0] + p_Vid->auto_crop_right) * (p_Inp->output.height[0] + p_Vid->auto_crop_bottom) ) >> 8; + unsigned int MBProcessingRate = (unsigned int) (PicSizeInMbs * p_Inp->output.frame_rate + 0.5); + int cpbBrFactor = ( p_Inp->ProfileIDC >= FREXT_HP ) ? 1500 : 1200; + + if ( (p_Inp->LevelIDC>=30) && (p_Inp->directInferenceFlag==0)) + { + fprintf( stdout, "\nWarning: LevelIDC 3.0 and above require direct_8x8_inference to be set to 1. Please check your settings.\n"); + p_Inp->directInferenceFlag=1; + } + if ( ((p_Inp->LevelIDC<21) || (p_Inp->LevelIDC>41)) && (p_Inp->PicInterlace > 0 || p_Inp->MbInterlace > 0) ) + { + snprintf(errortext, ET_SIZE, "\nInterlace modes only supported for LevelIDC in the range of 21 and 41. Please check your settings.\n"); + error (errortext, 500); + } + + if ( PicSizeInMbs > getMaxFs(p_Inp->LevelIDC) ) + { + snprintf(errortext, ET_SIZE, "\nPicSizeInMbs exceeds maximum allowed size at specified LevelIdc %.1f\n", (float) p_Inp->LevelIDC / 10.0); + error (errortext, 500); + } + + if (p_Inp->IntraProfile && (PicSizeInMbs > 1620) && p_Inp->slice_mode != 1) + { + error ("\nIntraProfile with PicSizeInMbs > 1620 requires SliceMode equal 1.", 500); + } + + if (p_Inp->IntraProfile && (PicSizeInMbs > 1620) && ((unsigned int)p_Inp->slice_argument > ( getMaxFs(p_Inp->LevelIDC) >> 2 ) ) ) + { + //when PicSizeInMbs is greater than 1620, the number of macroblocks in any coded slice shall not exceed MaxFS / 4 + snprintf(errortext, ET_SIZE, "\nIntraProfile requires SliceArgument smaller or equal to 1/4 MaxFs at specified LevelIdc %d.", p_Inp->LevelIDC); + error (errortext, 500); + } + + if ( MBProcessingRate > getMaxMBPS(p_Inp->LevelIDC) ) + { + snprintf(errortext, ET_SIZE, "\nMB Processing Rate (%d) exceeds maximum allowed processing rate (%d) at specified LevelIdc %.1f\n", + MBProcessingRate, getMaxMBPS(p_Inp->LevelIDC), (float) p_Inp->LevelIDC / 10.0); + error (errortext, 500); + } + + if ( p_Inp->bit_rate > (int)(cpbBrFactor * getMaxBR(p_Inp->LevelIDC)) ) + { + snprintf(errortext, ET_SIZE, "\nBit Rate (%d) exceeds maximum allowed bit rate (%d) at specified LevelIdc %.1f for NAL HRD\n", + p_Inp->bit_rate, cpbBrFactor * getMaxBR(p_Inp->LevelIDC), (float) p_Inp->LevelIDC / 10.0); + error (errortext, 500); + } +} + +/*! + *********************************************************************** + * \brief + * Update Motion Vector Limits + *********************************************************************** + */ +void update_mv_limits(VideoParameters *p_Vid, byte is_field) +{ + InputParameters *p_Inp = p_Vid->p_Inp; + memcpy(p_Vid->MaxVmvR, LEVELVMVLIMIT[p_Vid->LevelIndex], 6 * sizeof(int)); + memcpy(p_Vid->MaxHmvR, LEVELHMVLIMIT, 6 * sizeof(int)); + if (is_field) + { + int i; + for (i = 0; i < 6; i++) + p_Vid->MaxVmvR[i] = rshift_rnd(p_Vid->MaxVmvR[i], 1); + } + if (p_Inp->UseMVLimits) + { + p_Vid->MaxVmvR[0] = imax(p_Vid->MaxVmvR[0], -(p_Inp->SetMVYLimit)); + p_Vid->MaxVmvR[1] = imin(p_Vid->MaxVmvR[1], (p_Inp->SetMVYLimit)); + p_Vid->MaxVmvR[2] = imax(p_Vid->MaxVmvR[2], -(p_Inp->SetMVYLimit << 1)); + p_Vid->MaxVmvR[3] = imin(p_Vid->MaxVmvR[3], (p_Inp->SetMVYLimit << 1)); + p_Vid->MaxVmvR[4] = imax(p_Vid->MaxVmvR[4], -(p_Inp->SetMVYLimit << 2)); + p_Vid->MaxVmvR[5] = imin(p_Vid->MaxVmvR[5], (p_Inp->SetMVYLimit << 2)); + + p_Vid->MaxHmvR[0] = imax(p_Vid->MaxHmvR[0], -(p_Inp->SetMVXLimit)); + p_Vid->MaxHmvR[1] = imin(p_Vid->MaxHmvR[1], (p_Inp->SetMVXLimit)); + p_Vid->MaxHmvR[2] = imax(p_Vid->MaxHmvR[2], -(p_Inp->SetMVXLimit << 1)); + p_Vid->MaxHmvR[3] = imin(p_Vid->MaxHmvR[3], (p_Inp->SetMVXLimit << 1)); + p_Vid->MaxHmvR[4] = imax(p_Vid->MaxHmvR[4], -(p_Inp->SetMVXLimit << 2)); + p_Vid->MaxHmvR[5] = imin(p_Vid->MaxHmvR[5], (p_Inp->SetMVXLimit << 2)); + } +} + + +/*! + *********************************************************************** + * \brief + * Clip motion vector range given encoding level + *********************************************************************** + */ +void clip_mv_range(VideoParameters *p_Vid, int search_range, MotionVector *mv, int res) +{ + res <<= 1; + + mv->mv_x = (short) iClip3( p_Vid->MaxHmvR[0 + res] + search_range, p_Vid->MaxHmvR[1 + res] - search_range, mv->mv_x); + mv->mv_y = (short) iClip3( p_Vid->MaxVmvR[0 + res] + search_range, p_Vid->MaxVmvR[1 + res] - search_range, mv->mv_y); +} + +/*! + *********************************************************************** + * \brief + * Clip motion vector range given encoding level + *********************************************************************** + */ +void test_clip_mvs(VideoParameters *p_Vid, MotionVector *mv, Boolean write_mb) +{ + if ((mv->mv_x < p_Vid->MaxHmvR[4]) || (mv->mv_x > p_Vid->MaxHmvR[5]) || (mv->mv_y < p_Vid->MaxVmvR[4]) || (mv->mv_y > p_Vid->MaxVmvR[5])) + { + if (write_mb == TRUE) + printf("Warning MVs (%d %d) were out of range x(%d %d) y(%d %d). Clipping mvs before writing\n", mv->mv_x, mv->mv_y, p_Vid->MaxHmvR[4], p_Vid->MaxHmvR[5], p_Vid->MaxVmvR[4], p_Vid->MaxVmvR[5]); + mv->mv_x = (short) iClip3( p_Vid->MaxHmvR[4], p_Vid->MaxHmvR[5], mv->mv_x); + mv->mv_y = (short) iClip3( p_Vid->MaxVmvR[4], p_Vid->MaxVmvR[5], mv->mv_y); + } +} + +/*! + *********************************************************************** + * \brief + * Clip motion vector range given encoding level + *********************************************************************** + */ +int out_of_bounds_mvs(VideoParameters *p_Vid, const MotionVector *mv) +{ + return ((mv->mv_x < p_Vid->MaxHmvR[4]) || (mv->mv_x > p_Vid->MaxHmvR[5]) || (mv->mv_y < p_Vid->MaxVmvR[4]) || (mv->mv_y > p_Vid->MaxVmvR[5])); +} + +int InvalidWeightsForBiPrediction(Slice *currSlice, Block8x8Info* b8x8info, int mode) +{ + seq_parameter_set_rbsp_t *active_sps = currSlice->active_sps; + int cur_blk, cur_comp; + int best8x8l0ref, best8x8l1ref; + int weight_sum = 0; + int invalid_mode = 0; + short *wbp0, *wbp1; + for (cur_blk = 0; cur_blk < 4; cur_blk ++) + { + if (b8x8info->best[mode][cur_blk].pdir == 2) + { + best8x8l0ref = (int) b8x8info->best[mode][cur_blk].ref[LIST_0]; + best8x8l1ref = (int) b8x8info->best[mode][cur_blk].ref[LIST_1]; + wbp0 = &currSlice->wbp_weight[LIST_0][best8x8l0ref][best8x8l1ref][0]; + wbp1 = &currSlice->wbp_weight[LIST_1][best8x8l0ref][best8x8l1ref][0]; + + for (cur_comp = 0; cur_comp < (active_sps->chroma_format_idc == YUV400 ? 1 : 3) ; cur_comp ++) + { + weight_sum = *wbp0++ + *wbp1++; + + if (weight_sum < -128 || weight_sum > 127) + { + invalid_mode = 1; + break; + } + } + if (invalid_mode == 1) + break; + } + } + return invalid_mode; +} + +int InvalidMotionVectors(VideoParameters *p_Vid, Slice *currSlice, Block8x8Info* b8x8info, int mode) +{ + int cur_blk; + int l0ref, l1ref; + int invalid_mode = 0; + int i, j; + + if (mode > P8x8) + return invalid_mode; + + // Brute force method. Note that this ignores currently subpartitions in 8x8 modes + for (cur_blk = 0; cur_blk < 4; cur_blk ++) + { + i = (cur_blk & 0x01) << 1; + j = (cur_blk >> 1) << 1; + switch (b8x8info->best[mode][cur_blk].pdir) + { + case 0: + l0ref = (int) b8x8info->best[mode][cur_blk].ref[LIST_0]; + if (out_of_bounds_mvs(p_Vid, &currSlice->all_mv [LIST_0][l0ref][mode][j][i])) + { + invalid_mode = 1; + return invalid_mode; + } + break; + case 1: + l1ref = (int) b8x8info->best[mode][cur_blk].ref[LIST_1]; + if (out_of_bounds_mvs(p_Vid, &currSlice->all_mv [LIST_1][l1ref][mode][j][i])) + { + invalid_mode = 1; + return invalid_mode; + } + break; + case 2: + l0ref = (int) b8x8info->best[mode][cur_blk].ref[LIST_0]; + l1ref = (int) b8x8info->best[mode][cur_blk].ref[LIST_1]; + if (out_of_bounds_mvs(p_Vid, &currSlice->all_mv [LIST_0][l0ref][mode][j][i])) + { + invalid_mode = 1; + return invalid_mode; + } + if (out_of_bounds_mvs(p_Vid, &currSlice->all_mv [LIST_1][l1ref][mode][j][i])) + { + invalid_mode = 1; + return invalid_mode; + } + break; + default: + break; + } + } + + return invalid_mode; +} + +Boolean CheckPredictionParams(Macroblock *currMB, Block8x8Info *b8x8info, int mode) +{ + Slice *currSlice = currMB->p_Slice; + // check if all sub-macroblock partitions can be used with 8x8 transform + if (mode == P8x8 && currMB->luma_transform_size_8x8_flag == TRUE) + { + int i; + + for (i = 0; i < 4; i++) + { + if (b8x8info->best[P8x8][i].mode != 4 && b8x8info->best[P8x8][i].mode != 0) + { + return FALSE; + } + } + } + + if (InvalidMotionVectors(currSlice->p_Vid, currSlice, b8x8info, mode)) + return FALSE; + + if (currSlice->slice_type == B_SLICE) + { + // check if weights are in valid range for biprediction. + if (currSlice->weighted_prediction == 1 && mode < P8x8) + { + if (InvalidWeightsForBiPrediction(currSlice, b8x8info, mode)) + return FALSE; + } + + // Check if direct mode can be utilized for this partition + if (mode==0) + { + int i, j; + for (j = currMB->block_y; j < currMB->block_y + 4;j++) + { + for (i = currMB->block_x; i < currMB->block_x + 4;i++) + { + if (currSlice->direct_pdir[j][i] < 0) // direct_pdir should be moved and become part of p_Vid parameters + return FALSE; + } + } + } + + } + + return TRUE; +} diff --git a/lencod/src/context_ini.c b/lencod/src/context_ini.c new file mode 100644 index 0000000..7d43e73 --- /dev/null +++ b/lencod/src/context_ini.c @@ -0,0 +1,372 @@ + +/*! + ************************************************************************************* + * \file context_ini.c + * + * \brief + * CABAC context initializations + * + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * - Detlev Marpe + * - Heiko Schwarz + ************************************************************************************** + */ + +#define CONTEXT_INI_C + +#include + +#include "global.h" + +#include "ctx_tables.h" +#include "biariencode.h" +#include "memalloc.h" + +#define DEFAULT_CTX_MODEL 0 +#define RELIABLE_COUNT 32.0 +#define FRAME_TYPES 4 +#define FIXED 0 + +// These essentially are constants +static const double probabilities[64] = +{ + 0.500000, 0.474609, 0.450507, 0.427629, 0.405912, 0.385299, 0.365732, 0.347159, + 0.329530, 0.312795, 0.296911, 0.281833, 0.267520, 0.253935, 0.241039, 0.228799, + 0.217180, 0.206151, 0.195682, 0.185744, 0.176312, 0.167358, 0.158859, 0.150792, + 0.143134, 0.135866, 0.128966, 0.122417, 0.116200, 0.110299, 0.104698, 0.099381, + 0.094334, 0.089543, 0.084996, 0.080680, 0.076583, 0.072694, 0.069002, 0.065498, + 0.062172, 0.059014, 0.056018, 0.053173, 0.050473, 0.047909, 0.045476, 0.043167, + 0.040975, 0.038894, 0.036919, 0.035044, 0.033264, 0.031575, 0.029972, 0.028450, + 0.027005, 0.025633, 0.024332, 0.023096, 0.021923, 0.020810, 0.019753, 0.018750 +}; + +void create_context_memory (VideoParameters *p_Vid, InputParameters *p_Inp) +{ + int k; + int num_mb = p_Vid->FrameSizeInMbs; // number of macroblocks for frame + double log2 = log(2.0); + + p_Vid->num_mb_per_slice = (p_Inp->slice_mode == 1 ? p_Inp->slice_argument : num_mb); + p_Vid->number_of_slices = (num_mb + p_Vid->num_mb_per_slice - 1) / p_Vid->num_mb_per_slice; + get_mem3Dint(&p_Vid->initialized, 3, FRAME_TYPES, p_Vid->number_of_slices); + //===== set all context sets as "uninitialized" ===== + memset(&p_Vid->initialized[0][0][0], 0, 3 * FRAME_TYPES * p_Vid->number_of_slices * sizeof(int)); + get_mem3Dint(&p_Vid->modelNumber, 3, FRAME_TYPES, p_Vid->number_of_slices); + + //----- init tables ----- + for( k=0; k<64; k++ ) + { + p_Vid->probability[k + 64] = probabilities[k]; + p_Vid->probability[k] = 1.0 - probabilities[63 - k]; + p_Vid->entropy [k] = log( p_Vid->probability[ k] ) / log2; + p_Vid->entropy[127-k] = log( probabilities[63 - k] ) / log2; + p_Vid->enorm [k] = p_Vid->entropy[k] - p_Vid->entropy[127-k]; + p_Vid->enorm[127 - k] = -p_Vid->enorm[k]; + } +} + +void free_context_memory (VideoParameters *p_Vid) +{ + free_mem3Dint(p_Vid->initialized); + free_mem3Dint(p_Vid->modelNumber); +} + +#define BIARI_CTX_INIT2(qp, ii,jj,ctx,tab) \ +{ \ + for (i=0; ip_Vid; + InputParameters *p_Inp = currSlice->p_Inp; + + int frame_field = p_Vid->field_picture; + int img_type = currSlice->slice_type; + int ctx_number = currSlice->start_mb_nr / p_Vid->num_mb_per_slice; + + if(img_type == I_SLICE) + { + currSlice->model_number=DEFAULT_CTX_MODEL; + return; + } + + if(p_Inp->context_init_method==FIXED) + { + currSlice->model_number = p_Inp->model_number; + return; + } + + if (p_Vid->initialized [frame_field][img_type][ctx_number]) + { + currSlice->model_number = p_Vid->modelNumber[frame_field][img_type][ctx_number]; + } + else if (ctx_number && p_Vid->initialized[frame_field][img_type][ctx_number-1]) + { + currSlice->model_number = p_Vid->modelNumber[frame_field][img_type][ctx_number-1]; + } + else + { + currSlice->model_number = DEFAULT_CTX_MODEL; + } +} + +void init_contexts (Slice *currSlice) +{ + MotionInfoContexts* mc = currSlice->mot_ctx; + TextureInfoContexts* tc = currSlice->tex_ctx; + int model_number = currSlice->model_number; + int qp = imax(0, currSlice->qp); + int i, j; + + if ((currSlice->slice_type == I_SLICE) || (currSlice->slice_type == SI_SLICE)) + { + //--- motion coding contexts --- + BIARI_CTX_INIT2 (qp, 3, NUM_MB_TYPE_CTX, mc->mb_type_contexts, INIT_MB_TYPE_I[model_number]); + + //--- texture coding contexts --- + binary_context_init1 (qp, NUM_TRANSFORM_SIZE_CTX, tc->transform_size_contexts, INIT_TRANSFORM_SIZE_I[model_number][0]); + binary_context_init1 (qp, NUM_IPR_CTX, tc->ipr_contexts, INIT_IPR_I[model_number][0]); + binary_context_init1 (qp, NUM_CIPR_CTX, tc->cipr_contexts, INIT_CIPR_I[model_number][0]); + BIARI_CTX_INIT2 (qp, 3, NUM_CBP_CTX, tc->cbp_contexts, INIT_CBP_I[model_number]); + BIARI_CTX_INIT2 (qp, NUM_BLOCK_TYPES, NUM_BCBP_CTX, tc->bcbp_contexts, INIT_BCBP_I[model_number]); + binary_context_init1 (qp, NUM_DELTA_QP_CTX, tc->delta_qp_contexts,INIT_DELTA_QP_I[model_number][0]); + BIARI_CTX_INIT2 (qp, NUM_BLOCK_TYPES, NUM_MAP_CTX, tc->map_contexts[0], INIT_MAP_I[model_number]); + BIARI_CTX_INIT2 (qp, NUM_BLOCK_TYPES, NUM_LAST_CTX, tc->last_contexts[0], INIT_LAST_I[model_number]); + BIARI_CTX_INIT2 (qp, NUM_BLOCK_TYPES, NUM_ONE_CTX, tc->one_contexts, INIT_ONE_I[model_number]); + BIARI_CTX_INIT2 (qp, NUM_BLOCK_TYPES, NUM_ABS_CTX, tc->abs_contexts, INIT_ABS_I[model_number]); +#if ENABLE_FIELD_CTX + binary_context_init1 (qp, NUM_MB_AFF_CTX, tc->mb_aff_contexts, INIT_MB_AFF_I[model_number][0]); + BIARI_CTX_INIT2 (qp, NUM_BLOCK_TYPES, NUM_MAP_CTX, tc->map_contexts[1], INIT_FLD_MAP_I[model_number]); + BIARI_CTX_INIT2 (qp, NUM_BLOCK_TYPES, NUM_LAST_CTX, tc->last_contexts[1], INIT_FLD_LAST_I[model_number]); +#endif + } + else + { + //--- motion coding contexts --- + BIARI_CTX_INIT2 (qp, 3, NUM_MB_TYPE_CTX, mc->mb_type_contexts, INIT_MB_TYPE_P[model_number]); + BIARI_CTX_INIT2 (qp, 2, NUM_B8_TYPE_CTX, mc->b8_type_contexts, INIT_B8_TYPE_P[model_number]); + BIARI_CTX_INIT2 (qp, 2, NUM_MV_RES_CTX, mc->mv_res_contexts, INIT_MV_RES_P[model_number]); + BIARI_CTX_INIT2 (qp, 2, NUM_REF_NO_CTX, mc->ref_no_contexts, INIT_REF_NO_P[model_number]); + + //--- texture coding contexts --- + binary_context_init1(qp, NUM_TRANSFORM_SIZE_CTX, tc->transform_size_contexts, INIT_TRANSFORM_SIZE_P[model_number][0]); + binary_context_init1(qp, NUM_IPR_CTX, tc->ipr_contexts, INIT_IPR_P[model_number][0]); + binary_context_init1(qp, NUM_CIPR_CTX, tc->cipr_contexts, INIT_CIPR_P[model_number][0]); + BIARI_CTX_INIT2 (qp, 3, NUM_CBP_CTX, tc->cbp_contexts, INIT_CBP_P[model_number]); + BIARI_CTX_INIT2 (qp, NUM_BLOCK_TYPES, NUM_BCBP_CTX, tc->bcbp_contexts, INIT_BCBP_P[model_number]); + binary_context_init1 (qp, NUM_DELTA_QP_CTX, tc->delta_qp_contexts,INIT_DELTA_QP_P[model_number][0]); + BIARI_CTX_INIT2 (qp, NUM_BLOCK_TYPES, NUM_MAP_CTX, tc->map_contexts[0], INIT_MAP_P[model_number]); + BIARI_CTX_INIT2 (qp, NUM_BLOCK_TYPES, NUM_LAST_CTX, tc->last_contexts[0], INIT_LAST_P[model_number]); + BIARI_CTX_INIT2 (qp, NUM_BLOCK_TYPES, NUM_ONE_CTX, tc->one_contexts, INIT_ONE_P[model_number]); + BIARI_CTX_INIT2 (qp, NUM_BLOCK_TYPES, NUM_ABS_CTX, tc->abs_contexts, INIT_ABS_P[model_number]); +#if ENABLE_FIELD_CTX + binary_context_init1 (qp, NUM_MB_AFF_CTX, tc->mb_aff_contexts, INIT_MB_AFF_P[model_number][0]); + BIARI_CTX_INIT2 (qp, NUM_BLOCK_TYPES, NUM_MAP_CTX, tc->map_contexts[1], INIT_FLD_MAP_P[model_number]); + BIARI_CTX_INIT2 (qp, NUM_BLOCK_TYPES, NUM_LAST_CTX, tc->last_contexts[1], INIT_FLD_LAST_P[model_number]); +#endif + } +} + + +double XRate (VideoParameters *p_Vid, BiContextTypePtr ctx, const char* model) +{ + int ctx_state, mod_state; + double weight, xr = 0.0; + int qp = imax(0, p_Vid->qp); + + weight = dmin (1.0, (double)ctx->count/(double)RELIABLE_COUNT); + + mod_state = ((model[0]*qp)>>4)+model[1]; + mod_state = iClip3(0, 127, mod_state); + ctx_state = (ctx->MPS ? 64 + ctx->state : 63 - ctx->state); + + xr -= weight * (p_Vid->probability[ctx_state] * p_Vid->enorm[mod_state] + p_Vid->entropy[127 - mod_state]); + + return xr; +} + +/* +static inline void add_xrate2(VideoParameters *p_Vid, int ii, int jj, BiContextType **ctx, const char ***tab, double *xr) +{ + int i, j; + for (i=0; ip_Vid; + int model, j, i; + double xr, min_xr = 1e30; + + if (currSlice->slice_type == I_SLICE) + { + for (model = 0; model < NUM_CTX_MODELS_I; model++) + { + xr = 0.0; + //--- motion coding contexts --- + ADD_XRATE2 (p_Vid, 3, NUM_MB_TYPE_CTX, mc->mb_type_contexts, INIT_MB_TYPE_I [model]); + + //--- texture coding contexts --- + ADD_XRATE1 (p_Vid, NUM_TRANSFORM_SIZE_CTX, tc->transform_size_contexts, INIT_TRANSFORM_SIZE_I[model][0]); + ADD_XRATE1 (p_Vid, NUM_IPR_CTX, tc->ipr_contexts, INIT_IPR_I [model][0]); + ADD_XRATE1 (p_Vid, NUM_CIPR_CTX, tc->cipr_contexts, INIT_CIPR_I[model][0]); + ADD_XRATE2 (p_Vid, 3, NUM_CBP_CTX, tc->cbp_contexts, INIT_CBP_I [model]); + ADD_XRATE2 (p_Vid, NUM_BLOCK_TYPES, NUM_BCBP_CTX, tc->bcbp_contexts, INIT_BCBP_I[model]); + ADD_XRATE1 (p_Vid, NUM_DELTA_QP_CTX, tc->delta_qp_contexts, INIT_DELTA_QP_I[model][0]); + ADD_XRATE2 (p_Vid, NUM_BLOCK_TYPES, NUM_MAP_CTX, tc->map_contexts[0], INIT_MAP_I [model]); + ADD_XRATE2 (p_Vid, NUM_BLOCK_TYPES, NUM_LAST_CTX, tc->last_contexts[0], INIT_LAST_I[model]); + ADD_XRATE2 (p_Vid, NUM_BLOCK_TYPES, NUM_ONE_CTX, tc->one_contexts, INIT_ONE_I [model]); + ADD_XRATE2 (p_Vid, NUM_BLOCK_TYPES, NUM_ABS_CTX, tc->abs_contexts, INIT_ABS_I [model]); +#if ENABLE_FIELD_CTX + ADD_XRATE1 (p_Vid, NUM_MB_AFF_CTX, tc->mb_aff_contexts, INIT_MB_AFF_I [model][0]); + ADD_XRATE2 (p_Vid, NUM_BLOCK_TYPES, NUM_MAP_CTX, tc->map_contexts[1], INIT_FLD_MAP_I [model]); + ADD_XRATE2 (p_Vid, NUM_BLOCK_TYPES, NUM_LAST_CTX, tc->last_contexts[1], INIT_FLD_LAST_I[model]); +#endif + + if (xr < min_xr) + { + min_xr = xr; + *mnumber = model; + } + } + } + else + { + for (model=0; modelmb_type_contexts, INIT_MB_TYPE_P [model]); + ADD_XRATE2 (p_Vid, 2, NUM_B8_TYPE_CTX, mc->b8_type_contexts, INIT_B8_TYPE_P [model]); + ADD_XRATE2 (p_Vid, 2, NUM_MV_RES_CTX, mc->mv_res_contexts, INIT_MV_RES_P [model]); + ADD_XRATE2 (p_Vid, 2, NUM_REF_NO_CTX, mc->ref_no_contexts, INIT_REF_NO_P [model]); + + //--- texture coding contexts --- + ADD_XRATE1 (p_Vid, NUM_TRANSFORM_SIZE_CTX, tc->transform_size_contexts, INIT_TRANSFORM_SIZE_P[model][0]); + ADD_XRATE1 (p_Vid, NUM_IPR_CTX, tc->ipr_contexts, INIT_IPR_P [model][0]); + ADD_XRATE1 (p_Vid, NUM_CIPR_CTX, tc->cipr_contexts, INIT_CIPR_P [model][0]); + ADD_XRATE2 (p_Vid, 3, NUM_CBP_CTX, tc->cbp_contexts, INIT_CBP_P [model]); + ADD_XRATE2 (p_Vid, NUM_BLOCK_TYPES, NUM_BCBP_CTX, tc->bcbp_contexts, INIT_BCBP_P [model]); + ADD_XRATE1 (p_Vid, NUM_DELTA_QP_CTX, tc->delta_qp_contexts, INIT_DELTA_QP_P[model][0]); + ADD_XRATE2 (p_Vid, NUM_BLOCK_TYPES, NUM_MAP_CTX, tc->map_contexts[0], INIT_MAP_P [model]); + ADD_XRATE2 (p_Vid, NUM_BLOCK_TYPES, NUM_LAST_CTX, tc->last_contexts[0], INIT_LAST_P [model]); + ADD_XRATE2 (p_Vid, NUM_BLOCK_TYPES, NUM_ONE_CTX, tc->one_contexts, INIT_ONE_P [model]); + ADD_XRATE2 (p_Vid, NUM_BLOCK_TYPES, NUM_ABS_CTX, tc->abs_contexts, INIT_ABS_P [model]); +#if ENABLE_FIELD_CTX + ADD_XRATE1 (p_Vid, NUM_MB_AFF_CTX, tc->mb_aff_contexts, INIT_MB_AFF_P [model][0]); + ADD_XRATE2 (p_Vid, NUM_BLOCK_TYPES, NUM_MAP_CTX, tc->map_contexts[1], INIT_FLD_MAP_P [model]); + ADD_XRATE2 (p_Vid, NUM_BLOCK_TYPES, NUM_LAST_CTX, tc->last_contexts[1], INIT_FLD_LAST_P [model]); +#endif + + if (xr < min_xr) + { + min_xr = xr; + *mnumber = model; + } + } + } +} + +#undef ADD_XRATE2 +#undef ADD_XRATE1 + + +void store_contexts (Slice *currSlice) +{ + if( currSlice->p_Inp->context_init_method ) + { + VideoParameters *p_Vid = currSlice->p_Vid; + int frame_field = p_Vid->field_picture; + int img_type = currSlice->slice_type; + int ctx_number = currSlice->start_mb_nr / p_Vid->num_mb_per_slice; + + p_Vid->initialized [frame_field][img_type][ctx_number] = 1; + GetCtxModelNumber (currSlice, p_Vid->modelNumber[frame_field][img_type] + ctx_number, currSlice->mot_ctx, currSlice->tex_ctx); + } + else + { + // do nothing + } +} + + +void update_field_frame_contexts (VideoParameters *p_Vid, int field) +{ + int i, j; + + if (field) + { + // set frame contexts + for (j=0; jnumber_of_slices; i++) + { + p_Vid->initialized [0][j][i] = p_Vid->initialized [1][j][i>>1]; + p_Vid->modelNumber[0][j][i] = p_Vid->modelNumber[1][j][i>>1]; + } + } + } + else + { + // set field contexts + for (j=0; jnumber_of_slices+1)>>1); i++) + { + p_Vid->initialized [1][j][i] = p_Vid->initialized [0][j][i<<1]; + p_Vid->modelNumber[1][j][i] = p_Vid->modelNumber[0][j][i<<1]; + } + } + } +} + diff --git a/lencod/src/errdo.c b/lencod/src/errdo.c new file mode 100644 index 0000000..6163bb1 --- /dev/null +++ b/lencod/src/errdo.c @@ -0,0 +1,732 @@ + +/*! + ************************************************************************************* + * \file errdo.c + * + * \brief + * Contains functions that estimate the distortion for the + * rate-distortion optimization with losses. + * \date + * October 22nd, 2001 + * + * \author + * Main contributors (see contributors.h for copyright, address and + * affiliation details) + * - Dimitrios Kontopodis + * Code revamped July 2008 by: + * - Peshala Pahalawatta (ppaha@dolby.com) + * - Alexis Tourapis (atour@dolby.com) + * Code modularized to support more distortion estimation algorithms in June 2009 by + * - Zhifeng Chen (zzchen@dolby.com) + ************************************************************************************* + */ + +#include "global.h" +#include "memalloc.h" +#include "refbuf.h" +#include "image.h" +#include "md_common.h" +#include "errdo.h" +#include "errdo_dist_mhyp.h" + +/*! +************************************************************************************** +* \brief +* Allocate memory for error resilient RDO. +************************************************************************************** +*/ +int allocate_errdo_mem(VideoParameters *p_Vid, InputParameters *p_Inp) +{ + int memory_size = 0; + + //allocate shared memory for all algorithms + p_Vid->p_decs = (Decoders *) malloc(sizeof(Decoders)); + memory_size += get_mem3Dint(&p_Vid->p_decs->res_img, MAX_PLANE, MB_BLOCK_SIZE, MB_BLOCK_SIZE); + memory_size += get_mem3Dint(&p_Vid->p_decs->res_mb_best8x8, MAX_PLANE, MB_BLOCK_SIZE, MB_BLOCK_SIZE); + + p_Vid->p_decs->RCD_bestY_mb = NULL; + p_Vid->p_decs->RCD_bestY_b8x8 = NULL; + p_Vid->p_decs->MVCD_bestY_mb = NULL; + p_Vid->p_decs->MVCD_bestY_b8x8 = NULL; + p_Vid->p_decs->flag_bestY_mb = NULL; + p_Vid->p_decs->flag_bestY_b8x8 = NULL; + p_Vid->p_decs->flag_wo_res = NULL; + p_Vid->p_decs->flag_wo_res_bestY_b8x8 = NULL; + p_Vid->p_decs->trans_dist_bestY_mb = NULL; + p_Vid->p_decs->trans_dist_bestY_b8x8 = NULL; + p_Vid->p_decs->trans_dist_wo_res = NULL; //it is used for P8x8, where residual may be set to 0 + p_Vid->p_decs->trans_dist_wo_res_bestY_b8x8 = NULL; //it is used for P8x8, where residual may be set to 0 + p_Vid->p_decs->trans_err_bestY_mb = NULL; + p_Vid->p_decs->trans_err_bestY_b8x8 = NULL; + p_Vid->p_decs->trans_err_wo_res = NULL; //it is used for P8x8, where residual may be set to 0 + p_Vid->p_decs->trans_err_wo_res_bestY_b8x8 = NULL; //it is used for P8x8, where residual may be set to 0 + p_Vid->p_decs->dec_mb_pred = NULL; + p_Vid->p_decs->dec_mbY_best = NULL; + p_Vid->p_decs->dec_mb_pred_best8x8 = NULL; + p_Vid->p_decs->dec_mbY_best8x8 = NULL; + p_Vid->p_decs->first_moment_bestY_mb = NULL; + p_Vid->p_decs->first_moment_bestY_b8x8 = NULL; + p_Vid->p_decs->first_moment_pred_bestY_b8x8 = NULL; + p_Vid->p_decs->first_moment_pred = NULL; + p_Vid->p_decs->second_moment_bestY_mb = NULL; + p_Vid->p_decs->second_moment_bestY_b8x8 = NULL; + p_Vid->p_decs->second_moment_pred_bestY_b8x8 = NULL; + p_Vid->p_decs->second_moment_pred = NULL; + + //Zhifeng 090630 + switch (p_Inp->de) + { + case LLN: + //allocate memory for lln algorithm + memory_size += get_mem3Dpel(&p_Vid->p_decs->dec_mb_pred, p_Inp->NoOfDecoders, MB_BLOCK_SIZE, MB_BLOCK_SIZE); + memory_size += get_mem3Dpel(&p_Vid->p_decs->dec_mbY_best, p_Inp->NoOfDecoders, MB_BLOCK_SIZE, MB_BLOCK_SIZE); + memory_size += get_mem4Dpel(&p_Vid->p_decs->dec_mbY_best8x8, 2, p_Inp->NoOfDecoders, MB_BLOCK_SIZE, MB_BLOCK_SIZE); + memory_size += get_mem4Dpel(&p_Vid->p_decs->dec_mb_pred_best8x8, 2, p_Inp->NoOfDecoders, MB_BLOCK_SIZE, MB_BLOCK_SIZE); + break; + default: + ; + } + return memory_size; +} + +/*! +************************************************************************************** +* \brief +* free memory of error resilient RDO. +************************************************************************************** +*/ +void free_errdo_mem(VideoParameters *p_Vid) +{ + //for shared memory + if (p_Vid->p_decs->res_img) + { + free_mem3Dint(p_Vid->p_decs->res_img); + p_Vid->p_decs->res_img = NULL; + } + if (p_Vid->p_decs->res_mb_best8x8) + { + free_mem3Dint(p_Vid->p_decs->res_mb_best8x8); + p_Vid->p_decs->res_mb_best8x8 = NULL; + } + + //for RMPC + if (p_Vid->p_decs->RCD_bestY_mb) + { + free_mem2Dint(p_Vid->p_decs->RCD_bestY_mb); + p_Vid->p_decs->RCD_bestY_mb = NULL; + } + if (p_Vid->p_decs->RCD_bestY_b8x8) + { + free_mem3Dint(p_Vid->p_decs->RCD_bestY_b8x8); + p_Vid->p_decs->RCD_bestY_b8x8 = NULL; + } + if (p_Vid->p_decs->MVCD_bestY_mb) + { + free_mem2Dint(p_Vid->p_decs->MVCD_bestY_mb); + p_Vid->p_decs->MVCD_bestY_mb = NULL; + } + if (p_Vid->p_decs->MVCD_bestY_b8x8) + { + free_mem3Dint(p_Vid->p_decs->MVCD_bestY_b8x8); + p_Vid->p_decs->MVCD_bestY_b8x8 = NULL; + } + if (p_Vid->p_decs->flag_bestY_mb) + { + free_mem2D(p_Vid->p_decs->flag_bestY_mb); + p_Vid->p_decs->flag_bestY_mb = NULL; + } + if (p_Vid->p_decs->flag_bestY_b8x8) + { + free_mem3D(p_Vid->p_decs->flag_bestY_b8x8); + p_Vid->p_decs->flag_bestY_b8x8 = NULL; + } + if (p_Vid->p_decs->flag_wo_res) + { + free_mem2D(p_Vid->p_decs->flag_wo_res); + p_Vid->p_decs->flag_wo_res = NULL; + } + if (p_Vid->p_decs->flag_wo_res_bestY_b8x8) + { + free_mem3D(p_Vid->p_decs->flag_wo_res_bestY_b8x8); + p_Vid->p_decs->flag_wo_res_bestY_b8x8 = NULL; + } + if (p_Vid->p_decs->trans_dist_bestY_mb) + { + free_mem2Dint(p_Vid->p_decs->trans_dist_bestY_mb); + p_Vid->p_decs->trans_dist_bestY_mb = NULL; + } + if (p_Vid->p_decs->trans_dist_bestY_b8x8) + { + free_mem3Dint(p_Vid->p_decs->trans_dist_bestY_b8x8); + p_Vid->p_decs->trans_dist_bestY_b8x8 = NULL; + } + if (p_Vid->p_decs->trans_dist_wo_res) + { + free_mem2Dint(p_Vid->p_decs->trans_dist_wo_res); + p_Vid->p_decs->trans_dist_wo_res = NULL; //it is used for P8x8, where residual may be set to 0 + } + if (p_Vid->p_decs->trans_dist_wo_res_bestY_b8x8) + { + free_mem3Dint(p_Vid->p_decs->trans_dist_wo_res_bestY_b8x8); + p_Vid->p_decs->trans_dist_wo_res_bestY_b8x8 = NULL; //it is used for P8x8, where residual may be set to 0 + } + if (p_Vid->p_decs->trans_err_bestY_mb) + { + free_mem2Dint(p_Vid->p_decs->trans_err_bestY_mb); + p_Vid->p_decs->trans_err_bestY_mb = NULL; + } + if (p_Vid->p_decs->trans_err_bestY_b8x8) + { + free_mem3Dint(p_Vid->p_decs->trans_err_bestY_b8x8); + p_Vid->p_decs->trans_err_bestY_b8x8 = NULL; + } + if (p_Vid->p_decs->trans_err_wo_res) + { + free_mem2Dint(p_Vid->p_decs->trans_err_wo_res); + p_Vid->p_decs->trans_err_wo_res = NULL; //it is used for P8x8, where residual may be set to 0 + } + if (p_Vid->p_decs->trans_err_wo_res_bestY_b8x8) + { + free_mem3Dint(p_Vid->p_decs->trans_err_wo_res_bestY_b8x8); + p_Vid->p_decs->trans_err_wo_res_bestY_b8x8 = NULL; //it is used for P8x8, where residual may be set to 0 + } + + //for LLN + if (p_Vid->p_decs->dec_mb_pred) + { + free_mem3Dpel(p_Vid->p_decs->dec_mb_pred); + p_Vid->p_decs->dec_mb_pred = NULL; + } + if (p_Vid->p_decs->dec_mbY_best) + { + free_mem3Dpel(p_Vid->p_decs->dec_mbY_best); + p_Vid->p_decs->dec_mbY_best = NULL; + } + if (p_Vid->p_decs->dec_mbY_best8x8) + { + free_mem4Dpel(p_Vid->p_decs->dec_mbY_best8x8); + p_Vid->p_decs->dec_mb_pred_best8x8 = NULL; + } + if (p_Vid->p_decs->dec_mb_pred_best8x8) + { + free_mem4Dpel(p_Vid->p_decs->dec_mb_pred_best8x8); + p_Vid->p_decs->dec_mbY_best8x8 = NULL; + } + + //for ROPE + if (p_Vid->p_decs->first_moment_bestY_mb) + { + free_mem2Dpel(p_Vid->p_decs->first_moment_bestY_mb); + p_Vid->p_decs->first_moment_bestY_mb = NULL; + } + if (p_Vid->p_decs->first_moment_bestY_b8x8) + { + free_mem3Dpel(p_Vid->p_decs->first_moment_bestY_b8x8); + p_Vid->p_decs->first_moment_bestY_b8x8 = NULL; + } + if (p_Vid->p_decs->first_moment_pred_bestY_b8x8) + { + free_mem3Dpel(p_Vid->p_decs->first_moment_pred_bestY_b8x8); + p_Vid->p_decs->first_moment_pred_bestY_b8x8 = NULL; + } + if (p_Vid->p_decs->first_moment_pred) + { + free_mem2Dpel(p_Vid->p_decs->first_moment_pred); + p_Vid->p_decs->first_moment_pred = NULL; + } + if (p_Vid->p_decs->second_moment_bestY_mb) + { + free_mem2Duint16(p_Vid->p_decs->second_moment_bestY_mb); + p_Vid->p_decs->second_moment_bestY_mb = NULL; + } + if (p_Vid->p_decs->second_moment_bestY_b8x8) + { + free_mem3Duint16(p_Vid->p_decs->second_moment_bestY_b8x8); + p_Vid->p_decs->second_moment_bestY_b8x8 = NULL; + } + if (p_Vid->p_decs->second_moment_pred_bestY_b8x8) + { + free_mem3Duint16(p_Vid->p_decs->second_moment_pred_bestY_b8x8); + p_Vid->p_decs->second_moment_pred_bestY_b8x8 = NULL; + } + if (p_Vid->p_decs->second_moment_pred) + { + free_mem2Duint16(p_Vid->p_decs->second_moment_pred); + p_Vid->p_decs->second_moment_pred = NULL; + } + + free_pointer( p_Vid->p_decs ); +} + +/*! + ************************************************************************************* +* \brief + * Initialize error concealment function + * (Currently only copy concealment is implemented. Can extend to other concealment + * types when available.) + * + ************************************************************************************* +*/ +void init_error_conceal(VideoParameters *p_Vid, int concealment_type) +{ + p_Vid->error_conceal_picture = copy_conceal_picture; +} + + +/****************************************************************************************** +* +* Finds reference picture with nearest POC to current picture to use for error concealment +* +******************************************************************************************* +*/ +StorablePicture* find_nearest_ref_picture(DecodedPictureBuffer *p_Dpb, int poc) +{ + unsigned int i; + int min_poc_diff = 1000; + int poc_diff; + StorablePicture* refPic = NULL; + + for (i = 0; i < p_Dpb->ref_frames_in_buffer; i++) + { + if (p_Dpb->fs_ref[i]->is_used==3) + { + if ((p_Dpb->fs_ref[i]->frame->used_for_reference)&&(!p_Dpb->fs_ref[i]->frame->is_long_term)) + { + poc_diff = iabs(p_Dpb->fs_ref[i]->frame->poc - poc); + if (poc_diff < min_poc_diff) + { + refPic = p_Dpb->fs_ref[i]->frame; + min_poc_diff = poc_diff; + } + } + } + } + return refPic; +} + +/*! + ************************************************************************************* +* \brief + * Gives the prediction residue for a block + ************************************************************************************* +*/ +void errdo_compute_residue (Macroblock *currMB, imgpel **imgY, int **res_img, imgpel **mb_pred, int b8block, int block_size) +{ + int i,j; + int i0 = (b8block & 0x01)<<3, i1 = i0+block_size; + int j0 = (b8block >> 1)<<3, j1 = j0+block_size; + + for (i = i0; i < i1; i++) + { + for (j = j0; j < j1; j++) + { + res_img[j][i] = (int)imgY[j][currMB->pix_x + i] - mb_pred[j][i]; + } + } +} + + +/*! + ************************************************************************************* + * \brief + * Store the best 8x8 block of estimated distortion for errdo. + * + * \param + * + * \note + * For lln algorithm, we need to consider akip and direct? + * + ************************************************************************************* + */ +void errdo_store_best_b8x8(Macroblock *currMB, int transform8x8, int block) +{ + VideoParameters *p_Vid = currMB->p_Vid; + InputParameters *p_Inp = currMB->p_Inp; + + switch (p_Inp->de) + { + case LLN: + errdo_store_best_block_multihyp(p_Inp, p_Vid->p_decs->dec_mbY_best8x8[transform8x8], p_Vid->enc_picture->de_mem->p_dec_img[0], block, currMB->pix_x, currMB->pix_y, BLOCK_SIZE_8x8); + errdo_store_best_block_multihyp(p_Inp, p_Vid->p_decs->dec_mb_pred_best8x8[transform8x8], p_Vid->p_decs->dec_mb_pred, block, 0, 0, BLOCK_SIZE_8x8); + break; + default: + ; + } +} + + +/*! + ************************************************************************************* + * \brief + * get the best 8x8 block of estimated distortion. Original code seems to have problem. Why not get p_Vid->p_decs->dec_mb_pred_best8x8? + * But since there is errdo_get_best_P8x8() in SetCoeffAndReconstruction8x8(), errdo_get_best_b8x8 seems not necessary. + * + * \param + * + * \note + * For lln algorithm, we need to consider skip and direct? + * This function seems not necessary since we have errdo_get_best_P8x8 + ************************************************************************************* + */ +void errdo_get_best_b8x8(Macroblock *currMB, int transform8x8, int block) +{ + VideoParameters *p_Vid = currMB->p_Vid; + InputParameters *p_Inp = currMB->p_Inp; + + switch (p_Inp->de) + { + case LLN: + errdo_get_best_block_multihyp(currMB, p_Vid->enc_picture->de_mem->p_dec_img[0], p_Vid->p_decs->dec_mbY_best8x8[transform8x8], block, BLOCK_SIZE_8x8); + break; + default: + ; + } + +} + + + +/*! + ************************************************************************************* +* \brief + * Store the best macroblock of estimated distortion for errdo. +* + * \param + * + ************************************************************************************* +*/ +void errdo_store_best_MB(Macroblock *currMB) +{ + VideoParameters *p_Vid = currMB->p_Vid; + InputParameters *p_Inp = currMB->p_Inp; + + switch (p_Inp->de) + { + case LLN: + errdo_store_best_block_multihyp(p_Inp, p_Vid->p_decs->dec_mbY_best, p_Vid->enc_picture->de_mem->p_dec_img[0], 0, currMB->pix_x, currMB->pix_y, MB_BLOCK_SIZE); + break; + default: + ; + } +} + + + +/*! + ************************************************************************************* + * \brief + * Get the best macroblock of estimated distortion for storable picture. + * + * \param + * + ************************************************************************************* + */ +void errdo_get_best_MB(Macroblock *currMB) +{ + VideoParameters *p_Vid = currMB->p_Vid; + InputParameters *p_Inp = currMB->p_Inp; + + switch (p_Inp->de) + { + case LLN: + errdo_get_best_block_multihyp(currMB, p_Vid->enc_picture->de_mem->p_dec_img[0], p_Vid->p_decs->dec_mbY_best, 0, MB_BLOCK_SIZE); + break; + default: + ; + } + +} + + + +/*! + ************************************************************************************* + * \brief + * Store the best macroblock of estimated distortion for mode P8x8. + * + * \param + * + ************************************************************************************* + */ +void errdo_get_best_P8x8(Macroblock *currMB, int transform8x8) +{ + VideoParameters *p_Vid = currMB->p_Vid; + InputParameters *p_Inp = currMB->p_Inp; + + switch (p_Inp->de) + { + case LLN: + if (p_Vid->p_decs->rec_type == 0) + { + errdo_get_best_block_multihyp(currMB, p_Vid->enc_picture->de_mem->p_dec_img[0], p_Vid->p_decs->dec_mb_pred_best8x8[transform8x8], 0, MB_BLOCK_SIZE); + } + else + { + errdo_get_best_block_multihyp(currMB, p_Vid->enc_picture->de_mem->p_dec_img[0], p_Vid->p_decs->dec_mbY_best8x8[transform8x8], 0, MB_BLOCK_SIZE); + } + break; + default: + ; + } +} + +/*! + ************************************************************************************* + * \brief + * Initialize distortion estimation algorithm + * (Can extend to support more algorithms when available.) + * + ************************************************************************************* + */ +void init_distortion_estimation(VideoParameters *p_Vid, int de_algorithm) +{ + switch (de_algorithm) + { + case LLN: + p_Vid->estimate_distortion = errdo_distortion_estimation_multihyp; + break; + default: + ; + } +} + + +/*! + ************************************************************************************* + * \brief + * allocate storable picture memory for errdo +* + ************************************************************************************* +*/ +void errdo_alloc_storable_picture(StorablePicture *p, VideoParameters *p_Vid, InputParameters *p_Inp, int size_x, int size_y, int size_x_cr, int size_y_cr) +{ + Dist_Estm *s; + int dec, ndec, nplane; + + p->de_mem = (Dist_Estm *)malloc( sizeof(Dist_Estm) ); + s = p->de_mem; + + s->res_con_diff_Y = NULL; + s->res_con_diff_UV = NULL; + s->MV_con_diff_Y = NULL; + s->MV_con_diff_UV = NULL; + s->error_sign_flag_Y = NULL; + s->error_sign_flag_UV = NULL; + s->transmission_dist_Y = NULL; + s->transmission_dist_UV = NULL; + s->transmission_err_Y = NULL; + s->transmission_err_UV = NULL; + s->dec_imgY = NULL; + s->dec_imgUV = NULL; + s->mb_error_map = NULL; + s->first_moment_Y = NULL; + s->first_moment_UV = NULL; + s->second_moment_Y = NULL; + s->second_moment_UV = NULL; + for (nplane = 0; nplane < 3; nplane++) + { + s->p_res_con_diff[nplane] = NULL; + s->p_MV_con_diff[nplane] = NULL; + s->p_error_sign_flag[nplane] = NULL; + s->p_transmission_dist[nplane] = NULL; + s->p_transmission_err[nplane] = NULL; + s->p_dec_img[nplane] = NULL; + s->p_first_moment[nplane] = NULL; + s->p_second_moment[nplane] = NULL; + } + + switch (p_Inp->de) + { + case LLN: + ndec = p_Inp->NoOfDecoders; + //check the consistent + if (ndec == 0) + { + printf("Number of decoders cannot be zero for LLN and fast LLN algorithms, resetting to 30"); + ndec = 30; + } + get_mem3D(&(s->mb_error_map), ndec, size_y/MB_BLOCK_SIZE, size_x/MB_BLOCK_SIZE); + get_mem3Dpel(&(s->dec_imgY), ndec, size_y, size_x); + + // This seems somewhat inefficient. Why not allocate array as [ndec][x] where x goes from 0 to 2? + if ((s->p_dec_img[0] = (imgpel***)calloc(ndec,sizeof(imgpel**))) == NULL) + { + no_mem_exit("errdo.c: p_dec_img[0]"); + } + + if (p_Vid->yuv_format != YUV400) + { + get_mem4Dpel(&(s->dec_imgUV), ndec, 2, size_y_cr, size_x_cr); + if ((s->p_dec_img[1] = (imgpel***)calloc(ndec,sizeof(imgpel**))) == NULL) + { + no_mem_exit("errdo.c: p_dec_img[1]"); + } + if ((s->p_dec_img[2] = (imgpel***)calloc(ndec,sizeof(imgpel**))) == NULL) + { + no_mem_exit("errdo.c: p_dec_img[2]"); + } + } + + for (dec = 0; dec < ndec; dec++) + { + s->p_dec_img[0][dec] = s->dec_imgY[dec]; + } + + if (p_Vid->yuv_format != YUV400) + { + for (dec = 0; dec < ndec; dec++) + { + s->p_dec_img[1][dec] = s->dec_imgUV[dec][0]; + s->p_dec_img[2][dec] = s->dec_imgUV[dec][1]; + } + } + + break; + default: + ; + } +} + + +/*! + ************************************************************************************* + * \brief + * free storable picture memory for errdo + * + ************************************************************************************* + */ +void errdo_free_storable_picture(StorablePicture* s) +{ + int nplane; + Dist_Estm *p = s->de_mem; + //free memory for RMPC and extended RMPC algorithms + if (p->res_con_diff_Y) + { + free_mem2Dint(p->res_con_diff_Y); + p->res_con_diff_Y = NULL; + } + if (p->res_con_diff_UV) + { + free_mem3Dint(p->res_con_diff_UV); + p->res_con_diff_UV = NULL; + } + for (nplane = 0; nplane < 3; nplane++) + { + p->p_res_con_diff[nplane] = NULL; + } + + if (p->MV_con_diff_Y) + { + free_mem2Dint(p->MV_con_diff_Y); + p->MV_con_diff_Y = NULL; + } + if (p->MV_con_diff_UV) + { + free_mem3Dint(p->MV_con_diff_UV); + p->MV_con_diff_UV = NULL; + } + for (nplane = 0; nplane < 3; nplane++) + { + p->p_MV_con_diff[nplane] = NULL; + } + + if (p->error_sign_flag_Y) + { + free_mem2D(p->error_sign_flag_Y); + p->error_sign_flag_Y = NULL; + } + if (p->error_sign_flag_UV) + { + free_mem3D(p->error_sign_flag_UV); + p->error_sign_flag_UV = NULL; + } + for (nplane = 0; nplane < 3; nplane++) + { + p->p_error_sign_flag[nplane] = NULL; + } + + if (p->transmission_dist_Y) + { + free_mem2Dint(p->transmission_dist_Y); + p->transmission_dist_Y = NULL; + } + if (p->transmission_dist_UV) + { + free_mem3Dint(p->transmission_dist_UV); + p->transmission_dist_UV = NULL; + } + for (nplane = 0; nplane < 3; nplane++) + { + p->p_transmission_dist[nplane] = NULL; + } + + if (p->transmission_err_Y) + { + free_mem2Dint(p->transmission_err_Y); + p->transmission_err_Y = NULL; + } + if (p->transmission_err_UV) + { + free_mem3Dint(p->transmission_err_UV); + p->transmission_err_UV = NULL; + } + for (nplane = 0; nplane < 3; nplane++) + { + p->p_transmission_err[nplane] = NULL; + } + + //free memory for LLN and Faster LLN algorithms + if (p->dec_imgY) + { + free_mem3Dpel(p->dec_imgY); + p->dec_imgY = NULL; + } + if (p->dec_imgUV) + { + free_mem4Dpel(p->dec_imgUV); + p->dec_imgUV = NULL; + } + for (nplane = 0; nplane < 3; nplane++) + { + free_pointer(p->p_dec_img[nplane]); + } + if (p->mb_error_map) + { + free_mem3D(p->mb_error_map); + p->mb_error_map = NULL; + } + + + //free memory for ROPE and extended ROPE algorithms + if (p->first_moment_Y) + { + free_mem2Dpel(p->first_moment_Y); + p->first_moment_Y = NULL; + } + if (p->first_moment_UV) + { + free_mem3Dpel(p->first_moment_UV); + p->first_moment_UV = NULL; + } + for (nplane = 0; nplane < 3; nplane++) + { + p->p_first_moment[nplane] = NULL; + } + if (p->second_moment_Y) + { + free_mem2Duint16(p->second_moment_Y); + p->second_moment_Y = NULL; + } + if (p->second_moment_UV) + { + free_mem3Duint16(p->second_moment_UV); + p->second_moment_UV = NULL; + } + for (nplane = 0; nplane < 3; nplane++) + { + p->p_second_moment[nplane] = NULL; + } + + free_pointer(s->de_mem); +} + diff --git a/lencod/src/errdo_dist_mhyp.c b/lencod/src/errdo_dist_mhyp.c new file mode 100644 index 0000000..eb71269 --- /dev/null +++ b/lencod/src/errdo_dist_mhyp.c @@ -0,0 +1,671 @@ +/*! + ************************************************************************************* + * \file errdo_dist_estimation_multihyp.c + * \brief + * Expected distortion estimation using multiple decoder based method + * refer to the paper "Optimized transmission of H.26L/JVT coded video over packet-lossy networks" + * by T. Stockhammer, T. Wiegand, and S. Wenger in 2002 + * \date + * October 22nd, 2001 + * + * \author + * Main contributors (see contributors.h for copyright, address and + * affiliation details) + * - Dimitrios Kontopodis + * Code revamped July 2008 by: + * - Peshala Pahalawatta (ppaha@dolby.com) + * - Alexis Tourapis (atour@dolby.com) + * Code modularized to support more distortion estimation algorithms in June 2009 by + * - Zhifeng Chen (zzchen@dolby.com) + ************************************************************************************* + */ + +//For distortion estimation +#include "global.h" +#include "mbuffer.h" +#include "errdo.h" +#include "md_distortion.h" +#include "md_common.h" +#include "lln_mc_prediction.h" + +static void add_residue (Macroblock *currMB, StorablePicture *enc_pic, int decoder, int pl, int block8x8, int x_size, int y_size); +static void Build_Status_Map(VideoParameters *p_Vid, InputParameters *p_Inp, byte **s_map); +static void get_predicted_mb(Macroblock *currMB, StorablePicture *enc_pic, int decoder); +static void copy_conceal_mb (Macroblock *currMB, StorablePicture *enc_pic, int decoder, int mb_error, StorablePicture* refPic); +static void decode_one_b8block (Macroblock* currMB, StorablePicture *enc_pic, int decoder, int block8x8, short mv_mode, int pred_dir); +static void decode_one_mb (Macroblock* currMB, StorablePicture *enc, int decoder); + +extern void UpdateDecoders (VideoParameters *p_Vid, InputParameters *p_Inp, StorablePicture *enc_pic); +extern void DeblockFrame(VideoParameters *p_Vid, imgpel **, imgpel ***); + +/*! + ************************************************************************************* + * \brief + * Performs distortion estimation using multiple decoder based algorithm. + * + ************************************************************************************* + */ +distblk errdo_distortion_estimation_multihyp(Macroblock *currMB, int block, int block_size, short mode, short pdir, distblk min_rdcost) +{ + VideoParameters *p_Vid = currMB->p_Vid; + InputParameters *p_Inp = currMB->p_Inp; + Slice *currSlice = currMB->p_Slice; + seq_parameter_set_rbsp_t *active_sps = p_Vid->active_sps; + + int pax = 8*(block & 0x01); + int pay = 8*(block >> 1); + distblk distortion=0; + distblk temp_dist, ddistortion = 0; + int k; + + //Note that in rdcost_for_8x8blocks() no chroma distortion is calculated + //Refer to the function reset_adaptive_rounding(), + //maxplane = (p_Vid->yuv_format == YUV400)? 1 : 3; for p_Vid->ARCofAdj4x4 + //maxplane = (p_Vid->yuv_format == YUV400)? 1 : (p_Vid->P444_joined ? 3 : 1); for p_Vid->ARCofAdj8x8 + if (mode >= 4 && mode <= 7) //to be called by rdcost_for_8x8blocks() + { + //===== get residue ===== + // We need the reconstructed prediction residue for the simulated decoders. + //compute_residue_b8block (p_Vid, &p_Vid->enc_picture->p_img[0][currMB->pix_y], p_Vid->p_decs->res_img[0], block, -1); + errdo_compute_residue (currMB, &p_Vid->enc_picture->p_img[0][currMB->pix_y], p_Vid->p_decs->res_img[0], currSlice->mb_pred[0], block, 8); + + //===== GET DISTORTION + /* + //To keep exactly same as former version, below lines are used + for (k=0; kNoOfDecoders ;k++) + { + decode_one_b8block (currMB, p_Vid->enc_picture, k, block, mode, pdir); + distortion += compute_SSE8x8(&p_Vid->pCurImg[currMB->opix_y + pay], &p_Vid->enc_picture->p_dec_img[0][k][currMB->opix_y + pay], currMB->pix_x + pax, currMB->pix_x + pax); + } + distortion /= p_Inp->NoOfDecoders; + */ + + for (k = 0; k < p_Inp->NoOfDecoders; k++) + { + decode_one_b8block (currMB, p_Vid->enc_picture, k, block, mode, pdir); + ddistortion += compute_SSE8x8(&p_Vid->pCurImg[currMB->opix_y + pay], &p_Vid->enc_picture->de_mem->p_dec_img[0][k][currMB->opix_y + pay], currMB->pix_x + pax, currMB->pix_x + pax); + + { + temp_dist = min_rdcost * p_Inp->NoOfDecoders; + } + if (ddistortion > temp_dist) + { + //distortion = (distblk) ((ddistortion) / (k+1) + 0.5); + return DISTBLK_MAX; + } + } + + distortion = (distblk) (ddistortion / p_Inp->NoOfDecoders); + } + else if (mode != P8x8) //to be called by RDCost_for_macroblocks() + { + errdo_compute_residue (currMB, &p_Vid->enc_picture->p_curr_img[currMB->pix_y], p_Vid->p_decs->res_img[0], mode == I16MB ? currSlice->mpr_16x16[0][ (short) currMB->i16mode] : currSlice->mb_pred[0], 0, 16); + for (k = 0; kNoOfDecoders ;k++) + { + decode_one_mb (currMB, p_Vid->enc_picture, k); + ddistortion += compute_SSE16x16(&p_Vid->pImgOrg[0][currMB->opix_y], &p_Vid->enc_picture->de_mem->p_dec_img[0][k][currMB->pix_y], currMB->pix_x, currMB->pix_x); + + //Use integer calculation + if (min_rdcost < DISTBLK_MAX) + { + { + temp_dist = min_rdcost * p_Inp->NoOfDecoders; + } + } + else + temp_dist = DISTBLK_MAX; + + if (ddistortion > temp_dist) + { + //distortion = (distblk) ((ddistortion) / (k+1) + 0.5); + return DISTBLK_MAX; + } + } + + distortion = (distblk) (ddistortion / p_Inp->NoOfDecoders); + + if ((p_Vid->yuv_format != YUV400) && (active_sps->chroma_format_idc != YUV444)) + { + // CHROMA + distortion += compute_SSE_cr(&p_Vid->pImgOrg[1][currMB->opix_c_y], &p_Vid->enc_picture->imgUV[0][currMB->pix_c_y], currMB->pix_c_x, currMB->pix_c_x, p_Vid->mb_cr_size_y, p_Vid->mb_cr_size_x); + distortion += compute_SSE_cr(&p_Vid->pImgOrg[2][currMB->opix_c_y], &p_Vid->enc_picture->imgUV[1][currMB->pix_c_y], currMB->pix_c_x, currMB->pix_c_x, p_Vid->mb_cr_size_y, p_Vid->mb_cr_size_x); + } + } + else //to be called by RDCost_for_macroblocks() + { + for (k = 0; kNoOfDecoders ;k++) + { + ddistortion += compute_SSE16x16(&p_Vid->pImgOrg[0][currMB->opix_y], &p_Vid->enc_picture->de_mem->p_dec_img[0][k][currMB->pix_y], currMB->pix_x, currMB->pix_x); + + //Use integeter calculation + if (min_rdcost < DISTBLK_MAX) + { + { + temp_dist = min_rdcost * p_Inp->NoOfDecoders; + } + } + else + temp_dist = DISTBLK_MAX; + + if (ddistortion > temp_dist) + { + //distortion = (distblk) ((ddistortion) / (k+1) + 0.5); + return DISTBLK_MAX; + } + } + distortion = (distblk) (ddistortion / p_Inp->NoOfDecoders); + + if ((p_Vid->yuv_format != YUV400) && (active_sps->chroma_format_idc != YUV444)) + { + // CHROMA + distortion += compute_SSE_cr(&p_Vid->pImgOrg[1][currMB->opix_c_y], &p_Vid->enc_picture->imgUV[0][currMB->pix_c_y], currMB->pix_c_x, currMB->pix_c_x, p_Vid->mb_cr_size_y, p_Vid->mb_cr_size_x); + distortion += compute_SSE_cr(&p_Vid->pImgOrg[2][currMB->opix_c_y], &p_Vid->enc_picture->imgUV[1][currMB->pix_c_y], currMB->pix_c_x, currMB->pix_c_x, p_Vid->mb_cr_size_y, p_Vid->mb_cr_size_x); + } + } + + return distortion; +} + +/*! + ************************************************************************************* + * \brief + * Performs the simulation of the packet losses, calls the error concealment funcs + * and deblocks the error concealed pictures + * + ************************************************************************************* + */ +void UpdateDecoders(VideoParameters *p_Vid, InputParameters *p_Inp, StorablePicture *enc_pic) +{ + int k; + for (k = 0; k < p_Inp->NoOfDecoders; k++) + { + Build_Status_Map(p_Vid, p_Inp, enc_pic->de_mem->mb_error_map[k]); // simulates the packet losses + p_Vid->error_conceal_picture(p_Vid, enc_pic, k); + DeblockFrame (p_Vid, enc_pic->de_mem->p_dec_img[0][k], NULL); + } +} + + + +/*! + ************************************************************************************* + * \brief + * Builds a random status map showing whether each MB is received or lost, based + * on the packet loss rate and the slice structure. + * + * \param s_map + * The status map to be filled + ************************************************************************************* + */ +static void Build_Status_Map(VideoParameters *p_Vid, InputParameters *p_Inp, byte **s_map) +{ + int i,j,slice=-1,mb=0,jj,ii; + byte packet_lost=0; + + jj = p_Vid->height/MB_BLOCK_SIZE; + ii = p_Vid->width/MB_BLOCK_SIZE; + + for (j = 0; j < jj; j++) + { + for (i = 0; i < ii; i++) + { + if (!p_Inp->slice_mode || p_Vid->mb_data[mb].slice_nr != slice) /* new slice */ + { + packet_lost=0; + if ((double)rand()/(double)RAND_MAX*100 < p_Inp->LossRateC) packet_lost += 3; + if ((double)rand()/(double)RAND_MAX*100 < p_Inp->LossRateB) packet_lost += 2; + if ((double)rand()/(double)RAND_MAX*100 < p_Inp->LossRateA) packet_lost = 1; + slice++; + } + if (!packet_lost) + { + s_map[j][i]=0; //! Packet OK + } + else + { + s_map[j][i] = packet_lost; + if(p_Inp->partition_mode == 0) s_map[j][i]=1; + } + mb++; + } + } +} + + +/*! + ************************************************************************************* + * \brief + * Stores the pel values for the current best mode. + ************************************************************************************* + */ +void errdo_store_best_block_multihyp(InputParameters *p_Inp, imgpel*** mbY, imgpel*** dec_img, int block, int img_i, int img_j, int block_size) +{ + int i0 = ((block&0x01)<<3); + int j0 = ((block>>1)<<3); + int j, k; + int j1 = j0 + block_size; + + for (k = 0; k < p_Inp->NoOfDecoders; k++) + { + for (j = j0; j < j1; j++) + { + memcpy(&mbY[k][j][i0], &dec_img[k][img_j+j][img_i+i0], block_size * sizeof(imgpel)); + } + } +} + +/*! + ************************************************************************************* + * \brief + * Restores the pel values from the current best mode. + ************************************************************************************* + */ +void errdo_get_best_block_multihyp(Macroblock *currMB, imgpel*** dec_img, imgpel*** mbY, int block, int block_size) +{ + int i0 = ((block&0x01)<<3); + int j0 = ((block>>1)<<3); + int j, k; + int j1 = j0 + block_size; + + for (k = 0; k < currMB->p_Inp->NoOfDecoders; k++) + { + for (j = j0; j < j1; j++) + { + memcpy(&dec_img[k][currMB->pix_y + j][currMB->pix_x+i0], &mbY[k][j][i0], block_size * sizeof(imgpel)); + } + } +} + +/*! +************************************************************************************** +* \brief +* Decodes one macroblock for error resilient RDO. +* Currently does not support: +* 1) B coded pictures +* 2) Chroma components +* 3) Potential error propagation due to intra prediction +* 4) Field coding +************************************************************************************** +*/ +void decode_one_mb (Macroblock* currMB, StorablePicture *enc_pic, int decoder) +{ + int i0, j; + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + imgpel** curComp; + imgpel** oldComp; + + //printf("currMB->mb_type %d\n", currMB->mb_type); + if (currMB->mb_type > P8x8) //Intra MB + { + curComp = &enc_pic->de_mem->p_dec_img[0][decoder][currMB->pix_y]; + oldComp = &enc_pic->p_curr_img[currMB->pix_y]; + i0 = currMB->pix_x; + copy_image_data_16x16(curComp, oldComp, i0, i0); + } + else if ((currMB->mb_type == 0) && (currSlice->slice_type != B_SLICE)) + { + get_predicted_mb(currMB, enc_pic, decoder); + curComp = &enc_pic->de_mem->p_dec_img[0][decoder][currMB->pix_y]; + for(j = 0; j < p_Vid->mb_size[0][1]; j++) + { + memcpy(&(curComp[j][currMB->pix_x]), &(p_Vid->p_decs->dec_mb_pred[decoder][j][0]), p_Vid->mb_size[0][0] * sizeof(imgpel)); + } + } + else + { + get_predicted_mb(currMB, enc_pic, decoder); + add_residue(currMB, enc_pic, decoder, PLANE_Y, 0, MB_BLOCK_SIZE, MB_BLOCK_SIZE); + } +} + +/*! +************************************************************************************** +* \brief +* Finds predicted macroblock values +* and copies them to currSlice->mb_pred[0][][] +************************************************************************************** +*/ +static void get_predicted_mb(Macroblock *currMB, StorablePicture *enc_pic, int decoder) +{ + Slice *currSlice = currMB->p_Slice; + int i,j,k; + int block_size_x, block_size_y; + int mv_mode, pred_dir; + static const byte decode_block_scan[16] = {0,1,4,5,2,3,6,7,8,9,12,13,10,11,14,15}; + int block8x8; + int k_start, k_end, k_inc; + + if (currSlice->slice_type == P_SLICE && !currMB->mb_type) + { + block_size_x = MB_BLOCK_SIZE; + block_size_y = MB_BLOCK_SIZE; + perform_mc(currMB, decoder, PLANE_Y, enc_pic, LIST_0, 0, 0, enc_pic->mv_info, 0, 0, block_size_x, block_size_y, 0); + } + else if (currMB->mb_type == 1) + { + block_size_x = MB_BLOCK_SIZE; + block_size_y = MB_BLOCK_SIZE; + pred_dir = currMB->b8x8[0].pdir; + perform_mc(currMB, decoder, PLANE_Y, enc_pic, pred_dir, 1, 1, enc_pic->mv_info, 0, 0, block_size_x, block_size_y, currMB->b8x8[0].bipred); + } + else if (currMB->mb_type == 2) + { + block_size_x = MB_BLOCK_SIZE; + block_size_y = 8; + + for (block8x8 = 0; block8x8 < 4; block8x8 += 2) + { + pred_dir = currMB->b8x8[block8x8].pdir; + perform_mc(currMB, decoder, PLANE_Y, enc_pic, pred_dir, 2, 2, enc_pic->mv_info, 0, block8x8, block_size_x, block_size_y, currMB->b8x8[block8x8].bipred); + } + } + else if (currMB->mb_type == 3) + { + block_size_x = 8; + block_size_y = 16; + + for (block8x8 = 0; block8x8 < 2; block8x8++) + { + i = block8x8<<1; + j = 0; + pred_dir = currMB->b8x8[block8x8].pdir; + perform_mc(currMB, decoder, PLANE_Y, enc_pic, pred_dir, 3, 3, enc_pic->mv_info, i, j, block_size_x, block_size_y, currMB->b8x8[block8x8].bipred); + } + } + else + { + for (block8x8 = 0; block8x8 < 4; block8x8++) + { + mv_mode = currMB->b8x8[block8x8].mode; + pred_dir = currMB->b8x8[block8x8].pdir; + + k_start = (block8x8 << 2); + k_inc = (mv_mode == 5) ? 2 : 1; + + if (mv_mode == 0) + { + k_end = k_start + 1; + block_size_x = 8; + block_size_y = 8; + } + else + { + k_end = (mv_mode == 4) ? k_start + 1 : ((mv_mode == 7) ? k_start + 4 : k_start + k_inc + 1); + block_size_x = ( mv_mode == 5 || mv_mode == 4 ) ? 8 : 4; + block_size_y = ( mv_mode == 6 || mv_mode == 4 ) ? 8 : 4; + } + + for (k = k_start; k < k_end; k += k_inc) + { + i = (decode_block_scan[k] & 3); + j = ((decode_block_scan[k] >> 2) & 3); + perform_mc(currMB, decoder, PLANE_Y, enc_pic, pred_dir, mv_mode, mv_mode, enc_pic->mv_info, i, j, block_size_x, block_size_y, currMB->b8x8[block8x8].bipred); + } + } + } +} + + +/*! +************************************************************************************** +* \brief +* Decodes one 8x8 partition for error resilient RDO. +* Currently does not support: +* 1) B coded pictures +* 2) Chroma components +* 3) Potential error propagation due to intra prediction +* 4) Field coding +************************************************************************************** +*/ +void decode_one_b8block (Macroblock* currMB, StorablePicture *enc_pic, int decoder, int block8x8, short mv_mode, int pred_dir) +{ + int i,j,k; + int block_size_x, block_size_y; + int i0 = (block8x8 & 0x01)<<3; + int j0 = (block8x8 >> 1)<<3, j1 = j0+8; + //char*** ref_idx_buf = enc_pic->motion.ref_idx; + imgpel **curComp; + imgpel **oldComp; + int k_start, k_end, k_inc; + static const byte decode_block_scan[16] = {0,1,4,5,2,3,6,7,8,9,12,13,10,11,14,15}; + + if (mv_mode > 8) //Intra + { + for(j = j0; j < j1; j++) + { + curComp = &enc_pic->de_mem->p_dec_img[0][decoder][currMB->pix_y]; + oldComp = &enc_pic->p_curr_img[currMB->pix_y]; + memcpy(&(curComp[j][i0]), &(oldComp[j][i0]), sizeof(imgpel)*8); + } + } + else + { + k_start = (block8x8 << 2); + + if (mv_mode == 0) //Direct + { + k_inc = 1; + k_end = k_start+1; + block_size_x = 8; + block_size_y = 8; + //ref_idx_buf = currSlice->direct_ref_idx; + } + else //mv_mode from 4 to 7 + { + k_inc = (mv_mode == 5) ? 2 : 1; + k_end = (mv_mode == 4) ? k_start + 1 : ((mv_mode == 7) ? k_start + 4 : k_start + k_inc + 1); + + block_size_x = ( mv_mode == 5 || mv_mode == 4 ) ? 8 : 4; + block_size_y = ( mv_mode == 6 || mv_mode == 4 ) ? 8 : 4; + } + + for (k = k_start; k < k_end; k += k_inc) + { + i = (decode_block_scan[k] & 3); + j = ((decode_block_scan[k] >> 2) & 3); + perform_mc(currMB, decoder, PLANE_Y, enc_pic, pred_dir, mv_mode, mv_mode, enc_pic->mv_info, i, j, block_size_x, block_size_y, currMB->b8x8[block8x8].bipred); + } + + add_residue(currMB, enc_pic, decoder, PLANE_Y, block8x8, 8, 8); + } +} + +/*! +************************************************************************************** +* \brief +* Add residual to motion predicted block +************************************************************************************** +*/ +static void add_residue (Macroblock *currMB, StorablePicture *enc_pic, int decoder, int pl, int block8x8, int x_size, int y_size) +{ + VideoParameters *p_Vid = currMB->p_Vid; + int max_pel_value = currMB->p_Vid->max_pel_value_comp[pl]; + int i,j; + int i0 = (block8x8 & 0x01)<<3, i1 = i0 + x_size; + int j0 = (block8x8 >> 1)<<3, j1 = j0 + y_size; + + imgpel **p_dec_img = &enc_pic->de_mem->p_dec_img[pl][decoder][currMB->pix_y]; + int **res_img = p_Vid->p_decs->res_img[0]; + imgpel** mpr = p_Vid->p_decs->dec_mb_pred[decoder]; + + for (j = j0; j < j1; j++) + { + for (i = i0; i < i1; i++) + { + p_dec_img[j][currMB->pix_x + i] = (imgpel) iClip3(0, max_pel_value, (mpr[j][i] + res_img[j][i])); + } + } +} + + +/*! +************************************************************************************** +* \brief +* Finds predicted macroblock values for error concealment +* +* Requires enc_pic->motion.mv and enc_pic->motion.ref_idx to be correct for +* current picture. +************************************************************************************** +*/ +static void get_predicted_concealment_mb(Macroblock* currMB, StorablePicture* enc_pic, int decoder) +{ + Slice *currSlice = currMB->p_Slice; + int i,j,k; + int block_size_x, block_size_y; + int mv_mode, pred_dir; + static const byte decode_block_scan[16] = {0,1,4,5,2,3,6,7,8,9,12,13,10,11,14,15}; + int block8x8; + int k_start, k_end, k_inc; + + if (currSlice->slice_type == P_SLICE && !currMB->mb_type) + { + block_size_x = MB_BLOCK_SIZE; + block_size_y = MB_BLOCK_SIZE; + perform_mc_concealment(currMB, decoder, PLANE_Y, enc_pic, LIST_0, 0, 0, enc_pic->mv_info, 0, 0, block_size_x, block_size_y); + } + else if (currMB->mb_type == 1) + { + block_size_x = MB_BLOCK_SIZE; + block_size_y = MB_BLOCK_SIZE; + pred_dir = currMB->b8x8[0].pdir; + perform_mc_concealment(currMB, decoder, PLANE_Y, enc_pic, pred_dir, 1, 1, enc_pic->mv_info, 0, 0, block_size_x, block_size_y); + } + else if (currMB->mb_type == 2) + { + block_size_x = MB_BLOCK_SIZE; + block_size_y = 8; + + for (block8x8 = 0; block8x8 < 4; block8x8 += 2) + { + pred_dir = currMB->b8x8[block8x8].pdir; + perform_mc_concealment(currMB, decoder, PLANE_Y, enc_pic, pred_dir, 2, 2, enc_pic->mv_info, 0, block8x8, block_size_x, block_size_y); + } + } + else if (currMB->mb_type == 3) + { + block_size_x = 8; + block_size_y = 16; + + for (block8x8 = 0; block8x8 < 2; block8x8++) + { + i = block8x8<<1; + j = 0; + pred_dir = currMB->b8x8[block8x8].pdir; + perform_mc_concealment(currMB, decoder, PLANE_Y, enc_pic, pred_dir, 3, 3, enc_pic->mv_info, i, j, block_size_x, block_size_y); + } + } + else + { + for (block8x8 = 0; block8x8 < 4; block8x8++) + { + mv_mode = currMB->b8x8[block8x8].mode; + pred_dir = currMB->b8x8[block8x8].pdir; + + k_start = (block8x8 << 2); + k_inc = (mv_mode == 5) ? 2 : 1; + + if (mv_mode == 0) + { + k_end = k_start + 1; + block_size_x = 8; + block_size_y = 8; + } + else + { + k_end = (mv_mode == 4) ? k_start + 1 : ((mv_mode == 7) ? k_start + 4 : k_start + k_inc + 1); + block_size_x = ( mv_mode == 5 || mv_mode == 4 ) ? 8 : 4; + block_size_y = ( mv_mode == 6 || mv_mode == 4 ) ? 8 : 4; + } + + for (k = k_start; k < k_end; k += k_inc) + { + i = (decode_block_scan[k] & 3); + j = ((decode_block_scan[k] >> 2) & 3); + perform_mc_concealment(currMB, decoder, PLANE_Y, enc_pic, pred_dir, mv_mode, mv_mode, enc_pic->mv_info, i, j, block_size_x, block_size_y); + } + } + } +} + + +/*! + ************************************************************************************* + * \brief + * Performs copy error concealment for macroblocks with errors. + * Note: Currently assumes that the reference picture lists remain the same for all + * slices of a picture. + * + ************************************************************************************* + */ +void copy_conceal_picture(VideoParameters *p_Vid, StorablePicture *enc_pic, int decoder) +{ + unsigned int mb; + Macroblock* currMB; + int mb_error; + byte** mb_error_map = enc_pic->de_mem->mb_error_map[decoder]; + StorablePicture* refPic; + BlockPos *PicPos = p_Vid->PicPos; + + refPic = find_nearest_ref_picture(p_Vid->p_Dpb_layer[0], enc_pic->poc); //Used for concealment if actual reference pic is not known. + for (mb = 0; mb < p_Vid->PicSizeInMbs; mb++) + { + currMB = &p_Vid->mb_data[mb]; + currMB->mb_x = PicPos[mb].x; + currMB->mb_y = PicPos[mb].y; + mb_error = mb_error_map[currMB->mb_y][currMB->mb_x]; + if (mb_error) + { + currMB->block_x = currMB ->mb_x << 2; + currMB->block_y = currMB->mb_y << 2; + currMB->pix_x = currMB->block_x << 2; + currMB->pix_y = currMB->block_y << 2; + copy_conceal_mb(currMB, enc_pic, decoder, mb_error, refPic); + } + } +} + +/****************************************************************************************** +* +* Perform copy error concealment for macroblock. +* +******************************************************************************************* +*/ +static void copy_conceal_mb(Macroblock* currMB, StorablePicture *enc_pic, int decoder, int mb_error, StorablePicture* refPic) +{ + Slice *currSlice = currMB->p_Slice; + int j, i, i0 = currMB->pix_x; + imgpel** concealed_img = &(enc_pic->de_mem->p_dec_img[0][decoder][currMB->pix_y]); + imgpel** ref_img; + + if (mb_error == 1 || (mb_error != 3 && currMB->mb_type > P8x8)) //All partitions lost, or intra mb lost + { + if (refPic != NULL) //Use nearest reference picture for concealment + { + ref_img = &(refPic->de_mem->p_dec_img[0][decoder][currMB->pix_y]); + for (j = 0; j < MB_BLOCK_SIZE; j++) + { + memcpy(&(concealed_img[j][i0]), &(ref_img[j][i0]), sizeof(imgpel)*MB_BLOCK_SIZE); + } + } + else //No ref picture available + { + for(j=0; jslice_type != I_SLICE && currMB->mb_type < P8x8) //Only partition 3 lost, and P/B macroblock + { + VideoParameters *p_Vid = currMB->p_Vid; + get_predicted_concealment_mb(currMB, enc_pic, decoder); + for(j = 0; j < MB_BLOCK_SIZE; j++) + { + memcpy(&(concealed_img[j][i0]), &(p_Vid->p_decs->dec_mb_pred[decoder][j][0]), MB_BLOCK_SIZE * sizeof(imgpel)); + } + } +} diff --git a/lencod/src/explicit_gop.c b/lencod/src/explicit_gop.c new file mode 100644 index 0000000..1fe8c1f --- /dev/null +++ b/lencod/src/explicit_gop.c @@ -0,0 +1,216 @@ +/*! + ************************************************************************************* + * \file explicit_gop.c + * + * \brief + * Code for explicit gop support and hierarchical coding. + * + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * - Alexis Michael Tourapis + ************************************************************************************* + */ + +#include "contributors.h" + +#include +#include +#include "global.h" + +#include "explicit_gop.h" +#include "image.h" +#include "nalucommon.h" +#include "report.h" + +/*! +************************************************************************ +* \brief +* Initialization of GOP structure. +* +************************************************************************ +*/ +void init_gop_structure(VideoParameters *p_Vid, InputParameters *p_Inp) +{ + int max_gopsize = p_Inp->NumberBFrames; + + p_Vid->gop_structure = calloc(imax(10,max_gopsize), sizeof (GOP_DATA)); // +1 for reordering + if (NULL==p_Vid->gop_structure) + no_mem_exit("init_gop_structure: gop_structure"); +} + + +/*! +************************************************************************ +* \brief +* Clear GOP structure +************************************************************************ +*/ +void clear_gop_structure(VideoParameters *p_Vid) +{ + if (p_Vid->gop_structure) + free(p_Vid->gop_structure); +} + + +/*! +************************************************************************ +* \brief +* Interpret GOP struct from input parameters +************************************************************************ +*/ +void interpret_gop_structure(VideoParameters *p_Vid, InputParameters *p_Inp) +{ + int nLength = (int) strlen(p_Inp->ExplicitHierarchyFormat); + int i =0, k, dqp, display_no; + int slice_read =0, order_read = 0, stored_read = 0, qp_read =0; + int coded_frame = 0; + int tlyr, temporal_layer_read = 0; + + if (nLength > 0) + { + + for (i = 0; i < nLength ; i++) + { + //! First lets read slice type + if (slice_read == 0) + { + switch (p_Inp->ExplicitHierarchyFormat[i]) + { + case 'P': + case 'p': + p_Vid->gop_structure[coded_frame].slice_type = P_SLICE; + break; + case 'B': + case 'b': + p_Vid->gop_structure[coded_frame].slice_type = B_SLICE; + break; + case 'I': + case 'i': + p_Vid->gop_structure[coded_frame].slice_type = I_SLICE; + break; + default: + snprintf(errortext, ET_SIZE, "Slice Type invalid in ExplicitHierarchyFormat param. Please check configuration file."); + error (errortext, 400); + break; + } + slice_read = 1; + } + else + { + //! Next is Display Order + if (order_read == 0) + { + if (isdigit((int)(*(p_Inp->ExplicitHierarchyFormat+i)))) + { + sscanf(p_Inp->ExplicitHierarchyFormat+i,"%d",&display_no); + p_Vid->gop_structure[coded_frame].display_no = display_no; + order_read = 1; + if (display_no < 0 || display_no >= p_Inp->NumberBFrames) + { + snprintf(errortext, ET_SIZE, "Invalid Frame Order value. Frame position needs to be in [0,%d] range.",p_Inp->NumberBFrames - 1); + error (errortext, 400); + } + for (k=0;kgop_structure[k].display_no == display_no) + { + snprintf(errortext, ET_SIZE, "Frame Order value %d in frame %d already used for enhancement frame %d.",display_no,coded_frame,k); + error (errortext, 400); + } + } + } + else + { + snprintf(errortext, ET_SIZE, "Slice Type needs to be followed by Display Order. Please check configuration file."); + error (errortext, 400); + } + } + else if (order_read == 1) + { + if (stored_read == 0 && !(isdigit((int)(*(p_Inp->ExplicitHierarchyFormat+i))))) + { + switch (p_Inp->ExplicitHierarchyFormat[i]) + { + case 'E': + case 'e': + p_Vid->gop_structure[coded_frame].reference_idc = NALU_PRIORITY_DISPOSABLE; + p_Vid->gop_structure[coded_frame].hierarchy_layer = 0; + break; + case 'R': + case 'r': + p_Vid->gop_structure[coded_frame].reference_idc= NALU_PRIORITY_LOW; + p_Vid->gop_structure[coded_frame].hierarchy_layer = 1; + break; + default: + snprintf(errortext, ET_SIZE, "Reference_IDC invalid in ExplicitHierarchyFormat param. Please check configuration file."); + error (errortext, 400); + break; + } + stored_read = 1; + } + else if (stored_read == 1 && qp_read == 0) + { + if (isdigit((int)(*(p_Inp->ExplicitHierarchyFormat+i)))) + { + sscanf(p_Inp->ExplicitHierarchyFormat+i,"%d",&dqp); + + p_Vid->gop_structure[coded_frame].slice_qp_off = iClip3(-p_Vid->bitdepth_luma_qp_scale, 51, dqp); + qp_read = 1; + } + else + { + snprintf(errortext, ET_SIZE, "Reference_IDC needs to be followed by QP. Please check configuration file."); + error (errortext, 400); + } + } + else if (stored_read == 1 && qp_read == 1 && temporal_layer_read == 0) + { + if (!(isdigit((int)(*(p_Inp->ExplicitHierarchyFormat+i))))) + { + if (p_Inp->ExplicitHierarchyFormat[i] == 't' || p_Inp->ExplicitHierarchyFormat[i] == 'T') + { + p_Vid->gop_structure[coded_frame].temporal_layer = 0; + if (isdigit((int)(*(p_Inp->ExplicitHierarchyFormat+i+1)))) + { + sscanf(p_Inp->ExplicitHierarchyFormat+i+1,"%d",&tlyr); + + p_Vid->gop_structure[coded_frame].temporal_layer = tlyr; + i++; // temporal layer number is specified + } + } + else + { + i--; // temporal layer is optional and it is not specified + } + } + temporal_layer_read = 1; + } + else if (stored_read == 1 && qp_read == 1 && temporal_layer_read == 1 && !(isdigit((int)(*(p_Inp->ExplicitHierarchyFormat+i)))) && (i < nLength - 3)) + + { + stored_read =0; + qp_read=0; + order_read=0; + slice_read=0; + temporal_layer_read=0; + i--; + coded_frame ++; + if (coded_frame >= p_Inp->NumberBFrames ) + { + snprintf(errortext, ET_SIZE, "Total number of frames in Enhancement GOP need to be fewer or equal to NumberBFrames parameter."); + error (errortext, 400); + } + } + } + } + } + } + else + { + snprintf(errortext, ET_SIZE, "ExplicitHierarchyFormat is empty. Please check configuration file."); + error (errortext, 400); + } + + p_Inp->NumberBFrames = coded_frame + 1; +} + diff --git a/lencod/src/explicit_seq.c b/lencod/src/explicit_seq.c new file mode 100644 index 0000000..e8bb69d --- /dev/null +++ b/lencod/src/explicit_seq.c @@ -0,0 +1,329 @@ +/*! + ************************************************************************************* + * \file explicit_seq.c + * + * \brief + * Support for explicit coding order/structure support. + * + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * - Alexis Michael Tourapis + ************************************************************************************* + */ + +#include "contributors.h" +#include "global.h" +#include "report.h" +#include "explicit_seq.h" + +/*! +************************************************************************ +* \brief +* Read one Text Field +************************************************************************ +*/ +static int ReadTextField(FILE *expSeqFile, char* keyword) +{ + char readline [100]; + char word[64]; + int err = -1; + int read_line = 0; // read up to 5 lines to detect data + do + { + read_line++; + // let us read a full line of data + if (NULL == fgets(readline, 100, expSeqFile)) + error ("error parsing explicit sequence file", 500); + err = sscanf(readline,"%s",word); + if (err == -1) + break; + if (strcasecmp(word, keyword) != 0) + err = - 1; + + if (feof(expSeqFile)) // move to the end of the line + break; + } + while (err == - 1 && read_line < 6); + // Lets keep err disabled. This allows us to continue encoding even + // when input file is empty or reaches end. + /* + if (err != 1) + { + printf("Error while reading text \"%s\" from input file.\n", keyword); + report_stats_on_error(); + } + */ + return err; +} + +/*! +************************************************************************ +* \brief +* Read one Field of Integer type +************************************************************************ +*/ +void ReadIntField(FILE *expSeqFile, char* format, char* keyword, int* value) +{ + char readline [100]; + char word[64]; + int err = -1; + int read_line = 0; // read up to 5 lines to detect data + + do + { + read_line++; + if (NULL == fgets(readline, 100, expSeqFile)) + error ("error parsing explicit sequence file", 500); + err = sscanf(readline, format, word, value); + if (feof(expSeqFile)) + break; + } + while (strcasecmp(word, keyword) != 0 && read_line < 6); + + if (err != 2 || strcasecmp(word, keyword) != 0) + { + printf("Error while reading %s.\n", keyword); + report_stats_on_error(); + } +} + +/*! +************************************************************************ +* \brief +* Read one Field of String type +************************************************************************ +*/ +void ReadCharField(FILE *expSeqFile, char* format, char* keyword, char* value) +{ + char readline [100]; + char word[64]; + int err = -1; + int read_line = 0; // read up to 5 lines to detect data + + do + { + read_line++; + if (NULL == fgets(readline, 100, expSeqFile)) + error ("error parsing explicit sequence file", 500); + err = sscanf(readline, format, word, value); + + while (!feof(expSeqFile)) // move to the end of the line + break; + } + while (strcasecmp(word, keyword) != 0 && read_line < 6); + + if (err != 2 || strcasecmp(word, keyword) != 0) + { + printf("Error while reading %s.\n", keyword); + report_stats_on_error(); + } +} + + +static void ParseSliceType(char *slice_type, ExpFrameInfo *info, int coding_index) +{ + if ( strcasecmp(slice_type, "P") == 0 ) + { + info->slice_type = P_SLICE; + } + else if ( strcasecmp(slice_type, "B") == 0 ) + { + info->slice_type = B_SLICE; + } + else if ( strcasecmp(slice_type, "I") == 0 ) + { + info->slice_type = I_SLICE; + } + else if ( strcasecmp(slice_type, "SP") == 0 ) + { + info->slice_type = SP_SLICE; + } + else if ( strcasecmp(slice_type, "SI") == 0 ) + { + info->slice_type = SI_SLICE; + } + else + { + printf("ReadExplicitSeqFile : invalid slice type\n"); + report_stats_on_error(); + } + + if (coding_index == 0 && info->slice_type != I_SLICE) + { + printf("ReadExplicitSeqFile : First coded picture needs to be Intra.\n"); + report_stats_on_error(); + } +} + +static void ParseReferenceIDC(int reference_idc, int coding_index) +{ + if ( reference_idc < NALU_PRIORITY_DISPOSABLE || reference_idc > NALU_PRIORITY_HIGHEST) + { + printf("ReadExplicitSeqFile : Invalid reference indicator \n"); + report_stats_on_error(); + } + + if (coding_index == 0 && reference_idc == NALU_PRIORITY_DISPOSABLE) + { + printf("ReadExplicitSeqFile : First coded picture needs to be a reference picture.\n"); + report_stats_on_error(); + } +} + + +static void ParseSeqNumber(int seq_number, ExpSeqInfo *seq_info, int coding_index) +{ + int i; + for (i = 0; i < imin(coding_index, seq_info->no_frames); i++) + { + if (seq_info->info[i].seq_number == seq_number) + { + printf("ReadExplicitSeqFile : SeqNumber used for current frame already used. Terminating\n"); + report_stats_on_error(); + } + } +} + +/*! +************************************************************************ +* \brief +* Read Frame information +************************************************************************ +*/ +void ReadFrameData(FILE *expSeqFile, ExpSeqInfo *seq_info, int coding_index) +{ + Boolean slice_type_present = FALSE; + Boolean seq_number_present = FALSE; + char readline [100]; + char word[64], value[64]; + int err = -1; + ExpFrameInfo *info = &seq_info->info[coding_index % seq_info->no_frames]; + // Set some defaults + info->reference_idc = NALU_PRIORITY_HIGHEST; + + ReadTextField (expSeqFile, "{"); // Start bracket + do + { + // read one line of data + if (NULL == fgets(readline, 100, expSeqFile)) + error ("error parsing explicit sequence file", 500); + // let us check if ending character reached or error + err = sscanf(readline, "%s : %s", word, value); + if (err == 1) // Check ending characters + { + if (strcasecmp(word, "}") == 0) + break; + else if (strcasecmp(word, "{") == 0) + { + printf("Invalid \"{\" character found. Terminating\n"); + report_stats_on_error(); + } + } + else if (err == 2) // parse different parameters. Input method is flexible and does not fix input order. + { + if (strcasecmp(word, "SeqNumber") == 0) + { + info->seq_number = atoi(value); + ParseSeqNumber(info->seq_number, seq_info, coding_index); + seq_number_present = TRUE; + } + else if (strcasecmp(word, "SliceType") == 0) + { + ParseSliceType(value, info, coding_index); + slice_type_present = TRUE; + } + else if (strcasecmp(word, "IDRPicture") == 0) + info->is_idr = atoi(value); + else if (strcasecmp(word, "Reference") == 0) + { + info->reference_idc = atoi(value); + ParseReferenceIDC(info->reference_idc, coding_index); + } + } + } + while (!feof(expSeqFile)); + + if (slice_type_present == FALSE || seq_number_present == FALSE) + { + printf("Sequence info file does not contain all mandatory info (SeqNumber or SliceType). Terminating.\n"); + report_stats_on_error(); + } +} + +/*! + ************************************************************************ + * \brief + * Read one picture from explicit sequence information file + ************************************************************************ + */ +void ReadExplicitSeqFile(ExpSeqInfo *seq_info, FILE *expSFile, int coding_index) +{ + int frm_header = ReadTextField(expSFile, "Frame"); + + if (frm_header != -1) + { + ReadFrameData (expSFile, seq_info, coding_index); + } + else + { + printf("ReadExplicitSeqFile : No more data. \n"); + report_stats_on_error(); + } +} + +/*! + ************************************************************************ + * \brief + * Open explicit sequence information file + ************************************************************************ + */ +void OpenExplicitSeqFile(VideoParameters *p_Vid, InputParameters *p_Inp) +{ + int frm_count = 0; + p_Vid->expSFile = fopen(p_Inp->ExplicitSeqFile, "r"); + if (p_Vid->expSFile == NULL) + { + printf("ERROR while opening the explicit sequence information file.\n"); + report_stats_on_error(); + } + + if (ReadTextField(p_Vid->expSFile, "Sequence") == -1) + { + printf("Sequence info file is of invalid format. Terminating\n"); + report_stats_on_error(); + } + else + { + ReadIntField (p_Vid->expSFile, "%s : %d", "FrameCount", &frm_count); + if (frm_count > 0) + { + p_Vid->expSeq = (ExpSeqInfo *) malloc(sizeof(ExpSeqInfo)); + p_Vid->expSeq->no_frames = frm_count; + p_Vid->expSeq->info = (ExpFrameInfo *) calloc(frm_count, sizeof(ExpFrameInfo)); + } + else + { + printf("Invalid FrameCount in Sequence info file. Terminating\n"); + report_stats_on_error(); + } + } +} + +/*! +************************************************************************ +* \brief +* Close explicit sequence information file +************************************************************************ +*/ +void CloseExplicitSeqFile(VideoParameters *p_Vid) +{ + if (p_Vid->expSFile != NULL) + fclose(p_Vid->expSFile); + if (p_Vid->expSeq != NULL) + { + free(p_Vid->expSeq->info); + free(p_Vid->expSeq); + } +} + + diff --git a/lencod/src/filehandle.c b/lencod/src/filehandle.c new file mode 100644 index 0000000..9ac7ab8 --- /dev/null +++ b/lencod/src/filehandle.c @@ -0,0 +1,277 @@ + +/*! +************************************************************************************** +* \file +* filehandle.c +* \brief +* Start and terminate sequences +* \author +* Main contributors (see contributors.h for copyright, address and affiliation details) +* - Thomas Stockhammer +* - Detlev Marpe +*************************************************************************************** +*/ + +#include "contributors.h" + +#include "global.h" +#include "enc_statistics.h" + +#include "rtp.h" +#include "annexb.h" + +#include "annexb_split.h" + +#include "parset.h" +#include "mbuffer.h" + + +/*! +************************************************************************ +* \brief +* Error handling procedure. Print error message to stdout and exit +* with supplied code. +* \param text +* Error message +* \param code +* Exit code +************************************************************************ +*/ +void error(char *text, int code) +{ + fprintf(stdout, "%s\n", text); + flush_dpb(p_Enc->p_Vid->p_Dpb_layer[0], &p_Enc->p_Inp->output); + flush_dpb(p_Enc->p_Vid->p_Dpb_layer[1], &p_Enc->p_Inp->output); +#if (_DEBUG) +#if( WIN32 ) + system("pause"); +#endif +#endif + exit(code); +} + +/*! +************************************************************************ +* \brief +* This function generates and writes the PPS +* +************************************************************************ +*/ +int write_PPS(VideoParameters *p_Vid, int len, int PPS_id) +{ + NALU_t *nalu; + nalu = NULL; + nalu = GeneratePic_parameter_set_NALU (p_Vid, PPS_id); + len += p_Vid->WriteNALU (p_Vid, nalu, p_Vid->f_out); + FreeNALU (nalu); + + return len; +} + +/*! +************************************************************************ +* \brief +* This function opens the output files and generates the +* appropriate sequence header +************************************************************************ +*/ +int start_sequence(VideoParameters *p_Vid, InputParameters *p_Inp) +{ + int i,len=0, total_pps = (p_Inp->GenerateMultiplePPS) ? 3 : 1; + NALU_t *nalu; + + switch(p_Inp->of_mode) + { + case PAR_OF_ANNEXB: + p_Vid->WriteNALU = WriteAnnexbNALU; + p_Vid->f_out = &p_Vid->f_annexb; + OpenAnnexbFile (p_Inp->outfile, p_Vid->f_out); +#if (MVC_EXTENSION_ENABLE) + OpenAnnexbFileBaseView (p_Vid, p_Inp->outfile_base); + OpenAnnexbFileAuxView (p_Vid, p_Inp->outfile_aux); +#endif + break; + case PAR_OF_RTP: + p_Vid->WriteNALU = WriteRTPNALU; + p_Vid->f_out = &p_Vid->f_rtp; + OpenRTPFile (p_Inp->outfile, p_Vid->f_out); + break; + default: + snprintf(errortext, ET_SIZE, "Output File Mode %d not supported", p_Inp->of_mode); + error(errortext,1); + } + + // Access Unit Delimiter NALU + if ( p_Inp->SendAUD ) + { + len += Write_AUD_NALU(p_Vid); + } + + //! As a sequence header, here we write both sequence and picture + //! parameter sets. As soon as IDR is implemented, this should go to the + //! IDR part, as both parsets have to be transmitted as part of an IDR. + //! An alternative may be to consider this function the IDR start function. + + nalu = NULL; + nalu = GenerateSeq_parameter_set_NALU (p_Vid); + len += p_Vid->WriteNALU (p_Vid, nalu, p_Vid->f_out); + FreeNALU (nalu); + +#if (MVC_EXTENSION_ENABLE) + if(p_Vid->num_of_layers==2) + { + int bits; + nalu = NULL; + nalu = GenerateSubsetSeq_parameter_set_NALU (p_Vid); + bits = p_Vid->WriteNALU (p_Vid, nalu, p_Vid->f_out); + len += bits; + p_Vid->p_Stats->bit_ctr_parametersets_n_v[1] = bits; + FreeNALU (nalu); + } + else + { + p_Vid->p_Stats->bit_ctr_parametersets_n_v[1] = 0; + } +#endif + + //! Lets write now the Picture Parameter sets. Output will be equal to the total number of bits spend here. + for (i=0;iGenerateSEIMessage) + { + nalu = NULL; + nalu = GenerateSEImessage_NALU(p_Inp); + len += p_Vid->WriteNALU (p_Vid, nalu, p_Vid->f_out); + FreeNALU (nalu); + } + + p_Vid->p_Stats->bit_ctr_parametersets_n = len; +#if (MVC_EXTENSION_ENABLE) + if(p_Inp->num_of_views==2) + { + p_Vid->p_Stats->bit_ctr_parametersets_n_v[0] = len - p_Vid->p_Stats->bit_ctr_parametersets_n_v[1]; + } +#endif + return 0; +} + +int end_of_stream(VideoParameters *p_Vid) +{ + int bits; + NALU_t *nalu; + + nalu = AllocNALU(MAXNALUSIZE); + nalu->startcodeprefix_len = 4; + nalu->forbidden_bit = 0; + nalu->nal_reference_idc = 0; + nalu->nal_unit_type = NALU_TYPE_EOSTREAM; + nalu->len = 0; + bits = p_Vid->WriteNALU (p_Vid, nalu, p_Vid->f_out); + + p_Vid->p_Stats->bit_ctr_parametersets += bits; + FreeNALU (nalu); + return bits; +} + +/*! +************************************************************************ +* \brief +* This function opens the output files and generates the +* appropriate sequence header +************************************************************************ +*/ +int rewrite_paramsets(VideoParameters *p_Vid) +{ + InputParameters *p_Inp = p_Vid->p_Inp; + int i,len=0, total_pps = (p_Inp->GenerateMultiplePPS) ? 3 : 1; + NALU_t *nalu; + + // Access Unit Delimiter NALU + if ( p_Inp->SendAUD ) + { + len += Write_AUD_NALU(p_Vid); + } + + //! As a sequence header, here we write both sequence and picture + //! parameter sets. As soon as IDR is implemented, this should go to the + //! IDR part, as both parsets have to be transmitted as part of an IDR. + //! An alternative may be to consider this function the IDR start function. + + nalu = NULL; + nalu = GenerateSeq_parameter_set_NALU (p_Vid); + len += p_Vid->WriteNALU (p_Vid, nalu, p_Vid->f_out); + FreeNALU (nalu); + +#if (MVC_EXTENSION_ENABLE) + if(p_Vid->num_of_layers==2) + { + int bits; + nalu = GenerateSubsetSeq_parameter_set_NALU (p_Vid); + bits = p_Vid->WriteNALU (p_Vid, nalu, p_Vid->f_out); + len += bits; + p_Vid->p_Stats->bit_ctr_parametersets_n_v[1] = bits; + FreeNALU (nalu); + } +#endif + + //! Lets write now the Picture Parameter sets. Output will be equal to the total number of bits spend here. + for (i=0;iGenerateSEIMessage) + { + nalu = NULL; + nalu = GenerateSEImessage_NALU(p_Inp); + len += p_Vid->WriteNALU (p_Vid, nalu, p_Vid->f_out); + FreeNALU (nalu); + } + + p_Vid->p_Stats->bit_ctr_parametersets_n = len; +#if (MVC_EXTENSION_ENABLE) + if(p_Inp->num_of_views==2) + { + p_Vid->p_Stats->bit_ctr_parametersets_n_v[0] = len - p_Vid->p_Stats->bit_ctr_parametersets_n_v[1]; + } +#endif + return 0; +} + +/*! +************************************************************************ +* \brief +* This function terminates the sequence and closes the +* output files +************************************************************************ +*/ +int terminate_sequence(VideoParameters *p_Vid, InputParameters *p_Inp) +{ + // Bitstream *currStream; + + // Mainly flushing of everything + // Add termination symbol, etc. + + switch(p_Inp->of_mode) + { + case PAR_OF_ANNEXB: + CloseAnnexbFile(*p_Vid->f_out); + //! Edited by Joao Carreira +#if (MVC_EXTENSION_ENABLE) + CloseAnnexbFileBaseView(p_Vid); + CloseAnnexbFileAuxView(p_Vid); +#endif + break; + case PAR_OF_RTP: + CloseRTPFile(*p_Vid->f_out); + return 0; + default: + snprintf(errortext, ET_SIZE, "Output File Mode %d not supported", p_Inp->of_mode); + error(errortext,1); + } + return 1; // make lint happy +} + diff --git a/lencod/src/fmo.c b/lencod/src/fmo.c new file mode 100644 index 0000000..ec26877 --- /dev/null +++ b/lencod/src/fmo.c @@ -0,0 +1,717 @@ +/*! + ***************************************************************************** + * + * \file fmo.c + * + * \brief + * Support for Flexible Macroblock Ordering for different Slice Group Modes: MBAmap handling + * + * \date + * 16 June, 2002 Modified April 25, 2004 + * + * \author + * Stephan Wenger stewe@cs.tu-berlin.de + * Dong Wang Dong.Wang@bristol.ac.uk + * + *****************************************************************************/ + +/*! + **************************************************************************** + * Notes by Dong Wang (April 25 2004) + * + * Source codes are modified to support 7 slice group types (fmo modes). + * The functions for generating map are very similar to that in decoder, but have + * a little difference. + * + * The MB map is calculated at the beginning of coding of each picture (frame or field). + * + * 'slice_group_change_cycle' in structure 'VideoParameters' is the syntax in the slice + * header. It's set to be 1 before the initialization of FMO in function code_a_picture(). + * It can be changed every time if needed. + * + **************************************************************************** + */ + +/*! + ***************************************************************************** + * How does a MBAmap look like? + * + * An MBAmap is a one-diemnsional array of ints. Each int + * represents an MB in scan order. A zero or positive value represents + * a slice group ID. Negative values are reserved for future extensions. + * The numbering range for the SliceGroupIDs is 0..7 as per JVT-C167. + * + * This module contains a static variable MBAmap. This is the MBAmap of the + * picture currently coded. It can be accessed only through the access + * functions. + ***************************************************************************** +*/ + +//#define PRINT_FMO_MAPS 1 + + +#include "global.h" + +#include "fmo.h" + + +static void FmoGenerateType0MapUnitMap (VideoParameters * p_Vid, pic_parameter_set_rbsp_t * pps); +static void FmoGenerateType1MapUnitMap (VideoParameters * p_Vid, pic_parameter_set_rbsp_t * pps); +static void FmoGenerateType2MapUnitMap (VideoParameters * p_Vid, pic_parameter_set_rbsp_t * pps); +static void FmoGenerateType3MapUnitMap (VideoParameters * p_Vid, pic_parameter_set_rbsp_t * pps); +static void FmoGenerateType4MapUnitMap (VideoParameters * p_Vid, pic_parameter_set_rbsp_t * pps); +static void FmoGenerateType5MapUnitMap (VideoParameters * p_Vid, pic_parameter_set_rbsp_t * pps); +static void FmoGenerateType6MapUnitMap (VideoParameters * p_Vid, pic_parameter_set_rbsp_t * pps); + + +static int FmoGenerateMapUnitToSliceGroupMap (VideoParameters * p_Vid, pic_parameter_set_rbsp_t * pps); +static int FmoGenerateMBAmap (VideoParameters * p_Vid, seq_parameter_set_rbsp_t* sps); + + +/*! + ************************************************************************ + * \brief + * Generates p_Vid->MapUnitToSliceGroupMap + * + * \param p_Vid + * Image Parameter to be used for map generation + * \param pps + * Picture Parameter set to be used for map generation + * + ************************************************************************ + */ +static int FmoGenerateMapUnitToSliceGroupMap (VideoParameters * p_Vid, pic_parameter_set_rbsp_t * pps) +{ + p_Vid->PicSizeInMapUnits = p_Vid->PicHeightInMapUnits * p_Vid->PicWidthInMbs; + + + if (pps->slice_group_map_type == 6) + { + if ((pps->pic_size_in_map_units_minus1+1) != p_Vid->PicSizeInMapUnits) + { + error ("wrong pps->pic_size_in_map_units_minus1 for used SPS and FMO type 6", 500); + } + } + + // allocate memory for p_Vid->MapUnitToSliceGroupMap + if (p_Vid->MapUnitToSliceGroupMap) + free (p_Vid->MapUnitToSliceGroupMap); + + if ((p_Vid->MapUnitToSliceGroupMap = malloc ((p_Vid->PicSizeInMapUnits) * sizeof (byte))) == NULL) + { + printf ("cannot allocated %d bytes for p_Vid->MapUnitToSliceGroupMap , exit\n", (int) ( p_Vid->PicSizeInMapUnits * sizeof (byte))); + exit (-1); + } + + if (pps->num_slice_groups_minus1 == 0) // only one slice group + { + memset (p_Vid->MapUnitToSliceGroupMap, 0, p_Vid->PicSizeInMapUnits * sizeof (byte)); + return 0; + } + + switch (pps->slice_group_map_type) + { + case 0: + FmoGenerateType0MapUnitMap (p_Vid, pps); + break; + case 1: + FmoGenerateType1MapUnitMap (p_Vid, pps); + break; + case 2: + FmoGenerateType2MapUnitMap (p_Vid, pps); + break; + case 3: + FmoGenerateType3MapUnitMap (p_Vid, pps); + break; + case 4: + FmoGenerateType4MapUnitMap (p_Vid, pps); + break; + case 5: + FmoGenerateType5MapUnitMap (p_Vid, pps); + break; + case 6: + FmoGenerateType6MapUnitMap (p_Vid, pps); + break; + default: + printf ("Illegal slice_group_map_type %d , exit \n", pps->slice_group_map_type); + exit (-1); + } + return 0; +} + + +/*! + ************************************************************************ + * \brief + * Generates MBAmap from p_Vid->MapUnitToSliceGroupMap + * + * \param p_Vid + * Image Parameter to be used for map generation + * \param sps + * Sequence Parameter set to be used for map generation + * + ************************************************************************ + */ +static int FmoGenerateMBAmap (VideoParameters * p_Vid, seq_parameter_set_rbsp_t* sps) +{ + unsigned i; + + // allocate memory for p_Vid->MBAmap + if (p_Vid->MBAmap) + free (p_Vid->MBAmap); + + + if ((p_Vid->MBAmap = malloc ((p_Vid->PicSizeInMbs) * sizeof (byte))) == NULL) + { + printf ("cannot allocated %d bytes for p_Vid->MBAmap, exit\n", (int) ((p_Vid->PicSizeInMbs) * sizeof (byte))); + exit (-1); + } + + if ((sps->frame_mbs_only_flag) || p_Vid->field_picture) + { + for (i=0; iPicSizeInMbs; i++) + { + p_Vid->MBAmap[i] = p_Vid->MapUnitToSliceGroupMap [i]; + } + } + else + if (sps->mb_adaptive_frame_field_flag && (! p_Vid->field_picture)) + { + for (i=0; iPicSizeInMbs; i++) + { + p_Vid->MBAmap[i] = p_Vid->MapUnitToSliceGroupMap [i >> 1]; + } + } + else + { + for (i=0; iPicSizeInMbs; i++) + { + p_Vid->MBAmap[i] = p_Vid->MapUnitToSliceGroupMap [(i/(2*p_Vid->PicWidthInMbs))*p_Vid->PicWidthInMbs+(i%p_Vid->PicWidthInMbs)]; + } + } + return 0; +} + + +/*! + ************************************************************************ + * \brief + * FMO initialization: Generates p_Vid->MapUnitToSliceGroupMap and p_Vid->MBAmap. + * + * \param p_Vid + * Image Parameter to be used for map generation + * \param pps + * Picture Parameter set to be used for map generation + * \param sps + * Sequence Parameter set to be used for map generation + ************************************************************************ + */ +int FmoInit(VideoParameters * p_Vid, pic_parameter_set_rbsp_t * pps, seq_parameter_set_rbsp_t * sps) +{ + +#ifdef PRINT_FMO_MAPS + unsigned i,j; + int bottom; +#endif + + int k; + for (k = 0; k < MAXSLICEGROUPIDS; k++) + p_Vid->FirstMBInSlice[k] = -1; + + FmoGenerateMapUnitToSliceGroupMap(p_Vid, pps); + FmoGenerateMBAmap(p_Vid, sps); + +#ifdef PRINT_FMO_MAPS + printf("\n"); + printf("FMO Map (Units):\n"); + + for (j=0; jPicHeightInMapUnits; j++) + { + for (i=0; iPicWidthInMbs; i++) + { + printf("%d ",p_Vid->MapUnitToSliceGroupMap [i+j*p_Vid->PicWidthInMbs]); + } + printf("\n"); + } + printf("\n"); + + if(sps->mb_adaptive_frame_field_flag==0) + { + printf("FMO Map (Mb):\n"); + for (j=0; j<(p_Vid->PicSizeInMbs/p_Vid->PicWidthInMbs); j++) + { + for (i=0; iPicWidthInMbs; i++) + { + printf("%d ",p_Vid->MBAmap[i+j*p_Vid->PicWidthInMbs]); + } + printf("\n"); + } + printf("\n"); + } + else + { + printf("FMO Map (Mb in scan order for MBAFF):\n"); + for (j=0; j<(p_Vid->PicSizeInMbs/p_Vid->PicWidthInMbs); j++) + { + for (i=0; iPicWidthInMbs; i++) + { + bottom=(j & 0x01); + printf("%d ",p_Vid->MBAmap[(j-bottom)*p_Vid->PicWidthInMbs+i*2+bottom]); + } + printf("\n"); + + } + printf("\n"); + + } + +#endif + + return 0; +} + + +/*! + ************************************************************************ + * \brief + * Free memory if allocated by FMO functions + ************************************************************************ + */ +void FmoUninit(VideoParameters *p_Vid) +{ + if (p_Vid->MBAmap) + { + free (p_Vid->MBAmap); + p_Vid->MBAmap = NULL; + } + if (p_Vid->MapUnitToSliceGroupMap ) + { + free (p_Vid->MapUnitToSliceGroupMap ); + p_Vid->MapUnitToSliceGroupMap = NULL; + } + +} + + +/*! + ************************************************************************ + * \brief + * Generate interleaved slice group map type MapUnit map (type 0) + * + * \param p_Vid + * Image Parameter to be used for map generation + * \param pps + * Picture Parameter set to be used for map generation + ************************************************************************ + */ +static void FmoGenerateType0MapUnitMap (VideoParameters * p_Vid, pic_parameter_set_rbsp_t * pps ) +{ + unsigned iGroup, j; + unsigned i = 0; + + do + { + for( iGroup = 0; + (iGroup <= pps->num_slice_groups_minus1) && (i < p_Vid->PicSizeInMapUnits); + i += pps->run_length_minus1[iGroup++] + 1) + { + for( j = 0; j <= pps->run_length_minus1[ iGroup ] && i + j < p_Vid->PicSizeInMapUnits; j++ ) + p_Vid->MapUnitToSliceGroupMap [i+j] = (byte) iGroup; + } + } + while( i < p_Vid->PicSizeInMapUnits ); +} + + +/*! + ************************************************************************ + * \brief + * Generate dispersed slice group map type MapUnit map (type 1) + * + * \param p_Vid + * Image Parameter to be used for map generation + * \param pps + * Picture Parameter set to be used for map generation + ************************************************************************ + */ +static void FmoGenerateType1MapUnitMap (VideoParameters * p_Vid, pic_parameter_set_rbsp_t * pps ) +{ + unsigned i; + for( i = 0; i < p_Vid->PicSizeInMapUnits; i++ ) + { + p_Vid->MapUnitToSliceGroupMap [i] = (byte) (((i%p_Vid->PicWidthInMbs)+(((i/p_Vid->PicWidthInMbs)*(pps->num_slice_groups_minus1+1))>>1)) + %(pps->num_slice_groups_minus1+1)); + } +} + +/*! + ************************************************************************ + * \brief + * Generate foreground with left-over slice group map type MapUnit map (type 2) + * + * \param p_Vid + * Image Parameter to be used for map generation + * \param pps + * Picture Parameter set to be used for map generation + ************************************************************************ + */ +static void FmoGenerateType2MapUnitMap (VideoParameters * p_Vid, pic_parameter_set_rbsp_t * pps ) +{ + int iGroup; + unsigned i, x, y; + unsigned yTopLeft, xTopLeft, yBottomRight, xBottomRight; + + for( i = 0; i < p_Vid->PicSizeInMapUnits; i++ ) + p_Vid->MapUnitToSliceGroupMap [ i ] = (byte) pps->num_slice_groups_minus1; + + for( iGroup = pps->num_slice_groups_minus1 - 1 ; iGroup >= 0; iGroup-- ) + { + yTopLeft = pps->top_left[ iGroup ] / p_Vid->PicWidthInMbs; + xTopLeft = pps->top_left[ iGroup ] % p_Vid->PicWidthInMbs; + yBottomRight = pps->bottom_right[ iGroup ] / p_Vid->PicWidthInMbs; + xBottomRight = pps->bottom_right[ iGroup ] % p_Vid->PicWidthInMbs; + for( y = yTopLeft; y <= yBottomRight; y++ ) + for( x = xTopLeft; x <= xBottomRight; x++ ) + p_Vid->MapUnitToSliceGroupMap [ y * p_Vid->PicWidthInMbs + x ] = (byte) iGroup; + } +} + + +/*! + ************************************************************************ + * \brief + * Generate box-out slice group map type MapUnit map (type 3) + * + * \param p_Vid + * Image Parameter to be used for map generation + * \param pps + * Picture Parameter set to be used for map generation + ************************************************************************ + */ +static void FmoGenerateType3MapUnitMap (VideoParameters * p_Vid, pic_parameter_set_rbsp_t * pps ) +{ + unsigned i, k; + int leftBound, topBound, rightBound, bottomBound; + int x, y, xDir, yDir; + int mapUnitVacant; + + unsigned mapUnitsInSliceGroup0 = imin((pps->slice_group_change_rate_minus1 + 1) * p_Vid->slice_group_change_cycle, p_Vid->PicSizeInMapUnits); + + for( i = 0; i < p_Vid->PicSizeInMapUnits; i++ ) + p_Vid->MapUnitToSliceGroupMap [ i ] = 2; + + x = ( p_Vid->PicWidthInMbs - pps->slice_group_change_direction_flag ) / 2; + y = ( p_Vid->PicHeightInMapUnits - pps->slice_group_change_direction_flag ) / 2; + + leftBound = x; + topBound = y; + rightBound = x; + bottomBound = y; + + xDir = pps->slice_group_change_direction_flag - 1; + yDir = pps->slice_group_change_direction_flag; + + for( k = 0; k < p_Vid->PicSizeInMapUnits; k += mapUnitVacant ) + { + mapUnitVacant = ( p_Vid->MapUnitToSliceGroupMap [ y * p_Vid->PicWidthInMbs + x ] == 2 ); + if( mapUnitVacant ) + p_Vid->MapUnitToSliceGroupMap [ y * p_Vid->PicWidthInMbs + x ] = (byte) ( k >= mapUnitsInSliceGroup0 ); + + if( xDir == -1 && x == leftBound ) + { + leftBound = imax( leftBound - 1, 0 ); + x = leftBound; + xDir = 0; + yDir = 2 * pps->slice_group_change_direction_flag - 1; + } + else + if( xDir == 1 && x == rightBound ) + { + rightBound = imin( rightBound + 1, (int)p_Vid->PicWidthInMbs - 1 ); + x = rightBound; + xDir = 0; + yDir = 1 - 2 * pps->slice_group_change_direction_flag; + } + else + if( yDir == -1 && y == topBound ) + { + topBound = imax( topBound - 1, 0 ); + y = topBound; + xDir = 1 - 2 * pps->slice_group_change_direction_flag; + yDir = 0; + } + else + if( yDir == 1 && y == bottomBound ) + { + bottomBound = imin( bottomBound + 1, (int)p_Vid->PicHeightInMapUnits - 1 ); + y = bottomBound; + xDir = 2 * pps->slice_group_change_direction_flag - 1; + yDir = 0; + } + else + { + x = x + xDir; + y = y + yDir; + } + } + +} + +/*! + ************************************************************************ + * \brief + * Generate raster scan slice group map type MapUnit map (type 4) + * + * \param p_Vid + * Image Parameter to be used for map generation + * \param pps + * Picture Parameter set to be used for map generation + ************************************************************************ + */ +static void FmoGenerateType4MapUnitMap (VideoParameters * p_Vid, pic_parameter_set_rbsp_t * pps ) +{ + + unsigned mapUnitsInSliceGroup0 = imin((pps->slice_group_change_rate_minus1 + 1) * p_Vid->slice_group_change_cycle, p_Vid->PicSizeInMapUnits); + unsigned sizeOfUpperLeftGroup = pps->slice_group_change_direction_flag ? ( p_Vid->PicSizeInMapUnits - mapUnitsInSliceGroup0 ) : mapUnitsInSliceGroup0; + + unsigned i; + + for( i = 0; i < p_Vid->PicSizeInMapUnits; i++ ) + if( i < sizeOfUpperLeftGroup ) + p_Vid->MapUnitToSliceGroupMap [ i ] = (byte) pps->slice_group_change_direction_flag; + else + p_Vid->MapUnitToSliceGroupMap [ i ] = (byte) (1 - pps->slice_group_change_direction_flag); + +} + +/*! + ************************************************************************ + * \brief + * Generate wipe slice group map type MapUnit map (type 5) + * + * \param p_Vid + * Image Parameter to be used for map generation + * \param pps + * Picture Parameter set to be used for map generation + ************************************************************************ +*/ +static void FmoGenerateType5MapUnitMap (VideoParameters * p_Vid, pic_parameter_set_rbsp_t * pps ) +{ + + unsigned mapUnitsInSliceGroup0 = imin((pps->slice_group_change_rate_minus1 + 1) * p_Vid->slice_group_change_cycle, p_Vid->PicSizeInMapUnits); + unsigned sizeOfUpperLeftGroup = pps->slice_group_change_direction_flag ? ( p_Vid->PicSizeInMapUnits - mapUnitsInSliceGroup0 ) : mapUnitsInSliceGroup0; + + unsigned i,j, k = 0; + + for( j = 0; j < p_Vid->PicWidthInMbs; j++ ) + for( i = 0; i < p_Vid->PicHeightInMapUnits; i++ ) + if( k++ < sizeOfUpperLeftGroup ) + p_Vid->MapUnitToSliceGroupMap [ i * p_Vid->PicWidthInMbs + j ] = (byte) pps->slice_group_change_direction_flag; + else + p_Vid->MapUnitToSliceGroupMap [ i * p_Vid->PicWidthInMbs + j ] = (byte) (1 - pps->slice_group_change_direction_flag); + +} + +/*! + ************************************************************************ + * \brief + * Generate explicit slice group map type MapUnit map (type 6) + * + * \param p_Vid + * Image Parameter to be used for map generation + * \param pps + * Picture Parameter set to be used for map generation + ************************************************************************ + */ +static void FmoGenerateType6MapUnitMap (VideoParameters * p_Vid, pic_parameter_set_rbsp_t * pps ) +{ + unsigned i; + for (i=0; iPicSizeInMapUnits; i++) + { + p_Vid->MapUnitToSliceGroupMap [i] = (byte) pps->slice_group_id[i]; + } +} + +/*! + ************************************************************************ + * \brief + * FmoStartPicture: initializes FMO at the begin of each new picture + * + * \par Input: + * None + ************************************************************************ + */ +int FmoStartPicture (VideoParameters *p_Vid) +{ + int i; + + assert (p_Vid->MBAmap != NULL); + + for (i=0; iFirstMBInSlice[i] = FmoGetFirstMBOfSliceGroup (p_Vid, i); + return 0; +} + + + +/*! + ************************************************************************ + * \brief + * FmoEndPicture: Ends the Scattered Slices Module (called once + * per picture). + * + * \par Input: + * None + ************************************************************************ + */ +int FmoEndPicture () +{ + // Do nothing + return 0; +} + + +/*! + ************************************************************************ + * \brief + * FmoMB2Slice: Returns SliceID for a given MB + * + * \par Input: + * Macroblock Nr (in scan order) + ************************************************************************ + */ +int FmoMB2SliceGroup (VideoParameters *p_Vid, int mb) +{ + assert (mb < (int)p_Vid->PicSizeInMbs); + assert (p_Vid->MBAmap != NULL); + return p_Vid->MBAmap[mb]; +} + +/*! + ************************************************************************ + * \brief + * FmoGetNextMBBr: Returns the MB-Nr (in scan order) of the next + * MB in the (FMO) Slice, -1 if the SliceGroup is finished + * + * \par Input: + * CurrentMbNr + ************************************************************************ + */ +int FmoGetNextMBNr (VideoParameters *p_Vid, int CurrentMbNr) +{ + + int SliceGroupID = FmoMB2SliceGroup (p_Vid, CurrentMbNr); + + while (++CurrentMbNr<(int)p_Vid->PicSizeInMbs && p_Vid->MBAmap[CurrentMbNr] != SliceGroupID) + ; + + if (CurrentMbNr >= (int)p_Vid->PicSizeInMbs) + return -1; // No further MB in this slice (could be end of picture) + else + return CurrentMbNr; +} + + +/*! + ************************************************************************ + * \brief + * FmoGetNextMBBr: Returns the MB-Nr (in scan order) of the next + * MB in the (FMO) Slice, -1 if the SliceGroup is finished + * + * \par Input: + * CurrentMbNr + ************************************************************************ + */ +int FmoGetPreviousMBNr (VideoParameters *p_Vid, int CurrentMbNr) +{ + + int SliceGroupID = FmoMB2SliceGroup (p_Vid, CurrentMbNr); + CurrentMbNr--; + while (CurrentMbNr>=0 && p_Vid->MBAmap[CurrentMbNr] != SliceGroupID) + CurrentMbNr--; + + if (CurrentMbNr < 0) + return -1; // No previous MB in this slice + else + return CurrentMbNr; +} + + +/*! + ************************************************************************ + * \brief + * FmoGetFirstMBOfSliceGroup: Returns the MB-Nr (in scan order) of the + * next first MB of the Slice group, -1 if no such MB exists + * + * \par Input: + * SliceGroupID: Id of SliceGroup + ************************************************************************ + */ +int FmoGetFirstMBOfSliceGroup (VideoParameters *p_Vid, int SliceGroupID) +{ + int i = 0; + while ((i<(int)p_Vid->PicSizeInMbs) && (FmoMB2SliceGroup (p_Vid, i) != SliceGroupID)) + i++; + + if (i < (int)p_Vid->PicSizeInMbs) + return i; + else + return -1; +} + + +/*! + ************************************************************************ + * \brief + * FmoGetLastCodedMBOfSlice: Returns the MB-Nr (in scan order) of + * the last MB of the slice group + * + * \par Input: + * SliceGroupID + * \par Return + * MB Nr in case of success (is always >= 0) + * -1 if the SliceGroup doesn't exist + ************************************************************************ + */ +int FmoGetLastCodedMBOfSliceGroup (VideoParameters *p_Vid, int SliceGroupID) +{ + int i; + int LastMB = -1; + + for (i=0; i<(int)p_Vid->PicSizeInMbs; i++) + if (FmoMB2SliceGroup (p_Vid, i) == SliceGroupID) + LastMB = i; + return LastMB; +} + + +void FmoSetLastMacroblockInSlice ( VideoParameters *p_Vid, int mb) +{ + // called by terminate_slice(), writes the last processed MB into the + // FirstMBInSlice[MAXSLICEGROUPIDS] array. FmoGetFirstMacroblockInSlice() + // uses this info to identify the first uncoded MB in each slice group + + int currSliceGroup = FmoMB2SliceGroup (p_Vid, mb); + assert (mb >= 0); + mb = FmoGetNextMBNr (p_Vid, mb); // The next (still uncoded) MB, or -1 if SG is finished + p_Vid->FirstMBInSlice[currSliceGroup] = mb; +} + +int FmoGetFirstMacroblockInSlice ( VideoParameters *p_Vid, int SliceGroup) +{ + return p_Vid->FirstMBInSlice[SliceGroup]; + // returns the first uncoded MB in each slice group, -1 if there is no + // more to do in this slice group +} + + +int FmoSliceGroupCompletelyCoded( VideoParameters *p_Vid, int SliceGroupID) +{ + if (FmoGetFirstMacroblockInSlice (p_Vid, SliceGroupID) < 0) // slice group completelty coded or not present + return TRUE; + else + return FALSE; +} + + + diff --git a/lencod/src/get_block_otf.c b/lencod/src/get_block_otf.c new file mode 100644 index 0000000..e86161c --- /dev/null +++ b/lencod/src/get_block_otf.c @@ -0,0 +1,1083 @@ +/*! + ************************************************************************************* + * \file get_block_otf.c + * + * \brief + * get predicted (chroma|luma) block with subpel interpolation computes on-the-fly + * + * + ************************************************************************************* + */ + +#include "global.h" +#include "block.h" + +#include "macroblock.h" +#include "image.h" +#include "mb_access.h" +#include "get_block_otf.h" +#include "refbuf_otf.h" + + +/*! + ************************************************************************ + * \brief + * Integer positions + ************************************************************************ + */ + +static void get_block_00(imgpel *block, imgpel *cur_img, int block_size_y, int block_size_x, int shift_x) +{ + int j; + + for (j = 0; j < block_size_y; j += 1) + { + memcpy(block, cur_img, block_size_x * sizeof(imgpel)); + block += block_size_x ; + cur_img += shift_x; + } +} + +/*! + ************************************************************************ + * \brief + * Qpel (1,0) horizontal + ************************************************************************ + */ +static void get_luma_10(imgpel *block, imgpel **cur_imgY, int block_size_y, int block_size_x, int x_pos , int max_imgpel_value) +{ + imgpel *p0, *p1, *p2, *p3, *p4, *p5; + imgpel *orig_line, *cur_line; + int i, j; + int result; + + for (j = 0; j < block_size_y; j++) + { + cur_line = &(cur_imgY[j][x_pos]); + p0 = &cur_imgY[j][x_pos - 2]; + p1 = p0 + 1; + p2 = p1 + 1; + p3 = p2 + 1; + p4 = p3 + 1; + p5 = p4 + 1; + orig_line = block+j*block_size_x; + + for (i = 0; i < block_size_x; i++) + { + result = (*(p0++) + *(p5++)) - 5 * (*(p1++) + *(p4++)) + 20 * (*(p2++) + *(p3++)); + + *orig_line = (imgpel) iClip1(max_imgpel_value, ((result + 16)>>5)); + *orig_line = (imgpel) ((*orig_line + *(cur_line++) + 1 ) >> 1); + orig_line++; + } + } +} + +/*! + ************************************************************************ + * \brief + * Half horizontal + ************************************************************************ + */ +static void get_luma_20(imgpel *block, imgpel **cur_imgY, int block_size_y, int block_size_x, int x_pos , int max_imgpel_value) + { + imgpel *p0, *p1, *p2, *p3, *p4, *p5; + imgpel *orig_line; + int i, j; + int result; + + for (j = 0; j < block_size_y; j++) + { + p0 = &cur_imgY[j][x_pos - 2]; + p1 = p0 + 1; + p2 = p1 + 1; + p3 = p2 + 1; + p4 = p3 + 1; + p5 = p4 + 1; + orig_line = block + j*block_size_x; + + for (i = 0; i < block_size_x; i++) + { + result = (*(p0++) + *(p5++)) - 5 * (*(p1++) + *(p4++)) + 20 * (*(p2++) + *(p3++)); + + *orig_line++ = (imgpel) iClip1(max_imgpel_value, ((result + 16)>>5)); + } + } +} + +/*! + ************************************************************************ + * \brief + * Qpel (3,0) horizontal + ************************************************************************ + */ +static void get_luma_30(imgpel *block, imgpel **cur_imgY, int block_size_y, int block_size_x, int x_pos , int max_imgpel_value) +{ + imgpel *p0, *p1, *p2, *p3, *p4, *p5; + imgpel *orig_line, *cur_line; + int i, j; + int result; + + for (j = 0; j < block_size_y; j++) + { + cur_line = &(cur_imgY[j][x_pos + 1]); + p0 = &cur_imgY[j][x_pos - 2]; + p1 = p0 + 1; + p2 = p1 + 1; + p3 = p2 + 1; + p4 = p3 + 1; + p5 = p4 + 1; + orig_line = block + j*block_size_x ; + + for (i = 0; i < block_size_x; i++) + { + result = (*(p0++) + *(p5++)) - 5 * (*(p1++) + *(p4++)) + 20 * (*(p2++) + *(p3++)); + + *orig_line = (imgpel) iClip1(max_imgpel_value, ((result + 16)>>5)); + *orig_line = (imgpel) ((*orig_line + *(cur_line++) + 1 ) >> 1); + orig_line++; + } + } +} + + +/*! + ************************************************************************ + * \brief + * Qpel vertical (0, 1) + ************************************************************************ + */ +static void get_luma_01(imgpel *block, imgpel **cur_imgY, int block_size_y, int block_size_x, int x_pos, int shift_x, int max_imgpel_value) +{ + imgpel *p0, *p1, *p2, *p3, *p4, *p5; + imgpel *orig_line, *cur_line; + int i, j; + int result; + int jj = 0; + p0 = &(cur_imgY[ - 2][x_pos]); + for (j = 0; j < block_size_y; j++) + { + p1 = p0 + shift_x; + p2 = p1 + shift_x; + p3 = p2 + shift_x; + p4 = p3 + shift_x; + p5 = p4 + shift_x; + orig_line = block + j*block_size_x ; + cur_line = &(cur_imgY[jj++][x_pos]); + + for (i = 0; i < block_size_x; i++) + { + result = (*(p0++) + *(p5++)) - 5 * (*(p1++) + *(p4++)) + 20 * (*(p2++) + *(p3++)); + + *orig_line = (imgpel) iClip1(max_imgpel_value, ((result + 16)>>5)); + *orig_line = (imgpel) ((*orig_line + *(cur_line++) + 1 ) >> 1); + orig_line++; + } + p0 = p1 - block_size_x; + } +} + +/*! + ************************************************************************ + * \brief + * Half vertical + ************************************************************************ + */ +static void get_luma_02(imgpel *block, imgpel **cur_imgY, int block_size_y, int block_size_x, int x_pos, int shift_x, int max_imgpel_value) +{ + imgpel *p0, *p1, *p2, *p3, *p4, *p5; + imgpel *orig_line; + int i, j; + int result; + p0 = &(cur_imgY[ - 2][x_pos]); + for (j = 0; j < block_size_y; j++) + { + p1 = p0 + shift_x; + p2 = p1 + shift_x; + p3 = p2 + shift_x; + p4 = p3 + shift_x; + p5 = p4 + shift_x; + orig_line = block + j*block_size_x ; + + for (i = 0; i < block_size_x; i++) + { + result = (*(p0++) + *(p5++)) - 5 * (*(p1++) + *(p4++)) + 20 * (*(p2++) + *(p3++)); + + *orig_line++ = (imgpel) iClip1(max_imgpel_value, ((result + 16)>>5)); + } + p0 = p1 - block_size_x; + } +} + + +/*! + ************************************************************************ + * \brief + * Qpel vertical (0, 3) + ************************************************************************ + */ +static void get_luma_03(imgpel *block, imgpel **cur_imgY, int block_size_y, int block_size_x, int x_pos, int shift_x, int max_imgpel_value) +{ + imgpel *p0, *p1, *p2, *p3, *p4, *p5; + imgpel *orig_line, *cur_line; + int i, j; + int result; + int jj = 1; + + p0 = &(cur_imgY[ -2][x_pos]); + for (j = 0; j < block_size_y; j++) + { + p1 = p0 + shift_x; + p2 = p1 + shift_x; + p3 = p2 + shift_x; + p4 = p3 + shift_x; + p5 = p4 + shift_x; + orig_line = block + j*block_size_x ; + cur_line = &(cur_imgY[jj++][x_pos]); + + for (i = 0; i < block_size_x; i++) + { + result = (*(p0++) + *(p5++)) - 5 * (*(p1++) + *(p4++)) + 20 * (*(p2++) + *(p3++)); + + *orig_line = (imgpel) iClip1(max_imgpel_value, ((result + 16)>>5)); + *orig_line = (imgpel) ((*orig_line + *(cur_line++) + 1 ) >> 1); + orig_line++; + } + p0 = p1 - block_size_x; + } +} + +/*! + ************************************************************************ + * \brief + * Hpel horizontal, Qpel vertical (2, 1) + ************************************************************************ + */ +static void get_luma_21(imgpel *block, imgpel **cur_imgY, int *tmp_res, int block_size_y, int block_size_x, int x_pos, int max_imgpel_value) +{ + int i, j; + /* Vertical & horizontal interpolation */ + int *tmp_line; + imgpel *p0, *p1, *p2, *p3, *p4, *p5; + int *x0, *x1, *x2, *x3, *x4, *x5; + imgpel *orig_line; + int result; + + int jj = -2; + + for (j = 0; j < block_size_y + 5; j++) + { + p0 = &cur_imgY[jj++][x_pos - 2]; + p1 = p0 + 1; + p2 = p1 + 1; + p3 = p2 + 1; + p4 = p3 + 1; + p5 = p4 + 1; + tmp_line = tmp_res + j*block_size_x ; + + for (i = 0; i < block_size_x; i++) + { + *(tmp_line++) = (*(p0++) + *(p5++)) - 5 * (*(p1++) + *(p4++)) + 20 * (*(p2++) + *(p3++)); + } + } + + jj = 2; + for (j = 0; j < block_size_y; j++) + { + tmp_line = tmp_res+ jj*block_size_x; jj++; + x0 = tmp_res + j*block_size_x ; + x1 = tmp_res + (j + 1)*block_size_x; + x2 = tmp_res + (j + 2)*block_size_x; + x3 = tmp_res + (j + 3)*block_size_x; + x4 = tmp_res + (j + 4)*block_size_x; + x5 = tmp_res + (j + 5)*block_size_x; + orig_line = block + j*block_size_x ; + + for (i = 0; i < block_size_x; i++) + { + result = (*x0++ + *x5++) - 5 * (*x1++ + *x4++) + 20 * (*x2++ + *x3++); + + *orig_line = (imgpel) iClip1(max_imgpel_value, ((result + 512)>>10)); + *orig_line = (imgpel) ((*orig_line + iClip1(max_imgpel_value, ((*(tmp_line++) + 16) >> 5)) + 1 )>> 1); + orig_line++; + } + } +} + +/*! + ************************************************************************ + * \brief + * Hpel horizontal, Hpel vertical (2, 2) + ************************************************************************ + */ +static void get_luma_22(imgpel *block, imgpel **cur_imgY, int *tmp_res, int block_size_y, int block_size_x, int x_pos, int max_imgpel_value) +{ + int i, j; + /* Vertical & horizontal interpolation */ + int *tmp_line; + imgpel *p0, *p1, *p2, *p3, *p4, *p5; + int *x0, *x1, *x2, *x3, *x4, *x5; + imgpel *orig_line; + int result; + + int jj = - 2; + + for (j = 0; j < block_size_y + 5; j++) + { + p0 = &cur_imgY[jj++][x_pos - 2]; + p1 = p0 + 1; + p2 = p1 + 1; + p3 = p2 + 1; + p4 = p3 + 1; + p5 = p4 + 1; + tmp_line = tmp_res + j*block_size_x; + + for (i = 0; i < block_size_x; i++) + { + *(tmp_line++) = (*(p0++) + *(p5++)) - 5 * (*(p1++) + *(p4++)) + 20 * (*(p2++) + *(p3++)); + } + } + + for (j = 0; j < block_size_y; j++) + { + x0 = tmp_res + j*block_size_x ; + x1 = tmp_res + (j + 1)*block_size_x; + x2 = tmp_res + (j + 2)*block_size_x; + x3 = tmp_res + (j + 3)*block_size_x; + x4 = tmp_res + (j + 4)*block_size_x; + x5 = tmp_res + (j + 5)*block_size_x; + orig_line = block + j*block_size_x; + + for (i = 0; i < block_size_x; i++) + { + result = (*x0++ + *x5++) - 5 * (*x1++ + *x4++) + 20 * (*x2++ + *x3++); + + *(orig_line++) = (imgpel) iClip1(max_imgpel_value, ((result + 512)>>10)); + } + } +} + +/*! + ************************************************************************ + * \brief + * Hpel horizontal, Qpel vertical (2, 3) + ************************************************************************ + */ +static void get_luma_23(imgpel *block, imgpel **cur_imgY, int *tmp_res, int block_size_y, int block_size_x, int x_pos, int max_imgpel_value) +{ + int i, j; + /* Vertical & horizontal interpolation */ + int *tmp_line; + imgpel *p0, *p1, *p2, *p3, *p4, *p5; + int *x0, *x1, *x2, *x3, *x4, *x5; + imgpel *orig_line; + int result; + + int jj = -2; + + for (j = 0; j < block_size_y + 5; j++) + { + p0 = &cur_imgY[jj++][x_pos - 2]; + p1 = p0 + 1; + p2 = p1 + 1; + p3 = p2 + 1; + p4 = p3 + 1; + p5 = p4 + 1; + tmp_line = tmp_res + j*block_size_x; + + for (i = 0; i < block_size_x; i++) + { + *(tmp_line++) = (*(p0++) + *(p5++)) - 5 * (*(p1++) + *(p4++)) + 20 * (*(p2++) + *(p3++)); + } + } + + jj = 3; + for (j = 0; j < block_size_y; j++) + { + tmp_line = tmp_res+ jj*block_size_x; jj++; + x0 = tmp_res + j*block_size_x ; + x1 = tmp_res + (j + 1)*block_size_x; + x2 = tmp_res + (j + 2)*block_size_x; + x3 = tmp_res + (j + 3)*block_size_x; + x4 = tmp_res + (j + 4)*block_size_x; + x5 = tmp_res + (j + 5)*block_size_x; + orig_line = block + j*block_size_x; + + for (i = 0; i < block_size_x; i++) + { + result = (*x0++ + *x5++) - 5 * (*x1++ + *x4++) + 20 * (*x2++ + *x3++); + + *orig_line = (imgpel) iClip1(max_imgpel_value, ((result + 512)>>10)); + *orig_line = (imgpel) ((*orig_line + iClip1(max_imgpel_value, ((*(tmp_line++) + 16) >> 5)) + 1 )>> 1); + orig_line++; + } + } +} + +/*! + ************************************************************************ + * \brief + * Qpel horizontal, Hpel vertical (1, 2) + ************************************************************************ + */ +static void get_luma_12(imgpel *block, imgpel **cur_imgY, int *tmp_res, int block_size_y, int block_size_x, int x_pos, int shift_x, int max_imgpel_value) +{ + int i, j; + int *tmp_line; + imgpel *p0, *p1, *p2, *p3, *p4, *p5; + int *x0, *x1, *x2, *x3, *x4, *x5; + imgpel *orig_line; + int result; + + p0 = &(cur_imgY[ -2][x_pos - 2]); + for (j = 0; j < block_size_y; j++) + { + p1 = p0 + shift_x; + p2 = p1 + shift_x; + p3 = p2 + shift_x; + p4 = p3 + shift_x; + p5 = p4 + shift_x; + tmp_line = tmp_res + j*(block_size_x+5); + + for (i = 0; i < block_size_x + 5; i++) + { + *(tmp_line++) = (*(p0++) + *(p5++)) - 5 * (*(p1++) + *(p4++)) + 20 * (*(p2++) + *(p3++)); + } + p0 = p1 - (block_size_x + 5); + } + + for (j = 0; j < block_size_y; j++) + { + tmp_line = tmp_res + j*(block_size_x+5) + 2 ; + orig_line = block + j*block_size_x ; + x0 = tmp_res + j*(block_size_x+5) ; + x1 = x0 + 1; + x2 = x1 + 1; + x3 = x2 + 1; + x4 = x3 + 1; + x5 = x4 + 1; + + for (i = 0; i < block_size_x; i++) + { + result = (*(x0++) + *(x5++)) - 5 * (*(x1++) + *(x4++)) + 20 * (*(x2++) + *(x3++)); + + *orig_line = (imgpel) iClip1(max_imgpel_value, ((result + 512)>>10)); + *orig_line = (imgpel) ((*orig_line + iClip1(max_imgpel_value, ((*(tmp_line++) + 16)>>5))+1)>>1); + orig_line ++; + } + } +} + +/*! + ************************************************************************ + * \brief + * Qpel horizontal, Hpel vertical (3, 2) + ************************************************************************ + */ +static void get_luma_32(imgpel *block, imgpel **cur_imgY, int *tmp_res, int block_size_y, int block_size_x, int x_pos, int shift_x, int max_imgpel_value) +{ + int i, j; + int *tmp_line; + imgpel *p0, *p1, *p2, *p3, *p4, *p5; + int *x0, *x1, *x2, *x3, *x4, *x5; + imgpel *orig_line; + int result; + + p0 = &(cur_imgY[ -2][x_pos - 2]); + for (j = 0; j < block_size_y; j++) + { + p1 = p0 + shift_x; + p2 = p1 + shift_x; + p3 = p2 + shift_x; + p4 = p3 + shift_x; + p5 = p4 + shift_x; + tmp_line = tmp_res + j*(block_size_x+5); + + for (i = 0; i < block_size_x + 5; i++) + { + *(tmp_line++) = (*(p0++) + *(p5++)) - 5 * (*(p1++) + *(p4++)) + 20 * (*(p2++) + *(p3++)); + } + p0 = p1 - (block_size_x + 5); + } + + for (j = 0; j < block_size_y; j++) + { + tmp_line = tmp_res + j*(block_size_x+5) + 3; + orig_line = block + j*block_size_x; + x0 = tmp_res + j*(block_size_x+5); + x1 = x0 + 1; + x2 = x1 + 1; + x3 = x2 + 1; + x4 = x3 + 1; + x5 = x4 + 1; + + for (i = 0; i < block_size_x; i++) + { + result = (*(x0++) + *(x5++)) - 5 * (*(x1++) + *(x4++)) + 20 * (*(x2++) + *(x3++)); + + *orig_line = (imgpel) iClip1(max_imgpel_value, ((result + 512)>>10)); + *orig_line = (imgpel) ((*orig_line + iClip1(max_imgpel_value, ((*(tmp_line++) + 16)>>5))+1)>>1); + orig_line ++; + } + } +} + +/*! + ************************************************************************ + * \brief + * Qpel horizontal, Qpel vertical (3, 3) + ************************************************************************ + */ +static void get_luma_33(imgpel *block, imgpel **cur_imgY, int block_size_y, int block_size_x, int x_pos, int shift_x, int max_imgpel_value) +{ + int i, j; + imgpel *p0, *p1, *p2, *p3, *p4, *p5; + imgpel *orig_line; + int result; + + int jj = 1; + + for (j = 0; j < block_size_y; j++) + { + p0 = &cur_imgY[jj++][x_pos - 2]; + p1 = p0 + 1; + p2 = p1 + 1; + p3 = p2 + 1; + p4 = p3 + 1; + p5 = p4 + 1; + + orig_line = block + j*block_size_x; + + for (i = 0; i < block_size_x; i++) + { + result = (*(p0++) + *(p5++)) - 5 * (*(p1++) + *(p4++)) + 20 * (*(p2++) + *(p3++)); + + *(orig_line++) = (imgpel) iClip1(max_imgpel_value, ((result + 16)>>5)); + } + } + + p0 = &(cur_imgY[-2][x_pos + 1]); + for (j = 0; j < block_size_y; j++) + { + p1 = p0 + shift_x; + p2 = p1 + shift_x; + p3 = p2 + shift_x; + p4 = p3 + shift_x; + p5 = p4 + shift_x; + orig_line = block + j*block_size_x; + + for (i = 0; i < block_size_x; i++) + { + result = (*(p0++) + *(p5++)) - 5 * (*(p1++) + *(p4++)) + 20 * (*(p2++) + *(p3++)); + + *orig_line = (imgpel) ((*orig_line + iClip1(max_imgpel_value, ((result + 16) >> 5)) + 1) >> 1); + orig_line++; + } + p0 = p1 - block_size_x ; + } +} + + +/*! + ************************************************************************ + * \brief + * Qpel horizontal, Qpel vertical (1, 1) + ************************************************************************ + */ +static void get_luma_11(imgpel *block, imgpel **cur_imgY, int block_size_y, int block_size_x, int x_pos, int shift_x, int max_imgpel_value) +{ + int i, j; + imgpel *p0, *p1, *p2, *p3, *p4, *p5; + imgpel *orig_line; + int result; + + int jj = 0; + + for (j = 0; j < block_size_y; j++) + { + p0 = &cur_imgY[jj++][x_pos - 2]; + p1 = p0 + 1; + p2 = p1 + 1; + p3 = p2 + 1; + p4 = p3 + 1; + p5 = p4 + 1; + + orig_line = block + j*block_size_x; + + for (i = 0; i < block_size_x; i++) + { + result = (*(p0++) + *(p5++)) - 5 * (*(p1++) + *(p4++)) + 20 * (*(p2++) + *(p3++)); + + *(orig_line++) = (imgpel) iClip1(max_imgpel_value, ((result + 16)>>5)); + } + } + + p0 = &(cur_imgY[-2][x_pos]); + for (j = 0; j < block_size_y; j++) + { + p1 = p0 + shift_x; + p2 = p1 + shift_x; + p3 = p2 + shift_x; + p4 = p3 + shift_x; + p5 = p4 + shift_x; + orig_line = block + j*block_size_x; + + for (i = 0; i < block_size_x; i++) + { + result = (*(p0++) + *(p5++)) - 5 * (*(p1++) + *(p4++)) + 20 * (*(p2++) + *(p3++)); + + *orig_line = (imgpel) ((*orig_line + iClip1(max_imgpel_value, ((result + 16) >> 5)) + 1) >> 1); + orig_line++; + } + p0 = p1 - block_size_x ; + } +} + +/*! + ************************************************************************ + * \brief + * Qpel horizontal, Qpel vertical (1, 3) + ************************************************************************ + */ +static void get_luma_13(imgpel *block, imgpel **cur_imgY, int block_size_y, int block_size_x, int x_pos, int shift_x, int max_imgpel_value) +{ + /* Diagonal interpolation */ + int i, j; + imgpel *p0, *p1, *p2, *p3, *p4, *p5; + imgpel *orig_line; + int result; + + int jj = 1; + + for (j = 0; j < block_size_y; j++) + { + p0 = &cur_imgY[jj++][x_pos - 2]; + p1 = p0 + 1; + p2 = p1 + 1; + p3 = p2 + 1; + p4 = p3 + 1; + p5 = p4 + 1; + + orig_line = block + j*block_size_x; + + for (i = 0; i < block_size_x; i++) + { + result = (*(p0++) + *(p5++)) - 5 * (*(p1++) + *(p4++)) + 20 * (*(p2++) + *(p3++)); + + *(orig_line++) = (imgpel) iClip1(max_imgpel_value, ((result + 16)>>5)); + } + } + + p0 = &(cur_imgY[-2][x_pos]); + for (j = 0; j < block_size_y; j++) + { + p1 = p0 + shift_x; + p2 = p1 + shift_x; + p3 = p2 + shift_x; + p4 = p3 + shift_x; + p5 = p4 + shift_x; + orig_line = block + j*block_size_x; + + for (i = 0; i < block_size_x; i++) + { + result = (*(p0++) + *(p5++)) - 5 * (*(p1++) + *(p4++)) + 20 * (*(p2++) + *(p3++)); + + *orig_line = (imgpel) ((*orig_line + iClip1(max_imgpel_value, ((result + 16) >> 5)) + 1) >> 1); + orig_line++; + } + p0 = p1 - block_size_x ; + } +} + +/*! + ************************************************************************ + * \brief + * Qpel horizontal, Qpel vertical (3, 1) + ************************************************************************ + */ +static void get_luma_31(imgpel *block, imgpel **cur_imgY, int block_size_y, int block_size_x, int x_pos, int shift_x, int max_imgpel_value) +{ + /* Diagonal interpolation */ + int i, j; + imgpel *p0, *p1, *p2, *p3, *p4, *p5; + imgpel *orig_line; + int result; + + int jj = 0; + + for (j = 0; j < block_size_y; j++) + { + p0 = &cur_imgY[jj++][x_pos - 2]; + p1 = p0 + 1; + p2 = p1 + 1; + p3 = p2 + 1; + p4 = p3 + 1; + p5 = p4 + 1; + + orig_line = block + j*block_size_x; + + for (i = 0; i < block_size_x; i++) + { + result = (*(p0++) + *(p5++)) - 5 * (*(p1++) + *(p4++)) + 20 * (*(p2++) + *(p3++)); + + *(orig_line++) = (imgpel) iClip1(max_imgpel_value, ((result + 16)>>5)); + } + } + + p0 = &(cur_imgY[-2][x_pos + 1]); + for (j = 0; j < block_size_y; j++) + { + p1 = p0 + shift_x; + p2 = p1 + shift_x; + p3 = p2 + shift_x; + p4 = p3 + shift_x; + p5 = p4 + shift_x; + orig_line = block + j*block_size_x; + + for (i = 0; i < block_size_x; i++) + { + result = (*(p0++) + *(p5++)) - 5 * (*(p1++) + *(p4++)) + 20 * (*(p2++) + *(p3++)); + + *orig_line = (imgpel) ((*orig_line + iClip1(max_imgpel_value, ((result + 16) >> 5)) + 1) >> 1); + orig_line++; + } + p0 = p1 - block_size_x ; + } +} + +/*! + ************************************************************************ + * \brief + * Interpolation on-the-fly of 1/4 subpixel + ************************************************************************ + */ + +static inline void get_block_luma_otf( VideoParameters *p_Vid, //!< video encoding parameters for current picture + imgpel* mpred, //!< array of prediction values (row by row) + int* tmp_pred, //!< array of temporary prediction values (row by row), used for some hal-pel interpolations + int pic_pix_x, //!< motion shifted horizontal coordinate of block + int pic_pix_y, //!< motion shifted vertical coordinate of block + int block_size_x, //!< horizontal block size + int block_size_y, //!< vertical block size + StorablePicture *ref, //!< reference picture list + int pl //!< plane + ) +{ + imgpel **ref_block = (p_Vid->P444_joined && pl>PLANE_Y)? ref->imgUV[pl-1] : ref->imgY; + int dx = (pic_pix_x & 0x03); + int dy = (pic_pix_y & 0x03); + int x_pos = iClip3(-IMG_PAD_SIZE_X+2, ref->size_x_pad-2, pic_pix_x>>2); + int y_pos = iClip3(-IMG_PAD_SIZE_Y+2, ref->size_y_pad-2, pic_pix_y>>2); + + if (dx == 0 && dy == 0) + get_block_00(mpred, &(ref_block[y_pos][x_pos]), block_size_y, block_size_x, p_Vid->padded_size_x); + else + { /* other positions */ + if (dy == 0) /* No vertical interpolation */ + { + if (dx == 1) + get_luma_10(mpred, &ref_block[y_pos], block_size_y, block_size_x, x_pos, p_Vid->max_imgpel_value); + else if (dx == 2) + get_luma_20(mpred, &ref_block[y_pos], block_size_y, block_size_x, x_pos, p_Vid->max_imgpel_value); + else + get_luma_30(mpred, &ref_block[y_pos], block_size_y, block_size_x, x_pos, p_Vid->max_imgpel_value); + } + else if (dx == 0) /* No horizontal interpolation */ + { + if (dy == 1) + get_luma_01(mpred, &ref_block[y_pos], block_size_y, block_size_x, x_pos, p_Vid->padded_size_x, p_Vid->max_imgpel_value); + else if (dy == 2) + get_luma_02(mpred, &ref_block[y_pos], block_size_y, block_size_x, x_pos, p_Vid->padded_size_x, p_Vid->max_imgpel_value); + else + get_luma_03(mpred, &ref_block[y_pos], block_size_y, block_size_x, x_pos, p_Vid->padded_size_x, p_Vid->max_imgpel_value); + } + else if (dx == 2) /* Vertical & horizontal interpolation */ + { + if (dy == 1) + get_luma_21(mpred, &ref_block[y_pos], tmp_pred, block_size_y, block_size_x, x_pos, p_Vid->max_imgpel_value); + else if (dy == 2) + get_luma_22(mpred, &ref_block[y_pos], tmp_pred, block_size_y, block_size_x, x_pos, p_Vid->max_imgpel_value); + else + get_luma_23(mpred, &ref_block[y_pos], tmp_pred, block_size_y, block_size_x, x_pos, p_Vid->max_imgpel_value); + } + else if (dy == 2) + { + if (dx == 1) + get_luma_12(mpred, &ref_block[y_pos], tmp_pred, block_size_y, block_size_x, x_pos, p_Vid->padded_size_x, p_Vid->max_imgpel_value); + else + get_luma_32(mpred, &ref_block[y_pos], tmp_pred, block_size_y, block_size_x, x_pos, p_Vid->padded_size_x, p_Vid->max_imgpel_value); + } + else + { + if (dx == 1) + { + if (dy == 1) + get_luma_11(mpred, &ref_block[y_pos], block_size_y, block_size_x, x_pos, p_Vid->padded_size_x, p_Vid->max_imgpel_value); + else + get_luma_13(mpred, &ref_block[y_pos], block_size_y, block_size_x, x_pos, p_Vid->padded_size_x, p_Vid->max_imgpel_value); + } + else + { + if (dy == 1) + get_luma_31(mpred, &ref_block[y_pos], block_size_y, block_size_x, x_pos, p_Vid->padded_size_x, p_Vid->max_imgpel_value); + else + get_luma_33(mpred, &ref_block[y_pos], block_size_y, block_size_x, x_pos, p_Vid->padded_size_x, p_Vid->max_imgpel_value); + } + } + } +} + +void get_block_luma_otf_L2( VideoParameters *p_Vid, //!< video encoding parameters for current picture + imgpel* mpred, //!< array of prediction values (row by row) + int* tmp_pred, //!< array of temporary prediction values (row by row), used for some hal-pel interpolations + int pic_pix_x, //!< motion shifted horizontal coordinate of block + int pic_pix_y, //!< motion shifted vertical coordinate of block + int block_size_x, //!< horizontal block size + int block_size_y, //!< vertical block size + StorablePicture *ref, //!< reference picture list + int pl //!< plane + ) +{ + get_block_luma_otf( p_Vid, mpred, tmp_pred, pic_pix_x, pic_pix_y, block_size_x, block_size_y, ref, pl ) ; +} + +void get_block_luma_otf_L1( VideoParameters *p_Vid, //!< video encoding parameters for current picture + imgpel* mpred, //!< array of prediction values (row by row) + int* tmp_pred, //!< array of temporary prediction values (row by row), used for some hal-pel interpolations + int pic_pix_x, //!< motion shifted horizontal coordinate of block + int pic_pix_y, //!< motion shifted vertical coordinate of block + int block_size_x, //!< horizontal block size + int block_size_y, //!< vertical block size + StorablePicture *ref, //!< reference picture list + int pl //!< plane + ) +{ + if( is_qpel( pic_pix_x, pic_pix_y, 0x03, 0x03 ) ) + { + get_block_luma_otf( p_Vid, mpred, tmp_pred, pic_pix_x, pic_pix_y, block_size_x, block_size_y, ref, pl ) ; + } + else + { + int j; + imgpel *ref_line ; + ref->p_curr_img_sub = (p_Vid->P444_joined && pl>PLANE_Y) ? (ref->imgUV_sub[pl-1]):(ref->imgY_sub); // select plane for 4:4:4 compatibility + ref_line = UMVLine4X_otf ( ref, pic_pix_y, pic_pix_x ) ; + for (j = 0; j < block_size_y; j++) + { + memcpy(mpred, ref_line, block_size_x * sizeof(imgpel)); + ref_line += p_Vid->padded_size_x; + mpred += block_size_x; + } + } +} + +/*! + ************************************************************************ + * \brief + * Chroma (0,X) + ************************************************************************ + */ +static void get_chroma_0X(imgpel *block, imgpel *cur_img, int span, int block_size_y, int block_size_x, int w00, int w01, int total_scale) +{ + imgpel *cur_row = cur_img; + imgpel *nxt_row = cur_img + span; + + + imgpel *cur_line, *cur_line_p1; + imgpel *blk_line; + int result; + int i, j; + for (j = 0; j < block_size_y; j++) + { + cur_line = cur_row; + cur_line_p1 = nxt_row; + blk_line = block; + block += block_size_x ; + cur_row = nxt_row; + nxt_row += span; + for (i = 0; i < block_size_x; i++) + { + result = (w00 * *cur_line++ + w01 * *cur_line_p1++); + *(blk_line++) = (imgpel) rshift_rnd_sf(result, total_scale); + } + } +} + +/*! + ************************************************************************ + * \brief + * Chroma (X,0) + ************************************************************************ + */ +static void get_chroma_X0(imgpel *block, imgpel *cur_img, int span, int block_size_y, int block_size_x, int w00, int w10, int total_scale) +{ + imgpel *cur_row = cur_img; + imgpel *cur_line, *cur_line_p1; + imgpel *blk_line; + int result; + int i, j; + for (j = 0; j < block_size_y; j++) + { + cur_line = cur_row; + cur_line_p1 = cur_line + 1; + blk_line = block; + block += block_size_x ; + cur_row += span; + for (i = 0; i < block_size_x; i++) + { + result = (w00 * *cur_line++ + w10 * *cur_line_p1++); + //*(blk_line++) = (imgpel) iClip1(max_imgpel_value, rshift_rnd_sf(result, total_scale)); + *(blk_line++) = (imgpel) rshift_rnd_sf(result, total_scale); + } + } +} + + +/*! + ************************************************************************ + * \brief + * Chroma (X,X) + ************************************************************************ + */ +static void get_chroma_XX(imgpel *block, imgpel *cur_img, int span, int block_size_y, int block_size_x, int w00, int w01, int w10, int w11, int total_scale) +{ + imgpel *cur_row = cur_img; + imgpel *nxt_row = cur_img + span; + imgpel *cur_line, *cur_line_p1; + imgpel *blk_line; + int result; + int i, j; + for (j = 0; j < block_size_y; j++) + { + cur_line = cur_row; + cur_line_p1 = nxt_row; + blk_line = block; + block += block_size_x ; + cur_row = nxt_row; + nxt_row += span; + for (i = 0; i < block_size_x; i++) + { + result = (w00 * *(cur_line++) + w01 * *(cur_line_p1++)); + result += (w10 * *(cur_line ) + w11 * *(cur_line_p1 )); + *(blk_line++) = (imgpel) rshift_rnd_sf(result, total_scale); + } + } + +} + +static inline void get_block_chroma_otf ( VideoParameters *p_Vid, //!< video encoding parameters for current picture + imgpel* mpred, //!< array to store prediction values + int* tmp_pred, + int pic_pix_x, //!< motion shifted horizontal coordinate of block + int pic_pix_y, //!< motion shifted vertical block + int block_size_x, //!< horizontal block size + int block_size_y, //!< vertical block size + StorablePicture *ref, //!< reference picture list + int uv ) //!< chroma component +{ + int dx = (pic_pix_x & p_Vid->chroma_mask_mv_x) ; + int dy = (pic_pix_y & p_Vid->chroma_mask_mv_y) ; + int x_pos = iClip3(-ref->pad_size_uv_x+1, ref->size_x_cr_pad-1, pic_pix_x >> p_Vid->chroma_shift_x) ; + int y_pos = iClip3(-ref->pad_size_uv_y+1, ref->size_y_cr_pad-1, pic_pix_y >> p_Vid->chroma_shift_y) ; + int total_scale = p_Vid->chroma_shift_x + p_Vid->chroma_shift_y ; + + + imgpel *ref_line = &(ref->imgUV[ uv-1 ][y_pos][x_pos]) ; + + if (dx == 0 && dy == 0) + { + get_block_00(mpred, ref_line, block_size_y, block_size_x, p_Vid->cr_padded_size_x); + } + else + { + short dxcur = (short) (p_Vid->chroma_mask_mv_x + 1 - dx); + short dycur = (short) (p_Vid->chroma_mask_mv_y + 1 - dy); + short w00 = dxcur * dycur; + if (dx == 0) + { + short w01 = dxcur * dy; + get_chroma_0X(mpred, ref_line, p_Vid->cr_padded_size_x, block_size_y, block_size_x, w00, w01, total_scale); + } + else if (dy == 0) + { + short w10 = dx * dycur; + get_chroma_X0(mpred, ref_line, p_Vid->cr_padded_size_x, block_size_y, block_size_x, w00, w10, total_scale); + } + else + { + short w01 = dxcur * dy; + short w10 = dx * dycur; + short w11 = dx * dy; + get_chroma_XX(mpred, ref_line, p_Vid->cr_padded_size_x, block_size_y, block_size_x, w00, w01, w10, w11, total_scale); + } + } +} + + +void get_block_chroma_otf_L2 ( VideoParameters *p_Vid, //!< video encoding parameters for current picture + imgpel* mpred, //!< array to store prediction values + int* tmp_pred, + int pic_pix_x, //!< motion shifted horizontal coordinate of block + int pic_pix_y, //!< motion shifted vertical block + int block_size_x, //!< horizontal block size + int block_size_y, //!< vertical block size + StorablePicture *ref, //!< reference picture list + int uv ) //!< chroma component +{ + get_block_chroma_otf ( p_Vid, mpred, tmp_pred, pic_pix_x, pic_pix_y, block_size_x, block_size_y, ref, uv ); +} + + +void me_get_block_chroma_otf_L1 ( VideoParameters *p_Vid, //!< video encoding parameters for current picture + imgpel* mpred, //!< array to store prediction values + int* tmp_pred, + int pic_pix_x, //!< motion shifted horizontal coordinate of block + int pic_pix_y, //!< motion shifted vertical block + int block_size_x, //!< horizontal block size + int block_size_y, //!< vertical block size + StorablePicture *ref, //!< reference picture list + int uv ) //!< chroma component +{ + if( is_qpel( pic_pix_x, pic_pix_y, ref->chroma_mask_mv_x, ref->chroma_mask_mv_y) ) + { + get_block_chroma_otf ( p_Vid, mpred, tmp_pred, pic_pix_x, pic_pix_y, block_size_x, block_size_y, ref, uv ); + } + else + { + int j; + imgpel *ref_line = UMVLine8X_chroma_otf (ref, uv, pic_pix_y, pic_pix_x); + for (j = 0; j < block_size_y; j++) + { + memcpy(mpred, ref_line, block_size_x * sizeof(imgpel)); + ref_line += p_Vid->cr_padded_size_x; + mpred += block_size_x; + } + } +} + +void mc_get_block_chroma_otf_L1 ( VideoParameters *p_Vid, //!< video encoding parameters for current picture + imgpel* mpred, //!< array to store prediction values + int* tmp_pred, + int pic_pix_x, //!< motion shifted horizontal coordinate of block + int pic_pix_y, //!< motion shifted vertical block + int block_size_x, //!< horizontal block size + int block_size_y, //!< vertical block size + StorablePicture *ref, //!< reference picture list + int uv ) //!< chroma component +{ + if( is_qpel( pic_pix_x, pic_pix_y, ref->chroma_mask_mv_x, ref->chroma_mask_mv_y ) ) + { + get_block_chroma_otf ( p_Vid, mpred, tmp_pred, pic_pix_x, pic_pix_y, block_size_x, block_size_y, ref, uv ); + } + else + { + int j; + imgpel *ref_line = UMVLine4Xcr_otf (ref, uv, pic_pix_y, pic_pix_x); + for (j = 0; j < block_size_y; j++) + { + memcpy(mpred, ref_line, block_size_x * sizeof(imgpel)); + ref_line += p_Vid->cr_padded_size_x; + mpred += block_size_x; + } + } +} \ No newline at end of file diff --git a/lencod/src/header.c b/lencod/src/header.c new file mode 100644 index 0000000..685fd67 --- /dev/null +++ b/lencod/src/header.c @@ -0,0 +1,614 @@ +/*! + ************************************************************************************* + * \file header.c + * + * \brief + * H.264 Slice and Sequence headers + * + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * - Stephan Wenger + * - Karsten Suehring + ************************************************************************************* + */ + +#include + +#include "global.h" + +#include "elements.h" +#include "header.h" +#include "rtp.h" +#include "mbuffer.h" +#include "vlc.h" +#include "parset.h" +#if (MVC_EXTENSION_ENABLE) +#include "list_reorder.h" +#endif + +// A little trick to avoid those horrible #if TRACE all over the source code +#if TRACE +#define SYMTRACESTRING(s) strncpy(sym.tracestring,s,TRACESTRING_SIZE) +#else +#define SYMTRACESTRING(s) // do nothing +#endif + +const int * assignSE2partition[2] ; +const int assignSE2partition_NoDP[SE_MAX_ELEMENTS] = + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; +const int assignSE2partition_DP[SE_MAX_ELEMENTS] = + // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 + { 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 0, 0, 0 } ; + +#if (MVC_EXTENSION_ENABLE) +static int mvc_ref_pic_list_reordering(Slice *currSlice, Bitstream *bitstream); +#endif +static int ref_pic_list_reordering(Slice *currSlice, Bitstream *bitstream); +static int pred_weight_table (Slice *currSlice, Bitstream *bitstream); +static int get_picture_type (Slice *currSlice); +/*! + ******************************************************************************************** + * \brief + * Write a slice header + * + * \return + * number of bits used + ******************************************************************************************** +*/ +int SliceHeader(Slice* currSlice) +{ + VideoParameters *p_Vid = currSlice->p_Vid; + seq_parameter_set_rbsp_t *active_sps = currSlice->active_sps; + pic_parameter_set_rbsp_t *active_pps = currSlice->active_pps; + + int dP_nr = assignSE2partition[currSlice->partition_mode][SE_HEADER]; + Bitstream *bitstream = currSlice->partArr[dP_nr].bitstream; + int len = 0; + unsigned int field_pic_flag = 0; + byte bottom_field_flag = 0; + + int num_bits_slice_group_change_cycle; + float numtmp; + + if (currSlice->mb_aff_frame_flag) + len = write_ue_v("SH: first_mb_in_slice", p_Vid->current_mb_nr >> 1, bitstream); + else + len = write_ue_v("SH: first_mb_in_slice", p_Vid->current_mb_nr, bitstream); + + len += write_ue_v("SH: slice_type", get_picture_type (currSlice), bitstream); + + len += write_ue_v("SH: pic_parameter_set_id" , active_pps->pic_parameter_set_id ,bitstream); + + if( active_sps->separate_colour_plane_flag == 1 ) + len += write_u_v( 2, "SH: colour_plane_id", p_Vid->colour_plane_id, bitstream ); + +#if (MVC_EXTENSION_ENABLE) + //if(p_Vid->num_of_layers==2) + // len += write_u_v (p_Vid->log2_max_frame_num_minus4 + 4,"SH: frame_num", p_Vid->frame_num/2, bitstream); + //else + len += write_u_v (p_Vid->log2_max_frame_num_minus4 + 4,"SH: frame_num", p_Vid->frame_num, bitstream); +#else + len += write_u_v (p_Vid->log2_max_frame_num_minus4 + 4,"SH: frame_num", p_Vid->frame_num, bitstream); +#endif + + if (!currSlice->active_sps->frame_mbs_only_flag) + { + // field_pic_flag u(1) + field_pic_flag = (currSlice->structure == TOP_FIELD || currSlice->structure ==BOTTOM_FIELD)?1:0; + assert( field_pic_flag == p_Vid->fld_flag ); + len += write_u_1("SH: field_pic_flag", field_pic_flag, bitstream); + + if (field_pic_flag) + { + //bottom_field_flag u(1) + bottom_field_flag = (byte) (currSlice->structure == BOTTOM_FIELD); + len += write_u_1("SH: bottom_field_flag" , bottom_field_flag ,bitstream); + } + } + +#if (MVC_EXTENSION_ENABLE) + if (currSlice->idr_flag || (p_Vid->view_id && !p_Vid->non_idr_flag[currSlice->structure == BOTTOM_FIELD ? 1 : 0]) ) +#else + if (currSlice->idr_flag) +#endif + { + // idr_pic_id + len += write_ue_v ("SH: idr_pic_id", (p_Vid->number & 0x01), bitstream); + } + + if (p_Vid->pic_order_cnt_type == 0) + { + if (active_sps->frame_mbs_only_flag) + { + p_Vid->pic_order_cnt_lsb = (p_Vid->toppoc & ~((((unsigned int)(-1)) << (p_Vid->log2_max_pic_order_cnt_lsb_minus4+4))) ); + } + else + { + if (!field_pic_flag || currSlice->structure == TOP_FIELD) + p_Vid->pic_order_cnt_lsb = (p_Vid->toppoc & ~((((unsigned int)(-1)) << (p_Vid->log2_max_pic_order_cnt_lsb_minus4+4))) ); + else if ( currSlice->structure == BOTTOM_FIELD ) + p_Vid->pic_order_cnt_lsb = (p_Vid->bottompoc & ~((((unsigned int)(-1)) << (p_Vid->log2_max_pic_order_cnt_lsb_minus4+4))) ); + } + + len += write_u_v (p_Vid->log2_max_pic_order_cnt_lsb_minus4+4, "SH: pic_order_cnt_lsb", p_Vid->pic_order_cnt_lsb, bitstream); + + if (p_Vid->bottom_field_pic_order_in_frame_present_flag && !field_pic_flag) + { + len += write_se_v ("SH: delta_pic_order_cnt_bottom", p_Vid->delta_pic_order_cnt_bottom, bitstream); + } + } + if (p_Vid->pic_order_cnt_type == 1 && !p_Vid->delta_pic_order_always_zero_flag) + { + len += write_se_v ("SH: delta_pic_order_cnt[0]", p_Vid->delta_pic_order_cnt[0], bitstream); + + if (p_Vid->bottom_field_pic_order_in_frame_present_flag && !field_pic_flag) + { + len += write_se_v ("SH: delta_pic_order_cnt[1]", p_Vid->delta_pic_order_cnt[1], bitstream); + } + } + + if (active_pps->redundant_pic_cnt_present_flag) + { + len += write_ue_v ("SH: redundant_pic_cnt", p_Vid->redundant_pic_cnt, bitstream); + } + + // Direct Mode Type selection for B pictures + if (currSlice->slice_type == B_SLICE) + { + len += write_u_1 ("SH: direct_spatial_mv_pred_flag", p_Vid->direct_spatial_mv_pred_flag, bitstream); + } + + if ((currSlice->slice_type == P_SLICE) || (currSlice->slice_type == B_SLICE) || currSlice->slice_type==SP_SLICE) + { + int override_flag; + if ((currSlice->slice_type == P_SLICE) || (currSlice->slice_type == SP_SLICE)) + { + override_flag = (currSlice->num_ref_idx_active[LIST_0] != (active_pps->num_ref_idx_l0_default_active_minus1 +1)) ? 1 : 0; + } + else + { + override_flag = ((currSlice->num_ref_idx_active[LIST_0] != (active_pps->num_ref_idx_l0_default_active_minus1 +1)) + || (currSlice->num_ref_idx_active[LIST_1] != (active_pps->num_ref_idx_l1_default_active_minus1 +1))) ? 1 : 0; + } + + len += write_u_1 ("SH: num_ref_idx_active_override_flag", override_flag, bitstream); + + if (override_flag) + { + len += write_ue_v ("SH: num_ref_idx_l0_active_minus1", currSlice->num_ref_idx_active[LIST_0]-1, bitstream); + if (currSlice->slice_type == B_SLICE) + { + len += write_ue_v ("SH: num_ref_idx_l1_active_minus1", currSlice->num_ref_idx_active[LIST_1]-1, bitstream); + } + } + + } + + // add reference list modification info if needed +#if (MVC_EXTENSION_ENABLE) + if(currSlice->layer_id > 0) + len += mvc_ref_pic_list_reordering(currSlice, bitstream); + else +#endif + len += ref_pic_list_reordering(currSlice, bitstream); + + if (((currSlice->slice_type == P_SLICE || currSlice->slice_type == SP_SLICE) && active_pps->weighted_pred_flag) || + ((currSlice->slice_type == B_SLICE) && currSlice->weighted_prediction == 1)) + { + len += pred_weight_table(currSlice, bitstream); + } + + if (p_Vid->nal_reference_idc) + { + p_Vid->adaptive_ref_pic_buffering_flag = (p_Vid->dec_ref_pic_marking_buffer != NULL); + len += dec_ref_pic_marking(bitstream, + p_Vid->dec_ref_pic_marking_buffer, +#if (MVC_EXTENSION_ENABLE) + (currSlice->idr_flag || (p_Vid->view_id && !p_Vid->non_idr_flag[currSlice->structure == BOTTOM_FIELD ? 1 : 0]) ) ? 1 : 0, +#else + currSlice->idr_flag, +#endif + p_Vid->no_output_of_prior_pics_flag, + p_Vid->long_term_reference_flag ); + } + + if(currSlice->symbol_mode==CABAC && currSlice->slice_type!=I_SLICE /*&& currSlice->slice_type!=SI_IMG*/) + { + len += write_ue_v("SH: cabac_init_idc", currSlice->model_number, bitstream); + } + + len += write_se_v("SH: slice_qp_delta", (currSlice->qp - 26 - active_pps->pic_init_qp_minus26), bitstream); + + if (currSlice->slice_type == SP_SLICE || currSlice->slice_type == SI_SLICE) // SP or SI + { + if (currSlice->slice_type == SP_SLICE) // Switch Flag only for SP pictures + { + len += write_u_1 ("SH: sp_for_switch_flag", currSlice->sp2_frame_indicator, bitstream); // 1 for switching SP, 0 for normal SP + } + len += write_se_v ("SH: slice_qs_delta", (p_Vid->qpsp - 26), bitstream ); + } + + if (active_pps->deblocking_filter_control_present_flag) + { + len += write_ue_v("SH: disable_deblocking_filter_idc",currSlice->DFDisableIdc, bitstream); // Turn deblocking filter on/off on slice basis + + if (currSlice->DFDisableIdc!=1) + { + len += write_se_v ("SH: slice_alpha_c0_offset_div2", currSlice->DFAlphaC0Offset / 2, bitstream); + len += write_se_v ("SH: slice_beta_offset_div2 ", currSlice->DFBetaOffset / 2, bitstream); + } + } + + if ( active_pps->num_slice_groups_minus1 > 0 && + active_pps->slice_group_map_type >= 3 && active_pps->slice_group_map_type <= 5) + { + numtmp=p_Vid->PicHeightInMapUnits*p_Vid->PicWidthInMbs/(float)(active_pps->slice_group_change_rate_minus1+1)+1; + num_bits_slice_group_change_cycle = (int)ceil(log(numtmp)/log(2)); + + //! p_Vid->slice_group_change_cycle can be changed before calling FmoInit() + len += write_u_v (num_bits_slice_group_change_cycle, "SH: slice_group_change_cycle", p_Vid->slice_group_change_cycle, bitstream); + } + + // NOTE: The following syntax element is actually part + // Slice data bitstream A RBSP syntax + + if(currSlice->partition_mode && !currSlice->idr_flag) + { + len += write_ue_v("DPA: slice_id", currSlice->slice_nr, bitstream); + } + + return len; +} + +/*! + ******************************************************************************************** + * \brief + * writes the ref_pic_list_reordering syntax + * based on content of according fields in p_Vid structure + * + * \return + * number of bits used + ******************************************************************************************** +*/ +int ref_pic_list_reordering(Slice *currSlice, Bitstream *bitstream) +{ + int list; + int i, len=0; + + for(list = LIST_0; list <= LIST_1; list++) + { + if(currSlice->num_ref_idx_active[list]==0) + continue; + + reorder_against_default_ref_pic_lists(currSlice, list); + + if (currSlice->slice_type!=I_SLICE && currSlice->slice_type!=SI_SLICE) + { + len += write_u_1 ("SH: ref_pic_list_reordering_flag", currSlice->ref_pic_list_reordering_flag[list], bitstream); + if (currSlice->ref_pic_list_reordering_flag[list]) + { + i=-1; + do + { + i++; + len += write_ue_v ("SH: modification_of_pic_nums_idc", currSlice->modification_of_pic_nums_idc[list][i], bitstream); + if (currSlice->modification_of_pic_nums_idc[list][i] == 0 || currSlice->modification_of_pic_nums_idc[list][i] == 1) + { + len += write_ue_v ("SH: abs_diff_pic_num_minus1", currSlice->abs_diff_pic_num_minus1[list][i], bitstream); + } + else if (currSlice->modification_of_pic_nums_idc[list][i] == 2) + { + len += write_ue_v ("SH: long_term_pic_idx", currSlice->long_term_pic_idx[list][i], bitstream); + } + } while (currSlice->modification_of_pic_nums_idc[list][i] != 3); + } + } + } + + return len; +} + +#if (MVC_EXTENSION_ENABLE) +/*! +******************************************************************************************** +* \brief +* writes the mvc_ref_pic_list_reordering syntax +* for both LIST_0 and LIST_1 +* +* \return +* number of bits used +******************************************************************************************** +*/ + +static int mvc_ref_pic_list_reordering(Slice *currSlice, Bitstream *bitstream) +{ + int list; + int i, len=0; + + for(list = LIST_0; list <= LIST_1; list++) + { + if(currSlice->num_ref_idx_active[list]==0) + continue; + + reorder_against_default_ref_pic_lists(currSlice, list); + + if (currSlice->slice_type!=I_SLICE && currSlice->slice_type!=SI_SLICE) + { + len += write_u_1 ("SH: ref_pic_list_reordering_flag", currSlice->ref_pic_list_reordering_flag[list], bitstream); + if (currSlice->ref_pic_list_reordering_flag[list]) + { + i=-1; + do + { + i++; + len += write_ue_v ("SH: modification_of_pic_nums_idc", currSlice->modification_of_pic_nums_idc[list][i], bitstream); + if (currSlice->modification_of_pic_nums_idc[list][i] == 0 || currSlice->modification_of_pic_nums_idc[list][i] == 1) + { + len += write_ue_v ("SH: abs_diff_pic_num_minus1", currSlice->abs_diff_pic_num_minus1[list][i], bitstream); + } + else if (currSlice->modification_of_pic_nums_idc[list][i] == 2) + { + len += write_ue_v ("SH: long_term_pic_idx", currSlice->long_term_pic_idx[list][i], bitstream); + } + else if (currSlice->modification_of_pic_nums_idc[list][i] == 4 || currSlice->modification_of_pic_nums_idc[list][i] == 5) + { + len += write_ue_v ("SH: abs_diff_view_idx_minus1", currSlice->abs_diff_view_idx_minus1[list][i], bitstream); + } + } while (currSlice->modification_of_pic_nums_idc[list][i] != 3); + } + } + } + + return len; +} +#endif + +/*! + ************************************************************************ + * \brief + * write the memory management control operations + * + * \return + * number of bits used + ************************************************************************ + */ +int dec_ref_pic_marking(Bitstream *bitstream, DecRefPicMarking_t *p_drpm, int idr_flag, int no_output_of_prior_pics_flag, int long_term_reference_flag ) +{ + DecRefPicMarking_t *tmp_drpm; + + int val, len=0; + int adaptive_ref_pic_buffering_flag; + + if (idr_flag) + { + len += write_u_1("SH: no_output_of_prior_pics_flag", no_output_of_prior_pics_flag, bitstream); + len += write_u_1("SH: long_term_reference_flag", long_term_reference_flag, bitstream); + } + else + { + adaptive_ref_pic_buffering_flag = (p_drpm != NULL); + + len += write_u_1("SH: adaptive_ref_pic_buffering_flag", adaptive_ref_pic_buffering_flag, bitstream); + + if (adaptive_ref_pic_buffering_flag) + { + tmp_drpm = p_drpm; + // write Memory Management Control Operation + do + { + if (tmp_drpm==NULL) error ("Error encoding MMCO commands", 500); + + val = tmp_drpm->memory_management_control_operation; + len += write_ue_v("SH: memory_management_control_operation", val, bitstream); + + if ((val==1)||(val==3)) + { + len += 1 + write_ue_v("SH: difference_of_pic_nums_minus1", tmp_drpm->difference_of_pic_nums_minus1, bitstream); + } + if (val==2) + { + len+= write_ue_v("SH: long_term_pic_num", tmp_drpm->long_term_pic_num, bitstream); + } + if ((val==3)||(val==6)) + { + len+= write_ue_v("SH: long_term_frame_idx", tmp_drpm->long_term_frame_idx, bitstream); + } + if (val==4) + { + len += write_ue_v("SH: max_long_term_pic_idx_plus1", tmp_drpm->max_long_term_frame_idx_plus1, bitstream); + } + + tmp_drpm=tmp_drpm->Next; + + } while (val != 0); + + } + } + return len; +} + + +/*! + ************************************************************************ + * \brief + * write prediction weight table + * + * \return + * number of bits used + ************************************************************************ + */ +static int pred_weight_table(Slice *currSlice, Bitstream *bitstream) +{ + int len = 0; + int i,j; + + len += write_ue_v("SH: luma_log_weight_denom", currSlice->luma_log_weight_denom, bitstream); + + if ( 0 != currSlice->active_sps->chroma_format_idc) + { + len += write_ue_v("SH: chroma_log_weight_denom", currSlice->chroma_log_weight_denom, bitstream); + } + + for (i=0; i< currSlice->num_ref_idx_active[LIST_0]; i++) + { + if ( (currSlice->wp_weight[0][i][0] != 1<luma_log_weight_denom) || (currSlice->wp_offset[0][i][0] != 0) ) + { + len += write_u_1 ("SH: luma_weight_flag_l0", 1, bitstream); + len += write_se_v ("SH: luma_weight_l0", currSlice->wp_weight[0][i][0], bitstream); + len += write_se_v ("SH: luma_offset_l0", (currSlice->wp_offset[0][i][0]>>(currSlice->bitdepth_luma - 8)), bitstream); + } + else + { + len += write_u_1 ("SH: luma_weight_flag_l0", 0, bitstream); + } + + if (currSlice->active_sps->chroma_format_idc!=0) + { + if ( (currSlice->wp_weight[0][i][1] != 1<chroma_log_weight_denom) || (currSlice->wp_offset[0][i][1] != 0) || + (currSlice->wp_weight[0][i][2] != 1<chroma_log_weight_denom) || (currSlice->wp_offset[0][i][2] != 0) ) + { + len += write_u_1 ("chroma_weight_flag_l0", 1, bitstream); + for (j=1; j<3; j++) + { + len += write_se_v ("chroma_weight_l0", currSlice->wp_weight[0][i][j] ,bitstream); + len += write_se_v ("chroma_offset_l0", (currSlice->wp_offset[0][i][j]>>(currSlice->bitdepth_chroma-8)) ,bitstream); + } + } + else + { + len += write_u_1 ("chroma_weight_flag_l0", 0, bitstream); + } + } + } + + if (currSlice->slice_type == B_SLICE) + { + for (i=0; i< currSlice->num_ref_idx_active[LIST_1]; i++) + { + if ( (currSlice->wp_weight[1][i][0] != 1<luma_log_weight_denom) || (currSlice->wp_offset[1][i][0] != 0) ) + { + len += write_u_1 ("SH: luma_weight_flag_l1", 1, bitstream); + len += write_se_v ("SH: luma_weight_l1", currSlice->wp_weight[1][i][0], bitstream); + len += write_se_v ("SH: luma_offset_l1", (currSlice->wp_offset[1][i][0]>>(currSlice->bitdepth_luma-8)), bitstream); + } + else + { + len += write_u_1 ("SH: luma_weight_flag_l1", 0, bitstream); + } + + if (currSlice->active_sps->chroma_format_idc!=0) + { + if ( (currSlice->wp_weight[1][i][1] != 1<chroma_log_weight_denom) || (currSlice->wp_offset[1][i][1] != 0) || + (currSlice->wp_weight[1][i][2] != 1<chroma_log_weight_denom) || (currSlice->wp_offset[1][i][2] != 0) ) + { + len += write_u_1 ("chroma_weight_flag_l1", 1, bitstream); + for (j=1; j<3; j++) + { + len += write_se_v ("chroma_weight_l1", currSlice->wp_weight[1][i][j] ,bitstream); + len += write_se_v ("chroma_offset_l1", (currSlice->wp_offset[1][i][j]>>(currSlice->bitdepth_chroma-8)) ,bitstream); + } + } + else + { + len += write_u_1 ("chroma_weight_flag_l1", 0, bitstream); + } + } + } + } + return len; +} + + +/*! + ************************************************************************ + * \brief + * Selects picture type and codes it to symbol + * + * \return + * symbol value for picture type + ************************************************************************ + */ +static int get_picture_type(Slice *currSlice) +{ + // set this value to zero for transmission without signaling + // that the whole picture has the same slice type + int same_slicetype_for_whole_frame = 5; + +#if (MVC_EXTENSION_ENABLE) + if(currSlice->p_Inp->num_of_views==2) + { + same_slicetype_for_whole_frame = 0; + } +#endif + + switch (currSlice->slice_type) + { + case I_SLICE: + return 2 + same_slicetype_for_whole_frame; + break; + case P_SLICE: + return 0 + same_slicetype_for_whole_frame; + break; + case B_SLICE: + return 1 + same_slicetype_for_whole_frame; + break; + case SP_SLICE: + return 3 + same_slicetype_for_whole_frame; + break; + case SI_SLICE: + return 4 + same_slicetype_for_whole_frame; + break; + default: + error("Picture Type not supported!",1); + break; + } + + return 0; +} + + + +/*! + ***************************************************************************** + * + * \brief + * int Partition_BC_Header () write the Partition type B, C header + * + * \return + * Number of bits used by the partition header + * + * \par Parameters + * PartNo: Partition Number to which the header should be written + * + * \par Side effects + * Partition header as per VCEG-N72r2 is written into the appropriate + * partition bit buffer + * + * \par Limitations/Shortcomings/Tweaks + * The current code does not support the change of picture parameters within + * one coded sequence, hence there is only one parameter set necessary. This + * is hard coded to zero. + * + * \date + * October 24, 2001 + * + * \author + * Stephan Wenger stewe@cs.tu-berlin.de + *****************************************************************************/ +int Partition_BC_Header(Slice *currSlice, int PartNo) +{ + DataPartition *partition = &((currSlice)->partArr[PartNo]); + SyntaxElement sym; + + assert (PartNo > 0 && PartNo < currSlice->max_part_nr); + + sym.type = SE_HEADER; // This will be true for all symbols generated here + sym.value2 = 0; + + SYMTRACESTRING("RTP-PH: Slice ID"); + sym.value1 = currSlice->slice_nr; + writeSE_UVLC (&sym, partition); + + return sym.len; +} + diff --git a/lencod/src/image.c b/lencod/src/image.c new file mode 100644 index 0000000..5627aa2 --- /dev/null +++ b/lencod/src/image.c @@ -0,0 +1,3374 @@ + +/*! + ************************************************************************************* + * \file image.c + * + * \brief + * Code one image/slice + * + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * - Inge Lille-Langoy + * - Rickard Sjoberg + * - Jani Lainema + * - Sebastian Purreiter + * - Byeong-Moon Jeon + * - Yoon-Seong Soh + * - Thomas Stockhammer + * - Detlev Marpe + * - Guido Heising + * - Thomas Wedi + * - Ragip Kurceren + * - Antti Hallapuro + * - Alexis Michael Tourapis + * - Athanasios Leontaris + * - Yan Ye + ************************************************************************************* + */ +#include "contributors.h" + +#include +#include + +#include "global.h" + +#include "filehandle.h" +#include "mbuffer.h" +#include "img_luma.h" +#include "img_chroma.h" +#include "img_distortion.h" +#include "intrarefresh.h" +#include "slice.h" +#include "fmo.h" +#include "sei.h" +#include "memalloc.h" +#include "fast_memory.h" +#include "nalu.h" +#include "ratectl.h" +#include "mb_access.h" +#include "context_ini.h" +#include "biariencode.h" +#include "enc_statistics.h" +#include "conformance.h" +#include "report.h" + +#include "q_matrix.h" +#include "q_offsets.h" +#include "wp.h" +#include "input.h" +#include "image.h" +#include "errdo.h" +#include "img_process.h" +#include "rdopt.h" +#include "sei.h" +#include "configfile.h" +#include "mbuffer.h" + +//#include "resize.h" +#include "md_common.h" +#include "me_epzs_common.h" + +//! Edited by Joao Carreira +#include "transcode_enc.h" + +extern void UpdateDecoders (VideoParameters *p_Vid, InputParameters *p_Inp, StorablePicture *enc_pic); + +extern void DeblockFrame (VideoParameters *p_Vid, imgpel **, imgpel ***); + +static void code_a_picture (VideoParameters *p_Vid, Picture *pic); +static void field_picture (VideoParameters *p_Vid, Picture *top, Picture *bottom); +static void prepare_enc_frame_picture (VideoParameters *p_Vid, StorablePicture **stored_pic); +#if (MVC_EXTENSION_ENABLE) +static void writeout_picture (VideoParameters *p_Vid, Picture *pic, int is_bottom); +#else +static void writeout_picture (VideoParameters *p_Vid, Picture *pic); +#endif +static byte picture_structure_decision(VideoParameters *p_Vid, Picture *frame, Picture *top, Picture *bot); +static void distortion_fld (VideoParameters *p_Vid, InputParameters *p_Inp, Picture *field_pic, ImageData *imgData); + +static void field_mode_buffer (VideoParameters *p_Vid); +static void frame_mode_buffer (VideoParameters *p_Vid); +static void init_frame (VideoParameters *p_Vid, InputParameters *p_Inp); +static void init_field (VideoParameters *p_Vid, InputParameters *p_Inp); +static void put_buffer_frame (VideoParameters *p_Vid); +static void put_buffer_top (VideoParameters *p_Vid); +static void put_buffer_bot (VideoParameters *p_Vid); + +static void ReportFirstframe (VideoParameters *p_Vid, int64 tmp_time); +static void ReportI (VideoParameters *p_Vid, int64 tmp_time); +static void ReportP (VideoParameters *p_Vid, int64 tmp_time); +static void ReportB (VideoParameters *p_Vid, int64 tmp_time); +static void ReportNALNonVLCBits(VideoParameters *p_Vid, int64 tmp_time); + +extern void rd_picture_coding(VideoParameters *p_Vid, InputParameters *p_Inp); + +void MbAffPostProc(VideoParameters *p_Vid) +{ + imgpel temp[32][16]; + + StorablePicture *p_Enc_Pic = p_Vid->enc_picture; + imgpel ** imgY = p_Enc_Pic->imgY; + imgpel ***imgUV = p_Enc_Pic->imgUV; + short i, y, x0, y0, uv; + + if (p_Vid->yuv_format != YUV400) + { + for (i=0; i<(int)p_Vid->PicSizeInMbs; i+=2) + { + if (p_Enc_Pic->motion.mb_field[i]) + { + get_mb_pos(p_Vid, i, p_Vid->mb_size[IS_LUMA], &x0, &y0); + for (y=0; y<(2*MB_BLOCK_SIZE);y++) + memcpy(&temp[y],&imgY[y0+y][x0], MB_BLOCK_SIZE * sizeof(imgpel)); + + for (y=0; ymb_cr_size_x); + y0 = y0 / (16/p_Vid->mb_cr_size_y); + + for (uv=0; uv<2; uv++) + { + for (y=0; y < (2 * p_Vid->mb_cr_size_y); y++) + memcpy(&temp[y],&imgUV[uv][y0+y][x0], p_Vid->mb_cr_size_x * sizeof(imgpel)); + + for (y=0; ymb_cr_size_y;y++) + { + memcpy(&imgUV[uv][y0+(2*y)][x0],temp[y], p_Vid->mb_cr_size_x * sizeof(imgpel)); + memcpy(&imgUV[uv][y0+(2*y + 1)][x0],temp[y+ p_Vid->mb_cr_size_y], p_Vid->mb_cr_size_x * sizeof(imgpel)); + } + } + } + } + } + else + { + for (i=0; i<(int)p_Vid->PicSizeInMbs; i+=2) + { + if (p_Enc_Pic->motion.mb_field[i]) + { + get_mb_pos(p_Vid, i, p_Vid->mb_size[IS_LUMA], &x0, &y0); + for (y=0; y<(2*MB_BLOCK_SIZE);y++) + memcpy(&temp[y],&imgY[y0+y][x0], MB_BLOCK_SIZE * sizeof(imgpel)); + + for (y=0; ytype = (short) slice_type; // set slice type + p_Vid->RCMinQP = p_Inp->RCMinQP[p_Vid->type]; + p_Vid->RCMaxQP = p_Inp->RCMaxQP[p_Vid->type]; +} + +static void code_a_plane(VideoParameters *p_Vid, InputParameters *p_Inp) +{ + unsigned int NumberOfCodedMBs = 0; + int SliceGroup = 0; + DistMetric distortion; + + // The slice_group_change_cycle can be changed here. + // FmoInit() is called before coding each picture, frame or field + p_Vid->slice_group_change_cycle=1; + FmoInit(p_Vid, p_Vid->active_pps, p_Vid->active_sps); + FmoStartPicture (p_Vid); //! picture level initialization of FMO + + CalculateQuant4x4Param (p_Vid); + CalculateOffset4x4Param(p_Vid); + + if(p_Inp->Transform8x8Mode) + { + CalculateQuant8x8Param (p_Vid); + CalculateOffset8x8Param(p_Vid); + } + + reset_pic_bin_count(p_Vid); + p_Vid->bytes_in_picture = 0; + + while (NumberOfCodedMBs < p_Vid->PicSizeInMbs) // loop over slices + { + // Encode one SLice Group + while (!FmoSliceGroupCompletelyCoded (p_Vid, SliceGroup)) + { + // Encode the current slice + if (!p_Vid->mb_aff_frame_flag) + NumberOfCodedMBs += encode_one_slice (p_Vid, SliceGroup, NumberOfCodedMBs); + else + NumberOfCodedMBs += encode_one_slice_MBAFF (p_Vid, SliceGroup, NumberOfCodedMBs); + + FmoSetLastMacroblockInSlice (p_Vid, p_Vid->current_mb_nr); + // Proceed to next slice + p_Vid->current_slice_nr++; + p_Vid->p_Stats->bit_slice = 0; + } + // Proceed to next SliceGroup + SliceGroup++; + } + FmoEndPicture (); + + if ((p_Inp->SkipDeBlockNonRef == 0) || (p_Vid->nal_reference_idc != 0)) + { + if(p_Inp->RDPictureDeblocking && !p_Vid->TurnDBOff) + { + find_distortion(p_Vid, &p_Vid->imgData); + distortion.value[0] = p_Vid->p_Dist->metric[SSE].value[0]; + distortion.value[1] = p_Vid->p_Dist->metric[SSE].value[1]; + distortion.value[2] = p_Vid->p_Dist->metric[SSE].value[2]; + } + else + distortion.value[0] = distortion.value[1] = distortion.value[2] = 0; + + DeblockFrame (p_Vid, p_Vid->enc_picture->imgY, p_Vid->enc_picture->imgUV); //comment out to disable deblocking filter + + if(p_Inp->RDPictureDeblocking && !p_Vid->TurnDBOff) + { + find_distortion(p_Vid, &p_Vid->imgData); + if(distortion.value[0]+distortion.value[1]+distortion.value[2] < + p_Vid->p_Dist->metric[SSE].value[0]+ + p_Vid->p_Dist->metric[SSE].value[1]+ + p_Vid->p_Dist->metric[SSE].value[2]) + { + p_Vid->EvaluateDBOff = 1; + } + } + } + +} +/*! + ************************************************************************ + * \brief + * Encodes a picture + * + * This is the main picture coding loop.. It is called by all this + * frame and field coding stuff after the p_Vid-> elements have been + * set up. Not sure whether it is useful for MB-adaptive frame/field + * coding + ************************************************************************ + */ +static void code_a_picture(VideoParameters *p_Vid, Picture *pic) +{ + InputParameters *p_Inp = p_Vid->p_Inp; + int pl; + + p_Vid->currentPicture = pic; + p_Vid->currentPicture->idr_flag = get_idr_flag(p_Vid); + + pic->no_slices = 0; + + RandomIntraNewPicture (p_Vid); //! Allocates forced INTRA MBs (even for fields!) + if( (p_Inp->separate_colour_plane_flag != 0) ) + { + for( pl=0; plcurrent_mb_nr = 0; + p_Vid->current_slice_nr = 0; + p_Vid->SumFrameQP = 0; + p_Vid->num_ref_idx_l0_active = 0; + p_Vid->num_ref_idx_l1_active = 0; + + p_Vid->colour_plane_id = (char) pl; + + code_a_plane(p_Vid, p_Inp); + } + } + else + { + code_a_plane(p_Vid, p_Inp); + } + + if (p_Vid->mb_aff_frame_flag) + MbAffPostProc(p_Vid); + +} + +/*! + ************************************************************************ + * \brief + * Determine whether picture is coded as IDR + ************************************************************************ + */ +byte get_idr_flag( VideoParameters *p_Vid ) +{ + int idr_flag; + FrameUnitStruct *p_cur_frm = p_Vid->p_curr_frm_struct; + + switch( p_Vid->structure ) + { + default: + case FRAME: + idr_flag = p_cur_frm->p_frame_pic->idr_flag; + break; + case TOP_FIELD: + idr_flag = p_cur_frm->p_top_fld_pic->idr_flag; + break; + case BOTTOM_FIELD: + idr_flag = p_cur_frm->p_bot_fld_pic->idr_flag; + break; + } + + return (byte) idr_flag; +} + +/*! + ************************************************************************ + * \brief + * Determine whether picture is coded as IDR or Openg GOP Intra + ************************************************************************ + */ +byte get_random_access_flag( VideoParameters *p_Vid ) +{ + int random_access; + FrameUnitStruct *p_cur_frm = p_Vid->p_curr_frm_struct; + + switch( p_Vid->structure ) + { + default: + case FRAME: + random_access = p_cur_frm->p_frame_pic->random_access; + break; + case TOP_FIELD: + random_access = p_cur_frm->p_top_fld_pic->random_access; + break; + case BOTTOM_FIELD: + random_access = p_cur_frm->p_bot_fld_pic->random_access; + break; + } + + return (byte) random_access; +} + +/*! + ************************************************************************ + * \brief + * Update global stats + ************************************************************************ + */ +void update_global_stats(InputParameters *p_Inp, StatParameters *gl_stats, StatParameters *cur_stats) +{ + int i, j, k; + + if (p_Inp->skip_gl_stats == 0) + { + for (i = 0; i < 4; i++) + { + gl_stats->intra_chroma_mode[i] += cur_stats->intra_chroma_mode[i]; + } + + for (i = 0; i < 5; i++) + { + gl_stats->quant[i] += cur_stats->quant[i]; + gl_stats->num_macroblocks[i] += cur_stats->num_macroblocks[i]; + gl_stats->bit_use_mb_type [i] += cur_stats->bit_use_mb_type[i]; + gl_stats->bit_use_header [i] += cur_stats->bit_use_header[i]; + gl_stats->tmp_bit_use_cbp [i] += cur_stats->tmp_bit_use_cbp[i]; + gl_stats->bit_use_coeffC [i] += cur_stats->bit_use_coeffC[i]; + gl_stats->bit_use_coeff[0][i] += cur_stats->bit_use_coeff[0][i]; + gl_stats->bit_use_coeff[1][i] += cur_stats->bit_use_coeff[1][i]; + gl_stats->bit_use_coeff[2][i] += cur_stats->bit_use_coeff[2][i]; + gl_stats->bit_use_delta_quant[i] += cur_stats->bit_use_delta_quant[i]; + gl_stats->bit_use_stuffing_bits[i] += cur_stats->bit_use_stuffing_bits[i]; + + for (k = 0; k < 2; k++) + gl_stats->b8_mode_0_use[i][k] += cur_stats->b8_mode_0_use[i][k]; + + for (j = 0; j < 15; j++) + { + gl_stats->mode_use[i][j] += cur_stats->mode_use[i][j]; + gl_stats->bit_use_mode[i][j] += cur_stats->bit_use_mode[i][j]; + for (k = 0; k < 2; k++) + gl_stats->mode_use_transform[i][j][k] += cur_stats->mode_use_transform[i][j][k]; + } + } + } +} + +static void storeRedundantFrame(VideoParameters *p_Vid) +{ + int j, k; + if(p_Vid->key_frame) + { + for(j=0; jheight; j++) + { + memcpy(p_Vid->imgY_tmp[j], p_Vid->enc_frame_picture[0]->imgY[j], p_Vid->width * sizeof(imgpel)); + } + + for (k = 0; k < 2; k++) + { + for(j=0; jheight_cr; j++) + { + memcpy(p_Vid->imgUV_tmp[k][j], p_Vid->enc_frame_picture[0]->imgUV[k][j], p_Vid->width_cr * sizeof(imgpel)); + } + } + } + + if(p_Vid->redundant_coding) + { + for(j=0; jheight; j++) + { + memcpy(p_Vid->enc_frame_picture[0]->imgY[j], p_Vid->imgY_tmp[j], p_Vid->width * sizeof(imgpel)); + } + for (k = 0; k < 2; k++) + { + for(j=0; jheight_cr; j++) + { + memcpy(p_Vid->enc_frame_picture[0]->imgUV[k][j], p_Vid->imgUV_tmp[k][j], p_Vid->width_cr * sizeof(imgpel)); + } + } + } +} +/*! + ************************************************************************ + * \brief + * Free storable pictures + ************************************************************************ + */ +void free_pictures(VideoParameters *p_Vid, int dummy) +{ + int i; + for (i = 0; i < 6; i++) + { + if(p_Vid->enc_picture != p_Vid->enc_frame_picture[i]) + free_storable_picture(p_Vid, p_Vid->enc_frame_picture[i]); + } +} + +#if (MVC_EXTENSION_ENABLE) +/*! + ************************************************************************ + * \brief + * Write EL NALUs for field coded pictures + ************************************************************************ + */ +void write_el_field_vcl_nalu(VideoParameters *p_Vid) +{ + int temp1_nal_reference_idc; + int temp1_non_idr_flag[2]; + int temp1_priority_id; + int temp1_view_id; + int temp1_temporal_id; + int temp1_anchor_pic_flag[2]; + int temp1_inter_view_flag[2]; + + if(p_Vid->view_id == 1) + { + int temp_view_id = p_Vid->view_id; + + p_Vid->view_id = 0; + p_Vid->structure = TOP_FIELD; + write_non_vcl_nalu_mvc(p_Vid); + + //backup view_id 1 + temp1_nal_reference_idc = p_Vid->nal_reference_idc; + temp1_non_idr_flag[0] = p_Vid->non_idr_flag[0]; + temp1_non_idr_flag[1] = p_Vid->non_idr_flag[1]; + temp1_priority_id = p_Vid->priority_id; + temp1_view_id = temp_view_id; + temp1_temporal_id = p_Vid->temporal_id; + temp1_anchor_pic_flag[0] = p_Vid->anchor_pic_flag[0]; + temp1_anchor_pic_flag[1] = p_Vid->anchor_pic_flag[1]; + temp1_inter_view_flag[0] = p_Vid->inter_view_flag[0]; + temp1_inter_view_flag[1] = p_Vid->inter_view_flag[1]; + + //restore view_id 0 + p_Vid->nal_reference_idc = p_Vid->temp0_nal_reference_idc; + p_Vid->non_idr_flag[0] = p_Vid->temp0_non_idr_flag[0]; + p_Vid->non_idr_flag[1] = p_Vid->temp0_non_idr_flag[1]; + p_Vid->priority_id = p_Vid->temp0_priority_id; + p_Vid->view_id = p_Vid->temp0_view_id; + p_Vid->temporal_id = p_Vid->temp0_temporal_id; + p_Vid->anchor_pic_flag[0] = p_Vid->temp0_anchor_pic_flag[0]; + p_Vid->anchor_pic_flag[1] = p_Vid->temp0_anchor_pic_flag[1]; + p_Vid->inter_view_flag[0] = p_Vid->temp0_inter_view_flag[0]; + p_Vid->inter_view_flag[1] = p_Vid->temp0_inter_view_flag[1]; + writeout_picture (p_Vid, p_Vid->field_pic1[0], 0); //top + + //restore view_id 1 + p_Vid->nal_reference_idc = temp1_nal_reference_idc; + p_Vid->non_idr_flag[0] = temp1_non_idr_flag[0]; + p_Vid->non_idr_flag[1] = temp1_non_idr_flag[1]; + p_Vid->priority_id = temp1_priority_id; + p_Vid->view_id = temp1_view_id; + p_Vid->temporal_id = temp1_temporal_id; + p_Vid->anchor_pic_flag[0] = temp1_anchor_pic_flag[0]; + p_Vid->anchor_pic_flag[1] = temp1_anchor_pic_flag[1]; + p_Vid->inter_view_flag[0] = temp1_inter_view_flag[0]; + p_Vid->inter_view_flag[1] = temp1_inter_view_flag[1]; + write_non_vcl_nalu_mvc( p_Vid ); + writeout_picture (p_Vid, p_Vid->field_pic2[0], 0); //top + + //restore view_id 0 + p_Vid->nal_reference_idc = p_Vid->temp0_nal_reference_idc; + p_Vid->non_idr_flag[0] = p_Vid->temp0_non_idr_flag[0]; + p_Vid->non_idr_flag[1] = p_Vid->temp0_non_idr_flag[1]; + p_Vid->priority_id = p_Vid->temp0_priority_id; + p_Vid->view_id = p_Vid->temp0_view_id; + p_Vid->temporal_id = p_Vid->temp0_temporal_id; + p_Vid->anchor_pic_flag[0] = p_Vid->temp0_anchor_pic_flag[0]; + p_Vid->anchor_pic_flag[1] = p_Vid->temp0_anchor_pic_flag[1]; + p_Vid->inter_view_flag[0] = p_Vid->temp0_inter_view_flag[0]; + p_Vid->inter_view_flag[1] = p_Vid->temp0_inter_view_flag[1]; + p_Vid->structure = BOTTOM_FIELD; + write_non_vcl_nalu_mvc( p_Vid ); + writeout_picture (p_Vid, p_Vid->field_pic1[1], 1); //bot + + //restore view_id 1 + p_Vid->nal_reference_idc = temp1_nal_reference_idc; + p_Vid->non_idr_flag[0] = temp1_non_idr_flag[0]; + p_Vid->non_idr_flag[1] = temp1_non_idr_flag[1]; + p_Vid->priority_id = temp1_priority_id; + p_Vid->view_id = temp1_view_id; + p_Vid->temporal_id = temp1_temporal_id; + p_Vid->anchor_pic_flag[0] = temp1_anchor_pic_flag[0]; + p_Vid->anchor_pic_flag[1] = temp1_anchor_pic_flag[1]; + p_Vid->inter_view_flag[0] = temp1_inter_view_flag[0]; + p_Vid->inter_view_flag[1] = temp1_inter_view_flag[1]; + write_non_vcl_nalu_mvc( p_Vid ); + writeout_picture (p_Vid, p_Vid->field_pic2[1], 1); //bot + } + else // store the first view nal unit + { + p_Vid->temp0_nal_reference_idc = p_Vid->nal_reference_idc; + p_Vid->temp0_non_idr_flag[0] = p_Vid->non_idr_flag[0]; + p_Vid->temp0_non_idr_flag[1] = p_Vid->non_idr_flag[1]; + p_Vid->temp0_priority_id = p_Vid->priority_id; + p_Vid->temp0_view_id = p_Vid->view_id; + p_Vid->temp0_temporal_id = p_Vid->temporal_id; + p_Vid->temp0_anchor_pic_flag[0] = p_Vid->anchor_pic_flag[0]; + p_Vid->temp0_anchor_pic_flag[1] = p_Vid->anchor_pic_flag[1]; + p_Vid->temp0_inter_view_flag[0] = p_Vid->inter_view_flag[0]; + p_Vid->temp0_inter_view_flag[1] = p_Vid->inter_view_flag[1]; + } +} + +/*! + ************************************************************************ + * \brief + * Write coded frame (one frame picture or two field pictures) + ************************************************************************ + */ +void write_frame_picture(VideoParameters *p_Vid) +{ + InputParameters *p_Inp = p_Vid->p_Inp; + + if (p_Vid->fld_flag) // field mode (use field when fld_flag=1 only) + { + field_mode_buffer (p_Vid); + + if(p_Inp->num_of_views==1) + { + p_Vid->structure = TOP_FIELD; + write_non_vcl_nalu(p_Vid); + writeout_picture (p_Vid, p_Vid->field_pic1[0], 0); + p_Vid->structure = BOTTOM_FIELD; + write_non_vcl_nalu_bot_fld(p_Vid); + writeout_picture (p_Vid, p_Vid->field_pic1[1], 1); + } + else + { + write_el_field_vcl_nalu(p_Vid); + } + } + else //frame mode + { + frame_mode_buffer (p_Vid); + p_Vid->structure = FRAME; + +#if (MVC_EXTENSION_ENABLE) + if ( p_Inp->num_of_views == 2 ) + { + write_non_vcl_nalu_mvc(p_Vid); + } + else +#endif + write_non_vcl_nalu(p_Vid); + if (p_Vid->type==SI_SLICE) + { + writeout_picture (p_Vid, p_Vid->frame_pic_si, 0); + } + else + { + writeout_picture (p_Vid, p_Vid->p_frame_pic, 0); + } + } +} +#else +void write_frame_picture(VideoParameters *p_Vid) +{ + InputParameters *p_Inp = p_Vid->p_Inp; + if (p_Vid->fld_flag) // field mode (use field when fld_flag=1 only) + { + p_Vid->structure = TOP_FIELD; + field_mode_buffer (p_Vid); + write_non_vcl_nalu(p_Vid); + writeout_picture (p_Vid, p_Vid->field_pic[0]); + p_Vid->structure = BOTTOM_FIELD; + write_non_vcl_nalu_bot_fld(p_Vid); + writeout_picture (p_Vid, p_Vid->field_pic[1]); + } + else //frame mode + { + frame_mode_buffer (p_Vid); + + write_non_vcl_nalu(p_Vid); + if (p_Vid->type==SI_SLICE) + { + writeout_picture (p_Vid, p_Vid->frame_pic_si); + } + else + { + writeout_picture (p_Vid, p_Vid->p_frame_pic); + } + } +} +#endif + +/*! + ************************************************************************ + * \brief + * Read input data while considering and performing a 3:2 pulldown + * process. + ************************************************************************ + */ +static int read_input_data_32pulldown(VideoParameters *p_Vid) +{ + InputParameters *p_Inp = p_Vid->p_Inp; + int file_read = 0; + int frm_no_in_file_first = 0; + int frm_no_in_file_second = 0; + int pull_down_offset = p_Inp->enable_32_pulldown == 1 ? 1 : 2; + +#if (MVC_EXTENSION_ENABLE) + if(p_Inp->num_of_views==2 && p_Vid->view_id == 1) + { + frm_no_in_file_first = ((p_Vid->frm_no_in_file * 4 + pull_down_offset)/ 5); + frm_no_in_file_second = ((p_Vid->frm_no_in_file * 4 + 3)/ 5); + + // Read frame data (first frame) + file_read = read_one_frame (p_Vid, &p_Inp->input_file2, frm_no_in_file_first, p_Inp->infile_header, &p_Inp->source, &p_Inp->output, p_Vid->imgData0.frm_data); + if ( !file_read ) + { + // end of file or stream found: trigger error handling + get_number_of_frames (p_Inp, &p_Inp->input_file2); + fprintf(stdout, "\nIncorrect FramesToBeEncoded: actual number is %6d frames!\n", p_Inp->no_frames ); + return 0; + } + pad_borders (p_Inp->output, p_Vid->width, p_Vid->height, p_Vid->width_cr, p_Vid->height_cr, p_Vid->imgData0.frm_data); + + // Read frame data (second frame) + file_read = read_one_frame (p_Vid, &p_Inp->input_file2, frm_no_in_file_second, p_Inp->infile_header, &p_Inp->source, &p_Inp->output, p_Vid->imgData4.frm_data); + if ( !file_read ) + { + // end of file or stream found: trigger error handling + get_number_of_frames (p_Inp, &p_Inp->input_file2); + fprintf(stdout, "\nIncorrect FramesToBeEncoded: actual number is %6d frames!\n", p_Inp->no_frames ); + return 0; + } + pad_borders (p_Inp->output, p_Vid->width, p_Vid->height, p_Vid->width_cr, p_Vid->height_cr, p_Vid->imgData4.frm_data); + } + else +#endif + { + frm_no_in_file_first = ((p_Vid->frm_no_in_file * 4 + pull_down_offset)/ 5); + frm_no_in_file_second = ((p_Vid->frm_no_in_file * 4 + 3)/ 5); + + // Read frame data (first frame) + file_read = read_one_frame (p_Vid, &p_Inp->input_file1, frm_no_in_file_first, p_Inp->infile_header, &p_Inp->source, &p_Inp->output, p_Vid->imgData0.frm_data); + if ( !file_read ) + { + // end of file or stream found: trigger error handling + get_number_of_frames (p_Inp, &p_Inp->input_file1); + fprintf(stdout, "\nIncorrect FramesToBeEncoded: actual number is %6d frames!\n", p_Inp->no_frames ); + return 0; + } + pad_borders (p_Inp->output, p_Vid->width, p_Vid->height, p_Vid->width_cr, p_Vid->height_cr, p_Vid->imgData0.frm_data); + + // Read frame data (second frame) + file_read = read_one_frame (p_Vid, &p_Inp->input_file1, frm_no_in_file_second, p_Inp->infile_header, &p_Inp->source, &p_Inp->output, p_Vid->imgData4.frm_data); + if ( !file_read ) + { + // end of file or stream found: trigger error handling + get_number_of_frames (p_Inp, &p_Inp->input_file1); + fprintf(stdout, "\nIncorrect FramesToBeEncoded: actual number is %6d frames!\n", p_Inp->no_frames ); + return 0; + } + pad_borders (p_Inp->output, p_Vid->width, p_Vid->height, p_Vid->width_cr, p_Vid->height_cr, p_Vid->imgData4.frm_data); + } + + + return 1; +} + +static int read_input_data(VideoParameters *p_Vid) +{ + InputParameters *p_Inp = p_Vid->p_Inp; + int file_read = 0; + +#if (MVC_EXTENSION_ENABLE) + if(p_Inp->num_of_views==2 && p_Vid->view_id == 1) + { + file_read = read_one_frame (p_Vid, &p_Inp->input_file2, p_Vid->frm_no_in_file, p_Inp->infile_header, &p_Inp->source, &p_Inp->output, p_Vid->imgData0.frm_data); + if ( !file_read ) + { + // end of file or stream found: trigger error handling + get_number_of_frames (p_Inp, &p_Inp->input_file2); + fprintf(stdout, "\nIncorrect FramesToBeEncoded: actual number is %6d frames!\n", p_Inp->no_frames ); + return 0; + } + pad_borders (p_Inp->output, p_Vid->width, p_Vid->height, p_Vid->width_cr, p_Vid->height_cr, p_Vid->imgData0.frm_data); + } + else +#endif + { + file_read = read_one_frame (p_Vid, &p_Inp->input_file1, p_Vid->frm_no_in_file, p_Inp->infile_header, &p_Inp->source, &p_Inp->output, p_Vid->imgData0.frm_data); + if ( !file_read ) + { + // end of file or stream found: trigger error handling + get_number_of_frames (p_Inp, &p_Inp->input_file1); + fprintf(stdout, "\nIncorrect FramesToBeEncoded: actual number is %6d frames!\n", p_Inp->no_frames ); + return 0; + } + pad_borders (p_Inp->output, p_Vid->width, p_Vid->height, p_Vid->width_cr, p_Vid->height_cr, p_Vid->imgData0.frm_data); + } + + return 1; +} + +void perform_encode_field(VideoParameters *p_Vid) +{ + InputParameters *p_Inp = p_Vid->p_Inp; + + //Rate control + if ( p_Inp->RCEnable && p_Inp->RCUpdateMode <= MAX_RC_MODE ) + p_Vid->p_rc_gen->FieldControl = 1; + + p_Vid->field_picture = 1; // we encode fields + +#if (MVC_EXTENSION_ENABLE) + field_picture (p_Vid, p_Vid->field_pic_ptr[0], p_Vid->field_pic_ptr[1]); +#else + field_picture (p_Vid, p_Vid->field_pic[0], p_Vid->field_pic[1]); +#endif + p_Vid->fld_flag = TRUE; +} + +void perform_encode_frame(VideoParameters *p_Vid) +{ + InputParameters *p_Inp = p_Vid->p_Inp; + int tmpFrameQP = 0; + int num_ref_idx_l0 = 0; + int num_ref_idx_l1 = 0; + + int frame_type; + + //if( p_Vid->type != I_SLICE ) + // readTranscodingInfo( p_Vid ); + + //Rate control + if ( p_Inp->RCEnable && p_Inp->RCUpdateMode <= MAX_RC_MODE ) + p_Vid->p_rc_gen->FieldControl = 0; + + p_Vid->field_picture = 0; // we encode a frame + + //Rate control + if(p_Inp->RCEnable) + rc_init_frame(p_Vid, p_Inp); + // Ensure that p_Vid->p_curr_frm_struct->qp is properly set + p_Vid->p_curr_frm_struct->qp = p_Vid->qp; + + p_Vid->active_pps = p_Vid->PicParSet[0]; + +#if (MVC_EXTENSION_ENABLE) + if(p_Vid->view_id!=1 || !p_Vid->sec_view_force_fld) + { +#endif + if ((p_Vid->type == B_SLICE || p_Vid->type == P_SLICE || p_Vid->type==I_SLICE) && p_Inp->RDPictureDecision) + { + frame_picture_mp (p_Vid, p_Inp); + } + else + { + frame_picture (p_Vid, p_Vid->frame_pic[0], &p_Vid->imgData, 0); + p_Vid->p_frame_pic = p_Vid->frame_pic[0]; + } + + tmpFrameQP = p_Vid->SumFrameQP; // call it here since rd_picture_coding buffers it and may modify it + num_ref_idx_l0 = p_Vid->num_ref_idx_l0_active; + num_ref_idx_l1 = p_Vid->num_ref_idx_l1_active; + + if (p_Vid->p_curr_frm_struct->type == SI_SLICE) + { + // once the picture has been encoded as a primary SP frame encode as an SI frame + set_slice_type( p_Vid, p_Inp, SI_SLICE ); + frame_picture (p_Vid, p_Vid->frame_pic_si, &p_Vid->imgData, 0); + } + + if ((p_Vid->type == SP_SLICE) && (p_Inp->sp_output_indicator)) + { + // output the transformed and quantized coefficients (useful for switching SP frames) + output_SP_coefficients(p_Vid, p_Inp); + } +#if (MVC_EXTENSION_ENABLE) + } +#endif + +#if (MVC_EXTENSION_ENABLE) + if ((p_Inp->PicInterlace == ADAPTIVE_CODING) && (!is_MVC_profile(p_Vid->active_sps->profile_idc) || + (/*(p_Vid->active_sps->profile_idc == SBSPLUS_HIGH) &&*/ ((p_Vid->view_id == 0) || (p_Vid->view_id == 1 && p_Vid->sec_view_force_fld))))) +#else + if (p_Inp->PicInterlace == ADAPTIVE_CODING) +#endif + { + DecRefPicMarking_t *tmp_drpm; + + if ( p_Inp->RCEnable && p_Inp->RCUpdateMode <= MAX_RC_MODE ) + p_Vid->p_rc_gen->FieldControl=1; + p_Vid->write_macroblock = FALSE; + p_Vid->bot_MB = FALSE; + + frame_type = p_Vid->type; + + p_Vid->field_picture = 1; // we encode fields + + //Free frame based dec_ref_pic_marking_buffer + while (p_Vid->dec_ref_pic_marking_buffer) + { + tmp_drpm = p_Vid->dec_ref_pic_marking_buffer; + p_Vid->dec_ref_pic_marking_buffer = tmp_drpm->Next; + free(tmp_drpm); + } + +#if (MVC_EXTENSION_ENABLE) + field_picture (p_Vid, p_Vid->field_pic_ptr[0], p_Vid->field_pic_ptr[1]); + + if(p_Inp->num_of_views==1 || p_Vid->view_id==0) // first view will make the decision + { + if(p_Vid->rd_pass == 0) + p_Vid->fld_flag = picture_structure_decision (p_Vid, p_Vid->frame_pic[0], p_Vid->field_pic_ptr[0], p_Vid->field_pic_ptr[1]); + else if(p_Vid->rd_pass == 1) + p_Vid->fld_flag = picture_structure_decision (p_Vid, p_Vid->frame_pic[1], p_Vid->field_pic_ptr[0], p_Vid->field_pic_ptr[1]); + else + p_Vid->fld_flag = picture_structure_decision (p_Vid, p_Vid->frame_pic[2], p_Vid->field_pic_ptr[0], p_Vid->field_pic_ptr[1]); + + p_Vid->sec_view_force_fld = p_Vid->fld_flag; // store 1st view decision, same coding structure to be used for second view + } + else + { + p_Vid->fld_flag = (byte) p_Vid->sec_view_force_fld; + } +#else + field_picture (p_Vid, p_Vid->field_pic[0], p_Vid->field_pic[1]); + + if(p_Vid->rd_pass == 0) + p_Vid->fld_flag = picture_structure_decision (p_Vid, p_Vid->frame_pic[0], p_Vid->field_pic[0], p_Vid->field_pic[1]); + else if(p_Vid->rd_pass == 1) + p_Vid->fld_flag = picture_structure_decision (p_Vid, p_Vid->frame_pic[1], p_Vid->field_pic[0], p_Vid->field_pic[1]); + else + p_Vid->fld_flag = picture_structure_decision (p_Vid, p_Vid->frame_pic[2], p_Vid->field_pic[0], p_Vid->field_pic[1]); +#endif + + if ( p_Vid->fld_flag ) + { + tmpFrameQP = p_Vid->SumFrameQP; + num_ref_idx_l0 = p_Vid->num_ref_idx_l0_active; + num_ref_idx_l1 = p_Vid->num_ref_idx_l1_active; + } + + if ( p_Vid->fld_flag==0 ) + { + p_Vid->type = (short) frame_type; + } + + update_field_frame_contexts (p_Vid, p_Vid->fld_flag); + + //Rate control + if ( p_Inp->RCEnable && p_Inp->RCUpdateMode <= MAX_RC_MODE ) + p_Vid->p_rc_gen->FieldFrame = !(p_Vid->fld_flag) ? 1 : 0; + } + else + p_Vid->fld_flag = FALSE; + + p_Vid->SumFrameQP = tmpFrameQP; + p_Vid->num_ref_idx_l0_active = num_ref_idx_l0; + p_Vid->num_ref_idx_l1_active = num_ref_idx_l1; +} + +void free_slice_data(VideoParameters *p_Vid) +{ + int i; + + if (p_Vid->frame_pic_si) + { + free_slice_list(p_Vid->frame_pic_si); + } + + for (i = 0; i < p_Vid->frm_iter; i++) + { + if (p_Vid->frame_pic[i]) + { + free_slice_list(p_Vid->frame_pic[i]); + } + } + +#if (MVC_EXTENSION_ENABLE) + if ( p_Vid->field_pic_ptr && (p_Vid->p_Inp->num_of_views==1 || p_Vid->view_id==1) ) + { + for (i = 0; i < 2; i++) + { + if (p_Vid->field_pic1[i]) + free_slice_list(p_Vid->field_pic1[i]); + if (p_Vid->p_Inp->num_of_views==2 && p_Vid->field_pic2) + { + if (p_Vid->field_pic2[i]) + free_slice_list(p_Vid->field_pic2[i]); + } + } + } +#else + if (p_Vid->field_pic) + { + for (i = 0; i < 2; i++) + { + if (p_Vid->field_pic[i]) + free_slice_list(p_Vid->field_pic[i]); + } + } +#endif +} + +void store_coded_picture(DecodedPictureBuffer *p_Dpb) +{ + VideoParameters *p_Vid = p_Dpb->p_Vid; + InputParameters *p_Inp = p_Vid->p_Inp; + unsigned int profile_idc = p_Vid->active_sps->profile_idc; + +#if (MVC_EXTENSION_ENABLE) + if ( (p_Inp->PicInterlace == ADAPTIVE_CODING) && ((p_Dpb->layer_id == 0 ) || !is_MVC_profile(profile_idc))) +#else + if ( p_Inp->PicInterlace == ADAPTIVE_CODING ) +#endif + { + if (p_Vid->fld_flag) + { + update_global_stats(p_Inp, p_Vid->p_Stats, &p_Vid->enc_field_picture[0]->stats); + update_global_stats(p_Inp, p_Vid->p_Stats, &p_Vid->enc_field_picture[1]->stats); + // store bottom field + store_picture_in_dpb (p_Dpb, p_Vid->enc_field_picture[1], &p_Inp->output); +#if (MVC_EXTENSION_ENABLE) + if ( p_Vid->proc_picture && is_MVC_profile(profile_idc) ) + { + store_proc_picture_in_dpb (p_Vid->p_Dpb_layer[1], p_Vid->proc_picture, &p_Inp->output); + p_Vid->proc_picture = NULL; + } +#endif + free_storable_picture(p_Vid, p_Vid->enc_frame_picture[0]); + free_storable_picture(p_Vid, p_Vid->enc_frame_picture[1]); + free_storable_picture(p_Vid, p_Vid->enc_frame_picture[2]); + } + else + { + update_global_stats(p_Inp, p_Vid->p_Stats, &p_Vid->enc_frame_picture[p_Vid->rd_pass]->stats); + // replace top with frame + if (p_Vid->rd_pass==2) + { + replace_top_pic_with_frame(p_Dpb, p_Vid->enc_frame_picture[2], &p_Inp->output); + free_storable_picture (p_Vid, p_Vid->enc_frame_picture[0]); + free_storable_picture (p_Vid, p_Vid->enc_frame_picture[1]); + } + else if (p_Vid->rd_pass==1) + { + replace_top_pic_with_frame(p_Dpb, p_Vid->enc_frame_picture[1], &p_Inp->output); + free_storable_picture (p_Vid, p_Vid->enc_frame_picture[0]); + free_storable_picture (p_Vid, p_Vid->enc_frame_picture[2]); + } + else + { + if(p_Inp->redundant_pic_flag==0 || (p_Vid->key_frame==0)) + { + replace_top_pic_with_frame(p_Dpb, p_Vid->enc_frame_picture[0], &p_Inp->output); + free_storable_picture (p_Vid, p_Vid->enc_frame_picture[1]); + free_storable_picture (p_Vid, p_Vid->enc_frame_picture[2]); + } + } + free_storable_picture(p_Vid, p_Vid->enc_field_picture[1]); + } + } + else + { + if (p_Vid->fld_flag) + { + update_global_stats(p_Inp, p_Vid->p_Stats, &p_Vid->enc_field_picture[0]->stats); + update_global_stats(p_Inp, p_Vid->p_Stats, &p_Vid->enc_field_picture[1]->stats); + store_picture_in_dpb(p_Dpb, p_Vid->enc_field_picture[1], &p_Inp->output); +#if (MVC_EXTENSION_ENABLE) + if ( is_MVC_profile(profile_idc) && p_Vid->proc_picture) + { + store_proc_picture_in_dpb (p_Vid->p_Dpb_layer[1], p_Vid->proc_picture, &p_Inp->output); + p_Vid->proc_picture = NULL; + } +#endif + } + else + { + if ((p_Inp->redundant_pic_flag != 1) || (p_Vid->key_frame == 0)) + { + update_global_stats(p_Inp, p_Vid->p_Stats, &p_Vid->enc_picture->stats); + store_picture_in_dpb (p_Dpb, p_Vid->enc_picture, &p_Inp->output); + +#if (MVC_EXTENSION_ENABLE) + if ( is_MVC_profile(profile_idc) && p_Vid->proc_picture) + { + store_proc_picture_in_dpb (p_Vid->p_Dpb_layer[1], p_Vid->proc_picture, &p_Inp->output); + p_Vid->proc_picture = NULL; + } +#endif + free_pictures(p_Vid, -1); + } + } + } +} + +void update_bitcounter_stats(VideoParameters *p_Vid) +{ + p_Vid->p_Stats->bit_ctr_n = p_Vid->p_Stats->bit_ctr; + p_Vid->p_Stats->bit_ctr_parametersets_n = 0; + p_Vid->p_Stats->bit_ctr_filler_data_n = p_Vid->p_Stats->bit_ctr_filler_data; + +#if (MVC_EXTENSION_ENABLE) + if ( p_Vid->p_Inp->num_of_views == 2 ) + { + if ( p_Vid->fld_flag ) + { + if ( p_Vid->view_id == 1 ) + { + p_Vid->p_Stats->bit_ctr_parametersets_v[0] += p_Vid->p_Stats->bit_ctr_parametersets_n_v[0]; + p_Vid->p_Stats->bit_ctr_parametersets_v[1] += p_Vid->p_Stats->bit_ctr_parametersets_n_v[1]; + p_Vid->p_Stats->bit_ctr_n_v[0] = p_Vid->p_Stats->bit_ctr_v[0]; + p_Vid->p_Stats->bit_ctr_n_v[1] = p_Vid->p_Stats->bit_ctr_v[1]; + p_Vid->p_Stats->bit_ctr_parametersets_n_v[0] = 0; + p_Vid->p_Stats->bit_ctr_parametersets_n_v[1] = 0; + p_Vid->p_Stats->bit_ctr_filler_data_n_v[0] = p_Vid->p_Stats->bit_ctr_filler_data_v[0]; + p_Vid->p_Stats->bit_ctr_filler_data_n_v[1] = p_Vid->p_Stats->bit_ctr_filler_data_v[1]; + } + } + else // this seems to be enough and the check above seems redundant...check later... + { + p_Vid->p_Stats->bit_ctr_parametersets_v[p_Vid->view_id] += p_Vid->p_Stats->bit_ctr_parametersets_n_v[p_Vid->view_id]; + p_Vid->p_Stats->bit_ctr_n_v[p_Vid->view_id] = p_Vid->p_Stats->bit_ctr_v[p_Vid->view_id]; + p_Vid->p_Stats->bit_ctr_parametersets_n_v[p_Vid->view_id] = 0; + p_Vid->p_Stats->bit_ctr_filler_data_n_v[p_Vid->view_id] = p_Vid->p_Stats->bit_ctr_filler_data_v[p_Vid->view_id]; + } + } +#endif +} + +void update_idr_order_stats(VideoParameters *p_Vid) +{ + if ( p_Vid->fld_flag ) // need to check both fields here + { + // top field picture + if ( p_Vid->p_curr_frm_struct->p_top_fld_pic->p_Slice[0].type == I_SLICE && p_Vid->p_curr_frm_struct->p_top_fld_pic->nal_ref_idc) + { + p_Vid->lastINTRA = imax(p_Vid->lastINTRA, p_Vid->frame_no); + p_Vid->lastIntraNumber = p_Vid->curr_frm_idx; + if ( p_Vid->p_curr_frm_struct->p_top_fld_pic->idr_flag ) + { + p_Vid->last_idr_disp_order = imax( p_Vid->frame_no, p_Vid->last_idr_disp_order ); + p_Vid->last_idr_code_order = p_Vid->curr_frm_idx; + } + } + // bottom field picture + if ( p_Vid->p_curr_frm_struct->p_bot_fld_pic->p_Slice[0].type == I_SLICE && p_Vid->p_curr_frm_struct->p_bot_fld_pic->nal_ref_idc) + { + p_Vid->lastINTRA = imax(p_Vid->lastINTRA, p_Vid->frame_no); + p_Vid->lastIntraNumber = p_Vid->curr_frm_idx; + if ( p_Vid->p_curr_frm_struct->p_bot_fld_pic->idr_flag ) + { + p_Vid->last_idr_disp_order = imax( p_Vid->frame_no, p_Vid->last_idr_disp_order ); + p_Vid->last_idr_code_order = p_Vid->curr_frm_idx; + } + } + } + else + { + // frame picture + if ( p_Vid->p_curr_frm_struct->p_frame_pic->p_Slice[0].type == I_SLICE && p_Vid->p_curr_frm_struct->p_frame_pic->nal_ref_idc) + { + p_Vid->lastINTRA = imax(p_Vid->lastINTRA, p_Vid->frame_no); + p_Vid->lastIntraNumber = p_Vid->curr_frm_idx; + if ( p_Vid->p_curr_frm_struct->p_frame_pic->idr_flag ) + { + p_Vid->last_idr_disp_order = imax( p_Vid->frame_no, p_Vid->last_idr_disp_order ); + p_Vid->last_idr_code_order = p_Vid->curr_frm_idx; + } + } + } +} + +int update_video_stats(VideoParameters *p_Vid) +{ + int bits = 0; + InputParameters *p_Inp = p_Vid->p_Inp; + + //Rate control + if(p_Inp->RCEnable) + { + if ((!p_Inp->PicInterlace) && (!p_Inp->MbInterlace)) + { + bits = (int) (p_Vid->p_Stats->bit_ctr - p_Vid->p_Stats->bit_ctr_n) + + (int)( p_Vid->p_Stats->bit_ctr_filler_data - p_Vid->p_Stats->bit_ctr_filler_data_n ); + } + else if ( p_Inp->RCUpdateMode <= MAX_RC_MODE ) + { + bits = (int)(p_Vid->p_Stats->bit_ctr - (p_Vid->p_rc_quad->Pprev_bits)) + + (int)( p_Vid->p_Stats->bit_ctr_filler_data - p_Vid->p_Stats->bit_ctr_filler_data_n ); // used for rate control update + p_Vid->p_rc_quad->Pprev_bits = p_Vid->p_Stats->bit_ctr + p_Vid->p_Stats->bit_ctr_filler_data; + } + else + { + bits = (int) (p_Vid->p_Stats->bit_ctr - p_Vid->p_Stats->bit_ctr_n) + + (int)( p_Vid->p_Stats->bit_ctr_filler_data - p_Vid->p_Stats->bit_ctr_filler_data_n ); + } + } + + p_Vid->p_Stats->bit_counter[p_Vid->type] += p_Vid->p_Stats->bit_ctr - p_Vid->p_Stats->bit_ctr_n; + +#if (MVC_EXTENSION_ENABLE) + if ( p_Inp->num_of_views == 2 ) + { + if ( p_Vid->fld_flag ) + { + if ( p_Vid->view_id == 1 ) + { + int prev_type = (p_Vid->p_pred->p_frm_mvc + ( (p_Vid->curr_frm_idx << 1) % p_Vid->p_pred->num_frames_mvc ))->type; + + p_Vid->p_Stats->bit_counter_v[0][prev_type] += p_Vid->p_Stats->bit_ctr_v[0] - p_Vid->p_Stats->bit_ctr_n_v[0]; + p_Vid->p_Stats->bit_counter_v[1][p_Vid->type] += p_Vid->p_Stats->bit_ctr_v[1] - p_Vid->p_Stats->bit_ctr_n_v[1]; + } + } + else + { + p_Vid->p_Stats->bit_counter_v[p_Vid->view_id][p_Vid->type] += p_Vid->p_Stats->bit_ctr_v[p_Vid->view_id] - p_Vid->p_Stats->bit_ctr_n_v[p_Vid->view_id]; + } + } +#endif + + return bits; +} + +/*! + ************************************************************************ + * \brief + * Encodes one frame + ************************************************************************ + */ +int encode_one_frame (VideoParameters *p_Vid, InputParameters *p_Inp) +{ + int i; + int nplane; + + //Rate control + int bits = 0; + + TIME_T start_time; + TIME_T end_time; + int64 tmp_time; + + p_Vid->me_time = 0; + p_Vid->rd_pass = 0; + + if( (p_Inp->separate_colour_plane_flag != 0) ) + { + for( nplane=0; nplaneenc_frame_picture_JV[nplane] = NULL; + } + } + + for (i = 0; i < 6; i++) + p_Vid->enc_frame_picture[i] = NULL; + + gettime(&start_time); // start time in ms + + //Rate control + p_Vid->write_macroblock = FALSE; + /* + //Shankar Regunathan (Oct 2002) + //Prepare Panscanrect SEI payload + UpdatePanScanRectInfo (p_SEI); + //Prepare Arbitrarydata SEI Payload + UpdateUser_data_unregistered (p_SEI); + //Prepare Registered data SEI Payload + UpdateUser_data_registered_itu_t_t35 (p_SEI); + //Prepare RandomAccess SEI Payload + UpdateRandomAccess (p_Vid); + */ + + put_buffer_frame (p_Vid); // sets the pointers to the frame structures + // (and not to one of the field structures) + init_frame (p_Vid, p_Inp); + + if (p_Inp->enable_32_pulldown) + { + if ( !read_input_data_32pulldown (p_Vid) ) + { + return 0; + } + } + else + { + if ( !read_input_data (p_Vid) ) + { + return 0; + } + } + + process_image(p_Vid, p_Inp); + pad_borders (p_Inp->output, p_Vid->width, p_Vid->height, p_Vid->width_cr, p_Vid->height_cr, p_Vid->imgData.frm_data); + +#if (MVC_EXTENSION_ENABLE) + if(p_Inp->num_of_views==1 || p_Vid->view_id==0) + { + p_Vid->field_pic_ptr = p_Vid->field_pic1; + } + else + { + p_Vid->field_pic_ptr = p_Vid->field_pic2; + } +#endif + + + // Following code should consider optimal coding mode. Currently also does not support + // multiple slices per frame. + p_Vid->p_Dist->frame_ctr++; +#if (MVC_EXTENSION_ENABLE) + if (p_Inp->num_of_views == 2) + { + p_Vid->p_Dist->frame_ctr_v[p_Vid->view_id]++; + } +#endif + + if(p_Vid->type == SP_SLICE) + { + if(p_Inp->sp2_frame_indicator) + { // switching SP frame encoding + p_Vid->sp2_frame_indicator = TRUE; + read_SP_coefficients(p_Vid, p_Inp); + } + } + else + { + p_Vid->sp2_frame_indicator = FALSE; + } + + if ( p_Inp->WPMCPrecision ) + { + wpxInitWPXPasses(p_Vid, p_Inp); + p_Vid->pWPX->curr_wp_rd_pass = p_Vid->pWPX->wp_rd_passes; + p_Vid->pWPX->curr_wp_rd_pass->algorithm = WP_REGULAR; + } + + if (p_Inp->PicInterlace == FIELD_CODING) + perform_encode_field(p_Vid); + else + perform_encode_frame(p_Vid); + + p_Vid->p_Stats->frame_counter++; + p_Vid->p_Stats->frame_ctr[p_Vid->type]++; + + // Here, p_Vid->structure may be either FRAME or BOTTOM FIELD depending on whether AFF coding is used + // The picture structure decision changes really only the fld_flag + write_frame_picture(p_Vid); + +#if (MVC_EXTENSION_ENABLE) + if(p_Inp->num_of_views==2) + { + // view_id 1 will follow view_id 0 anchor_pic_flag value + if((p_Vid->anchor_pic_flag[0]==1) && (p_Vid->view_id&1)==0) + { + p_Vid->prev_view_is_anchor = 1; + } + else + { + p_Vid->prev_view_is_anchor = 0; + } + } +#endif + + //Need slice data for deblocking in UpdateDecoders + if (p_Inp->PicInterlace == FRAME_CODING) + { + if ((p_Inp->rdopt == 3) && (p_Inp->de == LLN) && (p_Vid->nal_reference_idc != 0)) + { + UpdateDecoders (p_Vid, p_Inp, p_Vid->enc_picture); // simulate packet losses and move decoded image to reference buffers + } + + if (p_Inp->RestrictRef) + UpdatePixelMap (p_Vid, p_Inp); + } + +#if OUTPUT_REF_LIST + { + int i; + printf( " [L0]: " ); + for ( i = 0; icurrentSlice->listXsize[0]; i++ ) + { + printf( "%d ", p_Vid->currentSlice->listX[0][i]->poc / 2 ); // may not work with field + } + printf( " [L1]: " ); + for ( i = 0; icurrentSlice->listXsize[1]; i++ ) + { + printf( "%d ", p_Vid->currentSlice->listX[1][i]->poc / 2 ); // may not work with field + } + printf("\n"); + } +#endif + + free_slice_data(p_Vid); + + //Rate control + if ( p_Inp->RCEnable ) + { + // we could add here a function pointer! + bits = (int)( p_Vid->p_Stats->bit_ctr - p_Vid->p_Stats->bit_ctr_n ) + + (int)( p_Vid->p_Stats->bit_ctr_filler_data - p_Vid->p_Stats->bit_ctr_filler_data_n ); + + if ( p_Inp->RCUpdateMode <= MAX_RC_MODE ) + p_Vid->rc_update_pict_frame_ptr(p_Vid, p_Inp, p_Vid->p_rc_quad, p_Vid->p_rc_gen, bits); + + } + + compute_distortion(p_Vid, &p_Vid->imgData); + + // redundant pictures: save reconstruction to calculate SNR and replace reference picture + if(p_Inp->redundant_pic_flag) + { + storeRedundantFrame(p_Vid); + } + + //printf("view_id:%d, frame_num:%d, poc:%d\n", p_Vid->view_id, (int)p_Vid->enc_picture->frame_num, p_Vid->enc_picture->poc); + store_coded_picture(p_Vid->p_Dpb_layer[p_Vid->view_id]); + + + p_Vid->AverageFrameQP = isign(p_Vid->SumFrameQP) * ((iabs(p_Vid->SumFrameQP) + (int) (p_Vid->FrameSizeInMbs >> 1))/ (int) p_Vid->FrameSizeInMbs); + + if ( p_Inp->RCEnable && p_Inp->RCUpdateMode <= MAX_RC_MODE && p_Vid->type != B_SLICE && p_Inp->basicunit < p_Vid->FrameSizeInMbs ) + p_Vid->p_rc_quad->CurrLastQP = p_Vid->AverageFrameQP + p_Vid->p_rc_quad->bitdepth_qp_scale; + +#ifdef _LEAKYBUCKET_ + // Store bits used for this frame and increment counter of no. of coded frames + if (!p_Vid->redundant_coding) + { + p_Vid->Bit_Buffer[p_Vid->total_frame_buffer++] = (long) (p_Vid->p_Stats->bit_ctr - p_Vid->p_Stats->bit_ctr_n) + + (long)( p_Vid->p_Stats->bit_ctr_filler_data - p_Vid->p_Stats->bit_ctr_filler_data_n ); + } +#endif + + // POC200301: Verify that POC coding type 2 is not used if more than one consecutive + // non-reference frame is requested or if decoding order is different from output order + if (p_Vid->pic_order_cnt_type == 2) + { + if (!p_Vid->nal_reference_idc) + p_Vid->consecutive_non_reference_pictures++; + else + p_Vid->consecutive_non_reference_pictures = 0; + + if (p_Vid->frame_no < p_Vid->prev_frame_no || p_Vid->consecutive_non_reference_pictures>1) + error("POC type 2 cannot be applied for the coding pattern where the encoding /decoding order of pictures are different from the output order.\n", -1); + p_Vid->prev_frame_no = p_Vid->frame_no; + } + + gettime(&end_time); // end time in ms + tmp_time = timediff(&start_time, &end_time); + p_Vid->tot_time += tmp_time; + tmp_time = timenorm(tmp_time); + p_Vid->me_time = timenorm(p_Vid->me_time); + if (p_Vid->p_Stats->bit_ctr_parametersets_n!=0 && p_Inp->Verbose != 3) + ReportNALNonVLCBits(p_Vid, tmp_time); + +#if (MVC_EXTENSION_ENABLE) + if (p_Vid->curr_frm_idx == 0 && !p_Vid->view_id) +#else + if (p_Vid->curr_frm_idx == 0) +#endif + ReportFirstframe(p_Vid, tmp_time); + else + { + bits = update_video_stats(p_Vid); + + switch (p_Vid->type) + { + case I_SLICE: + case SI_SLICE: + ReportI(p_Vid, tmp_time); + break; + case B_SLICE: + ReportB(p_Vid, tmp_time); + break; + case SP_SLICE: + ReportP(p_Vid, tmp_time); + break; + case P_SLICE: + default: + ReportP(p_Vid, tmp_time); + } + } + + if (p_Inp->Verbose == 0) + { + //for (i = 0; i <= (p_Vid->number & 0x0F); i++) + //printf("."); + //printf(" \r"); + printf("Completed Encoding Frame %05d.\r", p_Vid->frame_no); + } + // Flush output statistics + fflush(stdout); + + //Rate control + if(p_Inp->RCEnable) + p_Vid->rc_update_picture_ptr( p_Vid, p_Inp, bits ); + + // update bit counters + update_bitcounter_stats(p_Vid); + + update_idr_order_stats(p_Vid); + + return 1; +} + + +/*! + ************************************************************************ + * \brief + * This function write out a picture + * \return + * 0 if OK, \n + * 1 in case of error + * + ************************************************************************ + */ +#if (MVC_EXTENSION_ENABLE) +static void writeout_picture(VideoParameters *p_Vid, Picture *pic, int is_bottom) +{ + int partition, slice, bits; + Slice *currSlice; + NALU_t *nalu = NULL; + + p_Vid->currentPicture = pic; + + // loop over all slices of the picture + for (slice=0; slice < pic->no_slices; slice++) + { + currSlice = pic->slices[slice]; + + // loop over the partitions + for (partition=0; partition < currSlice->max_part_nr; partition++) + { + // write only if the partition has content + if ( currSlice->partArr[partition].bitstream->write_flag ) + { + if(p_Vid->p_Inp->num_of_views==2 && p_Vid->view_id==0) + { + nalu = AllocNALU(64000); + nalu->startcodeprefix_len = 4; + nalu->nal_unit_type = NALU_TYPE_PREFIX; + nalu->nal_reference_idc = NALU_PRIORITY_HIGHEST; + nalu->svc_extension_flag = 0; + nalu->non_idr_flag = p_Vid->non_idr_flag[is_bottom]; + nalu->priority_id = p_Vid->priority_id; + nalu->view_id = p_Vid->view_id; + nalu->temporal_id = p_Vid->temporal_id; + nalu->anchor_pic_flag = p_Vid->anchor_pic_flag[is_bottom]; + nalu->inter_view_flag = p_Vid->inter_view_flag[is_bottom]; + nalu->reserved_one_bit = 1; + + bits = p_Vid->WriteNALU (p_Vid, nalu, p_Vid->f_out); + p_Vid->p_Stats->bit_ctr += bits; + p_Vid->p_Stats->bit_ctr_v[0] += bits; + FreeNALU(nalu); + } + + bits = p_Vid->WriteNALU (p_Vid, currSlice->partArr[partition].nal_unit, p_Vid->f_out); + p_Vid->p_Stats->bit_ctr += bits; + if ( p_Vid->p_Inp->num_of_views == 2 ) + { + p_Vid->p_Stats->bit_ctr_v[p_Vid->view_id] += bits; + } + } + } + } +} +#else +static void writeout_picture(VideoParameters *p_Vid, Picture *pic) +{ + int partition, slice, bits; + Slice *currSlice; + + p_Vid->currentPicture = pic; + + // loop over all slices of the picture + for (slice=0; slice < pic->no_slices; slice++) + { + currSlice = pic->slices[slice]; + + // loop over the partitions + for (partition=0; partition < currSlice->max_part_nr; partition++) + { + // write only if the partition has content + if (currSlice->partArr[partition].bitstream->write_flag ) + { + bits = p_Vid->WriteNALU (p_Vid, currSlice->partArr[partition].nal_unit, p_Vid->f_out); + p_Vid->p_Stats->bit_ctr += bits; + + } + } + } +} +#endif + +void copy_params(VideoParameters *p_Vid, StorablePicture *enc_picture, seq_parameter_set_rbsp_t *active_sps) +{ + enc_picture->frame_mbs_only_flag = active_sps->frame_mbs_only_flag; + enc_picture->frame_cropping_flag = active_sps->frame_cropping_flag; + enc_picture->chroma_format_idc = active_sps->chroma_format_idc; + enc_picture->chroma_mask_mv_x = p_Vid->chroma_mask_mv_x; + enc_picture->chroma_mask_mv_y = p_Vid->chroma_mask_mv_y; + enc_picture->chroma_shift_y = p_Vid->chroma_shift_y; + enc_picture->chroma_shift_x = p_Vid->chroma_shift_x; + enc_picture->otf_flag = p_Vid->p_Inp->OnTheFlyFractMCP; + + if (active_sps->frame_cropping_flag) + { + enc_picture->frame_crop_left_offset = active_sps->frame_crop_left_offset; + enc_picture->frame_crop_right_offset = active_sps->frame_crop_right_offset; + enc_picture->frame_crop_top_offset = active_sps->frame_crop_top_offset; + enc_picture->frame_crop_bottom_offset = active_sps->frame_crop_bottom_offset; + } + else + { + enc_picture->frame_crop_left_offset = 0; + enc_picture->frame_crop_right_offset = 0; + enc_picture->frame_crop_top_offset = 0; + enc_picture->frame_crop_bottom_offset = 0; + } +} + +/*! + ************************************************************************ + * \brief + * Prepare and allocate an encoded frame picture structure + ************************************************************************ + */ +static void prepare_enc_frame_picture (VideoParameters *p_Vid, StorablePicture **stored_pic) +{ + InputParameters *p_Inp = p_Vid->p_Inp; + (*stored_pic) = alloc_storable_picture (p_Vid, (PictureStructure) p_Vid->structure, p_Vid->width, p_Vid->height, p_Vid->width_cr, p_Vid->height_cr); + + p_Vid->ThisPOC = p_Vid->framepoc; + (*stored_pic)->poc = p_Vid->framepoc; + (*stored_pic)->top_poc = p_Vid->toppoc; + (*stored_pic)->bottom_poc = p_Vid->bottompoc; + (*stored_pic)->frame_poc = p_Vid->framepoc; + (*stored_pic)->pic_num = p_Vid->frame_num; + (*stored_pic)->frame_num = p_Vid->frame_num; + (*stored_pic)->coded_frame = 1; + (*stored_pic)->mb_aff_frame_flag = p_Vid->mb_aff_frame_flag = (Boolean) (p_Inp->MbInterlace != FRAME_CODING); + + p_Vid->get_mb_block_pos = p_Vid->mb_aff_frame_flag ? get_mb_block_pos_mbaff : get_mb_block_pos_normal; + p_Vid->getNeighbour = p_Vid->mb_aff_frame_flag ? getAffNeighbour : getNonAffNeighbour; + p_Vid->enc_picture = *stored_pic; + + copy_params(p_Vid, p_Vid->enc_picture, p_Vid->active_sps); +} + +static void calc_picture_bits(Picture *frame) +{ + int i, j; + Slice *thisSlice = NULL; + + frame->bits_per_picture = 0; + + for ( i = 0; i < frame->no_slices; i++ ) + { + thisSlice = frame->slices[i]; + + for ( j = 0; j < thisSlice->max_part_nr; j++ ) + frame->bits_per_picture += 8 * ((thisSlice->partArr[j]).bitstream)->byte_pos; + } +} + +/*! + ************************************************************************ + * \brief + * Encodes a frame picture + ************************************************************************ + */ +void frame_picture (VideoParameters *p_Vid, Picture *frame, ImageData *imgData, int rd_pass) +{ + int nplane; + InputParameters *p_Inp = p_Vid->p_Inp; + p_Vid->rd_pass = rd_pass; + p_Vid->SumFrameQP = 0; + p_Vid->num_ref_idx_l0_active = 0; + p_Vid->num_ref_idx_l1_active = 0; + p_Vid->p_curr_pic = p_Vid->p_curr_frm_struct->p_frame_pic; + p_Vid->structure = FRAME; + p_Vid->PicSizeInMbs = p_Vid->FrameSizeInMbs; + //set mv limits to frame type + update_mv_limits(p_Vid, FALSE); + + InitWP(p_Vid, p_Inp, 0); + if(rd_pass == 0 && p_Vid->wp_parameters_set == 0) + ResetWP(p_Vid, p_Inp); + + if( (p_Inp->separate_colour_plane_flag != 0) ) + { + for( nplane=0; nplaneenc_frame_picture_JV[nplane] ); + } + } + else + { + prepare_enc_frame_picture( p_Vid, &p_Vid->enc_frame_picture[rd_pass] ); + } + + p_Vid->fld_flag = FALSE; + code_a_picture(p_Vid, frame); + + if( (p_Inp->separate_colour_plane_flag != 0) ) + { + make_frame_picture_JV(p_Vid); + } + + calc_picture_bits(frame); + + if (p_Vid->structure==FRAME) + { + find_distortion (p_Vid, imgData); + frame->distortion = p_Vid->p_Dist->metric[SSE]; + } +} + + +/*! + ************************************************************************ + * \brief + * Encodes a field picture, consisting of top and bottom field + ************************************************************************ + */ +static void field_picture (VideoParameters *p_Vid, Picture *top, Picture *bottom) +{ + InputParameters *p_Inp = p_Vid->p_Inp; + //Rate control + int TopFieldBits; + + p_Vid->SumFrameQP = 0; + p_Vid->num_ref_idx_l0_active = 0; + p_Vid->num_ref_idx_l1_active = 0; + + //set mv limits to field type + update_mv_limits(p_Vid, TRUE); + + p_Vid->height = (p_Inp->output.height[0] + p_Vid->auto_crop_bottom) >> 1; + p_Vid->height_cr = p_Vid->height_cr_frame >> 1; + p_Vid->fld_flag = TRUE; + p_Vid->PicSizeInMbs = p_Vid->FrameSizeInMbs >> 1; + // Top field + + p_Vid->enc_field_picture[0] = alloc_storable_picture (p_Vid, (PictureStructure) p_Vid->structure, p_Vid->width, p_Vid->height, p_Vid->width_cr, p_Vid->height_cr); + p_Vid->enc_field_picture[0]->poc = p_Vid->toppoc; + p_Vid->enc_field_picture[0]->frame_poc = p_Vid->toppoc; + /*Potentially buggy to not divide by num_of_layers */ + p_Vid->enc_field_picture[0]->pic_num = p_Vid->frame_num; + p_Vid->enc_field_picture[0]->frame_num = p_Vid->frame_num; + p_Vid->enc_field_picture[0]->coded_frame = 0; + p_Vid->enc_field_picture[0]->mb_aff_frame_flag = p_Vid->mb_aff_frame_flag = FALSE; + p_Vid->get_mb_block_pos = get_mb_block_pos_normal; + p_Vid->getNeighbour = getNonAffNeighbour; + p_Vid->ThisPOC = p_Vid->toppoc; + + p_Vid->p_curr_pic = p_Vid->p_curr_frm_struct->p_top_fld_pic; + p_Vid->structure = TOP_FIELD; + p_Vid->enc_picture = p_Vid->enc_field_picture[0]; + copy_params(p_Vid, p_Vid->enc_picture, p_Vid->active_sps); + + put_buffer_top (p_Vid); + init_field (p_Vid, p_Inp); + set_slice_type(p_Vid, p_Inp, p_Vid->p_curr_pic->p_Slice[0].type); + + p_Vid->fld_flag = TRUE; + + //Rate control + if(p_Inp->RCEnable && p_Inp->RCUpdateMode <= MAX_RC_MODE) + rc_init_top_field(p_Vid, p_Inp); + + code_a_picture(p_Vid, top); + p_Vid->enc_picture->structure = TOP_FIELD; + store_picture_in_dpb(p_Vid->p_Dpb_layer[p_Vid->view_id], p_Vid->enc_field_picture[0], &p_Inp->output); + +#if (MVC_EXTENSION_ENABLE) + if ( is_MVC_profile(p_Vid->active_sps->profile_idc) && p_Vid->proc_picture) + { + store_proc_picture_in_dpb (p_Vid->p_Dpb_layer[1], p_Vid->proc_picture, &p_Inp->output); + p_Vid->proc_picture = NULL; + } +#endif + calc_picture_bits(top); + + //Rate control + TopFieldBits=top->bits_per_picture; + + // Bottom field + p_Vid->enc_field_picture[1] = alloc_storable_picture (p_Vid, (PictureStructure) p_Vid->structure, p_Vid->width, p_Vid->height, p_Vid->width_cr, p_Vid->height_cr); + p_Vid->enc_field_picture[1]->poc=p_Vid->bottompoc; + p_Vid->enc_field_picture[1]->frame_poc = p_Vid->bottompoc; + p_Vid->enc_field_picture[1]->pic_num = p_Vid->frame_num; + p_Vid->enc_field_picture[1]->frame_num = p_Vid->frame_num; + p_Vid->enc_field_picture[1]->coded_frame = 0; + p_Vid->enc_field_picture[1]->mb_aff_frame_flag = p_Vid->mb_aff_frame_flag = FALSE; + p_Vid->get_mb_block_pos = get_mb_block_pos_normal; + p_Vid->getNeighbour = getNonAffNeighbour; + + p_Vid->ThisPOC = p_Vid->bottompoc; + p_Vid->p_curr_pic = p_Vid->p_curr_frm_struct->p_bot_fld_pic; + p_Vid->structure = BOTTOM_FIELD; + p_Vid->enc_picture = p_Vid->enc_field_picture[1]; + copy_params(p_Vid, p_Vid->enc_picture, p_Vid->active_sps); + put_buffer_bot (p_Vid); + + init_field (p_Vid, p_Inp); + set_slice_type(p_Vid, p_Inp, p_Vid->p_curr_pic->p_Slice[0].type); + + p_Vid->fld_flag = TRUE; + + //Rate control + if(p_Inp->RCEnable && p_Inp->RCUpdateMode <= MAX_RC_MODE) + rc_init_bottom_field( p_Vid, p_Inp, TopFieldBits ); + + p_Vid->enc_picture->structure = BOTTOM_FIELD; + code_a_picture(p_Vid, bottom); + + calc_picture_bits(bottom); + + // the distortion for a field coded frame (consisting of top and bottom field) + // lives in the top->distortion variables, the bottom-> are dummies + distortion_fld (p_Vid, p_Inp, top, &p_Vid->imgData); +} + +/*! + ************************************************************************ + * \brief + * form frame picture from two field pictures + ************************************************************************ + */ +static void combine_field(VideoParameters *p_Vid) +{ + int i, k; + + for (i = 0; i < (p_Vid->height >> 1); i++) + { + memcpy(p_Vid->imgY_com[i*2], p_Vid->enc_field_picture[0]->imgY[i], p_Vid->width*sizeof(imgpel)); // top field + memcpy(p_Vid->imgY_com[i*2 + 1], p_Vid->enc_field_picture[1]->imgY[i], p_Vid->width*sizeof(imgpel)); // bottom field + } + + if (p_Vid->yuv_format != YUV400) + { + for (k = 0; k < 2; k++) + { + for (i = 0; i < (p_Vid->height_cr >> 1); i++) + { + memcpy(p_Vid->imgUV_com[k][i * 2], p_Vid->enc_field_picture[0]->imgUV[k][i], p_Vid->width_cr*sizeof(imgpel)); + memcpy(p_Vid->imgUV_com[k][i * 2 + 1], p_Vid->enc_field_picture[1]->imgUV[k][i], p_Vid->width_cr*sizeof(imgpel)); + } + } + } +} + +/*! + ************************************************************************ + * \brief + * Distortion Field + ************************************************************************ + */ +static void distortion_fld (VideoParameters *p_Vid, InputParameters *p_Inp, Picture *field_pic, ImageData *imgData) +{ + p_Vid->height = (p_Inp->output.height[0] + p_Vid->auto_crop_bottom); + p_Vid->height_cr = p_Vid->height_cr_frame; + + combine_field (p_Vid); + + p_Vid->pCurImg = imgData->frm_data[0]; + p_Vid->pImgOrg[0] = imgData->frm_data[0]; + + if (p_Inp->output.yuv_format != YUV400) + { + p_Vid->pImgOrg[1] = imgData->frm_data[1]; + p_Vid->pImgOrg[2] = imgData->frm_data[2]; + } + + find_distortion (p_Vid, imgData); // find snr from original frame picture + field_pic->distortion = p_Vid->p_Dist->metric[SSE]; +} + + +/*! + ************************************************************************ + * \brief + * RD decision of frame and field coding + ************************************************************************ + */ +static byte decide_fld_frame(float snr_frame_Y, float snr_field_Y, int bit_field, int bit_frame, double lambda_picture) +{ + double cost_frame, cost_field; + + cost_frame = bit_frame * lambda_picture + snr_frame_Y; + cost_field = bit_field * lambda_picture + snr_field_Y; + + if (cost_field > cost_frame) + return FALSE; + else + return TRUE; +} + +/*! + ************************************************************************ + * \brief + * Picture Structure Decision + ************************************************************************ + */ +static byte picture_structure_decision (VideoParameters *p_Vid, Picture *frame, Picture *top, Picture *bot) +{ + double lambda_picture; + float sse_frame, sse_field; + int bit_frame, bit_field; + + lambda_picture = 0.68 * pow (2, p_Vid->bitdepth_lambda_scale + ((p_Vid->qp - SHIFT_QP) / 3.0)) * ((p_Vid->type == B_SLICE) ? 1 : 1); + + sse_frame = frame->distortion.value[0] + frame->distortion.value[1] + frame->distortion.value[2]; + //! all distrortions of a field picture are accumulated in the top field + sse_field = top->distortion.value[0] + top->distortion.value[1] + top->distortion.value[2]; + + bit_field = top->bits_per_picture + bot->bits_per_picture; + bit_frame = frame->bits_per_picture; + return decide_fld_frame (sse_frame, sse_field, bit_field, bit_frame, lambda_picture); +} + + +/*! + ************************************************************************ + * \brief + * Field Mode Buffer + ************************************************************************ + */ +static void field_mode_buffer (VideoParameters *p_Vid) +{ + put_buffer_frame (p_Vid); +} + + +/*! + ************************************************************************ + * \brief + * Frame Mode Buffer + ************************************************************************ + */ +static void frame_mode_buffer (VideoParameters *p_Vid) +{ + InputParameters *p_Inp = p_Vid->p_Inp; + put_buffer_frame (p_Vid); + + if ((p_Inp->PicInterlace != FRAME_CODING)||(p_Inp->MbInterlace != FRAME_CODING)) + { + p_Vid->height = p_Vid->height / 2; + p_Vid->height_cr = p_Vid->height_cr / 2; + + put_buffer_top (p_Vid); + + put_buffer_bot (p_Vid); + + p_Vid->height = (p_Inp->output.height[0] + p_Vid->auto_crop_bottom); + p_Vid->height_cr = p_Vid->height_cr_frame; + + put_buffer_frame (p_Vid); + } +} + + +/*! + ************************************************************************ + * \brief + * mmco initializations should go here + ************************************************************************ + */ +static void init_dec_ref_pic_marking_buffer(VideoParameters *p_Vid) +{ + p_Vid->dec_ref_pic_marking_buffer=NULL; +} + +/*! +************************************************************************ +* \brief +* Init fixed QP I_SLICE +************************************************************************ +*/ + +static inline void init_fixed_qp_i_slice(VideoParameters *p_Vid, InputParameters *p_Inp, FrameUnitStruct *p_cur_frm ) +{ + //QP oscillation for secondary SP frames + p_Vid->qp = p_cur_frm->qp; // set quant. parameter for I-frame + + if (p_Vid->redundant_coding) + { + //!KS: hard code qp increment + p_Vid->qp = imin(p_Vid->qp + 5, 51); + } +} + +/*! +************************************************************************ +* \brief +* Init fixed QP P_SLICE or B_SLICE +************************************************************************ +*/ + +static inline void init_fixed_qp_pb_slice(VideoParameters *p_Vid, InputParameters *p_Inp, FrameUnitStruct *p_cur_frm, int type ) +{ + //QP oscillation for secondary SP frames + p_Vid->qp = p_cur_frm->qp; + +} + +/*! +************************************************************************ +* \brief +* Init fixed QP SP_SLICE +************************************************************************ +*/ + +static inline void init_fixed_qp_sp_slice(VideoParameters *p_Vid, InputParameters *p_Inp, FrameUnitStruct *p_cur_frm ) +{ + p_Vid->qp = p_cur_frm->qp; + p_Vid->qpsp = p_Inp->qpsp; +} + +/*! +************************************************************************ +* \brief +* Initializes the quantization parameters for a new frame (no RC) +* ***Much of what happens in this function have to be moved elsewhere...*** +************************************************************************ +*/ + +static void init_fixed_qp(VideoParameters *p_Vid, InputParameters *p_Inp) +{ + FrameUnitStruct *p_cur_frm = p_Vid->p_curr_frm_struct; + + if (p_cur_frm->layer == 0) + { + switch( p_cur_frm->type ) + { + default: + case I_SLICE: + init_fixed_qp_i_slice( p_Vid, p_Inp, p_cur_frm ); + break; + case P_SLICE: + case B_SLICE: + init_fixed_qp_pb_slice( p_Vid, p_Inp, p_cur_frm, p_cur_frm->type ); + break; + case SP_SLICE: + case SI_SLICE: + init_fixed_qp_sp_slice( p_Vid, p_Inp, p_cur_frm ); + break; + } + + p_Vid->mb_y_intra = p_Vid->mb_y_upd; // p_Vid->mb_y_intra indicates which GOB to intra code for this frame + + if (p_Inp->intra_upd > 0) // if error robustness, find next GOB to update + { + p_Vid->mb_y_upd = (p_Vid->curr_frm_idx / p_Inp->intra_upd) % (p_Vid->height / MB_BLOCK_SIZE); + } + } + else + { + p_Vid->qp = p_cur_frm->qp; + } + + // Change QP + if ( p_Inp->qp2frame && p_Inp->qp2frame < p_Vid->frame_no ) + { + p_Vid->p_curr_frm_struct->qp = p_Vid->qp = iClip3( -p_Vid->bitdepth_luma_qp_scale, MAX_QP, p_Vid->qp + p_Inp->qp2off[p_cur_frm->type] ); + } +} + +/*! + ************************************************************************ + * \brief + * Initializes the quantization parameters for a new frame (no RC) + ************************************************************************ + */ + +static void init_mb_line_intra_up(VideoParameters *p_Vid, InputParameters *p_Inp) +{ + FrameUnitStruct *p_cur_frm = p_Vid->p_curr_frm_struct; + + if (p_cur_frm->layer == 0) + { + p_Vid->mb_y_intra = p_Vid->mb_y_upd; // p_Vid->mb_y_intra indicates which GOB to intra code for this frame + + if (p_Inp->intra_upd > 0) // if error robustness, find next GOB to update + { + p_Vid->mb_y_upd = (p_Vid->curr_frm_idx / p_Inp->intra_upd) % (p_Vid->width / MB_BLOCK_SIZE); + } + } +} + +/*! + ************************************************************************ + * \brief + * Initializes the parameters for a new frame + ************************************************************************ + */ +static void init_frame (VideoParameters *p_Vid, InputParameters *p_Inp) +{ + int i, j; + + p_Vid->current_mb_nr = 0; + p_Vid->current_slice_nr = 0; + p_Vid->p_Stats->bit_slice = 0; + + // The 'slice_nr' of each macroblock is set to -1 here, to guarantee the correct encoding + // with FMO (if no FMO, encoding is correct without following assignment), + // for which MBs may not be encoded with scan order + if( (p_Inp->separate_colour_plane_flag != 0) ) + { + for( j = 0; j < MAX_PLANE; j++ ){ + for(i = 0; i < ((int) (p_Vid->FrameSizeInMbs));i++) + p_Vid->mb_data_JV[j][i].slice_nr = -1; + } + } + else + { + for(i = 0; i < ((int) (p_Vid->FrameSizeInMbs)); i++) + p_Vid->mb_data[i].slice_nr = -1; + } + + if ( !(p_Inp->RCEnable) ) + { + init_fixed_qp(p_Vid, p_Inp); + } + init_mb_line_intra_up(p_Vid, p_Inp); + + p_Vid->no_output_of_prior_pics_flag = 0; + p_Vid->long_term_reference_flag = FALSE; + + init_dec_ref_pic_marking_buffer(p_Vid); + + if(p_Inp->WPIterMC) + p_Vid->frameOffsetAvail = 0; + + // set parameters for direct mode and deblocking filter + // currently selection is done at the frame level instead of slice level. This needs to be changed. + p_Vid->direct_spatial_mv_pred_flag = (char) p_Inp->direct_spatial_mv_pred_flag; +#if (MVC_EXTENSION_ENABLE) + if ( (p_Inp->num_of_views == 1) || (p_Vid->view_id == 0) ) +#endif + { + p_Vid->DFDisableIdc = (char) p_Inp->DFDisableIdc[p_Vid->nal_reference_idc > 0][p_Vid->type]; + p_Vid->DFAlphaC0Offset = (char) p_Inp->DFAlpha [p_Vid->nal_reference_idc > 0][p_Vid->type]; + p_Vid->DFBetaOffset = (char) p_Inp->DFBeta [p_Vid->nal_reference_idc > 0][p_Vid->type]; + } +#if (MVC_EXTENSION_ENABLE) + else + { + p_Vid->DFDisableIdc = (char) p_Inp->EnhLayerDFDisableIdc[p_Vid->nal_reference_idc > 0][p_Vid->type]; + p_Vid->DFAlphaC0Offset = (char) p_Inp->EnhLayerDFAlpha [p_Vid->nal_reference_idc > 0][p_Vid->type]; + p_Vid->DFBetaOffset = (char) p_Inp->EnhLayerDFBeta [p_Vid->nal_reference_idc > 0][p_Vid->type]; + } +#endif + p_Vid->AdaptiveRounding = p_Inp->AdaptiveRounding; +} + +/*! + ************************************************************************ + * \brief + * Initializes the parameters for a new field + ************************************************************************ + */ +static void init_field (VideoParameters *p_Vid, InputParameters *p_Inp) +{ + FrameUnitStruct *p_cur_frm = p_Vid->p_curr_frm_struct; + + p_Vid->current_mb_nr = 0; + p_Vid->current_slice_nr = 0; + p_Vid->p_Stats->bit_slice = 0; + + if (!p_cur_frm->layer) + { + if(!p_Inp->RCEnable) // without using rate control + { + p_Vid->qp = p_cur_frm->qp; + if (p_Vid->type == SP_SLICE || p_Vid->type == SI_SLICE) + { + p_Vid->qpsp = p_Inp->qpsp; + } + } + init_mb_line_intra_up( p_Vid, p_Inp ); + } + else + { + if(!p_Inp->RCEnable) + { + p_Vid->qp = p_cur_frm->qp; + } + } +} + +/*! + ************************************************************************ + * \brief + * Upsample 4 times, store them in out4x. Color is simply copied + * + * \par Input: + * srcy, srcu, srcv, out4y, out4u, out4v + * + * \par Side Effects_ + * Uses (writes) img4Y_tmp. This should be moved to a static variable + * in this module + ************************************************************************/ +void UnifiedOneForthPix ( VideoParameters *p_Vid, StorablePicture *s) +{ + InputParameters *p_Inp = p_Vid->p_Inp; + if(s->bInterpolated) + return; + s->bInterpolated = 1; + // Y component + s->p_img_sub[0] = s->imgY_sub; + s->p_curr_img_sub = s->imgY_sub; + s->p_curr_img = s->imgY; + + // derive the subpixel images for first component + // No need to interpolate if intra only encoding + //if (p_Inp->intra_period != 1) + if ( (!p_Inp->OnTheFlyFractMCP) || (p_Inp->OnTheFlyFractMCP==OTF_L1) ) // JLT : on-the-fly + { + getSubImagesLuma ( p_Vid, s ); + // and the sub-images for U and V + if ( ((p_Vid->yuv_format != YUV400) && (p_Inp->ChromaMCBuffer)) || p_Vid->P444_joined ) + { + if (p_Vid->P444_joined) + { + imgpel **** p_curr_img_sub = s->p_curr_img_sub; + imgpel ** p_curr_img = s->p_curr_img; + //U + select_plane(p_Vid, PLANE_U); + s->p_curr_img_sub = s->imgUV_sub[0]; + s->p_curr_img = s->imgUV[0]; + getSubImagesLuma (p_Vid, s); + //V + select_plane(p_Vid, PLANE_V); + s->p_curr_img_sub = s->imgUV_sub[1]; + s->p_curr_img = s->imgUV[1]; + getSubImagesLuma (p_Vid, s); + //Y + select_plane(p_Vid, PLANE_Y); + s->p_curr_img_sub = p_curr_img_sub; + s->p_curr_img = p_curr_img; + } + else + { + getSubImagesChroma( p_Vid, s ); + } + } + } + else + { + // perform padding ( copying borders) that is implicitly done above if p_Inp->OnTheFlyFractMCP=0 + OtfCompatibility_copyWithPadding( s->imgY, s->imgY, s->size_x, s->size_y, IMG_PAD_SIZE_X, IMG_PAD_SIZE_Y ) ; + OtfCompatibility_copyWithPadding( s->imgUV[0], s->imgUV[0], s->size_x_cr, s->size_y_cr, p_Vid->pad_size_uv_x,p_Vid->pad_size_uv_y ) ; + OtfCompatibility_copyWithPadding( s->imgUV[1], s->imgUV[1], s->size_x_cr, s->size_y_cr, p_Vid->pad_size_uv_x, p_Vid->pad_size_uv_y ) ; + } +} + +/*! + ************************************************************************ + * \brief + * Upsample 4 times, store them in out4x. Color is simply copied + * for 4:4:4 Independent mode + * + * \par Input: + * nplane + * + ************************************************************************/ +void UnifiedOneForthPix_JV (VideoParameters *p_Vid, int nplane, StorablePicture *s) +{ + InputParameters *p_Inp = p_Vid->p_Inp; + + if( nplane == 0 ) + { + if(s->bInterpolated) + return; + s->bInterpolated = 1; + s->p_img[0] = s->imgY; + s->p_img[1] = s->imgUV[0]; + s->p_img[2] = s->imgUV[1]; + + if ( (!p_Inp->OnTheFlyFractMCP) || (p_Inp->OnTheFlyFractMCP==OTF_L1) ) // JLT : on-the-fly + { + s->p_img_sub[0] = s->imgY_sub; + s->p_img_sub[1] = s->imgUV_sub[0]; + s->p_img_sub[2] = s->imgUV_sub[1]; + } + } + + // derive the subpixel images for first component + s->colour_plane_id = nplane; + s->p_curr_img = s->p_img[nplane]; + s->p_curr_img_sub = s->p_img_sub[nplane]; + + if( (!p_Inp->OnTheFlyFractMCP) || (p_Inp->OnTheFlyFractMCP==OTF_L1) ) + { + getSubImagesLuma ( p_Vid, s ); + } + else + { + // perform padding ( copying borders) that is implicitly done above if p_Inp->OnTheFlyFractMCP=0 + OtfCompatibility_copyWithPadding( s->p_img[nplane], s->p_img[nplane], s->size_x, s->size_y, IMG_PAD_SIZE_X, IMG_PAD_SIZE_Y ) ; + } +} + + /*! + ************************************************************************ + * \brief + * Just a placebo + ************************************************************************ + */ +Boolean dummy_slice_too_big (int bits_slice) +{ + return FALSE; +} + +static void ReportSimple(VideoParameters *p_Vid, char *pic_type, int cur_bits, DistMetric *metric, int tmp_time) +{ +#if (MVC_EXTENSION_ENABLE) + if ( p_Vid->p_Inp->num_of_views == 2 ) + { + if ( p_Vid->fld_flag && p_Vid->view_id == 0 ) + { + p_Vid->prev_cs.frm_no_in_file = p_Vid->frm_no_in_file; + p_Vid->prev_cs.view_id = p_Vid->view_id; + p_Vid->prev_cs.cur_bits = cur_bits; + strcpy( p_Vid->prev_cs.pic_type, pic_type ); + p_Vid->prev_cs.AverageFrameQP = p_Vid->AverageFrameQP; + p_Vid->prev_cs.lambda = 0; + p_Vid->prev_cs.psnr_value[0] = metric->value[0]; + p_Vid->prev_cs.psnr_value[1] = metric->value[1]; + p_Vid->prev_cs.psnr_value[2] = metric->value[2]; + p_Vid->prev_cs.ssim_value[0] = 0; + p_Vid->prev_cs.ssim_value[1] = 0; + p_Vid->prev_cs.ssim_value[2] = 0; + p_Vid->prev_cs.tmp_time = tmp_time; + p_Vid->prev_cs.me_time = (int)p_Vid->me_time; + p_Vid->prev_cs.fld_flag = p_Vid->fld_flag; + p_Vid->prev_cs.intras = p_Vid->intras; + p_Vid->prev_cs.direct_mode = 0; + p_Vid->prev_cs.num_ref_idx_l0_active = p_Vid->num_ref_idx_l0_active; + p_Vid->prev_cs.num_ref_idx_l1_active = p_Vid->num_ref_idx_l1_active; + p_Vid->prev_cs.rd_pass = p_Vid->rd_pass; + p_Vid->prev_cs.nal_reference_idc = p_Vid->nal_reference_idc; + } + else if ( p_Vid->fld_flag && p_Vid->view_id == 1 ) + { + printf ("%05d(%3s) %1d %8d %2d %7.3f %7.3f %7.3f %9d %7d %3s %d\n", + p_Vid->prev_cs.frm_no_in_file, p_Vid->prev_cs.pic_type, p_Vid->prev_cs.view_id, + (int)(p_Vid->p_Stats->bit_ctr_v[0] - p_Vid->p_Stats->bit_ctr_n_v[0]) + (int)(p_Vid->p_Stats->bit_ctr_filler_data_v[0] - p_Vid->p_Stats->bit_ctr_filler_data_n_v[0]), + p_Vid->prev_cs.AverageFrameQP, + p_Vid->prev_cs.psnr_value[0], p_Vid->prev_cs.psnr_value[1], p_Vid->prev_cs.psnr_value[2], + p_Vid->prev_cs.tmp_time, (int) p_Vid->prev_cs.me_time, + p_Vid->prev_cs.fld_flag ? "FLD" : "FRM", + p_Vid->prev_cs.nal_reference_idc); + printf ("%05d(%3s) %1d %8d %2d %7.3f %7.3f %7.3f %9d %7d %3s %d\n", + p_Vid->frm_no_in_file, pic_type, p_Vid->view_id, + (int)(p_Vid->p_Stats->bit_ctr_v[1] - p_Vid->p_Stats->bit_ctr_n_v[1]) + (int)(p_Vid->p_Stats->bit_ctr_filler_data_v[1] - p_Vid->p_Stats->bit_ctr_filler_data_n_v[1]), + p_Vid->AverageFrameQP, + metric->value[0], metric->value[1], metric->value[2], + tmp_time, (int) p_Vid->me_time, + p_Vid->fld_flag ? "FLD" : "FRM", + p_Vid->nal_reference_idc); + } + else + { + printf ("%05d(%3s) %1d %8d %2d %7.3f %7.3f %7.3f %9d %7d %3s %d\n", + p_Vid->frm_no_in_file, pic_type, p_Vid->view_id, cur_bits, + p_Vid->AverageFrameQP, + metric->value[0], metric->value[1], metric->value[2], + tmp_time, (int) p_Vid->me_time, + p_Vid->fld_flag ? "FLD" : "FRM", + p_Vid->nal_reference_idc); + } + } + else +#endif + printf ("%05d(%3s)%8d %2d %7.3f %7.3f %7.3f %9d %7d %3s %d\n", + p_Vid->frm_no_in_file, pic_type, cur_bits, + p_Vid->AverageFrameQP, + metric->value[0], metric->value[1], metric->value[2], + tmp_time, (int) p_Vid->me_time, + p_Vid->fld_flag ? "FLD" : "FRM", + p_Vid->nal_reference_idc); +} + +static void ReportVerbose(VideoParameters *p_Vid, char *pic_type, int cur_bits, int wp_method, int lambda, DistMetric *mPSNR, int tmp_time, int direct_mode) +{ +#if (MVC_EXTENSION_ENABLE) + if ( p_Vid->p_Inp->num_of_views == 2 ) + { + if ( p_Vid->fld_flag && p_Vid->view_id == 0 ) + { + p_Vid->prev_cs.frm_no_in_file = p_Vid->frm_no_in_file; + p_Vid->prev_cs.view_id = p_Vid->view_id; + p_Vid->prev_cs.cur_bits = cur_bits; + strcpy( p_Vid->prev_cs.pic_type, pic_type ); + p_Vid->prev_cs.AverageFrameQP = p_Vid->AverageFrameQP; + p_Vid->prev_cs.lambda = 0; + p_Vid->prev_cs.psnr_value[0] = mPSNR->value[0]; + p_Vid->prev_cs.psnr_value[1] = mPSNR->value[1]; + p_Vid->prev_cs.psnr_value[2] = mPSNR->value[2]; + p_Vid->prev_cs.ssim_value[0] = 0; + p_Vid->prev_cs.ssim_value[1] = 0; + p_Vid->prev_cs.ssim_value[2] = 0; + p_Vid->prev_cs.tmp_time = tmp_time; + p_Vid->prev_cs.me_time = (int)p_Vid->me_time; + p_Vid->prev_cs.fld_flag = p_Vid->fld_flag; + p_Vid->prev_cs.intras = p_Vid->intras; + p_Vid->prev_cs.direct_mode = direct_mode; + p_Vid->prev_cs.num_ref_idx_l0_active = p_Vid->num_ref_idx_l0_active; + p_Vid->prev_cs.num_ref_idx_l1_active = p_Vid->num_ref_idx_l1_active; + p_Vid->prev_cs.rd_pass = p_Vid->rd_pass; + p_Vid->prev_cs.nal_reference_idc = p_Vid->nal_reference_idc; + } + else if ( p_Vid->fld_flag && p_Vid->view_id == 1 ) + { + printf ("%05d(%3s) %1d %8d %1d %2d %4d %7.3f %7.3f %7.3f %9d %7d %3s %5d %1d %2d %2d %d %d\n", + p_Vid->prev_cs.frm_no_in_file, p_Vid->prev_cs.pic_type, p_Vid->prev_cs.view_id, + (int)(p_Vid->p_Stats->bit_ctr_v[0] - p_Vid->p_Stats->bit_ctr_n_v[0]) + (int)(p_Vid->p_Stats->bit_ctr_filler_data_v[0] - p_Vid->p_Stats->bit_ctr_filler_data_n_v[0]), + p_Vid->prev_cs.wp_method, p_Vid->prev_cs.AverageFrameQP, p_Vid->prev_cs.lambda, + p_Vid->prev_cs.psnr_value[0], p_Vid->prev_cs.psnr_value[1], p_Vid->prev_cs.psnr_value[2], + p_Vid->prev_cs.tmp_time, (int) p_Vid->prev_cs.me_time, + p_Vid->prev_cs.fld_flag ? "FLD" : "FRM", p_Vid->prev_cs.intras, p_Vid->prev_cs.direct_mode, + p_Vid->prev_cs.num_ref_idx_l0_active, p_Vid->prev_cs.num_ref_idx_l1_active, p_Vid->prev_cs.rd_pass, p_Vid->prev_cs.nal_reference_idc); + printf ("%05d(%3s) %1d %8d %1d %2d %4d %7.3f %7.3f %7.3f %9d %7d %3s %5d %1d %2d %2d %d %d\n", + p_Vid->frm_no_in_file, pic_type, p_Vid->view_id, + (int)(p_Vid->p_Stats->bit_ctr_v[1] - p_Vid->p_Stats->bit_ctr_n_v[1]) + (int)(p_Vid->p_Stats->bit_ctr_filler_data_v[1] - p_Vid->p_Stats->bit_ctr_filler_data_n_v[1]), wp_method, + p_Vid->AverageFrameQP, lambda, + mPSNR->value[0], mPSNR->value[1], mPSNR->value[2], + tmp_time, (int) p_Vid->me_time, + p_Vid->fld_flag ? "FLD" : "FRM", p_Vid->intras, direct_mode, + p_Vid->num_ref_idx_l0_active, p_Vid->num_ref_idx_l1_active, p_Vid->rd_pass, p_Vid->nal_reference_idc); + } + else + { + printf ("%05d(%3s) %1d %8d %1d %2d %4d %7.3f %7.3f %7.3f %9d %7d %3s %5d %1d %2d %2d %d %d\n", + p_Vid->frm_no_in_file, pic_type, p_Vid->view_id, cur_bits, wp_method, + p_Vid->AverageFrameQP, lambda, + mPSNR->value[0], mPSNR->value[1], mPSNR->value[2], + tmp_time, (int) p_Vid->me_time, + p_Vid->fld_flag ? "FLD" : "FRM", p_Vid->intras, direct_mode, + p_Vid->num_ref_idx_l0_active, p_Vid->num_ref_idx_l1_active, p_Vid->rd_pass, p_Vid->nal_reference_idc); + } + } + else +#endif + { + printf ("%05d(%3s)%8d %1d %2d %4d %7.3f %7.3f %7.3f %9d %7d %3s %5d %1d %2d %2d %d %d\n", + p_Vid->frm_no_in_file, pic_type, cur_bits, wp_method, + p_Vid->AverageFrameQP, lambda, + mPSNR->value[0], mPSNR->value[1], mPSNR->value[2], + tmp_time, (int) p_Vid->me_time, + p_Vid->fld_flag ? "FLD" : "FRM", p_Vid->intras, direct_mode, + p_Vid->num_ref_idx_l0_active, p_Vid->num_ref_idx_l1_active, p_Vid->rd_pass, p_Vid->nal_reference_idc); + } +} + +static void ReportVerboseNVB(VideoParameters *p_Vid, char *pic_type, int cur_bits, int nvb_bits, int wp_method, int lambda, DistMetric *mPSNR, int tmp_time, int direct_mode) +{ +#if (MVC_EXTENSION_ENABLE) + if ( p_Vid->num_of_layers == 2 ) + { + if ( p_Vid->fld_flag && p_Vid->view_id == 0 ) + { + p_Vid->prev_cs.frm_no_in_file = p_Vid->frm_no_in_file; + p_Vid->prev_cs.view_id = p_Vid->view_id; + p_Vid->prev_cs.cur_bits = cur_bits; + strcpy( p_Vid->prev_cs.pic_type, pic_type ); + p_Vid->prev_cs.AverageFrameQP = p_Vid->AverageFrameQP; + p_Vid->prev_cs.lambda = 0; + p_Vid->prev_cs.psnr_value[0] = mPSNR->value[0]; + p_Vid->prev_cs.psnr_value[1] = mPSNR->value[1]; + p_Vid->prev_cs.psnr_value[2] = mPSNR->value[2]; + p_Vid->prev_cs.ssim_value[0] = 0; + p_Vid->prev_cs.ssim_value[1] = 0; + p_Vid->prev_cs.ssim_value[2] = 0; + p_Vid->prev_cs.tmp_time = tmp_time; + p_Vid->prev_cs.me_time = (int)p_Vid->me_time; + p_Vid->prev_cs.fld_flag = p_Vid->fld_flag; + p_Vid->prev_cs.intras = p_Vid->intras; + p_Vid->prev_cs.direct_mode = direct_mode; + p_Vid->prev_cs.num_ref_idx_l0_active = p_Vid->num_ref_idx_l0_active; + p_Vid->prev_cs.num_ref_idx_l1_active = p_Vid->num_ref_idx_l1_active; + p_Vid->prev_cs.rd_pass = p_Vid->rd_pass; + p_Vid->prev_cs.nal_reference_idc = p_Vid->nal_reference_idc; + } + else if ( p_Vid->fld_flag && p_Vid->view_id == 1 ) + { + printf ("%05d(%3s) %1d %8d %3d %1d %2d %4d %7.3f %7.3f %7.3f %9d %7d %3s %5d %1d %2d %2d %d %d\n", + p_Vid->prev_cs.frm_no_in_file, p_Vid->prev_cs.pic_type, p_Vid->prev_cs.view_id, + (int)(p_Vid->p_Stats->bit_ctr_v[0] - p_Vid->p_Stats->bit_ctr_n_v[0]) + + (int)(p_Vid->p_Stats->bit_ctr_filler_data_v[0] - p_Vid->p_Stats->bit_ctr_filler_data_n_v[0]) + p_Vid->p_Stats->bit_ctr_parametersets_n_v[0], + p_Vid->p_Stats->bit_ctr_parametersets_n_v[0], p_Vid->prev_cs.wp_method, p_Vid->prev_cs.AverageFrameQP, p_Vid->prev_cs.lambda, + p_Vid->prev_cs.psnr_value[0], p_Vid->prev_cs.psnr_value[1], p_Vid->prev_cs.psnr_value[2], + p_Vid->prev_cs.tmp_time, (int) p_Vid->prev_cs.me_time, + p_Vid->prev_cs.fld_flag ? "FLD" : "FRM", p_Vid->prev_cs.intras, p_Vid->prev_cs.direct_mode, + p_Vid->prev_cs.num_ref_idx_l0_active, p_Vid->prev_cs.num_ref_idx_l1_active, p_Vid->prev_cs.rd_pass, p_Vid->prev_cs.nal_reference_idc); + printf ("%05d(%3s) %1d %8d %3d %1d %2d %4d %7.3f %7.3f %7.3f %9d %7d %3s %5d %1d %2d %2d %d %d\n", + p_Vid->frm_no_in_file, pic_type, p_Vid->view_id, + (int)(p_Vid->p_Stats->bit_ctr_v[1] - p_Vid->p_Stats->bit_ctr_n_v[1]) + + (int)(p_Vid->p_Stats->bit_ctr_filler_data_v[1] - p_Vid->p_Stats->bit_ctr_filler_data_n_v[1]) + p_Vid->p_Stats->bit_ctr_parametersets_n_v[1], + p_Vid->p_Stats->bit_ctr_parametersets_n_v[1], wp_method, + p_Vid->AverageFrameQP, lambda, + mPSNR->value[0], mPSNR->value[1], mPSNR->value[2], + tmp_time, (int) p_Vid->me_time, + p_Vid->fld_flag ? "FLD" : "FRM", p_Vid->intras, direct_mode, + p_Vid->num_ref_idx_l0_active, p_Vid->num_ref_idx_l1_active, p_Vid->rd_pass, p_Vid->nal_reference_idc); + } + else + { + printf ("%05d(%3s) %1d %8d %3d %1d %2d %4d %7.3f %7.3f %7.3f %9d %7d %3s %5d %1d %2d %2d %d %d %d\n", + p_Vid->frm_no_in_file, pic_type, p_Vid->view_id, cur_bits, nvb_bits, wp_method, + p_Vid->AverageFrameQP, lambda, + mPSNR->value[0], mPSNR->value[1], mPSNR->value[2], + tmp_time, (int) p_Vid->me_time, + p_Vid->fld_flag ? "FLD" : "FRM", p_Vid->intras, direct_mode, + p_Vid->num_ref_idx_l0_active, p_Vid->num_ref_idx_l1_active, p_Vid->rd_pass, p_Vid->nal_reference_idc, p_Vid->iInterViewMBs); + } + } + else +#endif + { + printf ("%05d(%3s)%8d %3d %1d %2d %4d %7.3f %7.3f %7.3f %9d %7d %3s %5d %1d %2d %2d %d %d\n", + p_Vid->frm_no_in_file, pic_type, cur_bits, nvb_bits, wp_method, + p_Vid->AverageFrameQP, lambda, + mPSNR->value[0], mPSNR->value[1], mPSNR->value[2], + tmp_time, (int) p_Vid->me_time, + p_Vid->fld_flag ? "FLD" : "FRM", p_Vid->intras, direct_mode, + p_Vid->num_ref_idx_l0_active, p_Vid->num_ref_idx_l1_active, p_Vid->rd_pass, p_Vid->nal_reference_idc); + + } +} + +static void ReportVerboseFDN(VideoParameters *p_Vid, char *pic_type, int cur_bits, int fdn_bits, int nvb_bits, int wp_method, int lambda, DistMetric *mPSNR, int tmp_time, int direct_mode) +{ +#if (MVC_EXTENSION_ENABLE) + if ( p_Vid->p_Inp->num_of_views == 2 ) + { + if ( p_Vid->fld_flag && p_Vid->view_id == 0 ) + { + p_Vid->prev_cs.frm_no_in_file = p_Vid->frm_no_in_file; + p_Vid->prev_cs.view_id = p_Vid->view_id; + p_Vid->prev_cs.cur_bits = cur_bits; + strcpy( p_Vid->prev_cs.pic_type, pic_type ); + p_Vid->prev_cs.AverageFrameQP = p_Vid->AverageFrameQP; + p_Vid->prev_cs.lambda = 0; + p_Vid->prev_cs.psnr_value[0] = mPSNR->value[0]; + p_Vid->prev_cs.psnr_value[1] = mPSNR->value[1]; + p_Vid->prev_cs.psnr_value[2] = mPSNR->value[2]; + p_Vid->prev_cs.ssim_value[0] = 0; + p_Vid->prev_cs.ssim_value[1] = 0; + p_Vid->prev_cs.ssim_value[2] = 0; + p_Vid->prev_cs.tmp_time = tmp_time; + p_Vid->prev_cs.me_time = (int)p_Vid->me_time; + p_Vid->prev_cs.fld_flag = p_Vid->fld_flag; + p_Vid->prev_cs.intras = p_Vid->intras; + p_Vid->prev_cs.direct_mode = direct_mode; + p_Vid->prev_cs.num_ref_idx_l0_active = p_Vid->num_ref_idx_l0_active; + p_Vid->prev_cs.num_ref_idx_l1_active = p_Vid->num_ref_idx_l1_active; + p_Vid->prev_cs.rd_pass = p_Vid->rd_pass; + p_Vid->prev_cs.nal_reference_idc = p_Vid->nal_reference_idc; + } + else if ( p_Vid->fld_flag && p_Vid->view_id == 1 ) + { + printf ("%05d(%3s) %1d %8d %8d %3d %1d %2d %4d %7.3f %7.3f %7.3f %9d %7d %3s %5d %1d %2d %2d %d %d\n", + p_Vid->prev_cs.frm_no_in_file, p_Vid->prev_cs.pic_type, p_Vid->prev_cs.view_id, + (int)(p_Vid->p_Stats->bit_ctr_v[0] - p_Vid->p_Stats->bit_ctr_n_v[0]) + + (int)(p_Vid->p_Stats->bit_ctr_filler_data_v[0] - p_Vid->p_Stats->bit_ctr_filler_data_n_v[0]) + p_Vid->p_Stats->bit_ctr_parametersets_n_v[0], + (int)(p_Vid->p_Stats->bit_ctr_filler_data_v[0] - p_Vid->p_Stats->bit_ctr_filler_data_n_v[0]), + p_Vid->p_Stats->bit_ctr_parametersets_n_v[0], + p_Vid->prev_cs.wp_method, p_Vid->prev_cs.AverageFrameQP, p_Vid->prev_cs.lambda, + p_Vid->prev_cs.psnr_value[0], p_Vid->prev_cs.psnr_value[1], p_Vid->prev_cs.psnr_value[2], + p_Vid->prev_cs.tmp_time, (int) p_Vid->prev_cs.me_time, + p_Vid->prev_cs.fld_flag ? "FLD" : "FRM", p_Vid->prev_cs.intras, p_Vid->prev_cs.direct_mode, + p_Vid->prev_cs.num_ref_idx_l0_active, p_Vid->prev_cs.num_ref_idx_l1_active, p_Vid->prev_cs.rd_pass, p_Vid->prev_cs.nal_reference_idc); + printf ("%05d(%3s) %1d %8d %8d %3d %1d %2d %4d %7.3f %7.3f %7.3f %9d %7d %3s %5d %1d %2d %2d %d %d\n", + p_Vid->frm_no_in_file, pic_type, p_Vid->view_id, + (int)(p_Vid->p_Stats->bit_ctr_v[1] - p_Vid->p_Stats->bit_ctr_n_v[1]) + + (int)(p_Vid->p_Stats->bit_ctr_filler_data_v[1] - p_Vid->p_Stats->bit_ctr_filler_data_n_v[1]) + p_Vid->p_Stats->bit_ctr_parametersets_n_v[1], + (int)(p_Vid->p_Stats->bit_ctr_filler_data_v[1] - p_Vid->p_Stats->bit_ctr_filler_data_n_v[1]), + p_Vid->p_Stats->bit_ctr_parametersets_n_v[1], + wp_method, + p_Vid->AverageFrameQP, lambda, + mPSNR->value[0], mPSNR->value[1], mPSNR->value[2], + tmp_time, (int) p_Vid->me_time, + p_Vid->fld_flag ? "FLD" : "FRM", p_Vid->intras, direct_mode, + p_Vid->num_ref_idx_l0_active, p_Vid->num_ref_idx_l1_active, p_Vid->rd_pass, p_Vid->nal_reference_idc); + } + else + { + printf ("%05d(%3s) %1d %8d %8d %3d %1d %2d %4d %7.3f %7.3f %7.3f %9d %7d %3s %5d %1d %2d %2d %d %d\n", + p_Vid->frm_no_in_file, pic_type, p_Vid->view_id, cur_bits, fdn_bits, nvb_bits, wp_method, + p_Vid->AverageFrameQP, lambda, + mPSNR->value[0], mPSNR->value[1], mPSNR->value[2], + tmp_time, (int) p_Vid->me_time, + p_Vid->fld_flag ? "FLD" : "FRM", p_Vid->intras, direct_mode, + p_Vid->num_ref_idx_l0_active, p_Vid->num_ref_idx_l1_active, p_Vid->rd_pass, p_Vid->nal_reference_idc); + } + } + else +#endif + { + printf ("%05d(%3s)%8d %8d %3d %1d %2d %4d %7.3f %7.3f %7.3f %9d %7d %3s %5d %1d %2d %2d %d %d\n", + p_Vid->frm_no_in_file, pic_type, cur_bits, fdn_bits, nvb_bits, wp_method, + p_Vid->AverageFrameQP, lambda, + mPSNR->value[0], mPSNR->value[1], mPSNR->value[2], + tmp_time, (int) p_Vid->me_time, + p_Vid->fld_flag ? "FLD" : "FRM", p_Vid->intras, direct_mode, + p_Vid->num_ref_idx_l0_active, p_Vid->num_ref_idx_l1_active, p_Vid->rd_pass, p_Vid->nal_reference_idc); + } +} + +static void ReportVerboseSSIM(VideoParameters *p_Vid, char *pic_type, int cur_bits, int wp_method, int lambda, DistMetric *mPSNR, DistMetric *mSSIM,int tmp_time, int direct_mode) +{ +#if (MVC_EXTENSION_ENABLE) + if ( p_Vid->p_Inp->num_of_views == 2 ) + { + printf ("%05d(%3s) %1d %8d %1d %2d %4d %7.3f %7.3f %7.3f %7.4f %7.4f %7.4f %9d %7d %3s %5d %1d %2d %2d %d %d\n", + p_Vid->frm_no_in_file, pic_type, p_Vid->view_id, cur_bits, wp_method, + p_Vid->AverageFrameQP, lambda, + mPSNR->value[0], mPSNR->value[1], mPSNR->value[2], + mSSIM->value[0], mSSIM->value[1], mSSIM->value[2], + tmp_time, (int) p_Vid->me_time, + p_Vid->fld_flag ? "FLD" : "FRM", p_Vid->intras, direct_mode, + p_Vid->num_ref_idx_l0_active, p_Vid->num_ref_idx_l1_active,p_Vid->rd_pass, p_Vid->nal_reference_idc); + } + else +#endif + { + printf ("%05d(%3s)%8d %1d %2d %4d %7.3f %7.3f %7.3f %7.4f %7.4f %7.4f %9d %7d %3s %5d %1d %2d %2d %d %d\n", + p_Vid->frm_no_in_file, pic_type, cur_bits, wp_method, + p_Vid->AverageFrameQP, lambda, + mPSNR->value[0], mPSNR->value[1], mPSNR->value[2], + mSSIM->value[0], mSSIM->value[1], mSSIM->value[2], + tmp_time, (int) p_Vid->me_time, + p_Vid->fld_flag ? "FLD" : "FRM", p_Vid->intras, direct_mode, + p_Vid->num_ref_idx_l0_active, p_Vid->num_ref_idx_l1_active,p_Vid->rd_pass, p_Vid->nal_reference_idc); + } +} + +static void ReportVerboseNVBSSIM(VideoParameters *p_Vid, char *pic_type, int cur_bits, int nvb_bits, int wp_method, int lambda, DistMetric *mPSNR, DistMetric *mSSIM,int tmp_time, int direct_mode) +{ +#if (MVC_EXTENSION_ENABLE) + if ( p_Vid->p_Inp->num_of_views == 2 ) + { + printf ("%05d(%3s) %1d %8d %3d %1d %2d %4d %7.3f %7.3f %7.3f %7.4f %7.4f %7.4f %9d %7d %3s %5d %1d %2d %2d %d %d\n", + p_Vid->frm_no_in_file, pic_type, p_Vid->view_id, cur_bits, nvb_bits, wp_method, + p_Vid->AverageFrameQP, lambda, + mPSNR->value[0], mPSNR->value[1], mPSNR->value[2], + mSSIM->value[0], mSSIM->value[1], mSSIM->value[2], + tmp_time, (int) p_Vid->me_time, + p_Vid->fld_flag ? "FLD" : "FRM", p_Vid->intras, direct_mode, + p_Vid->num_ref_idx_l0_active, p_Vid->num_ref_idx_l1_active, p_Vid->rd_pass, p_Vid->nal_reference_idc); + } + else +#endif + { + printf ("%05d(%3s)%8d %3d %1d %2d %4d %7.3f %7.3f %7.3f %7.4f %7.4f %7.4f %9d %7d %3s %5d %1d %2d %2d %d %d\n", + p_Vid->frm_no_in_file, pic_type, cur_bits, nvb_bits, wp_method, + p_Vid->AverageFrameQP, lambda, + mPSNR->value[0], mPSNR->value[1], mPSNR->value[2], + mSSIM->value[0], mSSIM->value[1], mSSIM->value[2], + tmp_time, (int) p_Vid->me_time, + p_Vid->fld_flag ? "FLD" : "FRM", p_Vid->intras, direct_mode, + p_Vid->num_ref_idx_l0_active, p_Vid->num_ref_idx_l1_active, p_Vid->rd_pass, p_Vid->nal_reference_idc); + } +} + +static void ReportVerboseFDNSSIM(VideoParameters *p_Vid, char *pic_type, int cur_bits, int fdn_bits, int nvb_bits, int wp_method, int lambda, DistMetric *mPSNR, DistMetric *mSSIM,int tmp_time, int direct_mode) +{ +#if (MVC_EXTENSION_ENABLE) + if ( p_Vid->p_Inp->num_of_views == 2 ) + { + printf ("%05d(%3s) %1d %8d %8d %3d %1d %2d %4d %7.3f %7.3f %7.3f %7.4f %7.4f %7.4f %9d %7d %3s %5d %1d %2d %2d %d %d\n", + p_Vid->frm_no_in_file, pic_type, p_Vid->view_id, cur_bits, fdn_bits, nvb_bits, wp_method, + p_Vid->AverageFrameQP, lambda, + mPSNR->value[0], mPSNR->value[1], mPSNR->value[2], + mSSIM->value[0], mSSIM->value[1], mSSIM->value[2], + tmp_time, (int) p_Vid->me_time, + p_Vid->fld_flag ? "FLD" : "FRM", p_Vid->intras, direct_mode, + p_Vid->num_ref_idx_l0_active, p_Vid->num_ref_idx_l1_active, p_Vid->rd_pass, p_Vid->nal_reference_idc); + } + else +#endif + { + printf ("%05d(%3s)%8d %8d %3d %1d %2d %4d %7.3f %7.3f %7.3f %7.4f %7.4f %7.4f %9d %7d %3s %5d %1d %2d %2d %d %d\n", + p_Vid->frm_no_in_file, pic_type, cur_bits, fdn_bits, nvb_bits, wp_method, + p_Vid->AverageFrameQP, lambda, + mPSNR->value[0], mPSNR->value[1], mPSNR->value[2], + mSSIM->value[0], mSSIM->value[1], mSSIM->value[2], + tmp_time, (int) p_Vid->me_time, + p_Vid->fld_flag ? "FLD" : "FRM", p_Vid->intras, direct_mode, + p_Vid->num_ref_idx_l0_active, p_Vid->num_ref_idx_l1_active, p_Vid->rd_pass, p_Vid->nal_reference_idc); + } +} + + +static void ReportNALNonVLCBits(VideoParameters *p_Vid, int64 tmp_time) +{ + InputParameters *p_Inp = p_Vid->p_Inp; + StatParameters *p_Stats = p_Vid->p_Stats; + + //! Need to add type (i.e. SPS, PPS, SEI etc). +#if (MVC_EXTENSION_ENABLE) + if (p_Inp->num_of_views == 2) + { + if (p_Inp->Verbose != 0) + printf ("%05d(NVB) %8d \n", p_Vid->frame_no, p_Stats->bit_ctr_parametersets_n); + } + else +#endif + { + if (p_Inp->Verbose != 0) + printf ("%05d(NVB)%8d \n", p_Vid->frame_no, p_Stats->bit_ctr_parametersets_n); + } +} + +static void ReportFirstframe(VideoParameters *p_Vid, int64 tmp_time) +{ + InputParameters *p_Inp = p_Vid->p_Inp; + StatParameters *stats = p_Vid->p_Stats; + + int cur_bits = (int)(stats->bit_ctr - stats->bit_ctr_n) + + (int)(stats->bit_ctr_filler_data - stats->bit_ctr_filler_data_n); + + if (p_Inp->Verbose == 1) + { + ReportSimple(p_Vid, "IDR", cur_bits, &p_Vid->p_Dist->metric[PSNR], (int) tmp_time); + } + else if (p_Inp->Verbose == 2) + { + int lambda = (int) p_Vid->lambda_me[I_SLICE][p_Vid->masterQP][F_PEL]; + if (p_Inp->Distortion[SSIM] == 1) + ReportVerboseSSIM(p_Vid, "IDR", cur_bits, 0, lambda, &p_Vid->p_Dist->metric[PSNR], &p_Vid->p_Dist->metric[SSIM], (int) tmp_time, 0); + else + ReportVerbose(p_Vid, "IDR", cur_bits, 0, lambda, &p_Vid->p_Dist->metric[PSNR], (int) tmp_time, 0); + } + else if (p_Inp->Verbose == 3) + { + int lambda = (int) p_Vid->lambda_me[I_SLICE][p_Vid->masterQP][F_PEL]; + if (p_Inp->Distortion[SSIM] == 1) + ReportVerboseNVBSSIM(p_Vid, "IDR", cur_bits + stats->bit_ctr_parametersets_n, stats->bit_ctr_parametersets_n, 0, lambda, &p_Vid->p_Dist->metric[PSNR], &p_Vid->p_Dist->metric[SSIM], (int) tmp_time, 0); + else + ReportVerboseNVB(p_Vid, "IDR", cur_bits + stats->bit_ctr_parametersets_n, stats->bit_ctr_parametersets_n, 0, lambda, &p_Vid->p_Dist->metric[PSNR], (int) tmp_time, 0); + } + else if (p_Inp->Verbose == 4) + { + int lambda = (int) p_Vid->lambda_me[I_SLICE][p_Vid->masterQP][F_PEL]; + if (p_Inp->Distortion[SSIM] == 1) + ReportVerboseFDNSSIM(p_Vid, "IDR", cur_bits + stats->bit_ctr_parametersets_n, (int)(stats->bit_ctr_filler_data - stats->bit_ctr_filler_data_n), stats->bit_ctr_parametersets_n, 0, lambda, &p_Vid->p_Dist->metric[PSNR], &p_Vid->p_Dist->metric[SSIM], (int) tmp_time, 0); + else + ReportVerboseFDN(p_Vid, "IDR", cur_bits + stats->bit_ctr_parametersets_n, (int)(stats->bit_ctr_filler_data - stats->bit_ctr_filler_data_n), stats->bit_ctr_parametersets_n, 0, lambda, &p_Vid->p_Dist->metric[PSNR], (int) tmp_time, 0); + } + + stats->bit_counter[I_SLICE] = stats->bit_ctr; +#if (MVC_EXTENSION_ENABLE) + if ( p_Inp->num_of_views == 2 ) + { + if ( !p_Vid->fld_flag ) + { + stats->bit_counter_v[p_Vid->view_id][I_SLICE] = stats->bit_ctr; + } + stats->bit_ctr_v[0] = 0; + stats->bit_ctr_v[1] = 0; + } +#endif + stats->bit_ctr = 0; +} + +static void ReportI(VideoParameters *p_Vid, int64 tmp_time) +{ + InputParameters *p_Inp = p_Vid->p_Inp; + StatParameters *stats = p_Vid->p_Stats; + char pic_type[4]; + int cur_bits = (int)(stats->bit_ctr - stats->bit_ctr_n) + + (int)(stats->bit_ctr_filler_data - stats->bit_ctr_filler_data_n); + + if ((p_Inp->redundant_pic_flag == 0) || !p_Vid->redundant_coding ) + { + if (p_Vid->currentPicture->idr_flag == TRUE) + strcpy(pic_type,"IDR"); + else if ( p_Vid->type == SI_SLICE ) + strcpy(pic_type,"SI "); + else + strcpy(pic_type," I "); + } + else + strcpy(pic_type,"R"); + + if (p_Inp->Verbose == 1) + { + ReportSimple(p_Vid, pic_type, cur_bits, &p_Vid->p_Dist->metric[PSNR], (int) tmp_time); + } + else if (p_Inp->Verbose == 2) + { + int lambda = (int) p_Vid->lambda_me[I_SLICE][p_Vid->masterQP][F_PEL]; + if (p_Inp->Distortion[SSIM] == 1) + { + ReportVerboseSSIM(p_Vid, pic_type, cur_bits, 0, lambda, &p_Vid->p_Dist->metric[PSNR], &p_Vid->p_Dist->metric[SSIM], (int) tmp_time, 0); + } + else + { + ReportVerbose(p_Vid, pic_type, cur_bits, 0, lambda, &p_Vid->p_Dist->metric[PSNR], (int) tmp_time, 0); + } + } + else if (p_Inp->Verbose == 3) + { + int lambda = (int) p_Vid->lambda_me[I_SLICE][p_Vid->masterQP][F_PEL]; + if (p_Inp->Distortion[SSIM] == 1) + { + ReportVerboseNVBSSIM(p_Vid, pic_type, cur_bits + stats->bit_ctr_parametersets_n, stats->bit_ctr_parametersets_n, 0, lambda, &p_Vid->p_Dist->metric[PSNR], &p_Vid->p_Dist->metric[SSIM], (int) tmp_time, 0); + } + else + { + ReportVerboseNVB(p_Vid, pic_type, cur_bits + stats->bit_ctr_parametersets_n, stats->bit_ctr_parametersets_n, 0, lambda, &p_Vid->p_Dist->metric[PSNR], (int) tmp_time, 0); + } + } + else if (p_Inp->Verbose == 4) + { + int lambda = (int) p_Vid->lambda_me[I_SLICE][p_Vid->masterQP][F_PEL]; + if (p_Inp->Distortion[SSIM] == 1) + { + ReportVerboseFDNSSIM(p_Vid, pic_type, cur_bits + stats->bit_ctr_parametersets_n, (int)(stats->bit_ctr_filler_data - stats->bit_ctr_filler_data_n), stats->bit_ctr_parametersets_n, 0, lambda, &p_Vid->p_Dist->metric[PSNR], &p_Vid->p_Dist->metric[SSIM], (int) tmp_time, 0); + } + else + { + ReportVerboseFDN(p_Vid, pic_type, cur_bits + stats->bit_ctr_parametersets_n, (int)(stats->bit_ctr_filler_data - stats->bit_ctr_filler_data_n), stats->bit_ctr_parametersets_n, 0, lambda, &p_Vid->p_Dist->metric[PSNR], (int) tmp_time, 0); + } + } +} + +static void ReportB(VideoParameters *p_Vid, int64 tmp_time) +{ + InputParameters *p_Inp = p_Vid->p_Inp; + StatParameters *stats = p_Vid->p_Stats; + + int cur_bits = (int)(stats->bit_ctr - stats->bit_ctr_n) + + (int)(stats->bit_ctr_filler_data - stats->bit_ctr_filler_data_n); + + if (p_Inp->Verbose == 1) + { + ReportSimple(p_Vid, " B ", cur_bits, &p_Vid->p_Dist->metric[PSNR], (int) tmp_time); + } + else if (p_Inp->Verbose == 2) + { + int lambda = (int) p_Vid->lambda_me[B_SLICE][p_Vid->masterQP][F_PEL]; + if (p_Inp->Distortion[SSIM] == 1) + ReportVerboseSSIM(p_Vid, " B ", cur_bits, p_Vid->active_pps->weighted_bipred_idc, lambda, &p_Vid->p_Dist->metric[PSNR], &p_Vid->p_Dist->metric[SSIM], (int) tmp_time, p_Vid->direct_spatial_mv_pred_flag); + else + ReportVerbose(p_Vid, " B ", cur_bits, p_Vid->active_pps->weighted_bipred_idc, lambda, &p_Vid->p_Dist->metric[PSNR], (int) tmp_time, p_Vid->direct_spatial_mv_pred_flag); + } + else if (p_Inp->Verbose == 3) + { + int lambda = (int) p_Vid->lambda_me[B_SLICE][p_Vid->masterQP][F_PEL]; + if (p_Inp->Distortion[SSIM] == 1) + ReportVerboseNVBSSIM(p_Vid, " B ", cur_bits + stats->bit_ctr_parametersets_n, stats->bit_ctr_parametersets_n, p_Vid->active_pps->weighted_bipred_idc, lambda, &p_Vid->p_Dist->metric[PSNR], &p_Vid->p_Dist->metric[SSIM], (int) tmp_time, p_Vid->direct_spatial_mv_pred_flag); + else + ReportVerboseNVB(p_Vid, " B ", cur_bits + stats->bit_ctr_parametersets_n, stats->bit_ctr_parametersets_n, p_Vid->active_pps->weighted_bipred_idc, lambda, &p_Vid->p_Dist->metric[PSNR], (int) tmp_time, p_Vid->direct_spatial_mv_pred_flag); + } + else if (p_Inp->Verbose == 4) + { + int lambda = (int) p_Vid->lambda_me[B_SLICE][p_Vid->masterQP][F_PEL]; + if (p_Inp->Distortion[SSIM] == 1) + ReportVerboseFDNSSIM(p_Vid, " B ", cur_bits + stats->bit_ctr_parametersets_n, (int)(stats->bit_ctr_filler_data - stats->bit_ctr_filler_data_n), stats->bit_ctr_parametersets_n, p_Vid->active_pps->weighted_bipred_idc, lambda, &p_Vid->p_Dist->metric[PSNR], &p_Vid->p_Dist->metric[SSIM], (int) tmp_time, p_Vid->direct_spatial_mv_pred_flag); + else + ReportVerboseFDN(p_Vid, " B ", cur_bits + stats->bit_ctr_parametersets_n, (int)(stats->bit_ctr_filler_data - stats->bit_ctr_filler_data_n), stats->bit_ctr_parametersets_n, p_Vid->active_pps->weighted_bipred_idc, lambda, &p_Vid->p_Dist->metric[PSNR], (int) tmp_time, p_Vid->direct_spatial_mv_pred_flag); + } +} + +static void ReportP(VideoParameters *p_Vid, int64 tmp_time) +{ + InputParameters *p_Inp = p_Vid->p_Inp; + StatParameters *stats = p_Vid->p_Stats; + + char pic_type[4]; + int cur_bits = (int)(stats->bit_ctr - stats->bit_ctr_n) + + (int)(stats->bit_ctr_filler_data - stats->bit_ctr_filler_data_n); + + if (p_Vid->type == SP_SLICE) + strcpy(pic_type,"SP "); + else if ((p_Inp->redundant_pic_flag == 0) || !p_Vid->redundant_coding ) + strcpy(pic_type," P "); + else + strcpy(pic_type," R "); + + if (p_Inp->Verbose == 1) + { + ReportSimple(p_Vid, pic_type, cur_bits, &p_Vid->p_Dist->metric[PSNR], (int) tmp_time); + } + else if (p_Inp->Verbose == 2) + { + int lambda = (int) p_Vid->lambda_me[P_SLICE][p_Vid->masterQP][F_PEL]; + if (p_Inp->Distortion[SSIM] == 1) + ReportVerboseSSIM(p_Vid, pic_type, cur_bits, p_Vid->active_pps->weighted_pred_flag, lambda, &p_Vid->p_Dist->metric[PSNR], &p_Vid->p_Dist->metric[SSIM], (int) tmp_time, 0); + else + ReportVerbose(p_Vid, pic_type, cur_bits, p_Vid->active_pps->weighted_pred_flag, lambda, &p_Vid->p_Dist->metric[PSNR], (int) tmp_time, 0); + } + else if (p_Inp->Verbose == 3) + { + int lambda = (int) p_Vid->lambda_me[P_SLICE][p_Vid->masterQP][F_PEL]; + if (p_Inp->Distortion[SSIM] == 1) + ReportVerboseNVBSSIM(p_Vid, pic_type, cur_bits + stats->bit_ctr_parametersets_n, stats->bit_ctr_parametersets_n, p_Vid->active_pps->weighted_pred_flag, lambda, &p_Vid->p_Dist->metric[PSNR], &p_Vid->p_Dist->metric[SSIM], (int) tmp_time, 0); + else + ReportVerboseNVB(p_Vid, pic_type, cur_bits + stats->bit_ctr_parametersets_n, stats->bit_ctr_parametersets_n, p_Vid->active_pps->weighted_pred_flag, lambda, &p_Vid->p_Dist->metric[PSNR], (int) tmp_time, 0); + } + else if (p_Inp->Verbose == 4) + { + int lambda = (int) p_Vid->lambda_me[P_SLICE][p_Vid->masterQP][F_PEL]; + if (p_Inp->Distortion[SSIM] == 1) + ReportVerboseFDNSSIM(p_Vid, pic_type, cur_bits + stats->bit_ctr_parametersets_n, (int)(stats->bit_ctr_filler_data - stats->bit_ctr_filler_data_n), stats->bit_ctr_parametersets_n, p_Vid->active_pps->weighted_pred_flag, lambda, &p_Vid->p_Dist->metric[PSNR], &p_Vid->p_Dist->metric[SSIM], (int) tmp_time, 0); + else + ReportVerboseFDN(p_Vid, pic_type, cur_bits + stats->bit_ctr_parametersets_n, (int)(stats->bit_ctr_filler_data - stats->bit_ctr_filler_data_n), stats->bit_ctr_parametersets_n, p_Vid->active_pps->weighted_pred_flag, lambda, &p_Vid->p_Dist->metric[PSNR], (int) tmp_time, 0); + } +} + +/*! + ************************************************************************ + * \brief + * point to frame coding variables + ************************************************************************ + */ +static void put_buffer_frame(VideoParameters *p_Vid) +{ + p_Vid->pCurImg = p_Vid->imgData.frm_data[0]; + p_Vid->pImgOrg[0] = p_Vid->imgData.frm_data[0]; + + if (p_Vid->yuv_format != YUV400) + { + p_Vid->pImgOrg[1] = p_Vid->imgData.frm_data[1]; + p_Vid->pImgOrg[2] = p_Vid->imgData.frm_data[2]; + } + + if (p_Vid->p_Inp->MDReference[0] || p_Vid->p_Inp->MDReference[1]) + { + p_Vid->pCurImgRef = p_Vid->imgRefData.frm_data[0]; + p_Vid->pImgOrgRef[0] = p_Vid->imgRefData.frm_data[0]; + + if (p_Vid->yuv_format != YUV400) + { + p_Vid->pImgOrgRef[1] = p_Vid->imgRefData.frm_data[1]; + p_Vid->pImgOrgRef[2] = p_Vid->imgRefData.frm_data[2]; + } + } +} + +/*! + ************************************************************************ + * \brief + * point to top field coding variables + ************************************************************************ + */ +static void put_buffer_top(VideoParameters *p_Vid) +{ + p_Vid->fld_type = 0; + + p_Vid->pCurImg = p_Vid->imgData.top_data[0]; + p_Vid->pImgOrg[0] = p_Vid->imgData.top_data[0]; + + if (p_Vid->yuv_format != YUV400) + { + p_Vid->pImgOrg[1] = p_Vid->imgData.top_data[1]; + p_Vid->pImgOrg[2] = p_Vid->imgData.top_data[2]; + } + + if ( p_Vid->p_Inp->MDReference[0] || p_Vid->p_Inp->MDReference[1] ) + { + p_Vid->pCurImgRef = p_Vid->imgRefData.top_data[0]; + p_Vid->pImgOrgRef[0] = p_Vid->imgRefData.top_data[0]; + + if (p_Vid->yuv_format != YUV400) + { + p_Vid->pImgOrgRef[1] = p_Vid->imgRefData.top_data[1]; + p_Vid->pImgOrgRef[2] = p_Vid->imgRefData.top_data[2]; + } + } +} + +/*! + ************************************************************************ + * \brief + * point to bottom field coding variables + ************************************************************************ + */ +static void put_buffer_bot(VideoParameters *p_Vid) +{ + p_Vid->fld_type = 1; + + p_Vid->pCurImg = p_Vid->imgData.bot_data[0]; + p_Vid->pImgOrg[0] = p_Vid->imgData.bot_data[0]; + + if (p_Vid->yuv_format != YUV400) + { + p_Vid->pImgOrg[1] = p_Vid->imgData.bot_data[1]; + p_Vid->pImgOrg[2] = p_Vid->imgData.bot_data[2]; + } + + if ( p_Vid->p_Inp->MDReference[0] || p_Vid->p_Inp->MDReference[1] ) + { + p_Vid->pCurImgRef = p_Vid->imgRefData.bot_data[0]; + p_Vid->pImgOrgRef[0] = p_Vid->imgRefData.bot_data[0]; + + if (p_Vid->yuv_format != YUV400) + { + p_Vid->pImgOrgRef[1] = p_Vid->imgRefData.bot_data[1]; + p_Vid->pImgOrgRef[2] = p_Vid->imgRefData.bot_data[2]; + } + } +} + +/*! +************************************************************************************* +* Brief +* Output SP frames coefficients +************************************************************************************* +*/ +void output_SP_coefficients(VideoParameters *p_Vid, InputParameters *p_Inp) +{ + int i,k; + FILE *SP_coeff_file; + int ret; + if(p_Vid->number_sp2_frames==0) + { + if ((SP_coeff_file = fopen(p_Inp->sp_output_filename,"wb")) == NULL) + { + printf ("Fatal: cannot open SP output file '%s', exit (-1)\n", p_Inp->sp_output_filename); + exit (-1); + } + p_Vid->number_sp2_frames++; + } + else + { + if ((SP_coeff_file = fopen(p_Inp->sp_output_filename,"ab")) == NULL) + { + printf ("Fatal: cannot open SP output file '%s', exit (-1)\n", p_Inp->sp_output_filename); + exit (-1); + } + } + + for(i=0;iheight;i++) + { + ret = (int) fwrite(p_Vid->lrec[i], sizeof(int), p_Vid->width, SP_coeff_file); + if (ret != p_Vid->width) + { + error ("cannot write to SP output file", -1); + } + } + + for(k=0;k<2;k++) + { + for(i=0;iheight_cr;i++) + { + ret = (int) fwrite(p_Vid->lrec_uv[k][i], sizeof(int), p_Vid->width_cr, SP_coeff_file); + if (ret != p_Vid->width_cr) + { + error ("cannot write to SP output file", -1); + } + } + } + fclose(SP_coeff_file); +} + +/*! +************************************************************************************* +* Brief +* Read SP frames coefficients +************************************************************************************* +*/ +void read_SP_coefficients(VideoParameters *p_Vid, InputParameters *p_Inp) +{ + int i,k; + FILE *SP_coeff_file; + + if ( p_Inp->sp_switch_period > 0 && ( p_Vid->number_sp2_frames % (p_Inp->sp_switch_period << 1) ) >= p_Inp->sp_switch_period ) + { + if ((SP_coeff_file = fopen(p_Inp->sp2_input_filename1,"rb")) == NULL) + { + printf ("Fatal: cannot open SP input file '%s', exit (-1)\n", p_Inp->sp2_input_filename2); + exit (-1); + } + } + else + { + if ((SP_coeff_file = fopen(p_Inp->sp2_input_filename2,"rb")) == NULL) + { + printf ("Fatal: cannot open SP input file '%s', exit (-1)\n", p_Inp->sp2_input_filename1); + exit (-1); + } + } + + if (0 != fseek (SP_coeff_file, p_Vid->size * 3/2*p_Vid->number_sp2_frames*sizeof(int), SEEK_SET)) + { + printf ("Fatal: cannot seek in SP input file, exit (-1)\n"); + exit (-1); + } + p_Vid->number_sp2_frames++; + + for(i=0;iheight;i++) + { + if(p_Vid->width!=(int)fread(p_Vid->lrec[i],sizeof(int),p_Vid->width,SP_coeff_file)) + { + printf ("Fatal: cannot read in SP input file, exit (-1)\n"); + exit (-1); + } + } + + for(k=0;k<2;k++) + { + for(i=0;iheight_cr;i++) + { + if(p_Vid->width_cr!=(int)fread(p_Vid->lrec_uv[k][i],sizeof(int),p_Vid->width_cr,SP_coeff_file)) + { + printf ("Fatal: cannot read in SP input file, exit (-1)\n"); + exit (-1); + } + } + } + fclose(SP_coeff_file); +} + + +/*! +************************************************************************************* +* Brief +* Select appropriate image plane (for 444 coding) +************************************************************************************* +*/ +void select_plane(VideoParameters *p_Vid, ColorPlane color_plane) +{ + p_Vid->pCurImg = p_Vid->pImgOrg[color_plane]; + p_Vid->enc_picture->p_curr_img = p_Vid->enc_picture->p_img[color_plane]; + p_Vid->enc_picture->p_curr_img_sub = p_Vid->enc_picture->p_img_sub[color_plane]; + p_Vid->max_imgpel_value = (short) p_Vid->max_pel_value_comp[color_plane]; + p_Vid->dc_pred_value = p_Vid->dc_pred_value_comp[color_plane]; +} + +/*! +************************************************************************************* +* Brief +* Is this picture the first access unit in this GOP? +************************************************************************************* +*/ +static int is_gop_first_unit(VideoParameters *p_Vid, InputParameters *p_Inp) +{ + return ( get_idr_flag(p_Vid) || ( p_Vid->type == I_SLICE && p_Inp->EnableOpenGOP && get_random_access_flag(p_Vid) ) ); +} + +/*! +************************************************************************************* +* Brief +* AUD, SPS, PPS, and SEI messages +************************************************************************************* +*/ +void write_non_vcl_nalu( VideoParameters *p_Vid ) +{ + InputParameters *p_Inp = p_Vid->p_Inp; + + // SPS + PPS + AUD + if (p_Inp->ResendSPS == 3 && is_gop_first_unit(p_Vid, p_Inp) && p_Vid->number) + { + p_Vid->p_Stats->bit_slice = rewrite_paramsets(p_Vid); + } + else if (p_Inp->ResendSPS == 2 && get_idr_flag(p_Vid) && p_Vid->number) + { + p_Vid->p_Stats->bit_slice = rewrite_paramsets(p_Vid); + } + else if (p_Inp->ResendSPS == 1 && p_Vid->type == I_SLICE && p_Vid->curr_frm_idx != 0) + { + p_Vid->p_Stats->bit_slice = rewrite_paramsets(p_Vid); + } + // PPS + AUD + else if ( p_Inp->ResendPPS && p_Vid->curr_frm_idx != 0 ) + { + int i, total_pps = p_Inp->GenerateMultiplePPS ? 3 : 1; + + p_Vid->p_Stats->bit_ctr_parametersets_n = 0; + // Access Unit Delimiter NALU + if ( p_Inp->SendAUD ) + { + p_Vid->p_Stats->bit_ctr_parametersets_n = Write_AUD_NALU(p_Vid); + } + for ( i = 0; i < total_pps; i++ ) + { + p_Vid->p_Stats->bit_ctr_parametersets_n += write_PPS(p_Vid, 0, i); + } + } + // Access Unit Delimiter NALU + else if ( p_Inp->SendAUD && p_Vid->curr_frm_idx != 0 ) + { + p_Vid->p_Stats->bit_ctr_parametersets_n += Write_AUD_NALU(p_Vid); + } + + UpdateSubseqInfo (p_Vid, p_Inp, p_Vid->layer); // Tian Dong (Sept 2002) + UpdateSceneInformation (p_Vid->p_SEI, FALSE, 0, 0, -1); // JVT-D099, scene information SEI, nothing included by default + + //! Commented out by StW, needs fixing in SEI.h to keep the trace file clean + // PrepareAggregationSEIMessage (p_Vid); + + // write tone mapping SEI message + if (p_Inp->ToneMappingSEIPresentFlag) + { + UpdateToneMapping(p_Vid->p_SEI); + } + + // pic timing + ClearPicTiming(p_Vid->p_SEI); + if ( p_Inp->SEIVUI32Pulldown ) + { + UpdatePicTiming(p_Vid, p_Inp); + PrepareAggregationSEIMessage(p_Vid); + p_Vid->p_Stats->bit_ctr_parametersets_n += Write_SEI_NALU(p_Vid, 0); + ClearPicTiming(p_Vid->p_SEI); + // clear message + clear_sei_message(p_Vid->p_SEI, AGGREGATION_SEI); + } + else + { + PrepareAggregationSEIMessage(p_Vid); + } + + p_Vid->p_Stats->bit_ctr_parametersets_n += Write_SEI_NALU(p_Vid, 0); + + // update seq NVB counter + p_Vid->p_Stats->bit_ctr_parametersets += p_Vid->p_Stats->bit_ctr_parametersets_n; +} + +/*! +************************************************************************************* +* Brief +* AUD, SPS, PPS, and SEI messages for the bottom field picture +************************************************************************************* +*/ +void write_non_vcl_nalu_bot_fld( VideoParameters *p_Vid ) +{ + InputParameters *p_Inp = p_Vid->p_Inp; + int init_param_set_bits = p_Vid->p_Stats->bit_ctr_parametersets_n; + + // SPS + PPS + AUD + if (p_Inp->ResendSPS == 1 && p_Vid->type == I_SLICE) + { + p_Vid->p_Stats->bit_slice = rewrite_paramsets(p_Vid); + } + // PPS + AUD + else if ( p_Inp->ResendPPS ) + { + int i, total_pps = p_Inp->GenerateMultiplePPS ? 3 : 1; + + p_Vid->p_Stats->bit_ctr_parametersets_n = 0; + // Access Unit Delimiter NALU + if ( p_Inp->SendAUD ) + { + p_Vid->p_Stats->bit_ctr_parametersets_n = Write_AUD_NALU(p_Vid); + } + for ( i = 0; i < total_pps; i++ ) + { + p_Vid->p_Stats->bit_ctr_parametersets_n += write_PPS(p_Vid, 0, i); + } + } + // Access Unit Delimiter NALU + else if ( p_Inp->SendAUD ) + { + p_Vid->p_Stats->bit_ctr_parametersets_n += Write_AUD_NALU(p_Vid); + } + + UpdateSubseqInfo (p_Vid, p_Inp, p_Vid->layer); // Tian Dong (Sept 2002) + UpdateSceneInformation (p_Vid->p_SEI, FALSE, 0, 0, -1); // JVT-D099, scene information SEI, nothing included by default + + //! Commented out by StW, needs fixing in SEI.h to keep the trace file clean + // PrepareAggregationSEIMessage (p_Vid); + + // write tone mapping SEI message + if (p_Inp->ToneMappingSEIPresentFlag) + { + UpdateToneMapping(p_Vid->p_SEI); + } + + PrepareAggregationSEIMessage(p_Vid); + + p_Vid->p_Stats->bit_ctr_parametersets_n += Write_SEI_NALU(p_Vid, 0); + + // update seq NVB counter + p_Vid->p_Stats->bit_ctr_parametersets += (p_Vid->p_Stats->bit_ctr_parametersets_n - init_param_set_bits); +} + +void OtfCompatibility_copyWithPadding ( imgpel **dstImg, imgpel **srcImg, int size_x, int size_y, int padding_x, int padding_y ) +{ + int i, j; + int size_x_minus1 = size_x - 1; + + imgpel *wBufSrc, *wBufDst; + + // Copy top line + wBufDst = &( dstImg[-padding_y][-padding_x] ); + wBufSrc = srcImg[0]; + // left IMG_PAD_SIZE + for (i = 0; i < padding_x; ++i) + *(wBufDst++) = wBufSrc[0]; + // center 0-(size_x) + memcpy(wBufDst, wBufSrc, size_x * sizeof(imgpel)); + wBufDst += size_x; + // right IMG_PAD_SIZE + for (i = 0; i < padding_x; ++i) + *(wBufDst++) = wBufSrc[size_x_minus1]; + + // Now copy remaining pad lines + for (j = -padding_y+1; j < 0; ++j) + { + memcpy(dstImg[j]-padding_x, dstImg[j - 1]-padding_x, (size_x+2*padding_x) * sizeof(imgpel)); + } + + for (j = 0; j < size_y; ++j) + { + wBufDst = &( dstImg[j][-padding_x] ); // 4:4:4 independent mode + wBufSrc = srcImg[j]; + // left IMG_PAD_SIZE + for (i = 0; i < padding_x; ++i) + *(wBufDst++) = wBufSrc[0]; + // center 0-(size_x) + //memcpy(wBufDst, wBufSrc, size_x * sizeof(imgpel)); + wBufDst += size_x; + // right IMG_PAD_SIZE + for (i = 0; i < padding_x; ++i) + *(wBufDst++) = wBufSrc[size_x_minus1]; + } + + // Replicate bottom pad lines + for (j = size_y; j < size_y+padding_y; ++j) + { + memcpy(dstImg[j]-padding_x, dstImg[j - 1]-padding_x, (size_x+2*padding_x) * sizeof(imgpel)); + } + +} + +#if (MVC_EXTENSION_ENABLE) +/*! +************************************************************************************* +* Brief +* MVC AUD, SPS, PPS, and SEI messages +************************************************************************************* +*/ +void write_non_vcl_nalu_mvc( VideoParameters *p_Vid ) +{ + InputParameters *p_Inp = p_Vid->p_Inp; + int bits; + + if ( p_Vid->view_id ) + { + if ( p_Inp->SendAUD == 2 ) + { + p_Vid->p_Stats->bit_ctr_parametersets_n += bits = Write_AUD_NALU(p_Vid); + p_Vid->p_Stats->bit_ctr_parametersets_n_v[p_Vid->view_id] += bits; + } + } + else // view_id == 0 + { + // SPS + PPS + AUD + if (p_Inp->ResendSPS == 3 && is_gop_first_unit(p_Vid, p_Inp) && p_Vid->number && p_Vid->structure != BOTTOM_FIELD) + { + p_Vid->p_Stats->bit_slice = rewrite_paramsets(p_Vid); + } + else if (p_Inp->ResendSPS == 2 && get_idr_flag(p_Vid) && p_Vid->number && p_Vid->structure != BOTTOM_FIELD) + { + p_Vid->p_Stats->bit_slice = rewrite_paramsets(p_Vid); + } + else if (p_Inp->ResendSPS == 1 && p_Vid->type == I_SLICE && p_Vid->curr_frm_idx != 0 && p_Vid->structure != BOTTOM_FIELD) + { + p_Vid->p_Stats->bit_slice = rewrite_paramsets(p_Vid); + } + // PPS + AUD + else if ( p_Inp->ResendPPS && p_Vid->curr_frm_idx != 0 ) + { + int i, total_pps = p_Inp->GenerateMultiplePPS ? 3 : 1; + + p_Vid->p_Stats->bit_ctr_parametersets_n = 0; + // Access Unit Delimiter NALU + if ( p_Inp->SendAUD ) + { + p_Vid->p_Stats->bit_ctr_parametersets_n = bits = Write_AUD_NALU(p_Vid); + p_Vid->p_Stats->bit_ctr_parametersets_n_v[p_Vid->view_id] += bits; + for ( i = 0; i < total_pps; i++ ) + { + p_Vid->p_Stats->bit_ctr_parametersets_n += bits = write_PPS(p_Vid, 0, i); + p_Vid->p_Stats->bit_ctr_parametersets_n_v[p_Vid->view_id] += bits; + } + } + else + { + for ( i = 0; i < total_pps; i++ ) + { + p_Vid->p_Stats->bit_ctr_parametersets_n += bits = write_PPS(p_Vid, 0, i); + p_Vid->p_Stats->bit_ctr_parametersets_n_v[p_Vid->view_id] += bits; + } + } + } + // Access Unit Delimiter NALU + else if ( p_Inp->SendAUD && (p_Vid->curr_frm_idx != 0 || p_Vid->structure == BOTTOM_FIELD) ) + { + p_Vid->p_Stats->bit_ctr_parametersets_n += bits = Write_AUD_NALU(p_Vid); + p_Vid->p_Stats->bit_ctr_parametersets_n_v[p_Vid->view_id] += bits; + } + } + + UpdateSubseqInfo (p_Vid, p_Inp, p_Vid->layer); // Tian Dong (Sept 2002) + UpdateSceneInformation (p_Vid->p_SEI, FALSE, 0, 0, -1); // JVT-D099, scene information SEI, nothing included by default + + //! Commented out by StW, needs fixing in SEI.h to keep the trace file clean + // PrepareAggregationSEIMessage (p_Vid); + + // write tone mapping SEI message + if (p_Inp->ToneMappingSEIPresentFlag) + { + UpdateToneMapping(p_Vid->p_SEI); + } + + PrepareAggregationSEIMessage(p_Vid); + + p_Vid->p_Stats->bit_ctr_parametersets_n += bits = Write_SEI_NALU(p_Vid, 0); + p_Vid->p_Stats->bit_ctr_parametersets_n_v[p_Vid->view_id] += bits; + + // update seq NVB counter + p_Vid->p_Stats->bit_ctr_parametersets += p_Vid->p_Stats->bit_ctr_parametersets_n; +} +#endif + + diff --git a/lencod/src/image_mp.c b/lencod/src/image_mp.c new file mode 100644 index 0000000..b9b5899 --- /dev/null +++ b/lencod/src/image_mp.c @@ -0,0 +1,691 @@ + +/*! + ************************************************************************************* + * \file image.c + * + * \brief + * multiple-pass encoding of one frame + * + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * - Yan Ye + ************************************************************************************* + */ +#include "contributors.h" + +#include "global.h" +#include "image.h" +#include "rc_quadratic.h" +#include "wp.h" +#include "pred_struct.h" +#include "slice.h" + +#define DBG_IMAGE_MP 0 + +typedef enum +{ + REGULAR = 0, + EXP_WP = 1, + IMP_WP = 2, // only applicable to B + FRAME_TYPE = 2, // only applicable to P + FRAME_QP = 3, + ALT_DIRECT = 4, + ALT_ENTROPY = 5, + DB_OFF = 6, +} FrameCodingMethod; + +void frame_picture_mp_i_slice(VideoParameters *p_Vid, InputParameters *p_Inp); +void frame_picture_mp_p_slice(VideoParameters *p_Vid, InputParameters *p_Inp); +void frame_picture_mp_b_slice(VideoParameters *p_Vid, InputParameters *p_Inp); + +void store_coding_info(VideoParameters *p_Vid, CodingInfo *coding_info) +{ + coding_info->type = p_Vid->type; + coding_info->intras = p_Vid->intras; + coding_info->sumFrameQP = p_Vid->SumFrameQP; + coding_info->num_ref_idx_l0 = p_Vid->num_ref_idx_l0_active; + coding_info->num_ref_idx_l1 = p_Vid->num_ref_idx_l1_active; + coding_info->active_pps = p_Vid->active_pps; +} + +void store_coding_and_rc_info(VideoParameters *p_Vid, CodingInfo *coding_info) +{ + InputParameters *p_Inp = p_Vid->p_Inp; + + store_coding_info( p_Vid, coding_info ); + + if ( p_Inp->RCEnable ) + { + rc_save_state( p_Vid, p_Inp ); + } +} + +void restore_coding_info(VideoParameters *p_Vid, CodingInfo *coding_info) +{ + p_Vid->type = coding_info->type; + p_Vid->intras = coding_info->intras; + p_Vid->SumFrameQP = coding_info->sumFrameQP; + p_Vid->num_ref_idx_l0_active = coding_info->num_ref_idx_l0; + p_Vid->num_ref_idx_l1_active = coding_info->num_ref_idx_l1; + p_Vid->active_pps = coding_info->active_pps; +} + +void swap_frame_buffer(VideoParameters *p_Vid, int a, int b) +{ + StorablePicture *s; + Picture *p; + + p = p_Vid->frame_pic[a]; + p_Vid->frame_pic[a] = p_Vid->frame_pic[b]; + p_Vid->frame_pic[b] = p; + + s = p_Vid->enc_frame_picture[a]; + p_Vid->enc_frame_picture[a] = p_Vid->enc_frame_picture[b]; + p_Vid->enc_frame_picture[b] = s; +} + + +void frame_picture_mp_exit(VideoParameters *p_Vid, CodingInfo *coding_info) +{ + InputParameters *p_Inp = p_Vid->p_Inp; + + p_Vid->p_curr_frm_struct->qp = p_Vid->qp; + p_Vid->enc_picture=p_Vid->enc_frame_picture[0]; + p_Vid->p_frame_pic = p_Vid->frame_pic[0]; + restore_coding_info(p_Vid, coding_info); + + if ( p_Inp->RCEnable ) + { + rc_restore_state(p_Vid, p_Inp); + } +} + + +void frame_picture_mp_p_slice(VideoParameters *p_Vid, InputParameters *p_Inp) +{ + int rd_pass = 0; + int rd_qp = p_Vid->p_curr_frm_struct->qp; + float rateRatio = 1.0F; + int wp_pass=0; + int frame_type_pass = 0; + CodingInfo coding_info; + FrameCodingMethod best_method = REGULAR; + int frame_type = P_SLICE; + int apply_wp = 0; + int selection; + + frame_picture (p_Vid, p_Vid->frame_pic[rd_pass], &p_Vid->imgData, rd_pass); + store_coding_and_rc_info(p_Vid, &coding_info); + + if(p_Inp->WPIterMC) + p_Vid->frameOffsetAvail = 1; + +#if (DBG_IMAGE_MP) + printf("rd_pass = %d: %d (%.0f, %.0f, %.0f)\n", rd_pass, + p_Vid->frame_pic[0]->bits_per_picture, + p_Vid->frame_pic[0]->distortion.value[0], p_Vid->frame_pic[0]->distortion.value[1], p_Vid->frame_pic[0]->distortion.value[2]); +#endif + + rd_pass++; + if(rd_pass >= p_Inp->RDPictureMaxPassPSlice) + { + frame_picture_mp_exit(p_Vid, &coding_info); + return; + } + + // for P_Slice, consider WP + wp_pass = 0; + if (p_Inp->GenerateMultiplePPS) + { + Slice *dummy_slice = NULL; + + InitWP(p_Vid, p_Inp, 0); + if ( p_Inp->WPMCPrecision ) + p_Vid->pWPX->curr_wp_rd_pass = p_Vid->pWPX->wp_rd_passes + 1; + init_slice_lite(p_Vid, &dummy_slice, 0); + + if (p_Vid->TestWPPSlice(dummy_slice, 0) == 1) + { + // regular WP pass + p_Vid->active_pps = p_Vid->PicParSet[1]; + if ( p_Inp->WPMCPrecision ) + p_Vid->pWPX->curr_wp_rd_pass->algorithm = WP_REGULAR; + wp_pass = 1; + } + else if ( p_Inp->WPMCPrecision ) + { + // WPMC pass + p_Vid->active_pps = p_Vid->PicParSet[1]; + wp_pass = 1; + } + + // The way it is, the code would only reach here if prior conditional using + // generatemultiplepps is satisfied + if(wp_pass) + { + p_Vid->write_macroblock = FALSE; + p_Vid->p_curr_frm_struct->qp = p_Vid->qp; + frame_picture (p_Vid, p_Vid->frame_pic[rd_pass], &p_Vid->imgData, rd_pass); + selection = picture_coding_decision(p_Vid, p_Vid->frame_pic[0], p_Vid->frame_pic[rd_pass], rd_qp); +#if (DBG_IMAGE_MP) + printf("rd_pass = %d, selection = %d\n", rd_pass, selection); +#endif +#if (DBG_IMAGE_MP) + printf("rd_pass = %d: %d (%.0f, %.0f, %.0f)\n", rd_pass, + p_Vid->frame_pic[rd_pass]->bits_per_picture, + p_Vid->frame_pic[rd_pass]->distortion.value[0], p_Vid->frame_pic[rd_pass]->distortion.value[1], p_Vid->frame_pic[rd_pass]->distortion.value[2]); +#endif + + if (selection) + { + swap_frame_buffer(p_Vid, 0, rd_pass); + store_coding_and_rc_info(p_Vid, &coding_info); + best_method = EXP_WP; + apply_wp = 1; + } + + if(p_Inp->WPMethod == 0 || p_Inp->WPMCPrecision) + { + wp_pass = 0; + if ( p_Inp->WPMCPrecision ) + p_Vid->pWPX->curr_wp_rd_pass = p_Vid->pWPX->wp_rd_passes + 2; + if (p_Inp->WPMethod == 0 && p_Vid->TestWPPSlice(dummy_slice, 1) == 1) + { + // regular WP pass + p_Vid->active_pps = p_Vid->PicParSet[1]; + if ( p_Inp->WPMCPrecision ) + p_Vid->pWPX->curr_wp_rd_pass->algorithm = WP_REGULAR; + wp_pass = 1; + } + else if ( p_Inp->WPMCPrecision ) + { + // WPMC pass + p_Vid->active_pps = p_Vid->PicParSet[1]; + wp_pass = 1; + } + + if(wp_pass) + { + p_Vid->write_macroblock = FALSE; + p_Vid->p_curr_frm_struct->qp = p_Vid->qp; + free_slice_list(p_Vid->frame_pic[rd_pass]); + free_storable_picture(p_Vid, p_Vid->enc_frame_picture[rd_pass]); + frame_picture (p_Vid, p_Vid->frame_pic[rd_pass], &p_Vid->imgData, rd_pass); + selection = picture_coding_decision(p_Vid, p_Vid->frame_pic[0], p_Vid->frame_pic[rd_pass], rd_qp); +#if (DBG_IMAGE_MP) + printf("rd_pass = %d, selection = %d\n", rd_pass, selection); +#endif +#if (DBG_IMAGE_MP) + printf("rd_pass = %d: %d (%.0f, %.0f, %.0f)\n", rd_pass, + p_Vid->frame_pic[rd_pass]->bits_per_picture, + p_Vid->frame_pic[rd_pass]->distortion.value[0], p_Vid->frame_pic[rd_pass]->distortion.value[1], p_Vid->frame_pic[rd_pass]->distortion.value[2]); +#endif + + if (selection) + { + swap_frame_buffer(p_Vid, 0, rd_pass); + store_coding_and_rc_info(p_Vid, &coding_info); + best_method = EXP_WP; + apply_wp = 1; + } + } + } + + rd_pass++; + //free_slice(dummy_slice); + + if(rd_pass >= p_Inp->RDPictureMaxPassPSlice) + { + frame_picture_mp_exit(p_Vid, &coding_info); + free_slice(dummy_slice); + return; + } + } + free_slice(dummy_slice); + } + + + // code as I? or maybe as B? + frame_type_pass = 0; + if(p_Inp->RDPSliceITest && (coding_info.intras * 100/p_Vid->FrameSizeInMbs) >= 75) + { + frame_type = I_SLICE; + set_slice_type(p_Vid, p_Inp, I_SLICE); + populate_frame_slice_type( p_Inp, p_Vid->p_curr_frm_struct, I_SLICE, p_Vid->p_pred->max_num_slices ); + p_Vid->active_pps = p_Vid->PicParSet[0]; + frame_type_pass = 1; + } + else if (p_Inp->RDPSliceBTest && p_Vid->active_sps->profile_idc != BASELINE) + // later need to add some automatic criterion to see if this (coding P as B) may be beneficial + { + frame_type = B_SLICE; + set_slice_type(p_Vid, p_Inp, B_SLICE ); + populate_frame_slice_type( p_Inp, p_Vid->p_curr_frm_struct, B_SLICE, p_Vid->p_pred->max_num_slices ); + p_Vid->active_pps = p_Vid->PicParSet[0]; + frame_type_pass = 1; + } + + if(frame_type_pass) + { + p_Vid->write_macroblock = FALSE; + p_Vid->p_curr_frm_struct->qp = p_Vid->qp; + frame_picture (p_Vid, p_Vid->frame_pic[rd_pass], &p_Vid->imgData, rd_pass); + selection = picture_coding_decision(p_Vid, p_Vid->frame_pic[0], p_Vid->frame_pic[rd_pass], rd_qp); +#if (DBG_IMAGE_MP) + printf("rd_pass = %d, selection = %d\n", rd_pass, selection); +#endif + + if (selection) + { + swap_frame_buffer(p_Vid, 0, rd_pass); + store_coding_and_rc_info(p_Vid, &coding_info); + best_method = FRAME_TYPE; + } + // reset frame_type + else + frame_type = P_SLICE; + + rd_pass++; + if(rd_pass >= p_Inp->RDPictureMaxPassPSlice) + { + frame_picture_mp_exit(p_Vid, &coding_info); + return; + } + } + + if(p_Vid->EvaluateDBOff) + { + // Perform DB off coding pass + p_Vid->active_pps = (best_method == EXP_WP?p_Vid->PicParSet[1]:p_Vid->PicParSet[0]); + if(frame_type != P_SLICE) + { + set_slice_type(p_Vid, p_Inp, frame_type); + populate_frame_slice_type( p_Inp, p_Vid->p_curr_frm_struct, frame_type, p_Vid->p_pred->max_num_slices ); + } + p_Vid->TurnDBOff = 1; + p_Vid->write_macroblock = FALSE; + p_Vid->p_curr_frm_struct->qp = p_Vid->qp; + frame_picture (p_Vid, p_Vid->frame_pic[rd_pass], &p_Vid->imgData, rd_pass); + selection = picture_coding_decision(p_Vid, p_Vid->frame_pic[0], p_Vid->frame_pic[rd_pass], rd_qp); +#if (DBG_IMAGE_MP) + printf("DB OFF, rd_pass = %d, selection = %d\n", rd_pass, selection); +#endif + + if (selection) + { + swap_frame_buffer(p_Vid, 0, rd_pass); + store_coding_and_rc_info(p_Vid, &coding_info); + best_method = DB_OFF; + } + + rd_pass++; + if(rd_pass >= p_Inp->RDPictureMaxPassPSlice) + { + frame_picture_mp_exit(p_Vid, &coding_info); + return; + } + } + + if(p_Inp->RDPictureFrameQPPSlice) + { + // frame QP pass + p_Vid->active_pps = (apply_wp?p_Vid->PicParSet[1]:p_Vid->PicParSet[0]); + if(frame_type != P_SLICE) + { + set_slice_type(p_Vid, p_Inp, frame_type); + populate_frame_slice_type( p_Inp, p_Vid->p_curr_frm_struct, I_SLICE, p_Vid->p_pred->max_num_slices ); + } + p_Vid->qp = (p_Vid->nal_reference_idc==0 ? rd_qp+1:rd_qp-1); + p_Vid->qp = iClip3( p_Vid->RCMinQP, p_Vid->RCMaxQP, p_Vid->qp ); + if ( p_Inp->RCEnable ) + { + rateRatio = p_Vid->nal_reference_idc ? 1.15F : 0.85F; + rc_init_frame_rdpic( p_Vid, p_Inp, rateRatio ); + } + p_Vid->TurnDBOff = 0; + p_Vid->write_macroblock = FALSE; + p_Vid->p_curr_frm_struct->qp = p_Vid->qp; + frame_picture (p_Vid, p_Vid->frame_pic[rd_pass], &p_Vid->imgData, rd_pass); + selection = picture_coding_decision(p_Vid, p_Vid->frame_pic[0], p_Vid->frame_pic[rd_pass], rd_qp); +#if (DBG_IMAGE_MP) + printf("rd_pass = %d, selection = %d\n", rd_pass, selection); +#endif + + if (selection) + { + swap_frame_buffer(p_Vid, 0, rd_pass); + store_coding_and_rc_info(p_Vid, &coding_info); + best_method = FRAME_QP; + } + + rd_pass++; + if(rd_pass >= p_Inp->RDPictureMaxPassPSlice) + { + frame_picture_mp_exit(p_Vid, &coding_info); + return; + } + } + + frame_picture_mp_exit(p_Vid, &coding_info); +} + +void frame_picture_mp_i_slice(VideoParameters *p_Vid, InputParameters *p_Inp) +{ + int rd_pass = 0; + int qp = p_Vid->qp; + float rateRatio = 1.0F; + CodingInfo coding_info; + int selection; + + // initial pass encoding + frame_picture (p_Vid, p_Vid->frame_pic[rd_pass], &p_Vid->imgData, rd_pass); + store_coding_and_rc_info(p_Vid, &coding_info); + + rd_pass++; + if(rd_pass >= p_Inp->RDPictureMaxPassISlice) + { + frame_picture_mp_exit(p_Vid, &coding_info); + return; + } + + { + // try QP-1 + qp = p_Vid->qp; + p_Vid->qp = qp - 1; + p_Vid->qp = iClip3( p_Vid->RCMinQP, p_Vid->RCMaxQP, p_Vid->qp ); + if ( p_Inp->RCEnable ) + { + rateRatio = 1.15F; + rc_init_frame_rdpic( p_Vid, p_Inp, rateRatio ); + } + + p_Vid->write_macroblock = FALSE; + p_Vid->p_curr_frm_struct->qp = p_Vid->qp; + frame_picture (p_Vid, p_Vid->frame_pic[rd_pass], &p_Vid->imgData, rd_pass); + selection = picture_coding_decision(p_Vid, p_Vid->frame_pic[0], p_Vid->frame_pic[rd_pass], qp); + + if (selection) + { + swap_frame_buffer(p_Vid, 0, rd_pass); + store_coding_and_rc_info(p_Vid, &coding_info); + } + + rd_pass++; + if(rd_pass >= p_Inp->RDPictureMaxPassISlice) + { + frame_picture_mp_exit(p_Vid, &coding_info); + return; + } + + // try QP+1 + p_Vid->qp = (qp + 1); + p_Vid->qp = iClip3( p_Vid->RCMinQP, p_Vid->RCMaxQP, p_Vid->qp ); + p_Vid->write_macroblock = FALSE; + + if ( p_Inp->RCEnable ) + { + rateRatio = 1.0F; + rc_init_frame_rdpic( p_Vid, p_Inp, rateRatio ); + } + + p_Vid->qp = iClip3( p_Vid->RCMinQP, p_Vid->RCMaxQP, p_Vid->qp ); + p_Vid->p_curr_frm_struct->qp = p_Vid->qp; + frame_picture (p_Vid, p_Vid->frame_pic[rd_pass], &p_Vid->imgData, rd_pass); + selection = picture_coding_decision(p_Vid, p_Vid->frame_pic[0], p_Vid->frame_pic[rd_pass], qp); + + if ( selection ) + { + swap_frame_buffer(p_Vid, 0, rd_pass); + store_coding_and_rc_info(p_Vid, &coding_info); + } + + rd_pass++; + if(rd_pass >= p_Inp->RDPictureMaxPassISlice) + { + frame_picture_mp_exit(p_Vid, &coding_info); + return; + } + } + + frame_picture_mp_exit(p_Vid, &coding_info); +} + +void frame_picture_mp_b_slice(VideoParameters *p_Vid, InputParameters *p_Inp) +{ + int rd_pass = 0; + int rd_qp = p_Vid->p_curr_frm_struct->qp; + float rateRatio = 1.0F; + int wp_pass = 0; + CodingInfo coding_info; + FrameCodingMethod best_method = REGULAR; + int apply_wp = 0; + int selection; + Slice *dummy_slice = NULL; + +#if (DBG_IMAGE_MP) + printf("pass0_wp = %d\n", p_Vid->pass0_wp); +#endif + + frame_picture (p_Vid, p_Vid->frame_pic[rd_pass], &p_Vid->imgData, rd_pass); + store_coding_and_rc_info(p_Vid, &coding_info); + + if(p_Inp->WPIterMC) + p_Vid->frameOffsetAvail = 1; + + rd_pass++; + if(rd_pass >= p_Inp->RDPictureMaxPassBSlice) + { + frame_picture_mp_exit(p_Vid, &coding_info); + return; + } + + init_slice_lite(p_Vid, &dummy_slice, 0); + +#if (DBG_IMAGE_MP) + printf("rd_pass = %d: %d (%.0f, %.0f, %.0f)\n", rd_pass, + p_Vid->frame_pic[0]->bits_per_picture, + p_Vid->frame_pic[0]->distortion.value[0], p_Vid->frame_pic[0]->distortion.value[1], p_Vid->frame_pic[0]->distortion.value[2]); +#endif + + // for B_Slice, consider implicit WP + wp_pass = 0; + { + if (p_Inp->GenerateMultiplePPS) + { + InitWP(p_Vid, p_Inp, 0); + if ( p_Inp->WPMCPrecision ) + p_Vid->pWPX->curr_wp_rd_pass = p_Vid->pWPX->wp_rd_passes + 1; + + if (p_Vid->TestWPBSlice(dummy_slice, 1) == 1) + { + // regular WP pass + p_Vid->active_pps = p_Vid->PicParSet[2]; + if(p_Inp->WPMCPrecision) + p_Vid->pWPX->curr_wp_rd_pass->algorithm = WP_REGULAR; + wp_pass = 1; + } + } + + if(wp_pass) + { + p_Vid->write_macroblock = FALSE; + p_Vid->p_curr_frm_struct->qp = p_Vid->qp; + frame_picture (p_Vid, p_Vid->frame_pic[rd_pass], &p_Vid->imgData, rd_pass); + selection = picture_coding_decision(p_Vid, p_Vid->frame_pic[0], p_Vid->frame_pic[rd_pass], rd_qp); +#if (DBG_IMAGE_MP) + printf("IMP WP, rd_pass = %d, selection = %d\n", rd_pass, selection); + printf("rd_pass = %d: %d (%.0f, %.0f, %.0f)\n", rd_pass, + p_Vid->frame_pic[rd_pass]->bits_per_picture, + p_Vid->frame_pic[rd_pass]->distortion.value[0], p_Vid->frame_pic[rd_pass]->distortion.value[1], p_Vid->frame_pic[rd_pass]->distortion.value[2]); +#endif + + if (selection) + { + swap_frame_buffer(p_Vid, 0, rd_pass); + store_coding_and_rc_info(p_Vid, &coding_info); + best_method = IMP_WP; + apply_wp = IMP_WP; + } + + rd_pass++; + if(rd_pass >= p_Inp->RDPictureMaxPassBSlice) + { + frame_picture_mp_exit(p_Vid, &coding_info); + free_slice(dummy_slice); + return; + } + } + + // for B_Slice, consider explicit WP + wp_pass = 0; + if (p_Inp->GenerateMultiplePPS) + { + InitWP(p_Vid, p_Inp, 0); + if( p_Inp->WPMCPrecision ) + p_Vid->pWPX->curr_wp_rd_pass = p_Vid->pWPX->wp_rd_passes + 1; + + if (p_Vid->TestWPBSlice(dummy_slice, 0) == 1) + { + // regular WP pass + p_Vid->active_pps = p_Vid->PicParSet[1]; + if( p_Inp->WPMCPrecision ) + p_Vid->pWPX->curr_wp_rd_pass->algorithm = WP_REGULAR; + wp_pass = 1; + } + else if ( p_Inp->WPMCPrecision == 2 && (p_Inp->WPMCPrecBSlice == 2 || (p_Inp->WPMCPrecBSlice == 1 && p_Vid->nal_reference_idc) ) ) + { + p_Vid->active_pps = p_Vid->PicParSet[1]; + wp_pass = 1; + } + } + + if(wp_pass) + { + p_Vid->write_macroblock = FALSE; + p_Vid->p_curr_frm_struct->qp = p_Vid->qp; + frame_picture (p_Vid, p_Vid->frame_pic[rd_pass], &p_Vid->imgData, rd_pass); + selection = picture_coding_decision(p_Vid, p_Vid->frame_pic[0], p_Vid->frame_pic[rd_pass], rd_qp); +#if (DBG_IMAGE_MP) + printf("EXP WP, rd_pass = %d, selection = %d\n", rd_pass, selection); + printf("rd_pass = %d: %d (%.0f, %.0f, %.0f)\n", rd_pass, + p_Vid->frame_pic[rd_pass]->bits_per_picture, + p_Vid->frame_pic[rd_pass]->distortion.value[0], p_Vid->frame_pic[rd_pass]->distortion.value[1], p_Vid->frame_pic[rd_pass]->distortion.value[2]); +#endif + + if (selection) + { + swap_frame_buffer(p_Vid, 0, rd_pass); + store_coding_and_rc_info(p_Vid, &coding_info); + best_method = EXP_WP; + apply_wp = EXP_WP; + } + + rd_pass++; + if(rd_pass >= p_Inp->RDPictureMaxPassBSlice) + { + frame_picture_mp_exit(p_Vid, &coding_info); + free_slice(dummy_slice); + return; + } + } + } + + if(p_Inp->RDPictureFrameQPBSlice) + { + // frame QP pass + p_Vid->active_pps = best_method == EXP_WP ? p_Vid->PicParSet[1]:best_method==IMP_WP?p_Vid->PicParSet[2]:p_Vid->PicParSet[0]; + //p_Vid->qp = (p_Vid->nal_reference_idc==0 ? rd_qp + 1 : rd_qp - 1); + p_Vid->qp = (p_Vid->nal_reference_idc==0 ? rd_qp + 1 : rd_qp ); + p_Vid->qp = iClip3( p_Vid->RCMinQP, p_Vid->RCMaxQP, p_Vid->qp ); + if ( p_Inp->RCEnable ) + { + rateRatio = p_Vid->nal_reference_idc ? 1.15F : 0.85F; + rc_init_frame_rdpic( p_Vid, p_Inp, rateRatio ); + } + + p_Vid->write_macroblock = FALSE; + p_Vid->p_curr_frm_struct->qp = p_Vid->qp; + frame_picture (p_Vid, p_Vid->frame_pic[rd_pass], &p_Vid->imgData, rd_pass); + selection = picture_coding_decision(p_Vid, p_Vid->frame_pic[0], p_Vid->frame_pic[rd_pass], rd_qp); +#if (DBG_IMAGE_MP) + printf("frame QP, rd_pass = %d, selection = %d \n", rd_pass, selection); + printf("rd_pass = %d: %d (%.0f, %.0f, %.0f)\n", rd_pass, + p_Vid->frame_pic[rd_pass]->bits_per_picture, + p_Vid->frame_pic[rd_pass]->distortion.value[0], p_Vid->frame_pic[rd_pass]->distortion.value[1], p_Vid->frame_pic[rd_pass]->distortion.value[2]); +#endif + + if (selection) + { + swap_frame_buffer(p_Vid, 0, rd_pass); + store_coding_and_rc_info(p_Vid, &coding_info); + best_method = FRAME_QP; + } + + rd_pass++; + if(rd_pass >= p_Inp->RDPictureMaxPassBSlice) + { + frame_picture_mp_exit(p_Vid, &coding_info); + free_slice(dummy_slice); + return; + } + } + + // alternative direct mode pass + if(p_Inp->RDPictureDirectMode) + { + // consider using the best coding "method" found so far + p_Vid->active_pps = apply_wp == IMP_WP ? p_Vid->PicParSet[1]:apply_wp==EXP_WP?p_Vid->PicParSet[2]:p_Vid->PicParSet[0]; + if(best_method == FRAME_QP) + p_Vid->qp = (p_Vid->nal_reference_idc==0 ? rd_qp+1:rd_qp-1); + else + p_Vid->qp = rd_qp; + p_Vid->qp = iClip3( p_Vid->RCMinQP, p_Vid->RCMaxQP, p_Vid->qp ); + // flip direct mode type + p_Vid->direct_spatial_mv_pred_flag = 1-p_Vid->direct_spatial_mv_pred_flag; + p_Vid->write_macroblock = FALSE; + p_Vid->p_curr_frm_struct->qp = p_Vid->qp; + frame_picture (p_Vid, p_Vid->frame_pic[rd_pass], &p_Vid->imgData, rd_pass); + selection = picture_coding_decision(p_Vid, p_Vid->frame_pic[0], p_Vid->frame_pic[rd_pass], rd_qp); +#if (DBG_IMAGE_MP) + printf("alternate direct mode, rd_pass = %d, selection = %d\n", rd_pass, selection); +#endif + + if (selection) + { + swap_frame_buffer(p_Vid, 0, rd_pass); + store_coding_and_rc_info(p_Vid, &coding_info); + best_method = ALT_DIRECT; + } + + rd_pass++; + if(rd_pass >= p_Inp->RDPictureMaxPassBSlice) + { + frame_picture_mp_exit(p_Vid, &coding_info); + free_slice(dummy_slice); + return; + } + } + frame_picture_mp_exit(p_Vid, &coding_info); + free_slice(dummy_slice); + return; +} + +void frame_picture_mp(VideoParameters *p_Vid, InputParameters *p_Inp) +{ + p_Vid->EvaluateDBOff = 0; + p_Vid->TurnDBOff = 0; + if(p_Vid->type == I_SLICE) + { + frame_picture_mp_i_slice(p_Vid, p_Inp); + } + else if(p_Vid->type == P_SLICE) + { + frame_picture_mp_p_slice(p_Vid, p_Inp); + } + else if(p_Vid->type == B_SLICE) + { + frame_picture_mp_b_slice(p_Vid, p_Inp); + } + p_Vid->rd_pass = 0; +} + diff --git a/lencod/src/img_chroma.c b/lencod/src/img_chroma.c new file mode 100644 index 0000000..497347c --- /dev/null +++ b/lencod/src/img_chroma.c @@ -0,0 +1,437 @@ + +/*! +************************************************************************************* +* \file img_chroma.c +* +* \brief +* Chroma interpolation functions +* +* \author +* Main contributors (see contributors.h for copyright, address and affiliation details) +* - Athanasios Leontaris +* - Alexis Michael Tourapis +* +************************************************************************************* +*/ + +#include "contributors.h" + +#include + +#include "global.h" +#include "image.h" +#include "img_chroma.h" + + +static void generateChroma00( VideoParameters *p_Vid, int size_x_minus1, int size_y_minus1, imgpel **wImgDst, imgpel **imgUV) +{ + int i;//, j; + int jpad = -p_Vid->pad_size_uv_y; + imgpel *wBufDst; + imgpel *wBufSrc0; + + wBufDst = wImgDst[jpad++]-p_Vid->pad_size_uv_x; + wBufSrc0 = imgUV[0]; + + for (i = -p_Vid->pad_size_uv_x; i < 0; i++) + { + *wBufDst++ = *wBufSrc0; + } + + memcpy(wBufDst, wBufSrc0, size_x_minus1 * sizeof(imgpel)); + wBufDst += size_x_minus1; + wBufSrc0 += size_x_minus1; + + for (i = -1; i < p_Vid->pad_size_uv_x; i++) + { + *wBufDst++ = *wBufSrc0; + } + + for(; jpad < 0; jpad++) + { + memcpy(wImgDst[jpad]-p_Vid->pad_size_uv_x, wImgDst[jpad - 1]-p_Vid->pad_size_uv_x, (2 * p_Vid->pad_size_uv_x + size_x_minus1+1) * sizeof(imgpel)); + } + + for (jpad = 0; jpad < size_y_minus1+1; jpad++) + { + wBufDst = wImgDst[jpad]-p_Vid->pad_size_uv_x; + wBufSrc0 = imgUV[jpad]; + + for (i = -p_Vid->pad_size_uv_x; i < 0; i++) + { + *wBufDst++ = *wBufSrc0; + } + + //memcpy(wBufDst, wBufSrc0, size_x_minus1 * sizeof(imgpel)); + wBufDst += size_x_minus1+1; + wBufSrc0 += size_x_minus1; + + for (i = 0; i < p_Vid->pad_size_uv_x; i++) + { + *wBufDst++ = *wBufSrc0; + } + } + + for (jpad = size_y_minus1+1; jpad < size_y_minus1+1+p_Vid->pad_size_uv_y; jpad++) + { + memcpy(wImgDst[jpad]-p_Vid->pad_size_uv_x, wImgDst[jpad - 1]-p_Vid->pad_size_uv_x, (2 * p_Vid->pad_size_uv_x + size_x_minus1+1) * sizeof(imgpel)); + } +} + +/*! + ************************************************************************ + * \brief + * Horizontal chroma interpolation image generation routine + ************************************************************************ + */ +static void generateChroma01( VideoParameters *p_Vid, int size_x_minus1, int size_y_minus1, int weight00, int weight01, imgpel **wImgDst, imgpel **imgUV) +{ + int i;//, j; + int jpad = -p_Vid->pad_size_uv_y; + int cur_value; + imgpel *wBufDst; + imgpel *wBufSrc0; + + wBufDst = wImgDst[jpad++]-p_Vid->pad_size_uv_x; + wBufSrc0 = imgUV[0]; + + for (i = -p_Vid->pad_size_uv_x; i < 0; i++) + { + *(wBufDst++) = *wBufSrc0; + } + + for (i = 0; i < size_x_minus1; i++) + { + cur_value = weight00 * (*wBufSrc0++); + cur_value += weight01 * (*wBufSrc0 ); + *(wBufDst++) = (imgpel) rshift_rnd_sf(cur_value, 6); + } + + for (i = -1; i < p_Vid->pad_size_uv_x; i++) + { + *(wBufDst++) = *wBufSrc0; + } + + for (; jpad < 1; jpad++) + { + memcpy(wImgDst[jpad]-p_Vid->pad_size_uv_x, wImgDst[jpad - 1]-p_Vid->pad_size_uv_x, (2 * p_Vid->pad_size_uv_x + size_x_minus1+1) * sizeof(imgpel)); + } + + for (jpad = 1; jpad < size_y_minus1+1; jpad++) + { + wBufDst = wImgDst[jpad]-p_Vid->pad_size_uv_x; + wBufSrc0 = imgUV[jpad]; + + for (i = -p_Vid->pad_size_uv_x; i < 0; i++) + { + *(wBufDst++) = *wBufSrc0; + } + + for (i = 0; i < size_x_minus1; i++) + { + cur_value = weight00 * (*wBufSrc0++); + cur_value += weight01 * (*wBufSrc0 ); + *(wBufDst++) = (imgpel) rshift_rnd_sf(cur_value, 6); + } + + for (i = -1; i < p_Vid->pad_size_uv_x; i++) + { + *(wBufDst++) = *wBufSrc0; + } + } + + for (jpad = size_y_minus1+1; jpad < size_y_minus1+1+p_Vid->pad_size_uv_y; jpad++) + { + memcpy(wImgDst[jpad]-p_Vid->pad_size_uv_x, wImgDst[jpad - 1]-p_Vid->pad_size_uv_x, (2 * p_Vid->pad_size_uv_x + size_x_minus1+1) * sizeof(imgpel)); + } +} + +/*! + ************************************************************************ + * \brief + * Vertical chroma interpolation image generation routine + ************************************************************************ + */ +static void generateChroma10(VideoParameters *p_Vid, int size_x_minus1, int size_y_minus1, int weight00, int weight10, imgpel **wImgDst, imgpel **imgUV) +{ + int i;//, j; + int jpad = -p_Vid->pad_size_uv_y; + int cur_value; + imgpel *wBufDst; + imgpel *wBufSrc0, *wBufSrc1; + + wBufDst = wImgDst[jpad++]-p_Vid->pad_size_uv_x; + wBufSrc0 = imgUV[0]; + + + for (i = -p_Vid->pad_size_uv_x; i < 0; i++) + { + *(wBufDst++) = *wBufSrc0; + } + + memcpy(wBufDst, wBufSrc0, size_x_minus1 * sizeof(imgpel)); + wBufDst += size_x_minus1; + wBufSrc0 += size_x_minus1; + + for (i = -1; i < p_Vid->pad_size_uv_x; i++) + { + *(wBufDst++) = *wBufSrc0; + } + + for(;jpad < 0; jpad++) + { + memcpy(wImgDst[jpad]-p_Vid->pad_size_uv_x, wImgDst[jpad - 1]-p_Vid->pad_size_uv_x, (2 * p_Vid->pad_size_uv_x + size_x_minus1+1) * sizeof(imgpel)); + } + + for (jpad = 0; jpad < size_y_minus1; jpad++) + { + wBufDst = wImgDst[jpad]-p_Vid->pad_size_uv_x; + wBufSrc0 = imgUV[jpad]; + wBufSrc1 = imgUV[jpad+1]; + + cur_value = rshift_rnd_sf(weight00 * (*wBufSrc0) + weight10 * (*wBufSrc1), 6 ); + for (i = -p_Vid->pad_size_uv_x; i < 0; i++) + { + *(wBufDst++) = (imgpel) cur_value; + } + + for (i = 0; i < size_x_minus1; i++) + { + cur_value = weight00 * (*wBufSrc0++) + weight10 * (*wBufSrc1++); + *(wBufDst++) = (imgpel) rshift_rnd_sf(cur_value, 6); + } + + cur_value = rshift_rnd_sf(weight00 * (*wBufSrc0) + weight10 * (*wBufSrc1), 6 ); + for (i = -1; i < p_Vid->pad_size_uv_x; i++) + { + *(wBufDst++) = (imgpel) cur_value; + } + } + + wBufDst = wImgDst[jpad]-p_Vid->pad_size_uv_x; + wBufSrc0 = imgUV[size_y_minus1]; + + for (i = -p_Vid->pad_size_uv_x; i < 0; i++) + { + *(wBufDst++) = *wBufSrc0; + } + + memcpy(wBufDst, wBufSrc0, size_x_minus1 * sizeof(imgpel)); + wBufDst += size_x_minus1; + wBufSrc0 += size_x_minus1; + + for (i = -1; i < p_Vid->pad_size_uv_x; i++) + { + *(wBufDst++) = *wBufSrc0; + } + + for (jpad = size_y_minus1+1; jpad < size_y_minus1+1+p_Vid->pad_size_uv_y; jpad++) + { + memcpy(wImgDst[jpad]-p_Vid->pad_size_uv_x, wImgDst[jpad - 1]-p_Vid->pad_size_uv_x, (2 * p_Vid->pad_size_uv_x + size_x_minus1+1) * sizeof(imgpel)); + } +} + +/*! + ************************************************************************ + * \brief + * Generic/Diagonal chroma interpolation image generation routine + ************************************************************************ + */ +static void generateChromaXX( VideoParameters *p_Vid, int size_x_minus1, int size_y_minus1, int weight00, int weight01, int weight10, int weight11, imgpel **wImgDst, imgpel **imgUV) +{ + int i;//, j; + int jpad = -p_Vid->pad_size_uv_y; + int cur_value; + imgpel *wBufDst; + imgpel *wBufSrc0, *wBufSrc1; + int weight0001 = weight00 + weight01; + int weight1011 = weight10 + weight11; + int weight0010 = weight00 + weight10; + int weight0111 = weight01 + weight11; + + wBufDst = wImgDst[jpad++]-p_Vid->pad_size_uv_x; + wBufSrc0 = imgUV[0]; + + for (i = -p_Vid->pad_size_uv_x; i < 0; i++) + { + *(wBufDst++) = *wBufSrc0; + } + + for (i = 0; i < size_x_minus1; i++) + { + cur_value = weight0010 * (*wBufSrc0++); + cur_value += weight0111 * (*wBufSrc0 ); + *(wBufDst++) = (imgpel) rshift_rnd_sf(cur_value, 6); + } + + for (i = -1; i < p_Vid->pad_size_uv_x; i++) + { + *(wBufDst++) = *wBufSrc0; + } + + for (; jpad < 0; jpad++) + { + memcpy(wImgDst[jpad]-p_Vid->pad_size_uv_x, wImgDst[jpad - 1]-p_Vid->pad_size_uv_x, (2 * p_Vid->pad_size_uv_x + size_x_minus1+1) * sizeof(imgpel)); + } + + for (jpad = 0; jpad < size_y_minus1; jpad++) + { + wBufDst = wImgDst[jpad]-p_Vid->pad_size_uv_x; + wBufSrc0 = imgUV[jpad]; + wBufSrc1 = imgUV[jpad + 1]; + + cur_value = rshift_rnd_sf(weight0001 * (*wBufSrc0) + weight1011 * (*wBufSrc1), 6 ); + for (i = -p_Vid->pad_size_uv_x; i < 0; i++) + { + *(wBufDst++) = (imgpel) cur_value; + } + + for (i = 0; i < size_x_minus1; i++) + { + cur_value = weight00 * (*wBufSrc0++) + weight10 * (*wBufSrc1++); + cur_value += weight01 * (*wBufSrc0 ) + weight11 * (*wBufSrc1 ); + *(wBufDst++) = (imgpel) rshift_rnd_sf(cur_value, 6); + } + + cur_value = rshift_rnd_sf(weight0001 * (*wBufSrc0) + weight1011 * (*wBufSrc1), 6 ); + for (i = -1; i < p_Vid->pad_size_uv_x; i++) + { + *(wBufDst++) = (imgpel) cur_value; + } + } + + wBufDst = wImgDst[jpad]-p_Vid->pad_size_uv_x; + wBufSrc0 = imgUV[size_y_minus1]; + + for (i = -p_Vid->pad_size_uv_x; i < 0; i++) + { + *(wBufDst++) = *wBufSrc0; + } + + for (i = 0; i < size_x_minus1; i++) + { + cur_value = weight0010 * (*wBufSrc0++); + cur_value += weight0111 * (*wBufSrc0 ); + *(wBufDst++) = (imgpel) rshift_rnd_sf(cur_value, 6); + } + + for (i = -1; i < p_Vid->pad_size_uv_x; i++) + { + *(wBufDst++) = *wBufSrc0; + } + + for (jpad = size_y_minus1+1; jpad < size_y_minus1+1+p_Vid->pad_size_uv_y; jpad++) + { + memcpy(wImgDst[jpad]-p_Vid->pad_size_uv_x, wImgDst[jpad - 1]-p_Vid->pad_size_uv_x, (2 * p_Vid->pad_size_uv_x + size_x_minus1+1) * sizeof(imgpel)); + } +} + +/*! + ************************************************************************ + * \brief + * Creates the 16 (YUV444), 32 (YUV422), or 64 (YUV420) sub-images that + * contain quarter-pel samples sub-sampled at different + * spatial orientationss; + * enables more efficient implementation + ************************************************************************* + */ +void getSubImagesChroma( VideoParameters *p_Vid, StorablePicture *s ) +{ + int uv, k, l, m; + int weight00, weight01, weight10, weight11; + int subimages_y, subimages_x, subx, suby; + int size_x_minus1, size_y_minus1; + imgpel ****curr_img_sub; + imgpel **curr_img; + + // multiplier factor for index to account for UV sampling ratios + int mul_x, mul_y; + int mm, kk; + + size_x_minus1 = s->size_x_cr - 1; + size_y_minus1 = s->size_y_cr - 1; + + if( p_Vid->p_Inp->OnTheFlyFractMCP == OTF_L1 ) // JLT : on-the-fly mode + { + if ( p_Vid->yuv_format == YUV420 ) + { + subimages_x = 4; + subimages_y = 4; + mul_x = mul_y = 2; + } + else if ( p_Vid->yuv_format == YUV422 ) + { + subimages_x = 4; + subimages_y = 2; + mul_y = 4; + mul_x = 2; + } + else + { // YUV444 + subimages_x = 2; + subimages_y = 2; + mul_x = mul_y = 4; + } + } + else + { + if ( p_Vid->yuv_format == YUV420 ) + { + subimages_x = 8; + subimages_y = 8; + mul_x = mul_y = 1; + } + else if ( p_Vid->yuv_format == YUV422 ) + { + subimages_x = 8; + subimages_y = 4; + mul_y = 2; + mul_x = 1; + } + else + { // YUV444 + subimages_x = 4; + subimages_y = 4; + mul_x = mul_y = 2; + } + } + + // U or V + for ( uv = 0; uv < 2; uv++ ) + { + curr_img = s->imgUV[uv]; + curr_img_sub = s->p_img_sub[uv + 1]; + + for ( suby = 0, k = 0; suby < subimages_y; suby++, k += mul_y ) + { + m = (8 - k); + mm = m << 3; + kk = k << 3; + for ( subx = 0, l = 0; subx < subimages_x; subx++, l += mul_x ) + { + weight01 = m * l; + weight00 = mm - weight01; + weight11 = k * l; + weight10 = kk - weight11; + + // Lets break things into cases + if (weight01 == 0 && weight10 == 0 && weight11 == 0) // integer + { + generateChroma00( p_Vid, size_x_minus1, size_y_minus1, curr_img_sub[suby][subx], curr_img); + } + else if (weight10 == 0 && weight11 == 0) // horizontal + { + generateChroma01( p_Vid, size_x_minus1, size_y_minus1, weight00, weight01, curr_img_sub[suby][subx], curr_img); + } + else if (weight01 == 0 && weight11 == 0) // vertical + { + generateChroma10( p_Vid, size_x_minus1, size_y_minus1, weight00, weight10, curr_img_sub[suby][subx], curr_img); + } + else //diagonal + { + generateChromaXX( p_Vid, size_x_minus1, size_y_minus1, weight00, weight01, weight10, weight11, curr_img_sub[suby][subx], curr_img); + } + } + } + } +} diff --git a/lencod/src/img_dist_ms_ssim.c b/lencod/src/img_dist_ms_ssim.c new file mode 100644 index 0000000..d6c9f10 --- /dev/null +++ b/lencod/src/img_dist_ms_ssim.c @@ -0,0 +1,349 @@ + +/*! + ************************************************************************************* + * \file img_dist_ms_ssim.c + * + * \brief + * Compute structural similarity (SSIM) index using the encoded image and the reference image + * + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * - Woo-Shik Kim + * - Peshala Pahalawatta + * - Zhen Li + * - Alexis Michael Tourapis + ************************************************************************************* + */ +#include "contributors.h" +#include "global.h" +#include "img_distortion.h" +#include "enc_statistics.h" +#include "memalloc.h" +#include "math.h" + +#ifndef UNBIASED_VARIANCE + #define UNBIASED_VARIANCE // unbiased estimation of the variance +#endif + +#define MS_SSIM_PAD 6 +#define MS_SSIM_PAD2 3 + +#define MS_SSIM_BETA0 0.0448 +#define MS_SSIM_BETA1 0.2856 +#define MS_SSIM_BETA2 0.3001 +#define MS_SSIM_BETA3 0.2363 +#define MS_SSIM_BETA4 0.1333 + +#define MAX_SSIM_LEVELS 5 + +//Computes the product of the contrast and structure componenents of the structural similarity metric. +float compute_structural_components (VideoParameters *p_Vid, InputParameters *p_Inp, imgpel **refImg, imgpel **encImg, int height, int width, int win_height, int win_width, int comp) +{ + static const float K2 = 0.03f; + float max_pix_value_sqd; + float C2; + float win_pixels = (float) (win_width * win_height); +#ifdef UNBIASED_VARIANCE + float win_pixels_bias = win_pixels - 1; +#else + float win_pixels_bias = win_pixels; +#endif + float mb_ssim, meanOrg, meanEnc; + float varOrg, varEnc, covOrgEnc; + int imeanOrg, imeanEnc, ivarOrg, ivarEnc, icovOrgEnc; + float cur_distortion = 0.0; + int i, j, n, m, win_cnt = 0; + int overlapSize = p_Inp->SSIMOverlapSize; + + max_pix_value_sqd = (float) (p_Vid->max_pel_value_comp[comp] * p_Vid->max_pel_value_comp[comp]); + C2 = K2 * K2 * max_pix_value_sqd; + + for (j = 0; j <= height - win_height; j += overlapSize) + { + for (i = 0; i <= width - win_width; i += overlapSize) + { + imeanOrg = 0; + imeanEnc = 0; + ivarOrg = 0; + ivarEnc = 0; + icovOrgEnc = 0; + + for ( n = j;n < j + win_height;n ++) + { + for (m = i;m < i + win_width;m ++) + { + imeanOrg += refImg[n][m]; + imeanEnc += encImg[n][m]; + ivarOrg += refImg[n][m] * refImg[n][m]; + ivarEnc += encImg[n][m] * encImg[n][m]; + icovOrgEnc += refImg[n][m] * encImg[n][m]; + } + } + + meanOrg = (float) imeanOrg / win_pixels; + meanEnc = (float) imeanEnc / win_pixels; + + varOrg = ((float) ivarOrg - ((float) imeanOrg) * meanOrg) / win_pixels_bias; + varEnc = ((float) ivarEnc - ((float) imeanEnc) * meanEnc) / win_pixels_bias; + covOrgEnc = ((float) icovOrgEnc - ((float) imeanOrg) * meanEnc) / win_pixels_bias; + + mb_ssim = (float) (2.0 * covOrgEnc + C2); + mb_ssim /= (float) (varOrg + varEnc + C2); + + cur_distortion += mb_ssim; + win_cnt++; + } + } + + cur_distortion /= (float) win_cnt; + + if (cur_distortion >= 1.0 && cur_distortion < 1.01) // avoid float accuracy problem at very low QP(e.g.2) + cur_distortion = 1.0; + + return cur_distortion; +} + +float compute_luminance_component (VideoParameters *p_Vid, InputParameters *p_Inp, imgpel **refImg, imgpel **encImg, int height, int width, int win_height, int win_width, int comp) +{ + static const float K1 = 0.01f; + float max_pix_value_sqd; + float C1; + float win_pixels = (float) (win_width * win_height); + float mb_ssim, meanOrg, meanEnc; + int imeanOrg, imeanEnc; + float cur_distortion = 0.0; + int i, j, n, m, win_cnt = 0; + int overlapSize = p_Inp->SSIMOverlapSize; + + max_pix_value_sqd = (float) (p_Vid->max_pel_value_comp[comp] * p_Vid->max_pel_value_comp[comp]); + C1 = K1 * K1 * max_pix_value_sqd; + + for (j = 0; j <= height - win_height; j += overlapSize) + { + for (i = 0; i <= width - win_width; i += overlapSize) + { + imeanOrg = 0; + imeanEnc = 0; + + for ( n = j;n < j + win_height;n ++) + { + for (m = i;m < i + win_width;m ++) + { + imeanOrg += refImg[n][m]; + imeanEnc += encImg[n][m]; + } + } + + meanOrg = (float) imeanOrg / win_pixels; + meanEnc = (float) imeanEnc / win_pixels; + + mb_ssim = (float) (2.0 * meanOrg * meanEnc + C1); + mb_ssim /= (float) (meanOrg * meanOrg + meanEnc * meanEnc + C1); + + cur_distortion += mb_ssim; + win_cnt++; + } + } + + cur_distortion /= (float) win_cnt; + + if (cur_distortion >= 1.0 && cur_distortion < 1.01) // avoid float accuracy problem at very low QP(e.g.2) + cur_distortion = 1.0; + + return cur_distortion; +} + +void horizontal_symmetric_extension(int **buffer, int width, int height ) +{ + int j; + int* buf; + + int height_plus_pad2 = height + MS_SSIM_PAD2; + int width_plus_pad2_minus_one = width + MS_SSIM_PAD2 - 1; + + // horizontal PSE + for (j = MS_SSIM_PAD2; j < height_plus_pad2; j++ ) { + // left column + buf = &buffer[j][MS_SSIM_PAD2]; + buf[-1] = buf[1]; + buf[-2] = buf[2]; + + // right column + buf = &buffer[j][width_plus_pad2_minus_one]; + buf[1] = buf[-1]; + buf[2] = buf[-2]; + buf[3] = buf[-3]; + } +} + +void vertical_symmetric_extension(int **buffer, int width, int height) +{ + int i; + + int *bufminus1 = &buffer[MS_SSIM_PAD2-1][MS_SSIM_PAD2]; + int *bufminus2 = &buffer[MS_SSIM_PAD2-2][MS_SSIM_PAD2]; + int *bufplus1 = &buffer[MS_SSIM_PAD2+1][MS_SSIM_PAD2]; + int *bufplus2 = &buffer[MS_SSIM_PAD2+2][MS_SSIM_PAD2]; + + int *bufhminus1 = &buffer[height + MS_SSIM_PAD2-2][MS_SSIM_PAD2]; + int *bufhminus2 = &buffer[height + MS_SSIM_PAD2-3][MS_SSIM_PAD2]; + int *bufhminus3 = &buffer[height + MS_SSIM_PAD2-4][MS_SSIM_PAD2]; + int *bufhplus1 = &buffer[height + MS_SSIM_PAD2][MS_SSIM_PAD2]; + int *bufhplus2 = &buffer[height + MS_SSIM_PAD2+1][MS_SSIM_PAD2]; + int *bufhplus3 = &buffer[height + MS_SSIM_PAD2+2][MS_SSIM_PAD2]; + + for (i = 0; i < width; i++) + { + //Top Rows + bufminus1[i] = bufplus1[i]; + bufminus2[i] = bufplus2[i]; + //Bottom Rows + bufhplus1[i] = bufhminus1[i]; + bufhplus2[i] = bufhminus2[i]; + bufhplus3[i] = bufhminus3[i]; + } +} + +static void imgpel_to_padded_int(imgpel** src, int **buffer, int width, int height) +{ + int i, j; + int* tmpDst; + imgpel* tmpSrc; + + tmpDst = buffer[MS_SSIM_PAD2]; + for (j = 0; j < height; j++) + { + tmpSrc = src[j]; + tmpDst = &buffer[j + MS_SSIM_PAD2][MS_SSIM_PAD2]; + for (i = 0; i < width; i++) + { + tmpDst[i] = (int)tmpSrc[i]; + } + } +} + +void downsample(imgpel** src, imgpel** out, int height, int width) +{ + int height2 = height >> 1; + int width2 = width >> 1; + int i, j; + int ii, jj; + int iDst; + int tmp, tmp1, tmp2; + int* tmpDst; + int* tmpSrc; + + int** itemp; + int** dest; + + get_mem2Dint(&itemp, height + MS_SSIM_PAD, width + MS_SSIM_PAD); + get_mem2Dint(&dest, height + MS_SSIM_PAD, width2 + MS_SSIM_PAD); + + imgpel_to_padded_int(src, itemp, width, height); + horizontal_symmetric_extension(itemp, width, height); + + for (j = 0; j < height; j++) + { + tmpDst = dest[j+MS_SSIM_PAD2]; + tmpSrc = itemp[j+MS_SSIM_PAD2]; + iDst = MS_SSIM_PAD2; + for (i = 0; i < width2; i++, iDst++) + { + ii = (i << 1) + MS_SSIM_PAD2; + tmp1 = tmpSrc[ii-1] + tmpSrc[ii+2]; + tmp2 = tmpSrc[ii] + tmpSrc[ii+1]; + tmpDst[iDst] = tmpSrc[ii-2] + tmpSrc[ii+3] + (tmp1 << 1) + tmp1 + (tmp2 << 5) - (tmp2 << 2); + tmpDst[iDst] >>= 6; + } + } + + //Periodic extension + vertical_symmetric_extension(dest, width2, height); + + for (i = 0; i < width2; i++) + { + ii = i + MS_SSIM_PAD2; + for (j = 0; j < height2; j++) + { + jj = (j << 1) + MS_SSIM_PAD2; + tmp1 = dest[jj-1][ii] + dest[jj+2][ii]; + tmp2 = dest[jj][ii] + dest[jj+1][ii]; + tmp = dest[jj-2][ii] + dest[jj+3][ii] + (tmp1 << 1) + tmp1 + (tmp2 << 5) - (tmp2 << 2); + out[j][i] = (unsigned char) (tmp >> 6); //Note: Should change for different bit depths + } + } + free_mem2Dint(itemp); + free_mem2Dint(dest); +} + +float compute_ms_ssim(VideoParameters *p_Vid, InputParameters *p_Inp, imgpel **refImg, imgpel **encImg, int height, int width, int win_height, int win_width, int comp) +{ + float structural[MAX_SSIM_LEVELS]; + float cur_distortion; + float luminance; + imgpel** dsRef; + imgpel** dsEnc; + int m; + static const int max_ssim_levels_minus_one = MAX_SSIM_LEVELS - 1; + static const float exponent[5] = {(float)MS_SSIM_BETA0, (float)MS_SSIM_BETA1, (float)MS_SSIM_BETA2, (float)MS_SSIM_BETA3, (float)MS_SSIM_BETA4}; + + dsRef = NULL; + dsEnc = NULL; + get_mem2Dpel(&dsRef, height>>1, width>>1); + get_mem2Dpel(&dsEnc, height>>1, width>>1); + + structural[0] = compute_structural_components(p_Vid, p_Inp, refImg, encImg, height, width, win_height, win_width, comp); + cur_distortion = (float)pow(structural[0], exponent[0]); + + downsample(refImg, dsRef, height, width); + downsample(encImg, dsEnc, height, width); + + for (m = 1; m < MAX_SSIM_LEVELS; m++) + { + height >>= 1; + width >>= 1; + structural[m] = compute_structural_components(p_Vid, p_Inp, dsRef, dsEnc, height, width, imin(win_height,height), imin(win_width,width), comp); + cur_distortion *= (float)pow(structural[m], exponent[m]); + if (m < max_ssim_levels_minus_one) + { + downsample(dsRef, dsRef, height, width); + downsample(dsEnc, dsEnc, height, width); + } + else + { + luminance = compute_luminance_component(p_Vid, p_Inp, dsRef, dsEnc, height, width, imin(win_height,height), imin(win_width,width), comp); + cur_distortion *= (float)pow(luminance, exponent[m]); + } + } + free_mem2Dpel(dsRef); + free_mem2Dpel(dsEnc); + dsRef = NULL; + dsEnc = NULL; + + return cur_distortion; +} + +/*! + ************************************************************************ + * \brief + * Find MS-SSIM for all three components + ************************************************************************ + */ +void find_ms_ssim (VideoParameters *p_Vid, InputParameters *p_Inp, ImageStructure *ref, ImageStructure *src, DistMetric *metricSSIM) +{ + DistortionParams *p_Dist = p_Vid->p_Dist; + FrameFormat *format = &ref->format; + + metricSSIM->value[0] = compute_ms_ssim (p_Vid, p_Inp, ref->data[0], src->data[0], format->height[0], format->width[0], BLOCK_SIZE_8x8, BLOCK_SIZE_8x8, 0); + // Chroma. + if (format->yuv_format != YUV400) + { + metricSSIM->value[1] = compute_ms_ssim (p_Vid, p_Inp, ref->data[1], src->data[1], format->height[1], format->width[1], p_Vid->mb_cr_size_y, p_Vid->mb_cr_size_x, 1); + metricSSIM->value[2] = compute_ms_ssim (p_Vid, p_Inp, ref->data[2], src->data[2], format->height[1], format->width[1], p_Vid->mb_cr_size_y, p_Vid->mb_cr_size_x, 2); + } + + { + accumulate_average(metricSSIM, p_Dist->frame_ctr); + accumulate_avslice(metricSSIM, p_Vid->type, p_Vid->p_Stats->frame_ctr[p_Vid->type]); + } +} diff --git a/lencod/src/img_dist_snr.c b/lencod/src/img_dist_snr.c new file mode 100644 index 0000000..c5292fc --- /dev/null +++ b/lencod/src/img_dist_snr.c @@ -0,0 +1,76 @@ + +/*! + ************************************************************************************* + * \file img_dist_snr.c + * + * \brief + * Compute signal to noise ratio (SNR) between the encoded image and the reference image + * + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * - Woo-Shik Kim + * - Alexis Michael Tourapis + ************************************************************************************* + */ +#include "contributors.h" + +#include + +#include "enc_statistics.h" +#include "global.h" +#include "img_distortion.h" +#include "md_distortion.h" + +/*! + ************************************************************************ + * \brief + * Find SNR for all three components + ************************************************************************ + */ +void find_snr(VideoParameters *p_Vid, ImageStructure *imgREF, ImageStructure *imgSRC, DistMetric *metricSSE, DistMetric *metricPSNR) +{ + DistortionParams *p_Dist = p_Vid->p_Dist; + FrameFormat *format = &imgREF->format; + // Luma. + metricSSE ->value[0] = (float) compute_SSE(imgREF->data[0], imgSRC->data[0], 0, 0, format->height[0], format->width[0]); + metricPSNR->value[0] = psnr(format->max_value_sq[0], format->size_cmp[0], metricSSE->value[0]); + // Chroma. + if (format->yuv_format != YUV400) + { + metricSSE ->value[1] = (float) compute_SSE(imgREF->data[1], imgSRC->data[1], 0, 0, format->height[1], format->width[1]); + metricPSNR->value[1] = psnr(format->max_value_sq[1], format->size_cmp[1], metricSSE->value[1]); + metricSSE ->value[2] = (float) compute_SSE(imgREF->data[2], imgSRC->data[2], 0, 0, format->height[1], format->width[1]); + metricPSNR->value[2] = psnr(format->max_value_sq[2], format->size_cmp[2], metricSSE->value[2]); + } +#if (MVC_EXTENSION_ENABLE) + if (p_Vid->p_Inp->num_of_views == 2) + { + // Luma + p_Vid->p_Dist->metric_v[p_Vid->view_id][SSE].value[0] = metricSSE ->value[0]; + p_Vid->p_Dist->metric_v[p_Vid->view_id][PSNR].value[0] = metricPSNR->value[0]; + // Chroma + if (format->yuv_format != YUV400) + { + p_Vid->p_Dist->metric_v[p_Vid->view_id][SSE].value[1] = metricSSE ->value[1]; + p_Vid->p_Dist->metric_v[p_Vid->view_id][PSNR].value[1] = metricPSNR->value[1]; + p_Vid->p_Dist->metric_v[p_Vid->view_id][SSE].value[2] = metricSSE ->value[2]; + p_Vid->p_Dist->metric_v[p_Vid->view_id][PSNR].value[2] = metricPSNR->value[2]; + } + } +#endif + + { + accumulate_average(metricSSE, p_Dist->frame_ctr); + accumulate_average(metricPSNR, p_Dist->frame_ctr); + + accumulate_avslice(metricSSE, p_Vid->type, p_Vid->p_Stats->frame_ctr[p_Vid->type]); + accumulate_avslice(metricPSNR, p_Vid->type, p_Vid->p_Stats->frame_ctr[p_Vid->type]); +#if (MVC_EXTENSION_ENABLE) + if (p_Vid->p_Inp->num_of_views == 2) + { + accumulate_average(&p_Dist->metric_v[p_Vid->view_id][SSE], p_Dist->frame_ctr_v[p_Vid->view_id]); + accumulate_average(&p_Dist->metric_v[p_Vid->view_id][PSNR], p_Dist->frame_ctr_v[p_Vid->view_id]); + } +#endif + } +} diff --git a/lencod/src/img_dist_ssim.c b/lencod/src/img_dist_ssim.c new file mode 100644 index 0000000..f2477f2 --- /dev/null +++ b/lencod/src/img_dist_ssim.c @@ -0,0 +1,115 @@ + +/*! + ************************************************************************************* + * \file img_dist_ssim.c + * + * \brief + * Compute structural similarity (SSIM) index using the encoded image and the reference image + * + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * - Woo-Shik Kim + * - Zhen Li + * - Alexis Michael Tourapis + ************************************************************************************* + */ +#include "contributors.h" +#include "global.h" +#include "img_distortion.h" +#include "enc_statistics.h" + +//#define UNBIASED_VARIANCE // unbiased estimation of the variance + +float compute_ssim (VideoParameters *p_Vid, InputParameters *p_Inp, imgpel **refImg, imgpel **encImg, int height, int width, int win_height, int win_width, int comp) +{ + + static const float K1 = 0.01f, K2 = 0.03f; + float max_pix_value_sqd; + float C1, C2; + float win_pixels = (float) (win_width * win_height); +#ifdef UNBIASED_VARIANCE + float win_pixels_bias = win_pixels - 1; +#else + float win_pixels_bias = win_pixels; +#endif + float mb_ssim, meanOrg, meanEnc; + float varOrg, varEnc, covOrgEnc; + int imeanOrg, imeanEnc, ivarOrg, ivarEnc, icovOrgEnc; + float cur_distortion = 0.0; + int i, j, n, m, win_cnt = 0; + int overlapSize = p_Inp->SSIMOverlapSize; + + max_pix_value_sqd = (float) (p_Vid->max_pel_value_comp[comp] * p_Vid->max_pel_value_comp[comp]); + C1 = K1 * K1 * max_pix_value_sqd; + C2 = K2 * K2 * max_pix_value_sqd; + + for (j = 0; j <= height - win_height; j += overlapSize) + { + for (i = 0; i <= width - win_width; i += overlapSize) + { + imeanOrg = 0; + imeanEnc = 0; + ivarOrg = 0; + ivarEnc = 0; + icovOrgEnc = 0; + + for ( n = j;n < j + win_height;n ++) + { + for (m = i;m < i + win_width;m ++) + { + imeanOrg += refImg[n][m]; + imeanEnc += encImg[n][m]; + ivarOrg += refImg[n][m] * refImg[n][m]; + ivarEnc += encImg[n][m] * encImg[n][m]; + icovOrgEnc += refImg[n][m] * encImg[n][m]; + } + } + + meanOrg = (float) imeanOrg / win_pixels; + meanEnc = (float) imeanEnc / win_pixels; + + varOrg = ((float) ivarOrg - ((float) imeanOrg) * meanOrg) / win_pixels_bias; + varEnc = ((float) ivarEnc - ((float) imeanEnc) * meanEnc) / win_pixels_bias; + covOrgEnc = ((float) icovOrgEnc - ((float) imeanOrg) * meanEnc) / win_pixels_bias; + + mb_ssim = (float) ((2.0 * meanOrg * meanEnc + C1) * (2.0 * covOrgEnc + C2)); + mb_ssim /= (float) (meanOrg * meanOrg + meanEnc * meanEnc + C1) * (varOrg + varEnc + C2); + + cur_distortion += mb_ssim; + win_cnt++; + } + } + + cur_distortion /= (float) win_cnt; + + if (cur_distortion >= 1.0 && cur_distortion < 1.01) // avoid float accuracy problem at very low QP(e.g.2) + cur_distortion = 1.0; + + return cur_distortion; +} + +/*! + ************************************************************************ + * \brief + * Find SSIM for all three components + ************************************************************************ + */ +void find_ssim (VideoParameters *p_Vid, InputParameters *p_Inp, ImageStructure *ref, ImageStructure *src, DistMetric *metricSSIM) +{ + DistortionParams *p_Dist = p_Vid->p_Dist; + FrameFormat *format = &ref->format; + + metricSSIM->value[0] = compute_ssim (p_Vid, p_Inp, ref->data[0], src->data[0], format->height[0], format->width[0], BLOCK_SIZE_8x8, BLOCK_SIZE_8x8, 0); + // Chroma. + if (format->yuv_format != YUV400) + { + metricSSIM->value[1] = compute_ssim (p_Vid, p_Inp, ref->data[1], src->data[1], format->height[1], format->width[1], p_Vid->mb_cr_size_y, p_Vid->mb_cr_size_x, 1); + metricSSIM->value[2] = compute_ssim (p_Vid, p_Inp, ref->data[2], src->data[2], format->height[1], format->width[1], p_Vid->mb_cr_size_y, p_Vid->mb_cr_size_x, 2); + } + + { + accumulate_average(metricSSIM, p_Dist->frame_ctr); + accumulate_avslice(metricSSIM, p_Vid->type, p_Vid->p_Stats->frame_ctr[p_Vid->type]); + } +} + diff --git a/lencod/src/img_distortion.c b/lencod/src/img_distortion.c new file mode 100644 index 0000000..138eeed --- /dev/null +++ b/lencod/src/img_distortion.c @@ -0,0 +1,211 @@ + +/*! + ************************************************************************************* + * \file img_distortion.c + * + * \brief + * Compute distortion for encoded image + * + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * - Woo-Shik Kim + * - Alexis Michael Tourapis + ************************************************************************************* + */ + +#include +#include + +#include "global.h" + +#include "refbuf.h" +#include "mbuffer.h" +#include "img_luma.h" +#include "img_chroma.h" +#include "intrarefresh.h" +#include "fmo.h" +#include "sei.h" +#include "memalloc.h" +#include "nalu.h" +#include "ratectl.h" +#include "mb_access.h" +#include "md_distortion.h" +#include "output.h" +#include "context_ini.h" +#include "conformance.h" +#include "enc_statistics.h" + +#include "q_matrix.h" +#include "q_offsets.h" +#include "quant4x4.h" +#include "quant8x8.h" +#include "wp.h" +#include "input.h" +#include "image.h" +#include "img_distortion.h" +#include "img_dist_snr.h" +#include "img_dist_ssim.h" +#include "img_dist_ms_ssim.h" +#include "cconv_yuv2rgb.h" + + +/*! + ************************************************************************ + * \brief + * Metric accumulator + ************************************************************************ + */ +void accumulate_metric(float *ave_metric, float cur_metric, int frames) +{ + *ave_metric = (float) (*ave_metric * (frames - 1) + cur_metric) / frames; +} + +/*! + ************************************************************************ + * \brief + * Accumulate distortion for all components + ************************************************************************ + */ +void accumulate_average(DistMetric *metric, int frames) +{ + accumulate_metric(&metric->average[0], metric->value[0], frames); + accumulate_metric(&metric->average[1], metric->value[1], frames); + accumulate_metric(&metric->average[2], metric->value[2], frames); +} + +/*! + ************************************************************************ + * \brief + * Accumulate distortion for all components for slice_type + ************************************************************************ + */ +void accumulate_avslice(DistMetric *metric, int slice_type, int frames) +{ + accumulate_metric(&metric->avslice[slice_type][0], metric->value[0], frames); + accumulate_metric(&metric->avslice[slice_type][1], metric->value[1], frames); + accumulate_metric(&metric->avslice[slice_type][2], metric->value[2], frames); +} + +/*! + ************************************************************************ + * \brief + * Find distortion for all three components + ************************************************************************ + */ +void find_distortion (VideoParameters *p_Vid, ImageData *imgData) +{ + InputParameters *p_Inp = p_Vid->p_Inp; + DistortionParams *p_Dist = p_Vid->p_Dist; + int64 diff_cmp[3] = {0}; + + // Calculate SSE for Y, U and V. + if (p_Vid->structure!=FRAME) + { + // Luma. + diff_cmp[0] += compute_SSE(p_Vid->pCurImg, p_Vid->imgY_com, 0, 0, p_Inp->output.height[0], p_Inp->output.width[0]); + + // Chroma. + if (p_Vid->yuv_format != YUV400) + { + diff_cmp[1] += compute_SSE(p_Vid->pImgOrg[1], p_Vid->imgUV_com[0], 0, 0, p_Inp->output.height[1], p_Inp->output.width[1]); + diff_cmp[2] += compute_SSE(p_Vid->pImgOrg[2], p_Vid->imgUV_com[1], 0, 0, p_Inp->output.height[1], p_Inp->output.width[1]); + } + } + else + { + if( (p_Inp->separate_colour_plane_flag != 0) ) + { + p_Vid->enc_picture = p_Vid->enc_frame_picture[0]; + } + p_Vid->pCurImg = imgData->frm_data[0]; + p_Vid->pImgOrg[0] = imgData->frm_data[0]; + + // Luma. + diff_cmp[0] += compute_SSE(p_Vid->pImgOrg[0], p_Vid->enc_picture->imgY, 0, 0, p_Inp->output.height[0], p_Inp->output.width[0]); + + // Chroma. + if (p_Vid->yuv_format != YUV400) + { + p_Vid->pImgOrg[1] = imgData->frm_data[1]; + p_Vid->pImgOrg[2] = imgData->frm_data[2]; + + diff_cmp[1] += compute_SSE(p_Vid->pImgOrg[1], p_Vid->enc_picture->imgUV[0], 0, 0, p_Inp->output.height[1], p_Inp->output.width[1]); + diff_cmp[2] += compute_SSE(p_Vid->pImgOrg[2], p_Vid->enc_picture->imgUV[1], 0, 0, p_Inp->output.height[1], p_Inp->output.width[1]); + } + } + + // This should be assigned to the SSE structure. Should double check code. + p_Dist->metric[SSE].value[0] = (float) diff_cmp[0]; + p_Dist->metric[SSE].value[1] = (float) diff_cmp[1]; + p_Dist->metric[SSE].value[2] = (float) diff_cmp[2]; +} + +void select_img(VideoParameters *p_Vid, ImageStructure *imgSRC, ImageStructure *imgREF, ImageData *imgData) +{ + InputParameters *p_Inp = p_Vid->p_Inp; + if (p_Vid->fld_flag != FALSE) + { + imgSRC->format = p_Inp->output; + imgREF->format = p_Inp->output; + + imgREF->data[0] = p_Vid->pCurImg; + imgSRC->data[0] = p_Vid->imgY_com; + + if (p_Vid->yuv_format != YUV400) + { + imgREF->data[1] = p_Vid->pImgOrg[1]; + imgREF->data[2] = p_Vid->pImgOrg[2]; + imgSRC->data[1] = p_Vid->imgUV_com[0]; + imgSRC->data[2] = p_Vid->imgUV_com[1]; + } + } + else + { + imgSRC->format = p_Inp->output; + imgREF->format = p_Inp->output; + + imgREF->data[0] = imgData->frm_data[0]; + + if ((p_Inp->PicInterlace == ADAPTIVE_CODING) || (p_Inp->separate_colour_plane_flag != 0)) + { + p_Vid->enc_picture = p_Vid->enc_frame_picture[0]; + } + imgSRC->data[0] = p_Vid->enc_picture->imgY; + + if (p_Vid->yuv_format != YUV400) + { + imgREF->data[1] = imgData->frm_data[1]; + imgREF->data[2] = imgData->frm_data[2]; + + imgSRC->data[1] = p_Vid->enc_picture->imgUV[0]; + imgSRC->data[2] = p_Vid->enc_picture->imgUV[1]; + } + } +} + +void compute_distortion(VideoParameters *p_Vid, ImageData *imgData) +{ + InputParameters *p_Inp = p_Vid->p_Inp; + DistortionParams *p_Dist = p_Vid->p_Dist; + if (p_Inp->Verbose != 0) + { + select_img(p_Vid, &p_Vid->imgSRC, &p_Vid->imgREF, imgData); + + find_snr (p_Vid, &p_Vid->imgREF, &p_Vid->imgSRC, &p_Dist->metric[SSE], &p_Dist->metric[PSNR]); + if (p_Inp->Distortion[SSIM] == 1) + find_ssim (p_Vid, p_Inp, &p_Vid->imgREF, &p_Vid->imgSRC, &p_Dist->metric[SSIM]); + if (p_Inp->Distortion[MS_SSIM] == 1) + find_ms_ssim(p_Vid, p_Inp, &p_Vid->imgREF, &p_Vid->imgSRC, &p_Dist->metric[MS_SSIM]); + // RGB Distortion + if(p_Inp->DistortionYUVtoRGB == 1) + { + YUVtoRGB(p_Vid, &p_Vid->imgREF, &p_Vid->imgRGB_ref); + YUVtoRGB(p_Vid, &p_Vid->imgSRC, &p_Vid->imgRGB_src); + find_snr (p_Vid, &p_Vid->imgRGB_ref, &p_Vid->imgRGB_src, &p_Dist->metric[SSE_RGB], &p_Dist->metric[PSNR_RGB]); + if (p_Inp->Distortion[SSIM] == 1) + find_ssim (p_Vid, p_Inp, &p_Vid->imgRGB_ref, &p_Vid->imgRGB_src, &p_Dist->metric[SSIM_RGB]); + if (p_Inp->Distortion[MS_SSIM] == 1) + find_ms_ssim(p_Vid, p_Inp, &p_Vid->imgRGB_ref, &p_Vid->imgRGB_src, &p_Dist->metric[MS_SSIM_RGB]); + } + } +} diff --git a/lencod/src/img_luma.c b/lencod/src/img_luma.c new file mode 100644 index 0000000..aca6337 --- /dev/null +++ b/lencod/src/img_luma.c @@ -0,0 +1,680 @@ +/*! +************************************************************************************* +* \file img_luma_uint8.c +* +* \brief +* Luma interpolation functions +* +* \author +* Main contributors (see contributors.h for copyright, address and affiliation details) +* - Alexis Michael Tourapis +* - Athanasios Leontaris +* - Yuwen He +* +************************************************************************************* +*/ + +#include "contributors.h" + +#include + +#include "global.h" +#include "image.h" +#include "img_luma.h" +#include "memalloc.h" + + +/*! + ************************************************************************ + * \brief + * Copy Integer Samples to image [0][0] + * + * \param s + * pointer to StorablePicture structure + * \param dstImg + * destination image + * \param srcImg + * source image + ************************************************************************ + */ +static void getSubImageInteger( StorablePicture *s, imgpel **dstImg, imgpel **srcImg) +{ + int i, j; + int size_x_minus1 = s->size_x - 1; + + imgpel *wBufSrc, *wBufDst; + + // Copy top line + wBufDst = &( dstImg[-IMG_PAD_SIZE_Y][-IMG_PAD_SIZE_X] ); + wBufSrc = srcImg[0]; + // left IMG_PAD_SIZE + for (i = 0; i < IMG_PAD_SIZE_X; i++) + *(wBufDst++) = wBufSrc[0]; + // center 0-(s->size_x) + memcpy(wBufDst, wBufSrc, s->size_x * sizeof(imgpel)); + wBufDst += s->size_x; + // right IMG_PAD_SIZE + for (i = 0; i < IMG_PAD_SIZE_X; i++) + *(wBufDst++) = wBufSrc[size_x_minus1]; + + // Now copy remaining pad lines + for (j = -IMG_PAD_SIZE_Y+1; j < 1; ++j) + { + memcpy(dstImg[j]-IMG_PAD_SIZE_X, dstImg[j - 1]-IMG_PAD_SIZE_X, s->size_x_padded * sizeof(imgpel)); + } + + for (j = 1; j < s->size_y; j++) + { + wBufDst = &( dstImg[j][-IMG_PAD_SIZE_X] ); + wBufSrc = srcImg[j]; + // left IMG_PAD_SIZE + for (i = 0; i < IMG_PAD_SIZE_X; i++) + *(wBufDst++) = wBufSrc[0]; + // center 0-(s->size_x) + memcpy(wBufDst, wBufSrc, s->size_x * sizeof(imgpel)); + wBufDst += s->size_x; + // right IMG_PAD_SIZE + for (i = 0; i < IMG_PAD_SIZE_X; i++) + *(wBufDst++) = wBufSrc[size_x_minus1]; + } + + // Replicate bottom pad lines + for (j = s->size_y; j < s->size_y+IMG_PAD_SIZE_Y; j++) + { + memcpy(dstImg[j]-IMG_PAD_SIZE_X, dstImg[j - 1]-IMG_PAD_SIZE_X, s->size_x_padded * sizeof(imgpel)); + } +} + +static void getSubImageInteger_s( StorablePicture *s, imgpel **dstImg, imgpel **srcImg) +{ + int i, j; + int size_x_minus1 = s->size_x - 1; + + imgpel *wBufSrc, *wBufDst; + + // Copy top line + wBufDst = &( dstImg[-IMG_PAD_SIZE_Y][-IMG_PAD_SIZE_X] ); + wBufSrc = srcImg[0]; + // left IMG_PAD_SIZE + for (i = 0; i < IMG_PAD_SIZE_X; ++i) + *(wBufDst++) = wBufSrc[0]; + // center 0-(s->size_x) + memcpy(wBufDst, wBufSrc, s->size_x * sizeof(imgpel)); + wBufDst += s->size_x; + // right IMG_PAD_SIZE + for (i = 0; i < IMG_PAD_SIZE_X; ++i) + *(wBufDst++) = wBufSrc[size_x_minus1]; + + // Now copy remaining pad lines + for (j = -IMG_PAD_SIZE_Y+1; j < 1; ++j) + { + memcpy(dstImg[j]-IMG_PAD_SIZE_X, dstImg[j - 1]-IMG_PAD_SIZE_X, s->size_x_padded * sizeof(imgpel)); + } + + for (j = 1; j < s->size_y; ++j) + { + wBufDst = &( dstImg[j][-IMG_PAD_SIZE_X] ); + wBufSrc = srcImg[j]; + // left IMG_PAD_SIZE + for (i = 0; i < IMG_PAD_SIZE_X; ++i) + *(wBufDst++) = wBufSrc[0]; + // center 0-(s->size_x) + //memcpy(wBufDst, wBufSrc, s->size_x * sizeof(imgpel)); + wBufDst += s->size_x; + // right IMG_PAD_SIZE + for (i = 0; i < IMG_PAD_SIZE_X; ++i) + *(wBufDst++) = wBufSrc[size_x_minus1]; + } + + // Replicate bottom pad lines + for (j = s->size_y; j < s->size_y+IMG_PAD_SIZE_Y; ++j) + { + memcpy(dstImg[j]-IMG_PAD_SIZE_X, dstImg[j - 1]-IMG_PAD_SIZE_X, s->size_x_padded * sizeof(imgpel)); + } +} + +/*! + ************************************************************************ + * \brief + * Does _horizontal_ interpolation using the SIX TAP filters + * + * \param p_Vid + * pointer to VideoParameters structure + * \param s + * pointer to StorablePicture structure + * \param dstImg + * destination image + * \param srcImg + * source image + ************************************************************************ + */ +static void getHorSubImageSixTap( VideoParameters *p_Vid, StorablePicture *s, imgpel **dstImg, imgpel **srcImg) +{ + int is, jpad, ipad; + int ypadded_size = s->size_y_padded; + int xpadded_size = s->size_x_padded; + + imgpel *wBufSrc, *wBufDst; + imgpel *srcImgA, *srcImgB, *srcImgC, *srcImgD, *srcImgE, *srcImgF; + int *iBufDst; + const int tap0 = ONE_FOURTH_TAP[0][0]; + const int tap1 = ONE_FOURTH_TAP[0][1]; + const int tap2 = ONE_FOURTH_TAP[0][2]; + + for (jpad = -IMG_PAD_SIZE_Y; jpad < ypadded_size-IMG_PAD_SIZE_Y; jpad++) + { + wBufSrc = srcImg[jpad]-IMG_PAD_SIZE_X; + wBufDst = dstImg[jpad]-IMG_PAD_SIZE_X; + iBufDst = p_Vid->imgY_sub_tmp[jpad]-IMG_PAD_SIZE_X; + + srcImgA = &wBufSrc[0]; + srcImgB = &wBufSrc[0]; + srcImgC = &wBufSrc[0]; + srcImgD = &wBufSrc[1]; + srcImgE = &wBufSrc[2]; + srcImgF = &wBufSrc[3]; + + // left padded area + is = + (tap0 * (*srcImgA++ + *srcImgD++) + + tap1 * (*srcImgB + *srcImgE++) + + tap2 * (*srcImgC + *srcImgF++)); + + *iBufDst++ = is; + *wBufDst++ = (imgpel) iClip1 ( p_Vid->max_imgpel_value, rshift_rnd_sf( is, 5 ) ); + + is = + (tap0 * (*srcImgA++ + *srcImgD++) + + tap1 * (*srcImgB++ + *srcImgE++) + + tap2 * (*srcImgC + *srcImgF++)); + + *iBufDst++ = is; + *wBufDst++ = (imgpel) iClip1 ( p_Vid->max_imgpel_value, rshift_rnd_sf( is, 5 ) ); + + // center + for (ipad = 2; ipad < xpadded_size - 4; ipad++) + { + is = + (tap0 * (*srcImgA++ + *srcImgD++) + + tap1 * (*srcImgB++ + *srcImgE++) + + tap2 * (*srcImgC++ + *srcImgF++)); + + *iBufDst++ = is; + *wBufDst++ = (imgpel) iClip1 ( p_Vid->max_imgpel_value, rshift_rnd_sf( is, 5 ) ); + } + + is = ( + tap0 * (*srcImgA++ + *srcImgD++) + + tap1 * (*srcImgB++ + *srcImgE++) + + tap2 * (*srcImgC++ + *srcImgF )); + + *iBufDst++ = is; + *wBufDst++ = (imgpel) iClip1 ( p_Vid->max_imgpel_value, rshift_rnd_sf( is, 5 ) ); + + // right padded area + is = ( + tap0 * (*srcImgA++ + *srcImgD++) + + tap1 * (*srcImgB++ + *srcImgE) + + tap2 * (*srcImgC++ + *srcImgF)); + + *iBufDst++ = is; + *wBufDst++ = (imgpel) iClip1 ( p_Vid->max_imgpel_value, rshift_rnd_sf( is, 5 ) ); + + is = ( + tap0 * (*srcImgA++ + *srcImgD) + + tap1 * (*srcImgB++ + *srcImgE) + + tap2 * (*srcImgC++ + *srcImgF)); + + *iBufDst++ = is; + *wBufDst++ = (imgpel) iClip1 ( p_Vid->max_imgpel_value, rshift_rnd_sf( is, 5 ) ); + + is = ( + tap0 * (*srcImgA + *srcImgD) + + tap1 * (*srcImgB + *srcImgE) + + tap2 * (*srcImgC + *srcImgF)); + + *iBufDst = is; + *wBufDst = (imgpel) iClip1 ( p_Vid->max_imgpel_value, rshift_rnd_sf( is, 5 ) ); + + } +} + +/*! + ************************************************************************ + * \brief + * Does _vertical_ interpolation using the SIX TAP filters + * + * \param p_Vid + * pointer to VideoParameters structure + * \param s + * pointer to StorablePicture structure + * \param dstImg + * pointer to target image + * \param srcImg + * pointer to source image + ************************************************************************ + */ +static void getVerSubImageSixTap( VideoParameters *p_Vid, StorablePicture *s, imgpel **dstImg, imgpel **srcImg) +{ + int is, jpad, ipad; + int ypadded_size = s->size_y_padded; + int xpadded_size = s->size_x_padded; + int maxy = ypadded_size - 1-IMG_PAD_SIZE_Y; + + imgpel *wxLineDst; + imgpel *srcImgA, *srcImgB, *srcImgC, *srcImgD, *srcImgE, *srcImgF; + const int tap0 = ONE_FOURTH_TAP[0][0]; + const int tap1 = ONE_FOURTH_TAP[0][1]; + const int tap2 = ONE_FOURTH_TAP[0][2]; + + // branches within the j loop + // top + for (jpad = -IMG_PAD_SIZE_Y; jpad < 2-IMG_PAD_SIZE_Y; jpad++) + { + wxLineDst = dstImg[jpad]-IMG_PAD_SIZE_X; + srcImgA = srcImg[jpad ]-IMG_PAD_SIZE_X; + srcImgB = srcImg[0-IMG_PAD_SIZE_Y]-IMG_PAD_SIZE_X; + srcImgC = srcImg[0-IMG_PAD_SIZE_Y]-IMG_PAD_SIZE_X; + srcImgD = srcImg[jpad + 1]-IMG_PAD_SIZE_X; + srcImgE = srcImg[jpad + 2]-IMG_PAD_SIZE_X; + srcImgF = srcImg[jpad + 3]-IMG_PAD_SIZE_X; + for (ipad = 0; ipad < xpadded_size; ipad++) + { + is = + (tap0 * (*srcImgA++ + *srcImgD++) + + tap1 * (*srcImgB++ + *srcImgE++) + + tap2 * (*srcImgC++ + *srcImgF++)); + + wxLineDst[ipad] = (imgpel) iClip1 (p_Vid->max_imgpel_value, rshift_rnd_sf( is, 5 ) ); + } + } + // center + for (jpad = 2-IMG_PAD_SIZE_Y; jpad < ypadded_size - 3-IMG_PAD_SIZE_Y; jpad++) + { + wxLineDst = dstImg[jpad]-IMG_PAD_SIZE_X; + srcImgA = srcImg[jpad ]-IMG_PAD_SIZE_X; + srcImgB = srcImg[jpad - 1]-IMG_PAD_SIZE_X; + srcImgC = srcImg[jpad - 2]-IMG_PAD_SIZE_X; + srcImgD = srcImg[jpad + 1]-IMG_PAD_SIZE_X; + srcImgE = srcImg[jpad + 2]-IMG_PAD_SIZE_X; + srcImgF = srcImg[jpad + 3]-IMG_PAD_SIZE_X; + for (ipad = 0; ipad < xpadded_size; ipad++) + { + is = + (tap0 * (*srcImgA++ + *srcImgD++) + + tap1 * (*srcImgB++ + *srcImgE++) + + tap2 * (*srcImgC++ + *srcImgF++)); + + wxLineDst[ipad] = (imgpel) iClip1 ( p_Vid->max_imgpel_value, rshift_rnd_sf( is, 5 ) ); + } + } + + // bottom + for (jpad = ypadded_size - 3-IMG_PAD_SIZE_Y; jpad < ypadded_size-IMG_PAD_SIZE_Y; jpad++) + { + wxLineDst = dstImg[jpad]-IMG_PAD_SIZE_X; + srcImgA = srcImg[jpad ]-IMG_PAD_SIZE_X; + srcImgB = srcImg[jpad - 1]-IMG_PAD_SIZE_X; + srcImgC = srcImg[jpad - 2]-IMG_PAD_SIZE_X; + srcImgD = srcImg[imin (maxy, jpad + 1)]-IMG_PAD_SIZE_X; + srcImgE = srcImg[maxy]-IMG_PAD_SIZE_X; + srcImgF = srcImg[maxy]-IMG_PAD_SIZE_X; + for (ipad = 0; ipad < xpadded_size; ipad++) + { + is = + (tap0 * (*srcImgA++ + *srcImgD++) + + tap1 * (*srcImgB++ + *srcImgE++) + + tap2 * (*srcImgC++ + *srcImgF++)); + + wxLineDst[ipad] = (imgpel) iClip1 ( p_Vid->max_imgpel_value, rshift_rnd_sf( is, 5 ) ); + } + } +} + +/*! + ************************************************************************ + * \brief + * Does _vertical_ interpolation using the SIX TAP filters + * + * \param p_Vid + * pointer to VideoParameters structure + * \param s + * pointer to StorablePicture structure + * \param dstImg + * pointer to source image + ************************************************************************ + */ +static void getVerSubImageSixTapTmp( VideoParameters *p_Vid, StorablePicture *s, imgpel **dstImg) +{ + int is, jpad, ipad; + int ypadded_size = s->size_y_padded; + int xpadded_size = s->size_x_padded; + int maxy = ypadded_size - 1-IMG_PAD_SIZE_Y; + + imgpel *wxLineDst; + int *srcImgA, *srcImgB, *srcImgC, *srcImgD, *srcImgE, *srcImgF; + const int tap0 = ONE_FOURTH_TAP[0][0]; + const int tap1 = ONE_FOURTH_TAP[0][1]; + const int tap2 = ONE_FOURTH_TAP[0][2]; + + // top + for (jpad = -IMG_PAD_SIZE_Y; jpad < 2-IMG_PAD_SIZE_Y; jpad++) + { + wxLineDst = dstImg[jpad]-IMG_PAD_SIZE_X; + srcImgA = p_Vid->imgY_sub_tmp[jpad ]-IMG_PAD_SIZE_X; + srcImgB = p_Vid->imgY_sub_tmp[0-IMG_PAD_SIZE_Y]-IMG_PAD_SIZE_X; + srcImgC = p_Vid->imgY_sub_tmp[0-IMG_PAD_SIZE_Y]-IMG_PAD_SIZE_X; + srcImgD = p_Vid->imgY_sub_tmp[jpad + 1]-IMG_PAD_SIZE_X; + srcImgE = p_Vid->imgY_sub_tmp[jpad + 2]-IMG_PAD_SIZE_X; + srcImgF = p_Vid->imgY_sub_tmp[jpad + 3]-IMG_PAD_SIZE_X; + + for (ipad = 0; ipad < xpadded_size; ipad++) + { + is = + (tap0 * (*srcImgA++ + *srcImgD++) + + tap1 * (*srcImgB++ + *srcImgE++) + + tap2 * (*srcImgC++ + *srcImgF++)); + + wxLineDst[ipad] = (imgpel) iClip1 ( p_Vid->max_imgpel_value, rshift_rnd_sf( is, 10 ) ); + } + } + + // center + for (jpad = 2-IMG_PAD_SIZE_Y; jpad < ypadded_size - 3-IMG_PAD_SIZE_Y; jpad++) + { + wxLineDst = dstImg[jpad]-IMG_PAD_SIZE_X; + srcImgA = p_Vid->imgY_sub_tmp[jpad ]-IMG_PAD_SIZE_X; + srcImgB = p_Vid->imgY_sub_tmp[jpad - 1]-IMG_PAD_SIZE_X; + srcImgC = p_Vid->imgY_sub_tmp[jpad - 2]-IMG_PAD_SIZE_X; + srcImgD = p_Vid->imgY_sub_tmp[jpad + 1]-IMG_PAD_SIZE_X; + srcImgE = p_Vid->imgY_sub_tmp[jpad + 2]-IMG_PAD_SIZE_X; + srcImgF = p_Vid->imgY_sub_tmp[jpad + 3]-IMG_PAD_SIZE_X; + for (ipad = 0; ipad < xpadded_size; ipad++) + { + is = + (tap0 * (*srcImgA++ + *srcImgD++) + + tap1 * (*srcImgB++ + *srcImgE++) + + tap2 * (*srcImgC++ + *srcImgF++)); + + wxLineDst[ipad] = (imgpel) iClip1 ( p_Vid->max_imgpel_value, rshift_rnd_sf( is, 10 ) ); + } + } + + // bottom + for (jpad = ypadded_size - 3-IMG_PAD_SIZE_Y; jpad < ypadded_size-IMG_PAD_SIZE_Y; jpad++) + { + wxLineDst = dstImg[jpad]-IMG_PAD_SIZE_X; + srcImgA = p_Vid->imgY_sub_tmp[jpad ]-IMG_PAD_SIZE_X; + srcImgB = p_Vid->imgY_sub_tmp[jpad - 1]-IMG_PAD_SIZE_X; + srcImgC = p_Vid->imgY_sub_tmp[jpad - 2]-IMG_PAD_SIZE_X; + srcImgD = p_Vid->imgY_sub_tmp[imin (maxy, jpad + 1)]-IMG_PAD_SIZE_X; + srcImgE = p_Vid->imgY_sub_tmp[maxy]-IMG_PAD_SIZE_X; + srcImgF = p_Vid->imgY_sub_tmp[maxy]-IMG_PAD_SIZE_X; + for (ipad = 0; ipad < xpadded_size; ipad++) + { + is = + (tap0 * (*srcImgA++ + *srcImgD++) + + tap1 * (*srcImgB++ + *srcImgE++) + + tap2 * (*srcImgC++ + *srcImgF++)); + + wxLineDst[ipad] = (imgpel) iClip1 ( p_Vid->max_imgpel_value, rshift_rnd_sf( is, 10 ) ); + } + } +} + +/*! + ************************************************************************ + * \brief + * Does _horizontal_ interpolation using the BiLinear filter + * + * \param s + * pointer to StorablePicture structure + * \param dstImg + * destination Image + * \param srcImgL + * source left image + * \param srcImgR + * source right image + ************************************************************************ + */ +static void getSubImageBiLinear( StorablePicture *s, imgpel **dstImg, imgpel **srcImgL, imgpel **srcImgR) +{ + int jpad, ipad; + int ypadded_size = s->size_y_padded; + int xpadded_size = s->size_x_padded; + + imgpel *wBufSrcL, *wBufSrcR, *wBufDst; + + for (jpad = -IMG_PAD_SIZE_Y; jpad < ypadded_size-IMG_PAD_SIZE_Y; jpad++) + { + wBufSrcL = srcImgL[jpad]-IMG_PAD_SIZE_X; + wBufSrcR = srcImgR[jpad]-IMG_PAD_SIZE_X; + wBufDst = dstImg[jpad]-IMG_PAD_SIZE_X; + + for (ipad = 0; ipad < xpadded_size; ipad++) + { + *wBufDst++ = (imgpel) rshift_rnd_sf( *wBufSrcL++ + *wBufSrcR++, 1 ); + } + } +} + + +/*! + ************************************************************************ + * \brief + * Does _horizontal_ interpolation using the BiLinear filter + * + * \param s + * pointer to StorablePicture structure + * \param dstImg + * destination Image + * \param srcImgL + * source left image + * \param srcImgR + * source right image + ************************************************************************ + */ +static void getHorSubImageBiLinear( StorablePicture *s, imgpel **dstImg, imgpel **srcImgL, imgpel **srcImgR) +{ + int jpad, ipad; + int ypadded_size = s->size_y_padded; + int xpadded_size = s->size_x_padded - 1; + + imgpel *wBufSrcL, *wBufSrcR, *wBufDst; + + for (jpad = -IMG_PAD_SIZE_Y; jpad < ypadded_size-IMG_PAD_SIZE_Y; jpad++) + { + wBufSrcL = srcImgL[jpad]-IMG_PAD_SIZE_X; + wBufSrcR = &srcImgR[jpad][1-IMG_PAD_SIZE_X]; + wBufDst = dstImg[jpad]-IMG_PAD_SIZE_X; + + // left padded area + center + for (ipad = 0; ipad < xpadded_size; ipad++) + { + *wBufDst++ = (imgpel) rshift_rnd_sf( *wBufSrcL++ + *wBufSrcR++, 1 ); + } + // right padded area + *wBufDst++ = (imgpel) rshift_rnd_sf( *wBufSrcL++ + wBufSrcR[-1], 1 ); + } +} + + +/*! + ************************************************************************ + * \brief + * Does _vertical_ interpolation using the BiLinear filter + * + * \param s + * pointer to StorablePicture structure + * \param dstImg + * destination Image + * \param srcImgT + * source top image + * \param srcImgB + * source bottom image + ************************************************************************ + */ +static void getVerSubImageBiLinear( StorablePicture *s, imgpel **dstImg, imgpel **srcImgT, imgpel **srcImgB) +{ + int jpad, ipad; + int ypadded_size = s->size_y_padded - 1; + int xpadded_size = s->size_x_padded; + + imgpel *wBufSrcT, *wBufSrcB, *wBufDst; + + // top + for (jpad = -IMG_PAD_SIZE_Y; jpad < ypadded_size-IMG_PAD_SIZE_Y; jpad++) + { + wBufSrcT = srcImgT[jpad]-IMG_PAD_SIZE_X; + wBufDst = dstImg[jpad]-IMG_PAD_SIZE_X; + wBufSrcB = srcImgB[jpad + 1]-IMG_PAD_SIZE_X; + + for (ipad = 0; ipad < xpadded_size; ipad++) + { + *wBufDst++ = (imgpel) rshift_rnd_sf(*wBufSrcT++ + *wBufSrcB++, 1); + } + } + // bottom + wBufSrcT = srcImgT[ypadded_size-IMG_PAD_SIZE_Y]-IMG_PAD_SIZE_X; + wBufDst = dstImg[ypadded_size-IMG_PAD_SIZE_Y]-IMG_PAD_SIZE_X; + wBufSrcB = srcImgB[ypadded_size-IMG_PAD_SIZE_Y]-IMG_PAD_SIZE_X; + + for (ipad = 0; ipad < xpadded_size; ipad++) + { + *wBufDst++ = (imgpel) rshift_rnd_sf(*wBufSrcT++ + *wBufSrcB++, 1); + } +} + + +/*! + ************************************************************************ + * \brief + * Does _diagonal_ interpolation using the BiLinear filter + * + * \param s + * pointer to StorablePicture structure + * \param dstImg + * destination Image + * \param srcImgT + * source top/left image + * \param srcImgB + * source bottom/right image + ************************************************************************ + */ +static void getDiagSubImageBiLinear( StorablePicture *s, imgpel **dstImg, imgpel **srcImgT, imgpel **srcImgB ) +{ + int jpad, ipad; + int maxx = s->size_x_padded - 1-IMG_PAD_SIZE_X; + int maxy = s->size_y_padded - 1-IMG_PAD_SIZE_Y; + + imgpel *wBufSrcL, *wBufSrcR, *wBufDst; + + for (jpad = -IMG_PAD_SIZE_Y; jpad < maxy; jpad++) + { + wBufSrcL = srcImgT[jpad + 1]-IMG_PAD_SIZE_X; + wBufSrcR = &srcImgB[jpad][1-IMG_PAD_SIZE_X]; + wBufDst = dstImg[jpad]-IMG_PAD_SIZE_X; + + for (ipad = -IMG_PAD_SIZE_X; ipad < maxx; ipad++) + { + *wBufDst++ = (imgpel) rshift_rnd_sf(*wBufSrcL++ + *wBufSrcR++, 1); + } + + *wBufDst++ = (imgpel) rshift_rnd_sf(*wBufSrcL++ + wBufSrcR[-1], 1); + } + + wBufSrcL = srcImgT[maxy]-IMG_PAD_SIZE_X; + wBufSrcR = &srcImgB[maxy][1-IMG_PAD_SIZE_X]; + wBufDst = dstImg[maxy]-IMG_PAD_SIZE_X; + + for (ipad = -IMG_PAD_SIZE_X; ipad < maxx; ipad++) + { + *wBufDst++ = (imgpel) rshift_rnd_sf(*wBufSrcL++ + *wBufSrcR++, 1); + } + + *wBufDst++ = (imgpel) rshift_rnd_sf(*wBufSrcL++ + wBufSrcR[-1], 1); +} + +/*! + ************************************************************************ + * \brief + * Creates the 4x4 = 16 images that contain quarter-pel samples + * sub-sampled at different spatial orientations; + * enables more efficient implementation + * + * \param p_Vid + * pointer to VideoParameters structure + * \param s + * pointer to StorablePicture structure + s************************************************************************ + */ +void getSubImagesLuma( VideoParameters *p_Vid, StorablePicture *s ) +{ + imgpel ****cImgSub = s->p_curr_img_sub; + int otf_shift = ( p_Vid->p_Inp->OnTheFlyFractMCP == OTF_L1 ) ? (1) : (0) ; + + // 0 1 2 3 + // 4 5 6 7 + // 8 9 10 11 + // 12 13 14 15 + + //// INTEGER PEL POSITIONS //// + + // sub-image 0 [0][0] + // simply copy the integer pels + if (cImgSub[0][0][0] != s->p_curr_img[0]) + { + getSubImageInteger( s, cImgSub[0][0], s->p_curr_img); + } + else + { + getSubImageInteger_s( s, cImgSub[0][0], s->p_curr_img); + } + + //// HALF-PEL POSITIONS: SIX-TAP FILTER //// + + // sub-image 2 [0][2] + // HOR interpolate (six-tap) sub-image [0][0] + getHorSubImageSixTap( p_Vid, s, cImgSub[0][2>>otf_shift], cImgSub[0][0] ); + + // sub-image 8 [2][0] + // VER interpolate (six-tap) sub-image [0][0] + getVerSubImageSixTap( p_Vid, s, cImgSub[2>>otf_shift][0], cImgSub[0][0]); + + // sub-image 10 [2][2] + // VER interpolate (six-tap) sub-image [0][2] + getVerSubImageSixTapTmp( p_Vid, s, cImgSub[2>>otf_shift][2>>otf_shift]); + + if( !p_Vid->p_Inp->OnTheFlyFractMCP ) + { + //// QUARTER-PEL POSITIONS: BI-LINEAR INTERPOLATION //// + + // sub-image 1 [0][1] + getSubImageBiLinear ( s, cImgSub[0][1], cImgSub[0][0], cImgSub[0][2]); + // sub-image 4 [1][0] + getSubImageBiLinear ( s, cImgSub[1][0], cImgSub[0][0], cImgSub[2][0]); + // sub-image 5 [1][1] + getSubImageBiLinear ( s, cImgSub[1][1], cImgSub[0][2], cImgSub[2][0]); + // sub-image 6 [1][2] + getSubImageBiLinear ( s, cImgSub[1][2], cImgSub[0][2], cImgSub[2][2]); + // sub-image 9 [2][1] + getSubImageBiLinear ( s, cImgSub[2][1], cImgSub[2][0], cImgSub[2][2]); + + // sub-image 3 [0][3] + getHorSubImageBiLinear ( s, cImgSub[0][3], cImgSub[0][2], cImgSub[0][0]); + // sub-image 7 [1][3] + getHorSubImageBiLinear ( s, cImgSub[1][3], cImgSub[0][2], cImgSub[2][0]); + // sub-image 11 [2][3] + getHorSubImageBiLinear ( s, cImgSub[2][3], cImgSub[2][2], cImgSub[2][0]); + + // sub-image 12 [3][0] + getVerSubImageBiLinear ( s, cImgSub[3][0], cImgSub[2][0], cImgSub[0][0]); + // sub-image 13 [3][1] + getVerSubImageBiLinear ( s, cImgSub[3][1], cImgSub[2][0], cImgSub[0][2]); + // sub-image 14 [3][2] + getVerSubImageBiLinear ( s, cImgSub[3][2], cImgSub[2][2], cImgSub[0][2]); + + // sub-image 15 [3][3] + getDiagSubImageBiLinear( s, cImgSub[3][3], cImgSub[0][2], cImgSub[2][0]); + } +} diff --git a/lencod/src/intra16x16.c b/lencod/src/intra16x16.c new file mode 100644 index 0000000..c17e880 --- /dev/null +++ b/lencod/src/intra16x16.c @@ -0,0 +1,517 @@ +/*! +************************************************************************************* + * \file intra16x16.c + * + * \brief + * Intra 16x16 mode functions + * + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * - Alexis Michael Tourapis + ************************************************************************************* + */ + +#include "contributors.h" + +#include "global.h" +#include "image.h" +#include "mb_access.h" +#include "transform.h" +#include "memalloc.h" + +/*! + ************************************************************************ + * \brief + * Vertical 16x16 Prediction + ************************************************************************ + */ +static inline void get_i16x16_vertical(imgpel **cur_pred, imgpel *PredPel) +{ + int j; + for (j = 0; j < MB_BLOCK_SIZE; j++) + memcpy(cur_pred[j], &PredPel[1], MB_BLOCK_SIZE * sizeof(imgpel)); +} + + +/*! + ************************************************************************ + * \brief + * Horizontal 16x16 Prediction + ************************************************************************ + */ +static inline void get_i16x16_horizontal(imgpel **cur_pred, imgpel *PredPel) +{ + int i, j; + imgpel *prediction = &PredPel[16]; + + for (j = 0; j < MB_BLOCK_SIZE; j++) + { + prediction++; + for (i = 0; i < MB_BLOCK_SIZE; i++) + { + cur_pred[j][i] = *prediction; + } + } +} + +/*! + ************************************************************************ + * \brief + * DC 16x16 Prediction + ************************************************************************ + */ +static inline void get_i16x16_dc(imgpel **cur_pred, imgpel *PredPel, int left_available, int up_available) +{ + int i, j, s0 = 0, s1 = 0, s2 = 0; + + if (up_available) + { + for (i = 1; i < MB_BLOCK_SIZE + 1; ++i) + s1 += PredPel[i]; + } + + if (left_available) + { + for (i = 17; i < 33; ++i) + s2 += PredPel[i]; // sum vert pix + } + + if (up_available) + { + s0 = left_available + ? rshift_rnd_sf((s1 + s2),(MB_BLOCK_SHIFT + 1)) // no edge + : rshift_rnd_sf(s1, MB_BLOCK_SHIFT); // left edge + } + else + { + s0 = left_available + ? rshift_rnd_sf(s2, MB_BLOCK_SHIFT) // upper edge + : PredPel[1]; // top left corner, nothing to predict from + } + + for (j = 0; j < MB_BLOCK_SIZE; j++) + { + for (i = 0; i < MB_BLOCK_SIZE; i++) + cur_pred[j][i] = (imgpel) s0; + } +} + +/*! + ************************************************************************ + * \brief + * Plane 16x16 Prediction + ************************************************************************ + */ +static inline void get_i16x16_plane(imgpel **cur_pred, imgpel *PredPel, int max_imgpel_value) +{ + int i, j; + // plane prediction + int ih=0, iv=0; + int ib, ic, iaa; + imgpel *t_pred = &PredPel[25]; + imgpel *u_pred = &PredPel[8]; + imgpel *b_pred = &PredPel[23]; + + for (i = 1; i < 8;++i) + { + ih += i*(*(u_pred + i) - *(u_pred - i)); + iv += i*(*t_pred++ - *b_pred--); + } + ih += 8 * (*(u_pred + 8) - PredPel[0]); + iv += 8 * (*t_pred++ - PredPel[0]); + + ib = (5 * ih + 32) >> 6; + ic = (5 * iv + 32) >> 6; + + iaa=16 * (PredPel[16] + PredPel[32]); + + for (j=0;j< MB_BLOCK_SIZE;++j) + { + for (i=0;i< MB_BLOCK_SIZE;++i) + { + cur_pred[j][i]= (imgpel) iClip1( max_imgpel_value, rshift_rnd_sf((iaa + (i - 7) * ib + (j - 7) * ic), 5));// store plane prediction + } + } +} + +/*! + ************************************************************************ + * \brief + * Set intra 16x16 prediction samples + * + * \par Input: + * + * \par Output: + * none + ************************************************************************ + */ +void set_intrapred_16x16(Macroblock *currMB, ColorPlane pl, int *left_available, int *up_available, int *all_available) +{ + VideoParameters *p_Vid = currMB->p_Vid; + InputParameters *p_Inp = currMB->p_Inp; + + int i; + imgpel *PredPel = currMB->intra16x16_pred[pl]; // array of predictor pels + imgpel **img_enc = (currMB->p_Slice->P444_joined)? p_Vid->enc_picture->p_img[pl]: p_Vid->enc_picture->p_curr_img; + + PixelPos pix_b; + PixelPos pix_a; + PixelPos pix_d; + + int *mb_size = p_Vid->mb_size[IS_LUMA]; + + p_Vid->getNeighbour(currMB, -1, -1, mb_size, &pix_d); + p_Vid->getNeighbour(currMB, -1, 0, mb_size, &pix_a); + p_Vid->getNeighbour(currMB, 0, -1, mb_size, &pix_b); + + if (p_Inp->UseConstrainedIntraPred == 0) + { + *up_available = pix_b.available; + *left_available = pix_a.available; + *all_available = pix_d.available; + } + else + { + *up_available = pix_b.available ? p_Vid->intra_block[pix_b.mb_addr] : 0; + *left_available = pix_a.available ? p_Vid->intra_block[pix_a.mb_addr] : 0; + *all_available = pix_d.available ? p_Vid->intra_block[pix_d.mb_addr] : 0; + } + + // form predictor pels + if (*up_available) + { + memcpy(&PredPel[1], &img_enc[pix_b.pos_y][pix_b.pos_x], MB_BLOCK_SIZE * sizeof(imgpel)); + } + else + { + for (i = 1; i < 17; ++i) + PredPel[i] = (imgpel) p_Vid->dc_pred_value; + } + + if (*left_available) + { + int pos_y = pix_a.pos_y; + int pos_x = pix_a.pos_x; + + for (i = 1; i < MB_BLOCK_SIZE + 1; ++i) + PredPel[i + 16] = img_enc[pos_y++][pos_x]; + } + else + { + for (i = 17; i < 33; ++i) + PredPel[i] = (imgpel) p_Vid->dc_pred_value; + } + + if (*all_available) + { + PredPel[0] = img_enc[pix_d.pos_y][pix_d.pos_x]; + } + else + { + PredPel[0] = p_Vid->dc_pred_value; + } +} + + +/*! + ************************************************************************ + * \brief + * Set intra 16x16 prediction samples (MBAFF) + * + * \par Input: + * + * \par Output: + * none + ************************************************************************ + */ +void set_intrapred_16x16_mbaff(Macroblock *currMB, ColorPlane pl, int *left_available, int *up_available, int *all_available) +{ + VideoParameters *p_Vid = currMB->p_Vid; + InputParameters *p_Inp = currMB->p_Inp; + + int i; + imgpel *PredPel = currMB->intra16x16_pred[pl]; // array of predictor pels + imgpel **img_enc = p_Vid->enc_picture->p_curr_img; + + PixelPos pix_b; + PixelPos pix_a[17]; + + int *mb_size = p_Vid->mb_size[IS_LUMA]; + + for (i = 0; i < 17; ++i) + { + p_Vid->getNeighbour(currMB, -1, i - 1, mb_size, &pix_a[i]); + } + + p_Vid->getNeighbour(currMB, 0, -1, mb_size, &pix_b); + + if (p_Inp->UseConstrainedIntraPred == 0) + { + *up_available = pix_b.available; + *left_available = pix_a[1].available; + *all_available = pix_a[0].available; + } + else + { + *up_available = pix_b.available ? p_Vid->intra_block[pix_b.mb_addr] : 0; + for (i=1, *left_available=1; i<17;++i) + *left_available &= pix_a[i].available ? p_Vid->intra_block[pix_a[i].mb_addr]: 0; + *all_available = pix_a[0].available ? p_Vid->intra_block[pix_a[0].mb_addr]: 0; + } + + // form predictor pels + if (*up_available) + { + memcpy(&PredPel[1], &img_enc[pix_b.pos_y][pix_b.pos_x], MB_BLOCK_SIZE * sizeof(imgpel)); + } + else + { + for (i = 1; i < 17; ++i) + PredPel[i] = (imgpel) p_Vid->dc_pred_value; + } + + if (*left_available) + { + for (i = 1; i < MB_BLOCK_SIZE + 1; ++i) + PredPel[i + 16] = img_enc[pix_a[i].pos_y][pix_a[i].pos_x]; + } + else + { + for (i = 17; i < 33; ++i) + PredPel[i] = (imgpel) p_Vid->dc_pred_value; + } + + if (*all_available) + { + PredPel[0] = img_enc[pix_a[0].pos_y][pix_a[0].pos_x]; + } + else + { + PredPel[0] = p_Vid->dc_pred_value; + } +} + + +/*! + ************************************************************************ + * \brief + * Generate 16x16 intra prediction block + * + * \par Input: + * Starting point of current 16x16 block image posision + * + * \par Output: + * none + ************************************************************************ + */ +void get_intrapred_16x16(Macroblock *currMB, ColorPlane pl, int i16x16_mode, int left_available, int up_available) +{ + imgpel *PredPel = currMB->intra16x16_pred[pl]; // array of predictor pels + imgpel ***curr_mpr_16x16 = currMB->p_Slice->mpr_16x16[pl]; + + switch (i16x16_mode) + { + case VERT_PRED_16 : + get_i16x16_vertical(curr_mpr_16x16[VERT_PRED_16], PredPel); + break; + case HOR_PRED_16 : + get_i16x16_horizontal(curr_mpr_16x16[HOR_PRED_16], PredPel); + break; + case DC_PRED_16 : + get_i16x16_dc(curr_mpr_16x16[DC_PRED_16], PredPel, left_available, up_available); + break; + case PLANE_16 : + get_i16x16_plane(curr_mpr_16x16[PLANE_16], PredPel, currMB->p_Vid->max_imgpel_value); + break; + default: + printf("invalid prediction mode \n"); + break; + } +} + +distblk distI16x16_satd(Macroblock *currMB, imgpel **img_org, imgpel **pred_img, distblk min_cost) +{ + Slice *currSlice = currMB->p_Slice; + int **M7 = NULL; + int **tblk4x4 = currSlice->tblk4x4; + int ****i16blk4x4 = currSlice->i16blk4x4; + imgpel *cur_img, *prd_img; + distblk current_intra_sad_2 = 0; + int ii, jj, i, j, i32Cost = 0; + int imin_cost = dist_down(min_cost); + + for (j = 0; j < MB_BLOCK_SIZE; j++) + { + cur_img = &img_org[currMB->opix_y + j][currMB->pix_x]; + prd_img = pred_img[j]; + for (i = 0; i < MB_BLOCK_SIZE; i++) + { + i16blk4x4[j >> 2][i >> 2][j & 0x03][i & 0x03] = cur_img[i] - prd_img[i]; + } + } + + for (jj = 0; jj < 4; jj++) + { + for (ii = 0; ii < 4;ii++) + { + M7 = i16blk4x4[jj][ii]; + hadamard4x4(M7, M7); + i32Cost += iabs(M7[0][1]); + i32Cost += iabs(M7[0][2]); + i32Cost += iabs(M7[0][3]); + + if (i32Cost > imin_cost) + return (min_cost); + + for (j = 1; j < 4; j++) + { + //i32Cost =0; + i32Cost += iabs(M7[j][0]); + i32Cost += iabs(M7[j][1]); + i32Cost += iabs(M7[j][2]); + i32Cost += iabs(M7[j][3]); + + if (i32Cost > imin_cost) + return (min_cost); + } + } + } + + for (j = 0; j < 4;j++) + { + tblk4x4[j][0] = (i16blk4x4[j][0][0][0] >> 1); + tblk4x4[j][1] = (i16blk4x4[j][1][0][0] >> 1); + tblk4x4[j][2] = (i16blk4x4[j][2][0][0] >> 1); + tblk4x4[j][3] = (i16blk4x4[j][3][0][0] >> 1); + } + + // Hadamard of DC coeff + hadamard4x4(tblk4x4, tblk4x4); + + for (j = 0; j < 4; j++) + { + i32Cost += iabs(tblk4x4[j][0]); + i32Cost += iabs(tblk4x4[j][1]); + i32Cost += iabs(tblk4x4[j][2]); + i32Cost += iabs(tblk4x4[j][3]); + + if (i32Cost > imin_cost) + return (min_cost); + } + + current_intra_sad_2 += (dist_scale((distblk)i32Cost)); + return current_intra_sad_2; +} + +distblk distI16x16_sad(Macroblock *currMB, imgpel **img_org, imgpel **pred_img, distblk min_cost) +{ + imgpel *cur_img, *prd_img; + int i32Cost = 0; + int i, j; + int imin_cost = dist_down(min_cost); + + for (j = 0; j < MB_BLOCK_SIZE; j++) + { + cur_img = &img_org[currMB->opix_y + j][currMB->pix_x]; + prd_img = pred_img[j]; + for (i = 0; i < MB_BLOCK_SIZE; i++) + { + i32Cost += iabs( *cur_img++ - *prd_img++ ); + } + + if (i32Cost > imin_cost) + return (min_cost); + } + + return (dist_scale((distblk) i32Cost)); +} + +distblk distI16x16_sse(Macroblock *currMB, imgpel **img_org, imgpel **pred_img, distblk min_cost) +{ + imgpel *cur_img, *prd_img; + int i, j, i32Cost = 0; + int imin_cost = dist_down(min_cost); + + for (j = 0; j < MB_BLOCK_SIZE;j++) + { + cur_img = &img_org[currMB->opix_y + j][currMB->pix_x]; + prd_img = pred_img[j]; + for (i = 0; i < MB_BLOCK_SIZE; i++) + { + i32Cost += iabs2( *cur_img++ - *prd_img++ ); + } + + if (i32Cost > imin_cost) + return (min_cost); + } + + return (dist_scale((distblk) i32Cost)); +} + + +/*! + ************************************************************************ + * \brief + * Find best 16x16 based intra mode + * + * \par Input: + * Image parameters, pointer to best 16x16 intra mode + * + * \par Output: + * best 16x16 based SAD + ************************************************************************/ +distblk find_sad_16x16_JM(Macroblock *currMB) +{ + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + InputParameters *p_Inp = currMB->p_Inp; + distblk current_intra_sad_2, best_intra_sad2 = DISTBLK_MAX; + int k; + imgpel ***curr_mpr_16x16 = currSlice->mpr_16x16[0]; + + int up_avail, left_avail, left_up_avail; + + currMB->i16mode = DC_PRED_16; + + currSlice->set_intrapred_16x16(currMB, PLANE_Y, &left_avail, &up_avail, &left_up_avail); + // For speed purposes, we should just unify all planes + if (currSlice->P444_joined) + { + currSlice->set_intrapred_16x16(currMB, PLANE_U, &left_avail, &up_avail, &left_up_avail); + currSlice->set_intrapred_16x16(currMB, PLANE_V, &left_avail, &up_avail, &left_up_avail); + } + + for (k = VERT_PRED_16; k <= PLANE_16; k++) + { + if (p_Inp->IntraDisableInterOnly == 0 || (currSlice->slice_type != I_SLICE && currSlice->slice_type != SI_SLICE) ) + { + if (p_Inp->Intra16x16ParDisable && (k == VERT_PRED_16||k == HOR_PRED_16)) + continue; + + if (p_Inp->Intra16x16PlaneDisable && k == PLANE_16) + continue; + } + //check if there are neighbours to predict from + if (!((k == VERT_PRED_16 && !up_avail) || (k == HOR_PRED_16 && !left_avail) || (k == PLANE_16 && (!left_avail || !up_avail || !left_up_avail)))) + { + get_intrapred_16x16(currMB, PLANE_Y, k, left_avail, up_avail); + current_intra_sad_2 = currSlice->distI16x16(currMB, p_Vid->pCurImg, curr_mpr_16x16[k], best_intra_sad2); + if (currSlice->P444_joined) + { + get_intrapred_16x16(currMB, PLANE_U, k, left_avail, up_avail); + current_intra_sad_2 += currSlice->distI16x16(currMB, p_Vid->pImgOrg[1], currSlice->mpr_16x16[1][k], best_intra_sad2); + get_intrapred_16x16(currMB, PLANE_V, k, left_avail, up_avail); + current_intra_sad_2 += currSlice->distI16x16(currMB, p_Vid->pImgOrg[2], currSlice->mpr_16x16[2][k], best_intra_sad2); + } + + if (current_intra_sad_2 < best_intra_sad2) + { + best_intra_sad2 = current_intra_sad_2; + currMB->i16mode = (char) k; // update best intra mode + } + } + } + + return best_intra_sad2; +} + diff --git a/lencod/src/intra4x4.c b/lencod/src/intra4x4.c new file mode 100644 index 0000000..f89ed62 --- /dev/null +++ b/lencod/src/intra4x4.c @@ -0,0 +1,561 @@ + +/*! + ************************************************************************************* + * \file intra4x4.c + * + * \brief + * Intra 4x4 mode functions + * + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * - Alexis Michael Tourapis + ************************************************************************************* + */ + +#include "contributors.h" + +#include "global.h" +#include "image.h" +#include "mb_access.h" + +void generate_pred_error_4x4(imgpel **cur_img, imgpel **prd_img, imgpel **cur_prd, int **m7, int pic_opix_x, int block_x) +{ + int j, i, *m7_line; + imgpel *cur_line, *prd_line; + + for (j = 0; j < BLOCK_SIZE; j++) + { + m7_line = &m7[j][block_x]; + cur_line = &cur_img[j][pic_opix_x]; + prd_line = prd_img[j]; + memcpy(&cur_prd[j][block_x], prd_line, BLOCK_SIZE * sizeof(imgpel)); + + for (i = 0; i < BLOCK_SIZE; i++) + { + *m7_line++ = (int) (*cur_line++ - *prd_line++); + } + } +} + +// Notation for comments regarding prediction and predictors. +// The pels of the 4x4 block are labelled a..p. The predictor pels above +// are labelled A..H, from the left I..P, and from above left X, as follows: +// +// X A B C D E F G H +// I a b c d +// J e f g h +// K i j k l +// L m n o p +// + +// Predictor array index definitions +#define P_X (PredPel[0]) +#define P_A (PredPel[1]) +#define P_B (PredPel[2]) +#define P_C (PredPel[3]) +#define P_D (PredPel[4]) +#define P_E (PredPel[5]) +#define P_F (PredPel[6]) +#define P_G (PredPel[7]) +#define P_H (PredPel[8]) +#define P_I (PredPel[9]) +#define P_J (PredPel[10]) +#define P_K (PredPel[11]) +#define P_L (PredPel[12]) + +/*! + ************************************************************************ + * \brief + * Vertical 4x4 Prediction + ************************************************************************ + */ +static inline void get_i4x4_vertical(imgpel **cur_pred, imgpel *PredPel) +{ + memcpy(cur_pred[0], &PredPel[1], BLOCK_SIZE * sizeof(imgpel)); + memcpy(cur_pred[1], &PredPel[1], BLOCK_SIZE * sizeof(imgpel)); + memcpy(cur_pred[2], &PredPel[1], BLOCK_SIZE * sizeof(imgpel)); + memcpy(cur_pred[3], &PredPel[1], BLOCK_SIZE * sizeof(imgpel)); +} + +/*! + ************************************************************************ + * \brief + * Horizontal 4x4 Prediction + ************************************************************************ + */ +static inline void get_i4x4_horizontal(imgpel **cur_pred, imgpel *PredPel) +{ + int i; + + for (i=0; i < BLOCK_SIZE; i++) + { + cur_pred[i][0] = + cur_pred[i][1] = + cur_pred[i][2] = + cur_pred[i][3] = (imgpel) (&P_I)[i]; + } +} + +/*! + ************************************************************************ + * \brief + * DC 4x4 Prediction + ************************************************************************ + */ +static inline void get_i4x4_dc(imgpel **cur_pred, imgpel *PredPel, int left_available, int up_available) +{ + int i, j, s0 = 0; + if (up_available && left_available) + { + // no edge + s0 = (P_A + P_B + P_C + P_D + P_I + P_J + P_K + P_L + 4) >> (BLOCK_SHIFT + 1); + } + else if (!up_available && left_available) + { + // upper edge + s0 = (P_I + P_J + P_K + P_L + 2) >> BLOCK_SHIFT;; + } + else if (up_available && !left_available) + { + // left edge + s0 = (P_A + P_B + P_C + P_D + 2) >> BLOCK_SHIFT; + } + else //if (!up_available && !left_available) + { + // top left corner, nothing to predict from + s0 = P_A; // P_A already set to p_Vid->dc_pred_value; + } + + for (j=0; j < BLOCK_SIZE; j++) + { + for (i=0; i < BLOCK_SIZE; i++) + cur_pred[j][i] = (imgpel) s0; + } +} + +/*! + ************************************************************************ + * \brief + * Diagonal Down Left 4x4 Prediction + ************************************************************************ + */ +static inline void get_i4x4_downleft(imgpel **cur_pred, imgpel *PredPel) +{ + cur_pred[0][0] = (imgpel) ((P_A + P_C + ((P_B) << 1) + 2) >> 2); + cur_pred[0][1] = + cur_pred[1][0] = (imgpel) ((P_B + P_D + ((P_C) << 1) + 2) >> 2); + cur_pred[0][2] = + cur_pred[1][1] = + cur_pred[2][0] = (imgpel) ((P_C + P_E + ((P_D) << 1) + 2) >> 2); + cur_pred[0][3] = + cur_pred[1][2] = + cur_pred[2][1] = + cur_pred[3][0] = (imgpel) ((P_D + P_F + ((P_E) << 1) + 2) >> 2); + cur_pred[1][3] = + cur_pred[2][2] = + cur_pred[3][1] = (imgpel) ((P_E + P_G + ((P_F)<<1) + 2) >> 2); + cur_pred[2][3] = + cur_pred[3][2] = (imgpel) ((P_F + P_H + ((P_G)<<1) + 2) >> 2); + cur_pred[3][3] = (imgpel) ((P_G + 3*(P_H) + 2) >> 2); +} + +/*! + ************************************************************************ + * \brief + * Diagonal Down Right 4x4 Prediction + ************************************************************************ + */ +static inline void get_i4x4_downright(imgpel **cur_pred, imgpel *PredPel) +{ + cur_pred[3][0] = (imgpel) ((P_L + 2*P_K + P_J + 2) >> 2); + cur_pred[2][0] = + cur_pred[3][1] = (imgpel) ((P_K + 2*P_J + P_I + 2) >> 2); + cur_pred[1][0] = + cur_pred[2][1] = + cur_pred[3][2] = (imgpel) ((P_J + 2*P_I + P_X + 2) >> 2); + cur_pred[0][0] = + cur_pred[1][1] = + cur_pred[2][2] = + cur_pred[3][3] = (imgpel) ((P_I + 2*P_X + P_A + 2) >> 2); + cur_pred[0][1] = + cur_pred[1][2] = + cur_pred[2][3] = (imgpel) ((P_X + 2*P_A + P_B + 2) >> 2); + cur_pred[0][2] = + cur_pred[1][3] = (imgpel) ((P_A + 2*P_B + P_C + 2) >> 2); + cur_pred[0][3] = (imgpel) ((P_B + 2*P_C + P_D + 2) >> 2); +} + + +/*! + ************************************************************************ + * \brief + * Vertical Left 4x4 Prediction + ************************************************************************ + */ +static inline void get_i4x4_vertleft(imgpel **cur_pred, imgpel *PredPel) +{ + cur_pred[0][0] = (imgpel) ((P_A + P_B + 1) >> 1); + cur_pred[0][1] = + cur_pred[2][0] = (imgpel) ((P_B + P_C + 1) >> 1); + cur_pred[0][2] = + cur_pred[2][1] = (imgpel) ((P_C + P_D + 1) >> 1); + cur_pred[0][3] = + cur_pred[2][2] = (imgpel) ((P_D + P_E + 1) >> 1); + cur_pred[2][3] = (imgpel) ((P_E + P_F + 1) >> 1); + cur_pred[1][0] = (imgpel) ((P_A + ((P_B)<<1) + P_C + 2) >> 2); + cur_pred[1][1] = + cur_pred[3][0] = (imgpel) ((P_B + ((P_C)<<1) + P_D + 2) >> 2); + cur_pred[1][2] = + cur_pred[3][1] = (imgpel) ((P_C + ((P_D)<<1) + P_E + 2) >> 2); + cur_pred[1][3] = + cur_pred[3][2] = (imgpel) ((P_D + ((P_E)<<1) + P_F + 2) >> 2); + cur_pred[3][3] = (imgpel) ((P_E + ((P_F)<<1) + P_G + 2) >> 2); +} + +/*! + ************************************************************************ + * \brief + * Vertical Right 4x4 Prediction + ************************************************************************ + */ +static inline void get_i4x4_vertright(imgpel **cur_pred, imgpel *PredPel) +{ + cur_pred[0][0] = + cur_pred[2][1] = (imgpel) ((P_X + P_A + 1) >> 1); + cur_pred[0][1] = + cur_pred[2][2] = (imgpel) ((P_A + P_B + 1) >> 1); + cur_pred[0][2] = + cur_pred[2][3] = (imgpel) ((P_B + P_C + 1) >> 1); + cur_pred[0][3] = (imgpel) ((P_C + P_D + 1) >> 1); + cur_pred[1][0] = + cur_pred[3][1] = (imgpel) ((P_I + 2*P_X + P_A + 2) >> 2); + cur_pred[1][1] = + cur_pred[3][2] = (imgpel) ((P_X + 2*P_A + P_B + 2) >> 2); + cur_pred[1][2] = + cur_pred[3][3] = (imgpel) ((P_A + 2*P_B + P_C + 2) >> 2); + cur_pred[1][3] = (imgpel) ((P_B + 2*P_C + P_D + 2) >> 2); + cur_pred[2][0] = (imgpel) ((P_X + 2*P_I + P_J + 2) >> 2); + cur_pred[3][0] = (imgpel) ((P_I + 2*P_J + P_K + 2) >> 2); +} + +/*! + ************************************************************************ + * \brief + * Horizontal Down 4x4 Prediction + ************************************************************************ + */ +static inline void get_i4x4_hordown(imgpel **cur_pred, imgpel *PredPel) +{ + cur_pred[0][0] = + cur_pred[1][2] = (imgpel) ((P_X + P_I + 1) >> 1); + cur_pred[0][1] = + cur_pred[1][3] = (imgpel) ((P_I + 2*P_X + P_A + 2) >> 2); + cur_pred[0][2] = (imgpel) ((P_X + 2*P_A + P_B + 2) >> 2); + cur_pred[0][3] = (imgpel) ((P_A + 2*P_B + P_C + 2) >> 2); + cur_pred[1][0] = + cur_pred[2][2] = (imgpel) ((P_I + P_J + 1) >> 1); + cur_pred[1][1] = + cur_pred[2][3] = (imgpel) ((P_X + 2*P_I + P_J + 2) >> 2); + cur_pred[2][0] = + cur_pred[3][2] = (imgpel) ((P_J + P_K + 1) >> 1); + cur_pred[2][1] = + cur_pred[3][3] = (imgpel) ((P_I + 2*P_J + P_K + 2) >> 2); + cur_pred[3][0] = (imgpel) ((P_K + P_L + 1) >> 1); + cur_pred[3][1] = (imgpel) ((P_J + 2*P_K + P_L + 2) >> 2); +} + + +/*! + ************************************************************************ + * \brief + * Horizontal Up 4x4 Prediction + ************************************************************************ + */ +static inline void get_i4x4_horup(imgpel **cur_pred, imgpel *PredPel) +{ + cur_pred[0][0] = (imgpel) ((P_I + P_J + 1) >> 1); + cur_pred[0][1] = (imgpel) ((P_I + 2*P_J + P_K + 2) >> 2); + cur_pred[0][2] = + cur_pred[1][0] = (imgpel) ((P_J + P_K + 1) >> 1); + cur_pred[0][3] = + cur_pred[1][1] = (imgpel) ((P_J + 2*P_K + P_L + 2) >> 2); + cur_pred[1][2] = + cur_pred[2][0] = (imgpel) ((P_K + P_L + 1) >> 1); + cur_pred[1][3] = + cur_pred[2][1] = (imgpel) ((P_K + 2*P_L + P_L + 2) >> 2); + cur_pred[3][0] = + cur_pred[2][2] = + cur_pred[2][3] = + cur_pred[3][1] = + cur_pred[3][2] = + cur_pred[3][3] = (imgpel) P_L; +} + +/*! + ************************************************************************ + * \brief + * Make intra 4x4 prediction according to all 9 prediction modes. + * The routine uses left and upper neighbouring points from + * previous coded blocks to do this (if available). Notice that + * inaccessible neighbouring points are signalled with a negative + * value in the predmode array . + * + * \par Input: + * Starting point of current 4x4 block image posision + * + * \par Output: + * none + ************************************************************************ + */ +void set_intrapred_4x4_mbaff(Macroblock *currMB, ColorPlane pl, int img_x,int img_y, int *left_available, int *up_available, int *all_available) +{ + VideoParameters *p_Vid = currMB->p_Vid; + InputParameters *p_Inp = currMB->p_Inp; + + int i; + imgpel *PredPel = currMB->intra4x4_pred[pl]; // array of predictor pels + imgpel **img_enc = p_Vid->enc_picture->p_curr_img; + + int ioff = (img_x & 15); + int joff = (img_y & 15); + + PixelPos pix_a[4]; + PixelPos pix_b, pix_c, pix_d; + + int block_available_up; + int block_available_left; + int block_available_up_left; + int block_available_up_right; + int *mb_size = p_Vid->mb_size[IS_LUMA]; + + for (i = 0; i < 4; i++) + { + p_Vid->getNeighbour(currMB, ioff -1 , joff + i , mb_size, &pix_a[i]); + } + + p_Vid->getNeighbour(currMB, ioff , joff -1 , mb_size, &pix_b); + p_Vid->getNeighbour(currMB, ioff + 4 , joff -1 , mb_size, &pix_c); + p_Vid->getNeighbour(currMB, ioff - 1 , joff -1 , mb_size, &pix_d); + + pix_c.available = pix_c.available && !((ioff==4) && ((joff==4)||(joff==12))); + + if (p_Inp->UseConstrainedIntraPred) + { + for (i=0, block_available_left=1; i<4;i++) + block_available_left &= pix_a[i].available ? p_Vid->intra_block[pix_a[i].mb_addr]: 0; + block_available_up = pix_b.available ? p_Vid->intra_block [pix_b.mb_addr] : 0; + block_available_up_right = pix_c.available ? p_Vid->intra_block [pix_c.mb_addr] : 0; + block_available_up_left = pix_d.available ? p_Vid->intra_block [pix_d.mb_addr] : 0; + } + else + { + block_available_left = pix_a[0].available; + block_available_up = pix_b.available; + block_available_up_right = pix_c.available; + block_available_up_left = pix_d.available; + } + + *left_available = block_available_left; + *up_available = block_available_up; + *all_available = block_available_up && block_available_left && block_available_up_left; + + i = (img_x & 15); + + // form predictor pels + if (block_available_up) + { + memcpy(&PredPel[1], &img_enc[pix_b.pos_y][pix_b.pos_x], BLOCK_SIZE * sizeof(imgpel)); + } + else + { + P_A = P_B = P_C = P_D = (imgpel) p_Vid->dc_pred_value; + } + + if (block_available_up_right) + { + memcpy(&PredPel[5], &img_enc[pix_c.pos_y][pix_c.pos_x], BLOCK_SIZE * sizeof(imgpel)); + } + else + { + P_E = P_F = P_G = P_H = P_D; + } + + if (block_available_left) + { + P_I = img_enc[pix_a[0].pos_y][pix_a[0].pos_x]; + P_J = img_enc[pix_a[1].pos_y][pix_a[1].pos_x]; + P_K = img_enc[pix_a[2].pos_y][pix_a[2].pos_x]; + P_L = img_enc[pix_a[3].pos_y][pix_a[3].pos_x]; + } + else + { + P_I = P_J = P_K = P_L = p_Vid->dc_pred_value; + } + + if (block_available_up_left) + { + P_X = img_enc[pix_d.pos_y][pix_d.pos_x]; + } + else + { + P_X = p_Vid->dc_pred_value; + } +} + +/*! + ************************************************************************ + * \brief + * Make intra 4x4 prediction according to all 9 prediction modes. + * The routine uses left and upper neighbouring points from + * previous coded blocks to do this (if available). Notice that + * inaccessible neighbouring points are signalled with a negative + * value in the predmode array . + * + * \par Input: + * Starting point of current 4x4 block image posision + * + * \par Output: + * none + ************************************************************************ + */ +void set_intrapred_4x4(Macroblock *currMB, ColorPlane pl, int img_x,int img_y, int *left_available, int *up_available, int *all_available) +{ + VideoParameters *p_Vid = currMB->p_Vid; + InputParameters *p_Inp = currMB->p_Inp; + + imgpel *PredPel = currMB->intra4x4_pred[pl]; // array of predictor pels + imgpel **img_enc = p_Vid->enc_picture->p_curr_img; + + int ioff = (img_x & 15); + int joff = (img_y & 15); + + PixelPos pix_a, pix_b, pix_c, pix_d; + + int block_available_up; + int block_available_left; + int block_available_up_left; + int block_available_up_right; + int *mb_size = p_Vid->mb_size[IS_LUMA]; + + p_Vid->getNeighbour(currMB, ioff - 1, joff , mb_size, &pix_a); + p_Vid->getNeighbour(currMB, ioff , joff - 1, mb_size, &pix_b); + p_Vid->getNeighbour(currMB, ioff + 4, joff - 1, mb_size, &pix_c); + p_Vid->getNeighbour(currMB, ioff - 1, joff - 1, mb_size, &pix_d); + + pix_c.available = pix_c.available && !((ioff==4) && ((joff==4)||(joff==12))); + + if (p_Inp->UseConstrainedIntraPred) + { + block_available_left = pix_a.available ? p_Vid->intra_block [pix_a.mb_addr]: 0; + block_available_up = pix_b.available ? p_Vid->intra_block [pix_b.mb_addr] : 0; + block_available_up_right = pix_c.available ? p_Vid->intra_block [pix_c.mb_addr] : 0; + block_available_up_left = pix_d.available ? p_Vid->intra_block [pix_d.mb_addr] : 0; + } + else + { + block_available_left = pix_a.available; + block_available_up = pix_b.available; + block_available_up_right = pix_c.available; + block_available_up_left = pix_d.available; + } + + *left_available = block_available_left; + *up_available = block_available_up; + *all_available = block_available_up && block_available_left && block_available_up_left; + + // form predictor pels + if (block_available_up) + { + memcpy(&PredPel[1], &img_enc[pix_b.pos_y][pix_b.pos_x], BLOCK_SIZE * sizeof(imgpel)); + } + else + { + P_A = P_B = P_C = P_D = (imgpel) p_Vid->dc_pred_value; + } + + if (block_available_up_right) + { + memcpy(&PredPel[5], &img_enc[pix_c.pos_y][pix_c.pos_x], BLOCK_SIZE * sizeof(imgpel)); + } + else + { + P_E = P_F = P_G = P_H = P_D; + } + + if (block_available_left) + { + int pos_y = pix_a.pos_y; + int pos_x = pix_a.pos_x; + P_I = img_enc[pos_y++][pos_x]; + P_J = img_enc[pos_y++][pos_x]; + P_K = img_enc[pos_y++][pos_x]; + P_L = img_enc[pos_y ][pos_x]; + } + else + { + P_I = P_J = P_K = P_L = p_Vid->dc_pred_value; + } + + if (block_available_up_left) + { + P_X = img_enc[pix_d.pos_y][pix_d.pos_x]; + } + else + { + P_X = p_Vid->dc_pred_value; + } +} + +/*! + ************************************************************************ + * \brief + * Generate 4x4 intra prediction block + * + * \par Input: + * Starting point of current 4x4 block image posision + * + * \par Output: + * none + ************************************************************************ + */ +void get_intrapred_4x4(Macroblock *currMB, ColorPlane pl, int i4x4_mode, int img_x, int img_y, int left_available, int up_available) +{ + imgpel *PredPel = currMB->intra4x4_pred[pl]; // array of predictor pels + imgpel ***curr_mpr_4x4 = currMB->p_Slice->mpr_4x4[pl]; + + // Note that currently prediction values are always placed starting from (0,0) and not according to block position. + + switch (i4x4_mode) + { + case VERT_PRED : + get_i4x4_vertical(curr_mpr_4x4[VERT_PRED], PredPel); + break; + case HOR_PRED : + get_i4x4_horizontal(curr_mpr_4x4[HOR_PRED], PredPel); + break; + case DC_PRED : + get_i4x4_dc(curr_mpr_4x4[DC_PRED], PredPel, left_available, up_available); + break; + case DIAG_DOWN_LEFT_PRED : + get_i4x4_downleft(curr_mpr_4x4[DIAG_DOWN_LEFT_PRED], PredPel); + break; + case DIAG_DOWN_RIGHT_PRED : + get_i4x4_downright(curr_mpr_4x4[DIAG_DOWN_RIGHT_PRED], PredPel); + break; + case VERT_RIGHT_PRED : + get_i4x4_vertright(curr_mpr_4x4[VERT_RIGHT_PRED], PredPel); + break; + case HOR_DOWN_PRED : + get_i4x4_hordown(curr_mpr_4x4[HOR_DOWN_PRED], PredPel); + break; + case VERT_LEFT_PRED : + get_i4x4_vertleft(curr_mpr_4x4[VERT_LEFT_PRED], PredPel); + break; + case HOR_UP_PRED : + get_i4x4_horup(curr_mpr_4x4[HOR_UP_PRED], PredPel); + break; + default: + printf("invalid prediction mode \n"); + break; + } +} diff --git a/lencod/src/intra8x8.c b/lencod/src/intra8x8.c new file mode 100644 index 0000000..56b9dd4 --- /dev/null +++ b/lencod/src/intra8x8.c @@ -0,0 +1,756 @@ +/*! + ************************************************************************************* + * \file intra8x8.c + * + * \brief + * Intra 8x8 mode functions + * + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * - Alexis Michael Tourapis + ************************************************************************************* + */ + +#include "global.h" +#include "image.h" +#include "mb_access.h" +#include "intra8x8.h" + +// Notation for comments regarding prediction and predictors. +// The pels of the 8x8 block are labelled a1..h8. The predictor pels above +// are labelled A..P, from the left Q..X, and from above left Z, as follows: +// +// Z A B C D E F G H I J K L M N O P +// Q a1 b1 c1 d1 e1 f1 g1 h1 +// R a2 b2 c2 d2 e2 f2 g2 h2 +// S a3 b3 c3 d3 e3 f3 g3 h3 +// T a4 b4 c4 d4 e4 f4 g4 h4 +// U a5 b5 c5 d5 e5 f5 g5 h5 +// V a6 b6 c6 d6 e6 f6 g6 h6 +// W a7 b7 c7 d7 e7 f7 g7 h7 +// X a8 b8 c8 d8 e8 f8 g8 h8 + +// Predictor array index definitions +#define P_Z (PredPel[0]) +#define P_A (PredPel[1]) +#define P_B (PredPel[2]) +#define P_C (PredPel[3]) +#define P_D (PredPel[4]) +#define P_E (PredPel[5]) +#define P_F (PredPel[6]) +#define P_G (PredPel[7]) +#define P_H (PredPel[8]) +#define P_I (PredPel[9]) +#define P_J (PredPel[10]) +#define P_K (PredPel[11]) +#define P_L (PredPel[12]) +#define P_M (PredPel[13]) +#define P_N (PredPel[14]) +#define P_O (PredPel[15]) +#define P_P (PredPel[16]) +#define P_Q (PredPel[17]) +#define P_R (PredPel[18]) +#define P_S (PredPel[19]) +#define P_T (PredPel[20]) +#define P_U (PredPel[21]) +#define P_V (PredPel[22]) +#define P_W (PredPel[23]) +#define P_X (PredPel[24]) + +void generate_pred_error_8x8(imgpel **cur_img, imgpel **prd_img, imgpel **cur_prd, + int **m7, int pic_opix_x, int block_x) +{ + int j, i, *m7_line; + imgpel *cur_line, *prd_line; + + for (j=0; j < BLOCK_SIZE_8x8; j++) + { + prd_line = prd_img[j]; + memcpy(&cur_prd[j][block_x], prd_line, BLOCK_SIZE_8x8 * sizeof(imgpel)); + cur_line = &cur_img[j][pic_opix_x]; + m7_line = &m7[j][block_x]; + for (i = 0; i < BLOCK_SIZE_8x8; i++) + { + *m7_line++ = (int) (*cur_line++ - *prd_line++); + } + } +} + +/*! + ************************************************************************************* + * \brief + * Prefiltering for Intra8x8 prediction + ************************************************************************************* + */ +void LowPassForIntra8x8Pred(imgpel *PredPel, int block_up_left, int block_up, int block_left) +{ + int i; + imgpel LoopArray[25]; + + memcpy(LoopArray,PredPel, 25 * sizeof(imgpel)); + + if(block_up) + { + if(block_up_left) + { + LoopArray[1] = (imgpel) ((PredPel[0] + (PredPel[1]<<1) + PredPel[2] + 2) >> 2); + } + else + LoopArray[1] = (imgpel) ((PredPel[1] + (PredPel[1]<<1) + PredPel[2] + 2) >> 2); + + + for(i = 2; i <16; i++) + { + LoopArray[i] = (imgpel) ((PredPel[i-1] + (PredPel[i]<<1) + PredPel[i+1] + 2) >> 2); + } + LoopArray[16] = (imgpel) ((PredPel[15] + PredPel[16] + (PredPel[16]<<1) + 2) >> 2); + } + + if(block_up_left) + { + if(block_up && block_left) + { + LoopArray[0] = (imgpel) (((PredPel[0] << 1) + PredPel[1] + PredPel[17] + 2) >> 2); + } + else + { + if(block_up) + LoopArray[0] = (imgpel) ((PredPel[0] + (PredPel[0] << 1) + PredPel[1] + 2) >> 2); + else + if(block_left) + LoopArray[0] = (imgpel) ((PredPel[0] + (PredPel[0] << 1) + PredPel[17] + 2) >> 2); + } + } + + if(block_left) + { + if(block_up_left) + LoopArray[17] = (imgpel) ((PredPel[0] + (PredPel[17] << 1) + PredPel[18] + 2) >> 2); + else + LoopArray[17] = (imgpel) ((PredPel[17] + (PredPel[17] << 1) + PredPel[18] + 2) >> 2); + + for(i = 18; i <24; i++) + { + LoopArray[i] = (imgpel) ((PredPel[i-1] + (PredPel[i]<<1) + PredPel[i+1] + 2) >> 2); + } + LoopArray[24] = (imgpel) ((PredPel[23] + (PredPel[24] << 1) + PredPel[24] + 2) >> 2); + } + + memcpy(PredPel, LoopArray, 25 * sizeof(imgpel)); +} + +/*! + ************************************************************************ + * \brief + * Vertical 8x8 Prediction + ************************************************************************ + */ +static inline void get_i8x8_vertical(imgpel **cur_pred, imgpel *PredPel) +{ + int j; + for (j=0; j < BLOCK_SIZE_8x8; j++) + memcpy(cur_pred[j], &P_A, BLOCK_SIZE_8x8 * sizeof(imgpel)); +} + +/*! + ************************************************************************ + * \brief + * Horizontal 8x8 Prediction + ************************************************************************ + */ +static inline void get_i8x8_horizontal(imgpel **cur_pred, imgpel *PredPel) +{ + int i; + for (i=0; i < BLOCK_SIZE_8x8; i++) + { + cur_pred[i][0] = + cur_pred[i][1] = + cur_pred[i][2] = + cur_pred[i][3] = + cur_pred[i][4] = + cur_pred[i][5] = + cur_pred[i][6] = + cur_pred[i][7] = (imgpel) (&P_Q)[i]; + } +} + +/*! + ************************************************************************ + * \brief + * DC 8x8 Prediction + ************************************************************************ + */ +static inline void get_i8x8_dc(imgpel **cur_pred, imgpel *PredPel, int left_available, int up_available) +{ + int i, j, s0 = 0; + if (up_available && left_available) + { + // no edge + s0 = rshift_rnd_sf((P_A + P_B + P_C + P_D + P_E + P_F + P_G + P_H + P_Q + P_R + P_S + P_T + P_U + P_V + P_W + P_X), 4); + } + else if (!up_available && left_available) + { + // upper edge + s0 = rshift_rnd_sf((P_Q + P_R + P_S + P_T + P_U + P_V + P_W + P_X), 3); + } + else if (up_available && !left_available) + { + // left edge + s0 = rshift_rnd_sf((P_A + P_B + P_C + P_D + P_E + P_F + P_G + P_H), 3); + } + else //if (!up_available && !left_available) + { + // top left corner, nothing to predict from + s0 = P_A; //p_Vid->dc_pred_value; + } + + // First line + for (i=0; i < BLOCK_SIZE_8x8; i++) + { + cur_pred[0][i] = (imgpel) s0; + } + + for (j=0; j < BLOCK_SIZE_8x8 - 1; j++) + { + memcpy (cur_pred[j + 1], cur_pred[j], BLOCK_SIZE_8x8 * sizeof(imgpel)); + } +} + +/*! + ************************************************************************ + * \brief + * Diagonal Down Left 8x8 Prediction + ************************************************************************ + */ +static inline void get_i8x8_downleft(imgpel **cur_pred, imgpel *PredPel) +{ + imgpel PredArray[16]; // array of final prediction values + imgpel *Pred = &PredArray[0]; + + // Mode DIAG_DOWN_LEFT_PRED + *Pred++ = (imgpel) ((P_A + P_C + ((P_B) << 1) + 2) >> 2); + *Pred++ = (imgpel) ((P_B + P_D + ((P_C) << 1) + 2) >> 2); + *Pred++ = (imgpel) ((P_C + P_E + ((P_D) << 1) + 2) >> 2); + *Pred++ = (imgpel) ((P_D + P_F + ((P_E) << 1) + 2) >> 2); + *Pred++ = (imgpel) ((P_E + P_G + ((P_F) << 1) + 2) >> 2); + *Pred++ = (imgpel) ((P_F + P_H + ((P_G) << 1) + 2) >> 2); + *Pred++ = (imgpel) ((P_G + P_I + ((P_H) << 1) + 2) >> 2); + *Pred++ = (imgpel) ((P_H + P_J + ((P_I) << 1) + 2) >> 2); + *Pred++ = (imgpel) ((P_I + P_K + ((P_J) << 1) + 2) >> 2); + *Pred++ = (imgpel) ((P_J + P_L + ((P_K) << 1) + 2) >> 2); + *Pred++ = (imgpel) ((P_K + P_M + ((P_L) << 1) + 2) >> 2); + *Pred++ = (imgpel) ((P_L + P_N + ((P_M) << 1) + 2) >> 2); + *Pred++ = (imgpel) ((P_M + P_O + ((P_N) << 1) + 2) >> 2); + *Pred++ = (imgpel) ((P_N + P_P + ((P_O) << 1) + 2) >> 2); + *Pred = (imgpel) ((P_O + P_P + ((P_P) << 1) + 2) >> 2); + + Pred = &PredArray[ 0]; + + memcpy(*cur_pred++, Pred++, 8 * sizeof(imgpel)); + memcpy(*cur_pred++, Pred++, 8 * sizeof(imgpel)); + memcpy(*cur_pred++, Pred++, 8 * sizeof(imgpel)); + memcpy(*cur_pred++, Pred++, 8 * sizeof(imgpel)); + memcpy(*cur_pred++, Pred++, 8 * sizeof(imgpel)); + memcpy(*cur_pred++, Pred++, 8 * sizeof(imgpel)); + memcpy(*cur_pred++, Pred++, 8 * sizeof(imgpel)); + memcpy(*cur_pred , Pred , 8 * sizeof(imgpel)); +} + +/*! + ************************************************************************ + * \brief + * Diagonal Down Right 8x8 Prediction + ************************************************************************ + */ +static inline void get_i8x8_downright(imgpel **cur_pred, imgpel *PredPel) +{ + imgpel PredArray[16]; // array of final prediction values + imgpel *Pred = &PredArray[0]; + + // Mode DIAG_DOWN_RIGHT_PRED + *Pred++ = (imgpel) ((P_X + P_V + ((P_W) << 1) + 2) >> 2); + *Pred++ = (imgpel) ((P_W + P_U + ((P_V) << 1) + 2) >> 2); + *Pred++ = (imgpel) ((P_V + P_T + ((P_U) << 1) + 2) >> 2); + *Pred++ = (imgpel) ((P_U + P_S + ((P_T) << 1) + 2) >> 2); + *Pred++ = (imgpel) ((P_T + P_R + ((P_S) << 1) + 2) >> 2); + *Pred++ = (imgpel) ((P_S + P_Q + ((P_R) << 1) + 2) >> 2); + *Pred++ = (imgpel) ((P_R + P_Z + ((P_Q) << 1) + 2) >> 2); + *Pred++ = (imgpel) ((P_Q + P_A + ((P_Z) << 1) + 2) >> 2); + *Pred++ = (imgpel) ((P_Z + P_B + ((P_A) << 1) + 2) >> 2); + *Pred++ = (imgpel) ((P_A + P_C + ((P_B) << 1) + 2) >> 2); + *Pred++ = (imgpel) ((P_B + P_D + ((P_C) << 1) + 2) >> 2); + *Pred++ = (imgpel) ((P_C + P_E + ((P_D) << 1) + 2) >> 2); + *Pred++ = (imgpel) ((P_D + P_F + ((P_E) << 1) + 2) >> 2); + *Pred++ = (imgpel) ((P_E + P_G + ((P_F) << 1) + 2) >> 2); + *Pred = (imgpel) ((P_F + P_H + ((P_G) << 1) + 2) >> 2); + + Pred = &PredArray[7]; + + memcpy(*cur_pred++, Pred--, 8 * sizeof(imgpel)); + memcpy(*cur_pred++, Pred--, 8 * sizeof(imgpel)); + memcpy(*cur_pred++, Pred--, 8 * sizeof(imgpel)); + memcpy(*cur_pred++, Pred--, 8 * sizeof(imgpel)); + memcpy(*cur_pred++, Pred--, 8 * sizeof(imgpel)); + memcpy(*cur_pred++, Pred--, 8 * sizeof(imgpel)); + memcpy(*cur_pred++, Pred--, 8 * sizeof(imgpel)); + memcpy(*cur_pred , Pred , 8 * sizeof(imgpel)); +} + +/*! + ************************************************************************ + * \brief + * Vertical Left 8x8 Prediction + ************************************************************************ + */ +static inline void get_i8x8_vertleft(imgpel **cur_pred, imgpel *PredPel) +{ + imgpel PredArray[22]; // array of final prediction values + imgpel *Pred = &PredArray[0]; + + *Pred++ = (imgpel) ((P_A + P_B + 1) >> 1); + *Pred++ = (imgpel) ((P_B + P_C + 1) >> 1); + *Pred++ = (imgpel) ((P_C + P_D + 1) >> 1); + *Pred++ = (imgpel) ((P_D + P_E + 1) >> 1); + *Pred++ = (imgpel) ((P_E + P_F + 1) >> 1); + *Pred++ = (imgpel) ((P_F + P_G + 1) >> 1); + *Pred++ = (imgpel) ((P_G + P_H + 1) >> 1); + *Pred++ = (imgpel) ((P_H + P_I + 1) >> 1); + *Pred++ = (imgpel) ((P_I + P_J + 1) >> 1); + *Pred++ = (imgpel) ((P_J + P_K + 1) >> 1); + *Pred++ = (imgpel) ((P_K + P_L + 1) >> 1); + *Pred++ = (imgpel) ((P_A + P_C + ((P_B) << 1) + 2) >> 2); + *Pred++ = (imgpel) ((P_B + P_D + ((P_C) << 1) + 2) >> 2); + *Pred++ = (imgpel) ((P_C + P_E + ((P_D) << 1) + 2) >> 2); + *Pred++ = (imgpel) ((P_D + P_F + ((P_E) << 1) + 2) >> 2); + *Pred++ = (imgpel) ((P_E + P_G + ((P_F) << 1) + 2) >> 2); + *Pred++ = (imgpel) ((P_F + P_H + ((P_G) << 1) + 2) >> 2); + *Pred++ = (imgpel) ((P_G + P_I + ((P_H) << 1) + 2) >> 2); + *Pred++ = (imgpel) ((P_H + P_J + ((P_I) << 1) + 2) >> 2); + *Pred++ = (imgpel) ((P_I + P_K + ((P_J) << 1) + 2) >> 2); + *Pred++ = (imgpel) ((P_J + P_L + ((P_K) << 1) + 2) >> 2); + *Pred = (imgpel) ((P_K + P_M + ((P_L) << 1) + 2) >> 2); + + memcpy(*cur_pred++, &PredArray[ 0], 8 * sizeof(imgpel)); + memcpy(*cur_pred++, &PredArray[11], 8 * sizeof(imgpel)); + memcpy(*cur_pred++, &PredArray[ 1], 8 * sizeof(imgpel)); + memcpy(*cur_pred++, &PredArray[12], 8 * sizeof(imgpel)); + memcpy(*cur_pred++, &PredArray[ 2], 8 * sizeof(imgpel)); + memcpy(*cur_pred++, &PredArray[13], 8 * sizeof(imgpel)); + memcpy(*cur_pred++, &PredArray[ 3], 8 * sizeof(imgpel)); + memcpy(*cur_pred , &PredArray[14], 8 * sizeof(imgpel)); +} + + +/*! + ************************************************************************ + * \brief + * Vertical Right 8x8 Prediction + ************************************************************************ + */ +static inline void get_i8x8_vertright(imgpel **cur_pred, imgpel *PredPel) +{ + imgpel PredArray[22]; // array of final prediction values + imgpel *Pred = &PredArray[0]; + + *Pred++ = (imgpel) ((P_V + P_T + ((P_U) << 1) + 2) >> 2); + *Pred++ = (imgpel) ((P_T + P_R + ((P_S) << 1) + 2) >> 2); + *Pred++ = (imgpel) ((P_R + P_Z + ((P_Q) << 1) + 2) >> 2); + *Pred++ = (imgpel) ((P_Z + P_A + 1) >> 1); + *Pred++ = (imgpel) ((P_A + P_B + 1) >> 1); + *Pred++ = (imgpel) ((P_B + P_C + 1) >> 1); + *Pred++ = (imgpel) ((P_C + P_D + 1) >> 1); + *Pred++ = (imgpel) ((P_D + P_E + 1) >> 1); + *Pred++ = (imgpel) ((P_E + P_F + 1) >> 1); + *Pred++ = (imgpel) ((P_F + P_G + 1) >> 1); + *Pred++ = (imgpel) ((P_G + P_H + 1) >> 1); + + *Pred++ = (imgpel) ((P_W + P_U + ((P_V) << 1) + 2) >> 2); + *Pred++ = (imgpel) ((P_U + P_S + ((P_T) << 1) + 2) >> 2); + *Pred++ = (imgpel) ((P_S + P_Q + ((P_R) << 1) + 2) >> 2); + *Pred++ = (imgpel) ((P_Q + P_A + ((P_Z) << 1) + 2) >> 2); + *Pred++ = (imgpel) ((P_Z + P_B + ((P_A) << 1) + 2) >> 2); + *Pred++ = (imgpel) ((P_A + P_C + ((P_B) << 1) + 2) >> 2); + *Pred++ = (imgpel) ((P_B + P_D + ((P_C) << 1) + 2) >> 2); + *Pred++ = (imgpel) ((P_C + P_E + ((P_D) << 1) + 2) >> 2); + *Pred++ = (imgpel) ((P_D + P_F + ((P_E) << 1) + 2) >> 2); + *Pred++ = (imgpel) ((P_E + P_G + ((P_F) << 1) + 2) >> 2); + *Pred = (imgpel) ((P_F + P_H + ((P_G) << 1) + 2) >> 2); + + memcpy(*cur_pred++, &PredArray[ 3], 8 * sizeof(imgpel)); + memcpy(*cur_pred++, &PredArray[14], 8 * sizeof(imgpel)); + memcpy(*cur_pred++, &PredArray[ 2], 8 * sizeof(imgpel)); + memcpy(*cur_pred++, &PredArray[13], 8 * sizeof(imgpel)); + memcpy(*cur_pred++, &PredArray[ 1], 8 * sizeof(imgpel)); + memcpy(*cur_pred++, &PredArray[12], 8 * sizeof(imgpel)); + memcpy(*cur_pred++, &PredArray[ 0], 8 * sizeof(imgpel)); + memcpy(*cur_pred , &PredArray[11], 8 * sizeof(imgpel)); +} + +/*! + ************************************************************************ + * \brief + * Horizontal Down 8x8 Prediction + ************************************************************************ + */ +static inline void get_i8x8_hordown(imgpel **cur_pred, imgpel *PredPel) +{ + imgpel PredArray[22]; // array of final prediction values + imgpel *Pred = &PredArray[0]; + + *Pred++ = (imgpel) ((P_X + P_W + 1) >> 1); + *Pred++ = (imgpel) ((P_V + P_X + (P_W << 1) + 2) >> 2); + *Pred++ = (imgpel) ((P_W + P_V + 1) >> 1); + *Pred++ = (imgpel) ((P_U + P_W + ((P_V) << 1) + 2) >> 2); + *Pred++ = (imgpel) ((P_V + P_U + 1) >> 1); + *Pred++ = (imgpel) ((P_T + P_V + ((P_U) << 1) + 2) >> 2); + *Pred++ = (imgpel) ((P_U + P_T + 1) >> 1); + *Pred++ = (imgpel) ((P_S + P_U + ((P_T) << 1) + 2) >> 2); + *Pred++ = (imgpel) ((P_T + P_S + 1) >> 1); + *Pred++ = (imgpel) ((P_R + P_T + ((P_S) << 1) + 2) >> 2); + *Pred++ = (imgpel) ((P_S + P_R + 1) >> 1); + *Pred++ = (imgpel) ((P_Q + P_S + ((P_R) << 1) + 2) >> 2); + *Pred++ = (imgpel) ((P_R + P_Q + 1) >> 1); + *Pred++ = (imgpel) ((P_Z + P_R + ((P_Q) << 1) + 2) >> 2); + *Pred++ = (imgpel) ((P_Q + P_Z + 1) >> 1); + *Pred++ = (imgpel) ((P_Q + P_A + ((P_Z) << 1) + 2) >> 2); + *Pred++ = (imgpel) ((P_Z + P_B + ((P_A) << 1) + 2) >> 2); + *Pred++ = (imgpel) ((P_A + P_C + ((P_B) << 1) + 2) >> 2); + *Pred++ = (imgpel) ((P_B + P_D + ((P_C) << 1) + 2) >> 2); + *Pred++ = (imgpel) ((P_C + P_E + ((P_D) << 1) + 2) >> 2); + *Pred++ = (imgpel) ((P_D + P_F + ((P_E) << 1) + 2) >> 2); + *Pred = (imgpel) ((P_E + P_G + ((P_F) << 1) + 2) >> 2); + + Pred = &PredArray[14]; + memcpy(*cur_pred++, Pred, 8 * sizeof(imgpel)); + Pred -= 2; + memcpy(*cur_pred++, Pred, 8 * sizeof(imgpel)); + Pred -= 2; + memcpy(*cur_pred++, Pred, 8 * sizeof(imgpel)); + Pred -= 2; + memcpy(*cur_pred++, Pred, 8 * sizeof(imgpel)); + Pred -= 2; + memcpy(*cur_pred++, Pred, 8 * sizeof(imgpel)); + Pred -= 2; + memcpy(*cur_pred++, Pred, 8 * sizeof(imgpel)); + Pred -= 2; + memcpy(*cur_pred++, Pred, 8 * sizeof(imgpel)); + Pred -= 2; + memcpy(*cur_pred , Pred, 8 * sizeof(imgpel)); +} + + +/*! + ************************************************************************ + * \brief + * Horizontal Up 8x8 Prediction + ************************************************************************ + */ +static inline void get_i8x8_horup(imgpel **cur_pred, imgpel *PredPel) +{ + imgpel PredArray[22]; + PredArray[ 0] = (imgpel) ((P_Q + P_R + 1) >> 1); + PredArray[ 1] = (imgpel) ((P_S + P_Q + ((P_R) << 1) + 2) >> 2); + PredArray[ 2] = (imgpel) ((P_R + P_S + 1) >> 1); + PredArray[ 3] = (imgpel) ((P_T + P_R + ((P_S) << 1) + 2) >> 2); + PredArray[ 4] = (imgpel) ((P_S + P_T + 1) >> 1); + PredArray[ 5] = (imgpel) ((P_U + P_S + ((P_T) << 1) + 2) >> 2); + PredArray[ 6] = (imgpel) ((P_T + P_U + 1) >> 1); + PredArray[ 7] = (imgpel) ((P_V + P_T + ((P_U) << 1) + 2) >> 2); + PredArray[ 8] = (imgpel) ((P_U + P_V + 1) >> 1); + PredArray[ 9] = (imgpel) ((P_W + P_U + ((P_V) << 1) + 2) >> 2); + PredArray[10] = (imgpel) ((P_V + P_W + 1) >> 1); + PredArray[11] = (imgpel) ((P_X + P_V + ((P_W) << 1) + 2) >> 2); + PredArray[12] = (imgpel) ((P_W + P_X + 1) >> 1); + PredArray[13] = (imgpel) ((P_W + P_X + ((P_X) << 1) + 2) >> 2); + PredArray[14] = (imgpel) P_X; + PredArray[15] = (imgpel) P_X; + PredArray[16] = (imgpel) P_X; + PredArray[17] = (imgpel) P_X; + PredArray[18] = (imgpel) P_X; + PredArray[19] = (imgpel) P_X; + PredArray[20] = (imgpel) P_X; + PredArray[21] = (imgpel) P_X; + + memcpy(*cur_pred++, &PredArray[ 0], 8 * sizeof(imgpel)); + memcpy(*cur_pred++, &PredArray[ 2], 8 * sizeof(imgpel)); + memcpy(*cur_pred++, &PredArray[ 4], 8 * sizeof(imgpel)); + memcpy(*cur_pred++, &PredArray[ 6], 8 * sizeof(imgpel)); + memcpy(*cur_pred++, &PredArray[ 8], 8 * sizeof(imgpel)); + memcpy(*cur_pred++, &PredArray[10], 8 * sizeof(imgpel)); + memcpy(*cur_pred++, &PredArray[12], 8 * sizeof(imgpel)); + memcpy(*cur_pred , &PredArray[14], 8 * sizeof(imgpel)); +} + + +/*! + ************************************************************************ + * \brief + * Set intra 8x8 prediction samples + * + * \par Input: + * Starting point of current 8x8 block image posision + * + * \par Output: + * none + ************************************************************************ + */ +void set_intrapred_8x8(Macroblock *currMB, ColorPlane pl, int img_x,int img_y, int *left_available, int *up_available, int *all_available) +{ + VideoParameters *p_Vid = currMB->p_Vid; + InputParameters *p_Inp = currMB->p_Inp; + + imgpel *PredPel = currMB->intra8x8_pred[pl]; // array of predictor pels + imgpel **img_enc = p_Vid->enc_picture->p_curr_img; + int *mb_size = p_Vid->mb_size[IS_LUMA]; + + int ioff = (img_x & 15); + int joff = (img_y & 15); + + PixelPos pix_a, pix_b, pix_c, pix_d; + + int block_available_up; + int block_available_left; + int block_available_up_left; + int block_available_up_right; + + p_Vid->getNeighbour(currMB, ioff - 1, joff , mb_size, &pix_a); + p_Vid->getNeighbour(currMB, ioff , joff - 1, mb_size, &pix_b); + p_Vid->getNeighbour(currMB, ioff + 8, joff - 1, mb_size, &pix_c); + p_Vid->getNeighbour(currMB, ioff - 1, joff - 1, mb_size, &pix_d); + + pix_c.available = pix_c.available &&!(ioff == 8 && joff == 8); + + if (p_Inp->UseConstrainedIntraPred) + { + block_available_left = pix_a.available ? p_Vid->intra_block [pix_a.mb_addr]: 0; + block_available_up = pix_b.available ? p_Vid->intra_block [pix_b.mb_addr] : 0; + block_available_up_right = pix_c.available ? p_Vid->intra_block [pix_c.mb_addr] : 0; + block_available_up_left = pix_d.available ? p_Vid->intra_block [pix_d.mb_addr] : 0; + } + else + { + block_available_left = pix_a.available; + block_available_up = pix_b.available; + block_available_up_right = pix_c.available; + block_available_up_left = pix_d.available; + } + + *left_available = block_available_left; + *up_available = block_available_up; + *all_available = block_available_up && block_available_left && block_available_up_left; + + // form predictor pels + if (block_available_up) + { + memcpy(&PredPel[1], &img_enc[pix_b.pos_y][pix_b.pos_x], 8 * sizeof(imgpel)); + } + else + { + P_A = P_B = P_C = P_D = P_E = P_F = P_G = P_H = p_Vid->dc_pred_value; + } + + if (block_available_up_right) + { + memcpy(&PredPel[9], &img_enc[pix_c.pos_y][pix_c.pos_x], 8 * sizeof(imgpel)); + } + else + { + P_I = P_J = P_K = P_L = P_M = P_N = P_O = P_P = P_H; + } + + if (block_available_left) + { + int pos_y = pix_a.pos_y; + int pos_x = pix_a.pos_x; + P_Q = img_enc[pos_y++][pos_x]; + P_R = img_enc[pos_y++][pos_x]; + P_S = img_enc[pos_y++][pos_x]; + P_T = img_enc[pos_y++][pos_x]; + P_U = img_enc[pos_y++][pos_x]; + P_V = img_enc[pos_y++][pos_x]; + P_W = img_enc[pos_y++][pos_x]; + P_X = img_enc[pos_y ][pos_x]; + } + else + { + P_Q = P_R = P_S = P_T = P_U = P_V = P_W = P_X = p_Vid->dc_pred_value; + } + + if (block_available_up_left) + { + P_Z = img_enc[pix_d.pos_y][pix_d.pos_x]; + } + else + { + P_Z = p_Vid->dc_pred_value; + } + + LowPassForIntra8x8Pred(PredPel, block_available_up_left, block_available_up, block_available_left); +} + + +/*! + ************************************************************************ + * \brief + * Set intra 8x8 prediction samples + * + * \par Input: + * Starting point of current 8x8 block image posision + * + * \par Output: + * none + ************************************************************************ + */ +void set_intrapred_8x8_mbaff(Macroblock *currMB, ColorPlane pl, int img_x,int img_y, int *left_available, int *up_available, int *all_available) +{ + VideoParameters *p_Vid = currMB->p_Vid; + InputParameters *p_Inp = currMB->p_Inp; + + int i; + imgpel *PredPel = currMB->intra8x8_pred[pl]; // array of predictor pels + imgpel **img_enc = p_Vid->enc_picture->p_curr_img; + int *mb_size = p_Vid->mb_size[IS_LUMA]; + + int ioff = (img_x & 15); + int joff = (img_y & 15); + + PixelPos pix_a[8]; + PixelPos pix_b, pix_c, pix_d; + + int block_available_up; + int block_available_left; + int block_available_up_left; + int block_available_up_right; + + for (i=0;i<8;i++) + { + p_Vid->getNeighbour(currMB, ioff - 1, joff + i , mb_size, &pix_a[i]); + } + + p_Vid->getNeighbour(currMB, ioff , joff - 1, mb_size, &pix_b); + p_Vid->getNeighbour(currMB, ioff + 8, joff - 1, mb_size, &pix_c); + p_Vid->getNeighbour(currMB, ioff - 1, joff - 1, mb_size, &pix_d); + + pix_c.available = pix_c.available &&!(ioff == 8 && joff == 8); + + if (p_Inp->UseConstrainedIntraPred) + { + for (i=0, block_available_left=1; i<8;i++) + block_available_left &= pix_a[i].available ? p_Vid->intra_block[pix_a[i].mb_addr]: 0; + + block_available_up = pix_b.available ? p_Vid->intra_block [pix_b.mb_addr] : 0; + block_available_up_right = pix_c.available ? p_Vid->intra_block [pix_c.mb_addr] : 0; + block_available_up_left = pix_d.available ? p_Vid->intra_block [pix_d.mb_addr] : 0; + } + else + { + block_available_left = pix_a[0].available; + block_available_up = pix_b.available; + block_available_up_right = pix_c.available; + block_available_up_left = pix_d.available; + } + + *left_available = block_available_left; + *up_available = block_available_up; + *all_available = block_available_up && block_available_left && block_available_up_left; + + // form predictor pels + if (block_available_up) + { + memcpy(&PredPel[1], &img_enc[pix_b.pos_y][pix_b.pos_x], 8 * sizeof(imgpel)); + } + else + { + P_A = P_B = P_C = P_D = P_E = P_F = P_G = P_H = p_Vid->dc_pred_value; + } + + if (block_available_up_right) + { + memcpy(&PredPel[9], &img_enc[pix_c.pos_y][pix_c.pos_x], 8 * sizeof(imgpel)); + } + else + { + P_I = P_J = P_K = P_L = P_M = P_N = P_O = P_P = P_H; + } + + if (block_available_left) + { + P_Q = img_enc[pix_a[0].pos_y][pix_a[0].pos_x]; + P_R = img_enc[pix_a[1].pos_y][pix_a[1].pos_x]; + P_S = img_enc[pix_a[2].pos_y][pix_a[2].pos_x]; + P_T = img_enc[pix_a[3].pos_y][pix_a[3].pos_x]; + P_U = img_enc[pix_a[4].pos_y][pix_a[4].pos_x]; + P_V = img_enc[pix_a[5].pos_y][pix_a[5].pos_x]; + P_W = img_enc[pix_a[6].pos_y][pix_a[6].pos_x]; + P_X = img_enc[pix_a[7].pos_y][pix_a[7].pos_x]; + } + else + { + P_Q = P_R = P_S = P_T = P_U = P_V = P_W = P_X = p_Vid->dc_pred_value; + } + + if (block_available_up_left) + { + P_Z = img_enc[pix_d.pos_y][pix_d.pos_x]; + } + else + { + P_Z = p_Vid->dc_pred_value; + } + + LowPassForIntra8x8Pred(PredPel, block_available_up_left, block_available_up, block_available_left); +} + +/*! + ************************************************************************ + * \brief + * Generate 8x8 intra prediction block + * + * \par Input: + * Starting point of current 8x8 block image posision + * + * \par Output: + * none + ************************************************************************ + */ +void get_intrapred_8x8(Macroblock *currMB, ColorPlane pl, int i8x8_mode, int left_available, int up_available) +{ + imgpel *PredPel = currMB->intra8x8_pred[pl]; // array of predictor pels + Slice *currSlice = currMB->p_Slice; + imgpel ***curr_mpr_8x8 = currSlice->mpr_8x8[pl]; + + switch (i8x8_mode) + { + case VERT_PRED : + get_i8x8_vertical(curr_mpr_8x8[VERT_PRED], PredPel); + break; + case HOR_PRED : + get_i8x8_horizontal(curr_mpr_8x8[HOR_PRED], PredPel); + break; + case DC_PRED : + get_i8x8_dc(curr_mpr_8x8[DC_PRED], PredPel, left_available, up_available); + break; + case DIAG_DOWN_LEFT_PRED : + get_i8x8_downleft(curr_mpr_8x8[DIAG_DOWN_LEFT_PRED], PredPel); + break; + case DIAG_DOWN_RIGHT_PRED : + get_i8x8_downright(curr_mpr_8x8[DIAG_DOWN_RIGHT_PRED], PredPel); + break; + case VERT_RIGHT_PRED : + get_i8x8_vertright(curr_mpr_8x8[VERT_RIGHT_PRED], PredPel); + break; + case HOR_DOWN_PRED : + get_i8x8_hordown(curr_mpr_8x8[HOR_DOWN_PRED], PredPel); + break; + case VERT_LEFT_PRED : + get_i8x8_vertleft(curr_mpr_8x8[VERT_LEFT_PRED], PredPel); + break; + case HOR_UP_PRED : + get_i8x8_horup(curr_mpr_8x8[HOR_UP_PRED], PredPel); + break; + default: + printf("invalid prediction mode \n"); + break; + } +} + diff --git a/lencod/src/intra_chroma.c b/lencod/src/intra_chroma.c new file mode 100644 index 0000000..ec7b851 --- /dev/null +++ b/lencod/src/intra_chroma.c @@ -0,0 +1,1215 @@ +/*! +************************************************************************************* + * \file intra_chroma.c + * + * \brief + * Intra chroma mode functions + * + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * - Alexis Michael Tourapis + ************************************************************************************* + */ + +#include "contributors.h" + +#include "global.h" +#include "image.h" +#include "mb_access.h" +#include "block.h" +#include "mv_search.h" + +void get_difference_4x4(imgpel **src, imgpel **prd, short *diff, int pos_x, int block_x) +{ + int i, j; + short *p_diff = diff; + imgpel *img_prd, *img_src; + for (j = 0; j < BLOCK_SIZE; j++) + { + img_src = &src[j][pos_x + block_x]; + img_prd = &prd[j][block_x]; + + for (i = 0; i < BLOCK_SIZE; i++) + *p_diff++ = img_src[i] - img_prd[i]; + } +} + +/*! + ************************************************************************ + * \brief + * Vertical Chroma Prediction + ************************************************************************ + */ +static inline void get_iChroma_vertical(imgpel **cur_pred, imgpel *PredPel, int cr_MB_x, int cr_MB_y) +{ + int j; + for (j = 0; j < cr_MB_y; j++) + memcpy(cur_pred[j], &PredPel[1], cr_MB_x * sizeof(imgpel)); +} + + +/*! + ************************************************************************ + * \brief + * Horizontal Chroma Prediction + ************************************************************************ + */ +static inline void get_iChroma_horizontal(imgpel **cur_pred, imgpel *PredPel, int cr_MB_x, int cr_MB_y) +{ + int i, j; + imgpel *prediction = &PredPel[16]; + + for (j = 0; j < cr_MB_y; j++) + { + prediction++; + for (i = 0; i < cr_MB_x; i++) + { + cur_pred[j][i] = *prediction; + } + } +} + +/*! + ************************************************************************ + * \brief + * DC Chroma Prediction + ************************************************************************ + */ +static inline void get_iChroma_dc(imgpel **cur_pred, imgpel *PredPel, int left_available, int up_available) +{ + int i, j, s0 = 0, s1 = 0, s2 = 0; + + if (up_available) + { + for (i = 1; i < MB_BLOCK_SIZE + 1; ++i) + s1 += PredPel[i]; + } + + if (left_available) + { + for (i = 17; i < 33; ++i) + s2 += PredPel[i]; // sum vert pix + } + + if (up_available) + { + s0 = left_available + ? rshift_rnd_sf((s1 + s2),(MB_BLOCK_SHIFT + 1)) // no edge + : rshift_rnd_sf(s1, MB_BLOCK_SHIFT); // left edge + } + else + { + s0 = left_available + ? rshift_rnd_sf(s2, MB_BLOCK_SHIFT) // upper edge + : PredPel[1]; // top left corner, nothing to predict from + } + + for (j = 0; j < MB_BLOCK_SIZE; j++) + { + for (i = 0; i < MB_BLOCK_SIZE; i++) + cur_pred[j][i] = (imgpel) s0; + } +} + +/*! + ************************************************************************ + * \brief + * Plane Chroma Prediction + ************************************************************************ + */ +static inline void get_iChroma_plane(imgpel **cur_pred, imgpel *PredPel, int max_imgpel_value) +{ + int i, j; + // plane prediction + int ih=0, iv=0; + int ib, ic, iaa; + imgpel *t_pred = &PredPel[25]; + imgpel *u_pred = &PredPel[8]; + imgpel *b_pred = &PredPel[23]; + + for (i = 1; i < 8;++i) + { + ih += i*(*(u_pred + i) - *(u_pred - i)); + iv += i*(*t_pred++ - *b_pred--); + } + ih += 8 * (*(u_pred + 8) - PredPel[0]); + iv += 8 * (*t_pred++ - PredPel[0]); + + ib = (5 * ih + 32) >> 6; + ic = (5 * iv + 32) >> 6; + + iaa=16 * (PredPel[16] + PredPel[32]); + + for (j=0;j< MB_BLOCK_SIZE;++j) + { + for (i=0;i< MB_BLOCK_SIZE;++i) + { + cur_pred[j][i]= (imgpel) iClip1( max_imgpel_value, rshift_rnd_sf((iaa + (i - 7) * ib + (j - 7) * ic), 5));// store plane prediction + } + } +} + +/*! + ************************************************************************ + * \brief + * Set intra Chroma prediction samples + * + * \par Input: + * + * \par Output: + * none + ************************************************************************ + */ +void set_intrapred_chroma(Macroblock *currMB, int *left_available, int *up_available, int *all_available) +{ + VideoParameters *p_Vid = currMB->p_Vid; + InputParameters *p_Inp = currMB->p_Inp; + + int i; + imgpel *PredPelU = currMB->intra16x16_pred[PLANE_U]; // array of predictor pels + imgpel *PredPelV = currMB->intra16x16_pred[PLANE_V]; // array of predictor pels + imgpel **img_enc_u = p_Vid->enc_picture->imgUV[0]; + imgpel **img_enc_v = p_Vid->enc_picture->imgUV[1]; + + PixelPos pix_b; + PixelPos pix_a; + PixelPos pix_d; + int cr_MB_x = p_Vid->mb_cr_size_x; + int cr_MB_y = p_Vid->mb_cr_size_y; + + imgpel dc_pred_value_chroma = p_Vid->dc_pred_value_comp[1]; + + int *mb_size = p_Vid->mb_size[IS_CHROMA]; + + p_Vid->getNeighbour(currMB, -1, -1, mb_size, &pix_d); + p_Vid->getNeighbour(currMB, -1, 0, mb_size, &pix_a); + p_Vid->getNeighbour(currMB, 0, -1, mb_size, &pix_b); + + if (p_Inp->UseConstrainedIntraPred == 0) + { + *up_available = pix_b.available; + *left_available = pix_a.available; + *all_available = pix_d.available; + } + else + { + *up_available = pix_b.available ? p_Vid->intra_block[pix_b.mb_addr] : 0; + *left_available = pix_a.available ? p_Vid->intra_block[pix_a.mb_addr] : 0; + *all_available = pix_d.available ? p_Vid->intra_block[pix_d.mb_addr] : 0; + } + + // form predictor pels + if (*up_available) + { + memcpy(&PredPelU[1], &img_enc_u[pix_b.pos_y][pix_b.pos_x], cr_MB_x * sizeof(imgpel)); + memcpy(&PredPelV[1], &img_enc_v[pix_b.pos_y][pix_b.pos_x], cr_MB_x * sizeof(imgpel)); + } + else + { + for (i = 1; i < cr_MB_x + 1; ++i) + PredPelU[i] = (imgpel) dc_pred_value_chroma; + for (i = 1; i < cr_MB_x + 1; ++i) + PredPelV[i] = (imgpel) dc_pred_value_chroma; + } + + if (*left_available) + { + int pos_y = pix_a.pos_y; + int pos_x = pix_a.pos_x; + + for (i = 1; i < cr_MB_y + 1; ++i) + { + PredPelU[i + cr_MB_x] = img_enc_u[pos_y ][pos_x]; + PredPelV[i + cr_MB_x] = img_enc_v[pos_y++][pos_x]; + } + } + else + { + for (i = cr_MB_x + 1; i < cr_MB_x + cr_MB_y + 1; ++i) + PredPelU[i] = (imgpel) dc_pred_value_chroma; + for (i = cr_MB_x + 1; i < cr_MB_x + cr_MB_y + 1; ++i) + PredPelV[i] = (imgpel) dc_pred_value_chroma; + } + + if (*all_available) + { + PredPelU[0] = img_enc_u[pix_d.pos_y][pix_d.pos_x]; + PredPelV[0] = img_enc_v[pix_d.pos_y][pix_d.pos_x]; + } + else + { + PredPelU[0] = dc_pred_value_chroma; + PredPelV[0] = dc_pred_value_chroma; + } +} + + +/*! + ************************************************************************ + * \brief + * Set intra Chroma prediction samples (MBAFF) + * + * \par Input: + * + * \par Output: + * none + ************************************************************************ + */ +void set_intrapred_chroma_mbaff(Macroblock *currMB, ColorPlane pl, int *left_available, int *up_available, int *all_available) +{ + VideoParameters *p_Vid = currMB->p_Vid; + InputParameters *p_Inp = currMB->p_Inp; + + int i; + imgpel *PredPelU = currMB->intra16x16_pred[PLANE_U]; // array of predictor pels + imgpel *PredPelV = currMB->intra16x16_pred[PLANE_V]; // array of predictor pels + imgpel **img_enc_u = p_Vid->enc_picture->imgUV[0]; + imgpel **img_enc_v = p_Vid->enc_picture->imgUV[1]; + + PixelPos pix_b; + PixelPos pix_a[17]; + int cr_MB_x = p_Vid->mb_cr_size_x; + int cr_MB_y = p_Vid->mb_cr_size_y; + + imgpel dc_pred_value_chroma = p_Vid->dc_pred_value_comp[1]; + + int *mb_size = p_Vid->mb_size[IS_CHROMA]; + + for (i = 0; i < cr_MB_y + 1; ++i) + { + p_Vid->getNeighbour(currMB, -1, i - 1, mb_size, &pix_a[i]); + } + + p_Vid->getNeighbour(currMB, 0, -1, mb_size, &pix_b); + + if (p_Inp->UseConstrainedIntraPred == 0) + { + *up_available = pix_b.available; + *left_available = pix_a[1].available; + *all_available = pix_a[0].available; + } + else + { + *up_available = pix_b.available ? p_Vid->intra_block[pix_b.mb_addr] : 0; + for (i=1, *left_available=1; i<17;++i) + *left_available &= pix_a[i].available ? p_Vid->intra_block[pix_a[i].mb_addr]: 0; + *all_available = pix_a[0].available ? p_Vid->intra_block[pix_a[0].mb_addr]: 0; + } + + // form predictor pels + if (*up_available) + { + memcpy(&PredPelU[1], &img_enc_u[pix_b.pos_y][pix_b.pos_x], cr_MB_x * sizeof(imgpel)); + memcpy(&PredPelV[1], &img_enc_v[pix_b.pos_y][pix_b.pos_x], cr_MB_x * sizeof(imgpel)); + } + else + { + for (i = 1; i < cr_MB_x + 1; ++i) + PredPelU[i] = (imgpel) dc_pred_value_chroma; + for (i = 1; i < cr_MB_x + 1; ++i) + PredPelV[i] = (imgpel) dc_pred_value_chroma; + } + + if (*left_available) + { + for (i = 1; i < cr_MB_y + 1; ++i) + PredPelU[i + cr_MB_x] = img_enc_u[pix_a[i].pos_y][pix_a[i].pos_x]; + for (i = 1; i < cr_MB_y + 1; ++i) + PredPelV[i + cr_MB_x] = img_enc_v[pix_a[i].pos_y][pix_a[i].pos_x]; + } + else + { + for (i = cr_MB_x + 1; i < cr_MB_x + cr_MB_y + 1; ++i) + PredPelU[i] = (imgpel) dc_pred_value_chroma; + for (i = cr_MB_x + 1; i < cr_MB_x + cr_MB_y + 1; ++i) + PredPelV[i] = (imgpel) dc_pred_value_chroma; + } + + if (*all_available) + { + PredPelU[0] = img_enc_u[pix_a[0].pos_y][pix_a[0].pos_x]; + PredPelV[0] = img_enc_v[pix_a[0].pos_y][pix_a[0].pos_x]; + } + else + { + PredPelU[0] = dc_pred_value_chroma; + PredPelV[0] = dc_pred_value_chroma; + } +} + + +/*! + ************************************************************************ + * \brief + * Generate Chroma intra prediction block + * + * \par Input: + * Starting point of current Chroma block image posision + * + * \par Output: + * none + ************************************************************************ + */ +void get_intrapred_chroma(Macroblock *currMB, ColorPlane pl, int iChroma_mode, int left_available, int up_available) +{ + VideoParameters *p_Vid = currMB->p_Vid; + imgpel *PredPel = currMB->intra16x16_pred[pl]; // array of predictor pels + imgpel ***curr_mpr_16x16 = currMB->p_Slice->mpr_16x16[pl]; + int cr_MB_x = p_Vid->mb_cr_size_x; + int cr_MB_y = p_Vid->mb_cr_size_y; + + switch (iChroma_mode) + { + case VERT_PRED_8 : + get_iChroma_vertical(curr_mpr_16x16[VERT_PRED_16], PredPel, cr_MB_x, cr_MB_y); + break; + case HOR_PRED_8 : + get_iChroma_horizontal(curr_mpr_16x16[HOR_PRED_16], PredPel, cr_MB_x, cr_MB_y); + break; + case DC_PRED_8 : + get_iChroma_dc(curr_mpr_16x16[DC_PRED_16], PredPel, left_available, up_available); + break; + case PLANE_8 : + get_iChroma_plane(curr_mpr_16x16[PLANE_16], PredPel, currMB->p_Vid->max_imgpel_value); + break; + default: + printf("invalid prediction mode \n"); + break; + } +} + +void rdo_low_intra_chroma_decision_mbaff(Macroblock *currMB, int mb_available_up, int mb_available_left[2], int mb_available_up_left) +{ // since ipredmodes could be overwritten => encoder-decoder-mismatches + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currSlice->p_Vid; + InputParameters *p_Inp = currSlice->p_Inp; + int i, j, uv; + int cr_MB_x = p_Vid->mb_cr_size_x; + int cr_MB_y = p_Vid->mb_cr_size_y; + int block_x, block_y; + PixelPos pix_a[17]; //!< pixel positions p(-1, -1..15) + imgpel **image = NULL; + imgpel ***curr_mpr_16x16 = NULL; + + + short diff [16]; + short *p_diff; + distblk cost, min_cost = DISTBLK_MAX; + int mode; + int best_mode = DC_PRED_8; //just an initilaization here, should always be overwritten + imgpel *img_org = NULL, *img_prd = NULL; + + for (i=0;igetNeighbour(currMB, 0 , i, p_Vid->mb_size[IS_CHROMA], &pix_a[i]); + } + + if ( p_Vid->mb_aff_frame_flag && p_Vid->field_mode ) + { + for (i=0;i> 1; + } + } + + for (mode=DC_PRED_8; mode<=PLANE_8; mode++) + { + if (((currSlice->slice_type != I_SLICE && currSlice->slice_type != SI_SLICE) || !p_Inp->IntraDisableInterOnly) && p_Inp->ChromaIntraDisable == 1 && mode!=DC_PRED_8) + continue; + + if ((mode==VERT_PRED_8 && !mb_available_up) || + (mode==HOR_PRED_8 && (!mb_available_left[0] || !mb_available_left[1])) || + (mode==PLANE_8 && (!mb_available_left[0] || !mb_available_left[1] || !mb_available_up || !mb_available_up_left))) + continue; + + cost = 0; + for (uv = 1; uv < 3; uv++) + { + image = p_Vid->pImgOrg[uv]; + curr_mpr_16x16 = currSlice->mpr_16x16[uv]; + + for (block_y = 0; block_y < cr_MB_y; block_y += 4) + { + for (block_x = 0; block_x < cr_MB_x; block_x += 4) + { + p_diff = &diff[0]; + for (j = block_y; j < block_y + 4; j++) + { + img_prd = curr_mpr_16x16[mode][j]; + img_org = &image[pix_a[j].pos_y][pix_a[j].pos_x]; + for (i = block_x; i < block_x + 4; i++) + *p_diff++ = img_org[i] - img_prd[i]; + } + cost += p_Vid->distortion4x4(diff, min_cost); + if (cost > min_cost) break; + } + if (cost > min_cost) break; + } + if (cost > min_cost) break; + } + if (cost < min_cost) + { + best_mode = mode; + min_cost = cost; + } + } + currMB->c_ipred_mode = (char) best_mode; +} + + +void rdo_low_intra_chroma_decision(Macroblock *currMB, int mb_available_up, int mb_available_left[2], int mb_available_up_left) +{ // since ipredmodes could be overwritten => encoder-decoder-mismatches + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currSlice->p_Vid; + InputParameters *p_Inp = currSlice->p_Inp; + int uv; + int cr_MB_x = p_Vid->mb_cr_size_x; + int cr_MB_y = p_Vid->mb_cr_size_y; + int block_x, block_y; + PixelPos pix_a; //!< pixel positions p(-1, 0) + + imgpel **image = NULL; + imgpel ***curr_mpr_16x16 = NULL; + + short diff [16]; + + distblk cost, min_cost = DISTBLK_MAX; + int mode; + int best_mode = DC_PRED_8; //just an initialization here, should always be overwritten + + p_Vid->getNeighbour(currMB, 0 , 0, p_Vid->mb_size[IS_CHROMA], &pix_a); + + for (mode = DC_PRED_8; mode <= PLANE_8; mode++) + { + if (((currSlice->slice_type != I_SLICE && currSlice->slice_type != SI_SLICE) || !p_Inp->IntraDisableInterOnly) && p_Inp->ChromaIntraDisable == 1 && mode!=DC_PRED_8) + continue; + + if ((mode==VERT_PRED_8 && !mb_available_up) || + (mode==HOR_PRED_8 && (!mb_available_left[0] || !mb_available_left[1])) || + (mode==PLANE_8 && (!mb_available_left[0] || !mb_available_left[1] || !mb_available_up || !mb_available_up_left))) + continue; + + cost = 0; + + for (uv = 1; uv < 3; uv++) + { + int pos_x = pix_a.pos_x; + int pos_y = pix_a.pos_y; + image = p_Vid->pImgOrg[uv]; + curr_mpr_16x16 = currSlice->mpr_16x16[uv]; + + for (block_y = 0; block_y < cr_MB_y; block_y += BLOCK_SIZE) + { + for (block_x = 0; block_x < cr_MB_x; block_x += BLOCK_SIZE) + { + get_difference_4x4(&image[pos_y + block_y], &curr_mpr_16x16[mode][block_y], diff, pos_x, block_x); + cost += p_Vid->distortion4x4(diff, min_cost); + if (cost > min_cost) break; + } + if (cost > min_cost) break; + } + if (cost > min_cost) break; + } + + if (cost < min_cost) + { + best_mode = mode; + min_cost = cost; + } + } + currMB->c_ipred_mode = (char) best_mode; +} + + +/*! + ************************************************************************ + * \brief + * Intra prediction of the chrminance layers of one macroblock + ************************************************************************ + */ +void intra_chroma_prediction (Macroblock *currMB, int *mb_up, int *mb_left, int*mb_up_left) +{ + int s, i, j; + + int uv; + int b8, b4; + imgpel vline[16]; + + int mb_available_up; + int mb_available_left[2]; + int mb_available_up_left; + + PixelPos pix_c; //!< pixel position p(0,-1) + PixelPos pix_d; + PixelPos pix_a; //!< pixel positions p(-1, -1..15) + + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currSlice->p_Vid; + InputParameters *p_Inp = currSlice->p_Inp; + int cr_MB_x = p_Vid->mb_cr_size_x; + int cr_MB_y = p_Vid->mb_cr_size_y; + imgpel **cur_pred = NULL; + + imgpel *hline = NULL; + + int yuv = p_Vid->yuv_format - 1; + int dc_pred_value_chroma = p_Vid->dc_pred_value_comp[1]; + int max_imgpel_value_uv = p_Vid->max_pel_value_comp[1]; + + static const int block_pos[3][4][4]= //[yuv][b8][b4] + { + { {0, 1, 2, 3},{0, 0, 0, 0},{0, 0, 0, 0},{0, 0, 0, 0}}, + { {0, 1, 2, 3},{2, 3, 2, 3},{0, 0, 0, 0},{0, 0, 0, 0}}, + { {0, 1, 2, 3},{1, 1, 3, 3},{2, 3, 2, 3},{3, 3, 3, 3}} + }; + + p_Vid->getNeighbour(currMB, -1, -1, p_Vid->mb_size[IS_CHROMA], &pix_d); + p_Vid->getNeighbour(currMB, -1, 0, p_Vid->mb_size[IS_CHROMA], &pix_a); + p_Vid->getNeighbour(currMB, 0, -1, p_Vid->mb_size[IS_CHROMA], &pix_c); + + mb_available_up = pix_c.available; + mb_available_up_left = pix_d.available; + mb_available_left[0] = mb_available_left[1] = pix_a.available; + + if(p_Inp->UseConstrainedIntraPred) + { + mb_available_up = pix_c.available ? p_Vid->intra_block[pix_c.mb_addr] : 0; + mb_available_left[0] = mb_available_left[1] = pix_a.available ? p_Vid->intra_block[pix_a.mb_addr] : 0; + mb_available_up_left = pix_d.available ? p_Vid->intra_block[pix_d.mb_addr] : 0; + } + + if (mb_up) + *mb_up = mb_available_up; + if (mb_left) + *mb_left = mb_available_left[0]; + if (mb_up_left) + *mb_up_left = mb_available_up_left; + + // compute all chroma intra prediction modes for both U and V + for (uv=0; uv<2; uv++) + { + imgpel **image = p_Vid->enc_picture->imgUV[uv]; + imgpel ***curr_mpr_16x16 = currSlice->mpr_16x16[uv + 1]; + + // DC prediction + for(b8=0; b8num_blk8x8_uv >> 1;b8++) + { + for (b4 = 0; b4 < 4; b4++) + { + int block_y = subblk_offset_y[yuv][b8][b4]; + int block_x = subblk_offset_x[yuv][b8][b4]; + int blk_x = block_x; + + s = dc_pred_value_chroma; + + //===== get prediction value ===== + switch (block_pos[yuv][b8][b4]) + { + case 0: //===== TOP LEFT ===== + { + int s0 = 0, s2 = 0; + if (mb_available_up) + { + int pos_x = pix_c.pos_x + blk_x; + int pos_y = pix_c.pos_y; + + for (i = 0; i < BLOCK_SIZE; i++) + s0 += image[pos_y][pos_x++]; + } + if (mb_available_left[0]) + { + int pos_x = pix_a.pos_x; + int pos_y = pix_a.pos_y + block_y; + + for (i = 0; i < BLOCK_SIZE;i++) + s2 += image[pos_y++][pos_x]; + } + if (mb_available_up && mb_available_left[0]) + s = (s0 + s2 + 4) >> 3; + else if (mb_available_up) + s = (s0 + 2) >> 2; + else if (mb_available_left[0]) + s = (s2 + 2) >> 2; + } + break; + case 1: //===== TOP RIGHT ===== + { + int s1 = 0, s2 = 0; + if (mb_available_up) + { + int pos_x = pix_c.pos_x + blk_x; + int pos_y = pix_c.pos_y; + for (i = 0; i < BLOCK_SIZE; i++) + s1 += image[pos_y][pos_x++]; + } + else if (mb_available_left[0]) + { + int pos_x = pix_a.pos_x; + int pos_y = pix_a.pos_y + block_y; + + for (i = 0; i < BLOCK_SIZE; i++) + s2 += image[pos_y++][pos_x]; + } + if (mb_available_up) + s = (s1 +2) >> 2; + else if (mb_available_left[0]) + s = (s2 +2) >> 2; + } + break; + case 2: //===== BOTTOM LEFT ===== + if (mb_available_left[0]) + { + int pos_x = pix_a.pos_x; + int pos_y = pix_a.pos_y + block_y; + int s3 = 0; + + for (i = 0; i < BLOCK_SIZE; i++) + s3 += image[pos_y++][pos_x]; + s = (s3 + 2) >> 2; + } + else if (mb_available_up) + { + int pos_x = pix_c.pos_x + blk_x; + int pos_y = pix_c.pos_y; + + int s0 = 0; + for (i = 0; i < BLOCK_SIZE; i++) + s0 += image[pos_y][pos_x++]; + s = (s0 + 2) >> 2; + } + break; + case 3: //===== BOTTOM RIGHT ===== + { + int s1 = 0, s3 = 0; + if (mb_available_up) + for (i=blk_x;i<(blk_x+4);i++) + s1 += image[pix_c.pos_y][pix_c.pos_x + i]; + if (mb_available_left[0]) + { + int pos_x = pix_a.pos_x; + int pos_y = pix_a.pos_y + block_y; + for (i = 0; i < BLOCK_SIZE;i++) + s3 += image[pos_y++][pos_x]; + } + if (mb_available_up && mb_available_left[0]) + s = (s1 + s3 + 4) >> 3; + else if (mb_available_up) + s = (s1 + 2) >> 2; + else if (mb_available_left[0]) + s = (s3 + 2) >> 2; + } + break; + } + + //===== prediction ===== + cur_pred = curr_mpr_16x16[DC_PRED_8]; + for (j = block_y; j < block_y+4; j++) + { + for (i = block_x; i < block_x+4; i++) + { + cur_pred[j][i] = (imgpel) s; + } + } + } + } + + // vertical prediction + if (mb_available_up) + { + cur_pred = curr_mpr_16x16[VERT_PRED_8]; + hline = &image[pix_c.pos_y][pix_c.pos_x]; + for (j=0; j> 1); + int cr_y = (cr_MB_y >> 1); + + int iaa, iv, ib, ic; + int ih = cr_x * (hline[cr_MB_x-1] - image[pix_d.pos_y][pix_d.pos_x]); + + for (i = 0; i < cr_x - 1; i++) + ih += (i + 1)*(hline[cr_x + i] - hline[cr_x - 2 - i]); + + iv = cr_y * (vline[cr_MB_y-1] - image[pix_d.pos_y][pix_d.pos_x]); + for (i = 0; i < cr_y - 1; i++) + iv += (i + 1) * (vline[cr_y + i] - vline[cr_y - 2 - i]); + + if (cr_MB_x == 8) + ib = (17 * ih + 2 * cr_MB_x) >> 5; + else + ib = ( 5 * ih + 2 * cr_MB_x) >> 6; + + if (cr_MB_y == 8) + ic = (17 * iv + 2 * cr_MB_y) >> 5; + else + ic = ( 5 * iv + 2 * cr_MB_y) >> 6; + + iaa = 16 * (hline[cr_MB_x - 1] + vline[cr_MB_y - 1]); + cur_pred = curr_mpr_16x16[PLANE_8]; + iaa += (1 - cr_x) * ib + (1 - cr_y) * ic; + for (j = 0; j < cr_MB_y; j++) + for (i = 0; i < cr_MB_x; i++) + cur_pred[j][i]= (imgpel) iClip1( max_imgpel_value_uv, (iaa + i * ib + j * ic + 16)>>5); + } + } + + if (!p_Inp->rdopt) // the rd-opt part does not work correctly (see encode_one_macroblock) + { // since ipredmodes could be overwritten => encoder-decoder-mismatches + currSlice->rdo_low_intra_chroma_decision(currMB, mb_available_up, mb_available_left, mb_available_up_left); + } +} + +/*! + ************************************************************************ + * \brief + * Intra prediction of the chrminance layers of one macroblock + ************************************************************************ + */ +void intra_chroma_prediction_mbaff(Macroblock *currMB, int *mb_up, int *mb_left, int*mb_up_left) +{ + int s, i, j; + + int uv; + int b8,b4; + imgpel vline[16]; + + int mb_available_up; + int mb_available_left[2]; + int mb_available_up_left; + + PixelPos pix_b; //!< pixel position p(0,-1) + PixelPos pix_a[17]; //!< pixel positions p(-1, -1..15) + + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currSlice->p_Vid; + InputParameters *p_Inp = currSlice->p_Inp; + int cr_MB_x = p_Vid->mb_cr_size_x; + int cr_MB_y = p_Vid->mb_cr_size_y; + imgpel **cur_pred = NULL; + imgpel ***curr_mpr_16x16 = NULL; + imgpel *hline = NULL; + + int yuv = p_Vid->yuv_format - 1; + int dc_pred_value_chroma = p_Vid->dc_pred_value_comp[1]; + int max_imgpel_value_uv = p_Vid->max_pel_value_comp[1]; + int *mb_size = p_Vid->mb_size[IS_CHROMA]; + + static const int block_pos[3][4][4]= //[yuv][b8][b4] + { + { {0, 1, 2, 3},{0, 0, 0, 0},{0, 0, 0, 0},{0, 0, 0, 0}}, + { {0, 1, 2, 3},{2, 3, 2, 3},{0, 0, 0, 0},{0, 0, 0, 0}}, + { {0, 1, 2, 3},{1, 1, 3, 3},{2, 3, 2, 3},{3, 3, 3, 3}} + }; + + for (i = 0; i < cr_MB_y + 1; ++i) + { + p_Vid->getNeighbour(currMB, -1 , i-1 , mb_size, &pix_a[i]); + } + + p_Vid->getNeighbour(currMB, 0 , -1 , mb_size, &pix_b); + + if (p_Inp->UseConstrainedIntraPred == 0) + { + mb_available_up = pix_b.available; + mb_available_up_left = pix_a[0].available; + mb_available_left[0] = mb_available_left[1] = pix_a[1].available; + } + else + { + mb_available_up = pix_b.available ? p_Vid->intra_block[pix_b.mb_addr] : 0; + for (i=0, mb_available_left[0] = 1; i < (cr_MB_y >> 1); i++) + mb_available_left[0] &= pix_a[i + 1].available ? p_Vid->intra_block[pix_a[i+1].mb_addr]: 0; + for (i=(cr_MB_y>>1), mb_available_left[1]=1; iintra_block[pix_a[i+1].mb_addr]: 0; + mb_available_up_left = pix_a[0].available ? p_Vid->intra_block[pix_a[0].mb_addr]: 0; + } + + if (mb_up) + *mb_up = mb_available_up; + if (mb_left) + *mb_left = mb_available_left[0] && mb_available_left[1]; + if (mb_up_left) + *mb_up_left = mb_available_up_left; + + + // compute all chroma intra prediction modes for both U and V + for (uv=0; uv<2; uv++) + { + imgpel **image = p_Vid->enc_picture->imgUV[uv]; + curr_mpr_16x16 = currSlice->mpr_16x16[uv + 1]; + + // DC prediction + for(b8=0; b8num_blk8x8_uv >> 1;b8++) + { + for (b4 = 0; b4 < 4; b4++) + { + int block_y = subblk_offset_y[yuv][b8][b4]; + int block_x = subblk_offset_x[yuv][b8][b4]; + int blk_x = block_x; + int blk_y = block_y + 1; + + s = dc_pred_value_chroma; + + //===== get prediction value ===== + switch (block_pos[yuv][b8][b4]) + { + case 0: //===== TOP LEFT ===== + { + int s0 = 0, s2 = 0; + if (mb_available_up) + for (i = blk_x; i < (blk_x + 4); i++) + s0 += image[pix_b.pos_y][pix_b.pos_x + i]; + if (mb_available_left[0]) + for (i = blk_y; i < (blk_y + 4);i++) + s2 += image[pix_a[i].pos_y][pix_a[i].pos_x]; + if (mb_available_up && mb_available_left[0]) + s = (s0 + s2 + 4) >> 3; + else if (mb_available_up) + s = (s0 + 2) >> 2; + else if (mb_available_left[0]) + s = (s2 + 2) >> 2; + } + break; + case 1: //===== TOP RIGHT ===== + { + int s1 = 0, s2 = 0; + if (mb_available_up) + for (i=blk_x;i<(blk_x+4);i++) + s1 += image[pix_b.pos_y][pix_b.pos_x + i]; + else if (mb_available_left[0]) + for (i=blk_y;i<(blk_y+4);i++) + s2 += image[pix_a[i].pos_y][pix_a[i].pos_x]; + if (mb_available_up) + s = (s1 +2) >> 2; + else if (mb_available_left[0]) + s = (s2 +2) >> 2; + } + break; + case 2: //===== BOTTOM LEFT ===== + if (mb_available_left[1]) + { + int s3 = 0; + for (i=blk_y;i<(blk_y+4);i++) + s3 += image[pix_a[i].pos_y][pix_a[i].pos_x]; + s = (s3 + 2) >> 2; + } + else if (mb_available_up) + { + int s0 = 0; + for (i=blk_x;i<(blk_x+4);i++) + s0 += image[pix_b.pos_y][pix_b.pos_x + i]; + s = (s0 + 2) >> 2; + } + break; + case 3: //===== BOTTOM RIGHT ===== + { + int s1 = 0, s3 = 0; + if (mb_available_up) + for (i=blk_x;i<(blk_x+4);i++) + s1 += image[pix_b.pos_y][pix_b.pos_x + i]; + if (mb_available_left[1]) + for (i=blk_y;i<(blk_y+4);i++) + s3 += image[pix_a[i].pos_y][pix_a[i].pos_x]; + if (mb_available_up && mb_available_left[1]) + s = (s1 + s3 + 4) >> 3; + else if (mb_available_up) + s = (s1 + 2) >> 2; + else if (mb_available_left[1]) + s = (s3 + 2) >> 2; + } + break; + } + + //===== prediction ===== + cur_pred = curr_mpr_16x16[DC_PRED_8]; + for (j = block_y; j < block_y+4; j++) + { + for (i = block_x; i < block_x+4; i++) + { + cur_pred[j][i] = (imgpel) s; + } + } + } + } + + // vertical prediction + if (mb_available_up) + { + cur_pred = curr_mpr_16x16[VERT_PRED_8]; + hline = &image[pix_b.pos_y][pix_b.pos_x]; + for (j=0; j> 1); + int cr_y = (cr_MB_y >> 1); + + int iaa, iv, ib, ic; + int ih = cr_x * (hline[cr_MB_x-1] - image[pix_a[0].pos_y][pix_a[0].pos_x]); + for (i = 0; i < cr_x - 1; i++) + ih += (i+1)*(hline[cr_x + i] - hline[cr_x - 2 - i]); + + iv = cr_y * (vline[cr_MB_y-1] - image[pix_a[0].pos_y][pix_a[0].pos_x]); + for (i = 0; i < cr_y - 1; i++) + iv += (i + 1) * (vline[cr_y + i] - vline[cr_y - 2 - i]); + + if (cr_MB_x == 8) + ib = (17 * ih + 2 * cr_MB_x) >> 5; + else + ib = ( 5 * ih + 2 * cr_MB_x) >> 6; + if (cr_MB_y == 8) + ic = (17 * iv + 2 * cr_MB_y) >> 5; + else + ic = ( 5 * iv + 2 * cr_MB_y) >> 6; + + iaa = 16 * (hline[cr_MB_x - 1] + vline[cr_MB_y - 1]); + cur_pred = curr_mpr_16x16[PLANE_8]; + iaa += (1 - cr_x) * ib + (1 - cr_y) * ic; + for (j = 0; j < cr_MB_y; j++) + for (i = 0; i < cr_MB_x; i++) + cur_pred[j][i]= (imgpel) iClip1( max_imgpel_value_uv, (iaa + i * ib + j * ic + 16)>>5); + } + } + + if (!p_Inp->rdopt) // the rd-opt part does not work correctly (see encode_one_macroblock) + { // since ipredmodes could be overwritten => encoder-decoder-mismatches + currSlice->rdo_low_intra_chroma_decision(currMB, mb_available_up, mb_available_left, mb_available_up_left); + } +} + +/*! + ************************************************************************************** + * \brief + * RD Decision for Intra prediction mode of the chrominance layers of one macroblock + ************************************************************************************** + */ +void intra_chroma_RD_decision (Macroblock *currMB, RD_PARAMS *enc_mb) +{ + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + InputParameters *p_Inp = currMB->p_Inp; + imgpel** image; + int block_x, block_y; + int mb_available_up; + int mb_available_left; + int mb_available_up_left; + int uv; + int mode; + int best_mode = DC_PRED_8; //just an initialization here, should always be overwritten + distblk cost; + // pick lowest cost prediction mode + distblk min_cost = DISTBLK_MAX; + + PixelPos pix_a; //!< pixel positions p(-1, -1..15) + PixelPos pix_b; //!< pixel position p(-1, -1) + PixelPos pix_c; //!< pixel position p( 0, -1) + PixelPos pix_d; + int cr_MB_x = p_Vid->mb_cr_size_x; + int cr_MB_y = p_Vid->mb_cr_size_y; + imgpel ***curr_mpr_16x16; + + short diff [16]; + + p_Vid->getNeighbour(currMB, -1 , -1 , p_Vid->mb_size[IS_CHROMA], &pix_b); + p_Vid->getNeighbour(currMB, -1 , 0 , p_Vid->mb_size[IS_CHROMA], &pix_d); + p_Vid->getNeighbour(currMB, 0 , -1 , p_Vid->mb_size[IS_CHROMA], &pix_c); + p_Vid->getNeighbour(currMB, 0 , 0 , p_Vid->mb_size[IS_CHROMA], &pix_a); + + mb_available_up = pix_c.available; + mb_available_up_left = pix_b.available; + mb_available_left = pix_d.available; + + if(p_Inp->UseConstrainedIntraPred) + { + mb_available_up = pix_c.available ? p_Vid->intra_block[pix_c.mb_addr] : 0; + mb_available_left = pix_d.available ? p_Vid->intra_block[pix_d.mb_addr] : 0; + mb_available_up_left = pix_b.available ? p_Vid->intra_block[pix_b.mb_addr] : 0; + } + + //printf("af pix_a %d %d\n", pix_a.pos_x, pix_a.pos_y); + for (mode = DC_PRED_8; mode <= PLANE_8; ++mode) + { + if ((mode==VERT_PRED_8 && !mb_available_up) || + (mode==HOR_PRED_8 && (!mb_available_left)) || + (mode==PLANE_8 && (!mb_available_left || !mb_available_up || !mb_available_up_left))) + continue; + + cost = weight_cost(enc_mb->lambda_mf[Q_PEL], p_Vid->mvbits[ mode ]); // exp golomb coding cost for mode signaling + + for (uv = 1; uv < 3; ++uv) + { + int pos_x = pix_a.pos_x; + int pos_y = pix_a.pos_y; + image = p_Vid->pImgOrg[uv]; + curr_mpr_16x16 = currSlice->mpr_16x16[uv]; + + for (block_y=0; block_ydistortion4x4(diff, min_cost); + if (cost > min_cost) break; + } + if (cost > min_cost) break; + } + if (cost > min_cost) break; + } + + if (cost < min_cost) + { + best_mode = mode; + min_cost = cost; + } + } + currMB->c_ipred_mode = (char) best_mode; +} + +/*! + ************************************************************************************** + * \brief + * RD Decision for Intra prediction mode of the chrominance layers of one macroblock + ************************************************************************************** + */ +void intra_chroma_RD_decision_mbaff (Macroblock *currMB, RD_PARAMS *enc_mb) +{ + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + InputParameters *p_Inp = currMB->p_Inp; + int i, j, k; + imgpel** image; + int block_x, block_y; + int mb_available_up; + int mb_available_left[2]; + int mb_available_up_left; + int uv; + int mode; + int best_mode = DC_PRED_8; //just an initialization here, should always be overwritten + distblk cost; + // pick lowest cost prediction mode + distblk min_cost = DISTBLK_MAX; + + PixelPos pix_c; //!< pixel position p(0,-1) + PixelPos pix_a[17]; //!< pixel positions p(-1, -1..15) + int cr_MB_x = p_Vid->mb_cr_size_x; + int cr_MB_y = p_Vid->mb_cr_size_y; + imgpel *img_org, *img_prd; + imgpel ***curr_mpr_16x16; + short diff [16]; + + for (i=0;igetNeighbour(currMB, -1 , i-1 , p_Vid->mb_size[IS_CHROMA], &pix_a[i]); + } + p_Vid->getNeighbour(currMB, 0 , -1 , p_Vid->mb_size[IS_CHROMA], &pix_c); + + mb_available_up = pix_c.available; + mb_available_up_left = pix_a[0].available; + mb_available_left[0] = mb_available_left[1] = pix_a[1].available; + + if(p_Inp->UseConstrainedIntraPred) + { + mb_available_up = pix_c.available ? p_Vid->intra_block[pix_c.mb_addr] : 0; + for (i=1, mb_available_left[0] = 1; i < (cr_MB_y>>1) + 1; ++i) + mb_available_left[0] &= pix_a[i].available ? p_Vid->intra_block[pix_a[i].mb_addr]: 0; + + for (i=(cr_MB_y>>1) + 1, mb_available_left[1]=1; i < cr_MB_y + 1;++i) + mb_available_left[1] &= pix_a[i].available ? p_Vid->intra_block[pix_a[i].mb_addr]: 0; + + mb_available_up_left = pix_a[0].available ? p_Vid->intra_block[pix_a[0].mb_addr]: 0; + } + + for (i=0;igetNeighbour(currMB, 0, i, p_Vid->mb_size[IS_CHROMA], &pix_a[i]); + } + if ( currSlice->mb_aff_frame_flag && p_Vid->field_mode ) + { + for (i=0;i> 1; + } + } + + for (mode=DC_PRED_8; mode<=PLANE_8; ++mode) + { + if ((mode==VERT_PRED_8 && !mb_available_up) || + (mode==HOR_PRED_8 && (!mb_available_left[0] || !mb_available_left[1])) || + (mode==PLANE_8 && (!mb_available_left[0] || !mb_available_left[1] || !mb_available_up || !mb_available_up_left))) + continue; + + cost = 0; + for (uv = 1; uv < 3; ++uv) + { + image = p_Vid->pImgOrg[uv]; + curr_mpr_16x16 = currSlice->mpr_16x16[uv]; + for (block_y=0; block_ydistortion4x4(diff, min_cost); + if (cost > min_cost) break; + } + if (cost > min_cost) break; + } + if (cost > min_cost) break; + } + + //cost += (int) (enc_mb->lambda_me[Q_PEL] * p_Vid->mvbits[ mode ]); // exp golomb coding cost for mode signaling + cost += weight_cost(enc_mb->lambda_mf[Q_PEL], p_Vid->mvbits[ mode ]); // exp golomb coding cost for mode signaling + + if (cost < min_cost) + { + best_mode = mode; + min_cost = cost; + } + } + currMB->c_ipred_mode = (char) best_mode; +} diff --git a/lencod/src/intrarefresh.c b/lencod/src/intrarefresh.c new file mode 100644 index 0000000..63b135a --- /dev/null +++ b/lencod/src/intrarefresh.c @@ -0,0 +1,125 @@ + +/*! + ***************************************************************************** + * + * \file intrarefresh.c + * + * \brief + * Encoder support for pseudo-random intra macroblock refresh + * + * \date + * 16 June 2002 + * + * \author + * Stephan Wenger stewe@cs.tu-berlin.de + *****************************************************************************/ + +#include "global.h" + + +/*! + ************************************************************************ + * \brief + * RandomIntraInit: Initializes Random Intra module. Should be called + * only after initialization (or changes) of the picture size or the + * random intra refresh value. In version jm2.1 it is impossible to + * change those values on-the-fly, hence RandomIntraInit should be + * called immediately after the parsing of the config file + * + * \par Input: + * xsize, ysize: size of the picture (in MBs) + * refresh : refresh rate in MBs per picture + ************************************************************************ + */ +void RandomIntraInit(VideoParameters *p_Vid, int xsize, int ysize, int refresh) +{ + int i, pos; + + srand (1); // A fixed random initializer to make things reproducible + p_Vid->NumberOfMBs = xsize * ysize; + p_Vid->NumberIntraPerPicture = refresh; + + if (refresh != 0) + { + p_Vid->RefreshPattern = malloc (sizeof (int) * p_Vid->NumberOfMBs); + if (p_Vid->RefreshPattern == NULL) no_mem_exit("RandomIntraInit: p_Vid->RefreshPattern"); + + p_Vid->IntraMBs = malloc (sizeof (int) * refresh); + if (p_Vid->IntraMBs == NULL) no_mem_exit("RandomIntraInit: p_Vid->IntraMBs"); + + for (i= 0; iNumberOfMBs; i++) + p_Vid->RefreshPattern[i] = -1; + + for (i=0; iNumberOfMBs; i++) + { + do + { + pos = rand() % p_Vid->NumberOfMBs; + } while (p_Vid->RefreshPattern [pos] != -1); + p_Vid->RefreshPattern [pos] = i; + } + /* + for (i=0; iNumberOfMBs; i++) printf ("%d\t", p_Vid->RefreshPattern[i]); + getchar(); + */ + } + else + { + p_Vid->RefreshPattern = NULL; + p_Vid->IntraMBs = NULL; + } +} + +/*! + ************************************************************************ + * \brief + * RandomIntra: Code an MB as Intra? + * + * \par Input + * MacroblockNumberInScanOrder + * \par Output + * 1 if an MB should be forced to Intra, according the the + * RefreshPattern + * 0 otherwise + * + ************************************************************************ + */ +int RandomIntra (VideoParameters *p_Vid, int mb) +{ + int i; + + for (i=0; iNumberIntraPerPicture; i++) + if (p_Vid->IntraMBs[i] == mb) + return 1; + return 0; +} + + +/*! + ************************************************************************ + * \brief + * RandomIntraNewPicture: Selects new set of MBs for forced Intra + * + * \par + * This function should be called exactly once per picture, and + * requires a finished initialization + * + ************************************************************************ + */ +void RandomIntraNewPicture (VideoParameters *p_Vid) +{ + int i, j; + + p_Vid->WalkAround += p_Vid->NumberIntraPerPicture; + for (j=0, i = p_Vid->WalkAround; jNumberIntraPerPicture; j++, i++) + p_Vid->IntraMBs[j] = p_Vid->RefreshPattern [i%p_Vid->NumberOfMBs]; +} + +void RandomIntraUninit(VideoParameters *p_Vid) +{ + if (p_Vid->NumberIntraPerPicture >0 ) + { + free(p_Vid->RefreshPattern); + free(p_Vid->IntraMBs); + } +} diff --git a/lencod/src/lambda.c b/lencod/src/lambda.c new file mode 100644 index 0000000..d24b40e --- /dev/null +++ b/lencod/src/lambda.c @@ -0,0 +1,365 @@ +/*! +*************************************************************************** +* +* \file lambda.c +* +* \brief +* Lambda computation related functions +* +* \date +* 13 September 2009 +* +* \author +* Alexis Michael Tourapis alexismt@ieee.org +**************************************************************************/ + +#include "global.h" +#include "slice.h" + +static void SetLambda(VideoParameters *p_Vid, int j, int qp, double lambda_scale) +{ + InputParameters *p_Inp = p_Vid->p_Inp; + int k; + p_Vid->lambda_md[j][qp] *= lambda_scale; + + for (k = F_PEL; k <= Q_PEL; k++) + { + //p_Vid->lambda_me[j][qp][k] = (p_Inp->MEErrorMetric[k] == ERROR_SSE) ? p_Vid->lambda_md[j][qp] : sqrt(p_Vid->lambda_md[j][qp]); + //p_Vid->lambda_me[j][qp][k] = sqrt(p_Vid->lambda_md[j][qp]); + p_Vid->lambda_me[j][qp][k] = (p_Inp->MEErrorMetric[k] == ERROR_SSE && p_Inp->MESoftenSSEMetric == 0) ? p_Vid->lambda_md[j][qp] : sqrt(p_Vid->lambda_md[j][qp]); + p_Vid->lambda_mf[j][qp][k] = LAMBDA_FACTOR (p_Vid->lambda_me[j][qp][k]); + } +} + +static void CalcMaxLamdaMD(VideoParameters *p_Vid, double *p_lambda_md) +{ + double max_lambda_md; + int iBits; + + if(p_Vid->p_Inp->ProfileIDC >= FREXT_HP) + iBits=128; //Spec. Page 306 + else + iBits=80; + + if(p_Vid->yuv_format == YUV420) + iBits += 3072; //(256+128)*8; + else if(p_Vid->yuv_format == YUV422) + iBits += 4096; //(256+256)*8; + else if(p_Vid->yuv_format == YUV444) + iBits += 6144; //(256*3)*8; + else if(p_Vid->yuv_format == YUV400) + iBits += 2048; //256*8; + + max_lambda_md = floor(((double)DISTBLK_MAX)/iBits/(1<=0 && cost<=DISTBLK_MAX); + } +#endif + *p_lambda_md = max_lambda_md; +} + +static void ClipLambda(double *p_lambda_max, double *p_lambda) +{ + if(*p_lambda > *p_lambda_max) + { + //printf("Clip: %lf -> %lf\n", *p_lambda, *p_lambda_max); + *p_lambda = *p_lambda_max; + } +} + +void set_rdoq_lambda( Slice *currSlice ) +{ + VideoParameters *p_Vid = currSlice->p_Vid; + int qp, j = currSlice->slice_type; + + for (qp = -currSlice->bitdepth_luma_qp_scale; qp < 52; qp++) + { + p_Vid->lambda_rdoq[j][qp] = p_Vid->lambda_md[j][qp]; + } +} + +void get_implicit_lambda_p_slice(Slice *currSlice) +{ + VideoParameters *p_Vid = currSlice->p_Vid; + InputParameters *p_Inp = currSlice->p_Inp; + int qp; + double qp_temp; + double lambda_md; + double lambda_scale = 1.0 - dClip3(0.0,0.5,0.05 * (double) p_Inp->jumpd); + //limit lambda for mode decison; + int bLimitsLambdaMD = ((p_Inp->EnableIPCM > 0) && (IMGTYPE==0)); + double dMaxLambdaMD =0; + + if(bLimitsLambdaMD) + CalcMaxLamdaMD(p_Vid, &dMaxLambdaMD); + + for (qp = -currSlice->bitdepth_luma_qp_scale; qp < 52; qp++) + { + qp_temp = (double)qp + currSlice->bitdepth_luma_qp_scale - SHIFT_QP; + + if (p_Inp->NumberBFrames > 0) + { + lambda_md = 0.68 * pow (2.0, qp_temp/3.0); + } + else + lambda_md = 0.85 * pow (2.0, qp_temp/3.0); + + // Scale lambda due to hadamard qpel only consideration + lambda_md = ((p_Inp->MEErrorMetric[H_PEL] == ERROR_SATD && p_Inp->MEErrorMetric[Q_PEL] == ERROR_SATD) ? 1.00 : 0.95) * lambda_md; + p_Vid->lambda_md[P_SLICE][qp] = lambda_scale * lambda_md; + + //clip lambda; + if(bLimitsLambdaMD) + ClipLambda(&dMaxLambdaMD, &p_Vid->lambda_md[P_SLICE][qp]); + + SetLambda(p_Vid, P_SLICE, qp, 1.0); + + if (p_Inp->CtxAdptLagrangeMult == 1) + { + int lambda_qp = (qp >= 32 && !p_Inp->RCEnable) ? imax(0, qp - 4) : imax(0, qp - 6); + p_Vid->lambda_mf_factor[P_SLICE][qp] = log (p_Vid->lambda_me[P_SLICE][lambda_qp][Q_PEL] + 1.0) / log (2.0); + } + } +} + +void get_implicit_lambda_b_slice(Slice *currSlice) +{ + VideoParameters *p_Vid = currSlice->p_Vid; + InputParameters *p_Inp = currSlice->p_Inp; + int qp; + double qp_temp; + double lambda_md; + FrameUnitStruct *p_cur_frm = p_Vid->p_curr_frm_struct; + //limit lambda for mode decison; + int bLimitsLambdaMD = ((p_Inp->EnableIPCM > 0) && (IMGTYPE==0)); + double dMaxLambdaMD =0; + + if(bLimitsLambdaMD) + CalcMaxLamdaMD(p_Vid, &dMaxLambdaMD); + + for (qp = -currSlice->bitdepth_luma_qp_scale; qp < 52; qp++) + { + qp_temp = (double)qp + currSlice->bitdepth_luma_qp_scale - SHIFT_QP; + + if (p_Inp->NumberBFrames > 0) + { + lambda_md = 0.68 * pow (2.0, qp_temp/3.0) + * (p_cur_frm->layer != 0 ? dClip3(2.00, 4.00, (qp_temp / 6.0)) : 1.0); + } + else + lambda_md = 0.85 * pow (2.0, qp_temp/3.0); + + // Scale lambda due to hadamard qpel only consideration + lambda_md = ((p_Inp->MEErrorMetric[H_PEL] == ERROR_SATD && p_Inp->MEErrorMetric[Q_PEL] == ERROR_SATD) ? 1.00 : 0.95) * lambda_md; + + if (p_cur_frm->layer != 0 && currSlice->nal_reference_idc) + { + if (p_Inp->HierarchicalCoding == 2) + lambda_md *= (1.0 - dmin(0.4, 0.2 * (double) (p_cur_frm->p_atom->gop_levels - p_cur_frm->layer))); + else + lambda_md *= 0.80; + } + p_Vid->lambda_md[B_SLICE][qp] = lambda_md; + + //clip lambda; + if(bLimitsLambdaMD) + ClipLambda(&dMaxLambdaMD, &p_Vid->lambda_md[B_SLICE][qp]); + + SetLambda(p_Vid, B_SLICE, qp, 1.0); + + if (p_Inp->CtxAdptLagrangeMult == 1) + { + int lambda_qp = (qp >= 32 && !p_Inp->RCEnable) ? imax(0, qp - 4) : imax(0, qp - 6); + p_Vid->lambda_mf_factor[B_SLICE][qp] = log (p_Vid->lambda_me[B_SLICE][lambda_qp][Q_PEL] + 1.0) / log (2.0); + } + } +} + +void get_implicit_lambda_i_slice(Slice *currSlice) +{ + VideoParameters *p_Vid = currSlice->p_Vid; + InputParameters *p_Inp = currSlice->p_Inp; + int qp; + double qp_temp; + double lambda_md; + double lambda_scale = 1.0 - dClip3(0.0,0.5,0.05 * (double) p_Inp->jumpd); + //limit lambda for mode decison; + int bLimitsLambdaMD = ((p_Inp->EnableIPCM > 0) && (IMGTYPE==0)); + double dMaxLambdaMD =0; + + if(bLimitsLambdaMD) + CalcMaxLamdaMD(p_Vid, &dMaxLambdaMD); + + for (qp = -currSlice->bitdepth_luma_qp_scale; qp < 52; qp++) + { + qp_temp = (double)qp + currSlice->bitdepth_luma_qp_scale - SHIFT_QP; + + if(p_Inp->UseRDOQuant && p_Vid->qp==qp) + lambda_md = 0.57 * pow (2.0, qp_temp/3.0); + else if (p_Inp->NumberBFrames > 0) + { + lambda_md = 0.68 * pow (2.0, qp_temp/3.0); + } + else + lambda_md = 0.85 * pow (2.0, qp_temp/3.0); + + // Scale lambda due to hadamard qpel only consideration + lambda_md = ((p_Inp->MEErrorMetric[H_PEL] == ERROR_SATD && p_Inp->MEErrorMetric[Q_PEL] == ERROR_SATD) ? 1.00 : 0.95) * lambda_md; + p_Vid->lambda_md[I_SLICE][qp] = lambda_scale * lambda_md; + + //clip lambda; + if(bLimitsLambdaMD) + ClipLambda(&dMaxLambdaMD, &p_Vid->lambda_md[I_SLICE][qp]); + + SetLambda(p_Vid, I_SLICE, qp, 1.0); + + if (p_Inp->CtxAdptLagrangeMult == 1) + { + int lambda_qp = (qp >= 32 && !p_Inp->RCEnable) ? imax(0, qp - 4) : imax(0, qp - 6); + p_Vid->lambda_mf_factor[I_SLICE][qp] = log (p_Vid->lambda_me[I_SLICE][lambda_qp][Q_PEL] + 1.0) / log (2.0); + } + } +} + +void get_implicit_lambda_sp_slice(Slice *currSlice) +{ + VideoParameters *p_Vid = currSlice->p_Vid; + InputParameters *p_Inp = currSlice->p_Inp; + int qp; + double qp_temp; + double lambda_md; + double lambda_scale = 1.0 - dClip3(0.0,0.5,0.05 * (double) p_Inp->jumpd); + //limit lambda for mode decison; + int bLimitsLambdaMD = ((p_Inp->EnableIPCM > 0) && (IMGTYPE==0)); + double dMaxLambdaMD =0; + + int slice_index = currSlice->slice_type; + + if(bLimitsLambdaMD) + CalcMaxLamdaMD(p_Vid, &dMaxLambdaMD); + + for (qp = -currSlice->bitdepth_luma_qp_scale; qp < 52; qp++) + { + qp_temp = (double)qp + currSlice->bitdepth_luma_qp_scale - SHIFT_QP; + + if(p_Inp->UseRDOQuant && p_Vid->type == I_SLICE && p_Vid->qp==qp) + lambda_md = 0.57 * pow (2.0, qp_temp/3.0); + else if (p_Inp->NumberBFrames > 0) + { + lambda_md = 0.68 * pow (2.0, qp_temp/3.0) * dClip3(1.4,3.0,(qp_temp / 12.0)); + } + else + lambda_md = 0.85 * pow (2.0, qp_temp/3.0) * dClip3(1.4, 3.0,(qp_temp / 12.0)); + + // Scale lambda due to hadamard qpel only consideration + lambda_md = ((p_Inp->MEErrorMetric[H_PEL] == ERROR_SATD && p_Inp->MEErrorMetric[Q_PEL] == ERROR_SATD) ? 1.00 : 0.95) * lambda_md; + + p_Vid->lambda_md[slice_index][qp] = lambda_scale * lambda_md; + + //clip lambda; + if(bLimitsLambdaMD) + ClipLambda(&dMaxLambdaMD, &p_Vid->lambda_md[slice_index][qp]); + + SetLambda(p_Vid, slice_index, qp, 1.0); + + if (p_Inp->CtxAdptLagrangeMult == 1) + { + int lambda_qp = (qp >= 32 && !p_Inp->RCEnable) ? imax(0, qp - 4) : imax(0, qp - 6); + p_Vid->lambda_mf_factor[slice_index][qp] = log (p_Vid->lambda_me[slice_index][lambda_qp][Q_PEL] + 1.0) / log (2.0); + } + } +} + +void get_explicit_lambda(Slice *currSlice) +{ + VideoParameters *p_Vid = currSlice->p_Vid; + InputParameters *p_Inp = currSlice->p_Inp; + int qp; + double qp_temp; + double lambda_scale = 1.0 - dClip3(0.0,0.5,0.05 * (double) p_Inp->jumpd); + //limit lambda for mode decison; + int bLimitsLambdaMD = ((p_Inp->EnableIPCM > 0) && (IMGTYPE==0)); + double dMaxLambdaMD =0; +#if (MVC_EXTENSION_ENABLE) + double tmp_lambda_md = 0.0; +#endif + + int j = currSlice->slice_type; + + if(bLimitsLambdaMD) + CalcMaxLamdaMD(p_Vid, &dMaxLambdaMD); + + for (qp = -currSlice->bitdepth_luma_qp_scale; qp < 52; qp++) + { + qp_temp = (double)qp + currSlice->bitdepth_luma_qp_scale - SHIFT_QP; + + if (j == B_SLICE && currSlice->nal_reference_idc) + p_Vid->lambda_md[j][qp] = p_Inp->LambdaWeight[5] * pow (2, qp_temp/3.0); + else + p_Vid->lambda_md[j][qp] = p_Inp->LambdaWeight[j] * pow (2, qp_temp/3.0); + +#if (MVC_EXTENSION_ENABLE) + tmp_lambda_md = p_Vid->lambda_md[j][qp]; + + if (currSlice->view_id) + { + p_Vid->lambda_md[j][qp] = p_Vid->lambda_md[j][qp] * p_Inp->enh_layer_me_lambda_multiplier; + } +#endif + + //clip lambda; + if(bLimitsLambdaMD) + ClipLambda(&dMaxLambdaMD, &p_Vid->lambda_md[j][qp]); + + SetLambda(p_Vid, j, qp, ((p_Inp->MEErrorMetric[H_PEL] == ERROR_SATD && p_Inp->MEErrorMetric[Q_PEL] == ERROR_SATD) ? 1.00 : 0.95)); + +#if (MVC_EXTENSION_ENABLE) + if (currSlice->view_id) + { + p_Vid->lambda_md[j][qp] = tmp_lambda_md * p_Inp->enh_layer_lambda_multiplier; + //clip lambda; + if(bLimitsLambdaMD) + ClipLambda(&dMaxLambdaMD, &p_Vid->lambda_md[j][qp]); + p_Vid->lambda_md[j][qp] *= ((p_Inp->MEErrorMetric[H_PEL] == ERROR_SATD && p_Inp->MEErrorMetric[Q_PEL] == ERROR_SATD) ? 1.00 : 0.95); + } +#endif + } + + if (j != B_SLICE) + { + for (qp = -currSlice->bitdepth_luma_qp_scale; qp < 52; qp++) + p_Vid->lambda_md[j][qp] *= lambda_scale; + } +} + + +void get_fixed_lambda(Slice *currSlice) +{ + VideoParameters *p_Vid = currSlice->p_Vid; + InputParameters *p_Inp = currSlice->p_Inp; + int qp; + //limit lambda for mode decison; + int bLimitsLambdaMD = ((p_Inp->EnableIPCM > 0) && (IMGTYPE==0)); + double dMaxLambdaMD =0; + + int j = currSlice->slice_type; + + if(bLimitsLambdaMD) + CalcMaxLamdaMD(p_Vid, &dMaxLambdaMD); + + for (qp = -currSlice->bitdepth_luma_qp_scale; qp < 52; qp++) + { + if (j == B_SLICE && currSlice->nal_reference_idc) + p_Vid->lambda_md[j][qp] = p_Inp->FixedLambda[5]; + else + p_Vid->lambda_md[j][qp] = p_Inp->FixedLambda[j]; + //clip lambda; + if(bLimitsLambdaMD) + ClipLambda(&dMaxLambdaMD, &p_Vid->lambda_md[j][qp]); + + SetLambda(p_Vid, j, qp, ((p_Inp->MEErrorMetric[H_PEL] == ERROR_SATD && p_Inp->MEErrorMetric[Q_PEL] == ERROR_SATD) ? 1.00 : 0.95)); + } +} + diff --git a/lencod/src/leaky_bucket.c b/lencod/src/leaky_bucket.c new file mode 100644 index 0000000..875e536 --- /dev/null +++ b/lencod/src/leaky_bucket.c @@ -0,0 +1,314 @@ + +/*! + *************************************************************************** + * \file leaky_bucket.c + * + * \brief + * calculate Leaky Buffer parameters + * + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * - Shankar Regunathan + *************************************************************************** + */ + +#include "contributors.h" +#include "global.h" + +#ifdef _LEAKYBUCKET_ + +/*! + *********************************************************************** + * \brief + * Function to get Leaky Bucket rates from rate file + * \param NumberLeakyBuckets + * Number of Leaky Bucket Parameters + * \param Rmin + * Rate values for each Bucket. + * \return + * returns 1 if successful; else returns zero. + * \para SideEffects + * None. + * \para Notes + * Failure if LeakyBucketRate is missing or if it does not have + * the correct number of entries. + * \author + * Shankar Regunathan shanre@microsoft.com + * \date + * December 06, 2001. + *********************************************************************** + */ + +int get_LeakyBucketRate(InputParameters *p_Inp, unsigned long NumberLeakyBuckets, unsigned long *Rmin) +{ + FILE *f; + unsigned long i, buf; + + if((f = fopen(p_Inp->LeakyBucketRateFile, "r")) == NULL) + { + printf(" LeakyBucketRate File does not exist. Using rate calculated from avg. rate \n"); + return 0; + } + + for(i=0; i> 0x18) & 0xFF, fp); + fputc((dw >> 0x10) & 0xFF, fp); + fputc((dw >> 0x08) & 0xFF, fp); + fputc(dw & 0xFF, fp); +} + +/*! + *********************************************************************** + * \brief + * Stores the Leaky BucketParameters in file p_Inp->LeakyBucketParamFile. + * \param NumberLeakyBuckets + * Number of LeakyBuckets. + * \param Rmin + * Rate values of the buckets. + * \param Bmin + * Minimum buffer values of the buckets. + * \param Fmin + * Minimum initial buffer fullness of the buckets + * \return + * None. + * \para + * Returns error if LeakyBucketParamFile cannot be opened. + * \para SideEffects + * Prints the LeakyBucket Parameters in standard output. + * \author + * Shankar Regunathan shanre@microsoft.com + * \date + * December 06, 2001. + *********************************************************************** + */ + + +void write_buffer(InputParameters *p_Inp, unsigned long NumberLeakyBuckets, unsigned long Rmin[], unsigned long Bmin[], unsigned long Fmin[]) +{ + FILE *outf; + unsigned long iBucket; + + if ((outf = fopen(p_Inp->LeakyBucketParamFile,"wb"))==NULL) + { + snprintf(errortext, ET_SIZE, "Error open file lk %s \n",p_Inp->LeakyBucketParamFile); + error(errortext,1); + } + + PutBigDoubleWord(NumberLeakyBuckets, outf); + if (p_Inp->Verbose != 0) + printf(" Number Leaky Buckets: %ld \n Rmin Bmin Fmin \n", NumberLeakyBuckets); + for(iBucket =0; iBucket < NumberLeakyBuckets; iBucket++) + { + //assert(Rmin[iBucket]<4294967296); //Overflow should be corrected already. + //assert(Bmin[iBucket]<4294967296); + //assert(Fmin[iBucket]<4294967296); + PutBigDoubleWord(Rmin[iBucket], outf); + PutBigDoubleWord(Bmin[iBucket], outf); + PutBigDoubleWord(Fmin[iBucket], outf); + if (p_Inp->Verbose != 0) + printf(" %8ld %8ld %8ld \n", Rmin[iBucket], Bmin[iBucket], Fmin[iBucket]); + } + fclose(outf); +} + +/*! + *********************************************************************** + * \brief + * Sorts the rate array in ascending order. + * \param NumberLeakyBuckets + * Number of LeakyBuckets. + * \param Rmin + * Rate values of the buckets. + * \return + * None. + * \author + * Shankar Regunathan shanre@microsoft.com + * \date + * December 06, 2001. + *********************************************************************** + */ + + +void Sort(unsigned long NumberLeakyBuckets, unsigned long *Rmin) +{ + unsigned long i, j; + unsigned long temp; + for(i=0; i< NumberLeakyBuckets-1; i++) + { + for(j=i+1; j Rmin[j]) + { + temp = Rmin[i]; + Rmin[i] = Rmin[j]; + Rmin[j] = temp; + } + } + } +} + +/*! + *********************************************************************** + * \brief + * Main Routine to calculate Leaky Buffer parameters + * \param NumberLeakyBuckets + * None. + * \return + * None. + * \author + * Shankar Regunathan shanre@microsoft.com + * \date + * December 06, 2001. + *********************************************************************** + */ + +void calc_buffer(VideoParameters *p_Vid, InputParameters *p_Inp) +{ + unsigned long AvgRate, TotalRate, NumberLeakyBuckets; + long *buffer_frame, minB; + unsigned long iBucket, iFrame, FrameIndex = 0; + long maxBuffer, actualBuffer, InitFullness, iChannelRate; + unsigned long *Rmin, *Bmin, *Fmin; + + switch (p_Inp->Verbose) + { + case 1: + fprintf(stdout,"-------------------------------------------------------------------------------\n"); + break; + case 2: + fprintf(stdout,"------------------------------------------------------------------------------------------------\n"); + break; + case 3: + fprintf(stdout,"-------------------------------------------------------------------------------------------------------\n"); + break; + case 0: + default: + fprintf(stdout,"\n-------------------------------------------------------------------------------\n"); + break; + } + printf(" Total Frames: %ld \n", p_Vid->total_frame_buffer); + NumberLeakyBuckets = (unsigned long) p_Inp->NumberLeakyBuckets; + buffer_frame = calloc(p_Vid->total_frame_buffer + 1, sizeof(long)); + if(!buffer_frame) + no_mem_exit("init_buffer: buffer_frame"); + Rmin = calloc(NumberLeakyBuckets, sizeof(unsigned long)); + if(!Rmin) + no_mem_exit("init_buffer: Rmin"); + Bmin = calloc(NumberLeakyBuckets, sizeof(unsigned long)); + if(!Bmin) + no_mem_exit("init_buffer: Bmin"); + Fmin = calloc(NumberLeakyBuckets, sizeof(unsigned long)); + if(!Fmin) + no_mem_exit("init_buffer: Fmin"); + + TotalRate = 0; + for(iFrame=0; iFrame < p_Vid->total_frame_buffer; iFrame++) + { + TotalRate += (unsigned long) p_Vid->Bit_Buffer[iFrame]; + } + AvgRate = (unsigned long) ((float) TotalRate/ p_Vid->total_frame_buffer); + + if(1 != get_LeakyBucketRate(p_Inp, NumberLeakyBuckets, Rmin)) + { /* if rate file is not present, use default calculated from avg.rate */ + for(iBucket=0; iBucket < NumberLeakyBuckets; iBucket++) + { + if(iBucket == 0) + Rmin[iBucket] = (unsigned long)((float) AvgRate * p_Vid->framerate); /* convert bits/frame to bits/second */ + else + Rmin[iBucket] = (unsigned long) ((float) Rmin[iBucket-1] + (AvgRate/4) * (p_Vid->framerate)); + } + } + Sort(NumberLeakyBuckets, Rmin); + + maxBuffer = AvgRate * 20; /* any initialization is good. */ + for(iBucket=0; iBucket< NumberLeakyBuckets; iBucket++) + { + iChannelRate = (long) (Rmin[iBucket] / (p_Vid->framerate)); /* converts bits/second to bits/frame */ + /* To calculate initial buffer size */ + InitFullness = maxBuffer; /* set Initial Fullness to be buffer size */ + buffer_frame[0] = InitFullness; + minB = maxBuffer; + + for(iFrame=0; iFrame < p_Vid->total_frame_buffer ; iFrame++) + { + buffer_frame[iFrame] = buffer_frame[iFrame] - p_Vid->Bit_Buffer[iFrame]; + if(buffer_frame[iFrame] < minB) + { + minB = buffer_frame[iFrame]; + FrameIndex = iFrame; + } + + if ( iFrame < p_Vid->total_frame_buffer ) + { + buffer_frame[iFrame+1] = buffer_frame[iFrame] + iChannelRate; + if(buffer_frame[iFrame+1] > maxBuffer) + buffer_frame[iFrame+1] = maxBuffer; + } + } + actualBuffer = (maxBuffer - minB); + + /* To calculate initial buffer Fullness */ + InitFullness = p_Vid->Bit_Buffer[0]; + buffer_frame[0] = InitFullness; + for(iFrame=0; iFrame < FrameIndex+1; iFrame++) + { + buffer_frame[iFrame] = buffer_frame[iFrame] - p_Vid->Bit_Buffer[iFrame]; + if(buffer_frame[iFrame] < 0) + { + InitFullness -= buffer_frame[iFrame]; + buffer_frame[iFrame] = 0; + } + if ( iFrame < p_Vid->total_frame_buffer ) + { + buffer_frame[iFrame+1] = buffer_frame[iFrame] + iChannelRate; + if(buffer_frame[iFrame+1] > actualBuffer) + break; + } + } + Bmin[iBucket] = (unsigned long) actualBuffer; + Fmin[iBucket] = (unsigned long) InitFullness; + } + + write_buffer(p_Inp, NumberLeakyBuckets, Rmin, Bmin, Fmin); + + free_pointer(buffer_frame); + free_pointer(Rmin); + free_pointer(Bmin); + free_pointer(Fmin); + return; +} +#endif + diff --git a/lencod/src/lencod.c b/lencod/src/lencod.c new file mode 100644 index 0000000..bc81c85 --- /dev/null +++ b/lencod/src/lencod.c @@ -0,0 +1,2639 @@ +/*! +*********************************************************************** +* \mainpage +* This is the H.264/AVC encoder reference software. For detailed documentation +* see the comments in each file. +* +* The JM software web site is located at: +* http://iphome.hhi.de/suehring/tml +* +* For bug reporting and known issues see: +* https://ipbt.hhi.fraunhofer.de +* +* \author +* The main contributors are listed in contributors.h +* +* \version +* JM 18.2 (FRExt) +* +* \note +* tags are used for document system "doxygen" +* available at http://www.doxygen.org +*/ +/*! +* \file +* lencod.c +* \brief +* H.264/AVC reference encoder project main() +* \author +* Main contributors (see contributors.h for copyright, address and affiliation details) +* - Inge Lille-Langoy +* - Rickard Sjoberg +* - Stephan Wenger +* - Jani Lainema +* - Byeong-Moon Jeon +* - Yoon-Seong Soh +* - Thomas Stockhammer +* - Detlev Marpe +* - Guido Heising +* - Valeri George +* - Karsten Suehring +* - Alexis Michael Tourapis +* - Athanasios Leontaris +*********************************************************************** +*/ + + +#if (_DEBUG) +#define _CRTDBG_MAP_ALLOC +#include +#include +#endif + +#include "contributors.h" + +#include +#include +#include "win32.h" +#include "global.h" +#include "cconv_yuv2rgb.h" +#include "configfile.h" +#include "conformance.h" +#include "context_ini.h" +#include "explicit_gop.h" +#include "explicit_seq.h" +#include "filehandle.h" +#include "image.h" +#include "input.h" +#include "img_io.h" +#include "slice.h" +#include "intrarefresh.h" +#include "leaky_bucket.h" +#include "mc_prediction.h" +#include "mc_prediction_otf.h" +#include "memalloc.h" +#include "me_epzs_common.h" +#include "me_epzs_int.h" +#include "me_umhex.h" +#include "me_umhexsmp.h" +#include "output.h" +#include "parset.h" +#include "q_matrix.h" +#include "q_offsets.h" +#include "ratectl.h" +#include "report.h" +#include "rdoq.h" +#include "errdo.h" +#include "rdopt.h" +#include "wp_mcprec.h" +#include "mv_search.h" +#include "img_process.h" +#include "q_offsets.h" +#include "pred_struct.h" +#include "blk_prediction.h" +#include "img_luma.h" +#include "img_chroma.h" +#include "mc_prediction.h" +#include "me_distortion.h" +#include "me_distortion_otf.h" +#include "md_distortion.h" +#include "mode_decision.h" +#include "transform8x8.h" +#include "intra16x16.h" +#include "intra4x4.h" +#include "intra8x8.h" +//#include "resize.h" +#include "md_common.h" +#include "macroblock.h" +#include "get_block_otf.h" +//! Edited by Joao Carreira +#include "transcode_enc.h" + +#include "wp.h" + +//check the scaling factor to avoid overflow; +#if !IMGTYPE +#if JCOST_CALC_SCALEUP && (LAMBDA_ACCURACY_BITS>8) +#error "LAMBDA_ACCURACY_BITS is greater than 8. Overflow!" +#endif +#endif +static const int mb_width_cr[4] = {0, 8, 8,16}; +static const int mb_height_cr[4]= {0, 8,16,16}; + +EncoderParams *p_Enc = NULL; + +static void set_level_indices (VideoParameters *p_Vid); +static void chroma_mc_setup (VideoParameters *p_Vid); +static void init_img (VideoParameters *p_Vid); +static void init_encoder (VideoParameters *p_Vid, InputParameters *p_Inp); +static int init_global_buffers (VideoParameters *p_Vid, InputParameters *p_Inp); +static void free_global_buffers (VideoParameters *p_Vid, InputParameters *p_Inp); +static void free_img (VideoParameters *p_Vid, InputParameters *p_Inp); +static void free_params (InputParameters *p_Inp); + +static void encode_sequence (VideoParameters *p_Vid, InputParameters *p_Inp); + + +static void generate_encode_parameters(VideoParameters *p_Vid); +static void free_encode_parameters(VideoParameters *p_Vid); + + +void init_stats (InputParameters *p_Inp, StatParameters *p_Stats) +{ + memset(p_Stats, 0, sizeof(StatParameters)); + p_Stats->NumberBFrames = p_Inp->NumberBFrames; +} + +void init_dstats (DistortionParams *p_Dist) +{ + p_Dist->frame_ctr = 0; + memset(p_Dist->metric, 0, TOTAL_DIST_TYPES * sizeof(DistMetric)); +} + +/*! +*********************************************************************** +* \brief +* Allocate the Video Parameters structure +* \par Output: +* Video Parameters VideoParameters *p_Vid +*********************************************************************** +*/ +static void alloc_video_params( VideoParameters **p_Vid) +{ + if ((*p_Vid = (VideoParameters *) calloc(1, sizeof(VideoParameters)))==NULL) + no_mem_exit("alloc_video_params: p_Vid"); + if ((((*p_Vid)->p_Dist) = (DistortionParams *) calloc(1, sizeof(DistortionParams)))==NULL) + no_mem_exit("alloc_video_params: p_Dist"); + if ((((*p_Vid)->p_Stats) = (StatParameters *) calloc(1, sizeof(StatParameters)))==NULL) + no_mem_exit("alloc_video_params: p_Stats"); + if (((*p_Vid)->p_Dpb_layer[0] = (DecodedPictureBuffer *) calloc(MAX_NUM_DPB_LAYERS, sizeof(DecodedPictureBuffer)))==NULL) + no_mem_exit("alloc_video_params: p_Dpb_layer"); + { + int i; + for(i=1; ip_Dpb_layer[i] = (*p_Vid)->p_Dpb_layer[i-1] + 1; + (*p_Vid)->p_Dpb_layer[i]->layer_id = i; + } + } + if ((((*p_Vid)->p_Quant) = (QuantParameters *) calloc(1, sizeof(QuantParameters)))==NULL) + no_mem_exit("alloc_video_params: p_Quant"); + if ((((*p_Vid)->p_QScale) = (ScaleParameters *) calloc(1, sizeof(ScaleParameters)))==NULL) + no_mem_exit("alloc_video_params: p_QScale"); + if ((((*p_Vid)->p_SEI) = (SEIParameters *) calloc(1, sizeof(SEIParameters)))==NULL) + no_mem_exit("alloc_video_params: p_SEI"); + + + (*p_Vid)->p_dec = -1; +#if (MVC_EXTENSION_ENABLE) + (*p_Vid)->p_dec2 = -1; +#endif + (*p_Vid)->p_log = NULL; + (*p_Vid)->f_annexb = NULL; + // Init rtp related info + (*p_Vid)->f_rtp = NULL; + (*p_Vid)->CurrentRTPTimestamp = 0; + (*p_Vid)->CurrentRTPSequenceNumber = 0; +} + +/*! +*********************************************************************** +* \brief +* Allocate the Input structure +* \par Output: +* Input Parameters InputParameters *p_Vid +*********************************************************************** +*/ +static void alloc_params( InputParameters **p_Inp ) +{ + if ((*p_Inp = (InputParameters *) calloc(1, sizeof(InputParameters)))==NULL) + no_mem_exit("alloc_params: p_Inp"); + + (*p_Inp)->top_left = NULL; + (*p_Inp)->bottom_right = NULL; + (*p_Inp)->slice_group_id = NULL; + (*p_Inp)->run_length_minus1 = NULL; +} + + +/*! +*********************************************************************** +* \brief +* Allocate the Encoder Structure +* \par Output: +* Encoder Parameters +*********************************************************************** +*/ +static void alloc_encoder( EncoderParams **p_Enc) +{ + if ((*p_Enc = (EncoderParams *) calloc(1, sizeof(EncoderParams)))==NULL) + no_mem_exit("alloc_encoder: p_Enc"); + + alloc_video_params(&((*p_Enc)->p_Vid)); + alloc_params(&((*p_Enc)->p_Inp)); + (*p_Enc)->p_trace = NULL; + (*p_Enc)->bufferSize = 0; +} + +/*! +*********************************************************************** +* \brief +* Free the Encoder Structure +*********************************************************************** +*/ +static void free_encoder (EncoderParams *p_Enc) +{ + free_pointer( p_Enc ); +} + +/*! +*********************************************************************** +* \brief +* Main function for encoder. +* \param argc +* number of command line arguments +* \param argv +* command line arguments +* \return +* exit code +*********************************************************************** +*/ +int main(int argc, char **argv) +{ + init_time(); + +#if MEMORY_DEBUG + _CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF ); +#endif + + alloc_encoder(&p_Enc); + + Configure (p_Enc->p_Vid, p_Enc->p_Inp, argc, argv); + + // init encoder + init_encoder(p_Enc->p_Vid, p_Enc->p_Inp); + + // encode sequence + encode_sequence(p_Enc->p_Vid, p_Enc->p_Inp); + + // terminate sequence + free_encoder_memory(p_Enc->p_Vid, p_Enc->p_Inp); + + free_params (p_Enc->p_Inp); + free_encoder(p_Enc); + +#if (_DEBUG ) + _CrtDumpMemoryLeaks(); +#if( WIN32 ) + system("pause"); +#endif +#endif + return 0; +} + + +/*! +************************************************************************ +* \brief +* calculate Ceil(Log2(uiVal)) +************************************************************************ +*/ +static unsigned CeilLog2( unsigned uiVal) +{ + unsigned uiTmp = uiVal-1; + unsigned uiRet = 0; + + while( uiTmp != 0 ) + { + uiTmp >>= 1; + uiRet++; + } + return uiRet; +} + +void set_dpb_layer_id(VideoParameters *p_Vid, int idx) +{ + assert(idx>=0 && idx<2); + p_Vid->dpb_layer_id = idx; +} + +void setup_dpb_layer(DecodedPictureBuffer *p_Dpb, VideoParameters *p_Vid, InputParameters *p_Inp) +{ + // on-the-fly interpolation init + switch ( p_Inp->OnTheFlyFractMCP ) + { + case OTF_L1: + p_Dpb->pf_computeSAD = computeSAD_otf; + p_Dpb->pf_computeSADWP = computeSADWP_otf; + p_Dpb->pf_computeSATD = computeSATD_otf; + p_Dpb->pf_computeSATDWP = computeSATDWP_otf; + p_Dpb->pf_computeBiPredSAD1 = computeBiPredSAD1_otf; + p_Dpb->pf_computeBiPredSAD2 = computeBiPredSAD2_otf; + p_Dpb->pf_computeBiPredSATD1 = computeBiPredSATD1_otf; + p_Dpb->pf_computeBiPredSATD2 = computeBiPredSATD2_otf; + p_Dpb->pf_computeSSE = computeSSE_otf; + p_Dpb->pf_computeSSEWP = computeSSEWP_otf; + p_Dpb->pf_computeBiPredSSE1 = computeBiPredSSE1_otf; + p_Dpb->pf_computeBiPredSSE2 = computeBiPredSSE2_otf; + p_Dpb->pf_luma_prediction = luma_prediction_otf ; + p_Dpb->pf_luma_prediction_bi = luma_prediction_bi_otf ; + p_Dpb->pf_chroma_prediction = chroma_prediction_otf ; + p_Dpb->pf_get_block_luma = get_block_luma_otf_L1 ; + p_Dpb->pf_get_block_chroma[OTF_ME] = (p_Vid->P444_joined) ? ( get_block_luma_otf_L1 ) : ( me_get_block_chroma_otf_L1 ) ; + p_Dpb->pf_get_block_chroma[OTF_MC] = (p_Vid->P444_joined) ? ( get_block_luma_otf_L1 ) : ( mc_get_block_chroma_otf_L1 ) ; + p_Dpb->pf_OneComponentChromaPrediction4x4_regenerate = OneComponentChromaPrediction4x4_regenerate; + p_Dpb->pf_OneComponentChromaPrediction4x4_retrieve = OneComponentChromaPrediction4x4_regenerate; + break; + case OTF_L2: + p_Dpb->pf_computeSAD = computeSAD_otf; + p_Dpb->pf_computeSADWP = computeSADWP_otf; + p_Dpb->pf_computeSATD = computeSATD_otf; + p_Dpb->pf_computeSATDWP = computeSATDWP_otf; + p_Dpb->pf_computeBiPredSAD1 = computeBiPredSAD1_otf; + p_Dpb->pf_computeBiPredSAD2 = computeBiPredSAD2_otf; + p_Dpb->pf_computeBiPredSATD1 = computeBiPredSATD1_otf; + p_Dpb->pf_computeBiPredSATD2 = computeBiPredSATD2_otf; + p_Dpb->pf_computeSSE = computeSSE_otf; + p_Dpb->pf_computeSSEWP = computeSSEWP_otf; + p_Dpb->pf_computeBiPredSSE1 = computeBiPredSSE1_otf; + p_Dpb->pf_computeBiPredSSE2 = computeBiPredSSE2_otf; + p_Dpb->pf_luma_prediction = luma_prediction_otf ; + p_Dpb->pf_luma_prediction_bi = luma_prediction_bi_otf ; + p_Dpb->pf_chroma_prediction = chroma_prediction_otf ; + p_Dpb->pf_get_block_luma = get_block_luma_otf_L2 ; + p_Dpb->pf_get_block_chroma[OTF_ME] = p_Dpb->pf_get_block_chroma[OTF_MC] = (p_Vid->P444_joined) ? ( get_block_luma_otf_L2 ) : ( get_block_chroma_otf_L2 ) ; + p_Dpb->pf_OneComponentChromaPrediction4x4_regenerate = OneComponentChromaPrediction4x4_regenerate; + p_Dpb->pf_OneComponentChromaPrediction4x4_retrieve = OneComponentChromaPrediction4x4_regenerate; + break; + default: // otf not used + p_Dpb->pf_computeSAD = computeSAD; + p_Dpb->pf_computeSADWP = computeSADWP; + p_Dpb->pf_computeSATD = computeSATD; + p_Dpb->pf_computeSATDWP = computeSATDWP; + p_Dpb->pf_computeBiPredSAD1 = computeBiPredSAD1; + p_Dpb->pf_computeBiPredSAD2 = computeBiPredSAD2; + p_Dpb->pf_computeBiPredSATD1 = computeBiPredSATD1; + p_Dpb->pf_computeBiPredSATD2 = computeBiPredSATD2; + p_Dpb->pf_computeSSE = computeSSE; + p_Dpb->pf_computeSSEWP = computeSSEWP; + p_Dpb->pf_computeBiPredSSE1 = computeBiPredSSE1; + p_Dpb->pf_computeBiPredSSE2 = computeBiPredSSE2; + p_Dpb->pf_luma_prediction = luma_prediction; + p_Dpb->pf_luma_prediction_bi = luma_prediction_bi; + p_Dpb->pf_chroma_prediction = chroma_prediction; + p_Dpb->pf_OneComponentChromaPrediction4x4_regenerate = OneComponentChromaPrediction4x4_regenerate; + p_Dpb->pf_OneComponentChromaPrediction4x4_retrieve = OneComponentChromaPrediction4x4_retrieve; + break; + } +} + +static void set_storage_format(VideoParameters *p_Vid, FrameFormat *p_src, FrameFormat *p_dst) +{ + InputParameters *p_Inp = p_Vid->p_Inp; + *p_dst = *p_src; + p_dst->width[0] = (p_Inp->output.width[0] + p_Vid->auto_crop_right); + p_dst->height[0] = (p_Inp->output.height[0] + p_Vid->auto_crop_bottom); + p_dst->width[1] = p_dst->width[2] = p_dst->width[0] * mb_width_cr [p_dst->yuv_format] / 16; + p_dst->height[1]= p_dst->height[2] = p_dst->height[0] * mb_height_cr[p_dst->yuv_format] / 16; + p_dst->size_cmp[0] = p_dst->width[0] * p_dst->height[0]; + p_dst->size_cmp[2] = p_dst->size_cmp[1] = p_dst->width[1] * p_dst->height[1]; + p_dst->size = p_dst->size_cmp[0] + p_dst->size_cmp[1] + p_dst->size_cmp[2]; +} + +/*! +*********************************************************************** +* \brief +* Initialize encoder +*********************************************************************** +*/ +static void init_encoder(VideoParameters *p_Vid, InputParameters *p_Inp) +{ + int i; + p_Vid->p_Inp = p_Inp; + p_Vid->giRDOpt_B8OnlyFlag = FALSE; + p_Vid->p_log = NULL; + + //set coding layer number; + p_Vid->num_of_layers = p_Inp->num_of_views; + + p_Vid->cabac_encoding = 0; + p_Vid->frame_statistic_start = 1; + + if (p_Inp->Log2MaxFNumMinus4 == -1) + { + p_Vid->log2_max_frame_num_minus4 = iClip3(0,12, (int) (CeilLog2(p_Inp->no_frames) - 4)); // hack for now... + } + else + p_Vid->log2_max_frame_num_minus4 = p_Inp->Log2MaxFNumMinus4; + + if (p_Vid->log2_max_frame_num_minus4 == 0 && p_Inp->num_ref_frames == 16) + { + snprintf(errortext, ET_SIZE, " NumberReferenceFrames=%d and Log2MaxFNumMinus4=%d may lead to an invalid value of frame_num.", p_Inp->num_ref_frames, p_Inp-> Log2MaxFNumMinus4); + error (errortext, 500); + } + + // set proper p_Vid->log2_max_pic_order_cnt_lsb_minus4. + if (p_Inp->Log2MaxPOCLsbMinus4 == - 1) + p_Vid->log2_max_pic_order_cnt_lsb_minus4 = iClip3(0,12, (int) (CeilLog2( imax( p_Inp->no_frames, (p_Inp->NumberBFrames + 1) << 1 ) << 1 ) - 4)); // hack for now + else + p_Vid->log2_max_pic_order_cnt_lsb_minus4 = p_Inp->Log2MaxPOCLsbMinus4; + + if (((1<<(p_Vid->log2_max_pic_order_cnt_lsb_minus4 + 3)) < p_Inp->jumpd * 4) && p_Inp->Log2MaxPOCLsbMinus4 != -1) + error("log2_max_pic_order_cnt_lsb_minus4 might not be sufficient for encoding. Increase value.",400); + + if (strlen (p_Inp->ReconFile) > 0 && (p_Vid->p_dec = open(p_Inp->ReconFile, OPENFLAGS_WRITE, OPEN_PERMISSIONS))==-1) + { + snprintf(errortext, ET_SIZE, "Error open file %s", p_Inp->ReconFile); + error (errortext, 500); + } + +#if (MVC_EXTENSION_ENABLE) + if(p_Vid->num_of_layers == 2) + { + if (strlen (p_Inp->ReconFile2) > 0 && (p_Vid->p_dec2 = open(p_Inp->ReconFile2, OPENFLAGS_WRITE, OPEN_PERMISSIONS))==-1) + { + snprintf(errortext, ET_SIZE, "Error open file %s", p_Inp->ReconFile2); + error (errortext, 500); + } + } +#endif + if ((p_Inp->output.width[0] & 0x0F) != 0) + { + p_Vid->auto_crop_right = 16 - (p_Inp->output.width[0] & 0x0F); + } + else + { + p_Vid->auto_crop_right = 0; + } + + if (p_Inp->PicInterlace || p_Inp->MbInterlace) + { + if ((p_Inp->output.height[0] & 0x01) != 0) + { + error ("even number of lines required for interlaced coding", 500); + } + + if ((p_Inp->output.height[0] & 0x1F) != 0) + { + p_Vid->auto_crop_bottom = 32 - (p_Inp->output.height[0] & 0x1F); + } + else + { + p_Vid->auto_crop_bottom=0; + } + } + else + { + if ((p_Inp->output.height[0] & 0x0F) != 0) + { + p_Vid->auto_crop_bottom = 16 - (p_Inp->output.height[0] & 0x0F); + } + else + { + p_Vid->auto_crop_bottom = 0; + } + } + if (p_Vid->auto_crop_bottom || p_Vid->auto_crop_right) + { + fprintf (stdout, "Warning: Automatic cropping activated: Coded frame Size: %dx%d\n", + p_Inp->output.width[0] + p_Vid->auto_crop_right, p_Inp->output.height[0] + p_Vid->auto_crop_bottom); + } + + // read the slice group configuration file. Only for types 0, 2 or 6 + if ( 0 != p_Inp->num_slice_groups_minus1 ) + { + read_slice_group_info(p_Vid, p_Inp); + } + //! the number of slice groups is forced to be 1 for slice group type 3-5 + if(p_Inp->num_slice_groups_minus1 > 0) + { + if( (p_Inp->slice_group_map_type >= 3) && (p_Inp->slice_group_map_type<=5) ) + p_Inp->num_slice_groups_minus1 = 1; + } + + if(p_Inp->RCEnable) + { + if (p_Inp->basicunit == 0) + p_Inp->basicunit = (p_Inp->output.height[0] + p_Vid->auto_crop_bottom)*(p_Inp->output.width[0] + p_Vid->auto_crop_right)/256; + + if ( ((p_Inp->output.height[0] + p_Vid->auto_crop_bottom)*(p_Inp->output.width[0] + p_Vid->auto_crop_right)/256) % p_Inp->basicunit != 0) + { + snprintf(errortext, ET_SIZE, "Frame size in macroblocks must be a multiple of BasicUnit."); + error (errortext, 500); + } + } + + level_check(p_Vid, p_Inp); + + // Open Files + OpenFiles(&p_Inp->input_file1); +#if (MVC_EXTENSION_ENABLE) + if(p_Vid->num_of_layers==2) + { + OpenFiles(&p_Inp->input_file2); + } + p_Vid->prev_view_is_anchor = 0; + p_Vid->view_id = 0; // initialise view_id + set_dpb_layer_id(p_Vid, p_Vid->view_id); +#endif + + //init dpb storage formage; + p_Vid->output_format[0] = p_Inp->output; + set_storage_format(p_Vid, &(p_Inp->source), &(p_Vid->p_Dpb_layer[0]->storage_format)); + if(p_Vid->num_of_layers == 2) + { + { + p_Vid->output_format[1] = p_Inp->output; + set_storage_format(p_Vid, &(p_Inp->source), &(p_Vid->p_Dpb_layer[1]->storage_format)); + } + } + //end; + + init_qmatrix(p_Vid, p_Inp); + init_qoffset(p_Vid); + + init_poc(p_Vid); + generate_parameter_sets(p_Vid); + generate_encode_parameters(p_Vid); + set_level_indices(p_Vid); + + init_img (p_Vid); + + if (p_Inp->rdopt == 3) + { + init_error_conceal(p_Vid,p_Inp->ErrorConcealment); + //Zhifeng 090611 + init_distortion_estimation(p_Vid,p_Inp->de); + } + +#ifdef _LEAKYBUCKET_ + p_Vid->initial_Bframes = 0; +#if (MVC_EXTENSION_ENABLE) + if(p_Vid->num_of_layers == 2) + p_Vid->Bit_Buffer = (long *)malloc(((p_Inp->no_frames << 1) + 1) * sizeof(long)); + else + p_Vid->Bit_Buffer = (long *)malloc((p_Inp->no_frames + 1) * sizeof(long)); +#else + p_Vid->Bit_Buffer = (long *)malloc((p_Inp->no_frames + 1) * sizeof(long)); +#endif + p_Vid->total_frame_buffer = 0; +#endif + + // Prepare hierarchical coding structures. + // Code could be extended in the future to allow structure adaptation. + if (p_Inp->NumberBFrames && p_Inp->HierarchicalCoding == 3) + { + init_gop_structure(p_Vid, p_Inp); + interpret_gop_structure(p_Vid, p_Inp); + } + + // Initialize dpb for each layer + for(i=0; ip_Dpb_layer[i]->init_done = 0; + } + for(i=0; inum_of_layers, MAX_NUM_DPB_LAYERS); i++) + { + init_dpb(p_Vid, p_Vid->p_Dpb_layer[i]); + } + + init_out_buffer(p_Vid); + init_stats (p_Inp, p_Vid->p_Stats); + init_dstats(p_Vid->p_Dist); + +#if (MVC_EXTENSION_ENABLE) + if(p_Vid->num_of_layers == 2) + { + p_Vid->p_Dist->frame_ctr_v[0] = 0; + p_Vid->p_Dist->frame_ctr_v[1] = 0; + memset(p_Vid->p_Dist->metric_v[0], 0, TOTAL_DIST_TYPES * sizeof(DistMetric)); + memset(p_Vid->p_Dist->metric_v[1], 0, TOTAL_DIST_TYPES * sizeof(DistMetric)); + } +#endif + + p_Vid->enc_picture = NULL; + + init_global_buffers(p_Vid, p_Inp); + + if ( p_Inp->WPMCPrecision ) + wpxInitWPXPasses(p_Vid, p_Inp); + + init_motion_search_module (p_Vid, p_Inp); + information_init(p_Vid, p_Inp, p_Vid->p_Stats); + + if(p_Inp->DistortionYUVtoRGB) + init_YUVtoRGB(p_Vid, p_Inp); + + //Rate control + if (p_Inp->RCEnable) + rc_init_sequence(p_Vid, p_Inp); + + p_Vid->last_valid_reference = 0; + p_Vid->tot_time = 0; // time for total encoding session + p_Vid->last_bit_ctr_n = 0; + + p_Vid->initial_Bframes = p_Inp->NumberBFrames; + + p_Vid->type = I_SLICE; + // Write sequence header (with parameter sets) + p_Vid->p_Stats->bit_ctr_filler_data = 0; + p_Vid->p_Stats->bit_ctr_filler_data_n = 0; + p_Vid->p_Stats->bit_ctr_parametersets = 0; + +#if (MVC_EXTENSION_ENABLE) + if ( p_Vid->num_of_layers == 2 ) + { + p_Vid->p_Stats->bit_ctr_filler_data_v[0] = 0; + p_Vid->p_Stats->bit_ctr_filler_data_v[1] = 0; + p_Vid->p_Stats->bit_ctr_filler_data_n_v[0] = 0; + p_Vid->p_Stats->bit_ctr_filler_data_n_v[1] = 0; + p_Vid->p_Stats->bit_ctr_parametersets_v[0] = 0; + p_Vid->p_Stats->bit_ctr_parametersets_v[1] = 0; + } +#endif + + p_Vid->p_Stats->bit_slice = start_sequence(p_Vid, p_Inp); + + if (p_Inp->UseRDOQuant) + precalculate_unary_exp_golomb_level(p_Vid); + + if (p_Inp->ExplicitSeqCoding) + OpenExplicitSeqFile(p_Vid, p_Inp); + + p_Vid->searchRange.min_x = -p_Inp->search_range[0] << 2; + p_Vid->searchRange.max_x = p_Inp->search_range[0] << 2; + p_Vid->searchRange.min_y = -p_Inp->search_range[0] << 2; + p_Vid->searchRange.max_y = p_Inp->search_range[0] << 2; + + for ( i = 0; i < p_Vid->num_of_layers; i++ ) + { + setup_dpb_layer(p_Vid->p_Dpb_layer[i], p_Vid, p_Inp); + } + + initTranscodeModule( p_Vid ); +} + +void setup_coding_layer(VideoParameters *p_Vid) +{ + //setup the buffers; + int i; + int dpb_layer_id = p_Vid->dpb_layer_id; + InputParameters *p_Inp = p_Vid->p_Inp; + DecodedPictureBuffer *p_Dpb = p_Vid->p_Dpb_layer[dpb_layer_id]; + + p_Vid->active_sps = p_Vid->sps[dpb_layer_id]; + p_Vid->p_CurrEncodePar = p_Vid->p_EncodePar[dpb_layer_id]; + + if (p_Vid->p_CurrEncodePar->last_ref_idc == 1) + { + p_Vid->p_CurrEncodePar->frame_num++; + p_Vid->p_CurrEncodePar->frame_num %= p_Vid->max_frame_num; + } + p_Vid->frame_num = p_Vid->p_CurrEncodePar->frame_num; + + //set bitdepth info; + if(p_Vid->num_of_layers >1) + { + //all these initializations will be removed later; + CodingParameters *cps = p_Vid->p_CurrEncodePar; + + p_Vid->yuv_format = cps->yuv_format; + p_Vid->P444_joined = cps->P444_joined; + p_Vid->bitdepth_luma = cps->bitdepth_luma; + p_Vid->bitdepth_scale[0] = cps->bitdepth_scale[0]; + p_Vid->bitdepth_lambda_scale = cps->bitdepth_lambda_scale; + p_Vid->bitdepth_luma_qp_scale = cps->bitdepth_luma_qp_scale; + p_Vid->dc_pred_value_comp[0] = cps->dc_pred_value_comp[0]; + + p_Vid->max_pel_value_comp[0] = cps->max_pel_value_comp[0]; + p_Vid->max_imgpel_value_comp_sq[0] = cps->max_imgpel_value_comp_sq[0]; + p_Vid->mb_size[0][0] = p_Vid->mb_size[0][1] = cps->mb_size[0][0]; + + if (p_Vid->yuv_format != YUV400) + { + p_Vid->chroma_qp_offset[0] = p_Vid->active_pps->cb_qp_index_offset; + p_Vid->chroma_qp_offset[1] = p_Vid->active_pps->cr_qp_index_offset; + } + else + { + p_Vid->chroma_qp_offset[0] = 0; + p_Vid->chroma_qp_offset[1] = 0; + } + p_Vid->bitdepth_chroma = cps->bitdepth_chroma; + p_Vid->bitdepth_scale[1] = cps->bitdepth_scale[1]; + p_Vid->dc_pred_value_comp[2] = p_Vid->dc_pred_value_comp[1] = cps->dc_pred_value_comp[1]; + p_Vid->max_pel_value_comp[2] = p_Vid->max_pel_value_comp[1] = cps->max_pel_value_comp[1]; + p_Vid->max_imgpel_value_comp_sq[1] = cps->max_imgpel_value_comp_sq[1]; + p_Vid->max_imgpel_value_comp_sq[2] = cps->max_imgpel_value_comp_sq[2]; + p_Vid->num_blk8x8_uv = cps->num_blk8x8_uv; + p_Vid->num_cdc_coeff = cps->num_cdc_coeff; + p_Vid->mb_size[1][0] = p_Vid->mb_size[2][0] = p_Vid->mb_cr_size_x = cps->mb_cr_size_x; + p_Vid->mb_size[1][1] = p_Vid->mb_size[2][1] = p_Vid->mb_cr_size_y = cps->mb_cr_size_y; + p_Vid->bitdepth_chroma_qp_scale = cps->bitdepth_chroma_qp_scale; + p_Vid->max_bitCount = cps->max_bitCount; + + ////if ( p_Inp->ChromaMCBuffer ) + //chroma_mc_setup(p_Vid); + p_Vid->pad_size_uv_x = cps->pad_size_uv_x; + p_Vid->pad_size_uv_y = cps->pad_size_uv_y; + p_Vid->chroma_mask_mv_y = cps->chroma_mask_mv_y; + p_Vid->chroma_mask_mv_x = cps->chroma_mask_mv_x; + p_Vid->chroma_shift_y = cps->chroma_shift_y; + p_Vid->chroma_shift_x = cps->chroma_shift_x; + p_Vid->shift_cr_y = cps->shift_cr_y; + p_Vid->shift_cr_x = cps->shift_cr_x; + + p_Vid->padded_size_x = cps->padded_size_x; + p_Vid->padded_size_x_m8x8 = cps->padded_size_x_m8x8; + p_Vid->padded_size_x_m4x4 = cps->padded_size_x_m4x4; + p_Vid->cr_padded_size_x = cps->cr_padded_size_x; + p_Vid->cr_padded_size_x2 = cps->cr_padded_size_x2; + p_Vid->cr_padded_size_x4 = cps->cr_padded_size_x4; + p_Vid->cr_padded_size_x_m8 = cps->cr_padded_size_x_m8; + //end; + + p_Vid->dc_pred_value = p_Vid->dc_pred_value_comp[0]; // set defaults + p_Vid->max_imgpel_value = (short) p_Vid->max_pel_value_comp[0]; + + p_Vid->lambda = p_Vid->lambda_buf[dpb_layer_id]; + p_Vid->lambda_md = p_Vid->lambda_md_buf[dpb_layer_id]; + p_Vid->lambda_me = p_Vid->lambda_me_buf[dpb_layer_id]; + p_Vid->lambda_mf = p_Vid->lambda_mf_buf[dpb_layer_id]; + if ( p_Inp->UseRDOQuant ) + { + p_Vid->lambda_rdoq = p_Vid->lambda_rdoq_buf[dpb_layer_id]; + } + if (p_Inp->CtxAdptLagrangeMult == 1) + { + p_Vid->lambda_mf_factor = p_Vid->lambda_mf_factor_buf[dpb_layer_id]; + } + + setup_mbs(p_Vid->mb_data, p_Vid->FrameSizeInMbs, dpb_layer_id); + p_Vid->nz_coeff = p_Vid->nz_coeff_buf[dpb_layer_id]; + } + + if(p_Vid->imgY_com_buf[0]) + { + p_Vid->imgY_com = p_Vid->imgY_com_buf[dpb_layer_id]; + p_Vid->imgUV_com[0] = p_Vid->imgUV_com_buf[dpb_layer_id][0]; + p_Vid->imgUV_com[1] = p_Vid->imgUV_com_buf[dpb_layer_id][1]; + } + if(p_Vid->imgY_tmp_buf[0]) + { + p_Vid->imgY_tmp = p_Vid->imgY_tmp_buf[dpb_layer_id]; + p_Vid->imgUV_tmp[0] = p_Vid->imgUV_tmp_buf[dpb_layer_id][0]; + p_Vid->imgUV_tmp[1] = p_Vid->imgUV_tmp_buf[dpb_layer_id][1]; + } + if ( p_Inp->ChromaMCBuffer ) + p_Vid->OneComponentChromaPrediction4x4 = p_Dpb->pf_OneComponentChromaPrediction4x4_retrieve; + else + p_Vid->OneComponentChromaPrediction4x4 = p_Dpb->pf_OneComponentChromaPrediction4x4_regenerate; + + select_distortion(p_Vid, p_Inp); + // Setup Distortion Metrics depending on refinement level + for (i=0; i<3; i++) + { + switch(p_Inp->MEErrorMetric[i]) + { + case ERROR_SAD: + p_Vid->computeUniPred[i] = p_Dpb->pf_computeSAD; + p_Vid->computeUniPred[i + 3] = p_Dpb->pf_computeSADWP; + p_Vid->computeBiPred1[i] = p_Dpb->pf_computeBiPredSAD1; + p_Vid->computeBiPred2[i] = p_Dpb->pf_computeBiPredSAD2; + break; + case ERROR_SSE: + p_Vid->computeUniPred[i] = p_Dpb->pf_computeSSE; + p_Vid->computeUniPred[i + 3] = p_Dpb->pf_computeSSEWP; + p_Vid->computeBiPred1[i] = p_Dpb->pf_computeBiPredSSE1; + p_Vid->computeBiPred2[i] = p_Dpb->pf_computeBiPredSSE2; + break; + case ERROR_SATD : + default: + p_Vid->computeUniPred[i] = p_Dpb->pf_computeSATD; + p_Vid->computeUniPred[i + 3] = p_Dpb->pf_computeSATDWP; + p_Vid->computeBiPred1[i] = p_Dpb->pf_computeBiPredSATD1; + p_Vid->computeBiPred2[i] = p_Dpb->pf_computeBiPredSATD2; + break; + } + } + +} + +/*! +************************************************************************ +* \brief +* Prepare parameters for the current frame +************************************************************************ +*/ +static void prepare_frame_params(VideoParameters *p_Vid, InputParameters *p_Inp, int curr_frame_to_code) +{ + SeqStructure *p_seq_struct = p_Vid->p_pred; + FrameUnitStruct *p_cur_frm = p_Vid->p_curr_frm_struct; + int i; + + setup_coding_layer(p_Vid); + + if ( p_Inp->ExplicitSeqCoding ) + { + ExpFrameInfo *info = &p_Vid->expSeq->info[curr_frame_to_code % p_Vid->expSeq->no_frames]; + ReadExplicitSeqFile(p_Vid->expSeq, p_Vid->expSFile, curr_frame_to_code); + // override and overwrite whatever was in p_cur_frm + populate_frame_explicit( info, p_Inp, p_cur_frm, p_seq_struct->max_num_slices ); + } + + // populate coding parameters for the current frame (frame_no, slice_type, nal_ref_idc, poc, reset frame_num) + p_Vid->frame_no = p_cur_frm->frame_no; + p_Vid->frm_no_in_file = (1 + p_Inp->frame_skip) * p_Vid->frame_no; + if ( p_cur_frm->type == SI_SLICE ) + { + set_slice_type( p_Vid, p_Inp, SP_SLICE ); + } + else + { + set_slice_type( p_Vid, p_Inp, p_cur_frm->type ); // dominant slice type; perhaps it is better to use the type of the first slice? + } + p_Vid->nal_reference_idc = p_cur_frm->nal_ref_idc; + switch( p_Vid->pic_order_cnt_type ) + { + case 0: + get_poc_type_zero( p_Vid, p_Inp, p_cur_frm ); + break; + case 1: + get_poc_type_one( p_Vid, p_Inp, p_cur_frm ); + break; + default: + case 2: + get_poc_type_zero( p_Vid, p_Inp, p_cur_frm ); + break; + } + + if (p_cur_frm->idr_flag == 1) + { + for (i = 0; i < p_Vid->num_of_layers; i++) + { + p_Vid->p_EncodePar[i]->frame_num = 0; + } + p_Vid->frame_num = 0; + } + + //Rate control + if (p_Inp->RCEnable && p_Vid->type == I_SLICE) + rc_init_gop_params(p_Vid, p_Inp); + + // which layer does the image belong to? + p_Vid->layer = ((p_Vid->curr_frm_idx - p_Vid->last_idr_code_order) % (p_Inp->NumFramesInELSubSeq + 1)) ? 0 : 1; +} + +/*! +*********************************************************************** +* \brief +* Encode a sequence +*********************************************************************** +*/ +static void encode_sequence(VideoParameters *p_Vid, InputParameters *p_Inp) +{ + int curr_frame_to_code; + int frames_to_code; + int frame_num_bak = 0, frame_coded; + int frm_struct_buffer; + SeqStructure *p_seq_struct = p_Vid->p_pred; + FrameUnitStruct *p_frm; + +#if (MVC_EXTENSION_ENABLE) + int tmp_rate_control_enable = p_Inp->RCEnable; + + if ( p_Inp->num_of_views == 2 ) + { + frames_to_code = p_Inp->no_frames << 1; + p_frm = p_seq_struct->p_frm_mvc; + frm_struct_buffer = p_seq_struct->num_frames_mvc; + } + else +#endif + { + frames_to_code = p_Inp->no_frames; + p_frm = p_seq_struct->p_frm; + frm_struct_buffer = p_Vid->frm_struct_buffer; + } + + for (curr_frame_to_code = 0; curr_frame_to_code < frames_to_code; curr_frame_to_code++) + { +#if (MVC_EXTENSION_ENABLE) + if ( p_Inp->num_of_views == 2 ) + { + if ( (curr_frame_to_code & 1) == 0 ) // call only for view_id 0 + { + // determine whether to populate additional frames in the prediction structure + if ( (curr_frame_to_code >> 1) >= p_Vid->p_pred->pop_start_frame ) + { + int start = p_seq_struct->pop_start_frame, end; + + populate_frm_struct( p_Vid, p_Inp, p_seq_struct, p_Inp->FrmStructBufferLength, frames_to_code >> 1 ); + end = p_seq_struct->pop_start_frame; + populate_frm_struct_mvc( p_Vid, p_Inp, p_seq_struct, start, end ); + } + } + + p_Vid->curr_frm_idx = curr_frame_to_code; + p_Vid->p_curr_frm_struct = p_frm + ( p_Vid->curr_frm_idx % frm_struct_buffer ); // pointer to current frame structure + p_Vid->number = curr_frame_to_code; + + p_Vid->view_id = p_Vid->p_curr_frm_struct->view_id; + set_dpb_layer_id(p_Vid, p_Vid->view_id); + if ( p_Vid->view_id == 1 ) + { + p_Vid->curr_frm_idx = p_Vid->number = (curr_frame_to_code - 1) >> 1; + p_Vid->p_curr_frm_struct->qp = p_Vid->qp = iClip3( -p_Vid->bitdepth_luma_qp_scale, MAX_QP, p_Vid->AverageFrameQP + p_Inp->View1QPOffset ); + } + else + { + p_Vid->curr_frm_idx = p_Vid->number = curr_frame_to_code >> 1; + } + if ( p_Vid->view_id == 1 && tmp_rate_control_enable ) + { + p_Inp->RCEnable = 0; + } + else + { + p_Inp->RCEnable = tmp_rate_control_enable; + } + } + else +#endif + { + // determine whether to populate additional frames in the prediction structure + if ( curr_frame_to_code >= p_Vid->p_pred->pop_start_frame ) + { + populate_frm_struct( p_Vid, p_Inp, p_seq_struct, p_Inp->FrmStructBufferLength, frames_to_code ); + } + p_Vid->curr_frm_idx = curr_frame_to_code; + p_Vid->p_curr_frm_struct = p_frm + ( p_Vid->curr_frm_idx % frm_struct_buffer ); // pointer to current frame structure + p_Vid->number = curr_frame_to_code; + + } + + if ( p_Vid->p_curr_frm_struct->frame_no >= p_Inp->no_frames ) + { + continue; + } + + // Update frame_num counter + frame_num_bak = p_Vid->p_EncodePar[p_Vid->dpb_layer_id]->frame_num; + + prepare_frame_params(p_Vid, p_Inp, curr_frame_to_code); + + // redundant frame initialization and allocation + if (p_Inp->redundant_pic_flag) + { + init_redundant_frame(p_Vid, p_Inp); + set_redundant_frame(p_Vid, p_Inp); + } + + frame_coded = encode_one_frame(p_Vid, p_Inp); // encode one frame; + if ( !frame_coded ) + { + p_Vid->frame_num = p_Vid->p_CurrEncodePar->frame_num = frame_num_bak; + continue; + } + + p_Vid->p_CurrEncodePar->last_ref_idc = p_Vid->nal_reference_idc ? 1 : 0; + + // if key frame is encoded, encode one redundant frame + if (p_Inp->redundant_pic_flag && p_Vid->key_frame) + { + encode_one_redundant_frame(p_Vid, p_Inp); + } + + if (p_Inp->EnableOpenGOP && p_Vid->p_curr_frm_struct->random_access) + { + if (p_Inp->PicInterlace) + { + if (p_Vid->p_curr_frm_struct->p_top_fld_pic->p_Slice[0].type == I_SLICE && p_Vid->p_curr_frm_struct->random_access) //Currently encoder always codes top field as I + { + p_Vid->last_valid_reference = p_Vid->ThisPOC & (~( (signed int)1 )); + //printf("last valid ref: %d", p_Vid->last_valid_reference); + } + } + else if (p_Vid->type == I_SLICE) + { + p_Vid->last_valid_reference = p_Vid->ThisPOC; + //printf("last valid ref: %d", p_Vid->last_valid_reference); + } + } + + if (p_Inp->ReportFrameStats) + { + report_frame_statistic(p_Vid, p_Inp); + } + + } + +#if EOS_OUTPUT + end_of_stream(p_Vid); +#endif + +#if (MVC_EXTENSION_ENABLE) + if(p_Inp->num_of_views == 2) //? it should use num_of_layers; + { + p_Inp->RCEnable = tmp_rate_control_enable; + } +#endif +} + + +/*! +*********************************************************************** +* \brief +* Free memory allocated for the encoder +*********************************************************************** +*/ + +void free_encoder_memory(VideoParameters *p_Vid, InputParameters *p_Inp) +{ + //! Edited by Joao Carreira + closeTranscodeModule( p_Vid ); + + terminate_sequence(p_Vid, p_Inp); + flush_dpb(p_Vid->p_Dpb_layer[0], &p_Inp->output); + flush_dpb(p_Vid->p_Dpb_layer[1], &p_Inp->output); + CloseFiles(&p_Inp->input_file1); + + if (-1 != p_Vid->p_dec) + close(p_Vid->p_dec); + +#if (MVC_EXTENSION_ENABLE) + if(p_Vid->num_of_layers==2) + CloseFiles(&p_Inp->input_file2); + if (-1 != p_Vid->p_dec2) + close(p_Vid->p_dec2); +#endif + + if (p_Enc->p_trace) + fclose(p_Enc->p_trace); + + clear_motion_search_module (p_Vid, p_Inp); + + RandomIntraUninit(p_Vid); + FmoUninit(p_Vid); + + if (p_Inp->NumberBFrames && p_Inp->HierarchicalCoding == 3) + { + clear_gop_structure (p_Vid); + } + +#ifdef _LEAKYBUCKET_ + calc_buffer(p_Vid, p_Inp); +#endif + + // report everything + report(p_Vid, p_Inp, p_Vid->p_Stats); + +#ifdef _LEAKYBUCKET_ + free_pointer(p_Vid->Bit_Buffer); +#endif + free_dpb(p_Vid->p_Dpb_layer[0]); + free_dpb(p_Vid->p_Dpb_layer[1]); + uninit_out_buffer(p_Vid); + + free_global_buffers(p_Vid, p_Inp); + + FreeParameterSets(p_Vid); + + if (p_Inp->ExplicitSeqCoding) + CloseExplicitSeqFile(p_Vid); + + // free image mem + free_img (p_Vid, p_Inp); +} + +/*! +*********************************************************************** +* \brief +* Initializes the Image structure with appropriate parameters. +* \par Input: +* Input Parameters InputParameters *inp +* \par Output: +* Image Parameters VideoParameters *p_Vid +*********************************************************************** +*/ +static void init_img( VideoParameters *p_Vid) +{ + InputParameters *p_Inp = p_Vid->p_Inp; + int i, j; + + p_Vid->number = -1; + + p_Vid->last_idr_code_order = 0; + p_Vid->last_idr_disp_order = 0; + p_Vid->last_mmco_5_code_order = -1; + p_Vid->last_mmco_5_disp_order = -1; + // Color format + p_Vid->yuv_format = p_Inp->output.yuv_format; + p_Vid->P444_joined = (p_Vid->yuv_format == YUV444 && (p_Inp->separate_colour_plane_flag == 0)); + + //pel bitdepth init + p_Vid->bitdepth_luma = (short) p_Inp->output.bit_depth[0]; + p_Vid->bitdepth_scale[0] = 1 << (p_Vid->bitdepth_luma - 8); + p_Vid->bitdepth_lambda_scale = 2 * (p_Vid->bitdepth_luma - 8); + p_Vid->bitdepth_luma_qp_scale = 3 * p_Vid->bitdepth_lambda_scale; + p_Vid->dc_pred_value_comp[0] = (imgpel) (1<<(p_Vid->bitdepth_luma - 1)); + p_Vid->max_pel_value_comp[0] = (1<bitdepth_luma) - 1; + p_Vid->max_imgpel_value_comp_sq[0] = p_Vid->max_pel_value_comp[0] * p_Vid->max_pel_value_comp[0]; + + p_Vid->dc_pred_value = p_Vid->dc_pred_value_comp[0]; // set defaults + p_Vid->max_imgpel_value = (short) p_Vid->max_pel_value_comp[0]; + p_Vid->mb_size[0][0] = p_Vid->mb_size[0][1] = MB_BLOCK_SIZE; + + // Initialization for RC QP parameters (could be placed in ratectl.c) + p_Vid->RCMinQP = p_Inp->RCMinQP[P_SLICE]; + p_Vid->RCMaxQP = p_Inp->RCMaxQP[P_SLICE]; + + p_Vid->WalkAround = 0; + p_Vid->NumberOfMBs = 0; + + // Set current residue & prediction array pointers + + if (p_Vid->active_sps->profile_idc == BASELINE || p_Vid->active_sps->profile_idc == MAIN || p_Vid->active_sps->profile_idc == EXTENDED) + p_Vid->min_IPCM_value = 1; // See Annex A for restriction in pcm sample values for pre FRExt profiles + else + p_Vid->min_IPCM_value = 0; + + if (p_Vid->yuv_format != YUV400) + { + p_Vid->bitdepth_chroma = (short) p_Inp->output.bit_depth[1]; + p_Vid->bitdepth_scale[1] = 1 << (p_Vid->bitdepth_chroma - 8); + p_Vid->dc_pred_value_comp[1] = (imgpel) (1<<(p_Vid->bitdepth_chroma - 1)); + p_Vid->dc_pred_value_comp[2] = p_Vid->dc_pred_value_comp[1]; + p_Vid->max_pel_value_comp[1] = (1<bitdepth_chroma) - 1; + p_Vid->max_pel_value_comp[2] = p_Vid->max_pel_value_comp[1]; + p_Vid->max_imgpel_value_comp_sq[1] = p_Vid->max_pel_value_comp[1] * p_Vid->max_pel_value_comp[1]; + p_Vid->max_imgpel_value_comp_sq[2] = p_Vid->max_pel_value_comp[2] * p_Vid->max_pel_value_comp[2]; + p_Vid->num_blk8x8_uv = (1<yuv_format)&(~(0x1)); + p_Vid->num_cdc_coeff = p_Vid->num_blk8x8_uv << 1; + + p_Vid->mb_size[1][0] = p_Vid->mb_size[2][0] = p_Vid->mb_cr_size_x = (p_Vid->yuv_format == YUV420 || p_Vid->yuv_format == YUV422) ? 8 : 16; + p_Vid->mb_size[1][1] = p_Vid->mb_size[2][1] = p_Vid->mb_cr_size_y = (p_Vid->yuv_format == YUV444 || p_Vid->yuv_format == YUV422) ? 16 : 8; + + p_Vid->bitdepth_chroma_qp_scale = 6*(p_Vid->bitdepth_chroma - 8); + + p_Vid->chroma_qp_offset[0] = p_Vid->active_pps->cb_qp_index_offset; + p_Vid->chroma_qp_offset[1] = p_Vid->active_pps->cr_qp_index_offset; + } + else + { + p_Vid->bitdepth_chroma = 0; + p_Vid->bitdepth_scale[1] = 0; + p_Vid->max_pel_value_comp[1] = 0; + p_Vid->max_pel_value_comp[2] = p_Vid->max_pel_value_comp[1]; + p_Vid->max_imgpel_value_comp_sq[1] = p_Vid->max_pel_value_comp[1] * p_Vid->max_pel_value_comp[1]; + p_Vid->max_imgpel_value_comp_sq[2] = p_Vid->max_pel_value_comp[2] * p_Vid->max_pel_value_comp[2]; + p_Vid->num_blk8x8_uv = 0; + p_Vid->num_cdc_coeff = 0; + p_Vid->mb_size[1][0] = p_Vid->mb_size[2][0] = p_Vid->mb_cr_size_x = 0; + p_Vid->mb_size[1][1] = p_Vid->mb_size[2][1] = p_Vid->mb_cr_size_y = 0; + + p_Vid->bitdepth_chroma_qp_scale = 0; + + p_Vid->chroma_qp_offset[0] = 0; + p_Vid->chroma_qp_offset[1] = 0; + } + + p_Vid->max_bitCount = 128 + 256 * p_Vid->bitdepth_luma + 2 * p_Vid->mb_cr_size_y * p_Vid->mb_cr_size_x * p_Vid->bitdepth_chroma; + //p_Vid->max_bitCount = (128 + 256 * p_Vid->bitdepth_luma + 2 *p_Vid->mb_cr_size_y * p_Vid->mb_cr_size_x * p_Vid->bitdepth_chroma)*2; + + p_Vid->max_qp_delta = (25 + (p_Vid->bitdepth_luma_qp_scale>>1)); + p_Vid->min_qp_delta = p_Vid->max_qp_delta + 1; + + p_Vid->num_ref_frames = p_Vid->active_sps->num_ref_frames; + p_Vid->max_num_references = p_Vid->active_sps->frame_mbs_only_flag ? p_Vid->active_sps->num_ref_frames : 2 * p_Vid->active_sps->num_ref_frames; + +#if (MVC_EXTENSION_ENABLE) + p_Vid->sec_view_force_fld = 0; +#endif + + p_Vid->base_dist = p_Inp->jumpd + 1; + + // Intra/IDR related parameters + p_Vid->lastIntraNumber = 0; + p_Vid->lastINTRA = 0; + p_Vid->last_ref_idc = 0; + p_Vid->idr_refresh = 0; + + p_Vid->framerate = (float) p_Inp->output.frame_rate; // The basic frame rate (of the original sequence) + + if (p_Inp->AdaptiveRounding) + { + if (p_Vid->yuv_format != 0) + { + get_mem4Dint(&(p_Vid->ARCofAdj4x4), 3, MAXMODE, MB_BLOCK_SIZE, MB_BLOCK_SIZE); //all modes + get_mem4Dint(&(p_Vid->ARCofAdj8x8), p_Vid->P444_joined ? 3 : 1, MAXMODE, MB_BLOCK_SIZE, MB_BLOCK_SIZE); //modes 0, 1, 2, 3, P8x8 + } + else + { + get_mem4Dint(&(p_Vid->ARCofAdj4x4), 1, MAXMODE, MB_BLOCK_SIZE, MB_BLOCK_SIZE); //all modes + get_mem4Dint(&(p_Vid->ARCofAdj8x8), 1, MAXMODE, MB_BLOCK_SIZE, MB_BLOCK_SIZE); //modes 0, 1, 2, 3, P8x8 + } + } + + p_Vid->width = (p_Inp->output.width[0] + p_Vid->auto_crop_right); + p_Vid->height = (p_Inp->output.height[0] + p_Vid->auto_crop_bottom); + p_Vid->width_blk = p_Vid->width / BLOCK_SIZE; + p_Vid->height_blk = p_Vid->height / BLOCK_SIZE; + p_Vid->width_padded = p_Vid->width + 2 * IMG_PAD_SIZE_X; + p_Vid->height_padded = p_Vid->height + 2 * IMG_PAD_SIZE_Y; + + if (p_Vid->yuv_format != YUV400) + { + p_Vid->width_cr = p_Vid->width * mb_width_cr [p_Vid->yuv_format] / 16; + p_Vid->height_cr= p_Vid->height * mb_height_cr[p_Vid->yuv_format] / 16; + } + else + { + p_Vid->width_cr = 0; + p_Vid->height_cr= 0; + } + + p_Vid->height_cr_frame = p_Vid->height_cr; + + p_Vid->size = p_Vid->width * p_Vid->height; + p_Vid->size_cr = p_Vid->width_cr * p_Vid->height_cr; + + p_Vid->PicWidthInMbs = p_Vid->width / MB_BLOCK_SIZE; + p_Vid->FrameHeightInMbs = p_Vid->height / MB_BLOCK_SIZE; + p_Vid->FrameSizeInMbs = p_Vid->PicWidthInMbs * p_Vid->FrameHeightInMbs; + + p_Vid->PicHeightInMapUnits = ( p_Vid->active_sps->frame_mbs_only_flag ? p_Vid->FrameHeightInMbs : p_Vid->FrameHeightInMbs >> 1 ); + + if ((p_Vid->b8x8info = (Block8x8Info *) calloc(1, sizeof(Block8x8Info))) == NULL) + no_mem_exit("init_img: p_Vid->block8x8info"); + + if( (p_Inp->separate_colour_plane_flag != 0) ) + { + for( i = 0; i < MAX_PLANE; i++ ){ + //if ((p_Vid->mb_data_JV[i] = (Macroblock *) calloc(p_Vid->FrameSizeInMbs,sizeof(Macroblock))) == NULL) + if((p_Vid->mb_data_JV[i] = alloc_mbs(p_Vid, p_Vid->FrameSizeInMbs, p_Vid->num_of_layers)) == NULL) + no_mem_exit("init_img: p_Vid->mb_data_JV"); + } + p_Vid->mb_data = NULL; + } + else + { + //if ((p_Vid->mb_data = (Macroblock *) calloc(p_Vid->FrameSizeInMbs, sizeof(Macroblock))) == NULL) + if ((p_Vid->mb_data = alloc_mbs(p_Vid, p_Vid->FrameSizeInMbs, p_Vid->num_of_layers)) == NULL) + no_mem_exit("init_img: p_Vid->mb_data"); + } + + if (p_Inp->UseConstrainedIntraPred) + { + if ((p_Vid->intra_block = (short*) calloc(p_Vid->FrameSizeInMbs, sizeof(short))) == NULL) + no_mem_exit("init_img: p_Vid->intra_block"); + } + + if (p_Inp->CtxAdptLagrangeMult == 1) + { + if ((p_Vid->mb16x16_cost_frame = (double*)calloc(p_Vid->FrameSizeInMbs, sizeof(double))) == NULL) + { + no_mem_exit("init p_Vid->mb16x16_cost_frame"); + } + } + get_mem2D((byte***)&(p_Vid->ipredmode), p_Vid->height_blk, p_Vid->width_blk); //need two extra rows at right and bottom + get_mem2D((byte***)&(p_Vid->ipredmode8x8), p_Vid->height_blk, p_Vid->width_blk); // help storage for ipredmode 8x8, inserted by YV + memset(&(p_Vid->ipredmode[0][0]) , -1, p_Vid->height_blk * p_Vid->width_blk *sizeof(char)); + memset(&(p_Vid->ipredmode8x8[0][0]), -1, p_Vid->height_blk * p_Vid->width_blk *sizeof(char)); + + + // CAVLC mem + if(p_Vid->num_of_layers>1) + { + get_mem3Dint(&(p_Vid->nz_coeff_buf[0]), p_Vid->FrameSizeInMbs, 4, 4+p_Vid->num_blk8x8_uv); + if(p_Vid->p_Dpb_layer[1]->storage_format.yuv_format != YUV400 && p_Vid->p_Dpb_layer[1]->storage_format.yuv_format != p_Vid->p_Dpb_layer[0]->storage_format.yuv_format) + { + int num_blk8x8_uv = (1<p_Dpb_layer[1]->storage_format.yuv_format)&(~(0x1)); + get_mem3Dint(&(p_Vid->nz_coeff_buf[1]), p_Vid->FrameSizeInMbs, 4, 4+num_blk8x8_uv); + } + else + p_Vid->nz_coeff_buf[1] = p_Vid->nz_coeff_buf[0]; + p_Vid->nz_coeff = p_Vid->nz_coeff_buf[0]; + } + else + { + get_mem3Dint(&(p_Vid->nz_coeff_buf[0]), p_Vid->FrameSizeInMbs, 4, 4+p_Vid->num_blk8x8_uv); + p_Vid->nz_coeff = p_Vid->nz_coeff_buf[0]; + p_Vid->nz_coeff_buf[1] = NULL; + } + + get_mem2Dolm (&(p_Vid->lambda_buf[0]) , 10, 52 + p_Vid->bitdepth_luma_qp_scale, p_Vid->bitdepth_luma_qp_scale); + p_Vid->lambda = p_Vid->lambda_buf[0]; + get_mem2Dodouble (&(p_Vid->lambda_md_buf[0]), 10, 52 + p_Vid->bitdepth_luma_qp_scale, p_Vid->bitdepth_luma_qp_scale); + p_Vid->lambda_md = p_Vid->lambda_md_buf[0]; + get_mem3Dodouble (&(p_Vid->lambda_me_buf[0]), 10, 52 + p_Vid->bitdepth_luma_qp_scale, 3, p_Vid->bitdepth_luma_qp_scale); + p_Vid->lambda_me = p_Vid->lambda_me_buf[0]; + get_mem3Doint (&(p_Vid->lambda_mf_buf[0]), 10, 52 + p_Vid->bitdepth_luma_qp_scale, 3, p_Vid->bitdepth_luma_qp_scale); + p_Vid->lambda_mf = p_Vid->lambda_mf_buf[0]; + if ( p_Inp->UseRDOQuant ) + { + get_mem2Dodouble (&(p_Vid->lambda_rdoq_buf[0]), 10, 52 + p_Vid->bitdepth_luma_qp_scale, p_Vid->bitdepth_luma_qp_scale); + p_Vid->lambda_rdoq = p_Vid->lambda_rdoq_buf[0]; + } + if (p_Inp->CtxAdptLagrangeMult == 1) + { + get_mem2Dodouble(&(p_Vid->lambda_mf_factor_buf[0]), 10, 52 + p_Vid->bitdepth_luma_qp_scale, p_Vid->bitdepth_luma_qp_scale); + p_Vid->lambda_mf_factor = p_Vid->lambda_mf_factor_buf[0]; + } + if(p_Vid->num_of_layers>1) + { + p_Vid->lambda_buf[1] = p_Vid->lambda_buf[0]; + p_Vid->lambda_md_buf[1] = p_Vid->lambda_md_buf[0]; + p_Vid->lambda_me_buf[1] = p_Vid->lambda_me_buf[0]; + p_Vid->lambda_mf_buf[1] = p_Vid->lambda_mf_buf[0]; + p_Vid->lambda_rdoq_buf[1] = p_Vid->lambda_rdoq_buf[0]; + p_Vid->lambda_mf_factor_buf[1] = p_Vid->lambda_mf_factor_buf[0]; + } + + p_Vid->mb_y_upd = 0; + + if(((p_Inp->RDPictureDecision) && p_Inp->GenerateMultiplePPS) || (p_Inp->WeightedPrediction || p_Inp->WeightedBiprediction)) + { + int num_slices; + + if(p_Inp->slice_mode == 1) + { + num_slices = p_Vid->FrameSizeInMbs/p_Inp->slice_argument; + if((unsigned int)(num_slices * p_Inp->slice_argument) < p_Vid->FrameSizeInMbs) + num_slices++; + } + else + num_slices = 1; + + // All weight structures assigned using "MAX_REFERENCE_PICTURES". This plays it safe since one may + // consider reordering or use this for MVC/FCFR coding where references are expanded by 1. + p_Vid->num_slices_wp = num_slices; + get_mem4Dshort(&(p_Vid->wp_weights), 3, 2, MAX_REFERENCE_PICTURES, num_slices); + get_mem4Dshort(&(p_Vid->wp_offsets), 3, 2, MAX_REFERENCE_PICTURES, num_slices); + get_mem5Dshort(&(p_Vid->wbp_weight), 3, 2, MAX_REFERENCE_PICTURES, MAX_REFERENCE_PICTURES, num_slices); + } + else + { + p_Vid->num_slices_wp = 0; + p_Vid->wp_weights = NULL; + p_Vid->wp_offsets = NULL; + p_Vid->wbp_weight = NULL; + } + + RandomIntraInit (p_Vid, p_Vid->PicWidthInMbs, p_Vid->FrameHeightInMbs, p_Inp->RandomIntraMBRefresh); + + InitSEIMessages(p_Vid, p_Inp); + + initInput(p_Vid, &p_Inp->source, &p_Inp->output); + + // Allocate I/O Frame memory + AllocateFrameMemory(p_Vid, p_Inp, &p_Inp->source); + + // Initialize filtering parameters. If sending parameters, the offsets are + // multiplied by 2 since inputs are taken in "div 2" format. + // If not sending parameters, all fields are cleared + if (p_Inp->DFSendParameters) + { + for (j = 0; j < 2; j++) + { + for (i = 0; i < NUM_SLICE_TYPES; i++) + { + p_Inp->DFAlpha[j][i] <<= 1; + p_Inp->DFBeta [j][i] <<= 1; +#if (MVC_EXTENSION_ENABLE) + if (p_Vid->num_of_layers == 2) + { + p_Inp->EnhLayerDFAlpha[j][i] <<= 1; + p_Inp->EnhLayerDFBeta [j][i] <<= 1; + } +#endif + } + } + } + else + { + for (j = 0; j < 2; j++) + { + for (i = 0; i < NUM_SLICE_TYPES; i++) + { + p_Inp->DFDisableIdc[j][i] = 0; + p_Inp->DFAlpha [j][i] = 0; + p_Inp->DFBeta [j][i] = 0; +#if (MVC_EXTENSION_ENABLE) + if (p_Vid->num_of_layers == 2) + { + p_Inp->EnhLayerDFDisableIdc[j][i] = 0; + p_Inp->EnhLayerDFAlpha[j][i] = 0; + p_Inp->EnhLayerDFBeta [j][i] = 0; + } +#endif + } + } + } + + p_Vid->ChromaArrayType = p_Inp->separate_colour_plane_flag ? 0 : p_Inp->output.yuv_format; + p_Vid->colour_plane_id = 0; + + if (p_Inp->RDPictureDecision) + p_Vid->frm_iter = 3; + else + p_Vid->frm_iter = 1; + + p_Vid->max_frame_num = 1 << (p_Vid->log2_max_frame_num_minus4 + 4); + p_Vid->max_pic_order_cnt_lsb = 1 << (p_Vid->log2_max_pic_order_cnt_lsb_minus4 + 4); + + p_Vid->prev_frame_no = 0; // POC200301 + p_Vid->consecutive_non_reference_pictures = 0; // POC200301 + + p_Vid->fld_type = 0; + + p_Vid->p_Inp = p_Inp; + + create_context_memory (p_Vid, p_Inp); + +} + + +/*! +*********************************************************************** +* \brief +* Free the Image structures +* \par Input: +* Image Parameters VideoParameters *p_Vid +*********************************************************************** +*/ +static void free_img (VideoParameters *p_Vid, InputParameters *p_Inp) +{ + // Delete Frame memory + DeleteFrameMemory(p_Vid); + + CloseSEIMessages(p_Vid, p_Inp); + + free_context_memory (p_Vid); + + if (p_Inp->AdaptiveRounding) + { + free_mem4Dint(p_Vid->ARCofAdj4x4); + free_mem4Dint(p_Vid->ARCofAdj8x8); + } + + if (p_Vid->wp_weights) + free_mem4Dshort(p_Vid->wp_weights); + if (p_Vid->wp_offsets) + free_mem4Dshort(p_Vid->wp_offsets); + if (p_Vid->wbp_weight) + free_mem5Dshort(p_Vid->wbp_weight); + + + free_pointer (p_Vid->p_SEI); + free_pointer (p_Vid->p_QScale); + free_pointer (p_Vid->p_Quant); + free_pointer(p_Vid->p_Dpb_layer[0]); + p_Vid->p_Dpb_layer[0] = p_Vid->p_Dpb_layer[1] = NULL; + free_pointer (p_Vid->p_Stats); + free_pointer (p_Vid->p_Dist); + // + free_encode_parameters(p_Vid); + free_pointer (p_Vid); +} + + +/*! +*********************************************************************** +* \brief +* Free the Input structures +* \par Input: +* Input Parameters InputParameters *p_Inp +*********************************************************************** +*/ +static void free_params (InputParameters *p_Inp) +{ + if ( p_Inp != NULL ) + { + free_pointer( p_Inp->top_left ); + free_pointer( p_Inp->bottom_right ); + free_pointer( p_Inp->slice_group_id ); + free_pointer( p_Inp->run_length_minus1 ); + free_pointer( p_Inp ); + } +} + + +/*! +************************************************************************ +* \brief +* Allocates the picture structure along with its dependent +* data structures +* \return +* Pointer to a Picture +************************************************************************ +*/ +Picture *malloc_picture() +{ + Picture *pic; + if ((pic = calloc (1, sizeof (Picture))) == NULL) no_mem_exit ("malloc_picture: Picture structure"); + //! Note: slice structures are allocated as needed in code_a_picture + return pic; +} + +/*! +************************************************************************ +* \brief +* Frees a picture +* \param +* pic: POinter to a Picture to be freed +************************************************************************ +*/ +void free_picture(Picture *pic) +{ + if (pic != NULL) + { + free_slice_list(pic); + free_pointer (pic); + } +} + + +/*! +************************************************************************ +* \brief +* memory allocation for original picture buffers +************************************************************************ +*/ +int init_orig_buffers(VideoParameters *p_Vid, ImageData *imgData) +{ + InputParameters *p_Inp = p_Vid->p_Inp; + int memory_size = 0; + int nplane; + + // allocate memory for reference frame buffers: imgData->frm_data + imgData->format = p_Inp->output; + imgData->format.width[0] = p_Vid->width; + imgData->format.width[1] = p_Vid->width_cr; + imgData->format.width[2] = p_Vid->width_cr; + imgData->format.height[0] = p_Vid->height; + imgData->format.height[1] = p_Vid->height_cr; + imgData->format.height[2] = p_Vid->height_cr; + imgData->format.yuv_format = p_Vid->yuv_format; + imgData->format.auto_crop_bottom = p_Vid->auto_crop_bottom; + imgData->format.auto_crop_right = p_Vid->auto_crop_right; + imgData->format.auto_crop_bottom_cr = (p_Vid->auto_crop_bottom * mb_height_cr [p_Vid->yuv_format]) / MB_BLOCK_SIZE; + imgData->format.auto_crop_right_cr = (p_Vid->auto_crop_right * mb_width_cr [p_Vid->yuv_format]) / MB_BLOCK_SIZE; + imgData->frm_stride[0] = p_Vid->width; + imgData->frm_stride[1] = imgData->frm_stride[2] = p_Vid->width_cr; + imgData->top_stride[0] = imgData->bot_stride[0] = imgData->frm_stride[0] << 1; + imgData->top_stride[1] = imgData->top_stride[2] = imgData->bot_stride[1] = imgData->bot_stride[2] = imgData->frm_stride[1] << 1; + + imgData->frm_data_buf[0][0] = imgData->frm_data_buf[0][1] = imgData->frm_data_buf[0][2] = NULL; + imgData->frm_data_buf[1][0] = imgData->frm_data_buf[1][1] = imgData->frm_data_buf[1][2] = NULL; + if( (p_Inp->separate_colour_plane_flag != 0) ) + { + for( nplane=0; nplanefrm_data[nplane]), p_Vid->height, p_Vid->width); + } + } + else + { + //imgData->format = p_Inp->input_file1.format; + memory_size += get_mem2Dpel(&(imgData->frm_data[0]), p_Vid->height, p_Vid->width); + + if (p_Vid->yuv_format != YUV400) + { + int i, j, k; + memory_size += get_mem2Dpel(&(imgData->frm_data[1]), p_Vid->height_cr, p_Vid->width_cr); + memory_size += get_mem2Dpel(&(imgData->frm_data[2]), p_Vid->height_cr, p_Vid->width_cr); + + if (sizeof(imgpel) == sizeof(unsigned char)) + { + for (k = 1; k < 3; k++) + memset(&(imgData->frm_data[k][0][0]), 128, p_Vid->height_cr * p_Vid->width_cr * sizeof(imgpel)); + } + else + { + for (k = 1; k < 3; k++) + for (j = 0; j < p_Vid->height_cr; j++) + for (i = 0; i < p_Vid->width_cr; i++) + imgData->frm_data[k][j][i] = 128; + } + } + } + + if (!p_Vid->active_sps->frame_mbs_only_flag) + { + // allocate memory for field reference frame buffers + memory_size += init_top_bot_planes(imgData->frm_data[0], p_Vid->height, &(imgData->top_data[0]), &(imgData->bot_data[0])); + + if (p_Vid->yuv_format != YUV400) + { + memory_size += 4*(sizeof(imgpel**)); + + memory_size += init_top_bot_planes(imgData->frm_data[1], p_Vid->height_cr, &(imgData->top_data[1]), &(imgData->bot_data[1])); + memory_size += init_top_bot_planes(imgData->frm_data[2], p_Vid->height_cr, &(imgData->top_data[2]), &(imgData->bot_data[2])); + } + } + return memory_size; +} + +/*! +************************************************************************ +* \brief +* Dynamic memory allocation of frame size related global buffers +* buffers are defined in global.h, allocated memory must be freed in +* void free_global_buffers() +* \par Input: +* Input Parameters InputParameters *inp, \n +* Image Parameters VideoParameters *p_Vid +* \return Number of allocated bytes +************************************************************************ +*/ +static int init_global_buffers(VideoParameters *p_Vid, InputParameters *p_Inp) +{ + int j, memory_size=0; + + if ((p_Vid->enc_frame_picture = (StorablePicture**)malloc(6 * sizeof(StorablePicture*))) == NULL) + no_mem_exit("init_global_buffers: *p_Vid->enc_frame_picture"); + + for (j = 0; j < 6; j++) + p_Vid->enc_frame_picture[j] = NULL; + + if ((p_Vid->enc_field_picture = (StorablePicture**)malloc(2 * sizeof(StorablePicture*))) == NULL) + no_mem_exit("init_global_buffers: *p_Vid->enc_field_picture"); + + for (j = 0; j < 2; j++) + p_Vid->enc_field_picture[j] = NULL; + + if ((p_Vid->frame_pic = (Picture**)malloc(p_Vid->frm_iter * sizeof(Picture*))) == NULL) + no_mem_exit("init_global_buffers: *p_Vid->frame_pic"); + + for (j = 0; j < p_Vid->frm_iter; j++) + p_Vid->frame_pic[j] = malloc_picture(); + + if (p_Inp->si_frame_indicator || p_Inp->sp_periodicity) + { + p_Vid->number_sp2_frames=0; + p_Vid->frame_pic_si = malloc_picture();//picture buffer for the encoded SI picture + //allocation of lrec and p_Vid->lrec_uv for SI picture + get_mem2Dint (&p_Vid->lrec, p_Vid->height, p_Vid->width); + get_mem3Dint (&p_Vid->lrec_uv, 2, p_Vid->height, p_Vid->width); + } + +#if (MVC_EXTENSION_ENABLE) + p_Vid->field_pic_ptr = NULL; + p_Vid->field_pic1 = NULL; + p_Vid->field_pic2 = NULL; + + // Allocate memory for field picture coding + if (p_Inp->PicInterlace != FRAME_CODING) + { + if ((p_Vid->field_pic1 = (Picture**)malloc(2 * sizeof(Picture*))) == NULL) + no_mem_exit("init_global_buffers: *p_Vid->field_pic1"); + + for (j = 0; j < 2; j++) + p_Vid->field_pic1[j] = malloc_picture(); + + if(p_Vid->num_of_layers==2) + { + if ((p_Vid->field_pic2 = (Picture**)malloc(2 * sizeof(Picture*))) == NULL) + no_mem_exit("init_global_buffers: *p_Vid->field_pic2"); + + for (j = 0; j < 2; j++) + p_Vid->field_pic2[j] = malloc_picture(); + } + } +#else + // Allocate memory for field picture coding + if (p_Inp->PicInterlace != FRAME_CODING) + { + if ((p_Vid->field_pic = (Picture**)malloc(2 * sizeof(Picture*))) == NULL) + no_mem_exit("init_global_buffers: *p_Vid->field_pic"); + + for (j = 0; j < 2; j++) + p_Vid->field_pic[j] = malloc_picture(); + } +#endif + + // Init memory data for input & encoded images + memory_size += init_orig_buffers(p_Vid, &p_Vid->imgData); + + if ( p_Inp->MDReference[0] || p_Inp->MDReference[1] ) + { + memory_size += init_orig_buffers(p_Vid, &p_Vid->imgRefData); + } + + memory_size += init_orig_buffers(p_Vid, &p_Vid->imgData0); + + if (p_Inp->enable_32_pulldown) + { + memory_size += init_orig_buffers(p_Vid, &p_Vid->imgData32); + memory_size += init_orig_buffers(p_Vid, &p_Vid->imgData4); + memory_size += init_orig_buffers(p_Vid, &p_Vid->imgData5); + memory_size += init_orig_buffers(p_Vid, &p_Vid->imgData6); + } + + + //memory_size += get_mem2Dshort(&PicPos, p_Vid->FrameSizeInMbs + 1, 2); + p_Vid->PicPos = calloc(p_Vid->FrameSizeInMbs + 1, sizeof(BlockPos)); + + for (j = 0; j < (int) p_Vid->FrameSizeInMbs + 1; j++) + { + p_Vid->PicPos[j].x = (short) (j % p_Vid->PicWidthInMbs); + p_Vid->PicPos[j].y = (short) (j / p_Vid->PicWidthInMbs); + } + + + if (p_Inp->rdopt == 3) + { + memory_size += allocate_errdo_mem(p_Vid, p_Inp); + } + + if (p_Inp->RestrictRef) + { + memory_size += get_mem2D(&p_Vid->pixel_map, p_Vid->height, p_Vid->width); + memory_size += get_mem2D(&p_Vid->refresh_map, p_Vid->height >> 3, p_Vid->width >> 3); + } + + if (!p_Vid->active_sps->frame_mbs_only_flag) + { + for(j=0; jnum_of_layers; j++) + { + CodingParameters *cps = p_Vid->p_EncodePar[j]; + memory_size += get_mem2Dpel(&(p_Vid->imgY_com_buf[j]), cps->height, cps->width); + if (cps->yuv_format != YUV400) + { + memory_size += get_mem2Dpel(&(p_Vid->imgUV_com_buf[j][0]), cps->height_cr, cps->width_cr); + memory_size += get_mem2Dpel(&(p_Vid->imgUV_com_buf[j][1]), cps->height_cr, cps->width_cr); + } + } + //set; + p_Vid->imgY_com = p_Vid->imgY_com_buf[0]; + p_Vid->imgUV_com[0] = p_Vid->imgUV_com_buf[0][0]; + p_Vid->imgUV_com[1] = p_Vid->imgUV_com_buf[0][1]; + } + + // allocate and set memory relating to motion estimation + if (!p_Inp->IntraProfile) + { + if (p_Inp->SearchMode[0] == UM_HEX || p_Inp->SearchMode[1] == UM_HEX) + { + if ((p_Vid->p_UMHex = (UMHexStruct*)calloc(1, sizeof(UMHexStruct))) == NULL) + no_mem_exit("init_mv_block: p_Vid->p_UMHex"); + memory_size += UMHEX_get_mem(p_Vid, p_Inp); + } + if (p_Inp->SearchMode[0] == UM_HEX_SIMPLE || p_Inp->SearchMode[1] == UM_HEX_SIMPLE) + { + if ((p_Vid->p_UMHexSMP = (UMHexSMPStruct*)calloc(1, sizeof(UMHexSMPStruct))) == NULL) + no_mem_exit("init_mv_block: p_Vid->p_UMHexSMP"); + + smpUMHEX_init(p_Vid); + memory_size += smpUMHEX_get_mem(p_Vid); + } + if (p_Inp->SearchMode[0] == EPZS || p_Inp->SearchMode[1] == EPZS) + { + memory_size += EPZSInit(p_Vid); + } + } + + if (p_Inp->RCEnable) + rc_allocate_memory(p_Vid, p_Inp); + + if (p_Inp->redundant_pic_flag) + { + for(j=0; jnum_of_layers; j++) + { + CodingParameters *cps = p_Vid->p_EncodePar[j]; + memory_size += get_mem2Dpel(&p_Vid->imgY_tmp_buf[j], cps->height, cps->width); + memory_size += get_mem2Dpel(&p_Vid->imgUV_tmp_buf[j][0], cps->height_cr, cps->width_cr); + memory_size += get_mem2Dpel(&p_Vid->imgUV_tmp_buf[j][1], cps->height_cr, cps->width_cr); + } + p_Vid->imgY_tmp = p_Vid->imgY_tmp_buf[0]; + p_Vid->imgUV_tmp[0] = p_Vid->imgUV_tmp_buf[0][0]; + p_Vid->imgUV_tmp[1] = p_Vid->imgUV_tmp_buf[0][1]; + } + + if( !(p_Inp->OnTheFlyFractMCP) || (p_Inp->OnTheFlyFractMCP==OTF_L1) ) // JLT : on-the-fly compatibility + { + memory_size += get_mem2Dint_pad (&p_Vid->imgY_sub_tmp, p_Vid->height, p_Vid->width, IMG_PAD_SIZE_Y, IMG_PAD_SIZE_X); + } + + //if ( p_Inp->ChromaMCBuffer ) + chroma_mc_setup(p_Vid); + + p_Vid->padded_size_x = (p_Vid->width + 2 * IMG_PAD_SIZE_X); + p_Vid->padded_size_x_m8x8 = (p_Vid->padded_size_x - BLOCK_SIZE_8x8); + p_Vid->padded_size_x_m4x4 = (p_Vid->padded_size_x - BLOCK_SIZE); + p_Vid->cr_padded_size_x = (p_Vid->width_cr + 2 * p_Vid->pad_size_uv_x); + p_Vid->cr_padded_size_x2 = (p_Vid->cr_padded_size_x << 1); + p_Vid->cr_padded_size_x4 = (p_Vid->cr_padded_size_x << 2); + p_Vid->cr_padded_size_x_m8 = (p_Vid->cr_padded_size_x - 8); + + // RGB images for distortion calculation + // Recommended to do this allocation (and de-allocation) in + // the appropriate file instead of here. + if(p_Inp->DistortionYUVtoRGB) + { + memory_size += create_RGB_memory(p_Vid); + } + + p_Vid->pWPX = NULL; + if ( p_Inp->WPMCPrecision ) + { + wpxInitWPXObject(p_Vid); + } + + memory_size += init_process_image( p_Vid, p_Inp ); + + p_Vid->p_pred = init_seq_structure( p_Vid, p_Inp, &memory_size ); + + return memory_size; +} + +/*! +************************************************************************ +* \brief +* Free allocated memory of original picture buffers +************************************************************************ +*/ +void free_orig_planes(VideoParameters *p_Vid, ImageData *imgData) +{ + if( (p_Vid->p_Inp->separate_colour_plane_flag != 0) ) + { + int nplane; + for( nplane=0; nplanefrm_data[nplane]); + imgData->frm_data[nplane] = NULL; + } + } + else + { + free_mem2Dpel(imgData->frm_data[0]); // free ref frame buffers + imgData->frm_data[0] = NULL; + if (imgData->format.yuv_format != YUV400) + { + free_mem2Dpel(imgData->frm_data[1]); + imgData->frm_data[1] = NULL; + free_mem2Dpel(imgData->frm_data[2]); + imgData->frm_data[2] = NULL; + } + } + + if (!p_Vid->active_sps->frame_mbs_only_flag) + { + free_top_bot_planes(imgData->top_data[0], imgData->bot_data[0]); + if (imgData->format.yuv_format != YUV400) + { + free_top_bot_planes(imgData->top_data[1], imgData->bot_data[1]); + free_top_bot_planes(imgData->top_data[2], imgData->bot_data[2]); + } + imgData->top_data[0] = imgData->bot_data[0] = NULL; + imgData->top_data[1] = imgData->bot_data[1] = NULL; + imgData->top_data[2] = imgData->bot_data[2] = NULL; + } +} + + +/*! +************************************************************************ +* \brief +* Free allocated memory of frame size related global buffers +* buffers are defined in global.h, allocated memory is allocated in +* int get_mem4global_buffers() +* \par Input: +* Input Parameters InputParameters *inp, \n +* Image Parameters VideoParameters *p_Vid +* \par Output: +* none +************************************************************************ +*/ +static void free_global_buffers(VideoParameters *p_Vid, InputParameters *p_Inp) +{ + int i,j; + + free_pointer (p_Vid->enc_frame_picture); + + if (p_Vid->frame_pic) + { + for (j = 0; j < p_Vid->frm_iter; j++) + { + if (p_Vid->frame_pic[j]) + free_picture (p_Vid->frame_pic[j]); + } + free_pointer (p_Vid->frame_pic); + } + + free_pointer (p_Vid->enc_field_picture); + +#if (MVC_EXTENSION_ENABLE) + if (p_Vid->field_pic1) + { + for (j = 0; j < 2; j++) + { + if (p_Vid->field_pic1[j]) + free_picture (p_Vid->field_pic1[j]); + } + free_pointer (p_Vid->field_pic1); + + if(p_Vid->num_of_layers==2) + { + for (j = 0; j < 2; j++) + { + if (p_Vid->field_pic2[j]) + free_picture (p_Vid->field_pic2[j]); + } + free_pointer (p_Vid->field_pic2); + } + } +#else + if (p_Vid->field_pic) + { + for (j = 0; j < 2; j++) + { + if (p_Vid->field_pic[j]) + free_picture (p_Vid->field_pic[j]); + } + free_pointer (p_Vid->field_pic); + } +#endif + + // Deallocation of SI picture related memory + if (p_Inp->si_frame_indicator || p_Inp->sp_periodicity) + { + free_picture (p_Vid->frame_pic_si); + //deallocation of lrec and p_Vid->lrec_uv for SI frames + free_mem2Dint (p_Vid->lrec); + free_mem3Dint (p_Vid->lrec_uv); + } + + free_orig_planes(p_Vid, &p_Vid->imgData); + if ( p_Vid->p_Inp->MDReference[0] || p_Vid->p_Inp->MDReference[1] ) + { + free_orig_planes(p_Vid, &p_Vid->imgRefData); + } + + free_orig_planes(p_Vid, &p_Vid->imgData0); + + if (p_Inp->enable_32_pulldown) + { + free_orig_planes(p_Vid, &p_Vid->imgData32); + free_orig_planes(p_Vid, &p_Vid->imgData4); + free_orig_planes(p_Vid, &p_Vid->imgData5); + free_orig_planes(p_Vid, &p_Vid->imgData6); + } + + // free lookup memory which helps avoid divides with PicWidthInMbs + //free_mem2Dshort(PicPos); + free_pointer(p_Vid->PicPos); + // Free Qmatrices and offsets + free_QMatrix(p_Vid->p_Quant); + free_QOffsets(p_Vid->p_Quant, p_Inp); + + + if ( p_Inp->WPMCPrecision ) + { + wpxFreeWPXObject(p_Vid); + } + + if (p_Vid->imgY_sub_tmp) // free temp quarter pel frame buffers + { + free_mem2Dint_pad (p_Vid->imgY_sub_tmp, IMG_PAD_SIZE_Y, IMG_PAD_SIZE_X); + p_Vid->imgY_sub_tmp = NULL; + } + + // free mem, allocated in init_img() + // free intra pred mode buffer for blocks + free_mem2D((byte**)p_Vid->ipredmode); + free_mem2D((byte**)p_Vid->ipredmode8x8); + + if(p_Vid->ipredmode4x4_line) + { + free_mem2D((byte**)p_Vid->ipredmode4x4_line); + p_Vid->ipredmode4x4_line=NULL; + } + if(p_Vid->ipredmode8x8_line) + { + free_mem2D((byte **)p_Vid->ipredmode8x8_line); + p_Vid->ipredmode8x8_line = NULL; + } + + free_pointer( p_Vid->b8x8info ); + + if( (p_Inp->separate_colour_plane_flag != 0) ) + { + for( i=0; imb_data_JV[i], p_Vid->FrameSizeInMbs); + } + } + else + { + //free_pointer(p_Vid->mb_data); + free_mbs(p_Vid->mb_data, p_Vid->FrameSizeInMbs); + } + + if(p_Inp->UseConstrainedIntraPred) + { + free_pointer (p_Vid->intra_block); + } + + if (p_Inp->CtxAdptLagrangeMult == 1) + { + free_pointer(p_Vid->mb16x16_cost_frame); + } + + if (p_Inp->rdopt == 3) + { + free_errdo_mem(p_Vid); + } + + if (p_Inp->RestrictRef) + { + free_pointer(p_Vid->pixel_map[0]); + free_pointer(p_Vid->pixel_map); + free_pointer(p_Vid->refresh_map[0]); + free_pointer(p_Vid->refresh_map); + } + + if (!p_Vid->active_sps->frame_mbs_only_flag) + { + for(i=0; inum_of_layers; i++) + { + free_mem2Dpel(p_Vid->imgY_com_buf[i]); + free_mem2Dpel(p_Vid->imgUV_com_buf[i][0]); + free_mem2Dpel(p_Vid->imgUV_com_buf[i][1]); + } + p_Vid->imgY_com = NULL; + p_Vid->imgUV_com[0] = p_Vid->imgUV_com[1] = NULL; + } + + if(p_Vid->num_of_layers>1) + { + int bEqual = p_Vid->nz_coeff_buf[0] == p_Vid->nz_coeff_buf[1]; + free_mem3Dint(p_Vid->nz_coeff_buf[0]); + p_Vid->nz_coeff_buf[0] = NULL; + if(!bEqual && p_Vid->nz_coeff_buf[1]) + free_mem3Dint(p_Vid->nz_coeff_buf[1]); + p_Vid->nz_coeff_buf[1] = NULL; + } + else + free_mem3Dint(p_Vid->nz_coeff_buf[0]); + p_Vid->nz_coeff = NULL; + + free_mem2Dolm (p_Vid->lambda_buf[0], p_Vid->bitdepth_luma_qp_scale); + p_Vid->lambda = p_Vid->lambda_buf[0] = p_Vid->lambda_buf[1] = NULL; + free_mem2Dodouble (p_Vid->lambda_md_buf[0], p_Vid->bitdepth_luma_qp_scale); + p_Vid->lambda_md = p_Vid->lambda_md_buf[0] = p_Vid->lambda_md_buf[1] = NULL; + free_mem3Dodouble (p_Vid->lambda_me_buf[0], 10, 52 + p_Vid->bitdepth_luma_qp_scale, p_Vid->bitdepth_luma_qp_scale); + p_Vid->lambda_me = p_Vid->lambda_me_buf[0] = p_Vid->lambda_me_buf[1] = NULL; + free_mem3Doint (p_Vid->lambda_mf_buf[0], 10, 52 + p_Vid->bitdepth_luma_qp_scale, p_Vid->bitdepth_luma_qp_scale); + p_Vid->lambda_mf = p_Vid->lambda_mf_buf[0] = p_Vid->lambda_mf_buf[1] = NULL; + if ( p_Inp->UseRDOQuant ) + { + free_mem2Dodouble (p_Vid->lambda_rdoq_buf[0], p_Vid->bitdepth_luma_qp_scale); + p_Vid->lambda_rdoq = p_Vid->lambda_rdoq_buf[0] = p_Vid->lambda_rdoq_buf[1] = NULL; + } + if (p_Inp->CtxAdptLagrangeMult == 1) + { + free_mem2Dodouble(p_Vid->lambda_mf_factor_buf[0], p_Vid->bitdepth_luma_qp_scale); + p_Vid->lambda_mf_factor = p_Vid->lambda_mf_factor_buf[0] = p_Vid->lambda_mf_factor_buf[1] = NULL; + } + + if (!p_Inp->IntraProfile) + { + if (p_Inp->SearchMode[0] == UM_HEX || p_Inp->SearchMode[1] == UM_HEX) + { + UMHEX_free_mem(p_Vid, p_Inp); + } + if (p_Inp->SearchMode[0] == UM_HEX_SIMPLE || p_Inp->SearchMode[1] == UM_HEX_SIMPLE) + { + smpUMHEX_free_mem(p_Vid); + } + if (p_Inp->SearchMode[0] == EPZS || p_Inp->SearchMode[1] == EPZS) + { + EPZSDelete(p_Vid); + } + } + + if (p_Inp->RCEnable) + rc_free_memory(p_Vid, p_Inp); + + if (p_Inp->redundant_pic_flag) + { + for(i=0; inum_of_layers; i++) + { + free_mem2Dpel(p_Vid->imgY_tmp_buf[i]); + free_mem2Dpel(p_Vid->imgUV_tmp_buf[i][0]); + free_mem2Dpel(p_Vid->imgUV_tmp_buf[i][1]); + } + p_Vid->imgY_tmp = NULL; + p_Vid->imgUV_tmp[0] = NULL; + p_Vid->imgUV_tmp[1] = NULL; + } + + // Again process should be moved into cconv_yuv2rgb.c file for cleanliness + // These should not be globals but instead only be visible through that code. + if(p_Inp->DistortionYUVtoRGB) + { + delete_RGB_memory(p_Vid); + } + + clear_process_image( p_Vid, p_Inp ); + free_seq_structure( p_Vid->p_pred ); +} + + +/*! +************************************************************************ +* \brief +* Allocate memory for AC coefficients +************************************************************************ +*/ +int get_mem_ACcoeff (VideoParameters *p_Vid, int***** cofAC) +{ + int num_blk8x8 = BLOCK_SIZE + p_Vid->num_blk8x8_uv; + + get_mem4Dint(cofAC, num_blk8x8, BLOCK_SIZE, 2, 65); + + return num_blk8x8 * BLOCK_SIZE * 2 * 65 * sizeof(int);// 18->65 for ABT +} + +/*! +************************************************************************ +* \brief +* Allocate memory for AC coefficients +************************************************************************ +*/ +int get_mem_ACcoeff_new (int****** cofAC, int chroma) +{ + get_mem5Dint(cofAC, BLOCK_SIZE, chroma, BLOCK_SIZE, 2, 65); + return chroma * BLOCK_PIXELS * 2 * 65 * sizeof(int);// 18->65 for ABT +} + +/*! +************************************************************************ +* \brief +* Allocate memory for DC coefficients +************************************************************************ +*/ +int get_mem_DCcoeff (int**** cofDC) +{ + get_mem3Dint(cofDC, 3, 2, 18); + return 3 * 2 * 18 * sizeof(int); +} + + +/*! +************************************************************************ +* \brief +* Free memory of AC coefficients +************************************************************************ +*/ +void free_mem_ACcoeff (int**** cofAC) +{ + free_mem4Dint(cofAC); +} + +/*! +************************************************************************ +* \brief +* Free memory of AC coefficients +************************************************************************ +*/ +void free_mem_ACcoeff_new (int***** cofAC) +{ + free_mem5Dint(cofAC); +} + +/*! +************************************************************************ +* \brief +* Free memory of DC coefficients +************************************************************************ +*/ +void free_mem_DCcoeff (int*** cofDC) +{ + free_mem3Dint(cofDC); +} + +/*! +************************************************************************ +* \brief +* Sets indices to appropriate level constraints, depending on +* current level_idc +************************************************************************ +*/ +static void set_level_indices(VideoParameters *p_Vid) +{ + switch(p_Vid->active_sps->level_idc) + { + case 9: + p_Vid->LevelIndex=1; + break; + case 10: + p_Vid->LevelIndex=0; + break; + case 11: + if (!is_FREXT_profile(p_Vid->active_sps->profile_idc) && (p_Vid->active_sps->constrained_set3_flag == 0)) + p_Vid->LevelIndex=2; + else + p_Vid->LevelIndex=1; + break; + case 12: + p_Vid->LevelIndex=3; + break; + case 13: + p_Vid->LevelIndex=4; + break; + case 20: + p_Vid->LevelIndex=5; + break; + case 21: + p_Vid->LevelIndex=6; + break; + case 22: + p_Vid->LevelIndex=7; + break; + case 30: + p_Vid->LevelIndex=8; + break; + case 31: + p_Vid->LevelIndex=9; + break; + case 32: + p_Vid->LevelIndex=10; + break; + case 40: + p_Vid->LevelIndex=11; + break; + case 41: + p_Vid->LevelIndex=12; + break; + case 42: + if (!is_FREXT_profile(p_Vid->active_sps->profile_idc)) + p_Vid->LevelIndex=13; + else + p_Vid->LevelIndex=14; + break; + case 50: + p_Vid->LevelIndex=15; + break; + case 51: + p_Vid->LevelIndex=16; + break; + default: + fprintf ( stdout, "Warning: unknown LevelIDC, using maximum level 5.1 \n" ); + p_Vid->LevelIndex=16; + break; + } +} + +/*! +************************************************************************ +* \brief +* initialize key frames and corresponding redundant frames. +************************************************************************ +*/ +void init_redundant_frame(VideoParameters *p_Vid, InputParameters *p_Inp) +{ + if (p_Inp->redundant_pic_flag) + { + if (p_Inp->NumberBFrames) + { + error("B frame not supported when redundant picture used!", 100); + } + + if (p_Inp->PicInterlace) + { + error("Interlace not supported when redundant picture used!", 100); + } + + if (p_Inp->num_ref_frames < p_Inp->PrimaryGOPLength) + { + error("NumberReferenceFrames must be no less than PrimaryGOPLength", 100); + } + + if ((1<NumRedundantHierarchy) > p_Inp->PrimaryGOPLength) + { + error("PrimaryGOPLength must be greater than 2^NumRedundantHeirarchy", 100); + } + + if (p_Inp->Verbose != 1) + { + error("Redundant slices not supported when Verbose != 1", 100); + } + } + + p_Vid->key_frame = 0; + p_Vid->redundant_coding = 0; + p_Vid->redundant_pic_cnt = 0; + p_Vid->frameNuminGOP = p_Vid->curr_frm_idx % p_Inp->PrimaryGOPLength; + if (p_Vid->curr_frm_idx == 0) + { + p_Vid->frameNuminGOP = -1; + } +} + +/*! +************************************************************************ +* \brief +* allocate redundant frames in a primary GOP. +************************************************************************ +*/ +void set_redundant_frame(VideoParameters *p_Vid, InputParameters *p_Inp) +{ + int GOPlength = p_Inp->PrimaryGOPLength; + + //start frame of GOP + if (p_Vid->frameNuminGOP == 0) + { + p_Vid->redundant_coding = 0; + p_Vid->key_frame = 1; + p_Vid->redundant_ref_idx = GOPlength; + } + + //1/2 position + if (p_Inp->NumRedundantHierarchy > 0) + { + if (p_Vid->frameNuminGOP == GOPlength >> 1) + { + p_Vid->redundant_coding = 0; + p_Vid->key_frame = 1; + p_Vid->redundant_ref_idx = GOPlength >> 1; + } + } + + //1/4, 3/4 position + if (p_Inp->NumRedundantHierarchy > 1) + { + if (p_Vid->frameNuminGOP == (GOPlength >> 2) || p_Vid->frameNuminGOP == ((GOPlength*3) >> 2)) + { + p_Vid->redundant_coding = 0; + p_Vid->key_frame = 1; + p_Vid->redundant_ref_idx = GOPlength >> 2; + } + } + + //1/8, 3/8, 5/8, 7/8 position + if (p_Inp->NumRedundantHierarchy > 2) + { + if (p_Vid->frameNuminGOP == GOPlength >> 3 || p_Vid->frameNuminGOP == ((GOPlength*3) >> 3) + || p_Vid->frameNuminGOP == ((GOPlength*5) >> 3) || p_Vid->frameNuminGOP == ((GOPlength*7) & 0x03)) + { + p_Vid->redundant_coding = 0; + p_Vid->key_frame = 1; + p_Vid->redundant_ref_idx = GOPlength >> 3; + } + } + + //1/16, 3/16, 5/16, 7/16, 9/16, 11/16, 13/16 position + if (p_Inp->NumRedundantHierarchy > 3) + { + if (p_Vid->frameNuminGOP == (GOPlength >> 4) || p_Vid->frameNuminGOP == ((GOPlength*3) >> 4) + || p_Vid->frameNuminGOP == ((GOPlength*5) >> 4) || p_Vid->frameNuminGOP == ((GOPlength*7) >> 4) + || p_Vid->frameNuminGOP == ((GOPlength*9) >> 4) || p_Vid->frameNuminGOP == ((GOPlength*11) >> 4) + || p_Vid->frameNuminGOP == ((GOPlength*13) >> 4)) + { + p_Vid->redundant_coding = 0; + p_Vid->key_frame = 1; + p_Vid->redundant_ref_idx = GOPlength >> 4; + } + } +} + +/*! +************************************************************************ +* \brief +* encode one redundant frame. +************************************************************************ +*/ +void encode_one_redundant_frame(VideoParameters *p_Vid, InputParameters *p_Inp) +{ + p_Vid->key_frame = 0; + p_Vid->redundant_coding = 1; + p_Vid->redundant_pic_cnt = 1; + + if (!p_Vid->currentPicture->idr_flag) + { + if (p_Vid->type == I_SLICE) + { + set_slice_type( p_Vid, p_Inp, P_SLICE ); + } + } + + encode_one_frame(p_Vid, p_Inp); +} + +/*! +************************************************************************ +* \brief +* Setup Chroma MC Variables +************************************************************************ +*/ +static void chroma_mc_setup(VideoParameters *p_Vid) +{ + // initialize global variables used for chroma interpolation and buffering + if ( p_Vid->yuv_format == YUV420 ) + { + p_Vid->pad_size_uv_x = IMG_PAD_SIZE_X >> 1; + p_Vid->pad_size_uv_y = IMG_PAD_SIZE_Y >> 1; + p_Vid->chroma_mask_mv_y = 7; + p_Vid->chroma_mask_mv_x = 7; + p_Vid->chroma_shift_x = 3; + p_Vid->chroma_shift_y = 3; + } + else if ( p_Vid->yuv_format == YUV422 ) + { + p_Vid->pad_size_uv_x = IMG_PAD_SIZE_X >> 1; + p_Vid->pad_size_uv_y = IMG_PAD_SIZE_Y; + p_Vid->chroma_mask_mv_y = 3; + p_Vid->chroma_mask_mv_x = 7; + p_Vid->chroma_shift_y = 2; + p_Vid->chroma_shift_x = 3; + } + else + { // YUV444 + p_Vid->pad_size_uv_x = IMG_PAD_SIZE_X; + p_Vid->pad_size_uv_y = IMG_PAD_SIZE_Y; + p_Vid->chroma_mask_mv_y = 3; + p_Vid->chroma_mask_mv_x = 3; + p_Vid->chroma_shift_y = 2; + p_Vid->chroma_shift_x = 2; + } + p_Vid->shift_cr_y = p_Vid->chroma_shift_y - 2; + p_Vid->shift_cr_x = p_Vid->chroma_shift_x - 2; +} + + +static void gen_enc_par(VideoParameters *p_Vid, int layer_idx) +{ + CodingParameters *cps; + DecodedPictureBuffer *p_Dpb; + InputParameters *p_Inp = p_Vid->p_Inp; + + cps = p_Vid->p_EncodePar[layer_idx]; + p_Dpb = p_Vid->p_Dpb_layer[layer_idx]; + + cps->layer_id = layer_idx; + cps->yuv_format = p_Dpb->storage_format.yuv_format; + cps->P444_joined = (cps->yuv_format == YUV444 && (p_Inp->separate_colour_plane_flag == 0)); + cps->bitdepth_luma = (short)p_Dpb->storage_format.bit_depth[0]; + cps->bitdepth_scale[0] = 1 << (cps->bitdepth_luma - 8); + cps->bitdepth_lambda_scale = 2 * (cps->bitdepth_luma - 8); + cps->bitdepth_luma_qp_scale = 3 * cps->bitdepth_lambda_scale; + cps->dc_pred_value_comp[0] = (uint32) (1<<(cps->bitdepth_luma - 1)); + cps->max_pel_value_comp[0] = (1<bitdepth_luma) - 1; + cps->max_imgpel_value_comp_sq[0] = cps->max_pel_value_comp[0] * cps->max_pel_value_comp[0]; + cps->mb_size[0][0] = cps->mb_size[0][1] = MB_BLOCK_SIZE; + if (cps->yuv_format != YUV400) + { + cps->bitdepth_chroma = (short)p_Dpb->storage_format.bit_depth[1]; + cps->bitdepth_scale[1] = 1 << (cps->bitdepth_chroma - 8); + cps->dc_pred_value_comp[1] = (uint32) (1<<(cps->bitdepth_chroma - 1)); + cps->dc_pred_value_comp[2] = cps->dc_pred_value_comp[1]; + cps->max_pel_value_comp[1] = (1<bitdepth_chroma) - 1; + cps->max_pel_value_comp[2] = cps->max_pel_value_comp[1]; + cps->max_imgpel_value_comp_sq[1] = cps->max_pel_value_comp[1] * cps->max_pel_value_comp[1]; + cps->max_imgpel_value_comp_sq[2] = cps->max_pel_value_comp[2] * cps->max_pel_value_comp[2]; + cps->num_blk8x8_uv = (1<yuv_format)&(~(0x1)); + cps->num_cdc_coeff = cps->num_blk8x8_uv << 1; + cps->mb_size[1][0] = cps->mb_size[2][0] = cps->mb_cr_size_x = (cps->yuv_format == YUV420 || cps->yuv_format == YUV422) ? 8 : 16; + cps->mb_size[1][1] = cps->mb_size[2][1] = cps->mb_cr_size_y = (cps->yuv_format == YUV444 || cps->yuv_format == YUV422) ? 16 : 8; + cps->bitdepth_chroma_qp_scale = 6*(cps->bitdepth_chroma - 8); + } + else + { + cps->bitdepth_chroma = 0; + cps->bitdepth_scale[1] = 0; + cps->max_pel_value_comp[1] = 0; + cps->max_pel_value_comp[2] = cps->max_pel_value_comp[1]; + cps->max_imgpel_value_comp_sq[1] = cps->max_pel_value_comp[1] * cps->max_pel_value_comp[1]; + cps->max_imgpel_value_comp_sq[2] = cps->max_pel_value_comp[2] * cps->max_pel_value_comp[2]; + cps->num_blk8x8_uv = 0; + cps->num_cdc_coeff = 0; + cps->mb_size[1][0] = cps->mb_size[2][0] = cps->mb_cr_size_x = 0; + cps->mb_size[1][1] = cps->mb_size[2][1] = cps->mb_cr_size_y = 0; + + cps->bitdepth_chroma_qp_scale = 0; + } + cps->max_bitCount = 128 + 256 * cps->bitdepth_luma + 2 * cps->mb_cr_size_y * cps->mb_cr_size_x * cps->bitdepth_chroma; + cps->width = (p_Inp->output.width[0] + p_Vid->auto_crop_right); + cps->height = (p_Inp->output.height[0] + p_Vid->auto_crop_bottom); + cps->width_blk = cps->width / BLOCK_SIZE; + cps->height_blk = cps->height / BLOCK_SIZE; + cps->width_padded = cps->width + 2 * IMG_PAD_SIZE_X; + cps->height_padded = cps->height + 2 * IMG_PAD_SIZE_Y; + + if (cps->yuv_format != YUV400) + { + cps->width_cr = cps->width * mb_width_cr [cps->yuv_format] / 16; + cps->height_cr= cps->height * mb_height_cr[cps->yuv_format] / 16; + } + else + { + cps->width_cr = 0; + cps->height_cr= 0; + } + + cps->height_cr_frame = cps->height_cr; + cps->size = cps->width * cps->height; + cps->size_cr = cps->width_cr * cps->height_cr; + + cps->PicWidthInMbs = cps->width / MB_BLOCK_SIZE; + cps->FrameHeightInMbs = cps->height / MB_BLOCK_SIZE; + cps->FrameSizeInMbs = cps->PicWidthInMbs * cps->FrameHeightInMbs; + cps->PicHeightInMapUnits = ( p_Vid->sps[layer_idx]->frame_mbs_only_flag ? cps->FrameHeightInMbs : cps->FrameHeightInMbs >> 1 ); + + if ( cps->yuv_format == YUV420 ) + { + cps->pad_size_uv_x = IMG_PAD_SIZE_X >> 1; + cps->pad_size_uv_y = IMG_PAD_SIZE_Y >> 1; + cps->chroma_mask_mv_y = 7; + cps->chroma_mask_mv_x = 7; + cps->chroma_shift_x = 3; + cps->chroma_shift_y = 3; + } + else if ( cps->yuv_format == YUV422 ) + { + cps->pad_size_uv_x = IMG_PAD_SIZE_X >> 1; + cps->pad_size_uv_y = IMG_PAD_SIZE_Y; + cps->chroma_mask_mv_y = 3; + cps->chroma_mask_mv_x = 7; + cps->chroma_shift_y = 2; + cps->chroma_shift_x = 3; + } + else + { // YUV444 + cps->pad_size_uv_x = IMG_PAD_SIZE_X; + cps->pad_size_uv_y = IMG_PAD_SIZE_Y; + cps->chroma_mask_mv_y = 3; + cps->chroma_mask_mv_x = 3; + cps->chroma_shift_y = 2; + cps->chroma_shift_x = 2; + } + cps->shift_cr_y = cps->chroma_shift_y - 2; + cps->shift_cr_x = cps->chroma_shift_x - 2; + + cps->padded_size_x = (cps->width + 2 * IMG_PAD_SIZE_X); + cps->padded_size_x_m8x8 = (cps->padded_size_x - BLOCK_SIZE_8x8); + cps->padded_size_x_m4x4 = (cps->padded_size_x - BLOCK_SIZE); + cps->cr_padded_size_x = (cps->width_cr + 2 * cps->pad_size_uv_x); + cps->cr_padded_size_x2 = (cps->cr_padded_size_x << 1); + cps->cr_padded_size_x4 = (cps->cr_padded_size_x << 2); + cps->cr_padded_size_x_m8 = (cps->cr_padded_size_x - 8); + +} + +static void generate_encode_parameters(VideoParameters *p_Vid) +{ + int i; + p_Vid->p_EncodePar[0] = (CodingParameters *)calloc(p_Vid->num_of_layers, sizeof(CodingParameters)); + //init; + for(i=0; inum_of_layers; i++) + { + p_Vid->p_EncodePar[i] = p_Vid->p_EncodePar[0]+i; + gen_enc_par(p_Vid, i); + } + p_Vid->p_CurrEncodePar = p_Vid->p_EncodePar[0]; +} + +static void free_encode_parameters(VideoParameters *p_Vid) +{ + int i; + if(p_Vid->p_EncodePar[0]) + { + free_pointer(p_Vid->p_EncodePar[0]); + } + for(i=0; inum_of_layers; i++) + { + p_Vid->p_EncodePar[i] = NULL; + } + p_Vid->p_CurrEncodePar = NULL; +} + +int init_process_image( VideoParameters *p_Vid, InputParameters *p_Inp) +{ + int memory_size = 0; + switch( p_Inp->ProcessInput ) + { + default: + break; + case 0: + if( (p_Vid->num_of_layers == 2) && is_MVC_profile(p_Inp->ProfileIDC) ) + memory_size += init_orig_buffers(p_Vid, &p_Vid->tempData3); + break; + } + return memory_size; +} + +void clear_process_image( VideoParameters *p_Vid, InputParameters *p_Inp) +{ + switch( p_Inp->ProcessInput ) + { + default: + break; + case 0: + if( (p_Vid->num_of_layers == 2) && is_MVC_profile(p_Inp->ProfileIDC) ) + free_orig_planes(p_Vid, &p_Vid->tempData3); + break; + } +} diff --git a/lencod/src/list_reorder.c b/lencod/src/list_reorder.c new file mode 100644 index 0000000..707bdb0 --- /dev/null +++ b/lencod/src/list_reorder.c @@ -0,0 +1,1411 @@ +/*! + ************************************************************************************** + * \file + * slice.c + * \brief + * generate the slice header, setup the bit buffer for slices, + * and generates the slice NALU(s) + + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * - Athanasios Leontaris + * - Karsten Suehring + * - Alexis Michael Tourapis + *************************************************************************************** + */ + +#include "contributors.h" + +#include +#include + +#include "global.h" +#include "image.h" +#include "wp.h" +#include "list_reorder.h" + +static void poc_ref_pic_reorder_dummy(Slice *currSlice, unsigned num_ref_idx_lX_active, int list_no); +static void reorder_lists_dummy( Slice *currSlice ); + +/*! + ************************************************************************ + * \brief + * init_ref_pic_list_reordering initializations should go here + ************************************************************************ + */ +void init_ref_pic_list_reordering(Slice* currSlice, int refReorderMethod) +{ + currSlice->ref_pic_list_reordering_flag[LIST_0] = 0; + currSlice->ref_pic_list_reordering_flag[LIST_1] = 0; + + if (refReorderMethod == 2) + { + currSlice->poc_ref_pic_reorder_frame = tlyr_ref_pic_reorder_frame_default; + currSlice->reorder_lists = reorder_lists; + } +#if (MVC_EXTENSION_ENABLE) + else if ((refReorderMethod == 1) && (currSlice->layer_id > 0) && (currSlice->p_Inp->MVCInterViewReorder > 0 )) + { + currSlice->poc_ref_pic_reorder_frame = poc_ref_pic_reorder_frame_enh; + currSlice->reorder_lists = reorder_lists; + } + else if ((refReorderMethod == 0) && (currSlice->layer_id > 0) && (currSlice->p_Inp->MVCInterViewReorder > 0 )) + { + currSlice->poc_ref_pic_reorder_frame = poc_ref_pic_reorder_frame_enh; + currSlice->reorder_lists = reorder_lists; + } +#endif + else if ((refReorderMethod == 0) || (currSlice->slice_type == I_SLICE) || (currSlice->slice_type == SI_SLICE)) + { + currSlice->poc_ref_pic_reorder_frame = poc_ref_pic_reorder_dummy; + currSlice->reorder_lists = reorder_lists_dummy; + } + else + { + currSlice->poc_ref_pic_reorder_frame = poc_ref_pic_reorder_frame_enh; + currSlice->reorder_lists = reorder_lists; + } +} + +/*! +************************************************************************ +* \brief +* decide reference picture reordering, Frame only +************************************************************************ +*/ +void poc_ref_pic_reorder_frame_default(Slice *currSlice, unsigned num_ref_idx_lX_active, int list_no) +{ + StorablePicture **list = currSlice->listX[list_no]; + int *modification_of_pic_nums_idc = currSlice->modification_of_pic_nums_idc[list_no]; + int *abs_diff_pic_num_minus1 = currSlice->abs_diff_pic_num_minus1[list_no]; + VideoParameters *p_Vid = currSlice->p_Vid; + InputParameters *p_Inp = currSlice->p_Inp; + StorablePicture *p_Enc_Pic = p_Vid->enc_picture; + DecodedPictureBuffer *p_Dpb = currSlice->p_Dpb; + + int i,j,k; + + int currPicNum, picNumLXPred; + + int default_order[32]; + int re_order[32]; + int tmp_reorder[32]; + int list_sign[32]; + int reorder_stop, no_reorder; + int poc_diff[32]; + int diff; + + int abs_poc_dist; + int maxPicNum; + unsigned int num_refs; + + maxPicNum = p_Vid->max_frame_num; + currPicNum = p_Vid->frame_num; + + picNumLXPred = currPicNum; + + // First assign default list order. + for (i=0; i<(int)num_ref_idx_lX_active; i++) + { + default_order[i] = list[i]->pic_num; + } + + // Now access all references in buffer and assign them + // to a potential reordering list. For each one of these + // references compute the poc distance compared to current + // frame. + num_refs = currSlice->p_Dpb->ref_frames_in_buffer; + for (i = 0; i < (int)p_Dpb->ref_frames_in_buffer; i++) + { + poc_diff[i] = 0xFFFF; + re_order[i] = p_Dpb->fs_ref[i]->frame->pic_num; + + if (p_Dpb->fs_ref[i]->is_used==3 && (p_Dpb->fs_ref[i]->frame->used_for_reference)&&(!p_Dpb->fs_ref[i]->frame->is_long_term)) + { + abs_poc_dist = iabs(p_Dpb->fs_ref[i]->frame->poc - p_Enc_Pic->poc) ; + poc_diff[i] = abs_poc_dist; + if (list_no == LIST_0) + { + list_sign[i] = (p_Enc_Pic->poc < p_Dpb->fs_ref[i]->frame->poc) ? +1 : -1; + } + else + { + list_sign[i] = (p_Enc_Pic->poc > p_Dpb->fs_ref[i]->frame->poc) ? +1 : -1; + } + } + } + + // now sort these references based on poc (temporal) distance + for (i = 0; i < ((int)num_refs - 1); i++) + { + for (j = i + 1; j < (int)num_refs; j++) + { + if (poc_diff[i]>poc_diff[j] || (poc_diff[i] == poc_diff[j] && list_sign[j] > list_sign[i])) + { + i32_swap(poc_diff[i], poc_diff[j]); + i32_swap(re_order[i], re_order[j]); + i32_swap(list_sign[i], list_sign[j]); + } + } + } + + // populate list with selections from the pre-analysis stage + if ( p_Inp->WPMCPrecision + && p_Vid->pWPX->curr_wp_rd_pass->algorithm != WP_REGULAR + && p_Vid->pWPX->num_wp_ref_list[list_no] ) + { + for (i=0; i<(int)num_ref_idx_lX_active; i++) + { + re_order[i] = p_Vid->pWPX->wp_ref_list[list_no][i].PicNum; + } + } + + if (p_Inp->EnableOpenGOP) + { + int counter = 0; + for (i = 0; i < currSlice->listXsize[list_no]; i++) + { + if (currSlice->listX[list_no][i]->poc < p_Vid->last_valid_reference && p_Vid->ThisPOC > p_Vid->last_valid_reference) + { + counter++; + } + } + + currSlice->listXsize[list_no] -= counter; + num_ref_idx_lX_active = currSlice->listXsize[list_no]; + } + + // Check versus default list to see if any + // change has happened + no_reorder = 1; + for (i=0; i<(int)num_ref_idx_lX_active; i++) + { + if (default_order[i] != re_order[i]) + { + no_reorder = 0; + } + } + + // If different, then signal reordering + if (no_reorder==0) + { + for (i=0; i<(int)num_ref_idx_lX_active; i++) + { + diff = re_order[i]-picNumLXPred; + if (diff <= 0) + { + modification_of_pic_nums_idc[i] = 0; + abs_diff_pic_num_minus1[i] = iabs(diff)-1; + if (abs_diff_pic_num_minus1[i] < 0) + abs_diff_pic_num_minus1[i] = maxPicNum -1; + } + else + { + modification_of_pic_nums_idc[i] = 1; + abs_diff_pic_num_minus1[i] = iabs(diff)-1; + } + picNumLXPred = re_order[i]; + + tmp_reorder[i] = re_order[i]; + + k = i; + for (j=i; j<(int)num_ref_idx_lX_active; j++) + { + if (default_order[j] != re_order[i]) + { + ++k; + tmp_reorder[k] = default_order[j]; + } + } + reorder_stop = 1; + for(j=i+1; j<(int)num_ref_idx_lX_active; j++) + { + if (tmp_reorder[j] != re_order[j]) + { + reorder_stop = 0; + break; + } + } + + if (reorder_stop==1) + { + ++i; + break; + } + memcpy ( default_order, tmp_reorder, num_ref_idx_lX_active * sizeof(int)); + + } + modification_of_pic_nums_idc[i] = 3; + + memcpy ( default_order, tmp_reorder, num_ref_idx_lX_active * sizeof(int)); + + if (list_no==0) + { + currSlice->ref_pic_list_reordering_flag[LIST_0] = 1; + } + else + { + currSlice->ref_pic_list_reordering_flag[LIST_1] = 1; + } + } +} + +/*! +************************************************************************ +* \brief +* Dummy reference reordering decision function +************************************************************************ +*/ +static void poc_ref_pic_reorder_dummy(Slice *currSlice, unsigned num_ref_idx_lX_active, int list_no) +{ +} + +/*! +************************************************************************ +* \brief +* decide reference picture reordering, Frame only, for enh layer. +************************************************************************ +*/ +void poc_ref_pic_reorder_frame_enh(Slice *currSlice, unsigned num_ref_idx_lX_active, int list_no) +{ + StorablePicture **list = currSlice->listX[list_no]; + VideoParameters *p_Vid = currSlice->p_Vid; + InputParameters *p_Inp = currSlice->p_Inp; + StorablePicture *p_Enc_Pic = p_Vid->enc_picture; + DecodedPictureBuffer *p_Dpb = currSlice->p_Dpb; + + int i, j, k; + int found = 0; + + StorablePicture *re_order[32]; + StorablePicture *inter_layer_ref[32]; + StorablePicture *tmp_pic_value; + + int list_sign[32]; + int poc_diff[32]; + int inter_layer_ref_idx[32]; + + int abs_poc_dist; + int num_refs; + int pic_num; + + // First find inter view ref/s + // This code is idiotic since this can be found immediately given that reference is in p_Dpb->fs_ilref + + if (currSlice->layer_id > 0) + { + for (i = 0; i < (int) num_ref_idx_lX_active; i++) + { + if (list[i]->view_id < currSlice->layer_id) + { + inter_layer_ref[found] = list[i]; + inter_layer_ref_idx[found] = i; + found++; + } + } + } + + // Now access all references in buffer and assign them + // to a potential reordering list. For each one of these + // references compute the poc distance compared to current + // frame. + num_refs = p_Dpb->ref_frames_in_buffer; + for (i = 0; i < num_refs; i++) + { + poc_diff[i] = 0xFFFF; + re_order[i] = p_Dpb->fs_ref[i]->frame; + + // following code seems to exclude long term references + if (p_Dpb->fs_ref[i]->is_used==3 && (p_Dpb->fs_ref[i]->frame->used_for_reference) && (!p_Dpb->fs_ref[i]->frame->is_long_term)) + { + abs_poc_dist = iabs(p_Dpb->fs_ref[i]->frame->poc - p_Enc_Pic->poc) ; + poc_diff[i] = abs_poc_dist; + if (list_no == LIST_0) + { + list_sign[i] = (p_Enc_Pic->poc < p_Dpb->fs_ref[i]->frame->poc) ? +1 : -1; + } + else + { + list_sign[i] = (p_Enc_Pic->poc > p_Dpb->fs_ref[i]->frame->poc) ? +1 : -1; + } + } + } + + // now sort these references based on poc (temporal) distance + for (i = 0; i < (num_refs - 1); i++) + { + for (j = i + 1; j < num_refs; j++) + { + if (poc_diff[i] > poc_diff[j] || (poc_diff[i] == poc_diff[j] && list_sign[j] > list_sign[i])) + { + i32_swap(poc_diff[i], poc_diff[j]); + tmp_pic_value = re_order[i]; + re_order[i] = re_order[j]; + re_order[j] = tmp_pic_value ; + i32_swap(list_sign[i], list_sign[j]); + } + } + } + + // populate list with selections from the pre-analysis stage + if ( p_Inp->WPMCPrecision + && p_Vid->pWPX->curr_wp_rd_pass->algorithm != WP_REGULAR + && p_Vid->pWPX->num_wp_ref_list[list_no] ) + { + for (i = 0; i < (int) num_ref_idx_lX_active; i++) + { + pic_num = p_Vid->pWPX->wp_ref_list[list_no][i].PicNum; + for (j = 0; j < (int) p_Dpb->ref_frames_in_buffer; j++) + { + if (pic_num == p_Dpb->fs_ref[i]->frame->pic_num) + { + re_order[i] = p_Dpb->fs_ref[i]->frame; + break; + } + } + } + } + + if (currSlice->layer_id > 0) + { + // This code currently only supports a single inter-layer reference. + // We need to have a flag in the code that points to how many such references + // exist and appropriately scan fl_ilref. + j = 0; + k = 0; + for (i = 0; i < (int) num_ref_idx_lX_active+found; i++) + { + if ((found > 0) && (i == inter_layer_ref_idx[k])) + { + list[i] = inter_layer_ref[k++]; + //printf("values %d %d", (int) list[i], (int) p_Dpb->fs_ilref[0]->frame); + found--; + } + else + { + list[i] = re_order[j++]; + } + } + } + else + { + for (i = 0; i < (int) num_ref_idx_lX_active; i++) + { + list[i] = re_order[i]; + } + } +} + +/*! +************************************************************************ +* \brief +* decide reference picture reordering, Field. +************************************************************************ +*/ +void poc_ref_pic_reorder_field_enh(Slice *currSlice, unsigned num_ref_idx_lX_active, int list_no) +{ + StorablePicture **list = currSlice->listX[list_no]; + VideoParameters *p_Vid = currSlice->p_Vid; + //InputParameters *p_Inp = currSlice->p_Inp; + StorablePicture *p_Enc_Pic = p_Vid->enc_picture; + DecodedPictureBuffer *p_Dpb = currSlice->p_Dpb; + + int i,j,k; + int found = 0; + + StorablePicture *re_order[32]; + StorablePicture *tmp_reorder[32]; + StorablePicture *inter_layer_ref[32]; + StorablePicture *tmp_pic_value; + StorablePicture *pField[2]; // 0: TOP_FIELD, 1: BOTTOM_FIELD + FrameStore *pFrameStore; + + int list_sign[32]; + int poc_diff[32]; + int inter_layer_ref_idx[32]; + + int abs_poc_dist; + int num_refs; + + int field_used[2] = {1, 2}; + int fld_type[32]; + int num_flds, fld, idx; + int list_size = 0; + int top_idx = 0; + int bot_idx = 0; + + + // First find inter view ref + if (currSlice->layer_id > 0) + { + for (i = 0; i < (int) num_ref_idx_lX_active; i++) + { + if (list[i]->view_id < currSlice->layer_id) + { + inter_layer_ref[found] = list[i]; + inter_layer_ref_idx[found] = i; + found++; + } + } + } + + // Now access all references in buffer and assign them + // to a potential reordering list. For each one of these + // references compute the poc distance compared to current + // frame. + idx = 0; + num_refs = p_Dpb->ref_frames_in_buffer; + for (i = 0; i < num_refs; i++) + { + pFrameStore = p_Dpb->fs_ref[i]; + pField[0] = pFrameStore->top_field; + pField[1] = pFrameStore->bottom_field; + num_flds = (currSlice->structure == BOTTOM_FIELD && (p_Enc_Pic->poc == (pField[0]->poc + 1) ) ) ? 1 : 2; + + poc_diff[2*i ] = 0xFFFF; + poc_diff[2*i + 1] = 0xFFFF; + + for ( fld = 0; fld < num_flds; fld++ ) + { + if ( (pFrameStore->is_used & field_used[fld]) && pField[fld]->used_for_reference && !(pField[fld]->is_long_term)) + { + abs_poc_dist = iabs(pField[fld]->poc - p_Enc_Pic->poc) ; + poc_diff[idx] = abs_poc_dist; + re_order[idx] = pField[fld]; + fld_type[idx] = fld + 1; + + if (list_no == LIST_0) + { + list_sign[idx] = (p_Enc_Pic->poc < pField[fld]->poc) ? +1 : -1; + } + else + { + list_sign[idx] = (p_Enc_Pic->poc > pField[fld]->poc) ? +1 : -1; + } + idx++; + } + } + } + num_refs = idx; + + // now sort these references based on poc (temporal) distance + for (i=0; i < num_refs-1; i++) + { + for (j = (i + 1); j < num_refs; j++) + { + if (poc_diff[i] > poc_diff[j] || (poc_diff[i] == poc_diff[j] && list_sign[j] > list_sign[i])) + { + // poc_diff + i32_swap (poc_diff[i], poc_diff[j]); + // re_order (PicNum) + tmp_pic_value = re_order[i]; + re_order[i] = re_order[j]; + re_order[j] = tmp_pic_value; + // list_sign + i32_swap (list_sign[i], list_sign[j]); + // fld_type + i32_swap (fld_type[i], fld_type[j]); + } + } + } + + if (currSlice->structure == TOP_FIELD) + { + while ((top_idx < num_refs)||(bot_idx < num_refs)) + { + for ( ; top_idx < num_refs; top_idx++) + { + if ( fld_type[top_idx] == TOP_FIELD ) + { + tmp_reorder[list_size] = re_order[top_idx]; + list_size++; + top_idx++; + break; + } + } + for ( ; bot_idx < num_refs; bot_idx++) + { + if ( fld_type[bot_idx] == BOTTOM_FIELD ) + { + tmp_reorder[list_size] = re_order[bot_idx]; + list_size++; + bot_idx++; + break; + } + } + } + } + + if (currSlice->structure == BOTTOM_FIELD) + { + while ((top_idx < num_refs)||(bot_idx < num_refs)) + { + for ( ; bot_idx < num_refs; bot_idx++) + { + if ( fld_type[bot_idx] == BOTTOM_FIELD ) + { + tmp_reorder[list_size] = re_order[bot_idx]; + list_size++; + bot_idx++; + break; + } + } + for ( ; top_idx < num_refs; top_idx++) + { + if ( fld_type[top_idx] == TOP_FIELD ) + { + tmp_reorder[list_size] = re_order[top_idx]; + list_size++; + top_idx++; + break; + } + } + } + } + + j = 0; + k = 0; + for (i = 0; i < (int) num_ref_idx_lX_active; i++) + { + if ((found > 0) && (i == inter_layer_ref_idx[k])) + { + list[i] = inter_layer_ref[k++]; + found--; + } + else + { + list[i] = tmp_reorder[j++]; + } + } +} + +/*! +************************************************************************ +* \brief +* decide reference picture reordering, Field only +************************************************************************ +*/ +void poc_ref_pic_reorder_field(Slice *currSlice, unsigned num_ref_idx_lX_active, int list_no) +{ + StorablePicture **list = currSlice->listX[list_no]; + int *modification_of_pic_nums_idc = currSlice->modification_of_pic_nums_idc[list_no]; + int *abs_diff_pic_num_minus1 = currSlice->abs_diff_pic_num_minus1[list_no]; + + VideoParameters *p_Vid = currSlice->p_Vid; + //InputParameters *p_Inp = currSlice->p_Inp; + StorablePicture *p_Enc_Pic = p_Vid->enc_picture; + DecodedPictureBuffer *p_Dpb = currSlice->p_Dpb; + + unsigned int i,j,k; + + int currPicNum, picNumLXPred; + + int default_order[32]; + int re_order[32]; + int tmp_reorder[32]; + int list_sign[32]; + int poc_diff[32]; + int fld_type[32]; + + int reorder_stop, no_reorder; + int tmp_value, diff; + + int abs_poc_dist; + int maxPicNum; + unsigned int num_refs; + + int field_used[2] = {1, 2}; + int fld, idx, num_flds; + + unsigned int top_idx = 0; + unsigned int bot_idx = 0; + unsigned int list_size = 0; + + StorablePicture *pField[2]; // 0: TOP_FIELD, 1: BOTTOM_FIELD + FrameStore *pFrameStore; + + maxPicNum = 2 * p_Vid->max_frame_num; + currPicNum = 2 * p_Vid->frame_num + 1; + + picNumLXPred = currPicNum; + + // First assign default list order. + for (i=0; ipic_num; + } + + // Now access all references in buffer and assign them + // to a potential reordering list. For each one of these + // references compute the poc distance compared to current + // frame. + // look for eligible fields + idx = 0; + + for (i=0; iref_frames_in_buffer; i++) + { + pFrameStore = p_Dpb->fs_ref[i]; + pField[0] = pFrameStore->top_field; + pField[1] = pFrameStore->bottom_field; + num_flds = (currSlice->structure == BOTTOM_FIELD && (p_Enc_Pic->poc == (pField[0]->poc + 1) ) ) ? 1 : 2; + + poc_diff[2*i ] = 0xFFFF; + poc_diff[2*i + 1] = 0xFFFF; + + for ( fld = 0; fld < num_flds; fld++ ) + { +#if (MVC_EXTENSION_ENABLE) + if ( (pFrameStore->is_used & field_used[fld]) && pField[fld]->used_for_reference && !(pField[fld]->is_long_term) + && (p_Vid->p_Inp->num_of_views==1 || pField[fld]->view_id==p_Vid->view_id)) +#else + if ( (pFrameStore->is_used & field_used[fld]) && pField[fld]->used_for_reference && !(pField[fld]->is_long_term)) +#endif + { + abs_poc_dist = iabs(pField[fld]->poc - p_Enc_Pic->poc) ; + poc_diff[idx] = abs_poc_dist; + re_order[idx] = pField[fld]->pic_num; + fld_type[idx] = fld + 1; + + if (list_no == LIST_0) + { + list_sign[idx] = (p_Enc_Pic->poc < pField[fld]->poc) ? +1 : -1; + } + else + { + list_sign[idx] = (p_Enc_Pic->poc > pField[fld]->poc) ? +1 : -1; + } + idx++; + } + } + } + num_refs = idx; + + // now sort these references based on poc (temporal) distance + for (i=0; i < num_refs-1; i++) + { + for (j = (i + 1); j < num_refs; j++) + { + if (poc_diff[i] > poc_diff[j] || (poc_diff[i] == poc_diff[j] && list_sign[j] > list_sign[i])) + { + // poc_diff + tmp_value = poc_diff[i]; + poc_diff[i] = poc_diff[j]; + poc_diff[j] = tmp_value; + // re_order (PicNum) + tmp_value = re_order[i]; + re_order[i] = re_order[j]; + re_order[j] = tmp_value; + // list_sign + tmp_value = list_sign[i]; + list_sign[i] = list_sign[j]; + list_sign[j] = tmp_value; + // fld_type + tmp_value = fld_type[i]; + fld_type[i] = fld_type[j]; + fld_type[j] = tmp_value ; + } + } + } + + if (currSlice->structure == TOP_FIELD) + { + while ((top_idx < num_refs)||(bot_idx < num_refs)) + { + for ( ; top_idx < num_refs; top_idx++) + { + if ( fld_type[top_idx] == TOP_FIELD ) + { + tmp_reorder[list_size] = re_order[top_idx]; + list_size++; + top_idx++; + break; + } + } + for ( ; bot_idx < num_refs; bot_idx++) + { + if ( fld_type[bot_idx] == BOTTOM_FIELD ) + { + tmp_reorder[list_size] = re_order[bot_idx]; + list_size++; + bot_idx++; + break; + } + } + } + } + if (currSlice->structure == BOTTOM_FIELD) + { + while ((top_idx < num_refs)||(bot_idx < num_refs)) + { + for ( ; bot_idx < num_refs; bot_idx++) + { + if ( fld_type[bot_idx] == BOTTOM_FIELD ) + { + tmp_reorder[list_size] = re_order[bot_idx]; + list_size++; + bot_idx++; + break; + } + } + for ( ; top_idx < num_refs; top_idx++) + { + if ( fld_type[top_idx] == TOP_FIELD ) + { + tmp_reorder[list_size] = re_order[top_idx]; + list_size++; + top_idx++; + break; + } + } + } + } + + // copy to final matrix + list_size = imin( list_size, 32 ); + for ( i = 0; i < list_size; i++ ) + { + re_order[i] = tmp_reorder[i]; + } + + // Check versus default list to see if any + // change has happened + no_reorder = 1; + for (i=0; iref_pic_list_reordering_flag[LIST_0] = 1; + } + else + { + currSlice->ref_pic_list_reordering_flag[LIST_1] = 1; + } + } +} + + +/*! +************************************************************************ +* \brief +* decide reference picture reordering based on temporal layer, Frame only +************************************************************************ +*/ +void tlyr_ref_pic_reorder_frame_default(Slice *currSlice, unsigned num_ref_idx_lX_active, int list_no) +{ + StorablePicture **list = currSlice->listX[list_no]; + int *modification_of_pic_nums_idc = currSlice->modification_of_pic_nums_idc[list_no]; + int *abs_diff_pic_num_minus1 = currSlice->abs_diff_pic_num_minus1[list_no]; + + VideoParameters *p_Vid = currSlice->p_Vid; + InputParameters *p_Inp = currSlice->p_Inp; + StorablePicture *p_Enc_Pic = p_Vid->enc_picture; + struct decoded_picture_buffer *p_Dpb = currSlice->p_Dpb; + + unsigned int i,j,k; + + int currPicNum, picNumLXPred; + + int default_order[32]; + int re_order[32]; + int tmp_reorder[32]; + int list_sign[32]; + int reorder_stop, no_reorder; + int poc_diff[32]; + + int temporal_layer[32]; + + int diff; + + int abs_poc_dist; + int maxPicNum; + unsigned int num_refs; + + int tmp_poc_diff, tmp_re_order, tmp_list_sign, tmp_temporal_layer; + unsigned int beginIdx = 0; + char newSize = 0; + + + maxPicNum = p_Vid->max_frame_num; + currPicNum = p_Vid->frame_num; + + picNumLXPred = currPicNum; + + // First assign default list order. + for (i=0; ipic_num; + } + + // Now access all references in buffer and assign them + // to a potential reordering list. For each one of these + // references compute the poc distance compared to current + // frame. + num_refs = p_Dpb->ref_frames_in_buffer; + for (i=0; iref_frames_in_buffer; i++) + { + poc_diff[i] = 0xFFFF; + re_order[i] = p_Dpb->fs_ref[i]->frame->pic_num; + + temporal_layer[i] = p_Dpb->fs_ref[i]->frame->temporal_layer; + + if (p_Dpb->fs_ref[i]->is_used==3 && (p_Dpb->fs_ref[i]->frame->used_for_reference)&&(!p_Dpb->fs_ref[i]->frame->is_long_term)) + { + abs_poc_dist = iabs(p_Dpb->fs_ref[i]->frame->poc - p_Enc_Pic->poc) ; + poc_diff[i] = abs_poc_dist; + if (list_no == LIST_0) + { + list_sign[i] = (p_Enc_Pic->poc < p_Dpb->fs_ref[i]->frame->poc) ? +1 : -1; + } + else + { + list_sign[i] = (p_Enc_Pic->poc > p_Dpb->fs_ref[i]->frame->poc) ? +1 : -1; + } + } + } + + // first, sort the references based on poc (temporal) distance + for (i = 0; i < (num_refs - 1); i++) + { + for (j = i + 1; j < num_refs; j++) + { + if (list_sign[i] == list_sign[j] && poc_diff[i] > poc_diff[j]) + { + tmp_poc_diff = poc_diff[i]; + tmp_re_order = re_order[i]; + tmp_list_sign = list_sign[i]; + tmp_temporal_layer = temporal_layer[i]; + poc_diff[i] = poc_diff[j]; + re_order[i] = re_order[j]; + list_sign[i] = list_sign[j]; + temporal_layer[i] = temporal_layer[j]; + poc_diff[j] = tmp_poc_diff; + re_order[j] = tmp_re_order ; + list_sign[j] = tmp_list_sign; + temporal_layer[j] = tmp_temporal_layer; + } + } + } + + // use the closest valid reference frame + 3 temporal layer 0 frames + for (i = 0; i < num_refs; i++) + { + /* + if ( (i == 0 && temporal_layer[i] <= p_Enc_Pic->temporal_layer) || + (i && !temporal_layer[i]) ) // valid reference pic + */ + if (temporal_layer[i] <= p_Enc_Pic->temporal_layer) + { + tmp_poc_diff = poc_diff[i]; + tmp_re_order = re_order[i]; + tmp_list_sign = list_sign[i]; + tmp_temporal_layer = temporal_layer[i]; + for (j = i; j > beginIdx; j--) + { + poc_diff[j] = poc_diff[j-1]; + re_order[j] = re_order[j-1]; + list_sign[j] = list_sign[j-1]; + temporal_layer[j] = temporal_layer[j-1]; + } + poc_diff[beginIdx] = tmp_poc_diff; + re_order[beginIdx] = tmp_re_order; + list_sign[beginIdx] = tmp_list_sign; + temporal_layer[beginIdx] = tmp_temporal_layer; + beginIdx += 1; + + newSize++; + } + } + //currSlice->listXsize[0] = newSize; // update with valid size + + // populate list with selections from the pre-analysis stage + if ( p_Inp->WPMCPrecision + && p_Vid->pWPX->curr_wp_rd_pass->algorithm != WP_REGULAR + && p_Vid->pWPX->num_wp_ref_list[list_no] ) + { + for (i=0; ipWPX->wp_ref_list[list_no][i].PicNum; + } + } + + // Check versus default list to see if any + // change has happened + no_reorder = 1; + for (i=0; iref_pic_list_reordering_flag[LIST_0] = 1; + } + else + { + currSlice->ref_pic_list_reordering_flag[LIST_1] = 1; + } + } +} + + +/*! +************************************************************************ +* \brief +* Dummy function for reference list reordering. Used when +* p_Inp->ReferenceReorder is 0 or if an I/SI slice +************************************************************************ +*/ +static void reorder_lists_dummy( Slice *currSlice ) +{ +} + +/*! +************************************************************************ +* \brief +* Reorder lists +************************************************************************ +*/ + +void reorder_lists( Slice *currSlice ) +{ + InputParameters *p_Inp = currSlice->p_Inp; + + // Perform reordering based on poc distances for HierarchicalCoding + if ( p_Inp->ReferenceReorder && (currSlice->slice_type == P_SLICE || currSlice->slice_type == SP_SLICE)) + { + int i, num_ref; + + for (i = 0; i < currSlice->num_ref_idx_active[LIST_0] + 1; i++) + { + currSlice->modification_of_pic_nums_idc[LIST_0][i] = 3; + currSlice->abs_diff_pic_num_minus1[LIST_0][i] = 0; + currSlice->long_term_pic_idx[LIST_0][i] = 0; + } + + num_ref = currSlice->num_ref_idx_active[LIST_0]; + + if ( p_Inp->ReferenceReorder == 2 ) // temporal layer based + { + if ( currSlice->structure == FRAME ) + currSlice->poc_ref_pic_reorder_frame(currSlice, num_ref, LIST_0); + } + else + { + if ( currSlice->structure == FRAME ) + currSlice->poc_ref_pic_reorder_frame(currSlice, num_ref, LIST_0); + else + { + poc_ref_pic_reorder_field_enh(currSlice, num_ref, LIST_0); + } + } + + currSlice->num_ref_idx_active[LIST_0] = currSlice->listXsize[0]; + + if (p_Inp->ReferenceReorder > 1) + { + free_ref_pic_list_reordering_buffer(currSlice); + alloc_ref_pic_list_reordering_buffer(currSlice); + reorder_ref_pic_list ( currSlice, LIST_0); + } + } + currSlice->num_ref_idx_active[LIST_0] = currSlice->listXsize[0]; + +#if KEEP_B_SAME_LIST + if ( currSlice->slice_type == B_SLICE && currSlice->p_Dpb->ref_frames_in_buffer >= 2 ) + { + int iSendAddtionalRPLR = 0; + iSendAddtionalRPLR = ( p_Inp->BIdenticalList == 1 && ( (currSlice->ThisPOC/2) % (p_Inp->NumberBFrames+1) ) == 0 ) || + ( p_Inp->BIdenticalList == 2 ); + + if ( iSendAddtionalRPLR ) + { + int i, num_ref, diff_num, last_frame_num; + + alloc_ref_pic_list_reordering_buffer(currSlice); + + for (i = 0; i < currSlice->num_ref_idx_active[LIST_1] + 1; i++) + { + currSlice->modification_of_pic_nums_idc[LIST_1][i] = 3; + currSlice->abs_diff_pic_num_minus1[LIST_1][i] = 0; + currSlice->long_term_pic_idx[LIST_1][i] = 0; + } + + num_ref = currSlice->num_ref_idx_active[LIST_1]; + + last_frame_num = currSlice->frame_num; + for ( i=0; inum_ref_idx_active[LIST_1]; i++ ) + { + diff_num = currSlice->listX[LIST_0][i]->frame_num - last_frame_num; + last_frame_num = currSlice->listX[LIST_0][i]->frame_num; + if ( diff_num <= 0 ) + { + currSlice->modification_of_pic_nums_idc[LIST_1][i] = 0; + currSlice->abs_diff_pic_num_minus1[LIST_1][i] = iabs(diff_num) - 1; + if ( currSlice->abs_diff_pic_num_minus1[LIST_1][i] < 0 ) + { + currSlice->abs_diff_pic_num_minus1[LIST_1][i] = currSlice->max_frame_num - 1; + } + } + else + { + currSlice->modification_of_pic_nums_idc[LIST_1][i] = 1; + currSlice->abs_diff_pic_num_minus1[LIST_1][i] = iabs(diff_num) - 1; + } + } + + currSlice->ref_pic_list_reordering_flag[LIST_1] = 1; + currSlice->num_ref_idx_active[LIST_1] = currSlice->listXsize[1]; + reorder_ref_pic_list ( currSlice, LIST_1); + currSlice->num_ref_idx_active[LIST_1] = currSlice->listXsize[1]; + + return; + } + } +#endif + +#if CRA + if ( p_Inp->useCRA ) + { + if ( currSlice->slice_type == B_SLICE && currSlice->p_Dpb->ref_frames_in_buffer >= 2 ) + { + if ( ( (currSlice->ThisPOC/2) - (p_Inp->NumberBFrames+1) ) % p_Inp->intra_period == 0 ) + { + int i, diff_num; + alloc_ref_pic_list_reordering_buffer(currSlice); + for (i = 0; i < currSlice->num_ref_idx_active[LIST_1] + 1; i++) + { + currSlice->modification_of_pic_nums_idc[LIST_1][i] = 3; + currSlice->abs_diff_pic_num_minus1[LIST_1][i] = 0; + currSlice->long_term_pic_idx[LIST_1][i] = 0; + } + + diff_num = currSlice->listX[LIST_0][0]->frame_num - currSlice->frame_num; + if ( diff_num <= 0 ) + { + currSlice->modification_of_pic_nums_idc[LIST_1][0] = 0; + currSlice->abs_diff_pic_num_minus1[LIST_1][0] = iabs(diff_num) - 1; + if ( currSlice->abs_diff_pic_num_minus1[LIST_1][0] < 0 ) + { + currSlice->abs_diff_pic_num_minus1[LIST_1][0] = currSlice->max_frame_num - 1; + } + } + else + { + currSlice->modification_of_pic_nums_idc[LIST_1][0] = 1; + currSlice->abs_diff_pic_num_minus1[LIST_1][0] = iabs(diff_num) - 1; + } + + currSlice->ref_pic_list_reordering_flag[LIST_1] = 1; + currSlice->num_ref_idx_active[LIST_1] = currSlice->listXsize[1]; + reorder_ref_pic_list ( currSlice, LIST_1); + currSlice->num_ref_idx_active[LIST_1] = currSlice->listXsize[1]; + } + } + } +#endif +} + +/*! +************************************************************************ +* \brief +* Reorder lists +************************************************************************ +*/ + +void wp_mcprec_reorder_lists( Slice *currSlice ) +{ + + int i, num_ref; + + wpxModifyRefPicList( currSlice ); + + alloc_ref_pic_list_reordering_buffer(currSlice); + + for (i = 0; i < currSlice->num_ref_idx_active[LIST_0] + 1; i++) + { + currSlice->modification_of_pic_nums_idc[LIST_0][i] = 3; + currSlice->abs_diff_pic_num_minus1[LIST_0][i] = 0; + currSlice->long_term_pic_idx[LIST_0][i] = 0; + } + + if (currSlice->slice_type == B_SLICE) // type should be part of currSlice not p_Vid + { + for (i = 0; i < currSlice->num_ref_idx_active[LIST_1] + 1; i++) + { + currSlice->modification_of_pic_nums_idc[LIST_1][i] = 3; + currSlice->abs_diff_pic_num_minus1[LIST_1][i] = 0; + currSlice->long_term_pic_idx[LIST_1][i] = 0; + } + } + + // LIST_0 + num_ref = currSlice->num_ref_idx_active[LIST_0]; + + currSlice->poc_ref_pic_reorder_frame(currSlice, num_ref, LIST_0); + // reference picture reordering + reorder_ref_pic_list ( currSlice, LIST_0); + + if ( currSlice->slice_type == B_SLICE ) + { + // LIST_1 + num_ref = currSlice->num_ref_idx_active[LIST_1]; + + currSlice->poc_ref_pic_reorder_frame(currSlice, num_ref, LIST_1); + // reference picture reordering + reorder_ref_pic_list ( currSlice, LIST_1); + } +} + +void set_default_ref_pic_lists(Slice *currSlice) +{ + int list, ref; + + for(list = LIST_0; list <= (currSlice->slice_type == P_SLICE ? LIST_0:LIST_1); list++) + { + for(ref = 0; ref < currSlice->listXsize[list]; ref++) + { + currSlice->default_pic_num[list][ref] = currSlice->listX[list][ref]->pic_num; +#if MVC_EXTENSION_ENABLE + currSlice->default_view_id[list][ref] = currSlice->listX[list][ref]->view_id; +#endif + } + } +} + +void reorder_against_default_ref_pic_lists(Slice *currSlice, int cur_list) +{ + VideoParameters *p_Vid = currSlice->p_Vid; + int *modification_of_pic_nums_idc = currSlice->modification_of_pic_nums_idc[cur_list]; + int *abs_diff_pic_num_minus1 = currSlice->abs_diff_pic_num_minus1[cur_list]; + int *abs_diff_view_idx_minus1 = currSlice->abs_diff_view_idx_minus1[cur_list]; + int i; + + int maxPicNum, currPicNum, picNumLXNoWrap, picNumLXPred; + int picViewIdxLX; + int maxViewIdx =0; + int currViewIdx = -1; + int picViewIdxLXPred=-1; + + int diff; + + if (p_Vid->structure==FRAME) + { + maxPicNum = p_Vid->max_frame_num; + currPicNum = p_Vid->frame_num; + } + else + { + maxPicNum = 2*p_Vid->max_frame_num; + currPicNum = 2*p_Vid->frame_num+1; + } + + currViewIdx = p_Vid->view_id; + maxViewIdx = 1; + picViewIdxLXPred=-1; + + picNumLXPred = currPicNum; + + currSlice->ref_pic_list_reordering_flag[cur_list] = 0; + for (i = 0; i < currSlice->listXsize[cur_list]; i++) + { + //printf("list %d %d : %d %d %d\n",currSlice->listXsize[cur_list], cur_list,i,currSlice->default_pic_num[cur_list][i], currSlice->listX[cur_list][i]->pic_num); + //if(currSlice->default_pic_num[cur_list][i] != currSlice->listX[cur_list][i]->pic_num) + if( +#if MVC_EXTENSION_ENABLE + currSlice->default_view_id[cur_list][i] != currSlice->listX[cur_list][i]->view_id || +#endif + currSlice->default_pic_num[cur_list][i] != currSlice->listX[cur_list][i]->pic_num) + { + currSlice->ref_pic_list_reordering_flag[cur_list] = 1; + break; + } + } + + if(!currSlice->ref_pic_list_reordering_flag[cur_list]) + return; + + // initialization + for (i = 0; i < currSlice->listXsize[cur_list]+1; i++) + modification_of_pic_nums_idc[i] = 3; + + for (i=0; i < currSlice->listXsize[cur_list]; i++) + { + StorablePicture *ref_pic = currSlice->listX[cur_list][i]; + // inter_view ref reordering + if(ref_pic->view_id != currViewIdx) + { + diff = 1; + + modification_of_pic_nums_idc[i] = 5; + abs_diff_view_idx_minus1[i] = iabs(diff)-1; + } + // temporal ref reordering + else + { + diff = ref_pic->pic_num-picNumLXPred; + if (diff <= 0) + { + modification_of_pic_nums_idc[i] = 0; + abs_diff_pic_num_minus1[i] = iabs(diff)-1; + if (abs_diff_pic_num_minus1[i] < 0) + abs_diff_pic_num_minus1[i] = maxPicNum-1; + } + else + { + modification_of_pic_nums_idc[i] = 1; + abs_diff_pic_num_minus1[i] = iabs(diff)-1; + } + } + + if (modification_of_pic_nums_idc[i] == 0 || modification_of_pic_nums_idc[i] == 1) + { + if (modification_of_pic_nums_idc[i] == 0) + { + if( picNumLXPred - ( abs_diff_pic_num_minus1[i] + 1 ) < 0 ) + picNumLXNoWrap = picNumLXPred - ( abs_diff_pic_num_minus1[i] + 1 ) + maxPicNum; + else + picNumLXNoWrap = picNumLXPred - ( abs_diff_pic_num_minus1[i] + 1 ); + } + else // (modification_of_pic_nums_idc[i] == 1) + { + if( picNumLXPred + ( abs_diff_pic_num_minus1[i] + 1 ) >= maxPicNum ) + picNumLXNoWrap = picNumLXPred + ( abs_diff_pic_num_minus1[i] + 1 ) - maxPicNum; + else + picNumLXNoWrap = picNumLXPred + ( abs_diff_pic_num_minus1[i] + 1 ); + } + picNumLXPred = picNumLXNoWrap; + } + else if (modification_of_pic_nums_idc[i] == 4 || modification_of_pic_nums_idc[i] == 5) + { + if(modification_of_pic_nums_idc[i] == 4) + { + picViewIdxLX = picViewIdxLXPred - (abs_diff_view_idx_minus1[i] + 1); + if( picViewIdxLX <0) + picViewIdxLX += maxViewIdx; + } + else //if(modification_of_pic_nums_idc[i] == 5) + { + picViewIdxLX = picViewIdxLXPred + (abs_diff_view_idx_minus1[i] + 1); + if( picViewIdxLX >= maxViewIdx) + picViewIdxLX -= maxViewIdx; + } + picViewIdxLXPred = picViewIdxLX; + } + } +} + diff --git a/lencod/src/lln_mc_prediction.c b/lencod/src/lln_mc_prediction.c new file mode 100644 index 0000000..b6a4d90 --- /dev/null +++ b/lencod/src/lln_mc_prediction.c @@ -0,0 +1,1316 @@ +/*! + ************************************************************************************* + * \file lln_mc_prediction.c + * + * \brief + * Functions for motion compensated prediction + * + * \author + * Main contributors (see contributors.h for copyright, + * address and affiliation details) + * - Alexis Michael Tourapis + * - Modified for encoder from ldecod/src/mc_prediction.c + * by Peshala V. Pahalawatta + ************************************************************************************* + */ +#include "global.h" +#include "mbuffer.h" +#include "mb_access.h" +#include "macroblock.h" +#include "errdo.h" +#include "block.h" +#include "lln_mc_prediction.h" + +static const int COEF[6] = { 1, -5, 20, 20, -5, 1 }; +/*! + ************************************************************************ + * \brief + * block single list prediction + ************************************************************************ + */ +static inline void mc_prediction(imgpel** mb_pred, + int block_size_y, + int block_size_x, + int ioff, + imgpel block[MB_BLOCK_SIZE][MB_BLOCK_SIZE]) +{ + int jj; + + if (block_size_x == MB_BLOCK_SIZE) + { + memcpy(&(mb_pred[0][ioff]), &(block[0][0]), block_size_x * block_size_y * sizeof(imgpel)); + } + else + { + for(jj = 0; jj < block_size_y; jj++) + { + memcpy(&(mb_pred[jj][ioff]), &(block[jj][0]), block_size_x * sizeof(imgpel)); + } + } +} + + + +/*! + ************************************************************************ + * \brief + * block single list weighted prediction + ************************************************************************ + */ +static inline void weighted_mc_prediction(imgpel** mb_pred, + int block_size_y, + int block_size_x, + int ioff, + imgpel block[MB_BLOCK_SIZE][MB_BLOCK_SIZE], + int wp_scale, + int wp_offset, + int weight_denom, + int color_clip) +{ + int ii, jj; + imgpel *mpr, *b0; + + for(jj=0;jjp_Vid; + InputParameters *p_Inp = currMB->p_Inp; + + int tmp_res[21][21]; + int *tmp_line; + imgpel *p0, *p1, *p2, *p3, *p4, *p5; + int *x0, *x1, *x2, *x3, *x4, *x5; + + imgpel **cur_imgY, *cur_lineY; + int ipos_m2, ipos_m1, ipos, ipos_p1, ipos_p2, ipos_p3; + imgpel *orig_line; + int tmp_pos; + + int dx = (x_pos & 3), dy = (y_pos & 3); + int i, j, jj; + int shift_x = dec_picture->size_x; + int maxold_x = dec_picture->size_x - 1; + int maxold_y = (currMB->mb_field) ? (dec_picture->size_y >> 1) - 1 : dec_picture->size_y - 1; + int result; + int pres_x; + int max_imgpel_value = p_Vid->max_pel_value_comp[pl]; + + if( (p_Inp->separate_colour_plane_flag != 0) ) + { + cur_imgY = curr_ref->de_mem->p_dec_img[(short) p_Vid->colour_plane_id][decoder]; + } + else + { + cur_imgY = curr_ref->de_mem->p_dec_img[pl][decoder]; + } + + x_pos = x_pos >> 2; + y_pos = y_pos >> 2; + + if ( (y_pos > 1) && (y_pos < maxold_y - 2 - block_size_y) && (x_pos > 1) && (x_pos < maxold_x - 2 - block_size_x)) + { + cur_imgY = &cur_imgY[ y_pos]; + if (dx == 0 && dy == 0) + { /* fullpel position */ + for (j = 0; j < block_size_y; j++) + { + memcpy(&(block[j][0]), &(cur_imgY[j][x_pos]), block_size_x * sizeof(imgpel)); + } + } + else + { /* other positions */ + + if (dy == 0) + { /* No vertical interpolation */ + for (j = 0; j < block_size_y; j++) + { + p0 = &cur_imgY[j][x_pos - 2]; + p1 = p0 + 1; + p2 = p1 + 1; + p3 = p2 + 1; + p4 = p3 + 1; + p5 = p4 + 1; + orig_line = block[j]; + + for (i = 0; i < block_size_x; i++) + { + result = (*(p0++) + *(p5++)) * COEF[0] + + (*(p1++) + *(p4++)) * COEF[1] + + (*(p2++) + *(p3++)) * COEF[2]; + + *orig_line++ = (imgpel) iClip1(max_imgpel_value, ((result + 16)>>5)); + } + } + + if ((dx&1) == 1) + { + for (j = 0; j < block_size_y; j++) + { + cur_lineY = &(cur_imgY[j][x_pos + (dx >> 1)]); + orig_line = block[j]; + for (i = 0; i < block_size_x; i++) + { + *orig_line = (imgpel) ((*orig_line + *(cur_lineY++) + 1 ) >> 1); + orig_line++; + } + } + } + } + else if (dx == 0) + { /* No horizontal interpolation */ + p0 = &(cur_imgY[ - 2][x_pos]); + for (j = 0; j < block_size_y; j++) + { + p1 = p0 + shift_x; + p2 = p1 + shift_x; + p3 = p2 + shift_x; + p4 = p3 + shift_x; + p5 = p4 + shift_x; + orig_line = block[j]; + + for (i = 0; i < block_size_x; i++) + { + result = (*(p0++) + *(p5++)) * COEF[0] + + (*(p1++) + *(p4++)) * COEF[1] + + (*(p2++) + *(p3++)) * COEF[2]; + + *orig_line++ = (imgpel) iClip1(max_imgpel_value, ((result + 16)>>5)); + } + p0 = p1 - block_size_x; + } + + if ((dy&1) == 1) + { + jj = (dy >> 1); + for (j = 0; j < block_size_y; j++) + { + cur_lineY = &(cur_imgY[jj++][x_pos]); + orig_line = block[j]; + for (i = 0; i < block_size_x; i++) + { + *orig_line = (imgpel) ((*orig_line + *(cur_lineY++) + 1 ) >> 1); + orig_line++; + } + } + } + } + else if (dx == 2) + { /* Vertical & horizontal interpolation */ + jj = - 2; + for (j = 0; j < block_size_y + 5; j++) + { + p0 = &cur_imgY[jj++][x_pos - 2]; + p1 = p0 + 1; + p2 = p1 + 1; + p3 = p2 + 1; + p4 = p3 + 1; + p5 = p4 + 1; + orig_line = block[j]; + tmp_line = tmp_res[j]; + + for (i = 0; i < block_size_x; i++) + { + *(tmp_line++) = (*(p0++) + *(p5++)) * COEF[0] + + (*(p1++) + *(p4++)) * COEF[1] + + (*(p2++) + *(p3++)) * COEF[2]; + } + } + + for (j = 0; j < block_size_y; j++) + { + x0 = tmp_res[j ]; + x1 = tmp_res[j + 1]; + x2 = tmp_res[j + 2]; + x3 = tmp_res[j + 3]; + x4 = tmp_res[j + 4]; + x5 = tmp_res[j + 5]; + orig_line = block[j]; + + for (i = 0; i < block_size_x; i++) + { + result = (*x0++ + *x5++) * COEF[0] + + (*x1++ + *x4++) * COEF[1] + + (*x2++ + *x3++) * COEF[2]; + + *(orig_line++) = (imgpel) iClip1(max_imgpel_value, ((result+512)>>10)); + } + } + + if ((dy&1) == 1) + { + jj = 2 + (dy>>1); + for (j = 0; j < block_size_y; j++) + { + tmp_line = tmp_res[jj++]; + orig_line = block[j]; + for (i = 0; i < block_size_x; i++) + { + *orig_line = (imgpel) ((*orig_line + iClip1(max_imgpel_value, ((*(tmp_line++) + 16) >> 5)) + 1 )>> 1); + orig_line++; + } + } + } + } + else if (dy == 2) + { /* Horizontal & vertical interpolation */ + p0 = &(cur_imgY[ -2][x_pos - 2]); + for (j = 0; j < block_size_y; j++) + { + p1 = p0 + shift_x; + p2 = p1 + shift_x; + p3 = p2 + shift_x; + p4 = p3 + shift_x; + p5 = p4 + shift_x; + tmp_line = tmp_res[j]; + + for (i = 0; i < block_size_x + 5; i++) + { + *(tmp_line++) = (*(p0++) + *(p5++)) * COEF[0] + + (*(p1++) + *(p4++)) * COEF[1] + + (*(p2++) + *(p3++)) * COEF[2]; + } + p0 = p1 - (block_size_x + 5); + } + + for (j = 0; j < block_size_y; j++) + { + orig_line = block[j]; + x0 = tmp_res[j]; + x1 = x0 + 1; + x2 = x1 + 1; + x3 = x2 + 1; + x4 = x3 + 1; + x5 = x4 + 1; + + for (i = 0; i < block_size_x; i++) + { + result = (*(x0++) + *(x5++)) * COEF[0] + + (*(x1++) + *(x4++)) * COEF[1] + + (*(x2++) + *(x3++)) * COEF[2]; + + *(orig_line++) = (imgpel) iClip1(max_imgpel_value, ((result + 512)>>10)); + } + } + + if ((dx&1) == 1) + { + for (j = 0; j < block_size_y; j++) + { + tmp_line = &tmp_res[j][2 + (dx>>1)]; + orig_line = block[j]; + for (i = 0; i < block_size_x; i++) + { + *orig_line = (imgpel) ((*orig_line + iClip1(max_imgpel_value, ((*(tmp_line++) + 16)>>5))+1)>>1); + orig_line ++; + } + } + } + } + else + { /* Diagonal interpolation */ + jj = (dy == 1 ? 0 : 1); + + for (j = 0; j < block_size_y; j++) + { + p0 = &cur_imgY[jj++][x_pos - 2]; + p1 = p0 + 1; + p2 = p1 + 1; + p3 = p2 + 1; + p4 = p3 + 1; + p5 = p4 + 1; + + orig_line = block[j]; + + for (i = 0; i < block_size_x; i++) + { + result = (*(p0++) + *(p5++)) * COEF[0] + + (*(p1++) + *(p4++)) * COEF[1] + + (*(p2++) + *(p3++)) * COEF[2]; + + *(orig_line++) = (imgpel) iClip1(max_imgpel_value, ((result + 16)>>5)); + } + } + + p0 = &(cur_imgY[-2][(dx == 1 ? x_pos : x_pos + 1)]); + for (j = 0; j < block_size_y; j++) + { + p1 = p0 + shift_x; + p2 = p1 + shift_x; + p3 = p2 + shift_x; + p4 = p3 + shift_x; + p5 = p4 + shift_x; + orig_line = block[j]; + + for (i = 0; i < block_size_x; i++) + { + result = (*(p0++) + *(p5++)) * COEF[0] + + (*(p1++) + *(p4++)) * COEF[1] + + (*(p2++) + *(p3++)) * COEF[2]; + + *orig_line = (imgpel) ((*orig_line + iClip1(max_imgpel_value, ((result + 16) >> 5)) + 1) >> 1); + orig_line++; + } + p0 = p1 - block_size_x ; + } + } + } + } + else // unsafe positions + { + if (dx == 0 && dy == 0) + { /* fullpel position */ + for (j = 0; j < block_size_y; j++) + { + cur_lineY = cur_imgY[iClip3(0, maxold_y, y_pos + j)]; + orig_line = block[j]; + for (i = 0; i < block_size_x; i++) + { + *(orig_line++) = cur_lineY[iClip3(0, maxold_x, x_pos + i )]; + } + } + } + else + { /* other positions */ + + if (dy == 0) + { /* No vertical interpolation */ + tmp_pos = x_pos - 2; + for (i = 0; i < block_size_x; i++) + { + ipos_m2 = iClip3(0, maxold_x, tmp_pos++); + ipos_m1 = iClip3(0, maxold_x, tmp_pos++); + ipos = iClip3(0, maxold_x, tmp_pos++); + ipos_p1 = iClip3(0, maxold_x, tmp_pos++); + ipos_p2 = iClip3(0, maxold_x, tmp_pos++); + ipos_p3 = iClip3(0, maxold_x, tmp_pos ); + tmp_pos -= 4; + + for (j = 0; j < block_size_y; j++) + { + cur_lineY = cur_imgY[iClip3(0,maxold_y,y_pos+j)]; + + result = (cur_lineY[ipos_m2] + cur_lineY[ipos_p3]) * COEF[0]; + result += (cur_lineY[ipos_m1] + cur_lineY[ipos_p2]) * COEF[1]; + result += (cur_lineY[ipos ] + cur_lineY[ipos_p1]) * COEF[2]; + + block[j][i] = (imgpel) iClip1(max_imgpel_value, ((result + 16)>>5)); + } + } + + if ((dx&1) == 1) + { + for (j = 0; j < block_size_y; j++) + { + cur_lineY = cur_imgY[iClip3(0,maxold_y,y_pos+j)]; + orig_line = block[j]; + for (i = 0; i < block_size_x; i++) + { + *orig_line = (imgpel) ((*orig_line + cur_lineY[iClip3(0, maxold_x, x_pos + i + (dx >> 1))] + 1 ) >> 1); + orig_line++; + } + } + } + } + else if (dx == 0) + { /* No horizontal interpolation */ + tmp_pos = y_pos - 2; + for (j = 0; j < block_size_y; j++) + { + p0 = cur_imgY[iClip3(0, maxold_y, tmp_pos++)]; + p1 = cur_imgY[iClip3(0, maxold_y, tmp_pos++)]; + p2 = cur_imgY[iClip3(0, maxold_y, tmp_pos++)]; + p3 = cur_imgY[iClip3(0, maxold_y, tmp_pos++)]; + p4 = cur_imgY[iClip3(0, maxold_y, tmp_pos++)]; + p5 = cur_imgY[iClip3(0, maxold_y, tmp_pos )]; + + tmp_pos -= 4; + orig_line = block[j]; + + for (i = 0; i < block_size_x; i++) + { + pres_x = iClip3(0,maxold_x,x_pos+i); + + result = (p0[pres_x] + p5[pres_x]) * COEF[0]; + result += (p1[pres_x] + p4[pres_x]) * COEF[1]; + result += (p2[pres_x] + p3[pres_x]) * COEF[2]; + *(orig_line++) = (imgpel) iClip1(max_imgpel_value, ((result+16)>>5)); + } + } + + if ((dy&1) == 1) + { + for (j = 0; j < block_size_y; j++) + { + cur_lineY = cur_imgY[iClip3(0,maxold_y,y_pos+j+(dy>>1))]; + orig_line = block[j]; + for (i = 0; i < block_size_x; i++) + { + *orig_line = (imgpel) ((*orig_line + cur_lineY[iClip3(0, maxold_x, x_pos + i)] + 1 ) >> 1); + orig_line++; + } + } + } + } + else if (dx == 2) + { /* Vertical & horizontal interpolation */ + tmp_pos = x_pos - 2; + for (i = 0; i < block_size_x; i++) + { + ipos_m2 = iClip3(0, maxold_x, tmp_pos++); + ipos_m1 = iClip3(0, maxold_x, tmp_pos++); + ipos = iClip3(0, maxold_x, tmp_pos++); + ipos_p1 = iClip3(0, maxold_x, tmp_pos++); + ipos_p2 = iClip3(0, maxold_x, tmp_pos++); + ipos_p3 = iClip3(0, maxold_x, tmp_pos ); + tmp_pos -= 4; + + for (j = 0; j < block_size_y + 5; j++) + { + cur_lineY = cur_imgY[iClip3(0,maxold_y,y_pos + j - 2)]; + + tmp_res[j][i] = (cur_lineY[ipos_m2] + cur_lineY[ipos_p3]) * COEF[0]; + tmp_res[j][i] += (cur_lineY[ipos_m1] + cur_lineY[ipos_p2]) * COEF[1]; + tmp_res[j][i] += (cur_lineY[ipos ] + cur_lineY[ipos_p1]) * COEF[2]; + } + } + + for (j = 0; j < block_size_y; j++) + { + x0 = tmp_res[j ]; + x1 = tmp_res[j + 1]; + x2 = tmp_res[j + 2]; + x3 = tmp_res[j + 3]; + x4 = tmp_res[j + 4]; + x5 = tmp_res[j + 5]; + orig_line = block[j]; + + for (i = 0; i < block_size_x; i++) + { + result = (*x0++ + *x5++) * COEF[0] + + (*x1++ + *x4++) * COEF[1] + + (*x2++ + *x3++) * COEF[2]; + + *(orig_line++) = (imgpel) iClip1(max_imgpel_value, ((result+512)>>10)); + } + } + + if ((dy&1) == 1) + { + for (j = 0; j < block_size_y; j++) + { + tmp_line = tmp_res[j + 2 + (dy>>1)]; + orig_line = block[j]; + for (i = 0; i < block_size_x; i++) + { + *orig_line = (imgpel) ((*orig_line + iClip1(max_imgpel_value, ((*(tmp_line++) + 16) >> 5)) + 1 )>>1); + orig_line++; + } + } + } + } + else if (dy == 2) + { /* Horizontal & vertical interpolation */ + + tmp_pos = y_pos - 2; + for (j = 0; j < block_size_y; j++) + { + p0 = cur_imgY[iClip3(0, maxold_y, tmp_pos++)]; + p1 = cur_imgY[iClip3(0, maxold_y, tmp_pos++)]; + p2 = cur_imgY[iClip3(0, maxold_y, tmp_pos++)]; + p3 = cur_imgY[iClip3(0, maxold_y, tmp_pos++)]; + p4 = cur_imgY[iClip3(0, maxold_y, tmp_pos++)]; + p5 = cur_imgY[iClip3(0, maxold_y, tmp_pos )]; + + tmp_pos -= 4; + + for (i = 0; i < block_size_x + 5; i++) + { + pres_x = iClip3(0,maxold_x, x_pos + i - 2); + result = (p0[pres_x] + p5[pres_x])*COEF[0] + + (p1[pres_x] + p4[pres_x])*COEF[1] + + (p2[pres_x] + p3[pres_x])*COEF[2]; + tmp_res[j][i] = result; + } + } + + for (j = 0; j < block_size_y; j++) + { + orig_line = block[j]; + tmp_line = tmp_res[j]; + x0 = tmp_res[j]; + x1 = x0 + 1; + x2 = x1 + 1; + x3 = x2 + 1; + x4 = x3 + 1; + x5 = x4 + 1; + + for (i = 0; i < block_size_x; i++) + { + result = (*(x0++) + *(x5++)) * COEF[0] + + (*(x1++) + *(x4++)) * COEF[1] + + (*(x2++) + *(x3++)) * COEF[2]; + + *(orig_line++) = (imgpel) iClip1(max_imgpel_value, ((result + 512)>>10)); + } + } + + if ((dx&1) == 1) + { + for (j = 0; j < block_size_y; j++) + { + tmp_line = &tmp_res[j][2 + (dx>>1)]; + orig_line = block[j]; + for (i = 0; i < block_size_x; i++) + { + *orig_line = (imgpel) ((*orig_line + iClip1(max_imgpel_value, ((*(tmp_line++) + 16)>>5)) + 1)>> 1); + orig_line++; + } + } + } + } + else + { /* Diagonal interpolation */ + tmp_pos = x_pos - 2; + for (i = 0; i < block_size_x; i++) + { + ipos_m2 = iClip3(0, maxold_x, tmp_pos++); + ipos_m1 = iClip3(0, maxold_x, tmp_pos++); + ipos = iClip3(0, maxold_x, tmp_pos++); + ipos_p1 = iClip3(0, maxold_x, tmp_pos++); + ipos_p2 = iClip3(0, maxold_x, tmp_pos++); + ipos_p3 = iClip3(0, maxold_x, tmp_pos ); + tmp_pos -= 4; + + for (j = 0; j < block_size_y; j++) + { + cur_lineY = cur_imgY[iClip3(0,maxold_y,(dy == 1 ? y_pos+j : y_pos+j+1))]; + + result = (cur_lineY[ipos_m2] + cur_lineY[ipos_p3]) * COEF[0]; + result += (cur_lineY[ipos_m1] + cur_lineY[ipos_p2]) * COEF[1]; + result += (cur_lineY[ipos ] + cur_lineY[ipos_p1]) * COEF[2]; + + block[j][i] = (imgpel) iClip1(max_imgpel_value, ((result+16)>>5)); + } + } + + tmp_pos = y_pos - 2; + for (j = 0; j < block_size_y; j++) + { + p0 = cur_imgY[iClip3(0, maxold_y, tmp_pos++)]; + p1 = cur_imgY[iClip3(0, maxold_y, tmp_pos++)]; + p2 = cur_imgY[iClip3(0, maxold_y, tmp_pos++)]; + p3 = cur_imgY[iClip3(0, maxold_y, tmp_pos++)]; + p4 = cur_imgY[iClip3(0, maxold_y, tmp_pos++)]; + p5 = cur_imgY[iClip3(0, maxold_y, tmp_pos )]; + + tmp_pos -= 4; + orig_line = block[j]; + + for (i = 0; i < block_size_x; i++) + { + pres_x = dx == 1 ? x_pos+i : x_pos+i+1; + pres_x = iClip3(0, maxold_x, pres_x); + + result = (p0[pres_x] + p5[pres_x]) * COEF[0]; + result += (p1[pres_x] + p4[pres_x]) * COEF[1]; + result += (p2[pres_x] + p3[pres_x]) * COEF[2]; + + *orig_line = (imgpel) ((*orig_line + iClip1(max_imgpel_value, ((result+16)>>5)) + 1 ) >> 1); + orig_line++; + } + } + } + } + } +} + +void get_block_chroma(Macroblock *currMB, int decoder, int uv, StorablePicture* dec_picture, StorablePicture *curr_ref, int x_pos, int y_pos, int block_size_x, int block_size_y, imgpel block[MB_BLOCK_SIZE][MB_BLOCK_SIZE]) +{ + VideoParameters *p_Vid = currMB->p_Vid; + int subpel_x = p_Vid->mb_cr_size_x == 8 ? 7 : 3; + int subpel_y = p_Vid->mb_cr_size_y == 8 ? 7 : 3; + int shiftpel_x = p_Vid->mb_cr_size_x == 8 ? 3 : 2; + int shiftpel_y = p_Vid->mb_cr_size_y == 8 ? 3 : 2; + int total_scale = shiftpel_x + shiftpel_y; + + int dx = (x_pos & subpel_x); + int dy = (y_pos & subpel_y); + int dxcur = (subpel_x + 1 - dx); + int dycur = (subpel_y + 1 - dy); + + int w00 = dxcur * dycur; + int w01 = dxcur * dy; + int w10 = dx * dycur; + int w11 = dx * dy; + + int i, j; + int maxold_x = dec_picture->size_x_cr - 1; + int maxold_y = (currMB->mb_field) ? (dec_picture->size_y_cr >> 1) - 1 : dec_picture->size_y_cr - 1; + int result; + + imgpel **cur_img, *blk_line; + imgpel *cur_line, *cur_line_p1; + int tmp_pos; + int ipos, ipos_p1; + int max_imgpel_value = p_Vid->max_pel_value_comp[uv + 1]; + + cur_img = curr_ref->de_mem->p_dec_img[uv+1][decoder]; + + x_pos = x_pos >> shiftpel_x; + y_pos = y_pos >> shiftpel_y; + + if ((y_pos >= 0) && (y_pos < maxold_y - block_size_y) && (x_pos >= 0) && (x_pos < maxold_x - block_size_x)) + { + if (dx == 0 && dy == 0) + { /* fullpel position */ + for (j = 0; j < block_size_y; j++) + { + memcpy(&(block[j][0]), &(cur_img[ y_pos + j ][x_pos]), block_size_x * sizeof(imgpel)); + } + } + else if (dx == 0) + { + for (j = 0; j < block_size_y; j++) + { + cur_line = &cur_img[y_pos + j ][x_pos]; + cur_line_p1 = &cur_img[y_pos + j + 1][x_pos]; + blk_line = block[j]; + + for (i = 0; i < block_size_x; i++) + { + result = (w00 * *cur_line++ + w01 * *cur_line_p1++); + *(blk_line++) = (imgpel) iClip1(max_imgpel_value, rshift_rnd_sf(result, total_scale)); + } + } + } + else if (dy == 0) + { + for (j = 0; j < block_size_y; j++) + { + cur_line = &cur_img[y_pos + j][x_pos]; + cur_line_p1 = cur_line + 1; + blk_line = block[j]; + + for (i = 0; i < block_size_x; i++) + { + result = (w00 * *cur_line++ + w10 * *cur_line_p1++); + *(blk_line++) = (imgpel) iClip1(max_imgpel_value, rshift_rnd_sf(result, total_scale)); + } + } + } + else + { /* other positions */ + for (j = 0; j < block_size_y; j++) + { + cur_line = &cur_img[y_pos + j ][x_pos]; + cur_line_p1 = &cur_img[y_pos + j + 1][x_pos]; + blk_line = block[j]; + + for (i = 0; i < block_size_x; i++) + { + result = (w00 * *(cur_line++) + w01 * *(cur_line_p1++)); + result += (w10 * *(cur_line ) + w11 * *(cur_line_p1 )); + *(blk_line++) = (imgpel) iClip1(max_imgpel_value, rshift_rnd_sf(result, total_scale)); + } + } + } + } + else // unsafe positions + { + if (dx == 0 && dy == 0) + { /* fullpel position */ + for (j = 0; j < block_size_y; j++) + { + cur_line = cur_img[iClip3(0, maxold_y, y_pos + j)]; + blk_line = block[j]; + for (i = 0; i < block_size_x; i++) + { + *(blk_line++) = cur_line[iClip3(0, maxold_x, x_pos + i )]; + } + } + } + else if (dx == 0) + { + for (j = 0; j < block_size_y; j++) + { + cur_line = cur_img[iClip3(0, maxold_y, y_pos + j)]; + cur_line_p1 = cur_img[iClip3(0, maxold_y, y_pos + j + 1)]; + tmp_pos = x_pos; + blk_line = block[j]; + + for (i = 0; i < block_size_x; i++) + { + ipos = iClip3(0, maxold_x, tmp_pos++); + + result = (w00 * cur_line[ipos] + w01 * cur_line_p1[ipos]); + *(blk_line++) = (imgpel) iClip1(max_imgpel_value, rshift_rnd_sf(result, total_scale)); + } + } + } + else if (dy == 0) + { + for (j = 0; j < block_size_y; j++) + { + cur_line = cur_img[iClip3(0, maxold_y, y_pos + j)]; + tmp_pos = x_pos; + blk_line = block[j]; + + for (i = 0; i < block_size_x; i++) + { + ipos = iClip3(0, maxold_x, tmp_pos++); + ipos_p1 = iClip3(0, maxold_x, tmp_pos ); + + result = (w00 * cur_line[ipos ] + w10 * cur_line[ipos_p1]); + *(blk_line++) = (imgpel)iClip1(max_imgpel_value, rshift_rnd_sf(result, total_scale)); + } + } + } + else + { /* other positions */ + for (j = 0; j < block_size_y; j++) + { + cur_line = cur_img[iClip3(0, maxold_y, y_pos + j)]; + cur_line_p1 = cur_img[iClip3(0, maxold_y, y_pos + j + 1)]; + tmp_pos = x_pos; + blk_line = block[j]; + + for (i = 0; i < block_size_x; i++) + { + ipos = iClip3(0, maxold_x, tmp_pos++); + ipos_p1 = iClip3(0, maxold_x, tmp_pos ); + + result = ( + w00 * cur_line [ipos ] + + w10 * cur_line [ipos_p1] + + w01 * cur_line_p1[ipos ] + + w11 * cur_line_p1[ipos_p1]); + *(blk_line++) = (imgpel) iClip1(max_imgpel_value, rshift_rnd_sf(result, total_scale)); + } + } + } + } +} + +#if 0 +void intra_cr_decoding(Macroblock *currMB, int yuv, int smb) +{ + VideoParameters *p_Vid = currMB->p_Vid; + imgpel **curUV, *cur_img; + int (*m7UV)[16], *m7; + int uv; + int b8,b4; + int ioff, joff, ii, jj; + + for(uv = 0; uv < 2; uv++) + { + Boolean lossless_qpprime = (Boolean) ((p_Vid->lossless_qpprime_flag == 1) &&((p_Vid->qp + dec_picture->chroma_qp_offset[uv] + p_Vid->bitdepth_chroma_qp_scale) == 0)); + itrans_4x4 = (!lossless_qpprime) ? itrans4x4 : itrans4x4_ls; + + curUV = dec_picture->imgUV[uv]; + m7UV = p_Vid->currentSlice->mb_rres[uv+1]; + intrapred_chroma(currMB, uv); + + if (!smb && (currMB->cbp >> 4)) + { + for (b8 = 0; b8 < (p_Vid->num_uv_blocks); b8++) + { + for(b4 = 0; b4 < 4; b4++) + { + joff = subblk_offset_y[yuv][b8][b4]; + ioff = subblk_offset_x[yuv][b8][b4]; + + itrans_4x4(p_Vid, (ColorPlane) (uv + 1), ioff, joff); + + for(jj=joff; jjpix_c_y + jj][currMB->pix_c_x + ioff]; + m7 = &m7UV[jj][ioff]; + + for(ii=0; ii<4;ii++) + { + *cur_img++ = (imgpel) *m7++; + } + } + } + } + } + else if ((currMB->cbp >> 4) == 0) + { + for (b8 = 0; b8 < (p_Vid->num_uv_blocks); b8++) + { + for(b4 = 0; b4 < 4; b4++) + { + joff = subblk_offset_y[yuv][b8][b4]; + ioff = subblk_offset_x[yuv][b8][b4]; + + for(jj = joff; jj < 4 + joff;jj++) + memcpy(&(curUV[currMB->pix_c_y + jj][currMB->pix_c_x + ioff]), &(p_Vid->currentSlice->mb_pred[uv + 1][jj][ioff]), BLOCK_SIZE * sizeof(imgpel)); + } + } + } + else + { + itrans_sp_cr(p_Vid, uv); + + for (joff = 0; joff < 8; joff += 4) + { + for(ioff = 0; ioff < 8;ioff+=4) + { + itrans_4x4(p_Vid, (ColorPlane) (uv + 1), ioff, joff); + + for(jj = joff; jj < joff + 4; jj++) + for(ii = ioff; ii < ioff + 4; ii++) + { + curUV[currMB->pix_c_y+jj][ii + currMB->pix_c_x] = (imgpel) p_Vid->currentSlice->mb_rres[uv+1][jj][ii]; + } + } + } + } + } +} + +void prepare_direct_params(Macroblock *currMB, StorablePicture *dec_picture, MotionVector *pmvl0, MotionVector *pmvl1, char *l0_rFrame, char *l1_rFrame) +{ + VideoParameters *p_Vid = currMB->p_Vid; + char l0_rFrameL, l0_rFrameU, l0_rFrameUR; + char l1_rFrameL, l1_rFrameU, l1_rFrameUR; + PicMotionParamsOld *motion = &dec_picture->motion; + + PixelPos mb[4]; + + get_neighbors(currMB, mb, 0, 0, 16); + + if (!p_Vid->mb_aff_frame_flag) + { + l0_rFrameL = (char) (mb[0].available ? dec_picture->mv_info[mb[0].pos_y][mb[0].pos_x].ref_idx[LIST_0] : -1); + l0_rFrameU = (char) (mb[1].available ? dec_picture->mv_info[mb[1].pos_y][mb[1].pos_x].ref_idx[LIST_0] : -1); + l0_rFrameUR = (char) (mb[2].available ? dec_picture->mv_info[mb[2].pos_y][mb[2].pos_x].ref_idx[LIST_0] : -1); + + l1_rFrameL = (char) (mb[0].available ? dec_picture->mv_info[mb[0].pos_y][mb[0].pos_x].ref_idx[LIST_1] : -1); + l1_rFrameU = (char) (mb[1].available ? dec_picture->mv_info[mb[1].pos_y][mb[1].pos_x].ref_idx[LIST_1] : -1); + l1_rFrameUR = (char) (mb[2].available ? dec_picture->mv_info[mb[2].pos_y][mb[2].pos_x].ref_idx[LIST_1] : -1); + } + else + { + if (currMB->mb_field) + { + l0_rFrameL = (char) (mb[0].available + ? p_Vid->mb_data[mb[0].mb_addr].mb_field || dec_picture->mv_info[mb[0].pos_y][mb[0].pos_x].ref_idx[LIST_0] < 0 + ? dec_picture->mv_info[mb[0].pos_y][mb[0].pos_x].ref_idx[LIST_0] + : dec_picture->mv_info[mb[0].pos_y][mb[0].pos_x].ref_idx[LIST_0] * 2: -1); + + l0_rFrameU = (char) (mb[1].available + ? p_Vid->mb_data[mb[1].mb_addr].mb_field || dec_picture->mv_info[mb[1].pos_y][mb[1].pos_x].ref_idx[LIST_0] < 0 + ? dec_picture->mv_info[mb[1].pos_y][mb[1].pos_x].ref_idx[LIST_0] + : dec_picture->mv_info[mb[1].pos_y][mb[1].pos_x].ref_idx[LIST_0] * 2: -1); + + l0_rFrameUR = (char) (mb[2].available + ? p_Vid->mb_data[mb[2].mb_addr].mb_field || dec_picture->mv_info[mb[2].pos_y][mb[2].pos_x].ref_idx[LIST_0] < 0 + ? dec_picture->mv_info[mb[2].pos_y][mb[2].pos_x].ref_idx[LIST_0] + : dec_picture->mv_info[mb[2].pos_y][mb[2].pos_x].ref_idx[LIST_0] * 2: -1); + + l1_rFrameL = (char) (mb[0].available + ? p_Vid->mb_data[mb[0].mb_addr].mb_field || dec_picture->mv_info[mb[0].pos_y][mb[0].pos_x].ref_idx[LIST_1] < 0 + ? dec_picture->mv_info[mb[0].pos_y][mb[0].pos_x].ref_idx[LIST_1] + : dec_picture->mv_info[mb[0].pos_y][mb[0].pos_x].ref_idx[LIST_1] * 2: -1); + + l1_rFrameU = (char) (mb[1].available + ? p_Vid->mb_data[mb[1].mb_addr].mb_field || dec_picture->mv_info[mb[1].pos_y][mb[1].pos_x].ref_idx[LIST_1] < 0 + ? dec_picture->mv_info[mb[1].pos_y][mb[1].pos_x].ref_idx[LIST_1] + : dec_picture->mv_info[mb[1].pos_y][mb[1].pos_x].ref_idx[LIST_1] * 2: -1); + + l1_rFrameUR = (char) (mb[2].available + ? p_Vid->mb_data[mb[2].mb_addr].mb_field || dec_picture->mv_info[mb[2].pos_y][mb[2].pos_x].ref_idx[LIST_1] < 0 + ? dec_picture->mv_info[mb[2].pos_y][mb[2].pos_x].ref_idx[LIST_1] + : dec_picture->mv_info[mb[2].pos_y][mb[2].pos_x].ref_idx[LIST_1] * 2: -1); + } + else + { + l0_rFrameL = (char) (mb[0].available + ? p_Vid->mb_data[mb[0].mb_addr].mb_field || dec_picture->mv_info[mb[0].pos_y][mb[0].pos_x].ref_idx[LIST_0] < 0 + ? dec_picture->mv_info[mb[0].pos_y][mb[0].pos_x].ref_idx[LIST_0] >> 1 + : dec_picture->mv_info[mb[0].pos_y][mb[0].pos_x].ref_idx[LIST_0]: -1); + + l0_rFrameU = (char) (mb[1].available + ? p_Vid->mb_data[mb[1].mb_addr].mb_field || dec_picture->mv_info[mb[1].pos_y][mb[1].pos_x].ref_idx[LIST_0] < 0 + ? dec_picture->mv_info[mb[1].pos_y][mb[1].pos_x].ref_idx[LIST_0] >> 1 + : dec_picture->mv_info[mb[1].pos_y][mb[1].pos_x].ref_idx[LIST_0] : -1); + + l0_rFrameUR = (char) (mb[2].available + ? p_Vid->mb_data[mb[2].mb_addr].mb_field || dec_picture->mv_info[mb[2].pos_y][mb[2].pos_x].ref_idx[LIST_0] < 0 + ? dec_picture->mv_info[mb[2].pos_y][mb[2].pos_x].ref_idx[LIST_0] >> 1 + : dec_picture->mv_info[mb[2].pos_y][mb[2].pos_x].ref_idx[LIST_0] : -1); + + l1_rFrameL = (char) (mb[0].available + ? p_Vid->mb_data[mb[0].mb_addr].mb_field || dec_picture->mv_info[mb[0].pos_y][mb[0].pos_x].ref_idx[LIST_1] < 0 + ? dec_picture->mv_info[mb[0].pos_y][mb[0].pos_x].ref_idx[LIST_1] >> 1 + : dec_picture->mv_info[mb[0].pos_y][mb[0].pos_x].ref_idx[LIST_1] : -1); + + l1_rFrameU = (char) (mb[1].available + ? p_Vid->mb_data[mb[1].mb_addr].mb_field || dec_picture->mv_info[mb[1].pos_y][mb[1].pos_x].ref_idx[LIST_1] < 0 + ? dec_picture->mv_info[mb[1].pos_y][mb[1].pos_x].ref_idx[LIST_1] >> 1 + : dec_picture->mv_info[mb[1].pos_y][mb[1].pos_x].ref_idx[LIST_1] : -1); + + l1_rFrameUR = (char) (mb[2].available + ? p_Vid->mb_data[mb[2].mb_addr].mb_field || dec_picture->mv_info[mb[2].pos_y][mb[2].pos_x].ref_idx[LIST_1] < 0 + ? dec_picture->mv_info[mb[2].pos_y][mb[2].pos_x].ref_idx[LIST_1] >> 1 + : dec_picture->mv_info[mb[2].pos_y][mb[2].pos_x].ref_idx[LIST_1] : -1); + } + } + + *l0_rFrame = (char) ((l0_rFrameL >= 0 && l0_rFrameU >= 0) ? imin(l0_rFrameL,l0_rFrameU) : imax(l0_rFrameL,l0_rFrameU)); + *l0_rFrame = (char) ((*l0_rFrame >= 0 && l0_rFrameUR >= 0) ? imin(*l0_rFrame,l0_rFrameUR): imax(*l0_rFrame,l0_rFrameUR)); + + *l1_rFrame = (char) ((l1_rFrameL >= 0 && l1_rFrameU >= 0) ? imin(l1_rFrameL,l1_rFrameU) : imax(l1_rFrameL,l1_rFrameU)); + *l1_rFrame = (char) ((*l1_rFrame >= 0 && l1_rFrameUR >= 0) ? imin(*l1_rFrame,l1_rFrameUR): imax(*l1_rFrame,l1_rFrameUR)); + + if (*l0_rFrame >=0) + currMB->GetMVPredictor (currMB, mb, pmvl0, *l0_rFrame, dec_picture->mv_info, LIST_0, 0, 0, 16, 16); + + if (*l1_rFrame >=0) + currMB->GetMVPredictor (currMB, mb, pmvl1, *l1_rFrame, dec_picture->mv_info, LIST_1, 0, 0, 16, 16); +} + + +static void check_motion_vector_range(VideoParameters *p_Vid, const MotionVector *mv) +{ + if (mv->mv_x > 8191 || mv->mv_x < -8192) + { + fprintf(stdout,"WARNING! Horizontal motion vector %d is out of allowed range {-8192, 8191} in picture %d, macroblock %d\n", mv->mv_x, p_Vid->number, p_Vid->current_mb_nr); + //error("invalid stream: too big horizontal motion vector", 500); + } + + if (mv->mv_y > (p_Vid->max_mb_vmv_r - 1) || mv->mv_y < (-p_Vid->max_mb_vmv_r)) + { + fprintf(stdout,"WARNING! Vertical motion vector %d is out of allowed range {%d, %d} in picture %d, macroblock %d\n", mv->mv_y, (-p_Vid->max_mb_vmv_r), (p_Vid->max_mb_vmv_r - 1), p_Vid->number, p_Vid->current_mb_nr); + //error("invalid stream: too big vertical motion vector", 500); + } +} + +#endif + +void perform_mc(Macroblock* currMB, int decoder, ColorPlane pl, StorablePicture *dec_picture, int pred_dir, int l0_mode, int l1_mode, + PicMotionParams **mv_info, int i, int j, int block_size_x, int block_size_y, short bipred_me) +{ + imgpel tmp_block_l0[MB_BLOCK_SIZE][MB_BLOCK_SIZE]; //!< l0 prediction + imgpel tmp_block_l1[MB_BLOCK_SIZE][MB_BLOCK_SIZE]; //!< l1 prediction + + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + + int vec1_x=0, vec1_y=0; + int vec2_x=0, vec2_y=0; + //int vec1_y_cr = 0, vec2_y_cr = 0; + int alpha_l0, alpha_l1, wp_off; + int max_imgpel_value = p_Vid->max_pel_value_comp[pl]; + int apply_weights = ( (p_Vid->active_pps->weighted_pred_flag && (currSlice->slice_type== P_SLICE || currSlice->slice_type == SP_SLICE)) || + (p_Vid->active_pps->weighted_bipred_idc && (currSlice->slice_type== B_SLICE))); + static const int mv_mul = 16; // 4 * 4 + + int i4 = currMB->block_x + i; + int j4 = currMB->block_y + j; + int ioff = (i << 2); + int joff = (j << 2); + + assert (pred_dir<=2); + + if (pred_dir != 2) + { + //===== Single List Prediction ===== + short ref_idx = mv_info[j4][i4].ref_idx[pred_dir]; + short ref_idx_wp = ref_idx; + MotionVector **mv_array = currSlice->all_mv[pred_dir][ref_idx][l0_mode]; + StorablePicture **list = currSlice->listX[currMB->list_offset + pred_dir]; + + vec1_x = i4 * mv_mul + mv_array[j][i].mv_x; + vec1_y = j4 * mv_mul + mv_array[j][i].mv_y; + + get_block_luma (currMB, decoder, pl, dec_picture, list[ref_idx], vec1_x, vec1_y, block_size_x, block_size_y, tmp_block_l0); + + if (apply_weights) + { + if (currMB->mb_field) + { + ref_idx_wp >>=1; + } + alpha_l0 = currSlice->wp_weight[pred_dir][ref_idx_wp][0]; + wp_off = currSlice->wp_offset[pred_dir][ref_idx_wp][0]; + + weighted_mc_prediction(&p_Vid->p_decs->dec_mb_pred[decoder][joff], block_size_y, block_size_x, ioff, tmp_block_l0, alpha_l0, wp_off, currSlice->luma_log_weight_denom, max_imgpel_value); + } + else + { + mc_prediction(&p_Vid->p_decs->dec_mb_pred[decoder][joff], block_size_y, block_size_x, ioff, tmp_block_l0); + } + +#if 0 + if ((dec_picture->chroma_format_idc != YUV400) && (dec_picture->chroma_format_idc != YUV444) ) + { + int uv; + + int ioff_cr = (p_Vid->mb_cr_size_x == MB_BLOCK_SIZE) ? ioff : ioff >> 1; + int joff_cr = (p_Vid->mb_cr_size_y == MB_BLOCK_SIZE) ? joff : joff >> 1; + int block_size_x_cr = p_Vid->mb_cr_size_x == MB_BLOCK_SIZE ? block_size_x : block_size_x >> 1; + int block_size_y_cr = p_Vid->mb_cr_size_y == MB_BLOCK_SIZE ? block_size_y : block_size_y >> 1; + + vec1_y_cr = vec1_y + ((active_sps->chroma_format_idc == 1)? list[ref_idx]->chroma_vector_adjustment : 0); + + for(uv=0;uv<2;uv++) + { + get_block_chroma (currMB, uv, list[ref_idx], vec1_x, vec1_y_cr, block_size_x_cr, block_size_y_cr, tmp_block_l0); + + if (p_Vid->apply_weights) + { + alpha_l0 = p_Vid->wp_weight[pred_dir][ref_idx_wp][uv + 1]; + wp_offset = p_Vid->wp_offset[pred_dir][ref_idx_wp][uv + 1]; + + weighted_mc_prediction(&currSlice->mb_pred[uv + 1][joff_cr], block_size_y_cr, block_size_x_cr, ioff_cr, tmp_block_l0, alpha_l0, wp_offset, p_Vid->chroma_log2_weight_denom, p_Vid->max_pel_value_comp[uv + 1]); + } + else + { + mc_prediction(&currSlice->mb_pred[uv + 1][joff_cr], block_size_y_cr, block_size_x_cr, ioff_cr, tmp_block_l0); + } + } + } +#endif + } + else + { + //===== BI-PREDICTION ===== + short l0_ref = mv_info[j4][i4].ref_idx[LIST_0]; + short l1_ref = mv_info[j4][i4].ref_idx[LIST_1]; + + MotionVector **l0_mv_array = (bipred_me ? currSlice->bipred_mv[bipred_me-1][LIST_0][l0_ref][l0_mode]:currSlice->all_mv[LIST_0][l0_ref][l0_mode]); + MotionVector **l1_mv_array = (bipred_me ? currSlice->bipred_mv[bipred_me-1][LIST_1][l1_ref][l1_mode]:currSlice->all_mv[LIST_1][l1_ref][l1_mode]); + + vec1_x = i4 * mv_mul + l0_mv_array[j][i].mv_x; + vec2_x = i4 * mv_mul + l1_mv_array[j][i].mv_x; + + vec1_y = j4 * mv_mul + l0_mv_array[j][i].mv_y; + vec2_y = j4 * mv_mul + l1_mv_array[j][i].mv_y; + + get_block_luma (currMB, decoder, pl, dec_picture, currSlice->listX[LIST_0 + currMB->list_offset][l0_ref], vec1_x, vec1_y, block_size_x, block_size_y, tmp_block_l0); + get_block_luma (currMB, decoder, pl, dec_picture, currSlice->listX[LIST_1 + currMB->list_offset][l1_ref], vec2_x, vec2_y, block_size_x, block_size_y, tmp_block_l1); + + if(apply_weights) + { + int wt_list_offset = (p_Vid->active_pps->weighted_bipred_idc==2)? currMB->list_offset : 0; + + // This code existed in the original. Seems pointless but copying it here for reference and in case temporal direct breaks. + // if (mv_mode==0 && p_Vid->direct_spatial_mv_pred_flag==0 ) l1_ref_idx=0; + if (currMB->mb_field) + { + l0_ref >>=1; + l1_ref >>=1; + } + + alpha_l0 = currSlice->wbp_weight[LIST_0 + wt_list_offset][l0_ref][l1_ref][0]; + alpha_l1 = currSlice->wbp_weight[LIST_1 + wt_list_offset][l0_ref][l1_ref][0]; + wp_off = ((currSlice->wp_offset [LIST_0 + wt_list_offset][l0_ref][0] + currSlice->wp_offset[LIST_1 + wt_list_offset][l1_ref][0] + 1) >>1); + + weighted_bi_prediction(&p_Vid->p_decs->dec_mb_pred[decoder][joff], tmp_block_l0, tmp_block_l1, block_size_y, block_size_x, ioff, alpha_l0, alpha_l1, wp_off, (currSlice->luma_log_weight_denom + 1), max_imgpel_value); + } + else + { + bi_prediction(&p_Vid->p_decs->dec_mb_pred[decoder][joff], tmp_block_l0, tmp_block_l1, block_size_y, block_size_x, ioff); + } +#if 0 + if ((dec_picture->chroma_format_idc != YUV400) && (dec_picture->chroma_format_idc != YUV444) ) + { + int uv; + + int ioff_cr = p_Vid->mb_cr_size_x == MB_BLOCK_SIZE ? ioff : ioff >> 1; + int joff_cr = p_Vid->mb_cr_size_y == MB_BLOCK_SIZE ? joff : joff >> 1; + int block_size_x_cr = p_Vid->mb_cr_size_x == MB_BLOCK_SIZE ? block_size_x : block_size_x >> 1; + int block_size_y_cr = p_Vid->mb_cr_size_y == MB_BLOCK_SIZE ? block_size_y : block_size_y >> 1; + + vec1_y_cr = vec1_y + ((active_sps->chroma_format_idc == 1)? currSlice->listX[LIST_0 + list_offset][l0_refframe]->chroma_vector_adjustment : 0); + vec2_y_cr = vec2_y + ((active_sps->chroma_format_idc == 1)? currSlice->listX[LIST_1 + list_offset][l1_refframe]->chroma_vector_adjustment : 0); + + for(uv=0;uv<2;uv++) + { + get_block_chroma (currMB, uv, currSlice->listX[LIST_0 + list_offset][l0_refframe], vec1_x, vec1_y_cr, block_size_x_cr, block_size_y_cr, tmp_block_l0); + get_block_chroma (currMB, uv, currSlice->listX[LIST_1 + list_offset][l1_refframe], vec2_x, vec2_y_cr, block_size_x_cr, block_size_y_cr, tmp_block_l1); + + if(p_Vid->apply_weights) + { + int wt_list_offset = (active_pps->weighted_bipred_idc==2)? list_offset : 0; + + alpha_l0 = p_Vid->wbp_weight[LIST_0 + wt_list_offset][l0_ref_idx][l1_ref_idx][uv + 1]; + alpha_l1 = p_Vid->wbp_weight[LIST_1 + wt_list_offset][l0_ref_idx][l1_ref_idx][uv + 1]; + wp_offset = ((p_Vid->wp_offset [LIST_0 + wt_list_offset][l0_ref_idx][uv + 1] + p_Vid->wp_offset[LIST_1 + wt_list_offset][l1_ref_idx][uv + 1] + 1) >>1); + + weighted_bi_prediction(&currSlice->mb_pred[uv+1][joff_cr], tmp_block_l0, tmp_block_l1, block_size_y_cr, block_size_x_cr, ioff_cr, alpha_l0, alpha_l1, wp_offset, (p_Vid->chroma_log2_weight_denom + 1), p_Vid->max_pel_value_comp[uv + 1]); + } + else + { + bi_prediction(&p_Vid->currentSlice->mb_pred[uv + 1][joff_cr], tmp_block_l0, tmp_block_l1, block_size_y_cr, block_size_x_cr, ioff_cr); + } + } + } +#endif + } +} + +void perform_mc_concealment(Macroblock* currMB, int decoder, ColorPlane pl, StorablePicture *dec_picture, int pred_dir, int l0_mode, int l1_mode, + PicMotionParams **mv_info, int i, int j, int block_size_x, int block_size_y) +{ + imgpel tmp_block_l0[MB_BLOCK_SIZE][MB_BLOCK_SIZE]; //!< l0 prediction + imgpel tmp_block_l1[MB_BLOCK_SIZE][MB_BLOCK_SIZE]; //!< l1 prediction + + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + + int vec1_x=0, vec1_y=0; + int vec2_x=0, vec2_y=0; + //int vec1_y_cr = 0, vec2_y_cr = 0; + int alpha_l0, alpha_l1, wp_off; + int max_imgpel_value = p_Vid->max_pel_value_comp[pl]; + int apply_weights = ( (p_Vid->active_pps->weighted_pred_flag && (currSlice->slice_type == P_SLICE || currSlice->slice_type == SP_SLICE)) || + (p_Vid->active_pps->weighted_bipred_idc && (currSlice->slice_type== B_SLICE))); + static const int mv_mul = 16; // 4 * 4 + + int i4 = currMB->block_x + i; + int j4 = currMB->block_y + j; + int ioff = (i << 2); + int joff = (j << 2); + + assert (pred_dir<=2); + + if (pred_dir != 2) + { + //===== Single List Prediction ===== + short ref_idx = mv_info[j4][i4].ref_idx[pred_dir]; + short ref_idx_wp = ref_idx; + PicMotionParams **motion = dec_picture->mv_info; + //MotionVector **mv_array = dec_picture->motion.mv[pred_dir]; + StorablePicture **list = currSlice->listX[currMB->list_offset + pred_dir]; + + vec1_x = i4 * mv_mul + motion[j4][i4].mv[pred_dir].mv_x; + //vec1_y = (currMB->block_y_aff + j) * mv_mul + mv_array[j4][i4][1]; + vec1_y = j4 * mv_mul + motion[j4][i4].mv[pred_dir].mv_y; + + get_block_luma (currMB, decoder, pl, dec_picture, list[ref_idx], vec1_x, vec1_y, block_size_x, block_size_y, tmp_block_l0); + + if (apply_weights) + { + if (currMB->mb_field) + { + ref_idx_wp >>=1; + } + alpha_l0 = p_Vid->currentSlice->wp_weight[pred_dir][ref_idx_wp][0]; + wp_off = p_Vid->currentSlice->wp_offset[pred_dir][ref_idx_wp][0]; + + weighted_mc_prediction(&p_Vid->p_decs->dec_mb_pred[decoder][joff], block_size_y, block_size_x, ioff, tmp_block_l0, alpha_l0, wp_off, p_Vid->currentSlice->luma_log_weight_denom, max_imgpel_value); + } + else + { + mc_prediction(&p_Vid->p_decs->dec_mb_pred[decoder][joff], block_size_y, block_size_x, ioff, tmp_block_l0); + } + } + else + { + //===== BI-PREDICTION ===== + short l0_ref = mv_info[j4][i4].ref_idx[LIST_0]; + short l1_ref = mv_info[j4][i4].ref_idx[LIST_1]; + + vec1_x = i4 * mv_mul + dec_picture->mv_info[j4][i4].mv[LIST_0].mv_x; + vec2_x = i4 * mv_mul + dec_picture->mv_info[j4][i4].mv[LIST_1].mv_x; + + vec1_y = j4 * mv_mul + dec_picture->mv_info[j4][i4].mv[LIST_0].mv_y; + vec2_y = j4 * mv_mul + dec_picture->mv_info[j4][i4].mv[LIST_1].mv_y; + + get_block_luma (currMB, decoder, pl, dec_picture, currSlice->listX[LIST_0 + currMB->list_offset][l0_ref], vec1_x, vec1_y, block_size_x, block_size_y, tmp_block_l0); + get_block_luma (currMB, decoder, pl, dec_picture, currSlice->listX[LIST_1 + currMB->list_offset][l1_ref], vec2_x, vec2_y, block_size_x, block_size_y, tmp_block_l1); + + if(apply_weights) + { + int wt_list_offset = (p_Vid->active_pps->weighted_bipred_idc==2)? currMB->list_offset : 0; + + // This code existed in the original. Seems pointless but copying it here for reference and in case temporal direct breaks. + // if (mv_mode==0 && p_Vid->direct_spatial_mv_pred_flag==0 ) l1_ref_idx=0; + if (currMB->mb_field) + { + l0_ref >>=1; + l1_ref >>=1; + } + + alpha_l0 = p_Vid->currentSlice->wbp_weight[LIST_0 + wt_list_offset][l0_ref][l1_ref][0]; + alpha_l1 = p_Vid->currentSlice->wbp_weight[LIST_1 + wt_list_offset][l0_ref][l1_ref][0]; + wp_off = ((p_Vid->currentSlice->wp_offset [LIST_0 + wt_list_offset][l0_ref][0] + p_Vid->currentSlice->wp_offset[LIST_1 + wt_list_offset][l1_ref][0] + 1) >>1); + + weighted_bi_prediction(&p_Vid->p_decs->dec_mb_pred[decoder][joff], tmp_block_l0, tmp_block_l1, block_size_y, block_size_x, ioff, alpha_l0, alpha_l1, wp_off, (p_Vid->currentSlice->luma_log_weight_denom + 1), max_imgpel_value); + } + else + { + bi_prediction(&p_Vid->p_decs->dec_mb_pred[decoder][joff], tmp_block_l0, tmp_block_l1, block_size_y, block_size_x, ioff); + } + } +} diff --git a/lencod/src/loopFilter.c b/lencod/src/loopFilter.c new file mode 100644 index 0000000..2f5cefe --- /dev/null +++ b/lencod/src/loopFilter.c @@ -0,0 +1,294 @@ + +/*! + ************************************************************************************* + * \file loopFilter.c + * + * \brief + * Filter to reduce blocking artifacts on a macroblock level. + * The filter strength is QP dependent. + * + * \author + * Contributors: + * - Peter List Peter.List@t-systems.de: Original code (13-Aug-2001) + * - Jani Lainema Jani.Lainema@nokia.com: Some bug fixing, removal of recursiveness (16-Aug-2001) + * - Peter List Peter.List@t-systems.de: inplace filtering and various simplifications (10-Jan-2002) + * - Anthony Joch anthony@ubvideo.com: Simplified switching between filters and + * non-recursive default filter. (08-Jul-2002) + * - Cristina Gomila cristina.gomila@thomson.net: Simplification of the chroma deblocking + * from JVT-E089 (21-Nov-2002) + * - Alexis Michael Tourapis atour@dolby.com: Speed/Architecture improvements (08-Feb-2007) + ************************************************************************************* + */ + +#include "global.h" +#include "image.h" +#include "mb_access.h" +#include "loop_filter.h" + +extern void set_loop_filter_functions_mbaff (VideoParameters *p_Vid); +extern void set_loop_filter_functions_normal(VideoParameters *p_Vid); + +static void DeblockMb(VideoParameters *p_Vid, imgpel **imgY, imgpel ***imgUV, int MbQAddr); + +static void init_Deblock(VideoParameters *p_Vid) +{ + unsigned int i; + if (p_Vid->mb_aff_frame_flag == 1) + { + set_loop_filter_functions_mbaff(p_Vid); + } + else + { + set_loop_filter_functions_normal(p_Vid); + } + + for (i=0; i < p_Vid->PicSizeInMbs; i++) + { + if (p_Vid->mb_data[i].mb_type==IPCM) + { + p_Vid->mb_data[i].qp = 0; + p_Vid->mb_data[i].qpc[0] = 0; + p_Vid->mb_data[i].qpc[1] = 0; + } + } +} + +#if (JM_PARALLEL_DEBLOCK == 0) +/*! + ***************************************************************************************** + * \brief + * Filter all macroblocks in order of increasing macroblock address. + ***************************************************************************************** + */ +void DeblockFrame(VideoParameters *p_Vid, imgpel **imgY, imgpel ***imgUV) +{ + unsigned int i; + init_Deblock(p_Vid); + for (i=0; i < p_Vid->PicSizeInMbs; i++) + { + DeblockMb( p_Vid, imgY, imgUV, i ) ; + } +} +#else +static void DeblockParallel(VideoParameters *p_Vid, imgpel **imgY, imgpel ***imgUV, unsigned int column, int block, int n_last) +{ + int i, j; + + for (j = 0; j < GROUP_SIZE; j++) + { + i = block++ * (p_Vid->PicWidthInMbs - 2) + column; + + DeblockMb( p_Vid, imgY, imgUV, i ) ; + if (block == n_last) break; + } +} + +/*! + ***************************************************************************************** + * \brief + * Filter all macroblocks in a diagonal manner to enable parallelization. + ***************************************************************************************** + */ +void DeblockFrame(VideoParameters *p_Vid, imgpel **imgY, imgpel ***imgUV) +{ + int iheightMBs =(p_Vid->PicSizeInMbs/p_Vid->PicWidthInMbs); + unsigned int i, k = p_Vid->PicWidthInMbs + 2 * (iheightMBs - 1); + init_Deblock(p_Vid); + + for (i = 0; i < k; i++) + { + int nn; + int n_last = imin(iheightMBs, (i >> 1) + 1); + int n_start = (i < p_Vid->PicWidthInMbs) ? 0 : ((i - p_Vid->PicWidthInMbs) >> 1) + 1; + +#if defined(OPENMP) +#pragma omp parallel for +#endif + for (nn = n_start; nn < n_last; nn += GROUP_SIZE) + DeblockParallel(p_Vid, imgY, imgUV, i, nn, n_last); + } +} +#endif + +/*! + ***************************************************************************************** + * \brief + * Deblocking filter for one macroblock. + ***************************************************************************************** + */ + +static void DeblockMb(VideoParameters *p_Vid, imgpel **imgY, imgpel ***imgUV, int MbQAddr) +{ + int edge; + byte Strength[16]; + + int64 *p_Strength64 = (int64 *) Strength; + short mb_x, mb_y; + + int filterNon8x8LumaEdgesFlag[4] = {1,1,1,1}; + int filterLeftMbEdgeFlag; + int filterTopMbEdgeFlag; + int edge_cr; + Macroblock *MbQ = &(p_Vid->mb_data[MbQAddr]) ; // current Mb + Slice *currSlice = MbQ->p_Slice; + int mvlimit = (p_Vid->structure!=FRAME) || (p_Vid->mb_aff_frame_flag && MbQ->mb_field) ? 2 : 4; + seq_parameter_set_rbsp_t *active_sps = p_Vid->active_sps; + p_Vid->mixedModeEdgeFlag = 0; + + // return, if filter is disabled + if (MbQ->DFDisableIdc == 1) + { + MbQ->DeblockCall = 0; + return; + } + + MbQ->DeblockCall = 1; + get_mb_pos (p_Vid, MbQAddr, p_Vid->mb_size[IS_LUMA], &mb_x, &mb_y); + + if (MbQ->mb_type == I8MB) + assert(MbQ->luma_transform_size_8x8_flag); + + filterNon8x8LumaEdgesFlag[1] = + filterNon8x8LumaEdgesFlag[3] = !(MbQ->luma_transform_size_8x8_flag); + + filterLeftMbEdgeFlag = (mb_x != 0); + filterTopMbEdgeFlag = (mb_y != 0); + + if (p_Vid->mb_aff_frame_flag && mb_y == MB_BLOCK_SIZE && MbQ->mb_field) + filterTopMbEdgeFlag = 0; + + if (MbQ->DFDisableIdc==2) + { + // don't filter at slice boundaries + filterLeftMbEdgeFlag = MbQ->mbAvailA; + // if this the bottom of a frame macroblock pair then always filter the top edge + filterTopMbEdgeFlag = (p_Vid->mb_aff_frame_flag && !MbQ->mb_field && (MbQAddr & 0x01)) ? 1 : MbQ->mbAvailB; + } + + CheckAvailabilityOfNeighbors(MbQ); + + // Vertical deblocking + for (edge = 0; edge < 4 ; ++edge ) + { + // If cbp == 0 then deblocking for some macroblock types could be skipped + if (MbQ->cbp == 0) + { + if (filterNon8x8LumaEdgesFlag[edge] == 0 && active_sps->chroma_format_idc!=YUV444) + continue; + else if (edge > 0 && (currSlice->slice_type == P_SLICE || currSlice->slice_type == B_SLICE)) + { + if ((MbQ->mb_type == 0 && currSlice->slice_type == P_SLICE) || (MbQ->mb_type == 1) || (MbQ->mb_type == 2)) + continue; + else if ((edge & 0x01) && ((MbQ->mb_type == 3) || ((edge & 0x01) && MbQ->mb_type == 0 && currSlice->slice_type == B_SLICE && active_sps->direct_8x8_inference_flag))) + continue; + } + } + + if( edge || filterLeftMbEdgeFlag ) + { + // Strength for 4 blks in 1 stripe + p_Vid->GetStrengthVer(Strength, MbQ, edge << 2, mvlimit); // Strength for 4 blks in 1 stripe + + if ((p_Strength64[0]) || (p_Strength64[1])) // only if one of the 16 Strength bytes is != 0 + { + if (filterNon8x8LumaEdgesFlag[edge]) + { + p_Vid->EdgeLoopLumaVer( PLANE_Y, imgY, Strength, MbQ, edge << 2) ; + if (p_Vid->P444_joined) + { + p_Vid->EdgeLoopLumaVer(PLANE_U, imgUV[0], Strength, MbQ, edge << 2); + p_Vid->EdgeLoopLumaVer(PLANE_V, imgUV[1], Strength, MbQ, edge << 2); + } + } + if(p_Vid->yuv_format==YUV420 || p_Vid->yuv_format==YUV422 ) + { + edge_cr = chroma_edge[0][edge][p_Vid->yuv_format]; + if( (imgUV != NULL) && (edge_cr >= 0)) + { + p_Vid->EdgeLoopChromaVer( imgUV[0], Strength, MbQ, edge_cr, 0); + p_Vid->EdgeLoopChromaVer( imgUV[1], Strength, MbQ, edge_cr, 1); + } + } + } + } + }//end edge + + // horizontal deblocking + for( edge = 0; edge < 4 ; ++edge ) + { + // If cbp == 0 then deblocking for some macroblock types could be skipped + if (MbQ->cbp == 0) + { + if (filterNon8x8LumaEdgesFlag[edge] == 0 && active_sps->chroma_format_idc==YUV420) + continue; + else if (edge > 0 && (currSlice->slice_type == P_SLICE || currSlice->slice_type == B_SLICE)) + { + if (((MbQ->mb_type == PSKIP && currSlice->slice_type == P_SLICE) || (MbQ->mb_type == P16x16) || (MbQ->mb_type == P8x16))) + continue; + else if ((edge & 0x01) && (( MbQ->mb_type == P16x8) || (currSlice->slice_type == B_SLICE && MbQ->mb_type == BSKIP_DIRECT && active_sps->direct_8x8_inference_flag))) + continue; + } + } + + if( edge || filterTopMbEdgeFlag ) + { + // Strength for 4 blks in 1 stripe + p_Vid->GetStrengthHor(Strength, MbQ, edge << 2, mvlimit); + + if ((p_Strength64[0]) || (p_Strength64[1])) // only if one of the 16 Strength bytes is != 0 + { + if (filterNon8x8LumaEdgesFlag[edge]) + { + p_Vid->EdgeLoopLumaHor( PLANE_Y, imgY, Strength, MbQ, edge << 2, p_Vid->width_padded) ; + if (p_Vid->P444_joined) + { + p_Vid->EdgeLoopLumaHor(PLANE_U, imgUV[0], Strength, MbQ, edge << 2, p_Vid->width_padded); + p_Vid->EdgeLoopLumaHor(PLANE_V, imgUV[1], Strength, MbQ, edge << 2, p_Vid->width_padded); + } + } + if(p_Vid->yuv_format==YUV420 || p_Vid->yuv_format==YUV422 ) + { + edge_cr = chroma_edge[1][edge][p_Vid->yuv_format]; + if( (imgUV != NULL) && (edge_cr >= 0)) + { + p_Vid->EdgeLoopChromaHor( imgUV[0], Strength, MbQ, edge_cr, p_Vid->width_cr + p_Vid->pad_size_uv_x * 2, 0); + p_Vid->EdgeLoopChromaHor( imgUV[1], Strength, MbQ, edge_cr, p_Vid->width_cr + p_Vid->pad_size_uv_x * 2, 1); + } + } + } + + if (!edge && !MbQ->mb_field && p_Vid->mixedModeEdgeFlag) + { + // this is the extra horizontal edge between a frame macroblock pair and a field above it + MbQ->DeblockCall = 2; + p_Vid->GetStrengthHor(Strength, MbQ, MB_BLOCK_SIZE, mvlimit); // Strength for 4 blks in 1 stripe + //if( *((int*)Strength) ) // only if one of the 4 Strength bytes is != 0 + { + if (filterNon8x8LumaEdgesFlag[edge]) + { + p_Vid->EdgeLoopLumaHor( PLANE_Y, imgY, Strength, MbQ, MB_BLOCK_SIZE, p_Vid->width_padded) ; + if (p_Vid->P444_joined) + { + p_Vid->EdgeLoopLumaHor(PLANE_U, imgUV[0], Strength, MbQ, MB_BLOCK_SIZE, p_Vid->width_padded) ; + p_Vid->EdgeLoopLumaHor(PLANE_V, imgUV[1], Strength, MbQ, MB_BLOCK_SIZE, p_Vid->width_padded) ; + } + } + if( p_Vid->yuv_format == YUV420 || p_Vid->yuv_format==YUV422 ) + { + edge_cr = chroma_edge[1][edge][p_Vid->yuv_format]; + if( (imgUV != NULL) && (edge_cr >= 0)) + { + p_Vid->EdgeLoopChromaHor( imgUV[0], Strength, MbQ, MB_BLOCK_SIZE, p_Vid->width_cr+p_Vid->pad_size_uv_x*2, 0) ; + p_Vid->EdgeLoopChromaHor( imgUV[1], Strength, MbQ, MB_BLOCK_SIZE, p_Vid->width_cr+p_Vid->pad_size_uv_x*2, 1) ; + } + } + } + MbQ->DeblockCall = 1; + } + } + }//end edge + + MbQ->DeblockCall = 0; +} + + diff --git a/lencod/src/loop_filter_mbaff.c b/lencod/src/loop_filter_mbaff.c new file mode 100644 index 0000000..77d312a --- /dev/null +++ b/lencod/src/loop_filter_mbaff.c @@ -0,0 +1,709 @@ + +/*! + ************************************************************************************* + * \file loop_filter_mbaff.c + * + * \brief + * Loop Filter to reduce blocking artifacts on a macroblock level (MBAFF). + * The filter strength is QP dependent. + * + * \author + * Contributors: + * - Peter List Peter.List@t-systems.de: Original code (13-Aug-2001) + * - Jani Lainema Jani.Lainema@nokia.com: Some bug fixing, removal of recursiveness (16-Aug-2001) + * - Peter List Peter.List@t-systems.de: inplace filtering and various simplifications (10-Jan-2002) + * - Anthony Joch anthony@ubvideo.com: Simplified switching between filters and + * non-recursive default filter. (08-Jul-2002) + * - Cristina Gomila cristina.gomila@thomson.net: Simplification of the chroma deblocking + * from JVT-E089 (21-Nov-2002) + * - Alexis Michael Tourapis atour@dolby.com: Speed/Architecture improvements (08-Feb-2007) + ************************************************************************************* + */ + +#include "global.h" +#include "image.h" +#include "mb_access.h" +#include "loop_filter.h" + + + +static void get_strength_ver_MBAff (byte Strength[MB_BLOCK_SIZE], Macroblock *MbQ, int edge, int mvlimit); +static void get_strength_hor_MBAff (byte Strength[MB_BLOCK_SIZE], Macroblock *MbQ, int edge, int mvlimit); +static void edge_loop_luma_ver_MBAff (ColorPlane pl, imgpel** Img, byte Strength[MB_BLOCK_SIZE], Macroblock *MbQ, int edge); +static void edge_loop_luma_hor_MBAff (ColorPlane pl, imgpel** Img, byte Strength[MB_BLOCK_SIZE], Macroblock *MbQ, int edge, int width); +static void edge_loop_chroma_ver_MBAff (imgpel** Img, byte Strength[MB_BLOCK_SIZE], Macroblock *MbQ, int edge, int uv); +static void edge_loop_chroma_hor_MBAff (imgpel** Img, byte Strength[MB_BLOCK_SIZE], Macroblock *MbQ, int edge, int width, int uv); + + +void set_loop_filter_functions_mbaff(VideoParameters *p_Vid) +{ + p_Vid->GetStrengthVer = get_strength_ver_MBAff; + p_Vid->GetStrengthHor = get_strength_hor_MBAff; + p_Vid->EdgeLoopLumaVer = edge_loop_luma_ver_MBAff; + p_Vid->EdgeLoopLumaHor = edge_loop_luma_hor_MBAff; + p_Vid->EdgeLoopChromaVer = edge_loop_chroma_ver_MBAff; + p_Vid->EdgeLoopChromaHor = edge_loop_chroma_hor_MBAff; +} + +/*! + ********************************************************************************************* + * \brief + * returns a buffer of 16 Strength values for one stripe in a mb (for MBAFF) + ********************************************************************************************* + */ +static void get_strength_ver_MBAff(byte Strength[MB_BLOCK_SIZE], Macroblock *MbQ, int edge, int mvlimit) +{ + short blkP, blkQ, idx; + short blk_x, blk_x2, blk_y, blk_y2 ; + + int xQ, yQ; + short mb_x, mb_y; + + Macroblock *MbP; + + PixelPos pixP; + VideoParameters *p_Vid = MbQ->p_Vid; + PicMotionParams **mv_info = p_Vid->enc_picture->mv_info; + + for( idx = 0; idx < 16; ++idx ) + { + xQ = edge; + yQ = idx; + p_Vid->getNeighbour(MbQ, xQ - 1, yQ, p_Vid->mb_size[IS_LUMA], &pixP); + blkQ = (short) ((yQ & 0xFFFC) + (xQ >> 2)); + blkP = (short) ((pixP.y & 0xFFFC) + (pixP.x >> 2)); + + MbP = &(p_Vid->mb_data[pixP.mb_addr]); + p_Vid->mixedModeEdgeFlag = (byte) (MbQ->mb_field != MbP->mb_field); + + if (p_Vid->type==SP_SLICE || p_Vid->type==SI_SLICE) + { + Strength[idx] = (edge == 0) ? 4 : 3; + } + else + { + // Start with Strength=3. or Strength=4 for Mb-edge + Strength[idx] = (edge == 0) ? 4 : 3; + + if( !(MbP->mb_type==I4MB || MbP->mb_type==I16MB || MbP->mb_type==I8MB || MbP->mb_type==IPCM) + && !(MbQ->mb_type==I4MB || MbQ->mb_type==I16MB || MbQ->mb_type==I8MB || MbQ->mb_type==IPCM) ) + { + if( ((MbQ->cbp_blk & ((int64)1 << blkQ )) != 0) || ((MbP->cbp_blk & ((int64)1 << blkP)) != 0) ) + Strength[idx] = 2 ; + else + { + // if no coefs, but vector difference >= 1 set Strength=1 + // if this is a mixed mode edge then one set of reference pictures will be frame and the + // other will be field + if (p_Vid->mixedModeEdgeFlag) + { + (Strength[idx] = 1); + } + else + { + p_Vid->get_mb_block_pos (p_Vid->PicPos, MbQ->mbAddrX, &mb_x, &mb_y); + blk_y = (short) ((mb_y<<2) + (blkQ >> 2)); + blk_x = (short) ((mb_x<<2) + (blkQ & 3)); + blk_y2 = (short) (pixP.pos_y >> 2); + blk_x2 = (short) (pixP.pos_x >> 2); + { + PicMotionParams *mv_info_p = &mv_info[blk_y ][blk_x ]; + PicMotionParams *mv_info_q = &mv_info[blk_y2][blk_x2]; + + StorablePicturePtr ref_p0 = mv_info_p->ref_idx[LIST_0] == -1 ? NULL : mv_info_p->ref_pic[LIST_0]; + StorablePicturePtr ref_q0 = mv_info_q->ref_idx[LIST_0] == -1 ? NULL : mv_info_q->ref_pic[LIST_0]; + StorablePicturePtr ref_p1 = mv_info_p->ref_idx[LIST_1] == -1 ? NULL : mv_info_p->ref_pic[LIST_1]; + StorablePicturePtr ref_q1 = mv_info_q->ref_idx[LIST_1] == -1 ? NULL : mv_info_q->ref_pic[LIST_1]; + + if ( ((ref_p0==ref_q0) && (ref_p1==ref_q1)) || + ((ref_p0==ref_q1) && (ref_p1==ref_q0))) + { + Strength[idx]=0; + // L0 and L1 reference pictures of p0 are different; q0 as well + if (ref_p0 != ref_p1) + { + // compare MV for the same reference picture + if (ref_p0==ref_q0) + { + Strength[idx] = (byte) ( + compare_mvs(&mv_info_p->mv[LIST_0], &mv_info_q->mv[LIST_0], mvlimit) || + compare_mvs(&mv_info_p->mv[LIST_1], &mv_info_q->mv[LIST_1], mvlimit)); + } + else + { + Strength[idx] = (byte) ( + compare_mvs(&mv_info_p->mv[LIST_0], &mv_info_q->mv[LIST_1], mvlimit) || + compare_mvs(&mv_info_p->mv[LIST_1], &mv_info_q->mv[LIST_0], mvlimit)); + } + } + else + { // L0 and L1 reference pictures of p0 are the same; q0 as well + + Strength[idx] = (byte) (( + compare_mvs(&mv_info_p->mv[LIST_0], &mv_info_q->mv[LIST_0], mvlimit) || + compare_mvs(&mv_info_p->mv[LIST_1], &mv_info_q->mv[LIST_1], mvlimit)) + &&( + compare_mvs(&mv_info_p->mv[LIST_0], &mv_info_q->mv[LIST_1], mvlimit) || + compare_mvs(&mv_info_p->mv[LIST_1], &mv_info_q->mv[LIST_0], mvlimit))); + } + } + else + { + Strength[idx] = 1; + } + } + } + } + } + } + } +} + +/*! + ********************************************************************************************* + * \brief + * returns a buffer of 16 Strength values for one stripe in a mb (for MBAFF) + ********************************************************************************************* + */ +static void get_strength_hor_MBAff(byte Strength[MB_BLOCK_SIZE], Macroblock *MbQ, int edge, int mvlimit) +{ + short blkP, blkQ, idx; + short blk_x, blk_x2, blk_y, blk_y2 ; + + int xQ, yQ; + short mb_x, mb_y; + + Macroblock *MbP; + + PixelPos pixP; + VideoParameters *p_Vid = MbQ->p_Vid; + PicMotionParams **mv_info = p_Vid->enc_picture->mv_info; + + + for( idx = 0; idx < 16; ++idx ) + { + xQ = idx; + yQ = (edge < MB_BLOCK_SIZE ? edge : 1); + p_Vid->getNeighbour(MbQ, xQ, yQ - 1, p_Vid->mb_size[IS_LUMA], &pixP); + blkQ = (short) ((yQ & 0xFFFC) + (xQ >> 2)); + blkP = (short) ((pixP.y & 0xFFFC) + (pixP.x >> 2)); + + MbP = &(p_Vid->mb_data[pixP.mb_addr]); + p_Vid->mixedModeEdgeFlag = (byte) (MbQ->mb_field != MbP->mb_field); + + if (p_Vid->type==SP_SLICE || p_Vid->type==SI_SLICE) + { + Strength[idx] = (edge == 0 && (((!p_Vid->mb_aff_frame_flag && (p_Vid->structure==FRAME)) || + (p_Vid->mb_aff_frame_flag && !MbP->mb_field && !MbQ->mb_field)))) ? 4 : 3; + } + else + { + // Start with Strength=3. or Strength=4 for Mb-edge + Strength[idx] = (edge == 0 && (((!p_Vid->mb_aff_frame_flag && (p_Vid->structure==FRAME)) || + (p_Vid->mb_aff_frame_flag && !MbP->mb_field && !MbQ->mb_field)))) ? 4 : 3; + + if( !(MbP->mb_type==I4MB || MbP->mb_type==I16MB || MbP->mb_type==I8MB || MbP->mb_type==IPCM) + && !(MbQ->mb_type==I4MB || MbQ->mb_type==I16MB || MbQ->mb_type==I8MB || MbQ->mb_type==IPCM) ) + { + if( ((MbQ->cbp_blk & ((int64)1 << blkQ )) != 0) || ((MbP->cbp_blk & ((int64)1 << blkP)) != 0) ) + Strength[idx] = 2 ; + else + { + // if no coefs, but vector difference >= 1 set Strength=1 + // if this is a mixed mode edge then one set of reference pictures will be frame and the + // other will be field + if (p_Vid->mixedModeEdgeFlag) + { + (Strength[idx] = 1); + } + else + { + p_Vid->get_mb_block_pos (p_Vid->PicPos, MbQ->mbAddrX, &mb_x, &mb_y); + blk_y = (short) ((mb_y<<2) + (blkQ >> 2)); + blk_x = (short) ((mb_x<<2) + (blkQ & 3)); + blk_y2 = (short) (pixP.pos_y >> 2); + blk_x2 = (short) (pixP.pos_x >> 2); + { + PicMotionParams *mv_info_p = &mv_info[blk_y ][blk_x ]; + PicMotionParams *mv_info_q = &mv_info[blk_y2][blk_x2]; + + StorablePicturePtr ref_p0 = mv_info_p->ref_idx[LIST_0] == -1 ? NULL : mv_info_p->ref_pic[LIST_0]; + StorablePicturePtr ref_q0 = mv_info_q->ref_idx[LIST_0] == -1 ? NULL : mv_info_q->ref_pic[LIST_0]; + StorablePicturePtr ref_p1 = mv_info_p->ref_idx[LIST_1] == -1 ? NULL : mv_info_p->ref_pic[LIST_1]; + StorablePicturePtr ref_q1 = mv_info_q->ref_idx[LIST_1] == -1 ? NULL : mv_info_q->ref_pic[LIST_1]; + + if ( ((ref_p0==ref_q0) && (ref_p1==ref_q1)) || + ((ref_p0==ref_q1) && (ref_p1==ref_q0))) + { + Strength[idx]=0; + // L0 and L1 reference pictures of p0 are different; q0 as well + if (ref_p0 != ref_p1) + { + // compare MV for the same reference picture + if (ref_p0==ref_q0) + { + Strength[idx] = (byte) ( + compare_mvs(&mv_info_p->mv[LIST_0], &mv_info_q->mv[LIST_0], mvlimit) || + compare_mvs(&mv_info_p->mv[LIST_1], &mv_info_q->mv[LIST_1], mvlimit)); + } + else + { + Strength[idx] = (byte) ( + compare_mvs(&mv_info_p->mv[LIST_0], &mv_info_q->mv[LIST_1], mvlimit) || + compare_mvs(&mv_info_p->mv[LIST_1], &mv_info_q->mv[LIST_0], mvlimit)); + } + } + else + { // L0 and L1 reference pictures of p0 are the same; q0 as well + + Strength[idx] = (byte) (( + compare_mvs(&mv_info_p->mv[LIST_0], &mv_info_q->mv[LIST_0], mvlimit) || + compare_mvs(&mv_info_p->mv[LIST_1], &mv_info_q->mv[LIST_1], mvlimit)) + &&( + compare_mvs(&mv_info_p->mv[LIST_0], &mv_info_q->mv[LIST_1], mvlimit) || + compare_mvs(&mv_info_p->mv[LIST_1], &mv_info_q->mv[LIST_0], mvlimit))); + } + } + else + { + Strength[idx] = 1; + } + } + } + } + } + } + } +} + + + + +/*! + ***************************************************************************************** + * \brief + * Filters 16 pel block edge of Super MB Frame coded MBs + ***************************************************************************************** + */ +static void edge_loop_luma_ver_MBAff(ColorPlane pl, imgpel** Img, byte Strength[16], Macroblock *MbQ, int edge) +{ + int pel, ap = 0, aq = 0, Strng ; + int C0, tc0, dif; + imgpel L2 = 0, L1, L0, R0, R1, R2 = 0, L3, R3; + int RL0; + int Alpha = 0, Beta = 0 ; + const byte* ClipTab = NULL; + int small_gap; + int indexA, indexB; + + int QP; + int xQ, yQ; + + PixelPos pixP, pixQ; + + VideoParameters *p_Vid = MbQ->p_Vid; + int bitdepth_scale = pl? p_Vid->bitdepth_scale[IS_CHROMA] : p_Vid->bitdepth_scale[IS_LUMA]; + int max_imgpel_value = p_Vid->max_pel_value_comp[pl]; + + int AlphaC0Offset = MbQ->DFAlphaC0Offset; + int BetaOffset = MbQ->DFBetaOffset; + + Macroblock *MbP; + imgpel *SrcPtrP, *SrcPtrQ; + + for( pel = 0 ; pel < MB_BLOCK_SIZE ; ++pel ) + { + xQ = edge; + yQ = pel; + p_Vid->getNeighbour(MbQ, xQ - 1, yQ, p_Vid->mb_size[IS_LUMA], &pixP); + + if (pixP.available || (MbQ->DFDisableIdc== 0)) + { + if( (Strng = Strength[pel]) != 0) + { + p_Vid->getNeighbour(MbQ, xQ, yQ, p_Vid->mb_size[IS_LUMA], &pixQ); + + MbP = &(p_Vid->mb_data[pixP.mb_addr]); + + SrcPtrQ = &(Img[pixQ.pos_y][pixQ.pos_x]); + SrcPtrP = &(Img[pixP.pos_y][pixP.pos_x]); + + // Average QP of the two blocks + QP = pl? ((MbP->qpc[pl-1] + MbQ->qpc[pl-1] + 1) >> 1) : (MbP->qp + MbQ->qp + 1) >> 1; + + indexA = iClip3(0, MAX_QP, QP + AlphaC0Offset); + indexB = iClip3(0, MAX_QP, QP + BetaOffset); + + Alpha = ALPHA_TABLE[indexA] * bitdepth_scale; + Beta = BETA_TABLE [indexB] * bitdepth_scale; + ClipTab = CLIP_TAB[indexA]; + + L3 = SrcPtrP[-3]; + L2 = SrcPtrP[-2]; + L1 = SrcPtrP[-1]; + L0 = SrcPtrP[0] ; + R0 = SrcPtrQ[0] ; + R1 = SrcPtrQ[ 1]; + R2 = SrcPtrQ[ 2]; + R3 = SrcPtrQ[ 3]; + + if( iabs( R0 - L0 ) < Alpha ) + { + if ((iabs( R0 - R1) < Beta ) && (iabs(L0 - L1) < Beta )) + { + if(Strng == 4 ) // INTRA strong filtering + { + RL0 = L0 + R0; + small_gap = (iabs( R0 - L0 ) < ((Alpha >> 2) + 2)); + aq = ( iabs( R0 - R2) < Beta ) & small_gap; + ap = ( iabs( L0 - L2) < Beta ) & small_gap; + + if (ap) + { + SrcPtrP[-2 ] = (imgpel) ((((L3 + L2) << 1) + L2 + L1 + RL0 + 4) >> 3); + SrcPtrP[-1 ] = (imgpel) (( L2 + L1 + L0 + R0 + 2) >> 2); + SrcPtrP[ 0 ] = (imgpel) (( R1 + ((L1 + RL0) << 1) + L2 + 4) >> 3); + } + else + { + SrcPtrP[ 0 ] = (imgpel) (((L1 << 1) + L0 + R1 + 2) >> 2) ; + } + + if (aq) + { + SrcPtrQ[ 0 ] = (imgpel) (( L1 + ((R1 + RL0) << 1) + R2 + 4) >> 3); + SrcPtrQ[ 1 ] = (imgpel) (( R2 + R0 + R1 + L0 + 2) >> 2); + SrcPtrQ[ 2 ] = (imgpel) ((((R3 + R2) << 1) + R2 + R1 + RL0 + 4) >> 3); + } + else + { + SrcPtrQ[ 0 ] = (imgpel) (((R1 << 1) + R0 + L1 + 2) >> 2); + } + } + else // normal filtering + { + RL0 = (L0 + R0 + 1) >> 1; + aq = (iabs( R0 - R2) < Beta); + ap = (iabs( L0 - L2) < Beta); + + C0 = ClipTab[ Strng ] * bitdepth_scale; + tc0 = (C0 + ap + aq) ; + dif = iClip3( -tc0, tc0, (((R0 - L0) << 2) + (L1 - R1) + 4) >> 3) ; + + if( ap ) + *(SrcPtrP - 1) += iClip3( -C0, C0, ( L2 + RL0 - (L1 << 1)) >> 1 ) ; + + *SrcPtrP = (imgpel) iClip1 (max_imgpel_value, L0 + dif) ; + *SrcPtrQ = (imgpel) iClip1 (max_imgpel_value, R0 - dif) ; + + if( aq ) + *(SrcPtrQ + 1) += iClip3( -C0, C0, ( R2 + RL0 - (R1 << 1)) >> 1 ) ; + } + } + } + } + } + } +} + +/*! + ***************************************************************************************** + * \brief + * Filters 16 pel block edge of Super MB Frame coded MBs + ***************************************************************************************** + */ +static void edge_loop_luma_hor_MBAff(ColorPlane pl, imgpel** Img, byte Strength[16], Macroblock *MbQ, int edge, int width) +{ + int pel, ap = 0, aq = 0, Strng ; + int incP, incQ; + int C0, tc0, dif; + imgpel L2 = 0, L1, L0, R0, R1, R2 = 0, L3, R3; + int RL0; + int Alpha = 0, Beta = 0 ; + const byte* ClipTab = NULL; + int small_gap; + int indexA, indexB; + + int QP; + int xQ, yQ; + + PixelPos pixP, pixQ; + VideoParameters *p_Vid = MbQ->p_Vid; + int bitdepth_scale = pl? p_Vid->bitdepth_scale[IS_CHROMA] : p_Vid->bitdepth_scale[IS_LUMA]; + int max_imgpel_value = p_Vid->max_pel_value_comp[pl]; + + int AlphaC0Offset = MbQ->DFAlphaC0Offset; + int BetaOffset = MbQ->DFBetaOffset; + + Macroblock *MbP; + imgpel *SrcPtrP, *SrcPtrQ; + + for( pel = 0 ; pel < MB_BLOCK_SIZE ; ++pel ) + { + xQ = pel; + yQ = (edge < 16 ? edge : 1); + p_Vid->getNeighbour(MbQ, xQ, yQ - 1, p_Vid->mb_size[IS_LUMA], &pixP); + + if (pixP.available || (MbQ->DFDisableIdc== 0)) + { + if( (Strng = Strength[pel]) != 0) + { + p_Vid->getNeighbour(MbQ, xQ, yQ, p_Vid->mb_size[IS_LUMA], &pixQ); + + MbP = &(p_Vid->mb_data[pixP.mb_addr]); + + incQ = ((MbP->mb_field && !MbQ->mb_field) ? 2 * width : width); + incP = ((MbQ->mb_field && !MbP->mb_field) ? 2 * width : width); + SrcPtrQ = &(Img[pixQ.pos_y][pixQ.pos_x]); + SrcPtrP = &(Img[pixP.pos_y][pixP.pos_x]); + + // Average QP of the two blocks + QP = pl? ((MbP->qpc[pl-1] + MbQ->qpc[pl-1] + 1) >> 1) : (MbP->qp + MbQ->qp + 1) >> 1; + + indexA = iClip3(0, MAX_QP, QP + AlphaC0Offset); + indexB = iClip3(0, MAX_QP, QP + BetaOffset); + + Alpha = ALPHA_TABLE[indexA] * bitdepth_scale; + Beta = BETA_TABLE [indexB] * bitdepth_scale; + ClipTab = CLIP_TAB[indexA]; + + L3 = SrcPtrP[-incP*3]; + L2 = SrcPtrP[-incP*2]; + L1 = SrcPtrP[-incP]; + L0 = SrcPtrP[0] ; + R0 = SrcPtrQ[0] ; + R1 = SrcPtrQ[ incQ]; + R2 = SrcPtrQ[ incQ*2]; + R3 = SrcPtrQ[ incQ*3]; + + if( iabs( R0 - L0 ) < Alpha ) + { + if ((iabs( R0 - R1) < Beta ) && (iabs(L0 - L1) < Beta )) + { + if(Strng == 4 ) // INTRA strong filtering + { + RL0 = L0 + R0; + small_gap = (iabs( R0 - L0 ) < ((Alpha >> 2) + 2)); + aq = ( iabs( R0 - R2) < Beta ) & small_gap; + ap = ( iabs( L0 - L2) < Beta ) & small_gap; + + if (ap) + { + SrcPtrP[-incP * 2] = (imgpel) ((((L3 + L2) << 1) + L2 + L1 + RL0 + 4) >> 3); + SrcPtrP[-incP ] = (imgpel) (( L2 + L1 + L0 + R0 + 2) >> 2); + SrcPtrP[ 0 ] = (imgpel) (( R1 + ((L1 + RL0) << 1) + L2 + 4) >> 3); + } + else + { + SrcPtrP[ 0 ] = (imgpel) (((L1 << 1) + L0 + R1 + 2) >> 2) ; + } + + if (aq) + { + SrcPtrQ[ 0 ] = (imgpel) (( L1 + ((R1 + RL0) << 1) + R2 + 4) >> 3); + SrcPtrQ[ incQ ] = (imgpel) (( R2 + R0 + R1 + L0 + 2) >> 2); + SrcPtrQ[ incQ * 2 ] = (imgpel) ((((R3 + R2) << 1) + R2 + R1 + RL0 + 4) >> 3); + } + else + { + SrcPtrQ[ 0 ] = (imgpel) (((R1 << 1) + R0 + L1 + 2) >> 2); + } + } + else // normal filtering + { + RL0 = (L0 + R0 + 1) >> 1; + aq = (iabs( R0 - R2) < Beta); + ap = (iabs( L0 - L2) < Beta); + + C0 = ClipTab[ Strng ] * bitdepth_scale; + tc0 = (C0 + ap + aq) ; + dif = iClip3( -tc0, tc0, (((R0 - L0) << 2) + (L1 - R1) + 4) >> 3) ; + + if( ap ) + *(SrcPtrP - incP) += iClip3( -C0, C0, ( L2 + RL0 - (L1 << 1)) >> 1 ) ; + + *SrcPtrP = (imgpel) iClip1 (max_imgpel_value, L0 + dif) ; + *SrcPtrQ = (imgpel) iClip1 (max_imgpel_value, R0 - dif) ; + + if( aq ) + *(SrcPtrQ + incQ) += iClip3( -C0, C0, ( R2 + RL0 - (R1 << 1)) >> 1 ) ; + } + } + } + } + } + } +} + +/*! +***************************************************************************************** +* \brief +* Filters chroma block edge for MBAFF types +***************************************************************************************** + */ +static void edge_loop_chroma_ver_MBAff(imgpel** Img, byte Strength[16], Macroblock *MbQ, int edge, int uv) +{ + int pel, Strng ; + int C0, tc0, dif; + imgpel L1, L0, R0, R1; + int Alpha = 0, Beta = 0; + const byte* ClipTab = NULL; + int indexA, indexB; + VideoParameters *p_Vid = MbQ->p_Vid; + int PelNum = pelnum_cr[0][p_Vid->yuv_format]; + int StrengthIdx; + int QP; + int xQ, yQ; + PixelPos pixP, pixQ; + int bitdepth_scale = p_Vid->bitdepth_scale[IS_CHROMA]; + int max_imgpel_value = p_Vid->max_pel_value_comp[uv + 1]; + + int AlphaC0Offset = MbQ->DFAlphaC0Offset; + int BetaOffset = MbQ->DFBetaOffset; + Macroblock *MbP; + imgpel *SrcPtrP, *SrcPtrQ; + + for( pel = 0 ; pel < PelNum ; ++pel ) + { + xQ = edge; + yQ = pel; + p_Vid->getNeighbour(MbQ, xQ, yQ, p_Vid->mb_size[IS_CHROMA], &pixQ); + p_Vid->getNeighbour(MbQ, xQ - 1, yQ, p_Vid->mb_size[IS_CHROMA], &pixP); + MbP = &(p_Vid->mb_data[pixP.mb_addr]); + StrengthIdx = (PelNum == 8) ? ((MbQ->mb_field && !MbP->mb_field) ? pel << 1 :((pel >> 1) << 2) + (pel & 0x01)) : pel; + + if (pixP.available || (MbQ->DFDisableIdc == 0)) + { + if( (Strng = Strength[StrengthIdx]) != 0) + { + SrcPtrQ = &(Img[pixQ.pos_y][pixQ.pos_x]); + SrcPtrP = &(Img[pixP.pos_y][pixP.pos_x]); + + // Average QP of the two blocks + QP = (MbP->qpc[uv] + MbQ->qpc[uv] + 1) >> 1; + + indexA = iClip3(0, MAX_QP, QP + AlphaC0Offset); + indexB = iClip3(0, MAX_QP, QP + BetaOffset); + + Alpha = ALPHA_TABLE[indexA] * bitdepth_scale; + Beta = BETA_TABLE [indexB] * bitdepth_scale; + ClipTab = CLIP_TAB[indexA]; + + L1 = SrcPtrP[-1]; + L0 = SrcPtrP[0] ; + R0 = SrcPtrQ[0] ; + R1 = SrcPtrQ[ 1]; + + if( iabs( R0 - L0 ) < Alpha ) + { + if( ((iabs( R0 - R1) - Beta < 0) && (iabs(L0 - L1) - Beta < 0 )) ) + { + if( Strng == 4 ) // INTRA strong filtering + { + SrcPtrQ[0] = (imgpel) ( ((R1 << 1) + R0 + L1 + 2) >> 2 ); + SrcPtrP[0] = (imgpel) ( ((L1 << 1) + L0 + R1 + 2) >> 2 ); + } + else + { + C0 = ClipTab[ Strng ] * bitdepth_scale; + tc0 = (C0 + 1); + dif = iClip3( -tc0, tc0, ( ((R0 - L0) << 2) + (L1 - R1) + 4) >> 3 ); + + SrcPtrP[0] = (imgpel) iClip1 ( max_imgpel_value, L0 + dif ); + SrcPtrQ[0] = (imgpel) iClip1 ( max_imgpel_value, R0 - dif ); + } + } + } + } + } + } +} + +/*! +***************************************************************************************** +* \brief +* Filters chroma block edge for MBAFF types +***************************************************************************************** + */ +static void edge_loop_chroma_hor_MBAff(imgpel** Img, byte Strength[16], Macroblock *MbQ, int edge, int width, int uv) +{ + int pel, Strng ; + int incP, incQ; + int C0, tc0, dif; + imgpel L1, L0, R0, R1; + int Alpha = 0, Beta = 0; + const byte* ClipTab = NULL; + int indexA, indexB; + VideoParameters *p_Vid = MbQ->p_Vid; + int PelNum = pelnum_cr[1][p_Vid->yuv_format]; + int StrengthIdx; + int QP; + int xQ, yQ; + PixelPos pixP, pixQ; + int bitdepth_scale = p_Vid->bitdepth_scale[IS_CHROMA]; + int max_imgpel_value = p_Vid->max_pel_value_comp[uv + 1]; + + int AlphaC0Offset = MbQ->DFAlphaC0Offset; + int BetaOffset = MbQ->DFBetaOffset; + Macroblock *MbP; + imgpel *SrcPtrP, *SrcPtrQ; + + for( pel = 0 ; pel < PelNum ; ++pel ) + { + xQ = pel; + yQ = (edge < 16? edge : 1); + p_Vid->getNeighbour(MbQ, xQ, yQ, p_Vid->mb_size[IS_CHROMA], &pixQ); + p_Vid->getNeighbour(MbQ, xQ, yQ - 1, p_Vid->mb_size[IS_CHROMA], &pixP); + MbP = &(p_Vid->mb_data[pixP.mb_addr]); + StrengthIdx = (PelNum == 8) ? ((MbQ->mb_field && !MbP->mb_field) ? pel << 1 :((pel >> 1) << 2) + (pel & 0x01)) : pel; + + if (pixP.available || (MbQ->DFDisableIdc == 0)) + { + if( (Strng = Strength[StrengthIdx]) != 0) + { + incQ = ((MbP->mb_field && !MbQ->mb_field) ? 2 * width : width); + incP = ((MbQ->mb_field && !MbP->mb_field) ? 2 * width : width); + SrcPtrQ = &(Img[pixQ.pos_y][pixQ.pos_x]); + SrcPtrP = &(Img[pixP.pos_y][pixP.pos_x]); + + // Average QP of the two blocks + QP = (MbP->qpc[uv] + MbQ->qpc[uv] + 1) >> 1; + + indexA = iClip3(0, MAX_QP, QP + AlphaC0Offset); + indexB = iClip3(0, MAX_QP, QP + BetaOffset); + + Alpha = ALPHA_TABLE[indexA] * bitdepth_scale; + Beta = BETA_TABLE [indexB] * bitdepth_scale; + ClipTab = CLIP_TAB[indexA]; + + L1 = SrcPtrP[-incP]; + L0 = SrcPtrP[0] ; + R0 = SrcPtrQ[0] ; + R1 = SrcPtrQ[ incQ]; + + if( iabs( R0 - L0 ) < Alpha ) + { + if( ((iabs( R0 - R1) - Beta < 0) && (iabs(L0 - L1) - Beta < 0 )) ) + { + if( Strng == 4 ) // INTRA strong filtering + { + SrcPtrQ[0] = (imgpel) ( ((R1 << 1) + R0 + L1 + 2) >> 2 ); + SrcPtrP[0] = (imgpel) ( ((L1 << 1) + L0 + R1 + 2) >> 2 ); + } + else + { + C0 = ClipTab[ Strng ] * bitdepth_scale; + tc0 = (C0 + 1); + dif = iClip3( -tc0, tc0, ( ((R0 - L0) << 2) + (L1 - R1) + 4) >> 3 ); + + SrcPtrP[0] = (imgpel) iClip1 ( max_imgpel_value, L0 + dif ); + SrcPtrQ[0] = (imgpel) iClip1 ( max_imgpel_value, R0 - dif ); + } + } + } + } + } + } +} + + diff --git a/lencod/src/loop_filter_normal.c b/lencod/src/loop_filter_normal.c new file mode 100644 index 0000000..40a6487 --- /dev/null +++ b/lencod/src/loop_filter_normal.c @@ -0,0 +1,758 @@ + +/*! + ************************************************************************************* + * \file loop_filter_normal.c + * + * \brief + * Loop filter to reduce blocking artifacts on a macroblock level (normal). + * The filter strength is QP dependent. + * + * \author + * Contributors: + * - Peter List Peter.List@t-systems.de: Original code (13-Aug-2001) + * - Jani Lainema Jani.Lainema@nokia.com: Some bug fixing, removal of recursiveness (16-Aug-2001) + * - Peter List Peter.List@t-systems.de: inplace filtering and various simplifications (10-Jan-2002) + * - Anthony Joch anthony@ubvideo.com: Simplified switching between filters and + * non-recursive default filter. (08-Jul-2002) + * - Cristina Gomila cristina.gomila@thomson.net: Simplification of the chroma deblocking + * from JVT-E089 (21-Nov-2002) + * - Alexis Michael Tourapis atour@dolby.com: Speed/Architecture improvements (08-Feb-2007) + ************************************************************************************* + */ + +#include "global.h" +#include "image.h" +#include "mb_access.h" +#include "loop_filter.h" + +static void GetStrengthVer (byte Strength[MB_BLOCK_SIZE], Macroblock *MbQ, int edge, int mvlimit); +static void GetStrengthHor (byte Strength[MB_BLOCK_SIZE], Macroblock *MbQ, int edge, int mvlimit); +static void EdgeLoopLumaVer (ColorPlane pl, imgpel** Img, byte Strength[MB_BLOCK_SIZE], Macroblock *MbQ, int edge); +static void EdgeLoopLumaHor (ColorPlane pl, imgpel** Img, byte Strength[MB_BLOCK_SIZE], Macroblock *MbQ, int edge, int width); +static void EdgeLoopChromaVer (imgpel** Img, byte Strength[MB_BLOCK_SIZE], Macroblock *MbQ, int edge, int uv); +static void EdgeLoopChromaHor (imgpel** Img, byte Strength[MB_BLOCK_SIZE], Macroblock *MbQ, int edge, int width, int uv); + + +void set_loop_filter_functions_normal(VideoParameters *p_Vid) +{ + p_Vid->GetStrengthVer = GetStrengthVer; + p_Vid->GetStrengthHor = GetStrengthHor; + p_Vid->EdgeLoopLumaVer = EdgeLoopLumaVer; + p_Vid->EdgeLoopLumaHor = EdgeLoopLumaHor; + p_Vid->EdgeLoopChromaVer = EdgeLoopChromaVer; + p_Vid->EdgeLoopChromaHor = EdgeLoopChromaHor; +} + + /*! + ********************************************************************************************* + * \brief + * returns a buffer of 16 Strength values for one stripe in a mb (for different Frame or Field types) + ********************************************************************************************* + */ +static void GetStrengthVer(byte Strength[MB_BLOCK_SIZE], Macroblock *MbQ, int edge, int mvlimit) +{ + VideoParameters *p_Vid = MbQ->p_Vid; + Slice *currSlice = MbQ->p_Slice; + int StrValue; + + if ((currSlice->slice_type==SP_SLICE)||(currSlice->slice_type==SI_SLICE) ) + { + // Set strength to either 3 or 4 regardless of pixel position + StrValue = (edge == 0) ? 4 : 3; + memset(Strength, (byte) StrValue, MB_BLOCK_SIZE * sizeof(byte)); + } + else + { + if (!(MbQ->mb_type==I4MB||MbQ->mb_type==I8MB||MbQ->mb_type==I16MB||MbQ->mb_type==IPCM)) + { + PixelPos pixMB; + Macroblock *MbP; + int xQ = edge - 1; + + getNonAffNeighbour(MbQ, xQ, 0, p_Vid->mb_size[IS_LUMA], &pixMB); + MbP = (edge) ? MbQ : &(p_Vid->mb_data[pixMB.mb_addr]); + + if (!(MbP->mb_type==I4MB||MbP->mb_type==I8MB||MbP->mb_type==I16MB||MbP->mb_type==IPCM)) + { + PixelPos pixP = pixMB; + PicMotionParams **mv_info = p_Vid->enc_picture->mv_info; + + int blkP, blkQ, idx; + + short mb_x, mb_y; + + get_mb_block_pos_normal (p_Vid->PicPos, MbQ->mbAddrX, &mb_x, &mb_y); + mb_x <<= 2; + mb_y <<= 2; + + xQ ++; + + for( idx = 0 ; idx < MB_BLOCK_SIZE ; idx += BLOCK_SIZE ) + { + pixP.y = (short) (pixMB.y + idx); + pixP.pos_y = (short) (pixMB.pos_y + idx); + + blkQ = (idx & 0xFFFC) + (xQ >> 2); + blkP = (pixP.y & 0xFFFC) + (pixP.x >> 2); + + if (((MbQ->cbp_blk & i64_power2(blkQ)) != 0) || ((MbP->cbp_blk & i64_power2(blkP)) != 0)) + StrValue = 2; + else if (edge && ((MbQ->mb_type == 1) || (MbQ->mb_type == 2))) + StrValue = 0; // if internal edge of certain types, we already know StrValue should be 0 + else // for everything else, if no coefs, but vector difference >= 1 set Strength=1 + { + int blk_y = mb_y + (blkQ >> 2); + int blk_x = mb_x + (blkQ & 3); + int blk_y2 = pixP.pos_y >> 2; + int blk_x2 = pixP.pos_x >> 2; + + PicMotionParams *mv_info_p = &mv_info[blk_y ][blk_x ]; + PicMotionParams *mv_info_q = &mv_info[blk_y2][blk_x2]; + + StorablePicturePtr ref_p0 = mv_info_p->ref_idx[LIST_0] == -1 ? NULL : mv_info_p->ref_pic[LIST_0]; + StorablePicturePtr ref_q0 = mv_info_q->ref_idx[LIST_0] == -1 ? NULL : mv_info_q->ref_pic[LIST_0]; + StorablePicturePtr ref_p1 = mv_info_p->ref_idx[LIST_1] == -1 ? NULL : mv_info_p->ref_pic[LIST_1]; + StorablePicturePtr ref_q1 = mv_info_q->ref_idx[LIST_1] == -1 ? NULL : mv_info_q->ref_pic[LIST_1]; + + if ( ((ref_p0==ref_q0) && (ref_p1==ref_q1)) || ((ref_p0==ref_q1) && (ref_p1==ref_q0))) + { + // L0 and L1 reference pictures of p0 are different; q0 as well + if (ref_p0 != ref_p1) + { + // compare MV for the same reference picture + if (ref_p0 == ref_q0) + { + StrValue = + compare_mvs(&mv_info_p->mv[LIST_0], &mv_info_q->mv[LIST_0], mvlimit) | + compare_mvs(&mv_info_p->mv[LIST_1], &mv_info_q->mv[LIST_1], mvlimit); + } + else + { + StrValue = + compare_mvs(&mv_info_p->mv[LIST_0], &mv_info_q->mv[LIST_1], mvlimit) | + compare_mvs(&mv_info_p->mv[LIST_1], &mv_info_q->mv[LIST_0], mvlimit); + } + } + else + { // L0 and L1 reference pictures of p0 are the same; q0 as well + StrValue = (( + compare_mvs(&mv_info_p->mv[LIST_0], &mv_info_q->mv[LIST_0], mvlimit) | + compare_mvs(&mv_info_p->mv[LIST_1], &mv_info_q->mv[LIST_1], mvlimit)) + && ( + compare_mvs(&mv_info_p->mv[LIST_0], &mv_info_q->mv[LIST_1], mvlimit) | + compare_mvs(&mv_info_p->mv[LIST_1], &mv_info_q->mv[LIST_0], mvlimit) + )); + } + } + else + StrValue = 1; + } + + //*(int*)(Strength+idx) = (StrValue<<24)|(StrValue<<16)|(StrValue<<8)|StrValue; + *(int*)(Strength+idx) = StrValue * 0x01010101; + } + } + else + { + // Start with Strength=3 or Strength=4 for Mb-edge + StrValue = (edge == 0) ? 4 : 3; + memset(Strength, (byte) StrValue, MB_BLOCK_SIZE * sizeof(byte)); + } + } + else + { + // Start with Strength=3. or Strength=4 for Mb-edge + StrValue = (edge == 0) ? 4 : 3; + memset(Strength, (byte) StrValue, MB_BLOCK_SIZE * sizeof(byte)); + } + } +} + + /*! + ********************************************************************************************* + * \brief + * returns a buffer of 16 Strength values for one stripe in a mb (for different Frame or Field types) + ********************************************************************************************* + */ +static void GetStrengthHor(byte Strength[MB_BLOCK_SIZE], Macroblock *MbQ, int edge, int mvlimit) +{ + int StrValue; + Slice *currSlice = MbQ->p_Slice; + VideoParameters *p_Vid = MbQ->p_Vid; + + if ((currSlice->slice_type==SP_SLICE)||(currSlice->slice_type==SI_SLICE) ) + { + // Set strength to either 3 or 4 regardless of pixel position + StrValue = (edge == 0 && (p_Vid->structure == FRAME)) ? 4 : 3; + memset(Strength, (byte) StrValue, MB_BLOCK_SIZE * sizeof(byte)); + } + else + { + if (!(MbQ->mb_type==I4MB||MbQ->mb_type==I8MB||MbQ->mb_type==I16MB||MbQ->mb_type==IPCM)) + { + PixelPos pixMB; + Macroblock *MbP; + int yQ = (edge < 16 ? edge - 1: 0); + + getNonAffNeighbour(MbQ, 0, yQ, p_Vid->mb_size[IS_LUMA], &pixMB); + MbP = (edge) ? MbQ : &(p_Vid->mb_data[pixMB.mb_addr]); + + if (!(MbP->mb_type==I4MB||MbP->mb_type==I8MB||MbP->mb_type==I16MB||MbP->mb_type==IPCM)) + { + PixelPos pixP = pixMB; + PicMotionParams **mv_info = p_Vid->enc_picture->mv_info; + + int blkP, blkQ, idx; + + short mb_x, mb_y; + + get_mb_block_pos_normal (p_Vid->PicPos, MbQ->mbAddrX, &mb_x, &mb_y); + mb_x <<= 2; + mb_y <<= 2; + yQ ++; + + for( idx = 0 ; idx < MB_BLOCK_SIZE ; idx += BLOCK_SIZE ) + { + pixP.x = (short) (pixMB.x + idx); + pixP.pos_x = (short) (pixMB.pos_x + idx); + + blkQ = (yQ & 0xFFFC) + (idx >> 2); + blkP = (pixP.y & 0xFFFC) + (pixP.x >> 2); + + if( ((MbQ->cbp_blk & i64_power2(blkQ)) != 0) || ((MbP->cbp_blk & i64_power2(blkP)) != 0) ) + StrValue = 2; + else if (edge && ((MbQ->mb_type == 1) || (MbQ->mb_type == 3))) + StrValue = 0; // if internal edge of certain types, we already know StrValue should be 0 + else // for everything else, if no coefs, but vector difference >= 1 set Strength=1 + { + int blk_y = mb_y + (blkQ >> 2); + int blk_x = mb_x + (blkQ & 3); + int blk_y2 = pixP.pos_y >> 2; + int blk_x2 = pixP.pos_x >> 2; + + PicMotionParams *mv_info_p = &mv_info[blk_y ][blk_x ]; + PicMotionParams *mv_info_q = &mv_info[blk_y2][blk_x2]; + + StorablePicturePtr ref_p0 = mv_info_p->ref_idx[LIST_0] == -1 ? NULL : mv_info_p->ref_pic[LIST_0]; + StorablePicturePtr ref_q0 = mv_info_q->ref_idx[LIST_0] == -1 ? NULL : mv_info_q->ref_pic[LIST_0]; + StorablePicturePtr ref_p1 = mv_info_p->ref_idx[LIST_1] == -1 ? NULL : mv_info_p->ref_pic[LIST_1]; + StorablePicturePtr ref_q1 = mv_info_q->ref_idx[LIST_1] == -1 ? NULL : mv_info_q->ref_pic[LIST_1]; + + if ( ((ref_p0==ref_q0) && (ref_p1==ref_q1)) || ((ref_p0==ref_q1) && (ref_p1==ref_q0))) + { + // L0 and L1 reference pictures of p0 are different; q0 as well + if (ref_p0 != ref_p1) + { + // compare MV for the same reference picture + if (ref_p0 == ref_q0) + { + StrValue = + compare_mvs(&mv_info_p->mv[LIST_0], &mv_info_q->mv[LIST_0], mvlimit) | + compare_mvs(&mv_info_p->mv[LIST_1], &mv_info_q->mv[LIST_1], mvlimit); + } + else + { + StrValue = + compare_mvs(&mv_info_p->mv[LIST_0], &mv_info_q->mv[LIST_1], mvlimit) | + compare_mvs(&mv_info_p->mv[LIST_1], &mv_info_q->mv[LIST_0], mvlimit); + } + } + else + { // L0 and L1 reference pictures of p0 are the same; q0 as well + StrValue = (( + compare_mvs(&mv_info_p->mv[LIST_0], &mv_info_q->mv[LIST_0], mvlimit) | + compare_mvs(&mv_info_p->mv[LIST_1], &mv_info_q->mv[LIST_1], mvlimit)) + && ( + compare_mvs(&mv_info_p->mv[LIST_0], &mv_info_q->mv[LIST_1], mvlimit) | + compare_mvs(&mv_info_p->mv[LIST_1], &mv_info_q->mv[LIST_0], mvlimit) + )); + } + } + else + StrValue = 1; + } + //*(int*)(Strength+idx) = (StrValue<<24)|(StrValue<<16)|(StrValue<<8)|StrValue; + *(int*)(Strength+idx) = StrValue * 0x01010101; + } + } + else + { + // Start with Strength=3. or Strength=4 for Mb-edge + StrValue = (edge == 0 && (p_Vid->structure == FRAME)) ? 4 : 3; + memset(Strength, (byte) StrValue, MB_BLOCK_SIZE * sizeof(byte)); + } + } + else + { + // Start with Strength=3. or Strength=4 for Mb-edge + StrValue = (edge == 0 && (p_Vid->structure == FRAME)) ? 4 : 3; + memset(Strength, (byte) StrValue, MB_BLOCK_SIZE * sizeof(byte)); + } + } +} + + +/*! + ***************************************************************************************** + * \brief + * Filters 16 pel block edge of Frame or Field coded MBs + ***************************************************************************************** + */ +static void EdgeLoopLumaVer(ColorPlane pl, imgpel** Img, byte Strength[16], Macroblock *MbQ, int edge) +{ + VideoParameters *p_Vid = MbQ->p_Vid; + + PixelPos pixMB1; + getNonAffNeighbour(MbQ, edge - 1, 0, p_Vid->mb_size[IS_LUMA], &pixMB1); + + if (pixMB1.available || (MbQ->DFDisableIdc== 0)) + { + int bitdepth_scale = pl ? p_Vid->bitdepth_scale[IS_CHROMA] : p_Vid->bitdepth_scale[IS_LUMA]; + + Macroblock *MbP = &(p_Vid->mb_data[pixMB1.mb_addr]); + + // Average QP of the two blocks + int QP = pl? ((MbP->qpc[pl-1] + MbQ->qpc[pl-1] + 1) >> 1) : (MbP->qp + MbQ->qp + 1) >> 1; + + int indexA = iClip3(0, MAX_QP, QP + MbQ->DFAlphaC0Offset); + int indexB = iClip3(0, MAX_QP, QP + MbQ->DFBetaOffset); + + int Alpha = ALPHA_TABLE[indexA] * bitdepth_scale; + int Beta = BETA_TABLE [indexB] * bitdepth_scale; + + if ((Alpha | Beta )!= 0) + { + const byte *ClipTab = CLIP_TAB[indexA]; + int max_imgpel_value = p_Vid->max_pel_value_comp[pl]; + + int pos_x1 = pixMB1.pos_x; + imgpel **cur_img = &Img[pixMB1.pos_y]; + int pel; + + for( pel = 0 ; pel < MB_BLOCK_SIZE ; pel += 4 ) + { + if(*Strength == 4 ) // INTRA strong filtering + { + int i; + for( i = 0 ; i < BLOCK_SIZE ; ++i ) + { + imgpel *SrcPtrP = *(cur_img++) + pos_x1; + imgpel *SrcPtrQ = SrcPtrP + 1; + imgpel L0 = *SrcPtrP; + imgpel R0 = *SrcPtrQ; + + if( iabs( R0 - L0 ) < Alpha ) + { + imgpel R1 = *(SrcPtrQ + 1); + imgpel L1 = *(SrcPtrP - 1); + if ((iabs( R0 - R1) < Beta) && (iabs(L0 - L1) < Beta)) + { + imgpel R2 = *(SrcPtrQ + 2); + imgpel L2 = *(SrcPtrP - 2); + int RL0 = L0 + R0; + int small_gap = (iabs( R0 - L0 ) < ((Alpha >> 2) + 2)); + int aq = ( iabs( R0 - R2) < Beta ) & small_gap; + int ap = ( iabs( L0 - L2) < Beta ) & small_gap; + + if (ap) + { + imgpel L3 = *(SrcPtrP - 3); + *(SrcPtrP--) = (imgpel) (( R1 + ((L1 + RL0) << 1) + L2 + 4) >> 3); + *(SrcPtrP--) = (imgpel) (( L2 + L1 + RL0 + 2) >> 2); + *(SrcPtrP ) = (imgpel) ((((L3 + L2) <<1) + L2 + L1 + RL0 + 4) >> 3); + } + else + { + *SrcPtrP = (imgpel) (((L1 << 1) + L0 + R1 + 2) >> 2); + } + + if (aq) + { + imgpel R3 = *(SrcPtrQ + 3); + *(SrcPtrQ++) = (imgpel) (( L1 + ((R1 + RL0) << 1) + R2 + 4) >> 3); + *(SrcPtrQ++) = (imgpel) (( R2 + R0 + L0 + R1 + 2) >> 2); + *(SrcPtrQ ) = (imgpel) ((((R3 + R2) <<1) + R2 + R1 + RL0 + 4) >> 3); + } + else + { + *SrcPtrQ = (imgpel) (((R1 << 1) + R0 + L1 + 2) >> 2); + } + } + } + } + } + else if( *Strength != 0) // normal filtering + { + int C0 = ClipTab[ *Strength ] * bitdepth_scale; + int i; + imgpel *SrcPtrP, *SrcPtrQ; + int edge_diff; + for( i = 0 ; i < BLOCK_SIZE ; ++i ) + { + SrcPtrP = *(cur_img++) + pos_x1; + SrcPtrQ = SrcPtrP + 1; + edge_diff = *SrcPtrQ - *SrcPtrP; + + if( iabs( edge_diff ) < Alpha ) + { + imgpel *SrcPtrQ1 = SrcPtrQ + 1; + imgpel *SrcPtrP1 = SrcPtrP - 1; + + if ((iabs( *SrcPtrQ - *SrcPtrQ1) < Beta) && (iabs(*SrcPtrP - *SrcPtrP1) < Beta)) + { + int RL0 = (*SrcPtrP + *SrcPtrQ + 1) >> 1; + imgpel R2 = *(SrcPtrQ1 + 1); + imgpel L2 = *(SrcPtrP1 - 1); + + int aq = (iabs(*SrcPtrQ - R2) < Beta); + int ap = (iabs(*SrcPtrP - L2) < Beta); + + int tc0 = (C0 + ap + aq) ; + int dif = iClip3( -tc0, tc0, (((edge_diff) << 2) + (*SrcPtrP1 - *SrcPtrQ1) + 4) >> 3 ); + + if( ap ) + *SrcPtrP1 += iClip3( -C0, C0, (L2 + RL0 - (*SrcPtrP1<<1)) >> 1 ); + + if (dif != 0) + { + *SrcPtrP = (imgpel) iClip1(max_imgpel_value, *SrcPtrP + dif); + *SrcPtrQ = (imgpel) iClip1(max_imgpel_value, *SrcPtrQ - dif); + } + if( aq ) + *SrcPtrQ1 += iClip3( -C0, C0, (R2 + RL0 - (*SrcPtrQ1<<1)) >> 1 ); + } + } + } + } + else + { + cur_img += 4; + } + Strength += 4; + } + } + } +} + + +/*! + ***************************************************************************************** + * \brief + * Filters 16 pel block edge of Frame or Field coded MBs + ***************************************************************************************** + */ +static void EdgeLoopLumaHor(ColorPlane pl, imgpel** Img, byte Strength[16], Macroblock *MbQ, int edge, int width) +{ + VideoParameters *p_Vid = MbQ->p_Vid; + + PixelPos pixMB1; + getNonAffNeighbour(MbQ, 0, (edge < MB_BLOCK_SIZE ? edge - 1: 0), p_Vid->mb_size[IS_LUMA], &pixMB1); + + if (pixMB1.available || (MbQ->DFDisableIdc== 0)) + { + int bitdepth_scale = pl ? p_Vid->bitdepth_scale[IS_CHROMA] : p_Vid->bitdepth_scale[IS_LUMA]; + + Macroblock *MbP = &(p_Vid->mb_data[pixMB1.mb_addr]); + + // Average QP of the two blocks + int QP = pl? ((MbP->qpc[pl-1] + MbQ->qpc[pl-1] + 1) >> 1) : (MbP->qp + MbQ->qp + 1) >> 1; + + int indexA = iClip3(0, MAX_QP, QP + MbQ->DFAlphaC0Offset); + int indexB = iClip3(0, MAX_QP, QP + MbQ->DFBetaOffset); + + int Alpha = ALPHA_TABLE[indexA] * bitdepth_scale; + int Beta = BETA_TABLE [indexB] * bitdepth_scale; + if ((Alpha | Beta )!= 0) + { + const byte *ClipTab = CLIP_TAB[indexA]; + int max_imgpel_value = p_Vid->max_pel_value_comp[pl]; + imgpel *imgP = &Img[pixMB1.pos_y ][pixMB1.pos_x]; + imgpel *imgQ = imgP + width; + int pel; + + for( pel = 0 ; pel < MB_BLOCK_SIZE ; pel += 4 ) + { + if(*Strength == 4 ) // INTRA strong filtering + { + int pixel; + int inc_dim2 = width * 2; + int inc_dim3 = width * 3; + for( pixel = 0 ; pixel < BLOCK_SIZE ; ++pixel ) + { + imgpel *SrcPtrP = imgP++; + imgpel *SrcPtrQ = imgQ++; + imgpel L0 = *SrcPtrP; + imgpel R0 = *SrcPtrQ; + + if( iabs( R0 - L0 ) < Alpha ) + { + imgpel L1 = *(SrcPtrP - width); + imgpel R1 = *(SrcPtrQ + width); + + if ((iabs( R0 - R1) < Beta) && (iabs(L0 - L1) < Beta)) + { + imgpel L2 = *(SrcPtrP - inc_dim2); + imgpel R2 = *(SrcPtrQ + inc_dim2); + int RL0 = L0 + R0; + int small_gap = (iabs( R0 - L0 ) < ((Alpha >> 2) + 2)); + int aq = ( iabs( R0 - R2) < Beta ) & small_gap; + int ap = ( iabs( L0 - L2) < Beta ) & small_gap; + + if (ap) + { + imgpel L3 = *(SrcPtrP - inc_dim3); + *SrcPtrP = (imgpel) (( R1 + ((L1 + RL0) << 1) + L2 + 4) >> 3); + *(SrcPtrP -= width) = (imgpel) (( L2 + L1 + RL0 + 2) >> 2); + *(SrcPtrP - width) = (imgpel) ((((L3 + L2) <<1) + L2 + L1 + RL0 + 4) >> 3); + } + else + { + *SrcPtrP = (imgpel) (((L1 << 1) + L0 + R1 + 2) >> 2); + } + + if (aq) + { + imgpel R3 = *(SrcPtrQ + inc_dim3); + *(SrcPtrQ ) = (imgpel) (( L1 + ((R1 + RL0) << 1) + R2 + 4) >> 3); + *(SrcPtrQ += width ) = (imgpel) (( R2 + R0 + L0 + R1 + 2) >> 2); + *(SrcPtrQ + width ) = (imgpel) ((((R3 + R2) <<1) + R2 + R1 + RL0 + 4) >> 3); + } + else + { + *SrcPtrQ = (imgpel) (((R1 << 1) + R0 + L1 + 2) >> 2); + } + } + } + } + } + else if( *Strength != 0) // normal filtering + { + int C0 = ClipTab[ *Strength ] * bitdepth_scale; + int i; + imgpel *SrcPtrP, *SrcPtrQ; + int edge_diff; + for( i= 0 ; i < BLOCK_SIZE ; ++i ) + { + SrcPtrP = imgP++; + SrcPtrQ = imgQ++; + edge_diff = *SrcPtrQ - *SrcPtrP; + + if( iabs( edge_diff ) < Alpha ) + { + imgpel *SrcPtrQ1 = SrcPtrQ + width; + imgpel *SrcPtrP1 = SrcPtrP - width; + + if ((iabs( *SrcPtrQ - *SrcPtrQ1) < Beta) && (iabs(*SrcPtrP - *SrcPtrP1) < Beta)) + { + int RL0 = (*SrcPtrP + *SrcPtrQ + 1) >> 1; + imgpel R2 = *(SrcPtrQ1 + width); + imgpel L2 = *(SrcPtrP1 - width); + + int aq = (iabs(*SrcPtrQ - R2) < Beta); + int ap = (iabs(*SrcPtrP - L2) < Beta); + + int tc0 = (C0 + ap + aq) ; + int dif = iClip3( -tc0, tc0, (((edge_diff) << 2) + (*SrcPtrP1 - *SrcPtrQ1) + 4) >> 3 ); + + if( ap ) + *SrcPtrP1 += iClip3( -C0, C0, (L2 + RL0 - (*SrcPtrP1<<1)) >> 1 ); + + if (dif != 0) + { + *SrcPtrP = (imgpel) iClip1(max_imgpel_value, *SrcPtrP + dif); + *SrcPtrQ = (imgpel) iClip1(max_imgpel_value, *SrcPtrQ - dif); + } + + if( aq ) + *SrcPtrQ1 += iClip3( -C0, C0, (R2 + RL0 - (*SrcPtrQ1<<1)) >> 1 ); + } + } + } + } + else + { + imgP += 4; + imgQ += 4; + } + Strength += 4; + } + } + } +} + + +/*! + ***************************************************************************************** + * \brief + * Filters chroma block edge for Frame or Field coded pictures + ***************************************************************************************** + */ +static void EdgeLoopChromaVer(imgpel** Img, byte Strength[16], Macroblock *MbQ, int edge, int uv) +{ + VideoParameters *p_Vid = MbQ->p_Vid; + + int xQ = edge - 1; + int yQ = 0; + PixelPos pixMB1; + + getNonAffNeighbour(MbQ, xQ, yQ, p_Vid->mb_size[IS_CHROMA], &pixMB1); + + if (pixMB1.available || (MbQ->DFDisableIdc == 0)) + { + int bitdepth_scale = p_Vid->bitdepth_scale[IS_CHROMA]; + int max_imgpel_value = p_Vid->max_pel_value_comp[uv + 1]; + + int AlphaC0Offset = MbQ->DFAlphaC0Offset; + int BetaOffset = MbQ->DFBetaOffset; + Macroblock *MbP = &(p_Vid->mb_data[pixMB1.mb_addr]); + + // Average QP of the two blocks + int QP = (MbP->qpc[uv] + MbQ->qpc[uv] + 1) >> 1; + + int indexA = iClip3(0, MAX_QP, QP + AlphaC0Offset); + int indexB = iClip3(0, MAX_QP, QP + BetaOffset); + + int Alpha = ALPHA_TABLE[indexA] * bitdepth_scale; + int Beta = BETA_TABLE [indexB] * bitdepth_scale; + if ((Alpha | Beta) != 0) + { + const int PelNum = pelnum_cr[0][p_Vid->yuv_format]; + const byte *ClipTab = CLIP_TAB[indexA]; + + int pel; + int pos_x1 = pixMB1.pos_x; + imgpel **cur_img = &Img[pixMB1.pos_y]; + + for( pel = 0 ; pel < PelNum ; ++pel ) + { + int Strng = Strength[(PelNum == 8) ? (((pel >> 1) << 2) + (pel & 0x01)) : pel]; + + if( Strng != 0) + { + imgpel *SrcPtrP = *cur_img + pos_x1; + imgpel *SrcPtrQ = SrcPtrP + 1; + int edge_diff = *SrcPtrQ - *SrcPtrP; + + if ( iabs( edge_diff ) < Alpha ) + { + imgpel R1 = *(SrcPtrQ + 1); + if ( iabs(*SrcPtrQ - R1) < Beta ) + { + imgpel L1 = *(SrcPtrP - 1); + if ( iabs(*SrcPtrP - L1) < Beta ) + { + if( Strng == 4 ) // INTRA strong filtering + { + *SrcPtrP = (imgpel) ( ((L1 << 1) + *SrcPtrP + R1 + 2) >> 2 ); + *SrcPtrQ = (imgpel) ( ((R1 << 1) + *SrcPtrQ + L1 + 2) >> 2 ); + } + else + { + int tc0 = ClipTab[ Strng ] * bitdepth_scale + 1; + int dif = iClip3( -tc0, tc0, ( ((edge_diff) << 2) + (L1 - R1) + 4) >> 3 ); + + if (dif != 0) + { + *SrcPtrP = (imgpel) iClip1 ( max_imgpel_value, *SrcPtrP + dif ); + *SrcPtrQ = (imgpel) iClip1 ( max_imgpel_value, *SrcPtrQ - dif ); + } + } + } + } + } + } + cur_img++; + } + } + } +} + + +/*! + ***************************************************************************************** + * \brief + * Filters chroma block edge for Frame or Field coded pictures + ***************************************************************************************** + */ +static void EdgeLoopChromaHor(imgpel** Img, byte Strength[16], Macroblock *MbQ, int edge, int width, int uv) +{ + VideoParameters *p_Vid = MbQ->p_Vid; + + int xQ = 0; + int yQ = (edge < 16 ? edge - 1: 0); + PixelPos pixMB1; + + getNonAffNeighbour(MbQ, xQ, yQ, p_Vid->mb_size[IS_CHROMA], &pixMB1); + + if (pixMB1.available || (MbQ->DFDisableIdc == 0)) + { + int bitdepth_scale = p_Vid->bitdepth_scale[IS_CHROMA]; + int max_imgpel_value = p_Vid->max_pel_value_comp[uv + 1]; + + int AlphaC0Offset = MbQ->DFAlphaC0Offset; + int BetaOffset = MbQ->DFBetaOffset; + Macroblock *MbP = &(p_Vid->mb_data[pixMB1.mb_addr]); + + // Average QP of the two blocks + int QP = (MbP->qpc[uv] + MbQ->qpc[uv] + 1) >> 1; + + int indexA = iClip3(0, MAX_QP, QP + AlphaC0Offset); + int indexB = iClip3(0, MAX_QP, QP + BetaOffset); + + int Alpha = ALPHA_TABLE[indexA] * bitdepth_scale; + int Beta = BETA_TABLE [indexB] * bitdepth_scale; + if ((Alpha | Beta) != 0) + { + const int PelNum = pelnum_cr[1][p_Vid->yuv_format]; + const byte *ClipTab = CLIP_TAB[indexA]; + + int pel; + + imgpel *imgP = &Img[pixMB1.pos_y ][pixMB1.pos_x]; + imgpel *imgQ = imgP + width ; + + for( pel = 0 ; pel < PelNum ; ++pel ) + { + int Strng = Strength[(PelNum == 8) ? (((pel >> 1) << 2) + (pel & 0x01)) : pel]; + + if( Strng != 0) + { + imgpel *SrcPtrP = imgP; + imgpel *SrcPtrQ = imgQ; + int edge_diff = *imgQ - *imgP; + + if ( iabs( edge_diff ) < Alpha ) + { + imgpel R1 = *(SrcPtrQ + width); + if ( iabs(*SrcPtrQ - R1) < Beta ) + { + imgpel L1 = *(SrcPtrP - width); + if ( iabs(*SrcPtrP - L1) < Beta ) + { + if( Strng == 4 ) // INTRA strong filtering + { + *SrcPtrP = (imgpel) ( ((L1 << 1) + *SrcPtrP + R1 + 2) >> 2 ); + *SrcPtrQ = (imgpel) ( ((R1 << 1) + *SrcPtrQ + L1 + 2) >> 2 ); + } + else + { + int tc0 = ClipTab[ Strng ] * bitdepth_scale + 1; + int dif = iClip3( -tc0, tc0, ( ((edge_diff) << 2) + (L1 - R1) + 4) >> 3 ); + + if (dif != 0) + { + *SrcPtrP = (imgpel) iClip1 ( max_imgpel_value, *SrcPtrP + dif ); + *SrcPtrQ = (imgpel) iClip1 ( max_imgpel_value, *SrcPtrQ - dif ); + } + } + } + } + } + } + imgP++; + imgQ++; + } + } + } +} + diff --git a/lencod/src/macroblock.c b/lencod/src/macroblock.c new file mode 100644 index 0000000..7e1f71a --- /dev/null +++ b/lencod/src/macroblock.c @@ -0,0 +1,4882 @@ +/*! + ************************************************************************************* + * \file macroblock.c + * + * \brief + * Process one macroblock + * + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * - Inge Lille-Langoy + * - Rickard Sjoberg + * - Jani Lainema + * - Sebastian Purreiter + * - Detlev Marpe + * - Thomas Wedi + * - Ragip Kurceren + * - Alexis Michael Tourapis + ************************************************************************************* + */ + +#include "contributors.h" + +#include +#include + +#include "global.h" +#include "enc_statistics.h" + +#include "block.h" +#include "elements.h" +#include "macroblock.h" +#include "memalloc.h" +#include "blk_prediction.h" +#include "mc_prediction.h" +#include "fmo.h" +#include "vlc.h" +#include "image.h" +#include "mb_access.h" +#include "ratectl.h" // header file for rate control +#include "cabac.h" +#include "biariencode.h" +#include "me_fullsearch.h" +#include "me_fullfast.h" +#include "symbol.h" +#include "conformance.h" +#include "md_common.h" +#include "mv_prediction.h" +#include "rdopt.h" +#include "transform.h" + + +#if TRACE +#define TRACE_SE(trace,str) snprintf(trace,TRACESTRING_SIZE,str) +#else +#define TRACE_SE(trace,str) +#endif + + +static int slice_too_big (Slice *currSlice, int rlc_bits); +static int write_chroma_intra_pred_mode (Macroblock* currMB); +static int write_chroma_coeff (Macroblock* currMB); +static int write_CBP_and_Dquant (Macroblock* currMB); + + /*! + ************************************************************************ + * \brief + * updates the coordinates for the next macroblock to be processed + * + * \param currSlice + * current slice pointer + * \param currMB + * macroblock structure to be set + ************************************************************************ + */ +void set_MB_parameters (Slice *currSlice, Macroblock *currMB) +{ + VideoParameters *p_Vid = currSlice->p_Vid; + InputParameters *p_Inp = currSlice->p_Inp; + + int mb_addr = currMB->mbAddrX; + p_Vid->current_mb_nr = mb_addr; + + p_Vid->get_mb_block_pos(p_Vid->PicPos, currMB->mbAddrX, &currMB->mb_x, &currMB->mb_y); + + currMB->block_x = currMB->mb_x << 2; + currMB->block_y = currMB->mb_y << 2; + + currMB->pix_x = currMB->block_x << 2; + currMB->pix_y = currMB->block_y << 2; + + if (currSlice->mb_aff_frame_flag) + { + if (currMB->mb_field) + { + p_Vid->pCurImg = (mb_addr & 0x01) ? p_Vid->imgData.bot_data[0] : p_Vid->imgData.top_data[0]; + p_Vid->pImgOrg[0] = p_Vid->pCurImg; + + if ((p_Vid->yuv_format != YUV400) && (p_Inp->separate_colour_plane_flag == 0)) + { + if (mb_addr & 0x01) + { + p_Vid->pImgOrg[1] = p_Vid->imgData.bot_data[1]; + p_Vid->pImgOrg[2] = p_Vid->imgData.bot_data[2]; + } + else + { + p_Vid->pImgOrg[1] = p_Vid->imgData.top_data[1]; + p_Vid->pImgOrg[2] = p_Vid->imgData.top_data[2]; + } + } + + currMB->opix_y = (currMB->mb_y >> 1 ) << 4; + currMB->list_offset = (mb_addr & 0x01) ? 4 : 2; + } + else + { + p_Vid->pCurImg = p_Vid->imgData.frm_data[0]; + p_Vid->pImgOrg[0] = p_Vid->imgData.frm_data[0]; + + if ((p_Vid->yuv_format != YUV400) && (p_Inp->separate_colour_plane_flag == 0)) + { + p_Vid->pImgOrg[1] = p_Vid->imgData.frm_data[1]; + p_Vid->pImgOrg[2] = p_Vid->imgData.frm_data[2]; + } + + currMB->opix_y = currMB->block_y << 2; + currMB->list_offset = 0; + } + } + else + { + currMB->opix_y = currMB->block_y << 2; + currMB->list_offset = 0; + } + + if (p_Vid->yuv_format != YUV400) + { + currMB->pix_c_x = (p_Vid->mb_cr_size_x * currMB->pix_x) >> 4; + currMB->pix_c_y = (p_Vid->mb_cr_size_y * currMB->pix_y) >> 4; + + currMB->opix_c_y = (p_Vid->mb_cr_size_y * currMB->opix_y) >> 4; + } + + // printf ("set_MB_parameters: mb %d, mb_x %d, mb_y %d\n", mb_addr, currMB->mb_x, currMB->mb_y); +} + + +/*! + ************************************************************************ + * \brief + * updates the coordinates and statistics parameter for the + * next macroblock + ************************************************************************ + */ +void next_macroblock(Macroblock *currMB) +{ + VideoParameters *p_Vid = currMB->p_Vid; + InputParameters *p_Inp = currMB->p_Inp; + Slice *currSlice = currMB->p_Slice; + int slice_type = currSlice->slice_type; + BitCounter *mbBits = &currMB->bits; + int i; + StatParameters *cur_stats = &p_Vid->enc_picture->stats; + + if (mbBits->mb_total > p_Vid->max_bitCount) + printf("Warning!!! Number of bits (%d) of macroblock_layer() data seems to exceed defined limit (%d).\n", mbBits->mb_total,p_Vid->max_bitCount); + + // Update the statistics + cur_stats->bit_use_mb_type[slice_type] += mbBits->mb_mode; + cur_stats->tmp_bit_use_cbp[slice_type] += mbBits->mb_cbp; + cur_stats->bit_use_coeffC[slice_type] += mbBits->mb_uv_coeff; + cur_stats->bit_use_coeff[0][slice_type] += mbBits->mb_y_coeff; + cur_stats->bit_use_coeff[1][slice_type] += mbBits->mb_cb_coeff; + cur_stats->bit_use_coeff[2][slice_type] += mbBits->mb_cr_coeff; + cur_stats->bit_use_delta_quant[slice_type] += mbBits->mb_delta_quant; + cur_stats->bit_use_stuffing_bits[slice_type] += mbBits->mb_stuffing; + + if (IS_INTRA(currMB)) + { + ++cur_stats->intra_chroma_mode[ (short) currMB->c_ipred_mode]; + + + if ((currMB->cbp&15) != 0) + { + ++cur_stats->mode_use_transform[slice_type][currMB->mb_type][currMB->luma_transform_size_8x8_flag]; + } + } + + ++cur_stats->mode_use[slice_type][currMB->mb_type]; + cur_stats->bit_use_mode[slice_type][currMB->mb_type] += mbBits->mb_inter; + + if (slice_type != I_SLICE) + { + if (currMB->mb_type == P8x8) + { + for(i=0;i<4;++i) + { + if (currMB->b8x8[i].mode > 0) + ++cur_stats->mode_use[slice_type][(short) currMB->b8x8[i].mode]; + else + ++cur_stats->b8_mode_0_use[slice_type][currMB->luma_transform_size_8x8_flag]; + + if (currMB->b8x8[i].mode ==4) + { + if ((currMB->luma_transform_size_8x8_flag && (currMB->cbp&15) != 0) || p_Inp->Transform8x8Mode == 2) + ++cur_stats->mode_use_transform[slice_type][4][1]; + else + ++cur_stats->mode_use_transform[slice_type][4][0]; + } + } + } + else if (currMB->mb_type >= 0 && currMB->mb_type <=3 && ((currMB->cbp&15) != 0)) + { + ++cur_stats->mode_use_transform[slice_type][currMB->mb_type][currMB->luma_transform_size_8x8_flag]; + } + } + + // Statistics + cur_stats->quant[slice_type] += currMB->qp; + ++cur_stats->num_macroblocks[slice_type]; +} + +static void set_chroma_qp(Macroblock* currMB) +{ + VideoParameters *p_Vid = currMB->p_Vid; + Slice *currSlice = currMB->p_Slice; + int i; + for (i = 0; i <= 1; ++i) + { + currMB->qpc[i] = (short) iClip3 ( -currSlice->bitdepth_chroma_qp_scale, 51, currMB->qp + p_Vid->chroma_qp_offset[i] ); + currMB->qpc[i] = (short) (currMB->qpc[i] < 0 ? currMB->qpc[i] : QP_SCALE_CR[currMB->qpc[i]]); + currMB->qp_scaled[i + 1] = (short) (currMB->qpc[i] + currSlice->bitdepth_chroma_qp_scale); + } +} + +/*! +************************************************************************ +* \brief +* updates chroma QP according to luma QP and bit depth +************************************************************************ +*/ +void update_qp(Macroblock *currMB) +{ + currMB->qp_scaled[PLANE_Y] = (short) (currMB->qp + currMB->p_Slice->bitdepth_luma_qp_scale); + set_chroma_qp(currMB); + + select_transform(currMB); +} + +/*! + ************************************************************************ + * \brief + * resets info for the current macroblock + * + * \param currMB + * current macroblock + ************************************************************************ + */ +void reset_macroblock(Macroblock *currMB) +{ + VideoParameters *p_Vid = currMB->p_Vid; + InputParameters *p_Inp = currMB->p_Inp; + Slice *currSlice = currMB->p_Slice; + PicMotionParams **motion = p_Vid->enc_picture->mv_info; + PicMotionParams *p_motion; + int j, i; + + // Reset vectors and reference indices + for (j=currMB->block_y; j < currMB->block_y + BLOCK_MULTIPLE; ++j) + { + for (i = currMB->block_x; i < currMB->block_x + BLOCK_MULTIPLE; ++i) + { + p_motion = &motion[j][i]; + p_motion->ref_pic[LIST_0] = NULL; + p_motion->ref_pic[LIST_1] = NULL; + p_motion->mv [LIST_0] = zero_mv; + p_motion->mv [LIST_1] = zero_mv; + p_motion->ref_idx[LIST_0] = -1; + p_motion->ref_idx[LIST_1] = -1; + } + } + + + // Reset syntax element entries in MB struct + currMB->mb_type = 0; + currMB->cbp_blk = 0; + currMB->cbp = 0; + currMB->c_ipred_mode = DC_PRED_8; + + currSlice->cmp_cbp[1] = currSlice->cmp_cbp[2] = currSlice->curr_cbp[0] = currSlice->curr_cbp[1] = 0; + + memset(currMB->cbp_bits , 0, 3 * sizeof(int64)); + memset(currMB->cbp_bits_8x8, 0, 3 * sizeof(int64)); + + memset (currMB->mvd, 0, BLOCK_CONTEXT * sizeof(short)); + memset (currMB->intra_pred_modes, DC_PRED, MB_BLOCK_PARTITIONS * sizeof(char)); // changing this to char would allow us to use memset + memset (currMB->intra_pred_modes8x8, DC_PRED, MB_BLOCK_PARTITIONS * sizeof(char)); + + currMB->b8x8[0].bipred = 0; + currMB->b8x8[1].bipred = 0; + currMB->b8x8[2].bipred = 0; + currMB->b8x8[3].bipred = 0; + + currMB->write_mb = FALSE; + + //initialize the whole MB as INTRA coded + //Blocks are set to notINTRA in write_macroblock + if (p_Inp->UseConstrainedIntraPred) + { + p_Vid->intra_block[currMB->mbAddrX] = 1; + } + + // Initialize bitcounters for this macroblock + memset(&currMB->bits, 0, sizeof(BitCounter)); + + currMB->min_rdcost = DISTBLK_MAX; + currMB->min_dcost = DISTBLK_MAX; + currMB->min_rate = DISTBLK_MAX; + + currMB->best_mode = 0; + currMB->best_c_imode = 0; + currMB->best_i16offset = 0; + currMB->best_cbp = 0; +} + + +/*! + ************************************************************************ + * \brief + * initializes the current macroblock + * + * \param currSlice + * current slice + * \param currMB + * current macroblock + * \param mb_addr + * current macroblock address + * \param mb_field + * true for field macroblock coding + ************************************************************************ + */ +void start_macroblock(Slice *currSlice, Macroblock **currMB, int mb_addr, Boolean mb_field) +{ + VideoParameters *p_Vid = currSlice->p_Vid; + InputParameters *p_Inp = currSlice->p_Inp; + int i, mb_qp; + int use_bitstream_backing = (p_Inp->slice_mode == FIXED_RATE || p_Inp->slice_mode == CALL_BACK); + + static Macroblock *last_coded_mb = NULL; // used to find whether this is recoding an MB + + DataPartition *dataPart; + Bitstream *currStream; + int prev_mb; + + *currMB = &p_Vid->mb_data[mb_addr]; + (*currMB)->p_Slice = currSlice; + (*currMB)->p_Vid = p_Vid; + (*currMB)->p_Inp = p_Inp; + + (*currMB)->mbAddrX = mb_addr; + + (*currMB)->is_intra_block = (currSlice->slice_type == I_SLICE || currSlice->slice_type == SI_SLICE) ? TRUE : FALSE; + + (*currMB)->mb_field = (byte) mb_field; + p_Vid->enc_picture->motion.mb_field[mb_addr] = (byte) mb_field; + + (*currMB)->is_field_mode = (byte) (p_Vid->field_picture || ( currSlice->mb_aff_frame_flag && (*currMB)->mb_field)); + (*currMB)->prev_recode_mb = FALSE; + (*currMB)->DeblockCall = FALSE; + //set buffer; + (*currMB)->intra4x4_pred = (*currMB)->intra4x4_pred_buf[p_Vid->dpb_layer_id]; + (*currMB)->intra8x8_pred = (*currMB)->intra8x8_pred_buf[p_Vid->dpb_layer_id]; + (*currMB)->intra16x16_pred = (*currMB)->intra16x16_pred_buf[p_Vid->dpb_layer_id]; + + set_MB_parameters (currSlice, *currMB); + + prev_mb = FmoGetPreviousMBNr(p_Vid, mb_addr); + + if(use_bitstream_backing) + { + if ((!p_Inp->MbInterlace)||((mb_addr & 0x01)==0)) // KS: MB AFF -> store stream positions for 1st MB only + { + // Keep the current state of the bitstreams + if(!p_Vid->cod_counter) + { + for (i=0; imax_part_nr; ++i) + { + dataPart = &(currSlice->partArr[i]); + currStream = dataPart->bitstream; + currStream->stored_bits_to_go = currStream->bits_to_go; + currStream->stored_byte_pos = currStream->byte_pos; + currStream->stored_byte_buf = currStream->byte_buf; + p_Vid->p_Stats->stored_bit_slice = p_Vid->p_Stats->bit_slice; + + if (currSlice->symbol_mode ==CABAC) + { + dataPart->ee_recode = dataPart->ee_cabac; + } + } + } + } + } + + // Save the slice number of this macroblock. When the macroblock below + // is coded it will use this to decide if prediction for above is possible + (*currMB)->slice_nr = currSlice->slice_nr; + + // Initialize delta qp change from last macroblock. Feature may be used for future rate control + // Rate control + (*currMB)->qpsp = (short) p_Vid->qpsp; + + if (prev_mb > -1 && (p_Vid->mb_data[prev_mb].slice_nr == currSlice->slice_nr)) + { + (*currMB)->PrevMB = &p_Vid->mb_data[prev_mb]; + (*currMB)->prev_qp = (*currMB)->PrevMB->qp; + (*currMB)->prev_dqp = (*currMB)->prev_qp - (*currMB)->PrevMB->prev_qp; + } + else + { + prev_mb = - 1; + (*currMB)->PrevMB = NULL; + (*currMB)->prev_qp = (short) currSlice->qp; + (*currMB)->prev_dqp = 0; + } + + if(p_Inp->RCEnable && (last_coded_mb != *currMB)) //!< avoid decreasing the NumberofbasicUnit for the same MB twice + { + mb_qp = rc_handle_mb( *currMB, prev_mb); + } + else + { + mb_qp = p_Vid->qp; + } + + last_coded_mb = *currMB; // save the address of the last coded MB + + if ((*currMB)->mbAddrX == 0) + p_Vid->BasicUnitQP = mb_qp; + + mb_qp = iClip3(-currSlice->bitdepth_luma_qp_scale, 51, mb_qp); + (*currMB)->qp = (short) mb_qp; + p_Vid->qp = mb_qp; + + update_qp (*currMB); + + + // deblocking filter parameter + if (p_Vid->active_pps->deblocking_filter_control_present_flag) + { + (*currMB)->DFDisableIdc = currSlice->DFDisableIdc; + (*currMB)->DFAlphaC0Offset = currSlice->DFAlphaC0Offset; + (*currMB)->DFBetaOffset = currSlice->DFBetaOffset; + } + else + { + (*currMB)->DFDisableIdc = 0; + (*currMB)->DFAlphaC0Offset = 0; + (*currMB)->DFBetaOffset = 0; + } + (*currMB)->min_rdcost = DISTBLK_MAX; + (*currMB)->min_dcost = DISTBLK_MAX; + (*currMB)->min_rate = DISTBLK_MAX; + + (*currMB)->i16mode = 0; + (*currMB)->best_mode = 10; + + if ((p_Vid->yuv_format!=0) && (p_Vid->yuv_format!=3)) + { + (*currMB)->cbp_linfo_intra = cbp_linfo_intra_normal; + (*currMB)->cbp_linfo_inter = cbp_linfo_inter_normal; + } + else + { + (*currMB)->cbp_linfo_intra = cbp_linfo_intra_other; + (*currMB)->cbp_linfo_inter = cbp_linfo_inter_other; + } + + // Select appropriate MV predictor function + if (currSlice->slice_type != I_SLICE && currSlice->slice_type != SI_SLICE) + { + init_motion_vector_prediction(*currMB, currSlice->mb_aff_frame_flag); + init_ME_engine(*currMB); + } + + // If MB is next to a slice boundary, mark neighboring blocks unavailable for prediction + CheckAvailabilityOfNeighbors(*currMB); + + if (currSlice->symbol_mode == CABAC) + CheckAvailabilityOfNeighborsCABAC(*currMB); + + reset_macroblock(*currMB); + + if ((p_Inp->SearchMode[p_Vid->view_id] == FAST_FULL_SEARCH) && (!p_Inp->IntraProfile)) + reset_fast_full_search (p_Vid); + + // disable writing of trace file +#if TRACE + for (i=0; imax_part_nr; ++i ) + { + currSlice->partArr[i].bitstream->trace_enabled = FALSE; + } +#endif +} + +/*! + ************************************************************************ + * \brief + * terminates processing of the current macroblock depending + * on the chosen slice mode + ************************************************************************ + */ +void end_macroblock(Macroblock *currMB, //!< Current Macroblock + Boolean *end_of_slice, //!< returns true for last macroblock of a slice, otherwise false + Boolean *recode_macroblock //!< returns true if max. slice size is exceeded an macroblock must be recoded in next slice + ) +{ + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + InputParameters *p_Inp = currMB->p_Inp; + int i; + SyntaxElement se; + const int *partMap = assignSE2partition[currSlice->partition_mode]; + DataPartition *dataPart; + Bitstream *currStream; + int rlc_bits=0; + int use_bitstream_backing = (p_Inp->slice_mode == FIXED_RATE || p_Inp->slice_mode == CALL_BACK); + int skip = FALSE; + int new_slice = 0; + + // if previous mb in the same slice group has different slice number as the current, it's the + // the start of new slice + if ((currMB->mbAddrX == 0) || currMB->PrevMB == NULL || ( currMB->PrevMB->slice_nr != currSlice->slice_nr )) + new_slice=1; + + *recode_macroblock=FALSE; + + switch(p_Inp->slice_mode) + { + case NO_SLICES: + ++currSlice->num_mb; + *recode_macroblock = FALSE; + if ((currSlice->num_mb) == (int)p_Vid->PicSizeInMbs) // maximum number of MBs reached + *end_of_slice = TRUE; + + // if it's end of current slice group, slice ends too + *end_of_slice = (Boolean) (*end_of_slice | (currMB->mbAddrX == FmoGetLastCodedMBOfSliceGroup (p_Vid, FmoMB2SliceGroup (p_Vid, currMB->mbAddrX)))); + + break; + case FIXED_MB: + // For slice mode one, check if a new slice boundary follows + ++currSlice->num_mb; + *recode_macroblock = FALSE; + //! Check end-of-slice group condition first + *end_of_slice = (Boolean) (currMB->mbAddrX == FmoGetLastCodedMBOfSliceGroup (p_Vid, FmoMB2SliceGroup (p_Vid, currMB->mbAddrX))); + //! Now check maximum # of MBs in slice + *end_of_slice = (Boolean) (*end_of_slice | (currSlice->num_mb >= p_Inp->slice_argument)); + + break; + + // For slice modes two and three, check if coding of this macroblock + // resulted in too many bits for this slice. If so, indicate slice + // boundary before this macroblock and code the macroblock again + case FIXED_RATE: + // in case of skip MBs check if there is a slice boundary + // only for CAVLC (p_Vid->cod_counter is always 0 in case of CABAC) + if(p_Vid->cod_counter) + { + // write out the skip MBs to know how many bits we need for the RLC + se.value1 = p_Vid->cod_counter; + se.value2 = 0; + se.type = SE_MBTYPE; + dataPart = &(currSlice->partArr[partMap[se.type]]); + + TRACE_SE (se.tracestring, "mb_skip_run"); + writeSE_UVLC(&se, dataPart); + rlc_bits=se.len; + + for (i=0; imax_part_nr; ++i) + { + dataPart = &(currSlice->partArr[i]); + currStream = dataPart->bitstream; + // save the bitstream as it would be if we write the skip MBs + currStream->bits_to_go_skip = currStream->bits_to_go; + currStream->byte_pos_skip = currStream->byte_pos; + currStream->byte_buf_skip = currStream->byte_buf; + // restore the bitstream + currStream->bits_to_go = currStream->stored_bits_to_go; + currStream->byte_pos = currStream->stored_byte_pos; + currStream->byte_buf = currStream->stored_byte_buf; + } + skip = TRUE; + } + //! Check if the last coded macroblock fits into the size of the slice + //! But only if this is not the first macroblock of this slice + if (!new_slice) + { + if(slice_too_big(currSlice, rlc_bits)) + { + *recode_macroblock = TRUE; + *end_of_slice = TRUE; + } + else if(!p_Vid->cod_counter) + skip = FALSE; + } + // maximum number of MBs + + // check if current slice group is finished + if ((*recode_macroblock == FALSE) && (currMB->mbAddrX == FmoGetLastCodedMBOfSliceGroup (p_Vid, FmoMB2SliceGroup (p_Vid, currMB->mbAddrX)))) + { + *end_of_slice = TRUE; + if(!p_Vid->cod_counter) + skip = FALSE; + } + + //! (first MB OR first MB in a slice) AND bigger that maximum size of slice + if (new_slice && slice_too_big(currSlice, rlc_bits)) + { + *end_of_slice = TRUE; + if(!p_Vid->cod_counter) + skip = FALSE; + } + if (!*recode_macroblock) + ++currSlice->num_mb; + break; + + case CALL_BACK: + if (currMB->mbAddrX > 0 && !new_slice) + { + if (currSlice->slice_too_big(rlc_bits)) + { + *recode_macroblock = TRUE; + *end_of_slice = TRUE; + } + } + + if ( (*recode_macroblock == FALSE) && (currMB->mbAddrX == FmoGetLastCodedMBOfSliceGroup (p_Vid, FmoMB2SliceGroup (p_Vid, currMB->mbAddrX)))) + *end_of_slice = TRUE; + break; + + default: + snprintf(errortext, ET_SIZE, "Slice Mode %d not supported", p_Inp->slice_mode); + error(errortext, 600); + } + + if (*recode_macroblock == TRUE) + { + // Restore everything + for (i=0; imax_part_nr; ++i) + { + dataPart = &(currSlice->partArr[i]); + currStream = dataPart->bitstream; + currStream->bits_to_go = currStream->stored_bits_to_go; + currStream->byte_pos = currStream->stored_byte_pos; + currStream->byte_buf = currStream->stored_byte_buf; + p_Vid->p_Stats->bit_slice = p_Vid->p_Stats->stored_bit_slice; + + if (currSlice->symbol_mode == CABAC) + { + dataPart->ee_cabac = dataPart->ee_recode; + } + } + } + + if (currSlice->symbol_mode == CAVLC) + { + // Skip MBs at the end of this slice + dataPart = &(currSlice->partArr[partMap[SE_MBTYPE]]); + if(*end_of_slice == TRUE && skip == TRUE) + { + // only for Slice Mode 2 or 3 + // If we still have to write the skip, let's do it! + if(p_Vid->cod_counter && *recode_macroblock == TRUE) // MB that did not fit in this slice + { + // If recoding is true and we have had skip, + // we have to reduce the counter in case of recoding + p_Vid->cod_counter--; + if(p_Vid->cod_counter) + { + se.value1 = p_Vid->cod_counter; + se.value2 = 0; + se.type = SE_MBTYPE; +#if TRACE + snprintf(se.tracestring, TRACESTRING_SIZE, "Final MB runlength = %3d",p_Vid->cod_counter); +#endif + writeSE_UVLC(&se, dataPart); + rlc_bits=se.len; + currMB->bits.mb_mode = currMB->bits.mb_mode + (unsigned short) rlc_bits; + p_Vid->cod_counter = 0; + } + } + else //! MB that did not fit in this slice anymore is not a Skip MB + { + for (i=0; imax_part_nr; ++i) + { + dataPart = &(currSlice->partArr[i]); + currStream = dataPart->bitstream; + // update the bitstream + currStream->bits_to_go = currStream->bits_to_go_skip; + currStream->byte_pos = currStream->byte_pos_skip; + currStream->byte_buf = currStream->byte_buf_skip; + } + // update the statistics + p_Vid->cod_counter = 0; + skip = FALSE; + } + } + + // Skip MBs at the end of this slice for Slice Mode 0 or 1 + if(*end_of_slice == TRUE && p_Vid->cod_counter && !use_bitstream_backing) + { + se.value1 = p_Vid->cod_counter; + se.value2 = 0; + se.type = SE_MBTYPE; + + TRACE_SE (se.tracestring, "mb_skip_run"); + writeSE_UVLC(&se, dataPart); + + rlc_bits=se.len; + currMB->bits.mb_mode = currMB->bits.mb_mode + (unsigned short) rlc_bits; + p_Vid->cod_counter = 0; + } + } +} + +/*! + ***************************************************************************** + * + * \brief + * For Slice Mode 2: Checks if one partition of one slice exceeds the + * allowed size + * + * \return + * FALSE if all Partitions of this slice are smaller than the allowed size + * TRUE is at least one Partition exceeds the limit + * + * \par Side effects + * none + * + * \date + * 4 November 2001 + * + * \author + * Tobias Oelbaum drehvial@gmx.net + *****************************************************************************/ + +int slice_too_big(Slice *currSlice, int rlc_bits) +{ + InputParameters *p_Inp = currSlice->p_Inp; + DataPartition *dataPart; + Bitstream *currStream; + EncodingEnvironmentPtr eep; + int i; + int size_in_bytes; + + //! CAVLC + if (currSlice->symbol_mode == CAVLC) + { + for (i = 0; i < currSlice->max_part_nr; ++i) + { + dataPart = &(currSlice->partArr[i]); + currStream = dataPart->bitstream; + size_in_bytes = currStream->byte_pos /*- currStream->tmp_byte_pos*/; + + if (currStream->bits_to_go < 8) + ++size_in_bytes; + if (currStream->bits_to_go < rlc_bits) + ++size_in_bytes; + if(size_in_bytes > p_Inp->slice_argument) + return TRUE; + } + } + else //! CABAC + { + for (i=0; imax_part_nr; ++i) + { + dataPart= &(currSlice->partArr[i]); + eep = &(dataPart->ee_cabac); + + if( arienco_bits_written(eep) > (p_Inp->slice_argument*8)) + return TRUE; + } + } + + return FALSE; +} + +/* +The purpose of the actions performed in this function is to prevent that single or 'expensive' coefficients are coded. +With 4x4 transform there is larger chance that a single coefficient in a 8x8 or 16x16 block may be nonzero. +A single small (level=1) coefficient in a 8x8 block will cost: 3 or more bits for the coefficient, +4 bits for EOBs for the 4x4 blocks,possibly also more bits for CBP. Hence the total 'cost' of that single +coefficient will typically be 10-12 bits which in a RD consideration is too much to justify the distortion improvement. +The action below is to watch such 'single' coefficients and set the reconstructed block equal to the prediction according +to a given criterium. The action is taken only for inter luma blocks. + +Notice that this is a pure encoder issue and hence does not have any implication on the standard. +coeff_cost is a parameter set in residual_transform_quant_luma_4x4() and accumulated for each 8x8 block. If level=1 for a coefficient, +coeff_cost is increased by a number depending on RUN for that coefficient.The numbers are (see also residual_transform_quant_luma_4x4()): 3,2,2,1,1,1,0,0,... +when RUN equals 0,1,2,3,4,5,6, etc. +If level >1 coeff_cost is increased by 9 (or any number above 3). The threshold is set to 3. This means for example: +1: If there is one coefficient with (RUN,level)=(0,1) in a 8x8 block this coefficient is discarded. +2: If there are two coefficients with (RUN,level)=(1,1) and (4,1) the coefficients are also discarded +sum_cnt_nonz is the accumulation of coeff_cost over a whole macro block. If sum_cnt_nonz is 5 or less for the whole MB, +all nonzero coefficients are discarded for the MB and the reconstructed block is set equal to the prediction. +*/ +int reset_block(Macroblock* currMB, int *cbp, int64 *cbp_blk, int block8x8) +{ + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + int i, j; + int mb_y = (block8x8 >> 1) << 3; + int mb_x = (block8x8 & 0x01) << 3; + int cbp_mask = 1 << block8x8; + + (*cbp) &= (63 - cbp_mask); + (*cbp_blk) &= ~(51 << (4 * block8x8 - 2 * (block8x8 & 0x01))); + + memset( currSlice->cofAC[block8x8][0][0], 0, 4 * 2 * 65 * sizeof(int)); + + copy_image_data_8x8(&p_Vid->enc_picture->imgY[currMB->pix_y + mb_y], &currSlice->mb_pred[0][mb_y], currMB->pix_x + mb_x, mb_x); + + if (currSlice->slice_type == SP_SLICE || currSlice->slice_type == SI_SLICE) + { + for (i = mb_x; i < mb_x + BLOCK_SIZE_8x8; i += BLOCK_SIZE) + for (j = mb_y; j < mb_y + BLOCK_SIZE_8x8; j += BLOCK_SIZE) + copyblock_sp(currMB, PLANE_Y, i, j); + } + + return 0; +} + +/*! + ************************************************************************ + * \brief + * Residual Coding of an 8x8 Luma block (not for intra) + * + * \return + * coefficient cost + ************************************************************************ + */ +int luma_residual_coding_16x16 (Macroblock* currMB, //!< Current Macroblock to be coded + int block8x8, //!< block number of 8x8 block + int list_mode[2] //!< list prediction mode (1-7, 0=DIRECT) +) +{ + + int *cbp = &(currMB->cbp); + int64 *cbp_blk = &(currMB->cbp_blk); + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + int nonzero = 0, cbp_blk_mask; + int coeff_cost = 0; + int mb_y = (block8x8 >> 1) << 3; + int mb_x = (block8x8 & 0x01) << 3; + int cbp_mask = 1 << block8x8; + + int skipped = (list_mode[0] == 0 && list_mode[1] == 0 && (currSlice->slice_type != B_SLICE)); + + //memset( currSlice->cofAC[block8x8][0][0], 0, 4 * 2 * 65 * sizeof(int)); + + //===== loop over 4x4 blocks ===== + if(!currMB->luma_transform_size_8x8_flag) + { + //===== forward transform, Quantization, inverse Quantization, inverse transform, Reconstruction ===== + if (!skipped && ( (currSlice->NoResidueDirect != 1) || (currMB->qp_scaled[PLANE_Y] == 0 && p_Vid->active_sps->lossless_qpprime_flag == 1) )) + { + int block_y, block_x; + + for (block_y=mb_y; block_yresidual_transform_quant_luma_4x4 (currMB, PLANE_Y, block_x, block_y, &coeff_cost, 0); + + if (nonzero) + { + cbp_blk_mask = (block_x >> 2) + block_y; + (*cbp_blk) |= (int64) 1 << cbp_blk_mask; // one bit for every 4x4 block + (*cbp) |= cbp_mask; // one bit for the 4x4 blocks of an 8x8 block + } + } + } + } + } + else + { + if (currSlice->NoResidueDirect != 1 && !skipped) + { + if (currSlice->slice_type != SP_SLICE && currSlice->slice_type != SI_SLICE) + nonzero = currMB->residual_transform_quant_luma_8x8 (currMB, PLANE_Y, block8x8, &coeff_cost, 0); + + if (nonzero) + { + (*cbp_blk) |= 51 << (4*block8x8 - 2*(block8x8 & 0x01)); // corresponds to 110011, as if all four 4x4 blocks contain coeff, shifted to block position + (*cbp) |= cbp_mask; // one bit for the 4x4 blocks of an 8x8 block + } + } + } + + if (currSlice->NoResidueDirect != 1 && !skipped && coeff_cost <= _LUMA_COEFF_COST_ && + ((currMB->qp_scaled[PLANE_Y])!=0 || p_Vid->active_sps->lossless_qpprime_flag==0)) + { + coeff_cost = reset_block(currMB, cbp, cbp_blk, block8x8); + } + + return coeff_cost; +} + +/*! + ************************************************************************ + * \brief + * Residual Coding of an 8x8 Luma block (not for intra) + * + * \return + * coefficient cost + ************************************************************************ + */ +int luma_residual_coding_8x8 (Macroblock* currMB, //!< Current Macroblock to be coded + int *cbp, //!< Output: cbp (updated according to processed 8x8 luminance block) + int64 *cbp_blk, //!< Output: block cbp (updated according to processed 8x8 luminance block) + int block8x8, //!< block number of 8x8 block + short p_dir, //!< prediction direction + int list_mode[2], //!< list0 prediction mode (1-7, 0=DIRECT) + char *ref_idx) //!< reference pictures for each list +{ + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currSlice->p_Vid; + DecodedPictureBuffer *p_Dpb = p_Vid->p_Dpb_layer[p_Vid->dpb_layer_id]; + int block_y, block_x, pic_pix_x, nonzero = 0, cbp_blk_mask; + int coeff_cost = 0; + int mb_y = (block8x8 >> 1) << 3; + int mb_x = (block8x8 & 0x01) << 3; + int cbp_mask = 1 << block8x8; + int skipped = (list_mode[0] == 0 && list_mode[1] == 0 && (currSlice->slice_type != B_SLICE)); + Boolean direct_inference = (list_mode[0] == 0 && list_mode[1] == 0 && currSlice->slice_type == B_SLICE && p_Vid->active_sps->direct_8x8_inference_flag == 0); + + char bipred_me = currMB->b8x8[block8x8].bipred; + currSlice->coeff_cost_cr[1] = currSlice->coeff_cost_cr[2] = 0; + + //memset( currSlice->cofAC[block8x8][0][0], 0, 4 * 2 * 65 * sizeof(int)); + + //===== loop over 4x4 blocks ===== + if(!currMB->luma_transform_size_8x8_flag) + { + int bxx, byy; // indexing curr_blk + if (!direct_inference && (((p_dir == 0 || p_dir == 2 )&& list_mode[0] < 5) || ((p_dir == 1 || p_dir == 2 ) && list_mode[1] < 5))) + { + p_Dpb->pf_luma_prediction (currMB, mb_x, mb_y, 8, 8, p_dir, list_mode, ref_idx, bipred_me); + + //===== compute prediction residual ====== + compute_residue (&p_Vid->pCurImg[currMB->opix_y + mb_y], &currSlice->mb_pred[0][mb_y], &currSlice->mb_ores[0][mb_y], mb_x, currMB->pix_x + mb_x, 8, 8); + } + + for (byy=0, block_y=mb_y; block_ypix_x + block_x; + cbp_blk_mask = (block_x >> 2) + block_y; + + //===== prediction of 4x4 block ===== + if (direct_inference || !(((p_dir == 0 || p_dir == 2 )&& list_mode[0] < 5) || ((p_dir == 1 || p_dir == 2 ) && list_mode[1] < 5))) + { + p_Dpb->pf_luma_prediction (currMB, block_x, block_y, 4, 4, p_dir, list_mode, ref_idx, bipred_me); + + //===== compute prediction residual ====== + compute_residue(&p_Vid->pCurImg[currMB->opix_y + block_y], &currSlice->mb_pred[0][block_y], &currSlice->mb_ores[0][block_y], block_x, pic_pix_x, 4, 4); + } + + //===== forward transform, Quantization, inverse Quantization, inverse transform, Reconstruction ===== + if (!skipped && ( (currSlice->NoResidueDirect != 1) || (currMB->qp_scaled[PLANE_Y] == 0 && p_Vid->active_sps->lossless_qpprime_flag == 1) )) + { + //===== forward transform, Quantization, inverse Quantization, inverse transform, Reconstruction ===== + nonzero = currMB->residual_transform_quant_luma_4x4 (currMB, PLANE_Y, block_x, block_y, &coeff_cost, 0); + + if (nonzero) + { + (*cbp_blk) |= (int64)1 << cbp_blk_mask; // one bit for every 4x4 block + (*cbp) |= cbp_mask; // one bit for the 4x4 blocks of an 8x8 block + } + } + } + } + } + else + { + block_y = mb_y; + block_x = mb_x; + pic_pix_x = currMB->pix_x + block_x; + + cbp_blk_mask = (block_x>>2) + block_y; + + //===== prediction of 4x4 block ===== + p_Dpb->pf_luma_prediction (currMB, block_x, block_y, 8, 8, p_dir, list_mode, ref_idx, bipred_me); + + //===== compute prediction residual ====== + compute_residue (&p_Vid->pCurImg[currMB->opix_y + block_y], &currSlice->mb_pred[0][block_y], &currSlice->mb_ores[0][block_y], block_x, pic_pix_x, 8, 8); + + if (currSlice->NoResidueDirect != 1 && !skipped) + { + if (currSlice->slice_type != SP_SLICE && currSlice->slice_type != SI_SLICE) + nonzero = currMB->residual_transform_quant_luma_8x8 (currMB, PLANE_Y, block8x8, &coeff_cost, 0); + + if (nonzero) + { + (*cbp_blk) |= 51 << (4*block8x8 - 2*(block8x8 & 0x01)); // corresponds to 110011, as if all four 4x4 blocks contain coeff, shifted to block position + (*cbp) |= cbp_mask; // one bit for the 4x4 blocks of an 8x8 block + } + } + } + + if (currSlice->NoResidueDirect != 1 && !skipped && coeff_cost <= _LUMA_COEFF_COST_ && + ((currMB->qp_scaled[PLANE_Y])!=0 || p_Vid->active_sps->lossless_qpprime_flag==0)) + { + coeff_cost = reset_block(currMB, cbp, cbp_blk, block8x8); + } + + return coeff_cost; +} + +/*! + ************************************************************************ + * \brief + * Set mode parameters and reference frames for an 8x8 block + ************************************************************************ + */ +void set_modes_and_reframe (Macroblock* currMB, int b8, short* p_dir, int list_mode[2], char *list_ref_idx) +{ + VideoParameters *p_Vid = currMB->p_Vid; + Slice *currSlice = currMB->p_Slice; + PicMotionParams **motion = p_Vid->enc_picture->mv_info; + int j = 2*(b8>>1); + int i = 2*(b8 & 0x01); + + list_mode[0] = list_mode[1] = list_ref_idx[0] = list_ref_idx[1] = -1; + + *p_dir = currMB->b8x8[b8].pdir; + + if (currSlice->slice_type != B_SLICE) + { + list_ref_idx[0] = motion[currMB->block_y + j][currMB->block_x + i].ref_idx[LIST_0]; + list_ref_idx[1] = 0; + list_mode[0] = currMB->b8x8[b8].mode; + list_mode[1] = 0; + } + else + { + if (*p_dir == -1) + { + list_ref_idx[0] = -1; + list_ref_idx[1] = -1; + list_mode[0] = 0; + list_mode[1] = 0; + } + else if (*p_dir == 0) + { + list_ref_idx[0] = motion[currMB->block_y + j][currMB->block_x + i].ref_idx[LIST_0]; + list_ref_idx[1] = 0; + list_mode[0] = currMB->b8x8[b8].mode; + list_mode[1] = 0; + } + else if (*p_dir == 1) + { + list_ref_idx[0] = 0; + list_ref_idx[1] = motion[currMB->block_y + j][currMB->block_x + i].ref_idx[LIST_1]; + list_mode[0] = 0; + list_mode[1] = currMB->b8x8[b8].mode; + } + else + { + list_ref_idx[0] = motion[currMB->block_y + j][currMB->block_x + i].ref_idx[LIST_0]; + list_ref_idx[1] = motion[currMB->block_y + j][currMB->block_x + i].ref_idx[LIST_1]; + list_mode[0] = currMB->b8x8[b8].mode; + list_mode[1] = currMB->b8x8[b8].mode; + } + } +} + + +/*! + ************************************************************************ + * \brief + * Set mode parameters and reference frames for an 8x8 block + ************************************************************************ + */ +void set_modes_and_reframe_i_slice (Macroblock* currMB, int b8, short* p_dir, int list_mode[2], char *list_ref_idx) +{ + *p_dir = currMB->b8x8[b8].pdir; + + list_ref_idx[0] = -1; + list_ref_idx[1] = 0; + list_mode[0] = currMB->b8x8[b8].mode; + list_mode[1] = 0; +} + +/*! + ************************************************************************ + * \brief + * Set mode parameters and reference frames for an 8x8 block + ************************************************************************ + */ +void set_modes_and_reframe_p_slice (Macroblock* currMB, int b8, short* p_dir, int list_mode[2], char *list_ref_idx) +{ + VideoParameters *p_Vid = currMB->p_Vid; + PicMotionParams **motion = p_Vid->enc_picture->mv_info; + int j = 2*(b8>>1); + int i = 2*(b8 & 0x01); + + *p_dir = currMB->b8x8[b8].pdir; + + list_ref_idx[0] = motion[currMB->block_y + j][currMB->block_x + i].ref_idx[LIST_0]; + list_ref_idx[1] = 0; + list_mode[0] = currMB->b8x8[b8].mode; + list_mode[1] = 0; +} + + + +/*! + ************************************************************************ + * \brief + * Set mode parameters and reference frames for an 8x8 block + ************************************************************************ + */ +void set_modes_and_reframe_b_slice (Macroblock* currMB, int b8, short* p_dir, int list_mode[2], char *list_ref_idx) +{ + *p_dir = currMB->b8x8[b8].pdir; + + if (*p_dir == -1) + { + list_ref_idx[0] = -1; + list_ref_idx[1] = -1; + list_mode[0] = 0; + list_mode[1] = 0; + } + else if (*p_dir == 0) + { + VideoParameters *p_Vid = currMB->p_Vid; + PicMotionParams **motion = p_Vid->enc_picture->mv_info; + int j = 2*(b8>>1); + int i = 2*(b8 & 0x01); + + list_ref_idx[0] = motion[currMB->block_y + j][currMB->block_x + i].ref_idx[LIST_0]; + list_ref_idx[1] = 0; + list_mode[0] = currMB->b8x8[b8].mode; + list_mode[1] = 0; + } + else if (*p_dir == 1) + { + VideoParameters *p_Vid = currMB->p_Vid; + PicMotionParams **motion = p_Vid->enc_picture->mv_info; + int j = 2*(b8>>1); + int i = 2*(b8 & 0x01); + + list_ref_idx[0] = 0; + list_ref_idx[1] = motion[currMB->block_y + j][currMB->block_x + i].ref_idx[LIST_1]; + list_mode[0] = 0; + list_mode[1] = currMB->b8x8[b8].mode; + } + else + { + VideoParameters *p_Vid = currMB->p_Vid; + PicMotionParams **motion = p_Vid->enc_picture->mv_info; + int j = 2*(b8>>1); + int i = 2*(b8 & 0x01); + + list_ref_idx[0] = motion[currMB->block_y + j][currMB->block_x + i].ref_idx[LIST_0]; + list_ref_idx[1] = motion[currMB->block_y + j][currMB->block_x + i].ref_idx[LIST_1]; + list_mode[0] = currMB->b8x8[b8].mode; + list_mode[1] = currMB->b8x8[b8].mode; + } +} + + +/*! + ************************************************************************ + * \brief + * Residual Coding of a Luma macroblock (not for intra) + ************************************************************************ + */ +void luma_residual_coding (Macroblock *currMB) +{ + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + DecodedPictureBuffer *p_Dpb = p_Vid->p_Dpb_layer[p_Vid->dpb_layer_id]; + + int block8x8; + int list_mode[2]; + char list_ref_idx[2]; + short p_dir = 0; + + int is_skip = currSlice->slice_type == P_SLICE && currMB->mb_type == PSKIP; + + currMB->cbp = 0; + currMB->cbp_blk = 0; + // the following parameters are not useful and should be removed for profiles < P444 + currSlice->cmp_cbp[1] = currSlice->cmp_cbp[2] = 0; + currSlice->cur_cbp_blk[1] = currSlice->cur_cbp_blk[2] = 0; + + if (is_skip) + { + for (block8x8=0; block8x8<4; ++block8x8) + { + currSlice->set_modes_and_reframe (currMB, block8x8, &p_dir, list_mode, list_ref_idx); + } + + //===== luma prediction ====== + p_Dpb->pf_luma_prediction (currMB, 0, 0, 16, 16, p_dir, list_mode, list_ref_idx, currMB->b8x8[0].bipred); + + //===== compute prediction residual ====== + // We should not need to compute this for skip mode, but currently computed for "debugging" purposes + compute_residue (&p_Vid->pCurImg[currMB->opix_y], *currSlice->mb_pred, *currSlice->mb_ores, 0, currMB->pix_x, MB_BLOCK_SIZE, MB_BLOCK_SIZE); + + copy_image_data_16x16(&p_Vid->enc_picture->imgY[currMB->pix_y], *currSlice->mb_pred, currMB->pix_x, 0); + } + else + { + int sum_cnt_nonz = 0; + if (currMB->mb_type == P16x16) + { + for (block8x8 = 0; block8x8 < 4; ++block8x8) + { + currSlice->set_modes_and_reframe (currMB, block8x8, &p_dir, list_mode, list_ref_idx); + } + //===== luma prediction ====== + p_Dpb->pf_luma_prediction (currMB, 0, 0, MB_BLOCK_SIZE, MB_BLOCK_SIZE, p_dir, list_mode, list_ref_idx, currMB->b8x8[0].bipred); + + //===== compute prediction residual ====== + compute_residue (&p_Vid->pCurImg[currMB->opix_y], *currSlice->mb_pred, *currSlice->mb_ores, 0, currMB->pix_x, MB_BLOCK_SIZE, MB_BLOCK_SIZE); + + // Luma residual coding (16x16 mode) + for (block8x8 = 0; block8x8 < 4; ++block8x8) + { + sum_cnt_nonz += luma_residual_coding_16x16 (currMB, block8x8, list_mode); + } + } + else + { + for (block8x8 = 0; block8x8 < 4; ++block8x8) + { + currSlice->set_modes_and_reframe (currMB, block8x8, &p_dir, list_mode, list_ref_idx); + sum_cnt_nonz += currSlice->luma_residual_coding_8x8 (currMB, &(currMB->cbp), &(currMB->cbp_blk), block8x8, p_dir, list_mode, list_ref_idx); + } + } + + // reset information (coefficient thresholding) + if (sum_cnt_nonz <= _LUMA_MB_COEFF_COST_ && ((currMB->qp_scaled[PLANE_Y])!=0 || p_Vid->active_sps->lossless_qpprime_flag == 0)) + { + currMB->cbp &= 0xfffff0 ; + currMB->cbp_blk &= 0xff0000 ; + + copy_image_data_16x16(&p_Vid->enc_picture->imgY[currMB->pix_y], currSlice->mb_pred[0], currMB->pix_x, 0); + //memset( currSlice->cofAC[0][0][0], 0, 2080 * sizeof(int)); // 4 * 4 * 2 * 65 + } + } +} + + +/*! + ************************************************************************ + * \brief + * Residual Coding of a Luma macroblock (not for intra) + ************************************************************************ + */ +void luma_residual_coding_sp (Macroblock *currMB) +{ + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + DecodedPictureBuffer *p_Dpb = p_Vid->p_Dpb_layer[p_Vid->dpb_layer_id]; + + int i, j, block8x8, b8_x, b8_y; + int list_mode[2]; + char list_ref_idx[2]; + short p_dir = 0; + int sum_cnt_nonz = 0; + + int is_skip = currSlice->slice_type == P_SLICE && currMB->mb_type == PSKIP; + + currMB->cbp = 0; + currMB->cbp_blk = 0; + // the following parameters are not useful and should be removed for profiles < P444 + currSlice->cmp_cbp[1] = currSlice->cmp_cbp[2] = 0; + currSlice->cur_cbp_blk[1] = currSlice->cur_cbp_blk[2] = 0; + + if (is_skip || currMB->mb_type == 1) + { + for (block8x8=0; block8x8<4; ++block8x8) + { + currSlice->set_modes_and_reframe (currMB, block8x8, &p_dir, list_mode, list_ref_idx); + } + + //===== luma prediction ====== + p_Dpb->pf_luma_prediction (currMB, 0, 0, 16, 16, p_dir, list_mode, list_ref_idx, currMB->b8x8[0].bipred); + + //===== compute prediction residual ====== + // We should not need to compute this for skip mode, but currently computed for "debugging" purposes + compute_residue (&p_Vid->pCurImg[currMB->opix_y], &currSlice->mb_pred[0][0], &currSlice->mb_ores[0][0], 0, currMB->pix_x, 16, 16); + + // Luma residual coding (16x16 mode) + if (!is_skip) + { + for (block8x8=0; block8x8<4; ++block8x8) + { + sum_cnt_nonz += luma_residual_coding_16x16 (currMB, block8x8, list_mode); + } + } + } + else + { + for (block8x8=0; block8x8<4; ++block8x8) + { + currSlice->set_modes_and_reframe (currMB, block8x8, &p_dir, list_mode, list_ref_idx); + sum_cnt_nonz += currSlice->luma_residual_coding_8x8 (currMB, &(currMB->cbp), &(currMB->cbp_blk), block8x8, p_dir, list_mode, list_ref_idx); + } + } + + // reset information (coefficient thresholding) + + if ((is_skip || + (sum_cnt_nonz <= _LUMA_MB_COEFF_COST_ && ((currMB->qp_scaled[PLANE_Y])!=0 || p_Vid->active_sps->lossless_qpprime_flag == 0))))// modif ES added last set of conditions + { + currMB->cbp &= 0xfffff0 ; + currMB->cbp_blk &= 0xff0000 ; + + copy_image_data_16x16(&p_Vid->enc_picture->imgY[currMB->pix_y], currSlice->mb_pred[0], currMB->pix_x, 0); + + //memset( currSlice->cofAC[0][0][0], 0, 2080 * sizeof(int)); // 4 * 4 * 2 * 65 + + for(block8x8 = 0;block8x8 < 4; ++block8x8) + { + b8_x=(block8x8&1)<<3; + b8_y=(block8x8&2)<<2; + for (i = b8_x; i < b8_x + BLOCK_SIZE_8x8; i += 4) + for (j = b8_y; j < b8_y + BLOCK_SIZE_8x8;j += 4) + copyblock_sp(currMB, PLANE_Y, i, j); + } + } +} + +/*! + ************************************************************************ + * \brief + * RDOff decision between 4x4 and 8x8 transform + ************************************************************************ + */ +byte transform_decision (Macroblock *currMB, int block_check, distblk *cost) +{ + if(currMB->p_Inp->Transform8x8Mode == 2) //always allow 8x8 transform + { + return 1; + } + else + { + VideoParameters *p_Vid = currMB->p_Vid; + Slice *currSlice = currMB->p_Slice; + DecodedPictureBuffer *p_Dpb = p_Vid->p_Dpb_layer[p_Vid->dpb_layer_id]; + int pic_pix_y, pic_pix_x, i, j; + int mb_y, mb_x, block8x8; + int list_mode[2]; + char list_ref_idx[2]; + short p_dir; + int num_blks; + distblk cost8x8=0, cost4x4=0; + char bipred_me; + imgpel **mb_pred = currSlice->mb_pred[0]; + short diff16[4][16]; + short diff64[64]; + int index; + + if(block_check == -1) + { + block8x8 = 0; + num_blks = 4; + } + else + { + block8x8 = block_check; + num_blks = block_check + 1; + } + + for (; block8x8set_modes_and_reframe (currMB, block8x8, &p_dir, list_mode, list_ref_idx); + bipred_me = currMB->b8x8[block8x8].bipred; + mb_y = (block8x8 >> 1) << 3; + mb_x = (block8x8 & 0x01) << 3; + + //===== prediction of 8x8 block ===== + p_Dpb->pf_luma_prediction (currMB, mb_x, mb_y, 8, 8, p_dir, list_mode, list_ref_idx, bipred_me); + + //===== loop over 8x8 blocks ===== + pic_pix_y = currMB->opix_y; + pic_pix_x = currMB->pix_x; + //===== get displaced frame difference ====== + for (j = mb_y; j < 8 + mb_y; j++) + { + for (i = mb_x; i < 8 + mb_x; i++) + { + index = 2 * ((j - mb_y)> 3) + ((i - mb_x)> 3); + *tmp64++ = *(tmp16[index])++ = (short) (p_Vid->pCurImg[pic_pix_y + j][pic_pix_x + i] - mb_pred[j][i]); + } + } + + cost4x4 += p_Vid->distortion4x4 (diff16[0], DISTBLK_MAX); + cost4x4 += p_Vid->distortion4x4 (diff16[1], DISTBLK_MAX); + cost4x4 += p_Vid->distortion4x4 (diff16[2], DISTBLK_MAX); + cost4x4 += p_Vid->distortion4x4 (diff16[3], DISTBLK_MAX); + cost8x8 += p_Vid->distortion8x8 (diff64 , DISTBLK_MAX); + } + + + if(cost8x8 < cost4x4) + { + return 1; + } + else + { + *cost += (cost4x4 - cost8x8); + return 0; + } + } +} + +/*! + ************************************************************************ + * \brief + * Chroma residual coding for an macroblock + ************************************************************************ + */ +void chroma_residual_coding (Macroblock *currMB) +{ + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currSlice->p_Vid; + int chroma_cbp = 0; + int uv, block_y, block_x; + int list_mode[2]; + char list_ref_idx[2]; + short p_dir; + int skipped = (currMB->mb_type == 0 && (currSlice->slice_type == P_SLICE)); + int yuv = p_Vid->yuv_format - 1; + + for (uv = 0; uv < 2; ++uv) + { + //===== prediction of chrominance blocks ===d== + // Intra modes + if (currMB->mb_type > P8x8) + { + for (block_y=0; block_y < p_Vid->mb_cr_size_y; block_y += BLOCK_SIZE) + { + for (block_x=0; block_x < p_Vid->mb_cr_size_x; block_x += BLOCK_SIZE) + { + int block8 = block8x8_idx[yuv][block_y>>2][block_x>>2]; + currSlice->set_modes_and_reframe (currMB, block8, &p_dir, list_mode, list_ref_idx); + IntraChromaPrediction4x4 (currMB, uv + 1, block_x, block_y); + } + } + } + else + { + for (block_y = 0; block_y < p_Vid->mb_cr_size_y; block_y += BLOCK_SIZE) + { + for (block_x = 0; block_x < p_Vid->mb_cr_size_x; block_x += BLOCK_SIZE) + { + int block8 = block8x8_idx[yuv][block_y>>2][block_x>>2]; + char bipred_me = currMB->b8x8[block8].bipred; + currSlice->set_modes_and_reframe (currMB, block8, &p_dir, list_mode, list_ref_idx); + + chroma_prediction_4x4 (currMB, uv, block_x, block_y, p_dir, list_mode[0], list_mode[1], list_ref_idx[0], list_ref_idx[1], bipred_me); + } + } + } + + // ==== set chroma residue to zero for skip Mode in SP frames + if (currSlice->NoResidueDirect) + { + copy_image_data(&p_Vid->enc_picture->imgUV[uv][currMB->pix_c_y], currSlice->mb_pred[ uv + 1], currMB->pix_c_x, 0, p_Vid->mb_cr_size_x, p_Vid->mb_cr_size_y); + } + else if (skipped) + { + copy_image_data(&p_Vid->enc_picture->imgUV[uv][currMB->pix_c_y], currSlice->mb_pred[ uv + 1], currMB->pix_c_x, 0, p_Vid->mb_cr_size_x, p_Vid->mb_cr_size_y); + } + else + { + compute_residue(&p_Vid->pImgOrg[uv + 1][currMB->opix_c_y ], currSlice->mb_pred[ uv + 1], currSlice->mb_ores[uv + 1], 0, currMB->pix_c_x, p_Vid->mb_cr_size_x, p_Vid->mb_cr_size_y); + + //===== forward transform, Quantization, inverse Quantization, inverse transform, and Reconstruction ===== + chroma_cbp = currMB->residual_transform_quant_chroma_4x4[uv] (currMB, uv, chroma_cbp); + } + } + + //===== update currMB->cbp ===== + currMB->cbp += ((chroma_cbp) << 4); +} + +/*! + ************************************************************************ + * \brief + * Chroma residual coding for an macroblock + ************************************************************************ + */ +void chroma_residual_coding_si (Macroblock *currMB) +{ + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currSlice->p_Vid; + int chroma_cbp = 0; + int uv, block_y, block_x; + int list_mode[2]; + char list_ref_idx[2]; + short p_dir; + int yuv = p_Vid->yuv_format - 1; + + for (uv = 0; uv < 2; ++uv) + { + //===== prediction of chrominance blocks ===d== + for (block_y=0; block_y < p_Vid->mb_cr_size_y; block_y+=4) + { + for (block_x=0; block_x < p_Vid->mb_cr_size_x; block_x+=4) + { + int block8 = block8x8_idx[yuv][block_y>>2][block_x>>2]; + currSlice->set_modes_and_reframe (currMB, block8, &p_dir, list_mode, list_ref_idx); + IntraChromaPrediction4x4 (currMB, uv + 1, block_x, block_y); + } + } + + compute_residue(&p_Vid->pImgOrg[uv + 1][currMB->opix_c_y ], currSlice->mb_pred[ uv + 1], currSlice->mb_ores[uv + 1], 0, currMB->pix_c_x, p_Vid->mb_cr_size_x, p_Vid->mb_cr_size_y); + + //===== forward transform, Quantization, inverse Quantization, inverse transform, and Reconstruction ===== + if (currMB->mb_type==I16MB || currMB->mb_type == I4MB) + { + chroma_cbp = residual_transform_quant_chroma_4x4(currMB, uv, chroma_cbp); + } + else + { + chroma_cbp = residual_transform_quant_chroma_4x4_sp2(currMB, uv, chroma_cbp); + } + } + + //===== update currMB->cbp ===== + currMB->cbp += ((chroma_cbp) << 4); +} + + +/*! + ************************************************************************ + * \brief + * Chroma residual coding for an macroblock + ************************************************************************ + */ +void chroma_residual_coding_sp (Macroblock *currMB) +{ + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currSlice->p_Vid; + int chroma_cbp; + int uv, block_y, block_x, j; + int list_mode[2]; + char list_ref_idx[2]; + short p_dir; + int skipped = (currMB->mb_type == 0); + int yuv = p_Vid->yuv_format - 1; + + + for (chroma_cbp = 0, uv=0; uv<2; ++uv) + { + //===== prediction of chrominance blocks ===d== + // Intra modes + if (currMB->mb_type > P8x8) + { + for (block_y=0; block_y < p_Vid->mb_cr_size_y; block_y += BLOCK_SIZE) + { + for (block_x=0; block_x < p_Vid->mb_cr_size_x; block_x += BLOCK_SIZE) + { + int block8 = block8x8_idx[yuv][block_y>>2][block_x>>2]; + currSlice->set_modes_and_reframe (currMB, block8, &p_dir, list_mode, list_ref_idx); + IntraChromaPrediction4x4 (currMB, uv + 1, block_x, block_y); + } + } + } + else + { + for (block_y=0; block_y < p_Vid->mb_cr_size_y; block_y += BLOCK_SIZE) + { + for (block_x=0; block_x < p_Vid->mb_cr_size_x; block_x += BLOCK_SIZE) + { + int block8 = block8x8_idx[yuv][block_y>>2][block_x>>2]; + char bipred_me = currMB->b8x8[block8].bipred; + currSlice->set_modes_and_reframe (currMB, block8, &p_dir, list_mode, list_ref_idx); + + chroma_prediction_4x4 (currMB, uv, block_x, block_y, p_dir, list_mode[0], list_mode[1], list_ref_idx[0], list_ref_idx[1], bipred_me); + } + } + } + + // ==== set chroma residue to zero for skip Mode in SP frames + if (currSlice->NoResidueDirect) + { + copy_image_data(&p_Vid->enc_picture->imgUV[uv][currMB->pix_c_y], currSlice->mb_pred[ uv + 1], currMB->pix_c_x, 0, p_Vid->mb_cr_size_x, p_Vid->mb_cr_size_y); + } + else if (skipped) + { + for (j=0; jmb_cr_size_y; ++j) + memset(currSlice->mb_ores[uv + 1][j], 0 , p_Vid->mb_cr_size_x * sizeof(int)); + for (j=0; jmb_cr_size_y; ++j) + memset(currSlice->mb_rres[uv + 1][j], 0 , p_Vid->mb_cr_size_x * sizeof(int)); + } + else + { + compute_residue(&p_Vid->pImgOrg[uv + 1][currMB->opix_c_y ], &currSlice->mb_pred[ uv + 1][0], &currSlice->mb_ores[uv + 1][0], 0, currMB->pix_c_x, p_Vid->mb_cr_size_x, p_Vid->mb_cr_size_y); + } + + //===== forward transform, Quantization, inverse Quantization, inverse transform, and Reconstruction ===== + //===== Call function for skip mode in SP frames to properly process frame ==== + + if (skipped) + { + if(p_Vid->sp2_frame_indicator) + chroma_cbp = residual_transform_quant_chroma_4x4_sp2(currMB, uv, chroma_cbp); + else + chroma_cbp = residual_transform_quant_chroma_4x4_sp(currMB, uv, chroma_cbp); + } + else + { + if (!currSlice->NoResidueDirect && !skipped) + { + if (currMB->mb_type==I16MB || currMB->mb_type == I4MB) + { + chroma_cbp = residual_transform_quant_chroma_4x4(currMB, uv,chroma_cbp); + } + else + { + if(p_Vid->sp2_frame_indicator) + chroma_cbp = residual_transform_quant_chroma_4x4_sp2(currMB, uv, chroma_cbp);// switching SP frames + else + chroma_cbp=residual_transform_quant_chroma_4x4_sp(currMB, uv, chroma_cbp); + } + } + } + } + + //===== update currMB->cbp ===== + currMB->cbp += ((chroma_cbp)<<4); +} + +/*! + ************************************************************************ + * \brief + * Check if all reference frames for a macroblock are zero + ************************************************************************ + */ +int ZeroRef (Macroblock* currMB) +{ + PicMotionParams **motion = currMB->p_Vid->enc_picture->mv_info; + int i,j; + + for (j=currMB->block_y; jblock_y + BLOCK_MULTIPLE; ++j) + { + for (i=currMB->block_x; iblock_x + BLOCK_MULTIPLE; ++i) + { + if (motion[j][i].ref_idx[LIST_0] != 0) + return 0; + } + } + return 1; +} + + +/*! + ************************************************************************ + * \brief + * Converts macroblock type to coding value + ************************************************************************ + */ +int MBType2Value (Macroblock* currMB) +{ + Slice *currSlice = currMB->p_Slice; + static const int dir1offset[3] = { 1, 2, 3}; + static const int dir2offset[3][3] = {{ 0, 4, 8}, // 1. block forward + { 6, 2, 10}, // 1. block backward + {12, 14, 16}}; // 1. block bi-directional + + int si_slice = (currSlice->slice_type == SI_SLICE) ? 1 : 0; + + if (currSlice->slice_type != B_SLICE) + { + switch( currMB->mb_type ) + { + case I8MB: + case I4MB: + return ((currSlice->slice_type == I_SLICE || si_slice) ? (0 + si_slice) : 6); + break; + case SI4MB: + return 0; + break; + case I16MB: + return ((currSlice->slice_type == I_SLICE || si_slice) ? (0 + si_slice) : 6) + currMB->i16offset; + break; + case IPCM: + return ((currSlice->slice_type == I_SLICE || si_slice ) ? (25 + si_slice) : 31); + break; + case P8x8: + { + if (currSlice->symbol_mode==CAVLC && ZeroRef (currMB)) + return 5; + else + return 4; + } + break; + default: + return currMB->mb_type; + break; + } + } + else + { + switch( currMB->mb_type ) + { + case 0: + return 0; + break; + case I4MB: + case I8MB: + return 23; + break; + case I16MB: + return 23 + currMB->i16offset; + break; + case IPCM: + return 48; + break; + case P8x8: + return 22; + break; + case 1: + { + int pdir0 = currMB->b8x8[0].pdir; + return dir1offset[pdir0]; + } + break; + case 2: + { + int pdir0 = currMB->b8x8[0].pdir; + int pdir1 = currMB->b8x8[3].pdir; + + return 4 + dir2offset[pdir0][pdir1]; + } + break; + default: + { + int pdir0 = currMB->b8x8[0].pdir; + int pdir1 = currMB->b8x8[3].pdir; + return 5 + dir2offset[pdir0][pdir1]; + } + break; + } + } +} + +/*! +************************************************************************ +* \brief +* Writes 4x4 intra prediction modes for a macroblock +************************************************************************ +*/ +static int writeIntra4x4Modes(Macroblock *currMB) +{ + Slice *currSlice = currMB->p_Slice; + int i; + SyntaxElement se; + BitCounter *mbBits = &currMB->bits; + const int *partMap = assignSE2partition[currSlice->partition_mode]; + DataPartition *dataPart = &(currSlice->partArr[partMap[SE_INTRAPREDMODE]]); + + int rate = 0; + + currMB->IntraChromaPredModeFlag = 1; + + for(i = 0; i < 16; ++i) + { + se.context = i; + se.value1 = currMB->intra_pred_modes[i]; + se.value2 = 0; + +#if TRACE + if (se.value1 < 0 ) + snprintf(se.tracestring, TRACESTRING_SIZE, "Intra 4x4 mode = predicted (context: %d)",se.context); + else + snprintf(se.tracestring, TRACESTRING_SIZE, "Intra 4x4 mode = %3d (context: %d)",se.value1,se.context); +#endif + + // set symbol type and function pointers + se.type = SE_INTRAPREDMODE; + + // encode and update rate + currSlice->writeIntraPredMode (&se, dataPart); + + rate += se.len; + } + + mbBits->mb_mode += rate; + + return rate; +} + +/*! +************************************************************************ +* \brief +* Writes 8x8 intra prediction modes for a macroblock +************************************************************************ +*/ +static int writeIntra8x8Modes(Macroblock *currMB) +{ + Slice *currSlice = currMB->p_Slice; + int block8x8; + SyntaxElement se; + BitCounter *mbBits = &currMB->bits; + const int *partMap = assignSE2partition[currSlice->partition_mode]; + DataPartition *dataPart = &(currSlice->partArr[partMap[SE_INTRAPREDMODE]]); + + int rate = 0; + + currMB->IntraChromaPredModeFlag = 1; + + for(block8x8 = 0; block8x8 < 16; block8x8 += 4) + { + + se.context = block8x8; + se.value1 = currMB->intra_pred_modes8x8[block8x8]; + se.value2 = 0; + +#if TRACE + if (se.value1 < 0 ) + snprintf(se.tracestring, TRACESTRING_SIZE, "Intra 8x8 mode = predicted (context: %d)",se.context); + else + snprintf(se.tracestring, TRACESTRING_SIZE, "Intra 8x8 mode = %3d (context: %d)",se.value1,se.context); +#endif + + // set symbol type and function pointers + se.type = SE_INTRAPREDMODE; + + // encode and update rate + currSlice->writeIntraPredMode (&se, dataPart); + + rate += se.len; + } + mbBits->mb_mode += rate; + + return rate; +} + +static int writeIntraModes(Macroblock *currMB) +{ + switch (currMB->mb_type) + { + case I4MB: + return writeIntra4x4Modes(currMB); + break; + case I8MB: + return writeIntra8x8Modes(currMB); + break; + default: + return 0; + break; + } +} + +/*! + ************************************************************************ + * \brief + * Converts 8x8 block type to coding value + ************************************************************************ + */ +int B8Mode2Value (Slice *currSlice, short b8mode, short b8pdir) +{ + static const int b8start[8] = {0,0,0,0, 1, 4, 5, 10}; + static const int b8inc [8] = {0,0,0,0, 1, 2, 2, 1}; + + return (currSlice->slice_type!=B_SLICE) ? (b8mode - 4) : b8start[b8mode] + b8inc[b8mode] * b8pdir; +} + +static int writeIPCMLuma (Macroblock *currMB, imgpel **imgY, Bitstream *currStream, SyntaxElement *se, int *bitCount) +{ + VideoParameters *p_Vid = currMB->p_Vid; + int i, j; + int bit_size = MB_BLOCK_SIZE * MB_BLOCK_SIZE * p_Vid->bitdepth_luma; + + *bitCount += bit_size; + se->len = p_Vid->bitdepth_luma; + se->type = SE_MBTYPE; + + for (j = currMB->pix_y; j < currMB->pix_y + MB_BLOCK_SIZE; ++j) + { + for (i = currMB->pix_x; i < currMB->pix_x + MB_BLOCK_SIZE; ++i) + { + se->bitpattern = imax(p_Vid->min_IPCM_value, imgY[j][i]); + se->value1 = se->bitpattern; +#if TRACE + snprintf(se->tracestring, TRACESTRING_SIZE, "pcm_sample_luma (%d %d) = %d", j, i, se->bitpattern); +#endif + writeSE_Fix(se, currStream); + } + } + return bit_size; +} + +static int writeIPCMChroma (Macroblock *currMB, imgpel **imgUV, Bitstream *currStream, SyntaxElement *se, int *bitCount) +{ + VideoParameters *p_Vid = currMB->p_Vid; + int i, j; + int bit_size = p_Vid->mb_cr_size_y * p_Vid->mb_cr_size_x * p_Vid->bitdepth_luma; + *bitCount += bit_size; + + se->len = p_Vid->bitdepth_chroma; + se->type = SE_MBTYPE; + + for (j = currMB->pix_c_y; j < currMB->pix_c_y + p_Vid->mb_cr_size_y; ++j) + { + for (i = currMB->pix_c_x; i < currMB->pix_c_x + p_Vid->mb_cr_size_x; ++i) + { + se->bitpattern = imax(p_Vid->min_IPCM_value, imgUV[j][i]); + se->value1 = se->bitpattern; +#if TRACE + //snprintf(se.tracestring, TRACESTRING_SIZE, "pcm_sample_chroma (%s) (%d %d) = %d", uv?"v":"u", j,i,se->bitpattern); + snprintf(se->tracestring, TRACESTRING_SIZE, "pcm_sample_chroma (%d %d) = %d", j, i, se->bitpattern); +#endif + writeSE_Fix(se, currStream); + } + } + return bit_size; +} + +static int writeIPCMByteAlign(Bitstream *currStream, SyntaxElement *se, int *bitCount) +{ + if (currStream->bits_to_go < 8) + { + // This will only happen in the CAVLC case, CABAC is already padded + se->type = SE_MBTYPE; + se->len = currStream->bits_to_go; + *bitCount += se->len; + se->bitpattern = 0; +#if TRACE + snprintf(se->tracestring, TRACESTRING_SIZE, "pcm_alignment_zero_bits = %d", se->len); +#endif + writeSE_Fix(se, currStream); + + return se->len; + } + else + return 0; +} + +/*! +************************************************************************ +* \brief +* Codes IPCM data +************************************************************************ +*/ +static int writeIPCMData(Macroblock *currMB, DataPartition* dataPart) +{ + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + InputParameters *p_Inp = currMB->p_Inp; + int no_bits = 0; + SyntaxElement se; + BitCounter *mbBits = &currMB->bits; + + if (currSlice->symbol_mode == CABAC) + { + int len; + EncodingEnvironmentPtr eep = &dataPart->ee_cabac; + len = arienco_bits_written(eep); + arienco_done_encoding(currMB, eep); // This pads to byte + len = arienco_bits_written(eep) - len; + no_bits += len; + // Now restart the encoder + arienco_start_encoding(eep, dataPart->bitstream->streamBuffer, &(dataPart->bitstream->byte_pos)); + } + + writeIPCMByteAlign(dataPart->bitstream, &se, &(currMB->bits.mb_y_coeff)); + + no_bits += writeIPCMLuma (currMB, p_Vid->enc_picture->imgY, dataPart->bitstream, &se, &mbBits->mb_y_coeff); + + if ((p_Vid->active_sps->chroma_format_idc != YUV400) && (p_Inp->separate_colour_plane_flag == 0)) + { + int uv; + for (uv = 0; uv < 2; ++uv ) + { + no_bits += writeIPCMChroma (currMB, p_Vid->enc_picture->imgUV[uv], dataPart->bitstream, &se, &mbBits->mb_uv_coeff); + } + } + return no_bits; +} + +/*! +************************************************************************ +* \brief +* Codes macroblock header +* \param currMB +* current macroblock +* \param rdopt +* true for calls during RD-optimization +* \param coeff_rate +* bitrate of Luma and Chroma coeff +************************************************************************ +*/ +int write_b_slice_MB_layer (Macroblock *currMB, int rdopt, int *coeff_rate) +{ + VideoParameters *p_Vid = currMB->p_Vid; + InputParameters *p_Inp = currMB->p_Inp; + Slice *currSlice = currMB->p_Slice; + + int i; + int mb_nr = currMB->mbAddrX; + Macroblock *prevMB = currMB->PrevMB; + int prev_mb_nr = (NULL == prevMB) ? -1 : prevMB->mbAddrX; + + SyntaxElement se; + BitCounter *mbBits = &currMB->bits; + const int* partMap = assignSE2partition[currSlice->partition_mode]; + // choose the appropriate data partition + DataPartition* dataPart = &(currSlice->partArr[partMap[SE_MBTYPE]]); + Bitstream *currStream; + + int no_bits = 0; + int skip = currMB->mb_type ? 0 : !currMB->cbp; + int prevMbSkipped = 0; + int WriteFrameFieldMBInHeader = 0; + + if (currSlice->mb_aff_frame_flag) + { + if ((mb_nr & 0x01) == 0) + { + WriteFrameFieldMBInHeader = 1; // top field + + prevMbSkipped = 0; + } + else + { + if (prevMB->mb_type ? 0: !prevMB->cbp) + { + WriteFrameFieldMBInHeader = 1; // bottom, if top was skipped + } + + prevMbSkipped = p_Vid->mb_data[prev_mb_nr].skip_flag; + } + } + currMB->IntraChromaPredModeFlag = (char) IS_INTRA(currMB); + + if (currSlice->symbol_mode == CABAC) + { + int mb_type = MBType2Value (currMB); + if (currSlice->mb_aff_frame_flag && ((currMB->mbAddrX & 0x01) == 0 || prevMbSkipped)) + { + byte mb_field_tmp = currMB->mb_field; + currMB->mb_field = field_flag_inference(currMB); + CheckAvailabilityOfNeighborsCABAC(currMB); + currMB->mb_field = mb_field_tmp; + } + + //========= write mb_skip_flag (CABAC) ========= + se.value1 = mb_type; + se.value2 = currMB->cbp; + se.type = SE_MBTYPE; + + TRACE_SE (se.tracestring, "mb_skip_flag"); + currSlice->writeMB_Skip(currMB, &se, dataPart); + + no_bits += se.len; + + CheckAvailabilityOfNeighborsCABAC(currMB); + + //========= write mb_aff (CABAC) ========= + if(currSlice->mb_aff_frame_flag && !skip) // check for copy mode + { + if(WriteFrameFieldMBInHeader) + { + se.value1 = currMB->mb_field; + se.value2 = 0; + se.type = SE_MBTYPE; + + TRACE_SE(se.tracestring, "mb_field_decoding_flag"); + currSlice->writeFieldModeInfo(currMB, &se, dataPart); + + no_bits += se.len; + } + } + + //========= write mb_type (CABAC) ========= + if (currMB->mb_type != 0 || ((currMB->cbp != 0 || currSlice->cmp_cbp[1] !=0 || currSlice->cmp_cbp[2]!=0 ))) + { + se.value1 = mb_type; + se.value2 = 0; + se.type = SE_MBTYPE; + +#if TRACE + snprintf(se.tracestring, TRACESTRING_SIZE, "mb_type (B_SLICE) (%2d,%2d) = %3d",currMB->mb_x, currMB->mb_y, currMB->mb_type); +#endif + currSlice->writeMB_typeInfo(currMB, &se, dataPart); + + no_bits += se.len; + } + } + // VLC not intra + else if (currMB->mb_type != 0 || ((currMB->cbp != 0 || currSlice->cmp_cbp[1] != 0 || currSlice->cmp_cbp[2]!=0 ))) + { + //===== Run Length Coding: Non-Skipped macroblock ===== + se.value1 = p_Vid->cod_counter; + se.value2 = 0; + se.type = SE_MBTYPE; + + TRACE_SE (se.tracestring, "mb_skip_run"); + writeSE_UVLC(&se, dataPart); + + no_bits += se.len; + + // store position after skip_run when re-encoding can occur (slices with fixed number of bytes) + if ( (p_Vid->cod_counter) && (p_Inp->slice_mode == FIXED_RATE) && (!rdopt)) + { + for (i=0; imax_part_nr; ++i) + { + currStream = currSlice->partArr[i].bitstream; + currStream->stored_bits_to_go = currStream->bits_to_go; + currStream->stored_byte_pos = currStream->byte_pos; + currStream->stored_byte_buf = currStream->byte_buf; + } + p_Vid->p_Stats->stored_bit_slice = p_Vid->p_Stats->bit_slice; + } + + // Reset cod counter + p_Vid->cod_counter = 0; + + // write mb_aff + if(currSlice->mb_aff_frame_flag && !skip) // check for copy mode + { + if(WriteFrameFieldMBInHeader) + { + se.value1 = currMB->mb_field; + se.type = SE_MBTYPE; + + TRACE_SE(se.tracestring, "mb_field_decoding_flag"); + writeSE_Flag (&se, dataPart); + + no_bits += se.len; + } + } + // Put out mb mode + se.value1 = MBType2Value (currMB); + + se.type = SE_MBTYPE; + se.value2 = 0; + +#if TRACE + snprintf(se.tracestring, TRACESTRING_SIZE, "mb_type (B_SLICE) (%2d,%2d) = %3d",currMB->mb_x, currMB->mb_y, currMB->mb_type); +#endif + + currSlice->writeMB_typeInfo(currMB, &se, dataPart); + + no_bits += se.len; + } + else + { + //Run Length Coding: Skipped macroblock + ++(p_Vid->cod_counter); + + currMB->skip_flag = 1; + // CAVLC + reset_mb_nz_coeff(p_Vid, currMB->mbAddrX); + + if(FmoGetNextMBNr(p_Vid, currMB->mbAddrX) == -1 && p_Vid->cod_counter > 0) + { + // Put out run + se.value1 = p_Vid->cod_counter; + se.value2 = 0; + se.type = SE_MBTYPE; + + TRACE_SE(se.tracestring, "mb_skip_run"); + writeSE_UVLC(&se, dataPart); + + no_bits += se.len; + + // Reset cod counter + p_Vid->cod_counter = 0; + } + } + mbBits->mb_mode = mbBits->mb_mode + (unsigned short) no_bits;; + + //init NoMbPartLessThan8x8Flag + currMB->NoMbPartLessThan8x8Flag = (currMB->mb_type == 0 && !(p_Vid->active_sps->direct_8x8_inference_flag))? FALSE : TRUE; + + if (currMB->mb_type == IPCM) + { + no_bits += writeIPCMData(currMB, &(currSlice->partArr[partMap[SE_LUM_DC_INTRA]])); + return no_bits; + } + + dataPart = &(currSlice->partArr[partMap[SE_MBTYPE]]); + + //===== BITS FOR 8x8 SUB-PARTITION MODES ===== + if (currMB->mb_type == P8x8) + { + for (i=0; i<4; ++i) + { + se.value1 = B8Mode2Value (currSlice, currMB->b8x8[i].mode, currMB->b8x8[i].pdir); + se.value2 = 0; + se.type = SE_MBTYPE; +#if TRACE + snprintf(se.tracestring, TRACESTRING_SIZE, "8x8 mode/pdir(%2d) = %3d/%d", i, currMB->b8x8[i].mode, currMB->b8x8[i].pdir); +#endif + currSlice->writeB8_typeInfo (&se, dataPart); + mbBits->mb_mode = mbBits->mb_mode + (unsigned short) se.len;; + no_bits += se.len; + + //set NoMbPartLessThan8x8Flag for P8x8 mode + currMB->NoMbPartLessThan8x8Flag &= (Boolean) ((currMB->b8x8[i].mode == 0 && p_Vid->active_sps->direct_8x8_inference_flag) + || (currMB->b8x8[i].mode == 4)); + } + + no_bits += currSlice->writeMotionInfo2NAL (currMB); + } + + //============= Transform size flag for INTRA MBs ============= + //------------------------------------------------------------- + //transform size flag for INTRA_4x4 and INTRA_8x8 modes + if ((currMB->mb_type == I8MB || currMB->mb_type == I4MB) && p_Inp->Transform8x8Mode) + { + se.value1 = currMB->luma_transform_size_8x8_flag; + se.type = SE_MBTYPE; + +#if TRACE + snprintf(se.tracestring, TRACESTRING_SIZE, "transform_size_8x8_flag = %3d", currMB->luma_transform_size_8x8_flag); +#endif + currSlice->writeMB_transform_size(currMB, &se, dataPart); + + mbBits->mb_mode = mbBits->mb_mode + (unsigned short) se.len;; + no_bits += se.len; + } + + //===== BITS FOR INTRA PREDICTION MODES ==== + no_bits += writeIntraModes(currMB); + //===== BITS FOR CHROMA INTRA PREDICTION MODE ==== + if (currMB->IntraChromaPredModeFlag && ((p_Vid->active_sps->chroma_format_idc != YUV400) && (p_Vid->active_sps->chroma_format_idc != YUV444))) + no_bits += write_chroma_intra_pred_mode(currMB); + else if(!rdopt) //GB CHROMA !!!!! + { + currMB->c_ipred_mode = DC_PRED_8; + } + //setting c_ipred_mode to default is not the right place here + //resetting in rdopt.c (but where ??) + //with cabac and bframes maybe it could crash without this default + //since cabac needs the right neighborhood for the later MBs + + //----- motion information ----- + if (currMB->mb_type !=0 && currMB->mb_type != P8x8 && (!IS_INTRA(currMB))) + { + no_bits += currSlice->writeMotionInfo2NAL (currMB); + } + + if ((currMB->mb_type!=0) || ((currMB->cbp!=0 || currSlice->cmp_cbp[1]!=0 || currSlice->cmp_cbp[2]!=0))) + { + *coeff_rate = write_CBP_and_Dquant (currMB); + *coeff_rate += currSlice->writeCoeff16x16 (currMB, PLANE_Y); + + if ((p_Vid->active_sps->chroma_format_idc != YUV400) && ((p_Inp->separate_colour_plane_flag == 0))) + { + if(p_Vid->active_sps->chroma_format_idc == YUV444) + { + *coeff_rate += currSlice->writeCoeff16x16(currMB, PLANE_U); + *coeff_rate += currSlice->writeCoeff16x16(currMB, PLANE_V); + } + else + *coeff_rate += write_chroma_coeff (currMB); + } + no_bits += *coeff_rate; + } + + return no_bits; +} + +/*! +************************************************************************ +* \brief +* Codes macroblock header +* \param currMB +* current macroblock +* \param rdopt +* true for calls during RD-optimization +* \param coeff_rate +* bitrate of Luma and Chroma coeff +************************************************************************ +*/ +int write_p_slice_MB_layer (Macroblock *currMB, int rdopt, int *coeff_rate) +{ + VideoParameters *p_Vid = currMB->p_Vid; + InputParameters *p_Inp = currMB->p_Inp; + Slice *currSlice = currMB->p_Slice; + + int i; + int mb_nr = currMB->mbAddrX; + Macroblock *prevMB = currMB->PrevMB; + int prev_mb_nr = (NULL == prevMB) ? -1 : prevMB->mbAddrX; + + SyntaxElement se; + BitCounter *mbBits = &currMB->bits; + const int* partMap = assignSE2partition[currSlice->partition_mode]; + // choose the appropriate data partition + DataPartition* dataPart = &(currSlice->partArr[partMap[SE_MBTYPE]]); + Bitstream *currStream; + + int no_bits = 0; + int skip = currMB->mb_type ? 0 : 1; + int prevMbSkipped = 0; + int WriteFrameFieldMBInHeader = 0; + + if (currSlice->mb_aff_frame_flag) + { + if ((mb_nr & 0x01) == 0) + { + WriteFrameFieldMBInHeader = 1; // top field + + prevMbSkipped = 0; + } + else + { + if (prevMB->mb_type ? 0 : 1) + { + WriteFrameFieldMBInHeader = 1; // bottom, if top was skipped + } + + prevMbSkipped = p_Vid->mb_data[prev_mb_nr].skip_flag; + } + } + currMB->IntraChromaPredModeFlag = (char) IS_INTRA(currMB); + + if (currSlice->symbol_mode == CABAC) + { + int mb_type = MBType2Value (currMB); + if (currSlice->mb_aff_frame_flag && ((currMB->mbAddrX & 0x01) == 0 || prevMbSkipped)) + { + byte mb_field_tmp = currMB->mb_field; + currMB->mb_field = field_flag_inference(currMB); + CheckAvailabilityOfNeighborsCABAC(currMB); + currMB->mb_field = mb_field_tmp; + } + + //========= write mb_skip_flag (CABAC) ========= + se.value1 = mb_type; + se.value2 = currMB->cbp; + se.type = SE_MBTYPE; + + TRACE_SE (se.tracestring, "mb_skip_flag"); + currSlice->writeMB_Skip(currMB, &se, dataPart); + + no_bits += se.len; + + CheckAvailabilityOfNeighborsCABAC(currMB); + + //========= write mb_aff (CABAC) ========= + if(currSlice->mb_aff_frame_flag && !skip) // check for copy mode + { + if(WriteFrameFieldMBInHeader) + { + se.value1 = currMB->mb_field; + se.value2 = 0; + se.type = SE_MBTYPE; + + TRACE_SE(se.tracestring, "mb_field_decoding_flag"); + currSlice->writeFieldModeInfo(currMB, &se, dataPart); + + no_bits += se.len; + } + } + + //========= write mb_type (CABAC) ========= + if (currMB->mb_type != 0) + { + se.value1 = mb_type; + se.value2 = 0; + se.type = SE_MBTYPE; + +#if TRACE + snprintf(se.tracestring, TRACESTRING_SIZE, "mb_type (P_SLICE) (%2d,%2d) = %3d",currMB->mb_x, currMB->mb_y, currMB->mb_type); +#endif + currSlice->writeMB_typeInfo(currMB, &se, dataPart); + + no_bits += se.len; + } + } + // VLC not intra + else if (currMB->mb_type != 0) + { + //===== Run Length Coding: Non-Skipped macroblock ===== + se.value1 = p_Vid->cod_counter; + se.value2 = 0; + se.type = SE_MBTYPE; + + TRACE_SE (se.tracestring, "mb_skip_run"); + writeSE_UVLC(&se, dataPart); + + no_bits += se.len; + + // store position after skip_run when re-encoding can occur (slices with fixed number of bytes) + if ( (p_Vid->cod_counter) && (p_Inp->slice_mode == FIXED_RATE) && (!rdopt)) + { + for (i=0; imax_part_nr; ++i) + { + currStream = currSlice->partArr[i].bitstream; + currStream->stored_bits_to_go = currStream->bits_to_go; + currStream->stored_byte_pos = currStream->byte_pos; + currStream->stored_byte_buf = currStream->byte_buf; + p_Vid->p_Stats->stored_bit_slice = p_Vid->p_Stats->bit_slice; + } + } + // Reset cod counter + p_Vid->cod_counter = 0; + + // write mb_aff + if(currSlice->mb_aff_frame_flag && !skip) // check for copy mode + { + if(WriteFrameFieldMBInHeader) + { + se.value1 = currMB->mb_field; + se.type = SE_MBTYPE; + + TRACE_SE(se.tracestring, "mb_field_decoding_flag"); + writeSE_Flag (&se, dataPart); + + no_bits += se.len; + } + } + // Put out mb mode + se.value1 = MBType2Value (currMB); + se.value1--; + + se.type = SE_MBTYPE; + se.value2 = 0; + +#if TRACE + snprintf(se.tracestring, TRACESTRING_SIZE, "mb_type (P_SLICE) (%2d,%2d) = %3d",currMB->mb_x, currMB->mb_y, currMB->mb_type); +#endif + + currSlice->writeMB_typeInfo(currMB, &se, dataPart); + + no_bits += se.len; + } + else + { + //Run Length Coding: Skipped macroblock + ++(p_Vid->cod_counter); + + currMB->skip_flag = 1; + // CAVLC + reset_mb_nz_coeff(p_Vid, currMB->mbAddrX); + + if(FmoGetNextMBNr(p_Vid, currMB->mbAddrX) == -1 && p_Vid->cod_counter > 0) + { + // Put out run + se.value1 = p_Vid->cod_counter; + se.value2 = 0; + se.type = SE_MBTYPE; + + TRACE_SE(se.tracestring, "mb_skip_run"); + writeSE_UVLC(&se, dataPart); + + no_bits += se.len; + + // Reset cod counter + p_Vid->cod_counter = 0; + } + } + mbBits->mb_mode = mbBits->mb_mode + (unsigned short) no_bits;; + + //init NoMbPartLessThan8x8Flag + currMB->NoMbPartLessThan8x8Flag = TRUE; + + if (currMB->mb_type == IPCM) + { + no_bits += writeIPCMData(currMB, &(currSlice->partArr[partMap[SE_LUM_DC_INTRA]])); + return no_bits; + } + + dataPart = &(currSlice->partArr[partMap[SE_MBTYPE]]); + + //===== BITS FOR 8x8 SUB-PARTITION MODES ===== + if (currMB->mb_type == P8x8) + { + for (i=0; i<4; ++i) + { + se.value1 = B8Mode2Value (currSlice, currMB->b8x8[i].mode, currMB->b8x8[i].pdir); + se.value2 = 0; + se.type = SE_MBTYPE; +#if TRACE + snprintf(se.tracestring, TRACESTRING_SIZE, "8x8 mode/pdir(%2d) = %3d/%d", i, currMB->b8x8[i].mode, currMB->b8x8[i].pdir); +#endif + currSlice->writeB8_typeInfo (&se, dataPart); + mbBits->mb_mode = mbBits->mb_mode + (unsigned short) se.len;; + no_bits += se.len; + + //set NoMbPartLessThan8x8Flag for P8x8 mode + currMB->NoMbPartLessThan8x8Flag &= (Boolean) ((currMB->b8x8[i].mode == 0 && p_Vid->active_sps->direct_8x8_inference_flag) + || (currMB->b8x8[i].mode ==4)); + } + + no_bits += currSlice->writeMotionInfo2NAL (currMB); + } + + //============= Transform size flag for INTRA MBs ============= + //------------------------------------------------------------- + //transform size flag for INTRA_4x4 and INTRA_8x8 modes + if ((currMB->mb_type == I8MB || currMB->mb_type == I4MB) && p_Inp->Transform8x8Mode) + { + se.value1 = currMB->luma_transform_size_8x8_flag; + se.type = SE_MBTYPE; + +#if TRACE + snprintf(se.tracestring, TRACESTRING_SIZE, "transform_size_8x8_flag = %3d", currMB->luma_transform_size_8x8_flag); +#endif + currSlice->writeMB_transform_size(currMB, &se, dataPart); + + mbBits->mb_mode = mbBits->mb_mode + (unsigned short) se.len;; + no_bits += se.len; + } + + //===== BITS FOR INTRA PREDICTION MODES ==== + no_bits += writeIntraModes(currMB); + //===== BITS FOR CHROMA INTRA PREDICTION MODE ==== + if (currMB->IntraChromaPredModeFlag && ((p_Vid->active_sps->chroma_format_idc != YUV400) && (p_Vid->active_sps->chroma_format_idc != YUV444))) + no_bits += write_chroma_intra_pred_mode(currMB); + else if(!rdopt) //GB CHROMA !!!!! + { + currMB->c_ipred_mode = DC_PRED_8; + } + //setting c_ipred_mode to default is not the right place here + //resetting in rdopt.c (but where ??) + //with cabac and bframes maybe it could crash without this default + //since cabac needs the right neighborhood for the later MBs + + //----- motion information ----- + if (currMB->mb_type !=0 && currMB->mb_type != P8x8 && (!IS_INTRA(currMB))) + { + no_bits += currSlice->writeMotionInfo2NAL (currMB); + } + + if (currMB->mb_type!=0) + { + *coeff_rate = write_CBP_and_Dquant (currMB); + *coeff_rate += currSlice->writeCoeff16x16 (currMB, PLANE_Y); + + if ((p_Vid->active_sps->chroma_format_idc != YUV400) && ((p_Inp->separate_colour_plane_flag == 0))) + { + if(p_Vid->active_sps->chroma_format_idc == YUV444) + { + *coeff_rate += currSlice->writeCoeff16x16(currMB, PLANE_U); + *coeff_rate += currSlice->writeCoeff16x16(currMB, PLANE_V); + } + else + *coeff_rate += write_chroma_coeff (currMB); + } + no_bits += *coeff_rate; + } + + return no_bits; +} + + +int write_i_slice_MB_layer (Macroblock *currMB, int rdopt, int *coeff_rate) +{ + VideoParameters *p_Vid = currMB->p_Vid; + InputParameters *p_Inp = currMB->p_Inp; + Slice *currSlice = currMB->p_Slice; + + int mb_nr = currMB->mbAddrX; + Macroblock *prevMB = currMB->PrevMB; + + SyntaxElement se; + BitCounter *mbBits = &currMB->bits; + const int* partMap = assignSE2partition[currSlice->partition_mode]; + // choose the appropriate data partition + DataPartition* dataPart = &(currSlice->partArr[partMap[SE_MBTYPE]]); + + int no_bits = 0; + int WriteFrameFieldMBInHeader = 0; + + if (currSlice->mb_aff_frame_flag) + { + if ((mb_nr & 0x01) == 0) + { + WriteFrameFieldMBInHeader = 1; // top field + } + else + { + if (prevMB->mb_type ? 0: 1) + { + WriteFrameFieldMBInHeader = 1; // bottom, if top was skipped + } + } + + //========= write mb_aff (I_SLICE) ========= + if(WriteFrameFieldMBInHeader) + { + se.value1 = currMB->mb_field; + se.value2 = 0; + se.type = SE_MBTYPE; + + TRACE_SE (se.tracestring, "mb_field_decoding_flag"); + currSlice->writeFieldModeInfo(currMB, &se, dataPart); + + no_bits += se.len; + } + } + currMB->IntraChromaPredModeFlag = (char) IS_INTRA(currMB); + + + //========= write mb_type (I_SLICE) ========= + se.value1 = MBType2Value (currMB); + se.value2 = 0; + se.type = SE_MBTYPE; + +#if TRACE + snprintf(se.tracestring, TRACESTRING_SIZE, "mb_type (I_SLICE) (%2d,%2d) = %3d",currMB->mb_x, currMB->mb_y, currMB->mb_type); +#endif + + currSlice->writeMB_typeInfo (currMB, &se, dataPart); + + no_bits += se.len; + + mbBits->mb_mode = mbBits->mb_mode + (unsigned short) no_bits; + + //init NoMbPartLessThan8x8Flag + currMB->NoMbPartLessThan8x8Flag = TRUE; + + if (currMB->mb_type == IPCM) + { + no_bits += writeIPCMData(currMB, &(currSlice->partArr[partMap[SE_LUM_DC_INTRA]])); + return no_bits; + } + + dataPart = &(currSlice->partArr[partMap[SE_MBTYPE]]); + + //============= Transform size flag for INTRA MBs ============= + //------------------------------------------------------------- + //transform size flag for INTRA_4x4 and INTRA_8x8 modes + if ((currMB->mb_type == I8MB || currMB->mb_type == I4MB) && p_Inp->Transform8x8Mode) + { + se.value1 = currMB->luma_transform_size_8x8_flag; + se.type = SE_MBTYPE; + +#if TRACE + snprintf(se.tracestring, TRACESTRING_SIZE, "transform_size_8x8_flag = %3d", currMB->luma_transform_size_8x8_flag); +#endif + currSlice->writeMB_transform_size(currMB, &se, dataPart); + + mbBits->mb_mode = mbBits->mb_mode + (unsigned short) se.len;; + no_bits += se.len; + } + + //===== BITS FOR INTRA PREDICTION MODES ==== + no_bits += writeIntraModes(currMB); + //===== BITS FOR CHROMA INTRA PREDICTION MODE ==== + if (currMB->IntraChromaPredModeFlag && ((p_Vid->active_sps->chroma_format_idc != YUV400) && (p_Vid->active_sps->chroma_format_idc != YUV444))) + no_bits += write_chroma_intra_pred_mode(currMB); + else if(!rdopt) //GB CHROMA !!!!! + { + currMB->c_ipred_mode = DC_PRED_8; + } + //setting c_ipred_mode to default is not the right place here + //resetting in rdopt.c (but where ??) + //with cabac and bframes maybe it could crash without this default + //since cabac needs the right neighborhood for the later MBs + + *coeff_rate = write_CBP_and_Dquant (currMB); + *coeff_rate += currSlice->writeCoeff16x16 (currMB, PLANE_Y); + + if ((p_Vid->active_sps->chroma_format_idc != YUV400) && ((p_Inp->separate_colour_plane_flag == 0))) + { + if(p_Vid->active_sps->chroma_format_idc == YUV444) + { + *coeff_rate += currSlice->writeCoeff16x16(currMB, PLANE_U); + *coeff_rate += currSlice->writeCoeff16x16(currMB, PLANE_V); + } + else + *coeff_rate += write_chroma_coeff (currMB); + } + + no_bits += *coeff_rate; + + return no_bits; +} + +void write_terminating_bit (Slice *currSlice, int bit) +{ + const int* partMap = assignSE2partition[currSlice->partition_mode]; + //--- write non-slice termination symbol if the macroblock is not the first one in its slice --- + DataPartition* dataPart = &(currSlice->partArr[partMap[SE_MBTYPE]]); + dataPart->bitstream->write_flag = 1; + + biari_encode_symbol_final(&(dataPart->ee_cabac), bit); +#if TRACE + fprintf (p_Enc->p_trace, " CABAC terminating bit = %d\n",bit); +#endif +} + + +/*! + ************************************************************************ + * \brief + * Write chroma intra prediction mode. + ************************************************************************ + */ +int write_chroma_intra_pred_mode(Macroblock* currMB) +{ + Slice* currSlice = currMB->p_Slice; + SyntaxElement se; + const int* partMap = assignSE2partition[currSlice->partition_mode]; + DataPartition* dataPart = &(currSlice->partArr[partMap[SE_INTRAPREDMODE]]); + int rate = 0; + + + //===== BITS FOR CHROMA INTRA PREDICTION MODES + + se.value1 = currMB->c_ipred_mode; + se.value2 = 0; + se.type = SE_INTRAPREDMODE; + + TRACE_SE(se.tracestring, "intra_chroma_pred_mode"); + currSlice->writeCIPredMode(currMB, &se, dataPart); + + currMB->bits.mb_uv_coeff += se.len; + rate += se.len; + + return rate; +} + + +#if (MVC_EXTENSION_ENABLE) +int is_interview_mb(Macroblock *currMB) +{ + VideoParameters *p_Vid = currMB->p_Vid; + Slice *currSlice = currMB->p_Slice; + struct storable_picture **pList = currSlice->listX[0]; + int i,j, iRefIdx=-1, k; + int interview_in_l0 = 1; + int step_h0 = (block_size[(currMB->mb_type == P8x8) ? 4 : currMB->mb_type][0] >> 2); + int step_v0 = (block_size[(currMB->mb_type == P8x8) ? 4 : currMB->mb_type][1] >> 2); + PicMotionParams **motion = p_Vid->enc_picture->mv_info; + + for(i=0; ilistXsize[LIST_0]; i++) + { + if(pList[i]->view_id==0) + { + iRefIdx = i; + break; + } + } + if(iRefIdx == -1 && currSlice->slice_type == B_SLICE) + { + interview_in_l0 = 0; + pList = currSlice->listX[LIST_1]; + for(i=0; ilistXsize[LIST_1]; i++) + { + if(pList[i]->view_id==0) + { + iRefIdx = i; + break; + } + } + } + + if(iRefIdx<0) + { + return 0; + } + if(currMB->mb_type == PSKIP && currSlice->slice_type==P_SLICE) + { + return (iRefIdx==0); + } + for (j=0; j<4; j+=step_v0) + { + for (i=0; i<4; i +=step_h0) + { + k = j + (i >> 1); + if (interview_in_l0 && (currMB->b8x8[k].pdir == 0 || currMB->b8x8[k].pdir == 2))//has forward vector + { + if(motion[currMB->block_y + j][currMB->block_x + i].ref_idx[LIST_0] == iRefIdx) + return 1; + } + else if (!interview_in_l0 && (currMB->b8x8[k].pdir == 1 || currMB->b8x8[k].pdir == 2))//has forward vector + { + if(motion[currMB->block_y + j][currMB->block_x + i].ref_idx[LIST_1] == iRefIdx) + return 1; + } + } + } + + return 0; +} +#endif +/*! + ************************************************************************ + * \brief + * Passes the chosen syntax elements to the NAL + ************************************************************************ + */ +void write_macroblock (Macroblock* currMB, int eos_bit) +{ + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + InputParameters *p_Inp = currMB->p_Inp; + BitCounter *mbBits = &currMB->bits; + int i; + + // enable writing of trace file +#if TRACE + if ( currMB->prev_recode_mb == FALSE ) + { + for (i=0; imax_part_nr; ++i) + { + currSlice->partArr[i].bitstream->trace_enabled = TRUE; + } + } +#endif + + //--- constrain intra prediction --- + if(p_Inp->UseConstrainedIntraPred && (currSlice->slice_type==P_SLICE || currSlice->slice_type==B_SLICE)) + { + p_Vid->intra_block[currMB->mbAddrX] = IS_INTRA(currMB); + } + + //===== init and update number of intra macroblocks ===== + if (currMB->mbAddrX == 0) + { + p_Vid->intras = 0; + p_Vid->iInterViewMBs =0; + } + + if (IS_INTRA(currMB)) + ++p_Vid->intras; +#if (MVC_EXTENSION_ENABLE) + else if(p_Vid->num_of_layers==2 && p_Vid->dpb_layer_id==1) + { + int is_interview = is_interview_mb(currMB); + + if(is_interview) + p_Vid->iInterViewMBs++; //p_Vid->enc_picture->mv_info + + } +#endif + //--- write non-slice termination symbol if the macroblock is not the first one in its slice --- + if (currSlice->symbol_mode==CABAC && currMB->mbAddrX != currSlice->start_mb_nr && eos_bit) + { + write_terminating_bit (currSlice, 0); + } + +#if TRACE + // trace: write macroblock header + if (p_Enc->p_trace) + { + fprintf(p_Enc->p_trace, "\n*********** Pic: %i (I/P) MB: %i Slice: %i **********\n\n", p_Vid->frame_no, currMB->mbAddrX, currSlice->slice_nr); + } +#endif + + p_Vid->cabac_encoding = 1; + + currMB->write_mb = TRUE; + //--- write macroblock --- + currSlice->write_MB_layer (currMB, 0, &i); // i is temporary + + if (!((currMB->mb_type !=0 ) || ((currSlice->slice_type==B_SLICE) && currMB->cbp != 0) )) + { + reset_mb_nz_coeff(p_Vid, currMB->mbAddrX); // CAVLC + } + + //--- set total bit-counter --- + mbBits->mb_total = mbBits->mb_mode + + mbBits->mb_y_coeff + + mbBits->mb_inter + + mbBits->mb_cbp + + mbBits->mb_delta_quant + + mbBits->mb_uv_coeff + + mbBits->mb_cb_coeff + + mbBits->mb_cr_coeff; + + if ( p_Inp->RCEnable ) + rc_update_mb_stats(currMB); + + /*record the total number of MBs*/ + ++(p_Vid->NumberofCodedMacroBlocks); + + p_Vid->p_Stats->bit_slice += mbBits->mb_total; + + p_Vid->cabac_encoding = 0; +} + + +/*! + ************************************************************************ + * \brief + * Codes the reference frame + ************************************************************************ + */ +int writeReferenceFrame (Macroblock* currMB, int i, int j, int list_idx, int ref) +{ + BitCounter *mbBits = &currMB->bits; + Slice *currSlice = currMB->p_Slice; + const int* partMap = assignSE2partition[currSlice->partition_mode]; + DataPartition* dataPart = &(currSlice->partArr[partMap[SE_REFFRAME]]); + int list = list_idx + currMB->list_offset; + SyntaxElement se; + + se.value1 = ref; + se.type = SE_REFFRAME; + se.value2 = list_idx; + + currMB->subblock_x = (short) (i << 2); // position used for context determination + currMB->subblock_y = (short) (j << 2); // position used for context determination + +#if TRACE + if (!list_idx) + snprintf(se.tracestring, TRACESTRING_SIZE, "ref_idx_l0 = %d", se.value1); + else + snprintf(se.tracestring, TRACESTRING_SIZE, "ref_idx_l1 = %d", se.value1); +#endif + + currSlice->writeRefFrame[list](currMB, &se, dataPart); + + mbBits->mb_inter = mbBits->mb_inter + (unsigned short) se.len; + + return (se.len); +} + + +/*! + ************************************************************************ + * \brief + * Writes motion vectors of an 8x8 block + ************************************************************************ + */ +int writeMotionVector8x8 (Macroblock *currMB, + int i0, + int j0, + int i1, + int j1, + int refframe, + int list_idx, + int mv_mode, + short bipred_me + ) +{ + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + + PixelPos block[4]; + int i, j, k, l, m; + int curr_mvd; + + int rate = 0; + int step_h = part_size[mv_mode][0]; + int step_v = part_size[mv_mode][1]; + SyntaxElement se; + BitCounter *mbBits = &currMB->bits; + + const int* partMap = assignSE2partition[currSlice->partition_mode]; + DataPartition* dataPart = &(currSlice->partArr[partMap[SE_MVD]]); + int refindex = refframe; + + MotionVector **all_mv = currSlice->all_mv[list_idx][refindex][mv_mode]; + MotionVector *cur_mv; + MotionVector predMV; + PicMotionParams **motion = p_Vid->enc_picture->mv_info; + //MotionVector **final_mv = &motion->mv[list_idx][currMB->block_y]; + short mvd[2]; + short (*currMB_mvd)[4][2] = currMB->mvd[list_idx]; + se.type = SE_MVD; + + if (bipred_me && is_bipred_enabled(p_Vid, mv_mode) && refindex == 0) + all_mv = currSlice->bipred_mv[bipred_me - 1][list_idx][refindex][mv_mode]; + + for (j = j0; j < j1; j += step_v) + { + currMB->subblock_y = (short) (j << 2); // position used for context determination + for (i = i0; i < i1; i += step_h) + { + currMB->subblock_x = (short) (i << 2); // position used for context determination + // When writing mv, let us use final mv instead of all_mv structure + if (currMB->write_mb) + cur_mv = &motion[currMB->block_y + j][currMB->block_x + i].mv[list_idx]; + else + cur_mv = &all_mv[j][i]; + + // Lets recompute MV predictor. This should avoid any problems with alterations of the motion vectors after ME + get_neighbors(currMB, block, i<<2, j<<2, step_h<<2); + currMB->GetMVPredictor (currMB, block, &predMV, (short) refindex, p_Vid->enc_picture->mv_info, list_idx, (i<<2), (j<<2), step_h<<2, step_v<<2); + //test_clip_mvs(p_Vid, cur_mv, currMB->write_mb); + mvd[0] = cur_mv->mv_x - predMV.mv_x; + mvd[1] = cur_mv->mv_y - predMV.mv_y; + + for (k=0; k<2; ++k) + { + curr_mvd = mvd[k]; + + //--- store (oversampled) mvd --- + for (l = j; l < j + step_v; ++l) + { + for (m = i; m < i + step_h; ++m) + { + currMB_mvd[l][m][k] = (short) curr_mvd; + } + } + + se.value1 = curr_mvd; + se.value2 = (k << 1) + list_idx; // identifies the component and the direction; only used for context determination +#if TRACE + if (k == 0) + snprintf(se.tracestring, TRACESTRING_SIZE, "mvd_l%d (%d) = %3d (org_mv %3d pred_mv %3d)",list_idx, k, curr_mvd, cur_mv->mv_x, predMV.mv_x); + else + snprintf(se.tracestring, TRACESTRING_SIZE, "mvd_l%d (%d) = %3d (org_mv %3d pred_mv %3d)",list_idx, k, curr_mvd, cur_mv->mv_y, predMV.mv_y); +#endif + currSlice->writeMVD (currMB, &se, dataPart); + + rate += se.len; + } + } + } + + mbBits->mb_inter = mbBits->mb_inter + (unsigned short) rate; + return rate; +} + + +/*! + ************************************************************************ + * \brief + * Writes motion info (b slice) + ************************************************************************ + */ +int write_b_slice_motion_info_to_NAL (Macroblock* currMB) +{ + int no_bits = 0; + //=== If multiple ref. frames, write reference frame for the MB === + if (currMB->mb_type != I4MB && currMB->mb_type != I16MB && currMB->mb_type != I8MB && currMB->mb_type != 0) + { + Slice* currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currSlice->p_Vid; + PicMotionParams **motion = p_Vid->enc_picture->mv_info; + int k, j0, i0; + + int step_h0 = (block_size[(currMB->mb_type == P8x8) ? 4 : currMB->mb_type][0] >> 2); + int step_v0 = (block_size[(currMB->mb_type == P8x8) ? 4 : currMB->mb_type][1] >> 2); + + // Disable for now so we can check MBAFF + //if (currSlice->listXsize[0] > 1) + { + for (j0 = 0; j0 < 4; j0 += step_v0) + { + for (i0 = 0; i0 < 4; i0 += step_h0) + { + k = j0 + (i0 >> 1); + if (currMB->b8x8[k].mode !=0 && (currMB->b8x8[k].pdir == 0 || currMB->b8x8[k].pdir == 2))//has forward vector + { + no_bits += writeReferenceFrame (currMB, i0, j0, LIST_0, motion[currMB->block_y + j0][currMB->block_x + i0].ref_idx[LIST_0]); + } + } + } + } + + //if (currSlice->listXsize[1] > 1) + { + for (j0 = 0; j0 < 4; j0 += step_v0) + { + for (i0 = 0; i0 < 4; i0 += step_h0) + { + k = j0 + (i0 >> 1); + if (currMB->b8x8[k].mode !=0 && (currMB->b8x8[k].pdir == 1 || currMB->b8x8[k].pdir == 2))//has backward vector + { + no_bits += writeReferenceFrame (currMB, i0, j0, LIST_1, motion[currMB->block_y + j0][currMB->block_x + i0].ref_idx[LIST_1]); + } + } + } + } + + //===== write forward motion vectors ===== + for (j0=0; j0<4; j0+=step_v0) + { + for (i0=0; i0<4; i0+=step_h0) + { + k = j0 + (i0 >> 1); + if (currMB->b8x8[k].mode !=0 && (currMB->b8x8[k].pdir == 0 || currMB->b8x8[k].pdir == 2))//has forward vector + { + + no_bits += writeMotionVector8x8 (currMB, i0, j0, i0 + step_h0, j0 + step_v0, motion[currMB->block_y + j0][currMB->block_x + i0].ref_idx[LIST_0], + LIST_0, currMB->b8x8[k].mode, currMB->b8x8[k].bipred); + } + } + } + + //===== write backward motion vectors ===== + + for (j0=0; j0<4; j0+=step_v0) + { + for (i0 = 0; i0 < 4; i0 += step_h0) + { + k=j0+(i0 >> 1); + if (currMB->b8x8[k].mode !=0 && (currMB->b8x8[k].pdir == 1 || currMB->b8x8[k].pdir == 2))//has backward vector + { + no_bits += writeMotionVector8x8 (currMB, i0, j0, i0+step_h0, j0+step_v0, motion[currMB->block_y + j0][currMB->block_x + i0].ref_idx[LIST_1], + LIST_1, currMB->b8x8[k].mode, currMB->b8x8[k].bipred); + } + } + } + } + + return no_bits; +} + + +/*! + ************************************************************************ + * \brief + * Writes motion info (p slice) + ************************************************************************ + */ +int write_p_slice_motion_info_to_NAL (Macroblock* currMB) +{ + int no_bits = 0; + //=== If multiple ref. frames, write reference frame for the MB === + if (currMB->mb_type != I4MB && currMB->mb_type != I16MB && currMB->mb_type != I8MB && currMB->mb_type != 0) + { + Slice* currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currSlice->p_Vid; + PicMotionParams **motion = p_Vid->enc_picture->mv_info; + int k, j0, i0; + int step_h0 = (block_size[(currMB->mb_type == P8x8) ? 4 : currMB->mb_type][0] >> 2); + int step_v0 = (block_size[(currMB->mb_type == P8x8) ? 4 : currMB->mb_type][1] >> 2); + + // if CAVLC is turned on, a 8x8 macroblock with all ref=0 in a P-frame is signalled in macroblock mode + if ((currMB->mb_type != P8x8) || !ZeroRef (currMB) || currSlice->symbol_mode==CABAC) + { + for (j0 = 0; j0 < 4; j0 += step_v0) + { + for (i0 = 0; i0 < 4; i0 += step_h0) + { + k = j0 + (i0 >> 1); + if (currMB->b8x8[k].mode !=0 && (currMB->b8x8[k].pdir == 0 || currMB->b8x8[k].pdir == 2)) + { + no_bits += writeReferenceFrame (currMB, i0, j0, LIST_0, motion[currMB->block_y + j0][currMB->block_x + i0].ref_idx[LIST_0]); + } + } + } + } + + //===== write forward motion vectors ===== + for (j0=0; j0<4; j0+=step_v0) + { + for (i0=0; i0<4; i0+=step_h0) + { + k = j0 + (i0 >> 1); + if (currMB->b8x8[k].mode !=0 && (currMB->b8x8[k].pdir == 0 || currMB->b8x8[k].pdir == 2))//has forward vector + { + no_bits += writeMotionVector8x8 (currMB, i0, j0, i0 + step_h0, j0 + step_v0, motion[currMB->block_y + j0][currMB->block_x + i0].ref_idx[LIST_0], + LIST_0, currMB->b8x8[k].mode, currMB->b8x8[k].bipred); + } + } + } + } + + return no_bits; +} + + +/*! + ************************************************************************ + * \brief + * Writes chrominance coefficients + ************************************************************************ + */ +static int write_chroma_coeff (Macroblock* currMB) +{ + Slice* currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currSlice->p_Vid; + + int rate = 0; + int block_rate = 0; + SyntaxElement se; + const int* partMap = assignSE2partition[currSlice->partition_mode]; + int cbp = currMB->cbp; + DataPartition* dataPart; + + int level; + int k, uv; + int b8, b4; + int* ACLevel; + int* ACRun; + int* DCLevel; + int* DCRun; + + static const int chroma_dc_context[3]= + { + CHROMA_DC, CHROMA_DC_2x4, CHROMA_DC_4x4 + }; + + static const unsigned char chroma_ac_param[3][8][4] = + { + {{ 4, 20, 5, 21}, + {36, 52, 37, 53}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}}, + {{ 4, 20, 5, 21}, + { 6, 22, 7, 23}, + {36, 52, 37, 53}, + {38, 54, 39, 55}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}}, + {{ 4, 20, 5, 21}, + {36, 52, 37, 53}, + { 6, 22, 7, 23}, + {38, 54, 39, 55}, + { 8, 24, 9, 25}, + {40, 56, 41, 57}, + {10, 26, 11, 27}, + {42, 58, 43, 59}} + }; + + int yuv = p_Vid->yuv_format - 1; + //===== + //===== D C - C O E F F I C I E N T S + //===== + if (cbp > 15) // check if any chroma bits in coded block pattern is set + { + if (currSlice->symbol_mode == CAVLC) + { + rate += currSlice->writeCoeff4x4_CAVLC (currMB, CHROMA_DC, 0, 0, 0); //PLANE_U + rate += currSlice->writeCoeff4x4_CAVLC (currMB, CHROMA_DC, 0, 0, 1); //PLANE_V + } + else + { + currMB->is_intra_block = (byte) IS_INTRA(currMB); + se.type = (currMB->is_intra_block ? SE_CHR_DC_INTRA : SE_CHR_DC_INTER); + // choose the appropriate data partition + dataPart = &(currSlice->partArr[partMap[se.type]]); + se.context = chroma_dc_context[yuv]; + + for (uv=0; uv < 2; ++uv) + { + p_Vid->is_v_block = uv; + + DCLevel = currSlice->cofDC[uv+1][0]; + DCRun = currSlice->cofDC[uv+1][1]; + + level = 1; + for (k=0; k <= p_Vid->num_cdc_coeff && level != 0; ++k) + { + level = se.value1 = DCLevel[k]; // level + se.value2 = DCRun [k]; // run + +#if TRACE + snprintf(se.tracestring, TRACESTRING_SIZE, "DC Chroma %2d: level =%3d run =%2d",k, level, se.value2); +#endif + writeRunLevel_CABAC(currMB, &se, dataPart); + + block_rate += se.len; + } + } + currMB->bits.mb_uv_coeff += block_rate; + rate += block_rate; + } + } + + //===== + //===== A C - C O E F F I C I E N T S + //===== + uv=-1; + if (cbp >> 4 == 2) // check if chroma bits in coded block pattern = 10b + { + if (currSlice->symbol_mode == CAVLC) + { + for (b8=4; b8 < (4 + p_Vid->num_blk8x8_uv); b8++) + { + rate += currSlice->writeCoeff4x4_CAVLC (currMB, CHROMA_AC, b8, 0, chroma_ac_param[yuv][b8 - 4][0]); + rate += currSlice->writeCoeff4x4_CAVLC (currMB, CHROMA_AC, b8, 1, chroma_ac_param[yuv][b8 - 4][1]); + rate += currSlice->writeCoeff4x4_CAVLC (currMB, CHROMA_AC, b8, 2, chroma_ac_param[yuv][b8 - 4][2]); + rate += currSlice->writeCoeff4x4_CAVLC (currMB, CHROMA_AC, b8, 3, chroma_ac_param[yuv][b8 - 4][3]); + } + } + else + { + block_rate = 0; + currMB->is_intra_block = (byte) IS_INTRA(currMB); + se.context = CHROMA_AC; + se.type = (currMB->is_intra_block ? SE_CHR_AC_INTRA : SE_CHR_AC_INTER); + // choose the appropriate data partition + dataPart = &(currSlice->partArr[partMap[se.type]]); + + for (b8=4; b8 < (4+p_Vid->num_blk8x8_uv); b8++) + { + for (b4=0; b4 < 4; b4++) + { + ACLevel = currSlice->cofAC[b8][b4][0]; + ACRun = currSlice->cofAC[b8][b4][1]; + + level=1; + uv++; + p_Vid->is_v_block = (uv >= (p_Vid->num_blk8x8_uv << 1)); + + currMB->subblock_y = subblk_offset_y[yuv][b8 - 4][b4]; + currMB->subblock_x = subblk_offset_x[yuv][b8 - 4][b4]; + + for (k=0; k < 16 && level != 0; k++) + { + level = se.value1 = ACLevel[k]; // level + se.value2 = ACRun [k]; // run + +#if TRACE + snprintf(se.tracestring, TRACESTRING_SIZE, "AC Chroma %2d: level =%3d run =%2d",k, level, se.value2); +#endif + writeRunLevel_CABAC(currMB, &se, dataPart); + block_rate += se.len; + } + } + } + currMB->bits.mb_uv_coeff += block_rate; + rate += block_rate; + } + } + + return rate; +} + +#if TRACE +static inline void trace_coeff(SyntaxElement *se, int plane, char *type, int k, int level, int run) +{ + if (plane == 0) + snprintf(se->tracestring, TRACESTRING_SIZE, "Luma%s sng(%2d) level =%3d run =%2d", type, k, level,run); + else if (plane == 1) + snprintf(se->tracestring, TRACESTRING_SIZE, "Cb%s sng(%2d) level =%3d run =%2d", type, k, level,run); + else + snprintf(se->tracestring, TRACESTRING_SIZE, "Cr%s sng(%2d) level =%3d run =%2d", type, k, level,run); +} +#endif + +/*! + ************************************************************************ + * \brief + * Writes Coeffs of an 4x4 block + ************************************************************************ + */ +int writeCoeff4x4_CABAC (Macroblock* currMB, ColorPlane plane, int b8, int b4, int intra4x4mode) +{ + Slice* currSlice = currMB->p_Slice; + + int rate = 0; + SyntaxElement se; + const int* partMap = assignSE2partition[currSlice->partition_mode]; + DataPartition* dataPart; + + int level; + int k; + int pl_off=plane<<2; + + int *mb_bits_coeff = ((plane==0) ? &currMB->bits.mb_y_coeff : ((plane==1) ? &currMB->bits.mb_cb_coeff : &currMB->bits.mb_cr_coeff)); + int* ACLevel = currSlice->cofAC[b8+pl_off][b4][0]; + int* ACRun = currSlice->cofAC[b8+pl_off][b4][1]; + + currMB->subblock_x = ((b8&0x1)==0) ? (((b4&0x1)==0)? 0: 4) : (((b4&0x1)==0)? 8: 12); // horiz. position for coeff_count context + currMB->subblock_y = (b8<2) ? ((b4<2) ? 0: 4) : ((b4<2) ? 8: 12); // vert. position for coeff_count context + currMB->is_intra_block = (byte) intra4x4mode; + se.context = ((plane == 0) ? (LUMA_4x4) : ((plane==1) ? CB_4x4 : CR_4x4)); + + // DC + se.type = (intra4x4mode ? SE_LUM_DC_INTRA : SE_LUM_DC_INTER); + + // choose the appropriate data partition + dataPart = &(currSlice->partArr[partMap[se.type]]); + + level = se.value1 = ACLevel[0]; // level + se.value2 = ACRun [0]; // run + +#if TRACE + trace_coeff(&se, plane, "4x4", 0, level, se.value2); +#endif + + writeRunLevel_CABAC(currMB, &se, dataPart); + rate += se.len; + + // AC Coefficients + se.type = (intra4x4mode ? SE_LUM_AC_INTRA : SE_LUM_AC_INTER); + // choose the appropriate data partition + dataPart = &(currSlice->partArr[partMap[se.type]]); + + for(k = 1; k <= 16 && level != 0; k++) + { + level = se.value1 = ACLevel[k]; // level + se.value2 = ACRun [k]; // run +#if TRACE + trace_coeff(&se, plane, "4x4", k, level, se.value2); +#endif + writeRunLevel_CABAC(currMB, &se, dataPart); + + rate += se.len; + } + + *mb_bits_coeff += rate; + + return rate; +} + +/*! +************************************************************************ +* \brief +* Writes coefficients of an 8x8 block (CABAC) +************************************************************************ +*/ +int writeCoeff8x8_CABAC (Macroblock* currMB, ColorPlane plane, int b8, int intra_mode) +{ + Slice* currSlice = currMB->p_Slice; + + int rate = 0; + SyntaxElement se; + const int* partMap = assignSE2partition[currSlice->partition_mode]; + DataPartition* dataPart; + + int level; + int k; + + int pl_off = plane<<2; + int *mb_bits_coeff = ((plane==0) ? &currMB->bits.mb_y_coeff : ((plane==1) ? &currMB->bits.mb_cb_coeff : &currMB->bits.mb_cr_coeff)); + int* ACLevel = currSlice->cofAC[b8+pl_off][0][0]; + int* ACRun = currSlice->cofAC[b8+pl_off][0][1]; + + currMB->subblock_x = ((b8&0x1) == 0) ? 0 : 8; // horiz. position for coeff_count context + currMB->subblock_y = (b8 < 2) ? 0 : 8; // vert. position for coeff_count context + currMB->is_intra_block = (byte) intra_mode; + + se.context = ((plane==0) ? (LUMA_8x8) : ((plane == 1)? CB_8x8 : CR_8x8)); + + // DC + level = se.value1 = ACLevel[0]; // level + se.value2 = ACRun [0]; // run + + se.type = (intra_mode ? SE_LUM_DC_INTRA : SE_LUM_DC_INTER); + + // choose the appropriate data partition + dataPart = &(currSlice->partArr[partMap[currSlice->slice_type != B_SLICE ? se.type : SE_BFRAME]]); + +#if TRACE + trace_coeff(&se, plane, "8x8", 0, level, se.value2); +#endif + + writeRunLevel_CABAC(currMB, &se, dataPart); + rate += se.len; + + // AC + se.type = (intra_mode ? SE_LUM_AC_INTRA : SE_LUM_AC_INTER); + // choose the appropriate data partition + dataPart = &(currSlice->partArr[partMap[currSlice->slice_type != B_SLICE ? se.type : SE_BFRAME]]); + + for(k=1; k<=64 && level !=0; k++) + { + level = se.value1 = ACLevel[k]; // level + se.value2 = ACRun [k]; // run + +#if TRACE + trace_coeff(&se, plane, "8x8", k, level, se.value2); +#endif + + writeRunLevel_CABAC(currMB, &se, dataPart); + rate += se.len; + } + + *mb_bits_coeff += rate; + return rate; +} + +/*! +************************************************************************ +* \brief +* Writes Luma Coeff of an 8x8 block +************************************************************************ +*/ +int writeCoeff8x8 (Macroblock* currMB, ColorPlane pl, int block8x8, int block_mode, int transform_size_flag) +{ + Slice* currSlice = currMB->p_Slice; + int rate = 0; + int is_intra = (block_mode == IBLOCK) || (block_mode == I8MB); + + //if (block_mode == I8MB) + //assert(transform_size_flag == 1); + + if (currSlice->symbol_mode == CAVLC ) + { + int block_color_type = ((pl == 0) ? LUMA : ((pl == 1) ? CB : CR)); + + rate += currSlice->writeCoeff4x4_CAVLC (currMB, block_color_type, block8x8, 0, is_intra); + rate += currSlice->writeCoeff4x4_CAVLC (currMB, block_color_type, block8x8, 1, is_intra); + rate += currSlice->writeCoeff4x4_CAVLC (currMB, block_color_type, block8x8, 2, is_intra); + rate += currSlice->writeCoeff4x4_CAVLC (currMB, block_color_type, block8x8, 3, is_intra); + } + else // CABAC + { + if(transform_size_flag) // 8x8 + rate += writeCoeff8x8_CABAC (currMB, pl, block8x8, is_intra); + else + { + rate += writeCoeff4x4_CABAC (currMB, pl, block8x8, 0, is_intra); + rate += writeCoeff4x4_CABAC (currMB, pl, block8x8, 1, is_intra); + rate += writeCoeff4x4_CABAC (currMB, pl, block8x8, 2, is_intra); + rate += writeCoeff4x4_CABAC (currMB, pl, block8x8, 3, is_intra); + } + } + + return rate; +} + +/*! + ************************************************************************ + * \brief + * Writes CBP, DQUANT of an macroblock + ************************************************************************ + */ +int write_CBP_and_Dquant (Macroblock* currMB) +{ + Slice* currSlice = currMB->p_Slice; + + int rate = 0; + BitCounter *mbBits = &currMB->bits; + SyntaxElement se; + const int* partMap = assignSE2partition[currSlice->partition_mode]; + int cbp = currMB->cbp; + DataPartition* dataPart; + + se.value2 = 0; // Initialize value2 to avoid problems + + if (currMB->mb_type != I16MB) + { + VideoParameters *p_Vid = currMB->p_Vid; + int need_transform_size_flag; //ADD-VG-24062004 + //===== C B P ===== + //--------------------- + se.value1 = cbp; + se.type = SE_CBP; + + // choose the appropriate data partition + dataPart = &(currSlice->partArr[partMap[se.type]]); + +#if TRACE + snprintf(se.tracestring, TRACESTRING_SIZE, "CBP (%2d,%2d) = %3d",currMB->mb_x, currMB->mb_y, cbp); +#endif + currSlice->writeCBP (currMB, &se, dataPart); + + currMB->bits.mb_cbp = currMB->bits.mb_cbp + (unsigned short) se.len; + rate += se.len; + + //============= Transform Size Flag for INTER MBs ============= + //------------------------------------------------------------- + need_transform_size_flag = (((currMB->mb_type >= 1 && currMB->mb_type <= 3)|| + (currSlice->slice_type == B_SLICE && currMB->mb_type == 0 && p_Vid->active_sps->direct_8x8_inference_flag) || + (currMB->NoMbPartLessThan8x8Flag)) + && currMB->mb_type != I8MB && currMB->mb_type != I4MB + && (currMB->cbp&15 || currSlice->cmp_cbp[1]&15 || currSlice->cmp_cbp[2] & 15) + && currSlice->p_Inp->Transform8x8Mode); + + if (need_transform_size_flag) + { + se.value1 = currMB->luma_transform_size_8x8_flag; + se.type = SE_MBTYPE; + +#if TRACE + snprintf(se.tracestring, TRACESTRING_SIZE, "transform_size_8x8_flag = %3d", currMB->luma_transform_size_8x8_flag); +#endif + currSlice->writeMB_transform_size(currMB, &se, dataPart); + + mbBits->mb_mode = mbBits->mb_mode + (unsigned short) se.len; + rate += se.len; + } + } + + //===== DQUANT ===== + //---------------------- + if (cbp!=0 || (currMB->mb_type == I16MB)) + { + se.value1 = currMB->qp - currMB->prev_qp; + se.type = SE_DELTA_QUANT; + + // choose the appropriate data partition + dataPart = &(currSlice->partArr[partMap[se.type]]); +#if TRACE + snprintf(se.tracestring, TRACESTRING_SIZE, "Delta QP (%2d,%2d) = %3d",currMB->mb_x, currMB->mb_y, se.value1); +#endif + currSlice->writeDquant (currMB, &se, dataPart); + currMB->bits.mb_delta_quant = currMB->bits.mb_delta_quant + (unsigned short) se.len; + rate += se.len; + } + + return rate; +} + +/*! +************************************************************************ +* \brief +* Write Luma Coeffcients, as well as Cb and Cr Coefficients in the +* case of 444 common mode, of an macroblock +************************************************************************ +*/ +int writeCoeff16x16_CAVLC (Macroblock* currMB, ColorPlane plane) +{ + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + + int cbp = currMB->cbp; + + int i, k; + int rate = 0; + + int b8, data_type; + int *mb_bits_coeff = ((plane==0) ? &currMB->bits.mb_y_coeff : ((plane==1) ? &currMB->bits.mb_cb_coeff : &currMB->bits.mb_cr_coeff)); + + if (currSlice->P444_joined) + { + for (i=0; i < 4; i++) + for (k=4*plane; k<4*(plane+1); k++) + p_Vid->nz_coeff [currMB->mbAddrX][i][k] = 0; + } + else + { + reset_mb_nz_coeff(p_Vid, currMB->mbAddrX); + } + + + if (currMB->mb_type != I16MB) + { + //===== L U M I N A N C E ===== + //-------------------------------- + for (i=0; i<4; i++) + { + if (cbp & (1<b8x8[i].mode, currMB->luma_transform_size_8x8_flag); + } + } + } + else + { + //===== L U M I N A N C E f o r 1 6 x 1 6 ===== + //---------------------------------------------------- + // DC coeffs + switch (plane) + { + case 0: + default: + data_type = LUMA_INTRA16x16DC; + break; + case 1: + data_type = CB_INTRA16x16DC; + break; + case 2: + data_type = CR_INTRA16x16DC; + break; + } + rate += currSlice->writeCoeff4x4_CAVLC (currMB, data_type, 0, 0, 0); // CAVLC + + + // AC coeffs + if (cbp & 15) + { + switch (plane) + { + case 0: + default: + data_type = LUMA_INTRA16x16AC; + break; + case 1: + data_type = CB_INTRA16x16AC; + break; + case 2: + data_type = CR_INTRA16x16AC; + break; + } + + for (b8 = 0; b8 < 4; b8 ++) + { + rate += currSlice->writeCoeff4x4_CAVLC (currMB, data_type, b8, 0, 0); + rate += currSlice->writeCoeff4x4_CAVLC (currMB, data_type, b8, 1, 0); + rate += currSlice->writeCoeff4x4_CAVLC (currMB, data_type, b8, 2, 0); + rate += currSlice->writeCoeff4x4_CAVLC (currMB, data_type, b8, 3, 0); + } + } + } + mb_bits_coeff += rate; + return rate; +} + + +/*! +************************************************************************ +* \brief +* Write Luma Coeffcients, as well as Cb and Cr Coefficients in the +* case of 444 common mode, of an macroblock +************************************************************************ +*/ +int writeCoeff16x16_CABAC (Macroblock* currMB, ColorPlane plane) +{ + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currSlice->p_Vid; + + int cbp = currMB->cbp; + + int mb_x, mb_y, i, j, k; + int level; + int rate = 0; + int block_rate = 0; + SyntaxElement se; + const int* partMap = assignSE2partition[currSlice->partition_mode]; + int pl_off = plane<<2; + DataPartition* dataPart; + + int b8, b4; + int* DCLevel = currSlice->cofDC[plane][0]; + int* DCRun = currSlice->cofDC[plane][1]; + int* ACLevel; + int* ACRun; + int data_type; + int* mb_bits_coeff = ((plane==0) ? &currMB->bits.mb_y_coeff : ((plane==1) ? &currMB->bits.mb_cb_coeff : &currMB->bits.mb_cr_coeff)); + + if (currSlice->P444_joined) + { + for (i=0; i < 4; i++) + for (k=4*plane; k<4*(plane+1); k++) + p_Vid->nz_coeff [currMB->mbAddrX][i][k] = 0; + } + else + { + reset_mb_nz_coeff(p_Vid, currMB->mbAddrX); + } + + + if (currMB->mb_type != I16MB) + { + //===== L U M I N A N C E ===== + //-------------------------------- + if (cbp != 0) + { + for (i=0; i<4; i++) + { + if (cbp & (1<b8x8[i].mode, currMB->luma_transform_size_8x8_flag); + } + } + } + } + else + { + //===== L U M I N A N C E f o r 1 6 x 1 6 ===== + //---------------------------------------------------- + // DC coeffs + currMB->is_intra_block = TRUE; + switch (plane) + { + case 0: + default: + data_type = LUMA_16DC; + break; + case 1: + data_type = CB_16DC; + break; + case 2: + data_type = CR_16DC; + break; + } + + se.context = data_type; + se.type = SE_LUM_DC_INTRA; // element is of type DC + + // choose the appropriate data partition + dataPart = &(currSlice->partArr[partMap[se.type]]); + + level = 1; // get inside loop + for (k=0; k<=16 && level!=0; k++) + { + level = se.value1 = DCLevel[k]; // level + se.value2 = DCRun [k]; // run +#if TRACE + trace_coeff(&se, plane, "16x16 DC", k, level, se.value2); +#endif + writeRunLevel_CABAC(currMB, &se, dataPart); + block_rate += se.len; + } + *mb_bits_coeff += block_rate; + rate += block_rate; + + // AC coeffs + if (cbp & 15) + { + switch (plane) + { + case 0: + default: + data_type = LUMA_16AC; + break; + case 1: + data_type = CB_16AC; + break; + case 2: + data_type = CR_16AC; + break; + } + + se.context = data_type; + se.type = SE_LUM_AC_INTRA; // element is of type AC + + // choose the appropriate data partition + dataPart = &(currSlice->partArr[partMap[se.type]]); + block_rate = 0; + + for (mb_y = 0; mb_y < 4; mb_y += 2) + { + for (mb_x = 0; mb_x < 4; mb_x += 2) + { + for (j = mb_y; j < mb_y + 2; j++) + { + int j1 = 2*(j >> 1); + int j2 = 2*(j & 0x01); + currMB->subblock_y = (short) (j << 2); + + for (i=mb_x; i < mb_x+2; i++) + { + currMB->subblock_x = (short) (i << 2); + b8 = j1 + (i >> 1) + pl_off; + b4 = j2 + (i & 0x01); + + ACLevel = currSlice->cofAC[b8][b4][0]; + ACRun = currSlice->cofAC[b8][b4][1]; + + level=1; // get inside loop + + for (k=0; k < 16 && level !=0; k++) + { + level = se.value1 = ACLevel[k]; // level + se.value2 = ACRun [k]; // run +#if TRACE + trace_coeff(&se, plane, "16x16 AC", k, level, se.value2); +#endif + writeRunLevel_CABAC(currMB, &se, dataPart); + block_rate += se.len; + } + } + } + } + } + + rate += block_rate; + *mb_bits_coeff += block_rate; + } + } + + return rate; +} + +/*! + ************************************************************************ + * \brief + * Get the Prediction from the Neighboring Blocks for Number of Nonzero Coefficients + * + * Luma Blocks + ************************************************************************ + */ +int predict_nnz(Macroblock *currMB, int block_type, int i,int j) +{ + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + PixelPos pix; + + int pred_nnz = 0; + int cnt = 0; + + // left block + get4x4Neighbour(currMB, (i << 2) - 1, (j << 2), p_Vid->mb_size[IS_LUMA], &pix); + + if (IS_INTRA(currMB) && pix.available && p_Vid->active_pps->constrained_intra_pred_flag && ((currSlice->partition_mode != 0) && !currSlice->idr_flag)) + { + pix.available &= p_Vid->intra_block[pix.mb_addr]; + if (!pix.available) + cnt++; + } + + if (pix.available) + { + switch (block_type) + { + case LUMA: + pred_nnz += p_Vid->nz_coeff [pix.mb_addr ][pix.x][pix.y]; + cnt++; + break; + case CB: + pred_nnz += p_Vid->nz_coeff [pix.mb_addr ][pix.x][4+pix.y]; + cnt++; + break; + case CR: + pred_nnz += p_Vid->nz_coeff [pix.mb_addr ][pix.x][8+pix.y]; + cnt++; + break; + default: + error("writeCoeff4x4_CAVLC: Invalid block type", 600); + break; + } + } + + // top block + get4x4Neighbour(currMB, (i<<2), (j<<2) - 1, p_Vid->mb_size[IS_LUMA], &pix); + + if (IS_INTRA(currMB) && pix.available && p_Vid->active_pps->constrained_intra_pred_flag && ((currSlice->partition_mode != 0) && !currSlice->idr_flag)) + { + pix.available &= p_Vid->intra_block[pix.mb_addr]; + if (!pix.available) + cnt++; + } + + if (pix.available) + { + switch (block_type) + { + case LUMA: + pred_nnz += p_Vid->nz_coeff [pix.mb_addr ][pix.x][pix.y]; + cnt++; + break; + case CB: + pred_nnz += p_Vid->nz_coeff [pix.mb_addr ][pix.x][4+pix.y]; + cnt++; + break; + case CR: + pred_nnz += p_Vid->nz_coeff [pix.mb_addr ][pix.x][8+pix.y]; + cnt++; + break; + default: + error("writeCoeff4x4_CAVLC: Invalid block type", 600); + break; + } + } + + if (cnt==2) + { + pred_nnz++; + pred_nnz>>=1; + } + + return pred_nnz; +} + + +/*! + ************************************************************************ + * \brief + * Get the Prediction from the Neighboring Blocks for Number of Nonzero Coefficients + * + * Chroma Blocks + ************************************************************************ + */ +int predict_nnz_chroma(Macroblock *currMB, int i,int j) +{ + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + PixelPos pix; + + int pred_nnz = 0; + int cnt = 0; + + if (p_Vid->yuv_format != YUV444) + { + //YUV420 and YUV422 + // left block + get4x4Neighbour(currMB, ((i & 0x01)<<2) - 1, ((j-4)<<2), p_Vid->mb_size[IS_CHROMA], &pix); + + if (IS_INTRA(currMB) && pix.available && p_Vid->active_pps->constrained_intra_pred_flag && ((currSlice->partition_mode != 0) && !currSlice->idr_flag)) + { + pix.available &= p_Vid->intra_block[pix.mb_addr]; + if (!pix.available) + cnt++; + } + + if (pix.available) + { + pred_nnz = p_Vid->nz_coeff [pix.mb_addr ][2 * (i >> 1) + pix.x][4 + pix.y]; + cnt++; + } + + // top block + get4x4Neighbour(currMB, ((i & 0x01)<<2), ((j-4)<<2) -1, p_Vid->mb_size[IS_CHROMA], &pix); + + if (IS_INTRA(currMB) && pix.available && p_Vid->active_pps->constrained_intra_pred_flag && ((currSlice->partition_mode != 0) && !currSlice->idr_flag)) + { + pix.available &= p_Vid->intra_block[pix.mb_addr]; + if (!pix.available) + cnt++; + } + + if (pix.available) + { + pred_nnz += p_Vid->nz_coeff [pix.mb_addr ][2 * (i >> 1) + pix.x][4 + pix.y]; + cnt++; + } + } + + + if (cnt==2) + { + pred_nnz++; + pred_nnz>>=1; + } + + return pred_nnz; +} + +/*! + ************************************************************************ + * \brief + * Writes coeff of an 4x4 block (CAVLC) + * + * \author + * Karl Lillevold + * contributions by James Au + ************************************************************************ + */ +int writeCoeff4x4_CAVLC_normal (Macroblock* currMB, int block_type, int b8, int b4, int param) +{ + Slice* currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currSlice->p_Vid; + int no_bits = 0; + SyntaxElement se; + DataPartition *dataPart; + const int *partMap = assignSE2partition[currSlice->partition_mode]; + + int k,level = 1,run = 0, vlcnum; + int numcoeff = 0, lastcoeff = 0, numtrailingones = 0; + int numones = 0, totzeros = 0, zerosleft, numcoef; + int numcoeff_vlc; + int code, level_two_or_higher; + int dptype = 0; + int nnz, max_coeff_num = 0, cdc = 0, cac = 0; + int subblock_x, subblock_y; + int *mb_bits_coeff = &currMB->bits.mb_y_coeff; +#if TRACE + char type[15]; +#endif + + static const int incVlc[] = {0, 3, 6, 12, 24, 48, 32768}; // maximum vlc = 6 + + + int* pLevel = NULL; + int* pRun = NULL; + + switch (block_type) + { + case LUMA: + max_coeff_num = 16; + + pLevel = currSlice->cofAC[b8][b4][0]; + pRun = currSlice->cofAC[b8][b4][1]; +#if TRACE + sprintf(type, "%s", "Luma"); +#endif + dptype = (IS_INTRA (currMB)) ? SE_LUM_AC_INTRA : SE_LUM_AC_INTER; + break; + + case CHROMA_AC: + max_coeff_num = 15; + mb_bits_coeff = &currMB->bits.mb_uv_coeff; + cac = 1; + + pLevel = currSlice->cofAC[b8][b4][0]; + pRun = currSlice->cofAC[b8][b4][1]; +#if TRACE + sprintf(type, "%s", "ChrAC"); +#endif + dptype = (IS_INTRA (currMB)) ? SE_CHR_AC_INTRA : SE_CHR_AC_INTER; + break; + + case CHROMA_DC: + max_coeff_num = p_Vid->num_cdc_coeff; + mb_bits_coeff = &currMB->bits.mb_uv_coeff; + cdc = 1; + + pLevel = currSlice->cofDC[param + 1][0]; + pRun = currSlice->cofDC[param + 1][1]; +#if TRACE + sprintf(type, "%s", "ChrDC"); +#endif + dptype = (IS_INTRA (currMB)) ? SE_CHR_DC_INTRA : SE_CHR_DC_INTER; + break; + + case LUMA_INTRA16x16AC: + max_coeff_num = 15; + + pLevel = currSlice->cofAC[b8][b4][0]; + pRun = currSlice->cofAC[b8][b4][1]; +#if TRACE + sprintf(type, "%s", "Lum16AC"); +#endif + dptype = SE_LUM_AC_INTRA; + break; + + case LUMA_INTRA16x16DC: + max_coeff_num = 16; + + pLevel = currSlice->cofDC[0][0]; + pRun = currSlice->cofDC[0][1]; +#if TRACE + sprintf(type, "%s", "Lum16DC"); +#endif + dptype = SE_LUM_DC_INTRA; + break; + + + default: + error("writeCoeff4x4_CAVLC: Invalid block type", 600); + break; + } + + dataPart = &(currSlice->partArr[partMap[dptype]]); + + for(k = 0; (k <= ((cdc) ? p_Vid->num_cdc_coeff : 16)) && level != 0; k++) + { + level = pLevel[k]; // level + run = pRun[k]; // run + + if (level) + { + + totzeros += run; + if (iabs(level) == 1) + { + numones ++; + numtrailingones ++; + numtrailingones = imin(numtrailingones, 3); // clip to 3 + } + else + { + numtrailingones = 0; + } + numcoeff ++; + lastcoeff = k; + } + } + + if (!cdc) + { + if (!cac) + { + // luma + subblock_x = ((b8 & 0x1) == 0) ? (((b4 & 0x1) == 0) ? 0 : 1) : (((b4 & 0x1) == 0) ? 2 : 3); + // horiz. position for coeff_count context + subblock_y = (b8 < 2) ? ((b4 < 2) ? 0 : 1) : ((b4 < 2) ? 2 : 3); + // vert. position for coeff_count context + nnz = predict_nnz(currMB, LUMA, subblock_x,subblock_y); + } + else + { + // chroma AC + subblock_x = param >> 4; + subblock_y = param & 15; + nnz = predict_nnz_chroma(currMB, subblock_x, subblock_y); + } + p_Vid->nz_coeff [currMB->mbAddrX ][subblock_x][subblock_y] = numcoeff; + + numcoeff_vlc = (nnz < 2) ? 0 : ((nnz < 4) ? 1 : ((nnz < 8) ? 2 : 3)); + } + else + { + // chroma DC (has its own VLC) + // numcoeff_vlc not relevant + numcoeff_vlc = 0; + + subblock_x = param; + subblock_y = param; + } + + se.type = dptype; + + se.value1 = numcoeff; + se.value2 = numtrailingones; + se.len = numcoeff_vlc; /* use len to pass vlcnum */ + +#if TRACE + snprintf(se.tracestring, + TRACESTRING_SIZE, "%s # c & tr.1s(%d,%d) vlc=%d #c=%d #t1=%d", + type, subblock_x, subblock_y, numcoeff_vlc, numcoeff, numtrailingones); +#endif + + if (!cdc) + writeSyntaxElement_NumCoeffTrailingOnes(&se, dataPart); + else + writeSyntaxElement_NumCoeffTrailingOnesChromaDC(p_Vid, &se, dataPart); + + *mb_bits_coeff += se.len; + no_bits += se.len; + + if (!numcoeff) + return no_bits; + + if (numcoeff) + { + code = 0; + for (k = lastcoeff; k > lastcoeff - numtrailingones; k--) + { + level = pLevel[k]; // level +#ifdef _DEBUG + if (iabs(level) > 1) + { + printf("ERROR: level > 1\n"); + exit(-1); + } +#endif + code <<= 1; + + code |= (level < 0); + } + + if (numtrailingones) + { + se.type = dptype; + + se.value2 = numtrailingones; + se.value1 = code; + +#if TRACE + snprintf(se.tracestring, + TRACESTRING_SIZE, "%s trailing ones sign (%d,%d)", + type, subblock_x, subblock_y); +#endif + + writeSyntaxElement_VLC (&se, dataPart); + *mb_bits_coeff += se.len; + no_bits += se.len; + + } + + // encode levels + level_two_or_higher = (numcoeff > 3 && numtrailingones == 3) ? 0 : 1; + + vlcnum = (numcoeff > 10 && numtrailingones < 3) ? 1 : 0; + + for (k = lastcoeff - numtrailingones; k >= 0; k--) + { + level = pLevel[k]; // level + + se.value1 = level; + se.type = dptype; + +#if TRACE + snprintf(se.tracestring, + TRACESTRING_SIZE, "%s lev (%d,%d) k=%d vlc=%d lev=%3d", + type, subblock_x, subblock_y, k, vlcnum, level); +#endif + + if (level_two_or_higher) + { + level_two_or_higher = 0; + + if (se.value1 > 0) + se.value1 --; + else + se.value1 ++; + } + + // encode level + + if (vlcnum == 0) + writeSyntaxElement_Level_VLC1(&se, dataPart, p_Vid->active_sps->profile_idc); + else + writeSyntaxElement_Level_VLCN(&se, vlcnum, dataPart, p_Vid->active_sps->profile_idc); + + // update VLC table + if (iabs(level) > incVlc[vlcnum]) + vlcnum++; + + if ((k == lastcoeff - numtrailingones) && iabs(level) > 3) + vlcnum = 2; + + *mb_bits_coeff += se.len; + no_bits += se.len; + } + + // encode total zeroes + if (numcoeff < max_coeff_num) + { + + se.type = dptype; + se.value1 = totzeros; + + vlcnum = numcoeff - 1; + + se.len = vlcnum; + +#if TRACE + snprintf(se.tracestring, + TRACESTRING_SIZE, "%s totalrun (%d,%d) vlc=%d totzeros=%3d", + type, subblock_x, subblock_y, vlcnum, totzeros); +#endif + if (!cdc) + writeSyntaxElement_TotalZeros(&se, dataPart); + else + writeSyntaxElement_TotalZerosChromaDC(p_Vid, &se, dataPart); + + *mb_bits_coeff += se.len; + no_bits += se.len; + } + + // encode run before each coefficient + zerosleft = totzeros; + numcoef = numcoeff; + for (k = lastcoeff; k >= 0; k--) + { + run = pRun[k]; // run + + se.value1 = run; + se.type = dptype; + + // for last coeff, run is remaining totzeros + // when zerosleft is zero, remaining coeffs have 0 run + if ((!zerosleft) || (numcoeff <= 1 )) + break; + + if (numcoef > 1 && zerosleft) + { + vlcnum = imin(zerosleft - 1, RUNBEFORE_NUM_M1); + se.len = vlcnum; + +#if TRACE + snprintf(se.tracestring, + TRACESTRING_SIZE, "%s run (%d,%d) k=%d vlc=%d run=%2d", + type, subblock_x, subblock_y, k, vlcnum, run); +#endif + + writeSyntaxElement_Run(&se, dataPart); + + *mb_bits_coeff += se.len; + no_bits += se.len; + + zerosleft -= run; + numcoef --; + } + } + } + + return no_bits; +} + +/*! + ************************************************************************ + * \brief + * Writes coeff of an 4x4 block (CAVLC) + * + * \author + * Karl Lillevold + * contributions by James Au + ************************************************************************ + */ +int writeCoeff4x4_CAVLC_444 (Macroblock* currMB, int block_type, int b8, int b4, int param) +{ + Slice* currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currSlice->p_Vid; + int no_bits = 0; + SyntaxElement se; + DataPartition *dataPart; + const int *partMap = assignSE2partition[currSlice->partition_mode]; + + int k,level = 1,run = 0, vlcnum; + int numcoeff = 0, lastcoeff = 0, numtrailingones = 0; + int numones = 0, totzeros = 0, zerosleft, numcoef; + int numcoeff_vlc; + int code, level_two_or_higher; + int dptype = 0; + int nnz, max_coeff_num = 0, cdc = 0, cac = 0; + int subblock_x, subblock_y; + int *mb_bits_coeff = &currMB->bits.mb_y_coeff; +#if TRACE + char type[15]; +#endif + + static const int incVlc[] = {0, 3, 6, 12, 24, 48, 32768}; // maximum vlc = 6 + + int* pLevel = NULL; + int* pRun = NULL; + + switch (block_type) + { + case LUMA: + max_coeff_num = 16; + + pLevel = currSlice->cofAC[b8][b4][0]; + pRun = currSlice->cofAC[b8][b4][1]; +#if TRACE + sprintf(type, "%s", "Luma"); +#endif + dptype = (IS_INTRA (currMB)) ? SE_LUM_AC_INTRA : SE_LUM_AC_INTER; + break; + case LUMA_INTRA16x16DC: + max_coeff_num = 16; + + pLevel = currSlice->cofDC[0][0]; + pRun = currSlice->cofDC[0][1]; +#if TRACE + sprintf(type, "%s", "Lum16DC"); +#endif + dptype = SE_LUM_DC_INTRA; + break; + case LUMA_INTRA16x16AC: + max_coeff_num = 15; + + pLevel = currSlice->cofAC[b8][b4][0]; + pRun = currSlice->cofAC[b8][b4][1]; +#if TRACE + sprintf(type, "%s", "Lum16AC"); +#endif + dptype = SE_LUM_AC_INTRA; + break; + case CB: + max_coeff_num = 16; + mb_bits_coeff = &currMB->bits.mb_cb_coeff; + + pLevel = currSlice->cofAC[4+b8][b4][0]; + pRun = currSlice->cofAC[4+b8][b4][1]; +#if TRACE + sprintf(type, "%s", "CB"); +#endif + dptype = (IS_INTRA (currMB)) ? SE_LUM_AC_INTRA : SE_LUM_AC_INTER; + break; + case CB_INTRA16x16DC: + max_coeff_num = 16; + mb_bits_coeff = &currMB->bits.mb_cb_coeff; + + pLevel = currSlice->cofDC[1][0]; + pRun = currSlice->cofDC[1][1]; +#if TRACE + sprintf(type, "%s", "CB_16DC"); +#endif + dptype = SE_LUM_DC_INTRA; + break; + case CB_INTRA16x16AC: + max_coeff_num = 15; + mb_bits_coeff = &currMB->bits.mb_cb_coeff; + + pLevel = currSlice->cofAC[4+b8][b4][0]; + pRun = currSlice->cofAC[4+b8][b4][1]; +#if TRACE + sprintf(type, "%s", "CB_16AC"); +#endif + dptype = SE_LUM_AC_INTRA; + break; + + case CR: + max_coeff_num = 16; + mb_bits_coeff = &currMB->bits.mb_cr_coeff; + + pLevel = currSlice->cofAC[8+b8][b4][0]; + pRun = currSlice->cofAC[8+b8][b4][1]; +#if TRACE + sprintf(type, "%s", "CR"); +#endif + dptype = (IS_INTRA (currMB)) ? SE_LUM_AC_INTRA : SE_LUM_AC_INTER; + break; + case CR_INTRA16x16DC: + max_coeff_num = 16; + mb_bits_coeff = &currMB->bits.mb_cr_coeff; + + pLevel = currSlice->cofDC[2][0]; + pRun = currSlice->cofDC[2][1]; +#if TRACE + sprintf(type, "%s", "CR_16DC"); +#endif + dptype = SE_LUM_DC_INTRA; + break; + case CR_INTRA16x16AC: + max_coeff_num = 15; + mb_bits_coeff = &currMB->bits.mb_cr_coeff; + + pLevel = currSlice->cofAC[8+b8][b4][0]; + pRun = currSlice->cofAC[8+b8][b4][1]; +#if TRACE + sprintf(type, "%s", "CR_16AC"); +#endif + dptype = SE_LUM_AC_INTRA; + break; + + case CHROMA_DC: + max_coeff_num = p_Vid->num_cdc_coeff; + mb_bits_coeff = &currMB->bits.mb_uv_coeff; + cdc = 1; + + pLevel = currSlice->cofDC[param+1][0]; + pRun = currSlice->cofDC[param+1][1]; +#if TRACE + sprintf(type, "%s", "ChrDC"); +#endif + dptype = (IS_INTRA (currMB)) ? SE_CHR_DC_INTRA : SE_CHR_DC_INTER; + break; + case CHROMA_AC: + max_coeff_num = 15; + mb_bits_coeff = &currMB->bits.mb_uv_coeff; + cac = 1; + + pLevel = currSlice->cofAC[b8][b4][0]; + pRun = currSlice->cofAC[b8][b4][1]; +#if TRACE + sprintf(type, "%s", "ChrAC"); +#endif + dptype = (IS_INTRA (currMB)) ? SE_CHR_AC_INTRA : SE_CHR_AC_INTER; + break; + default: + error("writeCoeff4x4_CAVLC: Invalid block type", 600); + break; + } + + dataPart = &(currSlice->partArr[partMap[dptype]]); + + for(k = 0; (k <= ((cdc) ? p_Vid->num_cdc_coeff : 16)) && level != 0; k++) + { + level = pLevel[k]; // level + run = pRun[k]; // run + + if (level) + { + + totzeros += run; // lets add run always (even if zero) to avoid conditional + if (iabs(level) == 1) + { + numones ++; + numtrailingones ++; + numtrailingones = imin(numtrailingones, 3); // clip to 3 + } + else + { + numtrailingones = 0; + } + numcoeff ++; + lastcoeff = k; + } + } + + if (!cdc) + { + if(block_type==LUMA || block_type==LUMA_INTRA16x16DC || block_type==LUMA_INTRA16x16AC + ||block_type==CHROMA_AC) + { + if (!cac) + { + // luma + subblock_x = ((b8 & 0x1) == 0) ? (((b4 & 0x1) == 0) ? 0 : 1) : (((b4 & 0x1) == 0) ? 2 : 3); + // horiz. position for coeff_count context + subblock_y = (b8 < 2) ? ((b4 < 2) ? 0 : 1) : ((b4 < 2) ? 2 : 3); + // vert. position for coeff_count context + nnz = predict_nnz(currMB, LUMA, subblock_x,subblock_y); + } + else + { + // chroma AC + subblock_x = param >> 4; + subblock_y = param & 15; + nnz = predict_nnz_chroma(currMB, subblock_x, subblock_y); + } + p_Vid->nz_coeff [currMB->mbAddrX ][subblock_x][subblock_y] = numcoeff; + } + else if (block_type==CB || block_type==CB_INTRA16x16DC + || block_type==CB_INTRA16x16AC) + { //CB in the common mode in 4:4:4 profiles + subblock_x = ((b8 & 0x1) == 0)?(((b4 & 0x1) == 0) ? 0 : 1):(((b4 & 0x1) == 0) ? 2 : 3); + // horiz. position for coeff_count context + subblock_y = (b8 < 2) ? ((b4 < 2) ? 0 : 1) : ((b4 < 2) ? 2 : 3); + // vert. position for coeff_count context + nnz = predict_nnz(currMB, CB, subblock_x,subblock_y); + p_Vid->nz_coeff [currMB->mbAddrX ][subblock_x][4+subblock_y] = numcoeff; + } + else + { //CR in the common mode in 4:4:4 profiles + subblock_x = ((b8 & 0x1) == 0)?(((b4 & 0x1) == 0) ? 0 : 1) : (((b4 & 0x1) == 0) ? 2 : 3); + // horiz. position for coeff_count context + subblock_y = (b8 < 2) ? ((b4 < 2) ? 0 : 1) : ((b4 < 2) ? 2 : 3); + // vert. position for coeff_count context + nnz = predict_nnz(currMB, CR, subblock_x,subblock_y); + p_Vid->nz_coeff [currMB->mbAddrX ][subblock_x][8+subblock_y] = numcoeff; + } + + numcoeff_vlc = (nnz < 2) ? 0 : ((nnz < 4) ? 1 : ((nnz < 8) ? 2 : 3)); + } + else + { + // chroma DC (has its own VLC) + // numcoeff_vlc not relevant + numcoeff_vlc = 0; + + subblock_x = param; + subblock_y = param; + } + + se.type = dptype; + + se.value1 = numcoeff; + se.value2 = numtrailingones; + se.len = numcoeff_vlc; /* use len to pass vlcnum */ + +#if TRACE + snprintf(se.tracestring, + TRACESTRING_SIZE, "%s # c & tr.1s(%d,%d) vlc=%d #c=%d #t1=%d", + type, subblock_x, subblock_y, numcoeff_vlc, numcoeff, numtrailingones); +#endif + + if (!cdc) + writeSyntaxElement_NumCoeffTrailingOnes(&se, dataPart); + else + writeSyntaxElement_NumCoeffTrailingOnesChromaDC(p_Vid, &se, dataPart); + + *mb_bits_coeff += se.len; + no_bits += se.len; + + if (!numcoeff) + return no_bits; + + if (numcoeff) + { + code = 0; + for (k = lastcoeff; k > lastcoeff - numtrailingones; k--) + { + level = pLevel[k]; // level + + if (iabs(level) > 1) + { + printf("ERROR: level > 1\n"); + exit(-1); + } + + code <<= 1; + + if (level < 0) + { + code |= 0x1; + } + } + + if (numtrailingones) + { + se.type = dptype; + + se.value2 = numtrailingones; + se.value1 = code; + +#if TRACE + snprintf(se.tracestring, + TRACESTRING_SIZE, "%s trailing ones sign (%d,%d)", + type, subblock_x, subblock_y); +#endif + + writeSyntaxElement_VLC (&se, dataPart); + *mb_bits_coeff += se.len; + no_bits += se.len; + + } + + // encode levels + level_two_or_higher = (numcoeff > 3 && numtrailingones == 3) ? 0 : 1; + + vlcnum = (numcoeff > 10 && numtrailingones < 3) ? 1 : 0; + + for (k = lastcoeff - numtrailingones; k >= 0; k--) + { + level = pLevel[k]; // level + + se.value1 = level; + se.type = dptype; + +#if TRACE + snprintf(se.tracestring, + TRACESTRING_SIZE, "%s lev (%d,%d) k=%d vlc=%d lev=%3d", + type, subblock_x, subblock_y, k, vlcnum, level); +#endif + + if (level_two_or_higher) + { + level_two_or_higher = 0; + + if (se.value1 > 0) + se.value1 --; + else + se.value1 ++; + } + + // encode level + + if (vlcnum == 0) + writeSyntaxElement_Level_VLC1(&se, dataPart, p_Vid->active_sps->profile_idc); + else + writeSyntaxElement_Level_VLCN(&se, vlcnum, dataPart, p_Vid->active_sps->profile_idc); + + // update VLC table + if (iabs(level) > incVlc[vlcnum]) + vlcnum++; + + if ((k == lastcoeff - numtrailingones) && iabs(level) > 3) + vlcnum = 2; + + *mb_bits_coeff += se.len; + no_bits += se.len; + } + + // encode total zeroes + if (numcoeff < max_coeff_num) + { + + se.type = dptype; + se.value1 = totzeros; + + vlcnum = numcoeff - 1; + + se.len = vlcnum; + +#if TRACE + snprintf(se.tracestring, + TRACESTRING_SIZE, "%s totalrun (%d,%d) vlc=%d totzeros=%3d", + type, subblock_x, subblock_y, vlcnum, totzeros); +#endif + if (!cdc) + writeSyntaxElement_TotalZeros(&se, dataPart); + else + writeSyntaxElement_TotalZerosChromaDC(p_Vid, &se, dataPart); + + *mb_bits_coeff += se.len; + no_bits += se.len; + } + + // encode run before each coefficient + zerosleft = totzeros; + numcoef = numcoeff; + for (k = lastcoeff; k >= 0; k--) + { + run = pRun[k]; // run + + se.value1 = run; + se.type = dptype; + + // for last coeff, run is remaining totzeros + // when zerosleft is zero, remaining coeffs have 0 run + if ((!zerosleft) || (numcoeff <= 1 )) + break; + + if (numcoef > 1 && zerosleft) + { + vlcnum = imin(zerosleft - 1, RUNBEFORE_NUM_M1); + se.len = vlcnum; + +#if TRACE + snprintf(se.tracestring, + TRACESTRING_SIZE, "%s run (%d,%d) k=%d vlc=%d run=%2d", + type, subblock_x, subblock_y, k, vlcnum, run); +#endif + + writeSyntaxElement_Run(&se, dataPart); + + *mb_bits_coeff += se.len; + no_bits += se.len; + + zerosleft -= run; + numcoef --; + } + } + } + + return no_bits; +} +/*! + ************************************************************************ + * \brief + * Change color plane for 4:4:4 Independent Mode + * + * \par Input: + * plane number + ************************************************************************/ +void change_plane_JV( VideoParameters *p_Vid, int nplane ) +{ + p_Vid->colour_plane_id = (char) nplane; + p_Vid->mb_data = p_Vid->mb_data_JV[nplane]; + p_Vid->enc_picture = p_Vid->enc_frame_picture_JV[nplane]; + p_Vid->pCurImg = p_Vid->imgData.frm_data[nplane]; +} + +/*! + ************************************************************************ + * \brief + * Make one frame picture from 4:4:4 plane + ************************************************************************/ +void make_frame_picture_JV(VideoParameters *p_Vid) +{ + int uv, line; + int nsize; + + p_Vid->enc_frame_picture[0] = p_Vid->enc_frame_picture_JV[0]; + + //copy; + if(p_Vid->currentSlice->nal_reference_idc) + { + nsize = (p_Vid->enc_frame_picture_JV[0]->size_y/BLOCK_SIZE)*(p_Vid->enc_frame_picture_JV[0]->size_x/BLOCK_SIZE)*sizeof(PicMotionParams); + memcpy( &(p_Vid->enc_frame_picture[0]->JVmv_info[PLANE_Y][0][0]), &(p_Vid->enc_frame_picture_JV[PLANE_Y]->mv_info[0][0]), nsize); + memcpy( &(p_Vid->enc_frame_picture[0]->JVmv_info[PLANE_U][0][0]), &(p_Vid->enc_frame_picture_JV[PLANE_U]->mv_info[0][0]), nsize); + memcpy( &(p_Vid->enc_frame_picture[0]->JVmv_info[PLANE_V][0][0]), &(p_Vid->enc_frame_picture_JV[PLANE_V]->mv_info[0][0]), nsize); + } + + for( uv=0; uv<2; uv++ ) + { + for( line=0; lineheight; line++ ) + { + nsize = sizeof(imgpel) * p_Vid->width; + memcpy( p_Vid->enc_frame_picture[0]->imgUV[uv][line], p_Vid->enc_frame_picture_JV[uv+1]->imgY[line], nsize ); + } + free_storable_picture(p_Vid, p_Vid->enc_frame_picture_JV[uv+1]); + } + +} + +Macroblock *alloc_mbs(VideoParameters *p_Vid, int mb_num, int layers) +{ + int i, j; + Macroblock *pMBs = (Macroblock *)calloc(mb_num, sizeof(Macroblock)), *pMB; + if(!pMBs) + return NULL; + for(i=0; iintra4x4_pred_buf[j], 3, 17); + get_mem2Dpel(&pMB->intra8x8_pred_buf[j], 3, 25); + get_mem2Dpel(&pMB->intra16x16_pred_buf[j], 3, 33); + } + pMB->intra4x4_pred = pMB->intra4x4_pred_buf[0]; + pMB->intra8x8_pred = pMB->intra8x8_pred_buf[0]; + pMB->intra16x16_pred = pMB->intra16x16_pred_buf[0]; + } + return pMBs; +} + +void setup_mbs(Macroblock *pMBs, int mb_num, int layer_id) +{ + int i; + Macroblock *pMB; + for(i=0; iintra4x4_pred = pMB->intra4x4_pred_buf[layer_id]; + pMB->intra8x8_pred = pMB->intra8x8_pred_buf[layer_id]; + pMB->intra16x16_pred = pMB->intra16x16_pred_buf[layer_id]; + } +} + +void free_mbs(Macroblock *pMBs, int mb_num) +{ + int i, j; + Macroblock *pMB; + if(!pMBs) + return; + for(i=0; iintra4x4_pred_buf[j]); + free_mem2Dpel(pMB->intra8x8_pred_buf[j]); + free_mem2Dpel(pMB->intra16x16_pred_buf[j]); + } + pMB->intra4x4_pred = NULL; + pMB->intra8x8_pred = NULL; + pMB->intra16x16_pred = NULL; + } + free(pMBs); +} diff --git a/lencod/src/macroblock_P444.c b/lencod/src/macroblock_P444.c new file mode 100644 index 0000000..83e0333 --- /dev/null +++ b/lencod/src/macroblock_P444.c @@ -0,0 +1,467 @@ +/*! + ************************************************************************************* + * \file macroblock_P444.c + * + * \brief + * Process one macroblock functions (P444 mode) + * + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * - Alexis Michael Tourapis + ************************************************************************************* + */ + +#include "contributors.h" +#include "global.h" +#include "blk_prediction.h" +#include "mc_prediction.h" +#include "md_common.h" +#include "rdopt.h" +#include "macroblock.h" + + +/*! + ************************************************************************ + * \brief + * Residual Coding of an 8x8 Luma block (not for intra) + * + * \return + * coefficient cost + ************************************************************************ + */ +int luma_residual_coding_p444_16x16 (Macroblock* currMB, //!< Current Macroblock to be coded + int block8x8, //!< block number of 8x8 block + short p_dir, //!< prediction direction + int list_mode[2], //!< list0 prediction mode (1-7, 0=DIRECT) + char *ref_idx) //!< reference pictures for each list +{ + + int *cbp = &(currMB->cbp); + int64 *cbp_blk = &(currMB->cbp_blk); + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + DecodedPictureBuffer *p_Dpb = p_Vid->p_Dpb_layer[p_Vid->dpb_layer_id]; + int i, j, nonzero = 0, cbp_blk_mask; + int coeff_cost = 0; + int mb_y = (block8x8 >> 1) << 3; + int mb_x = (block8x8 & 0x01) << 3; + int cbp_mask = 1 << block8x8; + + int skipped = (list_mode[0] == 0 && list_mode[1] == 0 && (currSlice->slice_type != B_SLICE)); + ColorPlane uv; + + char bipred_me = currMB->b8x8[block8x8].bipred; + currSlice->coeff_cost_cr[1] = currSlice->coeff_cost_cr[2] = 0; + + //memset( currSlice->cofAC[block8x8][0][0], 0, 4 * 2 * 65 * sizeof(int)); + + //===== luma prediction ====== + p_Dpb->pf_luma_prediction (currMB, mb_x, mb_y, 8, 8, p_dir, list_mode, ref_idx, bipred_me); + + //===== compute prediction residual ====== + compute_residue (&p_Vid->pCurImg[currMB->opix_y + mb_y], &currSlice->mb_pred[0][mb_y], &currSlice->mb_ores[0][mb_y], mb_x, currMB->pix_x + mb_x, 8, 8); + + for (uv = PLANE_U; uv <= PLANE_V; ++uv) + { + select_plane(p_Vid, uv); + p_Dpb->pf_chroma_prediction (currMB, uv - 1, mb_x, mb_y, 8, 8, p_dir, list_mode[0], list_mode[1], ref_idx[0], ref_idx[1], bipred_me); + + //===== compute prediction residual ====== + compute_residue(&p_Vid->pImgOrg[uv][currMB->opix_y + mb_y], &currSlice->mb_pred[uv][mb_y], &currSlice->mb_ores[uv][mb_y], mb_x, currMB->pix_x + mb_x, 8, 8); + } + select_plane(p_Vid, PLANE_Y); + + //===== loop over 4x4 blocks ===== + if(!currMB->luma_transform_size_8x8_flag) + { + //===== forward transform, Quantization, inverse Quantization, inverse transform, Reconstruction ===== + if (!skipped && ( (currSlice->NoResidueDirect != 1) || (currMB->qp_scaled[0] == 0 && p_Vid->active_sps->lossless_qpprime_flag == 1) )) + { + int block_y, block_x; + + for (block_y=mb_y; block_yresidual_transform_quant_luma_4x4 (currMB, PLANE_Y, block_x, block_y, &coeff_cost, 0); + + if (nonzero) + { + cbp_blk_mask = (block_x >> 2) + block_y; + (*cbp_blk) |= (int64) 1 << cbp_blk_mask; // one bit for every 4x4 block + (*cbp) |= cbp_mask; // one bit for the 4x4 blocks of an 8x8 block + } + + if (currSlice->slice_type != SP_SLICE && currSlice->slice_type != SI_SLICE) + { + for (uv = PLANE_U; uv <= PLANE_V; ++uv) + { + select_plane(p_Vid, (ColorPlane) uv); + nonzero = currMB->residual_transform_quant_luma_4x4( currMB, (ColorPlane) uv, block_x, block_y, &currSlice->coeff_cost_cr[uv], 0); + if (nonzero) + { + cbp_blk_mask = (block_x >> 2) + block_y; + (currSlice->cur_cbp_blk[uv]) |= (int64) 1 << cbp_blk_mask; // one bit for every 4x4 block + (currSlice->cmp_cbp[uv]) |= cbp_mask; // one bit for the 4x4 blocks of an 8x8 block + } + } + select_plane(p_Vid, PLANE_Y); + } + else + { + assert(currSlice->slice_type == SP_SLICE || currSlice->slice_type == SI_SLICE); //SP_SLICE not implemented for FREXT_AD444 + } + } + } + } + } + else + { + if (currSlice->NoResidueDirect != 1 && !skipped) + { + if (currSlice->slice_type != SP_SLICE && currSlice->slice_type != SI_SLICE) + nonzero = currMB->residual_transform_quant_luma_8x8 (currMB, PLANE_Y, block8x8, &coeff_cost, 0); + + if (nonzero) + { + (*cbp_blk) |= 51 << (4*block8x8 - 2*(block8x8 & 0x01)); // corresponds to 110011, as if all four 4x4 blocks contain coeff, shifted to block position + (*cbp) |= cbp_mask; // one bit for the 4x4 blocks of an 8x8 block + } + + if (currSlice->slice_type != SP_SLICE && currSlice->slice_type != SI_SLICE) + { + for (uv = PLANE_U; uv <= PLANE_V; ++uv) + { + select_plane(p_Vid, (ColorPlane) uv); + nonzero = currMB->residual_transform_quant_luma_8x8( currMB, (ColorPlane) uv, block8x8, &currSlice->coeff_cost_cr[uv], 0); + if (nonzero) + { + (currSlice->cur_cbp_blk[uv]) |= 51 << (4*block8x8-2*(block8x8 & 0x01)); // corresponds to 110011, as if all four 4x4 blocks contain coeff, shifted to block position + (currSlice->cmp_cbp[uv]) |= cbp_mask; // one bit for the 4x4 blocks of an 8x8 block + } + } + select_plane(p_Vid, PLANE_Y); + } + } + } + + if (currSlice->NoResidueDirect != 1 && !skipped && coeff_cost <= _LUMA_COEFF_COST_ && + ((currMB->qp_scaled[0])!=0 || p_Vid->active_sps->lossless_qpprime_flag==0)&& + !(currSlice->slice_type == SI_SLICE || (currSlice->slice_type == SP_SLICE && p_Vid->sp2_frame_indicator == TRUE )))// last set of conditions + // cannot skip when perfect reconstruction is as in switching pictures or SI pictures + { + coeff_cost = 0; + (*cbp) &= (63 - cbp_mask); + (*cbp_blk) &= ~(51 << (4 * block8x8 - 2 * (block8x8 & 0x01))); + + memset( currSlice->cofAC[block8x8][0][0], 0, 4 * 2 * 65 * sizeof(int)); + + copy_image_data_8x8(&p_Vid->enc_picture->imgY[currMB->pix_y + mb_y], &currSlice->mb_pred[0][mb_y], currMB->pix_x + mb_x, mb_x); + + if (currSlice->slice_type == SP_SLICE || currSlice->slice_type == SI_SLICE) + { + for (i = mb_x; i < mb_x + BLOCK_SIZE_8x8; i += BLOCK_SIZE) + for (j = mb_y; j < mb_y + BLOCK_SIZE_8x8; j += BLOCK_SIZE) + copyblock_sp(currMB, PLANE_Y, i, j); + } + } + + for (uv = PLANE_U; uv <= PLANE_V; ++uv) + { + if (currSlice->NoResidueDirect != 1 && !skipped && currSlice->coeff_cost_cr[uv] <= _LUMA_COEFF_COST_ && + (currMB->qp_scaled[uv]!=0 || p_Vid->active_sps->lossless_qpprime_flag==0))// last set of conditions + { + currSlice->coeff_cost_cr[uv] = 0; + currSlice->cmp_cbp[uv] &= (63 - cbp_mask); + currSlice->cur_cbp_blk[uv] &= ~(51 << (4*block8x8-2*(block8x8 & 0x01))); + + memset( currSlice->cofAC[block8x8 + 4 * uv][0][0], 0, 4 * 2 * 65 * sizeof(int)); + + copy_image_data_8x8(&p_Vid->enc_picture->imgUV[uv - 1][currMB->pix_y + mb_y], &currSlice->mb_pred[uv][mb_y], currMB->pix_x + mb_x, mb_x); + } + } + + return coeff_cost; +} + + +/*! + ************************************************************************ + * \brief + * Residual Coding of an 8x8 Luma block (not for intra) + * + * \return + * coefficient cost + ************************************************************************ + */ +int luma_residual_coding_p444_8x8 (Macroblock* currMB, //!< Current Macroblock to be coded + int *cbp, //!< Output: cbp (updated according to processed 8x8 luminance block) + int64 *cbp_blk, //!< Output: block cbp (updated according to processed 8x8 luminance block) + int block8x8, //!< block number of 8x8 block + short p_dir, //!< prediction direction + int list_mode[2], //!< list prediction mode (1-7, 0=DIRECT) + char *ref_idx) //!< reference pictures for each list +{ + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currSlice->p_Vid; + DecodedPictureBuffer *p_Dpb = p_Vid->p_Dpb_layer[p_Vid->dpb_layer_id]; + int block_y, block_x, pic_pix_x, nonzero = 0, cbp_blk_mask; + int coeff_cost = 0; + int mb_y = (block8x8 >> 1) << 3; + int mb_x = (block8x8 & 0x01) << 3; + int cbp_mask = 1 << block8x8; + int bxx, byy; // indexing curr_blk + int skipped = (list_mode[0] == 0 && list_mode[1] == 0 && (currSlice->slice_type != B_SLICE)); + ColorPlane uv; + + char bipred_me = currMB->b8x8[block8x8].bipred; + currSlice->coeff_cost_cr[1] = currSlice->coeff_cost_cr[2] = 0; + + //memset( currSlice->cofAC[block8x8][0][0], 0, 4 * 2 * 65 * sizeof(int)); + + //===== loop over 4x4 blocks ===== + if(!currMB->luma_transform_size_8x8_flag) + { + if (((p_dir == 0 || p_dir == 2 )&& list_mode[0] < 5) || ((p_dir == 1 || p_dir == 2 ) && list_mode[1] < 5)) + { + p_Dpb->pf_luma_prediction (currMB, mb_x, mb_y, 8, 8, p_dir, list_mode, ref_idx, bipred_me); + + //===== compute prediction residual ====== + compute_residue (&p_Vid->pCurImg[currMB->opix_y + mb_y], &currSlice->mb_pred[0][mb_y], &currSlice->mb_ores[0][mb_y], mb_x, currMB->pix_x + mb_x, 8, 8); + } + + for (byy=0, block_y=mb_y; block_ypix_x + block_x; + cbp_blk_mask = (block_x >> 2) + block_y; + + //===== prediction of 4x4 block ===== + if (!(((p_dir == 0 || p_dir == 2 )&& list_mode[0] < 5) || ((p_dir == 1 || p_dir == 2 ) && list_mode[1] < 5))) + { + p_Dpb->pf_luma_prediction (currMB, block_x, block_y, 4, 4, p_dir, list_mode, ref_idx, bipred_me); + + //===== compute prediction residual ====== + compute_residue(&p_Vid->pCurImg[currMB->opix_y + block_y], &currSlice->mb_pred[0][block_y], &currSlice->mb_ores[0][block_y], block_x, pic_pix_x, 4, 4); + } + + for (uv = PLANE_U; uv <= PLANE_V; ++uv) + { + select_plane(p_Vid, uv); + p_Dpb->pf_chroma_prediction (currMB, uv - 1, block_x, block_y, 4, 4, p_dir, list_mode[0], list_mode[1], ref_idx[0], ref_idx[1], bipred_me); + + //===== compute prediction residual ====== + compute_residue(&p_Vid->pImgOrg[uv][currMB->opix_y + block_y], &currSlice->mb_pred[uv][block_y], &currSlice->mb_ores[uv][block_y], block_x, pic_pix_x, 4, 4); + } + select_plane(p_Vid, PLANE_Y); + + //===== forward transform, Quantization, inverse Quantization, inverse transform, Reconstruction ===== + if (!skipped && ( (currSlice->NoResidueDirect != 1) || (currMB->qp_scaled[0] == 0 && p_Vid->active_sps->lossless_qpprime_flag == 1) )) + { + //===== forward transform, Quantization, inverse Quantization, inverse transform, Reconstruction ===== + nonzero = currMB->residual_transform_quant_luma_4x4 (currMB, PLANE_Y, block_x, block_y, &coeff_cost, 0); + + if (nonzero) + { + (*cbp_blk) |= (int64)1 << cbp_blk_mask; // one bit for every 4x4 block + (*cbp) |= cbp_mask; // one bit for the 4x4 blocks of an 8x8 block + } + + if (currSlice->slice_type != SP_SLICE && currSlice->slice_type != SI_SLICE) + { + for (uv = PLANE_U; uv <= PLANE_V; ++uv) + { + select_plane(p_Vid, (ColorPlane) uv); + nonzero = currMB->residual_transform_quant_luma_4x4( currMB, (ColorPlane) uv, block_x, block_y, &currSlice->coeff_cost_cr[uv], 0); + if (nonzero) + { + (currSlice->cur_cbp_blk[uv]) |= (int64) 1 << cbp_blk_mask; // one bit for every 4x4 block + (currSlice->cmp_cbp[uv]) |= cbp_mask; // one bit for the 4x4 blocks of an 8x8 block + } + } + select_plane(p_Vid, PLANE_Y); + } + else + { + assert(currSlice->slice_type == SP_SLICE || currSlice->slice_type == SI_SLICE); //SP_SLICE not implementd for FREXT_AD444 + } + } + } + } + } + else + { + block_y = mb_y; + block_x = mb_x; + + pic_pix_x = currMB->pix_x + block_x; + + cbp_blk_mask = (block_x>>2) + block_y; + + //===== prediction of 4x4 block ===== + p_Dpb->pf_luma_prediction (currMB, block_x, block_y, 8, 8, p_dir, list_mode, ref_idx, bipred_me); + + //===== compute prediction residual ====== + compute_residue (&p_Vid->pCurImg[currMB->opix_y + block_y], &currSlice->mb_pred[0][block_y], &currSlice->mb_ores[0][block_y], block_x, pic_pix_x, 8, 8); + + for (uv = PLANE_U; uv <= PLANE_V; ++uv) + { + select_plane(p_Vid, (ColorPlane) uv); + p_Dpb->pf_chroma_prediction (currMB, uv - 1, block_x, block_y, 8, 8, p_dir, list_mode[0], list_mode[1], ref_idx[0], ref_idx[1], bipred_me); + + //===== compute prediction residual ====== + compute_residue (&p_Vid->pImgOrg[uv][currMB->opix_y + block_y], &currSlice->mb_pred[uv][block_y], &currSlice->mb_ores[uv][block_y], block_x, pic_pix_x, 8, 8); + } + select_plane(p_Vid, PLANE_Y); + + if (currSlice->NoResidueDirect != 1 && !skipped) + { + if (currSlice->slice_type != SP_SLICE && currSlice->slice_type != SI_SLICE) + nonzero = currMB->residual_transform_quant_luma_8x8 (currMB, PLANE_Y, block8x8, &coeff_cost, 0); + + if (nonzero) + { + (*cbp_blk) |= 51 << (4*block8x8 - 2*(block8x8 & 0x01)); // corresponds to 110011, as if all four 4x4 blocks contain coeff, shifted to block position + (*cbp) |= cbp_mask; // one bit for the 4x4 blocks of an 8x8 block + } + + if (currSlice->slice_type != SP_SLICE && currSlice->slice_type != SI_SLICE) + { + for (uv = PLANE_U; uv <= PLANE_V; ++uv) + { + select_plane(p_Vid, (ColorPlane) uv); + nonzero = currMB->residual_transform_quant_luma_8x8( currMB, (ColorPlane) uv, block8x8, &currSlice->coeff_cost_cr[uv], 0); + if (nonzero) + { + (currSlice->cur_cbp_blk[uv]) |= 51 << (4*block8x8-2*(block8x8 & 0x01)); // corresponds to 110011, as if all four 4x4 blocks contain coeff, shifted to block position + (currSlice->cmp_cbp[uv]) |= cbp_mask; // one bit for the 4x4 blocks of an 8x8 block + } + } + select_plane(p_Vid, PLANE_Y); + } + } + } + + if (currSlice->NoResidueDirect != 1 && !skipped && coeff_cost <= _LUMA_COEFF_COST_ && + ((currMB->qp_scaled[0])!=0 || p_Vid->active_sps->lossless_qpprime_flag==0)&& + !(currSlice->slice_type == SI_SLICE || (currSlice->slice_type == SP_SLICE && p_Vid->sp2_frame_indicator == TRUE )))// last set of conditions + // cannot skip when perfect reconstruction is as in switching pictures or SI pictures + { + coeff_cost = reset_block(currMB, cbp, cbp_blk, block8x8); + } + + for (uv = PLANE_U; uv <= PLANE_V; ++uv) + { + if (currSlice->NoResidueDirect != 1 && !skipped && currSlice->coeff_cost_cr[uv] <= _LUMA_COEFF_COST_ && + (currMB->qp_scaled[uv]!=0 || p_Vid->active_sps->lossless_qpprime_flag==0))// last set of conditions + { + currSlice->coeff_cost_cr[uv] = 0; + currSlice->cmp_cbp[uv] &= (63 - cbp_mask); + currSlice->cur_cbp_blk[uv] &= ~(51 << (4*block8x8-2*(block8x8 & 0x01))); + + memset( currSlice->cofAC[block8x8 + 4 * uv][0][0], 0, 4 * 2 * 65 * sizeof(int)); + + copy_image_data_8x8(&p_Vid->enc_picture->imgUV[uv - 1][currMB->pix_y + mb_y], &currSlice->mb_pred[uv][mb_y], currMB->pix_x + mb_x, mb_x); + } + } + + return coeff_cost; +} + + +/*! + ************************************************************************ + * \brief + * Residual Coding of a Luma macroblock (not for intra) + ************************************************************************ + */ +void luma_residual_coding_p444 (Macroblock *currMB) +{ + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + + int uv, i,j,block8x8,b8_x,b8_y; + int list_mode[2]; + char list_ref_idx[2]; + short p_dir; + int sum_cnt_nonz[3] = {0 ,0, 0}; + int is_skip = currSlice->slice_type == P_SLICE && currMB->mb_type == PSKIP; + + currMB->cbp = 0; + currMB->cbp_blk = 0; + currSlice->cmp_cbp[1] = currSlice->cmp_cbp[2] = 0; + currSlice->cur_cbp_blk[1] = currSlice->cur_cbp_blk[2] = 0; + + if (is_skip || currMB->mb_type == 1) + { + + for (block8x8=0; block8x8<4; ++block8x8) + { + currSlice->set_modes_and_reframe (currMB, block8x8, &p_dir, list_mode, list_ref_idx); + + sum_cnt_nonz[0] += luma_residual_coding_p444_16x16 (currMB, block8x8, p_dir, list_mode, list_ref_idx); + sum_cnt_nonz[1] += currSlice->coeff_cost_cr[1]; + sum_cnt_nonz[2] += currSlice->coeff_cost_cr[2]; + } + } + else + { + for (block8x8=0; block8x8<4; ++block8x8) + { + currSlice->set_modes_and_reframe (currMB, block8x8, &p_dir, list_mode, list_ref_idx); + + sum_cnt_nonz[0] += luma_residual_coding_p444_8x8 (currMB, &(currMB->cbp), &(currMB->cbp_blk), block8x8, p_dir, list_mode, list_ref_idx); + + if(p_Vid->P444_joined) + { + sum_cnt_nonz[1] += currSlice->coeff_cost_cr[1]; + sum_cnt_nonz[2] += currSlice->coeff_cost_cr[2]; + } + } + } + + if ((is_skip || + (sum_cnt_nonz[0] <= _LUMA_MB_COEFF_COST_ && ((currMB->qp_scaled[0])!=0 || p_Vid->active_sps->lossless_qpprime_flag==0))) && + !(currSlice->slice_type == SI_SLICE || (currSlice->slice_type == SP_SLICE && p_Vid->sp2_frame_indicator == TRUE )))// last set of conditions + //cannot skip if SI or switching SP frame perfect reconstruction is needed + { + currMB->cbp &= 0xfffff0 ; + currMB->cbp_blk &= 0xff0000 ; + + copy_image_data_16x16(&p_Vid->enc_picture->imgY[currMB->pix_y], currSlice->mb_pred[0], currMB->pix_x, 0); + + memset( currSlice->cofAC[0][0][0], 0, 2080 * sizeof(int)); // 4 * 4 * 2 * 65 + + if (currSlice->slice_type == SP_SLICE || currSlice->slice_type == SI_SLICE) + { + for(block8x8=0;block8x8<4; ++block8x8) + { + b8_x=(block8x8&1)<<3; + b8_y=(block8x8&2)<<2; + for (i = b8_x; i < b8_x + BLOCK_SIZE_8x8; i += 4) + for (j = b8_y; j < b8_y + BLOCK_SIZE_8x8;j += 4) + copyblock_sp(currMB, PLANE_Y, i, j); + } + } + } + + for (uv = PLANE_U; uv <= PLANE_V; ++uv) + { + if(is_skip || (sum_cnt_nonz[uv] <= _LUMA_MB_COEFF_COST_ && + ((currMB->qp_scaled[uv])!=0 ||p_Vid->active_sps->lossless_qpprime_flag==0))) + { + currSlice->cmp_cbp[uv] &= 0xfffff0 ; + currSlice->cur_cbp_blk[uv] &= 0xff0000 ; + + copy_image_data_16x16(&p_Vid->enc_picture->p_img[uv][currMB->pix_y], currSlice->mb_pred[uv], currMB->pix_x, 0); + + memset( currSlice->cofAC[4 * uv][0][0], 0, 2080 * sizeof(int)); // 4 * 4 * 2 * 65 + } + currMB->cbp |= currSlice->cmp_cbp[uv]; + } +} + + + diff --git a/lencod/src/mb_access.c b/lencod/src/mb_access.c new file mode 100644 index 0000000..476658c --- /dev/null +++ b/lencod/src/mb_access.c @@ -0,0 +1,575 @@ + +/*! + ************************************************************************************* + * \file mb_access.c + * + * \brief + * Functions for macroblock neighborhoods + * + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * - Karsten Suehring + ************************************************************************************* + */ + +#include "global.h" +#include "mb_access.h" + +/*! + ************************************************************************ + * \brief + * returns 1 if the macroblock at the given address is available + ************************************************************************ + */ +Boolean mb_is_available(int mbAddr, Macroblock *currMB) +{ + VideoParameters *p_Vid = currMB->p_Vid; + if ((mbAddr < 0) || (mbAddr > ((int)p_Vid->PicSizeInMbs - 1))) + return FALSE; + + // the following line checks both: slice number and if the mb has been decoded + if (!currMB->DeblockCall) + { + if (p_Vid->mb_data[mbAddr].slice_nr != currMB->slice_nr) + return FALSE; + } + + return TRUE; +} + + +/*! + ************************************************************************ + * \brief + * Checks the availability of neighboring macroblocks of + * the current macroblock for prediction and context determination; + ************************************************************************ + */ +void CheckAvailabilityOfNeighbors(Macroblock *currMB) +{ + VideoParameters *p_Vid = currMB->p_Vid; + const int mb_nr = currMB->mbAddrX; + BlockPos *PicPos = p_Vid->PicPos; + + // mark all neighbors as unavailable + currMB->mb_up = NULL; + currMB->mb_left = NULL; + + if (p_Vid->mb_aff_frame_flag) + { + int cur_mb_pair = mb_nr >> 1; + currMB->mbAddrA = 2 * (cur_mb_pair - 1); + currMB->mbAddrB = 2 * (cur_mb_pair - p_Vid->PicWidthInMbs); + currMB->mbAddrC = 2 * (cur_mb_pair - p_Vid->PicWidthInMbs + 1); + currMB->mbAddrD = 2 * (cur_mb_pair - p_Vid->PicWidthInMbs - 1); + + currMB->mbAvailA = (byte) (mb_is_available(currMB->mbAddrA, currMB) && ((PicPos[cur_mb_pair ].x)!=0)); + currMB->mbAvailB = (byte) (mb_is_available(currMB->mbAddrB, currMB)); + currMB->mbAvailC = (byte) (mb_is_available(currMB->mbAddrC, currMB) && ((PicPos[cur_mb_pair + 1].x)!=0)); + currMB->mbAvailD = (byte) (mb_is_available(currMB->mbAddrD, currMB) && ((PicPos[cur_mb_pair ].x)!=0)); + } + else + { + currMB->mbAddrA = mb_nr - 1; + currMB->mbAddrB = mb_nr - p_Vid->PicWidthInMbs; + currMB->mbAddrC = mb_nr - p_Vid->PicWidthInMbs + 1; + currMB->mbAddrD = mb_nr - p_Vid->PicWidthInMbs - 1; + + currMB->mbAvailA = (byte) (mb_is_available(currMB->mbAddrA, currMB) && ((PicPos[mb_nr ].x)!=0)); + currMB->mbAvailB = (byte) (mb_is_available(currMB->mbAddrB, currMB)); + currMB->mbAvailC = (byte) (mb_is_available(currMB->mbAddrC, currMB) && ((PicPos[mb_nr + 1].x)!=0)); + currMB->mbAvailD = (byte) (mb_is_available(currMB->mbAddrD, currMB) && ((PicPos[mb_nr ].x)!=0)); + } + + if (currMB->mbAvailA) currMB->mb_left = &(p_Vid->mb_data[currMB->mbAddrA]); + if (currMB->mbAvailB) currMB->mb_up = &(p_Vid->mb_data[currMB->mbAddrB]); +} + + +/*! + ************************************************************************ + * \brief + * returns the x and y macroblock coordinates for a given MbAddress + ************************************************************************ + */ +void get_mb_block_pos_normal (BlockPos *PicPos, int mb_addr, short *x, short *y) +{ + *x = (short) PicPos[ mb_addr ].x; + *y = (short) PicPos[ mb_addr ].y; +} + +/*! + ************************************************************************ + * \brief + * returns the x and y macroblock coordinates for a given MbAddress + * for mbaff type slices + ************************************************************************ + */ +void get_mb_block_pos_mbaff (BlockPos *PicPos, int mb_addr, short *x, short *y) +{ + *x = (short) PicPos[mb_addr>>1].x; + *y = (short) ((PicPos[mb_addr>>1].y << 1) + (mb_addr & 0x01)); +} + +/*! + ************************************************************************ + * \brief + * returns the x and y sample coordinates for a given MbAddress + ************************************************************************ + */ +void get_mb_pos (VideoParameters *p_Vid, int mb_addr, int mb_size[2], short *x, short *y) +{ + p_Vid->get_mb_block_pos(p_Vid->PicPos, mb_addr, x, y); + + (*x) = (short) ((*x) * mb_size[0]); + (*y) = (short) ((*y) * mb_size[1]); +} + + +/*! + ************************************************************************ + * \brief + * get neighbouring positions for non-aff coding + * \param currMB + * current macroblock + * \param xN + * input x position + * \param yN + * input y position + * \param mb_size + * Macroblock size in pixel (according to luma or chroma MB access) + * \param pix + * returns position informations + ************************************************************************ + */ +void getNonAffNeighbour(Macroblock *currMB, int xN, int yN, int mb_size[2], PixelPos *pix) +{ + BlockPos *PicPos = currMB->p_Vid->PicPos; + if (xN < 0) + { + if (yN < 0) + { + pix->mb_addr = currMB->mbAddrD; + pix->available = currMB->mbAvailD; + } + else if ((yN >= 0)&&(yN < mb_size[1])) + { + pix->mb_addr = currMB->mbAddrA; + pix->available = currMB->mbAvailA; + } + else + { + pix->available = FALSE; + } + } + else if ((xN >= 0)&&(xN < mb_size[0])) + { + if (yN<0) + { + pix->mb_addr = currMB->mbAddrB; + pix->available = currMB->mbAvailB; + } + else if (((yN >= 0)&&(yN < mb_size[1]))) + { + pix->mb_addr = currMB->mbAddrX; + pix->available = TRUE; + } + else + { + pix->available = FALSE; + } + } + else if ((xN >= mb_size[0])&&(yN < 0)) + { + pix->mb_addr = currMB->mbAddrC; + pix->available = currMB->mbAvailC; + } + else + { + pix->available = FALSE; + } + + if (pix->available || currMB->DeblockCall) + { + pix->x = (short) (xN & (mb_size[0] - 1)); + pix->y = (short) (yN & (mb_size[1] - 1)); + pix->pos_x = (short) (pix->x + PicPos[ pix->mb_addr ].x * mb_size[0]); + pix->pos_y = (short) (pix->y + PicPos[ pix->mb_addr ].y * mb_size[1]); + } +} + +/*! + ************************************************************************ + * \brief + * get neighboring positions for aff coding + * \param currMB + * current macroblock + * \param xN + * input x position + * \param yN + * input y position + * \param mb_size + * Macroblock size in pixel (according to luma or chroma MB access) + * \param pix + * returns position informations + ************************************************************************ + */ +void getAffNeighbour(Macroblock *currMB, int xN, int yN, int mb_size[2], PixelPos *pix) +{ + VideoParameters *p_Vid = currMB->p_Vid; + int maxW, maxH; + int yM = -1; + + maxW = mb_size[0]; + maxH = mb_size[1]; + + // initialize to "not available" + pix->available = FALSE; + + if(yN > (maxH - 1)) + { + return; + } + if (xN > (maxW - 1) && yN >= 0 && yN < maxH) + { + return; + } + + if (xN < 0) + { + if (yN < 0) + { + if(!currMB->mb_field) + { + // frame + if ((currMB->mbAddrX & 0x01) == 0) + { + // top + pix->mb_addr = currMB->mbAddrD + 1; + pix->available = currMB->mbAvailD; + yM = yN; + } + else + { + // bottom + pix->mb_addr = currMB->mbAddrA; + pix->available = currMB->mbAvailA; + if (currMB->mbAvailA) + { + if(!p_Vid->mb_data[currMB->mbAddrA].mb_field) + { + yM = yN; + } + else + { + (pix->mb_addr)++; + yM = (yN + maxH) >> 1; + } + } + } + } + else + { + // field + if ((currMB->mbAddrX & 0x01) == 0) + { + // top + pix->mb_addr = currMB->mbAddrD; + pix->available = currMB->mbAvailD; + if (currMB->mbAvailD) + { + if(!p_Vid->mb_data[currMB->mbAddrD].mb_field) + { + (pix->mb_addr)++; + yM = 2 * yN; + } + else + { + yM = yN; + } + } + } + else + { + // bottom + pix->mb_addr = currMB->mbAddrD+1; + pix->available = currMB->mbAvailD; + yM = yN; + } + } + } + else + { // xN < 0 && yN >= 0 + if (yN >= 0 && yN mb_field) + { + // frame + if ((currMB->mbAddrX & 0x01) == 0) + { + // top + pix->mb_addr = currMB->mbAddrA; + pix->available = currMB->mbAvailA; + if (currMB->mbAvailA) + { + if(!p_Vid->mb_data[currMB->mbAddrA].mb_field) + { + yM = yN; + } + else + { + (pix->mb_addr)+= ((yN & 0x01) != 0); + yM = yN >> 1; + } + } + } + else + { + // bottom + pix->mb_addr = currMB->mbAddrA; + pix->available = currMB->mbAvailA; + if (currMB->mbAvailA) + { + if(!p_Vid->mb_data[currMB->mbAddrA].mb_field) + { + (pix->mb_addr)++; + yM = yN; + } + else + { + (pix->mb_addr)+= ((yN & 0x01) != 0); + yM = (yN + maxH) >> 1; + } + } + } + } + else + { + // field + if ((currMB->mbAddrX & 0x01) == 0) + { + // top + pix->mb_addr = currMB->mbAddrA; + pix->available = currMB->mbAvailA; + if (currMB->mbAvailA) + { + if(!p_Vid->mb_data[currMB->mbAddrA].mb_field) + { + if (yN < (maxH >> 1)) + { + yM = yN << 1; + } + else + { + (pix->mb_addr)++; + yM = (yN << 1 ) - maxH; + } + } + else + { + yM = yN; + } + } + } + else + { + // bottom + pix->mb_addr = currMB->mbAddrA; + pix->available = currMB->mbAvailA; + if (currMB->mbAvailA) + { + if(!p_Vid->mb_data[currMB->mbAddrA].mb_field) + { + if (yN < (maxH >> 1)) + { + yM = (yN << 1) + 1; + } + else + { + (pix->mb_addr)++; + yM = (yN << 1 ) + 1 - maxH; + } + } + else + { + (pix->mb_addr)++; + yM = yN; + } + } + } + } + } + } + } + else + { // xN >= 0 + if (xN >= 0 && xN < maxW) + { + if (yN<0) + { + if (!currMB->mb_field) + { + //frame + if ((currMB->mbAddrX & 0x01) == 0) + { + //top + pix->mb_addr = currMB->mbAddrB; + // for the deblocker if the current MB is a frame and the one above is a field + // then the neighbor is the top MB of the pair + if (currMB->mbAvailB) + { + if (!(currMB->DeblockCall == 1 && (p_Vid->mb_data[currMB->mbAddrB]).mb_field)) + pix->mb_addr += 1; + } + + pix->available = currMB->mbAvailB; + yM = yN; + } + else + { + // bottom + pix->mb_addr = currMB->mbAddrX - 1; + pix->available = TRUE; + yM = yN; + } + } + else + { + // field + if ((currMB->mbAddrX & 0x01) == 0) + { + // top + pix->mb_addr = currMB->mbAddrB; + pix->available = currMB->mbAvailB; + if (currMB->mbAvailB) + { + if(!p_Vid->mb_data[currMB->mbAddrB].mb_field) + { + (pix->mb_addr)++; + yM = 2 * yN; + } + else + { + yM = yN; + } + } + } + else + { + // bottom + pix->mb_addr = currMB->mbAddrB + 1; + pix->available = currMB->mbAvailB; + yM = yN; + } + } + } + else + { + // yN >=0 + // for the deblocker if this is the extra edge then do this special stuff + if (yN == 0 && currMB->DeblockCall == 2) + { + pix->mb_addr = currMB->mbAddrB + 1; + pix->available = TRUE; + yM = yN - 1; + } + + else if ((yN >= 0) && (yN mb_addr = currMB->mbAddrX; + pix->available = TRUE; + yM = yN; + } + } + } + else + { // xN >= maxW + if(yN < 0) + { + if (!currMB->mb_field) + { + // frame + if ((currMB->mbAddrX & 0x01) == 0) + { + // top + pix->mb_addr = currMB->mbAddrC + 1; + pix->available = currMB->mbAvailC; + yM = yN; + } + else + { + // bottom + pix->available = FALSE; + } + } + else + { + // field + if ((currMB->mbAddrX & 0x01) == 0) + { + // top + pix->mb_addr = currMB->mbAddrC; + pix->available = currMB->mbAvailC; + if (currMB->mbAvailC) + { + if(!p_Vid->mb_data[currMB->mbAddrC].mb_field) + { + (pix->mb_addr)++; + yM = 2* yN; + } + else + { + yM = yN; + } + } + } + else + { + // bottom + pix->mb_addr = currMB->mbAddrC + 1; + pix->available = currMB->mbAvailC; + yM = yN; + } + } + } + } + } + if (pix->available || currMB->DeblockCall) + { + pix->x = (short) (xN & (maxW - 1)); + pix->y = (short) (yM & (maxH - 1)); + get_mb_pos(p_Vid, pix->mb_addr, mb_size, &(pix->pos_x), &(pix->pos_y)); + pix->pos_x = pix->pos_x + pix->x; + pix->pos_y = pix->pos_y + pix->y; + } +} + + +/*! + ************************************************************************ + * \brief + * get neighboring 4x4 block + * \param currMB + * current macroblock + * \param block_x + * input x block position + * \param block_y + * input y block position + * \param mb_size + * Macroblock size in pixel (according to luma or chroma MB access) + * \param pix + * returns position informations + ************************************************************************ + */ +void get4x4Neighbour (Macroblock *currMB, int block_x, int block_y, int mb_size[2], PixelPos *pix) +{ + currMB->p_Vid->getNeighbour(currMB, block_x, block_y, mb_size, pix); + + if (pix->available) + { + pix->x >>= 2; + pix->y >>= 2; + pix->pos_x >>= 2; + pix->pos_y >>= 2; + } +} diff --git a/lencod/src/mbuffer.c b/lencod/src/mbuffer.c new file mode 100644 index 0000000..2dd6652 --- /dev/null +++ b/lencod/src/mbuffer.c @@ -0,0 +1,3407 @@ + +/*! + *********************************************************************** + * \file + * mbuffer.c + * + * \brief + * Frame buffer functions + * + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * - Karsten Suehring + * - Alexis Tourapis + *********************************************************************** + */ + +#include + +#include "global.h" +#include "mbuffer.h" +#include "mbuffer_common.h" +#include "memalloc.h" +#include "output.h" +#include "image.h" +#include "nalucommon.h" +#include "img_luma.h" +#include "img_chroma.h" +#include "errdo.h" + +extern void SbSMuxBasic(ImageData *imgOut, ImageData *imgIn0, ImageData *imgIn1, int offset); +extern void init_stats (InputParameters *p_Inp, StatParameters *stats); +static void insert_picture_in_dpb (VideoParameters *p_Vid, FrameStore* fs, StorablePicture* p); +static void output_one_frame_from_dpb (DecodedPictureBuffer *p_Dpb, FrameFormat *output); +static void gen_field_ref_ids (StorablePicture *p); +static int flush_unused_frame_from_dpb (DecodedPictureBuffer *p_Dpb); +static void process_picture_in_dpb_s(VideoParameters *p_Vid, StorablePicture *p_pic); +static StorablePicture * clone_storable_picture( VideoParameters *p_Vid, StorablePicture *p_pic ); + +#define MAX_LIST_SIZE 33 + +extern void free_mem2Duint16(uint16 **array2D); +extern void free_mem3Duint16(uint16 ***array3D); + +/*! + ************************************************************************ + * \brief + * Print out list of pictures in DPB. Used for debug purposes. + ************************************************************************ + */ +void dump_dpb(DecodedPictureBuffer *p_Dpb) +{ +#if DUMP_DPB + unsigned i; + + for (i=0; iused_size;i++) + { + printf("("); + printf("fn=%d ", p_Dpb->fs[i]->frame_num); + if (p_Dpb->fs[i]->is_used & 1) + { + if (p_Dpb->fs[i]->top_field) + printf("T: poc=%d ", p_Dpb->fs[i]->top_field->poc); + else + printf("T: poc=%d ", p_Dpb->fs[i]->frame->top_poc); + } + if (p_Dpb->fs[i]->is_used & 2) + { + if (p_Dpb->fs[i]->bottom_field) + printf("B: poc=%d ", p_Dpb->fs[i]->bottom_field->poc); + else + printf("B: poc=%d ", p_Dpb->fs[i]->frame->bottom_poc); + } + if (p_Dpb->fs[i]->is_used == 3) + printf("F: poc=%d ", p_Dpb->fs[i]->frame->poc); + printf("G: poc=%d) ", p_Dpb->fs[i]->poc); + if (p_Dpb->fs[i]->is_reference) printf ("ref (%d) ", p_Dpb->fs[i]->is_reference); + if (p_Dpb->fs[i]->is_long_term) printf ("lt_ref (%d) ", p_Dpb->fs[i]->is_reference); + if (p_Dpb->fs[i]->is_output) printf ("out "); + if (p_Dpb->fs[i]->is_used == 3) + { + if (p_Dpb->fs[i]->frame->non_existing) printf ("ne "); + } +#if (MVC_EXTENSION_ENABLE) + if(p_Dpb->p_Inp->num_of_views==2) + printf ("view_id=%d ", p_Dpb->fs[i]->view_id); +#endif + printf ("\n"); + } +#endif +} + +/*! + ************************************************************************ + * \brief + * Returns the size of the dpb depending on level and picture size + * + * + ************************************************************************ + */ +int getDpbSize(VideoParameters *p_Vid, seq_parameter_set_rbsp_t *active_sps) +{ + int pic_size = (active_sps->pic_width_in_mbs_minus1 + 1) * (active_sps->pic_height_in_map_units_minus1 + 1) * (active_sps->frame_mbs_only_flag?1:2) * 384; + + int size = 0; + + switch (active_sps->level_idc) + { + case 9: + size = 152064; + break; + case 10: + size = 152064; + break; + case 11: + if (!is_FREXT_profile(active_sps->profile_idc) && (active_sps->constrained_set3_flag == 1)) + size = 152064; + else + size = 345600; + break; + case 12: + size = 912384; + break; + case 13: + size = 912384; + break; + case 20: + size = 912384; + break; + case 21: + size = 1824768; + break; + case 22: + size = 3110400; + break; + case 30: + size = 3110400; + break; + case 31: + size = 6912000; + break; + case 32: + size = 7864320; + break; + case 40: + size = 12582912; + break; + case 41: + size = 12582912; + break; + case 42: + size = 13369344; + break; + case 50: + size = 42393600; + break; + case 51: + size = 70778880; + break; + default: + error ("undefined level", 500); + break; + } + + size /= pic_size; + if(is_MVC_profile(p_Vid->p_Inp->ProfileIDC)) + { + int num_views = p_Vid->p_Inp->num_of_views; + size = imin(2*size, imax(1, RoundLog2(num_views))*16)/num_views; + } + else + size = imin( size, 16); + + if (active_sps->vui_parameters_present_flag && active_sps->vui_seq_parameters.bitstream_restriction_flag) + { + if ((int)active_sps->vui_seq_parameters.max_dec_frame_buffering > size) + { + error ("max_dec_frame_buffering larger than MaxDpbSize", 500); + } + size = imax (1, active_sps->vui_seq_parameters.max_dec_frame_buffering); + } + + return size; +} + +/*! + ************************************************************************ + * \brief + * Check then number of frames marked "used for reference" and break + * if maximum is exceeded + * + ************************************************************************ + */ +void check_num_ref(DecodedPictureBuffer *p_Dpb) +{ + if ((int)(p_Dpb->ltref_frames_in_buffer + p_Dpb->ref_frames_in_buffer ) > imax(1, p_Dpb->num_ref_frames)) + { + error ("Max. number of reference frames exceeded. Invalid stream.", 500); + } +} + + +/*! + ************************************************************************ + * \brief + * Allocate memory for decoded picture buffer and initialize with sane values. + * + ************************************************************************ + */ +void init_dpb(VideoParameters *p_Vid, DecodedPictureBuffer *p_Dpb) +{ + unsigned i; + + p_Dpb->p_Vid = p_Vid; + p_Dpb->p_Inp = p_Vid->p_Inp; + p_Dpb->num_ref_frames = p_Vid->num_ref_frames; + + if (p_Dpb->init_done) + { + free_dpb(p_Dpb); + } + + p_Dpb->size = getDpbSize(p_Vid, p_Vid->sps[p_Dpb->layer_id]); + + + if (p_Dpb->size < (unsigned int) (p_Vid->p_Inp)->num_ref_frames) + { + error ("DPB size at specified level is smaller than the specified number of reference frames. This is not allowed.\n", 1000); + } + + p_Dpb->used_size = 0; + p_Dpb->last_picture = NULL; + + p_Dpb->ref_frames_in_buffer = 0; + p_Dpb->ltref_frames_in_buffer = 0; + + p_Dpb->fs = calloc(p_Dpb->size, sizeof (FrameStore*)); + if (NULL==p_Dpb->fs) + no_mem_exit("init_dpb: p_Dpb->fs"); + + p_Dpb->fs_ref = calloc(p_Dpb->size, sizeof (FrameStore*)); + if (NULL==p_Dpb->fs_ref) + no_mem_exit("init_dpb: p_Dpb->fs_ref"); + + p_Dpb->fs_ltref = calloc(p_Dpb->size, sizeof (FrameStore*)); + if (NULL==p_Dpb->fs_ltref) + no_mem_exit("init_dpb: p_Dpb->fs_ltref"); + + for (i = 0; i < p_Dpb->size; i++) + { + p_Dpb->fs[i] = alloc_frame_store(); + p_Dpb->fs_ref[i] = NULL; + p_Dpb->fs_ltref[i] = NULL; + } + p_Dpb->fs_ilref = calloc(1, sizeof (FrameStore*)); + if (NULL==p_Dpb->fs_ilref) + no_mem_exit("init_dpb: p_Dpb->fs_ilref"); + + if(p_Dpb->layer_id == 0) + { + p_Dpb->fs_ilref[0] = NULL; + } + else if(p_Dpb->layer_id == 1) + { + p_Dpb->fs_ilref[0] = alloc_frame_store(); + } + + p_Dpb->last_output_poc = INT_MIN; + +#if (MVC_EXTENSION_ENABLE) + p_Dpb->last_output_view_id = 0; +#endif + + p_Vid->last_has_mmco_5 = 0; + + p_Dpb->init_done = 1; + +} + + +/*! + ************************************************************************ + * \brief + * Free memory for decoded picture buffer. + ************************************************************************ + */ +void free_dpb(DecodedPictureBuffer *p_Dpb) +{ + VideoParameters *p_Vid = p_Dpb->p_Vid; + unsigned i; + if (p_Dpb->fs) + { + for (i=0; isize; i++) + { + free_frame_store(p_Vid, p_Dpb->fs[i]); + } + free (p_Dpb->fs); + p_Dpb->fs=NULL; + } + + if (p_Dpb->fs_ref) + { + free (p_Dpb->fs_ref); + } + if (p_Dpb->fs_ltref) + { + free (p_Dpb->fs_ltref); + } + if(p_Dpb->fs_ilref) + { + if(p_Dpb->fs_ilref[0]) + { + free_frame_store(p_Vid, p_Dpb->fs_ilref[0]); + p_Dpb->fs_ilref[0] = NULL; + } + free(p_Dpb->fs_ilref); + } + p_Dpb->last_output_poc = INT_MIN; + +#if (MVC_EXTENSION_ENABLE) + p_Dpb->last_output_view_id = 0; +#endif + + p_Dpb->init_done = 0; +} + + +/*! + ************************************************************************ + * \brief + * Allocate memory for decoded picture buffer frame stores and initialize with sane values. + * + * \return + * the allocated FrameStore structure + ************************************************************************ + */ +FrameStore* alloc_frame_store(void) +{ + FrameStore *f; + + f = calloc (1, sizeof(FrameStore)); + if (NULL==f) + no_mem_exit("alloc_frame_store: f"); + + f->is_used = 0; + f->is_reference = 0; + f->is_long_term = 0; + f->is_orig_reference = 0; + + f->is_output = 0; + + f->frame = NULL;; + f->top_field = NULL; + f->bottom_field = NULL; + +#if (MVC_EXTENSION_ENABLE) + f->view_id = -1; + f->inter_view_flag[0] = 0; + f->inter_view_flag[1] = 0; + f->anchor_pic_flag[0] = 0; + f->anchor_pic_flag[1] = 0; +#endif + + return f; +} + +void alloc_pic_motion(PicMotionParamsOld *motion, int size_y, int size_x) +{ + motion->mb_field = calloc (size_y * size_x, sizeof(byte)); + if (motion->mb_field == NULL) + no_mem_exit("alloc_storable_picture: motion->mb_field"); +} + +/*! + ************************************************************************ + * \brief + * Allocate memory for a stored picture. + * + * \param p_Vid + * VideoParameters + * \param structure + * picture structure + * \param size_x + * horizontal luma size + * \param size_y + * vertical luma size + * \param size_x_cr + * horizontal chroma size + * \param size_y_cr + * vertical chroma size + * + * \return + * the allocated StorablePicture structure + ************************************************************************ + */ +StorablePicture* alloc_storable_picture(VideoParameters *p_Vid, PictureStructure structure, int size_x, int size_y, int size_x_cr, int size_y_cr) +{ + StorablePicture *s; + int nplane; + InputParameters *p_Inp = p_Vid->p_Inp; + + //printf ("Allocating (%s) picture (x=%d, y=%d, x_cr=%d, y_cr=%d)\n", (type == FRAME)?"FRAME":(type == TOP_FIELD)?"TOP_FIELD":"BOTTOM_FIELD", size_x, size_y, size_x_cr, size_y_cr); + + s = calloc (1, sizeof(StorablePicture)); + if (NULL==s) + no_mem_exit("alloc_storable_picture: s"); + + s->imgY = NULL; + s->imgUV = NULL; + s->imgY_sub = NULL; + s->imgUV_sub = NULL; + + s->p_img_sub[0] = NULL; + s->p_img_sub[1] = NULL; + s->p_img_sub[2] = NULL; + + s->de_mem = NULL; + +#if (MVC_EXTENSION_ENABLE) + if ((p_Vid->nal_reference_idc != NALU_PRIORITY_DISPOSABLE) || ((p_Inp->num_of_views == 2) && p_Vid->view_id == 0)) //p_Vid->inter_view_flag[structure?structure-1: structure])) +#else + if (p_Vid->nal_reference_idc != NALU_PRIORITY_DISPOSABLE) +#endif + { + if (!p_Inp->OnTheFlyFractMCP) // JLT : on-the-fly flag + { + get_mem4Dpel_pad(&(s->imgY_sub), 4, 4, size_y, size_x, IMG_PAD_SIZE_Y, IMG_PAD_SIZE_X); + s->imgY = s->imgY_sub[0][0]; + + if ( p_Inp->ChromaMCBuffer || p_Vid->P444_joined || (p_Inp->yuv_format==YUV444 && !p_Vid->P444_joined)) + { + // UV components + if ( p_Vid->yuv_format != YUV400 ) + { + if ( p_Vid->yuv_format == YUV420 ) + { + get_mem5Dpel_pad(&(s->imgUV_sub), 2, 8, 8, size_y_cr, size_x_cr, p_Vid->pad_size_uv_y, p_Vid->pad_size_uv_x); + } + else if ( p_Vid->yuv_format == YUV422 ) + { + get_mem5Dpel_pad(&(s->imgUV_sub), 2, 4, 8, size_y_cr, size_x_cr, p_Vid->pad_size_uv_y, p_Vid->pad_size_uv_x); + } + else + { // YUV444 + get_mem5Dpel_pad(&(s->imgUV_sub), 2, 4, 4, size_y_cr, size_x_cr, p_Vid->pad_size_uv_y, p_Vid->pad_size_uv_x); + } + s->p_img_sub[1] = s->imgUV_sub[0]; + s->p_img_sub[2] = s->imgUV_sub[1]; + s->imgUV = (imgpel ***)malloc(2*sizeof(imgpel**)); + s->imgUV[0] = s->imgUV_sub[0][0][0]; + s->imgUV[1] = s->imgUV_sub[1][0][0]; + } + } + else + { + get_mem3Dpel_pad(&(s->imgUV), 2, size_y_cr, size_x_cr, p_Vid->pad_size_uv_y, p_Vid->pad_size_uv_x); + } + } + else if ( p_Inp->OnTheFlyFractMCP == OTF_L1 ) // OTF L1 + { + get_mem4Dpel_pad(&(s->imgY_sub), 2, 2, size_y, size_x, IMG_PAD_SIZE_Y, IMG_PAD_SIZE_X); + s->imgY = s->imgY_sub[0][0]; + + if ( p_Inp->ChromaMCBuffer || p_Vid->P444_joined || (p_Inp->yuv_format==YUV444 && !p_Vid->P444_joined)) + { + // UV components + if ( p_Vid->yuv_format != YUV400 ) + { + if ( p_Vid->yuv_format == YUV420 ) + { + get_mem5Dpel_pad(&(s->imgUV_sub), 2, 4, 4, size_y_cr, size_x_cr, p_Vid->pad_size_uv_y, p_Vid->pad_size_uv_x); + } + else if ( p_Vid->yuv_format == YUV422 ) + { + get_mem5Dpel_pad(&(s->imgUV_sub), 2, 2, 4, size_y_cr, size_x_cr, p_Vid->pad_size_uv_y, p_Vid->pad_size_uv_x); + } + else + { // YUV444 + get_mem5Dpel_pad(&(s->imgUV_sub), 2, 2, 2, size_y_cr, size_x_cr, p_Vid->pad_size_uv_y, p_Vid->pad_size_uv_x); + } + s->p_img_sub[1] = s->imgUV_sub[0]; + s->p_img_sub[2] = s->imgUV_sub[1]; + s->imgUV = (imgpel ***)malloc(2*sizeof(imgpel**)); + s->imgUV[0] = s->imgUV_sub[0][0][0]; + s->imgUV[1] = s->imgUV_sub[1][0][0]; + } + } + else + { + get_mem3Dpel_pad(&(s->imgUV), 2, size_y_cr, size_x_cr, p_Vid->pad_size_uv_y, p_Vid->pad_size_uv_x); + } + } + else // OTF_L2 + { + get_mem2Dpel_pad(&(s->imgY), size_y, size_x, IMG_PAD_SIZE_Y, IMG_PAD_SIZE_X); + get_mem3Dpel_pad(&(s->imgUV), 2, size_y_cr, size_x_cr, p_Vid->pad_size_uv_y, p_Vid->pad_size_uv_x); + } + } + else + { + get_mem2Dpel_pad(&(s->imgY), size_y, size_x, IMG_PAD_SIZE_Y, IMG_PAD_SIZE_X); + get_mem3Dpel_pad(&(s->imgUV), 2, size_y_cr, size_x_cr, p_Vid->pad_size_uv_y, p_Vid->pad_size_uv_x); + } + + s->p_img[0] = s->imgY; + s->p_curr_img = s->p_img[0]; + s->p_curr_img_sub = s->p_img_sub[0]; + + if (p_Vid->yuv_format != YUV400) + { + //get_mem3Dpel (&(s->imgUV), 2, size_y_cr, size_x_cr); + s->p_img[1] = s->imgUV[0]; + s->p_img[2] = s->imgUV[1]; + } + + get_mem2Dmp (&s->mv_info, (size_y >> BLOCK_SHIFT), (size_x >> BLOCK_SHIFT)); + alloc_pic_motion(&s->motion, (size_y >> BLOCK_SHIFT), (size_x >> BLOCK_SHIFT)); + + if( (p_Inp->separate_colour_plane_flag != 0) ) + { + for( nplane=0; nplaneJVmv_info[nplane], (size_y >> BLOCK_SHIFT), (size_x >> BLOCK_SHIFT)); + alloc_pic_motion(&s->JVmotion[nplane], (size_y >> BLOCK_SHIFT), (size_x >> BLOCK_SHIFT)); + } + } + + if (p_Inp->rdopt == 3) + { + errdo_alloc_storable_picture(s, p_Vid, p_Inp, size_x, size_y, size_x_cr, size_y_cr); + } + + s->pic_num=0; + s->frame_num=0; + s->long_term_frame_idx=0; + s->long_term_pic_num=0; + s->used_for_reference=0; + s->is_long_term=0; + s->non_existing=0; + s->is_output = 0; + + s->structure=structure; + s->size_x = size_x; + s->size_y = size_y; + s->size_x_padded = size_x + 2 * IMG_PAD_SIZE_X; + s->size_y_padded = size_y + 2 * IMG_PAD_SIZE_Y; + s->size_x_pad = size_x + 2 * IMG_PAD_SIZE_X - 1 - MB_BLOCK_SIZE -IMG_PAD_SIZE_X; + s->size_y_pad = size_y + 2 * IMG_PAD_SIZE_Y - 1 - MB_BLOCK_SIZE -IMG_PAD_SIZE_Y; + s->size_x_cr = size_x_cr; + s->size_y_cr = size_y_cr; + s->size_x_cr_pad = (int) (size_x_cr - 1) + (p_Vid->pad_size_uv_x << 1) - (p_Vid->mb_cr_size_x) - p_Vid->pad_size_uv_x; + s->size_y_cr_pad = (int) (size_y_cr - 1) + (p_Vid->pad_size_uv_y << 1) - (p_Vid->mb_cr_size_y) - p_Vid->pad_size_uv_y; + s->pad_size_uv_x = p_Vid->pad_size_uv_x; + s->pad_size_uv_y = p_Vid->pad_size_uv_y; + + s->top_field = NULL; + s->bottom_field = NULL; + s->frame = NULL; + + s->coded_frame = 0; + s->mb_aff_frame_flag = 0; + + memset(s->ref_pic_na, 0xff, sizeof(int)*6); + + init_stats(p_Inp, &s->stats); + return s; +} + + +/*! + ************************************************************************ + * \brief + * Free frame store memory. + * + * \param p_Vid + * VideoParameters + * \param f + * FrameStore to be freed + * + ************************************************************************ + */ +void free_frame_store(VideoParameters *p_Vid, FrameStore* f) +{ + if (f) + { + if (f->frame) + { + free_storable_picture(p_Vid, f->frame); + f->frame=NULL; + } + if (f->top_field) + { + free_storable_picture(p_Vid, f->top_field); + f->top_field=NULL; + } + if (f->bottom_field) + { + free_storable_picture(p_Vid, f->bottom_field); + f->bottom_field=NULL; + } + free(f); + } +} + +void free_pic_motion(PicMotionParamsOld *motion) +{ + if (motion->mb_field) + { + free(motion->mb_field); + motion->mb_field = NULL; + } +} + +static void free_frame_data_memory(StorablePicture *picture, int bFreeImage) +{ + if(picture) + { + if (picture->imgY_sub) + { + if(bFreeImage) + { + picture->imgY = NULL; + if( picture->otf_flag == OTF_L1 ) // JLT : on-the-fly mode + { + free_mem4Dpel_pad(picture->imgY_sub, 4, IMG_PAD_SIZE_Y, IMG_PAD_SIZE_X); + } + else + { + free_mem4Dpel_pad(picture->imgY_sub, 16, IMG_PAD_SIZE_Y, IMG_PAD_SIZE_X); + } + picture->imgY_sub=NULL; + } + else + { + int k; + int K = (picture->otf_flag==OTF_L1) ? (4):(16) ; + for(k = 1; k < K; k++) + { + if(picture->imgY_sub[k>>2][k&3]) + { + free_mem2Dpel_pad(picture->imgY_sub[k>>2][k&3], IMG_PAD_SIZE_Y, IMG_PAD_SIZE_X); + picture->imgY_sub[k>>2][k&3] = NULL; + } + } + } + } + + if (picture->imgUV_sub) + { + int iUVResX = 4*(picture->size_x/picture->size_x_cr); + int iUVResY = 4*(picture->size_y/picture->size_y_cr); + + if ( picture->otf_flag == OTF_L1 ) // JLT : on-the-fly mode + { + iUVResX >>= 1 ; + iUVResY >>= 1 ; + } + + if(bFreeImage) + { + if(picture->imgUV) + { + free(picture->imgUV); + picture->imgUV = NULL; + } + free_mem5Dpel_pad(picture->imgUV_sub, 2*iUVResY*iUVResX, picture->pad_size_uv_y, picture->pad_size_uv_x); + picture->imgUV_sub = NULL; + } + else + { + int i, j, k; + for(k=1; kimgUV_sub[0][j][i]) + { + free_mem2Dpel_pad(picture->imgUV_sub[0][j][i], picture->pad_size_uv_y, picture->pad_size_uv_x); + picture->imgUV_sub[0][j][i] = NULL; + } + if(picture->imgUV_sub[1][j][i]) + { + free_mem2Dpel_pad(picture->imgUV_sub[1][j][i], picture->pad_size_uv_y, picture->pad_size_uv_x); + picture->imgUV_sub[1][j][i] = NULL; + } + } + } + } + + if (picture->mv_info) + { + free_mem2Dmp(picture->mv_info); + picture->mv_info = NULL; + } + + free_pic_motion(&picture->motion); + } +} + +/*! + ************************************************************************ + * \brief + * Free picture memory. + * + * \param p_Vid + * VideoParameters + * \param p + * Picture to be freed + * + ************************************************************************ + */ +void free_storable_picture(VideoParameters *p_Vid, StorablePicture* p) +{ + if (p) + { + InputParameters *p_Inp = p_Vid->p_Inp; + + if(p->imgY && !p->imgY_sub) + { + free_mem2Dpel_pad(p->imgY, IMG_PAD_SIZE_Y, IMG_PAD_SIZE_X); + p->imgY=NULL; + } + + if (p->imgUV && !p->imgUV_sub) + { + free_mem3Dpel_pad(p->imgUV, 2, p->pad_size_uv_y, p->pad_size_uv_x); + p->imgUV=NULL; + } + + free_frame_data_memory(p, 1); + + if( (p_Inp->separate_colour_plane_flag != 0) ) + { + int nplane; + for( nplane=0; nplaneJVmv_info[nplane]) + { + free_mem2Dmp(p->JVmv_info[nplane]); + p->JVmv_info[nplane] = NULL; + } + free_pic_motion(&p->JVmotion[nplane]); + } + p->p_curr_img = NULL; + p->p_curr_img_sub = NULL; + p->p_img[0] = NULL; + p->p_img[1] = NULL; + p->p_img[2] = NULL; + p->p_img_sub[0] = NULL; + p->p_img_sub[1] = NULL; + p->p_img_sub[2] = NULL; + } + + if (p_Inp->rdopt == 3) + { + errdo_free_storable_picture(p); + } + + free(p); + p = NULL; + } +} + +/*! + ************************************************************************ + * \brief + * mark FrameStore unused for reference + * + ************************************************************************ + */ +void unmark_for_reference(FrameStore* fs) +{ + if (fs->is_used & 1) + { + if (fs->top_field) + { + fs->top_field->used_for_reference = 0; + } + } + if (fs->is_used & 2) + { + if (fs->bottom_field) + { + fs->bottom_field->used_for_reference = 0; + } + } + if (fs->is_used == 3) + { + if (fs->top_field && fs->bottom_field) + { + fs->top_field->used_for_reference = 0; + fs->bottom_field->used_for_reference = 0; + } + fs->frame->used_for_reference = 0; + } + + fs->is_reference = 0; + + free_frame_data_memory(fs->frame, 0); + free_frame_data_memory(fs->top_field, 0); + free_frame_data_memory(fs->bottom_field, 0); +} + + +/*! + ************************************************************************ + * \brief + * mark FrameStore unused for reference and reset long term flags + * + ************************************************************************ + */ +void unmark_for_long_term_reference(FrameStore* fs) +{ + if (fs->is_used & 1) + { + if (fs->top_field) + { + fs->top_field->used_for_reference = 0; + fs->top_field->is_long_term = 0; + } + } + if (fs->is_used & 2) + { + if (fs->bottom_field) + { + fs->bottom_field->used_for_reference = 0; + fs->bottom_field->is_long_term = 0; + } + } + if (fs->is_used == 3) + { + if (fs->top_field && fs->bottom_field) + { + fs->top_field->used_for_reference = 0; + fs->top_field->is_long_term = 0; + fs->bottom_field->used_for_reference = 0; + fs->bottom_field->is_long_term = 0; + } + fs->frame->used_for_reference = 0; + fs->frame->is_long_term = 0; + } + + free_frame_data_memory(fs->frame, 0); + free_frame_data_memory(fs->top_field, 0); + free_frame_data_memory(fs->bottom_field, 0); + + fs->is_reference = 0; + fs->is_long_term = 0; +} + + + +void update_pic_num(Slice *currSlice) +{ + unsigned int i; + //VideoParameters *p_Vid = currSlice->p_Vid; + DecodedPictureBuffer *p_Dpb = currSlice->p_Dpb; + + int add_top = 0, add_bottom = 0; + + int max_frame_num = currSlice->max_frame_num; + + + if (currSlice->structure == FRAME) + { + for (i=0; iref_frames_in_buffer; i++) + { + if ( p_Dpb->fs_ref[i]->is_used==3 ) + { + if ((p_Dpb->fs_ref[i]->frame->used_for_reference)&&(!p_Dpb->fs_ref[i]->frame->is_long_term)) + { + if( p_Dpb->fs_ref[i]->frame_num > currSlice->frame_num ) + { + p_Dpb->fs_ref[i]->frame_num_wrap = p_Dpb->fs_ref[i]->frame_num - max_frame_num; + } + else + { + p_Dpb->fs_ref[i]->frame_num_wrap = p_Dpb->fs_ref[i]->frame_num; + } + p_Dpb->fs_ref[i]->frame->pic_num = p_Dpb->fs_ref[i]->frame_num_wrap; + } + } + } + // update long_term_pic_num + for (i = 0; i < p_Dpb->ltref_frames_in_buffer; i++) + { + if (p_Dpb->fs_ltref[i]->is_used==3) + { + if (p_Dpb->fs_ltref[i]->frame->is_long_term) + { + p_Dpb->fs_ltref[i]->frame->long_term_pic_num = p_Dpb->fs_ltref[i]->frame->long_term_frame_idx; + } + } + } + } + else + { + if (currSlice->structure == TOP_FIELD) + { + add_top = 1; + add_bottom = 0; + } + else + { + add_top = 0; + add_bottom = 1; + } + + for (i=0; iref_frames_in_buffer; i++) + { + if (p_Dpb->fs_ref[i]->is_reference) + { + if( p_Dpb->fs_ref[i]->frame_num > currSlice->frame_num ) + { + p_Dpb->fs_ref[i]->frame_num_wrap = p_Dpb->fs_ref[i]->frame_num - max_frame_num; + } + else + { + p_Dpb->fs_ref[i]->frame_num_wrap = p_Dpb->fs_ref[i]->frame_num; + } + if (p_Dpb->fs_ref[i]->is_reference & 1) + { + p_Dpb->fs_ref[i]->top_field->pic_num = (2 * p_Dpb->fs_ref[i]->frame_num_wrap) + add_top; + } + if (p_Dpb->fs_ref[i]->is_reference & 2) + { + p_Dpb->fs_ref[i]->bottom_field->pic_num = (2 * p_Dpb->fs_ref[i]->frame_num_wrap) + add_bottom; + } + } + } + // update long_term_pic_num + for (i=0; iltref_frames_in_buffer; i++) + { + if (p_Dpb->fs_ltref[i]->is_long_term & 1) + { + p_Dpb->fs_ltref[i]->top_field->long_term_pic_num = 2 * p_Dpb->fs_ltref[i]->top_field->long_term_frame_idx + add_top; + } + if (p_Dpb->fs_ltref[i]->is_long_term & 2) + { + p_Dpb->fs_ltref[i]->bottom_field->long_term_pic_num = 2 * p_Dpb->fs_ltref[i]->bottom_field->long_term_frame_idx + add_bottom; + } + } + } +} +/*! + ************************************************************************ + * \brief + * Initialize reference lists depending on current slice type + * + ************************************************************************ + */ +void init_lists_i_slice(Slice *currSlice) +{ + currSlice->listXsize[0] = 0; + currSlice->listXsize[1] = 0; +} + + + +/*! + ************************************************************************ + * \brief + * Initialize reference lists for a P Slice + * + ************************************************************************ + */ +void init_lists_p_slice(Slice *currSlice) +{ + VideoParameters *p_Vid = currSlice->p_Vid; + DecodedPictureBuffer *p_Dpb = currSlice->p_Dpb; + + unsigned int i, j; + + int list0idx = 0; + int listltidx = 0; + +#if (MVC_EXTENSION_ENABLE) + InputParameters *p_Inp = currSlice->p_Inp; + int interview_pos = 0; +#endif + + FrameStore **fs_list0; + FrameStore **fs_listlt; + + for (i = 0; i < 6; i++) + { + currSlice->listX[i] = calloc(MAX_LIST_SIZE, sizeof (StorablePicture*)); // +1 for reordering + if (NULL == currSlice->listX[i]) + no_mem_exit("init_dpb: currSlice->listX[i]"); + } + + for (j = 0; j < 6; j++) + { + for (i = 0; i < MAX_LIST_SIZE; i++) + { + currSlice->listX[j][i] = NULL; + } + currSlice->listXsize[j]=0; + } + + // Calculate FrameNumWrap and PicNum + if (currSlice->structure == FRAME) + { + for (i=0; iref_frames_in_buffer; i++) + { + if (p_Dpb->fs_ref[i]->is_used==3) + { + if ((p_Dpb->fs_ref[i]->frame->used_for_reference)&&(!p_Dpb->fs_ref[i]->frame->is_long_term)) + { + currSlice->listX[0][list0idx++] = p_Dpb->fs_ref[i]->frame; + } + } + } + // order list 0 by PicNum + qsort((void *)currSlice->listX[0], list0idx, sizeof(StorablePicture*), compare_pic_by_pic_num_desc); + currSlice->listXsize[0] = (char) list0idx; + //printf("listX[0] (PicNum): "); for (i=0; ilistX[0][i]->pic_num);} printf("\n"); + + // long term handling + for (i=0; iltref_frames_in_buffer; i++) + { + if (p_Dpb->fs_ltref[i]->is_used==3) + { + if (p_Dpb->fs_ltref[i]->frame->is_long_term) + { + currSlice->listX[0][list0idx++] = p_Dpb->fs_ltref[i]->frame; + } + } + } + qsort((void *)&currSlice->listX[0][(short) currSlice->listXsize[0]], list0idx - currSlice->listXsize[0], sizeof(StorablePicture*), compare_pic_by_lt_pic_num_asc); + currSlice->listXsize[0] = (char) list0idx; + } + else + { + fs_list0 = calloc(p_Dpb->size, sizeof (FrameStore*)); + if (NULL==fs_list0) + no_mem_exit("init_lists: fs_list0"); + fs_listlt = calloc(p_Dpb->size, sizeof (FrameStore*)); + if (NULL==fs_listlt) + no_mem_exit("init_lists: fs_listlt"); + + for (i=0; iref_frames_in_buffer; i++) + { + if (p_Dpb->fs_ref[i]->is_reference) + { + fs_list0[list0idx++] = p_Dpb->fs_ref[i]; + } + } + + qsort((void *)fs_list0, list0idx, sizeof(FrameStore*), compare_fs_by_frame_num_desc); + + //printf("fs_list0 (FrameNum): "); for (i=0; iframe_num_wrap);} printf("\n"); + + currSlice->listXsize[0] = 0; + gen_pic_list_from_frame_list(currSlice->structure, fs_list0, list0idx, currSlice->listX[0], &currSlice->listXsize[0], 0); + + //printf("listX[0] (PicNum): "); for (i=0; i < currSlice->listXsize[0]; i++){printf ("%d ", currSlice->listX[0][i]->pic_num);} printf("\n"); + + // long term handling + for (i=0; iltref_frames_in_buffer; i++) + { + fs_listlt[listltidx++]=p_Dpb->fs_ltref[i]; + } + + qsort((void *)fs_listlt, listltidx, sizeof(FrameStore*), compare_fs_by_lt_pic_idx_asc); + + gen_pic_list_from_frame_list(currSlice->structure, fs_listlt, listltidx, currSlice->listX[0], &currSlice->listXsize[0], 1); + + free(fs_list0); + free(fs_listlt); + } + currSlice->listXsize[1] = 0; + + +#if (MVC_EXTENSION_ENABLE && !SIMULCAST_ENABLE) + // MVC interview pictures list + if(p_Inp->num_of_views == 2 && p_Vid->view_id == 1) + { + StorablePicture *base_ref; + if(currSlice->structure == FRAME) + { + currSlice->listXsize[0] = (char) imin (currSlice->listXsize[0], currSlice->num_ref_idx_active[LIST_0]); + currSlice->listXsize[1] = (char) imin (currSlice->listXsize[1], currSlice->num_ref_idx_active[LIST_1]); + list0idx = currSlice->listXsize[0]; + /* + interview_pos = p_Dpb->used_size - 1; + base_ref = p_Dpb->fs[interview_pos]->frame; + */ + interview_pos = 0; + base_ref = p_Dpb->fs_ilref[interview_pos]->frame; + //assert(base_ref->proc_flag); + if (base_ref->used_for_reference || base_ref->inter_view_flag[0]) + { + //p_Dpb->fs[interview_pos]->frame_num_wrap = -99; + p_Dpb->fs_ilref[interview_pos]->frame_num_wrap = -99; + currSlice->listX[0][list0idx++] = base_ref; + currSlice->listXsize[0] = (char) list0idx; + } + } + else //FIELD + { + int cur_inter_view_flag = currSlice->structure != BOTTOM_FIELD ? 0 : 1; + currSlice->listXsize[0] = (char) imin (currSlice->listXsize[0], currSlice->num_ref_idx_active[LIST_0]); + currSlice->listXsize[1] = (char) imin (currSlice->listXsize[1], currSlice->num_ref_idx_active[LIST_1]); + list0idx = currSlice->listXsize[0]; + /* + interview_pos = (currSlice->structure != BOTTOM_FIELD) ? p_Dpb->used_size - 1: p_Dpb->used_size - 2; // top field is at the last position + base_ref = currSlice->structure!=BOTTOM_FIELD?p_Dpb->fs[interview_pos]->top_field:p_Dpb->fs[interview_pos]->bottom_field; + */ + interview_pos = 0; + base_ref = currSlice->structure!=BOTTOM_FIELD? p_Dpb->fs_ilref[interview_pos]->top_field:p_Dpb->fs_ilref[interview_pos]->bottom_field; + if (base_ref->used_for_reference || base_ref->inter_view_flag[cur_inter_view_flag]) + { + //p_Dpb->fs[interview_pos]->frame_num_wrap = -99; + p_Dpb->fs_ilref[interview_pos]->frame_num_wrap = -99; + currSlice->listX[0][list0idx++] = base_ref; + currSlice->listXsize[0] = (char) list0idx; + } + } + } +#endif + + // set max size +#if (MVC_EXTENSION_ENABLE) + if(p_Vid->num_of_layers == 2 && p_Inp->MVCInterViewReorder && p_Vid->view_id == 1) + { + currSlice->listXsize[0] = (char) imin (currSlice->listXsize[0], currSlice->num_ref_idx_active[LIST_0] + 1); + currSlice->listXsize[1] = (char) imin (currSlice->listXsize[1], currSlice->num_ref_idx_active[LIST_1]); + } + else +#endif + { + currSlice->listXsize[0] = (char) imin (currSlice->listXsize[0], currSlice->num_ref_idx_active[LIST_0]); + currSlice->listXsize[1] = (char) imin (currSlice->listXsize[1], currSlice->num_ref_idx_active[LIST_1]); + } + + // set the unused list entries to NULL + for (i=currSlice->listXsize[0]; i< (MAX_LIST_SIZE) ; i++) + { + currSlice->listX[0][i] = NULL; + } + for (i=currSlice->listXsize[1]; i< (MAX_LIST_SIZE) ; i++) + { + currSlice->listX[1][i] = NULL; + } + +#if PRINTREFLIST +#if (MVC_EXTENSION_ENABLE) + // print out for debug purpose + if(p_Inp->num_of_views==2 && p_Vid->current_slice_nr==0) + { + if(currSlice->listXsize[0]>0) + { + printf("\n"); + printf(" ** (CurViewID:%d) %s Ref Pic List 0 ****\n", p_Vid->view_id, currSlice->structure==FRAME ? "FRM":(currSlice->structure==TOP_FIELD ? "TOP":"BOT")); + for(i=0; i<(unsigned int)(currSlice->listXsize[0]); i++) //ref list 0 + { + printf(" %2d -> POC: %4d PicNum: %4d ViewID: %d\n", i, currSlice->listX[0][i]->poc, currSlice->listX[0][i]->pic_num, currSlice->listX[0][i]->view_id); + } + } + } +#endif +#endif +} + + +/*! + ************************************************************************ + * \brief + * Initialize reference lists for a B Slice + * + ************************************************************************ + */ +void init_lists_b_slice(Slice *currSlice) +{ + VideoParameters *p_Vid = currSlice->p_Vid; + DecodedPictureBuffer *p_Dpb = currSlice->p_Dpb; + + unsigned int i; + int j; + + int list0idx = 0; + int list0idx_1 = 0; + int listltidx = 0; + +#if (MVC_EXTENSION_ENABLE) + InputParameters *p_Inp = currSlice->p_Inp; + int list1idx = 0; + int interview_pos = 0; +#endif + + FrameStore **fs_list0; + FrameStore **fs_list1; + FrameStore **fs_listlt; + + + for (i = 0; i < 6; i++) + { + currSlice->listX[i] = calloc(MAX_LIST_SIZE, sizeof (StorablePicture*)); // +1 for reordering + if (NULL == currSlice->listX[i]) + no_mem_exit("init_dpb: currSlice->listX[i]"); + } + + for (j = 0; j < 6; j++) + { + for (i = 0; i < MAX_LIST_SIZE; i++) + { + currSlice->listX[j][i] = NULL; + } + currSlice->listXsize[j]=0; + } + + { + // B-Slice + if (currSlice->structure == FRAME) + { + for (i = 0; i < p_Dpb->ref_frames_in_buffer; i++) + { + if (p_Dpb->fs_ref[i]->is_used==3) + { + if ((p_Dpb->fs_ref[i]->frame->used_for_reference)&&(!p_Dpb->fs_ref[i]->frame->is_long_term)) + { + if (currSlice->framepoc > p_Dpb->fs_ref[i]->frame->poc) + { + currSlice->listX[0][list0idx++] = p_Dpb->fs_ref[i]->frame; + } + } + } + } + qsort((void *)currSlice->listX[0], list0idx, sizeof(StorablePicture*), compare_pic_by_poc_desc); + + //get the backward reference picture (POC>current POC) in list0; + list0idx_1 = list0idx; + for (i=0; iref_frames_in_buffer; i++) + { + if (p_Dpb->fs_ref[i]->is_used==3) + { + if ((p_Dpb->fs_ref[i]->frame->used_for_reference)&&(!p_Dpb->fs_ref[i]->frame->is_long_term)) + { + if (currSlice->framepoc < p_Dpb->fs_ref[i]->frame->poc) + { + currSlice->listX[0][list0idx++] = p_Dpb->fs_ref[i]->frame; + } + } + } + } + qsort((void *)&currSlice->listX[0][list0idx_1], list0idx-list0idx_1, sizeof(StorablePicture*), compare_pic_by_poc_asc); + + for (j=0; jlistX[1][list0idx-list0idx_1+j]=currSlice->listX[0][j]; + } + for (j=list0idx_1; jlistX[1][j-list0idx_1]=currSlice->listX[0][j]; + } + + currSlice->listXsize[0] = currSlice->listXsize[1] = (char) list0idx; + + // printf("currSlice->listX[0] currPoc=%d (Poc): ", currSlice->framepoc); for (i=0; ilistXsize[0]; i++){printf ("%d ", currSlice->listX[0][i]->poc);} printf("\n"); + // printf("currSlice->listX[1] currPoc=%d (Poc): ", currSlice->framepoc); for (i=0; ilistXsize[1]; i++){printf ("%d ", currSlice->listX[1][i]->poc);} printf("\n"); + + // long term handling + for (i=0; iltref_frames_in_buffer; i++) + { + if (p_Dpb->fs_ltref[i]->is_used==3) + { + if (p_Dpb->fs_ltref[i]->frame->is_long_term) + { + currSlice->listX[0][list0idx] = p_Dpb->fs_ltref[i]->frame; + currSlice->listX[1][list0idx++] = p_Dpb->fs_ltref[i]->frame; + } + } + } + qsort((void *)&currSlice->listX[0][(short) currSlice->listXsize[0]], list0idx - currSlice->listXsize[0], sizeof(StorablePicture*), compare_pic_by_lt_pic_num_asc); + qsort((void *)&currSlice->listX[1][(short) currSlice->listXsize[0]], list0idx - currSlice->listXsize[0], sizeof(StorablePicture*), compare_pic_by_lt_pic_num_asc); + currSlice->listXsize[0] = currSlice->listXsize[1] = (char) list0idx; + } + else + { + fs_list0 = calloc(p_Dpb->size, sizeof (FrameStore*)); + if (NULL==fs_list0) + no_mem_exit("init_lists: fs_list0"); + fs_list1 = calloc(p_Dpb->size, sizeof (FrameStore*)); + if (NULL==fs_list1) + no_mem_exit("init_lists: fs_list1"); + fs_listlt = calloc(p_Dpb->size, sizeof (FrameStore*)); + if (NULL==fs_listlt) + no_mem_exit("init_lists: fs_listlt"); + + currSlice->listXsize[0] = 0; + currSlice->listXsize[1] = 1; + + for (i=0; iref_frames_in_buffer; i++) + { + if (p_Dpb->fs_ref[i]->is_used) + { + if (currSlice->ThisPOC >= p_Dpb->fs_ref[i]->poc) + { + fs_list0[list0idx++] = p_Dpb->fs_ref[i]; + } + } + } + qsort((void *)fs_list0, list0idx, sizeof(FrameStore*), compare_fs_by_poc_desc); + list0idx_1 = list0idx; + for (i=0; iref_frames_in_buffer; i++) + { + if (p_Dpb->fs_ref[i]->is_used) + { + if (currSlice->ThisPOC < p_Dpb->fs_ref[i]->poc) + { + fs_list0[list0idx++] = p_Dpb->fs_ref[i]; + } + } + } + qsort((void *)&fs_list0[list0idx_1], list0idx-list0idx_1, sizeof(FrameStore*), compare_fs_by_poc_asc); + + for (j=0; jThisPOC); for (i=0; ipoc);} printf("\n"); + // printf("fs_list1 currPoc=%d (Poc): ", currSlice->ThisPOC); for (i=0; ipoc);} printf("\n"); + + currSlice->listXsize[0] = 0; + currSlice->listXsize[1] = 0; + gen_pic_list_from_frame_list(currSlice->structure, fs_list0, list0idx, currSlice->listX[0], &currSlice->listXsize[0], 0); + gen_pic_list_from_frame_list(currSlice->structure, fs_list1, list0idx, currSlice->listX[1], &currSlice->listXsize[1], 0); + + // printf("currSlice->listX[0] currPoc=%d (Poc): ", currSlice->framepoc); for (i=0; ilistXsize[0]; i++){printf ("%d ", currSlice->listX[0][i]->poc);} printf("\n"); + // printf("currSlice->listX[1] currPoc=%d (Poc): ", currSlice->framepoc); for (i=0; ilistXsize[1]; i++){printf ("%d ", currSlice->listX[1][i]->poc);} printf("\n"); + + // long term handling + for (i=0; iltref_frames_in_buffer; i++) + { + fs_listlt[listltidx++]=p_Dpb->fs_ltref[i]; + } + + qsort((void *)fs_listlt, listltidx, sizeof(FrameStore*), compare_fs_by_lt_pic_idx_asc); + + gen_pic_list_from_frame_list(currSlice->structure, fs_listlt, listltidx, currSlice->listX[0], &currSlice->listXsize[0], 1); + gen_pic_list_from_frame_list(currSlice->structure, fs_listlt, listltidx, currSlice->listX[1], &currSlice->listXsize[1], 1); + + free(fs_list0); + free(fs_list1); + free(fs_listlt); + } + } + + if ((currSlice->listXsize[0] == currSlice->listXsize[1]) && (currSlice->listXsize[0] > 1)) + { + // check if lists are identical, if yes swap first two elements of currSlice->listX[1] + int diff=0; + for (j = 0; j< currSlice->listXsize[0]; j++) + { + if (currSlice->listX[0][j] != currSlice->listX[1][j]) + { + diff = 1; + break; + } + } + if (!diff) + { + StorablePicture *tmp_s = currSlice->listX[1][0]; + currSlice->listX[1][0]=currSlice->listX[1][1]; + currSlice->listX[1][1]=tmp_s; + } + } + +#if (MVC_EXTENSION_ENABLE && !SIMULCAST_ENABLE) + // MVC interview pictures list + if(p_Inp->num_of_views == 2 && p_Vid->view_id == 1) + { + StorablePicture *base_ref; + if(currSlice->structure == FRAME) + { + currSlice->listXsize[0] = (char) imin (currSlice->listXsize[0], currSlice->num_ref_idx_active[LIST_0]); + currSlice->listXsize[1] = (char) imin (currSlice->listXsize[1], currSlice->num_ref_idx_active[LIST_1]); + list0idx = currSlice->listXsize[0]; + list1idx = currSlice->listXsize[1]; + /* + interview_pos = p_Dpb->used_size-1; + base_ref = p_Dpb->fs[interview_pos]->frame; + */ + interview_pos = 0; + base_ref = p_Dpb->fs_ilref[interview_pos]->frame; + + if (base_ref->used_for_reference || base_ref->inter_view_flag[0]) + { + //p_Dpb->fs[interview_pos]->frame_num_wrap = -99; + p_Dpb->fs_ilref[interview_pos]->frame_num_wrap = -99; + currSlice->listX[0][list0idx++] = base_ref; + currSlice->listX[1][list1idx++] = base_ref; + currSlice->listXsize[0] = (char) list0idx; + currSlice->listXsize[1] = (char) list1idx; + } + } + else //FIELD + { + int cur_inter_view_flag = currSlice->structure != BOTTOM_FIELD ? 0 : 1; + currSlice->listXsize[0] = (char) imin (currSlice->listXsize[0], currSlice->num_ref_idx_active[LIST_0]); + currSlice->listXsize[1] = (char) imin (currSlice->listXsize[1], currSlice->num_ref_idx_active[LIST_1]); + list0idx = currSlice->listXsize[0]; + list1idx = currSlice->listXsize[1]; + /* + interview_pos = currSlice->structure!=BOTTOM_FIELD ? p_Dpb->used_size - 1: p_Dpb->used_size - 2; // top field is at the last position + base_ref = currSlice->structure != BOTTOM_FIELD ? p_Dpb->fs[interview_pos]->top_field:p_Dpb->fs[interview_pos]->bottom_field; + */ + interview_pos = 0; + base_ref = currSlice->structure != BOTTOM_FIELD ? p_Dpb->fs_ilref[interview_pos]->top_field:p_Dpb->fs_ilref[interview_pos]->bottom_field; + if (base_ref->used_for_reference || base_ref->inter_view_flag[cur_inter_view_flag]) + { + //p_Dpb->fs[interview_pos]->frame_num_wrap = -99; + p_Dpb->fs_ilref[interview_pos]->frame_num_wrap = -99; + currSlice->listX[0][list0idx++] = base_ref; + currSlice->listX[1][list1idx++] = base_ref; + currSlice->listXsize[0] = (char) list0idx; + currSlice->listXsize[1] = (char) list1idx; + } + } + } +#endif + + // set max size +#if (MVC_EXTENSION_ENABLE) + if(p_Vid->num_of_layers == 2 && p_Inp->MVCInterViewReorder && p_Vid->view_id == 1) + { + currSlice->listXsize[0] = (char) imin (currSlice->listXsize[0], currSlice->num_ref_idx_active[LIST_0] + 1); + currSlice->listXsize[1] = (char) imin (currSlice->listXsize[1], currSlice->num_ref_idx_active[LIST_1]); + } + else +#endif + { + currSlice->listXsize[0] = (char) imin (currSlice->listXsize[0], currSlice->num_ref_idx_active[LIST_0]); + currSlice->listXsize[1] = (char) imin (currSlice->listXsize[1], currSlice->num_ref_idx_active[LIST_1]); + } + + // set the unused list entries to NULL + for (i=currSlice->listXsize[0]; i< (MAX_LIST_SIZE) ; i++) + { + currSlice->listX[0][i] = NULL; + } + for (i=currSlice->listXsize[1]; i< (MAX_LIST_SIZE) ; i++) + { + currSlice->listX[1][i] = NULL; + } + +#if PRINTREFLIST +#if (MVC_EXTENSION_ENABLE) + // print out for debug purpose + if(p_Inp->num_of_views==2 && p_Vid->current_slice_nr==0) + { + if((currSlice->listXsize[0]>0) || (currSlice->listXsize[1]>0)) + printf("\n"); + if(currSlice->listXsize[0]>0) + { + printf(" ** (CurViewID:%d) %s Ref Pic List 0 ****\n", p_Vid->view_id, currSlice->structure==FRAME ? "FRM":(currSlice->structure==TOP_FIELD ? "TOP":"BOT")); + for(i=0; i<(unsigned int)(currSlice->listXsize[0]); i++) //ref list 0 + { + printf(" %2d -> POC: %4d PicNum: %4d ViewID: %d\n", i, currSlice->listX[0][i]->poc, currSlice->listX[0][i]->pic_num, currSlice->listX[0][i]->view_id); + } + } + if(currSlice->listXsize[1]>0) + { + printf(" ** (CurViewID:%d) %s Ref Pic List 1 ****\n", p_Vid->view_id, currSlice->structure==FRAME ? "FRM":(currSlice->structure==TOP_FIELD ? "TOP":"BOT")); + for(i=0; i<(unsigned int)(currSlice->listXsize[1]); i++) //ref list 1 + { + printf(" %2d -> POC: %4d PicNum: %4d ViewID: %d\n", i, currSlice->listX[1][i]->poc, currSlice->listX[1][i]->pic_num, currSlice->listX[1][i]->view_id); + } + } + } +#endif +#endif +} + +/*! + ************************************************************************ + * \brief + * Initialize listX[2..5] from lists 0 and 1 + * listX[2]: list0 for current_field==top + * listX[3]: list1 for current_field==top + * listX[4]: list0 for current_field==bottom + * listX[5]: list1 for current_field==bottom + * + ************************************************************************ + */ +void init_mbaff_lists(Slice *currSlice) +{ + unsigned j; + int i; + + for (i=2;i<6;i++) + { + if(currSlice->listX[i]) + { + for (j=0; jlistX[i][j] = NULL; + } + } + currSlice->listXsize[i]=0; + } + + for (i = 0; i < currSlice->listXsize[0]; i++) + { + currSlice->listX[2][2*i ] = currSlice->listX[0][i]->top_field; + currSlice->listX[2][2*i+1] = currSlice->listX[0][i]->bottom_field; + currSlice->listX[4][2*i ] = currSlice->listX[0][i]->bottom_field; + currSlice->listX[4][2*i+1] = currSlice->listX[0][i]->top_field; + } + currSlice->listXsize[2] = currSlice->listXsize[4] = currSlice->listXsize[0] * 2; + + for (i = 0; i < currSlice->listXsize[1]; i++) + { + currSlice->listX[3][2*i ] = currSlice->listX[1][i]->top_field; + currSlice->listX[3][2*i+1] = currSlice->listX[1][i]->bottom_field; + currSlice->listX[5][2*i ] = currSlice->listX[1][i]->bottom_field; + currSlice->listX[5][2*i+1] = currSlice->listX[1][i]->top_field; + } + currSlice->listXsize[3] = currSlice->listXsize[5] = currSlice->listXsize[1] * 2; +} + + /*! + ************************************************************************ + * \brief + * Returns short term pic with given picNum + * + ************************************************************************ + */ +StorablePicture *get_short_term_pic(Slice *currSlice, DecodedPictureBuffer *p_Dpb, int picNum) +{ + unsigned i; + + for (i = 0; i < p_Dpb->ref_frames_in_buffer; i++) + { + if (currSlice->structure == FRAME) + { + if (p_Dpb->fs_ref[i]->is_reference == 3) + if ((!p_Dpb->fs_ref[i]->frame->is_long_term)&&(p_Dpb->fs_ref[i]->frame->pic_num == picNum)) + return p_Dpb->fs_ref[i]->frame; + } + else + { + if (p_Dpb->fs_ref[i]->is_reference & 1) + if ((!p_Dpb->fs_ref[i]->top_field->is_long_term)&&(p_Dpb->fs_ref[i]->top_field->pic_num == picNum)) + return p_Dpb->fs_ref[i]->top_field; + if (p_Dpb->fs_ref[i]->is_reference & 2) + if ((!p_Dpb->fs_ref[i]->bottom_field->is_long_term)&&(p_Dpb->fs_ref[i]->bottom_field->pic_num == picNum)) + return p_Dpb->fs_ref[i]->bottom_field; + } + } + return NULL; +} + + +/*! + ************************************************************************ + * \brief + * Reordering process for short-term reference pictures + * + ************************************************************************ + */ +void reorder_short_term(Slice *currSlice, DecodedPictureBuffer *p_Dpb, int cur_list, int picNumLX, int *refIdxLX) +{ + StorablePicture **RefPicListX = currSlice->listX[cur_list]; + int cIdx, nIdx; + + StorablePicture *picLX; + + picLX = get_short_term_pic(currSlice, p_Dpb, picNumLX); + + for( cIdx = currSlice->num_ref_idx_active[cur_list]; cIdx > *refIdxLX; cIdx-- ) + RefPicListX[ cIdx ] = RefPicListX[ cIdx - 1]; + + RefPicListX[ (*refIdxLX)++ ] = picLX; + + nIdx = *refIdxLX; + + for( cIdx = *refIdxLX; cIdx <= currSlice->num_ref_idx_active[cur_list]; cIdx++ ) + { + if (RefPicListX[ cIdx ]) + if( (RefPicListX[ cIdx ]->is_long_term ) || (RefPicListX[ cIdx ]->pic_num != picNumLX )) + RefPicListX[ nIdx++ ] = RefPicListX[ cIdx ]; + } +} + + +/*! + ************************************************************************ + * \brief + * Reordering process for long-term reference pictures + * + ************************************************************************ + */ +static void reorder_long_term(Slice *currSlice, DecodedPictureBuffer *p_Dpb, StorablePicture **RefPicListX, int cur_list, int frame_no, int *refIdxLX) +{ + int cIdx, nIdx; + int LongTermPicNum = currSlice->long_term_pic_idx[cur_list][frame_no]; + + StorablePicture *picLX; + + picLX = get_long_term_pic(currSlice, p_Dpb, LongTermPicNum); + + for( cIdx = currSlice->num_ref_idx_active[cur_list]; cIdx > *refIdxLX; cIdx-- ) + RefPicListX[ cIdx ] = RefPicListX[ cIdx - 1]; + + RefPicListX[ (*refIdxLX)++ ] = picLX; + + nIdx = *refIdxLX; + + for( cIdx = *refIdxLX; cIdx <= currSlice->num_ref_idx_active[cur_list]; cIdx++ ) + if( (!RefPicListX[ cIdx ]->is_long_term ) || (RefPicListX[ cIdx ]->long_term_pic_num != LongTermPicNum )) + RefPicListX[ nIdx++ ] = RefPicListX[ cIdx ]; +} + + + +/*! + ************************************************************************ + * \brief + * Reordering process for reference picture lists + * + ************************************************************************ + */ +void reorder_ref_pic_list(Slice *currSlice, int cur_list) +{ + int *modification_of_pic_nums_idc = currSlice->modification_of_pic_nums_idc[cur_list]; + int *abs_diff_pic_num_minus1 = currSlice->abs_diff_pic_num_minus1[cur_list]; + DecodedPictureBuffer *p_Dpb = currSlice->p_Dpb; + int i; + + int maxPicNum, currPicNum, picNumLXNoWrap, picNumLXPred, picNumLX; + int refIdxLX = 0; + + if (currSlice->structure==FRAME) + { + maxPicNum = currSlice->max_frame_num; + currPicNum = currSlice->frame_num; + } + else + { + maxPicNum = 2 * currSlice->max_frame_num; + currPicNum = 2 * currSlice->frame_num + 1; + } + + picNumLXPred = currPicNum; + + for (i=0; modification_of_pic_nums_idc[i]!=3; i++) + { + if (modification_of_pic_nums_idc[i] > 3) + error ("Invalid modification_of_pic_nums_idc command", 500); + + if (modification_of_pic_nums_idc[i] < 2) + { + if (modification_of_pic_nums_idc[i] == 0) + { + if( picNumLXPred - ( abs_diff_pic_num_minus1[i] + 1 ) < 0 ) + picNumLXNoWrap = picNumLXPred - ( abs_diff_pic_num_minus1[i] + 1 ) + maxPicNum; + else + picNumLXNoWrap = picNumLXPred - ( abs_diff_pic_num_minus1[i] + 1 ); + } + else // (modification_of_pic_nums_idc[i] == 1) + { + if( picNumLXPred + ( abs_diff_pic_num_minus1[i] + 1 ) >= maxPicNum ) + picNumLXNoWrap = picNumLXPred + ( abs_diff_pic_num_minus1[i] + 1 ) - maxPicNum; + else + picNumLXNoWrap = picNumLXPred + ( abs_diff_pic_num_minus1[i] + 1 ); + } + picNumLXPred = picNumLXNoWrap; + + if( picNumLXNoWrap > currPicNum ) + picNumLX = picNumLXNoWrap - maxPicNum; + else + picNumLX = picNumLXNoWrap; + + reorder_short_term(currSlice, p_Dpb, cur_list, picNumLX, &refIdxLX); + } + else //(modification_of_pic_nums_idc[i] == 2) + { + reorder_long_term (currSlice, p_Dpb, currSlice->listX[cur_list], cur_list, i, &refIdxLX); + } + } + + // that's a definition + currSlice->listXsize[cur_list] = currSlice->num_ref_idx_active[cur_list]; +} + + + + +/*! + ************************************************************************ + * \brief + * Perform Memory management for idr pictures + * + ************************************************************************ + */ +static void idr_memory_management(DecodedPictureBuffer *p_Dpb, StorablePicture* p, FrameFormat *output) +{ + unsigned i; + VideoParameters *p_Vid = p_Dpb->p_Vid; + + assert (p_Vid->currentPicture->idr_flag); + + if (p_Vid->no_output_of_prior_pics_flag) + { + // free all stored pictures + for (i=0; iused_size; i++) + { + // reset all reference settings + free_frame_store(p_Vid, p_Dpb->fs[i]); + p_Dpb->fs[i] = alloc_frame_store(); + } + for (i=0; iref_frames_in_buffer; i++) + { + p_Dpb->fs_ref[i]=NULL; + } + for (i=0; iltref_frames_in_buffer; i++) + { + p_Dpb->fs_ltref[i]=NULL; + } + p_Dpb->used_size=0; + } + else + { + flush_dpb(p_Dpb, output); + } + p_Dpb->last_picture = NULL; + + update_ref_list(p_Dpb); + update_ltref_list(p_Dpb); + p_Dpb->last_output_poc = INT_MIN; +#if (MVC_EXTENSION_ENABLE) + p_Dpb->last_output_view_id = 0; +#endif + + if (p_Vid->long_term_reference_flag) + { + p_Dpb->max_long_term_pic_idx = 0; + p->is_long_term = 1; + p->long_term_frame_idx = 0; + } + else + { + p_Dpb->max_long_term_pic_idx = -1; + p->is_long_term = 0; + } +} + +/*! + ************************************************************************ + * \brief + * Perform Sliding window decoded reference picture marking process + * + ************************************************************************ + */ +static void sliding_window_memory_management(DecodedPictureBuffer *p_Dpb, StorablePicture* p) +{ + VideoParameters *p_Vid = p_Dpb->p_Vid; + unsigned i; + + // if this is a reference pic with sliding sliding window, unmark first ref frame + if (p_Dpb->ref_frames_in_buffer == imax(1, p_Vid->active_sps->num_ref_frames) - p_Dpb->ltref_frames_in_buffer) + { + for (i = 0; i < p_Dpb->used_size; i++) + { + if (p_Dpb->fs[i]->is_reference && (!(p_Dpb->fs[i]->is_long_term))) + { + unmark_for_reference(p_Dpb->fs[i]); + update_ref_list(p_Dpb); + break; + } + } + } + + p->is_long_term = 0; +} + + + + + + +/*! + ************************************************************************ + * \brief + * Perform Adaptive memory control decoded reference picture marking process + ************************************************************************ + */ +static void adaptive_memory_management(DecodedPictureBuffer *p_Dpb, StorablePicture* p, FrameFormat *output) +{ + DecRefPicMarking_t *tmp_drpm; + VideoParameters *p_Vid = p_Dpb->p_Vid; + + p_Vid->last_has_mmco_5 = 0; + + assert (!p_Vid->currentPicture->idr_flag); + assert (p_Vid->adaptive_ref_pic_buffering_flag); + + while (p_Vid->dec_ref_pic_marking_buffer) + { + tmp_drpm = p_Vid->dec_ref_pic_marking_buffer; + switch (tmp_drpm->memory_management_control_operation) + { + case 0: + if (tmp_drpm->Next != NULL) + { + error ("memory_management_control_operation = 0 not last operation in buffer", 500); + } + break; + case 1: + mm_unmark_short_term_for_reference(p_Dpb, p, tmp_drpm->difference_of_pic_nums_minus1); + update_ref_list(p_Dpb); + break; + case 2: + mm_unmark_long_term_for_reference(p_Dpb, p, tmp_drpm->long_term_pic_num); + update_ltref_list(p_Dpb); + break; + case 3: + mm_assign_long_term_frame_idx(p_Dpb, p, tmp_drpm->difference_of_pic_nums_minus1, tmp_drpm->long_term_frame_idx); + update_ref_list(p_Dpb); + update_ltref_list(p_Dpb); + break; + case 4: + mm_update_max_long_term_frame_idx (p_Dpb, tmp_drpm->max_long_term_frame_idx_plus1); + update_ltref_list(p_Dpb); + break; + case 5: + mm_unmark_all_short_term_for_reference(p_Dpb); + mm_unmark_all_long_term_for_reference(p_Dpb); + p_Vid->last_has_mmco_5 = 1; + break; + case 6: + mm_mark_current_picture_long_term(p_Dpb, p, tmp_drpm->long_term_frame_idx); + check_num_ref(p_Dpb); + break; + default: + error ("invalid memory_management_control_operation in buffer", 500); + } + p_Vid->dec_ref_pic_marking_buffer = tmp_drpm->Next; + free (tmp_drpm); + } + if ( p_Vid->last_has_mmco_5 ) + { + p->pic_num = p->frame_num = 0; + + switch (p->structure) + { + case TOP_FIELD: + { + p->poc = p->top_poc = p_Vid->toppoc =0; + break; + } + case BOTTOM_FIELD: + { + p->poc = p->bottom_poc = p_Vid->bottompoc = 0; + break; + } + case FRAME: + { + p->top_poc -= p->poc; + p->bottom_poc -= p->poc; + + p_Vid->toppoc = p->top_poc; + p_Vid->bottompoc = p->bottom_poc; + + p->poc = imin (p->top_poc, p->bottom_poc); + p_Vid->framepoc = p->poc; + break; + } + } + p_Vid->ThisPOC = p->poc; + flush_dpb(p_Vid->p_Dpb_layer[0], output); + if(p_Vid->num_of_layers >1) + flush_dpb(p_Vid->p_Dpb_layer[1], output); + } +} + + +/*! + ************************************************************************ + * \brief + * Store a picture in DPB. This includes cheking for space in DPB and + * flushing frames. + * If we received a frame, we need to check for a new store, if we + * got a field, check if it's the second field of an already allocated + * store. + * + * \param p_Vid + * VideoParameters + * \param p + * Picture to be stored + * \param output + * FrameFormat for output + * + ************************************************************************ + */ +void store_picture_in_dpb(DecodedPictureBuffer *p_Dpb, StorablePicture* p, FrameFormat *output) +{ + VideoParameters *p_Vid = p_Dpb->p_Vid; + unsigned i; + int poc, pos; + + // diagnostics + //printf ("Storing (%s) non-ref pic with frame_num #%d\n", (p->type == FRAME)?"FRAME":(p->type == TOP_FIELD)?"TOP_FIELD":"BOTTOM_FIELD", p->pic_num); + // if frame, check for new store, + assert (p!=NULL); + + p->otf_flag = p_Vid->p_Inp->OnTheFlyFractMCP ; // JLT : on-the-fly + + p->used_for_reference = (p_Vid->nal_reference_idc != NALU_PRIORITY_DISPOSABLE); + + p->type = p_Vid->type; + +#if (MVC_EXTENSION_ENABLE) + if(p_Vid->p_Inp->num_of_views==2) + { + p->view_id = p_Vid->view_id; + + if(p->structure==FRAME) + { + p->inter_view_flag[0] = p->inter_view_flag[1] = p_Vid->inter_view_flag[0]; + p->anchor_pic_flag[0] = p->anchor_pic_flag[1] = p_Vid->anchor_pic_flag[0]; + } + else + { + if(p->structure==TOP_FIELD) + { + p->inter_view_flag[0] = p_Vid->inter_view_flag[0]; + p->anchor_pic_flag[0] = p_Vid->anchor_pic_flag[0]; + } + else // BOTTOM_FIELD + { + p->inter_view_flag[1] = p_Vid->inter_view_flag[1]; + p->anchor_pic_flag[1] = p_Vid->anchor_pic_flag[1]; + } + } + + if(p_Vid->view_id == 0) + { + process_picture_in_dpb_s(p_Vid, p); + p_Vid->proc_picture = clone_storable_picture(p_Vid, p); + } + } +#endif + + p_Vid->last_has_mmco_5 = 0; + p_Vid->last_pic_bottom_field = (p_Vid->structure == BOTTOM_FIELD); + + if (p_Vid->currentPicture->idr_flag) + { + idr_memory_management(p_Dpb, p, output); + if(p_Vid->num_of_layers > 1) + idr_memory_management(p_Vid->p_Dpb_layer[1], p, output); + } + else + { + // adaptive memory management + if (p->used_for_reference && (p_Vid->adaptive_ref_pic_buffering_flag)) + adaptive_memory_management(p_Dpb, p, output); + } + + if ((p->structure==TOP_FIELD)||(p->structure==BOTTOM_FIELD)) + { + // check for frame store with same pic_number + if (p_Dpb->last_picture) + { + if ((int)p_Dpb->last_picture->frame_num == p->pic_num) + { + if (((p->structure==TOP_FIELD)&&(p_Dpb->last_picture->is_used==2))||((p->structure==BOTTOM_FIELD)&&(p_Dpb->last_picture->is_used==1))) + { + if ((p->used_for_reference && (p_Dpb->last_picture->is_orig_reference!=0))|| + (!p->used_for_reference && (p_Dpb->last_picture->is_orig_reference==0))) + { + insert_picture_in_dpb(p_Vid, p_Dpb->last_picture, p); + update_ref_list(p_Dpb); + update_ltref_list(p_Dpb); +#if (DUMP_DPB) + printf("\nstore_picture_in_dpb inserting complementary field\n"); +#endif + dump_dpb(p_Dpb); + p_Dpb->last_picture = NULL; + return; + } + } + } + } + } + + // this is a frame or a field which has no stored complementary field + + // sliding window, if necessary + if ((!p_Vid->currentPicture->idr_flag) && (p->used_for_reference && (!p_Vid->adaptive_ref_pic_buffering_flag))) + { + sliding_window_memory_management(p_Dpb, p); + } + + // first try to remove unused frames + if (p_Dpb->used_size == p_Dpb->size) + { + remove_unused_frame_from_dpb(p_Dpb); + } + + // then output frames until one can be removed + while (p_Dpb->used_size == p_Dpb->size) + { + // non-reference frames may be output directly + if (!p->used_for_reference) + { + get_smallest_poc(p_Dpb, &poc, &pos); + +#if (MVC_EXTENSION_ENABLE) + if(p_Vid->p_Inp->num_of_views==2) + { + if (((-1==pos) || (p->poc < poc))) + { + direct_output(p_Vid, p, output, (p_Dpb->layer_id ? p_Vid->p_dec2: p_Vid->p_dec)); + return; + } + } + else +#endif + { + if ((-1==pos) || (p->poc < poc)) + { + direct_output(p_Vid, p, output, p_Vid->p_dec); + return; + } + } + } + // flush a frame + output_one_frame_from_dpb(p_Dpb, output); + } + + // check for duplicate frame number in short term reference buffer + if ((p->used_for_reference)&&(!p->is_long_term)) + { + for (i=0; iref_frames_in_buffer; i++) + { + if (p_Dpb->fs_ref[i]->frame_num == p->frame_num) + { + error("duplicate frame_num in short-term reference picture buffer", 500); + } + } + } + // store at end of buffer + insert_picture_in_dpb(p_Vid, p_Dpb->fs[p_Dpb->used_size],p); + + if (p->structure != FRAME) + { + p_Dpb->last_picture = p_Dpb->fs[p_Dpb->used_size]; + } + else + { + p_Dpb->last_picture = NULL; + } + + p_Dpb->used_size++; + + update_ref_list(p_Dpb); + update_ltref_list(p_Dpb); + + check_num_ref(p_Dpb); +#if (DUMP_DPB) + printf("\nEnd of store_picture_in_dpb\n"); +#endif + dump_dpb(p_Dpb); +} + +#if (MVC_EXTENSION_ENABLE) +/*! + ************************************************************************ + * \brief + * Reference process the frame picture and insert into the buffer + * if the reference processed top field has already + * been stored + * + * \param p_Vid + * VideoParameters + * \param p + * StorablePicture to be inserted + * \param output + * FrameFormat for output + * + ************************************************************************ + */ +void replace_top_proc_pic_with_frame(DecodedPictureBuffer *p_Dpb, StorablePicture* p) +{ + VideoParameters *p_Vid = p_Dpb->p_Vid; + StorablePicture *proc_picture = p_Vid->proc_picture; + FrameStore* fs = NULL; + unsigned found;//i, + + assert (p!=NULL); + assert (p->structure==FRAME); + assert (proc_picture==NULL); + assert (p_Vid->p_Inp->ProfileIDC <=STEREO_HIGH); + process_picture_in_dpb_s(p_Vid, p); + proc_picture = clone_storable_picture(p_Vid, p); + + proc_picture->used_for_reference = (p_Vid->nal_reference_idc != NALU_PRIORITY_DISPOSABLE); + proc_picture->type = p_Vid->type; + p_Vid->last_has_mmco_5=0; + p_Vid->last_pic_bottom_field = (p_Vid->structure == BOTTOM_FIELD); + + // upsample a reference picture + if( (p_Vid->p_Inp->separate_colour_plane_flag != 0) ) + { + UnifiedOneForthPix_JV(p_Vid, 0, proc_picture); + UnifiedOneForthPix_JV(p_Vid, 1, proc_picture); + UnifiedOneForthPix_JV(p_Vid, 2, proc_picture); + } + else + { + UnifiedOneForthPix(p_Vid, proc_picture); + } + + found=0; + + if(p_Dpb->fs_ilref[0] && (p_Dpb->fs_ilref[0]->frame_num == p_Vid->frame_num) && (p_Dpb->fs_ilref[0]->is_used == 1)) + { + found = 1; + fs = p_Dpb->fs_ilref[0]; + } + + if (!found) + { + printf("replace_top_proc_pic_with_frame: Warning! Reference processed field not found. Processed frame not inserted in DPB\n"); + free_storable_picture(p_Vid, proc_picture); + } + else + { + free_storable_picture(p_Vid, fs->top_field); + fs->top_field=NULL; + fs->frame=proc_picture; + fs->is_used = 3; + if (p->used_for_reference) + { + fs->is_reference = 3; + if (p->is_long_term) + { + fs->is_long_term = 3; + } + } + // generate field views + dpb_split_field(p_Vid, fs); + fs->top_field->view_id = fs->bottom_field->view_id = fs->frame->view_id; + update_ref_list(p_Dpb); + update_ltref_list(p_Dpb); + } + + p_Vid->proc_picture = NULL; +} +#endif + +/*! + ************************************************************************ + * \brief + * Insert the frame picture into the buffer if the top field has already + * been stored for the coding decision + * + * \param p_Vid + * VideoParameters + * \param p + * StorablePicture to be inserted + * \param output + * FrameFormat for output + * + ************************************************************************ + */ +void replace_top_pic_with_frame(DecodedPictureBuffer *p_Dpb, StorablePicture* p, FrameFormat *output) +{ + VideoParameters *p_Vid = p_Dpb->p_Vid; + FrameStore* fs = NULL; + unsigned i, found; + + assert (p!=NULL); + assert (p->structure==FRAME); + + p->used_for_reference = (p_Vid->nal_reference_idc != NALU_PRIORITY_DISPOSABLE); + p->type = p_Vid->type; + // upsample a reference picture + +#if (MVC_EXTENSION_ENABLE) + if (p->used_for_reference || (p_Vid->p_Inp->num_of_views==2 && p_Vid->view_id == 0)) +#else + if (p->used_for_reference) +#endif + { + if( (p_Vid->p_Inp->separate_colour_plane_flag != 0) ) + { + UnifiedOneForthPix_JV(p_Vid, 0, p); + UnifiedOneForthPix_JV(p_Vid, 1, p); + UnifiedOneForthPix_JV(p_Vid, 2, p); + } + else + { + UnifiedOneForthPix(p_Vid, p); + } + } + + found=0; + + for (i = 0; i < p_Dpb->used_size; i++) + { + if((p_Dpb->fs[i]->frame_num == p_Vid->frame_num) && (p_Dpb->fs[i]->is_used == 1)) + { + found=1; + fs = p_Dpb->fs[i]; + break; + } + } + +#if (MVC_EXTENSION_ENABLE) + if(p_Vid->p_Inp->num_of_views == 2) + { + if (p_Dpb->layer_id == 0) + { + replace_top_proc_pic_with_frame(p_Vid->p_Dpb_layer[1], p); + } + } +#endif + + if (!found) + { + // this should only happen for non-reference pictures when the dpb is full of reference pics +#if (MVC_EXTENSION_ENABLE) + if(p_Vid->p_Inp->num_of_views == 2) + { + if (p_Dpb->layer_id == 0) // view 0 + direct_output_paff(p_Vid, p, output, p_Vid->p_dec); + else // view 1 + direct_output_paff(p_Vid, p, output, p_Vid->p_dec2); + } + else +#endif + direct_output_paff(p_Vid, p, output, p_Vid->p_dec); + } + else + { + free_storable_picture(p_Vid, fs->top_field); + fs->top_field = NULL; + fs->frame = p; + fs->is_used = 3; + if (p->used_for_reference) + { + fs->is_reference = 3; + if (p->is_long_term) + { + fs->is_long_term = 3; + } + } + // generate field views + dpb_split_field(p_Vid, fs); + update_ref_list(p_Dpb); + update_ltref_list(p_Dpb); + } +#if (DUMP_DPB) + printf("\nReplace Top pic with Frame\n"); + dump_dpb(p_Dpb); +#endif +} + + +/*! + ************************************************************************ + * \brief + * Insert the picture into the DPB. A free DPB position is necessary + * for frames, . + * + * \param p_Vid + * VideoParameters + * \param fs + * FrameStore into which the picture will be inserted + * \param p + * StorablePicture to be inserted + * + ************************************************************************ + */ +static void insert_picture_in_dpb(VideoParameters *p_Vid, FrameStore* fs, StorablePicture* p) +{ + // printf ("insert (%s) pic with frame_num #%d, poc %d\n", (p->structure == FRAME)?"FRAME":(p->structure == TOP_FIELD)?"TOP_FIELD":"BOTTOM_FIELD", p->pic_num, p->poc); + assert (p!=NULL); + assert (fs!=NULL); + + // upsample a reference picture + if (p->used_for_reference) + { + if( (p_Vid->p_Inp->separate_colour_plane_flag != 0) ) + { + UnifiedOneForthPix_JV(p_Vid, 0, p); + UnifiedOneForthPix_JV(p_Vid, 1, p); + UnifiedOneForthPix_JV(p_Vid, 2, p); + } + else + { + UnifiedOneForthPix(p_Vid, p); + } + } +#if (MVC_EXTENSION_ENABLE) + else if(p_Vid->p_Inp->num_of_views==2 && p->inter_view_flag[p->structure>>1]) + { + UnifiedOneForthPix(p_Vid, p); + } +#endif + + switch (p->structure) + { + case FRAME: + fs->frame = p; + fs->is_used = 3; + if (p->used_for_reference) + { + fs->is_reference = 3; + fs->is_orig_reference = 3; + if (p->is_long_term) + { + fs->is_long_term = 3; + fs->long_term_frame_idx = p->long_term_frame_idx; + } + } + // generate field views + dpb_split_field(p_Vid, fs); +#if (MVC_EXTENSION_ENABLE) + if(p_Vid->p_Inp->num_of_views==2) + { + fs->anchor_pic_flag[0] = fs->anchor_pic_flag[1] = p->anchor_pic_flag[0]; + fs->inter_view_flag[0] = fs->inter_view_flag[1] = p->inter_view_flag[0]; + } + if (fs->top_field) + { + fs->top_field->view_id = p->view_id; + } + if (fs->bottom_field) + { + fs->bottom_field->view_id = p->view_id; + } +#endif + break; + case TOP_FIELD: + fs->top_field = p; + fs->is_used |= 1; + if (p->used_for_reference) + { + fs->is_reference |= 1; + fs->is_orig_reference |= 1; + if (p->is_long_term) + { + fs->is_long_term |= 1; + fs->long_term_frame_idx = p->long_term_frame_idx; + } + } + if (fs->is_used == 3) + { + // generate frame view + dpb_combine_field(p_Vid, fs); + } + else + { + fs->poc = p->poc; + gen_field_ref_ids(p); + } +#if (MVC_EXTENSION_ENABLE) + if(p_Vid->p_Inp->num_of_views==2) + { + fs->anchor_pic_flag[0] = p->anchor_pic_flag[0]; + fs->inter_view_flag[0] = p->inter_view_flag[0]; + } + fs->top_field->view_id = p->view_id; +#endif + break; + case BOTTOM_FIELD: + fs->bottom_field = p; + fs->is_used |= 2; + if (p->used_for_reference) + { + fs->is_reference |= 2; + fs->is_orig_reference |= 2; + if (p->is_long_term) + { + fs->is_long_term |= 2; + fs->long_term_frame_idx = p->long_term_frame_idx; + } + } + if (fs->is_used == 3) + { + // generate frame view + dpb_combine_field(p_Vid, fs); +#if (MVC_EXTENSION_ENABLE) + fs->frame->view_id = p->view_id; +#endif + } + else + { + fs->poc = p->poc; + gen_field_ref_ids(p); + } +#if (MVC_EXTENSION_ENABLE) + if(p_Vid->p_Inp->num_of_views==2) + { + fs->anchor_pic_flag[1] = p->anchor_pic_flag[1]; + fs->inter_view_flag[1] = p->inter_view_flag[1]; + } + fs->bottom_field->view_id = p->view_id; +#endif + break; + } + fs->frame_num = p->pic_num; + fs->is_output = p->is_output; + fs->is_inter_layer = FALSE; + +#if (MVC_EXTENSION_ENABLE) + fs->view_id = p->view_id; +#endif +} + + + +/*! + ************************************************************************ + * \brief + * remove one frame from DPB + ************************************************************************ + */ +void remove_frame_from_dpb(DecodedPictureBuffer *p_Dpb, int pos) +{ + VideoParameters *p_Vid = p_Dpb->p_Vid; + FrameStore* fs = p_Dpb->fs[pos]; + FrameStore* tmp; + unsigned i; + + //printf ("remove frame with frame_num #%d\n", fs->frame_num); + switch (fs->is_used) + { + case 3: + free_storable_picture(p_Vid, fs->frame); + free_storable_picture(p_Vid, fs->top_field); + free_storable_picture(p_Vid, fs->bottom_field); + fs->frame=NULL; + fs->top_field=NULL; + fs->bottom_field=NULL; + break; + case 2: + free_storable_picture(p_Vid, fs->bottom_field); + fs->bottom_field=NULL; + break; + case 1: + free_storable_picture(p_Vid, fs->top_field); + fs->top_field=NULL; + break; + case 0: + break; + default: + error("invalid frame store type",500); + } + fs->is_used = 0; + fs->is_long_term = 0; + fs->is_reference = 0; + fs->is_orig_reference = 0; + + // move empty framestore to end of buffer + tmp = p_Dpb->fs[pos]; + + for (i=pos; iused_size-1;i++) + { + p_Dpb->fs[i] = p_Dpb->fs[i+1]; + } + p_Dpb->fs[p_Dpb->used_size-1] = tmp; + p_Dpb->used_size--; +} + + +/*! + ************************************************************************ + * \brief + * Flush a picture from DPB which is no longer needed. + ************************************************************************ + */ +static int flush_unused_frame_from_dpb(DecodedPictureBuffer *p_Dpb) +{ + unsigned i; + + // check for frames that were already output and no longer used for reference + for (i = 0; i < p_Dpb->used_size; i++) + { + if (p_Dpb->fs[i]->is_output && (!is_used_for_reference(p_Dpb->fs[i]))) + { + remove_frame_from_dpb(p_Dpb, i); + return 1; + } + } + return 0; +} + + + +/*! + ************************************************************************ + * \brief + * Output one picture stored in the DPB. + ************************************************************************ + */ +static void output_one_frame_from_dpb(DecodedPictureBuffer *p_Dpb, FrameFormat *output) +{ + VideoParameters *p_Vid = p_Dpb->p_Vid; + InputParameters *p_Inp = p_Dpb->p_Inp; + int poc, pos; + //diagnostics + if (p_Dpb->used_size < 1) + { + error("Cannot output frame, DPB empty.",150); + } + + // find smallest POC + get_smallest_poc(p_Dpb, &poc, &pos); + + if(pos==-1) + { + error("no frames for output available", 150); + } + + // call the output function + // printf ("output frame with frame_num #%d, poc %d (p_Dpb-> p_Dpb->size=%d, p_Dpb->used_size=%d)\n", p_Dpb->fs[pos]->frame_num, p_Dpb->fs[pos]->frame->poc, p_Dpb->size, p_Dpb->used_size); +#if (MVC_EXTENSION_ENABLE) + if(p_Inp->num_of_views==2) + { + if(p_Dpb->fs[pos]->view_id==1) + write_stored_frame(p_Vid, p_Dpb->fs[pos], output, p_Vid->p_dec2); + else + write_stored_frame(p_Vid, p_Dpb->fs[pos], output, p_Vid->p_dec); + } + else +#endif + write_stored_frame(p_Vid, p_Dpb->fs[pos], output, p_Vid->p_dec); + + // if redundant picture in use, output POC may be not in ascending order + if(p_Inp->redundant_pic_flag == 0) + { + if (p_Dpb->last_output_poc >= poc) + { + error ("output POC must be in ascending order", 150); + } + } + p_Dpb->last_output_poc = poc; + + // free frame store and move empty store to end of buffer + if (!is_used_for_reference(p_Dpb->fs[pos])) + { + remove_frame_from_dpb(p_Dpb, pos); + } +} + + + +/*! + ************************************************************************ + * \brief + * All stored picture are output. Should be called to empty the buffer + ************************************************************************ + */ +void flush_dpb(DecodedPictureBuffer *p_Dpb, FrameFormat *output) +{ + unsigned i; + + // diagnostics + // printf("Flush remaining frames from the dpb. p_Dpb->size=%d, p_Dpb->used_size=%d\n",p_Dpb->size,p_Dpb->used_size); + + // mark all frames unused + //VideoParameters *p_Vid = p_Dpb->p_Vid; + for (i=0; iused_size; i++) + { + unmark_for_reference (p_Dpb->fs[i]); + } + + while (flush_unused_frame_from_dpb(p_Dpb)) ; + + // output frames in POC order + while (p_Dpb->used_size) + { + output_one_frame_from_dpb(p_Dpb, output); + } + + p_Dpb->last_output_poc = INT_MIN; +#if (MVC_EXTENSION_ENABLE) + p_Dpb->last_output_view_id = 0; +#endif +} + + +static void gen_field_ref_ids(StorablePicture *p) +{ + int i,j; + //! Generate Frame parameters from field information. + for (i = 0; i < (p->size_x >> 2); i++) + { + for (j = 0; j < (p->size_y >> 2); j++) + { + p->mv_info[j][i].field_frame=1; + } + } +} + +/*! + ************************************************************************ + * \brief + * Extract top field from a frame + ************************************************************************ + */ +void dpb_split_field(VideoParameters *p_Vid, FrameStore *fs) +{ + int i, j, k, ii, jj, jj4; + int idiv,jdiv; + int currentmb; + + int twosz16 = 2 * (fs->frame->size_x >> 4); + StorablePicture *fs_top, *fs_btm; + StorablePicture *frame = fs->frame; + + fs->poc = frame->poc; + + if (!p_Vid->active_sps->frame_mbs_only_flag) + { + fs_top = fs->top_field = alloc_storable_picture(p_Vid, TOP_FIELD, frame->size_x, frame->size_y >> 1, frame->size_x_cr, frame->size_y_cr >> 1); + fs_btm = fs->bottom_field = alloc_storable_picture(p_Vid, BOTTOM_FIELD, frame->size_x, frame->size_y >> 1, frame->size_x_cr, frame->size_y_cr >> 1); + + for (i = 0; i < (frame->size_y >> 1); i++) + { + memcpy(fs_top->imgY[i], frame->imgY[i*2], frame->size_x*sizeof(imgpel)); + memcpy(fs_btm->imgY[i], frame->imgY[i*2 + 1], frame->size_x*sizeof(imgpel)); + } + + for (k = 0; k < 2; k++) + { + for (i=0; i< (frame->size_y_cr >> 1); i++) + { + memcpy(fs_top->imgUV[k][i], frame->imgUV[k][i*2], frame->size_x_cr*sizeof(imgpel)); + memcpy(fs_btm->imgUV[k][i], frame->imgUV[k][i*2 + 1], frame->size_x_cr*sizeof(imgpel)); + } + } + if(frame->used_for_reference) + { + if( (p_Vid->p_Inp->separate_colour_plane_flag != 0) ) + { + UnifiedOneForthPix_JV(p_Vid, 0, fs->top_field); + UnifiedOneForthPix_JV(p_Vid, 1, fs->top_field); + UnifiedOneForthPix_JV(p_Vid, 2, fs->top_field); + UnifiedOneForthPix_JV(p_Vid, 0, fs->bottom_field); + UnifiedOneForthPix_JV(p_Vid, 1, fs->bottom_field); + UnifiedOneForthPix_JV(p_Vid, 2, fs->bottom_field); + } + else + { + UnifiedOneForthPix(p_Vid, fs->top_field); + UnifiedOneForthPix(p_Vid, fs->bottom_field); + } + } + fs->top_field->poc = frame->top_poc; + fs->bottom_field->poc = frame->bottom_poc; + + fs->top_field->frame_poc = frame->frame_poc; + + fs->top_field->bottom_poc =fs->bottom_field->bottom_poc = frame->bottom_poc; + fs->top_field->top_poc =fs->bottom_field->top_poc = frame->top_poc; + fs->bottom_field->frame_poc = frame->frame_poc; + + fs->top_field->used_for_reference = fs->bottom_field->used_for_reference + = frame->used_for_reference; + fs->top_field->is_long_term = fs->bottom_field->is_long_term + = frame->is_long_term; + fs->long_term_frame_idx = fs->top_field->long_term_frame_idx + = fs->bottom_field->long_term_frame_idx + = frame->long_term_frame_idx; + + fs->top_field->coded_frame = fs->bottom_field->coded_frame = 1; + fs->top_field->mb_aff_frame_flag = fs->bottom_field->mb_aff_frame_flag + = frame->mb_aff_frame_flag; + + frame->top_field = fs->top_field; + frame->bottom_field = fs->bottom_field; + frame->frame = frame; + fs->top_field->bottom_field = fs->bottom_field; + fs->top_field->frame = fs->frame; + fs->top_field->top_field = fs->top_field; + fs->bottom_field->top_field = fs->top_field; + fs->bottom_field->frame = frame; + fs->bottom_field->bottom_field = fs->bottom_field; + + fs->top_field->chroma_format_idc = fs->bottom_field->chroma_format_idc = frame->chroma_format_idc; + fs->top_field->chroma_mask_mv_x = fs->bottom_field->chroma_mask_mv_x = frame->chroma_mask_mv_x; + fs->top_field->chroma_mask_mv_y = fs->bottom_field->chroma_mask_mv_y = frame->chroma_mask_mv_y; + fs->top_field->chroma_shift_x = fs->bottom_field->chroma_shift_x = frame->chroma_shift_x; + fs->top_field->chroma_shift_y = fs->bottom_field->chroma_shift_y = frame->chroma_shift_y; + + // JLT : otf flag + fs->top_field->otf_flag = fs->bottom_field->otf_flag = frame->otf_flag ; + } + else + { + fs->top_field=NULL; + fs->bottom_field=NULL; + frame->top_field=NULL; + frame->bottom_field=NULL; + frame->frame = frame; + } + + + if (!p_Vid->active_sps->frame_mbs_only_flag && fs->frame->mb_aff_frame_flag) + { + for (j=0 ; j< frame->size_y >> 3; j++) + { + jj = (j >> 2)*8 + (j & 0x03); + jj4 = jj + 4; + jdiv = (j >> 1); + for (i=0 ; i < (frame->size_x >> 2); i++) + { + idiv=i >> 2; + + currentmb = twosz16*(jdiv >> 1)+ (idiv)*2 + (jdiv & 0x01); + // Assign field mvs attached to MB-Frame buffer to the proper buffer + if (fs->frame->motion.mb_field[currentmb]) + { + fs->bottom_field->mv_info[j][i].field_frame = fs->top_field->mv_info[j][i].field_frame=1; + fs->frame->mv_info[2*j][i].field_frame = fs->frame->mv_info[2*j+1][i].field_frame = 1; + + fs->bottom_field->mv_info[j][i].mv[LIST_0] = fs->frame->mv_info[jj4][i].mv[LIST_0]; + fs->bottom_field->mv_info[j][i].mv[LIST_1] = fs->frame->mv_info[jj4][i].mv[LIST_1]; + fs->bottom_field->mv_info[j][i].ref_idx[LIST_0] = fs->frame->mv_info[jj4][i].ref_idx[LIST_0]; + /* + if(fs->bottom_field->mv_info[j][i].ref_idx[LIST_0] >=0) + fs->bottom_field->mv_info[j][i].ref_pic[LIST_0] = currSlice->listX[4][(short) fs->bottom_field->mv_info[j][i].ref_idx[LIST_0]]; + else + fs->bottom_field->mv_info[j][i].ref_pic[LIST_0] = NULL; + */ + fs->bottom_field->mv_info[j][i].ref_idx[LIST_1] = fs->frame->mv_info[jj4][i].ref_idx[LIST_1]; + /* + if(fs->bottom_field->mv_info[j][i].ref_idx[LIST_1] >=0) + fs->bottom_field->mv_info[j][i].ref_pic[LIST_1] = currSlice->listX[5][(short) fs->bottom_field->mv_info[j][i].ref_idx[LIST_1]]; + else + fs->bottom_field->mv_info[j][i].ref_pic[LIST_1] = NULL; + */ + + fs->top_field->mv_info[j][i].mv[LIST_0] = fs->frame->mv_info[jj][i].mv[LIST_0]; + fs->top_field->mv_info[j][i].mv[LIST_1] = fs->frame->mv_info[jj][i].mv[LIST_1]; + fs->top_field->mv_info[j][i].ref_idx[LIST_0] = fs->frame->mv_info[jj][i].ref_idx[LIST_0]; + /* + if(fs->top_field->mv_info[j][i].ref_idx[LIST_0] >=0) + fs->top_field->mv_info[j][i].ref_pic[LIST_0] = currSlice->listX[2][(short) fs->top_field->mv_info[j][i].ref_idx[LIST_0]]; + else + fs->top_field->mv_info[j][i].ref_pic[LIST_0] = NULL; + */ + fs->top_field->mv_info[j][i].ref_idx[LIST_1] = fs->frame->mv_info[jj][i].ref_idx[LIST_1]; + /* + if(fs->top_field->mv_info[j][i].ref_idx[LIST_1] >=0) + fs->top_field->mv_info[j][i].ref_pic[LIST_1] = currSlice->listX[3][(short) fs->top_field->mv_info[j][i].ref_idx[LIST_1]]; + else + fs->top_field->mv_info[j][i].ref_pic[LIST_1] = NULL; + */ + } + } + } + } + + //! Generate field MVs from Frame MVs + if (!p_Vid->active_sps->frame_mbs_only_flag) + { + for (j=0 ; jframe->size_y >> 3 ; j++) + { + jj = 2* RSD(j); + jdiv = j >> 1; + for (i=0 ; iframe->size_x >> 2 ; i++) + { + ii = RSD(i); + idiv = i >> 2; + + currentmb = twosz16*(jdiv >> 1)+ (idiv)*2 + (jdiv & 0x01); + + if (!fs->frame->mb_aff_frame_flag || !fs->frame->motion.mb_field[currentmb]) + { + fs->frame->mv_info[2*j+1][i].field_frame = fs->frame->mv_info[2*j][i].field_frame = 0; + + fs->top_field->mv_info[j][i].field_frame = fs->bottom_field->mv_info[j][i].field_frame = 0; + + fs->top_field->mv_info[j][i].mv[LIST_0] = fs->bottom_field->mv_info[j][i].mv[LIST_0] = fs->frame->mv_info[jj][ii].mv[LIST_0]; + fs->top_field->mv_info[j][i].mv[LIST_1] = fs->bottom_field->mv_info[j][i].mv[LIST_1] = fs->frame->mv_info[jj][ii].mv[LIST_1]; + + // Scaling of references is done here since it will not affect spatial direct (2*0 =0) + if (fs->frame->mv_info[jj][ii].ref_idx[LIST_0] == -1) + fs->top_field->mv_info[j][i].ref_idx[LIST_0] = fs->bottom_field->mv_info[j][i].ref_idx[LIST_0] = - 1; + else + { + fs->top_field->mv_info[j][i].ref_idx[LIST_0] = fs->bottom_field->mv_info[j][i].ref_idx[LIST_0] = fs->frame->mv_info[jj][ii].ref_idx[LIST_0]; + //fs->top_field->mv_info[j][i].ref_pic[LIST_0] = fs->bottom_field->mv_info[j][i].ref_pic[LIST_0] = currSlice->listX[LIST_0][fs->frame->mv_info[jj][ii].ref_idx[LIST_0]]; + } + + if (fs->frame->mv_info[jj][ii].ref_idx[LIST_1] == -1) + fs->top_field->mv_info[j][i].ref_idx[LIST_1] = fs->bottom_field->mv_info[j][i].ref_idx[LIST_1] = - 1; + else + { + fs->top_field->mv_info[j][i].ref_idx[LIST_1] = fs->bottom_field->mv_info[j][i].ref_idx[LIST_1] = fs->frame->mv_info[jj][ii].ref_idx[LIST_1]; + //fs->top_field->mv_info[j][i].ref_pic[LIST_1] = fs->bottom_field->mv_info[j][i].ref_pic[LIST_1] = currSlice->listX[LIST_1][fs->frame->mv_info[jj][ii].ref_idx[LIST_1]]; + } + } + else + { + fs->frame->mv_info[2*j+1][i].field_frame = fs->frame->mv_info[2*j][i].field_frame= fs->frame->motion.mb_field[currentmb]; + } + } + } + } + else + { + for (j=0 ; jframe->size_y >> 2 ; j++) + { + for (i=0 ; iframe->size_x >> 2 ; i++) + { + frame->mv_info[j][i].field_frame = 0; + } + } + } +} + + +/*! + ************************************************************************ + * \brief + * Generate a frame from top and bottom fields, + * YUV components and display information only + ************************************************************************ + */ +void dpb_combine_field_yuv(VideoParameters *p_Vid, FrameStore *fs) +{ + int i, j; + + if (!fs->frame) + { + fs->frame = alloc_storable_picture(p_Vid, FRAME, fs->top_field->size_x, fs->top_field->size_y*2, fs->top_field->size_x_cr, fs->top_field->size_y_cr*2); + } + + for (i=0; itop_field->size_y; i++) + { + memcpy(fs->frame->imgY[i*2], fs->top_field->imgY[i] , fs->top_field->size_x * sizeof(imgpel)); // top field + memcpy(fs->frame->imgY[i*2 + 1], fs->bottom_field->imgY[i], fs->bottom_field->size_x * sizeof(imgpel)); // bottom field + } + + for (j = 0; j < 2; j++) + { + for (i=0; itop_field->size_y_cr; i++) + { + memcpy(fs->frame->imgUV[j][i*2], fs->top_field->imgUV[j][i], fs->top_field->size_x_cr*sizeof(imgpel)); + memcpy(fs->frame->imgUV[j][i*2 + 1], fs->bottom_field->imgUV[j][i], fs->bottom_field->size_x_cr*sizeof(imgpel)); + } + } + + fs->poc=fs->frame->poc =fs->frame->frame_poc = imin (fs->top_field->poc, fs->bottom_field->poc); + + fs->bottom_field->frame_poc=fs->top_field->frame_poc=fs->frame->poc; + + fs->bottom_field->top_poc=fs->frame->top_poc=fs->top_field->poc; + fs->top_field->bottom_poc=fs->frame->bottom_poc=fs->bottom_field->poc; + + fs->frame->used_for_reference = (fs->top_field->used_for_reference && fs->bottom_field->used_for_reference ); + fs->frame->is_long_term = (fs->top_field->is_long_term && fs->bottom_field->is_long_term ); + + if (fs->frame->is_long_term) + fs->frame->long_term_frame_idx = fs->long_term_frame_idx; + + fs->frame->top_field = fs->top_field; + fs->frame->bottom_field = fs->bottom_field; + fs->frame->frame = fs->frame; + + fs->frame->coded_frame = 0; + + fs->frame->chroma_format_idc = fs->top_field->chroma_format_idc; + fs->frame->chroma_mask_mv_x = fs->top_field->chroma_mask_mv_x; + fs->frame->chroma_mask_mv_y = fs->top_field->chroma_mask_mv_y; + fs->frame->chroma_shift_x = fs->top_field->chroma_shift_x; + fs->frame->chroma_shift_y = fs->top_field->chroma_shift_y; + + fs->frame->frame_cropping_flag = fs->top_field->frame_cropping_flag; + if (fs->frame->frame_cropping_flag) + { + fs->frame->frame_crop_top_offset = fs->top_field->frame_crop_top_offset; + fs->frame->frame_crop_bottom_offset = fs->top_field->frame_crop_bottom_offset; + fs->frame->frame_crop_left_offset = fs->top_field->frame_crop_left_offset; + fs->frame->frame_crop_right_offset = fs->top_field->frame_crop_right_offset; + } + + fs->top_field->frame = fs->bottom_field->frame = fs->frame; + fs->top_field->top_field = fs->top_field; + fs->top_field->bottom_field = fs->bottom_field; + fs->bottom_field->top_field = fs->top_field; + fs->bottom_field->bottom_field = fs->bottom_field; + + // otf flag + fs->frame->otf_flag = fs->top_field->otf_flag; +} + + +/*! + ************************************************************************ + * \brief + * Generate a frame from top and bottom fields + ************************************************************************ + */ +void dpb_combine_field(VideoParameters *p_Vid, FrameStore *fs) +{ + int i,j, jj, jj4; + + dpb_combine_field_yuv(p_Vid, fs); + if (fs->frame->used_for_reference) + { + if( (p_Vid->p_Inp->separate_colour_plane_flag != 0) ) + { + UnifiedOneForthPix_JV(p_Vid, 0, fs->frame); + UnifiedOneForthPix_JV(p_Vid, 1, fs->frame); + UnifiedOneForthPix_JV(p_Vid, 2, fs->frame); + } + else + { + UnifiedOneForthPix(p_Vid, fs->frame); + } + } + + //! Generate Frame parameters from field information. + for (j=0 ; jtop_field->size_y >> 2 ; j++) + { + jj = 8*(j >> 2) + (j & 0x03); + jj4 = jj + 4; + for (i=0 ; itop_field->size_x >> 2 ; i++) + { + fs->frame->mv_info[jj][i].field_frame= fs->frame->mv_info[jj4][i].field_frame = 1; + + fs->frame->mv_info[jj][i].mv[LIST_0] = fs->top_field->mv_info[j][i].mv[LIST_0]; + fs->frame->mv_info[jj][i].mv[LIST_1] = fs->top_field->mv_info[j][i].mv[LIST_1]; + + fs->frame->mv_info[jj4][i].mv[LIST_0] = fs->bottom_field->mv_info[j][i].mv[LIST_0]; + fs->frame->mv_info[jj4][i].mv[LIST_1] = fs->bottom_field->mv_info[j][i].mv[LIST_1]; + + fs->top_field->mv_info[j][i].field_frame = 1; + fs->bottom_field->mv_info[j][i].field_frame = 1; + } + } +} + + +/*! + ************************************************************************ + * \brief + * Allocate memory for buffering of reference picture reordering commands + ************************************************************************ + */ +void alloc_ref_pic_list_reordering_buffer(Slice *currSlice) +{ + free_ref_pic_list_reordering_buffer(currSlice); + + if (currSlice->slice_type != I_SLICE && currSlice->slice_type != SI_SLICE) + { + int size = currSlice->num_ref_idx_active[LIST_0] + 1; + if ((currSlice->modification_of_pic_nums_idc[LIST_0] = calloc(size, sizeof(int)))==NULL) + no_mem_exit("alloc_ref_pic_list_reordering_buffer: modification_of_pic_nums_idc_l0"); + if ((currSlice->abs_diff_pic_num_minus1[LIST_0] = calloc(size, sizeof(int)))==NULL) + no_mem_exit("alloc_ref_pic_list_reordering_buffer: abs_diff_pic_num_minus1_l0"); + if ((currSlice->long_term_pic_idx[LIST_0] = calloc(size, sizeof(int)))==NULL) + no_mem_exit("alloc_ref_pic_list_reordering_buffer: long_term_pic_idx_l0"); +#if (MVC_EXTENSION_ENABLE) + if ((currSlice->abs_diff_view_idx_minus1[LIST_0] = calloc(size, sizeof(int)))==NULL) + no_mem_exit("alloc_ref_pic_list_reordering_buffer: abs_diff_view_idx_minus1_l0"); +#endif + } + else + { + currSlice->modification_of_pic_nums_idc[LIST_0] = NULL; + currSlice->abs_diff_pic_num_minus1[LIST_0] = NULL; + currSlice->long_term_pic_idx[LIST_0] = NULL; +#if (MVC_EXTENSION_ENABLE) + currSlice->abs_diff_view_idx_minus1[LIST_0] = NULL; +#endif + } + + if (currSlice->slice_type == B_SLICE) + { + int size = currSlice->num_ref_idx_active[LIST_1] + 1; + if ((currSlice->modification_of_pic_nums_idc[LIST_1] = calloc(size, sizeof(int)))==NULL) + no_mem_exit("alloc_ref_pic_list_reordering_buffer: modification_of_pic_nums_idc_l1"); + if ((currSlice->abs_diff_pic_num_minus1[LIST_1] = calloc(size, sizeof(int)))==NULL) + no_mem_exit("alloc_ref_pic_list_reordering_buffer: abs_diff_pic_num_minus1_l1"); + if ((currSlice->long_term_pic_idx[LIST_1] = calloc(size, sizeof(int)))==NULL) + no_mem_exit("alloc_ref_pic_list_reordering_buffer: long_term_pic_idx_l1"); +#if (MVC_EXTENSION_ENABLE) + if ((currSlice->abs_diff_view_idx_minus1[LIST_1] = calloc(size, sizeof(int)))==NULL) + no_mem_exit("alloc_ref_pic_list_reordering_buffer: abs_diff_view_idx_minus1_l1"); +#endif + } + else + { + currSlice->modification_of_pic_nums_idc[LIST_1] = NULL; + currSlice->abs_diff_pic_num_minus1[LIST_1] = NULL; + currSlice->long_term_pic_idx[LIST_1] = NULL; +#if (MVC_EXTENSION_ENABLE) + currSlice->abs_diff_view_idx_minus1[LIST_1] = NULL; +#endif + } +} + + +/*! + ************************************************************************ + * \brief + * Free memory for buffering of reference picture reordering commands + ************************************************************************ + */ +void free_ref_pic_list_reordering_buffer(Slice *currSlice) +{ + if (currSlice->modification_of_pic_nums_idc[LIST_0]) + free(currSlice->modification_of_pic_nums_idc[LIST_0]); + if (currSlice->abs_diff_pic_num_minus1[LIST_0]) + free(currSlice->abs_diff_pic_num_minus1[LIST_0]); + if (currSlice->long_term_pic_idx[LIST_0]) + free(currSlice->long_term_pic_idx[LIST_0]); + + currSlice->modification_of_pic_nums_idc[LIST_0] = NULL; + currSlice->abs_diff_pic_num_minus1[LIST_0] = NULL; + currSlice->long_term_pic_idx[LIST_0] = NULL; + + if (currSlice->modification_of_pic_nums_idc[LIST_1]) + free(currSlice->modification_of_pic_nums_idc[LIST_1]); + if (currSlice->abs_diff_pic_num_minus1[LIST_1]) + free(currSlice->abs_diff_pic_num_minus1[LIST_1]); + if (currSlice->long_term_pic_idx[LIST_1]) + free(currSlice->long_term_pic_idx[LIST_1]); + + currSlice->modification_of_pic_nums_idc[LIST_1] = NULL; + currSlice->abs_diff_pic_num_minus1[LIST_1] = NULL; + currSlice->long_term_pic_idx[LIST_1] = NULL; + +#if (MVC_EXTENSION_ENABLE) + if (currSlice->abs_diff_view_idx_minus1[LIST_0]) + free_pointer(currSlice->abs_diff_view_idx_minus1[LIST_0]); + if (currSlice->abs_diff_view_idx_minus1[LIST_1]) + free_pointer(currSlice->abs_diff_view_idx_minus1[LIST_1]); +#endif +} + +/*! + ************************************************************************ + * \brief + * Tian Dong + * June 13, 2002, Modified on July 30, 2003 + * + * If a gap in frame_num is found, try to fill the gap + * \param p_Vid + * VideoParameters structure + * \param output + * FrameFormat for ouput + * + ************************************************************************ + */ +void fill_frame_num_gap(VideoParameters *p_Vid, FrameFormat *output) +{ + int CurrFrameNum; + int UnusedShortTermFrameNum; + StorablePicture *picture = NULL; + int nal_ref_idc_bak = p_Vid->nal_reference_idc; + +// printf("A gap in frame number is found, try to fill it.\n"); + + + p_Vid->nal_reference_idc = NALU_PRIORITY_LOW; + + UnusedShortTermFrameNum = (p_Vid->pre_frame_num + 1) % p_Vid->max_frame_num; + CurrFrameNum = p_Vid->frame_num; + + while (CurrFrameNum != UnusedShortTermFrameNum) + { + picture = alloc_storable_picture (p_Vid, FRAME, p_Vid->width, p_Vid->height, p_Vid->width_cr, p_Vid->height_cr); + picture->coded_frame = 1; + picture->pic_num = UnusedShortTermFrameNum; + picture->non_existing = 1; + picture->is_output = 1; + + p_Vid->adaptive_ref_pic_buffering_flag = 0; + store_picture_in_dpb(p_Vid->p_Dpb_layer[0], picture, output); + picture=NULL; + UnusedShortTermFrameNum = (UnusedShortTermFrameNum + 1) % p_Vid->max_frame_num; + } + + p_Vid->nal_reference_idc = nal_ref_idc_bak; +} + +/*! + ************************************************************************ + * \brief + * Compute co-located motion info + * + ************************************************************************ + */ +void compute_colocated(Slice *currSlice, StorablePicture **listX[6]) +{ + int i, j; + + VideoParameters *p_Vid = currSlice->p_Vid; + + if (currSlice->direct_spatial_mv_pred_flag == 0) + { + for (j = 0; j < 2 + (currSlice->mb_aff_frame_flag * 4); j += 2) + { + for (i = 0; i < currSlice->listXsize[j];i++) + { + int prescale, iTRb, iTRp; + + if (j==0) + { + iTRb = iClip3( -128, 127, p_Vid->enc_picture->poc - listX[LIST_0 + j][i]->poc ); + } + else if (j == 2) + { + iTRb = iClip3( -128, 127, p_Vid->enc_picture->top_poc - listX[LIST_0 + j][i]->poc ); + } + else + { + iTRb = iClip3( -128, 127, p_Vid->enc_picture->bottom_poc - listX[LIST_0 + j][i]->poc ); + } + + iTRp = iClip3( -128, 127, listX[LIST_1 + j][0]->poc - listX[LIST_0 + j][i]->poc); + + if (iTRp!=0) + { + prescale = ( 16384 + iabs( iTRp / 2 ) ) / iTRp; + currSlice->mvscale[j][i] = iClip3( -1024, 1023, ( iTRb * prescale + 32 ) >> 6 ) ; + } + else + { + currSlice->mvscale[j][i] = 9999; + } + } + } + } +} + + +static void process_picture_in_dpb_s(VideoParameters *p_Vid, StorablePicture *p_pic) +{ + //InputParameters *p_Inp = p_Vid->p_Inp; + + ImageData *p_img_out = &p_Vid->tempData3; + ImageData img_in; + imgpel*** d_img; + int i; + + img_in.frm_stride[0] = p_pic->size_x_padded; + img_in.frm_stride[1] = img_in.frm_stride[2] = p_pic->size_x_cr + (p_pic->pad_size_uv_x << 1); + + if (p_pic->structure == FRAME) + { + img_in.frm_data[0] = p_pic->imgY; + img_in.frm_data[1] = p_pic->imgUV[0]; + img_in.frm_data[2] = p_pic->imgUV[1]; + img_in.top_stride[0] = img_in.bot_stride[0] = img_in.frm_stride[0] << 1; + img_in.top_stride[1] = img_in.bot_stride[1] = img_in.top_stride[2] = img_in.bot_stride[2] = img_in.frm_stride[1] << 1; + d_img = p_img_out->frm_data; + } + else //If reference picture is a field, then frm_data will actually contain field data and therefore top/bottom stride is set accordingly. + { + img_in.top_stride[0] = img_in.bot_stride[0] = img_in.frm_stride[0]; + img_in.top_stride[1] = img_in.bot_stride[1] = img_in.top_stride[2] = img_in.bot_stride[2] =img_in.frm_stride[1]; + if (p_pic->structure == TOP_FIELD) + { + img_in.top_data[0] = p_pic->imgY; + img_in.top_data[1] = p_pic->imgUV[0]; + img_in.top_data[2] = p_pic->imgUV[1]; + d_img = p_img_out->top_data; + } + else + { + img_in.bot_data[0] = p_pic->imgY; + img_in.bot_data[1] = p_pic->imgUV[0]; + img_in.bot_data[2] = p_pic->imgUV[1]; + d_img = p_img_out->bot_data; + } + } + + for(i=0; isize_y; i++) + memcpy(d_img[0][i], p_pic->imgY[i], p_pic->size_x*sizeof(imgpel)); + if (p_Vid->yuv_format != YUV400) + { + for(i=0; isize_y_cr; i++) + memcpy(d_img[1][i], p_pic->imgUV[0][i], p_pic->size_x_cr * sizeof(imgpel)); + for(i=0; isize_y_cr; i++) + memcpy(d_img[2][i], p_pic->imgUV[1][i], p_pic->size_x_cr * sizeof(imgpel)); + } +} + +/*! + ************************************************************************ + * \brief + * Copy a stored picture + ************************************************************************ + */ +static void copy_storable_picture(VideoParameters *p_Vid, StorablePicture *s, StorablePicture *d) +{ + InputParameters *p_Inp = p_Vid->p_Inp; + int nplane; + int i, j; + int size_x = s->size_x; + int size_y = s->size_y; + int size_x_cr = s->size_x_cr; + int size_y_cr = s->size_y_cr; + imgpel*** s_img; + extern void check_value(imgpel **pBufIn, int width, int height, int max); + // these ones are set in UnifiedOneForthPix + //d->imgY_sub = NULL; + //d->imgUV_sub = NULL; + //d->p_img_sub[0] = NULL; + //d->p_img_sub[1] = NULL; + //d->p_img_sub[2] = NULL; + + // store BL reconstruction + if (s->structure == FRAME) + { + s_img = p_Vid->tempData3.frm_data; + } + else if (s->structure == TOP_FIELD) + { + s_img = p_Vid->tempData3.top_data; + } + else + { + s_img = p_Vid->tempData3.bot_data; + } + + for(i=0; iimgY[i], (void *)s_img[0][i], size_x * sizeof (imgpel)); + + d->p_img[0] = d->imgY; + d->p_curr_img = d->p_img[0]; + + if (p_Vid->yuv_format != YUV400) + { + // store BL reconstruction + for(i=0; iimgUV[0][i], (void *)s_img[1][i], size_x_cr * sizeof (imgpel)); + for(i=0; iimgUV[1][i], (void *)s_img[2][i], size_x_cr * sizeof (imgpel)); + + d->p_img[1] = d->imgUV[0]; + d->p_img[2] = d->imgUV[1]; + } + + d->p_curr_img_sub = d->p_img_sub[0]; + for (j = 0; j < (size_y >> BLOCK_SHIFT); j++) + { + for (i = 0; i < (size_x >> BLOCK_SHIFT); i++) + { + d->mv_info[j][i].ref_idx[LIST_0] = -1; + d->mv_info[j][i].ref_idx[LIST_1] = -1; + d->mv_info[j][i].ref_pic[LIST_0] = NULL; + d->mv_info[j][i].ref_pic[LIST_1] = NULL; + } + } + + if( (p_Inp->separate_colour_plane_flag != 0) ) + { + for( nplane=0; nplane> BLOCK_SHIFT); j++) + { + for (i = 0; i < (size_x >> BLOCK_SHIFT); i++) + { + d->JVmv_info[nplane][j][i].ref_idx[LIST_0] = -1; + d->JVmv_info[nplane][j][i].ref_idx[LIST_1] = -1; + } + } + } + } + + d->pic_num = s->pic_num; + d->frame_num = s->frame_num; + d->long_term_frame_idx = s->long_term_frame_idx; + d->long_term_pic_num = s->long_term_pic_num; + d->used_for_reference = s->used_for_reference; + d->is_long_term = s->is_long_term; + d->non_existing = s->non_existing; + d->is_output = 1; + d->structure = s->structure; + + // these are set in insert_picture_in_dpb + d->top_field = NULL; + d->bottom_field = NULL; + d->frame = NULL; + + d->coded_frame = s->coded_frame; + d->mb_aff_frame_flag = s->mb_aff_frame_flag; + + memcpy( (void *)&d->stats, (void *)&s->stats, sizeof( StatParameters ) ); + d->stats.NumberBFrames = s->stats.NumberBFrames; +} + +/*! + ************************************************************************ + * \brief + * Clone an encoded frame picture structure + ************************************************************************ + */ +static StorablePicture * clone_storable_picture( VideoParameters *p_Vid, StorablePicture *p_pic ) +{ + InputParameters *p_Inp = p_Vid->p_Inp; + StorablePicture *p_stored_pic; + int nal_reference_idc= p_Vid->nal_reference_idc; + p_Vid->nal_reference_idc = NALU_PRIORITY_HIGH; //NALU_PRIORITY_DISPOSABLE; + p_stored_pic = alloc_storable_picture (p_Vid, (PictureStructure) p_Vid->structure, p_pic->size_x, p_pic->size_y, p_pic->size_x_cr, p_pic->size_y_cr); + p_Vid->nal_reference_idc = nal_reference_idc; + p_stored_pic->poc = p_Vid->framepoc; + p_stored_pic->top_poc = p_Vid->toppoc; + p_stored_pic->bottom_poc = p_Vid->bottompoc; + p_stored_pic->frame_poc = p_Vid->framepoc; + p_stored_pic->pic_num = p_pic->pic_num; + p_stored_pic->frame_num = p_pic->frame_num; + p_stored_pic->coded_frame = 1; + p_stored_pic->mb_aff_frame_flag = p_Vid->mb_aff_frame_flag = (Boolean) (p_Inp->MbInterlace != FRAME_CODING); + + copy_params(p_Vid, p_stored_pic, p_Vid->active_sps); + + + copy_storable_picture(p_Vid, p_pic, p_stored_pic); +#if (MVC_EXTENSION_ENABLE) + // MVC-related parameters + p_stored_pic->inter_view_flag[0] = p_pic->inter_view_flag[0]; + p_stored_pic->inter_view_flag[1] = p_pic->inter_view_flag[1]; + p_stored_pic->anchor_pic_flag[0] = p_stored_pic->anchor_pic_flag[1] = 0; + p_stored_pic->view_id = 0; + //p_stored_pic->proc_flag = 1; +#endif + return p_stored_pic; +} + +/*! + ************************************************************************ + * \brief + * Store a processed picture in DPB. + * \param p_Vid + * VideoParameters + * \param p + * Picture to be stored + * \param output + * FrameFormat for output + ************************************************************************ + */ +#if (MVC_EXTENSION_ENABLE) +void store_proc_picture_in_dpb(DecodedPictureBuffer *p_Dpb, StorablePicture* p, FrameFormat *output) +{ + VideoParameters *p_Vid = p_Dpb->p_Vid; + FrameStore *fs = p_Dpb->fs_ilref[0]; + if(p_Dpb->used_size_il>0 && fs->is_used==3) + { + //checking; +#ifdef _DEBUG + if(p->structure==FRAME) + assert(fs->frame->frame_poc != p->poc); + else if(p->structure==TOP_FIELD) + assert(fs->top_field->top_poc != p->poc); + else if(p->structure==BOTTOM_FIELD) + assert(fs->bottom_field->bottom_poc != p->poc); +#endif + if(fs->frame) + { + free_storable_picture(p_Vid, fs->frame); + fs->frame = NULL; + } + if(fs->top_field) + { + free_storable_picture(p_Vid, fs->top_field); + fs->top_field = NULL; + } + if(fs->bottom_field) + { + free_storable_picture(p_Vid, fs->bottom_field); + fs->bottom_field = NULL; + } + fs->is_used = 0; + fs->is_reference = 0; + fs->is_inter_layer = TRUE; + p_Dpb->used_size_il--; + } +#ifdef _DEBUG + if(fs->is_used>0) + { + //checking; + if(p->structure==FRAME) + assert(fs->frame == NULL); + else if(p->structure==TOP_FIELD) + assert(fs->top_field == NULL); + else if(p->structure==BOTTOM_FIELD) + assert(fs->bottom_field == NULL); + } +#endif + + insert_picture_in_dpb(p_Vid, fs, p); + if((p->structure==FRAME && fs->is_used == 3) || (p->structure!=FRAME && fs->is_used && fs->is_used <3)) + p_Dpb->used_size_il++; +} +#endif + + diff --git a/lencod/src/mc_prediction.c b/lencod/src/mc_prediction.c new file mode 100644 index 0000000..ea35276 --- /dev/null +++ b/lencod/src/mc_prediction.c @@ -0,0 +1,653 @@ +/*! + ************************************************************************************* + * \file mc_prediction.c + * + * \brief + * Motion Compensation + * + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * - Alexis Michael Tourapis + * + ************************************************************************************* + */ + +#include "contributors.h" + +#include +#include +#include +#include + +#include "global.h" +#include "block.h" + +#include "macroblock.h" +#include "mc_prediction.h" +#include "refbuf.h" +#include "image.h" +#include "mb_access.h" +#include "me_distortion.h" + +/*! + ************************************************************************ + * \brief + * Weighted BiPrediction + ************************************************************************ + */ +static inline void weighted_bi_prediction(imgpel** mb_pred, imgpel* l0pred, imgpel *l1pred, + int block_size_y, int block_x, int block_size_x, + int max_imgpel_value, + short wbp0, short wbp1, short offset, short wp_round, short weight_denom) +{ + int i, j; + int block_x4 = block_x + block_size_x; + + for (j = 0; j< block_size_y; j++) + { + for (i=block_x; i> (weight_denom)) + offset); + } +} + +/*! + ************************************************************************ + * \brief + * Weighted Prediction + ************************************************************************ + */ +static inline void weighted_mc_prediction(imgpel** mb_pred, imgpel* lpred, + int block_size_y, int block_x, int block_size_x, + int max_imgpel_value, + short wp, short offset, short wp_round, short weight_denom) +{ + int i, j; + int block_x4 = block_x + block_size_x; + + for (j = 0; j < block_size_y; j++) + { + for (i=block_x; i> weight_denom) + offset); + } +} + +/*! + ************************************************************************ + * \brief + * BiPrediction + ************************************************************************ + */ +static inline void bi_prediction(imgpel** mb_pred, imgpel* l0pred, imgpel *l1pred, + int block_size_y, int block_x, int block_size_x) +{ + int i, j; + int block_x4 = block_x + block_size_x; + + for (j = 0; j < block_size_y; j++) + { + for (i=block_x; i> 1; + } +} + +/*! + ************************************************************************ + * \brief + * BiPrediction + ************************************************************************ + */ +static inline void mc_prediction(imgpel** mb_pred, imgpel* lpred, int block_size_y, int block_x, int block_size_x) +{ + int j; + for (j = 0; j < block_size_y; j++) + { + memcpy(&(mb_pred[j][block_x]), lpred, block_size_x * sizeof(imgpel)); + lpred += block_size_x; + } +} + +/*! + ************************************************************************ + * \brief + * Predict Luma block + ************************************************************************ + */ +static inline void OneComponentLumaPrediction ( VideoParameters *p_Vid, //!< video encoding parameters for current picture + imgpel* mpred, //!< array of prediction values (row by row) + int pic_pix_x, //!< motion shifted horizontal coordinate of block + int pic_pix_y, //!< motion shifted vertical coordinate of block + int block_size_x, //!< horizontal block size + int block_size_y, //!< vertical block size + StorablePicture *list //!< reference picture list + ) +{ + int j; + imgpel *ref_line = UMVLine4X (list, pic_pix_y, pic_pix_x); + + for (j = 0; j < block_size_y; j++) + { + memcpy(mpred, ref_line, block_size_x * sizeof(imgpel)); + ref_line += p_Vid->padded_size_x; + mpred += block_size_x; + } +} + + +/*! + ************************************************************************ + * \brief + * Predict one Luma block + ************************************************************************ + */ +void luma_prediction (Macroblock* currMB, //!< Current Macroblock + int block_x, //!< relative horizontal block coordinate of block + int block_y, //!< relative vertical block coordinate of block + int block_size_x, //!< relative horizontal block coordinate of block + int block_size_y, //!< relative vertical block coordinate of block + int p_dir, //!< prediction direction (0=list0, 1=list1, 2=bipred) + int list_mode[2], //!< list prediction mode (1-7, 0=DIRECT) + char *ref_idx, //!< reference pictures + short bipred_me //!< use bi prediction mv (0=no bipred, 1 = use set 1, 2 = use set 2) + ) +{ + VideoParameters *p_Vid = currMB->p_Vid; + Slice *currSlice = currMB->p_Slice; + imgpel l0_pred[MB_PIXELS]; + imgpel l1_pred[MB_PIXELS]; + + int pic_opix_x = ((currMB->pix_x + block_x) << 2); + int pic_opix_y = ((currMB->opix_y + block_y) << 2); + int bx = block_x >> 2; + int by = block_y >> 2; + MotionVector ***** mv_array = currSlice->all_mv; + MotionVector *curr_mv = NULL; + imgpel **mb_pred = currSlice->mb_pred[0]; + + int apply_weights = ( (currSlice->weighted_prediction == 1) || (currSlice->weighted_prediction == 2 && p_dir == 2) ); + + if (bipred_me && ref_idx[0] == 0 && ref_idx[1] == 0 && p_dir == 2 && is_bipred_enabled(p_Vid, list_mode[0]) && is_bipred_enabled(p_Vid, list_mode[1])) + mv_array = currSlice->bipred_mv[bipred_me - 1]; + + switch (p_dir) + { + case 0: + curr_mv = &mv_array[LIST_0][(short) ref_idx[LIST_0]][list_mode[LIST_0]][by][bx]; + OneComponentLumaPrediction (p_Vid, l0_pred, pic_opix_x + curr_mv->mv_x, pic_opix_y + curr_mv->mv_y, block_size_x, block_size_y, currSlice->listX[LIST_0 + currMB->list_offset][(short) ref_idx[LIST_0]]); + break; + case 1: + curr_mv = &mv_array[LIST_1][(short) ref_idx[LIST_1]][list_mode[LIST_1]][by][bx]; + OneComponentLumaPrediction (p_Vid, l1_pred, pic_opix_x + curr_mv->mv_x, pic_opix_y + curr_mv->mv_y, block_size_x, block_size_y, currSlice->listX[LIST_1 + currMB->list_offset][(short)ref_idx[LIST_1]]); + break; + case 2: + curr_mv = &mv_array[LIST_0][(short) ref_idx[LIST_0]][list_mode[LIST_0]][by][bx]; + OneComponentLumaPrediction (p_Vid, l0_pred, pic_opix_x + curr_mv->mv_x, pic_opix_y + curr_mv->mv_y, block_size_x, block_size_y, currSlice->listX[LIST_0 + currMB->list_offset][(short)ref_idx[LIST_0]]); + curr_mv = &mv_array[LIST_1][(short) ref_idx[LIST_1]][list_mode[LIST_1]][by][bx]; + OneComponentLumaPrediction (p_Vid, l1_pred, pic_opix_x + curr_mv->mv_x, pic_opix_y + curr_mv->mv_y, block_size_x, block_size_y, currSlice->listX[LIST_1 + currMB->list_offset][(short)ref_idx[LIST_1]]); + break; + default: + break; + } + + if (apply_weights) + { + if (p_dir==2) + { + weighted_bi_prediction(&mb_pred[block_y], l0_pred, l1_pred, block_size_y, block_x, block_size_x, + p_Vid->max_imgpel_value, + currSlice->wbp_weight[0][(short)ref_idx[0]][(short)ref_idx[1]][0], currSlice->wbp_weight[1][(short)ref_idx[0]][(short)ref_idx[1]][0], + (currSlice->wp_offset[0][(short)ref_idx[0]][0] + currSlice->wp_offset[1][(short)ref_idx[1]][0] + 1)>>1, + (currSlice->wp_luma_round << 1), currSlice->luma_log_weight_denom + 1); + } + else if (p_dir==0) + { + weighted_mc_prediction(&mb_pred[block_y], l0_pred, block_size_y, block_x, block_size_x, + p_Vid->max_imgpel_value, + currSlice->wp_weight[0][(short)ref_idx[0]][0], currSlice->wp_offset[0][(short)ref_idx[0]][0], currSlice->wp_luma_round, currSlice->luma_log_weight_denom); + } + else // (p_dir==1) + { + weighted_mc_prediction(&mb_pred[block_y], l1_pred, block_size_y, block_x, block_size_x, + p_Vid->max_imgpel_value, + currSlice->wp_weight[1][(short)ref_idx[1]][0], currSlice->wp_offset[1][(short)ref_idx[1]][0], currSlice->wp_luma_round, currSlice->luma_log_weight_denom); + } + } + else + { + if (p_dir==2) + { + bi_prediction(&mb_pred[block_y], l0_pred, l1_pred, block_size_y, block_x, block_size_x); + } + else if (p_dir==0) + { + mc_prediction(&mb_pred[block_y], l0_pred, block_size_y, block_x, block_size_x); + } + else // (p_dir==1) + { + mc_prediction(&mb_pred[block_y], l1_pred, block_size_y, block_x, block_size_x); + } + } +} + +/*! + ************************************************************************ + * \brief + * Predict one Luma block + ************************************************************************ + */ +void luma_prediction_bi (Macroblock* currMB, //!< Current Macroblock + int block_x, //!< relative horizontal block coordinate of 4x4 block + int block_y, //!< relative vertical block coordinate of 4x4 block + int block_size_x, //!< horizontal block size + int block_size_y, //!< vertical block size + int l0_mode, //!< list0 prediction mode (1-7, 0=DIRECT if l1_mode=0) + int l1_mode, //!< list1 prediction mode (1-7, 0=DIRECT if l0_mode=0) + short l0_ref_idx, //!< reference frame for list0 prediction (-1: Intra4x4 pred. with l0_mode) + short l1_ref_idx, //!< reference frame for list1 prediction + int list //!< current list for prediction. + ) +{ + VideoParameters *p_Vid = currMB->p_Vid; + Slice *currSlice = currMB->p_Slice; + + imgpel l0_pred[MB_PIXELS]; + imgpel l1_pred[MB_PIXELS]; + int pic_opix_x = ((currMB->pix_x + block_x) << 2); + int pic_opix_y = ((currMB->opix_y + block_y) << 2); + int bx = block_x >> 2; + int by = block_y >> 2; + + int apply_weights = ( currSlice->weighted_prediction != 0 ); + + MotionVector *****mv_array = currSlice->bipred_mv[list]; + MotionVector *mv_arrayl0 = &mv_array[LIST_0][l0_ref_idx][l0_mode][by][bx]; + MotionVector *mv_arrayl1 = &mv_array[LIST_1][l1_ref_idx][l1_mode][by][bx]; + imgpel **mb_pred = currSlice->mb_pred[0]; + + OneComponentLumaPrediction (p_Vid, l0_pred, pic_opix_x + mv_arrayl0->mv_x, pic_opix_y + mv_arrayl0->mv_y, block_size_x, block_size_y, currSlice->listX[0+currMB->list_offset][l0_ref_idx]); + OneComponentLumaPrediction (p_Vid, l1_pred, pic_opix_x + mv_arrayl1->mv_x, pic_opix_y + mv_arrayl1->mv_y, block_size_x, block_size_y, currSlice->listX[1+currMB->list_offset][l1_ref_idx]); + + if (apply_weights) + { + weighted_bi_prediction(&mb_pred[block_y], l0_pred, l1_pred, block_size_y, block_x, block_size_x, + p_Vid->max_imgpel_value, + currSlice->wbp_weight[0][l0_ref_idx][l1_ref_idx][0], currSlice->wbp_weight[1][l0_ref_idx][l1_ref_idx][0], + (currSlice->wp_offset[0][l0_ref_idx][0] + currSlice->wp_offset[1][l1_ref_idx][0] + 1)>>1, + (currSlice->wp_luma_round << 1), currSlice->luma_log_weight_denom + 1); + } + else + { + bi_prediction(&mb_pred[block_y], l0_pred, l1_pred, block_size_y, block_x, block_size_x); + } +} + + +/*! + ************************************************************************ + * \brief + * Predict (on-the-fly) one component of a chroma 4x4 block + ************************************************************************ + */ +void OneComponentChromaPrediction4x4_regenerate ( + Macroblock *currMB, //!< Current Macroblock + imgpel* mpred, //!< array to store prediction values + int block_c_x, //!< horizontal pixel coordinate of 4x4 block + int block_c_y, //!< vertical pixel coordinate of 4x4 block + MotionVector **mv, //!< motion vector array + StorablePicture *list, //!< image components (color planes) + int uv) //!< chroma component +{ + VideoParameters *p_Vid = currMB->p_Vid; + int i, j, ii, jj, ii0, jj0, ii1, jj1, if0, if1, jf0, jf1; + MotionVector *mvb; + + int f1_x = 64/p_Vid->mb_cr_size_x; + int f2_x=f1_x-1; + + int f1_y = 64/p_Vid->mb_cr_size_y; + int f2_y=f1_y-1; + + int f3=f1_x*f1_y, f4=f3>>1; + int list_offset = p_Vid->mb_data[currMB->mbAddrX].list_offset; + int max_y_cr = (int) (list_offset ? (p_Vid->height_cr >> 1) - 1 : p_Vid->height_cr - 1); + int max_x_cr = (int) (p_Vid->width_cr - 1); + int jjx, iix; + int mb_cr_y_div4 = p_Vid->mb_cr_size_y>>2; + int mb_cr_x_div4 = p_Vid->mb_cr_size_x>>2; + int jpos; + + imgpel** refimage = list->imgUV[uv]; + + for (j=block_c_y; j < block_c_y + BLOCK_SIZE; j++) + { + jjx = j/mb_cr_y_div4; + jpos = (j + currMB->opix_c_y)*f1_y; + + for (i=block_c_x; i < block_c_x + BLOCK_SIZE; i++) + { + iix = i/mb_cr_x_div4; + mvb = &mv [jjx][iix]; + + ii = (i + currMB->pix_c_x)*f1_x + mvb->mv_x; + jj = jpos + mvb->mv_y; + + if (p_Vid->active_sps->chroma_format_idc == 1) + jj += list->chroma_vector_adjustment; + + ii0 = iClip3 (0, max_x_cr, ii/f1_x); + jj0 = iClip3 (0, max_y_cr, jj/f1_y); + ii1 = iClip3 (0, max_x_cr, (ii+f2_x)/f1_x); + jj1 = iClip3 (0, max_y_cr, (jj+f2_y)/f1_y); + + if1 = (ii&f2_x); if0 = f1_x-if1; + jf1 = (jj&f2_y); jf0 = f1_y-jf1; + + *mpred++ = (imgpel) ( + (if0 * jf0 * refimage[jj0][ii0] + + if1 * jf0 * refimage[jj0][ii1] + + if0 * jf1 * refimage[jj1][ii0] + + if1 * jf1 * refimage[jj1][ii1] + f4) / f3); + } + } +} + +/*! + ************************************************************************ + * \brief + * Retrieve one component of a chroma 4x4 block from the buffer + ************************************************************************ + */ +void OneComponentChromaPrediction4x4_retrieve ( + Macroblock *currMB, //!< Current Macroblock + imgpel* mpred, //!< array to store prediction values + int block_c_x, //!< horizontal pixel coordinate of 4x4 block + int block_c_y, //!< vertical pixel coordinate of 4x4 block + MotionVector **mv, //!< motion vector array + StorablePicture *list, //!< image components (color planes) + int uv) //!< chroma component +{ + VideoParameters *p_Vid = currMB->p_Vid; + int j, ii, jj; + MotionVector* mvb; + + int jjx; + int right_shift_x = 4 - p_Vid->chroma_shift_x; + int right_shift_y = 4 - p_Vid->chroma_shift_y; + int jpos; + + int pos_x1 = block_c_x >> right_shift_x; + int pos_x2 = (block_c_x + 2) >> right_shift_x; + + int ipos1 = ((block_c_x + currMB->pix_c_x ) << p_Vid->chroma_shift_x); + int ipos2 = ((block_c_x + currMB->pix_c_x + 2) << p_Vid->chroma_shift_x); + int jj_chroma = ((p_Vid->active_sps->chroma_format_idc == 1) ? list->chroma_vector_adjustment : 0); + + imgpel *line_ptr = NULL; + + for (j=block_c_y; j < block_c_y + BLOCK_SIZE; j++) + { + jjx = j >> right_shift_y; // translate into absolute block (luma) coordinates + + jpos = ( (j + currMB->opix_c_y) << p_Vid->chroma_shift_y ) + jj_chroma; + + mvb = &mv [jjx][pos_x1]; + + ii = ipos1 + mvb->mv_x; + jj = jpos + mvb->mv_y; + + line_ptr = UMVLine8X_chroma ( list, uv + 1, jj, ii); + *mpred++ = *line_ptr++; + *mpred++ = *line_ptr; + + mvb = &mv [jjx][pos_x2]; + + ii = ipos2 + mvb->mv_x; + jj = jpos + mvb->mv_y; + + line_ptr = UMVLine8X_chroma ( list, uv + 1, jj, ii); + *mpred++ = *line_ptr++; + *mpred++ = *line_ptr; + } +} + + +/*! + ************************************************************************ + * \brief + * Retrieve one component of a chroma block from the buffer + ************************************************************************ + */ +static inline +void OneComponentChromaPrediction (VideoParameters *p_Vid, //!< video encoding parameters for current picture + imgpel* mpred, //!< array to store prediction values + int pic_pix_x, //!< motion shifted horizontal coordinate of block + int pic_pix_y, //!< motion shifted vertical block + int block_size_x, //!< horizontal block size + int block_size_y, //!< vertical block size + StorablePicture *list, //!< reference picture list + int uv) //!< chroma component +{ + int j; + imgpel *ref_line = UMVLine4Xcr (list, uv + 1, pic_pix_y, pic_pix_x); + + for (j = 0; j < block_size_y; j++) + { + memcpy(mpred, ref_line, block_size_x * sizeof(imgpel)); + ref_line += p_Vid->cr_padded_size_x; + mpred += block_size_x; + } +} + +/*! + ************************************************************************ + * \brief + * Predict an intra chroma 4x4 block + ************************************************************************ + */ +void IntraChromaPrediction4x4 (Macroblock* currMB, //!< Current Macroblock + int uv, //!< color component + int block_x, //!< relative horizontal block coordinate of 4x4 block + int block_y) //!< relative vertical block coordinate of 4x4 block +{ + int j; + Slice *currSlice = currMB->p_Slice; + imgpel **mb_pred = currSlice->mb_pred[ uv ]; + imgpel **curr_mpr_16x16 = currSlice->mpr_16x16[uv][ (short) currMB->c_ipred_mode]; + + //===== prediction ===== + for (j=block_y; j intra prediction) + short l1_ref_idx, // <-- reference frame for list1 prediction + short bipred_me // <-- use bi prediction mv (0=no bipred, 1 = use set 1, 2 = use set 2) + ) +{ + VideoParameters *p_Vid = currMB->p_Vid; + Slice *currSlice = currMB->p_Slice; + + imgpel l0_pred[MB_PIXELS]; + imgpel l1_pred[MB_PIXELS]; + + int pic_opix_x = ((currMB->pix_c_x + block_x) << 2); + int pic_opix_y = ((currMB->opix_c_y + block_y) << 2); + + int bx = block_x >> 2; + int by = block_y >> 2; + MotionVector ***** mv_array = currSlice->all_mv; + int uv_comp = uv + 1; + imgpel **mb_pred = currSlice->mb_pred[ uv_comp]; + + int apply_weights = ( (currSlice->weighted_prediction == 1) || (currSlice->weighted_prediction == 2 && p_dir == 2) ); + + if (bipred_me && l0_ref_idx == 0 && l1_ref_idx == 0 && p_dir == 2 && is_bipred_enabled(p_Vid, l0_mode) && is_bipred_enabled(p_Vid, l1_mode)) + mv_array = currSlice->bipred_mv[bipred_me - 1]; + + //===== INTER PREDICTION ===== + switch (p_dir) + { + case 0: + OneComponentChromaPrediction (p_Vid, l0_pred, pic_opix_x + mv_array[LIST_0][l0_ref_idx][l0_mode][by][bx].mv_x, pic_opix_y + mv_array[LIST_0][l0_ref_idx][l0_mode][by][bx].mv_y, block_size_x, block_size_y, currSlice->listX[0+currMB->list_offset][l0_ref_idx], uv); + break; + case 1: + OneComponentChromaPrediction (p_Vid, l1_pred, pic_opix_x + mv_array[LIST_1][l1_ref_idx][l1_mode][by][bx].mv_x, pic_opix_y + mv_array[LIST_1][l1_ref_idx][l1_mode][by][bx].mv_y, block_size_x, block_size_y, currSlice->listX[1+currMB->list_offset][l1_ref_idx], uv); + break; + case 2: + OneComponentChromaPrediction (p_Vid, l0_pred, pic_opix_x + mv_array[LIST_0][l0_ref_idx][l0_mode][by][bx].mv_x, pic_opix_y + mv_array[LIST_0][l0_ref_idx][l0_mode][by][bx].mv_y, block_size_x, block_size_y, currSlice->listX[0+currMB->list_offset][l0_ref_idx], uv); + OneComponentChromaPrediction (p_Vid, l1_pred, pic_opix_x + mv_array[LIST_1][l1_ref_idx][l1_mode][by][bx].mv_x, pic_opix_y + mv_array[LIST_1][l1_ref_idx][l1_mode][by][bx].mv_y, block_size_x, block_size_y, currSlice->listX[1+currMB->list_offset][l1_ref_idx], uv); + break; + default: + break; + } + + if (apply_weights) + { + if (p_dir==2) + { + weighted_bi_prediction(&mb_pred[block_y], l0_pred, l1_pred, block_size_y, block_x, block_size_x, + p_Vid->max_pel_value_comp[1], + currSlice->wbp_weight[0][l0_ref_idx][l1_ref_idx][uv_comp], currSlice->wbp_weight[1][l0_ref_idx][l1_ref_idx][uv_comp], + (currSlice->wp_offset[0][l0_ref_idx][uv_comp] + currSlice->wp_offset[1][l1_ref_idx][uv_comp] + 1)>>1, + (currSlice->wp_chroma_round << 1), currSlice->chroma_log_weight_denom + 1); + } + else if (p_dir==0) + { + weighted_mc_prediction(&mb_pred[block_y], l0_pred, block_size_y, block_x, block_size_x, + p_Vid->max_pel_value_comp[1], + currSlice->wp_weight[0][l0_ref_idx][uv_comp], currSlice->wp_offset[0][l0_ref_idx][uv_comp], currSlice->wp_chroma_round, currSlice->chroma_log_weight_denom ); + } + else // (p_dir==1) + { + weighted_mc_prediction(&mb_pred[block_y], l1_pred, block_size_y, block_x, block_size_x, + p_Vid->max_pel_value_comp[1], + currSlice->wp_weight[1][l1_ref_idx][uv_comp], currSlice->wp_offset[1][l1_ref_idx][uv_comp], currSlice->wp_chroma_round, currSlice->chroma_log_weight_denom ); + } + } + else + { + if (p_dir==2) + { + bi_prediction(&mb_pred[block_y], l0_pred, l1_pred, block_size_y, block_x, block_size_x); + } + else if (p_dir==0) + { + mc_prediction(&mb_pred[block_y], l0_pred, block_size_y, block_x, block_size_x); + } + else // (p_dir==1) + { + mc_prediction(&mb_pred[block_y], l1_pred, block_size_y, block_x, block_size_x); + } + } +} + + + +/*! + ************************************************************************ + * \brief + * Predict one chroma 4x4 block + ************************************************************************ + */ +void chroma_prediction_4x4 (Macroblock* currMB, // <-- Current Macroblock + int uv, // <-- colour component + int block_x, // <-- relative horizontal block coordinate of 4x4 block + int block_y, // <-- relative vertical block coordinate of 4x4 block + int p_dir, // <-- prediction direction (0=list0, 1=list1, 2=bipred) + int l0_mode, // <-- list0 prediction mode (1-7, 0=DIRECT if l1_mode=0) + int l1_mode, // <-- list1 prediction mode (1-7, 0=DIRECT if l0_mode=0) + short l0_ref_idx, // <-- reference frame for list0 prediction (if (<0) -> intra prediction) + short l1_ref_idx, // <-- reference frame for list1 prediction + short bipred_me // <-- use bi prediction mv (0=no bipred, 1 = use set 1, 2 = use set 2) + ) +{ + VideoParameters *p_Vid = currMB->p_Vid; + Slice *currSlice = currMB->p_Slice; + + imgpel l0_pred[MB_PIXELS]; + imgpel l1_pred[MB_PIXELS]; + + MotionVector ***** mv_array = currSlice->all_mv; + int uv_comp = uv + 1; + imgpel **mb_pred = currSlice->mb_pred[uv_comp]; + int list_offset = currMB->list_offset; + + int apply_weights = ( (currSlice->weighted_prediction == 1) || (currSlice->weighted_prediction == 2 && p_dir == 2) ); + + if (bipred_me && l0_ref_idx == 0 && l1_ref_idx == 0 && p_dir == 2 && is_bipred_enabled(p_Vid, l0_mode) && is_bipred_enabled(p_Vid, l1_mode) ) + mv_array = currSlice->bipred_mv[bipred_me - 1]; + + //===== INTER PREDICTION ===== + switch (p_dir) + { + case 0: // LIST_0 + p_Vid->OneComponentChromaPrediction4x4 (currMB, l0_pred, block_x, block_y, mv_array[LIST_0][l0_ref_idx][l0_mode], currSlice->listX[LIST_0 + list_offset][l0_ref_idx], uv); + break; + case 1: // LIST_1 + p_Vid->OneComponentChromaPrediction4x4 (currMB, l1_pred, block_x, block_y, mv_array[LIST_1][l1_ref_idx][l1_mode], currSlice->listX[LIST_1 + list_offset][l1_ref_idx], uv); + break; + case 2: // BI_PRED + p_Vid->OneComponentChromaPrediction4x4 (currMB, l0_pred, block_x, block_y, mv_array[LIST_0][l0_ref_idx][l0_mode], currSlice->listX[LIST_0 + list_offset][l0_ref_idx], uv); + p_Vid->OneComponentChromaPrediction4x4 (currMB, l1_pred, block_x, block_y, mv_array[LIST_1][l1_ref_idx][l1_mode], currSlice->listX[LIST_1 + list_offset][l1_ref_idx], uv); + break; + default: + break; + } + + if (apply_weights) + { + if (p_dir==2) + { + weighted_bi_prediction(&mb_pred[block_y], l0_pred, l1_pred, BLOCK_SIZE, block_x, BLOCK_SIZE, + p_Vid->max_pel_value_comp[1], + currSlice->wbp_weight[0][l0_ref_idx][l1_ref_idx][uv_comp], currSlice->wbp_weight[1][l0_ref_idx][l1_ref_idx][uv_comp], + (currSlice->wp_offset[0][l0_ref_idx][uv_comp] + currSlice->wp_offset[1][l1_ref_idx][uv_comp] + 1)>>1, + (currSlice->wp_chroma_round << 1), currSlice->chroma_log_weight_denom + 1); + + } + else if (p_dir==0) + { + weighted_mc_prediction(&mb_pred[block_y], l0_pred, BLOCK_SIZE, block_x, BLOCK_SIZE, + p_Vid->max_pel_value_comp[1], + currSlice->wp_weight[0][l0_ref_idx][uv_comp], currSlice->wp_offset[0][l0_ref_idx][uv_comp], currSlice->wp_chroma_round, currSlice->chroma_log_weight_denom ); + } + else // (p_dir==1) + { + weighted_mc_prediction(&mb_pred[block_y], l1_pred, BLOCK_SIZE, block_x, BLOCK_SIZE, + p_Vid->max_pel_value_comp[1], + currSlice->wp_weight[1][l1_ref_idx][uv_comp], currSlice->wp_offset[1][l1_ref_idx][uv_comp], currSlice->wp_chroma_round, currSlice->chroma_log_weight_denom ); + } + } + else + { + if (p_dir==2) + { + bi_prediction(&mb_pred[block_y], l0_pred, l1_pred, BLOCK_SIZE, block_x, BLOCK_SIZE); + } + else if (p_dir==0) + { + mc_prediction(&mb_pred[block_y], l0_pred, BLOCK_SIZE, block_x, BLOCK_SIZE); + } + else // (p_dir==1) + { + mc_prediction(&mb_pred[block_y], l1_pred, BLOCK_SIZE, block_x, BLOCK_SIZE); + } + } +} + diff --git a/lencod/src/mc_prediction_otf.c b/lencod/src/mc_prediction_otf.c new file mode 100644 index 0000000..db7b45e --- /dev/null +++ b/lencod/src/mc_prediction_otf.c @@ -0,0 +1,387 @@ +/*! + ************************************************************************************* + * \file mc_prediction_otf.c + * + * \brief + * Motion Compensation + * + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * - Julien Le Tanou + * + ************************************************************************************* + */ + +#include "contributors.h" + +#include +#include +#include +#include + +#include "global.h" +#include "block.h" + +#include "macroblock.h" +#include "mc_prediction.h" +#include "mc_prediction_otf.h" + +#include "refbuf.h" +#include "image.h" +#include "mb_access.h" +#include "me_distortion.h" +#include "get_block_otf.h" + +/*! + ************************************************************************ + * \brief + * block single list prediction + ************************************************************************ + */ +static void mc_prediction(imgpel** mb_pred, imgpel* lpred, int block_size_y, int block_size_x, int ioff) +{ + int j; + for (j = 0; j < block_size_y; j++) + { + memcpy(&(mb_pred[j][ioff]), lpred, block_size_x * sizeof(imgpel)); + lpred += block_size_x; + } +} + +/*! + ************************************************************************ + * \brief + * Weighted Prediction + ************************************************************************ + */ +static void weighted_mc_prediction(imgpel** mb_pred, + imgpel* lpred, + int block_size_y, + int block_size_x, + int ioff, + int max_imgpel_value, + short wp_scale, + short wp_offset, + short wp_round, + short weight_denom) +{ + int i, j; + int result; + int block_x4 = ioff + block_size_x; + + for (j = 0; j < block_size_y; j++) + { + for (i = ioff; i < block_x4; i++) + { + result = rshift_rnd((wp_scale * *lpred++), weight_denom) + wp_offset; + mb_pred[j][i] = (imgpel) iClip1( max_imgpel_value, result); + } + } +} + +/*! + ************************************************************************ + * \brief + * block biprediction + ************************************************************************ + */ +static void bi_prediction(imgpel **mb_pred, + imgpel *l0pred, + imgpel *l1pred, + int block_size_y, + int block_size_x, + int ioff) +{ + int i, j; + int block_x4 = ioff + block_size_x; + + for (j = 0; j < block_size_y; j++) + { + for (i=ioff; i> 1; + } +} + + + +/*! + ************************************************************************ + * \brief + * block weighted biprediction + ************************************************************************ + */ +static void weighted_bi_prediction(imgpel** mb_pred, + imgpel *block_l0, + imgpel *block_l1, + int block_size_y, + int block_x, + int block_size_x, + int max_imgpel_value, + int wp_scale_l0, + int wp_scale_l1, + int wp_offset, + int weight_denom) +{ + int i, j, result; + int block_x4 = block_x + block_size_x; + + for (j = 0; j< block_size_y; j++) + { + for (i=block_x; ip_Vid; + Slice *currSlice = currMB->p_Slice; + DecodedPictureBuffer *p_Dpb = p_Vid->p_Dpb_layer[p_Vid->dpb_layer_id]; + imgpel l0_pred[MB_PIXELS]; + imgpel l1_pred[MB_PIXELS]; + int tmp_pred[ (MB_BLOCK_SIZE+5)*(MB_BLOCK_SIZE+5) ]; // temporary pred block to compute on-the-fly interpolation + + int pic_opix_x = ((currMB->pix_x + block_x) << 2); + int pic_opix_y = ((currMB->opix_y + block_y) << 2); + int bx = block_x >> 2; + int by = block_y >> 2; + MotionVector ***** mv_array = currSlice->all_mv; + MotionVector *curr_mv = NULL; + imgpel **mb_pred = currSlice->mb_pred[0]; + int apply_weights = ( (currSlice->weighted_prediction == 1) || (currSlice->weighted_prediction == 2 && p_dir == 2) ); + + if (bipred_me && ref_idx[0] == 0 && ref_idx[1] == 0 && p_dir == 2 && is_bipred_enabled(p_Vid, list_mode[0]) && is_bipred_enabled(p_Vid, list_mode[1])) + mv_array = currSlice->bipred_mv[bipred_me - 1]; + + switch (p_dir) + { + case 0: + curr_mv = &mv_array[LIST_0][(short) ref_idx[0]][list_mode[0]][by][bx]; + p_Dpb->pf_get_block_luma (p_Vid, l0_pred, tmp_pred, pic_opix_x + curr_mv->mv_x, pic_opix_y + curr_mv->mv_y, block_size_x, block_size_y, currSlice->listX[LIST_0 + currMB->list_offset][(short) ref_idx[0]], 0 ); + break; + case 1: + curr_mv = &mv_array[LIST_1][(short) ref_idx[1]][list_mode[1]][by][bx]; + p_Dpb->pf_get_block_luma (p_Vid, l1_pred, tmp_pred, pic_opix_x + curr_mv->mv_x, pic_opix_y + curr_mv->mv_y, block_size_x, block_size_y, currSlice->listX[LIST_1 + currMB->list_offset][(short)ref_idx[1]], 0 ); + break; + case 2: + curr_mv = &mv_array[LIST_0][(short) ref_idx[0]][list_mode[0]][by][bx]; + p_Dpb->pf_get_block_luma (p_Vid, l0_pred, tmp_pred, pic_opix_x + curr_mv->mv_x, pic_opix_y + curr_mv->mv_y, block_size_x, block_size_y, currSlice->listX[LIST_0 + currMB->list_offset][(short)ref_idx[0]], 0); + curr_mv = &mv_array[LIST_1][(short) ref_idx[1]][list_mode[1]][by][bx]; + p_Dpb->pf_get_block_luma (p_Vid, l1_pred, tmp_pred, pic_opix_x + curr_mv->mv_x, pic_opix_y + curr_mv->mv_y, block_size_x, block_size_y, currSlice->listX[LIST_1 + currMB->list_offset][(short)ref_idx[1]], 0); + break; + default: + break; + } + + if (apply_weights) + { + if (p_dir==2) + { + weighted_bi_prediction(&mb_pred[block_y], l0_pred, l1_pred, block_size_y, block_x, block_size_x, + p_Vid->max_imgpel_value, + currSlice->wbp_weight[0][(short)ref_idx[0]][(short)ref_idx[1]][0], currSlice->wbp_weight[1][(short)ref_idx[0]][(short)ref_idx[1]][0], + (currSlice->wp_offset[0][(short)ref_idx[0]][0] + currSlice->wp_offset[1][(short)ref_idx[1]][0] + 1)>>1, + currSlice->luma_log_weight_denom + 1); + } + else if (p_dir==0) + { + weighted_mc_prediction(&mb_pred[block_y], l0_pred, block_size_y, block_size_x, block_x, + p_Vid->max_imgpel_value, + currSlice->wp_weight[0][(short)ref_idx[0]][0], currSlice->wp_offset[0][(short)ref_idx[0]][0], currSlice->wp_luma_round, currSlice->luma_log_weight_denom); + } + else // (p_dir==1) + { + weighted_mc_prediction(&mb_pred[block_y], l1_pred, block_size_y, block_size_x, block_x, + p_Vid->max_imgpel_value, + currSlice->wp_weight[1][(short)ref_idx[1]][0], currSlice->wp_offset[1][(short)ref_idx[1]][0], currSlice->wp_luma_round, currSlice->luma_log_weight_denom); + } + } + else + { + if (p_dir==2) + { + bi_prediction(&mb_pred[block_y], l0_pred, l1_pred, block_size_y, block_size_x, block_x); + } + else if (p_dir==0) + { + mc_prediction(&mb_pred[block_y], l0_pred, block_size_y, block_size_x, block_x); + } + else // (p_dir==1) + { + mc_prediction(&mb_pred[block_y], l1_pred, block_size_y, block_size_x, block_x); + } + } +} + +/*! + ************************************************************************ + * \brief + * Predict one Luma block on-the-fly + ************************************************************************ + */ +void luma_prediction_bi_otf ( Macroblock* currMB, //!< Current Macroblock + int block_x, //!< relative horizontal block coordinate of 4x4 block + int block_y, //!< relative vertical block coordinate of 4x4 block + int block_size_x, //!< horizontal block size + int block_size_y, //!< vertical block size + int l0_mode, //!< list0 prediction mode (1-7, 0=DIRECT if l1_mode=0) + int l1_mode, //!< list1 prediction mode (1-7, 0=DIRECT if l0_mode=0) + short l0_ref_idx, //!< reference frame for list0 prediction (-1: Intra4x4 pred. with l0_mode) + short l1_ref_idx, //!< reference frame for list1 prediction + int list //!< current list for prediction. + ) +{ + VideoParameters *p_Vid = currMB->p_Vid; + Slice *currSlice = currMB->p_Slice; + DecodedPictureBuffer *p_Dpb = p_Vid->p_Dpb_layer[p_Vid->dpb_layer_id]; + + imgpel l0_pred[MB_PIXELS]; + imgpel l1_pred[MB_PIXELS]; + + int tmp_pred[ (MB_BLOCK_SIZE+5)*(MB_BLOCK_SIZE+5) ]; // temporary pred block for on-the-fly interpolation calculation + int pic_opix_x = ((currMB->pix_x + block_x) << 2); + int pic_opix_y = ((currMB->opix_y + block_y) << 2); + int bx = block_x >> 2; + int by = block_y >> 2; + + int apply_weights = ( currSlice->weighted_prediction != 0 ); + + MotionVector *****mv_array = currSlice->bipred_mv[list]; + MotionVector *mv_arrayl0 = &mv_array[LIST_0][l0_ref_idx][l0_mode][by][bx]; + MotionVector *mv_arrayl1 = &mv_array[LIST_1][l1_ref_idx][l1_mode][by][bx]; + imgpel **mb_pred = currSlice->mb_pred[0]; + + p_Dpb->pf_get_block_luma(p_Vid, l0_pred, tmp_pred, pic_opix_x + mv_arrayl0->mv_x, pic_opix_y + mv_arrayl0->mv_y, block_size_x, block_size_y, currSlice->listX[0+currMB->list_offset][l0_ref_idx], 0); + p_Dpb->pf_get_block_luma(p_Vid, l1_pred, tmp_pred, pic_opix_x + mv_arrayl1->mv_x, pic_opix_y + mv_arrayl1->mv_y, block_size_x, block_size_y, currSlice->listX[1+currMB->list_offset][l1_ref_idx], 0); + + if (apply_weights) + { + weighted_bi_prediction(&mb_pred[block_y], l0_pred, l1_pred, block_size_y, block_x, block_size_x, + p_Vid->max_imgpel_value, + currSlice->wbp_weight[0][l0_ref_idx][l1_ref_idx][0], currSlice->wbp_weight[1][l0_ref_idx][l1_ref_idx][0], + (currSlice->wp_offset[0][l0_ref_idx][0] + currSlice->wp_offset[1][l1_ref_idx][0] + 1)>>1, + currSlice->luma_log_weight_denom + 1); + } + else + { + bi_prediction(&mb_pred[block_y], l0_pred, l1_pred, block_size_y, block_size_x, block_x); + } +} + +/*! + ************************************************************************ + * \brief + * Predict one chroma block on-the-fly + ************************************************************************ + */ +void chroma_prediction_otf ( Macroblock* currMB, // <-- Current Macroblock + int uv, // <-- colour component + int block_x, // <-- relative horizontal block coordinate of block + int block_y, // <-- relative vertical block coordinate of block + int block_size_x, // <-- relative horizontal block coordinate of block + int block_size_y, // <-- relative vertical block coordinate of block + int p_dir, // <-- prediction direction (0=list0, 1=list1, 2=bipred) + int l0_mode, // <-- list0 prediction mode (1-7, 0=DIRECT if l1_mode=0) + int l1_mode, // <-- list1 prediction mode (1-7, 0=DIRECT if l0_mode=0) + short l0_ref_idx, // <-- reference frame for list0 prediction (if (<0) -> intra prediction) + short l1_ref_idx, // <-- reference frame for list1 prediction + short bipred_me // <-- use bi prediction mv (0=no bipred, 1 = use set 1, 2 = use set 2) + ) +{ + VideoParameters *p_Vid = currMB->p_Vid; + Slice *currSlice = currMB->p_Slice; + DecodedPictureBuffer *p_Dpb = p_Vid->p_Dpb_layer[p_Vid->dpb_layer_id]; + + imgpel l0_pred[MB_PIXELS]; + imgpel l1_pred[MB_PIXELS]; + int tmp_pred[(MB_BLOCK_SIZE+5)*(MB_BLOCK_SIZE+5)]; + + int pic_opix_x = ((currMB->pix_c_x + block_x) << 2); + int pic_opix_y = ((currMB->opix_c_y + block_y) << 2); + + int bx = block_x >> 2; + int by = block_y >> 2; + MotionVector ***** mv_array = currSlice->all_mv; + int uv_comp = uv + 1; + imgpel **mb_pred = currSlice->mb_pred[ uv_comp]; + + int apply_weights = ( (currSlice->weighted_prediction == 1) || (currSlice->weighted_prediction == 2 && p_dir == 2) ); + + if (bipred_me && l0_ref_idx == 0 && l1_ref_idx == 0 && p_dir == 2 && is_bipred_enabled(p_Vid, l0_mode) && is_bipred_enabled(p_Vid, l1_mode)) + mv_array = currSlice->bipred_mv[bipred_me - 1]; + + //===== INTER PREDICTION ===== + switch (p_dir) + { + case 0: + p_Dpb->pf_get_block_chroma[OTF_MC] (p_Vid, l0_pred, tmp_pred, pic_opix_x + mv_array[LIST_0][l0_ref_idx][l0_mode][by][bx].mv_x, pic_opix_y + mv_array[LIST_0][l0_ref_idx][l0_mode][by][bx].mv_y, block_size_x, block_size_y, currSlice->listX[0+currMB->list_offset][l0_ref_idx], uv+1 ); + break; + case 1: + p_Dpb->pf_get_block_chroma[OTF_MC] (p_Vid, l1_pred, tmp_pred, pic_opix_x + mv_array[LIST_1][l1_ref_idx][l1_mode][by][bx].mv_x, pic_opix_y + mv_array[LIST_1][l1_ref_idx][l1_mode][by][bx].mv_y, block_size_x, block_size_y, currSlice->listX[1+currMB->list_offset][l1_ref_idx], uv+1 ); + break; + case 2: + p_Dpb->pf_get_block_chroma[OTF_MC] (p_Vid, l0_pred, tmp_pred, pic_opix_x + mv_array[LIST_0][l0_ref_idx][l0_mode][by][bx].mv_x, pic_opix_y + mv_array[LIST_0][l0_ref_idx][l0_mode][by][bx].mv_y, block_size_x, block_size_y, currSlice->listX[0+currMB->list_offset][l0_ref_idx], uv+1 ); + p_Dpb->pf_get_block_chroma[OTF_MC] (p_Vid, l1_pred, tmp_pred, pic_opix_x + mv_array[LIST_1][l1_ref_idx][l1_mode][by][bx].mv_x, pic_opix_y + mv_array[LIST_1][l1_ref_idx][l1_mode][by][bx].mv_y, block_size_x, block_size_y, currSlice->listX[1+currMB->list_offset][l1_ref_idx], uv+1 ); + break; + default: + break; + } + + if (apply_weights) + { + if (p_dir==2) + { + weighted_bi_prediction(&mb_pred[block_y], l0_pred, l1_pred, block_size_y, block_x, block_size_x, + p_Vid->max_pel_value_comp[1], + currSlice->wbp_weight[0][l0_ref_idx][l1_ref_idx][uv_comp], currSlice->wbp_weight[1][l0_ref_idx][l1_ref_idx][uv_comp], + (currSlice->wp_offset[0][l0_ref_idx][uv_comp] + currSlice->wp_offset[1][l1_ref_idx][uv_comp] + 1)>>1, + currSlice->chroma_log_weight_denom + 1); + } + else if (p_dir==0) + { + weighted_mc_prediction(&mb_pred[block_y], l0_pred, block_size_y, block_size_x, block_x, + p_Vid->max_pel_value_comp[1], + currSlice->wp_weight[0][l0_ref_idx][uv_comp], currSlice->wp_offset[0][l0_ref_idx][uv_comp], currSlice->wp_chroma_round, currSlice->chroma_log_weight_denom ); + } + else // (p_dir==1) + { + weighted_mc_prediction(&mb_pred[block_y], l1_pred, block_size_y, block_size_x, block_x, + p_Vid->max_pel_value_comp[1], + currSlice->wp_weight[1][l1_ref_idx][uv_comp], currSlice->wp_offset[1][l1_ref_idx][uv_comp], currSlice->wp_chroma_round, currSlice->chroma_log_weight_denom ); + } + } + else + { + if (p_dir==2) + { + bi_prediction(&mb_pred[block_y], l0_pred, l1_pred, block_size_y, block_size_x, block_x); + } + else if (p_dir==0) + { + mc_prediction(&mb_pred[block_y], l0_pred, block_size_y, block_size_x, block_x); + } + else // (p_dir==1) + { + mc_prediction(&mb_pred[block_y], l1_pred, block_size_y, block_size_x, block_x); + } + } +} \ No newline at end of file diff --git a/lencod/src/md_common.c b/lencod/src/md_common.c new file mode 100644 index 0000000..3bf72e4 --- /dev/null +++ b/lencod/src/md_common.c @@ -0,0 +1,594 @@ +/*! + *************************************************************************** + * \file md_common.c + * + * \brief + * Mode Decision common functions + * + * \author + * - Alexis Michael Tourapis + * \date + * 04. October 2008 + ************************************************************************** + */ + +#include +#include "global.h" +#include "image.h" +#include "macroblock.h" +#include "mc_prediction.h" + + +/*! + ************************************************************************************* + * \brief + * Initialize the best variable + ************************************************************************************* + */ +Info8x8 init_best (void) +{ + Info8x8 best; + best.pdir = 0; + best.bipred = 0; + best.ref[LIST_0] = 0; + best.ref[LIST_1] = -1; + return best; +} + +/*! + ************************************************************************************* + * \brief + * Copy rdo mv info to 16xN picture mv buffer + ************************************************************************************* + */ +static inline void CopyMVBlock16(PicMotionParams **mv_info, MotionVector **rdo_mv, int list, int block_x, int block_y, int start, int end) +{ + int j, i; + for (j = start; j < end; j++) + { + for (i = 0; i < BLOCK_MULTIPLE; i++) + { + mv_info[block_y + j][block_x + i].mv[list] = rdo_mv[j][i]; + } + } +} + +/*! + ************************************************************************************* + * \brief + * Reset corresponding 16xN block picture mv buffer + ************************************************************************************* + */ +static inline void ResetMotionBlock16(PicMotionParams **mv_info, int list, int block_x, int block_y, int start, int end) +{ + int j, i; + for (j = block_y + start; j < block_y + end; j++) + { + for (i = block_x; i < block_x + BLOCK_MULTIPLE; i++) + { + mv_info[j][i].mv[list] = zero_mv; + mv_info[j][i].ref_idx[list] = -1; + mv_info[j][i].ref_pic[list] = NULL; + } + } +} + +/*! + ************************************************************************************* + * \brief + * Reset corresponding 16xN block picture mv buffer + ************************************************************************************* + */ +static inline void ResetMVBlock16(PicMotionParams **mv_info, int list, int block_x, int block_y, int start, int end) +{ + int j, i; + for (j = block_y + start; j < block_y + end; j++) + { + for (i = block_x; i < block_x + BLOCK_MULTIPLE; i++) + mv_info[j][i].mv[list] = zero_mv; + } +} + +/*! + ************************************************************************************* + * \brief + * Reset corresponding 16xN block picture ref buffer (intra) + ************************************************************************************* + */ +static inline void ResetRefBlock16(PicMotionParams **mv_info, int list, int block_x, int block_y, int start, int end) +{ + int j, i; + for (j = block_y + start; j < block_y + end; j++) + { + for (i = block_x; i < block_x + BLOCK_MULTIPLE; ++i) + { + mv_info[j][i].ref_idx[list] = -1; + mv_info[j][i].ref_pic[list] = NULL; + } + } +} + +/*! + ************************************************************************************* + * \brief + * Copy rdo mv info to 8xN picture mv buffer + ************************************************************************************* + */ +static inline void CopyMVBlock8(PicMotionParams **mv_info, MotionVector **rdo_mv, int list, int block_x, int start, int end, int offset) +{ + int j; + for (j = start; j < end; j++) + { + mv_info[j][block_x ].mv[list] = rdo_mv[j][offset]; + mv_info[j][block_x + 1].mv[list] = rdo_mv[j][offset + 1]; + } +} + +/*! + ************************************************************************************* + * \brief + * Reset corresponding 8xN block picture mv buffer + ************************************************************************************* + */ +static inline void ResetMotionBlock8(PicMotionParams **mv_info, int list, int block_x, int start, int end) +{ + int j; + + for (j = start; j < end; j++) + { + mv_info[j][block_x ].mv[list] = zero_mv; + mv_info[j][block_x ].ref_idx[list] = -1; + mv_info[j][block_x ].ref_pic[list] = NULL; + mv_info[j][block_x + 1].mv[list] = zero_mv; + mv_info[j][block_x + 1].ref_idx[list] = -1; + mv_info[j][block_x + 1].ref_pic[list] = NULL; + } +} + +/*! + ************************************************************************************* + * \brief + * Reset corresponding 8xN block picture mv buffer + ************************************************************************************* + */ +static inline void ResetMVBlock8(PicMotionParams **mv_info, int list, int block_x, int start, int end) +{ + int j; + + for (j = start; j < end; j++) + { + mv_info[j][block_x ].mv[list] = zero_mv; + mv_info[j][block_x + 1].mv[list] = zero_mv; + } +} + +/*! + ************************************************************************************* + * \brief + * Reset corresponding 8xN block picture ref buffer (intra) + ************************************************************************************* + */ +static inline void ResetRefBlock8(PicMotionParams **mv_info, int list, int block_x, int start, int end) +{ + int j; + for (j = start; j < end; j++) + { + mv_info[j][block_x ].ref_idx[list] = -1; + mv_info[j][block_x ].ref_pic[list] = NULL; + mv_info[j][block_x + 1].ref_idx[list] = -1; + mv_info[j][block_x + 1].ref_pic[list] = NULL; + } +} + +/*! + ************************************************************************************* + * \brief + * Sets motion vectors for a macroblock in a P Slice + ************************************************************************************* + */ +void SetMotionVectorsMBPSlice (Macroblock* currMB) +{ + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + PicMotionParams **mv_info = p_Vid->enc_picture->mv_info; + + RD_DATA *rdopt = currSlice->rddata; + MotionVector ****all_mv = currSlice->all_mv[LIST_0]; + int l0_ref, mode8; + + if (currSlice->mb_aff_frame_flag || (currSlice->UseRDOQuant && currSlice->RDOQ_QP_Num > 1)) + { + memcpy(&rdopt->all_mv[LIST_0][0][0][0][0], &all_mv[0][0][0][0], p_Vid->max_num_references * 9 * MB_BLOCK_PARTITIONS * sizeof(MotionVector)); + } + + if (currMB->mb_type == PSKIP) // Skip mode + { + CopyMVBlock16(mv_info, all_mv[0][PSKIP], LIST_0, currMB->block_x, currMB->block_y, 0, 4); + } + else if (currMB->mb_type == P16x16) // 16x16 + { + l0_ref = mv_info[currMB->block_y][currMB->block_x].ref_idx[LIST_0]; + CopyMVBlock16(mv_info, all_mv[l0_ref][P16x16], LIST_0, currMB->block_x, currMB->block_y, 0, 4); + } + else if (currMB->mb_type == P16x8) // 16x8 + { + l0_ref = mv_info[currMB->block_y][currMB->block_x].ref_idx[LIST_0]; + CopyMVBlock16(mv_info, all_mv[l0_ref][P16x8], LIST_0, currMB->block_x, currMB->block_y, 0, 2); + + l0_ref = mv_info[currMB->block_y + 2][currMB->block_x].ref_idx[LIST_0]; + CopyMVBlock16(mv_info, all_mv[l0_ref][P16x8], LIST_0, currMB->block_x, currMB->block_y, 2, 4); + } + else if (currMB->mb_type == P8x16) // 8x16 + { + l0_ref = mv_info[currMB->block_y][currMB->block_x].ref_idx[LIST_0]; + CopyMVBlock8(&mv_info[currMB->block_y], all_mv[l0_ref][P8x16], LIST_0, currMB->block_x , 0, 4, 0); + + l0_ref = mv_info[currMB->block_y][currMB->block_x + 2].ref_idx[LIST_0]; + CopyMVBlock8(&mv_info[currMB->block_y], all_mv[l0_ref][P8x16], LIST_0, currMB->block_x + 2, 0, 4, 2); + } + else if (currMB->mb_type == P8x8) // 8x8 + { + mode8 = currMB->b8x8[0].mode; + l0_ref = mv_info[currMB->block_y ][currMB->block_x ].ref_idx[LIST_0]; + CopyMVBlock8(&mv_info[currMB->block_y], all_mv[l0_ref][mode8], LIST_0, currMB->block_x , 0, 2, 0); + + mode8 = currMB->b8x8[1].mode; + l0_ref = mv_info[currMB->block_y ][currMB->block_x + 2].ref_idx[LIST_0]; + CopyMVBlock8(&mv_info[currMB->block_y], all_mv[l0_ref][mode8], LIST_0, currMB->block_x + 2, 0, 2, 2); + + mode8 = currMB->b8x8[2].mode; + l0_ref = mv_info[currMB->block_y + 2][currMB->block_x ].ref_idx[LIST_0]; + CopyMVBlock8(&mv_info[currMB->block_y], all_mv[l0_ref][mode8], LIST_0, currMB->block_x, 2, 4, 0); + + mode8 = currMB->b8x8[3].mode; + l0_ref = mv_info[currMB->block_y + 2][currMB->block_x + 2].ref_idx[LIST_0]; + CopyMVBlock8(&mv_info[currMB->block_y], all_mv[l0_ref][mode8], LIST_0, currMB->block_x + 2, 2, 4, 2); + } + else // Intra modes + { + ResetMotionBlock16 (mv_info, LIST_0, currMB->block_x, currMB->block_y, 0, 4); + } +} + +/*! + ************************************************************************************* + * \brief + * Sets motion vectors for a 16x8 partition in a B Slice + ************************************************************************************* + */ +static void SetMVBSlice16x8(Slice *currSlice, PicMotionParams **motion, Macroblock* currMB, int pos) +{ + int l0_ref, l1_ref; + int pdir = currMB->b8x8[pos].pdir; + if (pdir == LIST_0) + { + l0_ref = motion[currMB->block_y + pos][currMB->block_x].ref_idx[LIST_0]; + CopyMVBlock16(motion, currSlice->all_mv [LIST_0][l0_ref][P16x8], LIST_0, currMB->block_x, currMB->block_y, pos, pos + 2); + ResetMotionBlock16 (motion, LIST_1, currMB->block_x, currMB->block_y, pos, pos + 2); + } + else if (pdir == LIST_1) + { + ResetMotionBlock16 (motion, LIST_0, currMB->block_x, currMB->block_y, pos, pos + 2); + l1_ref = motion[currMB->block_y + pos][currMB->block_x].ref_idx[LIST_1]; + CopyMVBlock16 (motion, currSlice->all_mv [LIST_1][l1_ref][P16x8], LIST_1, currMB->block_x, currMB->block_y, pos, pos + 2); + } + else + { + int bipred_me = currMB->b8x8[pos].bipred; + MotionVector *****all_mv = bipred_me ? currSlice->bipred_mv[bipred_me - 1]: currSlice->all_mv; + l0_ref = motion[currMB->block_y + pos][currMB->block_x].ref_idx[LIST_0]; + CopyMVBlock16 (motion, all_mv [LIST_0][l0_ref][P16x8], LIST_0, currMB->block_x, currMB->block_y, pos, pos + 2); + + l1_ref = motion[currMB->block_y + pos][currMB->block_x].ref_idx[LIST_1]; + CopyMVBlock16 (motion, all_mv [LIST_1][l1_ref][P16x8], LIST_1, currMB->block_x, currMB->block_y, pos, pos + 2); + + if (bipred_me && (currSlice->mb_aff_frame_flag || (currSlice->UseRDOQuant && currSlice->RDOQ_QP_Num > 1))) + { + memcpy(currSlice->rddata->all_mv [LIST_0][l0_ref][P16x8][pos], all_mv [LIST_0][l0_ref][P16x8][pos], 2 * BLOCK_MULTIPLE * sizeof(MotionVector)); + memcpy(currSlice->rddata->all_mv [LIST_1][l1_ref][P16x8][pos], all_mv [LIST_1][l1_ref][P16x8][pos], 2 * BLOCK_MULTIPLE * sizeof(MotionVector)); + } + } +} + +/*! + ************************************************************************************* + * \brief + * Sets motion vectors for a 8x16 partition in a B Slice + ************************************************************************************* + */ +static void SetMVBSlice8x16(Slice *currSlice, PicMotionParams **motion, Macroblock* currMB, int pos) +{ + int l0_ref, l1_ref; + int pdir = currMB->b8x8[pos >> 1].pdir; + if (pdir == LIST_0) + { + l0_ref = motion[currMB->block_y][currMB->block_x + pos].ref_idx[LIST_0]; + CopyMVBlock8 (&motion[currMB->block_y], currSlice->all_mv [LIST_0][l0_ref][P8x16], LIST_0, currMB->block_x + pos, 0, 4, pos); + ResetMotionBlock8(&motion[currMB->block_y], LIST_1, currMB->block_x + pos, 0, 4); + } + else if (pdir == LIST_1) + { + ResetMotionBlock8(&motion[currMB->block_y], LIST_0, currMB->block_x + pos, 0, 4); + l1_ref = motion[currMB->block_y][currMB->block_x + pos].ref_idx[LIST_1]; + CopyMVBlock8 (&motion[currMB->block_y], currSlice->all_mv [LIST_1][l1_ref][P8x16], LIST_1, currMB->block_x + pos, 0, 4, pos); + } + else + { + int bipred_me = currMB->b8x8[pos >> 1].bipred; + MotionVector *****all_mv = bipred_me ? currSlice->bipred_mv[bipred_me - 1]: currSlice->all_mv; + + l0_ref = motion[currMB->block_y][currMB->block_x + pos].ref_idx[LIST_0]; + CopyMVBlock8(&motion[currMB->block_y], all_mv [LIST_0][l0_ref][P8x16], LIST_0, currMB->block_x + pos, 0, 4, pos); + l1_ref = motion[currMB->block_y][currMB->block_x + pos].ref_idx[LIST_1]; + CopyMVBlock8(&motion[currMB->block_y], all_mv [LIST_1][l1_ref][P8x16], LIST_1, currMB->block_x + pos, 0, 4, pos); + + if (bipred_me && (currSlice->mb_aff_frame_flag || (currSlice->UseRDOQuant && currSlice->RDOQ_QP_Num > 1))) + { + int j; + for (j = 0; j < BLOCK_MULTIPLE; j++) + { + memcpy(&currSlice->rddata->all_mv [LIST_0][l0_ref][P8x16][j][pos], &all_mv [LIST_0][l0_ref][P8x16][j][pos], 2 * sizeof(MotionVector)); + } + for (j = 0; j < BLOCK_MULTIPLE; j++) + { + memcpy(&currSlice->rddata->all_mv [LIST_1][l1_ref][P8x16][j][pos], &all_mv [LIST_1][l1_ref][P8x16][j][pos], 2 * sizeof(MotionVector)); + } + } + } +} + +/*! + ************************************************************************************* + * \brief + * Sets motion vectors for a 8x8 partition in a B Slice + ************************************************************************************* + */ +static void SetMVBSlice8x8(Slice *currSlice, PicMotionParams **motion, Macroblock* currMB, int pos_y, int pos_x) +{ + int l0_ref, l1_ref; + int pos = pos_y + (pos_x >> 1); + int pdir = currMB->b8x8[pos].pdir; + int mode = currMB->b8x8[pos].mode; + int block_y = currMB->block_y + pos_y; + int block_x = currMB->block_x + pos_x; + + if (pdir == LIST_0) + { + l0_ref = motion[block_y][block_x].ref_idx[LIST_0]; + CopyMVBlock8 (&motion[currMB->block_y], currSlice->all_mv [LIST_0][l0_ref][mode], LIST_0, currMB->block_x + pos_x, pos_y, pos_y + 2, pos_x); + ResetMotionBlock8 (&motion[currMB->block_y], LIST_1, currMB->block_x + pos_x, pos_y, pos_y + 2); + } + else if (pdir == LIST_1) + { + ResetMotionBlock8(&motion[currMB->block_y], LIST_0, currMB->block_x + pos_x, pos_y, pos_y + 2); + l1_ref = motion[block_y][block_x].ref_idx[LIST_1]; + CopyMVBlock8 (&motion[currMB->block_y], currSlice->all_mv [LIST_1][l1_ref][mode], LIST_1, currMB->block_x + pos_x, pos_y, pos_y + 2, pos_x); + } + else if (pdir == BI_PRED) + { + int bipred_me = currMB->b8x8[pos].bipred; + MotionVector *****all_mv = bipred_me ? currSlice->bipred_mv[bipred_me - 1]: currSlice->all_mv; + l0_ref = motion[block_y][block_x].ref_idx[LIST_0]; + CopyMVBlock8(&motion[currMB->block_y], all_mv [LIST_0][l0_ref][mode], LIST_0, currMB->block_x + pos_x, pos_y, pos_y + 2, pos_x); + + l1_ref = motion[block_y][block_x].ref_idx[LIST_1]; + CopyMVBlock8(&motion[currMB->block_y], all_mv [LIST_1][l1_ref][mode], LIST_1, currMB->block_x + pos_x, pos_y, pos_y + 2, pos_x); + + if (bipred_me && (currSlice->mb_aff_frame_flag || (currSlice->UseRDOQuant && currSlice->RDOQ_QP_Num > 1))) + { + memcpy(&currSlice->rddata->all_mv [LIST_0][l0_ref][mode][pos_y ][pos_x], &all_mv [LIST_0][l0_ref][mode][pos_y ][pos_x], 2 * sizeof(MotionVector)); + memcpy(&currSlice->rddata->all_mv [LIST_0][l0_ref][mode][pos_y + 1][pos_x], &all_mv [LIST_0][l0_ref][mode][pos_y + 1][pos_x], 2 * sizeof(MotionVector)); + memcpy(&currSlice->rddata->all_mv [LIST_1][l1_ref][mode][pos_y ][pos_x], &all_mv [LIST_1][l1_ref][mode][pos_y ][pos_x], 2 * sizeof(MotionVector)); + memcpy(&currSlice->rddata->all_mv [LIST_1][l1_ref][mode][pos_y + 1][pos_x], &all_mv [LIST_1][l1_ref][mode][pos_y + 1][pos_x], 2 * sizeof(MotionVector)); + } + } + else // Invalid direct modes (out of range mvs). Adding this here for precaution purposes. + { + ResetMotionBlock8(&motion[currMB->block_y], LIST_0, currMB->block_x + pos_x, pos_y, pos_y + 2); + ResetMotionBlock8(&motion[currMB->block_y], LIST_1, currMB->block_x + pos_x, pos_y, pos_y + 2); + } +} + +void SetMotionVectorsMBISlice (Macroblock* currMB) +{ +} + +/*! + ************************************************************************************* + * \brief + * Sets motion vectors for a macroblock in a P Slice + ************************************************************************************* + */ +void SetMotionVectorsMBBSlice (Macroblock* currMB) +{ + VideoParameters *p_Vid = currMB->p_Vid; + Slice *currSlice = currMB->p_Slice; + PicMotionParams **motion = p_Vid->enc_picture->mv_info; + + int l0_ref, l1_ref; + + // copy all the motion vectors into rdopt structure + // Can simplify this by copying the MV's of the best mode (TBD) + // Should maybe add code to check for Intra only profiles + + if (currSlice->mb_aff_frame_flag || (currSlice->UseRDOQuant && currSlice->RDOQ_QP_Num > 1)) + { + memcpy(&currSlice->rddata->all_mv[LIST_0][0][0][0][0], &currSlice->all_mv[LIST_0][0][0][0][0], 2 * p_Vid->max_num_references * 9 * MB_BLOCK_PARTITIONS * sizeof(MotionVector)); + } + + if (currMB->mb_type == P16x16) // 16x16 + { + int pdir = currMB->b8x8[0].pdir; + if (pdir == LIST_0) + { + l0_ref = motion[currMB->block_y][currMB->block_x].ref_idx[LIST_0]; + CopyMVBlock16 (motion, currSlice->all_mv [LIST_0][l0_ref][P16x16], LIST_0, currMB->block_x, currMB->block_y, 0, 4); + ResetMotionBlock16(motion, LIST_1, currMB->block_x, currMB->block_y, 0, 4); + } + else if (pdir == LIST_1) + { + l1_ref = motion[currMB->block_y][currMB->block_x].ref_idx[LIST_1]; + ResetMotionBlock16 (motion, LIST_0, currMB->block_x, currMB->block_y, 0, 4); + CopyMVBlock16 (motion, currSlice->all_mv [LIST_1][l1_ref][P16x16], LIST_1, currMB->block_x, currMB->block_y, 0, 4); + } + else + { + int bipred_me = currMB->b8x8[0].bipred; + MotionVector *****all_mv = bipred_me ? currSlice->bipred_mv[bipred_me - 1]: currSlice->all_mv; + l0_ref = motion[currMB->block_y][currMB->block_x].ref_idx[LIST_0]; + CopyMVBlock16 (motion, all_mv [LIST_0][l0_ref][P16x16], LIST_0, currMB->block_x, currMB->block_y, 0, 4); + l1_ref = motion[currMB->block_y][currMB->block_x].ref_idx[LIST_1]; + CopyMVBlock16 (motion, all_mv [LIST_1][l1_ref][P16x16], LIST_1, currMB->block_x, currMB->block_y, 0, 4); + + // Is this necessary here? Can this be moved somewhere else? + if (bipred_me && (currSlice->mb_aff_frame_flag || (currSlice->UseRDOQuant && currSlice->RDOQ_QP_Num > 1))) + { + memcpy(&currSlice->rddata->all_mv [LIST_0][l0_ref][P16x16][0][0], &all_mv [LIST_0][l0_ref][P16x16][0][0], MB_BLOCK_PARTITIONS * sizeof(MotionVector)); + memcpy(&currSlice->rddata->all_mv [LIST_1][l1_ref][P16x16][0][0], &all_mv [LIST_1][l1_ref][P16x16][0][0], MB_BLOCK_PARTITIONS * sizeof(MotionVector)); + } + } + } + else if (currMB->mb_type == P16x8) // 16x8 + { + SetMVBSlice16x8(currSlice, motion, currMB, 0); + SetMVBSlice16x8(currSlice, motion, currMB, 2); + } + else if (currMB->mb_type == P8x16) // 16x8 + { + SetMVBSlice8x16(currSlice, motion, currMB, 0); + SetMVBSlice8x16(currSlice, motion, currMB, 2); + } + else if (currMB->mb_type == P8x8 || currMB->mb_type == BSKIP_DIRECT) // 8x8 & Direct/SKIP + { + SetMVBSlice8x8(currSlice, motion, currMB, 0, 0); + SetMVBSlice8x8(currSlice, motion, currMB, 0, 2); + SetMVBSlice8x8(currSlice, motion, currMB, 2, 0); + SetMVBSlice8x8(currSlice, motion, currMB, 2, 2); + } + else // Intra modes + { + ResetMotionBlock16 (motion, LIST_0, currMB->block_x, currMB->block_y, 0, 4); + ResetMotionBlock16 (motion, LIST_1, currMB->block_x, currMB->block_y, 0, 4); + } +} + +/*! + ************************************************************************************* + * \brief + * Copy ImgPel Data from one structure to another (16x16) + ************************************************************************************* + */ +void copy_image_data_16x16(imgpel **imgBuf1, imgpel **imgBuf2, int off1, int off2) +{ + int j; + for(j=0; jgiRDOpt_B8OnlyFlag = TRUE; + + rd_data->mb_p8x8_cost = 0; + rd_data->cbp8x8 = 0; + rd_data->cbp_blk8x8 = 0; + rd_data->cnt_nonz_8x8 = 0; + + for (block = 0; block < 4; block++) + { + rd_data->smb_p8x8_cost[block] = DISTBLK_MAX; + rd_data->smb_p8x8_rdcost[block] = DISTBLK_MAX; + rd_data->part[block].mode = -1; + } +} + +/*! +************************************************************************************* +* \brief +* set the range of chroma prediction mode +************************************************************************************* +*/ +void set_chroma_pred_mode(Macroblock *currMB, RD_PARAMS enc_mb, int *mb_available, char chroma_pred_mode_range[2]) +{ + VideoParameters *p_Vid = currMB->p_Vid; + InputParameters *p_Inp = currMB->p_Inp; + + if ((p_Vid->yuv_format != YUV400) && (p_Inp->separate_colour_plane_flag == 0)) + { + Slice *currSlice = currMB->p_Slice; + // precompute all new chroma intra prediction modes + currSlice->intra_chroma_prediction(currMB, &mb_available[0], &mb_available[1], &mb_available[2]); + + if (p_Inp->FastCrIntraDecision) + { + currSlice->intra_chroma_RD_decision(currMB, &enc_mb); + + chroma_pred_mode_range[0] = currMB->c_ipred_mode; + chroma_pred_mode_range[1] = currMB->c_ipred_mode; + } + else + { + chroma_pred_mode_range[0] = DC_PRED_8; + chroma_pred_mode_range[1] = PLANE_8; + } + } + else + { + chroma_pred_mode_range[0] = DC_PRED_8; + chroma_pred_mode_range[1] = DC_PRED_8; + } +} diff --git a/lencod/src/md_distortion.c b/lencod/src/md_distortion.c new file mode 100644 index 0000000..ec218d4 --- /dev/null +++ b/lencod/src/md_distortion.c @@ -0,0 +1,200 @@ + +/*! + *************************************************************************** + * \file md_distortion.c + * + * \brief + * Main macroblock mode decision functions and helpers + * + ************************************************************************** + */ + +#include +#include +#include + +#include "global.h" +#include "rdopt_coding_state.h" +#include "mb_access.h" +#include "intrarefresh.h" +#include "image.h" +#include "transform8x8.h" +#include "ratectl.h" +#include "mode_decision.h" +#include "fmo.h" +#include "me_umhex.h" +#include "me_umhexsmp.h" +#include "macroblock.h" +#include "mv_search.h" +#include "md_distortion.h" + +void setupDistortion(Slice *currSlice) +{ + currSlice->getDistortion = distortionSSE; +} + +/*! + *********************************************************************** + * \brief + * compute generic SSE + *********************************************************************** + */ +int64 compute_SSE(imgpel **imgRef, imgpel **imgSrc, int xRef, int xSrc, int ySize, int xSize) +{ + int i, j; + imgpel *lineRef, *lineSrc; + int64 distortion = 0; + + for (j = 0; j < ySize; j++) + { + lineRef = &imgRef[j][xRef]; + lineSrc = &imgSrc[j][xSrc]; + + for (i = 0; i < xSize; i++) + distortion += iabs2( *lineRef++ - *lineSrc++ ); + } + return distortion; +} + +distblk compute_SSE_cr(imgpel **imgRef, imgpel **imgSrc, int xRef, int xSrc, int ySize, int xSize) +{ + int i, j; + imgpel *lineRef, *lineSrc; + distblk distortion = 0; + + for (j = 0; j < ySize; j++) + { + lineRef = &imgRef[j][xRef]; + lineSrc = &imgSrc[j][xSrc]; + + for (i = 0; i < xSize; i++) + distortion += iabs2( *lineRef++ - *lineSrc++ ); + } + + return dist_scale(distortion); +} + +/*! + *********************************************************************** + * \brief + * compute 16x16 SSE + *********************************************************************** + */ +distblk compute_SSE16x16(imgpel **imgRef, imgpel **imgSrc, int xRef, int xSrc) +{ + int i, j; + imgpel *lineRef, *lineSrc; + distblk distortion = 0; + + for (j = 0; j < MB_BLOCK_SIZE; j++) + { + lineRef = &imgRef[j][xRef]; + lineSrc = &imgSrc[j][xSrc]; + + for (i = 0; i < MB_BLOCK_SIZE; i++) + distortion += iabs2( *lineRef++ - *lineSrc++ ); + } + + return dist_scale(distortion); +} + +distblk compute_SSE16x16_thres(imgpel **imgRef, imgpel **imgSrc, int xRef, int xSrc, distblk min_cost) +{ + int i, j; + imgpel *lineRef, *lineSrc; + distblk distortion = 0; + int imin_cost = dist_down(min_cost); + + for (j = 0; j < MB_BLOCK_SIZE; j++) + { + lineRef = &imgRef[j][xRef]; + lineSrc = &imgSrc[j][xSrc]; + + for (i = 0; i < MB_BLOCK_SIZE; i++) + distortion += iabs2( *lineRef++ - *lineSrc++ ); + if (distortion > imin_cost) + return (min_cost); + } + + return dist_scale(distortion); +} +/*! + *********************************************************************** + * \brief + * compute 8x8 SSE + *********************************************************************** + */ +distblk compute_SSE8x8(imgpel **imgRef, imgpel **imgSrc, int xRef, int xSrc) +{ + int i, j; + imgpel *lineRef, *lineSrc; + distblk distortion = 0; + + for (j = 0; j < BLOCK_SIZE_8x8; j++) + { + lineRef = &imgRef[j][xRef]; + lineSrc = &imgSrc[j][xSrc]; + + for (i = 0; i < BLOCK_SIZE_8x8; i++) + distortion += iabs2( *lineRef++ - *lineSrc++ ); + } + + return dist_scale(distortion); +} + + +/*! + *********************************************************************** + * \brief + * compute 4x4 SSE + *********************************************************************** + */ +distblk compute_SSE4x4(imgpel **imgRef, imgpel **imgSrc, int xRef, int xSrc) +{ + int i, j; + imgpel *lineRef, *lineSrc; + distblk distortion = 0; + + for (j = 0; j < BLOCK_SIZE; j++) + { + lineRef = &imgRef[j][xRef]; + lineSrc = &imgSrc[j][xSrc]; + + for (i = 0; i < BLOCK_SIZE; i++) + distortion += iabs2( *lineRef++ - *lineSrc++ ); + } + + return dist_scale(distortion); +} + +/*! +************************************************************************************* +* \brief +* SSE distortion calculation for a macroblock +************************************************************************************* +*/ +distblk distortionSSE(Macroblock *currMB) +{ + VideoParameters *p_Vid = currMB->p_Vid; + InputParameters *p_Inp = currMB->p_Inp; + distblk distortionY = 0; + distblk distortionCr[2] = {0, 0}; + + // LUMA + distortionY = compute_SSE16x16(&p_Vid->pCurImg[currMB->opix_y], &p_Vid->enc_picture->p_curr_img[currMB->pix_y], currMB->pix_x, currMB->pix_x); + + // CHROMA + if ((p_Vid->yuv_format != YUV400) && (p_Inp->separate_colour_plane_flag == 0)) + { + distortionCr[0] = compute_SSE_cr(&p_Vid->pImgOrg[1][currMB->opix_c_y], &p_Vid->enc_picture->imgUV[0][currMB->pix_c_y], currMB->pix_c_x, currMB->pix_c_x, p_Vid->mb_cr_size_y, p_Vid->mb_cr_size_x); + distortionCr[1] = compute_SSE_cr(&p_Vid->pImgOrg[2][currMB->opix_c_y], &p_Vid->enc_picture->imgUV[1][currMB->pix_c_y], currMB->pix_c_x, currMB->pix_c_x, p_Vid->mb_cr_size_y, p_Vid->mb_cr_size_x); + } +#if JCOST_OVERFLOWCHECK //overflow checking; + if(distortionY * p_Inp->WeightY + distortionCr[0] * p_Inp->WeightCb + distortionCr[1] * p_Inp->WeightCr > DISTBLK_MAX) + { + printf("Overflow: %s : %d \n MB: %d, Value: %lf\n", __FILE__, __LINE__, currMB->mbAddrX, (distortionY * p_Inp->WeightY + distortionCr[0] * p_Inp->WeightCb + distortionCr[1] * p_Inp->WeightCr)); + exit(-1); + } +#endif //end; + return (distblk)( distortionY * p_Inp->WeightY + distortionCr[0] * p_Inp->WeightCb + distortionCr[1] * p_Inp->WeightCr ); +} diff --git a/lencod/src/md_high.c b/lencod/src/md_high.c new file mode 100644 index 0000000..0bbac9f --- /dev/null +++ b/lencod/src/md_high.c @@ -0,0 +1,316 @@ +/*! +*************************************************************************** +* \file md_high.c +* +* \brief +* Main macroblock mode decision functions and helpers +* +************************************************************************** +*/ + +#include +#include +#include + +#include "global.h" +#include "rdopt_coding_state.h" +#include "intrarefresh.h" +#include "image.h" +#include "ratectl.h" +#include "mode_decision.h" +#include "mode_decision_p8x8.h" +#include "fmo.h" +#include "me_umhex.h" +#include "me_umhexsmp.h" +#include "macroblock.h" +#include "md_common.h" +#include "conformance.h" +#include "vlc.h" +#include "rdopt.h" +#include "mv_search.h" + + +/*! +************************************************************************************* +* \brief +* Mode Decision for a macroblock +************************************************************************************* +*/ +void encode_one_macroblock_high (Macroblock *currMB) +{ + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + InputParameters *p_Inp = currMB->p_Inp; + PicMotionParams **motion = p_Vid->enc_picture->mv_info; + RDOPTStructure *p_RDO = currSlice->p_RDO; + + int max_index = 9; + int block, index, mode, i, j; + RD_PARAMS enc_mb; + distblk bmcost[5] = {DISTBLK_MAX}; + distblk cost=0; + distblk min_cost = DISTBLK_MAX; + int intra1 = 0; + int mb_available[3]; + + short bslice = (short) (currSlice->slice_type == B_SLICE); + short pslice = (short) ((currSlice->slice_type == P_SLICE) || (currSlice->slice_type == SP_SLICE)); + short intra = (short) ((currSlice->slice_type == I_SLICE) || (currSlice->slice_type == SI_SLICE) || (pslice && currMB->mb_y == p_Vid->mb_y_upd && p_Vid->mb_y_upd != p_Vid->mb_y_intra)); + int lambda_mf[3]; + + imgpel **mb_pred = currSlice->mb_pred[0]; + Block8x8Info *b8x8info = p_Vid->b8x8info; + + char chroma_pred_mode_range[2]; + short inter_skip = 0; + BestMode md_best; + Info8x8 best; + + init_md_best(&md_best); + + // Init best (need to create simple function) + best = init_best(); + + intra |= RandomIntra (p_Vid, currMB->mbAddrX); // Forced Pseudo-Random Intra + + //===== Setup Macroblock encoding parameters ===== + init_enc_mb_params(currMB, &enc_mb, intra); + if (p_Inp->AdaptiveRounding) + { + reset_adaptive_rounding(p_Vid); + } + + if (currSlice->mb_aff_frame_flag) + { + reset_mb_nz_coeff(p_Vid, currMB->mbAddrX); + } + + //===== S T O R E C O D I N G S T A T E ===== + //--------------------------------------------------- + currSlice->store_coding_state (currMB, currSlice->p_RDO->cs_cm); + + if (!intra) + { + //===== set skip/direct motion vectors ===== + if (enc_mb.valid[0]) + { + if (bslice) + currSlice->Get_Direct_Motion_Vectors (currMB); + else + FindSkipModeMotionVector (currMB); + } + if (p_Inp->CtxAdptLagrangeMult == 1) + { + get_initial_mb16x16_cost(currMB); + } + + //===== MOTION ESTIMATION FOR (1)16x16, (2)16x8, (3)8x16 BLOCKS ===== + for (mode = 1; mode < 4; mode++) + { + best.mode = (char) mode; + best.bipred = 0; + b8x8info->best[mode][0].bipred = 0; + + if (enc_mb.valid[mode]) + { + for (cost=0, block=0; block<(mode==1?1:2); block++) + { + update_lambda_costs(currMB, &enc_mb, lambda_mf); + PartitionMotionSearch (currMB, mode, block, lambda_mf); + + //--- set 4x4 block indices (for getting MV) --- + j = (block==1 && mode==2 ? 2 : 0); + i = (block==1 && mode==3 ? 2 : 0); + + //--- get cost and reference frame for List 0 prediction --- + bmcost[LIST_0] = DISTBLK_MAX; + list_prediction_cost(currMB, LIST_0, block, mode, &enc_mb, bmcost, best.ref); + + if (bslice) + { + //--- get cost and reference frame for List 1 prediction --- + bmcost[LIST_1] = DISTBLK_MAX; + list_prediction_cost(currMB, LIST_1, block, mode, &enc_mb, bmcost, best.ref); + + // Compute bipredictive cost between best list 0 and best list 1 references + list_prediction_cost(currMB, BI_PRED, block, mode, &enc_mb, bmcost, best.ref); + + // currently Bi predictive ME is only supported for modes (1)16x16, (2)16x8, (3)8x16 and ref 0 + if (is_bipred_enabled(p_Vid, mode)) + { + get_bipred_cost(currMB, mode, block, i, j, &best, &enc_mb, bmcost); + } + else + { + bmcost[BI_PRED_L0] = DISTBLK_MAX; + bmcost[BI_PRED_L1] = DISTBLK_MAX; + } + + if( p_Inp->Force_BiPredMultiview == 1 ) + { + bmcost[BI_PRED] = 0; + } + + // Determine prediction list based on mode cost + determine_prediction_list(bmcost, &best, &cost); + + } + else // if (bslice) + { + best.pdir = 0; + cost += bmcost[LIST_0]; + } + + assign_enc_picture_params(currMB, mode, &best, 2 * block); + + //----- set reference frame and direction parameters ----- + set_block8x8_info(b8x8info, mode, block, &best); + + //--- set reference frames and motion vectors --- + if (mode>1 && block == 0) + currSlice->set_ref_and_motion_vectors (currMB, motion, &best, block); + } // for (block=0; block<(mode==1?1:2); block++) + + if (cost < min_cost) + { + md_best.mode = (byte) mode; + md_best.cost = cost; + currMB->best_mode = (short) mode; + min_cost = cost; + if (p_Inp->CtxAdptLagrangeMult == 1) + { + adjust_mb16x16_cost(currMB, cost); + } + } + } // if (enc_mb.valid[mode]) + } // for (mode=1; mode<4; mode++) + + if (enc_mb.valid[P8x8]) + { + currMB->valid_8x8 = FALSE; + + if (p_Inp->Transform8x8Mode) + { + ResetRD8x8Data(p_Vid, p_RDO->tr8x8); + currMB->luma_transform_size_8x8_flag = TRUE; //switch to 8x8 transform size + //=========================================================== + // Check 8x8 partition with transform size 8x8 + //=========================================================== + //===== LOOP OVER 8x8 SUB-PARTITIONS (Motion Estimation & Mode Decision) ===== + for (block = 0; block < 4; block++) + { + currSlice->submacroblock_mode_decision(currMB, &enc_mb, p_RDO->tr8x8, p_RDO->cofAC8x8ts[block], block, &cost); + if(!currMB->valid_8x8) + break; + set_subblock8x8_info(b8x8info, P8x8, block, p_RDO->tr8x8); + } + + }// if (p_Inp->Transform8x8Mode) + + currMB->valid_4x4 = FALSE; + if (p_Inp->Transform8x8Mode != 2) + { + currMB->luma_transform_size_8x8_flag = FALSE; //switch to 8x8 transform size + ResetRD8x8Data(p_Vid, p_RDO->tr4x4); + //================================================================= + // Check 8x8, 8x4, 4x8 and 4x4 partitions with transform size 4x4 + //================================================================= + //===== LOOP OVER 8x8 SUB-PARTITIONS (Motion Estimation & Mode Decision) ===== + for (block = 0; block < 4; block++) + { + currSlice->submacroblock_mode_decision(currMB, &enc_mb, p_RDO->tr4x4, p_RDO->coefAC8x8[block], block, &cost); + if(!currMB->valid_4x4) + break; + set_subblock8x8_info(b8x8info, P8x8, block, p_RDO->tr4x4); + } + }// if (p_Inp->Transform8x8Mode != 2) + + if (p_Inp->RCEnable) + rc_store_diff(currSlice->diffy, &p_Vid->pCurImg[currMB->opix_y], currMB->pix_x, mb_pred); + + p_Vid->giRDOpt_B8OnlyFlag = FALSE; + } + } + else // if (!intra) + { + min_cost = DISTBLK_MAX; + } + + // Set Chroma mode + set_chroma_pred_mode(currMB, enc_mb, mb_available, chroma_pred_mode_range); + + //========= C H O O S E B E S T M A C R O B L O C K M O D E ========= + //------------------------------------------------------------------------- + + for (currMB->c_ipred_mode = chroma_pred_mode_range[0]; currMB->c_ipred_mode<=chroma_pred_mode_range[1]; currMB->c_ipred_mode++) + { + // bypass if c_ipred_mode is not allowed + if ( (p_Vid->yuv_format != YUV400) && + ( ((!intra || !p_Inp->IntraDisableInterOnly) && p_Inp->ChromaIntraDisable == 1 && currMB->c_ipred_mode!=DC_PRED_8) + || (currMB->c_ipred_mode == VERT_PRED_8 && !mb_available[0]) + || (currMB->c_ipred_mode == HOR_PRED_8 && !mb_available[1]) + || (currMB->c_ipred_mode == PLANE_8 && (!mb_available[1] || !mb_available[0] || !mb_available[2])))) + continue; + + //===== GET BEST MACROBLOCK MODE ===== + for (index=0; index < max_index; index++) + { + mode = mb_mode_table[index]; + if (enc_mb.valid[mode]) + { + if (p_Vid->yuv_format != YUV400) + { + currMB->i16mode = 0; + } + + // Skip intra modes in inter slices if best mode is inter P444_joined) + { + if (p_Inp->SkipIntraInInterSlices && !intra && mode >= I16MB + && currMB->best_mode <=3 && currMB->best_cbp == 0 && currSlice->cmp_cbp[1] == 0 && currSlice->cmp_cbp[2] == 0 && (currMB->min_rdcost < weighted_cost(enc_mb.lambda_mdfp,5))) + continue; + } + else + { + if (p_Inp->SkipIntraInInterSlices) + { + if (!intra && mode >= I4MB) + { + if (currMB->best_mode <=3 && currMB->best_cbp == 0 && (currMB->min_rdcost < weighted_cost(enc_mb.lambda_mdfp, 5))) + { + continue; + } + else if (currMB->best_mode == 0 && (currMB->min_rdcost < weighted_cost(enc_mb.lambda_mdfp,6))) + { + continue; + } + } + } + } + + compute_mode_RD_cost(currMB, &enc_mb, (short) mode, &inter_skip); + + } + }// for (index=0; indexc_ipred_mode=DC_PRED_8; currMB->c_ipred_mode<=chroma_pred_mode_range[1]; currMB->c_ipred_mode++) + + restore_nz_coeff(currMB); + + intra1 = IS_INTRA(currMB); + + //===== S E T F I N A L M A C R O B L O C K P A R A M E T E R S ====== + //--------------------------------------------------------------------------- + update_qp_cbp_tmp(currMB, p_RDO->cbp); + currSlice->set_stored_mb_parameters (currMB); + + // Rate control + if(p_Inp->RCEnable && p_Inp->RCUpdateMode <= MAX_RC_MODE) + rc_store_mad(currMB); + + + //===== Decide if this MB will restrict the reference frames ===== + if (p_Inp->RestrictRef) + update_refresh_map(currMB, intra, intra1); +} + + diff --git a/lencod/src/md_highfast.c b/lencod/src/md_highfast.c new file mode 100644 index 0000000..f57255b --- /dev/null +++ b/lencod/src/md_highfast.c @@ -0,0 +1,465 @@ +/*! +*************************************************************************** +* \file md_highfast.c +* +* \brief +* Main macroblock mode decision functions and helpers +* +************************************************************************** +*/ + +#include +#include +#include + +#include "global.h" +#include "rdopt_coding_state.h" +#include "mb_access.h" +#include "intrarefresh.h" +#include "image.h" +#include "ratectl.h" +#include "mode_decision.h" +#include "mode_decision_p8x8.h" +#include "fmo.h" +#include "me_umhex.h" +#include "me_umhexsmp.h" +#include "macroblock.h" +#include "md_common.h" +#include "conformance.h" +#include "vlc.h" +#include "rdopt.h" +#include "mv_search.h" + +/*! +************************************************************************************* +* \brief +* Fast intra decision +************************************************************************************* +*/ + +static void fast_mode_intra_decision(Macroblock *currMB, short *intra_skip) +{ + VideoParameters *p_Vid = currMB->p_Vid; + int i; + int mb_available_up, mb_available_left; + long SBE; + double AR = 0, ABE = 0; + PixelPos up; //!< pixel position p(0,-1) + PixelPos left[2]; //!< pixel positions p(-1, -1..0) + + p_Vid->getNeighbour(currMB, 0 , -1 , p_Vid->mb_size[IS_LUMA], &up); + p_Vid->getNeighbour(currMB, -1 , -1 , p_Vid->mb_size[IS_LUMA], &left[0]); + p_Vid->getNeighbour(currMB, -1 , 0 , p_Vid->mb_size[IS_LUMA], &left[1]); + + mb_available_up = up.available; + mb_available_left = left[1].available; + + AR=(1.0/384)*currMB->min_rate; + + SBE = 0; + + if( (currMB->mb_y != (int)p_Vid->FrameHeightInMbs-1) && (currMB->mb_x != (int)p_Vid->PicWidthInMbs-1) && mb_available_left && mb_available_up) + { + for(i = 0; i < MB_BLOCK_SIZE; i++) + { + SBE += iabs(p_Vid->pCurImg[currMB->opix_y][currMB->pix_x + i] - p_Vid->enc_picture->imgY[currMB->pix_y - 1][currMB->pix_x + i]); + SBE += iabs(p_Vid->pCurImg[currMB->opix_y + i][currMB->pix_x] - p_Vid->enc_picture->imgY[currMB->pix_y + i][currMB->pix_x - 1]); + } + + for(i = 0; i < 8; i++) + { + SBE += iabs(p_Vid->pImgOrg[1][currMB->opix_c_y][currMB->pix_c_x + i] - p_Vid->enc_picture->imgUV[0][currMB->pix_c_y - 1][currMB->pix_c_x + i]); + SBE += iabs(p_Vid->pImgOrg[1][currMB->opix_c_y+i][currMB->pix_c_x] - p_Vid->enc_picture->imgUV[0][currMB->pix_c_y + i][currMB->pix_c_x - 1]); + SBE += iabs(p_Vid->pImgOrg[2][currMB->opix_c_y][currMB->pix_c_x + i] - p_Vid->enc_picture->imgUV[1][currMB->pix_c_y - 1][currMB->pix_c_x + i]); + SBE += iabs(p_Vid->pImgOrg[2][currMB->opix_c_y+i][currMB->pix_c_x] - p_Vid->enc_picture->imgUV[1][currMB->pix_c_y + i][currMB->pix_c_x - 1]); + } + ABE = 1.0/64 * SBE; + } + else // Image boundary + { + ABE = 0; + } + + if(AR <= ABE) + { + *intra_skip = 1; + } +} + +/*! +************************************************************************************* +* \brief +* Mode Decision for a macroblock +************************************************************************************* +*/ +void encode_one_macroblock_highfast (Macroblock *currMB) +{ + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + InputParameters *p_Inp = currMB->p_Inp; + PicMotionParams **motion = p_Vid->enc_picture->mv_info; + RDOPTStructure *p_RDO = currSlice->p_RDO; + + int max_index; + int block, index, mode, i, j; + RD_PARAMS enc_mb; + distblk max_rdcost = DISTBLK_MAX; + distblk bmcost[5] = {DISTBLK_MAX}; + distblk cost=0; + distblk min_cost = DISTBLK_MAX; + int intra1 = 0; + int mb_available[3]; + + short islice = (short) (currSlice->slice_type == I_SLICE); + short bslice = (short) (currSlice->slice_type == B_SLICE); + short pslice = (short) ((currSlice->slice_type == P_SLICE) || (currSlice->slice_type == SP_SLICE)); + short intra = (short) ((currSlice->slice_type == I_SLICE) || (currSlice->slice_type == SI_SLICE) || (pslice && currMB->mb_y == p_Vid->mb_y_upd && p_Vid->mb_y_upd != p_Vid->mb_y_intra)); + int lambda_mf[3]; + + imgpel **mb_pred = currSlice->mb_pred[0]; + Block8x8Info *b8x8info = p_Vid->b8x8info; + + char chroma_pred_mode_range[2]; + + + MotionVector *allmvs = (currSlice->slice_type == I_SLICE) ? NULL: &currSlice->all_mv[0][0][0][0][0]; + + // Fast Mode Decision + short inter_skip = 0, intra_skip = 0; + int mode16 = 0; + distblk RDCost16 = DISTBLK_MAX; + BestMode md_best; + Info8x8 best; + + init_md_best(&md_best); + + // Init best (need to create simple function) + best.pdir = 0; + best.bipred = 0; + best.ref[LIST_0] = 0; + best.ref[LIST_1] = -1; + + intra |= RandomIntra (p_Vid, currMB->mbAddrX); // Forced Pseudo-Random Intra + + //===== Setup Macroblock encoding parameters ===== + init_enc_mb_params(currMB, &enc_mb, intra); + if (p_Inp->AdaptiveRounding) + { + reset_adaptive_rounding(p_Vid); + } + + if (currSlice->mb_aff_frame_flag) + { + reset_mb_nz_coeff(p_Vid, currMB->mbAddrX); + } + + //===== S T O R E C O D I N G S T A T E ===== + //--------------------------------------------------- + currSlice->store_coding_state (currMB, currSlice->p_RDO->cs_cm); + + if (!intra) + { + //===== set direct motion vectors ===== + if (bslice && enc_mb.valid[0]) + { + currSlice->Get_Direct_Motion_Vectors (currMB); + currMB->best_mode = 0; + currMB->c_ipred_mode=DC_PRED_8; + currMB->min_rdcost = max_rdcost; + compute_mode_RD_cost(currMB, &enc_mb, 0, &inter_skip); + } + if (p_Inp->CtxAdptLagrangeMult == 1) + { + get_initial_mb16x16_cost(currMB); + } + + //===== MOTION ESTIMATION FOR (1)16x16, (2)16x8, (3)8x16 BLOCKS ===== + for (mode = 1; mode < 4; mode++) + { + best.mode = (char) mode; + best.bipred = 0; + b8x8info->best[mode][0].bipred = 0; + + if (enc_mb.valid[mode] && !inter_skip) + { + for (cost=0, block=0; block<(mode==1?1:2); block++) + { + update_lambda_costs(currMB, &enc_mb, lambda_mf); + PartitionMotionSearch (currMB, mode, block, lambda_mf); + + //--- set 4x4 block indizes (for getting MV) --- + j = (block==1 && mode==2 ? 2 : 0); + i = (block==1 && mode==3 ? 2 : 0); + + //--- get cost and reference frame for List 0 prediction --- + bmcost[LIST_0] = DISTBLK_MAX; + list_prediction_cost(currMB, LIST_0, block, mode, &enc_mb, bmcost, best.ref); + + if (bslice) + { + //--- get cost and reference frame for List 1 prediction --- + bmcost[LIST_1] = DISTBLK_MAX; + list_prediction_cost(currMB, LIST_1, block, mode, &enc_mb, bmcost, best.ref); + + // Compute bipredictive cost between best list 0 and best list 1 references + list_prediction_cost(currMB, BI_PRED, block, mode, &enc_mb, bmcost, best.ref); + + // currently Bi predictive ME is only supported for modes (1)16x16, (2)16x8, (3)8x16 and ref 0 + if (is_bipred_enabled(p_Vid, mode)) + { + get_bipred_cost(currMB, mode, block, i, j, &best, &enc_mb, bmcost); + } + else + { + bmcost[BI_PRED_L0] = DISTBLK_MAX; + bmcost[BI_PRED_L1] = DISTBLK_MAX; + } + + // Determine prediction list based on mode cost + determine_prediction_list(bmcost, &best, &cost); + } + else // if (bslice) + { + best.pdir = 0; + cost += bmcost[LIST_0]; + } + + assign_enc_picture_params(currMB, mode, &best, 2 * block); + + //----- set reference frame and direction parameters ----- + set_block8x8_info(b8x8info, mode, block, &best); + + //--- set reference frames and motion vectors --- + if (mode>1 && block == 0) + currSlice->set_ref_and_motion_vectors (currMB, motion, &best, block); + } // for (block=0; block<(mode==1?1:2); block++) + + + if(mode == 1) + { + if(pslice) + currMB->min_rdcost = max_rdcost; + + currMB->c_ipred_mode=DC_PRED_8; + compute_mode_RD_cost(currMB, &enc_mb, (short) mode, &inter_skip); + + if(pslice) + { + // Get SKIP motion vector and compare SKIP_MV with best motion vector of 16x16 + FindSkipModeMotionVector (currMB); + + if(p_Inp->EarlySkipEnable) + { + //===== check for SKIP mode ===== + if ( currMB->cbp==0 && motion[currMB->block_y][currMB->block_x].ref_idx[LIST_0]==0 && + motion[currMB->block_y][currMB->block_x].mv[LIST_0].mv_x == allmvs->mv_x && + motion[currMB->block_y][currMB->block_x].mv[LIST_0].mv_y == allmvs->mv_y) + { + inter_skip = 1; + currMB->best_mode = 0; + } + } // if(p_Inp->EarlySkipEnable) + } + + // store variables. + RDCost16 = currMB->min_rdcost; + mode16 = currMB->best_mode; + } // if(mode == 1) + + if ((!inter_skip) && (cost < min_cost)) + { + md_best.mode = (byte) mode; + md_best.cost = cost; + currMB->best_mode = (short) mode; + min_cost = cost; + if (p_Inp->CtxAdptLagrangeMult == 1) + { + adjust_mb16x16_cost(currMB, cost); + } + } + } // if (enc_mb.valid[mode]) + } // for (mode=1; mode<4; mode++) + + if ((!inter_skip) && enc_mb.valid[P8x8]) + { + currMB->valid_8x8 = FALSE; + + if (p_Inp->Transform8x8Mode) + { + ResetRD8x8Data(p_Vid, p_RDO->tr8x8); + currMB->luma_transform_size_8x8_flag = TRUE; //switch to 8x8 transform size + //=========================================================== + // Check 8x8 partition with transform size 8x8 + //=========================================================== + //===== LOOP OVER 8x8 SUB-PARTITIONS (Motion Estimation & Mode Decision) ===== + for (block = 0; block < 4; block++) + { + currSlice->submacroblock_mode_decision(currMB, &enc_mb, p_RDO->tr8x8, p_RDO->cofAC8x8ts[block], block, &cost); + if(!currMB->valid_8x8) + break; + set_subblock8x8_info(b8x8info, P8x8, block, p_RDO->tr8x8); + } + }// if (p_Inp->Transform8x8Mode) + + currMB->valid_4x4 = FALSE; + if (p_Inp->Transform8x8Mode != 2) + { + currMB->luma_transform_size_8x8_flag = FALSE; //switch to 8x8 transform size + ResetRD8x8Data(p_Vid, p_RDO->tr4x4); + //================================================================= + // Check 8x8, 8x4, 4x8 and 4x4 partitions with transform size 4x4 + //================================================================= + //===== LOOP OVER 8x8 SUB-PARTITIONS (Motion Estimation & Mode Decision) ===== + for (block = 0; block < 4; block++) + { + currSlice->submacroblock_mode_decision(currMB, &enc_mb, p_RDO->tr4x4, p_RDO->coefAC8x8[block], block, &cost); + if(!currMB->valid_4x4) + break; + set_subblock8x8_info(b8x8info, P8x8, block, p_RDO->tr4x4); + } + }// if (p_Inp->Transform8x8Mode != 2) + + if (p_Inp->RCEnable) + rc_store_diff(currSlice->diffy, &p_Vid->pCurImg[currMB->opix_y], currMB->pix_x, mb_pred); + + p_Vid->giRDOpt_B8OnlyFlag = FALSE; + } + } + else // if (!intra) + { + min_cost = DISTBLK_MAX; + } + + //========= C H O O S E B E S T M A C R O B L O C K M O D E ========= + //------------------------------------------------------------------------- + { + if (!inter_skip) + { + if(currSlice->slice_type != I_SLICE && currSlice->slice_type != SI_SLICE) + { + currMB->min_rdcost = RDCost16; + currMB->best_mode = (short) mode16; + } + else + currMB->min_rdcost = max_rdcost; + + // if Fast High mode, compute inter modes separate process for inter/intra + max_index = ((!intra && p_Inp->SelectiveIntraEnable ) ? 5 : 9); + + // Set Chroma mode + set_chroma_pred_mode(currMB, enc_mb, mb_available, chroma_pred_mode_range); + + //========= C H O O S E B E S T M A C R O B L O C K M O D E ========= + //------------------------------------------------------------------------- + + for (currMB->c_ipred_mode = chroma_pred_mode_range[0]; currMB->c_ipred_mode<=chroma_pred_mode_range[1]; currMB->c_ipred_mode++) + { + // bypass if c_ipred_mode is not allowed + if ( (p_Vid->yuv_format != YUV400) && + ( ((!intra || !p_Inp->IntraDisableInterOnly) && p_Inp->ChromaIntraDisable == 1 && currMB->c_ipred_mode!=DC_PRED_8) + || (currMB->c_ipred_mode == VERT_PRED_8 && !mb_available[0]) + || (currMB->c_ipred_mode == HOR_PRED_8 && !mb_available[1]) + || (currMB->c_ipred_mode == PLANE_8 && (!mb_available[1] || !mb_available[0] || !mb_available[2])))) + continue; + + //===== GET BEST MACROBLOCK MODE ===== + for (index=0; index < max_index; index++) + { + mode = mb_mode_table[index]; + if (mode == 1) + continue; + if (enc_mb.valid[mode]) + { + if (p_Vid->yuv_format != YUV400) + { + currMB->i16mode = 0; + } + + // Skip intra modes in inter slices if best mode is inter P444_joined) + { + if (p_Inp->SkipIntraInInterSlices && !intra && mode >= I16MB + && currMB->best_mode <=3 && currMB->best_cbp == 0 && currSlice->cmp_cbp[1] == 0 && currSlice->cmp_cbp[2] == 0 && (currMB->min_rdcost < weighted_cost(enc_mb.lambda_mdfp, 5))) + continue; + } + else + { + if (p_Inp->SkipIntraInInterSlices && !intra && mode >= I4MB && currMB->best_mode <=3 && currMB->best_cbp == 0 && (currMB->min_rdcost < weighted_cost(enc_mb.lambda_mdfp,5))) + continue; + } + + compute_mode_RD_cost(currMB, &enc_mb, (short) mode, &inter_skip); + + } + }// for (index=0; indexc_ipred_mode=DC_PRED_8; currMB->c_ipred_mode<=chroma_pred_mode_range[1]; currMB->c_ipred_mode++) + + // Selective Intra Coding + if((currSlice->slice_type != I_SLICE && currSlice->slice_type != SI_SLICE) && p_Inp->SelectiveIntraEnable && !is_FREXT_profile(p_Inp->ProfileIDC)) + { + fast_mode_intra_decision(currMB, &intra_skip); + + if(!intra_skip) + { + // Set Chroma mode + set_chroma_pred_mode(currMB, enc_mb, mb_available, chroma_pred_mode_range); + + max_index = 9; + + for (currMB->c_ipred_mode=chroma_pred_mode_range[0]; currMB->c_ipred_mode<=chroma_pred_mode_range[1]; currMB->c_ipred_mode++) + { + + // bypass if c_ipred_mode is not allowed + if ( (p_Vid->yuv_format != YUV400) && + ( ((!intra || !p_Inp->IntraDisableInterOnly) && p_Inp->ChromaIntraDisable == 1 && currMB->c_ipred_mode!=DC_PRED_8) + || (currMB->c_ipred_mode == VERT_PRED_8 && !mb_available[0]) + || (currMB->c_ipred_mode == HOR_PRED_8 && !mb_available[1]) + || (currMB->c_ipred_mode == PLANE_8 && (!mb_available[0] || !mb_available[1]|| !mb_available[2])))) + continue; + + //===== GET BEST MACROBLOCK MODE ===== + for (index = 5; index < max_index; index++) + { + mode = mb_mode_table[index]; + + // Skip intra modes in inter slices if best mode is inter SkipIntraInInterSlices && !intra && mode >= I4MB && currMB->best_mode <=3 && currMB->best_cbp == 0) + continue; + + if (p_Vid->yuv_format != YUV400) + { + currMB->i16mode = 0; + // RDcost of mode 1 in P-slice and mode 0, 1 in B-slice are already available + if(((bslice && mode == 0) || (!islice && mode == 1))) + continue; + } + + if (enc_mb.valid[mode]) + compute_mode_RD_cost(currMB, &enc_mb, (short) mode, &inter_skip); + } // for (index = 5; index < max_index; index++) + } + } + } + } + restore_nz_coeff(currMB); + + } + + intra1 = IS_INTRA(currMB); + + //===== S E T F I N A L M A C R O B L O C K P A R A M E T E R S ====== + //--------------------------------------------------------------------------- + update_qp_cbp_tmp(currMB, p_RDO->cbp); + currSlice->set_stored_mb_parameters (currMB); + + // Rate control + if(p_Inp->RCEnable && p_Inp->RCUpdateMode <= MAX_RC_MODE) + rc_store_mad(currMB); + + //===== Decide if this MB will restrict the reference frames ===== + if (p_Inp->RestrictRef) + update_refresh_map(currMB, intra, intra1); + +} + + diff --git a/lencod/src/md_highloss.c b/lencod/src/md_highloss.c new file mode 100644 index 0000000..4f2fe66 --- /dev/null +++ b/lencod/src/md_highloss.c @@ -0,0 +1,318 @@ +/*! + *************************************************************************** + * \file md_highloss.c + * + * \brief + * Main macroblock mode decision functions and helpers + * + ************************************************************************** + */ + +#include +#include +#include + +#include "global.h" +#include "rdopt_coding_state.h" +#include "intrarefresh.h" +#include "image.h" +#include "ratectl.h" +#include "mode_decision.h" +#include "mode_decision_p8x8.h" +#include "fmo.h" +#include "me_umhex.h" +#include "me_umhexsmp.h" +#include "macroblock.h" +#include "md_common.h" +#include "conformance.h" +#include "vlc.h" +#include "rdopt.h" +#include "mv_search.h" + +/*! +************************************************************************************* +* \brief +* Mode Decision for a macroblock with error resilience +************************************************************************************* +*/ +void encode_one_macroblock_highloss (Macroblock *currMB) +{ + Slice *currSlice = currMB->p_Slice; + RDOPTStructure *p_RDO = currSlice->p_RDO; + VideoParameters *p_Vid = currMB->p_Vid; + InputParameters *p_Inp = currMB->p_Inp; + PicMotionParams **motion = p_Vid->enc_picture->mv_info; + + int max_index = 9; + int rerun, block, index, mode, i, j; + RD_PARAMS enc_mb; + distblk bmcost[5] = {DISTBLK_MAX}; + distblk cost=0; + distblk min_cost = DISTBLK_MAX; + int intra1 = 0; + int mb_available[3]; + + short bslice = (short) (currSlice->slice_type == B_SLICE); + short pslice = (short) ((currSlice->slice_type == P_SLICE) || (currSlice->slice_type == SP_SLICE)); + short intra = (short) ((currSlice->slice_type == I_SLICE) || (currSlice->slice_type == SI_SLICE) || (pslice && currMB->mb_y == p_Vid->mb_y_upd && p_Vid->mb_y_upd != p_Vid->mb_y_intra)); + int lambda_mf[3]; + short runs = (short) ((p_Inp->RestrictRef==1 && (pslice || (bslice && p_Vid->nal_reference_idc>0))) ? 2 : 1); + + imgpel **mb_pred = currSlice->mb_pred[0]; + Block8x8Info *b8x8info = p_Vid->b8x8info; + + char chroma_pred_mode_range[2]; + short inter_skip = 0; + BestMode md_best; + Info8x8 best; + + init_md_best(&md_best); + + // Init best (need to create simple function) + best.pdir = 0; + best.bipred = 0; + best.ref[LIST_0] = 0; + best.ref[LIST_1] = -1; + + intra |= RandomIntra (p_Vid, currMB->mbAddrX); // Forced Pseudo-Random Intra + + //===== Setup Macroblock encoding parameters ===== + init_enc_mb_params(currMB, &enc_mb, intra); + + // Perform multiple encodings if rdopt with losses is enabled + for (rerun=0; rerunrdopt= (rerun==0) ? 1 : 3; + + if (p_Inp->AdaptiveRounding) + { + reset_adaptive_rounding(p_Vid); + } + + if (currSlice->mb_aff_frame_flag) + { + reset_mb_nz_coeff(p_Vid, currMB->mbAddrX); + } + + //===== S T O R E C O D I N G S T A T E ===== + //--------------------------------------------------- + currSlice->store_coding_state (currMB, currSlice->p_RDO->cs_cm); + + if (!intra) + { + //===== set skip/direct motion vectors ===== + if (enc_mb.valid[0]) + { + if (bslice) + currSlice->Get_Direct_Motion_Vectors (currMB); + else + FindSkipModeMotionVector (currMB); + } + if (p_Inp->CtxAdptLagrangeMult == 1) + { + get_initial_mb16x16_cost(currMB); + } + + //===== MOTION ESTIMATION FOR 16x16, 16x8, 8x16 BLOCKS ===== + for (mode = 1; mode < 4; mode++) + { + best.mode = (char) mode; + best.bipred = 0; + b8x8info->best[mode][0].bipred = 0; + + if (enc_mb.valid[mode]) + { + for (cost=0, block=0; block<(mode==1?1:2); block++) + { + update_lambda_costs(currMB, &enc_mb, lambda_mf); + PartitionMotionSearch (currMB, mode, block, lambda_mf); + + //--- set 4x4 block indices (for getting MV) --- + j = (block==1 && mode==2 ? 2 : 0); + i = (block==1 && mode==3 ? 2 : 0); + + //--- get cost and reference frame for List 0 prediction --- + bmcost[LIST_0] = DISTBLK_MAX; + list_prediction_cost(currMB, LIST_0, block, mode, &enc_mb, bmcost, best.ref); + + if (bslice) + { + //--- get cost and reference frame for List 1 prediction --- + bmcost[LIST_1] = DISTBLK_MAX; + list_prediction_cost(currMB, LIST_1, block, mode, &enc_mb, bmcost, best.ref); + + // Compute bipredictive cost between best list 0 and best list 1 references + list_prediction_cost(currMB, BI_PRED, block, mode, &enc_mb, bmcost, best.ref); + + // currently Bi predictive ME is only supported for modes 1, 2, 3 and ref 0 + if (is_bipred_enabled(p_Vid, mode)) + { + get_bipred_cost(currMB, mode, block, i, j, &best, &enc_mb, bmcost); + } + else + { + bmcost[BI_PRED_L0] = DISTBLK_MAX; + bmcost[BI_PRED_L1] = DISTBLK_MAX; + } + + // Determine prediction list based on mode cost + determine_prediction_list(bmcost, &best, &cost); + } + else // if (bslice) + { + best.pdir = 0; + cost += bmcost[LIST_0]; + } + + assign_enc_picture_params(currMB, mode, &best, 2 * block); + + //----- set reference frame and direction parameters ----- + set_block8x8_info(b8x8info, mode, block, &best); + + //--- set reference frames and motion vectors --- + if (mode>1 && block==0) + currSlice->set_ref_and_motion_vectors (currMB, motion, &best, block); + } // for (block=0; block<(mode==1?1:2); block++) + if (cost < min_cost) + { + md_best.mode = (byte) mode; + md_best.cost = cost; + currMB->best_mode = (short) mode; + min_cost = cost; + if (p_Inp->CtxAdptLagrangeMult == 1) + { + adjust_mb16x16_cost(currMB, cost); + } + } + } // if (enc_mb.valid[mode]) + } // for (mode=1; mode<4; mode++) + + if (enc_mb.valid[P8x8]) + { + currMB->valid_8x8 = FALSE; + + if (p_Inp->Transform8x8Mode) + { + ResetRD8x8Data(p_Vid, p_RDO->tr8x8); + currMB->luma_transform_size_8x8_flag = TRUE; //switch to 8x8 transform size + //=========================================================== + // Check 8x8 partition with transform size 8x8 + //=========================================================== + //===== LOOP OVER 8x8 SUB-PARTITIONS (Motion Estimation & Mode Decision) ===== + for (block = 0; block < 4; block++) + { + currSlice->submacroblock_mode_decision(currMB, &enc_mb, p_RDO->tr8x8, p_RDO->cofAC8x8ts[block], block, &cost); + if(!currMB->valid_8x8) + break; + set_subblock8x8_info(b8x8info, P8x8, block, p_RDO->tr8x8); + } + }// if (p_Inp->Transform8x8Mode) + + currMB->valid_4x4 = FALSE; + if (p_Inp->Transform8x8Mode != 2) + { + currMB->luma_transform_size_8x8_flag = FALSE; //switch to 8x8 transform size + ResetRD8x8Data(p_Vid, p_RDO->tr4x4); + //================================================================= + // Check 8x8, 8x4, 4x8 and 4x4 partitions with transform size 4x4 + //================================================================= + //===== LOOP OVER 8x8 SUB-PARTITIONS (Motion Estimation & Mode Decision) ===== + for (block = 0; block < 4; block++) + { + currSlice->submacroblock_mode_decision(currMB, &enc_mb, p_RDO->tr4x4, p_RDO->coefAC8x8[block], block, &cost); + if(!currMB->valid_4x4) + break; + set_subblock8x8_info(b8x8info, P8x8, block, p_RDO->tr4x4); + } + + }// if (p_Inp->Transform8x8Mode != 2) + + if (p_Inp->RCEnable) + rc_store_diff(currSlice->diffy, &p_Vid->pCurImg[currMB->opix_y], currMB->pix_x, mb_pred); + + p_Vid->giRDOpt_B8OnlyFlag = FALSE; + } + } + else // if (!intra) + { + min_cost = DISTBLK_MAX; + } + + // Set Chroma mode + set_chroma_pred_mode(currMB, enc_mb, mb_available, chroma_pred_mode_range); + + //========= C H O O S E B E S T M A C R O B L O C K M O D E ========= + //------------------------------------------------------------------------- + for (currMB->c_ipred_mode = chroma_pred_mode_range[0]; currMB->c_ipred_mode<=chroma_pred_mode_range[1]; currMB->c_ipred_mode++) + { + // bypass if c_ipred_mode is not allowed + if ( (p_Vid->yuv_format != YUV400) && + ( ((!intra || !p_Inp->IntraDisableInterOnly) && p_Inp->ChromaIntraDisable == 1 && currMB->c_ipred_mode!=DC_PRED_8) + || (currMB->c_ipred_mode == VERT_PRED_8 && !mb_available[0]) + || (currMB->c_ipred_mode == HOR_PRED_8 && !mb_available[1]) + || (currMB->c_ipred_mode == PLANE_8 && (!mb_available[1] || !mb_available[0] || !mb_available[2])))) + continue; + + //===== GET BEST MACROBLOCK MODE ===== + for (index=0; index < max_index; index++) + { + mode = mb_mode_table[index]; + if (enc_mb.valid[mode]) + { + if (p_Vid->yuv_format != YUV400) + { + currMB->i16mode = 0; + } + + // Skip intra modes in inter slices if best mode is inter P444_joined) + { + if (p_Inp->SkipIntraInInterSlices && !intra && mode >= I16MB + && currMB->best_mode <=3 && currMB->best_cbp == 0 && currSlice->cmp_cbp[1] == 0 && currSlice->cmp_cbp[2] == 0 && (currMB->min_rdcost < weighted_cost(enc_mb.lambda_mdfp,5))) + continue; + } + else + { + if (p_Inp->SkipIntraInInterSlices) + { + if (!intra && mode >= I4MB) + { + if (currMB->best_mode <=3 && currMB->best_cbp == 0 && (currMB->min_rdcost < weighted_cost(enc_mb.lambda_mdfp, 5))) + { + continue; + } + else if (currMB->best_mode == 0 && (currMB->min_rdcost < weighted_cost(enc_mb.lambda_mdfp,6))) + { + continue; + } + } + } + } + compute_mode_RD_cost(currMB, &enc_mb, (short) mode, &inter_skip); + + } + }// for (index=0; indexc_ipred_mode=DC_PRED_8; currMB->c_ipred_mode<=chroma_pred_mode_range[1]; currMB->c_ipred_mode++) + + restore_nz_coeff(currMB); + + if (rerun==0) + intra1 = IS_INTRA(currMB); + } // for (rerun=0; reruncbp); + currSlice->set_stored_mb_parameters (currMB); + + // Rate control + if(p_Inp->RCEnable && p_Inp->RCUpdateMode <= MAX_RC_MODE) + rc_store_mad(currMB); + + //===== Decide if this MB will restrict the reference frames ===== + if (p_Inp->RestrictRef) + update_refresh_map(currMB, intra, intra1); +} + + diff --git a/lencod/src/md_low.c b/lencod/src/md_low.c new file mode 100644 index 0000000..93a95d6 --- /dev/null +++ b/lencod/src/md_low.c @@ -0,0 +1,688 @@ + +/*! + *************************************************************************** + * \file md_low.c + * + * \brief + * Main macroblock mode decision functions and helpers + * + ************************************************************************** + */ + +#include +#include +#include + +#include "global.h" +#include "rdopt_coding_state.h" +#include "mb_access.h" +#include "intrarefresh.h" +#include "image.h" +#include "transform8x8.h" +#include "ratectl.h" +#include "mode_decision.h" +#include "mode_decision_p8x8.h" +#include "fmo.h" +#include "me_umhex.h" +#include "me_umhexsmp.h" +#include "macroblock.h" +#include "q_around.h" +#include "vlc.h" +#include "md_common.h" +#include "rdopt.h" +#include "memalloc.h" +#include "mc_prediction.h" +#include "rd_intra_jm.h" + +/*! + ************************************************************************ + * \brief + * RDOff decision between 4x4 and 8x8 transform + ************************************************************************ + */ +static int get_best_transform_8x8(Macroblock *currMB) +{ + if(currMB->p_Inp->Transform8x8Mode == 2) //always use the 8x8 transform + return 1; + else + { + VideoParameters *p_Vid = currMB->p_Vid; + RDOPTStructure *p_RDO = currMB->p_Slice->p_RDO; + + short diff16[4][16]; + short diff64[64]; + int pic_pix_y, pic_pix_x, i, j; + int mb_y, mb_x, block8x8; + distblk cost8x8=0, cost4x4=0; + + for (block8x8 = 0; block8x8 < 4; block8x8++) + { + short *tmp64 = diff64; + short *tmp16[4]; //{diff16[0], diff16[1], diff16[2], diff16[3]}; + short source_pel, index; + + tmp16[0] = diff16[0]; + tmp16[1] = diff16[1]; + tmp16[2] = diff16[2]; + tmp16[3] = diff16[3]; + + mb_y = (block8x8 >> 1) << 3; + mb_x = (block8x8 & 0x01) << 3; + + //===== loop over 8x8 blocks ===== + pic_pix_y = currMB->opix_y; + pic_pix_x = currMB->pix_x; + //===== get displaced frame difference ====== + for (j = mb_y; j < 8 + mb_y; j++) + { + for (i = mb_x; i < 8 + mb_x; i++) + { + index = (short) (2 * ((j - mb_y)> 3) + ((i - mb_x)> 3)); + source_pel = p_Vid->pCurImg[pic_pix_y + j][pic_pix_x + i]; + *(tmp16[index])++ = (short) (source_pel - p_RDO->tr4x4->mpr8x8[j][i]); + *tmp64++ = (short) (source_pel - p_RDO->tr8x8->mpr8x8[j][i]); + } + } + + cost4x4 += p_Vid->distortion4x4 (diff16[0], DISTBLK_MAX); + cost4x4 += p_Vid->distortion4x4 (diff16[1], DISTBLK_MAX); + cost4x4 += p_Vid->distortion4x4 (diff16[2], DISTBLK_MAX); + cost4x4 += p_Vid->distortion4x4 (diff16[3], DISTBLK_MAX); + cost8x8 += p_Vid->distortion8x8 (diff64 , DISTBLK_MAX); + } + + return (cost8x8 < cost4x4); + } +} + +/*! +************************************************************************************* +* \brief +* Mode Decision for a macroblock +************************************************************************************* +*/ +void encode_one_macroblock_low (Macroblock *currMB) +{ + Slice *currSlice = currMB->p_Slice; + RDOPTStructure *p_RDO = currSlice->p_RDO; + VideoParameters *p_Vid = currMB->p_Vid; + InputParameters *p_Inp = currMB->p_Inp; + PicMotionParams **motion = p_Vid->enc_picture->mv_info; + + imgpel ***temp_img; // to temp store the Y data for 8x8 transform + + int block, mode, j; + RD_PARAMS enc_mb; + distblk bmcost[5] = {DISTBLK_MAX}; + distblk rd_cost = 0; + distblk cost = 0; + distblk min_cost = DISTBLK_MAX; + distblk cost_direct=0; + distblk cost8x8_direct = 0; + int have_direct=0; + int intra1 = 0; + int temp_cpb = 0; + byte best_transform_flag = (byte) 0; + short islice = (short) (currSlice->slice_type == I_SLICE); + short bslice = (short) (currSlice->slice_type == B_SLICE); + short pslice = (short) ((currSlice->slice_type == P_SLICE) || (currSlice->slice_type == SP_SLICE)); + short intra = (short) (islice || (currSlice->slice_type == SI_SLICE) || (pslice && currMB->mb_y == p_Vid->mb_y_upd && p_Vid->mb_y_upd!=p_Vid->mb_y_intra)); + int lambda_mf[3]; + Block8x8Info *b8x8info = p_Vid->b8x8info; + //int mb_available[3] = { 1, 1, 1}; + + char **ipredmodes = p_Vid->ipredmode; + MotionVector *allmvs = (currSlice->slice_type == I_SLICE || (currSlice->slice_type == SI_SLICE)) ? NULL: &currSlice->all_mv[0][0][0][0][0]; + int ****i4p; //for non-RD-opt. mode + imgpel **mb_pred = currSlice->mb_pred[0]; + + byte tmp_8x8_flag, tmp_no_mbpart; + + BestMode md_best; + Info8x8 best = init_info_8x8_struct(); + + init_md_best(&md_best); + + get_mem3Dpel(&temp_img, 3, MB_BLOCK_SIZE, MB_BLOCK_SIZE); + + intra |= RandomIntra (p_Vid, currMB->mbAddrX); // Forced Pseudo-Random Intra +#if (MVC_EXTENSION_ENABLE) + if(p_Inp->num_of_views==2) + intra |= (currSlice->num_ref_idx_active[LIST_0]==0 && currSlice->num_ref_idx_active[LIST_1]==0); // force intra if no ref available +#endif + + //===== Setup Macroblock encoding parameters ===== + init_enc_mb_params(currMB, &enc_mb, intra); + if (p_Inp->AdaptiveRounding) + { + reset_adaptive_rounding(p_Vid); + } + + if (currSlice->mb_aff_frame_flag) + { + reset_mb_nz_coeff(p_Vid, currMB->mbAddrX); + } + + //===== S T O R E C O D I N G S T A T E ===== + //--------------------------------------------------- + currSlice->store_coding_state (currMB, currSlice->p_RDO->cs_cm); + + if (!intra) + { + //===== set direct motion vectors ===== + currMB->best_mode = 10; // let us set best_mode to an intra mode to avoid possible bug with RDOQ + if (bslice && enc_mb.valid[0]) + { + currSlice->Get_Direct_Motion_Vectors (currMB); + } + + if (p_Inp->CtxAdptLagrangeMult == 1) + { + get_initial_mb16x16_cost(currMB); + } + + //===== MOTION ESTIMATION FOR 16x16, 16x8, 8x16 BLOCKS ===== + for (mode = 1; mode < 4; mode++) + { + best.bipred = 0; + best.mode = (char) mode; + b8x8info->best[mode][0].bipred = 0; + if (enc_mb.valid[mode]) + { + for (cost=0, block=0; block<(mode==1?1:2); block++) + { + update_lambda_costs(currMB, &enc_mb, lambda_mf); + PartitionMotionSearch (currMB, mode, block, lambda_mf); + + //--- set 4x4 block indizes (for getting MV) --- + j = (block==1 && mode==2 ? 2 : 0); + + //--- get cost and reference frame for List 0 prediction --- + bmcost[LIST_0] = DISTBLK_MAX; + list_prediction_cost(currMB, LIST_0, block, mode, &enc_mb, bmcost, best.ref); + + if (bslice) + { + //--- get cost and reference frame for List 1 prediction --- + bmcost[LIST_1] = DISTBLK_MAX; + list_prediction_cost(currMB, LIST_1, block, mode, &enc_mb, bmcost, best.ref); + + // Compute bipredictive cost between best list 0 and best list 1 references + list_prediction_cost(currMB, BI_PRED, block, mode, &enc_mb, bmcost, best.ref); + + // currently Bi predictive ME is only supported for modes 1, 2, 3 and ref 0 + if (is_bipred_enabled(p_Vid, mode)) + { + list_prediction_cost(currMB, BI_PRED_L0, block, mode, &enc_mb, bmcost, 0); + list_prediction_cost(currMB, BI_PRED_L1, block, mode, &enc_mb, bmcost, 0); + } + else + { + bmcost[BI_PRED_L0] = DISTBLK_MAX; + bmcost[BI_PRED_L1] = DISTBLK_MAX; + } + + // Determine prediction list based on mode cost + determine_prediction_list(bmcost, &best, &cost); + } + else // if (bslice) + { + best.pdir = 0; + cost += bmcost[LIST_0]; + } + + assign_enc_picture_params(currMB, mode, &best, 2 * block); + + //----- set reference frame and direction parameters ----- + set_block8x8_info(b8x8info, mode, block, &best); + + //--- set reference frames and motion vectors --- + if (mode>1 && block==0) + currSlice->set_ref_and_motion_vectors (currMB, motion, &best, block); + } // for (block=0; block<(mode==1?1:2); block++) + + currMB->luma_transform_size_8x8_flag = FALSE; + if (p_Inp->Transform8x8Mode) //for inter rd-off, set 8x8 to do 8x8 transform + { + currSlice->set_modes_and_refs_for_blocks(currMB, (short) mode); + currMB->luma_transform_size_8x8_flag = (byte) transform_decision(currMB, -1, &cost); + } + + if (cost < min_cost) + { + currMB->best_mode = (short) mode; + min_cost = cost; + best_transform_flag = currMB->luma_transform_size_8x8_flag; + + if (p_Inp->CtxAdptLagrangeMult == 1) + { + adjust_mb16x16_cost(currMB, cost); + } + } + } // if (enc_mb.valid[mode]) + } // for (mode=1; mode<4; mode++) + + if (enc_mb.valid[P8x8]) + { + //===== store coding state of macroblock ===== + currSlice->store_coding_state (currMB, currSlice->p_RDO->cs_mb); + memset( currSlice->cofAC[0][0][0], 0, 2080 * sizeof(int)); // 4 * 4 * 2 * 65 + + currMB->valid_8x8 = FALSE; + + if (p_Inp->Transform8x8Mode) + { + ResetRD8x8Data(p_Vid, p_RDO->tr8x8); + //=========================================================== + // Check 8x8 partition with transform size 8x8 + //=========================================================== + //===== LOOP OVER 8x8 SUB-PARTITIONS (Motion Estimation & Mode Decision) ===== + for (cost_direct = 0, block = 0; block < 4; block++) + { + submacroblock_mode_decision_low(currMB, &enc_mb, p_RDO->tr8x8, p_RDO->cofAC8x8ts[block], + &have_direct, block, &cost_direct, &cost, &cost8x8_direct, 1); + + set_subblock8x8_info(b8x8info, P8x8, block, p_RDO->tr8x8); + } + + currMB->luma_transform_size_8x8_flag = FALSE; //switch to 4x4 transform size + }// if (p_Inp->Transform8x8Mode) + + + if (p_Inp->Transform8x8Mode != 2) + { + ResetRD8x8Data(p_Vid, p_RDO->tr4x4); + //================================================================= + // Check 8x8, 8x4, 4x8 and 4x4 partitions with transform size 4x4 + //================================================================= + //===== LOOP OVER 8x8 SUB-PARTITIONS (Motion Estimation & Mode Decision) ===== + for (cost_direct = 0, block=0; block<4; block++) + { + submacroblock_mode_decision_low(currMB, &enc_mb, p_RDO->tr4x4, p_RDO->coefAC8x8[block], + &have_direct, block, &cost_direct, &cost, &cost8x8_direct, 0); + + set_subblock8x8_info(b8x8info, P8x8, block, p_RDO->tr4x4); + } + }// if (p_Inp->Transform8x8Mode != 2) + + if (p_Inp->RCEnable) + rc_store_diff(currSlice->diffy, &p_Vid->pCurImg[currMB->opix_y], currMB->pix_x, mb_pred); + + //check cost for P8x8 for non-rdopt mode + if (((p_Inp->Transform8x8Mode < 2) && (p_RDO->tr4x4->mb_p8x8_cost < min_cost)) || + ((p_Inp->Transform8x8Mode > 0) && (p_RDO->tr8x8->mb_p8x8_cost < min_cost))) + { + currMB->best_mode = P8x8; + if (p_Inp->Transform8x8Mode == 2) + { + min_cost = p_RDO->tr8x8->mb_p8x8_cost; + currMB->luma_transform_size_8x8_flag = TRUE; + } + else if (p_Inp->Transform8x8Mode) + { + if (p_RDO->tr8x8->mb_p8x8_cost < p_RDO->tr4x4->mb_p8x8_cost) + { + min_cost = p_RDO->tr8x8->mb_p8x8_cost; + currMB->luma_transform_size_8x8_flag = TRUE; + } + else if(p_RDO->tr4x4->mb_p8x8_cost < p_RDO->tr8x8->mb_p8x8_cost) + { + min_cost = p_RDO->tr4x4->mb_p8x8_cost; + currMB->luma_transform_size_8x8_flag = FALSE; + } + else + { + if (get_best_transform_8x8(currMB) == 0) + { + min_cost = p_RDO->tr4x4->mb_p8x8_cost; + currMB->luma_transform_size_8x8_flag = FALSE; + } + else + { + min_cost = p_RDO->tr8x8->mb_p8x8_cost; + currMB->luma_transform_size_8x8_flag = TRUE; + } + } + } + else + { + min_cost = p_RDO->tr4x4->mb_p8x8_cost; + currMB->luma_transform_size_8x8_flag = FALSE; + } + }// if ((p_RDO->tr4x4->mb_p8x8_cost < min_cost || p_RDO->tr8x8->mb_p8x8_cost < min_cost)) + p_Vid->giRDOpt_B8OnlyFlag = FALSE; + } + + // Find a motion vector for the Skip mode + if(pslice) + FindSkipModeMotionVector (currMB); + } + else // if (!intra) + { + min_cost = DISTBLK_MAX; + } + + + //========= C H O O S E B E S T M A C R O B L O C K M O D E ========= + //------------------------------------------------------------------------- + tmp_8x8_flag = currMB->luma_transform_size_8x8_flag; //save 8x8_flag + tmp_no_mbpart = currMB->NoMbPartLessThan8x8Flag; //save no-part-less + if ((p_Vid->yuv_format != YUV400) && (p_Vid->yuv_format != YUV444)) + // precompute all chroma intra prediction modes + currSlice->intra_chroma_prediction(currMB, NULL, NULL, NULL); + + if (enc_mb.valid[0] && bslice) // check DIRECT MODE + { + if(have_direct) + { + switch(p_Inp->Transform8x8Mode) + { + case 1: // Mixture of 8x8 & 4x4 transform + cost = ((cost8x8_direct < cost_direct) || !(enc_mb.valid[5] && enc_mb.valid[6] && enc_mb.valid[7])) + ? cost8x8_direct : cost_direct; + break; + case 2: // 8x8 Transform only + cost = cost8x8_direct; + break; + default: // 4x4 Transform only + cost = cost_direct; + break; + } + } + else + { //!have_direct + cost = GetDirectCostMB (currMB); + } + if (cost!=DISTBLK_MAX) + { + cost -= weighted_cost(enc_mb.lambda_mdfp, 16); + } + + if (cost <= min_cost) + { + if(p_Vid->active_sps->direct_8x8_inference_flag && p_Inp->Transform8x8Mode) + { + if(p_Inp->Transform8x8Mode==2) + currMB->luma_transform_size_8x8_flag = TRUE; + else + { + if(cost8x8_direct < cost_direct) + currMB->luma_transform_size_8x8_flag = TRUE; + else + currMB->luma_transform_size_8x8_flag = FALSE; + } + } + else + currMB->luma_transform_size_8x8_flag = FALSE; + + //Rate control + if (p_Inp->RCEnable) + rc_store_diff(currSlice->diffy, &p_Vid->pCurImg[currMB->opix_y], currMB->pix_x, mb_pred); + + min_cost = cost; + currMB->best_mode = 0; + tmp_8x8_flag = currMB->luma_transform_size_8x8_flag; + } + else + { + currMB->luma_transform_size_8x8_flag = tmp_8x8_flag; // restore if not best + currMB->NoMbPartLessThan8x8Flag = tmp_no_mbpart; // restore if not best + } + } + currMB->min_rdcost = min_cost; + if (enc_mb.valid[I8MB]) // check INTRA8x8 + { + currMB->luma_transform_size_8x8_flag = TRUE; // at this point cost will ALWAYS be less than min_cost + + currMB->mb_type = currMB->ar_mode = I8MB; + temp_cpb = mode_decision_for_I8x8_MB (currMB, enc_mb.lambda_mdfp, &rd_cost); + + if (rd_cost <= currMB->min_rdcost) //HYU_NOTE. bug fix. 08/15/07 + { + currMB->cbp = temp_cpb; + if (p_Vid->P444_joined) + { + currSlice->curr_cbp[0] = currSlice->cmp_cbp[1]; + currSlice->curr_cbp[1] = currSlice->cmp_cbp[2]; + } + + if(enc_mb.valid[I4MB]) + { + //coeffs + if (p_Inp->Transform8x8Mode != 2) + { + i4p = p_RDO->cofAC; + p_RDO->cofAC = currSlice->cofAC; + currSlice->cofAC = i4p; + } + } + + copy_image_data_16x16(temp_img[0], &p_Vid->enc_picture->imgY[currMB->pix_y], 0, currMB->pix_x); + + if (p_Vid->P444_joined) + { + copy_image_data_16x16(temp_img[1], &p_Vid->enc_picture->imgUV[0][currMB->pix_y], 0, currMB->pix_x); + copy_image_data_16x16(temp_img[2], &p_Vid->enc_picture->imgUV[1][currMB->pix_y], 0, currMB->pix_x); + } + + //Rate control + if (p_Inp->RCEnable) + rc_store_diff(currSlice->diffy, &p_Vid->pCurImg[currMB->opix_y], currMB->pix_x, mb_pred); + + currMB->min_rdcost = rd_cost; + currMB->best_mode = I8MB; + tmp_8x8_flag = currMB->luma_transform_size_8x8_flag; + } + else + { + currMB->luma_transform_size_8x8_flag = tmp_8x8_flag; // restore if not best + if (p_Vid->P444_joined) + { + currMB->cbp |= currSlice->curr_cbp[0]; + currMB->cbp |= currSlice->curr_cbp[1]; + currSlice->cmp_cbp[1] = currMB->cbp; + currSlice->cmp_cbp[2] = currMB->cbp; + } + } + } + + if (enc_mb.valid[I4MB]) // check INTRA4x4 + { + currMB->luma_transform_size_8x8_flag = FALSE; + currMB->mb_type = currMB->ar_mode = I4MB; + temp_cpb = mode_decision_for_I4x4_MB (currMB, enc_mb.lambda_mdfp, &rd_cost); + if (rd_cost <= currMB->min_rdcost) + { + currMB->cbp = temp_cpb; + + //Rate control + if (p_Inp->RCEnable) + rc_store_diff(currSlice->diffy, &p_Vid->pCurImg[currMB->opix_y], currMB->pix_x, mb_pred); + + currMB->min_rdcost = rd_cost; + currMB->best_mode = I4MB; + tmp_8x8_flag = currMB->luma_transform_size_8x8_flag; + } + else + { + currMB->luma_transform_size_8x8_flag = tmp_8x8_flag; // restore if not best + if (p_Vid->P444_joined) + { + currMB->cbp |= currSlice->curr_cbp[0]; + currMB->cbp |= currSlice->curr_cbp[1]; + currSlice->cmp_cbp[1] = currMB->cbp; + currSlice->cmp_cbp[2] = currMB->cbp; + } + //coeffs + i4p = p_RDO->cofAC; + p_RDO->cofAC = currSlice->cofAC; + currSlice->cofAC=i4p; + } + } + if (enc_mb.valid[I16MB]) // check INTRA16x16 + { + rd_cost = find_best_mode_I16x16_MB (currMB, enc_mb.lambda_mdfp, currMB->min_rdcost); + + if (rd_cost < currMB->min_rdcost) + { + //Rate control + if (p_Inp->RCEnable) + rc_store_diff(currSlice->diffy, &p_Vid->pCurImg[currMB->opix_y], currMB->pix_x, currSlice->mpr_16x16[0][(short) currMB->i16mode]); + + currMB->best_mode = I16MB; + currMB->min_rdcost = rd_cost; + currMB->cbp = currMB->residual_transform_quant_luma_16x16 (currMB, PLANE_Y); + + if (p_Vid->P444_joined) + { + select_plane(p_Vid, PLANE_U); + currSlice->cmp_cbp[1] = currMB->residual_transform_quant_luma_16x16(currMB, PLANE_U); + select_plane(p_Vid, PLANE_V); + currSlice->cmp_cbp[2] = currMB->residual_transform_quant_luma_16x16(currMB, PLANE_V); + + select_plane(p_Vid, PLANE_Y); + currMB->cbp |= currSlice->cmp_cbp[1]; + currMB->cbp |= currSlice->cmp_cbp[2]; + currSlice->cmp_cbp[1] = currMB->cbp; + currSlice->cmp_cbp[2] = currMB->cbp; + } + + } + else + { + currMB->luma_transform_size_8x8_flag = tmp_8x8_flag; // restore + currMB->NoMbPartLessThan8x8Flag = tmp_no_mbpart; // restore + } + } + + intra1 = IS_INTRA(currMB); + + //===== S E T F I N A L M A C R O B L O C K P A R A M E T E R S ====== + //--------------------------------------------------------------------------- + { + //===== set parameters for chosen mode ===== + currSlice->set_modes_and_refs_for_blocks (currMB, currMB->best_mode); + + if (currMB->best_mode == P8x8) + { + if (currMB->luma_transform_size_8x8_flag && (p_RDO->tr8x8->cbp8x8 == 0) && p_Inp->Transform8x8Mode != 2) + currMB->luma_transform_size_8x8_flag = FALSE; + + currSlice->set_coeff_and_recon_8x8 (currMB); + + memset(currMB->intra_pred_modes, DC_PRED, MB_BLOCK_PARTITIONS * sizeof(char)); + for (j = currMB->block_y; j < currMB->block_y + BLOCK_MULTIPLE; j++) + memset(&ipredmodes[j][currMB->block_x], DC_PRED, BLOCK_MULTIPLE * sizeof(char)); + } + else + { + //===== set parameters for chosen mode ===== + if (currMB->best_mode == I8MB) + { + memcpy(currMB->intra_pred_modes,currMB->intra_pred_modes8x8, MB_BLOCK_PARTITIONS * sizeof(char)); + for(j = currMB->block_y; j < currMB->block_y + BLOCK_MULTIPLE; j++) + memcpy(&p_Vid->ipredmode[j][currMB->block_x],&p_Vid->ipredmode8x8[j][currMB->block_x], BLOCK_MULTIPLE * sizeof(char)); + + //--- restore reconstruction for 8x8 transform --- + copy_image_data_16x16(&p_Vid->enc_picture->imgY[currMB->pix_y], temp_img[0], currMB->pix_x, 0); + + if (p_Vid->P444_joined) + { + copy_image_data_16x16(&p_Vid->enc_picture->imgUV[0][currMB->pix_y], temp_img[1], currMB->pix_x, 0); + copy_image_data_16x16(&p_Vid->enc_picture->imgUV[1][currMB->pix_y], temp_img[2], currMB->pix_x, 0); + } + } + + if ((currMB->best_mode!=I4MB)&&(currMB->best_mode != I8MB)) + { + memset(currMB->intra_pred_modes,DC_PRED, MB_BLOCK_PARTITIONS * sizeof(char)); + for(j = currMB->block_y; j < currMB->block_y + BLOCK_MULTIPLE; j++) + memset(&ipredmodes[j][currMB->block_x],DC_PRED, BLOCK_MULTIPLE * sizeof(char)); + currMB->ar_mode = currMB->best_mode; + + if (currMB->best_mode!=I16MB) + { + if((currMB->best_mode >= 1) && (currMB->best_mode <= 3)) + currMB->luma_transform_size_8x8_flag = best_transform_flag; + + currSlice->luma_residual_coding(currMB); + if (currSlice->P444_joined) + { + if((currMB->cbp==0 && currSlice->cmp_cbp[1] == 0 && currSlice->cmp_cbp[2] == 0) &&(currMB->best_mode == 0)) + currMB->luma_transform_size_8x8_flag = FALSE; + } + else + { + if((currMB->cbp==0)&&(currMB->best_mode == 0)) + currMB->luma_transform_size_8x8_flag = FALSE; + } + + //Rate control + if (p_Inp->RCEnable) + rc_store_diff(currSlice->diffy, &p_Vid->pCurImg[currMB->opix_y], currMB->pix_x, mb_pred); + } + } + } + //check luma cbp for transform size flag + if (((currMB->cbp&15) == 0) && currMB->mb_type != I4MB && currMB->mb_type != I8MB) + currMB->luma_transform_size_8x8_flag = FALSE; + + // precompute all chroma intra prediction modes + if ((p_Vid->yuv_format != YUV400) && (p_Vid->yuv_format != YUV444)) + currSlice->intra_chroma_prediction(currMB, NULL, NULL, NULL); + + currMB->i16offset = 0; + + if ((p_Vid->yuv_format != YUV400) && (p_Vid->yuv_format != YUV444)) + currSlice->chroma_residual_coding (currMB); + + if (currMB->best_mode == I16MB) + { + currMB->i16offset = I16Offset (currMB->cbp, currMB->i16mode); + } + + currSlice->set_motion_vectors_mb (currMB); + + //===== check for SKIP mode ===== + if(p_Vid->P444_joined) + { + if ((pslice) && currMB->best_mode == 1 && currMB->cbp==0 && currSlice->cmp_cbp[1] == 0 && currSlice->cmp_cbp[2] == 0 && + motion[currMB->block_y][currMB->block_x].ref_idx[LIST_0] == 0 && + motion[currMB->block_y][currMB->block_x].mv [LIST_0].mv_x == allmvs->mv_x && + motion[currMB->block_y][currMB->block_x].mv [LIST_0].mv_y == allmvs->mv_y) + { + currMB->mb_type = currMB->b8x8[0].mode = currMB->b8x8[1].mode = currMB->b8x8[2].mode = currMB->b8x8[3].mode = 0; + currMB->luma_transform_size_8x8_flag = FALSE; + } + } + else if ((pslice) && currMB->best_mode == 1 && currMB->cbp==0 && + motion[currMB->block_y][currMB->block_x].ref_idx[LIST_0] == 0 && + motion[currMB->block_y][currMB->block_x].mv [LIST_0].mv_x == allmvs->mv_x && + motion[currMB->block_y][currMB->block_x].mv [LIST_0].mv_y == allmvs->mv_y) + { + currMB->mb_type = currMB->b8x8[0].mode = currMB->b8x8[1].mode = currMB->b8x8[2].mode = currMB->b8x8[3].mode = 0; + currMB->luma_transform_size_8x8_flag = FALSE; + } + + if (currSlice->mb_aff_frame_flag || (p_Inp->UseRDOQuant && currSlice->RDOQ_QP_Num > 1)) + set_mbaff_parameters(currMB); + } + + // Rate control + if(p_Inp->RCEnable && p_Inp->RCUpdateMode <= MAX_RC_MODE) + rc_store_mad(currMB); + + //===== Decide if this MB will restrict the reference frames ===== + if (p_Inp->RestrictRef) + update_refresh_map(currMB, intra, intra1); + + + /*update adaptive rounding offset p_Inp*/ + if (p_Vid->AdaptiveRounding) + { + update_offset_params(currMB, currMB->best_mode, currMB->luma_transform_size_8x8_flag); + } + + free_mem3Dpel(temp_img); +} + diff --git a/lencod/src/me_distortion.c b/lencod/src/me_distortion.c new file mode 100644 index 0000000..634b8f6 --- /dev/null +++ b/lencod/src/me_distortion.c @@ -0,0 +1,1565 @@ +/*! +************************************************************************************* +* \file me_distortion.c +* +* \brief +* Motion estimation error calculation functions +* +* \author +* Main contributors (see contributors.h for copyright, address and affiliation details) +* - Alexis Michael Tourapis +* - Athanasios Leontaris +* +************************************************************************************* +*/ + +#include "contributors.h" + +#include + +#include "global.h" +#include "image.h" +#include "memalloc.h" +#include "mb_access.h" +#include "refbuf.h" +#include "mv_search.h" +#include "me_distortion.h" + + +//#define CHECKOVERFLOW(mcost) assert(mcost>=0) +#define CHECKOVERFLOW(mcost) + +/*! +*********************************************************************** +* \brief +* Calculate SAD +*********************************************************************** +*/ +distblk distortion4x4SAD(short* diff, distblk min_dist) +{ + int distortion = 0, k; + for (k = 0; k < 16; k++) + { + distortion += iabs(*diff++); + } + return (dist_scale((distblk) distortion)); +} + +/*! +*********************************************************************** +* \brief +* Calculate SSE 4x4 +*********************************************************************** +*/ +distblk distortion4x4SSE(short* diff, distblk min_dist) +{ + int distortion = 0, k; + for (k = 0; k < 16; k++) + { + distortion += iabs2(*diff++); + } + return (dist_scale((distblk) distortion)); +} + +/*! +*********************************************************************** +* \brief +* Calculate SATD +*********************************************************************** +*/ +distblk distortion4x4SATD(short* diff, distblk min_dist) +{ + distblk i64Ret = HadamardSAD4x4( diff ); + return (dist_scale(i64Ret)); +} + +/*! +*********************************************************************** +* \brief +* Calculate SAD for 8x8 (with a threshold) +*********************************************************************** +*/ +distblk distortion8x8SADthres(short* diff, distblk min_cost) +{ + int distortion = 0; + int i, j; + int imin_cost = dist_down(min_cost); + + for (j = 0; j < 8; j++) + { + for (i = 0; i < 8; i++) + { + distortion += iabs(*diff++); + } + if (distortion > imin_cost) + break; + } + + return (dist_scale((distblk) distortion)); +} + +/*! +*********************************************************************** +* \brief +* Calculate SAD for 8x8 +*********************************************************************** +*/ +distblk distortion8x8SAD(short* diff, distblk min_dist) +{ + int distortion = 0; + int k; + + for (k = 0; k < 64; k++) + { + distortion += iabs(*diff++); + } + return (dist_scale((distblk) distortion)); +} + +/*! +*********************************************************************** +* \brief +* Calculate SSE for 8x8 +*********************************************************************** +*/ +distblk distortion8x8SSE(short* diff, distblk min_dist) +{ + distblk distortion = 0; + int k; + for (k = 0; k < 64; k++) + { + distortion += iabs2(*diff++); + } + return (dist_scale(distortion)); +} + +/*! +*********************************************************************** +* \brief +* Calculate SATD for 8x8 +*********************************************************************** +*/ +distblk distortion8x8SATD(short* diff, distblk min_dist) +{ + distblk i64Ret = HadamardSAD8x8( diff ); + return (dist_scale(i64Ret)); +} + +void select_distortion(VideoParameters *p_Vid, InputParameters *p_Inp) +{ + switch(p_Inp->ModeDecisionMetric) + { + case ERROR_SAD: + p_Vid->distortion4x4 = distortion4x4SAD; + p_Vid->distortion8x8 = distortion8x8SAD; + break; + case ERROR_SSE: + p_Vid->distortion4x4 = distortion4x4SSE; + p_Vid->distortion8x8 = distortion8x8SSE; + break; + case ERROR_SATD : + default: + p_Vid->distortion4x4 = distortion4x4SATD; + p_Vid->distortion8x8 = distortion8x8SATD; + break; + } +} + + +/*! +*********************************************************************** +* \brief +* Calculate 4x4 Hadamard-Transformed SAD +*********************************************************************** +*/ +int HadamardSAD4x4 (short* diff) +{ + int k, satd = 0; + int m[16], d[16]; + + /*===== hadamard transform =====*/ + m[ 0] = diff[ 0] + diff[12]; + m[ 1] = diff[ 1] + diff[13]; + m[ 2] = diff[ 2] + diff[14]; + m[ 3] = diff[ 3] + diff[15]; + m[ 4] = diff[ 4] + diff[ 8]; + m[ 5] = diff[ 5] + diff[ 9]; + m[ 6] = diff[ 6] + diff[10]; + m[ 7] = diff[ 7] + diff[11]; + m[ 8] = diff[ 4] - diff[ 8]; + m[ 9] = diff[ 5] - diff[ 9]; + m[10] = diff[ 6] - diff[10]; + m[11] = diff[ 7] - diff[11]; + m[12] = diff[ 0] - diff[12]; + m[13] = diff[ 1] - diff[13]; + m[14] = diff[ 2] - diff[14]; + m[15] = diff[ 3] - diff[15]; + + d[ 0] = m[ 0] + m[ 4]; + d[ 1] = m[ 1] + m[ 5]; + d[ 2] = m[ 2] + m[ 6]; + d[ 3] = m[ 3] + m[ 7]; + d[ 4] = m[ 8] + m[12]; + d[ 5] = m[ 9] + m[13]; + d[ 6] = m[10] + m[14]; + d[ 7] = m[11] + m[15]; + d[ 8] = m[ 0] - m[ 4]; + d[ 9] = m[ 1] - m[ 5]; + d[10] = m[ 2] - m[ 6]; + d[11] = m[ 3] - m[ 7]; + d[12] = m[12] - m[ 8]; + d[13] = m[13] - m[ 9]; + d[14] = m[14] - m[10]; + d[15] = m[15] - m[11]; + + m[ 0] = d[ 0] + d[ 3]; + m[ 1] = d[ 1] + d[ 2]; + m[ 2] = d[ 1] - d[ 2]; + m[ 3] = d[ 0] - d[ 3]; + m[ 4] = d[ 4] + d[ 7]; + m[ 5] = d[ 5] + d[ 6]; + m[ 6] = d[ 5] - d[ 6]; + m[ 7] = d[ 4] - d[ 7]; + m[ 8] = d[ 8] + d[11]; + m[ 9] = d[ 9] + d[10]; + m[10] = d[ 9] - d[10]; + m[11] = d[ 8] - d[11]; + m[12] = d[12] + d[15]; + m[13] = d[13] + d[14]; + m[14] = d[13] - d[14]; + m[15] = d[12] - d[15]; + + d[ 0] = m[ 0] + m[ 1]; + d[ 1] = m[ 0] - m[ 1]; + d[ 2] = m[ 2] + m[ 3]; + d[ 3] = m[ 3] - m[ 2]; + d[ 4] = m[ 4] + m[ 5]; + d[ 5] = m[ 4] - m[ 5]; + d[ 6] = m[ 6] + m[ 7]; + d[ 7] = m[ 7] - m[ 6]; + d[ 8] = m[ 8] + m[ 9]; + d[ 9] = m[ 8] - m[ 9]; + d[10] = m[10] + m[11]; + d[11] = m[11] - m[10]; + d[12] = m[12] + m[13]; + d[13] = m[12] - m[13]; + d[14] = m[14] + m[15]; + d[15] = m[15] - m[14]; + + //===== sum up ===== + // Table lookup is faster than abs macro + for (k=0; k<16; ++k) + { + satd += iabs(d [k]); + } + + + return ((satd+1)>>1); +} + +/*! +*********************************************************************** +* \brief +* Calculate 8x8 Hadamard-Transformed SAD +*********************************************************************** +*/ +int HadamardSAD8x8 (short* diff) +{ + int i, j, jj, sad=0; + + // Hadamard related arrays + int m1[8][8], m2[8][8], m3[8][8]; + + + //horizontal + for (j=0; j < 8; j++) + { + jj = j << 3; + m2[j][0] = diff[jj ] + diff[jj+4]; + m2[j][1] = diff[jj+1] + diff[jj+5]; + m2[j][2] = diff[jj+2] + diff[jj+6]; + m2[j][3] = diff[jj+3] + diff[jj+7]; + m2[j][4] = diff[jj ] - diff[jj+4]; + m2[j][5] = diff[jj+1] - diff[jj+5]; + m2[j][6] = diff[jj+2] - diff[jj+6]; + m2[j][7] = diff[jj+3] - diff[jj+7]; + + m1[j][0] = m2[j][0] + m2[j][2]; + m1[j][1] = m2[j][1] + m2[j][3]; + m1[j][2] = m2[j][0] - m2[j][2]; + m1[j][3] = m2[j][1] - m2[j][3]; + m1[j][4] = m2[j][4] + m2[j][6]; + m1[j][5] = m2[j][5] + m2[j][7]; + m1[j][6] = m2[j][4] - m2[j][6]; + m1[j][7] = m2[j][5] - m2[j][7]; + + m2[j][0] = m1[j][0] + m1[j][1]; + m2[j][1] = m1[j][0] - m1[j][1]; + m2[j][2] = m1[j][2] + m1[j][3]; + m2[j][3] = m1[j][2] - m1[j][3]; + m2[j][4] = m1[j][4] + m1[j][5]; + m2[j][5] = m1[j][4] - m1[j][5]; + m2[j][6] = m1[j][6] + m1[j][7]; + m2[j][7] = m1[j][6] - m1[j][7]; + } + + //vertical + for (i=0; i < 8; i++) + { + m3[0][i] = m2[0][i] + m2[4][i]; + m3[1][i] = m2[1][i] + m2[5][i]; + m3[2][i] = m2[2][i] + m2[6][i]; + m3[3][i] = m2[3][i] + m2[7][i]; + m3[4][i] = m2[0][i] - m2[4][i]; + m3[5][i] = m2[1][i] - m2[5][i]; + m3[6][i] = m2[2][i] - m2[6][i]; + m3[7][i] = m2[3][i] - m2[7][i]; + + m1[0][i] = m3[0][i] + m3[2][i]; + m1[1][i] = m3[1][i] + m3[3][i]; + m1[2][i] = m3[0][i] - m3[2][i]; + m1[3][i] = m3[1][i] - m3[3][i]; + m1[4][i] = m3[4][i] + m3[6][i]; + m1[5][i] = m3[5][i] + m3[7][i]; + m1[6][i] = m3[4][i] - m3[6][i]; + m1[7][i] = m3[5][i] - m3[7][i]; + + m2[0][i] = m1[0][i] + m1[1][i]; + m2[1][i] = m1[0][i] - m1[1][i]; + m2[2][i] = m1[2][i] + m1[3][i]; + m2[3][i] = m1[2][i] - m1[3][i]; + m2[4][i] = m1[4][i] + m1[5][i]; + m2[5][i] = m1[4][i] - m1[5][i]; + m2[6][i] = m1[6][i] + m1[7][i]; + m2[7][i] = m1[6][i] - m1[7][i]; + } + for (j=0; j < 8; j++) + for (i=0; i < 8; i++) + sad += iabs (m2[j][i]); + + return ((sad+2)>>2); +} + +/*! +************************************************************************ +* \brief +* SAD computation +************************************************************************ +*/ +distblk computeSAD(StorablePicture *ref1, + MEBlock *mv_block, + distblk min_mcost, + MotionVector *cand) +{ + int mcost = 0; + int imin_cost = dist_down(min_mcost); + int y,x; + short blocksize_x = mv_block->blocksize_x; + short blocksize_y = mv_block->blocksize_y; + VideoParameters *p_Vid = mv_block->p_Vid; + int pad_size_x = p_Vid->padded_size_x - blocksize_x; +#if (JM_MEM_DISTORTION) + int *imgpel_abs = p_Vid->imgpel_abs; +#endif + + imgpel *src_line, *ref_line; + src_line = mv_block->orig_pic[0]; + ref_line = UMVLine4X (ref1, cand->mv_y, cand->mv_x); + for (y=0; y imin_cost) + return (dist_scale_f((distblk)mcost)); + ref_line += pad_size_x; + } + if ( mv_block->ChromaMEEnable ) + { + // calculate chroma conribution to motion compensation error + int blocksize_x_cr = mv_block->blocksize_cr_x; + int blocksize_y_cr = mv_block->blocksize_cr_y; + int cr_pad_size_x = p_Vid->cr_padded_size_x - blocksize_x_cr; + int k; + int mcr_cost = 0; // chroma me cost + + for (k=0; k < 2; k++) + { + src_line = mv_block->orig_pic[k+1]; + ref_line = UMVLine8X_chroma ( ref1, k+1, cand->mv_y, cand->mv_x); + mcr_cost = 0; + + for (y = 0; y < blocksize_y_cr; y++) + { + for (x = 0; x < blocksize_x_cr; x += 2) + { +#if (JM_MEM_DISTORTION) + mcr_cost += imgpel_abs[ *src_line++ - *ref_line++ ]; + mcr_cost += imgpel_abs[ *src_line++ - *ref_line++ ]; +#else + mcr_cost += iabs( *src_line++ - *ref_line++ ); + mcr_cost += iabs( *src_line++ - *ref_line++ ); +#endif + } + ref_line += cr_pad_size_x; + } + mcost += mv_block->ChromaMEWeight * mcr_cost; + + if(mcost >imin_cost) + return (dist_scale_f((distblk)mcost)); + } + } + + CHECKOVERFLOW(mcost); + return (dist_scale((distblk)mcost)); +} + +/*! +************************************************************************ +* \brief +* SAD computation for weighted samples +************************************************************************ +*/ +distblk computeSADWP(StorablePicture *ref1, + MEBlock *mv_block, + distblk min_mcost, + MotionVector *cand + ) +{ + int mcost = 0; + int imin_cost = dist_down(min_mcost); + int y, x; + int weighted_pel; + short blocksize_x = mv_block->blocksize_x; + short blocksize_y = mv_block->blocksize_y; + + VideoParameters *p_Vid = mv_block->p_Vid; + Slice *currSlice = mv_block->p_Slice; + int pad_size_x = p_Vid->padded_size_x - blocksize_x; + int max_imgpel_value = p_Vid->max_imgpel_value; + short weight = mv_block->weight_luma; + short offset = mv_block->offset_luma; + + int wp_luma_round = currSlice->wp_luma_round; + short luma_log_weight_denom = currSlice->luma_log_weight_denom; + + imgpel *src_line = mv_block->orig_pic[0]; + imgpel *ref_line = UMVLine4X (ref1, cand->mv_y, cand->mv_x); + + for (y=0; y> luma_log_weight_denom) + offset); + mcost += iabs( *src_line++ - weighted_pel ); + weighted_pel = iClip1( max_imgpel_value, ((weight * *ref_line++ + wp_luma_round) >> luma_log_weight_denom) + offset); + mcost += iabs( *src_line++ - weighted_pel ); + weighted_pel = iClip1( max_imgpel_value, ((weight * *ref_line++ + wp_luma_round) >> luma_log_weight_denom) + offset); + mcost += iabs( *src_line++ - weighted_pel ); + weighted_pel = iClip1( max_imgpel_value, ((weight * *ref_line++ + wp_luma_round) >> luma_log_weight_denom) + offset); + mcost += iabs( *src_line++ - weighted_pel ); + } + if(mcost > imin_cost) + return (dist_scale_f((distblk)mcost)); + ref_line += pad_size_x; + } + if ( mv_block->ChromaMEEnable ) + { + // calculate chroma conribution to motion compensation error + int blocksize_x_cr = mv_block->blocksize_cr_x; + int blocksize_y_cr = mv_block->blocksize_cr_y; + int cr_pad_size_x = p_Vid->cr_padded_size_x - blocksize_x_cr; + int k; + int mcr_cost = 0; + int max_imgpel_value_uv = p_Vid->max_pel_value_comp[1]; + int wp_chroma_round = currSlice->wp_chroma_round; + short chroma_log_weight_denom = currSlice->chroma_log_weight_denom; + + for (k=0; k < 2; k++) + { + weight = mv_block->weight_cr[k]; + offset = mv_block->offset_cr[k]; + + mcr_cost = 0; + src_line = mv_block->orig_pic[k+1]; + ref_line = UMVLine8X_chroma ( ref1, k+1, cand->mv_y, cand->mv_x); + for (y=0; y> chroma_log_weight_denom) + offset); + mcr_cost += iabs( *src_line++ - weighted_pel ); + weighted_pel = iClip1( max_imgpel_value_uv, ((weight * *ref_line++ + wp_chroma_round) >> chroma_log_weight_denom) + offset); + mcr_cost += iabs( *src_line++ - weighted_pel ); + } + ref_line += cr_pad_size_x; + } + mcost += mv_block->ChromaMEWeight * mcr_cost; + + if(mcost >imin_cost) + return (dist_scale_f((distblk)mcost)); + } + } + + CHECKOVERFLOW(mcost); + return (dist_scale((distblk)mcost)); +} + +/*! +************************************************************************ +* \brief +* BiPred SAD computation (no weights) +************************************************************************ +*/ +distblk computeBiPredSAD1(StorablePicture *ref1, + StorablePicture *ref2, + MEBlock *mv_block, + distblk min_mcost, + MotionVector *cand1, + MotionVector *cand2) +{ + int imin_cost = dist_down(min_mcost); + int mcost = 0; + int bi_diff; + int y,x; + short blocksize_x = mv_block->blocksize_x; + short blocksize_y = mv_block->blocksize_y; + VideoParameters *p_Vid = mv_block->p_Vid; + int pad_size_x = p_Vid->padded_size_x - blocksize_x; +#if (JM_MEM_DISTORTION) + int *imgpel_abs = p_Vid->imgpel_abs; +#endif + + imgpel *src_line = mv_block->orig_pic[0]; + imgpel *ref2_line = UMVLine4X(ref2, cand2->mv_y, cand2->mv_x); + imgpel *ref1_line = UMVLine4X(ref1, cand1->mv_y, cand1->mv_x); + + for (y = 0; y < blocksize_y; y++) + { + for (x = 0; x < blocksize_x; x+=4) + { +#if (JM_MEM_DISTORTION) + bi_diff = (*src_line++) - ((*ref1_line++ + *ref2_line++ + 1)>>1); + mcost += imgpel_abs[ bi_diff ]; + bi_diff = (*src_line++) - ((*ref1_line++ + *ref2_line++ + 1)>>1); + mcost += imgpel_abs[ bi_diff ]; + bi_diff = (*src_line++) - ((*ref1_line++ + *ref2_line++ + 1)>>1); + mcost += imgpel_abs[ bi_diff ]; + bi_diff = (*src_line++) - ((*ref1_line++ + *ref2_line++ + 1)>>1); + mcost += imgpel_abs[ bi_diff ]; +#else + bi_diff = (*src_line++) - ((*ref1_line++ + *ref2_line++ + 1)>>1); + mcost += iabs(bi_diff); + bi_diff = (*src_line++) - ((*ref1_line++ + *ref2_line++ + 1)>>1); + mcost += iabs(bi_diff); + bi_diff = (*src_line++) - ((*ref1_line++ + *ref2_line++ + 1)>>1); + mcost += iabs(bi_diff); + bi_diff = (*src_line++) - ((*ref1_line++ + *ref2_line++ + 1)>>1); + mcost += iabs(bi_diff); +#endif + } + if(mcost > imin_cost) + return (dist_scale_f((distblk)mcost)); + + ref2_line += pad_size_x; + ref1_line += pad_size_x; + } + + if ( mv_block->ChromaMEEnable ) + { + // calculate chroma conribution to motion compensation error + int blocksize_x_cr = mv_block->blocksize_cr_x; + int blocksize_y_cr = mv_block->blocksize_cr_y; + int cr_pad_size_x = p_Vid->cr_padded_size_x - blocksize_x_cr; + int k; + int mcr_cost = 0; + + for (k=1; k<3; k++) + { + mcr_cost = 0; + src_line = mv_block->orig_pic[k]; + ref2_line = UMVLine8X_chroma ( ref2, k, cand2->mv_y, cand2->mv_x); + ref1_line = UMVLine8X_chroma ( ref1, k, cand1->mv_y, cand1->mv_x); + + for (y=0; y>1); + mcr_cost += iabs(bi_diff); + bi_diff = (*src_line++) - ((*ref1_line++ + *ref2_line++ + 1)>>1); + mcr_cost += iabs(bi_diff); + } + ref2_line += cr_pad_size_x; + ref1_line += cr_pad_size_x; + } + mcost += mv_block->ChromaMEWeight * mcr_cost; + + if(mcost > imin_cost) + return (dist_scale_f((distblk)mcost)); + } + } + + CHECKOVERFLOW(mcost); + return (dist_scale((distblk)mcost)); +} + +/*! +************************************************************************ +* \brief +* BiPred SAD computation (with weights) +************************************************************************ +*/ +distblk computeBiPredSAD2(StorablePicture *ref1, + StorablePicture *ref2, + MEBlock *mv_block, + distblk min_mcost, + MotionVector *cand1, + MotionVector *cand2) +{ + int imin_cost = dist_down(min_mcost); + int mcost = 0; + int bi_diff; + VideoParameters *p_Vid = mv_block->p_Vid; + Slice *currSlice = mv_block->p_Slice; + int denom = currSlice->luma_log_weight_denom + 1; + int lround = 2 * currSlice->wp_luma_round; + int max_imgpel_value = p_Vid->max_imgpel_value; + int y,x; + int weighted_pel, pixel1, pixel2; + short blocksize_x = mv_block->blocksize_x; + short blocksize_y = mv_block->blocksize_y; + short weight1 = mv_block->weight1; + short weight2 = mv_block->weight2; + short offsetBi = mv_block->offsetBi; + + int pad_size_x = p_Vid->padded_size_x - blocksize_x; + + imgpel *src_line = mv_block->orig_pic[0]; + imgpel *ref2_line = UMVLine4X(ref2, cand2->mv_y, cand2->mv_x); + imgpel *ref1_line = UMVLine4X(ref1, cand1->mv_y, cand1->mv_x); + + for (y=0; y> denom) + offsetBi); + bi_diff = (*src_line++) - weighted_pel; + mcost += iabs(bi_diff); + + pixel1 = weight1 * (*ref1_line++); + pixel2 = weight2 * (*ref2_line++); + weighted_pel = iClip1( max_imgpel_value, ((pixel1 + pixel2 + lround) >> denom) + offsetBi); + bi_diff = (*src_line++) - weighted_pel; + mcost += iabs(bi_diff); + + pixel1 = weight1 * (*ref1_line++); + pixel2 = weight2 * (*ref2_line++); + weighted_pel = iClip1( max_imgpel_value, ((pixel1 + pixel2 + lround) >> denom) + offsetBi); + bi_diff = (*src_line++) - weighted_pel; + mcost += iabs(bi_diff); + + pixel1 = weight1 * (*ref1_line++); + pixel2 = weight2 * (*ref2_line++); + weighted_pel = iClip1( max_imgpel_value, ((pixel1 + pixel2 + lround) >> denom) + offsetBi); + bi_diff = (*src_line++) - weighted_pel; + mcost += iabs(bi_diff); + } + + if(mcost > imin_cost) + return dist_scale_f((distblk)mcost); + ref2_line += pad_size_x; + ref1_line += pad_size_x; + } + + if ( mv_block->ChromaMEEnable ) + { + // calculate chroma conribution to motion compensation error + int blocksize_x_cr = mv_block->blocksize_cr_x; + int blocksize_y_cr = mv_block->blocksize_cr_y; + int cr_pad_size_x = p_Vid->cr_padded_size_x - blocksize_x_cr; + int k; + int mcr_cost = 0; + int max_imgpel_value_uv = p_Vid->max_pel_value_comp[1]; + + for (k=0; k<2; k++) + { + weight1 = mv_block->weight1_cr[k]; + weight2 = mv_block->weight2_cr[k]; + offsetBi = mv_block->offsetBi_cr[k]; + + mcr_cost = 0; + src_line = mv_block->orig_pic[k+1]; + ref2_line = UMVLine8X_chroma ( ref2, k+1, cand2->mv_y, cand2->mv_x); + ref1_line = UMVLine8X_chroma ( ref1, k+1, cand1->mv_y, cand1->mv_x); + + for (y=0; y> denom) + offsetBi); + bi_diff = (*src_line++) - weighted_pel; + mcr_cost += iabs(bi_diff); + + pixel1 = weight1 * (*ref1_line++); + pixel2 = weight2 * (*ref2_line++); + weighted_pel = iClip1( max_imgpel_value_uv, ((pixel1 + pixel2 + lround) >> denom) + offsetBi); + bi_diff = (*src_line++) - weighted_pel; + mcr_cost += iabs(bi_diff); + } + ref2_line += cr_pad_size_x; + ref1_line += cr_pad_size_x; + } + mcost += mv_block->ChromaMEWeight * mcr_cost; + + if(mcost > imin_cost) + return dist_scale_f((distblk)mcost); + } + } + + CHECKOVERFLOW(mcost); + return dist_scale((distblk)mcost); +} + +/*! +************************************************************************ +* \brief +* SAD computation _with_ Hadamard Transform +************************************************************************ +*/ +distblk computeSATD(StorablePicture *ref1, + MEBlock *mv_block, + distblk min_mcost, + MotionVector *cand + ) +{ + int imin_cost = dist_down(min_mcost); + int mcost = 0; + int y, x, y4; + int src_size_x, src_size_mul; + short blocksize_x = mv_block->blocksize_x; + short blocksize_y = mv_block->blocksize_y; + VideoParameters *p_Vid = mv_block->p_Vid; + imgpel *src_tmp = mv_block->orig_pic[0]; + short *d, diff[MB_PIXELS]; + imgpel *src_line, *ref_line; + + if ( !mv_block->test8x8 ) + { // 4x4 TRANSFORM + src_size_x = blocksize_x - BLOCK_SIZE; + src_size_mul = blocksize_x * BLOCK_SIZE; + for (y = cand->mv_y; y < cand->mv_y + (blocksize_y<<2); y += (BLOCK_SIZE_SP)) + { + for (x=0; xmv_x + (x<<2)); + src_line = src_tmp + x; + for (y4 = 0; y4 < BLOCK_SIZE; y4++ ) + { + *d++ = *src_line++ - *ref_line++ ; + *d++ = *src_line++ - *ref_line++ ; + *d++ = *src_line++ - *ref_line++ ; + *d++ = *src_line++ - *ref_line++ ; + + ref_line += p_Vid->padded_size_x_m4x4; + src_line += src_size_x; + } + mcost += HadamardSAD4x4 (diff); + if(mcost > imin_cost) + return dist_scale_f((distblk)mcost); + } + src_tmp += src_size_mul; + } + } + else + { // 8x8 TRANSFORM + src_size_x = (blocksize_x - BLOCK_SIZE_8x8); + src_size_mul = blocksize_x * BLOCK_SIZE_8x8; + for (y = cand->mv_y; y < cand->mv_y + (blocksize_y<<2); y += (BLOCK_SIZE_8x8_SP) ) + { + for (x=0; xmv_x + (x<<2)); + src_line = src_tmp + x; + for (y4 = 0; y4 < BLOCK_SIZE_8x8; y4++ ) + { + *d++ = *src_line++ - *ref_line++ ; + *d++ = *src_line++ - *ref_line++ ; + *d++ = *src_line++ - *ref_line++ ; + *d++ = *src_line++ - *ref_line++ ; + *d++ = *src_line++ - *ref_line++ ; + *d++ = *src_line++ - *ref_line++ ; + *d++ = *src_line++ - *ref_line++ ; + *d++ = *src_line++ - *ref_line++ ; + + ref_line += p_Vid->padded_size_x_m8x8; + src_line += src_size_x; + } + mcost += HadamardSAD8x8 (diff); + if(mcost > imin_cost) + return dist_scale_f((distblk)mcost); + } + src_tmp += src_size_mul; + } + } + + CHECKOVERFLOW(mcost); + return dist_scale((distblk)mcost); +} + +/*! +************************************************************************ +* \brief +* SAD computation of weighted samples _with_ Hadamard Transform +************************************************************************ +*/ +distblk computeSATDWP(StorablePicture *ref1, + MEBlock *mv_block, + distblk min_mcost, + MotionVector *cand + ) +{ + int imin_cost = dist_down(min_mcost); + int mcost = 0; + int y, x, y4; + int weighted_pel; + int src_size_x, src_size_mul; + short blocksize_x = mv_block->blocksize_x; + short blocksize_y = mv_block->blocksize_y; + + imgpel *src_tmp = mv_block->orig_pic[0]; + VideoParameters *p_Vid = mv_block->p_Vid; + Slice *currSlice = mv_block->p_Slice; + short luma_log_weight_denom = currSlice->luma_log_weight_denom; + short weight = mv_block->weight_luma; + short offset = mv_block->offset_luma; + + int wp_luma_round = currSlice->wp_luma_round; + int max_imgpel_value = p_Vid->max_imgpel_value; + short *d, diff[MB_PIXELS]; + imgpel *src_line, *ref_line; + + if ( !mv_block->test8x8 ) + { // 4x4 TRANSFORM + src_size_x = (blocksize_x - BLOCK_SIZE); + src_size_mul = blocksize_x * BLOCK_SIZE; + for (y = cand->mv_y; y < cand->mv_y + (blocksize_y<<2); y += (BLOCK_SIZE_SP)) + { + for (x=0; xmv_x + (x<<2)); + src_line = src_tmp + x; + for (y4 = 0; y4 < BLOCK_SIZE; y4++ ) + { + weighted_pel = iClip1( max_imgpel_value, ((weight * *ref_line++ + wp_luma_round) >> luma_log_weight_denom) + offset); + *d++ = (short) (*src_line++ - weighted_pel); + weighted_pel = iClip1( max_imgpel_value, ((weight * *ref_line++ + wp_luma_round) >> luma_log_weight_denom) + offset); + *d++ = (short) (*src_line++ - weighted_pel); + weighted_pel = iClip1( max_imgpel_value, ((weight * *ref_line++ + wp_luma_round) >> luma_log_weight_denom) + offset); + *d++ = (short) (*src_line++ - weighted_pel); + weighted_pel = iClip1( max_imgpel_value, ((weight * *ref_line++ + wp_luma_round) >> luma_log_weight_denom) + offset); + *d++ = (short) (*src_line++ - weighted_pel); + + ref_line += p_Vid->padded_size_x_m4x4; + src_line += src_size_x; + } + mcost += HadamardSAD4x4 (diff); + + if(mcost > imin_cost) + return dist_scale_f((distblk)mcost); + } + src_tmp += src_size_mul; + } + } + else + { // 8x8 TRANSFORM + src_size_x = (blocksize_x - BLOCK_SIZE_8x8); + src_size_mul = blocksize_x * BLOCK_SIZE_8x8; + for (y = cand->mv_y; y < cand->mv_y + (blocksize_y<<2); y += (BLOCK_SIZE_8x8_SP) ) + { + for (x=0; xmv_x + (x<<2)); + src_line = src_tmp + x; + for (y4 = 0; y4 < BLOCK_SIZE_8x8; y4++ ) + { + weighted_pel = iClip1( max_imgpel_value, ((weight * *ref_line++ + wp_luma_round) >> luma_log_weight_denom) + offset); + *d++ = (short) (*src_line++ - weighted_pel); + weighted_pel = iClip1( max_imgpel_value, ((weight * *ref_line++ + wp_luma_round) >> luma_log_weight_denom) + offset); + *d++ = (short) (*src_line++ - weighted_pel); + weighted_pel = iClip1( max_imgpel_value, ((weight * *ref_line++ + wp_luma_round) >> luma_log_weight_denom) + offset); + *d++ = (short) (*src_line++ - weighted_pel); + weighted_pel = iClip1( max_imgpel_value, ((weight * *ref_line++ + wp_luma_round) >> luma_log_weight_denom) + offset); + *d++ = (short) (*src_line++ - weighted_pel); + weighted_pel = iClip1( max_imgpel_value, ((weight * *ref_line++ + wp_luma_round) >> luma_log_weight_denom) + offset); + *d++ = (short) (*src_line++ - weighted_pel); + weighted_pel = iClip1( max_imgpel_value, ((weight * *ref_line++ + wp_luma_round) >> luma_log_weight_denom) + offset); + *d++ = (short) (*src_line++ - weighted_pel); + weighted_pel = iClip1( max_imgpel_value, ((weight * *ref_line++ + wp_luma_round) >> luma_log_weight_denom) + offset); + *d++ = (short) (*src_line++ - weighted_pel); + weighted_pel = iClip1( max_imgpel_value, ((weight * *ref_line++ + wp_luma_round) >> luma_log_weight_denom) + offset); + *d++ = (short) (*src_line++ - weighted_pel); + + ref_line += p_Vid->padded_size_x_m8x8; + src_line += src_size_x; + } + mcost += HadamardSAD8x8 (diff); + if(mcost > imin_cost) + return dist_scale_f((distblk)mcost); + } + src_tmp += src_size_mul; + } + } + + CHECKOVERFLOW(mcost); + return dist_scale((distblk)mcost); +} + +/*! +************************************************************************ +* \brief +* BiPred (w/o weights) SATD computation +************************************************************************ +*/ +distblk computeBiPredSATD1(StorablePicture *ref1, + StorablePicture *ref2, + MEBlock *mv_block, + distblk min_mcost, + MotionVector *cand1, + MotionVector *cand2) +{ + int imin_cost = dist_down(min_mcost); + int mcost = 0; + int y, x, y4; + int src_size_x, src_size_mul; + imgpel *src_tmp = mv_block->orig_pic[0]; + short *d, diff[MB_PIXELS]; + imgpel *src_line, *ref1_line, *ref2_line; + short blocksize_x = mv_block->blocksize_x; + short blocksize_y = mv_block->blocksize_y; + VideoParameters *p_Vid = mv_block->p_Vid; + + if ( !mv_block->test8x8 ) + { // 4x4 TRANSFORM + src_size_x = (blocksize_x - BLOCK_SIZE); + src_size_mul = blocksize_x * BLOCK_SIZE; + for (y=0; y<(blocksize_y<<2); y += (BLOCK_SIZE_SP)) + { + for (x=0; xmv_y + y, cand2->mv_x + (x<<2)); + ref1_line = UMVLine4X(ref1, cand1->mv_y + y, cand1->mv_x + (x<<2)); + for (y4 = 0; y4 < BLOCK_SIZE; y4++ ) + { + *d++ = (*src_line++) - ((*ref1_line++ + *ref2_line++ + 1)>>1); + *d++ = (*src_line++) - ((*ref1_line++ + *ref2_line++ + 1)>>1); + *d++ = (*src_line++) - ((*ref1_line++ + *ref2_line++ + 1)>>1); + *d++ = (*src_line++) - ((*ref1_line++ + *ref2_line++ + 1)>>1); + + ref1_line += p_Vid->padded_size_x_m4x4; + ref2_line += p_Vid->padded_size_x_m4x4; + src_line += src_size_x; + } + mcost += HadamardSAD4x4 (diff); + if(mcost > imin_cost) + return dist_scale_f((distblk)mcost); + } + src_tmp += src_size_mul; + } + } + else + { // 8x8 TRANSFORM + src_size_x = (blocksize_x - BLOCK_SIZE_8x8); + src_size_mul = blocksize_x * BLOCK_SIZE_8x8; + for (y=0; y<(blocksize_y << 2); y += BLOCK_SIZE_8x8_SP ) + { + int y_pos2 = cand2->mv_y + y; + int y_pos1 = cand1->mv_y + y; + for (x=0; xmv_x + (x<<2)); + ref1_line = UMVLine4X(ref1, y_pos1, cand1->mv_x + (x<<2)); + for (y4 = 0; y4 < BLOCK_SIZE_8x8; y4++ ) + { + *d++ = (*src_line++) - ((*ref1_line++ + *ref2_line++ + 1)>>1); + *d++ = (*src_line++) - ((*ref1_line++ + *ref2_line++ + 1)>>1); + *d++ = (*src_line++) - ((*ref1_line++ + *ref2_line++ + 1)>>1); + *d++ = (*src_line++) - ((*ref1_line++ + *ref2_line++ + 1)>>1); + *d++ = (*src_line++) - ((*ref1_line++ + *ref2_line++ + 1)>>1); + *d++ = (*src_line++) - ((*ref1_line++ + *ref2_line++ + 1)>>1); + *d++ = (*src_line++) - ((*ref1_line++ + *ref2_line++ + 1)>>1); + *d++ = (*src_line++) - ((*ref1_line++ + *ref2_line++ + 1)>>1); + + ref1_line += p_Vid->padded_size_x_m8x8; + ref2_line += p_Vid->padded_size_x_m8x8; + src_line += src_size_x; + } + mcost += HadamardSAD8x8 (diff); + if(mcost > imin_cost) + return dist_scale_f((distblk)mcost); + } + src_tmp += src_size_mul; + } + } + + CHECKOVERFLOW(mcost); + return dist_scale((distblk)mcost); +} + +/*! +************************************************************************ +* \brief +* BiPred (w/ weights) SATD computation +************************************************************************ +*/ +distblk computeBiPredSATD2(StorablePicture *ref1, + StorablePicture *ref2, + MEBlock *mv_block, + distblk min_mcost, + MotionVector *cand1, + MotionVector *cand2) +{ + int imin_cost = dist_down(min_mcost); + int mcost = 0; + int y, x, y4; + int weighted_pel, pixel1, pixel2; + VideoParameters *p_Vid = mv_block->p_Vid; + Slice *currSlice = mv_block->p_Slice; + int denom = currSlice->luma_log_weight_denom + 1; + int lround = 2 * currSlice->wp_luma_round; + short weight1 = mv_block->weight1; + short weight2 = mv_block->weight2; + short offsetBi = mv_block->offsetBi; + + + int max_imgpel_value = p_Vid->max_imgpel_value; + int src_size_x, src_size_mul; + imgpel *src_tmp = mv_block->orig_pic[0]; + short *d, diff[MB_PIXELS]; + imgpel *src_line, *ref1_line, *ref2_line; + short blocksize_x = mv_block->blocksize_x; + short blocksize_y = mv_block->blocksize_y; + + if ( !mv_block->test8x8 ) + { // 4x4 TRANSFORM + src_size_x = (blocksize_x - BLOCK_SIZE); + src_size_mul = blocksize_x * BLOCK_SIZE; + for (y=0; y<(blocksize_y<<2); y += BLOCK_SIZE_SP) + { + for (x=0; xmv_y + y, cand2->mv_x + (x<<2)); + ref1_line = UMVLine4X(ref1, cand1->mv_y + y, cand1->mv_x + (x<<2)); + for (y4 = 0; y4 < BLOCK_SIZE; y4++ ) + { + // 0 + pixel1 = weight1 * (*ref1_line++); + pixel2 = weight2 * (*ref2_line++); + weighted_pel = iClip1( max_imgpel_value, ((pixel1 + pixel2 + lround) >> denom) + offsetBi); + *d++ = (short) ((*src_line++) - weighted_pel); + // 1 + pixel1 = weight1 * (*ref1_line++); + pixel2 = weight2 * (*ref2_line++); + weighted_pel = iClip1( max_imgpel_value, ((pixel1 + pixel2 + lround) >> denom) + offsetBi); + *d++ = (short) ((*src_line++) - weighted_pel); + // 2 + pixel1 = weight1 * (*ref1_line++); + pixel2 = weight2 * (*ref2_line++); + weighted_pel = iClip1( max_imgpel_value, ((pixel1 + pixel2 + lround) >> denom) + offsetBi); + *d++ = (short) ((*src_line++) - weighted_pel); + // 3 + pixel1 = weight1 * (*ref1_line++); + pixel2 = weight2 * (*ref2_line++); + weighted_pel = iClip1( max_imgpel_value, ((pixel1 + pixel2 + lround) >> denom) + offsetBi); + *d++ = (short) ((*src_line++) - weighted_pel); + + ref1_line += p_Vid->padded_size_x_m4x4; + ref2_line += p_Vid->padded_size_x_m4x4; + src_line += src_size_x; + } + mcost += HadamardSAD4x4 (diff); + if(mcost > imin_cost) + return dist_scale_f((distblk)mcost); + } + src_tmp += src_size_mul; + } + } + else + { // 8x8 TRANSFORM + src_size_x = (blocksize_x - BLOCK_SIZE_8x8); + src_size_mul = blocksize_x * BLOCK_SIZE_8x8; + for (y=0; y < (blocksize_y << 2); y += BLOCK_SIZE_8x8_SP ) + { + int y_pos2 = cand2->mv_y + y; + int y_pos1 = cand1->mv_y + y; + for (x=0; xmv_x + (x<<2)); + ref1_line = UMVLine4X(ref1, y_pos1, cand1->mv_x + (x<<2)); + for (y4 = 0; y4 < BLOCK_SIZE_8x8; y4++ ) + { + // 0 + pixel1 = weight1 * (*ref1_line++); + pixel2 = weight2 * (*ref2_line++); + weighted_pel = iClip1( max_imgpel_value, ((pixel1 + pixel2 + lround) >> denom) + offsetBi); + *d++ = (short) ((*src_line++) - weighted_pel); + // 1 + pixel1 = weight1 * (*ref1_line++); + pixel2 = weight2 * (*ref2_line++); + weighted_pel = iClip1( max_imgpel_value, ((pixel1 + pixel2 + lround) >> denom) + offsetBi); + *d++ = (short) ((*src_line++) - weighted_pel); + // 2 + pixel1 = weight1 * (*ref1_line++); + pixel2 = weight2 * (*ref2_line++); + weighted_pel = iClip1( max_imgpel_value, ((pixel1 + pixel2 + lround) >> denom) + offsetBi); + *d++ = (short) ((*src_line++) - weighted_pel); + // 3 + pixel1 = weight1 * (*ref1_line++); + pixel2 = weight2 * (*ref2_line++); + weighted_pel = iClip1( max_imgpel_value, ((pixel1 + pixel2 + lround) >> denom) + offsetBi); + *d++ = (short) ((*src_line++) - weighted_pel); + // 4 + pixel1 = weight1 * (*ref1_line++); + pixel2 = weight2 * (*ref2_line++); + weighted_pel = iClip1( max_imgpel_value, ((pixel1 + pixel2 + lround) >> denom) + offsetBi); + *d++ = (short) ((*src_line++) - weighted_pel); + // 5 + pixel1 = weight1 * (*ref1_line++); + pixel2 = weight2 * (*ref2_line++); + weighted_pel = iClip1( max_imgpel_value, ((pixel1 + pixel2 + lround) >> denom) + offsetBi); + *d++ = (short) ((*src_line++) - weighted_pel); + // 6 + pixel1 = weight1 * (*ref1_line++); + pixel2 = weight2 * (*ref2_line++); + weighted_pel = iClip1( max_imgpel_value, ((pixel1 + pixel2 + lround) >> denom) + offsetBi); + *d++ = (short) ((*src_line++) - weighted_pel); + // 7 + pixel1 = weight1 * (*ref1_line++); + pixel2 = weight2 * (*ref2_line++); + weighted_pel = iClip1( max_imgpel_value, ((pixel1 + pixel2 + lround) >> denom) + offsetBi); + *d++ = (short) ((*src_line) - weighted_pel); + + ref1_line += p_Vid->padded_size_x_m8x8; + ref2_line += p_Vid->padded_size_x_m8x8; + src_line += src_size_x; + } + mcost += HadamardSAD8x8 (diff); + if(mcost > imin_cost) + return dist_scale_f((distblk)mcost); + } + src_tmp += src_size_mul; + } + } + CHECKOVERFLOW(mcost); + return dist_scale((distblk)mcost); +} + +/*! +************************************************************************ +* \brief +* SSE computation +************************************************************************ +*/ +distblk computeSSE(StorablePicture *ref1, + MEBlock *mv_block, + distblk min_mcost, + MotionVector *cand + ) +{ + int imin_cost = dist_down(min_mcost); + int mcost = 0; + int y,x; + short blocksize_x = mv_block->blocksize_x; + short blocksize_y = mv_block->blocksize_y; + VideoParameters *p_Vid = mv_block->p_Vid; + int pad_size_x = p_Vid->padded_size_x - blocksize_x; + + imgpel *src_line = mv_block->orig_pic[0]; + imgpel *ref_line = UMVLine4X (ref1, cand->mv_y, cand->mv_x); + + for (y=0; y imin_cost) + return dist_scale_f((distblk)mcost); + ref_line += pad_size_x; + } + + if ( mv_block->ChromaMEEnable ) + { + // calculate chroma conribution to motion compensation error + int blocksize_x_cr = mv_block->blocksize_cr_x; + int blocksize_y_cr = mv_block->blocksize_cr_y; + int cr_pad_size_x = p_Vid->cr_padded_size_x - blocksize_x_cr; + int k; + int mcr_cost = 0; + + for (k=0; k<2; k++) + { + mcr_cost = 0; + src_line = mv_block->orig_pic[k+1]; + ref_line = UMVLine8X_chroma ( ref1, k+1, cand->mv_y, cand->mv_x); + for (y=0; yChromaMEWeight * mcr_cost; + if(mcost > imin_cost) + return dist_scale_f((distblk)mcost); + } + } + + CHECKOVERFLOW(mcost); + return dist_scale((distblk)mcost); +} + + +/*! +************************************************************************ +* \brief +* SSE computation of weighted samples +************************************************************************ +*/ +distblk computeSSEWP(StorablePicture *ref1, + MEBlock *mv_block, + distblk min_mcost, + MotionVector *cand + ) +{ + int imin_cost = dist_down(min_mcost); + int mcost = 0; + int y,x; + int weighted_pel; + short blocksize_x = mv_block->blocksize_x; + short blocksize_y = mv_block->blocksize_y; + VideoParameters *p_Vid = mv_block->p_Vid; + Slice *currSlice = mv_block->p_Slice; + short weight = mv_block->weight_luma; + short offset = mv_block->offset_luma; + + int wp_luma_round = currSlice->wp_luma_round; + int pad_size_x = p_Vid->padded_size_x - blocksize_x; + int max_imgpel_value = p_Vid->max_imgpel_value; + short luma_log_weight_denom = currSlice->luma_log_weight_denom; + + imgpel *src_line = mv_block->orig_pic[0]; + imgpel *ref_line = UMVLine4X (ref1, cand->mv_y, cand->mv_x); + + for (y=0; y> luma_log_weight_denom) + offset); + mcost += iabs2( *src_line++ - weighted_pel ); + weighted_pel = iClip1( max_imgpel_value, ((weight * *ref_line++ + wp_luma_round) >> luma_log_weight_denom) + offset); + mcost += iabs2( *src_line++ - weighted_pel ); + weighted_pel = iClip1( max_imgpel_value, ((weight * *ref_line++ + wp_luma_round) >> luma_log_weight_denom) + offset); + mcost += iabs2( *src_line++ - weighted_pel ); + weighted_pel = iClip1( max_imgpel_value, ((weight * *ref_line++ + wp_luma_round) >> luma_log_weight_denom) + offset); + mcost += iabs2( *src_line++ - weighted_pel ); + } + if(mcost > imin_cost) + return dist_scale_f((distblk)mcost); + ref_line += pad_size_x; + } + + if ( mv_block->ChromaMEEnable ) + { + // calculate chroma conribution to motion compensation error + // These could be made global to reduce computations + int blocksize_x_cr = mv_block->blocksize_cr_x; + int blocksize_y_cr = mv_block->blocksize_cr_y; + int cr_pad_size_x = p_Vid->cr_padded_size_x - blocksize_x_cr; + int k; + int mcr_cost = 0; + int max_imgpel_value_uv = p_Vid->max_pel_value_comp[1]; + int wp_chroma_round = currSlice->wp_chroma_round; + short chroma_log_weight_denom = currSlice->chroma_log_weight_denom; + + for (k=0; k<2; k++) + { + weight = mv_block->weight_cr[k]; + offset = mv_block->offset_cr[k]; + + mcr_cost = 0; + src_line = mv_block->orig_pic[k+1]; + ref_line = UMVLine8X_chroma ( ref1, k+1, cand->mv_y, cand->mv_x); + for (y=0; y> chroma_log_weight_denom) + offset); + mcr_cost += iabs2( *src_line++ - weighted_pel ); + weighted_pel = iClip1( max_imgpel_value_uv, ((weight * *ref_line++ + wp_chroma_round) >> chroma_log_weight_denom) + offset); + mcr_cost += iabs2( *src_line++ - weighted_pel ); + } + ref_line += cr_pad_size_x; + } + mcost += mv_block->ChromaMEWeight * mcr_cost; + if(mcost > imin_cost) + return dist_scale_f((distblk)mcost); + } + } + + CHECKOVERFLOW(mcost); + return dist_scale((distblk)mcost); +} + +/*! +************************************************************************ +* \brief +* BiPred SSE computation (no weights) +************************************************************************ +*/ +distblk computeBiPredSSE1(StorablePicture *ref1, + StorablePicture *ref2, + MEBlock *mv_block, + distblk min_mcost, + MotionVector *cand1, + MotionVector *cand2) +{ + int imin_cost = dist_down(min_mcost); + int mcost = 0; + int bi_diff; + int y,x; + short blocksize_x = mv_block->blocksize_x; + short blocksize_y = mv_block->blocksize_y; + VideoParameters *p_Vid = mv_block->p_Vid; + int pad_size_x = p_Vid->padded_size_x - blocksize_x; + + imgpel *src_line = mv_block->orig_pic[0]; + imgpel *ref2_line = UMVLine4X(ref2, cand2->mv_y, cand2->mv_x); + imgpel *ref1_line = UMVLine4X(ref1, cand1->mv_y, cand1->mv_x); + + for (y = 0; y < blocksize_y; y++) + { + for (x = 0; x < blocksize_x; x+=4) + { + bi_diff = (*src_line++) - ((*ref1_line++ + *ref2_line++ + 1)>>1); + mcost += iabs2(bi_diff); + bi_diff = (*src_line++) - ((*ref1_line++ + *ref2_line++ + 1)>>1); + mcost += iabs2(bi_diff); + bi_diff = (*src_line++) - ((*ref1_line++ + *ref2_line++ + 1)>>1); + mcost += iabs2(bi_diff); + bi_diff = (*src_line++) - ((*ref1_line++ + *ref2_line++ + 1)>>1); + mcost += iabs2(bi_diff); + } + if(mcost > imin_cost) + return dist_scale_f((distblk)mcost); + + ref2_line += pad_size_x; + ref1_line += pad_size_x; + } + + if ( mv_block->ChromaMEEnable ) + { + // calculate chroma conribution to motion compensation error + int blocksize_x_cr = mv_block->blocksize_cr_x; + int blocksize_y_cr = mv_block->blocksize_cr_y; + int cr_pad_size_x = p_Vid->cr_padded_size_x - blocksize_x_cr; + int k; + int mcr_cost = 0; + + for (k=0; k<2; k++) + { + mcr_cost = 0; + src_line = mv_block->orig_pic[k+1]; + ref2_line = UMVLine8X_chroma ( ref2, k+1, cand2->mv_y, cand2->mv_x); + ref1_line = UMVLine8X_chroma ( ref1, k+1, cand1->mv_y, cand1->mv_x); + + for (y=0; y>1); + mcr_cost += iabs2(bi_diff); + bi_diff = (*src_line++) - ((*ref1_line++ + *ref2_line++ + 1)>>1); + mcr_cost += iabs2(bi_diff); + } + ref2_line += cr_pad_size_x; + ref1_line += cr_pad_size_x; + } + mcost += mv_block->ChromaMEWeight * mcr_cost; + if(mcost > imin_cost) + return dist_scale_f((distblk)mcost); + } + } + + CHECKOVERFLOW(mcost); + return dist_scale((distblk)mcost); +} + + +/*! +************************************************************************ +* \brief +* BiPred SSE computation (with weights) +************************************************************************ +*/ +distblk computeBiPredSSE2(StorablePicture *ref1, + StorablePicture *ref2, + MEBlock *mv_block, + distblk min_mcost, + MotionVector *cand1, + MotionVector *cand2) +{ + int imin_cost = dist_down(min_mcost); + int mcost = 0; + int bi_diff; + VideoParameters *p_Vid = mv_block->p_Vid; + Slice *currSlice = mv_block->p_Slice; + int denom = currSlice->luma_log_weight_denom + 1; + int lround = 2 * currSlice->wp_luma_round; + int max_imgpel_value = p_Vid->max_imgpel_value; + int y,x; + int weighted_pel, pixel1, pixel2; + short weight1 = mv_block->weight1; + short weight2 = mv_block->weight2; + short offsetBi = mv_block->offsetBi; + + short blocksize_x = mv_block->blocksize_x; + short blocksize_y = mv_block->blocksize_y; + int pad_size_x = p_Vid->padded_size_x - blocksize_x; + + imgpel *src_line = mv_block->orig_pic[0]; + imgpel *ref2_line = UMVLine4X(ref2, cand2->mv_y, cand2->mv_x); + imgpel *ref1_line = UMVLine4X(ref1, cand1->mv_y, cand1->mv_x); + for (y=0; y> denom) + offsetBi); + bi_diff = (*src_line++) - weighted_pel; + mcost += bi_diff * bi_diff; + + pixel1 = weight1 * (*ref1_line++); + pixel2 = weight2 * (*ref2_line++); + weighted_pel = iClip1( max_imgpel_value, ((pixel1 + pixel2 + lround) >> denom) + offsetBi); + bi_diff = (*src_line++) - weighted_pel; + mcost += bi_diff * bi_diff; + + pixel1 = weight1 * (*ref1_line++); + pixel2 = weight2 * (*ref2_line++); + weighted_pel = iClip1( max_imgpel_value, ((pixel1 + pixel2 + lround) >> denom) + offsetBi); + bi_diff = (*src_line++) - weighted_pel; + mcost += bi_diff * bi_diff; + + pixel1 = weight1 * (*ref1_line++); + pixel2 = weight2 * (*ref2_line++); + weighted_pel = iClip1( max_imgpel_value, ((pixel1 + pixel2 + lround) >> denom) + offsetBi); + bi_diff = (*src_line++) - weighted_pel; + mcost += bi_diff * bi_diff; + } + if(mcost > imin_cost) + return dist_scale_f((distblk)mcost); + + ref2_line += pad_size_x; + ref1_line += pad_size_x; + } + + if ( mv_block->ChromaMEEnable ) + { + // calculate chroma conribution to motion compensation error + int blocksize_x_cr = mv_block->blocksize_cr_x; + int blocksize_y_cr = mv_block->blocksize_cr_y; + int cr_pad_size_x = p_Vid->cr_padded_size_x - blocksize_x_cr; + int k; + int mcr_cost = 0; + int max_imgpel_value_uv = p_Vid->max_pel_value_comp[1]; + + for (k=0; k<2; k++) + { + weight1 = mv_block->weight1_cr[k]; + weight2 = mv_block->weight2_cr[k]; + offsetBi = mv_block->offsetBi_cr[k]; + + mcr_cost = 0; + src_line = mv_block->orig_pic[k+1]; + ref2_line = UMVLine8X_chroma ( ref2, k+1, cand2->mv_y, cand2->mv_x); + ref1_line = UMVLine8X_chroma ( ref1, k+1, cand1->mv_y, cand1->mv_x); + + for (y=0; y> denom) + offsetBi); + bi_diff = (*src_line++) - weighted_pel; + mcr_cost += bi_diff * bi_diff; + + pixel1 = weight1 * (*ref1_line++); + pixel2 = weight2 * (*ref2_line++); + weighted_pel = iClip1( max_imgpel_value_uv, ((pixel1 + pixel2 + lround) >> denom) + offsetBi); + bi_diff = (*src_line++) - weighted_pel; + mcr_cost += bi_diff * bi_diff; + } + ref2_line += cr_pad_size_x; + ref1_line += cr_pad_size_x; + } + mcost += mv_block->ChromaMEWeight * mcr_cost; + if(mcost > imin_cost) + return dist_scale_f((distblk)mcost); + } + } + + CHECKOVERFLOW(mcost); + return dist_scale((distblk)mcost); +} + + +void calcDifference(imgpel **origImg, int ox, int oy, imgpel **predImg, int px, int py, int width, int height, short *diff) +{ + int i, j; + imgpel *orig, *pred; + for (j = 0; j < height; j++) + { + orig = origImg[oy+j]+ox; + pred = predImg[py+j]+px; + for (i = 0; i < width; i++) + { + *diff++ = *orig++ - *pred++; + } + } +} diff --git a/lencod/src/me_distortion_otf.c b/lencod/src/me_distortion_otf.c new file mode 100644 index 0000000..2d7e2f8 --- /dev/null +++ b/lencod/src/me_distortion_otf.c @@ -0,0 +1,1273 @@ +/*! +************************************************************************************* +* \file me_distortion_otf.c +* +* \brief +* Motion estimation error calculation functions with on-the-fly interpolation +* +* +************************************************************************************* +*/ + +#include "contributors.h" + +#include + +#include "global.h" +#include "image.h" +#include "memalloc.h" +#include "mb_access.h" +#include "mv_search.h" +#include "me_distortion.h" +#include "me_distortion_otf.h" +#include "get_block_otf.h" + +#include "refbuf_otf.h" + + +//#define CHECKOVERFLOW(mcost) assert(mcost>=0) +#define CHECKOVERFLOW(mcost) + + +/*! +************************************************************************ +* \brief +* JLT : SAD computation (on-the-fly) +************************************************************************ +*/ +distblk computeSAD_otf(StorablePicture *ref1, + MEBlock *mv_block, + distblk min_mcost, + MotionVector *cand) +{ + int mcost = 0; + int imin_cost = dist_down(min_mcost); + int y,x; + short blocksize_x = mv_block->blocksize_x; + short blocksize_y = mv_block->blocksize_y; + VideoParameters *p_Vid = mv_block->p_Vid; + DecodedPictureBuffer *p_Dpb = p_Vid->p_Dpb_layer[p_Vid->dpb_layer_id]; +#if (JM_MEM_DISTORTION) + int *imgpel_abs = p_Vid->imgpel_abs; +#endif + imgpel *src_line, *ref_line ; + imgpel data[MB_PIXELS]; // local allocation could be optimized by a global instanciation + int tmp_line[ (MB_BLOCK_SIZE+5)*(MB_BLOCK_SIZE+5) ] ; + + src_line = mv_block->orig_pic[0]; + ref_line = data ; + // get block with interpolation on-the-fly + p_Dpb->pf_get_block_luma( p_Vid, ref_line, tmp_line, cand->mv_x, cand->mv_y, blocksize_x, blocksize_y, ref1, 0 ) ; + + for (y=0; y imin_cost) + return (dist_scale_f((distblk)mcost)); + } + if ( mv_block->ChromaMEEnable ) + { + // calculate chroma conribution to motion compensation error + int blocksize_x_cr = mv_block->blocksize_cr_x; + int blocksize_y_cr = mv_block->blocksize_cr_y; + int k; + int mcr_cost = 0; // chroma me cost + + for (k=0; k < 2; k++) + { + src_line = mv_block->orig_pic[k+1]; + ref_line = data ; + p_Dpb->pf_get_block_chroma[OTF_ME]( p_Vid, ref_line, tmp_line, cand->mv_x, cand->mv_y, blocksize_x_cr, blocksize_y_cr, ref1, k+1 ) ; + mcr_cost = 0; + + for (y = 0; y < blocksize_y_cr; y++) + { + for (x = 0; x < blocksize_x_cr; x += 2) + { +#if (JM_MEM_DISTORTION) + mcr_cost += imgpel_abs[ *src_line++ - *ref_line++ ]; + mcr_cost += imgpel_abs[ *src_line++ - *ref_line++ ]; +#else + mcr_cost += iabs( *src_line++ - *ref_line++ ); + mcr_cost += iabs( *src_line++ - *ref_line++ ); +#endif + } + } + mcost += mv_block->ChromaMEWeight * mcr_cost; + + if(mcost >imin_cost) + return (dist_scale_f((distblk)mcost)); + } + } + + CHECKOVERFLOW(mcost); + return (dist_scale((distblk)mcost)); +} + +/*! +************************************************************************ +* \brief +* JLT : SADWP computation for weighted samples ( on-the-fly ) +************************************************************************ +*/ +distblk computeSADWP_otf(StorablePicture *ref1, + MEBlock *mv_block, + distblk min_mcost, + MotionVector *cand + ) +{ + int mcost = 0; + int imin_cost = dist_down(min_mcost); + int y, x; + int weighted_pel; + short blocksize_x = mv_block->blocksize_x; + short blocksize_y = mv_block->blocksize_y; + + VideoParameters *p_Vid = mv_block->p_Vid; + Slice *currSlice = mv_block->p_Slice; + DecodedPictureBuffer *p_Dpb = p_Vid->p_Dpb_layer[p_Vid->dpb_layer_id]; + int max_imgpel_value = p_Vid->max_imgpel_value; + short weight = mv_block->weight_luma; + short offset = mv_block->offset_luma; + + int wp_luma_round = currSlice->wp_luma_round; + short luma_log_weight_denom = currSlice->luma_log_weight_denom; + + imgpel *src_line, *ref_line ; + imgpel data[MB_PIXELS]; // local allocation could be optimized by a global instanciation + int tmp_line[ (MB_BLOCK_SIZE+5)*(MB_BLOCK_SIZE+5) ] ; + + src_line = mv_block->orig_pic[0]; + ref_line = data ; + // get block with interpolation on-the-fly + p_Dpb->pf_get_block_luma( p_Vid, ref_line, tmp_line, cand->mv_x, cand->mv_y, blocksize_x, blocksize_y, ref1, 0 ); + + for (y=0; y> luma_log_weight_denom) + offset); + mcost += iabs( *src_line++ - weighted_pel ); + weighted_pel = iClip1( max_imgpel_value, ((weight * *ref_line++ + wp_luma_round) >> luma_log_weight_denom) + offset); + mcost += iabs( *src_line++ - weighted_pel ); + weighted_pel = iClip1( max_imgpel_value, ((weight * *ref_line++ + wp_luma_round) >> luma_log_weight_denom) + offset); + mcost += iabs( *src_line++ - weighted_pel ); + weighted_pel = iClip1( max_imgpel_value, ((weight * *ref_line++ + wp_luma_round) >> luma_log_weight_denom) + offset); + mcost += iabs( *src_line++ - weighted_pel ); + } + if(mcost > imin_cost) + return (dist_scale_f((distblk)mcost)); + } + if ( mv_block->ChromaMEEnable ) + { + // calculate chroma conribution to motion compensation error + int blocksize_x_cr = mv_block->blocksize_cr_x; + int blocksize_y_cr = mv_block->blocksize_cr_y; + int k; + int mcr_cost = 0; + int max_imgpel_value_uv = p_Vid->max_pel_value_comp[1]; + int wp_chroma_round = currSlice->wp_chroma_round; + short chroma_log_weight_denom = currSlice->chroma_log_weight_denom; + + for (k=0; k < 2; k++) + { + weight = mv_block->weight_cr[k]; + offset = mv_block->offset_cr[k]; + + mcr_cost = 0; + src_line = mv_block->orig_pic[k+1]; + ref_line = data ; + p_Dpb->pf_get_block_chroma[OTF_ME]( p_Vid, ref_line, tmp_line, cand->mv_x, cand->mv_y, blocksize_x_cr, blocksize_y_cr, ref1, k+1 ) ; + + for (y=0; y> chroma_log_weight_denom) + offset); + mcr_cost += iabs( *src_line++ - weighted_pel ); + weighted_pel = iClip1( max_imgpel_value_uv, ((weight * *ref_line++ + wp_chroma_round) >> chroma_log_weight_denom) + offset); + mcr_cost += iabs( *src_line++ - weighted_pel ); + } + } + mcost += mv_block->ChromaMEWeight * mcr_cost; + + if(mcost >imin_cost) + return (dist_scale_f((distblk)mcost)); + } + } + + CHECKOVERFLOW(mcost); + return (dist_scale((distblk)mcost)); +} + +/*! +************************************************************************ +* \brief +* JLT : BiPred SAD computation (no weights) ( on-the-fly ) +************************************************************************ +*/ +distblk computeBiPredSAD1_otf(StorablePicture *ref1, + StorablePicture *ref2, + MEBlock *mv_block, + distblk min_mcost, + MotionVector *cand1, + MotionVector *cand2) +{ + int imin_cost = dist_down(min_mcost); + int mcost = 0; + int bi_diff; + int y,x; + short blocksize_x = mv_block->blocksize_x; + short blocksize_y = mv_block->blocksize_y; + VideoParameters *p_Vid = mv_block->p_Vid; + DecodedPictureBuffer *p_Dpb = p_Vid->p_Dpb_layer[p_Vid->dpb_layer_id]; +#if (JM_MEM_DISTORTION) + int *imgpel_abs = p_Vid->imgpel_abs; +#endif + + imgpel *src_line, *ref2_line, *ref1_line ; + imgpel data2[MB_PIXELS], data1[MB_PIXELS]; // local allocation could be optimized by a global instanciation + int tmp_line[ (MB_BLOCK_SIZE+5)*(MB_BLOCK_SIZE+5) ] ; + + src_line = mv_block->orig_pic[0]; + ref2_line = data2 ; + ref1_line = data1 ; + + p_Dpb->pf_get_block_luma( p_Vid, ref2_line, tmp_line, cand2->mv_x, cand2->mv_y, blocksize_x, blocksize_y, ref2, 0 ); + p_Dpb->pf_get_block_luma( p_Vid, ref1_line, tmp_line, cand1->mv_x, cand1->mv_y, blocksize_x, blocksize_y, ref1, 0 ); + + for (y = 0; y < blocksize_y; y++) + { + for (x = 0; x < blocksize_x; x+=4) + { +#if (JM_MEM_DISTORTION) + bi_diff = (*src_line++) - ((*ref1_line++ + *ref2_line++ + 1)>>1); + mcost += imgpel_abs[ bi_diff ]; + bi_diff = (*src_line++) - ((*ref1_line++ + *ref2_line++ + 1)>>1); + mcost += imgpel_abs[ bi_diff ]; + bi_diff = (*src_line++) - ((*ref1_line++ + *ref2_line++ + 1)>>1); + mcost += imgpel_abs[ bi_diff ]; + bi_diff = (*src_line++) - ((*ref1_line++ + *ref2_line++ + 1)>>1); + mcost += imgpel_abs[ bi_diff ]; +#else + bi_diff = (*src_line++) - ((*ref1_line++ + *ref2_line++ + 1)>>1); + mcost += iabs(bi_diff); + bi_diff = (*src_line++) - ((*ref1_line++ + *ref2_line++ + 1)>>1); + mcost += iabs(bi_diff); + bi_diff = (*src_line++) - ((*ref1_line++ + *ref2_line++ + 1)>>1); + mcost += iabs(bi_diff); + bi_diff = (*src_line++) - ((*ref1_line++ + *ref2_line++ + 1)>>1); + mcost += iabs(bi_diff); +#endif + } + if(mcost > imin_cost) + return (dist_scale_f((distblk)mcost)); + } + + if ( mv_block->ChromaMEEnable ) + { + // calculate chroma conribution to motion compensation error + int blocksize_x_cr = mv_block->blocksize_cr_x; + int blocksize_y_cr = mv_block->blocksize_cr_y; + int k; + int mcr_cost = 0; + + for (k=1; k<3; k++) + { + mcr_cost = 0; + + src_line = mv_block->orig_pic[k]; + ref2_line = data2 ; + ref1_line = data1 ; + p_Dpb->pf_get_block_chroma[OTF_ME]( p_Vid, ref2_line, tmp_line, cand2->mv_x, cand2->mv_y, blocksize_x_cr, blocksize_y_cr, ref2, k ) ; + p_Dpb->pf_get_block_chroma[OTF_ME]( p_Vid, ref1_line, tmp_line, cand1->mv_x, cand1->mv_y, blocksize_x_cr, blocksize_y_cr, ref1, k ) ; + + for (y=0; y>1); + mcr_cost += iabs(bi_diff); + bi_diff = (*src_line++) - ((*ref1_line++ + *ref2_line++ + 1)>>1); + mcr_cost += iabs(bi_diff); + } + } + mcost += mv_block->ChromaMEWeight * mcr_cost; + + if(mcost > imin_cost) + return (dist_scale_f((distblk)mcost)); + } + } + + CHECKOVERFLOW(mcost); + return (dist_scale((distblk)mcost)); +} + +/*! +************************************************************************ +* \brief +* JLT : BiPred SAD computation (with weights) ( on-the-fly ) +************************************************************************ +*/ +distblk computeBiPredSAD2_otf(StorablePicture *ref1, + StorablePicture *ref2, + MEBlock *mv_block, + distblk min_mcost, + MotionVector *cand1, + MotionVector *cand2) +{ + int imin_cost = dist_down(min_mcost); + int mcost = 0; + int bi_diff; + VideoParameters *p_Vid = mv_block->p_Vid; + Slice *currSlice = mv_block->p_Slice; + DecodedPictureBuffer *p_Dpb = p_Vid->p_Dpb_layer[p_Vid->dpb_layer_id]; + int denom = currSlice->luma_log_weight_denom + 1; + int lround = 2 * currSlice->wp_luma_round; + int max_imgpel_value = p_Vid->max_imgpel_value; + int y,x; + int weighted_pel, pixel1, pixel2; + short blocksize_x = mv_block->blocksize_x; + short blocksize_y = mv_block->blocksize_y; + short weight1 = mv_block->weight1; + short weight2 = mv_block->weight2; + short offsetBi = mv_block->offsetBi; + + imgpel *src_line, *ref2_line, *ref1_line ; + imgpel data2[MB_PIXELS], data1[MB_PIXELS]; // local allocation could be optimized by a global instanciation + int tmp_line[ (MB_BLOCK_SIZE+5)*(MB_BLOCK_SIZE+5) ] ; + src_line = mv_block->orig_pic[0]; + ref2_line = data2 ; + ref1_line = data1 ; + + p_Dpb->pf_get_block_luma( p_Vid, ref2_line, tmp_line, cand2->mv_x, cand2->mv_y, blocksize_x, blocksize_y, ref2, 0 ); + p_Dpb->pf_get_block_luma( p_Vid, ref1_line, tmp_line, cand1->mv_x, cand1->mv_y, blocksize_x, blocksize_y, ref1, 0 ); + + for (y=0; y> denom) + offsetBi); + bi_diff = (*src_line++) - weighted_pel; + mcost += iabs(bi_diff); + + pixel1 = weight1 * (*ref1_line++); + pixel2 = weight2 * (*ref2_line++); + weighted_pel = iClip1( max_imgpel_value, ((pixel1 + pixel2 + lround) >> denom) + offsetBi); + bi_diff = (*src_line++) - weighted_pel; + mcost += iabs(bi_diff); + + pixel1 = weight1 * (*ref1_line++); + pixel2 = weight2 * (*ref2_line++); + weighted_pel = iClip1( max_imgpel_value, ((pixel1 + pixel2 + lround) >> denom) + offsetBi); + bi_diff = (*src_line++) - weighted_pel; + mcost += iabs(bi_diff); + + pixel1 = weight1 * (*ref1_line++); + pixel2 = weight2 * (*ref2_line++); + weighted_pel = iClip1( max_imgpel_value, ((pixel1 + pixel2 + lround) >> denom) + offsetBi); + bi_diff = (*src_line++) - weighted_pel; + mcost += iabs(bi_diff); + } + + if(mcost > imin_cost) + return dist_scale_f((distblk)mcost); + } + + if ( mv_block->ChromaMEEnable ) + { + // calculate chroma conribution to motion compensation error + int blocksize_x_cr = mv_block->blocksize_cr_x; + int blocksize_y_cr = mv_block->blocksize_cr_y; + int k; + int mcr_cost = 0; + int max_imgpel_value_uv = p_Vid->max_pel_value_comp[1]; + + for (k=0; k<2; k++) + { + weight1 = mv_block->weight1_cr[k]; + weight2 = mv_block->weight2_cr[k]; + offsetBi = mv_block->offsetBi_cr[k]; + + mcr_cost = 0; + src_line = mv_block->orig_pic[k+1]; + ref2_line = data2 ; + ref1_line = data1 ; + p_Dpb->pf_get_block_chroma[OTF_ME]( p_Vid, ref2_line, tmp_line, cand2->mv_x, cand2->mv_y, blocksize_x_cr, blocksize_y_cr, ref2, k+1 ) ; + p_Dpb->pf_get_block_chroma[OTF_ME]( p_Vid, ref1_line, tmp_line, cand1->mv_x, cand1->mv_y, blocksize_x_cr, blocksize_y_cr, ref1, k+1 ) ; + + for (y=0; y> denom) + offsetBi); + bi_diff = (*src_line++) - weighted_pel; + mcr_cost += iabs(bi_diff); + + pixel1 = weight1 * (*ref1_line++); + pixel2 = weight2 * (*ref2_line++); + weighted_pel = iClip1( max_imgpel_value_uv, ((pixel1 + pixel2 + lround) >> denom) + offsetBi); + bi_diff = (*src_line++) - weighted_pel; + mcr_cost += iabs(bi_diff); + } + } + mcost += mv_block->ChromaMEWeight * mcr_cost; + + if(mcost > imin_cost) + return dist_scale_f((distblk)mcost); + } + } + + CHECKOVERFLOW(mcost); + return dist_scale((distblk)mcost); +} + +/*! +************************************************************************ +* \brief +* JLT : SAD computation _with_ Hadamard Transform (on-the-fly) +************************************************************************ +*/ +distblk computeSATD_otf(StorablePicture *ref1, + MEBlock *mv_block, + distblk min_mcost, + MotionVector *cand + ) +{ + int imin_cost = dist_down(min_mcost); + int mcost = 0; + int y, x, y4; + int src_size_x, src_size_mul; + short blocksize_x = mv_block->blocksize_x; + short blocksize_y = mv_block->blocksize_y; + VideoParameters *p_Vid = mv_block->p_Vid; + DecodedPictureBuffer *p_Dpb = p_Vid->p_Dpb_layer[p_Vid->dpb_layer_id]; + + imgpel *src_tmp = mv_block->orig_pic[0]; + short *d, diff[MB_PIXELS]; + imgpel *src_line, *ref_line, data[MB_PIXELS] ; + int tmp_line[ (MB_BLOCK_SIZE+5)*(MB_BLOCK_SIZE+5) ] ; + + + if ( !mv_block->test8x8 ) + { // 4x4 TRANSFORM + src_size_x = blocksize_x - BLOCK_SIZE; + src_size_mul = blocksize_x * BLOCK_SIZE; + for (y = cand->mv_y; y < cand->mv_y + (blocksize_y<<2); y += (BLOCK_SIZE_SP)) + { + for (x=0; xpf_get_block_luma( p_Vid, ref_line, tmp_line, cand->mv_x + (x<<2) , y, BLOCK_SIZE, BLOCK_SIZE, ref1, 0 ); + src_line = src_tmp + x; + for (y4 = 0; y4 < BLOCK_SIZE; y4++ ) + { + *d++ = *src_line++ - *ref_line++ ; + *d++ = *src_line++ - *ref_line++ ; + *d++ = *src_line++ - *ref_line++ ; + *d++ = *src_line++ - *ref_line++ ; + + src_line += src_size_x; + } + mcost += HadamardSAD4x4 (diff); + if(mcost > imin_cost) + return dist_scale_f((distblk)mcost); + } + src_tmp += src_size_mul; + } + } + else + { // 8x8 TRANSFORM + src_size_x = (blocksize_x - BLOCK_SIZE_8x8); + src_size_mul = blocksize_x * BLOCK_SIZE_8x8; + for (y = cand->mv_y; y < cand->mv_y + (blocksize_y<<2); y += (BLOCK_SIZE_8x8_SP) ) + { + for (x=0; xpf_get_block_luma( p_Vid, ref_line, tmp_line, cand->mv_x + (x<<2) , y, BLOCK_SIZE_8x8, BLOCK_SIZE_8x8, ref1, 0 ); + src_line = src_tmp + x; + for (y4 = 0; y4 < BLOCK_SIZE_8x8; y4++ ) + { + *d++ = *src_line++ - *ref_line++ ; + *d++ = *src_line++ - *ref_line++ ; + *d++ = *src_line++ - *ref_line++ ; + *d++ = *src_line++ - *ref_line++ ; + *d++ = *src_line++ - *ref_line++ ; + *d++ = *src_line++ - *ref_line++ ; + *d++ = *src_line++ - *ref_line++ ; + *d++ = *src_line++ - *ref_line++ ; + + src_line += src_size_x; + } + mcost += HadamardSAD8x8 (diff); + if(mcost > imin_cost) + return dist_scale_f((distblk)mcost); + } + src_tmp += src_size_mul; + } + } + + CHECKOVERFLOW(mcost); + return dist_scale((distblk)mcost); +} + +/*! +************************************************************************ +* \brief +* JLT : SAD computation of weighted samples _with_ Hadamard Transform ( on-the-fly ) +************************************************************************ +*/ +distblk computeSATDWP_otf(StorablePicture *ref1, + MEBlock *mv_block, + distblk min_mcost, + MotionVector *cand + ) +{ + int imin_cost = dist_down(min_mcost); + int mcost = 0; + int y, x, y4; + int weighted_pel; + int src_size_x, src_size_mul; + short blocksize_x = mv_block->blocksize_x; + short blocksize_y = mv_block->blocksize_y; + + imgpel *src_tmp = mv_block->orig_pic[0]; + VideoParameters *p_Vid = mv_block->p_Vid; + Slice *currSlice = mv_block->p_Slice; + DecodedPictureBuffer *p_Dpb = p_Vid->p_Dpb_layer[p_Vid->dpb_layer_id]; + short luma_log_weight_denom = currSlice->luma_log_weight_denom; + short weight = mv_block->weight_luma; + short offset = mv_block->offset_luma; + + int wp_luma_round = currSlice->wp_luma_round; + int max_imgpel_value = p_Vid->max_imgpel_value; + short *d, diff[MB_PIXELS]; + imgpel *src_line, *ref_line, data[MB_PIXELS]; + int tmp_line[ (MB_BLOCK_SIZE+5)*(MB_BLOCK_SIZE+5) ] ; + + if ( !mv_block->test8x8 ) + { // 4x4 TRANSFORM + src_size_x = (blocksize_x - BLOCK_SIZE); + src_size_mul = blocksize_x * BLOCK_SIZE; + for (y = cand->mv_y; y < cand->mv_y + (blocksize_y<<2); y += (BLOCK_SIZE_SP)) + { + for (x=0; xpf_get_block_luma( p_Vid, ref_line, tmp_line, cand->mv_x + (x<<2) , y, BLOCK_SIZE, BLOCK_SIZE, ref1, 0 ); + src_line = src_tmp + x; + for (y4 = 0; y4 < BLOCK_SIZE; y4++ ) + { + weighted_pel = iClip1( max_imgpel_value, ((weight * *ref_line++ + wp_luma_round) >> luma_log_weight_denom) + offset); + *d++ = (short) (*src_line++ - weighted_pel); + weighted_pel = iClip1( max_imgpel_value, ((weight * *ref_line++ + wp_luma_round) >> luma_log_weight_denom) + offset); + *d++ = (short) (*src_line++ - weighted_pel); + weighted_pel = iClip1( max_imgpel_value, ((weight * *ref_line++ + wp_luma_round) >> luma_log_weight_denom) + offset); + *d++ = (short) (*src_line++ - weighted_pel); + weighted_pel = iClip1( max_imgpel_value, ((weight * *ref_line++ + wp_luma_round) >> luma_log_weight_denom) + offset); + *d++ = (short) (*src_line++ - weighted_pel); + + src_line += src_size_x; + } + mcost += HadamardSAD4x4 (diff); + + if(mcost > imin_cost) + return dist_scale_f((distblk)mcost); + } + src_tmp += src_size_mul; + } + } + else + { // 8x8 TRANSFORM + src_size_x = (blocksize_x - BLOCK_SIZE_8x8); + src_size_mul = blocksize_x * BLOCK_SIZE_8x8; + for (y = cand->mv_y; y < cand->mv_y + (blocksize_y<<2); y += (BLOCK_SIZE_8x8_SP) ) + { + for (x=0; xpf_get_block_luma( p_Vid, ref_line, tmp_line, cand->mv_x + (x<<2) , y, BLOCK_SIZE_8x8, BLOCK_SIZE_8x8, ref1, 0 ); + src_line = src_tmp + x; + for (y4 = 0; y4 < BLOCK_SIZE_8x8; y4++ ) + { + weighted_pel = iClip1( max_imgpel_value, ((weight * *ref_line++ + wp_luma_round) >> luma_log_weight_denom) + offset); + *d++ = (short) (*src_line++ - weighted_pel); + weighted_pel = iClip1( max_imgpel_value, ((weight * *ref_line++ + wp_luma_round) >> luma_log_weight_denom) + offset); + *d++ = (short) (*src_line++ - weighted_pel); + weighted_pel = iClip1( max_imgpel_value, ((weight * *ref_line++ + wp_luma_round) >> luma_log_weight_denom) + offset); + *d++ = (short) (*src_line++ - weighted_pel); + weighted_pel = iClip1( max_imgpel_value, ((weight * *ref_line++ + wp_luma_round) >> luma_log_weight_denom) + offset); + *d++ = (short) (*src_line++ - weighted_pel); + weighted_pel = iClip1( max_imgpel_value, ((weight * *ref_line++ + wp_luma_round) >> luma_log_weight_denom) + offset); + *d++ = (short) (*src_line++ - weighted_pel); + weighted_pel = iClip1( max_imgpel_value, ((weight * *ref_line++ + wp_luma_round) >> luma_log_weight_denom) + offset); + *d++ = (short) (*src_line++ - weighted_pel); + weighted_pel = iClip1( max_imgpel_value, ((weight * *ref_line++ + wp_luma_round) >> luma_log_weight_denom) + offset); + *d++ = (short) (*src_line++ - weighted_pel); + weighted_pel = iClip1( max_imgpel_value, ((weight * *ref_line++ + wp_luma_round) >> luma_log_weight_denom) + offset); + *d++ = (short) (*src_line++ - weighted_pel); + + src_line += src_size_x; + } + mcost += HadamardSAD8x8 (diff); + if(mcost > imin_cost) + return dist_scale_f((distblk)mcost); + } + src_tmp += src_size_mul; + } + } + + CHECKOVERFLOW(mcost); + return dist_scale((distblk)mcost); +} + +/*! +************************************************************************ +* \brief +* JLT : BiPred (w/o weights) SATD computation ( on-the-fly ) +************************************************************************ +*/ +distblk computeBiPredSATD1_otf(StorablePicture *ref1, + StorablePicture *ref2, + MEBlock *mv_block, + distblk min_mcost, + MotionVector *cand1, + MotionVector *cand2) +{ + int imin_cost = dist_down(min_mcost); + int mcost = 0; + int y, x, y4; + int src_size_x, src_size_mul; + imgpel *src_tmp = mv_block->orig_pic[0]; + short *d, diff[MB_PIXELS]; + imgpel *src_line, *ref1_line, *ref2_line, data1[MB_PIXELS], data2[MB_PIXELS] ; + int tmp_line[ (MB_BLOCK_SIZE+5)*(MB_BLOCK_SIZE+5) ] ; + short blocksize_x = mv_block->blocksize_x; + short blocksize_y = mv_block->blocksize_y; + VideoParameters *p_Vid = mv_block->p_Vid; + DecodedPictureBuffer *p_Dpb = p_Vid->p_Dpb_layer[p_Vid->dpb_layer_id]; + + if ( !mv_block->test8x8 ) + { // 4x4 TRANSFORM + src_size_x = (blocksize_x - BLOCK_SIZE); + src_size_mul = blocksize_x * BLOCK_SIZE; + for (y=0; y<(blocksize_y<<2); y += (BLOCK_SIZE_SP)) + { + for (x=0; xpf_get_block_luma( p_Vid, ref2_line, tmp_line, cand2->mv_x + (x<<2) , cand2->mv_y + y, BLOCK_SIZE, BLOCK_SIZE, ref2, 0 ); + p_Dpb->pf_get_block_luma( p_Vid, ref1_line, tmp_line, cand1->mv_x + (x<<2) , cand1->mv_y + y, BLOCK_SIZE, BLOCK_SIZE, ref1, 0 ); + + for (y4 = 0; y4 < BLOCK_SIZE; y4++ ) + { + *d++ = (*src_line++) - ((*ref1_line++ + *ref2_line++ + 1)>>1); + *d++ = (*src_line++) - ((*ref1_line++ + *ref2_line++ + 1)>>1); + *d++ = (*src_line++) - ((*ref1_line++ + *ref2_line++ + 1)>>1); + *d++ = (*src_line++) - ((*ref1_line++ + *ref2_line++ + 1)>>1); + + src_line += src_size_x; + } + mcost += HadamardSAD4x4 (diff); + if(mcost > imin_cost) + return dist_scale_f((distblk)mcost); + } + src_tmp += src_size_mul; + } + } + else + { // 8x8 TRANSFORM + src_size_x = (blocksize_x - BLOCK_SIZE_8x8); + src_size_mul = blocksize_x * BLOCK_SIZE_8x8; + for (y=0; y<(blocksize_y << 2); y += BLOCK_SIZE_8x8_SP ) + { + int y_pos2 = cand2->mv_y + y; + int y_pos1 = cand1->mv_y + y; + for (x=0; xpf_get_block_luma( p_Vid, ref2_line, tmp_line, cand2->mv_x + (x<<2) , y_pos2, BLOCK_SIZE_8x8, BLOCK_SIZE_8x8, ref2, 0 ); + p_Dpb->pf_get_block_luma( p_Vid, ref1_line, tmp_line, cand1->mv_x + (x<<2) , y_pos1, BLOCK_SIZE_8x8, BLOCK_SIZE_8x8, ref1, 0 ); + + for (y4 = 0; y4 < BLOCK_SIZE_8x8; y4++ ) + { + *d++ = (*src_line++) - ((*ref1_line++ + *ref2_line++ + 1)>>1); + *d++ = (*src_line++) - ((*ref1_line++ + *ref2_line++ + 1)>>1); + *d++ = (*src_line++) - ((*ref1_line++ + *ref2_line++ + 1)>>1); + *d++ = (*src_line++) - ((*ref1_line++ + *ref2_line++ + 1)>>1); + *d++ = (*src_line++) - ((*ref1_line++ + *ref2_line++ + 1)>>1); + *d++ = (*src_line++) - ((*ref1_line++ + *ref2_line++ + 1)>>1); + *d++ = (*src_line++) - ((*ref1_line++ + *ref2_line++ + 1)>>1); + *d++ = (*src_line++) - ((*ref1_line++ + *ref2_line++ + 1)>>1); + + src_line += src_size_x; + } + mcost += HadamardSAD8x8 (diff); + if(mcost > imin_cost) + return dist_scale_f((distblk)mcost); + } + src_tmp += src_size_mul; + } + } + + CHECKOVERFLOW(mcost); + return dist_scale((distblk)mcost); +} + +/*! +************************************************************************ +* \brief +* JLT : BiPred (w/ weights) SATD computation ( on-the-fly ) +************************************************************************ +*/ +distblk computeBiPredSATD2_otf(StorablePicture *ref1, + StorablePicture *ref2, + MEBlock *mv_block, + distblk min_mcost, + MotionVector *cand1, + MotionVector *cand2) +{ + int imin_cost = dist_down(min_mcost); + int mcost = 0; + int y, x, y4; + int weighted_pel, pixel1, pixel2; + VideoParameters *p_Vid = mv_block->p_Vid; + Slice *currSlice = mv_block->p_Slice; + DecodedPictureBuffer *p_Dpb = p_Vid->p_Dpb_layer[p_Vid->dpb_layer_id]; + int denom = currSlice->luma_log_weight_denom + 1; + int lround = 2 * currSlice->wp_luma_round; + short weight1 = mv_block->weight1; + short weight2 = mv_block->weight2; + short offsetBi = mv_block->offsetBi; + + + int max_imgpel_value = p_Vid->max_imgpel_value; + int src_size_x, src_size_mul; + imgpel *src_tmp = mv_block->orig_pic[0]; + short *d, diff[MB_PIXELS]; + imgpel *src_line, *ref1_line, *ref2_line, data1[MB_PIXELS], data2[MB_PIXELS] ; + int tmp_line[ (MB_BLOCK_SIZE+5)*(MB_BLOCK_SIZE+5) ] ; + short blocksize_x = mv_block->blocksize_x; + short blocksize_y = mv_block->blocksize_y; + + if ( !mv_block->test8x8 ) + { // 4x4 TRANSFORM + src_size_x = (blocksize_x - BLOCK_SIZE); + src_size_mul = blocksize_x * BLOCK_SIZE; + for (y=0; y<(blocksize_y<<2); y += BLOCK_SIZE_SP) + { + for (x=0; xpf_get_block_luma( p_Vid, ref2_line, tmp_line, cand2->mv_x + (x<<2) , cand2->mv_y + y, BLOCK_SIZE, BLOCK_SIZE, ref2, 0 ); + p_Dpb->pf_get_block_luma( p_Vid, ref1_line, tmp_line, cand1->mv_x + (x<<2) , cand1->mv_y + y, BLOCK_SIZE, BLOCK_SIZE, ref1, 0 ); + + for (y4 = 0; y4 < BLOCK_SIZE; y4++ ) + { + // 0 + pixel1 = weight1 * (*ref1_line++); + pixel2 = weight2 * (*ref2_line++); + weighted_pel = iClip1( max_imgpel_value, ((pixel1 + pixel2 + lround) >> denom) + offsetBi); + *d++ = (short) ((*src_line++) - weighted_pel); + // 1 + pixel1 = weight1 * (*ref1_line++); + pixel2 = weight2 * (*ref2_line++); + weighted_pel = iClip1( max_imgpel_value, ((pixel1 + pixel2 + lround) >> denom) + offsetBi); + *d++ = (short) ((*src_line++) - weighted_pel); + // 2 + pixel1 = weight1 * (*ref1_line++); + pixel2 = weight2 * (*ref2_line++); + weighted_pel = iClip1( max_imgpel_value, ((pixel1 + pixel2 + lround) >> denom) + offsetBi); + *d++ = (short) ((*src_line++) - weighted_pel); + // 3 + pixel1 = weight1 * (*ref1_line++); + pixel2 = weight2 * (*ref2_line++); + weighted_pel = iClip1( max_imgpel_value, ((pixel1 + pixel2 + lround) >> denom) + offsetBi); + *d++ = (short) ((*src_line++) - weighted_pel); + + src_line += src_size_x; + } + mcost += HadamardSAD4x4 (diff); + if(mcost > imin_cost) + return dist_scale_f((distblk)mcost); + } + src_tmp += src_size_mul; + } + } + else + { // 8x8 TRANSFORM + src_size_x = (blocksize_x - BLOCK_SIZE_8x8); + src_size_mul = blocksize_x * BLOCK_SIZE_8x8; + for (y=0; y < (blocksize_y << 2); y += BLOCK_SIZE_8x8_SP ) + { + int y_pos2 = cand2->mv_y + y; + int y_pos1 = cand1->mv_y + y; + for (x=0; xpf_get_block_luma( p_Vid, ref2_line, tmp_line, cand2->mv_x + (x<<2) , y_pos2, BLOCK_SIZE_8x8, BLOCK_SIZE_8x8, ref2, 0 ); + p_Dpb->pf_get_block_luma( p_Vid, ref1_line, tmp_line, cand1->mv_x + (x<<2) , y_pos1, BLOCK_SIZE_8x8, BLOCK_SIZE_8x8, ref1, 0 ); + + for (y4 = 0; y4 < BLOCK_SIZE_8x8; y4++ ) + { + // 0 + pixel1 = weight1 * (*ref1_line++); + pixel2 = weight2 * (*ref2_line++); + weighted_pel = iClip1( max_imgpel_value, ((pixel1 + pixel2 + lround) >> denom) + offsetBi); + *d++ = (short) ((*src_line++) - weighted_pel); + // 1 + pixel1 = weight1 * (*ref1_line++); + pixel2 = weight2 * (*ref2_line++); + weighted_pel = iClip1( max_imgpel_value, ((pixel1 + pixel2 + lround) >> denom) + offsetBi); + *d++ = (short) ((*src_line++) - weighted_pel); + // 2 + pixel1 = weight1 * (*ref1_line++); + pixel2 = weight2 * (*ref2_line++); + weighted_pel = iClip1( max_imgpel_value, ((pixel1 + pixel2 + lround) >> denom) + offsetBi); + *d++ = (short) ((*src_line++) - weighted_pel); + // 3 + pixel1 = weight1 * (*ref1_line++); + pixel2 = weight2 * (*ref2_line++); + weighted_pel = iClip1( max_imgpel_value, ((pixel1 + pixel2 + lround) >> denom) + offsetBi); + *d++ = (short) ((*src_line++) - weighted_pel); + // 4 + pixel1 = weight1 * (*ref1_line++); + pixel2 = weight2 * (*ref2_line++); + weighted_pel = iClip1( max_imgpel_value, ((pixel1 + pixel2 + lround) >> denom) + offsetBi); + *d++ = (short) ((*src_line++) - weighted_pel); + // 5 + pixel1 = weight1 * (*ref1_line++); + pixel2 = weight2 * (*ref2_line++); + weighted_pel = iClip1( max_imgpel_value, ((pixel1 + pixel2 + lround) >> denom) + offsetBi); + *d++ = (short) ((*src_line++) - weighted_pel); + // 6 + pixel1 = weight1 * (*ref1_line++); + pixel2 = weight2 * (*ref2_line++); + weighted_pel = iClip1( max_imgpel_value, ((pixel1 + pixel2 + lround) >> denom) + offsetBi); + *d++ = (short) ((*src_line++) - weighted_pel); + // 7 + pixel1 = weight1 * (*ref1_line++); + pixel2 = weight2 * (*ref2_line++); + weighted_pel = iClip1( max_imgpel_value, ((pixel1 + pixel2 + lround) >> denom) + offsetBi); + *d++ = (short) ((*src_line) - weighted_pel); + + src_line += src_size_x; + } + mcost += HadamardSAD8x8 (diff); + if(mcost > imin_cost) + return dist_scale_f((distblk)mcost); + } + src_tmp += src_size_mul; + } + } + CHECKOVERFLOW(mcost); + return dist_scale((distblk)mcost); +} + +/*! +************************************************************************ +* \brief +* JLT : SSE computation ( on-the-fly ) +************************************************************************ +*/ +distblk computeSSE_otf(StorablePicture *ref1, + MEBlock *mv_block, + distblk min_mcost, + MotionVector *cand + ) +{ + int imin_cost = dist_down(min_mcost); + int mcost = 0; + int y,x; + short blocksize_x = mv_block->blocksize_x; + short blocksize_y = mv_block->blocksize_y; + VideoParameters *p_Vid = mv_block->p_Vid; + DecodedPictureBuffer *p_Dpb = p_Vid->p_Dpb_layer[p_Vid->dpb_layer_id]; + + imgpel *src_line = mv_block->orig_pic[0]; + imgpel *ref_line , data[MB_PIXELS] ; + int tmp_line[ (MB_BLOCK_SIZE+5)*(MB_BLOCK_SIZE+5) ] ; + ref_line = data; + + p_Dpb->pf_get_block_luma( p_Vid, ref_line, tmp_line, cand->mv_x, cand->mv_y, blocksize_x, blocksize_y, ref1, 0 ); + + for (y=0; y imin_cost) + return dist_scale_f((distblk)mcost); + } + + if ( mv_block->ChromaMEEnable ) + { + // calculate chroma conribution to motion compensation error + int blocksize_x_cr = mv_block->blocksize_cr_x; + int blocksize_y_cr = mv_block->blocksize_cr_y; + int k; + int mcr_cost = 0; + + for (k=0; k<2; k++) + { + mcr_cost = 0; + src_line = mv_block->orig_pic[k+1]; + ref_line = data ; + p_Dpb->pf_get_block_chroma[OTF_ME]( p_Vid, ref_line, tmp_line, cand->mv_x, cand->mv_y, blocksize_x_cr, blocksize_y_cr, ref1, k+1 ) ; + for (y=0; yChromaMEWeight * mcr_cost; + if(mcost > imin_cost) + return dist_scale_f((distblk)mcost); + } + } + + CHECKOVERFLOW(mcost); + return dist_scale((distblk)mcost); +} + +/*! +************************************************************************ +* \brief +* JLT : SSE computation of weighted samples ( on-the-fly ) +************************************************************************ +*/ +distblk computeSSEWP_otf(StorablePicture *ref1, + MEBlock *mv_block, + distblk min_mcost, + MotionVector *cand + ) +{ + int imin_cost = dist_down(min_mcost); + int mcost = 0; + int y,x; + int weighted_pel; + short blocksize_x = mv_block->blocksize_x; + short blocksize_y = mv_block->blocksize_y; + VideoParameters *p_Vid = mv_block->p_Vid; + Slice *currSlice = mv_block->p_Slice; + DecodedPictureBuffer *p_Dpb = p_Vid->p_Dpb_layer[p_Vid->dpb_layer_id]; + short weight = mv_block->weight_luma; + short offset = mv_block->offset_luma; + + int wp_luma_round = currSlice->wp_luma_round; + int max_imgpel_value = p_Vid->max_imgpel_value; + short luma_log_weight_denom = currSlice->luma_log_weight_denom; + + imgpel *src_line = mv_block->orig_pic[0]; + imgpel *ref_line , data[MB_PIXELS] ; + int tmp_line[ (MB_BLOCK_SIZE+5)*(MB_BLOCK_SIZE+5) ] ; + + ref_line = data; + p_Dpb->pf_get_block_luma( p_Vid, ref_line, tmp_line, cand->mv_x, cand->mv_y, blocksize_x, blocksize_y, ref1, 0 ); + + for (y=0; y> luma_log_weight_denom) + offset); + mcost += iabs2( *src_line++ - weighted_pel ); + weighted_pel = iClip1( max_imgpel_value, ((weight * *ref_line++ + wp_luma_round) >> luma_log_weight_denom) + offset); + mcost += iabs2( *src_line++ - weighted_pel ); + weighted_pel = iClip1( max_imgpel_value, ((weight * *ref_line++ + wp_luma_round) >> luma_log_weight_denom) + offset); + mcost += iabs2( *src_line++ - weighted_pel ); + weighted_pel = iClip1( max_imgpel_value, ((weight * *ref_line++ + wp_luma_round) >> luma_log_weight_denom) + offset); + mcost += iabs2( *src_line++ - weighted_pel ); + } + if(mcost > imin_cost) + return dist_scale_f((distblk)mcost); + } + + if ( mv_block->ChromaMEEnable ) + { + // calculate chroma conribution to motion compensation error + // These could be made global to reduce computations + int blocksize_x_cr = mv_block->blocksize_cr_x; + int blocksize_y_cr = mv_block->blocksize_cr_y; + int k; + int mcr_cost = 0; + int max_imgpel_value_uv = p_Vid->max_pel_value_comp[1]; + int wp_chroma_round = currSlice->wp_chroma_round; + short chroma_log_weight_denom = currSlice->chroma_log_weight_denom; + + for (k=0; k<2; k++) + { + weight = mv_block->weight_cr[k]; + offset = mv_block->offset_cr[k]; + + mcr_cost = 0; + src_line = mv_block->orig_pic[k+1]; + ref_line = data ; + p_Dpb->pf_get_block_chroma[OTF_ME]( p_Vid, ref_line, tmp_line, cand->mv_x, cand->mv_y, blocksize_x_cr, blocksize_y_cr, ref1, k+1 ) ; + for (y=0; y> chroma_log_weight_denom) + offset); + mcr_cost += iabs2( *src_line++ - weighted_pel ); + weighted_pel = iClip1( max_imgpel_value_uv, ((weight * *ref_line++ + wp_chroma_round) >> chroma_log_weight_denom) + offset); + mcr_cost += iabs2( *src_line++ - weighted_pel ); + } + } + mcost += mv_block->ChromaMEWeight * mcr_cost; + if(mcost > imin_cost) + return dist_scale_f((distblk)mcost); + } + } + + CHECKOVERFLOW(mcost); + return dist_scale((distblk)mcost); +} + +/*! +************************************************************************ +* \brief +* JLT : BiPred SSE computation (no weights) ( on-the-fly ) +************************************************************************ +*/ +distblk computeBiPredSSE1_otf(StorablePicture *ref1, + StorablePicture *ref2, + MEBlock *mv_block, + distblk min_mcost, + MotionVector *cand1, + MotionVector *cand2) +{ + int imin_cost = dist_down(min_mcost); + int mcost = 0; + int bi_diff; + int y,x; + short blocksize_x = mv_block->blocksize_x; + short blocksize_y = mv_block->blocksize_y; + VideoParameters *p_Vid = mv_block->p_Vid; + DecodedPictureBuffer *p_Dpb = p_Vid->p_Dpb_layer[p_Vid->dpb_layer_id]; + + imgpel *src_line, *ref2_line, *ref1_line ; + imgpel data2[MB_PIXELS], data1[MB_PIXELS]; // local allocation could be optimized by a global instanciation + int tmp_line[ (MB_BLOCK_SIZE+5)*(MB_BLOCK_SIZE+5) ] ; + src_line = mv_block->orig_pic[0]; + ref2_line = data2 ; + ref1_line = data1 ; + + p_Dpb->pf_get_block_luma( p_Vid, ref2_line, tmp_line, cand2->mv_x, cand2->mv_y, blocksize_x, blocksize_y, ref2, 0 ); + p_Dpb->pf_get_block_luma( p_Vid, ref1_line, tmp_line, cand1->mv_x, cand1->mv_y, blocksize_x, blocksize_y, ref1, 0 ); + + for (y = 0; y < blocksize_y; y++) + { + for (x = 0; x < blocksize_x; x+=4) + { + bi_diff = (*src_line++) - ((*ref1_line++ + *ref2_line++ + 1)>>1); + mcost += iabs2(bi_diff); + bi_diff = (*src_line++) - ((*ref1_line++ + *ref2_line++ + 1)>>1); + mcost += iabs2(bi_diff); + bi_diff = (*src_line++) - ((*ref1_line++ + *ref2_line++ + 1)>>1); + mcost += iabs2(bi_diff); + bi_diff = (*src_line++) - ((*ref1_line++ + *ref2_line++ + 1)>>1); + mcost += iabs2(bi_diff); + } + if(mcost > imin_cost) + return dist_scale_f((distblk)mcost); + } + + if ( mv_block->ChromaMEEnable ) + { + // calculate chroma conribution to motion compensation error + int blocksize_x_cr = mv_block->blocksize_cr_x; + int blocksize_y_cr = mv_block->blocksize_cr_y; + int k; + int mcr_cost = 0; + + for (k=0; k<2; k++) + { + mcr_cost = 0; + src_line = mv_block->orig_pic[k+1]; + ref2_line = data2 ; + ref1_line = data1 ; + p_Dpb->pf_get_block_chroma[OTF_ME]( p_Vid, ref2_line, tmp_line, cand2->mv_x, cand2->mv_y, blocksize_x_cr, blocksize_y_cr, ref2, k+1 ) ; + p_Dpb->pf_get_block_chroma[OTF_ME]( p_Vid, ref1_line, tmp_line, cand1->mv_x, cand1->mv_y, blocksize_x_cr, blocksize_y_cr, ref1, k+1 ) ; + + for (y=0; y>1); + mcr_cost += iabs2(bi_diff); + bi_diff = (*src_line++) - ((*ref1_line++ + *ref2_line++ + 1)>>1); + mcr_cost += iabs2(bi_diff); + } + } + mcost += mv_block->ChromaMEWeight * mcr_cost; + if(mcost > imin_cost) + return dist_scale_f((distblk)mcost); + } + } + + CHECKOVERFLOW(mcost); + return dist_scale((distblk)mcost); +} + +/*! +************************************************************************ +* \brief +* JLT : BiPred SSE computation (with weights) ( on-the-fly ) +************************************************************************ +*/ +distblk computeBiPredSSE2_otf(StorablePicture *ref1, + StorablePicture *ref2, + MEBlock *mv_block, + distblk min_mcost, + MotionVector *cand1, + MotionVector *cand2) +{ + int imin_cost = dist_down(min_mcost); + int mcost = 0; + int bi_diff; + VideoParameters *p_Vid = mv_block->p_Vid; + Slice *currSlice = mv_block->p_Slice; + DecodedPictureBuffer *p_Dpb = p_Vid->p_Dpb_layer[p_Vid->dpb_layer_id]; + int denom = currSlice->luma_log_weight_denom + 1; + int lround = 2 * currSlice->wp_luma_round; + int max_imgpel_value = p_Vid->max_imgpel_value; + int y,x; + int weighted_pel, pixel1, pixel2; + short weight1 = mv_block->weight1; + short weight2 = mv_block->weight2; + short offsetBi = mv_block->offsetBi; + + short blocksize_x = mv_block->blocksize_x; + short blocksize_y = mv_block->blocksize_y; + + imgpel *src_line, *ref2_line, *ref1_line ; + imgpel data2[MB_PIXELS], data1[MB_PIXELS]; // local allocation could be optimized by a global instanciation + int tmp_line[ (MB_BLOCK_SIZE+5)*(MB_BLOCK_SIZE+5) ] ; + src_line = mv_block->orig_pic[0]; + ref2_line = data2 ; + ref1_line = data1 ; + + p_Dpb->pf_get_block_luma( p_Vid, ref2_line, tmp_line, cand2->mv_x, cand2->mv_y, blocksize_x, blocksize_y, ref2, 0 ); + p_Dpb->pf_get_block_luma( p_Vid, ref1_line, tmp_line, cand1->mv_x, cand1->mv_y, blocksize_x, blocksize_y, ref1, 0 ); + + for (y=0; y> denom) + offsetBi); + bi_diff = (*src_line++) - weighted_pel; + mcost += bi_diff * bi_diff; + + pixel1 = weight1 * (*ref1_line++); + pixel2 = weight2 * (*ref2_line++); + weighted_pel = iClip1( max_imgpel_value, ((pixel1 + pixel2 + lround) >> denom) + offsetBi); + bi_diff = (*src_line++) - weighted_pel; + mcost += bi_diff * bi_diff; + + pixel1 = weight1 * (*ref1_line++); + pixel2 = weight2 * (*ref2_line++); + weighted_pel = iClip1( max_imgpel_value, ((pixel1 + pixel2 + lround) >> denom) + offsetBi); + bi_diff = (*src_line++) - weighted_pel; + mcost += bi_diff * bi_diff; + + pixel1 = weight1 * (*ref1_line++); + pixel2 = weight2 * (*ref2_line++); + weighted_pel = iClip1( max_imgpel_value, ((pixel1 + pixel2 + lround) >> denom) + offsetBi); + bi_diff = (*src_line++) - weighted_pel; + mcost += bi_diff * bi_diff; + } + if(mcost > imin_cost) + return dist_scale_f((distblk)mcost); + } + + if ( mv_block->ChromaMEEnable ) + { + // calculate chroma conribution to motion compensation error + int blocksize_x_cr = mv_block->blocksize_cr_x; + int blocksize_y_cr = mv_block->blocksize_cr_y; + int k; + int mcr_cost = 0; + int max_imgpel_value_uv = p_Vid->max_pel_value_comp[1]; + + for (k=0; k<2; k++) + { + weight1 = mv_block->weight1_cr[k]; + weight2 = mv_block->weight2_cr[k]; + offsetBi = mv_block->offsetBi_cr[k]; + + mcr_cost = 0; + src_line = mv_block->orig_pic[k+1]; + ref2_line = data2 ; + ref1_line = data1 ; + p_Dpb->pf_get_block_chroma[OTF_ME]( p_Vid, ref2_line, tmp_line, cand2->mv_x, cand2->mv_y, blocksize_x_cr, blocksize_y_cr, ref2, k+1 ) ; + p_Dpb->pf_get_block_chroma[OTF_ME]( p_Vid, ref1_line, tmp_line, cand1->mv_x, cand1->mv_y, blocksize_x_cr, blocksize_y_cr, ref1, k+1 ) ; + + for (y=0; y> denom) + offsetBi); + bi_diff = (*src_line++) - weighted_pel; + mcr_cost += bi_diff * bi_diff; + + pixel1 = weight1 * (*ref1_line++); + pixel2 = weight2 * (*ref2_line++); + weighted_pel = iClip1( max_imgpel_value_uv, ((pixel1 + pixel2 + lround) >> denom) + offsetBi); + bi_diff = (*src_line++) - weighted_pel; + mcr_cost += bi_diff * bi_diff; + } + } + mcost += mv_block->ChromaMEWeight * mcr_cost; + if(mcost > imin_cost) + return dist_scale_f((distblk)mcost); + } + } + + CHECKOVERFLOW(mcost); + return dist_scale((distblk)mcost); +} \ No newline at end of file diff --git a/lencod/src/me_epzs.c b/lencod/src/me_epzs.c new file mode 100644 index 0000000..9d46e35 --- /dev/null +++ b/lencod/src/me_epzs.c @@ -0,0 +1,1002 @@ + +/*! +************************************************************************************* +* \file me_epzs.c +* +* \brief +* Motion Estimation using EPZS +* +* \author +* Main contributors (see contributors.h for copyright, address and affiliation details) +* - Alexis Michael Tourapis +* - Athanasios Leontaris +* +************************************************************************************* +*/ + +#include "contributors.h" + +#include + +#include "global.h" +#include "image.h" +#include "memalloc.h" +#include "mb_access.h" +#include "refbuf.h" +#include "macroblock.h" +#include "me_distortion.h" +#include "me_epzs.h" +#include "me_epzs_common.h" +#include "mv_search.h" + +// Functions +/*! +*********************************************************************** +* \brief +* Reset MV to integer position +* AMT/HYC +*********************************************************************** +*/ +static inline void set_integer_mv(MotionVector *mv) +{ + mv->mv_x &= 0xFFFC; + mv->mv_y &= 0xFFFC; +} + +/*! +*********************************************************************** +* \brief +* FAST Motion Estimation using EPZS +* AMT/HYC +*********************************************************************** +*/ +distblk // ==> minimum motion cost after search +EPZS_motion_estimation (Macroblock * currMB, // <-- current Macroblock + MotionVector * pred_mv, // <-- motion vector predictor in sub-pel units + MEBlock * mv_block, // <-- motion vector information + distblk min_mcost, // <-- minimum motion cost (cost for center or huge value) + int lambda_factor // <-- lagrangian parameter for determining motion cost + ) +{ + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + InputParameters *p_Inp = currMB->p_Inp; + EPZSParameters *p_EPZS = currSlice->p_EPZS; + PicMotionParams **motion = p_Vid->enc_picture->mv_info; + + int blocktype = mv_block->blocktype; + + int list = mv_block->list; + int cur_list = list + currMB->list_offset; + short ref = mv_block->ref_idx; + MotionVector *mv = &mv_block->mv[list]; + SearchWindow *searchRange = &mv_block->searchRange; + int mapCenter_x = searchRange->max_x - mv->mv_x; + int mapCenter_y = searchRange->max_y - mv->mv_y; + StorablePicture *ref_picture = currSlice->listX[cur_list][ref]; + + distblk lambda_dist = weighted_cost(lambda_factor, 2); + distblk stopCriterion = p_EPZS->medthres[blocktype] + lambda_dist; + distblk *prevSad = &p_EPZS->distortion[cur_list][blocktype - 1][mv_block->pos_x2]; + + MotionVector *p_motion = NULL; + + EPZSStructure *searchPatternF = p_EPZS->searchPattern; + uint16 **EPZSMap = &p_EPZS->EPZSMap[mapCenter_y]; + uint16 *EPZSPoint = &p_EPZS->EPZSMap[searchRange->max_y][searchRange->max_x]; + + MotionVector center = pad_MVs (*mv, mv_block); + MotionVector pred = pad_MVs (*pred_mv, mv_block); + MotionVector tmp = *mv, cand = center; + + ++p_EPZS->BlkCount; + if (p_EPZS->BlkCount == 0) + ++p_EPZS->BlkCount; + + if (p_Inp->EPZSSpatialMem) + { +#if EPZSREF + p_motion = &p_EPZS->p_motion[cur_list][ref][blocktype - 1][mv_block->block_y][mv_block->pos_x2]; +#else + p_motion = &p_EPZS->p_motion[cur_list][blocktype - 1][mv_block->block_y][mv_block->pos_x2]; +#endif + } + + // Clear EPZSMap + // memset(EPZSMap[0],FALSE,searcharray*searcharray); + // Check median candidate; + //p_EPZS->EPZSMap[0][0] = p_EPZS->BlkCount; + *EPZSPoint = p_EPZS->BlkCount; + + //--- initialize motion cost (cost for motion vector) and check --- + min_mcost = mv_cost (p_Vid, lambda_factor, &cand, &pred); + + //--- add residual cost to motion cost --- + min_mcost += mv_block->computePredFPel (ref_picture, mv_block, DISTBLK_MAX - min_mcost, &cand); + + // Additional threshold for ref>0 + if ((ref > 0 && currSlice->structure == FRAME) && (*prevSad < distblkmin (p_EPZS->medthres[blocktype] + lambda_dist, min_mcost))) + { +#if EPZSREF + if (p_Inp->EPZSSpatialMem) +#else + if (p_Inp->EPZSSpatialMem && ref == 0) +#endif + { + *p_motion = tmp; + } + return min_mcost; + } + + //! If p_EPZS->medthres satisfied, then terminate, otherwise generate Predictors + //! Condition could be strengthened by consideration distortion of adjacent partitions. + if (min_mcost > stopCriterion) + { + SPoint *p_EPZS_point = p_EPZS->predictor->point; + Boolean checkMedian = FALSE; + distblk second_mcost = DISTBLK_MAX; + distblk mcost; + int prednum = 5; + int conditionEPZS; + MotionVector tmp2 = {0, 0}, tmv; + int pos; + short invalid_refs = 0; + + stopCriterion = EPZSDetermineStopCriterion (p_EPZS, prevSad, mv_block, lambda_dist); + + if (min_mcost < (stopCriterion >> 1)) + { +#if EPZSREF + if (p_Inp->EPZSSpatialMem) +#else + if (p_Inp->EPZSSpatialMem && ref == 0) +#endif + { + *p_motion = tmp; + } + + return min_mcost; + } + + //! Add Spatial Predictors in predictor list. + //! Scheme adds zero, left, top-left, top, top-right. Note that top-left adds very little + //! in terms of performance and could be removed with little penalty if any. + invalid_refs = EPZS_spatial_predictors (p_EPZS, mv_block, + list, currMB->list_offset, ref, motion); + + if (p_Inp->EPZSSpatialMem) + EPZS_spatial_memory_predictors (p_EPZS, mv_block, cur_list, &prednum, ref_picture->size_x >> 2); + +#if (MVC_EXTENSION_ENABLE) + if ( p_Inp->EPZSTemporal[currSlice->view_id] && blocktype < 5 ) +#else + // Temporal predictors + if (p_Inp->EPZSTemporal && blocktype < 5) +#endif + { + EPZS_temporal_predictors (currMB, ref_picture, p_EPZS, mv_block, &prednum, stopCriterion, min_mcost); + } + + //! Window Size Based Predictors + //! Basically replaces a Hierarchical ME concept and helps escaping local minima, or + //! determining large motion variations. + //! Following predictors can be adjusted further (i.e. removed, conditioned etc) + //! based on distortion, correlation of adjacent MVs, complexity etc. These predictors + //! and their conditioning could also be moved after all other predictors have been + //! tested. Adaptation could also be based on type of material and coding mode (i.e. + //! field/frame coding,MBAFF etc considering the higher dependency with opposite parity field + //conditionEPZS = ((min_mcost > stopCriterion) + // && (p_Inp->EPZSFixed > 1 || (p_Inp->EPZSFixed && currSlice->slice_type == P_SLICE))); + //conditionEPZS = ((ref == 0) && (blocktype < 5) && (min_mcost > stopCriterion) + //&& (p_Inp->EPZSFixed > 1 || (p_Inp->EPZSFixed && currSlice->slice_type == P_SLICE))); + //conditionEPZS = ((min_mcost > stopCriterion) && ((ref < 2 && blocktype < 4) + conditionEPZS = (p_Inp->EPZSFixed == 3 && (currMB->mb_x == 0 || currMB->mb_y == 0)) + || ((min_mcost > 3 * stopCriterion) && ((ref < 2 && blocktype < 4) || (ref < 1 && blocktype == 4) + || ((currSlice->structure != FRAME || currMB->list_offset) + && ref < 3)) + && (p_Inp->EPZSFixed > 1 || (p_Inp->EPZSFixed && currSlice->slice_type == P_SLICE))); + + if (conditionEPZS) + EPZSWindowPredictors (mv, p_EPZS->predictor, &prednum, + (blocktype < 5) && (invalid_refs > 2) && (ref < 1 + (currSlice->structure != FRAME || currMB->list_offset)) + ? p_EPZS->window_predictor_ext : p_EPZS->window_predictor); + + //! Blocktype/Reference dependent predictors. + //! Since already mvs for other blocktypes/references have been computed, we can reuse + //! them in order to easier determine the optimal point. Use of predictors could depend + //! on cost, + //conditionEPZS = (ref == 0 || (ref > 0 && min_mcost > stopCriterion) || currSlice->structure != FRAME || currMB->list_offset); + conditionEPZS = (ref == 0 || (ref > 0 && min_mcost > 2 * stopCriterion)); + + if (conditionEPZS && currMB->mbAddrX != 0 && p_Inp->EPZSBlockType) + EPZSBlockTypePredictorsMB (currSlice, mv_block, p_EPZS_point, &prednum); + + //! Check all predictors + for (pos = 0; pos < prednum; ++pos) + { + tmv = p_EPZS_point[pos].motion; + set_integer_mv(&tmv); + + //if (((iabs (tmv.mv_x - mv->mv_x) > searchRange->max_x || iabs (tmv.mv_y - mv->mv_y) > searchRange->max_y)) && (tmv.mv_x || tmv.mv_y)) + if ((iabs (tmv.mv_x - mv->mv_x) - searchRange->max_x <= 0) && (iabs (tmv.mv_y - mv->mv_y) - searchRange->max_y <= 0)) + { + EPZSPoint = &EPZSMap[tmv.mv_y][mapCenter_x + tmv.mv_x]; + if (*EPZSPoint != p_EPZS->BlkCount) + { + *EPZSPoint = p_EPZS->BlkCount; + cand = pad_MVs (tmv, mv_block); + + //--- set motion cost (cost for motion vector) and check --- + mcost = mv_cost (p_Vid, lambda_factor, &cand, &pred); + + if (mcost < second_mcost) + { + mcost += mv_block->computePredFPel (ref_picture, mv_block, second_mcost - mcost, &cand); + + //--- check if motion cost is less than minimum cost --- + if (mcost < min_mcost) + { + tmp2 = tmp; + tmp = tmv; + second_mcost = min_mcost; + min_mcost = mcost; + checkMedian = TRUE; + } + //else if (mcost < second_mcost && (tmp.mv_x != tmv.mv_x || tmp.mv_y != tmv.mv_y)) + else if (mcost < second_mcost) + { + tmp2 = tmv; + second_mcost = mcost; + checkMedian = TRUE; + } + } + } + } + } + + //! Refine using EPZS pattern if needed + //! Note that we are using a conservative threshold method. Threshold + //! could be tested after checking only a certain number of predictors + //! instead of the full set. Code could be easily modified for this task. + if (min_mcost > stopCriterion) + { + const int mv_range = 10; + int patternStop = 0, pointNumber = 0, checkPts, nextLast = 0; + int totalCheckPts = 0, motionDirection = 0; + + //! Adapt pattern based on different conditions. + if (p_Inp->EPZSPattern != 0) + { + if ((min_mcost < stopCriterion + ((3 * p_EPZS->medthres[blocktype]) >> 1))) + { + if ((tmp.mv_x == 0 && tmp.mv_y == 0) + || (iabs (tmp.mv_x - mv->mv_x) < (mv_range) && iabs (tmp.mv_y - mv->mv_y) < (mv_range))) + searchPatternF = p_Vid->sdiamond; + else + searchPatternF = p_Vid->square; + } + else if (blocktype > 4 || (ref > 0 && blocktype != 1)) + searchPatternF = p_Vid->square; + else + searchPatternF = p_EPZS->searchPattern; + } + + //! center on best predictor + center = tmp; + + for (;;) + { + totalCheckPts = searchPatternF->searchPoints; + do + { + checkPts = totalCheckPts; + do + { + tmv = add_MVs (center, &(searchPatternF->point[pointNumber].motion)); + + if (((iabs (tmv.mv_x - mv->mv_x) - searchRange->max_x) <= 0) && ((iabs (tmv.mv_y - mv->mv_y) - searchRange->max_y) <= 0)) + { + EPZSPoint = &EPZSMap[tmv.mv_y][mapCenter_x + tmv.mv_x]; + if (*EPZSPoint != p_EPZS->BlkCount) + { + *EPZSPoint = p_EPZS->BlkCount; + cand = pad_MVs (tmv, mv_block); + + mcost = mv_cost (p_Vid, lambda_factor, &cand, &pred); + + if (mcost < min_mcost) + { + mcost += mv_block->computePredFPel (ref_picture, mv_block, min_mcost - mcost, &cand); + + if (mcost < min_mcost) + { + tmp = tmv; + min_mcost = mcost; + motionDirection = pointNumber; + } + } + } + } + ++pointNumber; + if (pointNumber >= searchPatternF->searchPoints) + pointNumber -= searchPatternF->searchPoints; + checkPts--; + } + while (checkPts > 0); + + if (nextLast || ((tmp.mv_x == center.mv_x) && (tmp.mv_y == center.mv_y))) + { + patternStop = searchPatternF->stopSearch; + searchPatternF = searchPatternF->nextpattern; + totalCheckPts = searchPatternF->searchPoints; + nextLast = searchPatternF->nextLast; + motionDirection = 0; + pointNumber = 0; + } + else + { + totalCheckPts = searchPatternF->point[motionDirection].next_points; + pointNumber = searchPatternF->point[motionDirection].start_nmbr; + center = tmp; + } + } + while (patternStop != 1); + + if ((ref > 0) && (currSlice->structure == FRAME) + && ((4 * *prevSad < min_mcost) || ((3 * *prevSad < min_mcost) && (*prevSad <= stopCriterion)))) + { + *mv = tmp; +#if EPZSREF + if (p_Inp->EPZSSpatialMem) +#else + if (p_Inp->EPZSSpatialMem && ref == 0) +#endif + { + *p_motion = tmp; + } + + return min_mcost; + } + + //! Check Second best predictor with EPZS pattern + conditionEPZS = (checkMedian == TRUE) + && ((currSlice->slice_type == P_SLICE) || (blocktype < 5)) + && (min_mcost > stopCriterion) && (p_Inp->EPZSDual > 0); + + if (!conditionEPZS) + break; + + pointNumber = 0; + patternStop = 0; + motionDirection = 0; + nextLast = 0; + + if ((tmp.mv_x == 0 && tmp.mv_y == 0) || (tmp.mv_x == mv->mv_x && tmp.mv_y == mv->mv_y)) + { + if (iabs (tmp.mv_x - mv->mv_x) < (mv_range) && iabs (tmp.mv_y - mv->mv_y) < (mv_range)) + searchPatternF = p_Vid->sdiamond; + else + searchPatternF = p_Vid->square; + } + else + searchPatternF = p_EPZS->searchPatternD; + + //! Second best. Note that following code is identical as for best predictor. + center = tmp2; + checkMedian = FALSE; + } + } + } + + if ((ref == 0) || (*prevSad > min_mcost)) + *prevSad = min_mcost; +#if EPZSREF + if (p_Inp->EPZSSpatialMem) +#else + if (p_Inp->EPZSSpatialMem && ref == 0) +#endif + { + *p_motion = tmp; + //printf("value %d %d %d %d\n", p_motion->mv_x, p_motion->mv_y, p_motion[cur_list][ref][0][0][0].mv_x, p_motion[list + list_offset][ref][0][0][0].mv_y); + //printf("xxxxx %d %d %d %d\n", p_motion->mv_x, p_motion->mv_y, p_motion[cur_list][ref][blocktype - 1][mv_block->block_y][pic_pix_x2].mv_x, p_motion[cur_list][ref][blocktype - 1][mv_block->block_y][pic_pix_x2].mv_y); + } + + *mv = tmp; + + return min_mcost; +} + +/*! +*********************************************************************** +* \brief +* FAST Motion Estimation using EPZS +* AMT/HYC +*********************************************************************** +*/ +distblk // ==> minimum motion cost after search +EPZS_subMB_motion_estimation (Macroblock * currMB, // <-- current Macroblock + MotionVector * pred_mv, // <-- motion vector predictor in sub-pel units + MEBlock * mv_block, // <-- motion vector information + distblk min_mcost, // <-- minimum motion cost (cost for center or huge value) + int lambda_factor // <-- lagrangian parameter for determining motion cost + ) +{ + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + InputParameters *p_Inp = currMB->p_Inp; + EPZSParameters *p_EPZS = currSlice->p_EPZS; + PicMotionParams **motion = p_Vid->enc_picture->mv_info; + + int blocktype = mv_block->blocktype; + int list = mv_block->list; + int cur_list = list + currMB->list_offset; + short ref = mv_block->ref_idx; + StorablePicture *ref_picture = currSlice->listX[cur_list][ref]; + + MotionVector *mv = &mv_block->mv[list]; + MotionVector center = pad_MVs (*mv, mv_block); + MotionVector cand = center; + MotionVector pred = pad_MVs (*pred_mv, mv_block); + MotionVector tmp = *mv; + SearchWindow *searchRange = &mv_block->searchRange; + int mapCenter_x = searchRange->max_x - mv->mv_x; + int mapCenter_y = searchRange->max_y - mv->mv_y; + + short pic_pix_x2 = mv_block->pos_x2; + distblk lambda_dist = weighted_cost(lambda_factor,3); + distblk stopCriterion = p_EPZS->medthres[blocktype] + lambda_dist; + distblk *prevSad = &p_EPZS->distortion[cur_list][blocktype - 1][pic_pix_x2]; + MotionVector *p_motion = NULL; + + EPZSStructure *searchPatternF = p_EPZS->searchPattern; + uint16 **EPZSMap = &p_EPZS->EPZSMap[mapCenter_y]; + + ++p_EPZS->BlkCount; + if (p_EPZS->BlkCount == 0) + ++p_EPZS->BlkCount; + + if (p_Inp->EPZSSpatialMem) + { +#if EPZSREF + p_motion = &p_EPZS->p_motion[cur_list][ref][blocktype - 1][mv_block->block_y][pic_pix_x2]; +#else + p_motion = &p_EPZS->p_motion[cur_list][blocktype - 1][mv_block->block_y][pic_pix_x2]; +#endif + } + + // Clear p_EPZS->EPZSMap + // memset(p_EPZS->EPZSMap[0],FALSE,searcharray*searcharray); + // Check median candidate; + //p_EPZS->EPZSMap[0][0] = p_EPZS->BlkCount; + p_EPZS->EPZSMap[searchRange->max_y][searchRange->max_x] = p_EPZS->BlkCount; + + //--- initialize motion cost (cost for motion vector) and check --- + min_mcost = mv_cost (p_Vid, lambda_factor, &cand, &pred); + + //--- add residual cost to motion cost --- + min_mcost += mv_block->computePredFPel (ref_picture, mv_block, DISTBLK_MAX-min_mcost, &cand); + + // Additional threshold for ref>0 + if ((ref > 0 && currSlice->structure == FRAME) && + (*prevSad < distblkmin (p_EPZS->medthres[blocktype] + lambda_dist, min_mcost))) + { +#if EPZSREF + if (p_Inp->EPZSSpatialMem) +#else + if (p_Inp->EPZSSpatialMem && ref == 0) +#endif + { + *p_motion = tmp; + } + return min_mcost; + } + + //! If p_EPZS->medthres satisfied, then terminate, otherwise generate Predictors + //! Condition could be strengthened by consideration distortion of adjacent partitions. + if (min_mcost > stopCriterion) + { + SPoint *p_EPZS_point = p_EPZS->predictor->point; + Boolean checkMedian = FALSE; + distblk second_mcost = DISTBLK_MAX; + distblk mcost; + int prednum = 5; + int conditionEPZS; + + MotionVector tmv, tmp2 = {0,0}; + int pos; + + stopCriterion = EPZSDetermineStopCriterion (p_EPZS, prevSad, mv_block, lambda_dist); + + if (min_mcost < (stopCriterion >> 1)) + { +#if EPZSREF + if (p_Inp->EPZSSpatialMem) +#else + if (p_Inp->EPZSSpatialMem && ref == 0) +#endif + { + *p_motion = tmp; + } + return min_mcost; + } + + //! Add Spatial Predictors in predictor list. + //! Scheme adds zero, left, top-left, top, top-right. Note that top-left adds very little + //! in terms of performance and could be removed with little penalty if any. + EPZS_spatial_predictors (p_EPZS, mv_block/*->block*/, list, currMB->list_offset, ref, motion); + + if (p_Inp->EPZSSpatialMem) + EPZS_spatial_memory_predictors (p_EPZS, mv_block, cur_list, &prednum, ref_picture->size_x >> 2); + + //! Blocktype/Reference dependent predictors. + //! Since already mvs for other blocktypes/references have been computed, we can reuse + //! them in order to easier determine the optimal point. Use of predictors could depend + //! on cost, + conditionEPZS = (ref == 0 || (ref > 0 && min_mcost > 2 * stopCriterion)); + + if (conditionEPZS && currMB->mbAddrX != 0 && p_Inp->EPZSBlockType) + EPZSBlockTypePredictors (currSlice, mv_block, p_EPZS_point, &prednum); + + //! Check all predictors + for (pos = 0; pos < prednum; ++pos) + { + tmv = p_EPZS_point[pos].motion; + set_integer_mv(&tmv); + + //if (((iabs (tmv.mv_x - mv->mv_x) > searchRange->max_x || iabs (tmv.mv_y - mv->mv_y) > searchRange->max_y)) && (tmv.mv_x || tmv.mv_y)) + if ((iabs (tmv.mv_x - mv->mv_x) - searchRange->max_x <= 0) && (iabs (tmv.mv_y - mv->mv_y) - searchRange->max_y <= 0)) + { + + if (EPZSMap[tmv.mv_y][mapCenter_x + tmv.mv_x] != p_EPZS->BlkCount) + { + EPZSMap[tmv.mv_y][mapCenter_x + tmv.mv_x] = p_EPZS->BlkCount; + + cand = pad_MVs (tmv, mv_block); + + //--- set motion cost (cost for motion vector) and check --- + mcost = mv_cost (p_Vid, lambda_factor, &cand, &pred); + + if (mcost < second_mcost) + { + mcost += mv_block->computePredFPel (ref_picture, mv_block, second_mcost - mcost, &cand); + + //--- check if motion cost is less than minimum cost --- + if (mcost < min_mcost) + { + tmp2 = tmp; + tmp = tmv; + second_mcost = min_mcost; + min_mcost = mcost; + checkMedian = TRUE; + } + //else if (mcost < second_mcost && (tmp.mv_x != tmv.mv_x || tmp.mv_y != tmv.mv_y)) + else if (mcost < second_mcost) + { + tmp2 = tmv; + second_mcost = mcost; + checkMedian = TRUE; + } + } + } + } + + + // At this point, let us add an early termination criterion + // after checking each predictor. This can help speed up a lot. + if (min_mcost < ((3 * stopCriterion) >> 2)) + { +#if EPZSREF + if (p_Inp->EPZSSpatialMem) +#else + if (p_Inp->EPZSSpatialMem && ref == 0) +#endif + { + *p_motion = tmp; + } + *mv = tmp; + return min_mcost; + } + } + + //! Refine using EPZS pattern if needed + //! Note that we are using a conservative threshold method. Threshold + //! could be tested after checking only a certain number of predictors + //! instead of the full set. Code could be easily modified for this task. + if (min_mcost > stopCriterion) + { + int patternStop = 0, pointNumber = 0, checkPts, nextLast = 0; + int totalCheckPts = 0, motionDirection = 0; + const int mv_range = 12; + + //! Adapt pattern based on different conditions. + if (p_Inp->EPZSPattern != 0) + { + if ((min_mcost < stopCriterion + ((3 * p_EPZS->medthres[blocktype]) >> 1))) + { + if ((tmp.mv_x == 0 && tmp.mv_y == 0) || (iabs (tmp.mv_x - mv->mv_x) < (mv_range) && iabs (tmp.mv_y - mv->mv_y) < (mv_range))) + searchPatternF = p_Vid->sdiamond; + else + searchPatternF = p_Vid->square; + } + else + searchPatternF = p_Vid->square; + } + + //! center on best predictor + center = tmp; + for (;;) + { + totalCheckPts = searchPatternF->searchPoints; + do + { + checkPts = totalCheckPts; + do + { + tmv = add_MVs (center, &(searchPatternF->point[pointNumber].motion)); + + if ((iabs (tmv.mv_x - mv->mv_x) <= searchRange->max_x) && (iabs (tmv.mv_y - mv->mv_y) <= searchRange->max_y)) + { + if (EPZSMap[tmv.mv_y][mapCenter_x + tmv.mv_x] != p_EPZS->BlkCount) + { + EPZSMap[tmv.mv_y][mapCenter_x + tmv.mv_x] = p_EPZS->BlkCount; + cand = pad_MVs(tmv, mv_block); + + mcost = mv_cost (p_Vid, lambda_factor, &cand, &pred); + if (mcost < min_mcost) + { + + mcost += mv_block->computePredFPel (ref_picture, mv_block, min_mcost - mcost, &cand); + + if (mcost < min_mcost) + { + tmp = tmv; + min_mcost = mcost; + motionDirection = pointNumber; + } + } + } + } + ++pointNumber; + if (pointNumber >= searchPatternF->searchPoints) + pointNumber -= searchPatternF->searchPoints; + checkPts--; + } + while (checkPts > 0); + + if (nextLast || ((tmp.mv_x == center.mv_x) && (tmp.mv_y == center.mv_y))) + { + patternStop = searchPatternF->stopSearch; + searchPatternF = searchPatternF->nextpattern; + totalCheckPts = searchPatternF->searchPoints; + nextLast = searchPatternF->nextLast; + motionDirection = 0; + pointNumber = 0; + } + else + { + totalCheckPts = searchPatternF->point[motionDirection].next_points; + pointNumber = searchPatternF->point[motionDirection].start_nmbr; + center = tmp; + } + } + while (patternStop != 1); + + if ((ref > 0) && (currSlice->structure == FRAME) + && ((4 * *prevSad < min_mcost) || ((3 * *prevSad < min_mcost) && (*prevSad <= stopCriterion)))) + { + *mv = tmp; +#if EPZSREF + if (p_Inp->EPZSSpatialMem) +#else + if (p_Inp->EPZSSpatialMem && ref == 0) +#endif + { + *p_motion = tmp; + } + + return min_mcost; + } + + //! Check Second best predictor with EPZS pattern + conditionEPZS = (checkMedian == TRUE) + && ((currSlice->slice_type == P_SLICE)) + && (min_mcost > stopCriterion) && (p_Inp->EPZSDual > 0); + + if (!conditionEPZS) + break; + + pointNumber = 0; + patternStop = 0; + motionDirection = 0; + nextLast = 0; + + if ((tmp.mv_x == 0 && tmp.mv_y == 0) || (tmp.mv_x == mv->mv_x && tmp.mv_y == mv->mv_y)) + { + if (iabs (tmp.mv_x - mv->mv_x) < (mv_range) && iabs (tmp.mv_y - mv->mv_y) < (mv_range)) + searchPatternF = p_Vid->sdiamond; + else + searchPatternF = p_Vid->square; + } + else + searchPatternF = p_EPZS->searchPatternD; + + //! Now consider second best. + center = tmp2; + checkMedian = FALSE; + } + } + } + + if ((ref == 0) || (*prevSad > min_mcost)) + *prevSad = min_mcost; + +#if EPZSREF + if (p_Inp->EPZSSpatialMem) +#else + if (p_Inp->EPZSSpatialMem && ref == 0) +#endif + { + *p_motion = tmp; + } + + *mv = tmp; + + return min_mcost; +} + +/*! +*********************************************************************** +* \brief +* FAST Motion Estimation using EPZS +* AMT/HYC +* \return +* minimum motion cost after search +*********************************************************************** +*/ +distblk // ==> minimum motion cost after search +EPZS_bipred_motion_estimation (Macroblock * currMB, // <-- Current Macroblock + int list, // <-- reference list + MotionVector * pred_mv1, // <-- motion vector predictor in sub-pel units + MotionVector * pred_mv2, // <-- motion vector predictor in sub-pel units + MotionVector * mv1, // <--> in: search center (x|y) / out: motion vector (x|y) - in pel units + MotionVector * mv2, // <--> in: search center (x|y) + MEBlock * mv_block, // <-- motion vector information + int search_range, // <-- 1-d search range in pel units + distblk min_mcost, // <-- minimum motion cost (cost for center or huge value) + int lambda_factor // <-- lagrangian parameter for determining motion cost + ) +{ + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + InputParameters *p_Inp = currMB->p_Inp; + EPZSParameters *p_EPZS = currSlice->p_EPZS; + PicMotionParams **motion = p_Vid->enc_picture->mv_info; + + int blocktype = mv_block->blocktype; + short ref = mv_block->ref_idx; + + StorablePicture *ref_picture1 = currSlice->listX[list + currMB->list_offset][ref]; + StorablePicture *ref_picture2 = currSlice->listX[(list ^ 1) + currMB->list_offset][0]; + + int mapCenter_x = search_range - mv1->mv_x; + int mapCenter_y = search_range - mv1->mv_y; + distblk lambda_dist = weighted_cost(lambda_factor, 4); + distblk stopCriterion = p_EPZS->medthres[blocktype] + lambda_dist; + distblk *prevSad = &p_EPZS->bi_distortion[list + currMB->list_offset][blocktype - 1][mv_block->pos_x2]; + EPZSStructure *searchPatternF = p_EPZS->searchPattern; + uint16 **EPZSMap = &p_EPZS->EPZSMap[mapCenter_y]; + + MotionVector tmp = *mv1; + MotionVector center1 = pad_MVs (*mv1, mv_block); + MotionVector center2 = pad_MVs (*mv2, mv_block); + MotionVector pred1 = pad_MVs (*pred_mv1, mv_block); + MotionVector pred2 = pad_MVs (*pred_mv2, mv_block); + MotionVector cand1 = center1; + MotionVector cand2 = center2; + + ++p_EPZS->BlkCount; + if (p_EPZS->BlkCount == 0) + ++p_EPZS->BlkCount; + + + // Clear p_EPZS->EPZSMap + //memset(p_EPZS->EPZSMap[0],FALSE,searcharray*searcharray); + // Check median candidate; + //p_EPZS->EPZSMap[0][0] = p_EPZS->BlkCount; + p_EPZS->EPZSMap[search_range][search_range] = p_EPZS->BlkCount; + + //--- initialize motion cost (cost for motion vector) and check --- + min_mcost = mv_cost (p_Vid, lambda_factor, &cand1, &pred1); + min_mcost += mv_cost (p_Vid, lambda_factor, &cand2, &pred2); + + //--- add residual cost to motion cost --- + min_mcost += mv_block->computeBiPredFPel (ref_picture1, ref_picture2, mv_block, DISTBLK_MAX-min_mcost, &cand1, &cand2); + + //! If p_EPZS->medthres satisfied, then terminate, otherwise generate Predictors + if (min_mcost > stopCriterion) + { + SPoint *p_EPZS_point = p_EPZS->predictor->point; + Boolean checkMedian = FALSE; + distblk second_mcost = DISTBLK_MAX; + distblk mcost; + int prednum = 5; + MotionVector tmv, tmp2 = { 0, 0 }; + int pos; + + stopCriterion = EPZSDetermineStopCriterion (p_EPZS, prevSad, mv_block, lambda_dist); + + //! Add Spatial Predictors in predictor list. + //! Scheme adds zero, left, top-left, top, top-right. Note that top-left adds very little + //! in terms of performance and could be removed with little penalty if any. + EPZS_spatial_predictors (p_EPZS, mv_block/*->block*/, list, currMB->list_offset, ref, motion); + + //! Check all predictors + for (pos = 0; pos < prednum; ++pos) + { + tmv = p_EPZS_point[pos].motion; + set_integer_mv(&tmv); + //if ((iabs (tmv.mv_x - mv1->mv_x) > search_range || iabs (tmv.mv_y - mv1->mv_y) > search_range) && (tmv.mv_x || tmv.mv_y)) + if (iabs (tmv.mv_x - mv1->mv_x) <= search_range && iabs (tmv.mv_y - mv1->mv_y) <= search_range) + { + if (EPZSMap[tmv.mv_y][mapCenter_x + tmv.mv_x] != p_EPZS->BlkCount) + { + EPZSMap[tmv.mv_y][mapCenter_x + tmv.mv_x] = p_EPZS->BlkCount; + + cand1 = pad_MVs (tmv, mv_block); + + //--- set motion cost (cost for motion vector) and check --- + mcost = mv_cost (p_Vid, lambda_factor, &cand1, &pred1); + mcost += mv_cost (p_Vid, lambda_factor, &cand2, &pred2); + + if (mcost >= second_mcost) + continue; + + mcost += mv_block->computeBiPredFPel (ref_picture1, ref_picture2, mv_block, second_mcost - mcost, &cand1, &cand2); + + //--- check if motion cost is less than minimum cost --- + if (mcost < min_mcost) + { + tmp2 = tmp; + tmp = tmv; + second_mcost = min_mcost; + min_mcost = mcost; + checkMedian = TRUE; + } + //else if (mcost < second_mcost && (tmp.mv_x != tmv.mv_x || tmp.mv_y != tmv.mv_y)) + else if (mcost < second_mcost) + { + tmp2 = tmv; + second_mcost = mcost; + checkMedian = TRUE; + } + } + } + } + + //! Refine using EPZS pattern if needed + //! Note that we are using a conservative threshold method. Threshold + //! could be tested after checking only a certain number of predictors + //! instead of the full set. Code could be easily modified for this task. + if (min_mcost > stopCriterion) + { + int conditionEPZS; + int patternStop = 0, pointNumber = 0, checkPts, nextLast = 0; + int totalCheckPts = 0, motionDirection = 0; + + const int mv_range = 12; + + //! Adapt pattern based on different conditions. + if (p_Inp->EPZSPattern != 0) + { + if ((min_mcost < stopCriterion + ((4 * p_EPZS->medthres[blocktype]) >> 1))) + { + if ((tmp.mv_x == 0 && tmp.mv_y == 0) || (iabs (tmp.mv_x - mv1->mv_x) < (mv_range) && iabs (tmp.mv_y - mv1->mv_y) < (mv_range))) + searchPatternF = p_Vid->sdiamond; + else + searchPatternF = p_Vid->square; + } + else if (blocktype > 5 || (ref > 0 && blocktype != 1)) + searchPatternF = p_Vid->square; + else + searchPatternF = p_EPZS->searchPattern; + } + + //! center on best predictor + center1 = tmp; + for (;;) + { + totalCheckPts = searchPatternF->searchPoints; + do + { + checkPts = totalCheckPts; + do + { + tmv = add_MVs (center1, &(searchPatternF->point[pointNumber].motion)); + + if ((iabs (tmv.mv_x - mv1->mv_x) <= search_range) && (iabs (tmv.mv_y - mv1->mv_y) <= search_range)) + { + if (EPZSMap[tmv.mv_y][mapCenter_x + tmv.mv_x] != p_EPZS->BlkCount) + { + EPZSMap[tmv.mv_y][mapCenter_x + tmv.mv_x] = p_EPZS->BlkCount; + cand1 = pad_MVs (tmv, mv_block); + + mcost = mv_cost (p_Vid, lambda_factor, &cand1, &pred1); + mcost += mv_cost (p_Vid, lambda_factor, &cand2, &pred2); + + if (mcost < min_mcost) + { + mcost += mv_block->computeBiPredFPel (ref_picture1, ref_picture2, mv_block, min_mcost - mcost, &cand1, &cand2); + + if (mcost < min_mcost) + { + tmp = tmv; + min_mcost = mcost; + motionDirection = pointNumber; + } + } + } + } + ++pointNumber; + if (pointNumber >= searchPatternF->searchPoints) + pointNumber -= searchPatternF->searchPoints; + checkPts--; + } + while (checkPts > 0); + + if (nextLast || ((tmp.mv_x == center1.mv_x) && (tmp.mv_y == center1.mv_y))) + { + patternStop = searchPatternF->stopSearch; + searchPatternF = searchPatternF->nextpattern; + totalCheckPts = searchPatternF->searchPoints; + nextLast = searchPatternF->nextLast; + motionDirection = 0; + pointNumber = 0; + } + else + { + totalCheckPts = searchPatternF->point[motionDirection].next_points; + pointNumber = searchPatternF->point[motionDirection].start_nmbr; + center1 = tmp; + } + } + while (patternStop != 1); + + //! Check Second best predictor with EPZS pattern + conditionEPZS = (checkMedian == TRUE) && (blocktype < 5) && (min_mcost > stopCriterion) && (p_Inp->EPZSDual > 0); + + if (!conditionEPZS) + break; + + pointNumber = 0; + patternStop = 0; + motionDirection = 0; + nextLast = 0; + + if ((tmp.mv_x == 0 && tmp.mv_y == 0) || (tmp.mv_x == mv1->mv_x && tmp.mv_y == mv1->mv_y)) + { + if (iabs (tmp.mv_x - mv1->mv_x) < (mv_range) && iabs (tmp.mv_y - mv1->mv_y) < (mv_range)) + searchPatternF = p_Vid->sdiamond; + else + searchPatternF = p_Vid->square; + } + else + searchPatternF = p_EPZS->searchPatternD; + totalCheckPts = searchPatternF->searchPoints; + + //! Second best. Note that following code is identical as for best predictor. + center1 = tmp2; + checkMedian = FALSE; + } + } + } + + if (mv_block->iteration_no == 0) + { + *prevSad = min_mcost; + } + + *mv1 = tmp; + + return min_mcost; +} + diff --git a/lencod/src/me_epzs_common.c b/lencod/src/me_epzs_common.c new file mode 100644 index 0000000..6d3ace6 --- /dev/null +++ b/lencod/src/me_epzs_common.c @@ -0,0 +1,1862 @@ + +/*! +************************************************************************************* +* \file me_epzs_common.c +* +* \brief +* Common functions for EPZS scheme +* +* \author +* Main contributors (see contributors.h for copyright, address and affiliation details) +* - Alexis Michael Tourapis +* +************************************************************************************* +*/ + +#include + +#include "contributors.h" +#include "global.h" +#include "image.h" +#include "memalloc.h" +#include "mb_access.h" +#include "refbuf.h" +#include "macroblock.h" +#include "me_distortion.h" +#include "me_epzs.h" +#include "me_epzs_common.h" +#include "me_epzs_int.h" +#include "me_fullsearch.h" +#include "mv_search.h" + +static const short BLOCK_PARENT[8] = { 1, 1, 1, 1, 2, 4, 4, 5 }; //!< {skip, 16x16, 16x8, 8x16, 8x8, 8x4, 4x8, 4x4} +static const int MIN_THRES_BASE[8] = { 0, 64, 32, 32, 16, 8, 8, 4 }; +static const int MED_THRES_BASE[8] = { 0, 256, 128, 128, 64, 32, 32, 16 }; +static const int MAX_THRES_BASE[8] = { 0, 768, 384, 384, 192, 96, 96, 48 }; + +// Other definitions +static const char EPZS_PATTERN[6][20] = { "Diamond", "Square", "Extended Diamond", "Large Diamond", "SBP Large Diamond", "PMVFAST" }; +static const char EPZS_DUAL_PATTERN[7][20] = +{ "Disabled", "Diamond", "Square", "Extended Diamond", "Large Diamond", "SBP Large Diamond", "PMVFAST" }; +static const char EPZS_FIXED_PREDICTORS[4][20] = { "Disabled", "All P", "All P + B", "Aggressive" }; +static const char EPZS_OTHER_PREDICTORS[2][20] = { "Disabled", "Enabled" }; +static const char EPZS_SUBPEL_METHOD[3][20] = { "Full", "Basic", "Enhanced" }; + +//! Define EPZS Refinement patterns +static const short pattern_data[7][12][4] = +{ + { // Small Diamond pattern + { 0, 4, 3, 3 }, { 4, 0, 0, 3 }, { 0, -4, 1, 3 }, { -4, 0, 2, 3 } + }, + { // Square pattern + { 0, 4, 7, 3 }, { 4, 4, 7, 5 }, { 4, 0, 1, 3 }, { 4, -4, 1, 5 }, + { 0, -4, 3, 3 }, { -4, -4, 3, 5 }, { -4, 0, 5, 3 }, { -4, 4, 5, 5 } + }, + { // Enhanced Diamond pattern + { -4, 4, 10, 5 }, { 0, 8, 10, 8 }, { 0, 4, 10, 7 }, { 4, 4, 1, 5 }, + { 8, 0, 1, 8 }, { 4, 0, 1, 7 }, { 4, -4, 4, 5 }, { 0, -8, 4, 8 }, + { 0, -4, 4, 7 }, { -4, -4, 7, 5 }, { -8, 0, 7, 8 }, { -4, 0, 7, 7 } + + }, + { // Large Diamond pattern + { 0, 8, 6, 5 }, { 4, 4, 0, 3 }, { 8, 0, 0, 5 }, { 4, -4, 2, 3 }, + { 0, -8, 2, 5 }, { -4, -4, 4, 3 }, { -8, 0, 4, 5 }, { -4, 4, 6, 3 } + }, + { // Extended Subpixel pattern + { 0, 8, 6, 12 }, { 4, 4, 0, 12 }, { 8, 0, 0, 12 }, { 4, -4, 2, 12 }, + { 0, -8, 2, 12 }, { -4, -4, 4, 12 }, { -8, 0, 4, 12 }, { -4, 4, 6, 12 }, + { 0, 2, 6, 12 }, { 2, 0, 0, 12 }, { 0, -2, 2, 12 }, { -2, 0, 4, 12 } + } +}; + +/*! +************************************************************************ +* \brief +* Allocate EPZS pattern memory +* +* \param searchpoints +* number of searchpoints to allocate +* +* \return +* the allocated EPZSStructure structure +************************************************************************ +*/ +static EPZSStructure * +allocEPZSpattern (int searchpoints) +{ + EPZSStructure *s; + s = calloc (1, sizeof (EPZSStructure)); + + if (NULL == s) + no_mem_exit ("alloc_EPZSpattern: s"); + + s->searchPoints = searchpoints; + s->point = (SPoint *) calloc (searchpoints, sizeof (SPoint)); + + return s; +} + +/*! +************************************************************************ +* \brief +* Free EPZS pattern memory. +* +* \param p +* structure to be freed +* +************************************************************************ +*/ +static void +freeEPZSpattern (EPZSStructure * p) +{ + if (p) + { + free ((SPoint *) p->point); + free (p); + p = NULL; + } +} + +/*! +************************************************************************ +* \brief +* Assign EPZS pattern +* +* +************************************************************************ +*/ +static void +assignEPZSpattern (EPZSStructure * pattern, int type, int stopSearch, int nextLast, EPZSStructure * nextpattern) +{ + int i; + + for (i = 0; i < pattern->searchPoints; ++i) + { + pattern->point[i].motion.mv_x = pattern_data[type][i][0]; + pattern->point[i].motion.mv_y = pattern_data[type][i][1]; + pattern->point[i].start_nmbr = pattern_data[type][i][2]; + pattern->point[i].next_points = pattern_data[type][i][3]; + } + pattern->stopSearch = stopSearch; + pattern->nextLast = nextLast; + pattern->nextpattern = nextpattern; +} + +/*! +************************************************************************ +* \brief +* Setup EPZS ME engine +************************************************************************ +*/ +void EPZS_setup_engine(Macroblock *currMB, InputParameters *p_Inp) +{ + currMB->IntPelME = (p_Inp->EPZSSubPelGrid) ? EPZS_integer_motion_estimation : EPZS_motion_estimation; + currMB->BiPredME = (p_Inp->EPZSSubPelGrid) ? EPZS_integer_bipred_motion_estimation : EPZS_bipred_motion_estimation; + if (p_Inp->EPZSSubPelME == 1) + currMB->SubPelME = EPZS_sub_pel_motion_estimation; + else if (p_Inp->EPZSSubPelME == 2) + currMB->SubPelME = full_sub_pel_motion_estimation; + else + currMB->SubPelME = sub_pel_motion_estimation; + if (p_Inp->EPZSSubPelMEBiPred == 1) + currMB->SubPelBiPredME = EPZS_sub_pel_bipred_motion_estimation; + else if (p_Inp->EPZSSubPelMEBiPred == 2) + currMB->SubPelBiPredME = full_sub_pel_bipred_motion_estimation; + else + currMB->SubPelBiPredME = sub_pel_bipred_motion_estimation; +} + +/*! +************************************************************************ +* \brief +* EPZS Global Initialization +************************************************************************ +*/ +int +EPZSInit (VideoParameters * p_Vid) +{ + + int memory_size = 0; + + //! Definition of pottential EPZS patterns. + //! It is possible to also define other patterns, or even use + //! resizing patterns (such as the PMVFAST scheme. These patterns + //! are only shown here as reference, while the same also holds + //! for this implementation (i.e. new conditions could be added + //! on adapting predictors, or thresholds etc. Note that search + //! could also be performed on subpel positions directly while + //! pattern needs not be restricted on integer positions only. + + //! Allocate memory and assign search patterns + if(!p_Vid->sdiamond) + { + p_Vid->sdiamond = allocEPZSpattern (4); + assignEPZSpattern (p_Vid->sdiamond, SDIAMOND, TRUE, TRUE, p_Vid->sdiamond); + } + if(!p_Vid->square) + { + p_Vid->square = allocEPZSpattern (8); + assignEPZSpattern (p_Vid->square, SQUARE, TRUE, TRUE, p_Vid->square); + } + if(!p_Vid->ediamond) + { + p_Vid->ediamond = allocEPZSpattern (12); + assignEPZSpattern (p_Vid->ediamond, EDIAMOND, TRUE, TRUE, p_Vid->ediamond); + } + if(!p_Vid->ldiamond) + { + p_Vid->ldiamond = allocEPZSpattern (8); + assignEPZSpattern (p_Vid->ldiamond, LDIAMOND, TRUE, TRUE, p_Vid->ldiamond); + } + if(!p_Vid->sbdiamond) + { + p_Vid->sbdiamond = allocEPZSpattern (12); + assignEPZSpattern (p_Vid->sbdiamond, SBDIAMOND, FALSE, TRUE, p_Vid->sdiamond); + } + if(!p_Vid->pmvfast) + { + p_Vid->pmvfast = allocEPZSpattern (8); + assignEPZSpattern (p_Vid->pmvfast, LDIAMOND, FALSE, TRUE, p_Vid->sdiamond); + } + + return memory_size; +} + +/*! +************************************************************************ +* \brief +* Delete EPZS Alocated memory +************************************************************************ +*/ +void +EPZSDelete (VideoParameters * p_Vid) +{ + // Free search patterns + if(p_Vid->pmvfast) + { + freeEPZSpattern (p_Vid->pmvfast); + p_Vid->pmvfast = NULL; + } + if(p_Vid->sbdiamond) + { + freeEPZSpattern (p_Vid->sbdiamond); + p_Vid->sbdiamond = NULL; + } + if(p_Vid->ldiamond) + { + freeEPZSpattern (p_Vid->ldiamond); + p_Vid->ldiamond = NULL; + } + if(p_Vid->ediamond) + { + freeEPZSpattern (p_Vid->ediamond); + p_Vid->ediamond = NULL; + } + if(p_Vid->sdiamond) + { + freeEPZSpattern (p_Vid->sdiamond); + p_Vid->sdiamond = NULL; + } + if(p_Vid->square) + { + freeEPZSpattern (p_Vid->square); + p_Vid->square = NULL; + } +} + +/*! +************************************************************************ +* \brief +* Allocate co-located memory +* +* \param size_x +* horizontal luma size +* \param size_y +* vertical luma size +* \param mb_adaptive_frame_field_flag +* flag that indicates macroblock adaptive frame/field coding +* +* \return +* the allocated EPZSColocParams structure +************************************************************************ +*/ +static EPZSColocParams * +allocEPZScolocated (int size_x, int size_y, int mb_adaptive_frame_field_flag) +{ + EPZSColocParams *s; + s = calloc (1, sizeof (EPZSColocParams)); + if (NULL == s) + no_mem_exit ("alloc_EPZScolocated: s"); + + s->size_x = size_x; + s->size_y = size_y; + get_mem3Dmv (&(s->frame), 2, (size_y >> BLOCK_SHIFT), (size_x >> BLOCK_SHIFT)); + + if (mb_adaptive_frame_field_flag) + { + get_mem3Dmv (&(s->top), 2, (size_y >> (BLOCK_SHIFT + 1)), (size_x >> BLOCK_SHIFT)); + get_mem3Dmv (&(s->bot), 2, (size_y >> (BLOCK_SHIFT + 1)), (size_x >> BLOCK_SHIFT)); + } + + s->mb_adaptive_frame_field_flag = mb_adaptive_frame_field_flag; + + return s; +} + +/*! +************************************************************************ +* \brief +* Free co-located memory. +* +* \param p +* structure to be freed +* +************************************************************************ +*/ +static void +freeEPZScolocated (EPZSColocParams * p) +{ + + if (p) + { + free_mem3Dmv (p->frame); + if (p->mb_adaptive_frame_field_flag) + { + free_mem3Dmv (p->top); + free_mem3Dmv (p->bot); + } + + free (p); + p = NULL; + } +} + +/*! +************************************************************************ +* \brief +* EPZS Search Window Predictor Initialization +************************************************************************ +*/ +static void +EPZSWindowPredictorInit (short search_range, EPZSStructure * predictor, short mode) +{ + short pos; + short searchpos, fieldsearchpos; + int prednum = -1; + short i; + short search_range_qpel = 2; + SPoint *point = predictor->point; + + if (mode == 0) + { + for (pos = (short) (RoundLog2 (search_range) - 2); pos > -1; pos--) + { + searchpos = ((search_range << search_range_qpel) >> pos); + + for (i = 1; i >= -1; i -= 2) + { + point[++prednum].motion.mv_x = i * searchpos; + point[prednum].motion.mv_y = 0; + point[++prednum].motion.mv_x = i * searchpos; + point[prednum].motion.mv_y = i * searchpos; + point[++prednum].motion.mv_x = 0; + point[prednum].motion.mv_y = i * searchpos; + point[++prednum].motion.mv_x = -i * searchpos; + point[prednum].motion.mv_y = i * searchpos; + } + } + } + else // if (mode == 0) + { + for (pos = (short) (RoundLog2 (search_range) - 2); pos > -1; pos--) + { + searchpos = ((search_range << search_range_qpel) >> pos); + + fieldsearchpos = ((3 * searchpos + 1) << search_range_qpel) >> 1; + for (i = 1; i >= -1; i -= 2) + { + point[++prednum].motion.mv_x = i * searchpos; + point[prednum].motion.mv_y = 0; + point[++prednum].motion.mv_x = i * searchpos; + point[prednum].motion.mv_y = i * searchpos; + point[++prednum].motion.mv_x = 0; + point[prednum].motion.mv_y = i * searchpos; + point[++prednum].motion.mv_x = -i * searchpos; + point[prednum].motion.mv_y = i * searchpos; + } + + for (i = 1; i >= -1; i -= 2) + { + point[++prednum].motion.mv_x = i * fieldsearchpos; + point[prednum].motion.mv_y = -i * searchpos; + point[++prednum].motion.mv_x = i * fieldsearchpos; + point[prednum].motion.mv_y = 0; + point[++prednum].motion.mv_x = i * fieldsearchpos; + point[prednum].motion.mv_y = i * searchpos; + point[++prednum].motion.mv_x = i * searchpos; + point[prednum].motion.mv_y = i * fieldsearchpos; + point[++prednum].motion.mv_x = 0; + point[prednum].motion.mv_y = i * fieldsearchpos; + point[++prednum].motion.mv_x = -i * searchpos; + point[prednum].motion.mv_y = i * fieldsearchpos; + } + } + } + + predictor->searchPoints = prednum; +} + +/*! +************************************************************************ +* \brief +* EPZS Global Initialization +************************************************************************ +*/ +int +EPZSStructInit (Slice * currSlice) +{ + VideoParameters *p_Vid = currSlice->p_Vid; + InputParameters *p_Inp = currSlice->p_Inp; + EPZSParameters *p_EPZS = currSlice->p_EPZS; + int max_list_number = p_Vid->mb_aff_frame_flag ? 6 : 2; + int pel_error_me = 1 << (p_Vid->bitdepth_luma - 8); + int pel_error_me_cr = 1 << (p_Vid->bitdepth_chroma - 8); + int i, memory_size = 0; + double chroma_weight = + p_Inp->ChromaMEEnable ? pel_error_me_cr * p_Inp->ChromaMEWeight * (double) (p_Vid->width_cr * p_Vid->height_cr) / + (double) (p_Vid->width * p_Vid->height) : 0; + int searchlevels = RoundLog2 (p_Inp->search_range[p_Vid->view_id]) - 1; + int searcharray = + p_Inp->BiPredMotionEstimation ? (2 * imax (p_Inp->search_range[p_Vid->view_id], p_Inp->BiPredMESearchRange[p_Vid->view_id]) + + 1) << 2 : (2 * p_Inp->search_range[p_Vid->view_id] + 1) << 2; + p_EPZS->p_Vid = p_Vid; + p_EPZS->BlkCount = 1; + + //! In this implementation we keep threshold limits fixed. + //! However one could adapt these limits based on lagrangian + //! optimization considerations (i.e. qp), while also allow + //! adaptation of the limits themselves based on content or complexity. + + for (i = 0; i < 8; ++i) + { +#if (MVC_EXTENSION_ENABLE) + if ( (currSlice->view_id) && (p_Inp->EnableEnhLayerEPZSScalers) ) + { + p_EPZS->medthres[i] = p_Inp->EPZSMedThresScale[currSlice->view_id] * (MED_THRES_BASE[i] * pel_error_me + (int) (MED_THRES_BASE[i] * chroma_weight + 0.5)); + p_EPZS->maxthres[i] = p_Inp->EPZSMaxThresScale[currSlice->view_id] * (MAX_THRES_BASE[i] * pel_error_me + (int) (MAX_THRES_BASE[i] * chroma_weight + 0.5)); + p_EPZS->minthres[i] = p_Inp->EPZSMinThresScale[currSlice->view_id] * (MIN_THRES_BASE[i] * pel_error_me + (int) (MIN_THRES_BASE[i] * chroma_weight + 0.5)); + p_EPZS->subthres[i] = p_Inp->EPZSSubPelThresScale[currSlice->view_id] * (MED_THRES_BASE[i] * pel_error_me + (int) (MED_THRES_BASE[i] * chroma_weight + 0.5)); + } + else + { + p_EPZS->medthres[i] = p_Inp->EPZSMedThresScale[0] * (MED_THRES_BASE[i] * pel_error_me + (int) (MED_THRES_BASE[i] * chroma_weight + 0.5)); + p_EPZS->maxthres[i] = p_Inp->EPZSMaxThresScale[0] * (MAX_THRES_BASE[i] * pel_error_me + (int) (MAX_THRES_BASE[i] * chroma_weight + 0.5)); + p_EPZS->minthres[i] = p_Inp->EPZSMinThresScale[0] * (MIN_THRES_BASE[i] * pel_error_me + (int) (MIN_THRES_BASE[i] * chroma_weight + 0.5)); + p_EPZS->subthres[i] = p_Inp->EPZSSubPelThresScale[0] * (MED_THRES_BASE[i] * pel_error_me + (int) (MED_THRES_BASE[i] * chroma_weight + 0.5)); + } +#else + p_EPZS->medthres[i] = p_Inp->EPZSMedThresScale * (MED_THRES_BASE[i] * pel_error_me + (int) (MED_THRES_BASE[i] * chroma_weight + 0.5)); + p_EPZS->maxthres[i] = p_Inp->EPZSMaxThresScale * (MAX_THRES_BASE[i] * pel_error_me + (int) (MAX_THRES_BASE[i] * chroma_weight + 0.5)); + p_EPZS->minthres[i] = p_Inp->EPZSMinThresScale * (MIN_THRES_BASE[i] * pel_error_me + (int) (MIN_THRES_BASE[i] * chroma_weight + 0.5)); + p_EPZS->subthres[i] = p_Inp->EPZSSubPelThresScale * (MED_THRES_BASE[i] * pel_error_me + (int) (MED_THRES_BASE[i] * chroma_weight + 0.5)); +#endif + up_scale(&p_EPZS->medthres[i]); + up_scale(&p_EPZS->maxthres[i]); + up_scale(&p_EPZS->minthres[i]); + up_scale(&p_EPZS->subthres[i]); + } + //! Allocate and assign window based predictors. + //! Other window types could also be used, while method could be + //! made a bit more adaptive (i.e. patterns could be assigned + //! based on neighborhood + p_EPZS->window_predictor = allocEPZSpattern (searchlevels * 8); + p_EPZS->window_predictor_ext = allocEPZSpattern (searchlevels * 20); + EPZSWindowPredictorInit ((short) p_Inp->search_range[p_Vid->view_id], p_EPZS->window_predictor, 0); + EPZSWindowPredictorInit ((short) p_Inp->search_range[p_Vid->view_id], p_EPZS->window_predictor_ext, 1); + + //! Also assing search predictor memory + // maxwindow + spatial + blocktype + temporal + memspatial +#if (MVC_EXTENSION_ENABLE) + p_EPZS->predictor = allocEPZSpattern (searchlevels * 20 + 5 + 5 + 9 * (p_Inp->EPZSTemporal[0] | p_Inp->EPZSTemporal[1]) + 3 * (p_Inp->EPZSSpatialMem)); +#else + p_EPZS->predictor = allocEPZSpattern (searchlevels * 20 + 5 + 5 + 9 * (p_Inp->EPZSTemporal) + 3 * (p_Inp->EPZSSpatialMem)); +#endif + + //! Finally assign memory for all other elements + //! (distortion, EPZSMap, and temporal predictors) + + //memory_size += get_offset_mem2Dshort(&EPZSMap, searcharray, searcharray, (searcharray>>1), (searcharray>>1)); + memory_size += get_mem3Ddistblk (&(p_EPZS->distortion), max_list_number, 7, (p_Vid->width + MB_BLOCK_SIZE)/ BLOCK_SIZE); + if (p_Inp->BiPredMotionEstimation) + memory_size += get_mem3Ddistblk (&(p_EPZS->bi_distortion), max_list_number, 7, (p_Vid->width + MB_BLOCK_SIZE) / BLOCK_SIZE); + memory_size += get_mem3Ddistblk (&(p_EPZS->distortion_hpel), max_list_number, 7, (p_Vid->width + MB_BLOCK_SIZE)/ BLOCK_SIZE); + memory_size += get_mem2Dshort ((short ***) &(p_EPZS->EPZSMap), searcharray, searcharray); + + if (p_Inp->EPZSSpatialMem) + { +#if EPZSREF + memory_size += get_mem5Dmv (&(p_EPZS->p_motion), 6, p_Vid->max_num_references, 7, 4, p_Vid->width / BLOCK_SIZE); +#else + memory_size += get_mem4Dmv (&(p_EPZS->p_motion), 6, 7, 4, p_Vid->width / BLOCK_SIZE); +#endif + } + +#if (MVC_EXTENSION_ENABLE) + if ( p_Inp->EPZSTemporal[currSlice->view_id] ) +#else + if (p_Inp->EPZSTemporal) +#endif + { + p_EPZS->p_colocated = allocEPZScolocated (p_Vid->width, p_Vid->height, p_Vid->active_sps->mb_adaptive_frame_field_flag); + } + + switch (p_Inp->EPZSPattern) + { + case 5: + p_EPZS->searchPattern = p_Vid->pmvfast; + break; + case 4: + p_EPZS->searchPattern = p_Vid->sbdiamond; + break; + case 3: + p_EPZS->searchPattern = p_Vid->ldiamond; + break; + case 2: + p_EPZS->searchPattern = p_Vid->ediamond; + break; + case 1: + p_EPZS->searchPattern = p_Vid->square; + break; + case 0: + default: + p_EPZS->searchPattern = p_Vid->sdiamond; + break; + } + + switch (p_Inp->EPZSDual) + { + case 6: + p_EPZS->searchPatternD = p_Vid->pmvfast; + break; + case 5: + p_EPZS->searchPatternD = p_Vid->sbdiamond; + break; + case 4: + p_EPZS->searchPatternD = p_Vid->ldiamond; + break; + case 3: + p_EPZS->searchPatternD = p_Vid->ediamond; + break; + case 2: + p_EPZS->searchPatternD = p_Vid->square; + break; + case 1: + default: + p_EPZS->searchPatternD = p_Vid->sdiamond; + break; + } + + return memory_size; +} + +/*! +************************************************************************ +* \brief +* Delete EPZS Alocated memory +************************************************************************ +*/ +void +EPZSStructDelete (Slice * currSlice) +{ + InputParameters *p_Inp = currSlice->p_Inp; + EPZSParameters *p_EPZS = currSlice->p_EPZS; +#if (MVC_EXTENSION_ENABLE) + if (p_Inp->EPZSTemporal[currSlice->view_id]) +#else + if (p_Inp->EPZSTemporal) +#endif + freeEPZScolocated (p_EPZS->p_colocated); + + //free_offset_mem2Dshort(EPZSMap, searcharray, (searcharray>>1), (searcharray>>1)); + free_mem2Dshort ((short **) p_EPZS->EPZSMap); + free_mem3Ddistblk (p_EPZS->distortion); + free_mem3Ddistblk (p_EPZS->distortion_hpel); + + if (p_Inp->BiPredMotionEstimation) + free_mem3Ddistblk (p_EPZS->bi_distortion); + + freeEPZSpattern (p_EPZS->window_predictor_ext); + freeEPZSpattern (p_EPZS->window_predictor); + freeEPZSpattern (p_EPZS->predictor); + + if (p_Inp->EPZSSpatialMem) + { +#if EPZSREF + free_mem5Dmv (p_EPZS->p_motion); +#else + free_mem4Dmv (p_EPZS->p_motion); +#endif + } + + free (currSlice->p_EPZS); + currSlice->p_EPZS = NULL; +} + +//! For ME purposes restricting the co-located partition is not necessary. +/*! +************************************************************************ +* \brief +* EPZS Slice Level Initialization +************************************************************************ +*/ +void +EPZSSliceInit (Slice * currSlice) +{ + VideoParameters *p_Vid = currSlice->p_Vid; + InputParameters *p_Inp = currSlice->p_Inp; + StorablePicture *p_Pic = p_Vid->enc_picture; + EPZSColocParams *p = currSlice->p_EPZS->p_colocated; + StorablePicture ***listX = currSlice->listX; + StorablePicture *fs, *fs_top, *fs_bottom; + StorablePicture *fs1, *fs_top1, *fs_bottom1, *fsx; + EPZSParameters *p_EPZS = currSlice->p_EPZS; + int i, j, k, jj, jdiv, loffset; + int prescale, iTRb, iTRp; + int list = (currSlice->slice_type == B_SLICE) ? LIST_1 : LIST_0; + int tempmv_scale[2]; + int epzs_scale[2][6][MAX_LIST_SIZE]; + int iref; + int invmv_precision = 8; + + // Lets compute scaling factoes between all references in lists. + // Needed to scale spatial predictors. + for (j = LIST_0; j < 2 + (currSlice->mb_aff_frame_flag << 2); ++j) + { + for (k = 0; k < currSlice->listXsize[j]; ++k) + { + for (i = 0; i < currSlice->listXsize[j]; ++i) + { + if ((j >> 1) == 0) + { + iTRb = iClip3 (-128, 127, p_Pic->poc - listX[j][i]->poc); + iTRp = iClip3 (-128, 127, p_Pic->poc - listX[j][k]->poc); + } + else if ((j >> 1) == 1) + { + iTRb = iClip3 (-128, 127, p_Pic->top_poc - listX[j][i]->poc); + iTRp = iClip3 (-128, 127, p_Pic->top_poc - listX[j][k]->poc); + } + else + { + iTRb = iClip3 (-128, 127, p_Pic->bottom_poc - listX[j][i]->poc); + iTRp = iClip3 (-128, 127, p_Pic->bottom_poc - listX[j][k]->poc); + } + + if (iTRp != 0) + { + prescale = (16384 + iabs (iTRp / 2)) / iTRp; + p_EPZS->mv_scale[j][i][k] = iClip3 (-2048, 2047, rshift_rnd_sf ((iTRb * prescale), 6)); + } + else + p_EPZS->mv_scale[j][i][k] = 256; + } + } + } + +#if (MVC_EXTENSION_ENABLE) + if (p_Inp->EPZSTemporal[currSlice->view_id]) +#else + if (p_Inp->EPZSTemporal) +#endif + { + MotionVector **MotionVector0 = p->frame[LIST_0]; + MotionVector **MotionVector1 = p->frame[LIST_1]; + + fs_top = fs_bottom = fs = listX[list][0]; + if (currSlice->listXsize[list] > 1) + fs_top1 = fs_bottom1 = fs1 = listX[list][1]; + else + fs_top1 = fs_bottom1 = fs1 = listX[list][0]; + for (j = 0; j < 6; ++j) + { + for (i = 0; i < 6; ++i) + { + epzs_scale[0][j][i] = 256; + epzs_scale[1][j][i] = 256; + } + } + + for (j = 0; j < 2 + (currSlice->mb_aff_frame_flag << 2); j += 2) + { + for (i = 0; i < currSlice->listXsize[j]; ++i) + { + if (j == 0) + iTRb = iClip3 (-128, 127, p_Pic->poc - listX[LIST_0 + j][i]->poc); + else if (j == 2) + iTRb = iClip3 (-128, 127, p_Pic->top_poc - listX[LIST_0 + j][i]->poc); + else + iTRb = iClip3 (-128, 127, p_Pic->bottom_poc - listX[LIST_0 + j][i]->poc); + iTRp = iClip3 (-128, 127, listX[list + j][0]->poc - listX[LIST_0 + j][i]->poc); + + if (iTRp != 0) + { + prescale = (16384 + iabs (iTRp / 2)) / iTRp; + prescale = iClip3 (-2048, 2047, rshift_rnd_sf ((iTRb * prescale), 6)); + + //prescale = (iTRb * prescale + 32) >> 6; + } + else // This could not happen but lets use it in case that reference is removed. + prescale = 256; + + epzs_scale[0][j][i] = rshift_rnd_sf ((p_EPZS->mv_scale[j][0][i] * prescale), 8); + epzs_scale[0][j + 1][i] = prescale - 256; + + if (currSlice->listXsize[list + j] > 1) + { + iTRp = iClip3 (-128, 127, listX[list + j][1]->poc - listX[LIST_0 + j][i]->poc); + if (iTRp != 0) + { + prescale = (16384 + iabs (iTRp / 2)) / iTRp; + prescale = iClip3 (-2048, 2047, rshift_rnd_sf ((iTRb * prescale), 6)); + //prescale = (iTRb * prescale + 32) >> 6; + } + else // This could not happen but lets use it for case that reference is removed. + prescale = 256; + + epzs_scale[1][j][i] = rshift_rnd_sf ((p_EPZS->mv_scale[j][1][i] * prescale), 8); + epzs_scale[1][j + 1][i] = prescale - 256; + } + else + { + epzs_scale[1][j][i] = epzs_scale[0][j][i]; + epzs_scale[1][j + 1][i] = epzs_scale[0][j + 1][i]; + } + } + } + + if (currSlice->mb_aff_frame_flag) + { + fs_top = listX[list + 2][0]; + fs_bottom = listX[list + 4][0]; + + if (currSlice->listXsize[0] > 1) + { + fs_top1 = listX[list + 2][1]; + fs_bottom1 = listX[list + 4][1]; + } + } + else + { + if (currSlice->structure != FRAME) + { + if ((currSlice->structure != fs->structure) && (fs->coded_frame)) + { + if (currSlice->structure == TOP_FIELD) + { + fs_top = fs_bottom = fs = listX[list][0]->top_field; + fs_top1 = fs_bottom1 = fs1 = listX[list][0]->bottom_field; + } + else + { + fs_top = fs_bottom = fs = listX[list][0]->bottom_field; + fs_top1 = fs_bottom1 = fs1 = listX[list][0]->top_field; + } + } + } + } + + if (!currSlice->active_sps->frame_mbs_only_flag) + { + if (currSlice->mb_aff_frame_flag) + { + for (j = 0; j < fs->size_y >> 2; ++j) + { + jj = j >> 1; + jdiv = jj + 4 * (j >> 3); + for (i = 0; i < fs->size_x >> 2; ++i) + { + if (fs->mv_info[j][i].field_frame) + { + //! Assign frame buffers for field MBs + //! Check whether we should use top or bottom field mvs. + //! Depending on the assigned poc values. + if (iabs (p_Pic->poc - fs_bottom->poc) > iabs (p_Pic->poc - fs_top->poc)) + { + tempmv_scale[LIST_0] = 256; + tempmv_scale[LIST_1] = 0; + + if (fs->mv_info[jdiv][i].ref_pic[LIST_0] == NULL && currSlice->listXsize[LIST_0] > 1) + { + fsx = fs_top1; + loffset = 1; + } + else + { + fsx = fs_top; + loffset = 0; + } + + if (fs->mv_info[jdiv][i] .ref_pic[LIST_0] != NULL) + { + for (iref = 0; iref < imin (currSlice->num_ref_idx_active[LIST_0], currSlice->listXsize[LIST_0]); ++iref) + { + if (currSlice->listX[LIST_0][iref] == fs->mv_info[jdiv][i].ref_pic[LIST_0]) + { + tempmv_scale[LIST_0] = epzs_scale[loffset][LIST_0][iref]; + tempmv_scale[LIST_1] = epzs_scale[loffset][LIST_1][iref]; + break; + } + } + + compute_scaled (&MotionVector0[j][i], &MotionVector1[j][i], tempmv_scale, &fsx->mv_info[jj][i].mv[LIST_0], invmv_precision); + } + else + { + MotionVector0[j][i] = zero_mv; + MotionVector1[j][i] = zero_mv; + } + } + else + { + tempmv_scale[LIST_0] = 256; + tempmv_scale[LIST_1] = 0; + if (fs->mv_info[jdiv + 4][i].ref_pic[LIST_0] == NULL && currSlice->listXsize[LIST_0] > 1) + { + fsx = fs_bottom1; + loffset = 1; + } + else + { + fsx = fs_bottom; + loffset = 0; + } + + if (fs->mv_info[jdiv + 4][i].ref_pic[LIST_0] != NULL) + { + for (iref = 0; iref < imin (currSlice->num_ref_idx_active[LIST_0], currSlice->listXsize[LIST_0]); ++iref) + { + if (currSlice->listX[LIST_0][iref] == fs->mv_info[jdiv + 4][i].ref_pic[LIST_0]) + { + tempmv_scale[LIST_0] = epzs_scale[loffset][LIST_0][iref]; + tempmv_scale[LIST_1] = epzs_scale[loffset][LIST_1][iref]; + break; + } + } + + compute_scaled (&MotionVector0[j][i], &MotionVector1[j][i], tempmv_scale, &fsx->mv_info[jj][i].mv[LIST_0], invmv_precision); + } + else + { + MotionVector0[j][i] = zero_mv; + MotionVector1[j][i] = zero_mv; + } + } + } + else + { + tempmv_scale[LIST_0] = 256; + tempmv_scale[LIST_1] = 0; + + if (fs->mv_info[j][i].ref_pic[LIST_0] == NULL && currSlice->listXsize[LIST_0] > 1) + { + fsx = fs1; + loffset = 1; + } + else + { + fsx = fs; + loffset = 0; + } + + if (fsx->mv_info[j][i].ref_pic[LIST_0] != NULL) + { + for (iref = 0; iref < imin (currSlice->num_ref_idx_active[LIST_0], currSlice->listXsize[LIST_0]); ++iref) + { + if (currSlice->listX[LIST_0][iref] == fsx->mv_info[j][i].ref_pic[LIST_0]) + { + tempmv_scale[LIST_0] = epzs_scale[loffset][LIST_0][iref]; + tempmv_scale[LIST_1] = epzs_scale[loffset][LIST_1][iref]; + break; + } + } + compute_scaled (&MotionVector0[j][i], &MotionVector1[j][i], tempmv_scale, &fsx->mv_info[j][i].mv[LIST_0], invmv_precision); + } + else + { + MotionVector0[j][i] = zero_mv; + MotionVector1[j][i] = zero_mv; + } + } + } + } + } + else + { + for (j = 0; j < fs->size_y >> 2; ++j) + { + jj = j >> 1; + jdiv = jj + 4 * (j >> 3); + for (i = 0; i < fs->size_x >> 2; ++i) + { + tempmv_scale[LIST_0] = 256; + tempmv_scale[LIST_1] = 0; + if (fs->mv_info[j][i].ref_pic[LIST_0] == NULL && currSlice->listXsize[LIST_0] > 1) + { + fsx = fs1; + loffset = 1; + } + else + { + fsx = fs; + loffset = 0; + } + + if (fsx->mv_info[j][i].ref_pic[LIST_0] != NULL) + { + for (iref = 0; iref < imin (currSlice->num_ref_idx_active[LIST_0], currSlice->listXsize[LIST_0]); ++iref) + { + if (currSlice->listX[LIST_0][iref] == fsx->mv_info[j][i].ref_pic[LIST_0]) + { + tempmv_scale[LIST_0] = epzs_scale[loffset][LIST_0][iref]; + tempmv_scale[LIST_1] = epzs_scale[loffset][LIST_1][iref]; + + break; + } + } + + compute_scaled (&MotionVector0[j][i], &MotionVector1[j][i], tempmv_scale, &fsx->mv_info[j][i].mv[LIST_0], invmv_precision); + } + else + { + MotionVector0[j][i] = zero_mv; + MotionVector1[j][i] = zero_mv; + } + } + } + } + + //! Generate field MVs from Frame MVs + if (currSlice->structure || currSlice->mb_aff_frame_flag) + { + for (j = 0; j < fs->size_y >> 3; ++j) + { + for (i = 0; i < fs->size_x >> 2; ++i) + { + if (!currSlice->mb_aff_frame_flag) + { + tempmv_scale[LIST_0] = 256; + tempmv_scale[LIST_1] = 0; + if (fs->mv_info[j][i].ref_pic[LIST_0] == NULL && currSlice->listXsize[LIST_0] > 1) + { + fsx = fs1; + loffset = 1; + } + else + { + fsx = fs; + loffset = 0; + } + + if (fsx->mv_info[j][i].ref_pic[LIST_0] != NULL) + { + for (iref = 0; iref < imin (currSlice->num_ref_idx_active[LIST_0], currSlice->listXsize[LIST_0]); ++iref) + { + if (currSlice->listX[LIST_0][iref] == fsx->mv_info[j][i].ref_pic[LIST_0]) + { + tempmv_scale[LIST_0] = epzs_scale[loffset][LIST_0][iref]; + tempmv_scale[LIST_1] = epzs_scale[loffset][LIST_1][iref]; + break; + } + } + compute_scaled (&MotionVector0[j][i], &MotionVector1[j][i], tempmv_scale, &fsx->mv_info[j][i].mv[LIST_0], invmv_precision); + } + else + { + MotionVector0[j][i] = zero_mv; + MotionVector1[j][i] = zero_mv; + } + } + else + { + tempmv_scale[LIST_0] = 256; + tempmv_scale[LIST_1] = 0; + if (fs_bottom->mv_info[j][i].ref_pic[LIST_0] == NULL && currSlice->listXsize[LIST_0] > 1) + { + fsx = fs_bottom1; + loffset = 1; + } + else + { + fsx = fs_bottom; + loffset = 0; + } + + if (fsx->mv_info[j][i].ref_pic[LIST_0] != NULL) + { + for (iref = 0; iref < imin (2 * currSlice->num_ref_idx_active[LIST_0], currSlice->listXsize[LIST_0 + 4]); ++iref) + { + if (currSlice->listX[LIST_0 + 4][iref] == fsx->mv_info[j][i].ref_pic[LIST_0]) + { + tempmv_scale[LIST_0] = epzs_scale[loffset][LIST_0 + 4][iref]; + tempmv_scale[LIST_1] = epzs_scale[loffset][LIST_1 + 4][iref]; + break; + } + } + + compute_scaled (&p->bot[LIST_0][j][i], &p->bot[LIST_1][j][i], tempmv_scale, &fsx->mv_info[j][i].mv[LIST_0], invmv_precision); + } + else + { + p->bot[LIST_0][j][i] = zero_mv; + p->bot[LIST_1][j][i] = zero_mv; + } + + if (!fs->mv_info[2 * j][i].field_frame) + { + p->bot[LIST_0][j][i].mv_y = (p->bot[LIST_0][j][i].mv_y + 1) >> 1; + p->bot[LIST_1][j][i].mv_y = (p->bot[LIST_1][j][i].mv_y + 1) >> 1; + } + + tempmv_scale[LIST_0] = 256; + tempmv_scale[LIST_1] = 0; + if (fs_top->mv_info[j][i].ref_pic[LIST_0] == NULL && currSlice->listXsize[LIST_0] > 1) + { + fsx = fs_top1; + loffset = 1; + } + else + { + fsx = fs_top; + loffset = 0; + } + if (fsx->mv_info[j][i].ref_pic[LIST_0] != NULL) + { + for (iref = 0; iref < imin (2 * currSlice->num_ref_idx_active[LIST_0], currSlice->listXsize[LIST_0 + 2]); ++iref) + { + if (currSlice->listX[LIST_0 + 2][iref] == fsx->mv_info[j][i].ref_pic[LIST_0]) + { + tempmv_scale[LIST_0] = epzs_scale[loffset][LIST_0 + 2][iref]; + tempmv_scale[LIST_1] = epzs_scale[loffset][LIST_1 + 2][iref]; + break; + } + } + + compute_scaled (&p->top[LIST_0][j][i], &p->top[LIST_1][j][i], tempmv_scale, &fsx->mv_info[j][i].mv[LIST_0], invmv_precision); + } + else + { + p->top[LIST_0][j][i] = zero_mv; + p->top[LIST_1][j][i] = zero_mv; + } + + if (!fs->mv_info[2 * j][i].field_frame) + { + p->top[LIST_0][j][i].mv_y = (p->top[LIST_0][j][i].mv_y + 1) >> 1; + p->top[LIST_1][j][i].mv_y = (p->top[LIST_1][j][i].mv_y + 1) >> 1; + } + } + } + } + } + + //! Use inference flag to remap mvs/references + //! Frame with field co-located + if (!currSlice->structure) + { + for (j = 0; j < fs->size_y >> 2; ++j) + { + jj = j >> 1; + jdiv = (j >> 1) + ((j >> 3) << 2); + for (i = 0; i < fs->size_x >> 2; ++i) + { + if (fs->mv_info[j][i].field_frame) + { + tempmv_scale[LIST_0] = 256; + tempmv_scale[LIST_1] = 0; + if (fs->mv_info[jdiv][i].ref_pic[LIST_0] == NULL && currSlice->listXsize[LIST_0] > 1) + { + fsx = fs1; + loffset = 1; + } + else + { + fsx = fs; + loffset = 0; + } + + if (fsx->mv_info[jdiv][i].ref_pic[LIST_0] != NULL) + { + for (iref = 0; iref < imin (currSlice->num_ref_idx_active[LIST_0], currSlice->listXsize[LIST_0]); ++iref) + { + if (currSlice->listX[LIST_0][iref] == fsx->mv_info[jdiv][i].ref_pic[LIST_0]) + { + tempmv_scale[LIST_0] = epzs_scale[loffset][LIST_0][iref]; + tempmv_scale[LIST_1] = epzs_scale[loffset][LIST_1][iref]; + break; + } + } + if (iabs (p_Pic->poc - fsx->bottom_field->poc) > iabs (p_Pic->poc - fsx->top_field->poc)) + { + compute_scaled (&MotionVector0[j][i], &MotionVector1[j][i], tempmv_scale, + &fsx->top_field->mv_info[jj][i].mv[LIST_0], invmv_precision); + } + else + { + compute_scaled (&MotionVector0[j][i], &MotionVector1[j][i], tempmv_scale, + &fsx->bottom_field->mv_info[jj][i].mv[LIST_0], invmv_precision); + } + } + else + { + MotionVector0[j][i] = zero_mv; + MotionVector1[j][i] = zero_mv; + } + } + } + } + } + } + else + { + for (j = 0; j < fs->size_y >> 2; ++j) + { + for (i = 0; i < fs->size_x >> 2; ++i) + { + tempmv_scale[LIST_0] = 256; + tempmv_scale[LIST_1] = 0; + if (fs->mv_info[j][i].ref_pic[LIST_0] == NULL && currSlice->listXsize[LIST_0] > 1) + { + fsx = fs1; + loffset = 1; + } + else + { + fsx = fs; + loffset = 0; + } + //if (fsx->mv_info[j][i].ref_pic[LIST_0] != NULL) + if (fsx->mv_info[j][i].ref_pic[LIST_0] != NULL && (!p_Vid->view_id || ((fsx->ref_pic_na[0]<0 || fsx->mv_info[j][i].ref_idx[LIST_0] != fsx->ref_pic_na[0])))) + { + for (iref = 0; iref < imin (currSlice->num_ref_idx_active[LIST_0], currSlice->listXsize[LIST_0]); ++iref) + { + if (currSlice->listX[LIST_0][iref] == fsx->mv_info[j][i].ref_pic[LIST_0]) + { + tempmv_scale[LIST_0] = epzs_scale[loffset][LIST_0][iref]; + tempmv_scale[LIST_1] = epzs_scale[loffset][LIST_1][iref]; + break; + } + } + compute_scaled (&MotionVector0[j][i], &MotionVector1[j][i], tempmv_scale, &fsx->mv_info[j][i].mv[LIST_0], invmv_precision); + } + else + { + MotionVector0[j][i] = zero_mv; + MotionVector1[j][i] = zero_mv; + } + } + } + } + + if (!currSlice->active_sps->frame_mbs_only_flag) + { + for (j = 0; j < fs->size_y >> 2; ++j) + { + for (i = 0; i < fs->size_x >> 2; ++i) + { + if ((!currSlice->mb_aff_frame_flag && !currSlice->structure && fs->mv_info[j][i].field_frame) + || (currSlice->mb_aff_frame_flag && fs->mv_info[j][i].field_frame)) + { + MotionVector0[j][i].mv_y *= 2; + MotionVector1[j][i].mv_y *= 2; + } + else if (currSlice->structure && !fs->mv_info[j][i].field_frame) + { + MotionVector0[j][i].mv_y = (short) rshift_rnd_sf (MotionVector0[j][i].mv_y, 1); + MotionVector1[j][i].mv_y = (short) rshift_rnd_sf (MotionVector1[j][i].mv_y, 1); + } + } + } + } + } + +} + + +static void +is_block_available (Macroblock * currMB, StorablePicture * ref_picture, MEBlock * mv_block, int block_available[4]) +{ + if ((mv_block->block_y << 2) > 0) + { + if ((mv_block->block_x << 2) < 8) // first column of 8x8 blocks + { + if ((mv_block->block_y << 2) == 8) + { + block_available[0] = (mv_block->blocksize_x != MB_BLOCK_SIZE) || (currMB->mb_x < (ref_picture->size_x >> 4) - 1); + } + else + { + block_available[0] = ((mv_block->block_x << 2) + mv_block->blocksize_x != 8) + || (currMB->mb_x < (ref_picture->size_x >> 4) - 1); + } + } + else + { + block_available[0] = ((mv_block->block_x << 2) + mv_block->blocksize_x != MB_BLOCK_SIZE) || (currMB->mb_x < (ref_picture->size_x >> 4) - 1); + } + } + else + { + block_available[0] = ((mv_block->block_x << 2) + mv_block->blocksize_x != MB_BLOCK_SIZE) || (currMB->mb_x < (ref_picture->size_x >> 4) - 1); + } + + block_available[1] = ((mv_block->block_y << 2) + mv_block->blocksize_y != MB_BLOCK_SIZE) || ((currMB->mb_y < (ref_picture->size_y >> 4) - 1)); + block_available[2] = mv_block->block[0].available; + block_available[3] = mv_block->block[1].available; +} + +/*! +************************************************************************ +* \brief +* EPZS Block Type Predictors +************************************************************************ +*/ +void +EPZSBlockTypePredictorsMB (Slice * currSlice, MEBlock * mv_block, SPoint * point, int *prednum) +{ + int blocktype = mv_block->blocktype; + int block_x = mv_block->block_x; + int block_y = mv_block->block_y; + int list = mv_block->list; + int ref = mv_block->ref_idx; + EPZSParameters *p_EPZS = currSlice->p_EPZS; + MotionVector ****all_mv = currSlice->all_mv[list]; + MotionVector *cur_mv = &point[*prednum].motion; + + if (blocktype != 1) + { + *cur_mv = all_mv[ref][BLOCK_PARENT[blocktype]][block_y][block_x]; + + //*prednum += ((cur_mv->mv_x | cur_mv->mv_y) != 0); + *prednum += (*((int *) cur_mv) != 0); + + if(BLOCK_PARENT[blocktype] !=1) + { + cur_mv = &point[*prednum].motion; + *cur_mv = all_mv[ref][1][block_y][block_x]; + //*prednum += ((cur_mv->mv_x | cur_mv->mv_y) != 0); + *prednum += (*((int *) cur_mv) != 0); + } + } + + if (ref > 0) + { + cur_mv = &point[*prednum].motion; + scale_mv (cur_mv, p_EPZS->mv_scale[list][ref][ref - 1], &all_mv[ref - 1][blocktype][block_y][block_x], 8); + //*prednum += ((cur_mv->mv_x | cur_mv->mv_y) != 0); + *prednum += (*((int *) cur_mv) != 0); + + if (ref > 1) + { + cur_mv = &point[*prednum].motion; + scale_mv (cur_mv, p_EPZS->mv_scale[list][ref][0], &all_mv[0][blocktype][block_y][block_x], 8); + //*prednum += ((cur_mv->mv_x | cur_mv->mv_y) != 0); + *prednum += (*((int *) cur_mv) != 0); + } + } +} + +/*! +*********************************************************************** +* \brief +* Spatial Predictors +* AMT/HYC +*********************************************************************** +*/ +short +EPZS_spatial_predictors (EPZSParameters * p_EPZS, MEBlock *mv_block, int list, int list_offset, short ref, struct pic_motion_params **mv_info) +{ + PixelPos * block = mv_block->block; + short refA = 0, refB = 0, refC = 0, refD = 0; + VideoParameters *p_Vid = p_EPZS->p_Vid; + int *mot_scale = p_EPZS->mv_scale[list + list_offset][ref]; + SPoint *point = p_EPZS->predictor->point; + + // zero predictor + (point++)->motion = zero_mv; + + // Non MB-AFF mode + if (!p_Vid->mb_aff_frame_flag) + { + refA = block[0].available ? (short) mv_info[block[0].pos_y][block[0].pos_x].ref_idx[list] : -1; + refB = block[1].available ? (short) mv_info[block[1].pos_y][block[1].pos_x].ref_idx[list] : -1; + refC = block[2].available ? (short) mv_info[block[2].pos_y][block[2].pos_x].ref_idx[list] : -1; + refD = block[3].available ? (short) mv_info[block[3].pos_y][block[3].pos_x].ref_idx[list] : -1; + + // Left Predictor + if (block[0].available) + { + scale_mv (&point->motion, mot_scale[refA], &mv_info[block[0].pos_y][block[0].pos_x].mv[list], 8); + /* + if (*((int*) &point->motion) == 0) + { + point->motion.mv_x = 12; + point->motion.mv_y = 0; + } + */ + ++point; + } + else + { + (point)->motion.mv_x = 12; + (point++)->motion.mv_y = 0; + } + // Up predictor + if (block[1].available) + { + scale_mv (&point->motion, mot_scale[refB], &mv_info[block[1].pos_y][block[1].pos_x].mv[list], 8); + /* + if (*((int*) &point->motion) == 0) + { + point->motion.mv_x = 0; + point->motion.mv_y = 12; + } + */ + ++point; + } + else + { + (point)->motion.mv_x = 0; + (point++)->motion.mv_y = 12; + } + + // Up-Right predictor + if (block[2].available) + { + scale_mv (&point->motion, mot_scale[refC], &mv_info[block[2].pos_y][block[2].pos_x].mv[list], 8); + /* + if (*((int*) &point->motion) == 0) + { + point->motion.mv_x = -12; + point->motion.mv_y = 0; + } + */ + ++point; + } + else + { + (point)->motion.mv_x = -12; + (point++)->motion.mv_y = 0; + } + + //Up-Left predictor + if (block[3].available) + { + scale_mv (&point->motion, mot_scale[refD], &mv_info[block[3].pos_y][block[3].pos_x].mv[list], 8); + /* + if (*((int*) &point->motion) == 0) + { + point->motion.mv_x = 0; + point->motion.mv_y = -12; + } + */ + ++point; + } + else + { + (point)->motion.mv_x = 0; + (point++)->motion.mv_y = -12; + } + } + else // MB-AFF mode + { + // Field Macroblock + if (list_offset) + { + refA = block[0].available ? p_Vid->mb_data[block[0].mb_addr].mb_field ? (short) mv_info[block[0].pos_y][block[0].pos_x].ref_idx[list] + : (short) mv_info[block[0].pos_y][block[0].pos_x].ref_idx[list] * 2 : -1; + refB = block[1].available ? p_Vid->mb_data[block[1].mb_addr].mb_field ? (short) mv_info[block[1].pos_y][block[1].pos_x].ref_idx[list] + : (short) mv_info[block[1].pos_y][block[1].pos_x].ref_idx[list] * 2 : -1; + refC = block[2].available ? p_Vid->mb_data[block[2].mb_addr].mb_field ? (short) mv_info[block[2].pos_y][block[2].pos_x].ref_idx[list] + : (short) mv_info[block[2].pos_y][block[2].pos_x].ref_idx[list] * 2 : -1; + refD = block[3].available ? p_Vid->mb_data[block[3].mb_addr].mb_field ? (short) mv_info[block[3].pos_y][block[3].pos_x].ref_idx[list] + : (short) mv_info[block[3].pos_y][block[3].pos_x].ref_idx[list] * 2 : -1; + + // Left Predictor + if (block[0].available) + { + scale_mv (&point->motion, mot_scale[refA], &mv_info[block[0].pos_y][block[0].pos_x].mv[list], 8); + if (!p_Vid->mb_data[block[0].mb_addr].mb_field) + //point->motion.mv_y = (short) rshift_rnd_sf(point->motion.mv_y, 1); + point->motion.mv_y <<= 1; + ++point; + } + else + { + (point)->motion.mv_x = 12; + (point++)->motion.mv_y = 0; + } + + // Up predictor + if (block[1].available) + { + scale_mv (&point->motion, mot_scale[refB], &mv_info[block[1].pos_y][block[1].pos_x].mv[list], 8); + if (!p_Vid->mb_data[block[1].mb_addr].mb_field) + //point->motion.mv_y = (short) rshift_rnd_sf(point->motion.mv_y, 1); + point->motion.mv_y <<= 1; + ++point; + } + else + { + (point)->motion.mv_x = 0; + (point++)->motion.mv_y = 12; + } + + // Up-Right predictor + if (block[2].available) + { + scale_mv (&point->motion, mot_scale[refC], &mv_info[block[2].pos_y][block[2].pos_x].mv[list], 8); + if (!p_Vid->mb_data[block[2].mb_addr].mb_field) + //point->motion.mv_y = (short) rshift_rnd_sf(point->motion.mv_y, 1); + point->motion.mv_y <<= 1; + ++point; + } + else + { + (point)->motion.mv_x = -12; + (point++)->motion.mv_y = 0; + } + + //Up-Left predictor + if (block[3].available) + { + scale_mv (&point->motion, mot_scale[refD], &mv_info[block[3].pos_y][block[3].pos_x].mv[list], 8); + if (!p_Vid->mb_data[block[3].mb_addr].mb_field) + //point->motion.mv_y = (short) rshift_rnd_sf(point->motion.mv_y, 1); + point->motion.mv_y <<= 1; + ++point; + } + else + { + (point)->motion.mv_x = 0; + (point++)->motion.mv_y = -12; + } + } + else // Frame macroblock + { + refA = block[0].available + ? p_Vid->mb_data[block[0].mb_addr].mb_field + ? (short) mv_info[block[0].pos_y][block[0].pos_x].ref_idx[list] >> 1 : (short) mv_info[block[0].pos_y][block[0].pos_x].ref_idx[list] : -1; + refB = block[1].available + ? p_Vid->mb_data[block[1].mb_addr].mb_field + ? (short) mv_info[block[1].pos_y][block[1].pos_x].ref_idx[list] >> 1 : (short) mv_info[block[1].pos_y][block[1].pos_x].ref_idx[list] : -1; + refC = block[2].available + ? p_Vid->mb_data[block[2].mb_addr].mb_field + ? (short) mv_info[block[2].pos_y][block[2].pos_x].ref_idx[list] >> 1 : (short) mv_info[block[2].pos_y][block[2].pos_x].ref_idx[list] : -1; + refD = block[3].available + ? p_Vid->mb_data[block[3].mb_addr].mb_field + ? (short) mv_info[block[3].pos_y][block[3].pos_x].ref_idx[list] >> 1 : (short) mv_info[block[3].pos_y][block[3].pos_x].ref_idx[list] : -1; + + // Left Predictor + if (block[0].available) + { + scale_mv (&point->motion, mot_scale[refA], &mv_info[block[0].pos_y][block[0].pos_x].mv[list], 8); + if (p_Vid->mb_data[block[0].mb_addr].mb_field) + point->motion.mv_y = (short) rshift_rnd_sf (point->motion.mv_y, 1); + ++point; + } + else + { + (point)->motion.mv_x = 12; + (point++)->motion.mv_y = 0; + } + + // Up predictor + if (block[1].available) + { + scale_mv (&point->motion, mot_scale[refB], &mv_info[block[1].pos_y][block[1].pos_x].mv[list], 8); + if (p_Vid->mb_data[block[1].mb_addr].mb_field) + point->motion.mv_y = (short) rshift_rnd_sf (point->motion.mv_y, 1); + ++point; + } + else + { + (point)->motion.mv_x = 0; + (point++)->motion.mv_y = 12; + } + + // Up-Right predictor + if (block[2].available) + { + scale_mv (&point->motion, mot_scale[refC], &mv_info[block[2].pos_y][block[2].pos_x].mv[list], 8); + if (p_Vid->mb_data[block[2].mb_addr].mb_field) + point->motion.mv_y = (short) rshift_rnd_sf (point->motion.mv_y, 1); + ++point; + } + else + { + (point)->motion.mv_x = -12; + (point++)->motion.mv_y = 0; + } + + //Up-Left predictor + if (block[3].available) + { + scale_mv (&point->motion, mot_scale[refD], &mv_info[block[3].pos_y][block[3].pos_x].mv[list], 8); + if (p_Vid->mb_data[block[3].mb_addr].mb_field) + point->motion.mv_y = (short) rshift_rnd_sf (point->motion.mv_y, 1); + ++point; + } + else + { + (point)->motion.mv_x = 12; + (point++)->motion.mv_y = 0; + } + } + } + + return ((refA == -1) + (refB == -1) + ((refC == -1) && (refD == -1))); +} + +/*! +*********************************************************************** +* \brief +* Temporal Predictors +* AMT/HYC +*********************************************************************** +*/ +void +EPZS_temporal_predictors (Macroblock *currMB, //! <-- Current Macroblock + StorablePicture *ref_picture, //! <-- Current reference picture + EPZSParameters * p_EPZS, //! <-- EPZS structure + MEBlock *mv_block, //! <-- motion estimation information block + int *prednum, + distblk stopCriterion, + distblk min_mcost) +{ + int list_offset = currMB->list_offset; + int blockshape_x = (mv_block->blocksize_x >> 2); // horizontal block size in 4-pel units + int blockshape_y = (mv_block->blocksize_y >> 2); // vertical block size in 4-pel units + int o_block_x = mv_block->pos_x2; + int o_block_y = mv_block->pos_y2; + int list = mv_block->list; + int ref = mv_block->ref_idx; + + EPZSColocParams *p_Coloc = p_EPZS->p_colocated; + SPoint * point = p_EPZS->predictor->point; + int mvScale = p_EPZS->mv_scale[list + list_offset][ref][0]; + MotionVector **col_mv = (list_offset == 0) ? p_Coloc->frame[list] : (list_offset == 2) ? p_Coloc->top[list] : p_Coloc->bot[list]; + MotionVector *cur_mv = &point[*prednum].motion; + + *prednum += add_predictor (cur_mv, col_mv[o_block_y][o_block_x], mvScale, 8); + if (min_mcost > stopCriterion && ref < 2) + { + int block_available[4]; + is_block_available (currMB, ref_picture, mv_block, block_available); + + if (block_available[2]) + { + *prednum += add_predictor (&point[*prednum].motion, col_mv[o_block_y][o_block_x - 1], mvScale, 8); + //Up_Left + if (block_available[3]) + { + *prednum += add_predictor (&point[*prednum].motion, col_mv[o_block_y - 1][o_block_x - 1], mvScale, 8); + } + + //Down_Left + if (block_available[1]) + { + *prednum += add_predictor (&point[*prednum].motion, col_mv[o_block_y + blockshape_y][o_block_x - 1], mvScale, 8); + } + } + + // Up + if (block_available[3]) + { + *prednum += add_predictor (&point[*prednum].motion, col_mv[o_block_y - 1][o_block_x], mvScale, 8); + } + + // Up - Right + if (block_available[0]) + { + *prednum += add_predictor (&point[*prednum].motion, col_mv[o_block_y][o_block_x + blockshape_x], mvScale, 8); + if (block_available[3]) + { + *prednum += add_predictor (&point[*prednum].motion, col_mv[o_block_y - 1][o_block_x + blockshape_x], mvScale, 8); + } + + if (block_available[1]) + { + *prednum += add_predictor (&point[*prednum].motion, col_mv[o_block_y + blockshape_y][o_block_x + blockshape_x], mvScale, 8); + } + } + + if (block_available[1]) + { + *prednum += add_predictor (&point[*prednum].motion, col_mv[o_block_y + blockshape_y][o_block_x], mvScale, 8); + } + } +} + + +/*! +************************************************************************ +* \brief +* EPZS Block Type Predictors +************************************************************************ +*/ +void +EPZSBlockTypePredictors (Slice * currSlice, MEBlock *mv_block, SPoint * point, int *prednum) +{ + int blocktype = mv_block->blocktype; + int block_x = mv_block->block_x; + int block_y = mv_block->block_y; + int list = mv_block->list; + int ref = mv_block->ref_idx; + MotionVector ****all_mv = currSlice->all_mv[list]; + MotionVector *cur_mv = &point[*prednum].motion; + + *cur_mv = all_mv[ref][BLOCK_PARENT[blocktype]][block_y][block_x]; + + //*prednum += ((cur_mv->mv_x | cur_mv->mv_y) != 0); + *prednum += (*((int *) cur_mv) != 0); + + if ((ref > 0) && (currSlice->structure != FRAME)) + { + EPZSParameters *p_EPZS = currSlice->p_EPZS; + cur_mv = &point[*prednum].motion; + scale_mv (cur_mv, p_EPZS->mv_scale[list][ref][ref - 1], &all_mv[ref - 1][blocktype][block_y][block_x], 8); + + //*prednum += ((cur_mv->mv_x | cur_mv->mv_y) != 0); + *prednum += (*((int *) cur_mv) != 0); + if (ref > 1) + { + cur_mv = &point[*prednum].motion; + scale_mv (cur_mv, p_EPZS->mv_scale[list][ref][0], &all_mv[0][blocktype][block_y][block_x], 8); + //*prednum += ((cur_mv->mv_x | cur_mv->mv_y) != 0); + *prednum += (*((int *) cur_mv) != 0); + } + } + + cur_mv = &point[*prednum].motion; + *cur_mv = all_mv[ref][1][block_y][block_x]; + + //*prednum += ((cur_mv->mv_x | cur_mv->mv_y) != 0); + *prednum += (*((int *) cur_mv) != 0); +} + +/*! +************************************************************************ +* \brief +* EPZS Window Based Predictors +************************************************************************ +*/ +void +EPZSWindowPredictors (MotionVector * mv, EPZSStructure * predictor, int *prednum, EPZSStructure * windowPred) +{ + int pos; + SPoint *wPoint = &windowPred->point[0]; + SPoint *pPoint = &predictor->point[(*prednum)]; + + for (pos = 0; pos < windowPred->searchPoints; ++pos) + { + (pPoint++)->motion = add_MVs ((wPoint++)->motion, mv); + } + *prednum += windowPred->searchPoints; +} + +/*! +*********************************************************************** +* \brief +* Spatial Predictors +* AMT/HYC +*********************************************************************** +*/ +void +EPZS_spatial_memory_predictors (EPZSParameters * p_EPZS, //!< EPZS Parameters + MEBlock * mv_block, //!< Motion estimation structure + int list, //!< Current list + int *prednum, //!< predictor position + int img_width) //!< image width +{ + + SPoint *point = p_EPZS->predictor->point; + + int blocktype = mv_block->blocktype - 1; + + int by = mv_block->block_y; + + int bs_x = (mv_block->blocksize_x >> 2); // horizontal block size in 4-pel units + int bs_y = (mv_block->blocksize_y >> 2); // vertical block size in 4-pel units + int pic_x = mv_block->pos_x2; + + int ref = mv_block->ref_idx; + +#if EPZSREF + MotionVector **prd_mv = p_EPZS->p_motion[list][ref][blocktype]; + MotionVector *cur_mv = &point[*prednum].motion; + + // Left Predictor + if (pic_x > 0) + { + *cur_mv = prd_mv[by][pic_x - bs_x]; + *prednum += (*((int *) cur_mv) != 0); + cur_mv = &point[*prednum].motion; + } + + by = (by > 0) ? by - bs_y : 4 - bs_y; + + // Up predictor + *cur_mv = prd_mv[by][pic_x]; + *prednum += (*((int *) cur_mv) != 0); + + // Up-Right predictor + if (pic_x + bs_x < img_width) + { + cur_mv = &point[*prednum].motion; + *cur_mv = prd_mv[by][pic_x + bs_x]; + *prednum += (*((int *) cur_mv) != 0); + } +#else + int mot_scale = p_EPZS->mv_scale[list][ref][0]; + + MotionVector **prd_mv = p_EPZS->p_motion[list][blocktype]; + + // Left Predictor + point[*prednum].motion.mv_x = (pic_x > 0) + ? (short) rshift_rnd_sf ((mot_scale * prd_mv[by][pic_x - bs_x].mv_x), 8) + : 0; + point[*prednum].motion.mv_y = (pic_x > 0) + ? (short) rshift_rnd_sf ((mot_scale * prd_mv[by][pic_x - bs_x].mv_y), 8) + : 0; + *prednum += ((point[*prednum].motion.mv_x != 0) || (point[*prednum].motion.mv_y != 0)); + + // Up predictor + point[*prednum].motion.mv_x = (by > 0) + ? (short) rshift_rnd_sf ((mot_scale * prd_mv[by - bs_y][pic_x].mv_x), 8) + : (short) rshift_rnd_sf ((mot_scale * prd_mv[4 - bs_y][pic_x].mv_x), 8); + point[*prednum].motion.mv_y = (by > 0) + ? (short) rshift_rnd_sf ((mot_scale * prd_mv[by - bs_y][pic_x].mv_y), 8) + : (short) rshift_rnd_sf ((mot_scale * prd_mv[4 - bs_y][pic_x].mv_y), 8); + *prednum += ((point[*prednum].motion.mv_x != 0) || (point[*prednum].motion.mv_y != 0)); + + // Up-Right predictor + point[*prednum].motion.mv_x = (pic_x + bs_x < img_width) + ? (by > 0) + ? (short) rshift_rnd_sf ((mot_scale * prd_mv[by - bs_y][pic_x + bs_x].mv_x), 8) + : (short) rshift_rnd_sf ((mot_scale * prd_mv[4 - bs_y][pic_x + bs_x].mv_x), 8) + : 0; + point[*prednum].motion.mv_y = (pic_x + bs_x < img_width) + ? (by > 0) + ? (short) rshift_rnd_sf ((mot_scale * prd_mv[by - bs_y][pic_x + bs_x].mv_y), 8) + : (short) rshift_rnd_sf ((mot_scale * prd_mv[4 - bs_y][pic_x + bs_x].mv_y), 8) + : 0; + *prednum += ((point[*prednum].motion.mv_x != 0) || (point[*prednum].motion.mv_y != 0)); +#endif +} + +/*! +************************************************************************************* +* \brief +* Determine stop criterion for EPZS +************************************************************************************* +*/ +distblk +EPZSDetermineStopCriterion (EPZSParameters * p_EPZS, distblk *prevSad, MEBlock * mv_block, distblk lambda_dist) +{ + int blocktype = mv_block->blocktype; + int blockshape_x = (mv_block->blocksize_x >> 2); + PixelPos *block = mv_block->block; + distblk sadA, sadB, sadC, stopCriterion; + sadA = block[0].available ? prevSad[-blockshape_x] : DISTBLK_MAX; + sadB = block[1].available ? prevSad[0] : DISTBLK_MAX; + sadC = block[2].available ? prevSad[blockshape_x] : DISTBLK_MAX; + + stopCriterion = distblkmin (sadA, distblkmin (sadB, sadC)); + stopCriterion = distblkmax (stopCriterion, p_EPZS->minthres[blocktype]); + stopCriterion = distblkmin (stopCriterion, p_EPZS->maxthres[blocktype] + lambda_dist); + stopCriterion = (9 * distblkmax (p_EPZS->medthres[blocktype] + lambda_dist, stopCriterion) + 2 * p_EPZS->medthres[blocktype]) >> 3; + + return stopCriterion + lambda_dist; +} + +/*! +*********************************************************************** +* \brief +* Report function for EPZS Fast ME +* AMT/HYC +*********************************************************************** +*/ +void +EPZSOutputStats (InputParameters * p_Inp, FILE * stat, short stats_file) +{ + if (stats_file == 1) + { + fprintf (stat, " EPZS Pattern : %s\n", EPZS_PATTERN[p_Inp->EPZSPattern]); + fprintf (stat, " EPZS Dual Pattern : %s\n", EPZS_DUAL_PATTERN[p_Inp->EPZSDual]); + fprintf (stat, " EPZS Fixed Predictors : %s\n", EPZS_FIXED_PREDICTORS[p_Inp->EPZSFixed]); +#if (MVC_EXTENSION_ENABLE) + if (p_Inp->num_of_views == 2) + { + fprintf (stat, " BL EPZS Temporal Predictors : %s\n", EPZS_OTHER_PREDICTORS[p_Inp->EPZSTemporal[0]]); + fprintf (stat, " EL EPZS Temporal Predictors : %s\n", EPZS_OTHER_PREDICTORS[p_Inp->EPZSTemporal[1]]); + } + else + { + fprintf (stat, " EPZS Temporal Predictors : %s\n", EPZS_OTHER_PREDICTORS[p_Inp->EPZSTemporal[0]]); + } +#else + fprintf (stat, " EPZS Temporal Predictors : %s\n", EPZS_OTHER_PREDICTORS[p_Inp->EPZSTemporal]); +#endif + fprintf (stat, " EPZS Spatial Predictors : %s\n", EPZS_OTHER_PREDICTORS[p_Inp->EPZSSpatialMem]); +#if (MVC_EXTENSION_ENABLE) + if ( (p_Inp->num_of_views == 2) && (p_Inp->EnableEnhLayerEPZSScalers) ) + { + fprintf (stat, " BL EPZS Threshold Multipliers : (%d %d %d)\n", p_Inp->EPZSMedThresScale[0], p_Inp->EPZSMinThresScale[0], p_Inp->EPZSMaxThresScale[0]); + fprintf (stat, " EL EPZS Threshold Multipliers : (%d %d %d)\n", p_Inp->EPZSMedThresScale[1], p_Inp->EPZSMinThresScale[1], p_Inp->EPZSMaxThresScale[1]); + } + else + { + fprintf (stat, " EPZS Threshold Multipliers : (%d %d %d)\n", p_Inp->EPZSMedThresScale[0], p_Inp->EPZSMinThresScale[0], p_Inp->EPZSMaxThresScale[0]); + } +#else + fprintf (stat, " EPZS Threshold Multipliers : (%d %d %d)\n", p_Inp->EPZSMedThresScale, p_Inp->EPZSMinThresScale, p_Inp->EPZSMaxThresScale); +#endif + fprintf (stat, " EPZS Subpel ME : %s\n", EPZS_SUBPEL_METHOD[p_Inp->EPZSSubPelME]); + fprintf (stat, " EPZS Subpel ME BiPred : %s\n", EPZS_SUBPEL_METHOD[p_Inp->EPZSSubPelMEBiPred]); + } + else + { + fprintf (stat, " EPZS Pattern : %s\n", EPZS_PATTERN[p_Inp->EPZSPattern]); + fprintf (stat, " EPZS Dual Pattern : %s\n", EPZS_DUAL_PATTERN[p_Inp->EPZSDual]); + fprintf (stat, " EPZS Fixed Predictors : %s\n", EPZS_FIXED_PREDICTORS[p_Inp->EPZSFixed]); +#if (MVC_EXTENSION_ENABLE) + if (p_Inp->num_of_views == 2) + { + fprintf (stat, " BL EPZS Temporal Predictors : %s\n", EPZS_OTHER_PREDICTORS[p_Inp->EPZSTemporal[0]]); + fprintf (stat, " EL EPZS Temporal Predictors : %s\n", EPZS_OTHER_PREDICTORS[p_Inp->EPZSTemporal[1]]); + } + else + { + fprintf (stat, " EPZS Temporal Predictors : %s\n", EPZS_OTHER_PREDICTORS[p_Inp->EPZSTemporal[0]]); + } +#else + fprintf (stat, " EPZS Temporal Predictors : %s\n", EPZS_OTHER_PREDICTORS[p_Inp->EPZSTemporal]); +#endif + fprintf (stat, " EPZS Spatial Predictors : %s\n", EPZS_OTHER_PREDICTORS[p_Inp->EPZSSpatialMem]); +#if (MVC_EXTENSION_ENABLE) + if ( (p_Inp->num_of_views == 2) && (p_Inp->EnableEnhLayerEPZSScalers) ) + { + fprintf (stat, " BL EPZS Threshold Multipliers : (%d %d %d)\n", p_Inp->EPZSMedThresScale[0], p_Inp->EPZSMinThresScale[0], p_Inp->EPZSMaxThresScale[0]); + fprintf (stat, " EL EPZS Threshold Multipliers : (%d %d %d)\n", p_Inp->EPZSMedThresScale[1], p_Inp->EPZSMinThresScale[1], p_Inp->EPZSMaxThresScale[1]); + } + else + { + fprintf (stat, " EPZS Threshold Multipliers : (%d %d %d)\n", p_Inp->EPZSMedThresScale[0], p_Inp->EPZSMinThresScale[0], p_Inp->EPZSMaxThresScale[0]); + } +#else + fprintf (stat, " EPZS Threshold Multipliers : (%d %d %d)\n", p_Inp->EPZSMedThresScale, p_Inp->EPZSMinThresScale, p_Inp->EPZSMaxThresScale); +#endif + fprintf (stat, " EPZS Subpel ME : %s\n", EPZS_SUBPEL_METHOD[p_Inp->EPZSSubPelME]); + fprintf (stat, " EPZS Subpel ME BiPred : %s\n", EPZS_SUBPEL_METHOD[p_Inp->EPZSSubPelMEBiPred]); + } +} diff --git a/lencod/src/me_epzs_int.c b/lencod/src/me_epzs_int.c new file mode 100644 index 0000000..c30a691 --- /dev/null +++ b/lencod/src/me_epzs_int.c @@ -0,0 +1,1030 @@ + +/*! +************************************************************************************* +* \file me_epzs_int.c +* +* \brief +* Motion Estimation using EPZS +* +* \author +* Main contributors (see contributors.h for copyright, address and affiliation details) +* - Alexis Michael Tourapis +* - Athanasios Leontaris +* +************************************************************************************* +*/ + +#include "contributors.h" + +#include + +#include "global.h" +#include "image.h" +#include "memalloc.h" +#include "mb_access.h" +#include "refbuf.h" +#include "macroblock.h" +#include "me_distortion.h" +#include "me_epzs.h" +#include "me_epzs_common.h" +#include "mv_search.h" + +// Functions + +/*! +*********************************************************************** +* \brief +* FAST Motion Estimation using EPZS +* AMT/HYC +*********************************************************************** +*/ +distblk // ==> minimum motion cost after search +EPZS_integer_motion_estimation (Macroblock * currMB, // <-- current Macroblock + MotionVector * pred_mv, // <-- motion vector predictor in sub-pel units + MEBlock * mv_block, // <-- motion vector information + distblk min_mcost, // <-- minimum motion cost (cost for center or huge value) + int lambda_factor // <-- lagrangian parameter for determining motion cost + ) +{ + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + InputParameters *p_Inp = currMB->p_Inp; + EPZSParameters *p_EPZS = currSlice->p_EPZS; + PicMotionParams **motion = p_Vid->enc_picture->mv_info; + + int blocktype = mv_block->blocktype; + + int list = mv_block->list; + int cur_list = list + currMB->list_offset; + short ref = mv_block->ref_idx; + MotionVector *mv = &mv_block->mv[list]; + SearchWindow *searchRange = &mv_block->searchRange; + int mapCenter_x = searchRange->max_x - mv->mv_x; + int mapCenter_y = searchRange->max_y - mv->mv_y; + StorablePicture *ref_picture = currSlice->listX[cur_list][ref]; + + distblk lambda_dist = weighted_cost(lambda_factor, 2); + distblk stopCriterion = p_EPZS->medthres[blocktype] + lambda_dist; + distblk *prevSad = &p_EPZS->distortion[cur_list][blocktype - 1][mv_block->pos_x2]; + + MotionVector *p_motion = NULL; + + EPZSStructure *searchPatternF = p_EPZS->searchPattern; + uint16 **EPZSMap = &p_EPZS->EPZSMap[mapCenter_y]; + uint16 *EPZSPoint = &p_EPZS->EPZSMap[searchRange->max_y][searchRange->max_x]; + + MotionVector center = pad_MVs (*mv, mv_block); + MotionVector pred = pad_MVs (*pred_mv, mv_block); + MotionVector tmp = *mv, cand = center; + + ++p_EPZS->BlkCount; + if (p_EPZS->BlkCount == 0) + ++p_EPZS->BlkCount; + + if (p_Inp->EPZSSpatialMem) + { +#if EPZSREF + p_motion = &p_EPZS->p_motion[cur_list][ref][blocktype - 1][mv_block->block_y][mv_block->pos_x2]; +#else + p_motion = &p_EPZS->p_motion[cur_list][blocktype - 1][mv_block->block_y][mv_block->pos_x2]; +#endif + } + + // Clear EPZSMap + // memset(EPZSMap[0],FALSE,searcharray*searcharray); + // Check median candidate; + //p_EPZS->EPZSMap[0][0] = p_EPZS->BlkCount; + *EPZSPoint = p_EPZS->BlkCount; + + //--- initialize motion cost (cost for motion vector) and check --- + min_mcost = mv_cost (p_Vid, lambda_factor, &cand, &pred); + + //--- add residual cost to motion cost --- + min_mcost += mv_block->computePredFPel (ref_picture, mv_block, DISTBLK_MAX - min_mcost, &cand); + + + // Additional threshold for ref>0 + //if ((ref > 0 && currSlice->structure == FRAME) && (*prevSad < imin (p_EPZS->medthres[blocktype] + lambda_dist, min_mcost))) + if ((ref > 0 && currSlice->structure == FRAME) && + ((*prevSad < distblkmin (p_EPZS->medthres[blocktype] + lambda_dist, min_mcost)) || (*prevSad * 8 < min_mcost))) + { +#if EPZSREF + if (p_Inp->EPZSSpatialMem) +#else + if (p_Inp->EPZSSpatialMem && ref == 0) +#endif + { + *p_motion = tmp; + } + return min_mcost; + } + + //! If p_EPZS->medthres satisfied, then terminate, otherwise generate Predictors + //! Condition could be strengthened by consideration distortion of adjacent partitions. + if (min_mcost > stopCriterion) + { + SPoint *p_EPZS_point = p_EPZS->predictor->point; + Boolean checkMedian = FALSE; + distblk second_mcost = DISTBLK_MAX; + distblk mcost; + int prednum = 5; + int conditionEPZS; + MotionVector tmp2 = {0, 0}, tmv; + int pos; + short invalid_refs = 0; + + stopCriterion = EPZSDetermineStopCriterion (p_EPZS, prevSad, mv_block, lambda_dist); + + if (min_mcost < (stopCriterion >> 1)) + { +#if EPZSREF + if (p_Inp->EPZSSpatialMem) +#else + if (p_Inp->EPZSSpatialMem && ref == 0) +#endif + { + *p_motion = tmp; + } + + // This seems ok to add since this is considered a good enough position + if ((ref == 0) || (*prevSad > min_mcost)) + *prevSad = min_mcost; + + return min_mcost; + } + + //! Add Spatial Predictors in predictor list. + //! Scheme adds zero, left, top-left, top, top-right. Note that top-left adds very little + //! in terms of performance and could be removed with little penalty if any. + invalid_refs = EPZS_spatial_predictors (p_EPZS, mv_block, + list, currMB->list_offset, ref, motion); + + if (p_Inp->EPZSSpatialMem) + EPZS_spatial_memory_predictors (p_EPZS, mv_block, cur_list, &prednum, ref_picture->size_x >> 2); + + // Temporal predictors +#if (MVC_EXTENSION_ENABLE) + if ( p_Inp->EPZSTemporal[currSlice->view_id] ) +#else + // Temporal predictors + if (p_Inp->EPZSTemporal) +#endif + { + EPZS_temporal_predictors (currMB, ref_picture, p_EPZS, mv_block, &prednum, stopCriterion, min_mcost); + } + + //! Window Size Based Predictors + //! Basically replaces a Hierarchical ME concept and helps escaping local minima, or + //! determining large motion variations. + //! Following predictors can be adjusted further (i.e. removed, conditioned etc) + //! based on distortion, correlation of adjacent MVs, complexity etc. These predictors + //! and their conditioning could also be moved after all other predictors have been + //! tested. Adaptation could also be based on type of material and coding mode (i.e. + //! field/frame coding,MBAFF etc considering the higher dependency with opposite parity field + //conditionEPZS = ((min_mcost > stopCriterion) + // && (p_Inp->EPZSFixed > 1 || (p_Inp->EPZSFixed && currSlice->slice_type == P_SLICE))); + //conditionEPZS = ((ref == 0) && (min_mcost > stopCriterion) + //&& (p_Inp->EPZSFixed > 1 || (p_Inp->EPZSFixed && currSlice->slice_type == P_SLICE))); + //conditionEPZS = ((min_mcost > stopCriterion) && ((ref < 2 && blocktype < 4) + conditionEPZS = (p_Inp->EPZSFixed == 3 && (currMB->mb_x == 0 || currMB->mb_y == 0)) + || ((min_mcost > 3 * stopCriterion) && ((ref < 2 && blocktype < 4) || (ref < 1 && blocktype == 4) + || ((currSlice->structure != FRAME || currMB->list_offset) + && ref < 3)) + && (p_Inp->EPZSFixed > 1 || (p_Inp->EPZSFixed && currSlice->slice_type == P_SLICE))); + + if (conditionEPZS) + EPZSWindowPredictors (mv, p_EPZS->predictor, &prednum, + (invalid_refs > 2) && (ref < 1 + (currSlice->structure != FRAME || currMB->list_offset)) + ? p_EPZS->window_predictor_ext : p_EPZS->window_predictor); + + //! Blocktype/Reference dependent predictors. + //! Since already mvs for other blocktypes/references have been computed, we can reuse + //! them in order to easier determine the optimal point. Use of predictors could depend + //! on cost, + //conditionEPZS = (ref == 0 || (ref > 0 && min_mcost > stopCriterion) || currSlice->structure != FRAME || currMB->list_offset); + conditionEPZS = (ref == 0 || (ref > 0 && min_mcost > 2 * stopCriterion)); + + if (conditionEPZS && currMB->mbAddrX != 0 && p_Inp->EPZSBlockType) + EPZSBlockTypePredictorsMB (currSlice, mv_block, p_EPZS_point, &prednum); + + //! Check all predictors + for (pos = 0; pos < prednum; ++pos) + { + tmv = p_EPZS_point[pos].motion; + //if (((iabs (tmv.mv_x - mv->mv_x) > searchRange->max_x || iabs (tmv.mv_y - mv->mv_y) > searchRange->max_y)) && (tmv.mv_x || tmv.mv_y)) + if ((iabs (tmv.mv_x - mv->mv_x) - searchRange->max_x <= 0) && (iabs (tmv.mv_y - mv->mv_y) - searchRange->max_y <= 0)) + { + EPZSPoint = &EPZSMap[tmv.mv_y][mapCenter_x + tmv.mv_x]; + if (*EPZSPoint != p_EPZS->BlkCount) + { + *EPZSPoint = p_EPZS->BlkCount; + cand = pad_MVs (tmv, mv_block); + + //--- set motion cost (cost for motion vector) and check --- + mcost = mv_cost (p_Vid, lambda_factor, &cand, &pred); + + if (mcost < second_mcost) + { + mcost += mv_block->computePredFPel (ref_picture, mv_block, second_mcost - mcost, &cand); + + //--- check if motion cost is less than minimum cost --- + if (mcost < min_mcost) + { + tmp2 = tmp; + tmp = tmv; + second_mcost = min_mcost; + min_mcost = mcost; + checkMedian = TRUE; + } + //else if (mcost < second_mcost && (tmp.mv_x != tmv.mv_x || tmp.mv_y != tmv.mv_y)) + else if (mcost < second_mcost) + { + tmp2 = tmv; + second_mcost = mcost; + checkMedian = TRUE; + } + } + } + } + } + + if ((ref > 0 && currSlice->structure == FRAME) && (*prevSad * 3 < min_mcost)) + { +#if EPZSREF + if (p_Inp->EPZSSpatialMem) +#else + if (p_Inp->EPZSSpatialMem && ref == 0) +#endif + { + *p_motion = tmp; + } + + *mv = tmp; + + // this condition seems redundant since (*prevSad * 3 < min_mcost) + //if ((*prevSad > min_mcost)) + // *prevSad = min_mcost; + + return min_mcost; + } + + //! Refine using EPZS pattern if needed + //! Note that we are using a conservative threshold method. Threshold + //! could be tested after checking only a certain number of predictors + //! instead of the full set. Code could be easily modified for this task. + if (min_mcost > stopCriterion) + { + const int mv_range = 10; + int patternStop = 0, pointNumber = 0, checkPts, nextLast = 0; + int totalCheckPts = 0, motionDirection = 0; + + //! Adapt pattern based on different conditions. + if (p_Inp->EPZSPattern != 0) + { + if ((min_mcost < stopCriterion + ((3 * p_EPZS->medthres[blocktype]) >> 1))) + { + if ((tmp.mv_x == 0 && tmp.mv_y == 0) + || (iabs (tmp.mv_x - mv->mv_x) < (mv_range) && iabs (tmp.mv_y - mv->mv_y) < (mv_range))) + searchPatternF = p_Vid->sdiamond; + else + searchPatternF = p_Vid->square; + } + else if (ref > 0 && blocktype != 1) + searchPatternF = p_Vid->square; + else + searchPatternF = p_EPZS->searchPattern; + } + + //! center on best predictor + center = tmp; + + for (;;) + { + totalCheckPts = searchPatternF->searchPoints; + do + { + checkPts = totalCheckPts; + do + { + tmv = add_MVs (center, &(searchPatternF->point[pointNumber].motion)); + + if (((iabs (tmv.mv_x - mv->mv_x) - searchRange->max_x) <= 0) && ((iabs (tmv.mv_y - mv->mv_y) - searchRange->max_y) <= 0)) + { + EPZSPoint = &EPZSMap[tmv.mv_y][mapCenter_x + tmv.mv_x]; + if (*EPZSPoint != p_EPZS->BlkCount) + { + *EPZSPoint = p_EPZS->BlkCount; + cand = pad_MVs (tmv, mv_block); + + mcost = mv_cost (p_Vid, lambda_factor, &cand, &pred); + + if (mcost < min_mcost) + { + mcost += mv_block->computePredFPel (ref_picture, mv_block, min_mcost - mcost, &cand); + + if (mcost < min_mcost) + { + tmp = tmv; + min_mcost = mcost; + motionDirection = pointNumber; + } + } + } + } + ++pointNumber; + if (pointNumber >= searchPatternF->searchPoints) + pointNumber -= searchPatternF->searchPoints; + checkPts--; + } + while (checkPts > 0); + + if (nextLast || ((tmp.mv_x == center.mv_x) && (tmp.mv_y == center.mv_y))) + { + patternStop = searchPatternF->stopSearch; + searchPatternF = searchPatternF->nextpattern; + totalCheckPts = searchPatternF->searchPoints; + nextLast = searchPatternF->nextLast; + motionDirection = 0; + pointNumber = 0; + } + else + { + totalCheckPts = searchPatternF->point[motionDirection].next_points; + pointNumber = searchPatternF->point[motionDirection].start_nmbr; + center = tmp; + } + } + while (patternStop != 1); + + if ((ref > 0) && (currSlice->structure == FRAME) + && ((4 * *prevSad < min_mcost) || ((3 * *prevSad < min_mcost) && (*prevSad <= stopCriterion)))) + { + *mv = tmp; +#if EPZSREF + if (p_Inp->EPZSSpatialMem) +#else + if (p_Inp->EPZSSpatialMem && ref == 0) +#endif + { + *p_motion = tmp; + } + + return min_mcost; + } + + //! Check Second best predictor with EPZS pattern + conditionEPZS = (checkMedian == TRUE) + && (ref == 0 || (ref > 0 && min_mcost < 2 * *prevSad)) + && (min_mcost > (( 3 * stopCriterion) >> 1)) && (p_Inp->EPZSDual > 0); + + if (!conditionEPZS) + break; + + pointNumber = 0; + patternStop = 0; + motionDirection = 0; + nextLast = 0; + + if ((tmp.mv_x == 0 && tmp.mv_y == 0) || (tmp.mv_x == mv->mv_x && tmp.mv_y == mv->mv_y)) + { + if (iabs (tmp.mv_x - mv->mv_x) < (mv_range) && iabs (tmp.mv_y - mv->mv_y) < (mv_range)) + searchPatternF = p_Vid->sdiamond; + else + searchPatternF = p_Vid->square; + } + else + searchPatternF = p_EPZS->searchPatternD; + + //! Second best. Note that following code is identical as for best predictor. + center = tmp2; + checkMedian = FALSE; + } + } + } + + if ((ref == 0) || (*prevSad > min_mcost)) + *prevSad = min_mcost; +#if EPZSREF + if (p_Inp->EPZSSpatialMem) +#else + if (p_Inp->EPZSSpatialMem && ref == 0) +#endif + { + *p_motion = tmp; + //printf("value %d %d %d %d\n", p_motion->mv_x, p_motion->mv_y, p_motion[cur_list][ref][0][0][0].mv_x, p_motion[list + list_offset][ref][0][0][0].mv_y); + //printf("xxxxx %d %d %d %d\n", p_motion->mv_x, p_motion->mv_y, p_motion[cur_list][ref][blocktype - 1][mv_block->block_y][pic_pix_x2].mv_x, p_motion[cur_list][ref][blocktype - 1][mv_block->block_y][pic_pix_x2].mv_y); + } + + *mv = tmp; + + return min_mcost; +} + + +/*! +*********************************************************************** +* \brief +* FAST Motion Estimation using EPZS +* AMT/HYC +*********************************************************************** +*/ +distblk // ==> minimum motion cost after search +EPZS_integer_subMB_motion_estimation (Macroblock * currMB, // <-- current Macroblock + MotionVector * pred_mv, // <-- motion vector predictor in sub-pel units + MEBlock * mv_block, // <-- motion vector information + distblk min_mcost, // <-- minimum motion cost (cost for center or huge value) + int lambda_factor // <-- lagrangian parameter for determining motion cost + ) +{ + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + InputParameters *p_Inp = currMB->p_Inp; + EPZSParameters *p_EPZS = currSlice->p_EPZS; + PicMotionParams **motion = p_Vid->enc_picture->mv_info; + + int blocktype = mv_block->blocktype; + int list = mv_block->list; + int cur_list = list + currMB->list_offset; + short ref = mv_block->ref_idx; + StorablePicture *ref_picture = currSlice->listX[cur_list][ref]; + + MotionVector *mv = &mv_block->mv[list]; + MotionVector center = pad_MVs (*mv, mv_block); + MotionVector cand = center; + MotionVector pred = pad_MVs (*pred_mv, mv_block); + MotionVector tmp = *mv; + SearchWindow *searchRange = &mv_block->searchRange; + int mapCenter_x = searchRange->max_x - mv->mv_x; + int mapCenter_y = searchRange->max_y - mv->mv_y; + + short pic_pix_x2 = mv_block->pos_x2; + distblk lambda_dist = weighted_cost(lambda_factor,3); + distblk stopCriterion = p_EPZS->medthres[blocktype] + lambda_dist; + distblk *prevSad = &p_EPZS->distortion[cur_list][blocktype - 1][pic_pix_x2]; + MotionVector *p_motion = NULL; + + EPZSStructure *searchPatternF = p_EPZS->searchPattern; + uint16 **EPZSMap = &p_EPZS->EPZSMap[mapCenter_y]; + + ++p_EPZS->BlkCount; + if (p_EPZS->BlkCount == 0) + ++p_EPZS->BlkCount; + + if (p_Inp->EPZSSpatialMem) + { +#if EPZSREF + p_motion = &p_EPZS->p_motion[cur_list][ref][blocktype - 1][mv_block->block_y][pic_pix_x2]; +#else + p_motion = &p_EPZS->p_motion[cur_list][blocktype - 1][mv_block->block_y][pic_pix_x2]; +#endif + } + + // Clear p_EPZS->EPZSMap + // memset(p_EPZS->EPZSMap[0],FALSE,searcharray*searcharray); + // Check median candidate; + //p_EPZS->EPZSMap[0][0] = p_EPZS->BlkCount; + p_EPZS->EPZSMap[searchRange->max_y][searchRange->max_x] = p_EPZS->BlkCount; + + //--- initialize motion cost (cost for motion vector) and check --- + min_mcost = mv_cost (p_Vid, lambda_factor, &cand, &pred); + + //--- add residual cost to motion cost --- + min_mcost += mv_block->computePredFPel (ref_picture, mv_block, DISTBLK_MAX-min_mcost, &cand); + + + // Additional threshold for ref>0 + if ((ref > 0 && currSlice->structure == FRAME) && + ((*prevSad < distblkmin (p_EPZS->medthres[blocktype] + lambda_dist, min_mcost)) || (*prevSad * 6 < min_mcost))) + { +#if EPZSREF + if (p_Inp->EPZSSpatialMem) +#else + if (p_Inp->EPZSSpatialMem && ref == 0) +#endif + { + *p_motion = tmp; + } + return min_mcost; + } + + //! If p_EPZS->medthres satisfied, then terminate, otherwise generate Predictors + //! Condition could be strengthened by consideration distortion of adjacent partitions. + if (min_mcost > stopCriterion) + { + SPoint *p_EPZS_point = p_EPZS->predictor->point; + Boolean checkMedian = FALSE; + distblk second_mcost = DISTBLK_MAX; + distblk mcost; + int prednum = 5; + int conditionEPZS; + + MotionVector tmv, tmp2 = {0,0}; + int pos; + + stopCriterion = EPZSDetermineStopCriterion (p_EPZS, prevSad, mv_block, lambda_dist); + + if (min_mcost < (stopCriterion >> 1)) + { +#if EPZSREF + if (p_Inp->EPZSSpatialMem) +#else + if (p_Inp->EPZSSpatialMem && ref == 0) +#endif + { + *p_motion = tmp; + } + return min_mcost; + } + + //! Add Spatial Predictors in predictor list. + //! Scheme adds zero, left, top-left, top, top-right. Note that top-left adds very little + //! in terms of performance and could be removed with little penalty if any. + EPZS_spatial_predictors (p_EPZS, mv_block, list, currMB->list_offset, ref, motion); + + if (p_Inp->EPZSSpatialMem) + EPZS_spatial_memory_predictors (p_EPZS, mv_block, cur_list, &prednum, ref_picture->size_x >> 2); + + //! Blocktype/Reference dependent predictors. + //! Since already mvs for other blocktypes/references have been computed, we can reuse + //! them in order to easier determine the optimal point. Use of predictors could depend + //! on cost, + conditionEPZS = (ref == 0 || (ref > 0 && min_mcost > 2 * stopCriterion)); + + if (conditionEPZS && currMB->mbAddrX != 0 && p_Inp->EPZSBlockType) + EPZSBlockTypePredictors (currSlice, mv_block, p_EPZS_point, &prednum); + + //! Check all predictors + for (pos = 0; pos < prednum; ++pos) + { + tmv = p_EPZS_point[pos].motion; + //if (((iabs (tmv.mv_x - mv->mv_x) > searchRange->max_x || iabs (tmv.mv_y - mv->mv_y) > searchRange->max_y)) && (tmv.mv_x || tmv.mv_y)) + if ((iabs (tmv.mv_x - mv->mv_x) - searchRange->max_x <= 0) && (iabs (tmv.mv_y - mv->mv_y) - searchRange->max_y <= 0)) + { + + if (EPZSMap[tmv.mv_y][mapCenter_x + tmv.mv_x] != p_EPZS->BlkCount) + { + EPZSMap[tmv.mv_y][mapCenter_x + tmv.mv_x] = p_EPZS->BlkCount; + + cand = pad_MVs (tmv, mv_block); + + //--- set motion cost (cost for motion vector) and check --- + mcost = mv_cost (p_Vid, lambda_factor, &cand, &pred); + + if (mcost < second_mcost) + { + mcost += mv_block->computePredFPel (ref_picture, mv_block, second_mcost - mcost, &cand); + + //--- check if motion cost is less than minimum cost --- + if (mcost < min_mcost) + { + tmp2 = tmp; + tmp = tmv; + second_mcost = min_mcost; + min_mcost = mcost; + checkMedian = TRUE; + } + //else if (mcost < second_mcost && (tmp.mv_x != tmv.mv_x || tmp.mv_y != tmv.mv_y)) + else if (mcost < second_mcost) + { + tmp2 = tmv; + second_mcost = mcost; + checkMedian = TRUE; + } + } + } + } + + if ((ref > 0 && currSlice->structure == FRAME) && (*prevSad * 3 < min_mcost)) + { +#if EPZSREF + if (p_Inp->EPZSSpatialMem) +#else + if (p_Inp->EPZSSpatialMem && ref == 0) +#endif + { + *p_motion = tmp; + } + return min_mcost; + } + + // At this point, let us add an early termination criterion + // after checking each predictor. This can help speed up a lot. + if (min_mcost < ((3 * stopCriterion) >> 2)) + { +#if EPZSREF + if (p_Inp->EPZSSpatialMem) +#else + if (p_Inp->EPZSSpatialMem && ref == 0) +#endif + { + *p_motion = tmp; + } + *mv = tmp; + return min_mcost; + } + } + + //! Refine using EPZS pattern if needed + //! Note that we are using a conservative threshold method. Threshold + //! could be tested after checking only a certain number of predictors + //! instead of the full set. Code could be easily modified for this task. + if (min_mcost > stopCriterion) + { + int patternStop = 0, pointNumber = 0, checkPts, nextLast = 0; + int totalCheckPts = 0, motionDirection = 0; + const int mv_range = 12; + + //! Adapt pattern based on different conditions. + if (p_Inp->EPZSPattern != 0) + { + if ((min_mcost < stopCriterion + ((3 * p_EPZS->medthres[blocktype]) >> 1))) + { + if ((blocktype == 7) + || (tmp.mv_x == 0 && tmp.mv_y == 0) || (iabs (tmp.mv_x - mv->mv_x) < (mv_range) && iabs (tmp.mv_y - mv->mv_y) < (mv_range))) + searchPatternF = p_Vid->sdiamond; + else + searchPatternF = p_Vid->square; + } + else + searchPatternF = p_Vid->square; + } + + //! center on best predictor + center = tmp; + for (;;) + { + totalCheckPts = searchPatternF->searchPoints; + do + { + checkPts = totalCheckPts; + do + { + tmv = add_MVs (center, &(searchPatternF->point[pointNumber].motion)); + + if ((iabs (tmv.mv_x - mv->mv_x) <= searchRange->max_x) && (iabs (tmv.mv_y - mv->mv_y) <= searchRange->max_y)) + { + if (EPZSMap[tmv.mv_y][mapCenter_x + tmv.mv_x] != p_EPZS->BlkCount) + { + EPZSMap[tmv.mv_y][mapCenter_x + tmv.mv_x] = p_EPZS->BlkCount; + cand = pad_MVs(tmv, mv_block); + + mcost = mv_cost (p_Vid, lambda_factor, &cand, &pred); + if (mcost < min_mcost) + { + + mcost += mv_block->computePredFPel (ref_picture, mv_block, min_mcost - mcost, &cand); + + if (mcost < min_mcost) + { + tmp = tmv; + min_mcost = mcost; + motionDirection = pointNumber; + } + } + } + } + ++pointNumber; + if (pointNumber >= searchPatternF->searchPoints) + pointNumber -= searchPatternF->searchPoints; + checkPts--; + } + while (checkPts > 0); + + if (nextLast || ((tmp.mv_x == center.mv_x) && (tmp.mv_y == center.mv_y))) + { + patternStop = searchPatternF->stopSearch; + searchPatternF = searchPatternF->nextpattern; + totalCheckPts = searchPatternF->searchPoints; + nextLast = searchPatternF->nextLast; + motionDirection = 0; + pointNumber = 0; + } + else + { + totalCheckPts = searchPatternF->point[motionDirection].next_points; + pointNumber = searchPatternF->point[motionDirection].start_nmbr; + center = tmp; + } + } + while (patternStop != 1); + + if ((ref > 0) && (currSlice->structure == FRAME) + && ((4 * *prevSad < min_mcost) || ((3 * *prevSad < min_mcost) && (*prevSad <= stopCriterion)))) + { + *mv = tmp; +#if EPZSREF + if (p_Inp->EPZSSpatialMem) +#else + if (p_Inp->EPZSSpatialMem && ref == 0) +#endif + { + *p_motion = tmp; + } + + return min_mcost; + } + + //! Check Second best predictor with EPZS pattern + conditionEPZS = (checkMedian == TRUE) && (blocktype != 7) + //conditionEPZS = (checkMedian == TRUE) + && (ref == 0 || (ref > 0 && min_mcost < 2 * *prevSad)) + && ((currSlice->slice_type == P_SLICE)) && (min_mcost > ((3 * stopCriterion) >> 1)) && (p_Inp->EPZSDual > 0); + + if (!conditionEPZS) + break; + + pointNumber = 0; + patternStop = 0; + motionDirection = 0; + nextLast = 0; + + if ((tmp.mv_x == 0 && tmp.mv_y == 0) || (tmp.mv_x == mv->mv_x && tmp.mv_y == mv->mv_y)) + { + if ((blocktype == 7) || (iabs (tmp.mv_x - mv->mv_x) < (mv_range) && iabs (tmp.mv_y - mv->mv_y) < (mv_range))) + searchPatternF = p_Vid->sdiamond; + else + searchPatternF = p_Vid->square; + } + else + searchPatternF = p_EPZS->searchPatternD; + + //! Now consider second best. + center = tmp2; + checkMedian = FALSE; + } + } + } + + if ((ref == 0) || (*prevSad > min_mcost)) + *prevSad = min_mcost; + +#if EPZSREF + if (p_Inp->EPZSSpatialMem) +#else + if (p_Inp->EPZSSpatialMem && ref == 0) +#endif + { + *p_motion = tmp; + } + + *mv = tmp; + + return min_mcost; +} + +/*! +*********************************************************************** +* \brief +* FAST Motion Estimation using EPZS +* AMT/HYC +* \return +* minimum motion cost after search +*********************************************************************** +*/ +distblk // ==> minimum motion cost after search +EPZS_integer_bipred_motion_estimation (Macroblock * currMB, // <-- Current Macroblock + int list, // <-- reference list + MotionVector * pred_mv1, // <-- motion vector predictor in sub-pel units + MotionVector * pred_mv2, // <-- motion vector predictor in sub-pel units + MotionVector * mv1, // <--> in: search center (x|y) / out: motion vector (x|y) - in sub-pel units + MotionVector * mv2, // <--> in: search center (x|y) + MEBlock * mv_block, // <-- motion vector information + int search_range, // <-- 1-d search range in sub-pel units + distblk min_mcost, // <-- minimum motion cost (cost for center or huge value) + int lambda_factor // <-- lagrangian parameter for determining motion cost + ) +{ + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + InputParameters *p_Inp = currMB->p_Inp; + EPZSParameters *p_EPZS = currSlice->p_EPZS; + PicMotionParams **motion = p_Vid->enc_picture->mv_info; + + int blocktype = mv_block->blocktype; + short ref = mv_block->ref_idx; + + StorablePicture *ref_picture1 = currSlice->listX[list + currMB->list_offset][ref]; + StorablePicture *ref_picture2 = currSlice->listX[(list ^ 1) + currMB->list_offset][0]; + + int mapCenter_x = search_range - mv1->mv_x; + int mapCenter_y = search_range - mv1->mv_y; + distblk lambda_dist = weighted_cost(lambda_factor, 4); + distblk stopCriterion = p_EPZS->medthres[blocktype] + lambda_dist; + distblk *prevSad = &p_EPZS->bi_distortion[list + currMB->list_offset][blocktype - 1][mv_block->pos_x2]; + EPZSStructure *searchPatternF = p_EPZS->searchPattern; + uint16 **EPZSMap = &p_EPZS->EPZSMap[mapCenter_y]; + + MotionVector tmp = *mv1; + MotionVector center1 = pad_MVs (*mv1, mv_block); + MotionVector center2 = pad_MVs (*mv2, mv_block); + MotionVector pred1 = pad_MVs (*pred_mv1, mv_block); + MotionVector pred2 = pad_MVs (*pred_mv2, mv_block); + MotionVector cand1 = center1; + MotionVector cand2 = center2; + + ++p_EPZS->BlkCount; + if (p_EPZS->BlkCount == 0) + ++p_EPZS->BlkCount; + + + // Clear p_EPZS->EPZSMap + //memset(p_EPZS->EPZSMap[0],FALSE,searcharray*searcharray); + // Check median candidate; + //p_EPZS->EPZSMap[0][0] = p_EPZS->BlkCount; + p_EPZS->EPZSMap[search_range][search_range] = p_EPZS->BlkCount; + + //--- initialize motion cost (cost for motion vector) and check --- + min_mcost = mv_cost (p_Vid, lambda_factor, &cand1, &pred1); + min_mcost += mv_cost (p_Vid, lambda_factor, &cand2, &pred2); + + //--- add residual cost to motion cost --- + min_mcost += mv_block->computeBiPredFPel (ref_picture1, ref_picture2, mv_block, DISTBLK_MAX-min_mcost, &cand1, &cand2); + + //! If p_EPZS->medthres satisfied, then terminate, otherwise generate Predictors + if (min_mcost > stopCriterion) + { + SPoint *p_EPZS_point = p_EPZS->predictor->point; + Boolean checkMedian = FALSE; + distblk second_mcost = DISTBLK_MAX; + distblk mcost; + int prednum = 5; + MotionVector tmv, tmp2 = { 0, 0 }; + int pos; + + stopCriterion = EPZSDetermineStopCriterion (p_EPZS, prevSad, mv_block, lambda_dist); + + + //! Add Spatial Predictors in predictor list. + //! Scheme adds zero, left, top-left, top, top-right. Note that top-left adds very little + //! in terms of performance and could be removed with little penalty if any. + EPZS_spatial_predictors (p_EPZS, mv_block, list, currMB->list_offset, ref, motion); + + //! Check all predictors + for (pos = 0; pos < prednum; ++pos) + { + tmv = p_EPZS_point[pos].motion; + //if ((iabs (tmv.mv_x - mv1->mv_x) > search_range || iabs (tmv.mv_y - mv1->mv_y) > search_range) && (tmv.mv_x || tmv.mv_y)) + if (iabs (tmv.mv_x - mv1->mv_x) <= search_range && iabs (tmv.mv_y - mv1->mv_y) <= search_range) + { + if (EPZSMap[tmv.mv_y][mapCenter_x + tmv.mv_x] != p_EPZS->BlkCount) + { + EPZSMap[tmv.mv_y][mapCenter_x + tmv.mv_x] = p_EPZS->BlkCount; + + cand1 = pad_MVs (tmv, mv_block); + + //--- set motion cost (cost for motion vector) and check --- + mcost = mv_cost (p_Vid, lambda_factor, &cand1, &pred1); + mcost += mv_cost (p_Vid, lambda_factor, &cand2, &pred2); + + if (mcost >= second_mcost) + continue; + + mcost += mv_block->computeBiPredFPel (ref_picture1, ref_picture2, mv_block, second_mcost - mcost, &cand1, &cand2); + + //--- check if motion cost is less than minimum cost --- + if (mcost < min_mcost) + { + tmp2 = tmp; + tmp = tmv; + second_mcost = min_mcost; + min_mcost = mcost; + checkMedian = TRUE; + } + //else if (mcost < second_mcost && (tmp.mv_x != tmv.mv_x || tmp.mv_y != tmv.mv_y)) + else if (mcost < second_mcost) + { + tmp2 = tmv; + second_mcost = mcost; + checkMedian = TRUE; + } + } + } + } + + //! Refine using EPZS pattern if needed + //! Note that we are using a conservative threshold method. Threshold + //! could be tested after checking only a certain number of predictors + //! instead of the full set. Code could be easily modified for this task. + if (min_mcost > stopCriterion) + { + int conditionEPZS; + int patternStop = 0, pointNumber = 0, checkPts, nextLast = 0; + int totalCheckPts = 0, motionDirection = 0; + + const int mv_range = 12; + + //! Adapt pattern based on different conditions. + if (p_Inp->EPZSPattern != 0) + { + if ((min_mcost < stopCriterion + ((4 * p_EPZS->medthres[blocktype]) >> 1))) + { + if ((tmp.mv_x == 0 && tmp.mv_y == 0) || (iabs (tmp.mv_x - mv1->mv_x) < (mv_range) && iabs (tmp.mv_y - mv1->mv_y) < (mv_range))) + searchPatternF = p_Vid->sdiamond; + else + searchPatternF = p_Vid->square; + } + else if (blocktype > 5 || (ref > 0 && blocktype != 1)) + searchPatternF = p_Vid->square; + else + searchPatternF = p_EPZS->searchPattern; + } + + //! center on best predictor + center1 = tmp; + for (;;) + { + totalCheckPts = searchPatternF->searchPoints; + do + { + checkPts = totalCheckPts; + do + { + tmv = add_MVs (center1, &(searchPatternF->point[pointNumber].motion)); + + if ((iabs (tmv.mv_x - mv1->mv_x) <= search_range) && (iabs (tmv.mv_y - mv1->mv_y) <= search_range)) + { + if (EPZSMap[tmv.mv_y][mapCenter_x + tmv.mv_x] != p_EPZS->BlkCount) + { + EPZSMap[tmv.mv_y][mapCenter_x + tmv.mv_x] = p_EPZS->BlkCount; + cand1 = pad_MVs (tmv, mv_block); + + mcost = mv_cost (p_Vid, lambda_factor, &cand1, &pred1); + mcost += mv_cost (p_Vid, lambda_factor, &cand2, &pred2); + + if (mcost < min_mcost) + { + mcost += mv_block->computeBiPredFPel (ref_picture1, ref_picture2, mv_block, min_mcost - mcost, &cand1, &cand2); + + if (mcost < min_mcost) + { + tmp = tmv; + min_mcost = mcost; + motionDirection = pointNumber; + } + } + } + } + ++pointNumber; + if (pointNumber >= searchPatternF->searchPoints) + pointNumber -= searchPatternF->searchPoints; + checkPts--; + } + while (checkPts > 0); + + if (nextLast || ((tmp.mv_x == center1.mv_x) && (tmp.mv_y == center1.mv_y))) + { + patternStop = searchPatternF->stopSearch; + searchPatternF = searchPatternF->nextpattern; + totalCheckPts = searchPatternF->searchPoints; + nextLast = searchPatternF->nextLast; + motionDirection = 0; + pointNumber = 0; + } + else + { + totalCheckPts = searchPatternF->point[motionDirection].next_points; + pointNumber = searchPatternF->point[motionDirection].start_nmbr; + center1 = tmp; + } + } + while (patternStop != 1); + + //! Check Second best predictor with EPZS pattern + conditionEPZS = (checkMedian == TRUE) && (blocktype < 5) && (min_mcost > stopCriterion) && (p_Inp->EPZSDual > 0); + + if (!conditionEPZS) + break; + + pointNumber = 0; + patternStop = 0; + motionDirection = 0; + nextLast = 0; + + if ((tmp.mv_x == 0 && tmp.mv_y == 0) || (tmp.mv_x == mv1->mv_x && tmp.mv_y == mv1->mv_y)) + { + if (iabs (tmp.mv_x - mv1->mv_x) < (mv_range) && iabs (tmp.mv_y - mv1->mv_y) < (mv_range)) + searchPatternF = p_Vid->sdiamond; + else + searchPatternF = p_Vid->square; + } + else + searchPatternF = p_EPZS->searchPatternD; + totalCheckPts = searchPatternF->searchPoints; + + //! Second best. Note that following code is identical as for best predictor. + center1 = tmp2; + checkMedian = FALSE; + } + } + } + + if (mv_block->iteration_no == 0) + { + *prevSad = min_mcost; + } + + *mv1 = tmp; + + return min_mcost; +} + diff --git a/lencod/src/me_epzs_sub.c b/lencod/src/me_epzs_sub.c new file mode 100644 index 0000000..220a0c4 --- /dev/null +++ b/lencod/src/me_epzs_sub.c @@ -0,0 +1,423 @@ + +/*! +************************************************************************************* +* \file me_epzs_sub.c +* +* \brief +* SubPel Motion Estimation refinement using EPZS +* +* \author +* Main contributors (see contributors.h for copyright, address and affiliation details) +* - Alexis Michael Tourapis +* +************************************************************************************* +*/ + +#include "contributors.h" + +#include "global.h" +#include "refbuf.h" +#include "me_epzs.h" +#include "mv_search.h" + +/*! +*********************************************************************** +* \brief +* Fast sub pixel block motion search to support EPZS +*********************************************************************** +*/ +distblk // ==> minimum motion cost after search +EPZS_sub_pel_motion_estimation ( Macroblock *currMB, // <-- current Macroblock + MotionVector *pred, // <-- motion vector predictor in sub-pel units + MEBlock *mv_block, // <-- motion vector information + distblk min_mcost, // <-- minimum motion cost (cost for center or huge value) + int* lambda // <-- lagrangian parameter for determining motion cost + ) +{ + VideoParameters *p_Vid = currMB->p_Vid; + Slice *currSlice = currMB->p_Slice; + EPZSParameters *p_EPZS = currSlice->p_EPZS; + int pos, best_pos = 0, second_pos = 0; + distblk mcost; + distblk second_mcost = DISTBLK_MAX; + int max_pos2 = ( (!p_Vid->start_me_refinement_hp || !p_Vid->start_me_refinement_qp) ? imax(1, mv_block->search_pos2) : mv_block->search_pos2); + + int list = mv_block->list; + int cur_list = list + currMB->list_offset; + short ref = mv_block->ref_idx; + StorablePicture *ref_picture = currSlice->listX[cur_list][ref]; + MotionVector *mv = &mv_block->mv[list]; + MotionVector cand; + MotionVector padded_mv = pad_MVs (*mv, mv_block); + MotionVector pred_mv = pad_MVs (*pred, mv_block); + + int start_pos = 5, end_pos = max_pos2; + int lambda_factor = lambda[H_PEL]; + distblk lambda_dist = weighted_cost(lambda_factor, 2); + distblk sub_threshold = p_EPZS->subthres[mv_block->blocktype] + lambda_dist; + + /********************************* + ***** ***** + ***** HALF-PEL REFINEMENT ***** + ***** ***** + *********************************/ + + //===== loop over search positions ===== + for (best_pos = 0, pos = p_Vid->start_me_refinement_hp; pos < imin(5, max_pos2); ++pos) + { + cand = add_MVs(search_point_hp[pos], &padded_mv); + + //----- set motion vector cost ----- + mcost = mv_cost (p_Vid, lambda_factor, &cand, &pred_mv); + if (mcost < second_mcost) + { + mcost += mv_block->computePredHPel(ref_picture, mv_block, second_mcost - mcost, &cand); + + if (mcost < min_mcost) + { + second_mcost = min_mcost; + second_pos = best_pos; + min_mcost = mcost; + best_pos = pos; + } + else if (mcost < second_mcost) + { + second_mcost = mcost; + second_pos = pos; + } + } + } + + if (best_pos ==0 && (pred->mv_x == mv->mv_x) && (pred->mv_y == mv->mv_y) && min_mcost < sub_threshold) + { + return min_mcost; + } + + if(mv_block->search_pos2 >= 9) + { + if (best_pos !=0 || (iabs(pred->mv_x - mv->mv_x) + iabs(pred->mv_y - mv->mv_y))) + { + start_pos = next_start_pos[best_pos][second_pos]; + end_pos = next_end_pos[best_pos][second_pos]; + + for (pos = start_pos; pos < end_pos; ++pos) + { + cand = add_MVs(search_point_hp[pos], &padded_mv); + + //----- set motion vector cost ----- + mcost = mv_cost (p_Vid, lambda_factor, &cand, &pred_mv); + + if (mcost < min_mcost) + { + mcost += mv_block->computePredHPel( ref_picture, mv_block, min_mcost - mcost, &cand); + + if (mcost < min_mcost) + { + min_mcost = mcost; + best_pos = pos; + } + } + } + } + } + + if (best_pos) + { + add_mvs(mv, &search_point_hp[best_pos]); + padded_mv = pad_MVs (*mv, mv_block); + } + + /************************************ + ***** ***** + ***** QUARTER-PEL REFINEMENT ***** + ***** ***** + ************************************/ + end_pos = (min_mcost < sub_threshold) ? 1 : 5; + second_mcost = DISTBLK_MAX; + + if ( !p_Vid->start_me_refinement_qp ) + { + best_pos = -1; + min_mcost = DISTBLK_MAX; + } + else + { + best_pos = 0; + } + lambda_factor = lambda[Q_PEL]; + + //===== loop over search positions ===== + for (pos = p_Vid->start_me_refinement_qp; pos < end_pos; ++pos) + { + cand = add_MVs(search_point_qp[pos], &padded_mv); + + //----- set motion vector cost ----- + mcost = mv_cost (p_Vid, lambda_factor, &cand, &pred_mv); + + if (mcost < second_mcost) + { + mcost += mv_block->computePredQPel(ref_picture, mv_block, second_mcost - mcost, &cand); + + if (mcost < min_mcost) + { + second_mcost = min_mcost; + second_pos = best_pos; + min_mcost = mcost; + best_pos = pos; + } + else if (mcost < second_mcost) + { + second_mcost = mcost; + second_pos = pos; + } + } + } + + //if (best_pos ==0 && (pred->mv_x == mv->mv_x) && (pred->mv_y == mv->mv_y) && min_mcost < sub_threshold) + if ( (min_mcost > sub_threshold)) + { + + if (best_pos !=0 || (iabs(pred->mv_x - mv->mv_x) + iabs(pred->mv_y - mv->mv_y))) + { + start_pos = next_start_pos[best_pos][second_pos]; + end_pos = next_end_pos[best_pos][second_pos]; + + for (pos = start_pos; pos < end_pos; ++pos) + { + cand = add_MVs(search_point_qp[pos], &padded_mv); + + //----- set motion vector cost ----- + mcost = mv_cost (p_Vid, lambda_factor, &cand, &pred_mv); + + if (mcost < min_mcost) + { + mcost += mv_block->computePredQPel(ref_picture, mv_block, min_mcost - mcost, &cand); + + if (mcost < min_mcost) + { + min_mcost = mcost; + best_pos = pos; + } + } + } + } + } + + if (best_pos) + { + add_mvs(mv, &search_point_qp[best_pos]); + } + + //===== return minimum motion cost ===== + return min_mcost; +} + + +/*! +*********************************************************************** +* \brief +* Fast bipred sub pixel block motion search to support EPZS +*********************************************************************** +*/ +distblk // ==> minimum motion cost after search +EPZS_sub_pel_bipred_motion_estimation (Macroblock *currMB, // <-- current Macroblock + MEBlock *mv_block, // <-- motion vector information + int list, // <-- reference picture list + MotionVector *pred1, // <-- motion vector predictor in sub-pel units + MotionVector *pred2, // <-- motion vector predictor in sub-pel units + MotionVector *mv1, // <--> in: search center / out: motion vector - in sub-pel units + MotionVector *mv2, // <--> in: search center / out: motion vector - in sun-pel units + distblk min_mcost, // <-- minimum motion cost (cost for center or huge value) + int* lambda // <-- lagrangian parameter for determining motion cost + ) +{ + VideoParameters *p_Vid = currMB->p_Vid; + Slice *currSlice = currMB->p_Slice; + EPZSParameters *p_EPZS = currSlice->p_EPZS; + + int list_offset = p_Vid->mb_data[currMB->mbAddrX].list_offset; + + int pos, best_pos = 0, second_pos = 0; + distblk mcost; + distblk second_mcost = DISTBLK_MAX; + + + int start_hp = (min_mcost == DISTBLK_MAX) ? 0 : p_Vid->start_me_refinement_hp; + int max_pos2 = ( (!p_Vid->start_me_refinement_hp || !p_Vid->start_me_refinement_qp) ? imax(1, mv_block->search_pos2) : mv_block->search_pos2); + + short ref = mv_block->ref_idx; + StorablePicture *ref_picture1 = currSlice->listX[list + list_offset][ref]; + StorablePicture *ref_picture2 = currSlice->listX[(list ^ 1) + list_offset][0]; + MotionVector cand; + MotionVector padded_mv1 = pad_MVs (*mv1, mv_block); + MotionVector padded_mv2 = pad_MVs (*mv2, mv_block); + MotionVector pred_mv1 = pad_MVs (*pred1, mv_block); + MotionVector pred_mv2 = pad_MVs (*pred2, mv_block); + + int start_pos = 5, end_pos = max_pos2; + int lambda_factor = lambda[H_PEL]; + distblk lambda_dist = weighted_cost(lambda_factor, 2); + distblk sub_threshold = p_EPZS->subthres[mv_block->blocktype] + lambda_dist; + + distblk mcost2 = mv_cost (p_Vid, lambda_factor, &padded_mv2, &pred_mv2); + min_mcost -= mcost2; + + /********************************* + ***** ***** + ***** HALF-PEL REFINEMENT ***** + ***** ***** + *********************************/ + + //===== loop over search positions ===== + for (best_pos = 0, pos = start_hp; pos < 5; ++pos) + { + cand = add_MVs(search_point_hp[pos], &padded_mv1); + + //----- set motion vector cost ----- + mcost = mv_cost (p_Vid, lambda_factor, &cand, &pred_mv1); + + if (mcost < second_mcost) + { + mcost += mv_block->computeBiPredHPel(ref_picture1, ref_picture2, mv_block, second_mcost - mcost, &cand, &padded_mv2); + + if (mcost < min_mcost) + { + second_mcost = min_mcost; + second_pos = best_pos; + min_mcost = mcost; + best_pos = pos; + } + else if (mcost < second_mcost) + { + second_mcost = mcost; + second_pos = pos; + } + } + } + + if (best_pos ==0 && (pred1->mv_x == mv1->mv_x) && (pred1->mv_y == mv1->mv_y) && min_mcost < sub_threshold) + { + return min_mcost; + } + + if(mv_block->search_pos2 >= 9) + { + if (best_pos !=0 || (iabs(pred1->mv_x - mv1->mv_x) + iabs(pred1->mv_y - mv1->mv_y))) + { + start_pos = next_start_pos[best_pos][second_pos]; + end_pos = next_end_pos[best_pos][second_pos]; + + for (pos = start_pos; pos < end_pos; ++pos) + { + cand = add_MVs(search_point_hp[pos], &padded_mv1); + + //----- set motion vector cost ----- + mcost = mv_cost (p_Vid, lambda_factor, &cand, &pred_mv1); + + if (mcost < min_mcost) + { + mcost += mv_block->computeBiPredHPel(ref_picture1, ref_picture2, mv_block, min_mcost - mcost, &cand, &padded_mv2); + + if (mcost < min_mcost) + { + min_mcost = mcost; + best_pos = pos; + } + } + } + } + } + + if (best_pos) + { + add_mvs (mv1, &search_point_hp[best_pos]); + padded_mv1 = pad_MVs (*mv1, mv_block); + } + + /************************************ + ***** ***** + ***** QUARTER-PEL REFINEMENT ***** + ***** ***** + ************************************/ + end_pos = (min_mcost < sub_threshold) ? 1 : 5; + second_mcost = DISTBLK_MAX; + + if ( !p_Vid->start_me_refinement_qp ) + { + best_pos = -1; + min_mcost = DISTBLK_MAX; + } + else + { + best_pos = 0; + min_mcost += mcost2; + } + lambda_factor = lambda[Q_PEL]; + mcost2 = mv_cost (p_Vid, lambda_factor, &padded_mv2, &pred_mv2); + min_mcost -= mcost2; + + //===== loop over search positions ===== + for (pos = p_Vid->start_me_refinement_qp; pos < 5; ++pos) + { + cand = add_MVs(search_point_qp[pos], &padded_mv1); + + //----- set motion vector cost ----- + mcost = mv_cost (p_Vid, lambda_factor, &cand, &pred_mv1); + + if (mcost < second_mcost) + { + mcost += mv_block->computeBiPredQPel(ref_picture1, ref_picture2, mv_block, second_mcost - mcost, &cand, &padded_mv2); + + if (mcost < min_mcost) + { + second_mcost = min_mcost; + second_pos = best_pos; + min_mcost = mcost; + best_pos = pos; + } + else if (mcost < second_mcost) + { + second_mcost = mcost; + second_pos = pos; + } + } + } + + if ( (min_mcost > sub_threshold)) + { + + if (best_pos !=0 || (iabs(pred1->mv_x - mv1->mv_x) + iabs(pred1->mv_y - mv1->mv_y))) + { + start_pos = next_start_pos[best_pos][second_pos]; + end_pos = next_end_pos[best_pos][second_pos]; + + for (pos = start_pos; pos < end_pos; ++pos) + { + cand = add_MVs(search_point_qp[pos], &padded_mv1); + + //----- set motion vector cost ----- + mcost = mv_cost (p_Vid, lambda_factor, &cand, &pred_mv1); + + if (mcost < min_mcost) + { + mcost += mv_block->computeBiPredQPel(ref_picture1, ref_picture2, mv_block, min_mcost - mcost, &cand, &padded_mv2); + + if (mcost < min_mcost) + { + min_mcost = mcost; + best_pos = pos; + } + } + } + } + } + + if (best_pos) + { + add_mvs(mv1, &search_point_qp[best_pos]); + } + + //===== return minimum motion cost ===== + return min_mcost + mcost2; +} + diff --git a/lencod/src/me_fullfast.c b/lencod/src/me_fullfast.c new file mode 100644 index 0000000..b2d00f3 --- /dev/null +++ b/lencod/src/me_fullfast.c @@ -0,0 +1,690 @@ + +/*! +************************************************************************************* +* \file me_fullfast.c +* +* \brief +* Motion Estimation using Full Search Fast +* +* \author +* Main contributors (see contributors.h for copyright, address and affiliation details) +* - Alexis Michael Tourapis +* +************************************************************************************* +*/ + +// Includes +#include "contributors.h" +#include + +#include "global.h" +#include "image.h" +#include "memalloc.h" +#include "mb_access.h" +#include "refbuf.h" + +#include "me_distortion.h" +#include "me_fullsearch.h" +#include "me_fullfast.h" +#include "conformance.h" +#include "mv_search.h" + + +// Functions +/*! + *********************************************************************** + * \brief + * Full pixel block motion search + *********************************************************************** + */ + + +/*! + *********************************************************************** + * \brief + * function creating arrays for fast integer motion estimation + *********************************************************************** + */ +void initialize_fast_full_search (VideoParameters *p_Vid, InputParameters *p_Inp) +{ + int i, j, k, list; + int search_range = p_Inp->SearchMode[0] == FAST_FULL_SEARCH ? p_Inp->search_range[0] : p_Inp->search_range[1]; + int max_pos = (2*search_range+1) * (2*search_range+1); + MEFullFast *p_me_ffast = NULL; + + if ((p_Vid->p_ffast_me = calloc (1, sizeof (MEFullFast))) == NULL) + no_mem_exit ("initialize_fast_full_search: p_Vid->p_ffast_me"); + p_me_ffast = p_Vid->p_ffast_me; + + if ((p_me_ffast->BlockSAD = (distpel*****)malloc (2 * sizeof(distpel****))) == NULL) + no_mem_exit ("initialize_fast_full_search: p_me_ffast->BlockSAD"); + + for (list=0; list<2;list++) + { + if ((p_me_ffast->BlockSAD[list] = (distpel****)malloc ((p_Vid->max_num_references) * sizeof(distpel***))) == NULL) + no_mem_exit ("initialize_fast_full_search: p_me_ffast->BlockSAD"); + for (i = 0; i < p_Vid->max_num_references; i++) + { + if ((p_me_ffast->BlockSAD[list][i] = (distpel***)malloc (8 * sizeof(distpel**))) == NULL) + no_mem_exit ("initialize_fast_full_search: p_me_ffast->BlockSAD"); + for (j = 1; j < 8; j++) + { + if ((p_me_ffast->BlockSAD[list][i][j] = (distpel**)malloc (16 * sizeof(distpel*))) == NULL) + no_mem_exit ("initialize_fast_full_search: p_me_ffast->BlockSAD"); + for (k = 0; k < 16; k++) + { + if ((p_me_ffast->BlockSAD[list][i][j][k] = (distpel*)malloc (max_pos * sizeof(distpel))) == NULL) + no_mem_exit ("initialize_fast_full_search: p_me_ffast->BlockSAD"); + } + } + } + } + + if ((p_me_ffast->search_setup_done = (int**)malloc (2*sizeof(int*)))==NULL) + no_mem_exit ("initialize_fast_full_search: p_me_ffast->search_setup_done"); + if ((p_me_ffast->search_center = (MotionVector**) malloc (2 * sizeof(MotionVector*)))==NULL) + no_mem_exit ("initialize_fast_full_search: p_me_ffast->search_center"); + if ((p_me_ffast->search_center_padded = (MotionVector**) malloc (2 * sizeof(MotionVector*)))==NULL) + no_mem_exit ("initialize_fast_full_search: p_me_ffast->search_center_padded"); + + if ((p_me_ffast->pos_00 = (int**)malloc (2*sizeof(int*)))==NULL) + no_mem_exit ("initialize_fast_full_search: p_me_ffast->pos_00"); + if ((p_me_ffast->max_search_range = (int**)malloc (2*sizeof(int*)))==NULL) + no_mem_exit ("initialize_fast_full_search: p_me_ffast->max_search_range"); + + for (list=0; list<2; list++) + { + if ((p_me_ffast->search_setup_done[list] = (int*)malloc ((p_Vid->max_num_references)*sizeof(int)))==NULL) + no_mem_exit ("initialize_fast_full_search: p_me_ffast->search_setup_done"); + if ((p_me_ffast->search_center[list] = (MotionVector*) malloc ((p_Vid->max_num_references) * sizeof(MotionVector)))==NULL) + no_mem_exit ("initialize_fast_full_search: p_me_ffast->search_center"); + if ((p_me_ffast->search_center_padded[list] = (MotionVector*) malloc ((p_Vid->max_num_references) * sizeof(MotionVector)))==NULL) + no_mem_exit ("initialize_fast_full_search: p_me_ffast->search_center_padded"); + + + if ((p_me_ffast->pos_00[list] = (int*)malloc ((p_Vid->max_num_references)*sizeof(int)))==NULL) + no_mem_exit ("initialize_fast_full_search: p_me_ffast->pos_00"); + if ((p_me_ffast->max_search_range[list] = (int*)malloc ((p_Vid->max_num_references)*sizeof(int)))==NULL) + no_mem_exit ("initialize_fast_full_search: p_me_ffast->max_search_range"); + } + + // assign max search ranges for reference frames + if (p_Inp->full_search == 2) + { + for (list=0;list<2;list++) + for (i=0; imax_num_references; i++) + p_me_ffast->max_search_range[list][i] = search_range; + } + else + { + for (list=0;list<2;list++) + { + p_me_ffast->max_search_range[list][0] = search_range; + for (i=1; i< p_Vid->max_num_references; i++) p_me_ffast->max_search_range[list][i] = search_range / 2; + } + } +} + +/*! + *********************************************************************** + * \brief + * function for deleting the arrays for fast integer motion estimation + *********************************************************************** + */ +void +clear_fast_full_search (VideoParameters *p_Vid) +{ + int i, j, k, list; + MEFullFast *p_me_ffast = p_Vid->p_ffast_me; + + for (list=0; list<2; list++) + { + for (i = 0; i < p_Vid->max_num_references; i++) + { + for (j = 1; j < 8; j++) + { + for (k = 0; k < 16; k++) + { + free (p_me_ffast->BlockSAD[list][i][j][k]); + } + free (p_me_ffast->BlockSAD[list][i][j]); + } + free (p_me_ffast->BlockSAD[list][i]); + } + free (p_me_ffast->BlockSAD[list]); + } + free (p_me_ffast->BlockSAD); + + for (list=0; list<2; list++) + { + free (p_me_ffast->search_setup_done[list]); + free (p_me_ffast->search_center_padded[list]); + free (p_me_ffast->search_center[list]); + free (p_me_ffast->pos_00[list]); + free (p_me_ffast->max_search_range[list]); + } + free (p_me_ffast->search_setup_done); + free (p_me_ffast->search_center_padded); + free (p_me_ffast->search_center); + free (p_me_ffast->pos_00); + free (p_me_ffast->max_search_range); + free (p_me_ffast); + +} + + +/*! + *********************************************************************** + * \brief + * function resetting flags for fast integer motion estimation + * (have to be called in start_macroblock()) + *********************************************************************** + */ +void reset_fast_full_search (VideoParameters *p_Vid) +{ + int list; + for (list=0; list<2; list++) + memset(&p_Vid->p_ffast_me->search_setup_done [list][0], 0, p_Vid->max_num_references * sizeof(int)); +} +/*! + *********************************************************************** + * \brief + * calculation of SAD for larger blocks on the basis of 4x4 blocks + *********************************************************************** + */ +void +update_full_search_large_blocks (MEFullFast *p_ffast_me, int list, int refindex, int max_pos) +{ +#define ADD_UP_BLOCKS() _o=*_bo; _i=*_bi; _j=*_bj; for(pos=0;posBlockSAD; + + int pos; + distpel **_bo, **_bi, **_bj; + distpel *_o, *_i, *_j; + + //--- blocktype 6 --- + _bo = BlockSAD[list][refindex][6]; + _bi = BlockSAD[list][refindex][7]; + _bj = _bi + 4; + ADD_UP_BLOCKS(); INCREMENT(1); + ADD_UP_BLOCKS(); INCREMENT(1); + ADD_UP_BLOCKS(); INCREMENT(1); + ADD_UP_BLOCKS(); INCREMENT(5); + ADD_UP_BLOCKS(); INCREMENT(1); + ADD_UP_BLOCKS(); INCREMENT(1); + ADD_UP_BLOCKS(); INCREMENT(1); + ADD_UP_BLOCKS(); + + //--- blocktype 5 --- + _bo = BlockSAD[list][refindex][5]; + _bi = BlockSAD[list][refindex][7]; + _bj = _bi + 1; + ADD_UP_BLOCKS(); INCREMENT(2); + ADD_UP_BLOCKS(); INCREMENT(2); + ADD_UP_BLOCKS(); INCREMENT(2); + ADD_UP_BLOCKS(); INCREMENT(2); + ADD_UP_BLOCKS(); INCREMENT(2); + ADD_UP_BLOCKS(); INCREMENT(2); + ADD_UP_BLOCKS(); INCREMENT(2); + ADD_UP_BLOCKS(); + + //--- blocktype 4 --- + _bo = BlockSAD[list][refindex][4]; + _bi = BlockSAD[list][refindex][6]; + _bj = _bi + 1; + ADD_UP_BLOCKS(); INCREMENT(2); + ADD_UP_BLOCKS(); INCREMENT(6); + ADD_UP_BLOCKS(); INCREMENT(2); + ADD_UP_BLOCKS(); + + //--- blocktype 3 --- + _bo = BlockSAD[list][refindex][3]; + _bi = BlockSAD[list][refindex][4]; + _bj = _bi + 8; + ADD_UP_BLOCKS(); INCREMENT(2); + ADD_UP_BLOCKS(); + + //--- blocktype 2 --- + _bo = BlockSAD[list][refindex][2]; + _bi = BlockSAD[list][refindex][4]; + _bj = _bi + 2; + ADD_UP_BLOCKS(); INCREMENT(8); + ADD_UP_BLOCKS(); + + //--- blocktype 1 --- + _bo = BlockSAD[list][refindex][1]; + _bi = BlockSAD[list][refindex][3]; + _bj = _bi + 2; + ADD_UP_BLOCKS(); +} + + +/*! + *********************************************************************** + * \brief + * Setup the fast search for an macroblock + *********************************************************************** + */ +void setup_fast_full_search (Macroblock *currMB, MEBlock *mv_block, int list) // <-- reference frame parameter, list0 or 1 +{ + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currSlice->p_Vid; + InputParameters *p_Inp = currSlice->p_Inp; + int (*dist_method) (int x) = p_Inp->MEErrorMetric[0] ? iabs2 : iabs; +#if (JM_MEM_DISTORTION) + int* imgpel_dist = p_Inp->MEErrorMetric[0] ? p_Vid->imgpel_quad : p_Vid->imgpel_abs; +#endif + MotionVector pmv; + imgpel orig_pels[768]; + imgpel *srcptr = orig_pels, *refptr; + int k, x, y; + MotionVector cand, offset; + int ref_x, ref_y, pos, bindex, blky; + distblk LineSadBlk0, LineSadBlk1, LineSadBlk2, LineSadBlk3; + PixelPos block[4]; // neighbor blocks + MEFullFast *p_me_ffast = p_Vid->p_ffast_me; + + + short ref = mv_block->ref_idx; + distpel** block_sad = p_me_ffast->BlockSAD[list][ref][7]; + int search_range = p_me_ffast->max_search_range[list][ref]; + int max_pos = (2*search_range+1) * (2*search_range+1); + + int list_offset = p_Vid->mb_data[currMB->mbAddrX].list_offset; + int apply_weights = ( (p_Vid->active_pps->weighted_pred_flag && (currSlice->slice_type == P_SLICE || (currSlice->slice_type == SP_SLICE))) || + (p_Vid->active_pps->weighted_bipred_idc && (currSlice->slice_type == B_SLICE))) && p_Inp->UseWeightedReferenceME; + int weighted_pel; + StorablePicture *ref_picture = currSlice->listX[list+list_offset][ref]; + + int wp_luma_round = 0; + short luma_log_weight_denom = 5; + short chroma_log_weight_denom = 5; + int wp_chroma_round = 0; + short weight_luma = mv_block->weight_luma, weight_cr[2]; + short offset_luma = mv_block->offset_luma, offset_cr[2]; + search_range <<= 2; + + //===== get search center: predictor of 16x16 block ===== + get_neighbors(currMB, block, 0, 0, 16); + currMB->GetMVPredictor (currMB, block, &pmv, ref, p_Vid->enc_picture->mv_info, list, 0, 0, 16, 16); + +#if (JM_INT_DIVIDE) + p_Vid->p_ffast_me->search_center[list][ref].mv_x = ((pmv.mv_x + 2) >> 2) * 4; + p_Vid->p_ffast_me->search_center[list][ref].mv_y = ((pmv.mv_y + 2) >> 2) * 4; +#else + p_Vid->p_ffast_me->search_center[list][ref].mv_x = (pmv.mv_x / 4) * 4; + p_Vid->p_ffast_me->search_center[list][ref].mv_y = (pmv.mv_y / 4) * 4; +#endif + if (!p_Inp->rdopt) + { + //--- correct center so that (0,0) vector is inside --- + p_Vid->p_ffast_me->search_center[list][ref].mv_x = (short) iClip3(-search_range, search_range, p_Vid->p_ffast_me->search_center[list][ref].mv_x); + p_Vid->p_ffast_me->search_center[list][ref].mv_y = (short) iClip3(-search_range, search_range, p_Vid->p_ffast_me->search_center[list][ref].mv_y); + } + + p_Vid->p_ffast_me->search_center[list][ref].mv_x = (short) iClip3(p_Vid->MaxHmvR[4] + search_range, p_Vid->MaxHmvR[5] - search_range, p_Vid->p_ffast_me->search_center[list][ref].mv_x); + p_Vid->p_ffast_me->search_center[list][ref].mv_y = (short) iClip3(p_Vid->MaxVmvR[4] + search_range, p_Vid->MaxVmvR[5] - search_range, p_Vid->p_ffast_me->search_center[list][ref].mv_y); + + p_Vid->p_ffast_me->search_center_padded[list][ref] = pad_MVs(p_Vid->p_ffast_me->search_center[list][ref], mv_block); + + offset = p_Vid->p_ffast_me->search_center_padded[list][ref]; + //===== copy original block for fast access ===== + for (y = currMB->opix_y; y < currMB->opix_y + MB_BLOCK_SIZE; y++) + { + memcpy(srcptr, &p_Vid->pCurImg[y][currMB->pix_x], MB_BLOCK_SIZE * sizeof(imgpel)); + srcptr += MB_BLOCK_SIZE; + } + + if ( mv_block->ChromaMEEnable) + { + for (k = 1; k < 3; k++) + { + for (y = currMB->opix_c_y; y < currMB->opix_c_y + p_Vid->mb_cr_size_y; y++) + { + memcpy(srcptr, &p_Vid->pImgOrg[k][y][currMB->pix_c_x], p_Vid->mb_cr_size_x * sizeof(imgpel)); + srcptr += p_Vid->mb_cr_size_x; + } + } + } + + + //===== determine position of (0,0)-vector ===== + if (!p_Inp->rdopt) + { + ref_x = mv_block->pos_x_padded - offset.mv_x; + ref_y = mv_block->pos_y_padded - offset.mv_y; + + for (pos = 0; pos < max_pos; pos++) + { + if (ref_x == p_Vid->spiral_qpel_search[pos].mv_x && ref_y == p_Vid->spiral_qpel_search[pos].mv_y) + { + p_me_ffast->pos_00[list][ref] = pos; + break; + } + } + } + + //===== loop over search range (spiral search): get blockwise SAD ===== + if (apply_weights) + { + weight_luma = currSlice->wp_weight[list + list_offset][ref][0]; + offset_luma = currSlice->wp_offset[list + list_offset][ref][0]; + wp_luma_round = currSlice->wp_luma_round; + luma_log_weight_denom = currSlice->luma_log_weight_denom; + chroma_log_weight_denom = currSlice->chroma_log_weight_denom; + wp_chroma_round = currSlice->wp_chroma_round; + + if (mv_block->ChromaMEEnable) + { + weight_cr[0] = currSlice->wp_weight[list + list_offset][ref][1]; + weight_cr[1] = currSlice->wp_weight[list + list_offset][ref][2]; + offset_cr[0] = currSlice->wp_offset[list + list_offset][ref][1]; + offset_cr[1] = currSlice->wp_offset[list + list_offset][ref][2]; + } + + for (pos = 0; pos < max_pos; pos++) + { + cand = add_MVs(offset, &p_Vid->spiral_qpel_search[pos]); + + srcptr = orig_pels; + bindex = 0; + + refptr = UMVLine4X (ref_picture, cand.mv_y, cand.mv_x); + + for (blky = 0; blky < 4; blky++) + { + LineSadBlk0 = LineSadBlk1 = LineSadBlk2 = LineSadBlk3 = 0; + + for (y = 0; y < 4; y++) + { + weighted_pel = iClip1( p_Vid->max_imgpel_value, ((weight_luma * *refptr++ + wp_luma_round) >> luma_log_weight_denom) + offset_luma); + LineSadBlk0 += dist_method (weighted_pel - *srcptr++); + weighted_pel = iClip1( p_Vid->max_imgpel_value, ((weight_luma * *refptr++ + wp_luma_round) >> luma_log_weight_denom) + offset_luma); + LineSadBlk0 += dist_method (weighted_pel - *srcptr++); + weighted_pel = iClip1( p_Vid->max_imgpel_value, ((weight_luma * *refptr++ + wp_luma_round) >> luma_log_weight_denom) + offset_luma); + LineSadBlk0 += dist_method (weighted_pel - *srcptr++); + weighted_pel = iClip1( p_Vid->max_imgpel_value, ((weight_luma * *refptr++ + wp_luma_round) >> luma_log_weight_denom) + offset_luma); + LineSadBlk0 += dist_method (weighted_pel - *srcptr++); + weighted_pel = iClip1( p_Vid->max_imgpel_value, ((weight_luma * *refptr++ + wp_luma_round) >> luma_log_weight_denom) + offset_luma); + LineSadBlk1 += dist_method (weighted_pel - *srcptr++); + weighted_pel = iClip1( p_Vid->max_imgpel_value, ((weight_luma * *refptr++ + wp_luma_round) >> luma_log_weight_denom) + offset_luma); + LineSadBlk1 += dist_method (weighted_pel - *srcptr++); + weighted_pel = iClip1( p_Vid->max_imgpel_value, ((weight_luma * *refptr++ + wp_luma_round) >> luma_log_weight_denom) + offset_luma); + LineSadBlk1 += dist_method (weighted_pel - *srcptr++); + weighted_pel = iClip1( p_Vid->max_imgpel_value, ((weight_luma * *refptr++ + wp_luma_round) >> luma_log_weight_denom) + offset_luma); + LineSadBlk1 += dist_method (weighted_pel - *srcptr++); + weighted_pel = iClip1( p_Vid->max_imgpel_value, ((weight_luma * *refptr++ + wp_luma_round) >> luma_log_weight_denom) + offset_luma); + LineSadBlk2 += dist_method (weighted_pel - *srcptr++); + weighted_pel = iClip1( p_Vid->max_imgpel_value, ((weight_luma * *refptr++ + wp_luma_round) >> luma_log_weight_denom) + offset_luma); + LineSadBlk2 += dist_method (weighted_pel - *srcptr++); + weighted_pel = iClip1( p_Vid->max_imgpel_value, ((weight_luma * *refptr++ + wp_luma_round) >> luma_log_weight_denom) + offset_luma); + LineSadBlk2 += dist_method (weighted_pel - *srcptr++); + weighted_pel = iClip1( p_Vid->max_imgpel_value, ((weight_luma * *refptr++ + wp_luma_round) >> luma_log_weight_denom) + offset_luma); + LineSadBlk2 += dist_method (weighted_pel - *srcptr++); + weighted_pel = iClip1( p_Vid->max_imgpel_value, ((weight_luma * *refptr++ + wp_luma_round) >> luma_log_weight_denom) + offset_luma); + LineSadBlk3 += dist_method (weighted_pel - *srcptr++); + weighted_pel = iClip1( p_Vid->max_imgpel_value, ((weight_luma * *refptr++ + wp_luma_round) >> luma_log_weight_denom) + offset_luma); + LineSadBlk3 += dist_method (weighted_pel - *srcptr++); + weighted_pel = iClip1( p_Vid->max_imgpel_value, ((weight_luma * *refptr++ + wp_luma_round) >> luma_log_weight_denom) + offset_luma); + LineSadBlk3 += dist_method (weighted_pel - *srcptr++); + weighted_pel = iClip1( p_Vid->max_imgpel_value, ((weight_luma * *refptr++ + wp_luma_round) >> luma_log_weight_denom) + offset_luma); + LineSadBlk3 += dist_method (weighted_pel - *srcptr++); + refptr += p_Vid->padded_size_x - MB_BLOCK_SIZE; + } + block_sad[bindex++][pos] = (distpel) LineSadBlk0; + block_sad[bindex++][pos] = (distpel) LineSadBlk1; + block_sad[bindex++][pos] = (distpel) LineSadBlk2; + block_sad[bindex++][pos] = (distpel) LineSadBlk3; + + } + if (mv_block->ChromaMEEnable) + { + int max_imgpel_value_uv = p_Vid->max_pel_value_comp[1]; + for (k = 0; k < 2; k ++) + { + bindex = 0; + + refptr = UMVLine8X_chroma (ref_picture, k+1, cand.mv_y, cand.mv_x); + for (blky = 0; blky < 4; blky++) + { + LineSadBlk0 = LineSadBlk1 = LineSadBlk2 = LineSadBlk3 = 0; + + for (y = 0; y < p_Vid->mb_cr_size_y; y+=BLOCK_SIZE) + { + for (x = 0; x < p_Vid->mb_cr_size_x; x += BLOCK_SIZE) + { + weighted_pel = iClip1( max_imgpel_value_uv, ((weight_cr[k] * *refptr++ + wp_chroma_round) >> chroma_log_weight_denom) + offset_cr[k]); + LineSadBlk0 += dist_method (weighted_pel - *srcptr++); + } + for (x = 0; x < p_Vid->mb_cr_size_x; x += BLOCK_SIZE) + { + weighted_pel = iClip1( max_imgpel_value_uv, ((weight_cr[k] * *refptr++ + wp_chroma_round) >> chroma_log_weight_denom) + offset_cr[k]); + LineSadBlk1 += dist_method (weighted_pel - *srcptr++); + } + for (x = 0; x < p_Vid->mb_cr_size_x; x += BLOCK_SIZE) + { + weighted_pel = iClip1( max_imgpel_value_uv, ((weight_cr[k] * *refptr++ + wp_chroma_round) >> chroma_log_weight_denom) + offset_cr[k]); + LineSadBlk2 += dist_method (weighted_pel - *srcptr++); + } + for (x = 0; x < p_Vid->mb_cr_size_x; x += BLOCK_SIZE) + { + weighted_pel = iClip1( max_imgpel_value_uv, ((weight_cr[k] * *refptr++ + wp_chroma_round) >> chroma_log_weight_denom) + offset_cr[k]); + LineSadBlk3 += dist_method (weighted_pel - *srcptr++); + } + refptr += p_Vid->cr_padded_size_x - p_Vid->mb_cr_size_x; + } + block_sad[bindex][pos] = block_sad[bindex][pos] + ((distpel) LineSadBlk0); + ++bindex; + block_sad[bindex][pos] = block_sad[bindex][pos] + ((distpel) LineSadBlk1); + ++bindex; + block_sad[bindex][pos] = block_sad[bindex][pos] + ((distpel) LineSadBlk2); + ++bindex; + block_sad[bindex][pos] = block_sad[bindex][pos] + ((distpel) LineSadBlk3); + ++bindex; + } + } + } + } + } + else + { + for (pos = 0; pos < max_pos; pos++) + { + cand = add_MVs(offset, &p_Vid->spiral_qpel_search[pos]); + srcptr = orig_pels; + bindex = 0; + + refptr = UMVLine4X (ref_picture, cand.mv_y, cand.mv_x); + + for (blky = 0; blky < 4; blky++) + { + LineSadBlk0 = LineSadBlk1 = LineSadBlk2 = LineSadBlk3 = 0; + + for (y = 0; y < 4; y++) + { +#if (JM_MEM_DISTORTION) + // Distortion for first 4x4 block + LineSadBlk0 += imgpel_dist[ *refptr++ - *srcptr++ ]; + LineSadBlk0 += imgpel_dist[ *refptr++ - *srcptr++ ]; + LineSadBlk0 += imgpel_dist[ *refptr++ - *srcptr++ ]; + LineSadBlk0 += imgpel_dist[ *refptr++ - *srcptr++ ]; + // Distortion for second 4x4 block + LineSadBlk1 += imgpel_dist[ *refptr++ - *srcptr++ ]; + LineSadBlk1 += imgpel_dist[ *refptr++ - *srcptr++ ]; + LineSadBlk1 += imgpel_dist[ *refptr++ - *srcptr++ ]; + LineSadBlk1 += imgpel_dist[ *refptr++ - *srcptr++ ]; + // Distortion for third 4x4 block + LineSadBlk2 += imgpel_dist[ *refptr++ - *srcptr++ ]; + LineSadBlk2 += imgpel_dist[ *refptr++ - *srcptr++ ]; + LineSadBlk2 += imgpel_dist[ *refptr++ - *srcptr++ ]; + LineSadBlk2 += imgpel_dist[ *refptr++ - *srcptr++ ]; + // Distortion for fourth 4x4 block + LineSadBlk3 += imgpel_dist[ *refptr++ - *srcptr++ ]; + LineSadBlk3 += imgpel_dist[ *refptr++ - *srcptr++ ]; + LineSadBlk3 += imgpel_dist[ *refptr++ - *srcptr++ ]; + LineSadBlk3 += imgpel_dist[ *refptr++ - *srcptr++ ]; +#else + // Distortion for first 4x4 block + LineSadBlk0 += dist_method (*refptr++ - *srcptr++); + LineSadBlk0 += dist_method (*refptr++ - *srcptr++); + LineSadBlk0 += dist_method (*refptr++ - *srcptr++); + LineSadBlk0 += dist_method (*refptr++ - *srcptr++); + // Distortion for second 4x4 block + LineSadBlk1 += dist_method (*refptr++ - *srcptr++); + LineSadBlk1 += dist_method (*refptr++ - *srcptr++); + LineSadBlk1 += dist_method (*refptr++ - *srcptr++); + LineSadBlk1 += dist_method (*refptr++ - *srcptr++); + // Distortion for third 4x4 block + LineSadBlk2 += dist_method (*refptr++ - *srcptr++); + LineSadBlk2 += dist_method (*refptr++ - *srcptr++); + LineSadBlk2 += dist_method (*refptr++ - *srcptr++); + LineSadBlk2 += dist_method (*refptr++ - *srcptr++); + // Distortion for fourth 4x4 block + LineSadBlk3 += dist_method (*refptr++ - *srcptr++); + LineSadBlk3 += dist_method (*refptr++ - *srcptr++); + LineSadBlk3 += dist_method (*refptr++ - *srcptr++); + LineSadBlk3 += dist_method (*refptr++ - *srcptr++); +#endif + + refptr += p_Vid->padded_size_x - MB_BLOCK_SIZE; + } + block_sad[bindex++][pos] = (distpel) LineSadBlk0; + block_sad[bindex++][pos] = (distpel) LineSadBlk1; + block_sad[bindex++][pos] = (distpel) LineSadBlk2; + block_sad[bindex++][pos] = (distpel) LineSadBlk3; + } + + if (mv_block->ChromaMEEnable) + { + for (k = 0; k < 2; k ++) + { + bindex = 0; + + refptr = UMVLine8X_chroma (ref_picture, k+1, cand.mv_y, cand.mv_x); + for (blky = 0; blky < 4; blky++) + { + LineSadBlk0 = LineSadBlk1 = LineSadBlk2 = LineSadBlk3 = 0; + + for (y = 0; y < p_Vid->mb_cr_size_y; y+=BLOCK_SIZE) + { + for (x = 0; x < p_Vid->mb_cr_size_x; x += BLOCK_SIZE) + { + LineSadBlk0 += dist_method (*refptr++ - *srcptr++); + } + for (x = 0; x < p_Vid->mb_cr_size_x; x += BLOCK_SIZE) + { + LineSadBlk1 += dist_method (*refptr++ - *srcptr++); + } + for (x = 0; x < p_Vid->mb_cr_size_x; x += BLOCK_SIZE) + { + LineSadBlk2 += dist_method (*refptr++ - *srcptr++); + } + for (x = 0; x < p_Vid->mb_cr_size_x; x += BLOCK_SIZE) + { + LineSadBlk3 += dist_method (*refptr++ - *srcptr++); + } + refptr += p_Vid->cr_padded_size_x - p_Vid->mb_cr_size_x; + } + block_sad[bindex][pos] = block_sad[bindex][pos] + ((distpel) LineSadBlk0); + ++bindex; + block_sad[bindex][pos] = block_sad[bindex][pos] + ((distpel) LineSadBlk1); + ++bindex; + block_sad[bindex][pos] = block_sad[bindex][pos] + ((distpel) LineSadBlk2); + ++bindex; + block_sad[bindex][pos] = block_sad[bindex][pos] + ((distpel) LineSadBlk3); + ++bindex; + } + } + } + } + } + + //===== combine SAD's for larger block types ===== + update_full_search_large_blocks (p_Vid->p_ffast_me, list, ref, max_pos); + + //===== set flag marking that search setup have been done ===== + p_Vid->p_ffast_me->search_setup_done[list][ref] = 1; +} + + +/*! + *********************************************************************** + * \brief + * Fast Full pixel block motion search + *********************************************************************** + */ +distblk // ==> minimum motion cost after search +fast_full_search_motion_estimation (Macroblock *currMB, // <-- current Macroblock + MotionVector *pred_mv, // <-- motion vector predictor (x) in sub-pel units + MEBlock *mv_block, // <-- motion estimation parameters + distblk min_mcost, // <-- minimum motion cost (cost for center or huge value) + int lambda_factor // <-- lagrangian parameter for determining motion cost + ) +{ + VideoParameters *p_Vid = currMB->p_Vid; + InputParameters *p_Inp = currMB->p_Inp; + MEFullFast *p_me_ffast = p_Vid->p_ffast_me; + distblk mcost; + int pos; + int search_range = imax(mv_block->searchRange.max_x, mv_block->searchRange.max_y) >> 2; + int max_pos = (2*search_range+1)*(2*search_range+1); // number of search positions + int best_pos = 0; // position with minimum motion cost + int block_index; // block index for indexing SAD array + distpel* block_sad; // pointer to SAD array + int list = mv_block->list; + short ref = mv_block->ref_idx; + MotionVector cand = {0, 0}, *offset = &p_Vid->p_ffast_me->search_center[list][ref]; + int max_mvd = p_Vid->max_mvd-1; + + block_index = (mv_block->block_y << 2) + (mv_block->block_x); // block index for indexing SAD array + block_sad = p_me_ffast->BlockSAD[list][ref][mv_block->blocktype][block_index]; // pointer to SAD array + + //===== set up fast full integer search if needed / set search center ===== + if (!p_Vid->p_ffast_me->search_setup_done[list][ref]) + { + currMB->p_SetupFastFullPelSearch (currMB, mv_block, list); + } + + //===== cost for (0,0)-vector: it is done before, because MVCost can be negative ===== + if (!p_Inp->rdopt && GetMaxMVD(&cand, pred_mv)pos_00[list][ref]]; + up_scale(&min_mcost); + min_mcost += mv_cost (p_Vid, lambda_factor, &cand, pred_mv); + + best_pos = p_me_ffast->pos_00[list][ref]; + } + + //===== loop over all search positions ===== + for (pos=0; posspiral_qpel_search[pos], offset); + /* //only for debug purpose; + if(GetMaxMVD(&cand, pred_mv) >= max_mvd) + { + printf("\n\n%d: out of range(%s%d)!\n\n", p_Vid->frame_no, __FILE__, __LINE__); + } + */ + //--- check residual cost --- + if (mcost < min_mcost && GetMaxMVD(&cand, pred_mv)mv[list] = add_MVs (p_Vid->spiral_qpel_search[best_pos], offset); + + return min_mcost; +} + diff --git a/lencod/src/me_fullfast_otf.c b/lencod/src/me_fullfast_otf.c new file mode 100644 index 0000000..eb4959a --- /dev/null +++ b/lencod/src/me_fullfast_otf.c @@ -0,0 +1,378 @@ +/*! +************************************************************************************* +* \file me_fullfast_otf.c +* +* \brief +* Motion Estimation using Full Search Fast (extention for on-the-fly interpolation) +* +* +************************************************************************************* +*/ + +// Includes +#include "contributors.h" +#include + +#include "global.h" +#include "image.h" +#include "memalloc.h" +#include "mb_access.h" + +#include "me_distortion.h" +#include "get_block_otf.h" + +#include "me_fullsearch.h" +#include "me_fullfast.h" +#include "me_fullfast_otf.h" +#include "conformance.h" +#include "mv_search.h" + +/*! + *********************************************************************** + * \brief + * Setup the fast search for an macroblock (interpolation on-the-fly) + *********************************************************************** + */ +void SetupFastFullPelSearch_otf (Macroblock *currMB, MEBlock *mv_block, int list) // <-- reference frame parameter, list0 or 1 +{ + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currSlice->p_Vid; + InputParameters *p_Inp = currSlice->p_Inp; + DecodedPictureBuffer *p_Dpb = p_Vid->p_Dpb_layer[p_Vid->dpb_layer_id]; + int (*dist_method) (int x) = p_Inp->MEErrorMetric[0 + (( p_Vid->dpb_layer_id >= 1 ) ? 3 : 0)] ? iabs2 : iabs; +#if (JM_MEM_DISTORTION) + int* imgpel_dist = p_Inp->MEErrorMetric[0 + p_Vid->layer_offset] ? p_Vid->imgpel_quad : p_Vid->imgpel_abs; +#endif + MotionVector pmv; + imgpel orig_pels[768]; + imgpel data[MB_PIXELS] ; + int tmp_data[ (MB_BLOCK_SIZE+5)*(MB_BLOCK_SIZE+5) ] ; + imgpel *srcptr = orig_pels, *refptr = data ; + int k, x, y; + MotionVector cand, offset; + int ref_x, ref_y, pos, bindex, blky; + distblk LineSadBlk0, LineSadBlk1, LineSadBlk2, LineSadBlk3; + PixelPos block[4]; // neighbor blocks + MEFullFast *p_me_ffast = p_Vid->p_ffast_me; + + + short ref = mv_block->ref_idx; + distpel** block_sad = p_me_ffast->BlockSAD[list][ref][7]; + int search_range = p_me_ffast->max_search_range[list][ref]; + int max_pos = (2*search_range+1) * (2*search_range+1); + + int list_offset = p_Vid->mb_data[currMB->mbAddrX].list_offset; + int apply_weights = ( (p_Vid->active_pps->weighted_pred_flag && (currSlice->slice_type == P_SLICE || (currSlice->slice_type == SP_SLICE))) || + (p_Vid->active_pps->weighted_bipred_idc && (currSlice->slice_type == B_SLICE))) && p_Inp->UseWeightedReferenceME; + int weighted_pel; + StorablePicture *ref_picture = currSlice->listX[list+list_offset][ref]; + + int wp_luma_round = 0; + short luma_log_weight_denom = 5; + short chroma_log_weight_denom = 5; + int wp_chroma_round = 0; + short weight_luma = mv_block->weight_luma, weight_cr[2]; + short offset_luma = mv_block->offset_luma, offset_cr[2]; + search_range <<= 2; + + //===== get search center: predictor of 16x16 block ===== + get_neighbors(currMB, block, 0, 0, 16); + currMB->GetMVPredictor (currMB, block, &pmv, ref, p_Vid->enc_picture->mv_info, list, 0, 0, 16, 16); + +#if (JM_INT_DIVIDE) + p_Vid->p_ffast_me->search_center[list][ref].mv_x = ((pmv.mv_x + 2) >> 2) * 4; + p_Vid->p_ffast_me->search_center[list][ref].mv_y = ((pmv.mv_y + 2) >> 2) * 4; +#else + p_Vid->p_ffast_me->search_center[list][ref].mv_x = (pmv.mv_x / 4) * 4; + p_Vid->p_ffast_me->search_center[list][ref].mv_y = (pmv.mv_y / 4) * 4; +#endif + if (!p_Inp->rdopt) + { + //--- correct center so that (0,0) vector is inside --- + p_Vid->p_ffast_me->search_center[list][ref].mv_x = (short) iClip3(-search_range, search_range, p_Vid->p_ffast_me->search_center[list][ref].mv_x); + p_Vid->p_ffast_me->search_center[list][ref].mv_y = (short) iClip3(-search_range, search_range, p_Vid->p_ffast_me->search_center[list][ref].mv_y); + } + + p_Vid->p_ffast_me->search_center[list][ref].mv_x = (short) iClip3(p_Vid->MaxHmvR[4] + search_range, p_Vid->MaxHmvR[5] - search_range, p_Vid->p_ffast_me->search_center[list][ref].mv_x); + p_Vid->p_ffast_me->search_center[list][ref].mv_y = (short) iClip3(p_Vid->MaxVmvR[4] + search_range, p_Vid->MaxVmvR[5] - search_range, p_Vid->p_ffast_me->search_center[list][ref].mv_y); + + p_Vid->p_ffast_me->search_center_padded[list][ref] = pad_MVs(p_Vid->p_ffast_me->search_center[list][ref], mv_block); + + offset = p_Vid->p_ffast_me->search_center_padded[list][ref]; + //===== copy original block for fast access ===== + for (y = currMB->opix_y; y < currMB->opix_y + MB_BLOCK_SIZE; y++) + { + memcpy(srcptr, &p_Vid->pCurImg[y][currMB->pix_x], MB_BLOCK_SIZE * sizeof(imgpel)); + srcptr += MB_BLOCK_SIZE; + } + + if ( mv_block->ChromaMEEnable) + { + for (k = 1; k < 3; k++) + { + for (y = currMB->opix_c_y; y < currMB->opix_c_y + p_Vid->mb_cr_size_y; y++) + { + memcpy(srcptr, &p_Vid->pImgOrg[k][y][currMB->pix_c_x], p_Vid->mb_cr_size_x * sizeof(imgpel)); + srcptr += p_Vid->mb_cr_size_x; + } + } + } + + + //===== determine position of (0,0)-vector ===== + if (!p_Inp->rdopt) + { + ref_x = mv_block->pos_x_padded - offset.mv_x; + ref_y = mv_block->pos_y_padded - offset.mv_y; + + for (pos = 0; pos < max_pos; pos++) + { + if (ref_x == p_Vid->spiral_qpel_search[pos].mv_x && ref_y == p_Vid->spiral_qpel_search[pos].mv_y) + { + p_me_ffast->pos_00[list][ref] = pos; + break; + } + } + } + + //===== loop over search range (spiral search): get blockwise SAD ===== + if (apply_weights) + { + weight_luma = currSlice->wp_weight[list + list_offset][ref][0]; + offset_luma = currSlice->wp_offset[list + list_offset][ref][0]; + wp_luma_round = currSlice->wp_luma_round; + luma_log_weight_denom = currSlice->luma_log_weight_denom; + chroma_log_weight_denom = currSlice->chroma_log_weight_denom; + wp_chroma_round = currSlice->wp_chroma_round; + + if (mv_block->ChromaMEEnable) + { + weight_cr[0] = currSlice->wp_weight[list + list_offset][ref][1]; + weight_cr[1] = currSlice->wp_weight[list + list_offset][ref][2]; + offset_cr[0] = currSlice->wp_offset[list + list_offset][ref][1]; + offset_cr[1] = currSlice->wp_offset[list + list_offset][ref][2]; + } + + for (pos = 0; pos < max_pos; pos++) + { + cand = add_MVs(offset, &p_Vid->spiral_qpel_search[pos]); + + srcptr = orig_pels; + bindex = 0; + refptr = data ; + p_Dpb->pf_get_block_luma ( p_Vid, refptr, tmp_data, cand.mv_x, cand.mv_y, MB_BLOCK_SIZE, MB_BLOCK_SIZE, ref_picture, 0 ) ; + + for (blky = 0; blky < 4; blky++) + { + LineSadBlk0 = LineSadBlk1 = LineSadBlk2 = LineSadBlk3 = 0; + + for (y = 0; y < 4; y++) + { + weighted_pel = iClip1( p_Vid->max_imgpel_value, ((weight_luma * *refptr++ + wp_luma_round) >> luma_log_weight_denom) + offset_luma); + LineSadBlk0 += dist_method (weighted_pel - *srcptr++); + weighted_pel = iClip1( p_Vid->max_imgpel_value, ((weight_luma * *refptr++ + wp_luma_round) >> luma_log_weight_denom) + offset_luma); + LineSadBlk0 += dist_method (weighted_pel - *srcptr++); + weighted_pel = iClip1( p_Vid->max_imgpel_value, ((weight_luma * *refptr++ + wp_luma_round) >> luma_log_weight_denom) + offset_luma); + LineSadBlk0 += dist_method (weighted_pel - *srcptr++); + weighted_pel = iClip1( p_Vid->max_imgpel_value, ((weight_luma * *refptr++ + wp_luma_round) >> luma_log_weight_denom) + offset_luma); + LineSadBlk0 += dist_method (weighted_pel - *srcptr++); + weighted_pel = iClip1( p_Vid->max_imgpel_value, ((weight_luma * *refptr++ + wp_luma_round) >> luma_log_weight_denom) + offset_luma); + LineSadBlk1 += dist_method (weighted_pel - *srcptr++); + weighted_pel = iClip1( p_Vid->max_imgpel_value, ((weight_luma * *refptr++ + wp_luma_round) >> luma_log_weight_denom) + offset_luma); + LineSadBlk1 += dist_method (weighted_pel - *srcptr++); + weighted_pel = iClip1( p_Vid->max_imgpel_value, ((weight_luma * *refptr++ + wp_luma_round) >> luma_log_weight_denom) + offset_luma); + LineSadBlk1 += dist_method (weighted_pel - *srcptr++); + weighted_pel = iClip1( p_Vid->max_imgpel_value, ((weight_luma * *refptr++ + wp_luma_round) >> luma_log_weight_denom) + offset_luma); + LineSadBlk1 += dist_method (weighted_pel - *srcptr++); + weighted_pel = iClip1( p_Vid->max_imgpel_value, ((weight_luma * *refptr++ + wp_luma_round) >> luma_log_weight_denom) + offset_luma); + LineSadBlk2 += dist_method (weighted_pel - *srcptr++); + weighted_pel = iClip1( p_Vid->max_imgpel_value, ((weight_luma * *refptr++ + wp_luma_round) >> luma_log_weight_denom) + offset_luma); + LineSadBlk2 += dist_method (weighted_pel - *srcptr++); + weighted_pel = iClip1( p_Vid->max_imgpel_value, ((weight_luma * *refptr++ + wp_luma_round) >> luma_log_weight_denom) + offset_luma); + LineSadBlk2 += dist_method (weighted_pel - *srcptr++); + weighted_pel = iClip1( p_Vid->max_imgpel_value, ((weight_luma * *refptr++ + wp_luma_round) >> luma_log_weight_denom) + offset_luma); + LineSadBlk2 += dist_method (weighted_pel - *srcptr++); + weighted_pel = iClip1( p_Vid->max_imgpel_value, ((weight_luma * *refptr++ + wp_luma_round) >> luma_log_weight_denom) + offset_luma); + LineSadBlk3 += dist_method (weighted_pel - *srcptr++); + weighted_pel = iClip1( p_Vid->max_imgpel_value, ((weight_luma * *refptr++ + wp_luma_round) >> luma_log_weight_denom) + offset_luma); + LineSadBlk3 += dist_method (weighted_pel - *srcptr++); + weighted_pel = iClip1( p_Vid->max_imgpel_value, ((weight_luma * *refptr++ + wp_luma_round) >> luma_log_weight_denom) + offset_luma); + LineSadBlk3 += dist_method (weighted_pel - *srcptr++); + weighted_pel = iClip1( p_Vid->max_imgpel_value, ((weight_luma * *refptr++ + wp_luma_round) >> luma_log_weight_denom) + offset_luma); + LineSadBlk3 += dist_method (weighted_pel - *srcptr++); + } + block_sad[bindex++][pos] = (distpel) LineSadBlk0; + block_sad[bindex++][pos] = (distpel) LineSadBlk1; + block_sad[bindex++][pos] = (distpel) LineSadBlk2; + block_sad[bindex++][pos] = (distpel) LineSadBlk3; + + } + if (mv_block->ChromaMEEnable) + { + int max_imgpel_value_uv = p_Vid->max_pel_value_comp[1]; + + for (k = 0; k < 2; k ++) + { + bindex = 0; + refptr = data ; + p_Dpb->pf_get_block_chroma[OTF_ME]( p_Vid, refptr, tmp_data, cand.mv_x, cand.mv_y, p_Vid->mb_cr_size_x, p_Vid->mb_cr_size_y, ref_picture, k+1 ); + + for (blky = 0; blky < 4; blky++) + { + LineSadBlk0 = LineSadBlk1 = LineSadBlk2 = LineSadBlk3 = 0; + + for (y = 0; y < p_Vid->mb_cr_size_y; y+=BLOCK_SIZE) + { + for (x = 0; x < p_Vid->mb_cr_size_x; x += BLOCK_SIZE) + { + weighted_pel = iClip1( max_imgpel_value_uv, ((weight_cr[k] * *refptr++ + wp_chroma_round) >> chroma_log_weight_denom) + offset_cr[k]); + LineSadBlk0 += dist_method (weighted_pel - *srcptr++); + } + for (x = 0; x < p_Vid->mb_cr_size_x; x += BLOCK_SIZE) + { + weighted_pel = iClip1( max_imgpel_value_uv, ((weight_cr[k] * *refptr++ + wp_chroma_round) >> chroma_log_weight_denom) + offset_cr[k]); + LineSadBlk1 += dist_method (weighted_pel - *srcptr++); + } + for (x = 0; x < p_Vid->mb_cr_size_x; x += BLOCK_SIZE) + { + weighted_pel = iClip1( max_imgpel_value_uv, ((weight_cr[k] * *refptr++ + wp_chroma_round) >> chroma_log_weight_denom) + offset_cr[k]); + LineSadBlk2 += dist_method (weighted_pel - *srcptr++); + } + for (x = 0; x < p_Vid->mb_cr_size_x; x += BLOCK_SIZE) + { + weighted_pel = iClip1( max_imgpel_value_uv, ((weight_cr[k] * *refptr++ + wp_chroma_round) >> chroma_log_weight_denom) + offset_cr[k]); + LineSadBlk3 += dist_method (weighted_pel - *srcptr++); + } + } + block_sad[bindex][pos] = block_sad[bindex][pos] + ((distpel) LineSadBlk0); + ++bindex; + block_sad[bindex][pos] = block_sad[bindex][pos] + ((distpel) LineSadBlk1); + ++bindex; + block_sad[bindex][pos] = block_sad[bindex][pos] + ((distpel) LineSadBlk2); + ++bindex; + block_sad[bindex][pos] = block_sad[bindex][pos] + ((distpel) LineSadBlk3); + ++bindex; + } + } + } + } + } + else + { + for (pos = 0; pos < max_pos; pos++) + { + cand = add_MVs(offset, &p_Vid->spiral_qpel_search[pos]); + srcptr = orig_pels; + bindex = 0; + + refptr = data; + p_Dpb->pf_get_block_luma( p_Vid, refptr, tmp_data, cand.mv_x, cand.mv_y, MB_BLOCK_SIZE, MB_BLOCK_SIZE, ref_picture, 0 ) ; + + for (blky = 0; blky < 4; blky++) + { + LineSadBlk0 = LineSadBlk1 = LineSadBlk2 = LineSadBlk3 = 0; + + for (y = 0; y < 4; y++) + { +#if (JM_MEM_DISTORTION) + // Distortion for first 4x4 block + LineSadBlk0 += imgpel_dist[ *refptr++ - *srcptr++ ]; + LineSadBlk0 += imgpel_dist[ *refptr++ - *srcptr++ ]; + LineSadBlk0 += imgpel_dist[ *refptr++ - *srcptr++ ]; + LineSadBlk0 += imgpel_dist[ *refptr++ - *srcptr++ ]; + // Distortion for second 4x4 block + LineSadBlk1 += imgpel_dist[ *refptr++ - *srcptr++ ]; + LineSadBlk1 += imgpel_dist[ *refptr++ - *srcptr++ ]; + LineSadBlk1 += imgpel_dist[ *refptr++ - *srcptr++ ]; + LineSadBlk1 += imgpel_dist[ *refptr++ - *srcptr++ ]; + // Distortion for third 4x4 block + LineSadBlk2 += imgpel_dist[ *refptr++ - *srcptr++ ]; + LineSadBlk2 += imgpel_dist[ *refptr++ - *srcptr++ ]; + LineSadBlk2 += imgpel_dist[ *refptr++ - *srcptr++ ]; + LineSadBlk2 += imgpel_dist[ *refptr++ - *srcptr++ ]; + // Distortion for fourth 4x4 block + LineSadBlk3 += imgpel_dist[ *refptr++ - *srcptr++ ]; + LineSadBlk3 += imgpel_dist[ *refptr++ - *srcptr++ ]; + LineSadBlk3 += imgpel_dist[ *refptr++ - *srcptr++ ]; + LineSadBlk3 += imgpel_dist[ *refptr++ - *srcptr++ ]; +#else + // Distortion for first 4x4 block + LineSadBlk0 += dist_method (*refptr++ - *srcptr++); + LineSadBlk0 += dist_method (*refptr++ - *srcptr++); + LineSadBlk0 += dist_method (*refptr++ - *srcptr++); + LineSadBlk0 += dist_method (*refptr++ - *srcptr++); + // Distortion for second 4x4 block + LineSadBlk1 += dist_method (*refptr++ - *srcptr++); + LineSadBlk1 += dist_method (*refptr++ - *srcptr++); + LineSadBlk1 += dist_method (*refptr++ - *srcptr++); + LineSadBlk1 += dist_method (*refptr++ - *srcptr++); + // Distortion for third 4x4 block + LineSadBlk2 += dist_method (*refptr++ - *srcptr++); + LineSadBlk2 += dist_method (*refptr++ - *srcptr++); + LineSadBlk2 += dist_method (*refptr++ - *srcptr++); + LineSadBlk2 += dist_method (*refptr++ - *srcptr++); + // Distortion for fourth 4x4 block + LineSadBlk3 += dist_method (*refptr++ - *srcptr++); + LineSadBlk3 += dist_method (*refptr++ - *srcptr++); + LineSadBlk3 += dist_method (*refptr++ - *srcptr++); + LineSadBlk3 += dist_method (*refptr++ - *srcptr++); +#endif + } + block_sad[bindex++][pos] = (distpel) LineSadBlk0; + block_sad[bindex++][pos] = (distpel) LineSadBlk1; + block_sad[bindex++][pos] = (distpel) LineSadBlk2; + block_sad[bindex++][pos] = (distpel) LineSadBlk3; + } + + if (mv_block->ChromaMEEnable) + { + for (k = 0; k < 2; k ++) + { + bindex = 0; + + refptr = data ; + p_Dpb->pf_get_block_chroma[OTF_ME]( p_Vid, refptr, tmp_data, cand.mv_x, cand.mv_y, p_Vid->mb_cr_size_x, p_Vid->mb_cr_size_y, ref_picture, k+1 ); + + for (blky = 0; blky < 4; blky++) + { + LineSadBlk0 = LineSadBlk1 = LineSadBlk2 = LineSadBlk3 = 0; + + for (y = 0; y < p_Vid->mb_cr_size_y; y+=BLOCK_SIZE) + { + for (x = 0; x < p_Vid->mb_cr_size_x; x += BLOCK_SIZE) + { + LineSadBlk0 += dist_method (*refptr++ - *srcptr++); + } + for (x = 0; x < p_Vid->mb_cr_size_x; x += BLOCK_SIZE) + { + LineSadBlk1 += dist_method (*refptr++ - *srcptr++); + } + for (x = 0; x < p_Vid->mb_cr_size_x; x += BLOCK_SIZE) + { + LineSadBlk2 += dist_method (*refptr++ - *srcptr++); + } + for (x = 0; x < p_Vid->mb_cr_size_x; x += BLOCK_SIZE) + { + LineSadBlk3 += dist_method (*refptr++ - *srcptr++); + } + } + block_sad[bindex][pos] = block_sad[bindex][pos] + ((distpel) LineSadBlk0); + ++bindex; + block_sad[bindex][pos] = block_sad[bindex][pos] + ((distpel) LineSadBlk1); + ++bindex; + block_sad[bindex][pos] = block_sad[bindex][pos] + ((distpel) LineSadBlk2); + ++bindex; + block_sad[bindex][pos] = block_sad[bindex][pos] + ((distpel) LineSadBlk3); + ++bindex; + } + } + } + } + } + + //===== combine SAD's for larger block types ===== + update_full_search_large_blocks (p_Vid->p_ffast_me, list, ref, max_pos); + + //===== set flag marking that search setup have been done ===== + p_Vid->p_ffast_me->search_setup_done[list][ref] = 1; +} diff --git a/lencod/src/me_fullsearch.c b/lencod/src/me_fullsearch.c new file mode 100644 index 0000000..d702fce --- /dev/null +++ b/lencod/src/me_fullsearch.c @@ -0,0 +1,534 @@ + +/*! +************************************************************************************* +* \file me_fullsearch.c +* +* \brief +* Motion Estimation using Fullsearch +* +* \author +* Main contributors (see contributors.h for copyright, address and affiliation details) +* - Alexis Michael Tourapis +* - Athanasios Leontaris +* +************************************************************************************* +*/ + +// Includes +#include "contributors.h" +#include + +#include "global.h" +#include "image.h" +#include "memalloc.h" +#include "mb_access.h" +#include "refbuf.h" +#include "macroblock.h" +#include "me_distortion.h" +#include "me_fullsearch.h" +#include "mv_search.h" + +// Functions +/*! + *********************************************************************** + * \brief + * Full pixel block motion search + *********************************************************************** + */ +distblk // ==> minimum motion cost after search +full_search_motion_estimation (Macroblock *currMB , // <-- current Macroblock + MotionVector *pred_mv, // <-- motion vector predictor in sub-pel units + MEBlock *mv_block, // <-- motion estimation structure + distblk min_mcost, // <-- minimum motion cost (cost for center or huge value) + int lambda_factor // <-- lagrangian parameter for determining motion cost + ) +{ + VideoParameters *p_Vid = currMB->p_Vid; + InputParameters *p_Inp = currMB->p_Inp; + Slice *currSlice = currMB->p_Slice; + int search_range = imin(mv_block->searchRange.max_x, mv_block->searchRange.max_y)>> 2; + distblk mcost; + int pos; + MotionVector cand, center, pred; + short ref = mv_block->ref_idx; + + StorablePicture *ref_picture = currSlice->listX[mv_block->list+currMB->list_offset][ref]; + + int best_pos = 0; // position with minimum motion cost + int max_pos = (2*search_range+1)*(2*search_range+1); // number of search positions + + MotionVector *mv = &mv_block->mv[(short) mv_block->list]; + int check_for_00 = (mv_block->blocktype==1 && !p_Inp->rdopt && currSlice->slice_type!=B_SLICE && ref==0); + center.mv_x = mv_block->pos_x_padded + mv->mv_x; // center position x (in sub-pel units) + center.mv_y = mv_block->pos_y_padded + mv->mv_y; // center position y (in sub-pel units) + pred.mv_x = mv_block->pos_x_padded + pred_mv->mv_x; // predicted position x (in sub-pel units) + pred.mv_y = mv_block->pos_y_padded + pred_mv->mv_y; // predicted position y (in sub-pel units) + + + //===== loop over all search positions ===== + for (pos=0; posspiral_qpel_search[pos].mv_x; + cand.mv_y = center.mv_y + p_Vid->spiral_qpel_search[pos].mv_y; + + //--- initialize motion cost (cost for motion vector) and check --- + mcost = mv_cost (p_Vid, lambda_factor, &cand, &pred); + + if (check_for_00 && cand.mv_x == mv_block->pos_x_padded && cand.mv_y == mv_block->pos_y_padded) + { + distblk tmp = weighted_cost (lambda_factor, 16); + mcost = mcost > tmp? (mcost-tmp): 0; + } + if (mcost >= min_mcost) continue; + + //--- add residual cost to motion cost --- + mcost += mv_block->computePredFPel(ref_picture, mv_block, min_mcost - mcost, &cand); + + //--- check if motion cost is less than minimum cost --- + if (mcost < min_mcost) + { + best_pos = pos; + min_mcost = mcost; + } + } + + + //===== set best motion vector and return minimum motion cost ===== + if (best_pos) + { + add_mvs(mv, &p_Vid->spiral_qpel_search[best_pos]); + } + return min_mcost; +} + +/*! + *********************************************************************** + * \brief + * Full pixel block motion search + *********************************************************************** + */ +distblk // ==> minimum motion cost after search +full_search_bipred_motion_estimation (Macroblock *currMB, // <-- current Macroblock + int list, // <-- reference list + MotionVector *pred_mv1, // <-- motion vector predictor from first list (x|y) in sub-pel units + MotionVector *pred_mv2, // <-- motion vector predictor from second list (x|y) in sub-pel units + MotionVector *mv1, // <--> in: search center (x|y) / out: motion vector (x|y) - in sub-pel units + MotionVector *mv2, // <--> in: search center (x|y) + MEBlock *mv_block, // <-- motion vector information + int search_range, // <-- 1-d search range in sub-pel units + distblk min_mcost, // <-- minimum motion cost (cost for center or huge value) + int lambda_factor // <-- lagrangian parameter for determining motion cost + ) +{ + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + short ref = mv_block->ref_idx; + StorablePicture *ref_picture1 = currSlice->listX[list + currMB->list_offset][ref]; + StorablePicture *ref_picture2 = currSlice->listX[(list ^ 1) + currMB->list_offset][0]; + distblk mcost; + int pos; + + int best_pos = 0; // position with minimum motion cost + int max_pos = (2 * (search_range >> 2) +1)*(2 * (search_range >> 2) + 1); // number of search positions + + MotionVector center1, center2, cand, pred1, pred2; + pred1.mv_x = mv_block->pos_x_padded + pred_mv1->mv_x; // predicted position x (in sub-pel units) + pred1.mv_y = mv_block->pos_y_padded + pred_mv1->mv_y; // predicted position y (in sub-pel units) + pred2.mv_x = mv_block->pos_x_padded + pred_mv2->mv_x; // predicted position x (in sub-pel units) + pred2.mv_y = mv_block->pos_y_padded + pred_mv2->mv_y; // predicted position y (in sub-pel units) + center1.mv_x = mv_block->pos_x_padded + mv1->mv_x; // center position x (in sub-pel units) + center1.mv_y = mv_block->pos_y_padded + mv1->mv_y; // center position y (in sub-pel units) + center2.mv_x = mv_block->pos_x_padded + mv2->mv_x; // center position x of static mv (in sub-pel units) + center2.mv_y = mv_block->pos_y_padded + mv2->mv_y; // center position y of static mv (in sub-pel units) + + + //===== loop over all search positions ===== + for (pos=0; posspiral_qpel_search[pos].mv_x; + cand.mv_y = center1.mv_y + p_Vid->spiral_qpel_search[pos].mv_y; + + //--- initialize motion cost (cost for motion vector) and check --- + mcost = mv_cost (p_Vid, lambda_factor, &cand, &pred1); + mcost += mv_cost (p_Vid, lambda_factor, ¢er2, &pred2); + + if (mcost >= min_mcost) continue; + + //--- add residual cost to motion cost --- + mcost += mv_block->computeBiPredFPel(ref_picture1, ref_picture2, mv_block, min_mcost - mcost, + &cand, ¢er2); + + //--- check if motion cost is less than minimum cost --- + if (mcost < min_mcost) + { + best_pos = pos; + min_mcost = mcost; + } + } + + //===== set best motion vector and return minimum motion cost ===== + if (best_pos) + { + add_mvs(mv1, &p_Vid->spiral_qpel_search[best_pos]); + } + return min_mcost; +} + +/*! + *********************************************************************** + * \brief + * Sub pixel block motion search + *********************************************************************** + */ +distblk // ==> minimum motion cost after search +sub_pel_motion_estimation (Macroblock *currMB, // <-- Current Macroblock + MotionVector *pred, // <-- motion vector predictor in sub-pel units + MEBlock *mv_block, // <-- motion estimation structure + distblk min_mcost, // <-- minimum motion cost (cost for center or huge value) + int* lambda // <-- lagrangian parameter for determining motion cost + ) +{ + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + InputParameters *p_Inp = currMB->p_Inp; + distblk mcost; + int pos, best_pos; + + MotionVector cand; + int list = mv_block->list; + int cur_list = list + currMB->list_offset; + short ref = mv_block->ref_idx; + StorablePicture *ref_picture = currSlice->listX[cur_list][ref]; + + MotionVector *mv = &mv_block->mv[list]; + + int check_position0 = (!p_Inp->rdopt && currSlice->slice_type != B_SLICE && ref==0 && mv_block->blocktype==1 && mv->mv_x == 0 && mv->mv_y ==0); + + int max_pos2 = ( !p_Vid->start_me_refinement_hp ? imax(1, mv_block->search_pos2) : mv_block->search_pos2); + MotionVector cmv; + + int lambda_factor = lambda[H_PEL]; + + /********************************* + ***** ***** + ***** HALF-PEL REFINEMENT ***** + ***** ***** + *********************************/ + + //===== loop over search positions ===== + for (best_pos = 0, pos = p_Vid->start_me_refinement_hp; pos < max_pos2; pos++) + { + cand.mv_x = mv->mv_x + p_Vid->spiral_hpel_search[pos].mv_x; // quarter-pel units + cand.mv_y = mv->mv_y + p_Vid->spiral_hpel_search[pos].mv_y; // quarter-pel units + + //----- set motion vector cost ----- + mcost = mv_cost (p_Vid, lambda_factor, &cand, pred); + + + if (mcost >= min_mcost) continue; + + cmv = pad_MVs(cand, mv_block); + + mcost += mv_block->computePredHPel( ref_picture, mv_block, min_mcost - mcost, &cmv); + + if (pos==0 && check_position0) + { + mcost -= weighted_cost (lambda_factor, 16); + } + + if (mcost < min_mcost) + { + min_mcost = mcost; + best_pos = pos; + } + } + if (best_pos) + { + mv->mv_x = mv->mv_x + p_Vid->spiral_hpel_search[best_pos].mv_x; + mv->mv_y = mv->mv_y + p_Vid->spiral_hpel_search[best_pos].mv_y; + } + if ( !p_Vid->start_me_refinement_qp ) + min_mcost = DISTBLK_MAX; + /************************************ + ***** ***** + ***** QUARTER-PEL REFINEMENT ***** + ***** ***** + ************************************/ + + lambda_factor = lambda[Q_PEL]; + + //===== loop over search positions ===== + for (best_pos = 0, pos = p_Vid->start_me_refinement_qp; pos < mv_block->search_pos4; pos++) + { + cand.mv_x = mv->mv_x + p_Vid->spiral_search[pos].mv_x; // quarter-pel units + cand.mv_y = mv->mv_y + p_Vid->spiral_search[pos].mv_y; // quarter-pel units + + //----- set motion vector cost ----- + mcost = mv_cost (p_Vid, lambda_factor, &cand, pred); + + if (mcost >= min_mcost) continue; + + cmv = pad_MVs(cand, mv_block); + + mcost += mv_block->computePredQPel( ref_picture, mv_block, min_mcost - mcost, &cmv); + if (mcost < min_mcost) + { + min_mcost = mcost; + best_pos = pos; + } + } + if (best_pos) + { + add_mvs(mv, &p_Vid->spiral_search[best_pos]); + } + + //===== return minimum motion cost ===== + return min_mcost; +} + + +/*! +*********************************************************************** +* \brief +* Bipred Sub pixel block motion search +*********************************************************************** +*/ +distblk // ==> minimum motion cost after search +sub_pel_bipred_motion_estimation (Macroblock *currMB, // <-- current Macroblock + MEBlock *mv_block, // <-- motion vector information + int list, // <-- reference picture list + MotionVector *pred_mv1, // <-- motion vector predictor (x) in sub-pel units + MotionVector *pred_mv2, // <-- motion vector predictor (x) in sub-pel units + MotionVector *mv1, // <--> in: search center (x) / out: motion vector (x) - in sub-pel units + MotionVector *mv2, // <--> in: search center (x) / out: motion vector (x) - in sub-pel units + distblk min_mcost, // <-- minimum motion cost (cost for center or huge value) + int* lambda // <-- lagrangian parameter for determining motion cost + ) +{ + VideoParameters *p_Vid = currMB->p_Vid; + Slice *currSlice = currMB->p_Slice; + int list_offset = p_Vid->mb_data[currMB->mbAddrX].list_offset; + distblk mcost; + int pos, best_pos; + MotionVector cand; + int start_hp = (min_mcost == DISTBLK_MAX) ? 0 : p_Vid->start_me_refinement_hp; + int max_pos2 = ( !p_Vid->start_me_refinement_hp ? imax(1, mv_block->search_pos2) : mv_block->search_pos2); + + MotionVector cmv, smv = pad_MVs(*mv2, mv_block); + short ref = mv_block->ref_idx; + + StorablePicture *ref_picture1 = currSlice->listX[list + list_offset][ref]; + StorablePicture *ref_picture2 = currSlice->listX[(list ^ 1) + list_offset][0]; + + int lambda_factor = lambda[H_PEL]; + + /********************************* + ***** ***** + ***** HALF-PEL REFINEMENT ***** + ***** ***** + *********************************/ + + //===== loop over search positions ===== + for (best_pos = 0, pos = start_hp; pos < max_pos2; pos++) + { + cand.mv_x = mv1->mv_x + p_Vid->spiral_hpel_search[pos].mv_x; // quarter-pel units + cand.mv_y = mv1->mv_y + p_Vid->spiral_hpel_search[pos].mv_y; // quarter-pel units + + //----- set motion vector cost ----- + mcost = mv_cost (p_Vid, lambda_factor, &cand, pred_mv1); + mcost += mv_cost (p_Vid, lambda_factor, mv2, pred_mv2); + + if (mcost >= min_mcost) continue; + + cmv = pad_MVs(cand, mv_block); + mcost += mv_block->computeBiPredHPel(ref_picture1, ref_picture2, mv_block, min_mcost - mcost, &cmv, &smv); + + if (mcost < min_mcost) + { + min_mcost = mcost; + best_pos = pos; + } + } + + if (best_pos) + { + add_mvs(mv1, &p_Vid->spiral_hpel_search[best_pos]); + } + + /************************************ + ***** ***** + ***** QUARTER-PEL REFINEMENT ***** + ***** ***** + ************************************/ + if ( !p_Vid->start_me_refinement_qp ) + min_mcost = DISTBLK_MAX; + + lambda_factor = lambda[Q_PEL]; + + //===== loop over search positions ===== + for (best_pos = 0, pos = p_Vid->start_me_refinement_qp; pos < mv_block->search_pos4; pos++) + { + cand.mv_x = mv1->mv_x + p_Vid->spiral_search[pos].mv_x; // quarter-pel units + cand.mv_y = mv1->mv_y + p_Vid->spiral_search[pos].mv_y; // quarter-pel units + + //----- set motion vector cost ----- + mcost = mv_cost (p_Vid, lambda_factor, &cand, pred_mv1); + mcost += mv_cost (p_Vid, lambda_factor, mv2, pred_mv2); + + if (mcost >= min_mcost) continue; + + cmv = pad_MVs(cand, mv_block); + mcost += mv_block->computeBiPredQPel(ref_picture1, ref_picture2, mv_block, min_mcost - mcost, &cmv, &smv); + + if (mcost < min_mcost) + { + min_mcost = mcost; + best_pos = pos; + } + + } + + if (best_pos) + { + add_mvs(mv1, &p_Vid->spiral_search[best_pos]); + } + + //===== return minimum motion cost ===== + return min_mcost; +} + +/*! + *********************************************************************** + * \brief + * Brute Force Sub pixel block motion search + *********************************************************************** + */ +distblk // ==> minimum motion cost after search +full_sub_pel_motion_estimation (Macroblock *currMB, // <-- Current Macroblock + MotionVector *pred, // <-- motion vector predictor in sub-pel units + MEBlock *mv_block, // <-- motion estimation structure + distblk min_mcost, // <-- minimum motion cost (cost for center or huge value) + int* lambda // <-- lagrangian parameter for determining motion cost + ) +{ + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + InputParameters *p_Inp = currMB->p_Inp; + distblk mcost; + int pos, best_pos; + + MotionVector cand; + int list = mv_block->list; + int cur_list = list + currMB->list_offset; + short ref = mv_block->ref_idx; + StorablePicture *ref_picture = currSlice->listX[cur_list][ref]; + + MotionVector *mv = &mv_block->mv[list]; + + int check_position0 = (!p_Inp->rdopt && currSlice->slice_type != B_SLICE && ref==0 && mv_block->blocktype==1 && mv->mv_x == 0 && mv->mv_y ==0); + + MotionVector cmv; + + int lambda_factor = lambda[Q_PEL]; + + //===== loop over all search positions ===== + for (best_pos = 0, pos = 0; pos < 81; pos++) + { + cand.mv_x = mv->mv_x + p_Vid->spiral_search[pos].mv_x; + cand.mv_y = mv->mv_y + p_Vid->spiral_search[pos].mv_y; + + //----- set motion vector cost ----- + mcost = mv_cost (p_Vid, lambda_factor, &cand, pred); + + if (mcost >= min_mcost) continue; + + cmv = pad_MVs(cand, mv_block); + mcost += mv_block->computePredQPel( ref_picture, mv_block, min_mcost - mcost, &cmv); + + if (pos == 0 && check_position0) + { + mcost -= weighted_cost (lambda_factor, 16); + } + + if (mcost < min_mcost) + { + min_mcost = mcost; + best_pos = pos; + } + } + + if (best_pos) + { + add_mvs(mv, &p_Vid->spiral_search[best_pos]); + } + + //===== return minimum motion cost ===== + return min_mcost; +} + +/*! +*********************************************************************** +* \brief +* Brute Force Bipred Sub pixel block motion search +*********************************************************************** +*/ +distblk // ==> minimum motion cost after search +full_sub_pel_bipred_motion_estimation (Macroblock *currMB, // <-- current Macroblock + MEBlock *mv_block, // <-- motion vector information + int list, // <-- reference picture list + MotionVector *pred_mv1, // <-- motion vector predictor (x) in sub-pel units + MotionVector *pred_mv2, // <-- motion vector predictor (x) in sub-pel units + MotionVector *mv1, // <--> in: search center (x) / out: motion vector (x) - in pel units + MotionVector *mv2, // <--> in: search center (x) / out: motion vector (x) - in pel units + distblk min_mcost, // <-- minimum motion cost (cost for center or huge value) + int* lambda // <-- lagrangian parameter for determining motion cost + ) +{ + VideoParameters *p_Vid = currMB->p_Vid; + Slice *currSlice = currMB->p_Slice; + int list_offset = p_Vid->mb_data[currMB->mbAddrX].list_offset; + distblk mcost; + int pos, best_pos; + MotionVector cand; + + MotionVector cmv, smv = pad_MVs(*mv2, mv_block); + short ref = mv_block->ref_idx; + + StorablePicture *ref_picture1 = currSlice->listX[list + list_offset][ref]; + StorablePicture *ref_picture2 = currSlice->listX[(list ^ 1) + list_offset][0]; + + int lambda_factor = lambda[Q_PEL]; + + //===== loop over search positions ===== + for (best_pos = 0, pos = 0; pos < 81; pos++) + { + cand.mv_x = mv1->mv_x + p_Vid->spiral_search[pos].mv_x; // quarter-pel units + cand.mv_y = mv1->mv_y + p_Vid->spiral_search[pos].mv_y; // quarter-pel units + + //----- set motion vector cost ----- + mcost = mv_cost (p_Vid, lambda_factor, &cand, pred_mv1); + mcost += mv_cost (p_Vid, lambda_factor, mv2, pred_mv2); + + if (mcost >= min_mcost) continue; + + cmv = pad_MVs(cand, mv_block); + mcost += mv_block->computeBiPredQPel(ref_picture1, ref_picture2, mv_block, min_mcost - mcost, &cmv, &smv); + + if (mcost < min_mcost) + { + min_mcost = mcost; + best_pos = pos; + } + } + + if (best_pos) + { + add_mvs(mv1, &p_Vid->spiral_search[best_pos]); + } + + //===== return minimum motion cost ===== + return min_mcost; +} + diff --git a/lencod/src/me_umhex.c b/lencod/src/me_umhex.c new file mode 100644 index 0000000..d5655f3 --- /dev/null +++ b/lencod/src/me_umhex.c @@ -0,0 +1,1396 @@ + +/*! + ************************************************************************ + * + * \file me_umhex.c + * + * \brief + * Fast integer pel motion estimation and fractional pel motion estimation + * algorithms are described in this file. + * 1. UMHEX_get_mem() and UMHEX_free_mem() are functions for allocation and release + * of memories about motion estimation + * 2. UMHEX_BlockMotionSearch() is the function for fast integer pel motion + * estimation and fractional pel motion estimation + * 3. UMHEX_DefineThreshold() defined thresholds for early termination + * \author + * Main contributors: (see contributors.h for copyright, address and affiliation details) + * - Zhibo Chen + * - JianFeng Xu + * - Wenfang Fu + * - Xiaozhong Xu + * \date + * 2006.1 + ************************************************************************ + */ + +#include + +#include "global.h" +#include "memalloc.h" +#include "me_umhex.h" +#include "refbuf.h" +#include "mb_access.h" +#include "image.h" +#include "enc_statistics.h" +#include "macroblock.h" +#include "me_distortion.h" +#include "mv_search.h" +#include "me_fullsearch.h" + +#define Q_BITS 15 +#define MIN_IMG_WIDTH 176 + +static const MotionVector Diamond[4] = {{-4, 0}, {4, 0}, {0, -4}, {0, 4}}; +static const MotionVector Hexagon[6] = {{-8, 0}, {8, 0},{-4, -8}, {4, 8}, {-4, 8}, {4 , -8}}; +static const short Big_Hexagon_X[16] = {0,-8, -16,-16,-16, -16, -16, -8, 0, 8, 16, 16, 16, 16, 16, 8}; +static const short Big_Hexagon_Y[16] = {8, 12, 8, 4, 0, -4, -8, -12, -16, -12, -8, -4, 0, 4, 8, 12}; + +static const int Multi_Ref_Thd[8] = {0, 300, 120, 120, 60, 30, 30, 15}; +static const int Big_Hexagon_Thd[8] = {0, 3000, 1500, 1500, 800, 400, 400, 200}; +static const int Median_Pred_Thd[8] = {0, 750, 350, 350, 170, 80, 80, 40}; +static const int Threshold_DSR[8] = {0, 2200, 1000, 1000, 500, 250, 250, 120}; + + +void UMHEX_DefineThreshold(VideoParameters *p_Vid) +{ + UMHexStruct *p_UMHex = p_Vid->p_UMHex; + + p_UMHex->AlphaFourth_1[1] = 0.01f; + p_UMHex->AlphaFourth_1[2] = 0.01f; + p_UMHex->AlphaFourth_1[3] = 0.01f; + p_UMHex->AlphaFourth_1[4] = 0.02f; + p_UMHex->AlphaFourth_1[5] = 0.03f; + p_UMHex->AlphaFourth_1[6] = 0.03f; + p_UMHex->AlphaFourth_1[7] = 0.04f; + + p_UMHex->AlphaFourth_2[1] = 0.06f; + p_UMHex->AlphaFourth_2[2] = 0.07f; + p_UMHex->AlphaFourth_2[3] = 0.07f; + p_UMHex->AlphaFourth_2[4] = 0.08f; + p_UMHex->AlphaFourth_2[5] = 0.12f; + p_UMHex->AlphaFourth_2[6] = 0.11f; + p_UMHex->AlphaFourth_2[7] = 0.15f; + + p_UMHex->BlockType_LUT[0][0] = 7; // 4x4 + p_UMHex->BlockType_LUT[0][1] = 6; // 4x8 + p_UMHex->BlockType_LUT[1][0] = 5; // 8x4 + p_UMHex->BlockType_LUT[1][1] = 4; // 8x8 + p_UMHex->BlockType_LUT[1][3] = 3; // 8x16 + p_UMHex->BlockType_LUT[3][1] = 2; // 16x8 + p_UMHex->BlockType_LUT[3][3] = 1; // 16x16 + + return; +} +/*! +************************************************************************ +* \brief +* Set MB thresholds for fast motion estimation +* Those thresholds may be adjusted to trade off rate-distortion +* performance and UMHEX speed +************************************************************************ +*/ + +void UMHEX_DefineThresholdMB(VideoParameters *p_Vid, InputParameters *p_Inp) +{ + UMHexStruct *p_UMHex = p_Vid->p_UMHex; + int gb_qp_per = (p_Inp->qp[P_SLICE])/6; + int gb_qp_rem = (p_Inp->qp[P_SLICE])%6; + + int gb_q_bits = Q_BITS+gb_qp_per; + int gb_qp_const,Thresh4x4; + + float Quantize_step; + int i; + // scale factor: defined for different image sizes + float scale_factor = (float)((1-p_Inp->UMHexScale*0.1)+p_Inp->UMHexScale*0.1*(p_Vid->width/MIN_IMG_WIDTH)); + // QP factor: defined for different quantization steps + float QP_factor = (float)((1.0-0.90*(p_Inp->qp[P_SLICE]/51.0f))); + distblk dbScalar = dist_scale(1); + + gb_qp_const=(1<p_Quant->q_params_4x4[0][1][gb_qp_rem][0][0].ScaleComp); + Quantize_step = Thresh4x4/(4*5.61f)*2.0f*scale_factor; + p_UMHex->Bsize[7]=(16*16)*Quantize_step*dbScalar; + + p_UMHex->Bsize[6] = p_UMHex->Bsize[7]*4*dbScalar; + p_UMHex->Bsize[5] = p_UMHex->Bsize[7]*4*dbScalar; + p_UMHex->Bsize[4] = p_UMHex->Bsize[5]*4*dbScalar; + p_UMHex->Bsize[3] = p_UMHex->Bsize[4]*4*dbScalar; + p_UMHex->Bsize[2] = p_UMHex->Bsize[4]*4*dbScalar; + p_UMHex->Bsize[1] = p_UMHex->Bsize[2]*4*dbScalar; + + for(i=1;i<8;i++) + { + //ET_Thd1: early termination after median prediction + p_UMHex->Median_Pred_Thd_MB[i] = (distblk) (Median_Pred_Thd[i]* scale_factor*QP_factor*dbScalar); + //ET_thd2: early termination after every circle of 16 points Big-Hex Search + p_UMHex->Big_Hexagon_Thd_MB[i] = (distblk) (Big_Hexagon_Thd[i]* scale_factor*QP_factor*dbScalar); + //threshold for multi ref case + p_UMHex->Multi_Ref_Thd_MB[i] = (distblk) (Multi_Ref_Thd[i] * scale_factor*QP_factor*dbScalar); + //threshold for usage of DSR technique. DSR ref to JVT-R088 + p_UMHex->Threshold_DSR_MB[i] = (distblk) (Threshold_DSR[i] * scale_factor*QP_factor*dbScalar); + } +} + +/*! +************************************************************************ +* \brief +* Allocation of space for fast motion estimation +************************************************************************ +*/ +int UMHEX_get_mem(VideoParameters *p_Vid, InputParameters *p_Inp) +{ + UMHexStruct *p_UMHex = p_Vid->p_UMHex; + + int memory_size = 0; + int search_range = p_Inp->SearchMode[0] == UM_HEX ? p_Inp->search_range[0] : p_Inp->search_range[1]; + + if (NULL==(p_UMHex->flag_intra = calloc ((p_Vid->width>>4)+1,sizeof(byte)))) no_mem_exit("UMHEX_get_mem: p_UMHex->flag_intra"); //fwf 20050330 + + memory_size += get_mem2D(&p_UMHex->McostState, 2*search_range+1, 2*search_range+1); + + memory_size += get_mem4Ddistblk(&(p_UMHex->fastme_ref_cost), p_Vid->max_num_references, 9, 4, 4); + memory_size += get_mem3Ddistblk(&(p_UMHex->fastme_l0_cost), 9, p_Vid->height >> 2, p_Vid->width >> 2); + memory_size += get_mem3Ddistblk(&(p_UMHex->fastme_l1_cost), 9, p_Vid->height >> 2, p_Vid->width >> 2); + memory_size += get_mem2Ddistblk(&(p_UMHex->fastme_best_cost), 7, p_Vid->width >> 2); + + memory_size += get_mem2D(&p_UMHex->SearchState, 7, 7); + if(p_Inp->BiPredMotionEstimation == 1)//memory allocation for bipred mode + { + memory_size += get_mem3Ddistblk(&(p_UMHex->fastme_l0_cost_bipred), 9, p_Vid->height >> 2, p_Vid->width >> 2);//for bipred + memory_size += get_mem3Ddistblk(&(p_UMHex->fastme_l1_cost_bipred), 9, p_Vid->height >> 2, p_Vid->width >> 2);//for bipred + } + + return memory_size; +} + +/*! +************************************************************************ +* \brief +* Free space for fast motion estimation +************************************************************************ +*/ +void UMHEX_free_mem(VideoParameters *p_Vid, InputParameters *p_Inp) +{ + UMHexStruct *p_UMHex = p_Vid->p_UMHex; + free_mem2D(p_UMHex->McostState); + + free_mem4Ddistblk(p_UMHex->fastme_ref_cost); + free_mem3Ddistblk(p_UMHex->fastme_l0_cost ); + free_mem3Ddistblk(p_UMHex->fastme_l1_cost); + free_mem2Ddistblk(p_UMHex->fastme_best_cost); + + free_mem2D(p_UMHex->SearchState); + free (p_UMHex->flag_intra); + if(p_Inp->BiPredMotionEstimation == 1) + { + free_mem3Ddistblk(p_UMHex->fastme_l0_cost_bipred);//for bipred + free_mem3Ddistblk(p_UMHex->fastme_l1_cost_bipred);//for bipred + } + free(p_UMHex); +} + +/*! +************************************************************************ +* \brief +* UMHEXIntegerPelBlockMotionSearch: fast pixel block motion search +* this algorithm is called UMHexagonS(see JVT-D016),which includes +* four steps with different kinds of search patterns +* \par Input: +* imgpel* orig_pic, // <-- original picture +* int ref, // <-- reference frame (0... or -1 (backward)) +* int pic_pix_x, // <-- absolute x-coordinate of regarded AxB block +* int pic_pix_y, // <-- absolute y-coordinate of regarded AxB block +* int blocktype, // <-- block type (1-16x16 ... 7-4x4) +* int pred_mv[2], // <-- motion vector predictor (x|y) in sub-pel units +* MotionVector *mv, // --> motion vector (x|y) - in sub-pel units +* int search_range, // <-- 1-d search range in sub-pel units +* int min_mcost, // <-- minimum motion cost (cost for center or huge value) +* int lambda_factor // <-- lagrangian parameter for determining motion cost +* \par +* Two macro definitions defined in this program: +* 1. EARLY_TERMINATION: early termination algrithm, refer to JVT-D016.doc +* 2. SEARCH_ONE_PIXEL: search one pixel in search range +* \author +* Main contributors: (see contributors.h for copyright, address and affiliation details) +* - Zhibo Chen +* - JianFeng Xu +* - Xiaozhong Xu +* \date : +* 2006.1 +************************************************************************ +*/ +distblk // ==> minimum motion cost after search +UMHEXIntegerPelBlockMotionSearch (Macroblock *currMB, // <-- current Macroblock + MotionVector *pred_mv, // < <-- motion vector predictor (x|y) in sub-pel units + MEBlock *mv_block, + distblk min_mcost, // < <-- minimum motion cost (cost for center or huge value) + int lambda_factor // < <-- lagrangian parameter for determining motion cost + ) +{ + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + InputParameters *p_Inp = currMB->p_Inp; + UMHexStruct *p_UMHex = p_Vid->p_UMHex; + + int blocktype = mv_block->blocktype; + short blocksize_x = mv_block->blocksize_x; // horizontal block size + short blocksize_y = mv_block->blocksize_y; // vertical block size + short pic_pix_x2 = mv_block->pos_x2; + short block_x = mv_block->block_x; + short block_y = mv_block->block_y; + + int list = mv_block->list; + int cur_list = list + currMB->list_offset; + short ref = mv_block->ref_idx; + StorablePicture *ref_picture = currSlice->listX[cur_list][ref]; + + MotionVector *mv = &mv_block->mv[list]; + MotionVector iMinNow, cand, center, pred, best = {0, 0}; + + int search_step; + int pos; + distblk mcost; + distblk *SAD_prediction = p_UMHex->fastme_best_cost[blocktype-1];//multi ref SAD prediction + int i, j, m; + float betaFourth_1,betaFourth_2; + int temp_Big_Hexagon_X[16];// temp for Big_Hexagon_X; + int temp_Big_Hexagon_Y[16];// temp for Big_Hexagon_Y; + distblk ET_Thred = p_UMHex->Median_Pred_Thd_MB[blocktype];//ET threshold in use + int search_range = mv_block->searchRange.max_x >> 2; + + short pic_pix_x = mv_block->pos_x_padded; + short pic_pix_y = mv_block->pos_y_padded; + pred.mv_x = pic_pix_x + pred_mv->mv_x; // predicted position x (in sub-pel units) + pred.mv_y = pic_pix_y + pred_mv->mv_y; // predicted position y (in sub-pel units) + center.mv_x = pic_pix_x + mv->mv_x; // center position x (in sub-pel units) + center.mv_y = pic_pix_y + mv->mv_y; // center position y (in sub-pel units) + + + + //////allocate memory for search state////////////////////////// + memset(p_UMHex->McostState[0],0,(2*p_Inp->search_range[p_Vid->view_id]+1)*(2*p_Inp->search_range[p_Vid->view_id]+1)); + + + //check the center median predictor + + cand = center; + mcost = mv_cost (p_Vid, lambda_factor, &cand, &pred); + + mcost += mv_block->computePredFPel(ref_picture, mv_block, min_mcost - mcost, &cand); + + p_UMHex->McostState[search_range][search_range] = 1; + if (mcost < min_mcost) + { + min_mcost = mcost; + best = cand; + } + + iMinNow = best; + + for (m = 0; m < 4; m++) + { + cand.mv_x = iMinNow.mv_x + Diamond[m].mv_x; + cand.mv_y = iMinNow.mv_y + Diamond[m].mv_y; + SEARCH_ONE_PIXEL + } + + if(center.mv_x != pic_pix_x || center.mv_y != pic_pix_y) + { + cand.mv_x = pic_pix_x ; + cand.mv_y = pic_pix_y ; + SEARCH_ONE_PIXEL + iMinNow = best; + for (m = 0; m < 4; m++) + { + cand.mv_x = iMinNow.mv_x + Diamond[m].mv_x; + cand.mv_y = iMinNow.mv_y + Diamond[m].mv_y; + SEARCH_ONE_PIXEL + } + } + /***********************************init process*************************/ + //for multi ref + if(ref>0 && currSlice->structure == FRAME && min_mcost > ET_Thred && SAD_prediction[pic_pix_x2] < p_UMHex->Multi_Ref_Thd_MB[blocktype]) + goto terminate_step; + + //ET_Thd1: early termination for low motion case + if( min_mcost < ET_Thred) + { + goto terminate_step; + } + else // hybrid search for main search loop + { + /****************************(MV and SAD prediction)********************************/ + UMHEX_setup(currMB, ref, mv_block->list, block_y, block_x, blocktype, currSlice->all_mv ); + ET_Thred = p_UMHex->Big_Hexagon_Thd_MB[blocktype]; // ET_Thd2: early termination Threshold for strong motion + + // Threshold defined for EARLY_TERMINATION + if (p_UMHex->pred_SAD == 0) + { + betaFourth_1=0; + betaFourth_2=0; + } + else + { + betaFourth_1 = p_UMHex->Bsize[blocktype]/((float)p_UMHex->pred_SAD * p_UMHex->pred_SAD)-p_UMHex->AlphaFourth_1[blocktype]; + betaFourth_2 = p_UMHex->Bsize[blocktype]/((float)p_UMHex->pred_SAD * p_UMHex->pred_SAD)-p_UMHex->AlphaFourth_2[blocktype]; + + } + /*********************************************end of init ***********************************************/ + } + // first_step: initial start point prediction + + if(blocktype>1) + { + cand.mv_x = (short) (pic_pix_x + (p_UMHex->pred_MV_uplayer[0] / 4) * 4); + cand.mv_y = (short) (pic_pix_y + (p_UMHex->pred_MV_uplayer[1] / 4) * 4); + SEARCH_ONE_PIXEL + } + + + //prediction using mV of last ref moiton vector + if(p_UMHex->pred_MV_ref_flag == 1) //Notes: for interlace case, ref==1 should be added + { + cand.mv_x = (short) (pic_pix_x + (p_UMHex->pred_MV_ref[0] / 4) * 4); + cand.mv_y = (short) (pic_pix_y + (p_UMHex->pred_MV_ref[1] / 4) * 4); + SEARCH_ONE_PIXEL + } + // Small local search + iMinNow = best; + for (m = 0; m < 4; m++) + { + cand.mv_x = iMinNow.mv_x + Diamond[m].mv_x; + cand.mv_y = iMinNow.mv_y + Diamond[m].mv_y; + SEARCH_ONE_PIXEL + } + + //early termination algorithm, refer to JVT-G016 + EARLY_TERMINATION + + if(blocktype>6) + goto fourth_1_step; + else + goto sec_step; + +sec_step: //Unsymmetrical-cross search + iMinNow = best; + + for(i = 4; i < search_range << 2; i+=8) + { + search_step = i; + cand.mv_x = (short) (iMinNow.mv_x + search_step); + cand.mv_y = iMinNow.mv_y ; + SEARCH_ONE_PIXEL + cand.mv_x = (short) (iMinNow.mv_x - search_step); + cand.mv_y = iMinNow.mv_y ; + SEARCH_ONE_PIXEL + } + for(i = 4; i < (search_range << 1);i+=8) + { + search_step = i; + cand.mv_x = iMinNow.mv_x ; + cand.mv_y = (short) (iMinNow.mv_y + search_step); + SEARCH_ONE_PIXEL + cand.mv_x = iMinNow.mv_x ; + cand.mv_y = (short) (iMinNow.mv_y - search_step); + SEARCH_ONE_PIXEL + } + + + //early termination alogrithm, refer to JVT-G016 + EARLY_TERMINATION + + iMinNow = best; + + //third_step: // Uneven Multi-Hexagon-grid Search + //sub step 1: 5x5 squre search + for(pos=1;pos<25;pos++) + { + cand.mv_x = iMinNow.mv_x + p_Vid->spiral_qpel_search[pos].mv_x; + cand.mv_y = iMinNow.mv_y + p_Vid->spiral_qpel_search[pos].mv_y; + SEARCH_ONE_PIXEL + } + + //early termination alogrithm, refer to JVT-G016 + EARLY_TERMINATION + + //sub step 2: Multi-Hexagon-grid search + memcpy(temp_Big_Hexagon_X,Big_Hexagon_X,64); + memcpy(temp_Big_Hexagon_Y,Big_Hexagon_Y,64); + for(i=1;i<=(search_range >> 2); i++) + { + + for (m = 0; m < 16; m++) + { + cand.mv_x = (short) (iMinNow.mv_x + temp_Big_Hexagon_X[m]); + cand.mv_y = (short) (iMinNow.mv_y + temp_Big_Hexagon_Y[m]); + temp_Big_Hexagon_X[m] += Big_Hexagon_X[m]; + temp_Big_Hexagon_Y[m] += Big_Hexagon_Y[m]; + + SEARCH_ONE_PIXEL + } + // ET_Thd2: early termination Threshold for strong motion + if(min_mcost < ET_Thred) + { + goto terminate_step; + } + } + + + //fourth_step: //Extended Hexagon-based Search + // the fourth step with a small search pattern +fourth_1_step: //sub step 1: small Hexagon search + for(i = 0; i < search_range; i++) + { + iMinNow = best; + for (m = 0; m < 6; m++) + { + cand.mv_x = iMinNow.mv_x + Hexagon[m].mv_x; + cand.mv_y = iMinNow.mv_y + Hexagon[m].mv_y; + SEARCH_ONE_PIXEL + } + + if (best.mv_x == iMinNow.mv_x && best.mv_y == iMinNow.mv_y) + { + break; + } + } +fourth_2_step: //sub step 2: small Diamond search + + for(i = 0; i < search_range; i++) + { + iMinNow = best; + for (m = 0; m < 4; m++) + { + cand.mv_x = iMinNow.mv_x + Diamond[m].mv_x; + cand.mv_y = iMinNow.mv_y + Diamond[m].mv_y; + SEARCH_ONE_PIXEL + } + if(best.mv_x == iMinNow.mv_x && best.mv_y == iMinNow.mv_y) + break; + } + +terminate_step: + + // store SAD infomation for prediction + //FAST MOTION ESTIMATION. ZHIBO CHEN 2003.3 + for (i=0; i < (blocksize_x>>2); i++) + { + for (j=0; j < (blocksize_y>>2); j++) + { + if(mv_block->list == 0) + { + p_UMHex->fastme_ref_cost[ref][blocktype][block_y+j][block_x+i] = min_mcost; + if (ref==0) + p_UMHex->fastme_l0_cost[blocktype][(currMB->block_y)+block_y+j][(currMB->block_x)+block_x+i] = min_mcost; + } + else + { + p_UMHex->fastme_l1_cost[blocktype][(currMB->block_y)+block_y+j][(currMB->block_x)+block_x+i] = min_mcost; + } + } + } + //for multi ref SAD prediction + if ((ref==0) || (SAD_prediction[pic_pix_x2] > min_mcost)) + SAD_prediction[pic_pix_x2] = min_mcost; + + mv->mv_x = (short) (best.mv_x - pic_pix_x); + mv->mv_y = (short) (best.mv_y - pic_pix_y); + return min_mcost; +} + +distblk // ==> minimum motion cost after search +UMHEXSubPelBlockMotionSearch (Macroblock *currMB, // <-- current Macroblock + MotionVector *pred_mv, // < <-- motion vector predictor (x|y) in sub-pel units + MEBlock *mv_block, + distblk min_mcost, // <-- minimum motion cost (cost for center or huge value) + int lambda_factor // <-- lagrangian parameter for determining motion cost + ) +{ + VideoParameters *p_Vid = currMB->p_Vid; + Slice *currSlice = currMB->p_Slice; + UMHexStruct *p_UMHex = p_Vid->p_UMHex; + static const MotionVector DiamondQ[4] = {{-1, 0}, { 0, 1}, { 1, 0}, { 0, -1}}; + distblk mcost; + MotionVector cand, iMinNow, currmv = {0, 0}, cand_pad; + + int list = mv_block->list; + int list_offset = currMB->list_offset; + short ref = mv_block->ref_idx; + MotionVector *mv = &mv_block->mv[list]; + + StorablePicture *ref_picture = currSlice->listX[list+list_offset][ref]; + + int dynamic_search_range = 3, i; + int m; + int pred_frac_mv_x,pred_frac_mv_y,abort_search; + + //int pred_frac_up_mv_x, pred_frac_up_mv_y; + + pred_frac_mv_x = (pred_mv->mv_x - mv->mv_x) & 0x03; + pred_frac_mv_y = (pred_mv->mv_y - mv->mv_y) & 0x03; + + //pred_frac_up_mv_x = (p_UMHex->pred_MV_uplayer[0] - mv->mv_x) & 0x03; + //pred_frac_up_mv_y = (p_UMHex->pred_MV_uplayer[1] - mv->mv_y) & 0x03; + + + memset(p_UMHex->SearchState[0], 0,(2 * dynamic_search_range + 1)*(2 * dynamic_search_range + 1)); + + if( !p_Vid->start_me_refinement_hp ) + { + p_UMHex->SearchState[dynamic_search_range][dynamic_search_range] = 1; + cand = *mv; + mcost = mv_cost (p_Vid, lambda_factor, &cand, pred_mv); + cand_pad = pad_MVs (cand, mv_block); //cand = pad_MVs (cand, mv_block); + mcost += mv_block->computePredQPel( ref_picture, mv_block, min_mcost - mcost, &cand_pad); //&cand); + + if (mcost < min_mcost) + { + min_mcost = mcost; + currmv = cand; + } + } + else + { + p_UMHex->SearchState[dynamic_search_range][dynamic_search_range] = 1; + currmv = *mv; + } + + if(pred_frac_mv_x!=0 || pred_frac_mv_y!=0) + { + cand.mv_x = (short) (mv->mv_x + pred_frac_mv_x); + cand.mv_y = (short) (mv->mv_y + pred_frac_mv_y); + mcost = mv_cost (p_Vid, lambda_factor, &cand, pred_mv); + p_UMHex->SearchState[cand.mv_y -mv->mv_y + dynamic_search_range][cand.mv_x - mv->mv_x + dynamic_search_range] = 1; + cand_pad = pad_MVs (cand, mv_block); //cand = pad_MVs (cand, mv_block); + + mcost += mv_block->computePredQPel( ref_picture, mv_block, min_mcost - mcost, &cand_pad); //&cand); + + if (mcost < min_mcost) + { + min_mcost = mcost; + currmv = cand; + } + } + + iMinNow = currmv; + + for(i = 0; i < dynamic_search_range; i++) + { + abort_search=1; + for (m = 0; m < 4; m++) + { + cand.mv_x = iMinNow.mv_x + DiamondQ[m].mv_x; + cand.mv_y = iMinNow.mv_y + DiamondQ[m].mv_y; + + if(iabs(cand.mv_x - mv->mv_x) <= dynamic_search_range && iabs(cand.mv_y - mv->mv_y) <= dynamic_search_range) + { + if(!p_UMHex->SearchState[cand.mv_y -mv->mv_y + dynamic_search_range][cand.mv_x -mv->mv_x + dynamic_search_range]) + { + p_UMHex->SearchState[cand.mv_y -mv->mv_y + dynamic_search_range][cand.mv_x -mv->mv_x + dynamic_search_range] = 1; + mcost = mv_cost (p_Vid, lambda_factor, &cand, pred_mv); + cand_pad = pad_MVs (cand, mv_block); //cand = pad_MVs (cand, mv_block); + + mcost += mv_block->computePredQPel( ref_picture, mv_block, min_mcost - mcost, &cand_pad); // &cand); + if (mcost < min_mcost) + { + min_mcost = mcost; + currmv = cand; + abort_search = 0; + } + } + } + } + iMinNow = currmv; + if(abort_search) + { + break; + } + } + + *mv = currmv; + + //===== return minimum motion cost ===== + return min_mcost; +} + +distblk // ==> minimum motion cost after search +UMHEXSubPelBlockME (Macroblock *currMB, // <-- Current Macroblock + MotionVector *pred_mv, // <-- motion vector predictor (x|y) in sub-pel units + MEBlock *mv_block, + distblk min_mcost, // <-- minimum motion cost (cost for center or huge value) + int* lambda + ) +{ + if(mv_block->blocktype >3) + { + min_mcost = UMHEXSubPelBlockMotionSearch (currMB, pred_mv, mv_block, min_mcost, lambda[Q_PEL]); + } + else + { + min_mcost = sub_pel_motion_estimation (currMB, pred_mv, mv_block, min_mcost, lambda); + } + + return min_mcost; +} + +/*! +************************************************************************ +* \brief +* Functions for SAD prediction of intra block cases. +* 1. void UMHEX_decide_intrabk_SAD() judges the block coding type(intra/inter) +* of neibouring blocks +* 2. void UMHEX_skip_intrabk_SAD() set the SAD to zero if neigouring block coding +* type is intra +* \date +* 2003.4 +************************************************************************ +*/ +void UMHEX_decide_intrabk_SAD(Macroblock *currMB) +{ + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + UMHexStruct *p_UMHex = p_Vid->p_UMHex; + if (currSlice->slice_type != I_SLICE && currSlice->slice_type != SI_SLICE) + { + if (currMB->pix_x == 0 && currMB->pix_y == 0) + { + p_UMHex->flag_intra_SAD = 0; + } + else if (currMB->pix_x == 0) + { + p_UMHex->flag_intra_SAD = p_UMHex->flag_intra[(currMB->pix_x)>>4]; + } + else if (currMB->pix_y == 0) + { + p_UMHex->flag_intra_SAD = p_UMHex->flag_intra[((currMB->pix_x)>>4)-1]; + } + else + { + p_UMHex->flag_intra_SAD = ((p_UMHex->flag_intra[(currMB->pix_x)>>4])||(p_UMHex->flag_intra[((currMB->pix_x)>>4)-1])||(p_UMHex->flag_intra[((currMB->pix_x)>>4)+1])) ; + } + } + return; +} + +void UMHEX_skip_intrabk_SAD(Macroblock *currMB, int ref_max) +{ + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + UMHexStruct *p_UMHex = p_Vid->p_UMHex; + int i,j,k, ref; + if (p_Vid->number > 0) + p_UMHex->flag_intra[(currMB->pix_x)>>4] = (currMB->best_mode == 9 || currMB->best_mode == 10) ? 1:0; + + if (currSlice->slice_type != I_SLICE && currSlice->slice_type != SI_SLICE && (currMB->best_mode == 9 || currMB->best_mode == 10)) + { + for (k=0; k < 9;k++) + { + for (j=0; j < 4; j++) + { + for (i=0; i < 4; i++) + { + p_UMHex->fastme_l0_cost[k][j][i] = 0; + p_UMHex->fastme_l1_cost[k][j][i] = 0; + + for (ref=0; ref < ref_max;ref++) + { + p_UMHex->fastme_ref_cost[ref][k][j][i] = 0; + } + } + } + } + + } + return; +} + + +void UMHEX_setup(Macroblock *currMB, short ref, int list, int block_y, int block_x, int blocktype, MotionVector *****all_mv) +{ + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + UMHexStruct *p_UMHex = p_Vid->p_UMHex; + + int N_Bframe=0; + int n_Bframe=0; + int temp_blocktype = 0; + int indication_blocktype[8]={0,0,1,1,2,4,4,5}; + InputParameters *p_Inp = currMB->p_Inp; + N_Bframe = p_Inp->NumberBFrames; + n_Bframe =(N_Bframe) ? (p_Vid->p_Stats->frame_ctr[B_SLICE]%(N_Bframe+1)): 0; + + + /**************************** MV prediction **********************/ + //MV uplayer prediction + if (blocktype>1) + { + temp_blocktype = indication_blocktype[blocktype]; + p_UMHex->pred_MV_uplayer[0] = all_mv[list][ref][temp_blocktype][block_y][block_x].mv_x; + p_UMHex->pred_MV_uplayer[1] = all_mv[list][ref][temp_blocktype][block_y][block_x].mv_y; + } + + + //MV ref-frame prediction + p_UMHex->pred_MV_ref_flag = 0; + if(list==0) + { + if (p_Vid->field_picture) + { + if ( ref > 1) + { + p_UMHex->pred_MV_ref[0] = all_mv[0][ref-2][blocktype][block_y][block_x].mv_x; + p_UMHex->pred_MV_ref[0] = (int)(p_UMHex->pred_MV_ref[0]*((ref>>1)+1)/(float)((ref>>1))); + p_UMHex->pred_MV_ref[1] = all_mv[0][ref-2][blocktype][block_y][block_x].mv_y; + p_UMHex->pred_MV_ref[1] = (int)(p_UMHex->pred_MV_ref[1]*((ref>>1)+1)/(float)((ref>>1))); + p_UMHex->pred_MV_ref_flag = 1; + } + if (currSlice->slice_type == B_SLICE && (ref==0 || ref==1) ) + { + p_UMHex->pred_MV_ref[0] =(int) (all_mv[1][0][blocktype][block_y][block_x].mv_x * (-n_Bframe)/(N_Bframe-n_Bframe+1.0f)); + p_UMHex->pred_MV_ref[1] =(int) (all_mv[1][0][blocktype][block_y][block_x].mv_y * (-n_Bframe)/(N_Bframe-n_Bframe+1.0f)); + p_UMHex->pred_MV_ref_flag = 1; + } + } + else //frame case + { + if ( ref > 0) + { + p_UMHex->pred_MV_ref[0] = all_mv[0][ref-1][blocktype][block_y][block_x].mv_x; + p_UMHex->pred_MV_ref[0] = (int)(p_UMHex->pred_MV_ref[0]*(ref+1)/(float)(ref)); + p_UMHex->pred_MV_ref[1] = all_mv[0][ref-1][blocktype][block_y][block_x].mv_y; + p_UMHex->pred_MV_ref[1] = (int)(p_UMHex->pred_MV_ref[1]*(ref+1)/(float)(ref)); + p_UMHex->pred_MV_ref_flag = 1; + } + if (currSlice->slice_type == B_SLICE && (ref==0)) //B frame forward prediction, first ref + { + p_UMHex->pred_MV_ref[0] =(int) (all_mv[1][0][blocktype][block_y][block_x].mv_x * (-n_Bframe)/(N_Bframe-n_Bframe+1.0f)); + p_UMHex->pred_MV_ref[1] =(int) (all_mv[1][0][blocktype][block_y][block_x].mv_y * (-n_Bframe)/(N_Bframe-n_Bframe+1.0f)); + p_UMHex->pred_MV_ref_flag = 1; + } + } + } + /******************************SAD prediction**********************************/ + if (list==0 && ref>0) //pred_SAD_ref + { + + if (p_UMHex->flag_intra_SAD) //add this for irregular motion + { + p_UMHex->pred_SAD = 0; + } + else + { + if (p_Vid->field_picture) + { + if (ref > 1) + { + p_UMHex->pred_SAD = p_UMHex->fastme_ref_cost[ref-2][blocktype][block_y][block_x]; + } + else + { + p_UMHex->pred_SAD = p_UMHex->fastme_ref_cost[0][blocktype][block_y][block_x]; + } + } + else + { + p_UMHex->pred_SAD = p_UMHex->fastme_ref_cost[ref-1][blocktype][block_y][block_x]; + } + + } + } + else if (blocktype>1) // pred_SAD_uplayer + { + if (p_UMHex->flag_intra_SAD) + { + p_UMHex->pred_SAD = 0; + } + else + { + p_UMHex->pred_SAD = (list==1) ? (p_UMHex->fastme_l1_cost[temp_blocktype][(currMB->block_y)+block_y][(currMB->block_x)+block_x]) : (p_UMHex->fastme_l0_cost[temp_blocktype][(currMB->block_y)+block_y][(currMB->block_x)+block_x]); + p_UMHex->pred_SAD /= 2; + } + } + else p_UMHex->pred_SAD = 0 ; // pred_SAD_space + +} + +/*! +************************************************************************ +* \brief +* UMHEXBipredIntegerPelBlockMotionSearch: fast pixel block motion search for bipred mode +* this algrithm is called UMHexagonS(see JVT-D016),which includes +* four steps with different kinds of search patterns +* \author +* Main contributors: (see contributors.h for copyright, address and affiliation details) +* - Zhibo Chen +* - JianFeng Xu +* - Xiaozhong Xu +* \date : +* 2006.1 +************************************************************************ +*/ +distblk // ==> minimum motion cost after search +UMHEXBipredIntegerPelBlockMotionSearch (Macroblock *currMB, // <-- current Macroblock + int list, // <-- current reference list + MotionVector *pred_mv1, // <-- motion vector predictor (x|y) in sub-pel units + MotionVector *pred_mv2, // <-- motion vector predictor (x|y) in sub-pel units + MotionVector *mv1, // <--> in: search center (x|y) / out: motion vector (x|y) - in sub-pel units + MotionVector *mv2, // <--> in: search center (x|y) + MEBlock *mv_block, // <-- motion vector information + int search_range, // <-- 1-d search range in sub-pel units + distblk min_mcost, // <-- minimum motion cost (cost for center or huge value) + int lambda_factor // <-- lagrangian parameter for determining motion cost + ) +{ + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + InputParameters *p_Inp = currMB->p_Inp; + UMHexStruct *p_UMHex = p_Vid->p_UMHex; + int temp_Big_Hexagon_X[16];// = Big_Hexagon_X; + int temp_Big_Hexagon_Y[16];// = Big_Hexagon_Y; + + int search_step; + int i,m,j; + float betaFourth_1,betaFourth_2; + int pos; + distblk mcost; + short blocktype = mv_block->blocktype; + short blocksize_x = mv_block->blocksize_x; // horizontal block size + short blocksize_y = mv_block->blocksize_y; // vertical block size + + short pic_pix_x = mv_block->pos_x_padded; + short pic_pix_y = mv_block->pos_y_padded; + + short block_x = mv_block->block_x; + short block_y = mv_block->block_y; + distblk ET_Thred = p_UMHex->Median_Pred_Thd_MB[blocktype]; + short ref = mv_block->ref_idx; + + StorablePicture *ref_picture1 = currSlice->listX[list + currMB->list_offset][ref]; + StorablePicture *ref_picture2 = currSlice->listX[list == 0 ? 1 + currMB->list_offset: currMB->list_offset][ 0 ]; + + MotionVector iMinNow, best, cand; + + MotionVector pred1 = pad_MVs(*pred_mv1, mv_block); // predicted position x (in sub-pel units) + MotionVector pred2 = pad_MVs(*pred_mv2, mv_block); // predicted position x (in sub-pel units) + MotionVector center1 = pad_MVs(*mv1, mv_block); // predicted position x (in sub-pel units) + MotionVector center2 = pad_MVs(*mv2, mv_block); // predicted position x (in sub-pel units) + + + search_range >>= 2; + ////////////////////////////////////////////////////////////////////////// + + //////allocate memory for search state////////////////////////// + memset(p_UMHex->McostState[0],0,(2*search_range+1)*(2*search_range+1)); + + //check the center median predictor + best = cand = center2; + mcost = mv_cost (p_Vid, lambda_factor, ¢er1, &pred1); + mcost += mv_cost (p_Vid, lambda_factor, &cand, &pred2); + mcost += mv_block->computeBiPredFPel(ref_picture1, ref_picture2, mv_block, DISTBLK_MAX-mcost, ¢er1, &cand); + + p_UMHex->McostState[search_range][search_range] = 1; + + if (mcost < min_mcost) + { + min_mcost = mcost; + best = cand; + } + + iMinNow = best; + for (m = 0; m < 4; m++) + { + cand.mv_x = iMinNow.mv_x + Diamond[m].mv_x; + cand.mv_y = iMinNow.mv_y + Diamond[m].mv_y; + SEARCH_ONE_PIXEL_BIPRED; + } + + if(center2.mv_x != pic_pix_x || center2.mv_y != pic_pix_y) + { + cand.mv_x = pic_pix_x ; + cand.mv_y = pic_pix_y ; + SEARCH_ONE_PIXEL_BIPRED; + + iMinNow = best; + + for (m = 0; m < 4; m++) + { + cand.mv_x = iMinNow.mv_x + Diamond[m].mv_x; + cand.mv_y = iMinNow.mv_y + Diamond[m].mv_y; + SEARCH_ONE_PIXEL_BIPRED; + } + } + /***********************************init process*************************/ + + if( min_mcost < ET_Thred) + { + goto terminate_step; + } + else + { + int N_Bframe=0; + int n_Bframe=0; + MotionVector *****bipred_mv = currSlice->bipred_mv[list]; + N_Bframe = p_Inp->NumberBFrames; + n_Bframe = p_Vid->p_Stats->frame_ctr[B_SLICE]%(N_Bframe+1); + + + /**************************** MV prediction **********************/ + //MV uplayer prediction + // non for bipred mode + + //MV ref-frame prediction + + if(list==0) + { + if (p_Vid->field_picture) + { + p_UMHex->pred_MV_ref[0] =(int) (bipred_mv[1][0][blocktype][block_y][block_x].mv_x * (-n_Bframe)/(N_Bframe-n_Bframe+1.0f)); + p_UMHex->pred_MV_ref[1] =(int) (bipred_mv[1][0][blocktype][block_y][block_x].mv_y * (-n_Bframe)/(N_Bframe-n_Bframe+1.0f)); + } + else //frame case + { + p_UMHex->pred_MV_ref[0] =(int) (bipred_mv[1][0][blocktype][block_y][block_x].mv_x * (-n_Bframe)/(N_Bframe-n_Bframe+1.0f)); + p_UMHex->pred_MV_ref[1] =(int) (bipred_mv[1][0][blocktype][block_y][block_x].mv_y * (-n_Bframe)/(N_Bframe-n_Bframe+1.0f)); + } + } + /******************************SAD prediction**********************************/ + p_UMHex->pred_SAD =distblkmin(distblkmin(p_UMHex->SAD_a,p_UMHex->SAD_b),p_UMHex->SAD_c); // pred_SAD_space + ET_Thred = p_UMHex->Big_Hexagon_Thd_MB[blocktype]; + + ///////Threshold defined for early termination/////////////////// + if (p_UMHex->pred_SAD == 0) + { + betaFourth_1=0; + betaFourth_2=0; + } + else + { + betaFourth_1 = p_UMHex->Bsize[blocktype]/(p_UMHex->pred_SAD * p_UMHex->pred_SAD)-p_UMHex->AlphaFourth_1[blocktype]; + betaFourth_2 = p_UMHex->Bsize[blocktype]/(p_UMHex->pred_SAD * p_UMHex->pred_SAD)-p_UMHex->AlphaFourth_2[blocktype]; + } + } + + /***********************************end of init *************************/ + + + + // first_step: initial start point prediction + //prediction using mV of last ref moiton vector + if(list == 0) + { + cand.mv_x = (short) (pic_pix_x + (p_UMHex->pred_MV_ref[0] / 4) * 4); + cand.mv_y = (short) (pic_pix_y + (p_UMHex->pred_MV_ref[1] / 4) * 4); + SEARCH_ONE_PIXEL_BIPRED; + } + + + //small local search + iMinNow = best; + for (m = 0; m < 4; m++) + { + cand.mv_x = iMinNow.mv_x + Diamond[m].mv_x; + cand.mv_y = iMinNow.mv_y + Diamond[m].mv_y; + SEARCH_ONE_PIXEL_BIPRED; + } + + //early termination alogrithm, refer to JVT-G016 + EARLY_TERMINATION; + + + //sec_step: //Unsymmetrical-cross search + iMinNow = best; + + for(i = 1; i < search_range; i+=2) + { + search_step = i; + cand.mv_x = (short) (iMinNow.mv_x + search_step); + cand.mv_y = iMinNow.mv_y ; + SEARCH_ONE_PIXEL_BIPRED; + cand.mv_x = (short) (iMinNow.mv_x - search_step); + SEARCH_ONE_PIXEL_BIPRED; + } + + for(i = 1; i < (search_range >> 1);i+=2) + { + search_step = i; + cand.mv_x = iMinNow.mv_x ; + cand.mv_y = (short) (iMinNow.mv_y + search_step); + SEARCH_ONE_PIXEL_BIPRED; + cand.mv_y = (short) (iMinNow.mv_y - search_step); + SEARCH_ONE_PIXEL_BIPRED; + } + //early termination alogrithm, refer to JVT-G016 + EARLY_TERMINATION; + + //third_step: // Uneven Multi-Hexagon-grid Search + iMinNow = best; + //sub step1: 5x5 square search + for(pos=1;pos<25;pos++) + { + cand.mv_x = iMinNow.mv_x + p_Vid->spiral_qpel_search[pos].mv_x; + cand.mv_y = iMinNow.mv_y + p_Vid->spiral_qpel_search[pos].mv_y; + SEARCH_ONE_PIXEL_BIPRED; + } + + //early termination alogrithm, refer to JVT-G016 + EARLY_TERMINATION; //added back by xxz + + //sub step2: multi-grid-hexagon-search + memcpy(temp_Big_Hexagon_X,Big_Hexagon_X,64); + memcpy(temp_Big_Hexagon_Y,Big_Hexagon_Y,64); + for(i=1;i<=(p_Inp->search_range[p_Vid->view_id] >> 2); i++) + { + + for (m = 0; m < 16; m++) + { + cand.mv_x = (short) (iMinNow.mv_x + temp_Big_Hexagon_X[m]); + cand.mv_y = (short) (iMinNow.mv_y + temp_Big_Hexagon_Y[m]); + temp_Big_Hexagon_X[m] += Big_Hexagon_X[m]; + temp_Big_Hexagon_Y[m] += Big_Hexagon_Y[m]; + + SEARCH_ONE_PIXEL_BIPRED; + } + if(min_mcost < ET_Thred) + { + goto terminate_step; + + } + } + //fourth step: Local Refinement: Extended Hexagon-based Search +fourth_1_step: + + for(i=0; i < search_range; i++) + { + iMinNow = best; + for (m = 0; m < 6; m++) + { + cand.mv_x = iMinNow.mv_x + Hexagon[m].mv_x; + cand.mv_y = iMinNow.mv_y + Hexagon[m].mv_y; + SEARCH_ONE_PIXEL_BIPRED; + } + if(best.mv_x == iMinNow.mv_x && best.mv_y == iMinNow.mv_y) + break; + } +fourth_2_step: + + for(i = 0; i < search_range; i++) + { + iMinNow = best; + for (m = 0; m < 4; m++) + { + cand.mv_x = iMinNow.mv_x + Diamond[m].mv_x; + cand.mv_y = iMinNow.mv_y + Diamond[m].mv_y; + SEARCH_ONE_PIXEL_BIPRED; + } + if(best.mv_x == iMinNow.mv_x && best.mv_y == iMinNow.mv_y) + break; + } + +terminate_step: + for (i=0; i < (blocksize_x>>2); i++) + { + for (j=0; j < (blocksize_y>>2); j++) + { + if(list == 0) + { + p_UMHex->fastme_l0_cost_bipred[blocktype][(currMB->block_y)+block_y+j][(currMB->block_x)+block_x+i] = min_mcost; + } + else + { + p_UMHex->fastme_l1_cost_bipred[blocktype][(currMB->block_y)+block_y+j][(currMB->block_x)+block_x+i] = min_mcost; + } + } + } + + mv1->mv_x = (short) (best.mv_x - pic_pix_x); + mv1->mv_y = (short) (best.mv_y - pic_pix_y); + + return min_mcost; +} + +/*! +************************************************************************ +* \brief +* Set motion vector predictor +************************************************************************ +*/ +void UMHEXSetMotionVectorPredictor (Macroblock *currMB, + MotionVector *pmv, + struct pic_motion_params **mv_info, + short ref_frame, + int list, + int mb_x, + int mb_y, + int blockshape_x, + int blockshape_y, + MEBlock *mv_block) +{ + VideoParameters *p_Vid = currMB->p_Vid; + InputParameters *p_Inp = currMB->p_Inp; + UMHexStruct *p_UMHex = p_Vid->p_UMHex; + int mv_a, mv_b, mv_c; + short pred_vec=0; + int mvPredType, rFrameL, rFrameU, rFrameUR; + int hv; + + PixelPos block_a, block_b, block_c, block_d; + + // added for bipred mode + distblk *** fastme_l0_cost_flag = (p_UMHex->bipred_flag ? p_UMHex->fastme_l0_cost_bipred : p_UMHex->fastme_l0_cost); + distblk *** fastme_l1_cost_flag = (p_UMHex->bipred_flag ? p_UMHex->fastme_l1_cost_bipred : p_UMHex->fastme_l1_cost); + //Dynamic Search Range + + int dsr_temp_search_range[2]; + int dsr_mv_avail, dsr_mv_max, dsr_mv_sum, dsr_small_search_range; + int *mb_size = p_Vid->mb_size[IS_LUMA]; + + // neighborhood SAD init + p_UMHex->SAD_a = 0; + p_UMHex->SAD_b = 0; + p_UMHex->SAD_c = 0; + p_UMHex->SAD_d = 0; + + get4x4Neighbour(currMB, mb_x - 1 , mb_y , mb_size, &block_a); + get4x4Neighbour(currMB, mb_x , mb_y - 1, mb_size, &block_b); + get4x4Neighbour(currMB, mb_x + blockshape_x, mb_y - 1, mb_size, &block_c); + get4x4Neighbour(currMB, mb_x - 1 , mb_y - 1, mb_size, &block_d); + + if (mb_y > 0) + { + if (mb_x < 8) // first column of 8x8 blocks + { + if (mb_y==8) + { + if (blockshape_x == 16) block_c.available = 0; + } + else + { + if (mb_x+blockshape_x == 8) block_c.available = 0; + } + } + else + { + if (mb_x+blockshape_x == 16) block_c.available = 0; + } + } + + if (!block_c.available) + { + block_c=block_d; + } + + mvPredType = MVPRED_MEDIAN; + + if (!p_Vid->mb_aff_frame_flag) + { + rFrameL = block_a.available ? mv_info[block_a.pos_y][block_a.pos_x].ref_idx[list] : -1; + rFrameU = block_b.available ? mv_info[block_b.pos_y][block_b.pos_x].ref_idx[list] : -1; + rFrameUR = block_c.available ? mv_info[block_c.pos_y][block_c.pos_x].ref_idx[list] : -1; + } + else + { + if (p_Vid->mb_data[currMB->mbAddrX].mb_field) + { + rFrameL = block_a.available + ? (p_Vid->mb_data[block_a.mb_addr].mb_field + ? mv_info[block_a.pos_y][block_a.pos_x].ref_idx[list] + : mv_info[block_a.pos_y][block_a.pos_x].ref_idx[list] * 2) : -1; + rFrameU = block_b.available + ? (p_Vid->mb_data[block_b.mb_addr].mb_field + ? mv_info[block_b.pos_y][block_b.pos_x].ref_idx[list] + : mv_info[block_b.pos_y][block_b.pos_x].ref_idx[list] * 2) : -1; + rFrameUR = block_c.available + ? (p_Vid->mb_data[block_c.mb_addr].mb_field + ? mv_info[block_c.pos_y][block_c.pos_x].ref_idx[list] + : mv_info[block_c.pos_y][block_c.pos_x].ref_idx[list] * 2) : -1; + } + else + { + rFrameL = block_a.available + ? (p_Vid->mb_data[block_a.mb_addr].mb_field + ? mv_info[block_a.pos_y][block_a.pos_x].ref_idx[list] >>1 + : mv_info[block_a.pos_y][block_a.pos_x].ref_idx[list]) : -1; + rFrameU = block_b.available ? + p_Vid->mb_data[block_b.mb_addr].mb_field ? + mv_info[block_b.pos_y][block_b.pos_x].ref_idx[list] >>1: + mv_info[block_b.pos_y][block_b.pos_x].ref_idx[list] : + -1; + rFrameUR = block_c.available ? + p_Vid->mb_data[block_c.mb_addr].mb_field ? + mv_info[block_c.pos_y][block_c.pos_x].ref_idx[list] >>1: + mv_info[block_c.pos_y][block_c.pos_x].ref_idx[list] : + -1; + } + } + + /* Prediction if only one of the neighbors uses the reference frame + * we are checking + */ + if(rFrameL == ref_frame && rFrameU != ref_frame && rFrameUR != ref_frame) mvPredType = MVPRED_L; + else if(rFrameL != ref_frame && rFrameU == ref_frame && rFrameUR != ref_frame) mvPredType = MVPRED_U; + else if(rFrameL != ref_frame && rFrameU != ref_frame && rFrameUR == ref_frame) mvPredType = MVPRED_UR; + // Directional predictions + if(blockshape_x == 8 && blockshape_y == 16) + { + if(mb_x == 0) + { + if(rFrameL == ref_frame) + mvPredType = MVPRED_L; + } + else + { + if( rFrameUR == ref_frame) + mvPredType = MVPRED_UR; + } + } + else if(blockshape_x == 16 && blockshape_y == 8) + { + if(mb_y == 0) + { + if(rFrameU == ref_frame) + mvPredType = MVPRED_U; + } + else + { + if(rFrameL == ref_frame) + mvPredType = MVPRED_L; + } + } + + // neighborhood SAD prediction + if((p_Inp->UMHexDSR == 1 || p_Inp->BiPredMotionEstimation == 1)) + { + p_UMHex->SAD_a = block_a.available ? ((list==1) ? (fastme_l1_cost_flag[p_UMHex->UMHEX_blocktype][block_a.pos_y][block_a.pos_x]) : (fastme_l0_cost_flag[p_UMHex->UMHEX_blocktype][block_a.pos_y][block_a.pos_x])) : 0; + p_UMHex->SAD_b = block_b.available ? ((list==1) ? (fastme_l1_cost_flag[p_UMHex->UMHEX_blocktype][block_b.pos_y][block_b.pos_x]) : (fastme_l0_cost_flag[p_UMHex->UMHEX_blocktype][block_b.pos_y][block_b.pos_x])) : 0; + p_UMHex->SAD_d = block_d.available ? ((list==1) ? (fastme_l1_cost_flag[p_UMHex->UMHEX_blocktype][block_d.pos_y][block_d.pos_x]) : (fastme_l0_cost_flag[p_UMHex->UMHEX_blocktype][block_d.pos_y][block_d.pos_x])) : 0; + p_UMHex->SAD_c = block_c.available ? ((list==1) ? (fastme_l1_cost_flag[p_UMHex->UMHEX_blocktype][block_c.pos_y][block_c.pos_x]) : (fastme_l0_cost_flag[p_UMHex->UMHEX_blocktype][block_c.pos_y][block_c.pos_x])) : p_UMHex->SAD_d; + } + for (hv=0; hv < 2; hv++) + { + if (!p_Vid->mb_aff_frame_flag || hv==0) + { + mv_a = block_a.available ? mv_info[block_a.pos_y][block_a.pos_x].mv[list].mv_x : 0; + mv_b = block_b.available ? mv_info[block_b.pos_y][block_b.pos_x].mv[list].mv_x : 0; + mv_c = block_c.available ? mv_info[block_c.pos_y][block_c.pos_x].mv[list].mv_x : 0; + } + else + { + if (p_Vid->mb_data[currMB->mbAddrX].mb_field) + { + mv_a = block_a.available ? p_Vid->mb_data[block_a.mb_addr].mb_field + ? mv_info[block_a.pos_y][block_a.pos_x].mv[list].mv_y + : mv_info[block_a.pos_y][block_a.pos_x].mv[list].mv_y / 2 + : 0; + mv_b = block_b.available ? p_Vid->mb_data[block_b.mb_addr].mb_field + ? mv_info[block_b.pos_y][block_b.pos_x].mv[list].mv_y + : mv_info[block_b.pos_y][block_b.pos_x].mv[list].mv_y / 2 + : 0; + mv_c = block_c.available ? p_Vid->mb_data[block_c.mb_addr].mb_field + ? mv_info[block_c.pos_y][block_c.pos_x].mv[list].mv_y + : mv_info[block_c.pos_y][block_c.pos_x].mv[list].mv_y / 2 + : 0; + } + else + { + mv_a = block_a.available ? p_Vid->mb_data[block_a.mb_addr].mb_field + ? mv_info[block_a.pos_y][block_a.pos_x].mv[list].mv_y * 2 + : mv_info[block_a.pos_y][block_a.pos_x].mv[list].mv_y + : 0; + mv_b = block_b.available ? p_Vid->mb_data[block_b.mb_addr].mb_field + ? mv_info[block_b.pos_y][block_b.pos_x].mv[list].mv_y * 2 + : mv_info[block_b.pos_y][block_b.pos_x].mv[list].mv_y + : 0; + mv_c = block_c.available ? p_Vid->mb_data[block_c.mb_addr].mb_field + ? mv_info[block_c.pos_y][block_c.pos_x].mv[list].mv_y * 2 + : mv_info[block_c.pos_y][block_c.pos_x].mv[list].mv_y + : 0; + } + } + + switch (mvPredType) + { + case MVPRED_MEDIAN: + if(!(block_b.available || block_c.available)) + { + pred_vec = (short) mv_a; + } + else + { + pred_vec = (short) (mv_a+mv_b+mv_c-imin(mv_a,imin(mv_b,mv_c))-imax(mv_a,imax(mv_b,mv_c))); + } + break; + case MVPRED_L: + pred_vec = (short) mv_a; + break; + case MVPRED_U: + pred_vec = (short) mv_b; + break; + case MVPRED_UR: + pred_vec = (short) mv_c; + break; + default: + break; + } + + if (hv == 0) + pmv->mv_x = pred_vec; + else + pmv->mv_y = pred_vec; + + //Dynamic Search Range + if (p_Inp->UMHexDSR) + { + dsr_mv_avail=block_a.available+block_b.available+block_c.available; + if(dsr_mv_avail < 2) + { + dsr_temp_search_range[hv] = p_Inp->search_range[p_Vid->view_id]; + } + else + { + dsr_mv_max = imax(iabs(mv_a),imax(iabs(mv_b),iabs(mv_c))); + dsr_mv_sum = (iabs(mv_a)+iabs(mv_b)+iabs(mv_c)); + if(dsr_mv_sum == 0) dsr_small_search_range = (p_Inp->search_range[p_Vid->view_id] + 4) >> 3; + else if(dsr_mv_sum > 3 ) dsr_small_search_range = (p_Inp->search_range[p_Vid->view_id] + 2) >>2; + else dsr_small_search_range = (3*p_Inp->search_range[p_Vid->view_id] + 8) >> 4; + dsr_temp_search_range[hv]=imin(p_Inp->search_range[p_Vid->view_id],imax(dsr_small_search_range,dsr_mv_max<<1)); + if(distblkmax(p_UMHex->SAD_a, distblkmax(p_UMHex->SAD_b,p_UMHex->SAD_c)) > p_UMHex->Threshold_DSR_MB[p_UMHex->UMHEX_blocktype]) + dsr_temp_search_range[hv] = p_Inp->search_range[p_Vid->view_id]; + } + } + } + + //Dynamic Search Range + if (p_Inp->UMHexDSR) + { + int search_range = imax(dsr_temp_search_range[0],dsr_temp_search_range[1]); + search_range <<= 2; + + if (p_Inp->full_search == 2) + { + mv_block->searchRange.min_x = -search_range; + mv_block->searchRange.max_x = search_range; + mv_block->searchRange.min_y = -search_range; + mv_block->searchRange.max_y = search_range; + } + else if (p_Inp->full_search == 1) + { + int scale = (imin(ref_frame,1)+1); + mv_block->searchRange.min_x = -search_range / scale; + mv_block->searchRange.max_x = search_range / scale; + mv_block->searchRange.min_y = -search_range / scale; + mv_block->searchRange.max_y = search_range / scale; + } + else + { + int scale = ((imin(ref_frame,1)+1) * imin(2,p_UMHex->BlockType_LUT[(blockshape_y >> 2) - 1][(blockshape_x >> 2) - 1])); + mv_block->searchRange.min_x = -search_range / scale; + mv_block->searchRange.max_x = search_range / scale; + mv_block->searchRange.min_y = -search_range / scale; + mv_block->searchRange.max_y = search_range / scale; + } + } +} + +#undef SEARCH_ONE_PIXEL + diff --git a/lencod/src/me_umhexsmp.c b/lencod/src/me_umhexsmp.c new file mode 100644 index 0000000..01f4ac8 --- /dev/null +++ b/lencod/src/me_umhexsmp.c @@ -0,0 +1,1025 @@ + +/*! + ************************************************************************************* + * + * \file me_umhexsmp.c + * + * \brief + * Fast integer pixel and sub pixel motion estimation + * Improved and simplified from the original UMHexagonS algorithms + * See JVT-P021 for details + * + * \author + * Main contributors: (see contributors.h for copyright, address and affiliation details) + * - Zhibo Chen + * - JianFeng Xu + * - Wenfang Fu + * + * - Xiaoquan Yi + * - Jun Zhang + * + * \date + * 6. Nov. 2006 + ************************************************************************************* + */ + +#include + +#include "global.h" +#include "memalloc.h" +#include "me_umhexsmp.h" +#include "refbuf.h" +#include "macroblock.h" +#include "me_distortion.h" +#include "mv_search.h" + + +static const MotionVector Diamond[4] = {{-4, 0}, {4, 0}, {0, -4}, {0, 4}}; +static const MotionVector Diamond_SubPelSearch[4] = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}}; +static const MotionVector Hexagon[6] = {{-8, 0}, {8, 0},{-4, -8}, {4, 8}, {-4, 8}, {4 , -8}}; +static const short Big_Hexagon_X[16] = {-16, 16, 0, 0,-16, 16,-16, 16,-16, 16,-16, 16,-8, 8,-8, 8}; +static const short Big_Hexagon_Y[16] = { 0, 0,-16, 16,-4, 4, 4,-4,-8, 8, 8,-8,-12, 12, 12,-12}; + +static const short block_type_shift_factor[8] = {0, 0, 1, 1, 2, 3, 3, 1}; // last one relaxed to 1 instead 4 + +// Macro for motion estimation cost computation per match +#undef SEARCH_ONE_PIXEL_BIPRED +#undef SEARCH_ONE_PIXEL +#define SEARCH_ONE_PIXEL \ +if((iabs(cand.mv_x - center.mv_x)>>2) <= search_range && \ + (iabs(cand.mv_y - center.mv_y)>>2) <= search_range) \ +{ \ + mcost = mv_cost (p_Vid, lambda_factor, &cand, &pred); \ + mcost += mv_block->computePredFPel( ref_picture, mv_block, min_mcost - mcost, &cand); \ + if (mcost < min_mcost) \ + { \ + best = cand; \ + min_mcost = mcost; \ + } \ +} + +#define SEARCH_ONE_PIXEL_BIPRED \ +if ((iabs(cand.mv_x - center2.mv_x)>>2) <= search_range \ +&& (iabs(cand.mv_y - center2.mv_y)>>2) <= search_range) \ +{ \ + mcost = mv_cost (p_Vid, lambda_factor, ¢er1, &pred1); \ + mcost += mv_cost (p_Vid, lambda_factor, &cand, &pred2); \ + if (mcost < min_mcost) \ + { \ + mcost += mv_block->computeBiPredFPel(ref_picture1, ref_picture2, \ + mv_block, min_mcost - mcost, ¢er1, &cand); \ + if (mcost < min_mcost) \ + { \ + best = cand; \ + min_mcost = mcost; \ + } \ + } \ +} + +/*! + ************************************************************************ + * \brief + * Set thresholds for fast motion estimation + * Those thresholds may be adjusted to trade off rate-distortion + * performance and simplified UMHEX speed + ************************************************************************ + */ +void smpUMHEX_init(VideoParameters *p_Vid) +{ + UMHexSMPStruct *p_UMHexSMP = p_Vid->p_UMHexSMP; + + p_UMHexSMP->SymmetricalCrossSearchThreshold1 = dist_scale(800); + p_UMHexSMP->SymmetricalCrossSearchThreshold2 = dist_scale(7000); + p_UMHexSMP->ConvergeThreshold = dist_scale(1000); + p_UMHexSMP->SubPelThreshold1 = dist_scale(1000); + p_UMHexSMP->SubPelThreshold3 = dist_scale(400); +} + +/*! + ************************************************************************ + * \brief + * Allocation of space for fast motion estimation + ************************************************************************ + */ +int smpUMHEX_get_mem(VideoParameters *p_Vid) +{ + UMHexSMPStruct *p_UMHexSMP = p_Vid->p_UMHexSMP; + int memory_size = 0; + if (NULL==(p_UMHexSMP->flag_intra = calloc((p_Vid->width>>4)+1, sizeof(byte)))) + no_mem_exit("smpUMHEX_get_mem: flag_intra"); + + memory_size += get_mem3Ddistblk(&p_UMHexSMP->l0_cost, 9, p_Vid->height >> 2, p_Vid->width >> 2); + memory_size += get_mem3Ddistblk(&p_UMHexSMP->l1_cost, 9, p_Vid->height >> 2, p_Vid->width >> 2); + memory_size += get_mem2D(&p_UMHexSMP->SearchState, 7, 7); + + return memory_size; +} + +/*! + ************************************************************************ + * \brief + * Free space for fast motion estimation + ************************************************************************ + */ +void smpUMHEX_free_mem(VideoParameters *p_Vid) +{ + UMHexSMPStruct *p_UMHexSMP = p_Vid->p_UMHexSMP; + free_mem3Ddistblk(p_UMHexSMP->l0_cost ); + free_mem3Ddistblk(p_UMHexSMP->l1_cost ); + free_mem2D(p_UMHexSMP->SearchState); + + free (p_UMHexSMP->flag_intra); + free (p_UMHexSMP); +} + + +/*! +************************************************************************ +* \brief +* Fast integer pixel block motion estimation +************************************************************************ +*/ +distblk // ==> minimum motion cost after search +smpUMHEXIntegerPelBlockMotionSearch (Macroblock *currMB, // <-- current Macroblock + MotionVector *pred_mv, // <-- motion vector predictor (x|y) in sub-pel units + MEBlock *mv_block, + distblk min_mcost, // <-- minimum motion cost (cost for center or huge value) + int lambda_factor // <-- lagrangian parameter for determining motion cost + ) +{ + VideoParameters *p_Vid = currMB->p_Vid; + Slice *currSlice = currMB->p_Slice; + UMHexSMPStruct *p_UMHexSMP = p_Vid->p_UMHexSMP; + int blocktype = mv_block->blocktype; + short blocksize_x = mv_block->blocksize_x; // horizontal block size + short blocksize_y = mv_block->blocksize_y; // vertical block size + short pic_pix_x = mv_block->pos_x_padded; + short pic_pix_y = mv_block->pos_y_padded; + + int list = mv_block->list; + int cur_list = list + currMB->list_offset; + short ref = mv_block->ref_idx; + StorablePicture *ref_picture = currSlice->listX[cur_list][ref]; + int search_range = mv_block->searchRange.max_x >> 2; + + MotionVector *mv = &mv_block->mv[list]; + MotionVector iMinNow, cand, center, pred, best = {0, 0}; + + int search_step; + distblk mcost; + uint16 i, j, m; + + pic_pix_x = mv_block->pos_x_padded; + pic_pix_y = mv_block->pos_y_padded; + pred.mv_x = pic_pix_x + pred_mv->mv_x; // predicted position x (in sub-pel units) + pred.mv_y = pic_pix_y + pred_mv->mv_y; // predicted position y (in sub-pel units) + center.mv_x = pic_pix_x + mv->mv_x; // center position x (in sub-pel units) + center.mv_y = pic_pix_y + mv->mv_y; // center position y (in sub-pel units) + + + //check the center median predictor + + cand = center; + mcost = mv_cost (p_Vid, lambda_factor, &cand, &pred); + + mcost += mv_block->computePredFPel(ref_picture, mv_block, min_mcost - mcost, &cand); + + if (mcost < min_mcost) + { + min_mcost = mcost; + best = cand; + } + + iMinNow = best; + + // check 0 + if ((0 != pred_mv->mv_x) || (0 != pred_mv->mv_y)) + { + cand.mv_x = pic_pix_x; + cand.mv_y = pic_pix_y; + SEARCH_ONE_PIXEL; + iMinNow = best; + } + + // If the min_mcost is small enough, do a local search then terminate + // Ihis is good for stationary or quasi-stationary areas + if (min_mcost < (p_UMHexSMP->ConvergeThreshold >> block_type_shift_factor[blocktype])) + { + for (m = 0; m < 4; m++) + { + cand.mv_x = iMinNow.mv_x + Diamond[m].mv_x; + cand.mv_y = iMinNow.mv_y + Diamond[m].mv_y; + SEARCH_ONE_PIXEL; + } + mv->mv_x = (short) (best.mv_x - pic_pix_x); + mv->mv_y = (short) (best.mv_y - pic_pix_y); + return min_mcost; + } + + // Small local search + iMinNow = best; + for (m = 0; m < 4; m++) + { + cand.mv_x = iMinNow.mv_x + Diamond[m].mv_x; + cand.mv_y = iMinNow.mv_y + Diamond[m].mv_y; + SEARCH_ONE_PIXEL; + } + + // First_step: Symmetrical-cross search + // If distortion is large, use large shapes. Otherwise, compact shapes are faster + if ( (blocktype == 1 && + min_mcost > (p_UMHexSMP->SymmetricalCrossSearchThreshold1 >> block_type_shift_factor[blocktype])) || + (min_mcost > (p_UMHexSMP->SymmetricalCrossSearchThreshold2>>block_type_shift_factor[blocktype])) ) + { + iMinNow = best; + + for(i = 4; i <= 4 * search_range - 4; i+= 8) + { + search_step = i; + cand.mv_x = (short) (iMinNow.mv_x + search_step); + cand.mv_y = iMinNow.mv_y; + SEARCH_ONE_PIXEL; + + cand.mv_x = (short) (iMinNow.mv_x - search_step); + SEARCH_ONE_PIXEL; + + cand.mv_x = iMinNow.mv_x; + cand.mv_y = (short) (iMinNow.mv_y + search_step); + SEARCH_ONE_PIXEL; + + cand.mv_y = (short) (iMinNow.mv_y - search_step); + SEARCH_ONE_PIXEL; + } + + // Hexagon Search + iMinNow = best; + + for (m = 0; m < 6; m++) + { + cand.mv_x = iMinNow.mv_x + Hexagon[m].mv_x; + cand.mv_y = iMinNow.mv_y + Hexagon[m].mv_y; + SEARCH_ONE_PIXEL; + } + // Multi Big Hexagon Search + iMinNow = best; + + for(i = 1; i <= search_range >> 2; i++) + { + for (m = 0; m < 16; m++) + { + cand.mv_x = iMinNow.mv_x + Big_Hexagon_X[m]*i; + cand.mv_y = iMinNow.mv_y + Big_Hexagon_Y[m]*i; + SEARCH_ONE_PIXEL; + } + } + } + + // Search up_layer predictor for non 16x16 blocks + if (blocktype > 1) + { + cand.mv_x = pic_pix_x + (p_UMHexSMP->pred_MV_uplayer_X / 4) * 4; + cand.mv_y = pic_pix_y + (p_UMHexSMP->pred_MV_uplayer_Y / 4) * 4; + SEARCH_ONE_PIXEL; + } + + if(center.mv_x != pic_pix_x || center.mv_y != pic_pix_y) + { + cand.mv_x = pic_pix_x; + cand.mv_y = pic_pix_y; + SEARCH_ONE_PIXEL; + + iMinNow = best; + // Local diamond search + for (m = 0; m < 4; m++) + { + cand.mv_x = iMinNow.mv_x + Diamond[m].mv_x; + cand.mv_y = iMinNow.mv_y + Diamond[m].mv_y; + SEARCH_ONE_PIXEL; + } + } + + // If the minimum cost is small enough, do a local search + // and finish the search here + if (min_mcost < (p_UMHexSMP->ConvergeThreshold >> block_type_shift_factor[blocktype])) + { + iMinNow = best; + + for (m = 0; m < 4; m++) + { + cand.mv_x = iMinNow.mv_x + Diamond[m].mv_x; + cand.mv_y = iMinNow.mv_y + Diamond[m].mv_y; + SEARCH_ONE_PIXEL; + } + mv->mv_x = (short) (best.mv_x - pic_pix_x); + mv->mv_y = (short) (best.mv_y - pic_pix_y); + return min_mcost; + } + + //second_step: Extended Hexagon-based Search + for(i = 0; i < search_range; i++) + { + iMinNow = best; + for (m = 0; m < 6; m++) + { + cand.mv_x = iMinNow.mv_x + Hexagon[m].mv_x; + cand.mv_y = iMinNow.mv_y + Hexagon[m].mv_y; + SEARCH_ONE_PIXEL; + } + // The minimum cost point happens in the center + if (best.mv_x == iMinNow.mv_x && best.mv_y == iMinNow.mv_y) + { + break; + } + } + + //third_step: Small diamond search + for(i = 0; i < search_range; i++) + { + iMinNow = best; + for (m = 0; m < 4; m++) + { + cand.mv_x = iMinNow.mv_x + Diamond[m].mv_x; + cand.mv_y = iMinNow.mv_y + Diamond[m].mv_y; + SEARCH_ONE_PIXEL; + } + + // The minimum cost point happens in the center + if (best.mv_x == iMinNow.mv_x && best.mv_y == iMinNow.mv_y) + { + break; + } + } + + mv->mv_x = (short) (best.mv_x - pic_pix_x); + mv->mv_y = (short) (best.mv_y - pic_pix_y); + + for (j=(mv_block->pos_y2); j < (mv_block->pos_y2) + (blocksize_y>>2); j++) + { + for (i=(mv_block->pos_x2); i < (mv_block->pos_x2) + (blocksize_x>>2); i++) + { + if(mv_block->list == 0) + { + p_UMHexSMP->l0_cost[blocktype][j][i] = min_mcost; + } + else + { + p_UMHexSMP->l1_cost[blocktype][j][i] = min_mcost; + } + } + } + + return min_mcost; +} + +/*! + *********************************************************************** + * \brief + * Sub pixel block motion search enhanced + *********************************************************************** + */ +distblk // ==> minimum motion cost after search +smpUMHEXFullSubPelBlockMotionSearch (Macroblock *currMB, // <-- current Macroblock + MotionVector *pred_mv, // <-- motion vector predictor (x|y) in sub-pel units + MEBlock *mv_block, + distblk min_mcost, // <-- minimum motion cost (cost for center or huge value) + int lambda_factor // <-- lagrangian parameter for determining motion cost + ) +{ + VideoParameters *p_Vid = currMB->p_Vid; + InputParameters *p_Inp = currMB->p_Inp; + Slice *currSlice = currMB->p_Slice; + UMHexSMPStruct *p_UMHexSMP = p_Vid->p_UMHexSMP; + int pos, best_pos; + distblk mcost; + + MotionVector cand; + + int blocktype = mv_block->blocktype; + int list = mv_block->list; + int list_offset = currMB->list_offset; + short ref = mv_block->ref_idx; + MotionVector *mv = &mv_block->mv[list]; + + int check_position0 = (!p_Inp->rdopt && currSlice->slice_type!=B_SLICE && ref==0 && blocktype==1 && mv->mv_x==0 && mv->mv_y==0); + + int max_pos2 = ( !p_Vid->start_me_refinement_hp ? imax(1, mv_block->search_pos2) : mv_block->search_pos2); + MotionVector cmv; + + StorablePicture *ref_picture = currSlice->listX[list + list_offset][ref]; + + + /********************************* + ***** ***** + ***** HALF-PEL REFINEMENT ***** + ***** ***** + *********************************/ + + //===== loop over search positions ===== + for (best_pos = 0, pos = p_Vid->start_me_refinement_hp; pos < max_pos2; pos++) + { + cand.mv_x = mv->mv_x + p_Vid->spiral_hpel_search[pos].mv_x; // quarter-pel units + cand.mv_y = mv->mv_y + p_Vid->spiral_hpel_search[pos].mv_y; // quarter-pel units + + //----- set motion vector cost ----- + mcost = mv_cost (p_Vid, lambda_factor, &cand, pred_mv); + + if (mcost >= min_mcost) continue; + + cmv = pad_MVs(cand, mv_block); + + mcost += mv_block->computePredQPel( ref_picture, mv_block, min_mcost - mcost, &cmv); + + if (pos==0 && check_position0) + { + distblk dbTmp = weighted_cost (lambda_factor, 16); + mcost = (mcost > dbTmp)? (mcost - dbTmp):0; + } + + if (mcost < min_mcost) + { + min_mcost = mcost; + best_pos = pos; + } + if (min_mcost < (p_UMHexSMP->SubPelThreshold3 >> block_type_shift_factor[blocktype])) + { + break; + } + } + + if (best_pos) + { + add_mvs(mv, &p_Vid->spiral_hpel_search[best_pos]); + } + + if ((mv->mv_x == 0) && (mv->mv_y == 0) && (pred_mv->mv_x == 0 && pred_mv->mv_y == 0) && + (min_mcost < (p_UMHexSMP->SubPelThreshold1 >> block_type_shift_factor[blocktype])) ) + { + best_pos = 0; + return min_mcost; + } + if ( !p_Vid->start_me_refinement_qp ) + min_mcost = DISTBLK_MAX; + /************************************ + ***** ***** + ***** QUARTER-PEL REFINEMENT ***** + ***** ***** + ************************************/ + + //===== loop over search positions ===== + for (best_pos = 0, pos = p_Vid->start_me_refinement_qp; pos < mv_block->search_pos4; pos++) + { + cand.mv_x = mv->mv_x + p_Vid->spiral_search[pos].mv_x; // quarter-pel units + cand.mv_y = mv->mv_y + p_Vid->spiral_search[pos].mv_y; // quarter-pel units + + //----- set motion vector cost ----- + mcost = mv_cost (p_Vid, lambda_factor, &cand, pred_mv); + + if (mcost >= min_mcost) continue; + + cmv = pad_MVs(cand, mv_block); + + mcost += mv_block->computePredQPel( ref_picture, mv_block, min_mcost - mcost, &cmv); + + if (mcost < min_mcost) + { + min_mcost = mcost; + best_pos = pos; + } + if (min_mcost < (p_UMHexSMP->SubPelThreshold3 >> block_type_shift_factor[blocktype])) + { + break; + } + } + + if (best_pos) + { + add_mvs(mv, &p_Vid->spiral_search[best_pos]); + } + + //===== return minimum motion cost ===== + return min_mcost; +} + + +/*! + ************************************************************************ + * \brief + * Fast sub pixel block motion estimation + ************************************************************************ + */ +distblk // ==> minimum motion cost after search +smpUMHEXSubPelBlockMotionSearch ( + Macroblock *currMB, // <-- current Macroblock + MotionVector *pred_mv, // <-- motion vector predictor (x|y) in sub-pel units + MEBlock *mv_block, + distblk min_mcost, // <-- minimum motion cost (cost for center or huge value) + int lambda_factor // <-- lagrangian parameter for determining motion cost + ) +{ + VideoParameters *p_Vid = currMB->p_Vid; + Slice *currSlice = currMB->p_Slice; + UMHexSMPStruct *p_UMHexSMP = p_Vid->p_UMHexSMP; + distblk mcost; + MotionVector cand, iMinNow, currmv = {0, 0}, cand_pad; + + int blocktype = mv_block->blocktype; + int list = mv_block->list; + int list_offset = currMB->list_offset; + short ref = mv_block->ref_idx; + MotionVector *mv = &mv_block->mv[list]; + + StorablePicture *ref_picture = currSlice->listX[list+list_offset][ref]; + + short dynamic_search_range = 3, i, m; + int pred_frac_mv_x,pred_frac_mv_y,abort_search; + int pred_frac_up_mv_x, pred_frac_up_mv_y; + + pred_frac_mv_x = (pred_mv->mv_x - mv->mv_x) %4; //& 0x03; + pred_frac_mv_y = (pred_mv->mv_y - mv->mv_y) %4; //& 0x03; + + pred_frac_up_mv_x = (p_UMHexSMP->pred_MV_uplayer_X - mv->mv_x) & 0x03; + pred_frac_up_mv_y = (p_UMHexSMP->pred_MV_uplayer_Y - mv->mv_y) & 0x03; + + memset(p_UMHexSMP->SearchState[0], 0, (2 * dynamic_search_range + 1)*(2 * dynamic_search_range + 1)); + + p_UMHexSMP->SearchState[dynamic_search_range][dynamic_search_range] = 1; + if( !p_Vid->start_me_refinement_hp ) + { + cand.mv_x = mv->mv_x; + cand.mv_y = mv->mv_y; + mcost = mv_cost (p_Vid, lambda_factor, &cand, pred_mv); + cand_pad = pad_MVs (cand, mv_block); //cand = pad_MVs (cand, mv_block); + + mcost += mv_block->computePredQPel( ref_picture, mv_block, min_mcost - mcost, &cand_pad); //&cand); + if (mcost < min_mcost) + { + min_mcost = mcost; + currmv = cand; + } + } + else + { + currmv = *mv; + } + + // If the min_mcost is small enough and other statistics are positive, + // better to stop the search now + if ( ((mv->mv_x) == 0) && ((mv->mv_y) == 0) && + (pred_frac_mv_x == 0 && pred_frac_up_mv_x == 0) && + (pred_frac_mv_y == 0 && pred_frac_up_mv_y == 0) && + (min_mcost < (p_UMHexSMP->SubPelThreshold1 >> block_type_shift_factor[blocktype])) ) + { + *mv = currmv; + return min_mcost; + } + + if(pred_frac_mv_x || pred_frac_mv_y) + { + cand.mv_x = (short) (mv->mv_x + pred_frac_mv_x); + cand.mv_y = (short) (mv->mv_y + pred_frac_mv_y); + p_UMHexSMP->SearchState[cand.mv_y -mv->mv_y + dynamic_search_range][cand.mv_x - mv->mv_x + dynamic_search_range] = 1; + mcost = mv_cost (p_Vid, lambda_factor, &cand, pred_mv); + + cand_pad = pad_MVs (cand, mv_block); //cand = pad_MVs (cand, mv_block); + mcost += mv_block->computePredQPel( ref_picture, mv_block, min_mcost - mcost, &cand_pad); //&cand); + + if (mcost < min_mcost) + { + min_mcost = mcost; + currmv = cand; + } + } + + // Multiple small diamond search + for(i = 0; i < dynamic_search_range; i++) + { + abort_search = 1; + + iMinNow = currmv; + + for (m = 0; m < 4; m++) + { + cand.mv_x = iMinNow.mv_x + Diamond_SubPelSearch[m].mv_x; //Diamond[m].mv_x; + cand.mv_y = iMinNow.mv_y + Diamond_SubPelSearch[m].mv_y; //Diamond[m].mv_y; + + if(iabs(cand.mv_x - mv->mv_x) <= dynamic_search_range && iabs(cand.mv_y - mv->mv_y) <= dynamic_search_range) + { + if(!p_UMHexSMP->SearchState[cand.mv_y - mv->mv_y + dynamic_search_range][cand.mv_x - mv->mv_x + dynamic_search_range]) + { + p_UMHexSMP->SearchState[cand.mv_y - mv->mv_y + dynamic_search_range][cand.mv_x - mv->mv_x + dynamic_search_range] = 1; + mcost = mv_cost (p_Vid, lambda_factor, &cand, pred_mv); + cand_pad = pad_MVs (cand, mv_block); //cand = pad_MVs (cand, mv_block); + mcost += mv_block->computePredQPel( ref_picture, mv_block, min_mcost - mcost, &cand_pad); //&cand); + if (mcost < min_mcost) + { + min_mcost = mcost; + currmv = cand; + abort_search = 0; + } + if (min_mcost < (p_UMHexSMP->SubPelThreshold3 >> block_type_shift_factor[blocktype])) + { + *mv = currmv; + return min_mcost; + } + } + } + } + // If the minimum cost point is in the center, break out the loop + if (abort_search) + { + break; + } + } + + *mv = currmv; + + //===== return minimum motion cost ===== + return min_mcost; +} + +distblk // ==> minimum motion cost after search +smpUMHEXSubPelBlockME (Macroblock *currMB, // <-- current Macroblock + MotionVector *pred_mv, // <-- motion vector predictor (x|y) in sub-pel units + MEBlock *mv_block, + distblk min_mcost, // <-- minimum motion cost (cost for center or huge value) + int* lambda_factor ) +{ + if(mv_block->blocktype > 1) + { + min_mcost = smpUMHEXSubPelBlockMotionSearch (currMB, pred_mv, mv_block, min_mcost, lambda_factor[Q_PEL]); + } + else + { + min_mcost = smpUMHEXFullSubPelBlockMotionSearch (currMB, pred_mv, mv_block, min_mcost, lambda_factor[Q_PEL]); + } + + return min_mcost; +} + +/*! + ************************************************************************ + * \brief + * smpUMHEXBipredIntegerPelBlockMotionSearch: fast pixel block motion search for bipred mode + * + ************************************************************************ + */ +distblk // ==> minimum motion cost after search +smpUMHEXBipredIntegerPelBlockMotionSearch (Macroblock *currMB, // <-- current Macroblock + int list, // <-- Current reference list + MotionVector *pred_mv1, // <-- motion vector predictor (x|y) in sub-pel units + MotionVector *pred_mv2, // <-- motion vector predictor (x|y) in sub-pel units + MotionVector *mv1, // <--> in: search center (x|y) / out: motion vector (x|y) - in sub-pel units + MotionVector *mv2, // <--> in: search center (x|y) + MEBlock *mv_block, // <-- motion vector information + int search_range, // <-- 1-d search range in sub-pel units + distblk min_mcost, // <-- minimum motion cost (cost for center or huge value) + int lambda_factor // <-- lagrangian parameter for determining motion cost + ) +{ + VideoParameters *p_Vid = currMB->p_Vid; + Slice *currSlice = currMB->p_Slice; + UMHexSMPStruct *p_UMHexSMP = p_Vid->p_UMHexSMP; + + int search_step; + int i, m; + distblk mcost; + short blocktype = mv_block->blocktype; + + short pic_pix_x = mv_block->pos_x_padded; + short pic_pix_y = mv_block->pos_y_padded; + short ref = mv_block->ref_idx; + + StorablePicture *ref_picture1 = currSlice->listX[list + currMB->list_offset][ref]; + StorablePicture *ref_picture2 = currSlice->listX[list == 0 ? 1 + currMB->list_offset: currMB->list_offset][ 0 ]; + + MotionVector iMinNow, best, cand, pred1, pred2, center1, center2; + search_range >>= 2; + + pred1.mv_x = mv_block->pos_x_padded + pred_mv1->mv_x; // predicted position x (in sub-pel units) + pred1.mv_y = mv_block->pos_y_padded + pred_mv1->mv_y; // predicted position y (in sub-pel units) + pred2.mv_x = mv_block->pos_x_padded + pred_mv2->mv_x; // predicted position x (in sub-pel units) + pred2.mv_y = mv_block->pos_y_padded + pred_mv2->mv_y; // predicted position y (in sub-pel units) + + center2.mv_x = mv_block->pos_x_padded + mv1->mv_x; // center position x (in sub-pel units) + center2.mv_y = mv_block->pos_y_padded + mv1->mv_y; // center position y (in sub-pel units) + center1.mv_x = mv_block->pos_x_padded + mv2->mv_x; // mvx of second pred (in sub-pel units) + center1.mv_y = mv_block->pos_y_padded + mv2->mv_y; // mvy of second pred (in sub-pel units) + + //check the center median predictor + best = cand = center2; + mcost = mv_cost (p_Vid, lambda_factor, ¢er1, &pred1); + mcost += mv_cost (p_Vid, lambda_factor, &cand, &pred2); + mcost += mv_block->computeBiPredFPel(ref_picture1, ref_picture2, mv_block, + DISTBLK_MAX, ¢er1, &cand); + if (mcost < min_mcost) + { + min_mcost = mcost; + best = cand; + } + + iMinNow = best; + if (0 != pred_mv1->mv_x || 0 != pred_mv1->mv_y || 0 != pred_mv2->mv_x || 0 != pred_mv2->mv_y) + { + cand.mv_x = pic_pix_x; + cand.mv_y = pic_pix_y; + SEARCH_ONE_PIXEL_BIPRED; + } + + // If the min_mcost is small enough, do a local search then terminate + // This is good for stationary or quasi-stationary areas + if ((min_mcost<<3) < (p_UMHexSMP->ConvergeThreshold >> (block_type_shift_factor[blocktype]))) + { + for (m = 0; m < 4; m++) + { + cand.mv_x = iMinNow.mv_x + Diamond[m].mv_x; + cand.mv_y = iMinNow.mv_y + Diamond[m].mv_y; + SEARCH_ONE_PIXEL_BIPRED; + } + + mv1->mv_x = (short) (best.mv_x - pic_pix_x); + mv1->mv_y = (short) (best.mv_y - pic_pix_y); + return min_mcost; + } + + // Small local search + for (m = 0; m < 4; m++) + { + cand.mv_x = iMinNow.mv_x + Diamond[m].mv_x; + cand.mv_y = iMinNow.mv_y + Diamond[m].mv_y; + SEARCH_ONE_PIXEL_BIPRED; + } + + // First_step: Symmetrical-cross search + // If distortion is large, use large shapes. Otherwise, compact shapes are faster + if ((blocktype == 1 && + (min_mcost<<2) > (p_UMHexSMP->SymmetricalCrossSearchThreshold1 >> block_type_shift_factor[blocktype])) || + ((min_mcost<<2) > (p_UMHexSMP->SymmetricalCrossSearchThreshold2>>block_type_shift_factor[blocktype]))) + { + iMinNow.mv_x = best.mv_x; + iMinNow.mv_y = best.mv_y; + + for (i = 8; i <= 4 * search_range; i+= 8) + { + search_step = i - 4; + cand.mv_x = (short) (iMinNow.mv_x + search_step); + cand.mv_y = iMinNow.mv_y; + SEARCH_ONE_PIXEL_BIPRED; + cand.mv_x = (short) (iMinNow.mv_x - search_step); + SEARCH_ONE_PIXEL_BIPRED; + + cand.mv_x = iMinNow.mv_x; + cand.mv_y = (short) (iMinNow.mv_y + search_step); + SEARCH_ONE_PIXEL_BIPRED; + cand.mv_y = (short) (iMinNow.mv_y - search_step); + SEARCH_ONE_PIXEL_BIPRED; + } + + // Hexagon Search + iMinNow.mv_x = best.mv_x; + iMinNow.mv_y = best.mv_y; + for (m = 0; m < 6; m++) + { + cand.mv_x = iMinNow.mv_x + Hexagon[m].mv_x; + cand.mv_y = iMinNow.mv_y + Hexagon[m].mv_y; + SEARCH_ONE_PIXEL_BIPRED; + } + // Multi Big Hexagon Search + iMinNow = best; + for(i = 1; i <= search_range / 4; i++) + { + for (m = 0; m < 16; m++) + { + cand.mv_x = (short) (iMinNow.mv_x + Big_Hexagon_X[m] * i); + cand.mv_y = (short) (iMinNow.mv_y + Big_Hexagon_Y[m] * i); + SEARCH_ONE_PIXEL_BIPRED; + } + } + } + + // Search up_layer predictor for non 16x16 blocks + if (blocktype > 1) + { + cand.mv_x = pic_pix_x + (p_UMHexSMP->pred_MV_uplayer_X / 4) * 4; + cand.mv_y = pic_pix_y + (p_UMHexSMP->pred_MV_uplayer_Y / 4) * 4; + SEARCH_ONE_PIXEL_BIPRED; + } + + if(center2.mv_x != pic_pix_x || center2.mv_x != pic_pix_y) + { + cand.mv_x = pic_pix_x; + cand.mv_y = pic_pix_y; + SEARCH_ONE_PIXEL_BIPRED; + + iMinNow = best; + // Local diamond search + for (m = 0; m < 4; m++) + { + cand.mv_x = iMinNow.mv_x + Diamond[m].mv_x; + cand.mv_y = iMinNow.mv_y + Diamond[m].mv_y; + SEARCH_ONE_PIXEL_BIPRED; + } + } + + // If the minimum cost is small enough, do a local search + // and finish the search here + if ((min_mcost<<2) < (p_UMHexSMP->ConvergeThreshold >> block_type_shift_factor[blocktype])) + { + iMinNow = best; + for (m = 0; m < 4; m++) + { + cand.mv_x = iMinNow.mv_x + Diamond[m].mv_x; + cand.mv_y = iMinNow.mv_y + Diamond[m].mv_y; + SEARCH_ONE_PIXEL_BIPRED; + } + mv1->mv_x = (short) (best.mv_x - pic_pix_x); + mv1->mv_y = (short) (best.mv_y - pic_pix_y); + return min_mcost; + } + + // Second_step: Extended Hexagon-based Search + for (i = 0; i < search_range; i++) + { + iMinNow = best; + for (m = 0; m < 6; m++) + { + cand.mv_x = iMinNow.mv_x + Hexagon[m].mv_x; + cand.mv_y = iMinNow.mv_y + Hexagon[m].mv_y; + SEARCH_ONE_PIXEL_BIPRED; + } + // The minimum cost point happens in the center + if (best.mv_x == iMinNow.mv_x && best.mv_y == iMinNow.mv_y) + { + break; + } + } + + // Third_step: Small diamond search + for (i = 0; i < search_range; i++) + { + iMinNow = best; + for (m = 0; m < 4; m++) + { + cand.mv_x = iMinNow.mv_x + Diamond[m].mv_x; + cand.mv_y = iMinNow.mv_y + Diamond[m].mv_y; + SEARCH_ONE_PIXEL_BIPRED; + } + + // The minimum cost point happens in the center + if (best.mv_x == iMinNow.mv_x && best.mv_y == iMinNow.mv_y) + { + break; + } + } + + mv1->mv_x = (short) (best.mv_x - pic_pix_x); + mv1->mv_y = (short) (best.mv_y - pic_pix_y); + return min_mcost; +} + +/*! + ************************************************************************ + * \brief + * Set neighbouring block mode (intra/inter) + * used for fast motion estimation + ************************************************************************ + */ +void smpUMHEX_decide_intrabk_SAD(Macroblock *currMB) +{ + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + UMHexSMPStruct *p_UMHexSMP = p_Vid->p_UMHexSMP; + + if (currSlice->slice_type != I_SLICE && currSlice->slice_type != SI_SLICE) + { + if (currMB->pix_x == 0 && currMB->pix_y == 0) + { + p_UMHexSMP->flag_intra_SAD = 0; + } + else if (currMB->pix_x == 0) + { + p_UMHexSMP->flag_intra_SAD = p_UMHexSMP->flag_intra[(currMB->pix_x)>>4]; + } + else if (currMB->pix_y == 0) + { + p_UMHexSMP->flag_intra_SAD = p_UMHexSMP->flag_intra[((currMB->pix_x)>>4)-1]; + } + else + { + p_UMHexSMP->flag_intra_SAD = ((p_UMHexSMP->flag_intra[(currMB->pix_x)>>4])|| + (p_UMHexSMP->flag_intra[((currMB->pix_x)>>4)-1])|| + (p_UMHexSMP->flag_intra[((currMB->pix_x)>>4)+1])) ; + } + } + return; +} + +/*! + ************************************************************************ + * \brief + * Set cost to zero if neighbouring block is intra + * used for fast motion estimation + ************************************************************************ + */ +void smpUMHEX_skip_intrabk_SAD(Macroblock *currMB) +{ + short i, j, k; + VideoParameters *p_Vid = currMB->p_Vid; + Slice *currSlice = currMB->p_Slice; + UMHexSMPStruct *p_UMHexSMP = p_Vid->p_UMHexSMP; + + if (p_Vid->number > 0) + { + p_UMHexSMP->flag_intra[(currMB->pix_x)>>4] = (currMB->best_mode == 9 || currMB->best_mode == 10) ? 1 : 0; + } + + if (currSlice->slice_type != I_SLICE && currSlice->slice_type != SI_SLICE && (currMB->best_mode == 9 || currMB->best_mode == 10)) + { + for (k=0; k < 9;k++) + { + for (j=0; j < 4; j++) + { + for (i=0; i < 4; i++) + { + p_UMHexSMP->l0_cost[k][j][i] = 0; + p_UMHexSMP->l1_cost[k][j][i] = 0; + } + } + } + } + return; +} + +/*! + ************************************************************************ + * \brief + * Set up prediction MV and prediction up layer cost + * used for fast motion estimation + ************************************************************************ + */ +void smpUMHEX_setup(Macroblock *currMB, + short ref, + int list, + int block_y, + int block_x, + int blocktype, + MotionVector *****all_mv) +{ + VideoParameters *p_Vid = currMB->p_Vid; + UMHexSMPStruct *p_UMHexSMP = p_Vid->p_UMHexSMP; + + if (blocktype > 6) + { + p_UMHexSMP->pred_MV_uplayer_X = all_mv[list][ref][5][block_y][block_x].mv_x; + p_UMHexSMP->pred_MV_uplayer_Y = all_mv[list][ref][5][block_y][block_x].mv_y; + } + else if (blocktype > 4) + { + p_UMHexSMP->pred_MV_uplayer_X = all_mv[list][ref][4][block_y][block_x].mv_x; + p_UMHexSMP->pred_MV_uplayer_Y = all_mv[list][ref][4][block_y][block_x].mv_y; + } + else if (blocktype == 4) + { + p_UMHexSMP->pred_MV_uplayer_X = all_mv[list][ref][2][block_y][block_x].mv_x; + p_UMHexSMP->pred_MV_uplayer_Y = all_mv[list][ref][2][block_y][block_x].mv_y; + } + else if (blocktype > 1) + { + p_UMHexSMP->pred_MV_uplayer_X = all_mv[list][ref][1][block_y][block_x].mv_x; + p_UMHexSMP->pred_MV_uplayer_Y = all_mv[list][ref][1][block_y][block_x].mv_y; + } + + if (blocktype > 1) + { + if (blocktype > 6) + { + p_UMHexSMP->pred_SAD_uplayer = (list==1) ? + (p_UMHexSMP->l1_cost[5][(currMB->block_y)+block_y][(currMB->block_x)+block_x]) + : (p_UMHexSMP->l0_cost[5][(currMB->block_y)+block_y][(currMB->block_x)+block_x]); + p_UMHexSMP->pred_SAD_uplayer /= 2; + } + else if (blocktype > 4) + { + p_UMHexSMP->pred_SAD_uplayer = (list==1) ? + (p_UMHexSMP->l1_cost[4][(currMB->block_y)+block_y][(currMB->block_x)+block_x]) + : (p_UMHexSMP->l0_cost[4][(currMB->block_y)+block_y][(currMB->block_x)+block_x]); + p_UMHexSMP->pred_SAD_uplayer /= 2; + } + else if (blocktype == 4) + { + p_UMHexSMP->pred_SAD_uplayer = (list==1) ? + (p_UMHexSMP->l1_cost[2][(currMB->block_y)+block_y][(currMB->block_x)+block_x]) + : (p_UMHexSMP->l0_cost[2][(currMB->block_y)+block_y][(currMB->block_x)+block_x]); + p_UMHexSMP->pred_SAD_uplayer /= 2; + } + else + { + p_UMHexSMP->pred_SAD_uplayer = (list==1) ? + (p_UMHexSMP->l1_cost[1][(currMB->block_y)+block_y][(currMB->block_x)+block_x]) + : (p_UMHexSMP->l0_cost[1][(currMB->block_y)+block_y][(currMB->block_x)+block_x]); + p_UMHexSMP->pred_SAD_uplayer /= 2; + } + + p_UMHexSMP->pred_SAD_uplayer = p_UMHexSMP->flag_intra_SAD ? 0 : p_UMHexSMP->pred_SAD_uplayer; + } +} + diff --git a/lencod/src/mmco.c b/lencod/src/mmco.c new file mode 100644 index 0000000..d6ff3bf --- /dev/null +++ b/lencod/src/mmco.c @@ -0,0 +1,479 @@ +/*! + ************************************************************************************* + * \file mmco.c + * + * \brief + * MMCO example operations. + * + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * - Alexis Michael Tourapis + * - Athanasios Leontaris + ************************************************************************************* + */ + +#include "contributors.h" + +#include +#include +#include "global.h" + +#include "image.h" +#include "nalucommon.h" +#include "report.h" + + +void mmco_long_term(VideoParameters *p_Vid, int current_pic_num) +{ + DecRefPicMarking_t *tmp_drpm,*tmp_drpm2; + + if ( !p_Vid->currentPicture->idr_flag ) + { + if (p_Vid->dec_ref_pic_marking_buffer!=NULL) + return; + + if (NULL==(tmp_drpm=(DecRefPicMarking_t*)calloc (1,sizeof (DecRefPicMarking_t)))) + no_mem_exit("poc_based_ref_management: tmp_drpm"); + + tmp_drpm->Next=NULL; + + tmp_drpm->memory_management_control_operation = 0; + + if (NULL==(tmp_drpm2=(DecRefPicMarking_t*)calloc (1,sizeof (DecRefPicMarking_t)))) + no_mem_exit("poc_based_ref_management: tmp_drpm2"); + tmp_drpm2->Next=tmp_drpm; + + tmp_drpm2->memory_management_control_operation = 3; + tmp_drpm2->long_term_frame_idx = current_pic_num; + p_Vid->dec_ref_pic_marking_buffer = tmp_drpm2; + } + else + { + p_Vid->long_term_reference_flag = TRUE; + } +} + +#if HM50_LIKE_MMCO +void hm50_ref_management_frame_pic(DecodedPictureBuffer *p_Dpb, int current_pic_num) +{ + VideoParameters *p_Vid = p_Dpb->p_Vid; + unsigned i; + int pic_num = 0; + + int target_poc=-1; + DecRefPicMarking_t *tmp_drpm,*tmp_drpm2; + + // only support GOP Size = 8, 4 reference frames, 2 in list0 and 2 in list1 + if ( p_Vid->p_Inp->NumberBFrames != 7 || + p_Vid->p_Inp->num_ref_frames != 4 || + p_Vid->p_Inp->B_List0_refs[0] != 2 || + p_Vid->p_Inp->B_List1_refs[0] != 2 ) + { + return; + } + +#if CRA + // for CRA pictures, we have MMCO already + if ( p_Vid->p_Inp->useCRA ) + { + if ( ( p_Vid->currentSlice->slice_type == B_SLICE || p_Vid->currentSlice->slice_type == P_SLICE ) && p_Vid->currentSlice->p_Dpb->ref_frames_in_buffer >= 2 ) + { + if ( ( (p_Vid->currentSlice->ThisPOC/2) - (p_Vid->p_Inp->NumberBFrames+1) ) % p_Vid->p_Inp->intra_period == 0 ) + { + return; + } + } + } +#endif + + if (p_Vid->dec_ref_pic_marking_buffer!=NULL) + return; + + if ( p_Vid->currentPicture->idr_flag ) + return; + + if ((p_Dpb->ref_frames_in_buffer + p_Dpb->ltref_frames_in_buffer)==0) + return; + + switch ( p_Vid->currentSlice->ThisPOC / 2 % 8 ) + { + case 0: + target_poc = p_Vid->currentSlice->ThisPOC - 32; + break; + case 4: + target_poc = p_Vid->currentSlice->ThisPOC - 16; + break; + case 2: + target_poc = p_Vid->currentSlice->ThisPOC - 8; + break; + case 6: + target_poc = p_Vid->currentSlice->ThisPOC - 8; + break; + default: + target_poc = -1; + break; + } + + if ( target_poc < 0 ) + return; + + + for (i = 0; i < p_Dpb->used_size; i++) + { + if (p_Dpb->fs[i]->is_reference && (!(p_Dpb->fs[i]->is_long_term)) && p_Dpb->fs[i]->poc == target_poc) + { + pic_num = p_Dpb->fs[i]->frame->pic_num; + } + } + + if (NULL==(tmp_drpm=(DecRefPicMarking_t*)calloc (1,sizeof (DecRefPicMarking_t)))) + no_mem_exit("poc_based_ref_management: tmp_drpm"); + tmp_drpm->Next=NULL; + + tmp_drpm->memory_management_control_operation = 0; + + if (NULL==(tmp_drpm2=(DecRefPicMarking_t*)calloc (1,sizeof (DecRefPicMarking_t)))) + no_mem_exit("poc_based_ref_management: tmp_drpm2"); + tmp_drpm2->Next=tmp_drpm; + + tmp_drpm2->memory_management_control_operation = 1; + + if ( p_Vid->num_of_layers == 1 ) + tmp_drpm2->difference_of_pic_nums_minus1 = current_pic_num - pic_num - 1; + else + tmp_drpm2->difference_of_pic_nums_minus1 = (current_pic_num - pic_num)/2 - 1; + + p_Vid->dec_ref_pic_marking_buffer = tmp_drpm2; +} +#endif + +#if CRA +void cra_ref_management_frame_pic(DecodedPictureBuffer *p_Dpb, int current_pic_num) +{ + VideoParameters *p_Vid = p_Dpb->p_Vid; + unsigned i; + int pic_num = 0; + + int min_poc=INT_MAX; + DecRefPicMarking_t *tmp_drpm,*tmp_drpm2; + + int last_intra_poc = p_Vid->currentSlice->ThisPOC / 2 / p_Vid->p_Inp->intra_period * p_Vid->p_Inp->intra_period * 2; + + if (p_Vid->dec_ref_pic_marking_buffer!=NULL) + return; + + if ( p_Vid->currentPicture->idr_flag ) + return; + + if ((p_Dpb->ref_frames_in_buffer + p_Dpb->ltref_frames_in_buffer)==0) + return; + + if (NULL==(tmp_drpm=(DecRefPicMarking_t*)calloc (1,sizeof (DecRefPicMarking_t)))) + no_mem_exit("poc_based_ref_management: tmp_drpm"); + tmp_drpm->Next=NULL; + + tmp_drpm->memory_management_control_operation = 0; + + for (i = 0; i < p_Dpb->used_size; i++) + { + if (p_Dpb->fs[i]->is_reference && (!(p_Dpb->fs[i]->is_long_term)) && p_Dpb->fs[i]->poc < last_intra_poc) + { + if (NULL==(tmp_drpm2=(DecRefPicMarking_t*)calloc (1,sizeof (DecRefPicMarking_t)))) + no_mem_exit("poc_based_ref_management: tmp_drpm2"); + tmp_drpm2->Next=tmp_drpm; + tmp_drpm2->memory_management_control_operation = 1; + + pic_num = p_Dpb->fs[i]->frame->pic_num; + + if ( p_Vid->num_of_layers == 1 ) + tmp_drpm2->difference_of_pic_nums_minus1 = current_pic_num - pic_num - 1; + else + tmp_drpm2->difference_of_pic_nums_minus1 = (current_pic_num - pic_num)/2 - 1; + + if ( tmp_drpm2->difference_of_pic_nums_minus1 < 0 ) + { + tmp_drpm2->difference_of_pic_nums_minus1 += p_Vid->currentSlice->max_frame_num; + } + if ( tmp_drpm2->difference_of_pic_nums_minus1 >= p_Vid->currentSlice->max_frame_num ) + { + tmp_drpm2->difference_of_pic_nums_minus1 -= p_Vid->currentSlice->max_frame_num; + } + + tmp_drpm = tmp_drpm2; + } + } + + p_Vid->dec_ref_pic_marking_buffer = tmp_drpm; +} +#endif + +#if LD_REF_SETTING +void low_delay_ref_management_frame_pic(DecodedPictureBuffer *p_Dpb, int current_pic_num) +{ + VideoParameters *p_Vid = p_Dpb->p_Vid; + unsigned i; + int pic_num = 0; + + int min_poc=INT_MAX; + int min_poc_not_4x = INT_MAX; + DecRefPicMarking_t *tmp_drpm,*tmp_drpm2; + +#if CRA + // for CRA pictures, we have MMCO already + if ( p_Vid->p_Inp->useCRA ) + { + if ( ( p_Vid->currentSlice->slice_type == B_SLICE || p_Vid->currentSlice->slice_type == P_SLICE ) && p_Vid->currentSlice->p_Dpb->ref_frames_in_buffer >= 2 ) + { + if ( ( (p_Vid->currentSlice->ThisPOC/2) - (p_Vid->p_Inp->NumberBFrames+1) ) % p_Vid->p_Inp->intra_period == 0 ) + { + return; + } + } + } +#endif + + if (p_Vid->dec_ref_pic_marking_buffer!=NULL) + return; + + if ( p_Vid->currentPicture->idr_flag ) + return; + + if ((p_Dpb->ref_frames_in_buffer + p_Dpb->ltref_frames_in_buffer)==0) + return; + + for (i = 0; i < p_Dpb->used_size; i++) + { + if (p_Dpb->fs[i]->is_reference && (!(p_Dpb->fs[i]->is_long_term)) && p_Dpb->fs[i]->poc < min_poc) + { + min_poc = p_Dpb->fs[i]->frame->poc ; + pic_num = p_Dpb->fs[i]->frame->pic_num; + } + } + for (i = 0; i < p_Dpb->used_size; i++) + { + if (p_Dpb->fs[i]->is_reference && (!(p_Dpb->fs[i]->is_long_term)) && p_Dpb->fs[i]->poc < min_poc_not_4x && (p_Dpb->fs[i]->poc/2)%4!=0 ) + { + min_poc_not_4x = p_Dpb->fs[i]->frame->poc ; + pic_num = p_Dpb->fs[i]->frame->pic_num; + } + } + + if (NULL==(tmp_drpm=(DecRefPicMarking_t*)calloc (1,sizeof (DecRefPicMarking_t)))) + no_mem_exit("poc_based_ref_management: tmp_drpm"); + tmp_drpm->Next=NULL; + + tmp_drpm->memory_management_control_operation = 0; + + if (NULL==(tmp_drpm2=(DecRefPicMarking_t*)calloc (1,sizeof (DecRefPicMarking_t)))) + no_mem_exit("poc_based_ref_management: tmp_drpm2"); + tmp_drpm2->Next=tmp_drpm; + + tmp_drpm2->memory_management_control_operation = 1; + + if ( p_Vid->num_of_layers == 1 ) + tmp_drpm2->difference_of_pic_nums_minus1 = current_pic_num - pic_num - 1; + else + tmp_drpm2->difference_of_pic_nums_minus1 = (current_pic_num - pic_num)/2 - 1; + + p_Vid->dec_ref_pic_marking_buffer = tmp_drpm2; +} +#endif + +/*! +************************************************************************ +* \brief +* POC-based reference management (FRAME) +************************************************************************ +*/ + +void poc_based_ref_management_frame_pic(DecodedPictureBuffer *p_Dpb, int current_pic_num) +{ + VideoParameters *p_Vid = p_Dpb->p_Vid; + unsigned i; + int pic_num = 0; + + int min_poc=INT_MAX; + DecRefPicMarking_t *tmp_drpm,*tmp_drpm2; + +#if CRA + // for CRA pictures, we have MMCO already + if ( p_Vid->p_Inp->useCRA ) + { + if ( ( p_Vid->currentSlice->slice_type == B_SLICE || p_Vid->currentSlice->slice_type == P_SLICE ) && p_Vid->currentSlice->p_Dpb->ref_frames_in_buffer >= 2 ) + { + if ( ( (p_Vid->currentSlice->ThisPOC/2) - (p_Vid->p_Inp->NumberBFrames+1) ) % p_Vid->p_Inp->intra_period == 0 ) + { + return; + } + } + } +#endif + +#if HM50_LIKE_MMCO + if ( p_Vid->p_Inp->HM50LikeMMCO ) + { + // for the case that we already have MMCO, just return + if ( p_Vid->p_Inp->NumberBFrames != 7 || + p_Vid->p_Inp->num_ref_frames != 4 || + p_Vid->p_Inp->B_List0_refs[0] != 2 || + p_Vid->p_Inp->B_List1_refs[0] != 2 ) + { + return; + } + } +#endif + + if (p_Vid->dec_ref_pic_marking_buffer!=NULL) + return; + + if ( p_Vid->currentPicture->idr_flag ) + return; + + if ((p_Dpb->ref_frames_in_buffer + p_Dpb->ltref_frames_in_buffer)==0) + return; + + for (i = 0; i < p_Dpb->used_size; i++) + { + if (p_Dpb->fs[i]->is_reference && (!(p_Dpb->fs[i]->is_long_term)) && p_Dpb->fs[i]->poc < min_poc) + { + min_poc = p_Dpb->fs[i]->frame->poc ; + pic_num = p_Dpb->fs[i]->frame->pic_num; + } + } + + if (NULL==(tmp_drpm=(DecRefPicMarking_t*)calloc (1,sizeof (DecRefPicMarking_t)))) + no_mem_exit("poc_based_ref_management: tmp_drpm"); + tmp_drpm->Next=NULL; + + tmp_drpm->memory_management_control_operation = 0; + + if (NULL==(tmp_drpm2=(DecRefPicMarking_t*)calloc (1,sizeof (DecRefPicMarking_t)))) + no_mem_exit("poc_based_ref_management: tmp_drpm2"); + tmp_drpm2->Next=tmp_drpm; + + tmp_drpm2->memory_management_control_operation = 1; + tmp_drpm2->difference_of_pic_nums_minus1 = current_pic_num - pic_num - 1; + + p_Vid->dec_ref_pic_marking_buffer = tmp_drpm2; +} + +/*! +************************************************************************ +* \brief +* POC-based reference management (FIELD) +************************************************************************ +*/ + +void poc_based_ref_management_field_pic(DecodedPictureBuffer *p_Dpb, int current_pic_num) +{ + VideoParameters *p_Vid = p_Dpb->p_Vid; + unsigned int i; + int pic_num1 = 0, pic_num2 = 0; + + int min_poc=INT_MAX; + DecRefPicMarking_t *tmp_drpm,*tmp_drpm2, *tmp_drpm3; + + if (p_Vid->dec_ref_pic_marking_buffer!=NULL) + return; + + if ( p_Vid->currentPicture->idr_flag ) + return; + + if ((p_Dpb->ref_frames_in_buffer + p_Dpb->ltref_frames_in_buffer)==0) + return; + + if ( p_Vid->structure == TOP_FIELD ) + { + for (i=0; iused_size;i++) + { + if (p_Dpb->fs[i]->is_reference && (!(p_Dpb->fs[i]->is_long_term)) && p_Dpb->fs[i]->poc < min_poc) + { + min_poc = p_Dpb->fs[i]->poc; + pic_num1 = p_Dpb->fs[i]->top_field->pic_num; + pic_num2 = p_Dpb->fs[i]->bottom_field->pic_num; + } + } + } + + if (NULL==(tmp_drpm=(DecRefPicMarking_t*)calloc (1,sizeof (DecRefPicMarking_t)))) no_mem_exit("poc_based_ref_management_field_pic: tmp_drpm"); + tmp_drpm->Next=NULL; + tmp_drpm->memory_management_control_operation = 0; + + if ( p_Vid->structure == BOTTOM_FIELD ) + { + p_Vid->dec_ref_pic_marking_buffer = tmp_drpm; + return; + } + + if (NULL==(tmp_drpm2=(DecRefPicMarking_t*)calloc (1,sizeof (DecRefPicMarking_t)))) no_mem_exit("poc_based_ref_management_field_pic: tmp_drpm2"); + tmp_drpm2->Next=tmp_drpm; + tmp_drpm2->memory_management_control_operation = 1; + tmp_drpm2->difference_of_pic_nums_minus1 = current_pic_num - pic_num1 - 1; + + if (NULL==(tmp_drpm3=(DecRefPicMarking_t*)calloc (1,sizeof (DecRefPicMarking_t)))) no_mem_exit("poc_based_ref_management_field_pic: tmp_drpm3"); + tmp_drpm3->Next=tmp_drpm2; + tmp_drpm3->memory_management_control_operation = 1; + tmp_drpm3->difference_of_pic_nums_minus1 = current_pic_num - pic_num2 - 1; + + p_Vid->dec_ref_pic_marking_buffer = tmp_drpm3; +} + + +/*! +************************************************************************ +* \brief +* Temporal layer-based reference management (FRAME) +************************************************************************ +*/ + +void tlyr_based_ref_management_frame_pic(VideoParameters *p_Vid, int current_pic_num) +{ + unsigned i, first = 1; + DecRefPicMarking_t *drpm = NULL, *current_drpm = NULL, *tmp_drpm = NULL; + struct decoded_picture_buffer *p_Dpb = p_Vid->p_Dpb_layer[0]; + + if (p_Vid->dec_ref_pic_marking_buffer!=NULL) + return; + + if ( p_Vid->currentPicture->idr_flag ) + return; + + if ((p_Dpb->ref_frames_in_buffer + p_Dpb->ltref_frames_in_buffer)==0) + return; + + for (i = 0; i < p_Dpb->used_size; i++) + { + if (p_Dpb->fs[i]->is_reference && (!(p_Dpb->fs[i]->is_long_term)) && p_Dpb->fs[i]->frame->temporal_layer > p_Vid->enc_picture->temporal_layer) + { + if (NULL == (tmp_drpm=(DecRefPicMarking_t*)calloc (1,sizeof (DecRefPicMarking_t)))) + no_mem_exit("poc_based_ref_management: tmp_drpm2"); + tmp_drpm->memory_management_control_operation = 1; + tmp_drpm->difference_of_pic_nums_minus1 = current_pic_num - p_Dpb->fs[i]->frame->pic_num - 1; + + if (first) + { + drpm = current_drpm = tmp_drpm; + first = 0; + } + else + { + current_drpm->Next = tmp_drpm; + current_drpm = current_drpm->Next; + } + } + } + + if (first) + return; + + if (NULL==(tmp_drpm=(DecRefPicMarking_t*)calloc (1,sizeof (DecRefPicMarking_t)))) + no_mem_exit("poc_based_ref_management: tmp_drpm"); + tmp_drpm->Next=NULL; + + tmp_drpm->memory_management_control_operation = 0; + + current_drpm->Next=tmp_drpm; + + p_Vid->dec_ref_pic_marking_buffer = drpm; +} + diff --git a/lencod/src/mode_decision.c b/lencod/src/mode_decision.c new file mode 100644 index 0000000..13a201a --- /dev/null +++ b/lencod/src/mode_decision.c @@ -0,0 +1,819 @@ + +/*! +*************************************************************************** +* \file mode_decision.c +* +* \brief +* Main macroblock mode decision functions and helpers +* +************************************************************************** +*/ + +#include +#include +#include + +#include "global.h" +#include "image.h" +#include "ratectl.h" +#include "mode_decision.h" +#include "md_common.h" +#include "me_umhex.h" +#include "me_umhexsmp.h" +#include "macroblock.h" +#include "rdoq.h" +#include "q_around.h" +#include "slice.h" +#include "conformance.h" +#include "rdopt.h" + + +/*! +************************************************************************************* +* \brief +* Reset Valid Modes +************************************************************************************* +*/ +void reset_valid_modes(RD_PARAMS *enc_mb) +{ + memset(enc_mb->valid, 0, MAXMODE * sizeof(short)); +} + + +/*! +************************************************************************************* +* \brief +* Checks whether a primary SP slice macroblock was encoded as I16 +************************************************************************************* +*/ +static inline int check_for_SI16(int **lrec, int pix_x, int pix_y) +{ + int i,j; + for (i = pix_y; i < pix_y + MB_BLOCK_SIZE; i++) + { + for (j = pix_x;j < pix_x + MB_BLOCK_SIZE; j++) + if (lrec[i][j] != -16) + return 0; + } + return 1; +} + +/*! +************************************************************************************* +* \brief +* Update parameters after encoding a macroblock +************************************************************************************* +*/ +void end_encode_one_macroblock(Macroblock *currMB) +{ + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + InputParameters *p_Inp = currMB->p_Inp; + + update_qp_cbp(currMB); + + if ( (currSlice->mb_aff_frame_flag) + && (currMB->mbAddrX & 0x01) + && (currMB->mb_type ? 0:(((currSlice->slice_type == B_SLICE)) ? !currMB->cbp:1)) // bottom is skip + && ((currMB->PrevMB)->mb_type ? 0:(((currSlice->slice_type == B_SLICE)) ? !(currMB->PrevMB)->cbp:1)) + && !(field_flag_inference(currMB) == currMB->mb_field)) // top is skip + { + currSlice->rddata->min_rdcost = 1e30; // don't allow coding of a MB pair as skip if wrong inference + } + else + currSlice->rddata->min_rdcost = (double)currMB->min_rdcost; + + currSlice->rddata->min_dcost = (double)currMB->min_dcost; + currSlice->rddata->min_rate = (double)currMB->min_rate; + + if(p_Inp->SearchMode[p_Vid->view_id] == UM_HEX) + { + UMHEX_skip_intrabk_SAD(currMB, currSlice->listXsize[currMB->list_offset]); + } + else if(p_Inp->SearchMode[p_Vid->view_id] == UM_HEX_SIMPLE) + { + smpUMHEX_skip_intrabk_SAD(currMB); + } + + //--- constrain intra prediction --- + if(p_Inp->UseConstrainedIntraPred && (currSlice->slice_type == P_SLICE || currSlice->slice_type == B_SLICE)) + { + p_Vid->intra_block[currMB->mbAddrX] = IS_INTRA(currMB); + } +} + +/*! +************************************************************************************* +* \brief +* Initialize Encoding parameters for Macroblock +************************************************************************************* +*/ +void init_enc_mb_params(Macroblock* currMB, RD_PARAMS *enc_mb, int intra) +{ + VideoParameters *p_Vid = currMB->p_Vid; + InputParameters *p_Inp = currMB->p_Inp; + Slice *currSlice = currMB->p_Slice; +#if (MVC_EXTENSION_ENABLE) + int *InterSearch = p_Inp->InterSearch[(p_Vid->num_of_layers > 1) ? currSlice->view_id : 0][(currSlice->slice_type == B_SLICE)]; +#else + int *InterSearch = p_Inp->InterSearch[0][(currSlice->slice_type == B_SLICE)]; +#endif + + int l,k; + + enc_mb->curr_mb_field = (short) ((currSlice->mb_aff_frame_flag) && (currMB->mb_field)); + + // Set valid modes + enc_mb->valid[I8MB] = (short) ((!p_Inp->DisableIntraInInter[p_Vid->view_id] || intra )? p_Inp->Transform8x8Mode : 0); + enc_mb->valid[I4MB] = (short) ((!p_Inp->DisableIntraInInter[p_Vid->view_id] || intra )? ((p_Inp->Transform8x8Mode == 2) ? 0 : 1) : 0); + enc_mb->valid[I4MB] = (short) ((!p_Inp->DisableIntra4x4 ) ? enc_mb->valid[I4MB] : 0); + enc_mb->valid[I16MB] = (short) ((!p_Inp->DisableIntraInInter[p_Vid->view_id] || intra )? 1 : 0); + enc_mb->valid[I16MB] = (short) ((!p_Inp->DisableIntra16x16) ? enc_mb->valid[I16MB] : 0); + enc_mb->valid[IPCM] = (short) ((!p_Inp->DisableIntraInInter[p_Vid->view_id] || intra )? (p_Inp->EnableIPCM > 0 ? 1:0) : 0); + enc_mb->valid[SI4MB] = 0; + //enc_mb->valid[SI4MB] = (short) (currSlice->slice_type == SI_SLICE); + //enc_mb->valid[SI4MB] = (short) ( p_Inp->DisableIntra4x4 ? 0 : enc_mb->valid[SI4MB]); + + enc_mb->valid[0] = (short) (!intra && InterSearch[0]); + enc_mb->valid[1] = (short) (!intra && InterSearch[1]); + enc_mb->valid[2] = (short) (!intra && InterSearch[2]); + enc_mb->valid[3] = (short) (!intra && InterSearch[3]); + enc_mb->valid[4] = (short) (!intra && InterSearch[4]); + enc_mb->valid[5] = (short) (!intra && InterSearch[5] && !(p_Inp->Transform8x8Mode==2)); + enc_mb->valid[6] = (short) (!intra && InterSearch[6] && !(p_Inp->Transform8x8Mode==2)); + enc_mb->valid[7] = (short) (!intra && InterSearch[7] && !(p_Inp->Transform8x8Mode==2)); + enc_mb->valid[P8x8] = (short) (enc_mb->valid[4] || enc_mb->valid[5] || enc_mb->valid[6] || enc_mb->valid[7]); + + + if (currSlice->UseRDOQuant && p_Inp->RDOQ_CP_Mode && (p_Vid->qp != p_Vid->masterQP) ) + RDOQ_update_mode(currSlice, enc_mb); + + if(currSlice->slice_type == SP_SLICE || currSlice->slice_type == SI_SLICE) + { + if(currSlice->slice_type == SI_SLICE) + { + reset_valid_modes(enc_mb); + if(check_for_SI16(p_Vid->lrec, currMB->pix_x, currMB->pix_y)) + { + enc_mb->valid[I16MB] = 1; + } + else + { + enc_mb->valid[I4MB] = 1; + } + } + + if(p_Vid->sp2_frame_indicator) + { + if(check_for_SI16(p_Vid->lrec, currMB->pix_x, currMB->pix_y)) + { + reset_valid_modes(enc_mb); + enc_mb->valid[I16MB] = 1; + } + else + { + enc_mb->valid[I8MB] = 0; + enc_mb->valid[IPCM] = 0; + enc_mb->valid[0] = 0; + //enc_mb->valid[I16MB] = 0; + } + } + } + + //===== SET LAGRANGE PARAMETERS ===== + // Note that these are now computed at the slice level to reduce + // computations and cleanup code. + enc_mb->lambda_md = p_Vid->lambda_md[currSlice->slice_type][p_Vid->masterQP]; + //#if JCOST_CALC_SCALEUP + enc_mb->lambda_mdfp = LAMBDA_FACTOR(enc_mb->lambda_md); + //#endif + + memcpy(enc_mb->lambda_me, p_Vid->lambda_me[currSlice->slice_type][p_Vid->masterQP], 3 * sizeof(double)); + memcpy(enc_mb->lambda_mf, p_Vid->lambda_mf[currSlice->slice_type][p_Vid->masterQP], 3 * sizeof(int)); + + if (!currSlice->mb_aff_frame_flag) + { + for (l = LIST_0; l < BI_PRED; l++) + { + for(k = 0; k < currSlice->listXsize[l]; k++) + { + if(currSlice->structure != currSlice->listX[l][k]->structure) + { + if (currSlice->structure == TOP_FIELD) + currSlice->listX[l][k]->chroma_vector_adjustment = -2; + else if (currSlice->structure == BOTTOM_FIELD) + currSlice->listX[l][k]->chroma_vector_adjustment = 2; + else + currSlice->listX[l][k]->chroma_vector_adjustment= 0; + } + else + currSlice->listX[l][k]->chroma_vector_adjustment= 0; + } + } + } + else + { + if (enc_mb->curr_mb_field) + { + for (l = currMB->list_offset; l <= currMB->list_offset + LIST_1; l++) + { + for(k = 0; k < currSlice->listXsize[l]; k++) + { + currSlice->listX[l][k]->chroma_vector_adjustment= 0; + if((currMB->mbAddrX & 0x01) == 0 && currSlice->listX[l][k]->structure == BOTTOM_FIELD) + currSlice->listX[l][k]->chroma_vector_adjustment = -2; + if((currMB->mbAddrX & 0x01) == 1 && currSlice->listX[l][k]->structure == TOP_FIELD) + currSlice->listX[l][k]->chroma_vector_adjustment = 2; + } + } + } + else + { + for (l = currMB->list_offset; l <= currMB->list_offset + LIST_1; l++) + { + for(k = 0; k < currSlice->listXsize[l]; k++) + currSlice->listX[l][k]->chroma_vector_adjustment = 0; + } + } + } + + // reset chroma intra predictor to default + currMB->c_ipred_mode = DC_PRED_8; + + + if(p_Inp->SearchMode[p_Vid->view_id] == UM_HEX) + { + UMHEX_decide_intrabk_SAD(currMB); + UMHEX_DefineThresholdMB(p_Vid, p_Inp); + } + else if (p_Inp->SearchMode[p_Vid->view_id] == UM_HEX_SIMPLE) + { + smpUMHEX_decide_intrabk_SAD(currMB); + } + +} + + +void update_mcost(Slice *currSlice, int ref_lambda, short ref, int cur_list, distblk mcost, distblk *bmcost, char *best_ref) +{ + {//! Edited by Joao Carreira - Force InterView Prediction +#if( MVC_EXTENSION_ENABLE) + //! check if you want to force interview prediction + if( currSlice->flag_Force_InterView == 1 && currSlice->view_id != BASE_VIEW_IDX && cur_list == LIST_0) + { + if( currSlice->listX[cur_list][ref]->view_id != BASE_VIEW_IDX ) + return; + + //! check if we already force it + //! if we already force the best reference to be a base view frame we do not + // need to do it again. now just let the encoder run normally + if( currSlice->listX[cur_list][*best_ref]->view_id != BASE_VIEW_IDX ) + { + //! reset best motion cost to the maximum value + *bmcost = INT_MAX; + } + } +#endif + } + + if (mcost < *bmcost) + { + mcost += ref_cost(currSlice, ref_lambda, ref, cur_list); + + if (mcost < *bmcost) + { + *bmcost = mcost; + *best_ref = (char)ref; + } + } +} +/*! +************************************************************************************* +* \brief +* computation of prediction list (including biprediction) cost +************************************************************************************* +*/ +void list_prediction_cost(Macroblock *currMB, int list, int block, int mode, RD_PARAMS *enc_mb, distblk bmcost[5], char best_ref[2]) +{ + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + InputParameters *p_Inp = currMB->p_Inp; + + short ref; + int cur_list = list < BI_PRED ? currMB->list_offset + list : currMB->list_offset; + int ref_lambda = (p_Inp->rdopt) ? enc_mb->lambda_mf[Q_PEL] : enc_mb->lambda_mf[Q_PEL] >> 2; + + //--- get cost and reference frame for forward prediction --- + if (list < BI_PRED) + { + distblk **motion_cost = p_Vid->motion_cost[mode][list]; + + int sp_indicator = (p_Inp->sp2_frame_indicator || p_Inp->sp_output_indicator); + + if (!p_Vid->checkref || list) + { + if (p_Inp->sp2_frame_indicator == 0 && p_Inp->sp_output_indicator == 0) + { + for (ref = 0; ref < currSlice->listXsize[cur_list]; ref++) + { + update_mcost(currSlice, ref_lambda, ref, cur_list, motion_cost[ref][block], &bmcost[list], &best_ref[list]); + } + } + else + { + for (ref = 0; ref < currSlice->listXsize[cur_list]; ref++) + { + // limit the number of reference frames to 1 when switching SP frames are used + if((((currSlice->slice_type != P_SLICE && currSlice->slice_type != SP_SLICE)) || (ref == 0))) + { + update_mcost(currSlice, ref_lambda, ref, cur_list, motion_cost[ref][block], &bmcost[list], &best_ref[list]); + } + } + } + } + else + { + for (ref = 0; ref < currSlice->listXsize[cur_list]; ref++) + { + if (ref == 0 || (p_Inp->RestrictRef && CheckReliabilityOfRef (currMB, block, list, ref, mode))) + { + // limit the number of reference frames to 1 when switching SP frames are used + if( !(sp_indicator) || (((currSlice->slice_type != P_SLICE && currSlice->slice_type != SP_SLICE))|| (ref == 0))) + { + update_mcost(currSlice, ref_lambda, ref, cur_list, motion_cost[ref][block], &bmcost[list], &best_ref[list]); + } + } + } + } + } + else if (list == BI_PRED) + { + if (p_Vid->active_pps->weighted_bipred_idc == 1) + { + int weight_sum = currSlice->wbp_weight[0][(int) best_ref[LIST_0]][(int) best_ref[LIST_1]][0] + + currSlice->wbp_weight[1][(int) best_ref[LIST_0]][(int) best_ref[LIST_1]][0]; + + if (weight_sum < -128 || weight_sum > 127) + { + bmcost[list] = DISTBLK_MAX; + } + else + { + bmcost[list] = ref_cost(currSlice, ref_lambda, (short) best_ref[LIST_0], cur_list) + + ref_cost(currSlice, ref_lambda, (short) best_ref[LIST_1], cur_list + LIST_1); + bmcost[list] += BIDPartitionCost (currMB, mode, block, best_ref, enc_mb->lambda_mf[Q_PEL]); + } + } + else + { + bmcost[list] = ref_cost(currSlice, ref_lambda, (short) best_ref[LIST_0], cur_list) + + ref_cost(currSlice, ref_lambda, (short) best_ref[LIST_1], cur_list + LIST_1); + bmcost[list] += BIDPartitionCost (currMB, mode, block, best_ref, enc_mb->lambda_mf[Q_PEL]); + } + } + else + { + if (p_Vid->active_pps->weighted_bipred_idc == 1) + { + int weight_sum = currSlice->wbp_weight[0][0][0][0] + currSlice->wbp_weight[1][0][0][0]; + + if (weight_sum < -128 || weight_sum > 127) + { + bmcost[list] = DISTBLK_MAX; + } + else + { + bmcost[list] = ref_cost(currSlice, ref_lambda, 0, cur_list) + ref_cost(currSlice, ref_lambda, 0, cur_list + LIST_1); + bmcost[list] += BPredPartitionCost(currMB, mode, block, 0, 0, enc_mb->lambda_mf[Q_PEL], !(list&1)); + } + } + else + { + bmcost[list] = ref_cost(currSlice, ref_lambda, 0, cur_list) + ref_cost(currSlice, ref_lambda, 0, cur_list + LIST_1); + bmcost[list] += BPredPartitionCost(currMB, mode, block, 0, 0, enc_mb->lambda_mf[Q_PEL], !(list&1)); + } + } +} + +static inline distblk compute_ref_cost(Slice *currSlice, RD_PARAMS *enc_mb, int ref, int list) +{ + return weighted_cost(enc_mb->lambda_mf[Q_PEL],((currSlice->listXsize[list] <= 1)? 0 : currSlice->p_Vid->refbits[ref])); +} + +/*! +************************************************************************************* +* \brief +* Determination of prediction list based on simple distortion computation +************************************************************************************* +*/ +void determine_prediction_list( distblk bmcost[5], Info8x8 *best, distblk *cost) +{ + int bestlist; + *cost += distblkminarray ( bmcost, 5, &bestlist); + + if (bestlist <= BI_PRED) //LIST_0, LIST_1 & BI_DIR + { + best->pdir = (char) bestlist; + best->bipred= 0; + } + else //BI_PRED_L0 & BI_PRED_L1 + { + best->pdir = 2; + best->bipred = (char) (bestlist - 2); + //! Edited by Joao Carreira - Do not limit bi prediction to the first frame of list + best->ref[LIST_0] = 0; + best->ref[LIST_1] = 0; + } +} + + +/*! +************************************************************************************* +* \brief +* RD decision process +************************************************************************************* +*/ +void compute_mode_RD_cost(Macroblock *currMB, + RD_PARAMS *enc_mb, + short mode, + short *inter_skip) +{ + VideoParameters *p_Vid = currMB->p_Vid; + InputParameters *p_Inp = currMB->p_Inp; + int terminate_16x16 = 0, terminate_trans = 0, ctr16x16 = 0; + Slice *currSlice = currMB->p_Slice; + RDOPTStructure *p_RDO = currSlice->p_RDO; + int bslice = (currSlice->slice_type == B_SLICE); + + //--- transform size --- + currMB->luma_transform_size_8x8_flag = (byte) (p_Inp->Transform8x8Mode==2 + ? (mode >= 1 && mode <= 3) + || (mode == 0 && bslice && p_Vid->active_sps->direct_8x8_inference_flag) + || ((mode == P8x8) && (enc_mb->valid[4])) + : 0); + + currSlice->set_modes_and_refs_for_blocks (currMB, (short) mode); + //memset( currSlice->cofAC[0][0][0], 0, 2080 * sizeof(int)); // 4 * 4 * 2 * 65 + + // Encode with coefficients + currSlice->NoResidueDirect = 0; + + if ((p_Inp->FastCrIntraDecision ) || (currMB->c_ipred_mode == DC_PRED_8 || (IS_INTRA(currMB) ))) + { + do + { + // This seems to have a problem since we are not properly copying the right b8x8info (transform based) + terminate_16x16 = bslice_16x16_termination_control(p_Inp, p_Vid->b8x8info, &ctr16x16, mode, bslice); + do + { + RD_8x8DATA *t8x8_data = currMB->luma_transform_size_8x8_flag ? p_RDO->tr8x8 : p_RDO->tr4x4; + + if (mode == P8x8 ) //&& !(p_Inp->RDOQ_QP_Num > 1 && currSlice->dQPCnt > 0)) + { + p_Vid->b8x8info->best[P8x8][0] = t8x8_data->part[0]; + p_Vid->b8x8info->best[P8x8][1] = t8x8_data->part[1]; + p_Vid->b8x8info->best[P8x8][2] = t8x8_data->part[2]; + p_Vid->b8x8info->best[P8x8][3] = t8x8_data->part[3]; + } + // check if prediction parameters are in valid range. + if (CheckPredictionParams(currMB, p_Vid->b8x8info, mode) == TRUE) + { + if (RDCost_for_macroblocks (currMB, enc_mb->lambda_mdfp, mode)) + { + //Rate control + if (p_Inp->RCEnable) + { + if(mode == P8x8) + { + rc_store_diff(currSlice->diffy, &p_Vid->pCurImg[currMB->opix_y], currMB->pix_x, t8x8_data->mpr8x8); + } + else + rc_store_diff(currSlice->diffy, &p_Vid->pCurImg[currMB->opix_y], currMB->pix_x, p_RDO->pred); + } + + store_macroblock_parameters (currMB, mode); + + if(p_Inp->rdopt == 2 && mode == 0 && p_Inp->EarlySkipEnable) + { + // check transform quantized coeff. + if(currMB->cbp == 0) + *inter_skip = 1; + } + } + } + // This code needs to be fixed - BUG? ATOUR + + terminate_trans = transform_termination_control(currMB, mode); + + }while (!terminate_trans); + }while (!terminate_16x16); + + // Encode with no coefficients. Currently only for direct. This could be extended to all other modes as in example. + //if (bslice && mode < P8x8 && (*inter_skip == 0) && enc_mb->valid[mode] && currMB->cbp && (currMB->cbp&15) != 15 && !p_Inp->nobskip + if ( (bslice && mode == 0 && (*inter_skip == 0) && enc_mb->valid[mode] && currMB->cbp && (currMB->cbp&15) != 15 && !p_Inp->nobskip + && !(currMB->qp_scaled[0] == 0 && p_Vid->active_sps->lossless_qpprime_flag==1) ) + ) + { + currSlice->NoResidueDirect = 1; + if(mode == P8x8) + { + RD_8x8DATA *t8x8_data = currMB->luma_transform_size_8x8_flag ? p_RDO->tr8x8 : p_RDO->tr4x4; + p_Vid->b8x8info->best[P8x8][0] = t8x8_data->part[0]; + p_Vid->b8x8info->best[P8x8][1] = t8x8_data->part[1]; + p_Vid->b8x8info->best[P8x8][2] = t8x8_data->part[2]; + p_Vid->b8x8info->best[P8x8][3] = t8x8_data->part[3]; + if(p_Inp->Transform8x8Mode == 1 && currMB->valid_8x8 && currMB->valid_4x4) + { + RestoreMV8x8(currSlice, (!currMB->luma_transform_size_8x8_flag)); + } + } + if (CheckPredictionParams(currMB, p_Vid->b8x8info, mode) == TRUE) + { + if (RDCost_for_macroblocks (currMB, enc_mb->lambda_mdfp, mode)) + { + //Rate control + if (p_Inp->RCEnable) + rc_store_diff(currSlice->diffy, &p_Vid->pCurImg[currMB->opix_y], currMB->pix_x, p_RDO->pred); + + if (p_Vid->AdaptiveRounding) + reset_adaptive_rounding_direct(p_Vid); + + store_macroblock_parameters (currMB, mode); + } + } + if(mode == P8x8) + { + if(p_Inp->Transform8x8Mode == 1 && currMB->valid_8x8 && currMB->valid_4x4) + { + RestoreMV8x8(currSlice, 0); + } + } + } + + //modes 0 and 1 of a B frame + if (p_Vid->AdaptiveRounding && bslice && mode <= 1) + { + if (currMB->temp_transform_size_8x8_flag) + update_adaptive_rounding_16x16( p_Vid, p_Vid->ARCofAdj8x8, mode); + else + update_adaptive_rounding_16x16( p_Vid, p_Vid->ARCofAdj4x4, mode); + } + } +} + +void get_initial_mb16x16_cost(Macroblock* currMB) +{ + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + RDOPTStructure *p_RDO = currSlice->p_RDO; + if (currMB->mb_left && currMB->mb_up) + { + p_Vid->mb16x16_cost = (p_Vid->mb16x16_cost_frame[currMB->mbAddrX - 1] + + p_Vid->mb16x16_cost_frame[currMB->mbAddrX - (p_Vid->width>>4)] + 1)/2.0; + } + else if (currMB->mb_left) + { + p_Vid->mb16x16_cost = p_Vid->mb16x16_cost_frame[currMB->mbAddrX - 1]; + } + else if (currMB->mb_up) + { + p_Vid->mb16x16_cost = p_Vid->mb16x16_cost_frame[currMB->mbAddrX - (p_Vid->width>>4)]; + } + else + { + p_Vid->mb16x16_cost = CALM_MF_FACTOR_THRESHOLD; + } + + p_RDO->lambda_mf_factor = p_Vid->mb16x16_cost < CALM_MF_FACTOR_THRESHOLD ? 1.0 : sqrt(p_Vid->mb16x16_cost / (CALM_MF_FACTOR_THRESHOLD * p_Vid->lambda_mf_factor[currSlice->slice_type][p_Vid->qp])); +} + +void adjust_mb16x16_cost(Macroblock *currMB, distblk cost) +{ + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + RDOPTStructure *p_RDO = currSlice->p_RDO; + + p_Vid->mb16x16_cost = (double) cost; + p_Vid->mb16x16_cost_frame[currMB->mbAddrX] = p_Vid->mb16x16_cost; +#if JCOST_CALC_SCALEUP + p_RDO->lambda_mf_factor = (p_Vid->mb16x16_cost < CALM_MF_FACTOR_THRESHOLD * (1 << LAMBDA_ACCURACY_BITS)) + ? 1.0 + : sqrt(p_Vid->mb16x16_cost / (CALM_MF_FACTOR_THRESHOLD*(1<lambda_mf_factor[currSlice->slice_type][p_Vid->qp])); +#else + p_RDO->lambda_mf_factor = (p_Vid->mb16x16_cost < CALM_MF_FACTOR_THRESHOLD) + ? 1.0 + : sqrt(p_Vid->mb16x16_cost / (CALM_MF_FACTOR_THRESHOLD * p_Vid->lambda_mf_factor[currSlice->slice_type][p_Vid->qp])); +#endif +} + +void update_lambda_costs(Macroblock *currMB, RD_PARAMS *enc_mb, int lambda_mf[3]) +{ + InputParameters *p_Inp = currMB->p_Inp; + RDOPTStructure *p_RDO = currMB->p_Slice->p_RDO; + + int MEPos; + for (MEPos = 0; MEPos < 3; MEPos ++) + { + lambda_mf[MEPos] = p_Inp->CtxAdptLagrangeMult == 0 ? enc_mb->lambda_mf[MEPos] : (int)(enc_mb->lambda_mf[MEPos] * sqrt(p_RDO->lambda_mf_factor)); + } +} + +/*! +************************************************************************************* +* \brief +* Return array's minimum and its index +************************************************************************************* +*/ +int iminarray ( int arr[], int size, int *minind ) +{ + int i; + int mincand = arr[0]; + *minind = 0; + for ( i = 1; i < size; i++ ) + { + if (arr[i] < mincand) + { + mincand = arr[i]; + *minind = i; + } + } + return mincand; +} + +distblk distblkminarray ( distblk arr[], int size, int *minind ) +{ + int i; + distblk mincand = arr[0]; + *minind = 0; + for ( i = 1; i < size; i++ ) + { + if (arr[i] < mincand) + { + mincand = arr[i]; + *minind = i; + } + } + return mincand; +} + +/*! +************************************************************************************* +* \brief +* Determines whether bi prediction is enabaled for current mode +************************************************************************************* +*/ +int is_bipred_enabled(VideoParameters *p_Vid, int mode) +{ + //return 0; + return p_Vid->bipred_enabled[(mode == P8x8) ? 4: mode]; +} + +/*! +************************************************************************************* +* \brief +* Decides whether to perform transform 8x8 for this mode +************************************************************************************* +*/ +int transform_termination_control(Macroblock* currMB, int mode) +{ + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + InputParameters *p_Inp = currMB->p_Inp; + + // Go through transform modes. + // Note that if currMB->cbp is 0 one could choose to skip 8x8 mode + // although this could be due to deadzoning decisions. + //if (p_Inp->Transform8x8Mode==1 && currMB->cbp!=0) + if (p_Inp->Transform8x8Mode == 1) + { + //=========== try the 8x8 transform with mb_types 16x16,16x8, 8x16, 8x8, and DIRECT 16x16 =========== + if (currMB->luma_transform_size_8x8_flag == FALSE && + ((mode >= 1 && mode <= 3) || ((currSlice->slice_type == B_SLICE) && mode == 0 && p_Vid->active_sps->direct_8x8_inference_flag) || (mode == P8x8))) + //if (currMB->luma_transform_size_8x8_flag == FALSE && + //((mode >= 1 && mode <= 3) || ((currSlice->slice_type == B_SLICE) && mode == 0 && active_sps->direct_8x8_inference_flag))) + { + //try with 8x8 transform size + currMB->luma_transform_size_8x8_flag = TRUE; + return 0; + } + else + { + currMB->luma_transform_size_8x8_flag = FALSE; + return 1; + } + } + else + { + return 1; + } +} + +/*! +************************************************************************************* +* \brief +* Update prediction direction for mode P16x16 to check all prediction directions +************************************************************************************* +*/ +int bslice_16x16_termination_control(InputParameters *p_Inp, Block8x8Info *b8x8info, int *ctr16x16, int mode, int bslice) +{ + int lastcheck = 1; + //--- for INTER16x16 in BSLICEs check all prediction directions --- + if (mode == 1 && bslice) + { + char pdir = 0; + short i; + char bipred_me = 0; + + switch (*ctr16x16) + { + case 0: + pdir = 0; + lastcheck = 0; + break; + case 1: + pdir = 1; + lastcheck = 0; + break; + case 2: + pdir = 2; + if (p_Inp->BiPredMotionEstimation) + { + lastcheck = 0; + } + break; + case 3: + pdir = 2; + bipred_me = 1; + lastcheck = 0; + break; + case 4: + pdir = 2; + bipred_me = 2; + break; + default: + error("invalid 'ctr16x16' value", -1); + break; + } + for (i = 0; i< 4; i++) + { + b8x8info->best[1][i].bipred = bipred_me; + b8x8info->best[1][i].pdir = pdir; + } + (*ctr16x16)++; + } + return lastcheck; +} + +/*! +************************************************************************************** +* \brief +* Compute bipred costs +************************************************************************************** +*/ +void get_bipred_cost(Macroblock *currMB, int mode, int block, int i, int j, Info8x8 *best, RD_PARAMS *enc_mb, distblk bmcost[5]) +{ + Slice *currSlice = currMB->p_Slice; + MotionVector *bi0_mv_l0 = &currSlice->bipred_mv[0][LIST_0][0][mode][j][i]; + MotionVector *bi0_mv_l1 = &currSlice->bipred_mv[0][LIST_1][0][mode][j][i]; + MotionVector *bi1_mv_l0 = &currSlice->bipred_mv[1][LIST_0][0][mode][j][i]; + MotionVector *bi1_mv_l1 = &currSlice->bipred_mv[1][LIST_1][0][mode][j][i]; + distblk MaxVal = DISTBLK_MAX; + + if (best->ref[0] == 0 && best->ref[1] == 0) + { + MotionVector *single_mv_l0 = &currSlice->all_mv [LIST_0][0][mode][j][i]; + MotionVector *single_mv_l1 = &currSlice->all_mv [LIST_1][0][mode][j][i]; + + if ((single_mv_l0->mv_x != bi0_mv_l0->mv_x) || (single_mv_l0->mv_y != bi0_mv_l0->mv_y) || + (single_mv_l1->mv_x != bi0_mv_l1->mv_x) || (single_mv_l1->mv_y != bi0_mv_l1->mv_y)) + list_prediction_cost(currMB, BI_PRED_L0, block, mode, enc_mb, bmcost, 0); + else + bmcost[BI_PRED_L0] = MaxVal; + + if ((single_mv_l0->mv_x != bi1_mv_l0->mv_x) || (single_mv_l0->mv_y != bi1_mv_l0->mv_y) || + (single_mv_l1->mv_x != bi1_mv_l1->mv_x) || (single_mv_l1->mv_y != bi1_mv_l1->mv_y)) + { + if ((bi0_mv_l0->mv_x != bi1_mv_l0->mv_x) || (bi0_mv_l0->mv_y != bi1_mv_l0->mv_y) || + (bi0_mv_l1->mv_x != bi1_mv_l1->mv_x) || (bi0_mv_l1->mv_y != bi1_mv_l1->mv_y)) + list_prediction_cost(currMB, BI_PRED_L1, block, mode, enc_mb, bmcost, 0); + else + bmcost[BI_PRED_L1] = MaxVal; + } + else + bmcost[BI_PRED_L1] = MaxVal; + } + else + { + //! Edited by Joao Carreira + list_prediction_cost(currMB, BI_PRED_L0, block, mode, enc_mb, bmcost, 0); + //list_prediction_cost(currMB, BI_PRED_L0, block, mode, enc_mb, bmcost, best->ref); + if ((bi0_mv_l0->mv_x != bi1_mv_l0->mv_x) || (bi0_mv_l0->mv_y != bi1_mv_l0->mv_y) || + (bi0_mv_l1->mv_x != bi1_mv_l1->mv_x) || (bi0_mv_l1->mv_y != bi1_mv_l1->mv_y)) + list_prediction_cost(currMB, BI_PRED_L1, block, mode, enc_mb, bmcost, 0); + //list_prediction_cost(currMB, BI_PRED_L1, block, mode, enc_mb, bmcost, best->ref); + else + bmcost[BI_PRED_L1] = MaxVal; + } +} + + diff --git a/lencod/src/mode_decision_P8x8.c b/lencod/src/mode_decision_P8x8.c new file mode 100644 index 0000000..829dff8 --- /dev/null +++ b/lencod/src/mode_decision_P8x8.c @@ -0,0 +1,1080 @@ + +/*! + *************************************************************************** + * \file mode_decision_8x8.c + * + * \brief + * Mode decision functions for 8x8 submacroblock + * + ************************************************************************** + */ + +#include +#include +#include + +#include "global.h" +#include "image.h" +#include "mode_decision.h" +#include "macroblock.h" +#include "errdo.h" +#include "q_around.h" +#include "md_common.h" +#include "rdopt.h" + + +void copy_part_info(Info8x8 *b8x8, Info8x8 *part) +{ + b8x8[0] = part[0]; + b8x8[1] = part[1]; + b8x8[2] = part[2]; + b8x8[3] = part[3]; +} + +/*! +************************************************************************************* +* \brief +* Mode Decision for an 8x8 sub-macroblock +************************************************************************************* +*/ +void submacroblock_mode_decision_p_slice(Macroblock *currMB, + RD_PARAMS *enc_mb, + RD_8x8DATA *dataTr, + int ****cofACtr, + int block, + distblk *cost) +{ + VideoParameters *p_Vid = currMB->p_Vid; + InputParameters *p_Inp = currMB->p_Inp; + Slice *currSlice = currMB->p_Slice; + PicMotionParams **motion = p_Vid->enc_picture->mv_info; + int transform8x8 = currMB->luma_transform_size_8x8_flag; + int64 curr_cbp_blk; + int j, k; + int index; + int mode; + distblk min_rdcost, rdcost = 0; + distblk min_cost8x8; + distblk bmcost[5] = {DISTBLK_MAX}; + int cnt_nonz = 0; + int best_cnt_nonz = 0; + int maxindex = (transform8x8) ? 2 : 5; + int block_x, block_y; + int lambda_mf[3]; + + int ****fadjust = transform8x8? p_Vid->ARCofAdj8x8 : p_Vid->ARCofAdj4x4; + + //--- set coordinates --- + int j0 = ((block>>1)<<3); + int j1 = (j0>>2); + int i0 = ((block&0x01)<<3); + int i1 = (i0>>2); + + Boolean valid_8x8 = FALSE; + Boolean stored_state_8x8 = FALSE; + +#ifdef BEST_NZ_COEFF + int best_nz_coeff[2][2]; +#endif + + Info8x8 *partition = &(dataTr->part[block]); + Info8x8 best = init_info_8x8_struct(); + + *partition = best; + if (transform8x8) + currMB->valid_8x8 = FALSE; + else + currMB->valid_4x4 = FALSE; + +#ifdef BEST_NZ_COEFF + for(j = 0; j <= 1; j++) + { + for(i = 0; i <= 1; i++) + best_nz_coeff[i][j] = p_Vid->nz_coeff[currMB->mbAddrX][i1 + i][j1 + j] = 0; + } +#endif + + if(p_Inp->subMBCodingState == 2) + currSlice->store_coding_state(currMB, currSlice->p_RDO->cs_tmp); + + //===== LOOP OVER POSSIBLE CODING MODES FOR 8x8 SUB-PARTITION ===== + for (min_cost8x8 = DISTBLK_MAX, min_rdcost = DISTBLK_MAX, index = 1; index < maxindex; index++) + { + + mode = b8_mode_table[index]; + + best.mode = (char) mode; + *cost = 0; + + if (enc_mb->valid[mode] && (!(transform8x8 == 1 && mode > 4)) && (transform8x8 == 0 || mode != 0 )) + { + if (transform8x8) + { + currMB->valid_8x8 = TRUE; + } + else + currMB->valid_4x4 = TRUE; + valid_8x8 = TRUE; + curr_cbp_blk = 0; + + { + short b_ref; + + //======= motion estimation for all reference frames ======== + //----------------------------------------------------------- + memcpy(lambda_mf, enc_mb->lambda_mf, 3 * sizeof(int)); + + if (p_Inp->CtxAdptLagrangeMult == 1) + { + RDOPTStructure *p_RDO = currSlice->p_RDO; + lambda_mf[F_PEL] = (int)(lambda_mf[F_PEL] * p_RDO->lambda_mf_factor); + lambda_mf[H_PEL] = (int)(lambda_mf[H_PEL] * p_RDO->lambda_mf_factor); + lambda_mf[Q_PEL] = (int)(lambda_mf[Q_PEL] * p_RDO->lambda_mf_factor); + } + + SubPartitionMotionSearch (currMB, mode, block, lambda_mf); + + //--- get cost and reference frame for LIST 0 prediction --- + bmcost[LIST_0] = DISTBLK_MAX; + list_prediction_cost(currMB, LIST_0, block, mode, enc_mb, bmcost, best.ref); + + //store LIST 0 reference index for every block + block_x = currMB->block_x + (block & 0x01)*2; + block_y = currMB->block_y + (block & 0x02); + b_ref = best.ref[LIST_0]; + + motion[block_y ][block_x ].ref_pic [LIST_0] = currSlice->listX[currMB->list_offset][b_ref]; + motion[block_y ][block_x ].mv [LIST_0] = currSlice->all_mv[LIST_0][b_ref][mode][j1 ][i1 ]; + motion[block_y ][block_x ].ref_idx [LIST_0] = (char) b_ref; + motion[block_y ][block_x + 1].ref_pic [LIST_0] = currSlice->listX[currMB->list_offset][b_ref]; + motion[block_y ][block_x + 1].mv [LIST_0] = currSlice->all_mv[LIST_0][b_ref][mode][j1 ][i1 + 1]; + motion[block_y ][block_x + 1].ref_idx [LIST_0] = (char) b_ref; + motion[block_y + 1][block_x ].ref_pic [LIST_0] = currSlice->listX[currMB->list_offset][b_ref]; + motion[block_y + 1][block_x ].mv [LIST_0] = currSlice->all_mv[LIST_0][b_ref][mode][j1 + 1][i1 ]; + motion[block_y + 1][block_x ].ref_idx [LIST_0] = (char) b_ref; + motion[block_y + 1][block_x + 1].ref_pic [LIST_0] = currSlice->listX[currMB->list_offset][b_ref]; + motion[block_y + 1][block_x + 1].mv [LIST_0] = currSlice->all_mv[LIST_0][b_ref][mode][j1 + 1][i1 + 1]; + motion[block_y + 1][block_x + 1].ref_idx [LIST_0] = (char) b_ref; + + best.pdir = 0; + *cost = bmcost[LIST_0]; + } // if (mode!=0) + + //--- get and check rate-distortion cost --- + rdcost = rdcost_for_8x8blocks (currMB, dataTr, &cnt_nonz, &curr_cbp_blk, enc_mb->lambda_mdfp, block, (short) mode, &best, min_rdcost); + + //--- set variables if best mode has changed --- + if (rdcost < min_rdcost) + { + min_cost8x8 = *cost; + min_rdcost = rdcost; + *partition = best; + partition->mode = (char) mode; + currMB->b8x8[block].mode = (char) mode; + +#ifdef BEST_NZ_COEFF + if (cnt_nonz) + { + for(i = 0; i <= 1; i++) + { + best_nz_coeff[i][0]= p_Vid->nz_coeff[currMB->mbAddrX][i1 + i][j1 ]; + best_nz_coeff[i][1]= p_Vid->nz_coeff[currMB->mbAddrX][i1 + i][j1 + 1]; + } + } + else + { + for(i = 0; i <= 1; i++) + { + best_nz_coeff[i][0]= 0; + best_nz_coeff[i][1]= 0; + } + } +#endif + + //--- store number of nonzero coefficients --- + best_cnt_nonz = cnt_nonz; + + //--- store block cbp --- + dataTr->cbp_blk8x8 &= (~(0x33 << (((block>>1)<<3)+((block & 0x01)<<1)))); // delete bits for block + dataTr->cbp_blk8x8 |= curr_cbp_blk; + + //--- store coefficients --- + memcpy(&cofACtr[0][0][0][0],&currSlice->cofAC[block][0][0][0], 4 * 2 * 65 * sizeof(int)); + + if( currSlice->P444_joined ) + { + //--- store coefficients --- + memcpy(&cofACtr[1][0][0][0],&currSlice->cofAC[block + 4][0][0][0], 4 * 2 * 65 * sizeof(int)); + memcpy(&cofACtr[2][0][0][0],&currSlice->cofAC[block + 8][0][0][0], 4 * 2 * 65 * sizeof(int)); + } + + //--- store reconstruction and prediction --- + copy_image_data_8x8(&dataTr->rec_mbY8x8[j0], &p_Vid->enc_picture->imgY[currMB->pix_y + j0], i0, currMB->pix_x + i0); + copy_image_data_8x8(&dataTr->mpr8x8[j0], &currSlice->mb_pred[0][j0], i0, i0); + + if (p_Inp->rdopt == 3) + { + errdo_store_best_b8x8(currMB, transform8x8, block); + } + + if(currSlice->slice_type == SP_SLICE) + { + for (j = j0; j < j0 + BLOCK_SIZE_8x8; j++) + { + memcpy(&dataTr->lrec[j][i0],&p_Vid->lrec[currMB->pix_y + j][currMB->pix_x + i0], BLOCK_SIZE_8x8 * sizeof(int)); + } + } + + if(currSlice->P444_joined) + { + copy_image_data_8x8(&dataTr->rec_mb8x8_cr[0][j0], &p_Vid->enc_picture->imgUV[0][currMB->pix_y + j0], i0, currMB->pix_x + i0); + copy_image_data_8x8(&dataTr->mpr8x8CbCr[0][j0], &currSlice->mb_pred[1][j0], i0, i0); + + copy_image_data_8x8(&dataTr->rec_mb8x8_cr[1][j0], &p_Vid->enc_picture->imgUV[1][currMB->pix_y + j0], i0, currMB->pix_x + i0); + copy_image_data_8x8(&dataTr->mpr8x8CbCr[1][j0], &currSlice->mb_pred[2][j0], i0, i0); + } + + //--- store best 8x8 coding state --- + if (block < 3) + { + currSlice->store_coding_state (currMB, currSlice->p_RDO->cs_b8); + stored_state_8x8 = TRUE; + } + } // if (rdcost <= min_rdcost) + + //--- re-set coding state as it was before coding with current mode was performed --- + if (index != maxindex - 1) + { + if(p_Inp->subMBCodingState == 1) + currSlice->reset_coding_state (currMB, currSlice->p_RDO->cs_cm); + else if(p_Inp->subMBCodingState == 2) + currSlice->reset_coding_state (currMB, currSlice->p_RDO->cs_tmp); + } + } // if ((enc_mb->valid[mode] && (transform8x8 == 0 || mode != 0 || (mode == 0 && active_sps->direct_8x8_inference_flag))) + } // for (min_rdcost=1e30, index = 1; index<6; index++) + + if (valid_8x8 == TRUE) + { +#ifdef BEST_NZ_COEFF + for(i = 0; i <= 1; i++) + { + for(j = 0; j <= 1; j++) + p_Vid->nz_coeff[currMB->mbAddrX][i1 + i][j1 + j] = best_nz_coeff[i][j]; + } +#endif + + if (!transform8x8) + { + if (min_cost8x8 != DISTBLK_MAX) + dataTr->mb_p8x8_cost += min_cost8x8; + else + dataTr->mb_p8x8_cost = DISTBLK_MAX; + } + + //----- set cbp and count of nonzero coefficients --- + if (best_cnt_nonz) + { + dataTr->cbp8x8 |= (1 << block); + dataTr->cnt_nonz_8x8 += best_cnt_nonz; + } + + if (!transform8x8) + { + if (block < 3) + { + //===== re-set reconstructed block ===== + j0 = 8*(block >> 1); + i0 = 8*(block & 0x01); + + // need to double check code since original did not use i0 + copy_image_data_8x8(&p_Vid->enc_picture->imgY[currMB->pix_y + j0], &dataTr->rec_mbY8x8[j0], currMB->pix_x + i0, i0); + + if (p_Inp->rdopt == 3) + { + errdo_get_best_b8x8(currMB, transform8x8, block); + } + + if(currSlice->slice_type == SP_SLICE) + { + for (j = j0; j < j0 + BLOCK_SIZE_8x8; j++) + { + memcpy(&p_Vid->lrec[currMB->pix_y + j][currMB->pix_x], dataTr->lrec[j], 2 * BLOCK_SIZE * sizeof(int)); // reset the coefficients for SP slice + } + } + + if(currSlice->P444_joined) + { + + for (k=0; k<2; k++) + { + copy_image_data_8x8(&p_Vid->enc_picture->imgUV[k][currMB->pix_y + j0], &dataTr->rec_mb8x8_cr[k][j0], currMB->pix_x + i0, i0); + } + } + } // if (block<3) + } + else + { + //======= save motion data for 8x8 partition for transform size 8x8 ======== + currSlice->store_8x8_motion_vectors(currSlice, 0, block, partition); + } + + //===== set motion vectors and reference frames (prediction) ===== + currSlice->set_ref_and_motion_vectors (currMB, motion, partition, block); + + //===== set the coding state after current block ===== + //if (transform8x8 == 0 || block < 3) + if (stored_state_8x8 == TRUE) + currSlice->reset_coding_state (currMB, currSlice->p_RDO->cs_b8); + else + { + currSlice->reset_coding_state (currMB, currSlice->p_RDO->cs_cm); + update_adaptive_rounding_8x8(p_Vid, p_Inp, dataTr, fadjust); + } + } +} + +/*! +************************************************************************************* +* \brief +* Mode Decision for an 8x8 sub-macroblock +************************************************************************************* +*/ +void submacroblock_mode_decision_b_slice(Macroblock *currMB, + RD_PARAMS *enc_mb, + RD_8x8DATA *dataTr, + int ****cofACtr, + int block, + distblk *cost) +{ + VideoParameters *p_Vid = currMB->p_Vid; + InputParameters *p_Inp = currMB->p_Inp; + Slice *currSlice = currMB->p_Slice; + PicMotionParams **motion = p_Vid->enc_picture->mv_info; + int transform8x8 = currMB->luma_transform_size_8x8_flag; + int maxindex = (transform8x8) ? 2 : 5; + int64 curr_cbp_blk; + int k; + int index; + int mode; + distblk min_rdcost, rdcost = 0; + distblk min_cost8x8; + distblk bmcost[5] = {DISTBLK_MAX}; + int cnt_nonz = 0; + int best_cnt_nonz = 0; + int block_x, block_y; + int lambda_mf[3]; + + int ****fadjust = transform8x8? p_Vid->ARCofAdj8x8 : p_Vid->ARCofAdj4x4; + + //--- set coordinates --- + int j0 = ((block>>1)<<3); + int j1 = (j0>>2); + int i0 = ((block&0x01)<<3); + int i1 = (i0>>2); + + Boolean valid_8x8 = FALSE; + Boolean stored_state_8x8 = FALSE; + +#ifdef BEST_NZ_COEFF + int best_nz_coeff[2][2]; +#endif + + + Info8x8 *partition = &(dataTr->part[block]); + Info8x8 best = init_info_8x8_struct(); + *partition = best; + if (transform8x8) + currMB->valid_8x8 = FALSE; + else + currMB->valid_4x4 = FALSE; + +#ifdef BEST_NZ_COEFF + for(j = 0; j <= 1; j++) + { + for(i = 0; i <= 1; i++) + best_nz_coeff[i][j] = p_Vid->nz_coeff[currMB->mbAddrX][i1 + i][j1 + j] = 0; + } +#endif + + if(p_Inp->subMBCodingState == 2) + currSlice->store_coding_state(currMB, currSlice->p_RDO->cs_tmp); + + //===== LOOP OVER POSSIBLE CODING MODES FOR 8x8 SUB-PARTITION ===== + for (min_cost8x8 = DISTBLK_MAX, min_rdcost = DISTBLK_MAX, index = 0; index < maxindex; index++) + { + + mode = b8_mode_table[index]; + + best.mode = (char) mode; + *cost = 0; + + if (enc_mb->valid[mode] && (!(transform8x8 == 1 && mode > 4)) && (transform8x8 == 0 || mode != 0 || (mode == 0 && p_Vid->active_sps->direct_8x8_inference_flag))) + { + if (transform8x8) + { + currMB->valid_8x8 = TRUE; + } + else + currMB->valid_4x4 = TRUE; + + valid_8x8 = TRUE; + curr_cbp_blk = 0; + + if (mode==0) //--- Direct8x8 Mode --- + { + block_x = currMB->block_x + (block & 0x01)*2; + block_y = currMB->block_y + (block & 0x02); + best.ref[LIST_0] = currSlice->direct_ref_idx[block_y][block_x][LIST_0]; + best.ref[LIST_1] = currSlice->direct_ref_idx[block_y][block_x][LIST_1]; + best.pdir = currSlice->direct_pdir[block_y][block_x]; + } // if (mode==0) + else + { + short b_ref; + + //======= motion estimation for all reference frames ======== + //----------------------------------------------------------- + memcpy(lambda_mf, enc_mb->lambda_mf, 3 * sizeof(int)); + + if (p_Inp->CtxAdptLagrangeMult == 1) + { + RDOPTStructure *p_RDO = currSlice->p_RDO; + lambda_mf[F_PEL] = (int)(lambda_mf[F_PEL] * p_RDO->lambda_mf_factor); + lambda_mf[H_PEL] = (int)(lambda_mf[H_PEL] * p_RDO->lambda_mf_factor); + lambda_mf[Q_PEL] = (int)(lambda_mf[Q_PEL] * p_RDO->lambda_mf_factor); + } + + SubPartitionMotionSearch (currMB, mode, block, lambda_mf); + + //--- get cost and reference frame for LIST 0 prediction --- + bmcost[LIST_0] = DISTBLK_MAX; + list_prediction_cost(currMB, LIST_0, block, mode, enc_mb, bmcost, best.ref); + + //store LIST 0 reference index for every block + block_x = currMB->block_x + (block & 0x01)*2; + block_y = currMB->block_y + (block & 0x02); + b_ref = best.ref[LIST_0]; + + motion[block_y ][block_x ].ref_pic [LIST_0] = currSlice->listX[currMB->list_offset][b_ref]; + motion[block_y ][block_x ].mv [LIST_0] = currSlice->all_mv[LIST_0][b_ref][mode][j1 ][i1 ]; + motion[block_y ][block_x ].ref_idx [LIST_0] = (char) b_ref; + motion[block_y ][block_x + 1].ref_pic [LIST_0] = currSlice->listX[currMB->list_offset][b_ref]; + motion[block_y ][block_x + 1].mv [LIST_0] = currSlice->all_mv[LIST_0][b_ref][mode][j1 ][i1 + 1]; + motion[block_y ][block_x + 1].ref_idx [LIST_0] = (char) b_ref; + motion[block_y + 1][block_x ].ref_pic [LIST_0] = currSlice->listX[currMB->list_offset][b_ref]; + motion[block_y + 1][block_x ].mv [LIST_0] = currSlice->all_mv[LIST_0][b_ref][mode][j1 + 1][i1 ]; + motion[block_y + 1][block_x ].ref_idx [LIST_0] = (char) b_ref; + motion[block_y + 1][block_x + 1].ref_pic [LIST_0] = currSlice->listX[currMB->list_offset][b_ref]; + motion[block_y + 1][block_x + 1].mv [LIST_0] = currSlice->all_mv[LIST_0][b_ref][mode][j1 + 1][i1 + 1]; + motion[block_y + 1][block_x + 1].ref_idx [LIST_0] = (char) b_ref; + + //--- get cost and reference frame for LIST 1 prediction --- + bmcost[LIST_1] = DISTBLK_MAX; + bmcost[BI_PRED] = DISTBLK_MAX; + list_prediction_cost(currMB, LIST_1, block, mode, enc_mb, bmcost, best.ref); + + // Compute bipredictive cost between best list 0 and best list 1 references + list_prediction_cost(currMB, BI_PRED, block, mode, enc_mb, bmcost, best.ref); + + // currently Bi prediction ME is only supported for modes 1, 2, 3 and only for ref 0 and only for ref 0 + if (is_bipred_enabled(p_Vid, mode)) + { + get_bipred_cost(currMB, mode, block, i1, j1, &best, enc_mb, bmcost); + } + else + { + bmcost[BI_PRED_L0] = DISTBLK_MAX; + bmcost[BI_PRED_L1] = DISTBLK_MAX; + } + + //--- get prediction direction ---- + determine_prediction_list(bmcost, &best, cost); + + //store backward reference index for every block + for (k = LIST_0; k <= LIST_1; k++) + { + motion[block_y ][block_x ].ref_idx[k] = best.ref[k]; + motion[block_y ][block_x + 1].ref_idx[k] = best.ref[k]; + motion[block_y + 1][block_x ].ref_idx[k] = best.ref[k]; + motion[block_y + 1][block_x + 1].ref_idx[k] = best.ref[k]; + + if (best.bipred) + { + motion[block_y ][block_x ].mv[k] = currSlice->bipred_mv[best.bipred - 1][k][(short) best.ref[k]][mode][j1 ][i1 ]; + motion[block_y ][block_x + 1].mv[k] = currSlice->bipred_mv[best.bipred - 1][k][(short) best.ref[k]][mode][j1 ][i1 + 1]; + motion[block_y + 1][block_x ].mv[k] = currSlice->bipred_mv[best.bipred - 1][k][(short) best.ref[k]][mode][j1 + 1][i1 ]; + motion[block_y + 1][block_x + 1].mv[k] = currSlice->bipred_mv[best.bipred - 1][k][(short) best.ref[k]][mode][j1 + 1][i1 + 1]; + } + else + { + motion[block_y ][block_x ].mv[k] = currSlice->all_mv[k][(short) best.ref[k]][mode][j1 ][i1 ]; + motion[block_y ][block_x + 1].mv[k] = currSlice->all_mv[k][(short) best.ref[k]][mode][j1 ][i1 + 1]; + motion[block_y + 1][block_x ].mv[k] = currSlice->all_mv[k][(short) best.ref[k]][mode][j1 + 1][i1 ]; + motion[block_y + 1][block_x + 1].mv[k] = currSlice->all_mv[k][(short) best.ref[k]][mode][j1 + 1][i1 + 1]; + } + } + } // if (mode!=0) + + //--- get and check rate-distortion cost --- + rdcost = rdcost_for_8x8blocks (currMB, dataTr, &cnt_nonz, &curr_cbp_blk, enc_mb->lambda_mdfp, block, (short) mode, &best, min_rdcost); + + //--- set variables if best mode has changed --- + if (rdcost < min_rdcost) + { + min_cost8x8 = *cost; + min_rdcost = rdcost; + *partition = best; + partition->mode = (char) mode; + currMB->b8x8[block].mode = (char) mode; + +#ifdef BEST_NZ_COEFF + if (cnt_nonz) + { + for(i = 0; i <= 1; i++) + { + best_nz_coeff[i][0]= p_Vid->nz_coeff[currMB->mbAddrX][i1 + i][j1 ]; + best_nz_coeff[i][1]= p_Vid->nz_coeff[currMB->mbAddrX][i1 + i][j1 + 1]; + } + } + else + { + for(i = 0; i <= 1; i++) + { + best_nz_coeff[i][0]= 0; + best_nz_coeff[i][1]= 0; + } + } +#endif + + //--- store number of nonzero coefficients --- + best_cnt_nonz = cnt_nonz; + + //--- store block cbp --- + dataTr->cbp_blk8x8 &= (~(0x33 << (((block>>1)<<3)+((block & 0x01)<<1)))); // delete bits for block + dataTr->cbp_blk8x8 |= curr_cbp_blk; + + //--- store coefficients --- + memcpy(&cofACtr[0][0][0][0],&currSlice->cofAC[block][0][0][0], 4 * 2 * 65 * sizeof(int)); + + if( currSlice->P444_joined ) + { + //--- store coefficients --- + memcpy(&cofACtr[1][0][0][0],&currSlice->cofAC[block + 4][0][0][0], 4 * 2 * 65 * sizeof(int)); + memcpy(&cofACtr[2][0][0][0],&currSlice->cofAC[block + 8][0][0][0], 4 * 2 * 65 * sizeof(int)); + } + + //--- store reconstruction and prediction --- + copy_image_data_8x8(&dataTr->rec_mbY8x8[j0], &p_Vid->enc_picture->imgY[currMB->pix_y + j0], i0, currMB->pix_x + i0); + copy_image_data_8x8(&dataTr->mpr8x8[j0], &currSlice->mb_pred[0][j0], i0, i0); + + if (p_Inp->rdopt == 3) + { + errdo_store_best_b8x8(currMB, transform8x8, block); + } + + if(currSlice->P444_joined) + { + copy_image_data_8x8(&dataTr->rec_mb8x8_cr[0][j0], &p_Vid->enc_picture->imgUV[0][currMB->pix_y + j0], i0, currMB->pix_x + i0); + copy_image_data_8x8(&dataTr->mpr8x8CbCr[0][j0], &currSlice->mb_pred[1][j0], i0, i0); + + copy_image_data_8x8(&dataTr->rec_mb8x8_cr[1][j0], &p_Vid->enc_picture->imgUV[1][currMB->pix_y + j0], i0, currMB->pix_x + i0); + copy_image_data_8x8(&dataTr->mpr8x8CbCr[1][j0], &currSlice->mb_pred[2][j0], i0, i0); + } + + //--- store best 8x8 coding state --- + if (block < 3) + { + currSlice->store_coding_state (currMB, currSlice->p_RDO->cs_b8); + stored_state_8x8 = TRUE; + } + } // if (rdcost <= min_rdcost) + + //--- re-set coding state as it was before coding with current mode was performed --- + if (index != maxindex - 1) + { + if(p_Inp->subMBCodingState == 1) + currSlice->reset_coding_state (currMB, currSlice->p_RDO->cs_cm); + else if(p_Inp->subMBCodingState == 2) + currSlice->reset_coding_state (currMB, currSlice->p_RDO->cs_tmp); + } + } // if ((enc_mb->valid[mode] && (transform8x8 == 0 || mode != 0 || (mode == 0 && active_sps->direct_8x8_inference_flag))) + } // for (min_rdcost=1e30, index = 0; index<6; index++) + + if (valid_8x8 == TRUE) + { +#ifdef BEST_NZ_COEFF + for(i = 0; i <= 1; i++) + { + for(j = 0; j <= 1; j++) + p_Vid->nz_coeff[currMB->mbAddrX][i1 + i][j1 + j] = best_nz_coeff[i][j]; + } +#endif + + if (!transform8x8) + { + if (min_cost8x8 != DISTBLK_MAX) + dataTr->mb_p8x8_cost += min_cost8x8; + else + dataTr->mb_p8x8_cost = DISTBLK_MAX; + } + + //----- set cbp and count of nonzero coefficients --- + if (best_cnt_nonz) + { + dataTr->cbp8x8 |= (1 << block); + dataTr->cnt_nonz_8x8 += best_cnt_nonz; + } + + if (!transform8x8) + { + if (block < 3) + { + //===== re-set reconstructed block ===== + j0 = 8*(block >> 1); + i0 = 8*(block & 0x01); + + // need to double check code since original did not use i0 + copy_image_data_8x8(&p_Vid->enc_picture->imgY[currMB->pix_y + j0], &dataTr->rec_mbY8x8[j0], currMB->pix_x + i0, i0); + + if (p_Inp->rdopt == 3) + { + errdo_get_best_b8x8(currMB, transform8x8, block); + } + + if(currSlice->P444_joined) + { + + for (k=0; k<2; k++) + { + copy_image_data_8x8(&p_Vid->enc_picture->imgUV[k][currMB->pix_y + j0], &dataTr->rec_mb8x8_cr[k][j0], currMB->pix_x + i0, i0); + } + } + } // if (block<3) + } + else + { + //======= save motion data for 8x8 partition for transform size 8x8 ======== + currSlice->store_8x8_motion_vectors(currSlice, 0, block, partition); + } + + //===== set motion vectors and reference frames (prediction) ===== + currSlice->set_ref_and_motion_vectors (currMB, motion, partition, block); + + //===== set the coding state after current block ===== + //if (transform8x8 == 0 || block < 3) + if (stored_state_8x8 == TRUE) + currSlice->reset_coding_state (currMB, currSlice->p_RDO->cs_b8); + else + { + currSlice->reset_coding_state (currMB, currSlice->p_RDO->cs_cm); + update_adaptive_rounding_8x8(p_Vid, p_Inp, dataTr, fadjust); + } + } +} + +/*! +************************************************************************************* +* \brief +* Low Complexity Mode Decision for an 8x8 sub-macroblock +************************************************************************************* +*/ +void submacroblock_mode_decision_low(Macroblock *currMB, + RD_PARAMS *enc_mb, + RD_8x8DATA *dataTr, + int ****cofACtr, + int *have_direct, + int block, + distblk *cost_direct, + distblk *cost, + distblk *cost8x8_direct, + int transform8x8) +{ + VideoParameters *p_Vid = currMB->p_Vid; + InputParameters *p_Inp = currMB->p_Inp; + Slice *currSlice = currMB->p_Slice; + PicMotionParams **motion = p_Vid->enc_picture->mv_info; + + int64 curr_cbp_blk; + //double rdcost = 0.0; + int j0, i0; + int i, j; + int index; + int mode; + distblk min_cost8x8; + distblk direct4x4_tmp, direct8x8_tmp; + distblk bmcost[5] = {DISTBLK_MAX}; + int cnt_nonz = 0; + int dummy; + int best_cnt_nonz = 0; + int maxindex = (transform8x8) ? 2 : 5; + int block_x, block_y; + int lambda_mf[3]; + + int ****fadjust = transform8x8? p_Vid->ARCofAdj8x8 : p_Vid->ARCofAdj4x4; + short pdir; + + Boolean valid_8x8 = FALSE; + Boolean stored_state_8x8 = FALSE; + +#ifdef BEST_NZ_COEFF + int j1, i1; + int best_nz_coeff[2][2]; +#endif + + Info8x8 best = init_info_8x8_struct(); + dataTr->part[block].mode = 0; + if (transform8x8) + currMB->valid_8x8 = FALSE; + else + currMB->valid_4x4 = FALSE; + + //--- set coordinates --- + j0 = ((block>>1)<<3); + i0 = ((block&0x01)<<3); + +#ifdef BEST_NZ_COEFF + j1 = (j0>>2); + i1 = (i0>>2); + for(j = 0; j <= 1; j++) + { + for(i = 0; i <= 1; i++) + best_nz_coeff[i][j] = p_Vid->nz_coeff[currMB->mbAddrX][i1 + i][j1 + j] = 0; + } +#endif + + if (transform8x8) + currMB->luma_transform_size_8x8_flag = TRUE; //switch to transform size 8x8 + + //--- store coding state before coding --- + currSlice->store_coding_state (currMB, currSlice->p_RDO->cs_cm); + + //===== LOOP OVER POSSIBLE CODING MODES FOR 8x8 SUB-PARTITION ===== + for (min_cost8x8 = DISTBLK_MAX, index = (currSlice->slice_type == B_SLICE ? 0 : 1); index < maxindex; index++) + { + mode = b8_mode_table[index]; + best.mode = (char) mode; + *cost = 0; + + if (enc_mb->valid[mode] && (!(transform8x8 == 1 && mode > 4)) && (transform8x8 == 0 || mode != 0 || (mode == 0 && p_Vid->active_sps->direct_8x8_inference_flag))) + { + if (transform8x8) + { + currMB->valid_8x8 = TRUE; + } + else + currMB->valid_4x4 = TRUE; + + valid_8x8 = TRUE; + curr_cbp_blk = 0; + + if (mode==0) + { + //--- Direct Mode --- + direct4x4_tmp = 0; + direct8x8_tmp = 0; + direct4x4_tmp = GetDirectCost8x8 ( currMB, block, &direct8x8_tmp); + if ((direct4x4_tmp==DISTBLK_MAX)||(*cost_direct==DISTBLK_MAX)) + { + *cost_direct = DISTBLK_MAX; + if (transform8x8) + *cost8x8_direct = DISTBLK_MAX; + } + else + { + *cost_direct += direct4x4_tmp; + if (transform8x8) + *cost8x8_direct += direct8x8_tmp; + } + (*have_direct) ++; + + if (transform8x8) + { + switch(p_Inp->Transform8x8Mode) + { + case 1: // Mixture of 8x8 & 4x4 transform + if((direct8x8_tmp < direct4x4_tmp) || !(enc_mb->valid[5] && enc_mb->valid[6] && enc_mb->valid[7])) + *cost = direct8x8_tmp; + else + *cost = direct4x4_tmp; + break; + case 2: // 8x8 Transform only + *cost = direct8x8_tmp; + break; + default: // 4x4 Transform only + *cost = direct4x4_tmp; + break; + } + if (p_Inp->Transform8x8Mode==2) + *cost = DISTBLK_MAX; + } + else + { + *cost = direct4x4_tmp; + } + + block_x = currMB->block_x + (block & 0x01)*2; + block_y = currMB->block_y + (block & 0x02); + best.ref[LIST_0] = currSlice->direct_ref_idx[block_y][block_x][LIST_0]; + best.ref[LIST_1] = currSlice->direct_ref_idx[block_y][block_x][LIST_1]; + best.pdir = currSlice->direct_pdir[block_y][block_x]; + } // if (mode==0) + else + { + short b_ref; + + //======= motion estimation for all reference frames ======== + //----------------------------------------------------------- + memcpy(lambda_mf, enc_mb->lambda_mf, 3 * sizeof(int)); + if (p_Inp->CtxAdptLagrangeMult == 1) + { + RDOPTStructure *p_RDO = currSlice->p_RDO; + lambda_mf[F_PEL] = (int)(lambda_mf[F_PEL] * p_RDO->lambda_mf_factor); + lambda_mf[H_PEL] = (int)(lambda_mf[H_PEL] * p_RDO->lambda_mf_factor); + lambda_mf[Q_PEL] = (int)(lambda_mf[Q_PEL] * p_RDO->lambda_mf_factor); + } + + SubPartitionMotionSearch (currMB, mode, block, lambda_mf); + + //--- get cost and reference frame for LIST 0 prediction --- + bmcost[LIST_0] = DISTBLK_MAX; + list_prediction_cost(currMB, LIST_0, block, mode, enc_mb, bmcost, best.ref); + + //store LIST 0 reference index for every block + block_x = currMB->block_x + (block & 0x01)*2; + block_y = currMB->block_y + (block & 0x02); + b_ref = best.ref[LIST_0]; + + for (j = block_y; j< block_y + 2; j++) + { + for (i = block_x; i< block_x + 2; i++) + { + motion[j][i].ref_pic [LIST_0] = currSlice->listX[currMB->list_offset][b_ref]; + motion[j][i].ref_idx [LIST_0] = (char) b_ref; + } + } + + if (currSlice->slice_type == B_SLICE) + { + //--- get cost and reference frame for LIST 1 prediction --- + bmcost[LIST_1] = DISTBLK_MAX; + bmcost[BI_PRED] = DISTBLK_MAX; + list_prediction_cost(currMB, LIST_1, block, mode, enc_mb, bmcost, best.ref); + + // Compute bipredictive cost between best list 0 and best list 1 references + list_prediction_cost(currMB, BI_PRED, block, mode, enc_mb, bmcost, best.ref); + + // currently Bi prediction ME is only supported for modes 1, 2, 3 and only for ref 0 and only for ref 0 + if (is_bipred_enabled(p_Vid, mode)) + { + list_prediction_cost(currMB, BI_PRED_L0, block, mode, enc_mb, bmcost, 0); + list_prediction_cost(currMB, BI_PRED_L1, block, mode, enc_mb, bmcost, 0); + } + else + { + bmcost[BI_PRED_L0] = DISTBLK_MAX; + bmcost[BI_PRED_L1] = DISTBLK_MAX; + } + + //--- get prediction direction ---- + determine_prediction_list(bmcost, &best, cost); + + //store backward reference index for every block + for (j = block_y; j< block_y + 2; j++) + { + for (i = block_x; i < block_x + 2; i++) + { + //motion[j][i].ref_pic [LIST_0] = currSlice->listX[currMB->list_offset][b_ref]; + motion[j][i].ref_idx [LIST_0] = (char) best.ref[LIST_0]; + motion[j][i].ref_idx [LIST_0] = (char) best.ref[LIST_1]; + } + } + } // if (currSlice->slice_type == B_SLICE) + else + { + best.pdir = 0; + *cost = bmcost[LIST_0]; + } + } // if (mode!=0) + if (*cost != DISTBLK_MAX) + *cost += (ref_cost(currSlice, enc_mb->lambda_mf[Q_PEL], (short) B8Mode2Value (currSlice, (short) mode, best.pdir), + (best.pdir < 1 ? currMB->list_offset : currMB->list_offset + LIST_1)) - 1); + + //--- set variables if best mode has changed --- + if (*cost < min_cost8x8) + { + min_cost8x8 = *cost; + + dataTr->part[block] = best; + currMB->b8x8[block].mode = (char) mode; + +#ifdef BEST_NZ_COEFF + if (cnt_nonz) + { + best_nz_coeff[0][0]= p_Vid->nz_coeff[currMB->mbAddrX][i1 ][j1 ]; + best_nz_coeff[0][1]= p_Vid->nz_coeff[currMB->mbAddrX][i1 ][j1 + 1]; + best_nz_coeff[1][0]= p_Vid->nz_coeff[currMB->mbAddrX][i1 + 1][j1 ]; + best_nz_coeff[1][1]= p_Vid->nz_coeff[currMB->mbAddrX][i1 + 1][j1 + 1]; + } + else + { + best_nz_coeff[0][0]= 0; + best_nz_coeff[0][1]= 0; + best_nz_coeff[1][0]= 0; + best_nz_coeff[1][1]= 0; + } +#endif + + //--- store number of nonzero coefficients --- + best_cnt_nonz = cnt_nonz; + + //--- store best 8x8 coding state --- + if (block < 3) + { + currSlice->store_coding_state (currMB, currSlice->p_RDO->cs_b8); + stored_state_8x8 = TRUE; + } + } // if (rdcost <= min_rdcost) + + //--- re-set coding state as it was before coding with current mode was performed --- + currSlice->reset_coding_state (currMB, currSlice->p_RDO->cs_cm); + } // if ((enc_mb->valid[mode] && (transform8x8 == 0 || mode != 0 || (mode == 0 && active_sps->direct_8x8_inference_flag))) + } // for (min_rdcost=1e30, index=(currSlice->slice_type == B_SLICE ? 0 : 1); index<6; index++) + + if (valid_8x8 == TRUE) + { + int list_mode[2]; +#ifdef BEST_NZ_COEFF + for(i = 0; i <= 1; i++) + { + for(j = 0; j <= 1; j++) + p_Vid->nz_coeff[currMB->mbAddrX][i1 + i][j1 + j] = best_nz_coeff[i][j]; + } +#endif + + if (!transform8x8) + { + if (min_cost8x8 != DISTBLK_MAX) + dataTr->mb_p8x8_cost += min_cost8x8; + else + dataTr->mb_p8x8_cost = DISTBLK_MAX; + } + + + if (transform8x8) + { + if (min_cost8x8 != DISTBLK_MAX) + dataTr->mb_p8x8_cost += min_cost8x8; + else + dataTr->mb_p8x8_cost = DISTBLK_MAX; + + mode = dataTr->part[block].mode; + pdir = dataTr->part[block].pdir; + } + else + { + mode = dataTr->part[block].mode; + pdir = dataTr->part[block].pdir; + } + + curr_cbp_blk = 0; + currMB->b8x8[block].bipred = dataTr->part[block].bipred; + currMB->ar_mode = (short) ((mode != 0)? mode: P8x8); + + list_mode[0] = (pdir == 0 || pdir == 2 ? mode : 0); + list_mode[1] = (pdir == 1 || pdir == 2 ? mode : 0); + + best_cnt_nonz = currSlice->luma_residual_coding_8x8 (currMB, &dummy, &curr_cbp_blk, block, pdir, list_mode, dataTr->part[block].ref); + + if (currSlice->P444_joined) + { + best_cnt_nonz += currSlice->coeff_cost_cr[1] + currSlice->coeff_cost_cr[2]; + } + + dataTr->cbp_blk8x8 &= (~(0x33 << (((block>>1)<<3)+((block & 0x01)<<1)))); // delete bits for block + dataTr->cbp_blk8x8 |= curr_cbp_blk; + + //--- store coefficients --- + memcpy(cofACtr[0][0][0],currSlice->cofAC[block][0][0], 4 * 2 * 65 * sizeof(int)); + + if(currSlice->P444_joined) + { + //--- store coefficients --- + memcpy(cofACtr[1][0][0],currSlice->cofAC[block + 4][0][0], 4 * 2 * 65 * sizeof(int)); + memcpy(cofACtr[2][0][0],currSlice->cofAC[block + 8][0][0], 4 * 2 * 65 * sizeof(int)); + } + + + //--- store reconstruction and prediction --- + copy_image_data_8x8(&dataTr->rec_mbY8x8[j0], &p_Vid->enc_picture->imgY[currMB->pix_y + j0], i0, currMB->pix_x + i0); + copy_image_data_8x8(&dataTr->mpr8x8[j0], &currSlice->mb_pred[0][j0], i0, i0); + + + //--- store reconstruction and prediction --- + if(currSlice->slice_type == SP_SLICE) + { + for (j=j0; j < j0 + BLOCK_SIZE_8x8; j++) + { + memcpy(&dataTr->lrec[j][i0],&p_Vid->lrec[currMB->pix_y+j][currMB->pix_x+i0],BLOCK_SIZE_8x8 * sizeof(int)); // store coefficients for primary SP slice + } + } + if(currSlice->P444_joined) + { + copy_image_data_8x8(&dataTr->rec_mb8x8_cr[0][j0], &p_Vid->enc_picture->imgUV[0][currMB->pix_y + j0], i0, currMB->pix_x + i0); + copy_image_data_8x8(&dataTr->mpr8x8CbCr[0][j0], &currSlice->mb_pred[1][j0], i0, i0); + copy_image_data_8x8(&dataTr->rec_mb8x8_cr[1][j0], &p_Vid->enc_picture->imgUV[1][currMB->pix_y + j0], i0, currMB->pix_x + i0); + copy_image_data_8x8(&dataTr->mpr8x8CbCr[1][j0], &currSlice->mb_pred[2][j0], i0, i0); + } + + + //----- set cbp and count of nonzero coefficients --- + if (best_cnt_nonz) + { + dataTr->cbp8x8 |= (1 << block); + dataTr->cnt_nonz_8x8 += best_cnt_nonz; + } + + if (!transform8x8) + { + if (block < 3) + { + //===== re-set reconstructed block ===== + j0 = 8*(block >> 1); + i0 = 8*(block & 0x01); + + copy_image_data_8x8(&p_Vid->enc_picture->imgY[currMB->pix_y + j0], &dataTr->rec_mbY8x8[j0], currMB->pix_x + i0, i0); + + if(currSlice->slice_type == SP_SLICE) + { + for (j = j0; j < j0 + BLOCK_SIZE_8x8; j++) + { + memcpy(&p_Vid->lrec[currMB->pix_y + j][currMB->pix_x], dataTr->lrec[j], 2 * BLOCK_SIZE * sizeof(int)); // reset the coefficients for SP slice + } + } + + if(currSlice->P444_joined) + { + copy_image_data_8x8(&p_Vid->enc_picture->imgUV[0][currMB->pix_y + j0], &dataTr->rec_mb8x8_cr[0][j0], currMB->pix_x + i0, i0); + copy_image_data_8x8(&p_Vid->enc_picture->imgUV[1][currMB->pix_y + j0], &dataTr->rec_mb8x8_cr[1][j0], currMB->pix_x + i0, i0); + } + } // if (block<3) + } + else + { + //======= save motion data for 8x8 partition for transform size 8x8 ======== + currSlice->store_8x8_motion_vectors(currSlice, 0, block, &dataTr->part[block]); + } + + //===== set motion vectors and reference frames (prediction) ===== + currSlice->set_ref_and_motion_vectors (currMB, motion, &dataTr->part[block], block); + + //===== set the coding state after current block ===== + //if (transform8x8 == 0 || block < 3) + if (stored_state_8x8 == TRUE) + currSlice->reset_coding_state (currMB, currSlice->p_RDO->cs_b8); + else + { + update_adaptive_rounding_8x8(p_Vid, p_Inp, dataTr, fadjust); + } + } +} + diff --git a/lencod/src/mv_direct.c b/lencod/src/mv_direct.c new file mode 100644 index 0000000..a4e990a --- /dev/null +++ b/lencod/src/mv_direct.c @@ -0,0 +1,814 @@ + +/*! + ************************************************************************************* + * \file mv_direct.c + * + * \brief + * Direct Motion Vector Generation + * + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * - Alexis Michael Tourapis + * + ************************************************************************************* +*/ + +#include "contributors.h" + +#include +#include +#include + +#include "global.h" + +#include "image.h" +#include "mv_search.h" +#include "refbuf.h" +#include "memalloc.h" +#include "mb_access.h" +#include "macroblock.h" +#include "mc_prediction.h" +#include "conformance.h" +#include "mode_decision.h" + +/*! + ************************************************************************ + * \brief + * Calculate Temporal Direct Mode Motion Vectors + ************************************************************************ + */ +void Get_Direct_MV_Temporal (Macroblock *currMB) +{ + Slice *currSlice = currMB->p_Slice; + int block_x, block_y, pic_block_x, pic_block_y, opic_block_x, opic_block_y; + MotionVector *****all_mvs; + int mv_scale; + int refList; + int ref_idx; + VideoParameters *p_Vid = currMB->p_Vid; + int list_offset = currMB->list_offset; + + StorablePicture **list1 = currSlice->listX[LIST_1 + list_offset]; + + PicMotionParams colocated; + + //temporal direct mode copy from decoder + for (block_y = 0; block_y < 4; block_y++) + { + pic_block_y = currMB->block_y + block_y; + opic_block_y = (currMB->opix_y >> 2) + block_y; + + for (block_x = 0; block_x < 4; block_x++) + { + pic_block_x = currMB->block_x + block_x; + opic_block_x = (currMB->pix_x>>2) + block_x; + + all_mvs = currSlice->all_mv; + if (p_Vid->active_sps->direct_8x8_inference_flag) + { + if(currMB->p_Inp->separate_colour_plane_flag && currMB->p_Vid->yuv_format==YUV444) + colocated = list1[0]->JVmv_info[currMB->p_Slice->colour_plane_id][RSD(opic_block_y)][RSD(opic_block_x)]; + else + colocated = list1[0]->mv_info[RSD(opic_block_y)][RSD(opic_block_x)]; + if(currSlice->mb_aff_frame_flag && currMB->mb_field && currSlice->listX[LIST_1][0]->coded_frame) + { + int iPosBlkY; + if(currSlice->listX[LIST_1][0]->motion.mb_field[currMB->mbAddrX] ) + iPosBlkY = (opic_block_y>>2)*8+4*(currMB->mbAddrX&1)+(opic_block_y&0x03); + else + iPosBlkY = RSD(opic_block_y)*2; + + if(colocated.ref_idx[LIST_0]>=0) + colocated.ref_pic[LIST_0] = list1[0]->frame->mv_info[iPosBlkY][RSD(opic_block_x)].ref_pic[LIST_0]; + if(colocated.ref_idx[LIST_1]>=0) + colocated.ref_pic[LIST_1] = list1[0]->frame->mv_info[iPosBlkY][RSD(opic_block_x)].ref_pic[LIST_1]; + } + } + else + { + if(currMB->p_Inp->separate_colour_plane_flag && currMB->p_Vid->yuv_format==YUV444) + colocated = list1[0]->JVmv_info[currMB->p_Slice->colour_plane_id][opic_block_y][opic_block_x]; + else + colocated = list1[0]->mv_info[opic_block_y][opic_block_x]; + } + if(currSlice->mb_aff_frame_flag) + { + if(!currMB->mb_field && ((currSlice->listX[LIST_1][0]->coded_frame && currSlice->listX[LIST_1][0]->motion.mb_field[currMB->mbAddrX]) || + (!currSlice->listX[LIST_1][0]->coded_frame))) + { + if (iabs(p_Vid->enc_picture->poc - currSlice->listX[LIST_1+4][0]->poc)> iabs(p_Vid->enc_picture->poc -currSlice->listX[LIST_1+2][0]->poc) ) + { + if ( p_Vid->active_sps->direct_8x8_inference_flag) + { + if(currMB->p_Inp->separate_colour_plane_flag && currMB->p_Vid->yuv_format==YUV444) + colocated = currSlice->listX[LIST_1+2][0]->JVmv_info[currMB->p_Slice->colour_plane_id][RSD(opic_block_y)>>1][RSD(opic_block_x)]; + else + colocated = currSlice->listX[LIST_1+2][0]->mv_info[RSD(opic_block_y)>>1][RSD(opic_block_x)]; + } + else + { + if(currMB->p_Inp->separate_colour_plane_flag && currMB->p_Vid->yuv_format==YUV444) + colocated = currSlice->listX[LIST_1+2][0]->JVmv_info[currMB->p_Slice->colour_plane_id][(opic_block_y)>>1][(opic_block_x)]; + else + colocated = currSlice->listX[LIST_1+2][0]->mv_info[(opic_block_y)>>1][opic_block_x]; + } + if(currSlice->listX[LIST_1][0]->coded_frame) + { + int iPosBlkY = (RSD(opic_block_y)>>3)*8 + ((RSD(opic_block_y)>>1) & 0x03); + if(colocated.ref_idx[LIST_0] >=0) // && !colocated.ref_pic[LIST_0]) + colocated.ref_pic[LIST_0] = currSlice->listX[LIST_1+2][0]->frame->mv_info[iPosBlkY][RSD(opic_block_x)].ref_pic[LIST_0]; + if(colocated.ref_idx[LIST_1] >=0) // && !colocated.ref_pic[LIST_1]) + colocated.ref_pic[LIST_1] = currSlice->listX[LIST_1+2][0]->frame->mv_info[iPosBlkY][RSD(opic_block_x)].ref_pic[LIST_1]; + } + } + else + { + if (p_Vid->active_sps->direct_8x8_inference_flag ) + { + if(currMB->p_Inp->separate_colour_plane_flag && currMB->p_Vid->yuv_format==YUV444) + colocated = currSlice->listX[LIST_1+4][0]->JVmv_info[currMB->p_Slice->colour_plane_id][RSD(opic_block_y)>>1][RSD(opic_block_x)]; + else + colocated = currSlice->listX[LIST_1+4][0]->mv_info[RSD(opic_block_y)>>1][RSD(opic_block_x)]; + + } + else + { + if(currMB->p_Inp->separate_colour_plane_flag && currMB->p_Vid->yuv_format==YUV444) + colocated = currSlice->listX[LIST_1+4][0]->JVmv_info[currMB->p_Slice->colour_plane_id][(opic_block_y)>>1][opic_block_x]; + else + colocated = currSlice->listX[LIST_1+4][0]->mv_info[(opic_block_y)>>1][opic_block_x]; + } + if(currSlice->listX[LIST_1][0]->coded_frame) + { + int iPosBlkY = (RSD(opic_block_y)>>3)*8 + ((RSD(opic_block_y)>>1) & 0x03)+4; + if(colocated.ref_idx[LIST_0] >=0) // && !colocated.ref_pic[LIST_0]) + colocated.ref_pic[LIST_0] = currSlice->listX[LIST_1+4][0]->frame->mv_info[iPosBlkY][RSD(opic_block_x)].ref_pic[LIST_0]; + if(colocated.ref_idx[LIST_1] >=0)// && !colocated.ref_pic[LIST_1]) + colocated.ref_pic[LIST_1] = currSlice->listX[LIST_1+4][0]->frame->mv_info[iPosBlkY][RSD(opic_block_x)].ref_pic[LIST_1]; + } + } + } + } + else if(!p_Vid->active_sps->frame_mbs_only_flag && !p_Vid->structure && !currSlice->listX[LIST_1][0]->coded_frame) + { + if (iabs(p_Vid->enc_picture->poc - list1[0]->bottom_field->poc)> iabs(p_Vid->enc_picture->poc -list1[0]->top_field->poc) ) + { + colocated = p_Vid->active_sps->direct_8x8_inference_flag ? + list1[0]->top_field->mv_info[RSD(opic_block_y)>>1][RSD(opic_block_x)] : list1[0]->top_field->mv_info[(opic_block_y)>>1][opic_block_x]; + } + else + { + colocated = p_Vid->active_sps->direct_8x8_inference_flag ? + list1[0]->bottom_field->mv_info[RSD(opic_block_y)>>1][RSD(opic_block_x)] : list1[0]->bottom_field->mv_info[(opic_block_y)>>1][opic_block_x]; + } + } + else if(!p_Vid->active_sps->frame_mbs_only_flag && p_Vid->structure && list1[0]->coded_frame) + { + int iPosBlkY; + int currentmb = 2*(list1[0]->size_x>>4) * (opic_block_y >> 2)+ (opic_block_x>>2)*2 + ((opic_block_y>>1) & 0x01); + if(p_Vid->structure!=list1[0]->structure) + { + if (p_Vid->structure == TOP_FIELD) + { + colocated = p_Vid->active_sps->direct_8x8_inference_flag ? + list1[0]->frame->top_field->mv_info[RSD(opic_block_y)][RSD(opic_block_x)] : list1[0]->frame->top_field->mv_info[opic_block_y][opic_block_x]; + } + else + { + colocated = p_Vid->active_sps->direct_8x8_inference_flag ? + list1[0]->frame->bottom_field->mv_info[RSD(opic_block_y)][RSD(opic_block_x)] : list1[0]->frame->bottom_field->mv_info[opic_block_y][opic_block_x]; + } + } + + if(!currSlice->listX[LIST_1][0]->frame->mb_aff_frame_flag || !list1[0]->frame->motion.mb_field[currentmb]) + iPosBlkY = 2*(RSD(opic_block_y)); + else + iPosBlkY = (RSD(opic_block_y)>>2)*8 + (RSD(opic_block_y) & 0x03)+4*(p_Vid->structure == BOTTOM_FIELD); + if(colocated.ref_idx[LIST_0] >=0) // && !colocated.ref_pic[LIST_0]) + colocated.ref_pic[LIST_0] = list1[0]->frame->mv_info[iPosBlkY][RSD(opic_block_x)].ref_pic[LIST_0]; + if(colocated.ref_idx[LIST_1] >=0)// && !colocated.ref_pic[LIST_1]) + colocated.ref_pic[LIST_1] = list1[0]->frame->mv_info[iPosBlkY][RSD(opic_block_x)].ref_pic[LIST_1]; + } + + refList = ((colocated.ref_idx[LIST_0] == -1 || (p_Vid->view_id && colocated.ref_idx[LIST_0]==list1[0]->ref_pic_na[0]))? LIST_1 : LIST_0); + ref_idx = colocated.ref_idx[refList]; + + // next P is intra mode + if (ref_idx == -1 || (p_Vid->view_id && ref_idx==list1[0]->ref_pic_na[refList])) + { + all_mvs[LIST_0][0][0][block_y][block_x] = zero_mv; + all_mvs[LIST_1][0][0][block_y][block_x] = zero_mv; + currSlice->direct_ref_idx[pic_block_y][pic_block_x][LIST_0] = 0; + currSlice->direct_ref_idx[pic_block_y][pic_block_x][LIST_1] = 0; + currSlice->direct_pdir[pic_block_y][pic_block_x] = 2; + } + // next P is skip or inter mode + else + { + int mapped_idx=INVALIDINDEX; + int iref; + if( (currSlice->mb_aff_frame_flag && ( (currMB->mb_field && colocated.ref_pic[refList]->structure==FRAME) || + (!currMB->mb_field && colocated.ref_pic[refList]->structure!=FRAME))) || + (!currSlice->mb_aff_frame_flag && ((p_Vid->structure==FRAME && colocated.ref_pic[refList]->structure!=FRAME)|| + (p_Vid->structure!=FRAME && colocated.ref_pic[refList]->structure==FRAME))) ) + { + //! Frame with field co-located + for (iref = 0; iref < imin(currSlice->num_ref_idx_active[LIST_0], currSlice->listXsize[LIST_0 + list_offset]); iref++) + { + if (currSlice->listX[LIST_0 + list_offset][iref]->top_field == colocated.ref_pic[refList] || + currSlice->listX[LIST_0 + list_offset][iref]->bottom_field == colocated.ref_pic[refList] || + currSlice->listX[LIST_0 + list_offset][iref]->frame == colocated.ref_pic[refList] ) + { + if ((p_Vid->field_picture==1) && (currSlice->listX[LIST_0 + list_offset][iref]->structure != currSlice->structure)) + { + mapped_idx=INVALIDINDEX; + } + else + { + mapped_idx = iref; + break; + } + } + else //! invalid index. Default to zero even though this case should not happen + mapped_idx=INVALIDINDEX; + } + } + else + { + for (iref = 0; iref < imin(currSlice->num_ref_idx_active[LIST_0], currSlice->listXsize[LIST_0 + list_offset]);iref++) + { + if(currSlice->listX[LIST_0 + list_offset][iref] == colocated.ref_pic[refList]) + { + mapped_idx = iref; + break; + } + else //! invalid index. Default to zero even though this case should not happen + { + mapped_idx=INVALIDINDEX; + } + } + } + + if (mapped_idx != INVALIDINDEX) + { + MotionVector mv = colocated.mv[refList]; + mv_scale = currSlice->mvscale[LIST_0 + list_offset][mapped_idx]; + + if((currSlice->mb_aff_frame_flag && !currMB->mb_field && colocated.ref_pic[refList]->structure!=FRAME) || + (!currSlice->mb_aff_frame_flag && p_Vid->structure==FRAME && colocated.ref_pic[refList]->structure!=FRAME)) + mv.mv_y *= 2; + else if((currSlice->mb_aff_frame_flag && currMB->mb_field && colocated.ref_pic[refList]->structure==FRAME) || + (!currSlice->mb_aff_frame_flag && p_Vid->structure!=FRAME && colocated.ref_pic[refList]->structure==FRAME)) + mv.mv_y /= 2; + + if (mv_scale==9999) + { + // forward + all_mvs[LIST_0][0][0][block_y][block_x] = mv; + // backward + all_mvs[LIST_1][0][0][block_y][block_x] = zero_mv; + } + else + { + // forward + all_mvs[LIST_0][mapped_idx][0][block_y][block_x].mv_x = (short) ((mv_scale * mv.mv_x + 128) >> 8); + all_mvs[LIST_0][mapped_idx][0][block_y][block_x].mv_y = (short) ((mv_scale * mv.mv_y + 128) >> 8); + // backward + all_mvs[LIST_1][ 0][0][block_y][block_x].mv_x = (short) (((mv_scale - 256) * mv.mv_x + 128) >> 8); + all_mvs[LIST_1][ 0][0][block_y][block_x].mv_y = (short) (((mv_scale - 256) * mv.mv_y + 128) >> 8); + + } + + // Test Level Limits if satisfied. + if ( out_of_bounds_mvs(p_Vid, &all_mvs[LIST_0][mapped_idx][0][block_y][block_x])|| out_of_bounds_mvs(p_Vid, &all_mvs[LIST_1][0][0][block_y][block_x])) + { + currSlice->direct_ref_idx[pic_block_y][pic_block_x][LIST_0] = -1; + currSlice->direct_ref_idx[pic_block_y][pic_block_x][LIST_1] = -1; + currSlice->direct_pdir[pic_block_y][pic_block_x] = -1; + } + else + { + currSlice->direct_ref_idx[pic_block_y][pic_block_x][LIST_0] = (char) mapped_idx; + currSlice->direct_ref_idx[pic_block_y][pic_block_x][LIST_1] = 0; + currSlice->direct_pdir[pic_block_y][pic_block_x] = 2; + } + } + else + { + currSlice->direct_ref_idx[pic_block_y][pic_block_x][LIST_0] = -1; + currSlice->direct_ref_idx[pic_block_y][pic_block_x][LIST_1] = -1; + currSlice->direct_pdir[pic_block_y][pic_block_x] = -1; + } + } + + if (p_Vid->active_pps->weighted_bipred_idc == 1 && currSlice->direct_pdir[pic_block_y][pic_block_x] == 2) + { + int weight_sum, i; + short l0_refX = currSlice->direct_ref_idx[pic_block_y][pic_block_x][LIST_0]; + short l1_refX = currSlice->direct_ref_idx[pic_block_y][pic_block_x][LIST_1]; + for (i=0;i< (p_Vid->active_sps->chroma_format_idc == YUV400 ? 1 : 3); i++) + { + weight_sum = currSlice->wbp_weight[0][l0_refX][l1_refX][i] + currSlice->wbp_weight[1][l0_refX][l1_refX][i]; + if (weight_sum < -128 || weight_sum > 127) + { + currSlice->direct_ref_idx[pic_block_y][pic_block_x][LIST_0] = -1; + currSlice->direct_ref_idx[pic_block_y][pic_block_x][LIST_1] = -1; + currSlice->direct_pdir [pic_block_y][pic_block_x] = -1; + break; + } + } + } + } + } +} + +static inline void set_direct_references(const PixelPos *mb, char *l0_rFrame, char *l1_rFrame, PicMotionParams **mv_info) +{ + if (mb->available) + { + char *ref_idx = mv_info[mb->pos_y][mb->pos_x].ref_idx; + *l0_rFrame = ref_idx[LIST_0]; + *l1_rFrame = ref_idx[LIST_1]; + } + else + { + *l0_rFrame = -1; + *l1_rFrame = -1; + } +} + +static void set_direct_references_mb_field(const PixelPos *mb, char *l0_rFrame, char *l1_rFrame, PicMotionParams **mv_info, Macroblock *mb_data) +{ + if (mb->available) + { + char *ref_idx = mv_info[mb->pos_y][mb->pos_x].ref_idx; + if (mb_data[mb->mb_addr].mb_field) + { + *l0_rFrame = ref_idx[LIST_0]; + *l1_rFrame = ref_idx[LIST_1]; + } + else + { + *l0_rFrame = (ref_idx[LIST_0] < 0) ? ref_idx[LIST_0] : ref_idx[LIST_0] * 2; + *l1_rFrame = (ref_idx[LIST_1] < 0) ? ref_idx[LIST_1] : ref_idx[LIST_1] * 2; + } + } + else + { + *l0_rFrame = -1; + *l1_rFrame = -1; + } +} + +static void set_direct_references_mb_frame(const PixelPos *mb, char *l0_rFrame, char *l1_rFrame, PicMotionParams **mv_info, Macroblock *mb_data) +{ + if (mb->available) + { + char *ref_idx = mv_info[mb->pos_y][mb->pos_x].ref_idx; + if (mb_data[mb->mb_addr].mb_field) + { + *l0_rFrame = (ref_idx[LIST_0] >> 1); + *l1_rFrame = (ref_idx[LIST_1] >> 1); + } + else + { + *l0_rFrame = ref_idx[LIST_0]; + *l1_rFrame = ref_idx[LIST_1]; + } + } + else + { + *l0_rFrame = -1; + *l1_rFrame = -1; + } +} + +static void test_valid_direct(Slice *currSlice, seq_parameter_set_rbsp_t *active_sps, char *direct_ref_idx, short l0_refX, short l1_refX, int pic_block_y, int pic_block_x) +{ + int weight_sum, i; + Boolean invalid_wp = FALSE; + for (i=0;i< (active_sps->chroma_format_idc == YUV400 ? 1 : 3); i++) + { + weight_sum = currSlice->wbp_weight[0][l0_refX][l1_refX][i] + currSlice->wbp_weight[1][l0_refX][l1_refX][i]; + if (weight_sum < -128 || weight_sum > 127) + { + invalid_wp = TRUE; + break; + } + } + if (invalid_wp == FALSE) + currSlice->direct_pdir[pic_block_y][pic_block_x] = 2; + else + { + direct_ref_idx[LIST_0] = -1; + direct_ref_idx[LIST_1] = -1; + currSlice->direct_pdir[pic_block_y][pic_block_x] = -1; + } +} + +/*! +************************************************************************************* +* \brief +* Temporary function for colocated info when direct_inference is enabled. +* +************************************************************************************* +*/ +int get_colocated_info(Macroblock *currMB, StorablePicture *list1, int i, int j) +{ + if (list1->is_long_term) + return 1; + else + { + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + if( (currSlice->mb_aff_frame_flag) || + (!p_Vid->active_sps->frame_mbs_only_flag && ((!currSlice->structure && !list1->coded_frame) || (currSlice->structure!=list1->structure && list1->coded_frame)))) + { + int jj = RSD(j); + int ii = RSD(i); + int jdiv = (jj>>1); + int moving; + PicMotionParams *fs = &list1->mv_info[jj][ii]; + + if(currSlice->structure && currSlice->structure!=list1->structure && list1->coded_frame) + { + if(currSlice->structure == TOP_FIELD) + fs = list1->top_field->mv_info[jj] + ii; + else + fs = list1->bottom_field->mv_info[jj] + ii; + } + else + { + if( (currSlice->mb_aff_frame_flag && ((!currMB->mb_field && list1->motion.mb_field[currMB->mbAddrX]) || + (!currMB->mb_field && !list1->coded_frame))) + || (!currSlice->mb_aff_frame_flag)) + { + if (iabs(p_Vid->enc_picture->poc - list1->bottom_field->poc)> iabs(p_Vid->enc_picture->poc -list1->top_field->poc) ) + { + fs = list1->top_field->mv_info[jdiv] + ii; + } + else + { + fs = list1->bottom_field->mv_info[jdiv] + ii; + } + } + } + moving = !((((fs->ref_idx[LIST_0] == 0) + && (iabs(fs->mv[LIST_0].mv_x)>>1 == 0) + && (iabs(fs->mv[LIST_0].mv_y)>>1 == 0))) + || ((fs->ref_idx[LIST_0] == -1) + && (fs->ref_idx[LIST_1] == 0) + && (iabs(fs->mv[LIST_1].mv_x)>>1 == 0) + && (iabs(fs->mv[LIST_1].mv_y)>>1 == 0))); + return moving; + } + else + { + PicMotionParams *fs = &list1->mv_info[RSD(j)][RSD(i)]; + int moving; + if(currMB->p_Vid->yuv_format == YUV444 && !currSlice->P444_joined) + fs = &list1->JVmv_info[(int)(p_Vid->colour_plane_id)][RSD(j)][RSD(i)]; + moving= !((((fs->ref_idx[LIST_0] == 0) + && (iabs(fs->mv[LIST_0].mv_x)>>1 == 0) + && (iabs(fs->mv[LIST_0].mv_y)>>1 == 0))) + || ((fs->ref_idx[LIST_0] == -1) + && (fs->ref_idx[LIST_1] == 0) + && (iabs(fs->mv[LIST_1].mv_x)>>1 == 0) + && (iabs(fs->mv[LIST_1].mv_y)>>1 == 0))); + + return moving; + } + } +} + +/*! +************************************************************************************* +* \brief +* Colocated info <= direct_inference is disabled. +************************************************************************************* +*/ +int get_colocated_info_4x4(Macroblock *currMB, StorablePicture *list1, int i, int j) +{ + if (list1->is_long_term) + return 1; + else + { + PicMotionParams *fs = &list1->mv_info[j][i]; + + int moving = !((((fs->ref_idx[LIST_0] == 0) + && (iabs(fs->mv[LIST_0].mv_x)>>1 == 0) + && (iabs(fs->mv[LIST_0].mv_y)>>1 == 0))) + || ((fs->ref_idx[LIST_0] == -1) + && (fs->ref_idx[LIST_1] == 0) + && (iabs(fs->mv[LIST_1].mv_x)>>1 == 0) + && (iabs(fs->mv[LIST_1].mv_y)>>1 == 0))); + + return moving; + } +} + +/*! +************************************************************************ +* \brief +* Calculate Spatial Direct Mode Motion Vectors +************************************************************************ +*/ +void Get_Direct_MV_Spatial_Normal (Macroblock *currMB) +{ + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + PicMotionParams **mv_info = p_Vid->enc_picture->mv_info; + char l0_refA, l0_refB, l0_refC; + char l1_refA, l1_refB, l1_refC; + char l0_refX,l1_refX; + MotionVector pmvfw = zero_mv, pmvbw = zero_mv; + + int block_x, block_y, pic_block_x, pic_block_y, opic_block_x, opic_block_y; + MotionVector *****all_mvs; + char *direct_ref_idx; + StorablePicture **list1 = currSlice->listX[LIST_1]; + + PixelPos mb[4]; + get_neighbors(currMB, mb, 0, 0, 16); + + set_direct_references(&mb[0], &l0_refA, &l1_refA, mv_info); + set_direct_references(&mb[1], &l0_refB, &l1_refB, mv_info); + set_direct_references(&mb[2], &l0_refC, &l1_refC, mv_info); + + l0_refX = (char) imin(imin((unsigned char) l0_refA, (unsigned char) l0_refB), (unsigned char) l0_refC); + l1_refX = (char) imin(imin((unsigned char) l1_refA, (unsigned char) l1_refB), (unsigned char) l1_refC); + + if (l0_refX >= 0) + currMB->GetMVPredictor (currMB, mb, &pmvfw, l0_refX, mv_info, LIST_0, 0, 0, 16, 16); + + if (l1_refX >= 0) + currMB->GetMVPredictor (currMB, mb, &pmvbw, l1_refX, mv_info, LIST_1, 0, 0, 16, 16); + + if (l0_refX == -1 && l1_refX == -1) + { + for (block_y=0; block_y<4; block_y++) + { + pic_block_y = currMB->block_y + block_y; + for (block_x=0; block_x<4; block_x++) + { + pic_block_x = currMB->block_x + block_x; + direct_ref_idx = currSlice->direct_ref_idx[pic_block_y][pic_block_x]; + + currSlice->all_mv[LIST_0][0][0][block_y][block_x] = zero_mv; + currSlice->all_mv[LIST_1][0][0][block_y][block_x] = zero_mv; + + direct_ref_idx[LIST_0] = direct_ref_idx[LIST_1] = 0; + + if (p_Vid->active_pps->weighted_bipred_idc == 1) + test_valid_direct(currSlice, currSlice->active_sps, direct_ref_idx, 0, 0, pic_block_y, pic_block_x); + else + currSlice->direct_pdir[pic_block_y][pic_block_x] = 2; + } + } + } + else if (l0_refX == 0 || l1_refX == 0) + { + int (*get_colocated)(Macroblock *currMB, StorablePicture *list1, int i, int j) = + p_Vid->active_sps->direct_8x8_inference_flag ? get_colocated_info : get_colocated_info_4x4; + + int is_moving_block; + for (block_y = 0; block_y < 4; block_y++) + { + pic_block_y = currMB->block_y + block_y; + opic_block_y = (currMB->opix_y >> 2) + block_y; + + for (block_x=0; block_x<4; block_x++) + { + pic_block_x = currMB->block_x + block_x; + direct_ref_idx = currSlice->direct_ref_idx[pic_block_y][pic_block_x]; + opic_block_x = (currMB->pix_x >> 2) + block_x; + + all_mvs = currSlice->all_mv; + is_moving_block = (get_colocated(currMB, list1[0], opic_block_x, opic_block_y) == 0); + + if (l0_refX < 0) + { + all_mvs[LIST_0][0][0][block_y][block_x] = zero_mv; + direct_ref_idx[LIST_0] = -1; + } + else if ((l0_refX == 0) && is_moving_block) + { + all_mvs[LIST_0][0][0][block_y][block_x] = zero_mv; + direct_ref_idx[LIST_0] = 0; + } + else + { + all_mvs[LIST_0][(short) l0_refX][0][block_y][block_x] = pmvfw; + direct_ref_idx[LIST_0] = (char)l0_refX; + } + + if (l1_refX < 0) + { + all_mvs[LIST_1][0][0][block_y][block_x] = zero_mv; + direct_ref_idx[LIST_1] = -1; + } + else if((l1_refX == 0) && is_moving_block) + { + all_mvs[LIST_1][0][0][block_y][block_x] = zero_mv; + direct_ref_idx[LIST_1] = 0; + } + else + { + all_mvs[LIST_1][(short) l1_refX][0][block_y][block_x] = pmvbw; + direct_ref_idx[LIST_1] = (char)l1_refX; + } + + if (direct_ref_idx[LIST_1] == -1) + currSlice->direct_pdir[pic_block_y][pic_block_x] = 0; + else if (direct_ref_idx[LIST_0] == -1) + currSlice->direct_pdir[pic_block_y][pic_block_x] = 1; + else if (p_Vid->active_pps->weighted_bipred_idc == 1) + test_valid_direct(currSlice, currSlice->active_sps, direct_ref_idx, l0_refX, l1_refX, pic_block_y, pic_block_x); + else + currSlice->direct_pdir[pic_block_y][pic_block_x] = 2; + } + } + } + else + { + for (block_y=0; block_y<4; block_y++) + { + pic_block_y = currMB->block_y + block_y; + + for (block_x=0; block_x<4; block_x++) + { + pic_block_x = currMB->block_x + block_x; + direct_ref_idx = currSlice->direct_ref_idx[pic_block_y][pic_block_x]; + + all_mvs = currSlice->all_mv; + + if (l0_refX > 0) + { + all_mvs[LIST_0][(short) l0_refX][0][block_y][block_x] = pmvfw; + direct_ref_idx[LIST_0]= (char)l0_refX; + } + else + { + all_mvs[LIST_0][0][0][block_y][block_x] = zero_mv; + direct_ref_idx[LIST_0]=-1; + } + + if (l1_refX > 0) + { + all_mvs[LIST_1][(short) l1_refX][0][block_y][block_x] = pmvbw; + direct_ref_idx[LIST_1] = (char)l1_refX; + } + else + { + all_mvs[LIST_1][0][0][block_y][block_x] = zero_mv; + direct_ref_idx[LIST_1] = -1; + } + + if (direct_ref_idx[LIST_1] == -1) + currSlice->direct_pdir[pic_block_y][pic_block_x] = 0; + else if (direct_ref_idx[LIST_0] == -1) + currSlice->direct_pdir[pic_block_y][pic_block_x] = 1; + else if (p_Vid->active_pps->weighted_bipred_idc == 1) + test_valid_direct(currSlice, currSlice->active_sps, direct_ref_idx, l0_refX, l1_refX, pic_block_y, pic_block_x); + else + currSlice->direct_pdir[pic_block_y][pic_block_x] = 2; + } + } + } +} + + +/*! +************************************************************************ +* \brief +* Calculate Spatial Direct Mode Motion Vectors +************************************************************************ +*/ +void Get_Direct_MV_Spatial_MBAFF (Macroblock *currMB) +{ + char l0_refA, l0_refB, l0_refC; + char l1_refA, l1_refB, l1_refC; + char l0_refX,l1_refX; + MotionVector pmvfw = zero_mv, pmvbw = zero_mv; + + int block_x, block_y, pic_block_x, pic_block_y, opic_block_x, opic_block_y; + MotionVector *****all_mvs; + char *direct_ref_idx; + int is_moving_block; + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + PicMotionParams **mv_info = p_Vid->enc_picture->mv_info; + StorablePicture **list1 = currSlice->listX[LIST_1 + currMB->list_offset]; + + int (*get_colocated)(Macroblock *currMB, StorablePicture *list1, int i, int j) = + p_Vid->active_sps->direct_8x8_inference_flag ? get_colocated_info : get_colocated_info_4x4; + + PixelPos mb[4]; + get_neighbors(currMB, mb, 0, 0, 16); + + + if (currMB->mb_field) + { + set_direct_references_mb_field(&mb[0], &l0_refA, &l1_refA, mv_info, p_Vid->mb_data); + set_direct_references_mb_field(&mb[1], &l0_refB, &l1_refB, mv_info, p_Vid->mb_data); + set_direct_references_mb_field(&mb[2], &l0_refC, &l1_refC, mv_info, p_Vid->mb_data); + } + else + { + set_direct_references_mb_frame(&mb[0], &l0_refA, &l1_refA, mv_info, p_Vid->mb_data); + set_direct_references_mb_frame(&mb[1], &l0_refB, &l1_refB, mv_info, p_Vid->mb_data); + set_direct_references_mb_frame(&mb[2], &l0_refC, &l1_refC, mv_info, p_Vid->mb_data); + } + + l0_refX = (char) imin(imin((unsigned char) l0_refA, (unsigned char) l0_refB), (unsigned char) l0_refC); + l1_refX = (char) imin(imin((unsigned char) l1_refA, (unsigned char) l1_refB), (unsigned char) l1_refC); + + if (l0_refX >=0) + currMB->GetMVPredictor (currMB, mb, &pmvfw, l0_refX, mv_info, LIST_0, 0, 0, 16, 16); + + if (l1_refX >=0) + currMB->GetMVPredictor (currMB, mb, &pmvbw, l1_refX, mv_info, LIST_1, 0, 0, 16, 16); + + for (block_y=0; block_y<4; block_y++) + { + pic_block_y = currMB->block_y + block_y; + opic_block_y = (currMB->opix_y >> 2) + block_y; + + for (block_x=0; block_x<4; block_x++) + { + pic_block_x = currMB->block_x + block_x; + direct_ref_idx = currSlice->direct_ref_idx[pic_block_y][pic_block_x]; + opic_block_x = (currMB->pix_x >> 2) + block_x; + is_moving_block = (get_colocated(currMB, list1[0], opic_block_x, opic_block_y) == 0); + + all_mvs = currSlice->all_mv; + + if (l0_refX >=0) + { + if (!l0_refX && is_moving_block) + { + all_mvs[LIST_0][0][0][block_y][block_x] = zero_mv; + direct_ref_idx[LIST_0] = 0; + } + else + { + all_mvs[LIST_0][(short) l0_refX][0][block_y][block_x] = pmvfw; + direct_ref_idx[LIST_0] = (char)l0_refX; + } + } + else + { + all_mvs[LIST_0][0][0][block_y][block_x] = zero_mv; + direct_ref_idx[LIST_0] = -1; + } + + if (l1_refX >=0) + { + if(l1_refX==0 && is_moving_block) + { + all_mvs[LIST_1][0][0][block_y][block_x] = zero_mv; + direct_ref_idx[LIST_1] = (char)l1_refX; + } + else + { + all_mvs[LIST_1][(short) l1_refX][0][block_y][block_x] = pmvbw; + direct_ref_idx[LIST_1] = (char)l1_refX; + } + } + else + { + all_mvs[LIST_1][0][0][block_y][block_x] = zero_mv; + direct_ref_idx[LIST_1] = -1; + } + + // Test Level Limits if satisfied. + + // Test Level Limits if satisfied. + if ((out_of_bounds_mvs(p_Vid, &all_mvs[LIST_0][l0_refX < 0? 0 : l0_refX][0][block_y][block_x]) + || out_of_bounds_mvs(p_Vid, &all_mvs[LIST_1][l1_refX < 0? 0 : l1_refX][0][block_y][block_x]))) + { + direct_ref_idx[LIST_0] = -1; + direct_ref_idx[LIST_1] = -1; + currSlice->direct_pdir [pic_block_y][pic_block_x] = -1; + } + else + { + if (l0_refX < 0 && l1_refX < 0) + { + direct_ref_idx[LIST_0] = direct_ref_idx[LIST_1] = 0; + l0_refX = 0; + l1_refX = 0; + } + + if (direct_ref_idx[LIST_1] == -1) + currSlice->direct_pdir[pic_block_y][pic_block_x] = 0; + else if (direct_ref_idx[LIST_0] == -1) + currSlice->direct_pdir[pic_block_y][pic_block_x] = 1; + else if (p_Vid->active_pps->weighted_bipred_idc == 1) + test_valid_direct(currSlice, currSlice->active_sps, direct_ref_idx, l0_refX, l1_refX, pic_block_y, pic_block_x); + else + currSlice->direct_pdir[pic_block_y][pic_block_x] = 2; + } + } + } +} diff --git a/lencod/src/mv_search.c b/lencod/src/mv_search.c new file mode 100644 index 0000000..e7cabe3 --- /dev/null +++ b/lencod/src/mv_search.c @@ -0,0 +1,1861 @@ + +/*! + ************************************************************************************* + * \file mv_search.c + * + * \brief + * Motion Vector Search, unified for B and P Pictures + * + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * - Stephan Wenger + * - Inge Lille-Langoy + * - Rickard Sjoberg + * - Stephan Wenger + * - Jani Lainema + * - Detlev Marpe + * - Thomas Wedi + * - Heiko Schwarz + * - Alexis Michael Tourapis + * + ************************************************************************************* +*/ + +#include "contributors.h" + +#include +#include +#include + +#include "global.h" + +#include "image.h" +#include "mv_search.h" +#include "refbuf.h" +#include "memalloc.h" +#include "mb_access.h" +#include "macroblock.h" +#include "mc_prediction.h" +#include "conformance.h" +#include "mode_decision.h" + +// Motion estimation distortion header file +#include "me_distortion.h" +#include "me_distortion_otf.h" + +// Motion estimation search algorithms +#include "me_epzs.h" +#include "me_epzs_int.h" +#include "me_fullfast.h" +#include "me_fullfast_otf.h" +#include "me_fullsearch.h" +#include "me_umhex.h" +#include "me_umhexsmp.h" +#include "rdoq.h" + + +static const short bx0[5][4] = {{0,0,0,0}, {0,0,0,0}, {0,0,0,0}, {0,2,0,0}, {0,2,0,2}}; +static const short by0[5][4] = {{0,0,0,0}, {0,0,0,0}, {0,2,0,0}, {0,0,0,0}, {0,0,2,2}}; + + +static distblk GetSkipCostMB (Macroblock *currMB, int lambda); +static distblk BiPredBlockMotionSearch(Macroblock *currMB, MEBlock *, MotionVector*, int, int , int*); + +/*! + ************************************************************************ + * \brief + * Set search range. This needs to be changed to provide 2D support + ************************************************************************ + */ +void get_search_range(MEBlock *mv_block, InputParameters *p_Inp, short ref, int blocktype) +{ + SearchWindow *searchRange = &mv_block->searchRange; + + *searchRange = mv_block->p_Vid->searchRange; + //----- set search range --- + if (p_Inp->full_search == 1) + { + int scale = (imin(ref, 1) + 1); + searchRange->min_x /= scale; + searchRange->max_x /= scale; + searchRange->min_y /= scale; + searchRange->max_y /= scale; + } + else if (p_Inp->full_search != 2) + { + int scale = ((imin(ref, 1) + 1) * imin(2, blocktype)); + searchRange->min_x /= scale; + searchRange->max_x /= scale; + searchRange->min_y /= scale; + searchRange->max_y /= scale; + } +} + +/*! + ************************************************************************ + * \brief + * Set search range. This needs to be changed to provide 2D support + ************************************************************************ + */ +static inline void set_me_parameters( PicMotionParams **motion, const MotionVector *all_mv, int list, char ref, int step_h, int step_v, int pic_block_y, int pic_block_x) +{ + int i, j; + + // Set first line + for (j = pic_block_y; j < pic_block_y + step_v; j++) + { + for (i=pic_block_x; imv_x > min_x) && (blk->mv_x < max_x) && (blk->mv_y > min_y) && (blk->mv_y < max_y)) + { + *access_method = FAST_ACCESS; + } + else + { + *access_method = UMV_ACCESS; + } +} + +/*! + ************************************************************************ + * \brief + * Initialize ME engine + ************************************************************************ + */ +void init_ME_engine(Macroblock *currMB) +{ + InputParameters *p_Inp = currMB->p_Inp; + VideoParameters *p_Vid = currMB->p_Vid; + switch (p_Inp->SearchMode[p_Vid->view_id]) + { + case EPZS: + EPZS_setup_engine(currMB, p_Inp); + break; + case UM_HEX: + currMB->IntPelME = UMHEXIntegerPelBlockMotionSearch; + currMB->BiPredME = UMHEXBipredIntegerPelBlockMotionSearch; + currMB->SubPelBiPredME = sub_pel_bipred_motion_estimation; + currMB->SubPelME = UMHEXSubPelBlockME; + break; + case UM_HEX_SIMPLE: + currMB->IntPelME = smpUMHEXIntegerPelBlockMotionSearch; + currMB->BiPredME = smpUMHEXBipredIntegerPelBlockMotionSearch; + currMB->SubPelBiPredME = sub_pel_bipred_motion_estimation; + currMB->SubPelME = smpUMHEXSubPelBlockME; + break; + case FULL_SEARCH: + currMB->IntPelME = full_search_motion_estimation; + currMB->BiPredME = full_search_bipred_motion_estimation; + currMB->SubPelBiPredME = sub_pel_bipred_motion_estimation; + currMB->SubPelME = sub_pel_motion_estimation; + break; + case FAST_FULL_SEARCH: + default: + currMB->IntPelME = fast_full_search_motion_estimation; + currMB->BiPredME = full_search_bipred_motion_estimation; + currMB->SubPelBiPredME = sub_pel_bipred_motion_estimation; + currMB->SubPelME = sub_pel_motion_estimation; + currMB->p_SetupFastFullPelSearch = (p_Inp->OnTheFlyFractMCP) ? (SetupFastFullPelSearch_otf):(setup_fast_full_search); + break; + } +} + +/*! + ************************************************************************ + * \brief + * Prepare Motion Estimation parameters for single list ME + ************************************************************************ + */ +void PrepareMEParams(Slice *currSlice, MEBlock *mv_block, int ChromaMEEnable, int list, int ref) +{ + if (mv_block->apply_weights) + { + mv_block->weight_luma = currSlice->wp_weight[list][ref][0]; + mv_block->offset_luma = currSlice->wp_offset[list][ref][0]; + + if ( ChromaMEEnable) + { + mv_block->weight_cr[0] = currSlice->wp_weight[list][ref][1]; + mv_block->weight_cr[1] = currSlice->wp_weight[list][ref][2]; + mv_block->offset_cr[0] = currSlice->wp_offset[list][ref][1]; + mv_block->offset_cr[1] = currSlice->wp_offset[list][ref][2]; + } + } +} + +/*! + ************************************************************************ + * \brief + * Prepare Motion Estimation parameters for bipred list ME + ************************************************************************ + */ +void PrepareBiPredMEParams(Slice *currSlice, MEBlock *mv_block, int ChromaMEEnable, int list, int list_offset, int ref) +{ + if (mv_block->apply_weights) + { + if (list == LIST_0) + { + mv_block->weight1 = currSlice->wbp_weight[list_offset ][ref][0][0]; + mv_block->weight2 = currSlice->wbp_weight[list_offset + LIST_1][ref][0][0]; + mv_block->offsetBi = (currSlice->wp_offset[list_offset ][ref][0] + currSlice->wp_offset[list_offset + LIST_1][ref][0] + 1)>>1; + + if ( ChromaMEEnable) + { + mv_block->weight1_cr[0] = currSlice->wbp_weight[list_offset ][ref][0][1]; + mv_block->weight1_cr[1] = currSlice->wbp_weight[list_offset ][ref][0][2]; + mv_block->weight2_cr[0] = currSlice->wbp_weight[list_offset + LIST_1][ref][0][1]; + mv_block->weight2_cr[1] = currSlice->wbp_weight[list_offset + LIST_1][ref][0][2]; + + mv_block->offsetBi_cr[0] = (currSlice->wp_offset[list_offset ][ref][1] + currSlice->wp_offset[list_offset + LIST_1][ref][1] + 1) >> 1; + mv_block->offsetBi_cr[1] = (currSlice->wp_offset[list_offset ][ref][2] + currSlice->wp_offset[list_offset + LIST_1][ref][2] + 1) >> 1; + } + } + else + { + mv_block->weight1 = currSlice->wbp_weight[list_offset + LIST_1][0 ][ref][0]; + mv_block->weight2 = currSlice->wbp_weight[list_offset ][0 ][ref][0]; + mv_block->offsetBi = (currSlice->wp_offset[list_offset + LIST_1][0][0] + currSlice->wp_offset[list_offset][0][0] + 1)>>1; + + if ( ChromaMEEnable) + { + mv_block->weight1_cr[0] = currSlice->wbp_weight[list_offset + LIST_1][0 ][ref][1]; + mv_block->weight1_cr[1] = currSlice->wbp_weight[list_offset + LIST_1][0 ][ref][2]; + mv_block->weight2_cr[0] = currSlice->wbp_weight[list_offset ][0 ][ref][1]; + mv_block->weight2_cr[1] = currSlice->wbp_weight[list_offset ][0 ][ref][2]; + + mv_block->offsetBi_cr[0] = (currSlice->wp_offset[list_offset + LIST_1][0 ][1] + currSlice->wp_offset[list_offset ][0 ][1] + 1) >> 1; + mv_block->offsetBi_cr[1] = (currSlice->wp_offset[list_offset + LIST_1][0 ][2] + currSlice->wp_offset[list_offset ][0 ][2] + 1) >> 1; + } + } + } + else + { + mv_block->weight1 = (short) (1 << currSlice->luma_log_weight_denom); + mv_block->weight2 = (short) (1 << currSlice->luma_log_weight_denom); + mv_block->offsetBi = 0; + if ( ChromaMEEnable) + { + mv_block->weight1_cr[0] = 1<chroma_log_weight_denom; + mv_block->weight1_cr[1] = 1<chroma_log_weight_denom; + mv_block->weight2_cr[0] = 1<chroma_log_weight_denom; + mv_block->weight2_cr[1] = 1<chroma_log_weight_denom; + mv_block->offsetBi_cr[0] = 0; + mv_block->offsetBi_cr[1] = 0; + } + } +} + +/*! + ************************************************************************ + * \brief + * Get current block spatial neighbors + ************************************************************************ + */ +void get_neighbors(Macroblock *currMB, // <-- current Macroblock + PixelPos *block, // <--> neighbor blocks + int mb_x, // <-- block x position + int mb_y, // <-- block y position + int blockshape_x // <-- block width + ) +{ + VideoParameters *p_Vid = currMB->p_Vid; + int *mb_size = p_Vid->mb_size[IS_LUMA]; + + get4x4Neighbour(currMB, mb_x - 1, mb_y , mb_size, &block[0]); + get4x4Neighbour(currMB, mb_x, mb_y - 1, mb_size, &block[1]); + get4x4Neighbour(currMB, mb_x + blockshape_x, mb_y - 1, mb_size, &block[2]); + get4x4Neighbour(currMB, mb_x - 1, mb_y - 1, mb_size, &block[3]); + + if (mb_y > 0) + { + if (mb_x < 8) // first column of 8x8 blocks + { + if (mb_y == 8 ) + { + if (blockshape_x == MB_BLOCK_SIZE) + block[2].available = 0; + } + else if (mb_x + blockshape_x == 8) + { + block[2].available = 0; + } + } + else if (mb_x + blockshape_x == MB_BLOCK_SIZE) + { + block[2].available = 0; + } + } + + if (!block[2].available) + { + block[2] = block[3]; + } +} + +/*! +************************************************************************ +* \brief +* Initialize the motion search +************************************************************************ +*/ +void init_motion_search_module (VideoParameters *p_Vid, InputParameters *p_Inp) +{ + int bits; + int i_min, i_max,k; + int i, l; + int search_range_orig = p_Inp->SepViewInterSearch ? imax( p_Inp->search_range[0], p_Inp->search_range[1] ) : p_Inp->search_range[0]; + int search_range = search_range_orig; + int max_search_points = imax(9, (2 * search_range + 1) * (2 * search_range + 1)); + int max_ref_bits = 1 + 2 * (int)floor(log(imax(16, p_Vid->max_num_references + 1)) / log(2) + 1e-10); + int max_ref = (1<<((max_ref_bits>>1)+1))-1; + int number_of_subpel_positions = 4 * (2*search_range+3); + int max_mv_bits = 3 + 2 * (int)ceil (log(number_of_subpel_positions + 1) / log(2) + 1e-10); + int max_mvd = p_Inp->UseMVLimits? imax(4*imax(p_Inp->SetMVXLimit, p_Inp->SetMVYLimit), ((1<<( max_mv_bits >>1) ) - 1)): ((1<<( max_mv_bits >>1)) - 1); + + p_Vid->max_mvd = max_mvd; + p_Vid->imgpel_abs_range = (imax(p_Vid->max_pel_value_comp[0],p_Vid->max_pel_value_comp[1]) + 1) * 64; + + //===== CREATE ARRAYS ===== + //----------------------------- + if ((p_Vid->spiral_search = (MotionVector*)calloc(max_search_points, sizeof(MotionVector))) == NULL) + no_mem_exit("init_motion_search_module: p_Vid->spiral_search"); + if ((p_Vid->spiral_hpel_search = (MotionVector*)calloc(max_search_points, sizeof(MotionVector))) == NULL) + no_mem_exit("init_motion_search_module: p_Vid->spiral_hpel_search"); + if ((p_Vid->spiral_qpel_search = (MotionVector*)calloc(max_search_points, sizeof(MotionVector))) == NULL) + no_mem_exit("init_motion_search_module: p_Vid->spiral_qpel_search"); + + + if ((p_Vid->mvbits = (int*)calloc(2 * max_mvd + 1, sizeof(int))) == NULL) + no_mem_exit("init_motion_search_module: p_Vid->mvbits"); + + if ((p_Vid->refbits = (int*)calloc(max_ref, sizeof(int))) == NULL) + no_mem_exit("init_motion_search_module: p_Vid->refbits"); + +#if (JM_MEM_DISTORTION) + if ((p_Vid->imgpel_abs = (int*)calloc(p_Vid->imgpel_abs_range, sizeof(int))) == NULL) + no_mem_exit("init_motion_search_module: p_Vid->imgpel_abs"); + if ((p_Vid->imgpel_quad = (int*)calloc(p_Vid->imgpel_abs_range, sizeof(int))) == NULL) + no_mem_exit("init_motion_search_module: p_Vid->imgpel_quad"); + p_Vid->imgpel_abs += p_Vid->imgpel_abs_range / 2; + p_Vid->imgpel_quad += p_Vid->imgpel_abs_range / 2; +#endif + + if (p_Vid->max_num_references) + get_mem4Ddistblk (&p_Vid->motion_cost, 8, 2, p_Vid->max_num_references, 4); + + //--- set array offsets --- + p_Vid->mvbits += max_mvd; + + //===== INIT ARRAYS ===== + //--------------------------- + //--- init array: motion vector bits --- + p_Vid->mvbits[0] = 1; + for (bits = 3; bits <= max_mv_bits; bits += 2) + { + i_max = (short) (1 << (bits >> 1)); + i_min = i_max >> 1; + + for (i = i_min; i < i_max; i++) + p_Vid->mvbits[-i] = p_Vid->mvbits[i] = bits; + } + + //--- init array: reference frame bits --- + p_Vid->refbits[0] = 1; + for (bits=3; bits<=max_ref_bits; bits+=2) + { + i_max = (short) (1 << ((bits >> 1) + 1)) - 1; + i_min = i_max >> 1; + + for (i = i_min; i < i_max; i++) + p_Vid->refbits[i] = bits; + } + +#if (JM_MEM_DISTORTION) + //--- init array: absolute value --- + p_Vid->imgpel_abs[0] = 0; + + for (i=1; iimgpel_abs_range / 2; i++) + { + p_Vid->imgpel_abs[i] = p_Vid->imgpel_abs[-i] = i; + } + + //--- init array: square value --- + p_Vid->imgpel_quad[0] = 0; + + for (i=1; iimgpel_abs_range / 2; i++) + { + p_Vid->imgpel_quad[i] = p_Vid->imgpel_quad[-i] = i * i; + } +#endif + + //--- init array: search pattern --- + p_Vid->spiral_search[0].mv_x = p_Vid->spiral_search[0].mv_y = 0; + p_Vid->spiral_hpel_search[0].mv_x = p_Vid->spiral_hpel_search[0].mv_y = 0; + p_Vid->spiral_qpel_search[0].mv_x = p_Vid->spiral_qpel_search[0].mv_y = 0; + + for (k=1, l=1; l <= imax(1,search_range); l++) + { + for (i=-l+1; i< l; i++) + { + p_Vid->spiral_search[k].mv_x = (short) i; + p_Vid->spiral_search[k].mv_y = (short) -l; + p_Vid->spiral_hpel_search[k].mv_x = (short) (i<<1); + p_Vid->spiral_hpel_search[k].mv_y = (short) -(l<<1); + p_Vid->spiral_qpel_search[k].mv_x = (short) (i<<2); + p_Vid->spiral_qpel_search[k++].mv_y = (short) -(l<<2); + p_Vid->spiral_search[k].mv_x = (short) i; + p_Vid->spiral_search[k].mv_y = (short) l; + p_Vid->spiral_hpel_search[k].mv_x = (short) (i<<1); + p_Vid->spiral_hpel_search[k].mv_y = (short) (l<<1); + p_Vid->spiral_qpel_search[k].mv_x = (short) (i<<2); + p_Vid->spiral_qpel_search[k++].mv_y = (short) (l<<2); + } + for (i=-l; i<=l; i++) + { + p_Vid->spiral_search[k].mv_x = (short) -l; + p_Vid->spiral_search[k].mv_y = (short) i; + p_Vid->spiral_hpel_search[k].mv_x = (short) -(l<<1); + p_Vid->spiral_hpel_search[k].mv_y = (short) (i<<1); + p_Vid->spiral_qpel_search[k].mv_x = (short) -(l<<2); + p_Vid->spiral_qpel_search[k++].mv_y = (short) (i<<2); + p_Vid->spiral_search[k].mv_x = (short) l; + p_Vid->spiral_search[k].mv_y = (short) i; + p_Vid->spiral_hpel_search[k].mv_x = (short) (l<<1); + p_Vid->spiral_hpel_search[k].mv_y = (short) (i<<1); + p_Vid->spiral_qpel_search[k].mv_x = (short) (l<<2); + p_Vid->spiral_qpel_search[k++].mv_y = (short) (i<<2); + } + } + + // set global variable prior to ME + p_Vid->start_me_refinement_hp = (p_Inp->ChromaMEEnable == 1 || p_Inp->MEErrorMetric[F_PEL] != p_Inp->MEErrorMetric[H_PEL] ) ? 0 : 1; + p_Vid->start_me_refinement_qp = (p_Inp->ChromaMEEnable == 1 || p_Inp->MEErrorMetric[H_PEL] != p_Inp->MEErrorMetric[Q_PEL] ) ? 0 : 1; + + select_distortion(p_Vid, p_Inp); + + // Setup Distortion Metrics depending on refinement level + if( p_Inp->OnTheFlyFractMCP ) + { + for (i=0; i<3; i++) + { + switch( p_Inp->MEErrorMetric[i]) + { + case ERROR_SAD: + p_Vid->computeUniPred[i] = computeSAD_otf ; + p_Vid->computeUniPred[i + 3] = computeSADWP_otf ; + p_Vid->computeBiPred1[i] = computeBiPredSAD1_otf ; + p_Vid->computeBiPred2[i] = computeBiPredSAD2_otf ; + break; + case ERROR_SSE: + p_Vid->computeUniPred[i] = computeSSE_otf; + p_Vid->computeUniPred[i + 3] = computeSSEWP_otf; + p_Vid->computeBiPred1[i] = computeBiPredSSE1_otf; + p_Vid->computeBiPred2[i] = computeBiPredSSE2_otf; + break; + case ERROR_SATD : + default: + p_Vid->computeUniPred[i] = computeSATD_otf ; + p_Vid->computeUniPred[i + 3] = computeSATDWP_otf ; + p_Vid->computeBiPred1[i] = computeBiPredSATD1_otf ; + p_Vid->computeBiPred2[i] = computeBiPredSATD2_otf ; + break; + } + } + } + else + { + for (i=0; i<3; i++) + { + switch(p_Inp->MEErrorMetric[i]) + { + case ERROR_SAD: + p_Vid->computeUniPred[i] = computeSAD; + p_Vid->computeUniPred[i + 3] = computeSADWP; + p_Vid->computeBiPred1[i] = computeBiPredSAD1; + p_Vid->computeBiPred2[i] = computeBiPredSAD2; + break; + case ERROR_SSE: + p_Vid->computeUniPred[i] = computeSSE; + p_Vid->computeUniPred[i + 3] = computeSSEWP; + p_Vid->computeBiPred1[i] = computeBiPredSSE1; + p_Vid->computeBiPred2[i] = computeBiPredSSE2; + break; + case ERROR_SATD : + default: + p_Vid->computeUniPred[i] = computeSATD; + p_Vid->computeUniPred[i + 3] = computeSATDWP; + p_Vid->computeBiPred1[i] = computeBiPredSATD1; + p_Vid->computeBiPred2[i] = computeBiPredSATD2; + break; + } + } + } + if (!p_Inp->IntraProfile) + { + if(p_Inp->SearchMode[0] == FAST_FULL_SEARCH || p_Inp->SearchMode[1] == FAST_FULL_SEARCH) + initialize_fast_full_search (p_Vid, p_Inp); + + if (p_Inp->SearchMode[0] == UM_HEX || p_Inp->SearchMode[1] == UM_HEX) + UMHEX_DefineThreshold(p_Vid); + } +} + +/*! + ************************************************************************ + * \brief + * Free memory used by motion search + ************************************************************************ + */ +void +clear_motion_search_module (VideoParameters *p_Vid, InputParameters *p_Inp) +{ + //int search_range = p_Inp->search_range; + //int number_of_subpel_positions = 4 * (2*search_range+3); + //int max_mv_bits = 3 + 2 * (int)ceil (log(number_of_subpel_positions + 1) / log(2) + 1e-10); + int max_mvd = p_Vid->max_mvd; //(1<<( max_mv_bits >>1) ) - 1; + + + //--- correct array offset --- + p_Vid->mvbits -= max_mvd; +#if (JM_MEM_DISTORTION) + p_Vid->imgpel_abs -= p_Vid->imgpel_abs_range / 2; + p_Vid->imgpel_quad -= p_Vid->imgpel_abs_range / 2; +#endif + + //--- delete arrays --- + free (p_Vid->spiral_search); + free (p_Vid->spiral_hpel_search); + free (p_Vid->spiral_qpel_search); + free (p_Vid->mvbits); + free (p_Vid->refbits); + +#if (JM_MEM_DISTORTION) + free (p_Vid->imgpel_abs); + free (p_Vid->imgpel_quad); +#endif + + + if (p_Vid->motion_cost) + free_mem4Ddistblk (p_Vid->motion_cost); + + if ((p_Inp->SearchMode[0] == FAST_FULL_SEARCH || p_Inp->SearchMode[1] == FAST_FULL_SEARCH) && (!p_Inp->IntraProfile) ) + clear_fast_full_search (p_Vid); +} + +static inline int mv_bit_cost(Macroblock *currMB, MotionVector **all_mv, int cur_list, short cur_ref, int by, int bx, int step_v0, int step_v, int step_h0, int step_h, int mvd_bits) +{ + int v, h; + MotionVector predMV; + PixelPos block[4]; // neighbor blocks + VideoParameters *p_Vid = currMB->p_Vid; + + for (v=by; vGetMVPredictor (currMB, block, &predMV, cur_ref, p_Vid->enc_picture->mv_info, cur_list, h, v, step_h, step_v); + + mvd_bits += p_Vid->mvbits[ all_mv[v>>2][h>>2].mv_x - predMV.mv_x ]; + mvd_bits += p_Vid->mvbits[ all_mv[v>>2][h>>2].mv_y - predMV.mv_y ]; + } + } + + return mvd_bits; +} + +/*! + *********************************************************************** + * \brief + * Motion Cost for Bidirectional modes + *********************************************************************** + */ +distblk BPredPartitionCost (Macroblock *currMB, + int blocktype, + int block8x8, + short ref_l0, + short ref_l1, + int lambda_factor, + int list) +{ + VideoParameters *p_Vid = currMB->p_Vid; + Slice *currSlice = currMB->p_Slice; + DecodedPictureBuffer *p_Dpb = p_Vid->p_Dpb_layer[p_Vid->dpb_layer_id]; + imgpel **cur_img = p_Vid->pCurImg; + + short pic_pix_x, pic_pix_y; + short v, h; + distblk mcost; + + int mvd_bits = 0; + + short parttype = (short) (blocktype < 4 ? blocktype : 4); + short step_h0 = block_size[ parttype][0]; + short step_v0 = block_size[ parttype][1]; + short step_h = block_size[blocktype][0]; + short step_v = block_size[blocktype][1]; + short by0_part = by0[parttype][block8x8] << 2; + short bx0_part = bx0[parttype][block8x8] << 2; + short block_size_x = block_size[blocktype][0]; + short block_size_y = block_size[blocktype][1]; + + MotionVector **all_mv_l0 = currSlice->bipred_mv[list][LIST_0][ref_l0][blocktype]; + MotionVector **all_mv_l1 = currSlice->bipred_mv[list][LIST_1][ref_l1][blocktype]; + imgpel **mb_pred = currSlice->mb_pred[0]; + + // List0 + mvd_bits = mv_bit_cost(currMB, all_mv_l0, LIST_0, ref_l0, by0_part, bx0_part, step_v0, step_v, step_h0, step_h, mvd_bits); + // List1 + mvd_bits = mv_bit_cost(currMB, all_mv_l1, LIST_1, ref_l1, by0_part, bx0_part, step_v0, step_v, step_h0, step_h, mvd_bits); + + mcost = weighted_cost (lambda_factor, mvd_bits); + + // Get prediction + for (v = by0_part; v < by0_part + step_v0; v = (short) (v + block_size_y)) + { + for (h = bx0_part; h < bx0_part + step_h0; h = (short) (h + block_size_x)) + { + p_Dpb->pf_luma_prediction_bi (currMB, h, v, block_size_x, block_size_y, blocktype, blocktype, ref_l0, ref_l1, list); + } + } + + //----- cost of residual signal ----- + if ((!currSlice->p_Inp->Transform8x8Mode) || (blocktype>4)) + { + short diff16[16]; + short *diff; + + pic_pix_y = currMB->opix_y; + pic_pix_x = currMB->pix_x; + for (v = by0_part; v < by0_part + step_v0; v += 4) + { + for (h = bx0_part; h < bx0_part + step_h0; h += 4) + { + diff = diff16; + calcDifference(cur_img, pic_pix_x+h, pic_pix_y+v, mb_pred, h, v, 4, 4, diff); + mcost += p_Vid->distortion4x4 (diff16, DISTBLK_MAX); + } + } + } + else + { + short diff64[64]; + short *diff; + + pic_pix_y = currMB->opix_y; + pic_pix_x = currMB->pix_x; + for (v = by0_part; v < by0_part + step_v0; v += 8) + { + for (h = bx0_part; h < bx0_part + step_h0; h += 8) + { + diff = diff64; + calcDifference(cur_img, pic_pix_x+h, pic_pix_y+v, mb_pred, h, v, 8, 8, diff); + mcost += p_Vid->distortion8x8(diff64, DISTBLK_MAX); + } + } + } + + return mcost; +} + +void update_mv_block(Macroblock *currMB, MEBlock *mv_block, int h, int v) +{ + mv_block->block_x = (short) h; + mv_block->block_y = (short) v; + mv_block->pos_x = (short) (currMB->pix_x + (h << 2)); + mv_block->pos_y = (short) (currMB->opix_y + (v << 2)); + mv_block->pos_x2 = (short) (mv_block->pos_x >> 2); + mv_block->pos_y2 = (short) (mv_block->pos_y >> 2); + mv_block->pos_x_padded = (short) (mv_block->pos_x << 2); + mv_block->pos_y_padded = (short) (mv_block->pos_y << 2); + + mv_block->pos_cr_x = (short) (mv_block->pos_x >> currMB->p_Vid->shift_cr_x); + mv_block->pos_cr_y = (short) (mv_block->pos_y >> currMB->p_Vid->shift_cr_y); +} + +/*! + *********************************************************************** + * \brief + * Init motion vector block + *********************************************************************** + */ +void init_mv_block(Macroblock *currMB, MEBlock *mv_block, short blocktype, int list, char ref_idx, short mb_x, short mb_y) +{ + InputParameters *p_Inp = currMB->p_Inp; + VideoParameters *p_Vid = currMB->p_Vid; + Slice *currSlice = currMB->p_Slice; + mv_block->blocktype = blocktype; + mv_block->blocksize_x = block_size[blocktype][0]; // horizontal block size + mv_block->blocksize_y = block_size[blocktype][1]; // vertical block size + // update position info + update_mv_block(currMB, mv_block, mb_x, mb_y); + + mv_block->list = (char) list; + mv_block->ref_idx = ref_idx; + + mv_block->mv[LIST_0].mv_x = 0; + mv_block->mv[LIST_0].mv_y = 0; + mv_block->mv[LIST_1].mv_x = 0; + mv_block->mv[LIST_1].mv_y = 0; + // Init WP parameters + mv_block->p_Vid = p_Vid; + mv_block->p_Slice = currSlice; + mv_block->cost = INT_MAX; + mv_block->search_pos2 = 9; + mv_block->search_pos4 = 9; + + if (p_Inp->ChromaMEEnable) + get_mem2Dpel(&mv_block->orig_pic, 3, mv_block->blocksize_x * mv_block->blocksize_y); + else + get_mem2Dpel(&mv_block->orig_pic, 1, mv_block->blocksize_x * mv_block->blocksize_y); + + mv_block->ChromaMEEnable = p_Inp->ChromaMEEnable; + + mv_block->apply_bi_weights = p_Inp->UseWeightedReferenceME && ((currSlice->slice_type == B_SLICE) && p_Vid->active_pps->weighted_bipred_idc != 0); + mv_block->apply_weights = p_Inp->UseWeightedReferenceME && ( currSlice->weighted_prediction != 0 ); + + if (p_Inp->ChromaMEEnable) + { + mv_block->blocksize_cr_x = (short) (mv_block->blocksize_x >> p_Vid->shift_cr_x); + mv_block->blocksize_cr_y = (short) (mv_block->blocksize_y >> p_Vid->shift_cr_y); + + mv_block->ChromaMEWeight = p_Inp->ChromaMEWeight; + } + + if (mv_block->apply_weights) + { + // If implicit WP, single list weights are always non weighted + if ((currSlice->slice_type == B_SLICE) && (p_Vid->active_pps->weighted_bipred_idc == 2)) + { + mv_block->computePredFPel = p_Vid->computeUniPred[F_PEL]; + mv_block->computePredHPel = p_Vid->computeUniPred[H_PEL]; + mv_block->computePredQPel = p_Vid->computeUniPred[Q_PEL]; + } + else + { + mv_block->computePredFPel = p_Vid->computeUniPred[F_PEL + 3]; + mv_block->computePredHPel = p_Vid->computeUniPred[H_PEL + 3]; + mv_block->computePredQPel = p_Vid->computeUniPred[Q_PEL + 3]; + } + mv_block->computeBiPredFPel = p_Vid->computeBiPred2[F_PEL]; + mv_block->computeBiPredHPel = p_Vid->computeBiPred2[H_PEL]; + mv_block->computeBiPredQPel = p_Vid->computeBiPred2[Q_PEL]; + } + else + { + mv_block->computePredFPel = p_Vid->computeUniPred[F_PEL]; + mv_block->computePredHPel = p_Vid->computeUniPred[H_PEL]; + mv_block->computePredQPel = p_Vid->computeUniPred[Q_PEL]; + mv_block->computeBiPredFPel = p_Vid->computeBiPred1[F_PEL]; + mv_block->computeBiPredHPel = p_Vid->computeBiPred1[H_PEL]; + mv_block->computeBiPredQPel = p_Vid->computeBiPred1[Q_PEL]; + } +} + +/*! + *********************************************************************** + * \brief + * free motion vector block + *********************************************************************** + */ +void free_mv_block(MEBlock *mv_block) +{ + if (mv_block->orig_pic) + { + free_mem2Dpel(mv_block->orig_pic); + } +} + + +void get_original_block(VideoParameters *p_Vid, MEBlock *mv_block) +{ + //================================== + //===== GET ORIGINAL BLOCK ===== + //================================== + imgpel *orig_pic_tmp = mv_block->orig_pic[0]; + int bsx = mv_block->blocksize_x; + int pic_pix_x = mv_block->pos_x; + int i, j; + imgpel **cur_img = &p_Vid->pCurImg[mv_block->pos_y]; + + for (j = 0; j < mv_block->blocksize_y; j++) + { + memcpy(orig_pic_tmp,&cur_img[j][pic_pix_x], bsx * sizeof(imgpel)); + orig_pic_tmp += bsx; + } + + if ( p_Vid->p_Inp->ChromaMEEnable ) + { + bsx = mv_block->blocksize_cr_x; + pic_pix_x = mv_block->pos_cr_x; + + // copy the original cmp1 and cmp2 data to the orig_pic matrix + for ( i = 1; i<=2; i++) + { + cur_img = &p_Vid->pImgOrg[i][mv_block->pos_cr_y]; + orig_pic_tmp = mv_block->orig_pic[i]; + for (j = 0; j < mv_block->blocksize_cr_y; j++) + { + memcpy(orig_pic_tmp, &(cur_img[j][pic_pix_x]), bsx * sizeof(imgpel)); + orig_pic_tmp += bsx; + } + } + } +} + +void CheckSearchRange(VideoParameters *p_Vid, MotionVector *pPredMV, MotionVector *pSWC, MEBlock *mv_block) +{ + int iMaxMVD = p_Vid->max_mvd - 2; + SearchWindow *searchRange = &mv_block->searchRange; + int left = pSWC->mv_x + searchRange->min_x; + int right = pSWC->mv_x + searchRange->max_x; + int top = pSWC->mv_y + searchRange->min_y; + int down = pSWC->mv_y + searchRange->max_y; + + left = iClip3(pPredMV->mv_x - iMaxMVD, pPredMV->mv_x + iMaxMVD, left); + right = iClip3(pPredMV->mv_x - iMaxMVD, pPredMV->mv_x + iMaxMVD, right); + top = iClip3(pPredMV->mv_y - iMaxMVD, pPredMV->mv_y + iMaxMVD, top); + down = iClip3(pPredMV->mv_y - iMaxMVD, pPredMV->mv_y + iMaxMVD, down); + + if(leftmv_x = (short) ((left + right)>>1); + pSWC->mv_y = (short) ((top + down)>>1); + searchRange->min_x = left - pSWC->mv_x; + searchRange->max_x = imin(pSWC->mv_x-left, right-pSWC->mv_x); + searchRange->min_y = top - pSWC->mv_y; + searchRange->max_y = imin(pSWC->mv_y-top, down-pSWC->mv_y); + } + else + { + *pSWC = *pPredMV; + } +} + +/*! + *********************************************************************** + * \brief + * Block motion search + *********************************************************************** + */ +distblk //!< minimum motion cost after search +BlockMotionSearch (Macroblock *currMB, //!< Current Macroblock + MEBlock *mv_block, //!< Motion estimation information block + int mb_x, //!< x-coordinate inside macroblock + int mb_y, //!< y-coordinate inside macroblock + int* lambda_factor) //!< lagrangian parameter for determining motion cost +{ + // each 48-pel line stores the 16 luma pels (at 0) followed by 8 or 16 crcb[0] (at 16) and crcb[1] (at 32) pels + // depending on the type of chroma subsampling used: YUV 4:4:4, 4:2:2, and 4:2:0 + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + InputParameters *p_Inp = currMB->p_Inp; + + int i, j; + distblk max_value = DISTBLK_MAX; + distblk min_mcost = max_value; + int block_x = (mb_x>>2); + int block_y = (mb_y>>2); + + int bsx = mv_block->blocksize_x; + int bsy = mv_block->blocksize_y; + + short pic_pix_x = (short) (currMB->pix_x + mb_x); + + int blocktype = mv_block->blocktype; + int list = mv_block->list; + short ref = mv_block->ref_idx; + MotionVector *mv = &mv_block->mv[list], pred; + + MotionVector **all_mv = &currSlice->all_mv[list][ref][blocktype][block_y]; + + distblk *prevSad = (p_Inp->SearchMode[p_Vid->view_id] == EPZS)? currSlice->p_EPZS->distortion[list + currMB->list_offset][blocktype - 1]: NULL; + + get_neighbors(currMB, mv_block->block, mb_x, mb_y, bsx); + + PrepareMEParams(currSlice, mv_block, p_Inp->ChromaMEEnable, list + currMB->list_offset, ref); + + //================================== + //===== GET ORIGINAL BLOCK ===== + //================================== + if (blocktype > 4) + get_original_block(p_Vid, mv_block); + + //=========================================== + //===== GET MOTION VECTOR PREDICTOR ===== + //=========================================== + if (p_Inp->SearchMode[p_Vid->view_id] == UM_HEX) + { + p_Vid->p_UMHex->UMHEX_blocktype = blocktype; + p_Vid->p_UMHex->bipred_flag = 0; + UMHEXSetMotionVectorPredictor(currMB, &pred, p_Vid->enc_picture->mv_info, ref, list, mb_x, mb_y, bsx, bsy, mv_block); + } + else if (p_Inp->SearchMode[p_Vid->view_id] == UM_HEX_SIMPLE) + { + smpUMHEX_setup(currMB, ref, list, block_y, block_x, blocktype, currSlice->all_mv ); + currMB->GetMVPredictor (currMB, mv_block->block, &pred, ref, p_Vid->enc_picture->mv_info, list, mb_x, mb_y, bsx, bsy); + } + else + { + currMB->GetMVPredictor (currMB, mv_block->block, &pred, ref, p_Vid->enc_picture->mv_info, list, mb_x, mb_y, bsx, bsy); + } + + + + //================================== + //===== INTEGER-PEL SEARCH ===== + //================================== + if (p_Inp->EPZSSubPelGrid) + { + *mv = pred; + } + else + { +#if (JM_INT_DIVIDE) + mv->mv_x = (short) (((pred.mv_x + 2) >> 2) * 4); + mv->mv_y = (short) (((pred.mv_y + 2) >> 2) * 4); +#else + mv->mv_x = (short) ((pred.mv_x / 4) * 4); + mv->mv_y = (short) ((pred.mv_y / 4) * 4); +#endif + } + + if (p_Inp->DisableMEPrediction == TRUE) + { + mv->mv_x = 0; + mv->mv_y = 0; + } + + if (!p_Inp->rdopt) + { + MotionVector center = *mv; + //--- adjust search center so that the (0,0)-vector is inside --- + mv->mv_x = (short) iClip3 (mv_block->searchRange.min_x, mv_block->searchRange.max_x, mv->mv_x); + mv->mv_y = (short) iClip3 (mv_block->searchRange.min_y, mv_block->searchRange.max_y, mv->mv_y); + //mvbits overflow checking; + if((mv->mv_x != center.mv_x) || (mv->mv_y != center.mv_y)) + CheckSearchRange(p_Vid, ¢er, mv, mv_block); + } + + // valid search range limits could be precomputed once during the initialization process + clip_mv_range(p_Vid, 0, mv, Q_PEL); + + //--- perform motion search --- + min_mcost = currMB->IntPelME (currMB, &pred, mv_block, min_mcost, lambda_factor[F_PEL]); + + //============================== + //===== SUB-PEL SEARCH ===== + //============================== + mv_block->ChromaMEEnable = (p_Inp->ChromaMEEnable == ME_YUV_FP_SP ) ? TRUE : FALSE; // set it externally + + if (!p_Inp->DisableSubpelME[p_Vid->view_id]) + { + if (p_Inp->SearchMode[p_Vid->view_id] != EPZS || (ref == 0 || currSlice->structure != FRAME || (ref > 0 && min_mcost < 3.5 * prevSad[pic_pix_x >> 2]))) + { + if ( !p_Vid->start_me_refinement_hp ) + { + min_mcost = max_value; + } + min_mcost = currMB->SubPelME (currMB, &pred, mv_block, min_mcost, lambda_factor); + } + } + + // clip mvs after me is performed (is not exactly the best) + // better solution is to modify search window appropriately + clip_mv_range(p_Vid, 0, mv, Q_PEL); + + if (!p_Inp->rdopt) + { + // Get the skip mode cost + if (blocktype == 1 && (currSlice->slice_type == P_SLICE|| (currSlice->slice_type == SP_SLICE) )) + { + distblk cost; + FindSkipModeMotionVector (currMB); + + cost = GetSkipCostMB (currMB, lambda_factor[Q_PEL]); + if (cost < min_mcost) + { + min_mcost = cost; + *mv = currSlice->all_mv [0][0][0][0][0]; + } + } + } + + //=============================================== + //===== SET MV'S AND RETURN MOTION COST ===== + //=============================================== + + // Set first line + for (i=block_x; i < block_x + (bsx>>2); i++) + { + all_mv[0][i] = *mv; + } + + // set all other lines + for (j=1; j < (bsy>>2); j++) + { + memcpy(&all_mv[j][block_x], &all_mv[0][block_x], (bsx>>2) * sizeof(MotionVector)); + } + + + // Bipred ME consideration: returns minimum bipred cost + if (is_bipred_enabled(p_Vid, blocktype) && (ref == 0)) + { + BiPredBlockMotionSearch(currMB, mv_block, &pred, mb_x, mb_y, lambda_factor); + } + + return min_mcost; +} + + +/*! + *********************************************************************** + * \brief + * Bi-predictive motion search + *********************************************************************** + */ +static distblk BiPredBlockMotionSearch(Macroblock *currMB, //!< Current Macroblock + MEBlock *mv_block, + MotionVector *pred_mv, //!< current list motion vector predictor + int mb_x, //!< x-coordinate inside macroblock + int mb_y, //!< y-coordinate inside macroblock + int* lambda_factor) //!< lagrangian parameter for determining motion cost +{ + VideoParameters *p_Vid = currMB->p_Vid; + InputParameters *p_Inp = currMB->p_Inp; + Slice *currSlice = currMB->p_Slice; + int list = mv_block->list; + int i, j; + short bipred_type = list ? 0 : 1; + MotionVector ***** bipred_mv = currSlice->bipred_mv[bipred_type]; + distblk min_mcostbi = DISTBLK_MAX; + MotionVector *mv = &mv_block->mv[list]; + MotionVector bimv, tempmv; + MotionVector pred_mv1, pred_mv2, pred_bi; + MotionVector *bi_mv1 = NULL, *bi_mv2 = NULL; + short iterlist = (short) list; + int block_x = (mb_x>>2); + int block_y = (mb_y>>2); + int blocktype = mv_block->blocktype; + int bsx = mv_block->blocksize_x; + int bsy = mv_block->blocksize_y; + //PixelPos block[4]; // neighbor blocks + + //get_neighbors(currMB, mv_block->block, mb_x, mb_y, bsx); + + if (p_Inp->SearchMode[p_Vid->view_id] == UM_HEX) + { + p_Vid->p_UMHex->bipred_flag = 1; + UMHEXSetMotionVectorPredictor(currMB, &pred_bi, p_Vid->enc_picture->mv_info, 0, list ^ 1, mb_x, mb_y, bsx, bsy, mv_block); + } + else + currMB->GetMVPredictor (currMB, mv_block->block, &pred_bi, 0, p_Vid->enc_picture->mv_info, list ^ 1, mb_x, mb_y, bsx, bsy); + + if ((p_Inp->SearchMode[p_Vid->view_id] != EPZS) || (p_Inp->EPZSSubPelGrid == 0)) + { + mv->mv_x = ((mv->mv_x + 2) >> 2) * 4; + mv->mv_y = ((mv->mv_y + 2) >> 2) * 4; + bimv.mv_x = ((pred_bi.mv_x + 2) >> 2) * 4; + bimv.mv_y = ((pred_bi.mv_y + 2) >> 2) * 4; + } + else + { + bimv = pred_bi; + } + + //Bi-predictive motion Refinements + for (mv_block->iteration_no = 0; mv_block->iteration_no <= p_Inp->BiPredMERefinements; mv_block->iteration_no++) + { + if (mv_block->iteration_no & 0x01) + { + pred_mv1 = *pred_mv; + pred_mv2 = pred_bi; + bi_mv1 = mv; + bi_mv2 = &bimv; + iterlist = (short) list; + } + else + { + pred_mv1 = pred_bi; + pred_mv2 = *pred_mv; + bi_mv1 = &bimv; + bi_mv2 = mv; + iterlist = (short) (list ^ 1); + } + + tempmv = *bi_mv1; + + PrepareBiPredMEParams(currSlice, mv_block, mv_block->ChromaMEEnable, iterlist, currMB->list_offset, mv_block->ref_idx); + // Get bipred mvs for list iterlist given previously computed mvs from other list + min_mcostbi = currMB->BiPredME (currMB, iterlist, + &pred_mv1, &pred_mv2, bi_mv1, bi_mv2, mv_block, + (p_Inp->BiPredMESearchRange[p_Vid->view_id] <<2)>>mv_block->iteration_no, min_mcostbi, lambda_factor[F_PEL]); + + if (mv_block->iteration_no > 0 && (tempmv.mv_x == bi_mv1->mv_x) && (tempmv.mv_y == bi_mv1->mv_y)) + { + break; + } + } + + if (!p_Inp->DisableSubpelME[p_Vid->view_id]) + { + if (p_Inp->BiPredMESubPel) + { + if ( !p_Vid->start_me_refinement_hp ) + min_mcostbi = DISTBLK_MAX; + PrepareBiPredMEParams(currSlice, mv_block, mv_block->ChromaMEEnable, iterlist, currMB->list_offset, mv_block->ref_idx); + + min_mcostbi = currMB->SubPelBiPredME (currMB, mv_block, iterlist, &pred_mv1, &pred_mv2, bi_mv1, bi_mv2, min_mcostbi, lambda_factor); + } + + if (p_Inp->BiPredMESubPel==2) + { + if ( !p_Vid->start_me_refinement_qp ) + min_mcostbi = DISTBLK_MAX; + PrepareBiPredMEParams(currSlice, mv_block, mv_block->ChromaMEEnable, iterlist ^ 1, currMB->list_offset, mv_block->ref_idx); + + min_mcostbi = currMB->SubPelBiPredME (currMB, mv_block, iterlist ^ 1, &pred_mv2, &pred_mv1, bi_mv2, bi_mv1, min_mcostbi, lambda_factor); + } + } + + clip_mv_range(p_Vid, 0, bi_mv1, Q_PEL); + clip_mv_range(p_Vid, 0, bi_mv2, Q_PEL); + + for (j=block_y; j < block_y + (bsy>>2); j++) + { + for (i=block_x ; i < block_x + (bsx>>2); i++) + { + bipred_mv[iterlist ][(short) mv_block->ref_idx][blocktype][j][i] = *bi_mv1; + bipred_mv[iterlist ^ 1][(short) mv_block->ref_idx][blocktype][j][i] = *bi_mv2; + } + } + + + return min_mcostbi; +} + +/*! + *********************************************************************** + * \brief + * Motion Cost for Bidirectional modes + *********************************************************************** + */ +distblk BIDPartitionCost (Macroblock *currMB, + int blocktype, + int block8x8, + char cur_ref[2], + int lambda_factor) +{ + VideoParameters *p_Vid = currMB->p_Vid; + Slice *currSlice = currMB->p_Slice; + DecodedPictureBuffer *p_Dpb = p_Vid->p_Dpb_layer[p_Vid->dpb_layer_id]; + imgpel **cur_img = p_Vid->pCurImg; + + short pic_pix_x, pic_pix_y; + int v, h; + distblk mcost; + + int mvd_bits = 0; + + int parttype = (blocktype < 4 ? blocktype : 4); + int step_h0 = block_size[ parttype][0]; + int step_v0 = block_size[ parttype][1]; + int step_h = block_size[blocktype][0]; + int step_v = block_size[blocktype][1]; + int bx = bx0[parttype][block8x8] << 2; + int by = by0[parttype][block8x8] << 2; + short block_size_x = block_size[blocktype][0]; // this is the same as step_h and could be removed + short block_size_y = block_size[blocktype][1]; // this is the same as step_v and could be removed + + MotionVector **all_mv_l0 = currSlice->all_mv [LIST_0][(int) cur_ref[LIST_0]][blocktype]; + MotionVector **all_mv_l1 = currSlice->all_mv [LIST_1][(int) cur_ref[LIST_1]][blocktype]; + short bipred_me = 0; //no bipred for this case + imgpel **mb_pred = currSlice->mb_pred[0]; + int list_mode[2]; + list_mode[0] = blocktype; + list_mode[1] = blocktype; + + //----- cost for motion vector bits ----- + // Should write a separate, small function to do this processing + // List0 + mvd_bits = mv_bit_cost(currMB, all_mv_l0, LIST_0, cur_ref[LIST_0], by, bx, step_v0, step_v, step_h0, step_h, mvd_bits); + // List1 + mvd_bits = mv_bit_cost(currMB, all_mv_l1, LIST_1, cur_ref[LIST_1], by, bx, step_v0, step_v, step_h0, step_h, mvd_bits); + + mcost = weighted_cost (lambda_factor, mvd_bits); + + // Get prediction + for (v = by; v < by + step_v0; v += block_size_y) + { + for (h = bx; h < bx + step_h0; h += block_size_x) + { + p_Dpb->pf_luma_prediction (currMB, h, v, block_size_x, block_size_y, 2, list_mode, cur_ref, bipred_me); + } + } + + //----- cost of residual signal ----- + if ((!currSlice->p_Inp->Transform8x8Mode) || (blocktype>4)) + { + short diff16[16]; + short *diff; + + pic_pix_y = (short) currMB->opix_y; + pic_pix_x = (short) currMB->pix_x; + for (v= by; v < by + step_v0; v += BLOCK_SIZE) + { + for (h = bx; h < bx + step_h0; h += BLOCK_SIZE) + { + diff = diff16; + calcDifference(cur_img, pic_pix_x+h, pic_pix_y+v, mb_pred, h,v, 4, 4, diff); + mcost += p_Vid->distortion4x4 (diff16, DISTBLK_MAX); + } + } + } + else + { + short diff64[64]; + short *diff; + + pic_pix_y = (short) currMB->opix_y; + pic_pix_x = (short) currMB->pix_x; + for (v= by; v < by + step_v0; v += BLOCK_SIZE_8x8) + { + for (h = bx; h < bx + step_h0; h += BLOCK_SIZE_8x8) + { + diff = diff64; + calcDifference(cur_img, pic_pix_x+h, pic_pix_y+v, mb_pred, h, v, 8, 8, diff); + mcost += p_Vid->distortion8x8(diff64, DISTBLK_MAX); + } + } + } + + return mcost; +} + +/*! + ************************************************************************ + * \brief + * Get cost for skip mode for an macroblock + ************************************************************************ + */ +static distblk GetSkipCostMB (Macroblock *currMB, int lambda) +{ + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + InputParameters *p_Inp = currMB->p_Inp; + DecodedPictureBuffer *p_Dpb = p_Vid->p_Dpb_layer[p_Vid->dpb_layer_id]; + distblk cost = 0; + + int block; + imgpel **mb_pred = currSlice->mb_pred[0]; + char cur_ref[2] = {0, 0}; + int list_mode[2] = {0, 0}; + + //===== prediction of 16x16 skip block ===== + p_Dpb->pf_luma_prediction (currMB, 0, 0, MB_BLOCK_SIZE, MB_BLOCK_SIZE, 0, list_mode, cur_ref, 0); + + if (p_Inp->Transform8x8Mode == 0) + { + short diff16[16]; + short *diff; + int block_y, block_x;//, i, j; + int mb_x, mb_y; + + int pic_pix_y = currMB->opix_y; + int pic_pix_x = currMB->pix_x; + + for(block = 0;block < 4; block++) + { + mb_y = (block >> 1)<<3; + mb_x = (block & 0x01)<<3; + for (block_y = mb_y; block_y < mb_y + 8; block_y += 4) + { + for (block_x = mb_x; block_x < mb_x + 8; block_x += 4) + { + diff = diff16; + //===== get displaced frame difference ====== + calcDifference(p_Vid->pCurImg, pic_pix_x+block_x, pic_pix_y+block_y, mb_pred, block_x, block_y, 4, 4, diff); + cost += p_Vid->distortion4x4 (diff16, DISTBLK_MAX); + } + } + } + } + else + { + short diff64[64]; + short *diff; + //int i, j; + int mb_x, mb_y; + + int pic_pix_y = currMB->opix_y; + int pic_pix_x = currMB->pix_x; + + for(block = 0;block < 4;block++) + { + mb_y = (block >> 1)<<3; + mb_x = (block & 0x01)<<3; + + //===== get displaced frame difference ====== + diff = diff64; + calcDifference(p_Vid->pCurImg, pic_pix_x+mb_x, pic_pix_y+mb_y, mb_pred, mb_x, mb_y, 8, 8, diff); + cost += p_Vid->distortion8x8 (diff64, DISTBLK_MAX); + } + } + + //cost -= ((lambda_factor[Q_PEL] + 4096) >> 13); + cost -= weight_cost(lambda, 8); + + return cost; +} + +/*! + ************************************************************************ + * \brief + * Find motion vector for the Skip mode + ************************************************************************ + */ +void FindSkipModeMotionVector (Macroblock *currMB) +{ + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + PicMotionParams **motion = p_Vid->enc_picture->mv_info; + int bx, by; + MotionVector **all_mv = currSlice->all_mv[0][0][0]; + + MotionVector pmv; + + int zeroMotionAbove; + int zeroMotionLeft; + PixelPos mb[4]; + int a_mv_y = 0; + int a_ref_idx = 0; + int b_mv_y = 0; + int b_ref_idx = 0; + + get_neighbors(currMB, mb, 0, 0, 16); + + if (mb[0].available) + { + a_mv_y = motion[mb[0].pos_y][mb[0].pos_x].mv[LIST_0].mv_y; + a_ref_idx = motion[mb[0].pos_y][mb[0].pos_x].ref_idx[LIST_0]; + + if (currMB->mb_field && !p_Vid->mb_data[mb[0].mb_addr].mb_field) + { + a_mv_y /=2; + a_ref_idx *=2; + } + if (!currMB->mb_field && p_Vid->mb_data[mb[0].mb_addr].mb_field) + { + a_mv_y *= 2; + a_ref_idx >>=1; + } + } + + if (mb[1].available) + { + b_mv_y = motion[mb[1].pos_y][mb[1].pos_x].mv[LIST_0].mv_y; + b_ref_idx = motion[mb[1].pos_y][mb[1].pos_x].ref_idx[LIST_0]; + + if (currMB->mb_field && !p_Vid->mb_data[mb[1].mb_addr].mb_field) + { + b_mv_y /=2; + b_ref_idx *=2; + } + if (!currMB->mb_field && p_Vid->mb_data[mb[1].mb_addr].mb_field) + { + b_mv_y *=2; + b_ref_idx >>=1; + } + } + + zeroMotionLeft = !mb[0].available ? 1 : a_ref_idx==0 && motion[mb[0].pos_y][mb[0].pos_x].mv[LIST_0].mv_x ==0 && a_mv_y==0 ? 1 : 0; + zeroMotionAbove = !mb[1].available ? 1 : b_ref_idx==0 && motion[mb[1].pos_y][mb[1].pos_x].mv[LIST_0].mv_x ==0 && b_mv_y==0 ? 1 : 0; + + if (zeroMotionAbove || zeroMotionLeft) + { + memset(&all_mv [0][0], 0, 16 * sizeof(MotionVector)); // 4 * 4 + } + else + { + currMB->GetMVPredictor (currMB, mb, &pmv, 0, motion, LIST_0, 0, 0, 16, 16); + + for (by = 0;by < 4;by++) + for (bx = 0;bx < 4;bx++) + { + all_mv [by][bx] = pmv; + } + } +} + +/*! + ************************************************************************ + * \brief + * Get cost for direct mode for an 8x8 block + ************************************************************************ + */ +distblk GetDirectCost8x8 (Macroblock *currMB, int block, distblk *cost8x8) +{ + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + InputParameters *p_Inp = currMB->p_Inp; + DecodedPictureBuffer *p_Dpb = p_Vid->p_Dpb_layer[p_Vid->dpb_layer_id]; + int pic_pix_y, pic_pix_x, i, j; + + distblk cost = 0; + int mb_y = (block >> 1)<<3; + int mb_x = (block & 0x01)<<3; + imgpel **mb_pred = currSlice->mb_pred[0]; + int list_mode[2] = {0, 0}; + + // Check if valid + for (j=(currMB->opix_y + mb_y) >> 2; j < (currMB->opix_y + mb_y + 8) >> 2; j++) + { + for (i=(currMB->pix_x + mb_x) >> 2; i < (currMB->pix_x + mb_x + 8) >> 2; i++) + { + if (currSlice->direct_pdir[j][i] < 0) + { + *cost8x8 = DISTBLK_MAX; + return DISTBLK_MAX; //mode not allowed + } + } + } + + //===== Generate direct prediction ===== + for (j = mb_y; j < mb_y + 8; j += 4) + { + pic_pix_y = (currMB->opix_y + j) >> 2; + for (i = mb_x; i < mb_x + 8; i += 4) + { + pic_pix_x = (currMB->pix_x + i) >> 2; + p_Dpb->pf_luma_prediction (currMB, i, j, 4, 4, currSlice->direct_pdir[pic_pix_y][pic_pix_x], + list_mode, currSlice->direct_ref_idx[pic_pix_y][pic_pix_x], 0); + } + } + + if(p_Inp->Transform8x8Mode) + { + short diff16[4][16]; + short diff64[64]; + short *tmp64 = diff64; + short *tmp16[4]; //{diff16[0], diff16[1], diff16[2], diff16[3]}; + int index; + + tmp16[0] = diff16[0]; + tmp16[1] = diff16[1]; + tmp16[2] = diff16[2]; + tmp16[3] = diff16[3]; + + pic_pix_y = currMB->opix_y; + pic_pix_x = currMB->pix_x; + //===== get displaced frame difference ====== + //p_Dpb->pf_calcDifference(p_Vid->pCurImg, pic_pix_x+mb_x, pic_pix_y+mb_y, mb_pred, mb_x, mb_y, 8, 8, tmp64); + for (j = mb_y; j < 8 + mb_y; j++) + { + for (i = mb_x; i < 8 + mb_x; i++) + { + index = 2 * ((j - mb_y)> 3) + ((i - mb_x)> 3); + *tmp64++ = *(tmp16[index])++ = (short) (p_Vid->pCurImg[pic_pix_y + j][pic_pix_x + i] - mb_pred[j][i]); + } + } + + cost += p_Vid->distortion4x4 (diff16[0], DISTBLK_MAX); + cost += p_Vid->distortion4x4 (diff16[1], DISTBLK_MAX); + cost += p_Vid->distortion4x4 (diff16[2], DISTBLK_MAX); + cost += p_Vid->distortion4x4 (diff16[3], DISTBLK_MAX); + *cost8x8 += p_Vid->distortion8x8 (diff64, DISTBLK_MAX); + } + else + { + int block_y, block_x; + short diff16[16]; + short *diff; + + for (block_y=mb_y; block_y < mb_y + 8; block_y += 4) + { + pic_pix_y = currMB->opix_y + block_y; + + for (block_x=mb_x; block_xpix_x + block_x; + diff = diff16; + + //===== get displaced frame difference ====== + calcDifference(p_Vid->pCurImg, pic_pix_x, pic_pix_y, mb_pred, block_x, block_y, 4, 4, diff); + cost += p_Vid->distortion4x4 (diff16, DISTBLK_MAX); + } + } + } + + return cost; +} + + + +/*! + ************************************************************************ + * \brief + * Get cost for direct mode for an macroblock + ************************************************************************ + */ +distblk GetDirectCostMB (Macroblock *currMB) +{ + Slice *currSlice = currMB->p_Slice; + InputParameters *p_Inp = currSlice->p_Inp; + int i; + distblk cost = 0; + distblk cost8x8 = 0; + int bslice = currSlice->slice_type == B_SLICE; +#if (MVC_EXTENSION_ENABLE) + int *InterSearch = p_Inp->InterSearch[(currSlice->p_Vid->num_of_layers > 1) ? currSlice->view_id : 0][bslice]; +#else + int *InterSearch = p_Inp->InterSearch[0][bslice]; +#endif + + for (i=0; i<4; i++) + { + cost += GetDirectCost8x8 (currMB, i, &cost8x8); + if (cost8x8 == DISTBLK_MAX) return DISTBLK_MAX; + } + + switch(p_Inp->Transform8x8Mode) + { + case 1: // Mixture of 8x8 & 4x4 transform + if((cost8x8 < cost)|| + !(InterSearch[5] && + InterSearch[6] && + InterSearch[7]) + ) + { + cost = cost8x8; //return 8x8 cost + } + break; + case 2: // 8x8 Transform only + cost = cost8x8; + break; + default: // 4x4 Transform only + break; + } + + return cost; +} + +/*! + ************************************************************************ + * \brief + * Motion search for a macroblock partition + ************************************************************************ + */ +void PartitionMotionSearch (Macroblock *currMB, + int blocktype, + int block8x8, + int *lambda_factor) +{ + VideoParameters *p_Vid = currMB->p_Vid; + Slice *currSlice = currMB->p_Slice; + +#if GET_METIME + TIME_T me_time_start; + TIME_T me_time_end; + int64 me_tmp_time; + gettime( &me_time_start ); // start time ms +#endif + + if (currSlice->rdoq_motion_copy == 1) + { + PicMotionParams **motion = p_Vid->enc_picture->mv_info; + short by = by0[blocktype][block8x8]; + short bx = bx0[blocktype][block8x8]; + short step_h = (part_size[blocktype][0]); + short step_v = (part_size[blocktype][1]); + + short pic_block_y = currMB->block_y + by; + short pic_block_x = currMB->block_x + bx; + int list_offset = currMB->list_offset; + int numlists = (currSlice->slice_type == B_SLICE) ? 2 : 1; + distblk *m_cost; + + short list = LIST_0; + short ref = 0; + + //===== LOOP OVER REFERENCE FRAMES ===== + for (list = 0; list < numlists; list++) + { + for (ref=0; ref < currSlice->listXsize[list+list_offset]; ref++) + { + m_cost = &p_Vid->motion_cost[blocktype][list][ref][block8x8]; + + //===== LOOP OVER SUB MACRO BLOCK partitions + updateMV_mp(currMB, m_cost, ref, list, bx, by, blocktype, block8x8); + set_me_parameters(motion, &currSlice->all_mv[list][ref][blocktype][by][bx], list, (char) ref, step_h, step_v, pic_block_y, pic_block_x); + } + } + } + else + { + InputParameters *p_Inp = currMB->p_Inp; + short by = by0[blocktype][block8x8]; + short bx = bx0[blocktype][block8x8]; + short step_h = (part_size[blocktype][0]); + short step_v = (part_size[blocktype][1]); + + short pic_block_y = currMB->block_y + by; + short pic_block_x = currMB->block_x + bx; + int list_offset = currMB->list_offset; + int numlists = (currSlice->slice_type == B_SLICE) ? 2 : 1; + short list = LIST_0; + short ref = 0; + MEBlock mv_block; + distblk *m_cost; + + + PicMotionParams **motion = p_Vid->enc_picture->mv_info; + + // Set flag for 8x8 Hadamard consideration for SATD (only used when 8x8 integer transform is used for encoding) + mv_block.test8x8 = p_Inp->Transform8x8Mode; + + init_mv_block(currMB, &mv_block, (short) blocktype, list, (char) ref, bx, by); + + if (p_Inp->SearchMode[p_Vid->view_id] == EPZS) + { + if (p_Inp->EPZSSubPelGrid) + currMB->IntPelME = EPZS_integer_motion_estimation; + else + currMB->IntPelME = EPZS_motion_estimation; + } + + get_original_block(p_Vid, &mv_block); + + //--- motion search for block --- + { + //===== LOOP OVER REFERENCE FRAMES ===== + for (list = 0; list < numlists; list++) + { + //----- set arrays ----- + mv_block.list = (char) list; + for (ref=0; ref < currSlice->listXsize[list+list_offset]; ref++) + { + mv_block.ref_idx = (char) ref; + m_cost = &p_Vid->motion_cost[blocktype][list][ref][block8x8]; + + { + //----- set search range --- + get_search_range(&mv_block, p_Inp, ref, blocktype); + + //===== LOOP OVER MACROBLOCK partitions + *m_cost = BlockMotionSearch (currMB, &mv_block, bx<<2, by<<2, lambda_factor); + } + //--- set motion vectors and reference frame --- + set_me_parameters(motion, &currSlice->all_mv[list][ref][blocktype][by][bx], list, (char) ref, step_h, step_v, pic_block_y, pic_block_x); + } + } + } + + free_mv_block(&mv_block); + } + +#if GET_METIME + gettime(&me_time_end); // end time ms + me_tmp_time = timediff (&me_time_start, &me_time_end); + p_Vid->me_tot_time += me_tmp_time; + p_Vid->me_time += me_tmp_time; +#endif +} + +/*! + ************************************************************************ + * \brief + * Motion search for a submacroblock partition + ************************************************************************ + */ +void SubPartitionMotionSearch (Macroblock *currMB, + int blocktype, + int block8x8, + int *lambda_factor) + +{ + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + +#if GET_METIME + TIME_T me_time_start; + TIME_T me_time_end; + int64 me_tmp_time; + gettime( &me_time_start ); // start time ms +#endif + + if (currSlice->rdoq_motion_copy == 1) + { + int parttype = 4; + PicMotionParams **motion = p_Vid->enc_picture->mv_info; + short by = by0[parttype][block8x8]; + short bx = bx0[parttype][block8x8]; + short step_h = (part_size[blocktype][0]); + short step_v = (part_size[blocktype][1]); + int list_offset = currMB->list_offset; + int numlists = (currSlice->slice_type == B_SLICE) ? 2 : 1; + distblk *m_cost; + MotionVector *all_mv; + short list = LIST_0; + short ref = 0; + + short step_h0 = (part_size[ parttype][0]); + short step_v0 = (part_size[ parttype][1]); + + int v, h; + int pic_block_y; + + //===== LOOP OVER REFERENCE FRAMES ===== + for (list = 0; list < numlists; list++) + { + for (ref=0; ref < currSlice->listXsize[list+list_offset]; ref++) + { + m_cost = &p_Vid->motion_cost[blocktype][list][ref][block8x8]; + + //===== LOOP OVER SUB MACRO BLOCK partitions + for (v=by; vblock_y + v; + for (h=bx; hall_mv[list][ref][blocktype][v][h]; + + updateMV_mp(currMB, m_cost, ref, list, h, v, blocktype, block8x8); + + //--- set motion vectors and reference frame (for motion vector prediction) --- + set_me_parameters(motion, all_mv, list, (char) ref, step_h, step_v, pic_block_y, currMB->block_x + h); + } // h + } // v + } + } + } + else + { + InputParameters *p_Inp = currMB->p_Inp; + PicMotionParams **motion = p_Vid->enc_picture->mv_info; + int parttype = 4; + short by = by0[parttype][block8x8]; + short bx = bx0[parttype][block8x8]; + short step_h0 = (part_size[ parttype][0]); + short step_v0 = (part_size[ parttype][1]); + short step_h = (part_size[blocktype][0]); + short step_v = (part_size[blocktype][1]); + int list_offset = currMB->list_offset; + int numlists = (currSlice->slice_type == B_SLICE) ? 2 : 1; + MotionVector *all_mv; + short list = LIST_0; + short ref = 0; + MEBlock mv_block; + distblk *m_cost; + distblk mcost; + int v, h; + int pic_block_y; + + // Set if 8x8 transform will be used if SATD is used + mv_block.test8x8 = p_Inp->Transform8x8Mode && blocktype == 4; + + if (p_Inp->SearchMode[p_Vid->view_id] == EPZS) + { + if (p_Inp->EPZSSubPelGrid) + { + if (blocktype > 4) + currMB->IntPelME = EPZS_integer_subMB_motion_estimation; + else + currMB->IntPelME = EPZS_integer_motion_estimation; + } + else + { + if (blocktype > 4) + currMB->IntPelME = EPZS_subMB_motion_estimation; + else + currMB->IntPelME = EPZS_motion_estimation; + } + } + + init_mv_block(currMB, &mv_block, (short) blocktype, list, (char) ref, bx, by); + + if (blocktype == 4) + get_original_block(p_Vid, &mv_block); + + //===== LOOP OVER REFERENCE FRAMES ===== + for (list=0; listlistXsize[list+list_offset]; ref++) + { + mv_block.ref_idx = (char) ref; + m_cost = &p_Vid->motion_cost[blocktype][list][ref][block8x8]; + //----- set search range --- + get_search_range(&mv_block, p_Inp, ref, blocktype); + + //----- init motion cost ----- + *m_cost = 0; + + //===== LOOP OVER SUB MACRO BLOCK partitions + for (v=by; vblock_y + v; + + for (h=bx; hall_mv[list][ref][blocktype][v][h]; + + //--- motion search for block --- + update_mv_block(currMB, &mv_block, h, v); + { + //----- set search range --- + get_search_range(&mv_block, p_Inp, ref, blocktype); + + mcost = BlockMotionSearch (currMB, &mv_block, h<<2, v<<2, lambda_factor); + + *m_cost += mcost; + } + //--- set motion vectors and reference frame (for motion vector prediction) --- + set_me_parameters(motion, all_mv, list, (char) ref, step_h, step_v, pic_block_y, currMB->block_x + h); + } + } + + if ((p_Inp->Transform8x8Mode == 1) && p_Inp->RDOQ_CP_MV && (blocktype == 4)) + { + if (currMB->luma_transform_size_8x8_flag) + { + currSlice->tmp_mv8[list][ref][by][bx] = currSlice->all_mv[list][ref][blocktype][by][bx]; + currSlice->motion_cost8[list][ref][block8x8] = *m_cost; + } + else + { + currSlice->tmp_mv4[list][ref][by][bx] = currSlice->all_mv[list][ref][blocktype][by][bx]; + currSlice->motion_cost4[list][ref][block8x8] = *m_cost; + } + } + } + + } + + free_mv_block(&mv_block); + } + +#if GET_METIME + gettime(&me_time_end); // end time ms + me_tmp_time = timediff (&me_time_start, &me_time_end); + p_Vid->me_tot_time += me_tmp_time; + p_Vid->me_time += me_tmp_time; +#endif +} + + diff --git a/lencod/src/nal.c b/lencod/src/nal.c new file mode 100644 index 0000000..fc2f35a --- /dev/null +++ b/lencod/src/nal.c @@ -0,0 +1,145 @@ + +/*! + ************************************************************************************** + * \file + * nal.c + * \brief + * Handles the operations on converting String of Data Bits (SODB) + * to Raw Byte Sequence Payload (RBSP), and then + * onto Encapsulate Byte Sequence Payload (EBSP). + * \date 14 June 2002 + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * - Shankar Regunathan + * - Stephan Wenger + *************************************************************************************** + */ + +#include "contributors.h" +#include "global.h" +#include "enc_statistics.h" +#include "biariencode.h" +#include "nal.h" + +static const int MbWidthC [4]= { 0, 8, 8, 16}; +static const int MbHeightC [4]= { 0, 8, 16, 16}; + + /*! + ************************************************************************ + * \brief + * Converts String Of Data Bits (SODB) to Raw Byte Sequence + * Packet (RBSP) + * \param currStream + * Bitstream which contains data bits. + * \return None + * \note currStream is byte-aligned at the end of this function + * + ************************************************************************ +*/ + +void SODBtoRBSP(Bitstream *currStream) +{ + currStream->byte_buf <<= 1; + currStream->byte_buf |= 1; + currStream->bits_to_go--; + currStream->byte_buf <<= currStream->bits_to_go; + currStream->streamBuffer[currStream->byte_pos++] = currStream->byte_buf; + currStream->bits_to_go = 8; + currStream->byte_buf = 0; +} + + +/*! +************************************************************************ +* \brief +* This function add emulation_prevention_three_byte for all occurrences +* of the following byte sequences in the stream +* 0x000000 -> 0x00000300 +* 0x000001 -> 0x00000301 +* 0x000002 -> 0x00000302 +* 0x000003 -> 0x00000303 +* +* \param NaluBuffer +* pointer to target buffer +* \param rbsp +* pointer to source buffer +* \param rbsp_size +* Size of source +* \return +* Size target buffer after emulation prevention. +* +************************************************************************ +*/ + +int RBSPtoEBSP(byte *NaluBuffer, unsigned char *rbsp, int rbsp_size) +{ + int j = 0; + int count = 0; + int i; + + for(i = 0; i < rbsp_size; i++) + { + if(count == ZEROBYTES_SHORTSTARTCODE && !(rbsp[i] & 0xFC)) + { + NaluBuffer[j] = 0x03; + j++; + count = 0; + } + NaluBuffer[j] = rbsp[i]; + if(rbsp[i] == 0x00) + count++; + else + count = 0; + j++; + } + + return j; +} + +/*! +************************************************************************ +* \brief +* This function adds cabac_zero_word syntax elements at the end of the +* NAL unit to +* +* \param p_Vid +* VideoParameters structure +* \param nalu +* target NAL unit +* \param cur_stats +* currently used statistics parameters +* \return +* number of added bytes +* +************************************************************************ +*/ +int addCabacZeroWords(VideoParameters *p_Vid, NALU_t *nalu, StatParameters *cur_stats) +{ + seq_parameter_set_rbsp_t *active_sps = p_Vid->active_sps; + + int stuffing_bytes = 0; + int i = 0; + + byte *buf = &nalu->buf[nalu->len]; + + int RawMbBits = 256 * p_Vid->bitdepth_luma + 2 * MbWidthC[active_sps->chroma_format_idc] * MbHeightC[active_sps->chroma_format_idc] * p_Vid->bitdepth_chroma; + int min_num_bytes = ((96 * get_pic_bin_count(p_Vid)) - (RawMbBits * (int)p_Vid->PicSizeInMbs *3) + 1023) / 1024; + + if (min_num_bytes > p_Vid->bytes_in_picture) + { + stuffing_bytes = min_num_bytes - p_Vid->bytes_in_picture; + printf ("Inserting %d/%d cabac_zero_word syntax elements/bytes (Clause 7.4.2.10)\n", ((stuffing_bytes + 2)/3), stuffing_bytes); + + for (i = 0; i < stuffing_bytes; i+=3 ) + { + *buf++ = 0x00; // CABAC zero word + *buf++ = 0x00; + *buf++ = 0x03; + } + cur_stats->bit_use_stuffing_bits[p_Vid->type] += (i<<3); + nalu->len += i; + } + + return i; +} + diff --git a/lencod/src/nalu.c b/lencod/src/nalu.c new file mode 100644 index 0000000..01c4619 --- /dev/null +++ b/lencod/src/nalu.c @@ -0,0 +1,164 @@ + +/*! + ************************************************************************ + * \file nalu.c + * + * \brief + * Common NALU support functions + * + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * - Stephan Wenger + ************************************************************************ + */ + +#include "global.h" +#include "nalu.h" +#include "nal.h" + +/*! + ************************************************************************************* + * \brief + * Converts an RBSP to a NALU + * + * \param rbsp + * byte buffer with the rbsp + * \param nalu + * nalu structure to be filled + * \param rbsp_size + * size of the rbsp in bytes + * \param nal_unit_type + * as in JVT doc + * \param nal_reference_idc + * as in JVT doc + * \param UseAnnexbLongStartcode + * some incomprehensible CABAC stuff + * \param UseAnnexbLongStartcode + * when 1 and when using AnnexB bytestreams, then use a long startcode prefix + * + * \return + * length of the NALU in bytes + ************************************************************************************* + */ + +int RBSPtoNALU (unsigned char *rbsp, NALU_t *nalu, int rbsp_size, int nal_unit_type, int nal_reference_idc, int UseAnnexbLongStartcode) +{ + int len; + + assert (nalu != NULL); + assert (nal_reference_idc <=3 && nal_reference_idc >=0); +#if (MVC_EXTENSION_ENABLE) + assert (nal_unit_type > 0 && nal_unit_type <= NALU_TYPE_SLC_EXT); +#else + assert (nal_unit_type > 0 && nal_unit_type <= NALU_TYPE_FILL); +#endif + assert (rbsp_size < MAXRBSPSIZE); + + nalu->startcodeprefix_len = UseAnnexbLongStartcode ? 4 : 3; + nalu->forbidden_bit = 0; + nalu->nal_reference_idc = (NalRefIdc) nal_reference_idc; + nalu->nal_unit_type = (NaluType) nal_unit_type; + +#if (MVC_EXTENSION_ENABLE) + if(nal_unit_type==NALU_TYPE_PREFIX || nal_unit_type==NALU_TYPE_SLC_EXT) + { + nalu->svc_extension_flag = 0; + //nalu->non_idr_flag = (nal_reference_idc==NALU_PRIORITY_HIGHEST) ? 0:1; + nalu->reserved_one_bit = 1; + + } + else + nalu->svc_extension_flag = 0; +#endif + + + len = RBSPtoEBSP (nalu->buf, rbsp, rbsp_size); + nalu->len = len; + + return len; +} + +/*! + ************************************************************************ + * \brief + * write AUD NALU + ************************************************************************ + */ + +int Write_AUD_NALU( VideoParameters *p_Vid ) +{ + int RBSPlen = 0; + int len; + byte rbsp[MAXRBSPSIZE]; + NALU_t *nalu = AllocNALU( MAXNALUSIZE ); + + switch( p_Vid->type ) + { + case I_SLICE: + p_Vid->primary_pic_type = 0; + break; + case P_SLICE: + p_Vid->primary_pic_type = 1; + break; + case B_SLICE: + p_Vid->primary_pic_type = 2; + break; + } + RBSPlen = 1; + rbsp[0] = (byte) (p_Vid->primary_pic_type << 5); + rbsp[0] |= (1 << 4); + + // write RBSP into NALU + RBSPtoNALU( rbsp, nalu, RBSPlen, NALU_TYPE_AUD, NALU_PRIORITY_DISPOSABLE, 1 ); + // write NALU into bitstream + len = p_Vid->WriteNALU( p_Vid, nalu, p_Vid->f_out ); + + FreeNALU( nalu ); + + return len; +} + +/*! + ************************************************************************ + * \brief + * write Filler Data NALU (results to num_bytes + 4 bytes written) + ************************************************************************ + */ + +int Write_Filler_Data_NALU( VideoParameters *p_Vid, int num_bytes ) +{ + int RBSPlen = num_bytes - 1; + int len, bytes_written = 0; + byte rbsp[MAXRBSPSIZE]; + byte filler_byte = (byte)0xFF; + byte trailing_byte = (byte)0x80; + NALU_t *nalu = AllocNALU( MAXNALUSIZE ); + + num_bytes = iClip3( 1, (MAXRBSPSIZE - 2), num_bytes ); + assert( num_bytes > 0 && num_bytes < (MAXRBSPSIZE - 1) ); + + num_bytes = imax( 2, num_bytes ); // one byte for the NAL header and one for the rbsp trailing byte + + if ( RBSPlen > 1 ) + { + while ( bytes_written < (RBSPlen - 1) ) + { + rbsp[ bytes_written++ ] = filler_byte; + } + } + rbsp[ bytes_written++ ] = trailing_byte; // rbsp_trailing_bits + assert( num_bytes == (bytes_written + 1) ); + + // write RBSP into NALU + RBSPtoNALU( rbsp, nalu, RBSPlen, NALU_TYPE_FILL, NALU_PRIORITY_DISPOSABLE, 1 ); + // write NALU into bitstream + len = p_Vid->WriteNALU( p_Vid, nalu, p_Vid->f_out ); + p_Vid->bytes_in_picture += (nalu->len + 1); + + FreeNALU( nalu ); + + return len; +} + + + diff --git a/lencod/src/output.c b/lencod/src/output.c new file mode 100644 index 0000000..a493f85 --- /dev/null +++ b/lencod/src/output.c @@ -0,0 +1,556 @@ + +/*! + ************************************************************************ + * \file output.c + * + * \brief + * Output an image and Trance support + * + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * - Karsten Suehring + ************************************************************************ + */ + +#include "contributors.h" +#include "global.h" +#include "image.h" +#include "input.h" +#include "output.h" + +/*! + ************************************************************************ + * \brief + * Convert image plane to temporary buffer for file writing + * \param imgX + * Pointer to image plane + * \param buf + * Buffer for file output + * \param size_x + * horizontal size + * \param size_y + * vertical size + * \param symbol_size_in_bytes + * number of bytes used per pel + * \param crop_left + * pixels to crop from left + * \param crop_right + * pixels to crop from right + * \param crop_top + * pixels to crop from top + * \param crop_bottom + * pixels to crop from bottom + ************************************************************************ + */ +void img2buf (imgpel** imgX, unsigned char* buf, int size_x, int size_y, int symbol_size_in_bytes, int crop_left, int crop_right, int crop_top, int crop_bottom) +{ + int i,j; + + int twidth = size_x - crop_left - crop_right; + int theight = size_y - crop_top - crop_bottom; + int size = 0; + unsigned char ui8; + uint16 tmp16, ui16; + unsigned long tmp32, ui32; + + if (( sizeof(char) == sizeof (imgpel)) && ( sizeof(char) == symbol_size_in_bytes)) + { + // imgpel == pixel_in_file == 1 byte -> simple copy + if (crop_left == 0 && crop_top == 0 && crop_right == 0 && crop_bottom == 0) + { + //memcpy(buf,&(imgX[0][0]), twidth * theight); + for(j=0; j sizeof(char) + if (testEndian()) + { + // big endian + switch (symbol_size_in_bytes) + { + case 1: + { + for(i=crop_top;i> 8) | ((tmp16&0xFF)<<8); + memcpy(buf+((j-crop_left+((i-crop_top)*(twidth)))*2),&(ui16), 2); + } + break; + } + case 4: + { + for(i=crop_top;i>8) | ((tmp32&0xFF000000)>>24); + memcpy(buf+((j-crop_left+((i-crop_top)*(twidth)))*4),&(ui32), 4); + } + break; + } + default: + { + error ("writing only to formats of 8, 16 or 32 bit allowed on big endian architecture", 500); + break; + } + } + + } + else + { + // little endian + if (sizeof (imgpel) < symbol_size_in_bytes) + { + // this should not happen. we should not have smaller imgpel than our source material. + size = sizeof (imgpel); + // clear buffer + memset (buf, 0, (twidth*theight*symbol_size_in_bytes)); + } + else + { + size = symbol_size_in_bytes; + } + + for(i=crop_top;ipic_unit_size_shift3; + Boolean rgb_output = (Boolean) (output->color_model != CM_YUV && output->yuv_format == YUV444); + unsigned char *buf; + + if (p->non_existing) + return; + + if (p_out == -1) + return; + + if (p->frame_cropping_flag) + { + crop_left = SubWidthC[p->chroma_format_idc] * p->frame_crop_left_offset; + crop_right = SubWidthC[p->chroma_format_idc] * p->frame_crop_right_offset; + crop_top = SubHeightC[p->chroma_format_idc]*( 2 - p->frame_mbs_only_flag ) * p->frame_crop_top_offset; + crop_bottom = SubHeightC[p->chroma_format_idc]*( 2 - p->frame_mbs_only_flag ) * p->frame_crop_bottom_offset; + } + else + { + crop_left = crop_right = crop_top = crop_bottom = 0; + } + + //printf ("write frame size: %dx%d\n", p->size_x-crop_left-crop_right,p->size_y-crop_top-crop_bottom ); + + // KS: this buffer should actually be allocated only once, but this is still much faster than the previous version + buf = malloc (p->size_x * p->size_y * symbol_size_in_bytes); + if (NULL==buf) + { + no_mem_exit("write_out_picture: buf"); + } + + if(rgb_output) + { + crop_left = p->frame_crop_left_offset; + crop_right = p->frame_crop_right_offset; + crop_top = ( 2 - p->frame_mbs_only_flag ) * p->frame_crop_top_offset; + crop_bottom = ( 2 - p->frame_mbs_only_flag ) * p->frame_crop_bottom_offset; + + img2buf (p->imgUV[1], buf, p->size_x_cr, p->size_y_cr, symbol_size_in_bytes, crop_left, crop_right, crop_top, crop_bottom); + ret = write(p_out, buf, (p->size_y_cr-crop_bottom-crop_top)*(p->size_x_cr-crop_right-crop_left)*symbol_size_in_bytes); + if (ret != ((p->size_y_cr-crop_bottom-crop_top)*(p->size_x_cr-crop_right-crop_left)*symbol_size_in_bytes)) + { + error ("write_out_picture: error writing to RGB output file.", 500); + } + + if (p->frame_cropping_flag) + { + crop_left = SubWidthC[p->chroma_format_idc] * p->frame_crop_left_offset; + crop_right = SubWidthC[p->chroma_format_idc] * p->frame_crop_right_offset; + crop_top = SubHeightC[p->chroma_format_idc]*( 2 - p->frame_mbs_only_flag ) * p->frame_crop_top_offset; + crop_bottom = SubHeightC[p->chroma_format_idc]*( 2 - p->frame_mbs_only_flag ) * p->frame_crop_bottom_offset; + } + else + { + crop_left = crop_right = crop_top = crop_bottom = 0; + } + } + + img2buf (p->imgY, buf, p->size_x, p->size_y, symbol_size_in_bytes, crop_left, crop_right, crop_top, crop_bottom); + ret = write(p_out, buf, (p->size_y-crop_bottom-crop_top)*(p->size_x-crop_right-crop_left)*symbol_size_in_bytes); + if (ret != ((p->size_y-crop_bottom-crop_top)*(p->size_x-crop_right-crop_left)*symbol_size_in_bytes)) + { + error ("write_out_picture: error writing to YUV output file.", 500); + } + + if (p->chroma_format_idc != YUV400) + { + crop_left = p->frame_crop_left_offset; + crop_right = p->frame_crop_right_offset; + crop_top = ( 2 - p->frame_mbs_only_flag ) * p->frame_crop_top_offset; + crop_bottom = ( 2 - p->frame_mbs_only_flag ) * p->frame_crop_bottom_offset; + + img2buf (p->imgUV[0], buf, p->size_x_cr, p->size_y_cr, symbol_size_in_bytes, crop_left, crop_right, crop_top, crop_bottom); + ret = write(p_out, buf, (p->size_y_cr-crop_bottom-crop_top)*(p->size_x_cr-crop_right-crop_left)* symbol_size_in_bytes); + if (ret != ((p->size_y_cr-crop_bottom-crop_top)*(p->size_x_cr-crop_right-crop_left)* symbol_size_in_bytes)) + { + error ("write_out_picture: error writing to YUV output file.", 500); + } + + if (!rgb_output) + { + img2buf (p->imgUV[1], buf, p->size_x_cr, p->size_y_cr, symbol_size_in_bytes, crop_left, crop_right, crop_top, crop_bottom); + ret = write(p_out, buf, (p->size_y_cr-crop_bottom-crop_top)*(p->size_x_cr-crop_right-crop_left)*symbol_size_in_bytes); + if (ret != ((p->size_y_cr-crop_bottom-crop_top)*(p->size_x_cr-crop_right-crop_left)*symbol_size_in_bytes)) + { + error ("write_out_picture: error writing to YUV output file.", 500); + } + } + } + + free(buf); + +// fsync(p_out); +} + +/*! + ************************************************************************ + * \brief + * Initialize output buffer for direct output + ************************************************************************ + */ +void init_out_buffer(VideoParameters *p_Vid) +{ + p_Vid->out_buffer = alloc_frame_store(); +} + +/*! + ************************************************************************ + * \brief + * Uninitialize output buffer for direct output + ************************************************************************ + */ +void uninit_out_buffer(VideoParameters *p_Vid) +{ + free_frame_store(p_Vid, p_Vid->out_buffer); + p_Vid->out_buffer=NULL; +} + +/*! + ************************************************************************ + * \brief + * Initialize picture memory with (Y:0,U:128,V:128) + ************************************************************************ + */ +void clear_picture(VideoParameters *p_Vid, StorablePicture *p) +{ + int i; + + if (p_Vid->bitdepth_luma == 8) + { + // this does not seem to be right since it seems to work only for imgpel == byte + // should be fixed + for(i=0;isize_y;i++) + memset(p->imgY[i], p_Vid->dc_pred_value_comp[0], p->size_x*sizeof(imgpel)); + } + else + { + int j; + for(i=0;i < p->size_y; i++) + for(j=0;j < p->size_x; j++) + p->imgY[i][j] = (imgpel) p_Vid->dc_pred_value_comp[0]; + } + + if (p_Vid->bitdepth_chroma == 8) + { + for(i=0;isize_y_cr;i++) + memset(p->imgUV[0][i], p_Vid->dc_pred_value_comp[1], p->size_x_cr*sizeof(imgpel)); + for(i=0;isize_y_cr;i++) + memset(p->imgUV[1][i], p_Vid->dc_pred_value_comp[2], p->size_x_cr*sizeof(imgpel)); + } + else + { + int k, j; + for (k = 0; k < 2; k++) + { + for(i=0;isize_y_cr;i++) + for(j=0;j < p->size_x_cr; j++) + p->imgUV[k][i][j] = (imgpel) p_Vid->dc_pred_value_comp[1]; + } + } +} + +/*! + ************************************************************************ + * \brief + * Write out not paired direct output fields. A second empty field is generated + * and combined into the frame buffer. + * \param p_Vid + * point to VideoParameters + * \param fs + * FrameStore that contains a single field + * \param output + * Frame format to + * \param p_out + * Output file + ************************************************************************ + */ +void write_unpaired_field(VideoParameters *p_Vid, FrameStore* fs, FrameFormat *output, int p_out) +{ + StorablePicture *p; + assert (fs->is_used<3); + if(fs->is_used &1) + { + // we have a top field + // construct an empty bottom field + p = fs->top_field; + fs->bottom_field = alloc_storable_picture(p_Vid, BOTTOM_FIELD, p->size_x, p->size_y, p->size_x_cr, p->size_y_cr); + fs->bottom_field->chroma_format_idc = p->chroma_format_idc; + fs->bottom_field->chroma_mask_mv_x = p->chroma_mask_mv_x; + fs->bottom_field->chroma_mask_mv_y = p->chroma_mask_mv_y; + fs->bottom_field->chroma_shift_x = p->chroma_shift_x; + fs->bottom_field->chroma_shift_y = p->chroma_shift_y; + + clear_picture(p_Vid, fs->bottom_field); + dpb_combine_field_yuv(p_Vid, fs); + write_picture (fs->frame, output, p_out); + } + + if(fs->is_used &2) + { + // we have a bottom field + // construct an empty top field + p = fs->bottom_field; + fs->top_field = alloc_storable_picture(p_Vid, TOP_FIELD, p->size_x, p->size_y, p->size_x_cr, p->size_y_cr); + clear_picture(p_Vid, fs->top_field); + fs->top_field->chroma_format_idc = p->chroma_format_idc; + fs->top_field->chroma_mask_mv_x = p->chroma_mask_mv_x; + fs->top_field->chroma_mask_mv_y = p->chroma_mask_mv_y; + fs->top_field->chroma_shift_x = p->chroma_shift_x; + fs->top_field->chroma_shift_y = p->chroma_shift_y; + + clear_picture(p_Vid, fs->top_field); + fs ->top_field->frame_cropping_flag = fs->bottom_field->frame_cropping_flag; + if(fs ->top_field->frame_cropping_flag) + { + fs ->top_field->frame_crop_top_offset = fs->bottom_field->frame_crop_top_offset; + fs ->top_field->frame_crop_bottom_offset = fs->bottom_field->frame_crop_bottom_offset; + fs ->top_field->frame_crop_left_offset = fs->bottom_field->frame_crop_left_offset; + fs ->top_field->frame_crop_right_offset = fs->bottom_field->frame_crop_right_offset; + } + dpb_combine_field_yuv(p_Vid, fs); + write_picture (fs->frame, output, p_out); + } + + fs->is_used=3; +} + +/*! + ************************************************************************ + * \brief + * Write out unpaired fields from output buffer. + * \param p_Vid + * VideoParameters structure + * \param output + * FrameFormat structure for output + * \param p_out + * Output file + ************************************************************************ + */ +void flush_direct_output(VideoParameters *p_Vid, FrameFormat *output, int p_out) +{ + write_unpaired_field(p_Vid, p_Vid->out_buffer, output, p_out); + + free_storable_picture(p_Vid, p_Vid->out_buffer->frame); + p_Vid->out_buffer->frame = NULL; + free_storable_picture(p_Vid, p_Vid->out_buffer->top_field); + p_Vid->out_buffer->top_field = NULL; + free_storable_picture(p_Vid, p_Vid->out_buffer->bottom_field); + p_Vid->out_buffer->bottom_field = NULL; + p_Vid->out_buffer->is_used = 0; +} + + +/*! + ************************************************************************ + * \brief + * Write a frame (from FrameStore) + * \param p_Vid + * VideoParameters structure + * \param fs + * FrameStore containing the frame + * \param output + * Frame format for output + * \param p_out + * Output file + ************************************************************************ + */ +void write_stored_frame( VideoParameters *p_Vid, FrameStore *fs, FrameFormat *output, int p_out) +{ + // make sure no direct output field is pending + flush_direct_output(p_Vid, output, p_out); + + if (fs->is_used<3) + { + write_unpaired_field(p_Vid, fs, output, p_out); + } + else + { + write_picture(fs->frame, output, p_out); + } + + fs->is_output = 1; +} + + +/*! + ************************************************************************ + * \brief + * Directly output a picture without storing it in the DPB. Fields + * are buffered before they are written to the file. + * \param p_Vid + * VideoParameters structure + * \param p + * Picture for output + * \param output + * Frame format for output + * \param p_out + * Output file + ************************************************************************ + */ +void direct_output(VideoParameters *p_Vid, StorablePicture *p, FrameFormat *output, int p_out) +{ + switch ( p->structure ) + { + case FRAME: + // we have a frame (or complementary field pair) + // so output it directly + flush_direct_output(p_Vid, output, p_out); + write_picture (p, output, p_out); + free_storable_picture(p_Vid, p); + return; + break; + case TOP_FIELD: + if (p_Vid->out_buffer->is_used &1) + flush_direct_output(p_Vid, output, p_out); + p_Vid->out_buffer->top_field = p; + p_Vid->out_buffer->is_used |= 1; + break; + case BOTTOM_FIELD: + if (p_Vid->out_buffer->is_used &2) + flush_direct_output(p_Vid, output, p_out); + p_Vid->out_buffer->bottom_field = p; + p_Vid->out_buffer->is_used |= 2; + break; + default: + printf("invalid picture type\n"); + break; + } + + if (p_Vid->out_buffer->is_used == 3) + { + // we have both fields, so output them + dpb_combine_field_yuv(p_Vid, p_Vid->out_buffer); + write_picture (p_Vid->out_buffer->frame, output, p_out); + free_storable_picture(p_Vid, p_Vid->out_buffer->frame); + p_Vid->out_buffer->frame = NULL; + free_storable_picture(p_Vid, p_Vid->out_buffer->top_field); + p_Vid->out_buffer->top_field = NULL; + free_storable_picture(p_Vid, p_Vid->out_buffer->bottom_field); + p_Vid->out_buffer->bottom_field = NULL; + p_Vid->out_buffer->is_used = 0; + } +} + + +/*! +************************************************************************ +* \brief +* For adaptive frame/field coding remove dangling top field from direct +* output frame version instead. +* \param p_Vid +* Picture for output +* \param p +* Picture for output +* \param output +* Frame format for output +* \param p_out +* Output file +************************************************************************ +*/ +void direct_output_paff(VideoParameters *p_Vid, StorablePicture *p, FrameFormat *output, int p_out) +{ + printf("Warning!!! Frame can't fit in DPB. Displayed out of sequence.\n"); + free_storable_picture(p_Vid, p_Vid->out_buffer->frame); + p_Vid->out_buffer->frame = NULL; + free_storable_picture(p_Vid, p_Vid->out_buffer->top_field); + p_Vid->out_buffer->top_field = NULL; + free_storable_picture(p_Vid, p_Vid->out_buffer->bottom_field); + p_Vid->out_buffer->bottom_field = NULL; + p_Vid->out_buffer->is_used = 0; + + direct_output(p_Vid, p, output, p_out); +} diff --git a/lencod/src/parset.c b/lencod/src/parset.c new file mode 100644 index 0000000..de9bcbe --- /dev/null +++ b/lencod/src/parset.c @@ -0,0 +1,1453 @@ +/*! + ************************************************************************************** + * \file + * parset.c + * \brief + * Picture and Sequence Parameter set generation and handling + * \date 25 November 2002 + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * - Stephan Wenger + * + ************************************************************************************** + */ + +#include +#include + +#include "global.h" + +#include "contributors.h" +#include "nal.h" +#include "mbuffer.h" +#include "parset.h" +#include "vlc.h" +#include "q_matrix.h" + +// Local helpers +static int identify_profile(InputParameters *p_Inp); +static int identify_level(InputParameters *p_Inp); +static int GenerateVUI_parameters_rbsp(seq_parameter_set_rbsp_t *sps, Bitstream *bitstream); + +static const byte ZZ_SCAN[16] = +{ + 0, 1, 4, 8, 5, 2, 3, 6, 9, 12, 13, 10, 7, 11, 14, 15 +}; + +static const byte ZZ_SCAN8[64] = +{ 0, 1, 8, 16, 9, 2, 3, 10, 17, 24, 32, 25, 18, 11, 4, 5, + 12, 19, 26, 33, 40, 48, 41, 34, 27, 20, 13, 6, 7, 14, 21, 28, + 35, 42, 49, 56, 57, 50, 43, 36, 29, 22, 15, 23, 30, 37, 44, 51, + 58, 59, 52, 45, 38, 31, 39, 46, 53, 60, 61, 54, 47, 55, 62, 63 +}; + + +/*! + ************************************************************************************* + * \brief + * generates a sequence and picture parameter set and stores these in global + * active_sps and active_pps + * + * \return + * A NALU containing the Sequence ParameterSet + * + ************************************************************************************* +*/ +void generate_parameter_sets (VideoParameters *p_Vid) +{ + InputParameters *p_Inp = p_Vid->p_Inp; + int i; + seq_parameter_set_rbsp_t *sps = NULL; + + sps = AllocSPS(); + + for (i=0; iPicParSet[i] = NULL; + } + + GenerateSequenceParameterSet(sps, p_Vid, 0); + + if (p_Inp->GenerateMultiplePPS) + { + p_Vid->PicParSet[0] = AllocPPS(); + p_Vid->PicParSet[1] = AllocPPS(); + p_Vid->PicParSet[2] = AllocPPS(); + + if (is_FREXT_profile(sps->profile_idc)) + { + GeneratePictureParameterSet( p_Vid->PicParSet[0], sps, p_Vid, p_Inp, 0, 0, 0, p_Inp->cb_qp_index_offset, p_Inp->cr_qp_index_offset); + GeneratePictureParameterSet( p_Vid->PicParSet[1], sps, p_Vid, p_Inp, 1, 1, 1, p_Inp->cb_qp_index_offset, p_Inp->cr_qp_index_offset); + GeneratePictureParameterSet( p_Vid->PicParSet[2], sps, p_Vid, p_Inp, 2, 1, 2, p_Inp->cb_qp_index_offset, p_Inp->cr_qp_index_offset); + } + else + { + GeneratePictureParameterSet( p_Vid->PicParSet[0], sps, p_Vid, p_Inp, 0, 0, 0, p_Inp->chroma_qp_index_offset, 0); + GeneratePictureParameterSet( p_Vid->PicParSet[1], sps, p_Vid, p_Inp, 1, 1, 1, p_Inp->chroma_qp_index_offset, 0); + GeneratePictureParameterSet( p_Vid->PicParSet[2], sps, p_Vid, p_Inp, 2, 1, 2, p_Inp->chroma_qp_index_offset, 0); + } + } + else + { + p_Vid->PicParSet[0] = AllocPPS(); + if (is_FREXT_profile(sps->profile_idc)) + GeneratePictureParameterSet( p_Vid->PicParSet[0], sps, p_Vid, p_Inp, 0, p_Inp->WeightedPrediction, p_Inp->WeightedBiprediction, + p_Inp->cb_qp_index_offset, p_Inp->cr_qp_index_offset); + else + GeneratePictureParameterSet( p_Vid->PicParSet[0], sps, p_Vid, p_Inp, 0, p_Inp->WeightedPrediction, p_Inp->WeightedBiprediction, + p_Inp->chroma_qp_index_offset, 0); + } + + p_Vid->active_sps = sps; + p_Vid->active_pps = p_Vid->PicParSet[0]; + // + p_Vid->sps[0] = sps; + if(p_Vid->num_of_layers == 2) + { + p_Vid->sps[1] = AllocSPS(); + *(p_Vid->sps[1]) = *sps; + } + else + p_Vid->sps[1] = NULL; +} + +/*! +************************************************************************************* +* \brief +* frees global parameter sets active_sps and active_pps +* +* \return +* A NALU containing the Sequence ParameterSet +* +************************************************************************************* +*/ +void FreeParameterSets (VideoParameters *p_Vid) +{ + int i; + for (i=0; iPicParSet[i]) + { + FreePPS(p_Vid->PicParSet[i]); + p_Vid->PicParSet[i] = NULL; + } + } + for(i=0; inum_of_layers; i++) + if(p_Vid->sps[i]) + { + FreeSPS(p_Vid->sps[i]); + p_Vid->sps[i] = NULL; + } + p_Vid->active_sps = NULL; +} + +/*! +************************************************************************************* +* \brief +* int GenerateSeq_parameter_set_NALU (VideoParameters *p_Vid); +* +* \note +* Uses the global variables through GenerateSequenceParameterSet() +* and GeneratePictureParameterSet +* \param p_Vid +* VideoParameters structure +* \return +* A NALU containing the Sequence ParameterSet +* +************************************************************************************* +*/ + +NALU_t *GenerateSeq_parameter_set_NALU (VideoParameters *p_Vid) +{ + NALU_t *n = AllocNALU(MAXNALUSIZE); + int RBSPlen = 0; + byte rbsp[MAXRBSPSIZE]; + +#if (MVC_EXTENSION_ENABLE) + RBSPlen = GenerateSeq_parameter_set_rbsp (p_Vid, p_Vid->sps[0]/*p_Vid->active_sps*/, rbsp, 0); +#else + RBSPlen = GenerateSeq_parameter_set_rbsp (p_Vid, p_Vid->active_sps, rbsp); +#endif + RBSPtoNALU (rbsp, n, RBSPlen, NALU_TYPE_SPS, NALU_PRIORITY_HIGHEST, 1); + n->startcodeprefix_len = 4; + + return n; +} + +#if (MVC_EXTENSION_ENABLE) +/*! +************************************************************************************* +* \brief +* int GenerateSubsetSeq_parameter_set_NALU (VideoParameters *p_Vid); +* +* \note +* Uses the global variables through GenerateSequenceParameterSet() +* and GeneratePictureParameterSet +* \param p_Vid +* VideoParameters structure +* \return +* A NALU containing the Sequence ParameterSet +* +************************************************************************************* +*/ + +NALU_t *GenerateSubsetSeq_parameter_set_NALU (VideoParameters *p_Vid) +{ + NALU_t *n = AllocNALU(MAXNALUSIZE); + int RBSPlen = 0; + byte rbsp[MAXRBSPSIZE]; + + RBSPlen = GenerateSeq_parameter_set_rbsp (p_Vid, p_Vid->sps[1]/*p_Vid->active_sps*/, rbsp, 1); + RBSPtoNALU (rbsp, n, RBSPlen, NALU_TYPE_SUB_SPS, NALU_PRIORITY_HIGHEST, 1); + n->startcodeprefix_len = 4; + + return n; +} +#endif + +/*! +************************************************************************************* +* \brief +* NALU_t *GeneratePic_parameter_set_NALU (VideoParameters *p_Vid, int PPS_id); +* +* \note +* Uses the global variables through GenerateSequenceParameterSet() +* and GeneratePictureParameterSet +* +* \return +* A NALU containing the Picture Parameter Set +* +************************************************************************************* +*/ + +NALU_t *GeneratePic_parameter_set_NALU(VideoParameters *p_Vid, int PPS_id) +{ + NALU_t *n = AllocNALU(MAXNALUSIZE); + int RBSPlen = 0; + byte rbsp[MAXRBSPSIZE]; + + RBSPlen = GeneratePic_parameter_set_rbsp (p_Vid, p_Vid->PicParSet[PPS_id], rbsp); + RBSPtoNALU (rbsp, n, RBSPlen, NALU_TYPE_PPS, NALU_PRIORITY_HIGHEST, 1); + n->startcodeprefix_len = 4; + + return n; +} + + +/*! + ************************************************************************ + * \brief + * GenerateSequenceParameterSet: extracts info from global variables and + * generates sequence parameter set structure + * \param sps + * Sequence Parameter Set to be filled + * \param p_Vid + * VideoParameters for encoding + * \param SPS_id + * SPS ID + * + * \par + * Function reads all kinds of values from several global variables, + * including p_Inp-> and image-> and fills in the sps. Many + * values are current hard-coded to defaults. + * + ************************************************************************ + */ + +void GenerateSequenceParameterSet( seq_parameter_set_rbsp_t *sps, //!< Sequence Parameter Set to be filled + VideoParameters *p_Vid, //!< VideoParameters for encoding + int SPS_id //!< SPS ID + ) +{ + static const int SubWidthC [4]= { 1, 2, 2, 1}; + static const int SubHeightC [4]= { 1, 2, 1, 1}; + unsigned i; + unsigned n_ScalingList; + InputParameters *p_Inp = p_Vid->p_Inp; + + int frext_profile = ((identify_profile(p_Inp)==FREXT_HP) || + (identify_profile(p_Inp)==FREXT_Hi10P) || + (identify_profile(p_Inp)==FREXT_Hi422) || + (identify_profile(p_Inp)==FREXT_Hi444) || + (identify_profile(p_Inp)==FREXT_CAVLC444) +#if (MVC_EXTENSION_ENABLE) + || (identify_profile(p_Inp)==MULTIVIEW_HIGH) + || (identify_profile(p_Inp)==STEREO_HIGH) +#endif + ); + // ************************************************************************* + // Sequence Parameter Set + // ************************************************************************* + assert (sps != NULL); + // Profile and Level should be calculated using the info from the config + // file. Calculation is hidden in identify_profile() and identify_level() + sps->profile_idc = identify_profile(p_Inp); + sps->level_idc = identify_level(p_Inp); + + // needs to be set according to profile + sps->constrained_set0_flag = FALSE; + sps->constrained_set1_flag = FALSE; + sps->constrained_set2_flag = FALSE; + + if ( (sps->level_idc == 9) && !is_FREXT_profile(sps->profile_idc) ) // Level 1.b + { + sps->constrained_set3_flag = TRUE; + sps->level_idc = 11; + } + else if (frext_profile && p_Inp->IntraProfile) + { + sps->constrained_set3_flag = TRUE; + } + else + { + sps->constrained_set3_flag = FALSE; + } + + // Parameter Set ID hard coded to zero + sps->seq_parameter_set_id = SPS_id; + + // Fidelity Range Extensions stuff + sps->bit_depth_luma_minus8 = p_Inp->output.bit_depth[0] - 8; + sps->bit_depth_chroma_minus8 = p_Inp->output.bit_depth[1] - 8; + sps->lossless_qpprime_flag = p_Inp->LosslessCoding & + (sps->profile_idc==FREXT_Hi444 || sps->profile_idc==FREXT_CAVLC444); + + //! POC stuff: + //! The following values are hard-coded in init_poc(). Apparently, + //! the poc implementation covers only a subset of the poc functionality. + //! Here, the same subset is implemented. Changes in the POC stuff have + //! also to be reflected here + sps->log2_max_frame_num_minus4 = p_Vid->log2_max_frame_num_minus4; + sps->log2_max_pic_order_cnt_lsb_minus4 = p_Vid->log2_max_pic_order_cnt_lsb_minus4; + + sps->pic_order_cnt_type = p_Inp->pic_order_cnt_type; + sps->num_ref_frames_in_pic_order_cnt_cycle = p_Vid->num_ref_frames_in_pic_order_cnt_cycle; + sps->delta_pic_order_always_zero_flag = p_Vid->delta_pic_order_always_zero_flag; + sps->offset_for_non_ref_pic = p_Vid->offset_for_non_ref_pic; + sps->offset_for_top_to_bottom_field = p_Vid->offset_for_top_to_bottom_field; + + for (i=0; inum_ref_frames_in_pic_order_cnt_cycle; i++) + { + sps->offset_for_ref_frame[i] = p_Vid->offset_for_ref_frame[i]; + } + // End of POC stuff + + // Number of Reference Frames + sps->num_ref_frames = (unsigned char) p_Inp->num_ref_frames; + + //required_frame_num_update_behaviour_flag hardcoded to zero + sps->gaps_in_frame_num_value_allowed_flag = FALSE; // double check + + sps->frame_mbs_only_flag = (Boolean) !(p_Inp->PicInterlace || p_Inp->MbInterlace); + + // Picture size, finally a simple one :-) + sps->pic_width_in_mbs_minus1 = (( p_Inp->output.width[0] + p_Vid->auto_crop_right) >> 4) -1; + sps->pic_height_in_map_units_minus1 = (((p_Inp->output.height[0] + p_Vid->auto_crop_bottom) >> 4)/ (2 - sps->frame_mbs_only_flag)) - 1; + + // a couple of flags, simple + sps->mb_adaptive_frame_field_flag = (Boolean) (FRAME_CODING != p_Inp->MbInterlace); + sps->direct_8x8_inference_flag = (Boolean) p_Inp->directInferenceFlag; + + // Sequence VUI not implemented, signalled as not present + sps->vui_parameters_present_flag = (Boolean) ((p_Inp->output.color_model != CM_YUV && p_Inp->output.yuv_format == YUV444) || p_Inp->EnableVUISupport); + sps->chroma_format_idc = p_Inp->output.yuv_format; + sps->separate_colour_plane_flag = ( sps->chroma_format_idc == YUV444 ) ? p_Inp->separate_colour_plane_flag : 0; + + if ( sps->vui_parameters_present_flag ) + GenerateVUIParameters(sps, p_Inp); + + // Fidelity Range Extensions stuff + if(frext_profile) + { + sps->seq_scaling_matrix_present_flag = (Boolean) (p_Inp->ScalingMatrixPresentFlag&1); + n_ScalingList = (sps->chroma_format_idc != YUV444) ? 8 : 12; + for(i=0; iseq_scaling_list_present_flag[i] = (p_Inp->ScalingListPresentFlag[i]&1); + else + { + if(p_Inp->Transform8x8Mode) + sps->seq_scaling_list_present_flag[i] = (p_Inp->ScalingListPresentFlag[i]&1); + else + sps->seq_scaling_list_present_flag[i] = 0; + } + if( sps->seq_scaling_matrix_present_flag == FALSE ) + sps->seq_scaling_list_present_flag[i] = 0; + } + } + else + { + sps->seq_scaling_matrix_present_flag = FALSE; + for(i=0; i<12; i++) + sps->seq_scaling_list_present_flag[i] = 0; + + } + + + if (p_Vid->auto_crop_right || p_Vid->auto_crop_bottom) + { + sps->frame_cropping_flag = TRUE; + sps->frame_crop_left_offset=0; + sps->frame_crop_top_offset=0; + sps->frame_crop_right_offset= (p_Vid->auto_crop_right / SubWidthC[sps->chroma_format_idc]); + sps->frame_crop_bottom_offset= (p_Vid->auto_crop_bottom / (SubHeightC[sps->chroma_format_idc] * (2 - sps->frame_mbs_only_flag))); + if (p_Vid->auto_crop_right % SubWidthC[sps->chroma_format_idc]) + { + error("automatic frame cropping (width) not possible",500); + } + if (p_Vid->auto_crop_bottom % (SubHeightC[sps->chroma_format_idc] * (2 - sps->frame_mbs_only_flag))) + { + error("automatic frame cropping (height) not possible",500); + } + } + else + { + sps->frame_cropping_flag = FALSE; + } + +} + +/*! + ************************************************************************ + * \brief + * GeneratePictureParameterSet: + * Generates a Picture Parameter Set structure + * \par + * Regarding the QP + * The previous software versions coded the absolute QP only in the + * slice header. This is kept, and the offset in the PPS is coded + * even if we could save bits by intelligently using this field. + * + ************************************************************************ + */ +void GeneratePictureParameterSet( pic_parameter_set_rbsp_t *pps, //!< Picture Parameter Set to be filled + seq_parameter_set_rbsp_t *sps, //!< used Sequence Parameter Set + VideoParameters *p_Vid, //!< the image pointer + InputParameters *p_Inp, //!< Input configuration parameters + int PPS_id, //!< PPS ID + int WeightedPrediction, //!< value of weighted_pred_flag + int WeightedBiprediction, //!< value of weighted_bipred_idc + int cb_qp_index_offset, //!< value of cb_qp_index_offset + int cr_qp_index_offset //!< value of cr_qp_index_offset + ) + +{ + unsigned i; + unsigned n_ScalingList; + unsigned FrameSizeInMapUnits = 0; + + int frext_profile = ((identify_profile(p_Inp)==FREXT_HP) || + (identify_profile(p_Inp)==FREXT_Hi10P) || + (identify_profile(p_Inp)==FREXT_Hi422) || + (identify_profile(p_Inp)==FREXT_Hi444) || + (identify_profile(p_Inp)==FREXT_CAVLC444) +#if (MVC_EXTENSION_ENABLE) + || (identify_profile(p_Inp)==MULTIVIEW_HIGH) + || (identify_profile(p_Inp)==STEREO_HIGH) +#endif + ); + + // ************************************************************************* + // Picture Parameter Set + // ************************************************************************* + + pps->seq_parameter_set_id = sps->seq_parameter_set_id; + pps->pic_parameter_set_id = PPS_id; + pps->entropy_coding_mode_flag = (p_Inp->symbol_mode == CAVLC ? FALSE : TRUE); + // Fidelity Range Extensions stuff + if(frext_profile) + { + pps->transform_8x8_mode_flag = (p_Inp->Transform8x8Mode ? TRUE:FALSE); + pps->pic_scaling_matrix_present_flag = (Boolean) ((p_Inp->ScalingMatrixPresentFlag&2)>>1); + n_ScalingList = (sps->chroma_format_idc != YUV444) ? 8 : 12; + for(i=0; ipic_scaling_list_present_flag[i] = (p_Inp->ScalingListPresentFlag[i]&2)>>1; + else + { + if(pps->transform_8x8_mode_flag) + pps->pic_scaling_list_present_flag[i] = (p_Inp->ScalingListPresentFlag[i]&2)>>1; + else + pps->pic_scaling_list_present_flag[i] = 0; + } + if( pps->pic_scaling_matrix_present_flag == FALSE ) + pps->pic_scaling_list_present_flag[i] = 0; + } + } + else + { + pps->pic_scaling_matrix_present_flag = FALSE; + for(i=0; i<12; i++) + pps->pic_scaling_list_present_flag[i] = 0; + + pps->transform_8x8_mode_flag = FALSE; + p_Inp->Transform8x8Mode = 0; + } + + // JVT-Fxxx (by Stephan Wenger, make this flag unconditional + pps->bottom_field_pic_order_in_frame_present_flag = p_Vid->bottom_field_pic_order_in_frame_present_flag; + + // Begin FMO stuff + pps->num_slice_groups_minus1 = p_Inp->num_slice_groups_minus1; + + //! Following set the parameter for different slice group types + if (pps->num_slice_groups_minus1 > 0) + { + if ((pps->slice_group_id = calloc ((sps->pic_height_in_map_units_minus1+1)*(sps->pic_width_in_mbs_minus1+1), sizeof(byte))) == NULL) + no_mem_exit ("GeneratePictureParameterSet: slice_group_id"); + + switch (p_Inp->slice_group_map_type) + { + case 0: + pps->slice_group_map_type = 0; + for(i=0; i<=pps->num_slice_groups_minus1; i++) + { + pps->run_length_minus1[i]=p_Inp->run_length_minus1[i]; + } + break; + case 1: + pps->slice_group_map_type = 1; + break; + case 2: + // i loops from 0 to num_slice_groups_minus1-1, because no info for background needed + pps->slice_group_map_type = 2; + FrameSizeInMapUnits = (sps->pic_height_in_map_units_minus1) * (sps->pic_width_in_mbs_minus1 + 1); + for(i=0; inum_slice_groups_minus1; i++) + { + // make sure the macroblock map does not lie outside the picture + pps->top_left[i] = imin (p_Inp->top_left[i], FrameSizeInMapUnits); + pps->bottom_right[i] = imin (p_Inp->bottom_right[i], FrameSizeInMapUnits); + } + break; + case 3: + case 4: + case 5: + pps->slice_group_map_type = p_Inp->slice_group_map_type; + pps->slice_group_change_direction_flag = (Boolean) p_Inp->slice_group_change_direction_flag; + pps->slice_group_change_rate_minus1 = p_Inp->slice_group_change_rate_minus1; + break; + case 6: + pps->slice_group_map_type = 6; + pps->pic_size_in_map_units_minus1 = + (((p_Inp->output.height[0] + p_Vid->auto_crop_bottom)/MB_BLOCK_SIZE)/(2-sps->frame_mbs_only_flag)) + *((p_Inp->output.width[0] + p_Vid->auto_crop_right)/MB_BLOCK_SIZE) -1; + + for (i=0;i<=pps->pic_size_in_map_units_minus1; i++) + pps->slice_group_id[i] = p_Inp->slice_group_id[i]; + + break; + default: + printf ("Parset.c: slice_group_map_type invalid, default\n"); + assert (0==1); + } + } +// End FMO stuff + + pps->num_ref_idx_l0_default_active_minus1 = sps->frame_mbs_only_flag ? (sps->num_ref_frames - 1) : (2 * sps->num_ref_frames - 1) ; // set defaults + pps->num_ref_idx_l1_default_active_minus1 = sps->frame_mbs_only_flag ? (sps->num_ref_frames - 1) : (2 * sps->num_ref_frames - 1) ; // set defaults + + pps->weighted_pred_flag = (byte) WeightedPrediction; + pps->weighted_bipred_idc = (byte) WeightedBiprediction; + + pps->pic_init_qp_minus26 = 0; // hard coded to zero, QP lives in the slice header + pps->pic_init_qs_minus26 = 0; + + pps->chroma_qp_index_offset = cb_qp_index_offset; + if (frext_profile) + { + pps->cb_qp_index_offset = cb_qp_index_offset; + pps->cr_qp_index_offset = cr_qp_index_offset; + } + else + pps->cb_qp_index_offset = pps->cr_qp_index_offset = pps->chroma_qp_index_offset; + + pps->deblocking_filter_control_present_flag = (Boolean) ((p_Inp->DFSendParameters != 0) || (p_Inp->RDPictureDeblocking != 0)); + + pps->constrained_intra_pred_flag = (Boolean) p_Inp->UseConstrainedIntraPred; + + // if redundant slice is in use. + pps->redundant_pic_cnt_present_flag = (Boolean) p_Inp->redundant_pic_flag; +} + +/*! + ************************************************************************************* + * \brief + * syntax for scaling list matrix values + * + * \param scalingListinput + * input scaling list + * \param scalingList + * scaling list to be used + * \param sizeOfScalingList + * size of the scaling list + * \param UseDefaultScalingMatrix + * usage of default Scaling Matrix + * \param bitstream + * target bitstream for writing syntax + * + * \return + * size of the RBSP in bytes + * + ************************************************************************************* + */ +int Scaling_List(short *scalingListinput, short *scalingList, int sizeOfScalingList, short *UseDefaultScalingMatrix, Bitstream *bitstream) +{ + int j, scanj; + int len=0; + int delta_scale, lastScale, nextScale; + + lastScale = 8; + nextScale = 8; + + for(j=0; j127) + delta_scale=delta_scale-256; + else if(delta_scale<-128) + delta_scale=delta_scale+256; + + len+=write_se_v (" : delta_sl ", delta_scale, bitstream); + nextScale = scalingListinput[scanj]; + *UseDefaultScalingMatrix|=(scanj==0 && nextScale==0); // Check first matrix value for zero + } + + scalingList[scanj] = (short) ((nextScale==0) ? lastScale:nextScale); // Update the actual scalingList matrix with the correct values + lastScale = scalingList[scanj]; + } + + return len; +} + + +/*! + ************************************************************************************* + * \brief + * int GenerateSeq_parameter_set_rbsp (VideoParameters *p_Vid, seq_parameter_set_rbsp_t *sps, char *rbsp); + * + * \param p_Vid + * Image parameters structure + * \param sps + * sequence parameter structure + * \param rbsp + * buffer to be filled with the rbsp, size should be at least MAXIMUMPARSETRBSPSIZE + * \return + * size of the RBSP in bytes + * + * \note + * Sequence Parameter VUI function is called, but the function implements + * an exit (-1) + ************************************************************************************* + */ +#if (MVC_EXTENSION_ENABLE) +int GenerateSeq_parameter_set_rbsp (VideoParameters *p_Vid, seq_parameter_set_rbsp_t *sps, byte *rbsp, short Is_Subset) +#else +int GenerateSeq_parameter_set_rbsp (VideoParameters *p_Vid, seq_parameter_set_rbsp_t *sps, byte *rbsp) +#endif +{ + Bitstream *bitstream; + int len = 0, LenInBytes; + unsigned i; + unsigned n_ScalingList; + + assert (rbsp != NULL); + + if ((bitstream=calloc(1, sizeof(Bitstream)))==NULL) no_mem_exit("SeqParameterSet:bitstream"); + + // .. and use the rbsp provided (or allocated above) for the data + bitstream->streamBuffer = rbsp; + bitstream->bits_to_go = 8; + +#if (MVC_EXTENSION_ENABLE) + if(p_Vid->p_Inp->num_of_views==2) + { + if(Is_Subset==1) + { + assert(sps->profile_idc==MULTIVIEW_HIGH || sps->profile_idc==STEREO_HIGH); + len+=write_u_v (8, "SPS: profile_idc", sps->profile_idc, bitstream); + } + else + len+=write_u_v (8, "SPS: profile_idc", 100, bitstream); + } + else +#endif + len+=write_u_v (8, "SPS: profile_idc", sps->profile_idc, bitstream); + + len+=write_u_1 ("SPS: constrained_set0_flag", sps->constrained_set0_flag, bitstream); + len+=write_u_1 ("SPS: constrained_set1_flag", sps->constrained_set1_flag, bitstream); + len+=write_u_1 ("SPS: constrained_set2_flag", sps->constrained_set2_flag, bitstream); + len+=write_u_1 ("SPS: constrained_set3_flag", sps->constrained_set3_flag, bitstream); + len+=write_u_v (4, "SPS: reserved_zero_4bits", 0, bitstream); + + len+=write_u_v (8, "SPS: level_idc", sps->level_idc, bitstream); + + len+=write_ue_v ("SPS: seq_parameter_set_id", sps->seq_parameter_set_id, bitstream); + + // Fidelity Range Extensions stuff + if( is_FREXT_profile(sps->profile_idc) ) + { + len+=write_ue_v ("SPS: chroma_format_idc", sps->chroma_format_idc, bitstream); + if(sps->chroma_format_idc == YUV444) //if(p_Vid->yuv_format == YUV444) + len+=write_u_1 ("SPS: separate_colour_plane_flag", sps->separate_colour_plane_flag, bitstream); + len+=write_ue_v ("SPS: bit_depth_luma_minus8", sps->bit_depth_luma_minus8, bitstream); + len+=write_ue_v ("SPS: bit_depth_chroma_minus8", sps->bit_depth_chroma_minus8, bitstream); + + len+=write_u_1 ("SPS: lossless_qpprime_y_zero_flag", sps->lossless_qpprime_flag, bitstream); + //other chroma info to be added in the future + + len+=write_u_1 ("SPS: seq_scaling_matrix_present_flag", sps->seq_scaling_matrix_present_flag, bitstream); + + if(sps->seq_scaling_matrix_present_flag) + { + ScaleParameters *p_QScale = p_Vid->p_QScale; + n_ScalingList = (sps->chroma_format_idc != YUV444) ? 8 : 12; + for(i=0; iseq_scaling_list_present_flag[i], bitstream); + if(sps->seq_scaling_list_present_flag[i]) + { + if(i<6) + len+=Scaling_List(p_QScale->ScalingList4x4input[i], p_QScale->ScalingList4x4[i], 16, &p_QScale->UseDefaultScalingMatrix4x4Flag[i], bitstream); + else + len+=Scaling_List(p_QScale->ScalingList8x8input[i-6], p_QScale->ScalingList8x8[i-6], 64, &p_QScale->UseDefaultScalingMatrix8x8Flag[i-6], bitstream); + } + } + } + } + + len+=write_ue_v ("SPS: log2_max_frame_num_minus4", sps->log2_max_frame_num_minus4, bitstream); + len+=write_ue_v ("SPS: pic_order_cnt_type", sps->pic_order_cnt_type, bitstream); + + if (sps->pic_order_cnt_type == 0) + len+=write_ue_v ("SPS: log2_max_pic_order_cnt_lsb_minus4", sps->log2_max_pic_order_cnt_lsb_minus4, bitstream); + else if (sps->pic_order_cnt_type == 1) + { + len+=write_u_1 ("SPS: delta_pic_order_always_zero_flag", sps->delta_pic_order_always_zero_flag, bitstream); + len+=write_se_v ("SPS: offset_for_non_ref_pic", sps->offset_for_non_ref_pic, bitstream); + len+=write_se_v ("SPS: offset_for_top_to_bottom_field", sps->offset_for_top_to_bottom_field, bitstream); + len+=write_ue_v ("SPS: num_ref_frames_in_pic_order_cnt_cycle", sps->num_ref_frames_in_pic_order_cnt_cycle, bitstream); + for (i=0; inum_ref_frames_in_pic_order_cnt_cycle; i++) + len+=write_se_v ("SPS: offset_for_ref_frame", sps->offset_for_ref_frame[i], bitstream); + } + len+=write_ue_v ("SPS: num_ref_frames", sps->num_ref_frames, bitstream); + len+=write_u_1 ("SPS: gaps_in_frame_num_value_allowed_flag", sps->gaps_in_frame_num_value_allowed_flag, bitstream); + len+=write_ue_v ("SPS: pic_width_in_mbs_minus1", sps->pic_width_in_mbs_minus1, bitstream); + len+=write_ue_v ("SPS: pic_height_in_map_units_minus1", sps->pic_height_in_map_units_minus1, bitstream); + len+=write_u_1 ("SPS: frame_mbs_only_flag", sps->frame_mbs_only_flag, bitstream); + if (!sps->frame_mbs_only_flag) + { + len+=write_u_1 ("SPS: mb_adaptive_frame_field_flag", sps->mb_adaptive_frame_field_flag, bitstream); + } + len+=write_u_1 ("SPS: direct_8x8_inference_flag", sps->direct_8x8_inference_flag, bitstream); + + len+=write_u_1 ("SPS: frame_cropping_flag", sps->frame_cropping_flag, bitstream); + if (sps->frame_cropping_flag) + { + len+=write_ue_v ("SPS: frame_crop_left_offset", sps->frame_crop_left_offset, bitstream); + len+=write_ue_v ("SPS: frame_crop_right_offset", sps->frame_crop_right_offset, bitstream); + len+=write_ue_v ("SPS: frame_crop_top_offset", sps->frame_crop_top_offset, bitstream); + len+=write_ue_v ("SPS: frame_crop_bottom_offset", sps->frame_crop_bottom_offset, bitstream); + } + + len+=write_u_1 ("SPS: vui_parameters_present_flag", sps->vui_parameters_present_flag, bitstream); + + if (sps->vui_parameters_present_flag) + len+=GenerateVUI_parameters_rbsp(sps, bitstream); // currently a dummy, asserting + +#if (MVC_EXTENSION_ENABLE) + if(Is_Subset==1) + { + // profile_idc fixed to 118 only + len+=write_u_1 ("SPS: bit_equal_to_one", 1, bitstream); + + // fixed to 2 views only + { + len+=write_ue_v ("SPS: num_views_minus1", 1, bitstream); + if ( p_Vid->p_Inp->MVCFlipViews ) + { + len+=write_ue_v ("SPS: view_id[ 0 ]", 1, bitstream); + len+=write_ue_v ("SPS: view_id[ 1 ]", 0, bitstream); + + len+=write_ue_v ("SPS: num_anchor_refs_l0[ 1 ]", 1, bitstream); + len+=write_ue_v ("SPS: anchor_ref_l0[ 1 ][ 0 ]", 1, bitstream); + len+=write_ue_v ("SPS: num_anchor_refs_l1[ 1 ]", 1, bitstream); + len+=write_ue_v ("SPS: anchor_ref_l1[ 1 ][ 0 ]", 1, bitstream); + + len+=write_ue_v ("SPS: num_non_anchor_refs_l0[ 1 ]", 1, bitstream); + len+=write_ue_v ("SPS: non_anchor_ref_l0[ 1 ][ 0 ]", 1, bitstream); + len+=write_ue_v ("SPS: num_non_anchor_refs_l1[ 1 ]", 1, bitstream); + len+=write_ue_v ("SPS: non_anchor_ref_l1[ 1 ][ 0 ]", 1, bitstream); + } + else + { + len+=write_ue_v ("SPS: view_id[ 0 ]", 0, bitstream); + len+=write_ue_v ("SPS: view_id[ 1 ]", 1, bitstream); + + len+=write_ue_v ("SPS: num_anchor_refs_l0[ 1 ]", 1, bitstream); + len+=write_ue_v ("SPS: anchor_ref_l0[ 1 ][ 0 ]", 0, bitstream); + len+=write_ue_v ("SPS: num_anchor_refs_l1[ 1 ]", 1, bitstream); + len+=write_ue_v ("SPS: anchor_ref_l1[ 1 ][ 0 ]", 0, bitstream); + + len+=write_ue_v ("SPS: num_non_anchor_refs_l0[ 1 ]", 1, bitstream); + len+=write_ue_v ("SPS: non_anchor_ref_l0[ 1 ][ 0 ]", 0, bitstream); + len+=write_ue_v ("SPS: num_non_anchor_refs_l1[ 1 ]", 1, bitstream); + len+=write_ue_v ("SPS: non_anchor_ref_l1[ 1 ][ 0 ]", 0, bitstream); + } + + len+=write_ue_v ("SPS: num_level_values_signalled_minus1", 0, bitstream); + len+=write_u_v (8, "SPS: level_idc[ 0 ]", sps->level_idc, bitstream); + len+=write_ue_v ("SPS: num_applicable_ops_minus1[ 0 ]", 0, bitstream); + len+=write_u_v (3, "SPS: applicable_op_temporal_id[ 0 ][ 0 ]", 0, bitstream); + len+=write_ue_v ("SPS: applicable_op_num_target_views_minus1[ 0 ][ 0 ]", 0, bitstream); + len+=write_ue_v ("SPS: applicable_op_target_view_id[ 0 ][ 0 ][ 0 ]", 0, bitstream); + len+=write_ue_v ("SPS: applicable_op_num_views_minus1[ 0 ][ 0 ]", 0, bitstream); + } + + len+=write_u_1 ("SPS: mvc_vui_parameters_present_flag", sps->vui_parameters_present_flag, bitstream); + { + if(sps->vui_parameters_present_flag) + { + len+=write_ue_v ( "SPS: vui_mvc_num_ops_minus1", 0, bitstream); + len+=write_u_v ( 3, "SPS: vui_mvc_temporal_id[ 0 ]", 0, bitstream); + len+=write_ue_v ( "SPS: vui_mvc_num_target_output_views_minus1[ 0 ]", 0, bitstream); + len+=write_ue_v ( "SPS: vui_mvc_view_id[ 0 ][ 0 ]", 0, bitstream); + len+=write_u_1 ( "SPS: vui_mvc_timing_info_present_flag[ 0 ]", sps->vui_parameters_present_flag, bitstream); + len+=write_u_v (32, "SPS: vui_mvc_num_units_in_tick[ 0 ]", sps->vui_seq_parameters.num_units_in_tick, bitstream); + len+=write_u_v (32, "SPS: vui_mvc_time_scale[ 0 ]", sps->vui_seq_parameters.time_scale, bitstream); + len+=write_u_1 ( "SPS: vui_mvc_fixed_frame_rate_flag[ 0 ]", sps->vui_seq_parameters.fixed_frame_rate_flag, bitstream); + len+=write_u_1 ( "SPS: vui_mvc_nal_hrd_parameters_present_flag[ 0 ]", sps->vui_seq_parameters.nal_hrd_parameters_present_flag, bitstream); + if(sps->vui_seq_parameters.nal_hrd_parameters_present_flag) + { + len += WriteHRDParameters(sps, bitstream); + } + len+=write_u_1 ("SPS: vui_mvc_vcl_hrd_parameters_present_flag[ 0 ]", sps->vui_seq_parameters.vcl_hrd_parameters_present_flag, bitstream); + if ( sps->vui_seq_parameters.vcl_hrd_parameters_present_flag ) + { + len += WriteHRDParameters(sps, bitstream); + } + if ( sps->vui_seq_parameters.nal_hrd_parameters_present_flag || sps->vui_seq_parameters.vcl_hrd_parameters_present_flag ) + { + len+=write_u_1 ("SPS: vui_mvc_low_delay_hrd_flag[ 0 ]", sps->vui_seq_parameters.low_delay_hrd_flag, bitstream ); + } + len+=write_u_1 ("SPS: vui_mvc_pic_struct_present_flag[ 0 ]", sps->vui_seq_parameters.pic_struct_present_flag, bitstream); + } + } + + len+=write_u_1 ("SPS: additional_extension2_flag", 0, bitstream); + } +#endif + + SODBtoRBSP(bitstream); // copies the last couple of bits into the byte buffer + + LenInBytes=bitstream->byte_pos; + + free (bitstream); + + return LenInBytes; +} + + +/*! + *********************************************************************************************** + * \brief + * int GeneratePic_parameter_set_rbsp (VideoParameters *p_Vid, pic_parameter_set_rbsp_t *pps, char *rbsp); + * + * \param p_Vid + * picture parameter structure + * \param pps + * picture parameter structure + * \param rbsp + * buffer to be filled with the rbsp, size should be at least MAXIMUMPARSETRBSPSIZE + * + * \return + * size of the RBSP in bytes, negative in case of an error + * + * \note + * Picture Parameter VUI function is called, but the function implements + * an exit (-1) + ************************************************************************************************ + */ + +int GeneratePic_parameter_set_rbsp (VideoParameters *p_Vid, pic_parameter_set_rbsp_t *pps, byte *rbsp) +{ + InputParameters *p_Inp = p_Vid->p_Inp; + Bitstream *bitstream; + int len = 0, LenInBytes; + unsigned i; + int profile_idc; + + assert (rbsp != NULL); + + if ((bitstream=calloc(1, sizeof(Bitstream)))==NULL) no_mem_exit("PicParameterSet:bitstream"); + + // .. and use the rbsp provided (or allocated above) for the data + bitstream->streamBuffer = rbsp; + bitstream->bits_to_go = 8; + + pps->bottom_field_pic_order_in_frame_present_flag = p_Vid->bottom_field_pic_order_in_frame_present_flag; + + len += write_ue_v ("PPS: pic_parameter_set_id", pps->pic_parameter_set_id, bitstream); + len += write_ue_v ("PPS: seq_parameter_set_id", pps->seq_parameter_set_id, bitstream); + len += write_u_1 ("PPS: entropy_coding_mode_flag", pps->entropy_coding_mode_flag, bitstream); + len += write_u_1 ("PPS: bottom_field_pic_order_in_frame_present_flag", pps->bottom_field_pic_order_in_frame_present_flag, bitstream); + len += write_ue_v ("PPS: num_slice_groups_minus1", pps->num_slice_groups_minus1, bitstream); + + // FMO stuff + if(pps->num_slice_groups_minus1 > 0 ) + { + len += write_ue_v ("PPS: slice_group_map_type", pps->slice_group_map_type, bitstream); + if (pps->slice_group_map_type == 0) + for (i=0; i<=pps->num_slice_groups_minus1; i++) + len += write_ue_v ("PPS: run_length_minus1[i]", pps->run_length_minus1[i], bitstream); + else if (pps->slice_group_map_type==2) + { + for (i=0; inum_slice_groups_minus1; i++) + { + len += write_ue_v ("PPS: top_left[i]", pps->top_left[i], bitstream); + len += write_ue_v ("PPS: bottom_right[i]", pps->bottom_right[i], bitstream); + } + } + else if (pps->slice_group_map_type == 3 || + pps->slice_group_map_type == 4 || + pps->slice_group_map_type == 5) + { + len += write_u_1 ("PPS: slice_group_change_direction_flag", pps->slice_group_change_direction_flag, bitstream); + len += write_ue_v ("PPS: slice_group_change_rate_minus1", pps->slice_group_change_rate_minus1, bitstream); + } + else if (pps->slice_group_map_type == 6) + { + unsigned NumberBitsPerSliceGroupId = 0; + + if (pps->num_slice_groups_minus1>=4) + NumberBitsPerSliceGroupId=3; + else if (pps->num_slice_groups_minus1>=2) + NumberBitsPerSliceGroupId=2; + else if (pps->num_slice_groups_minus1>=1) + NumberBitsPerSliceGroupId=1; + + len += write_ue_v ("PPS: pic_size_in_map_units_minus1", pps->pic_size_in_map_units_minus1, bitstream); + for(i = 0; i <= pps->pic_size_in_map_units_minus1; i++) + len += write_u_v (NumberBitsPerSliceGroupId, "PPS: >slice_group_id[i]", pps->slice_group_id[i], bitstream); + } + } + // End of FMO stuff + + len += write_ue_v ("PPS: num_ref_idx_l0_default_active_minus1", pps->num_ref_idx_l0_default_active_minus1, bitstream); + len += write_ue_v ("PPS: num_ref_idx_l1_default_active_minus1", pps->num_ref_idx_l1_default_active_minus1, bitstream); + len += write_u_1 ("PPS: weighted_pred_flag", pps->weighted_pred_flag, bitstream); + len += write_u_v (2, "PPS: weighted_bipred_idc", pps->weighted_bipred_idc, bitstream); + len += write_se_v ("PPS: pic_init_qp_minus26", pps->pic_init_qp_minus26, bitstream); + len += write_se_v ("PPS: pic_init_qs_minus26", pps->pic_init_qs_minus26, bitstream); + + profile_idc = identify_profile(p_Inp); + if( is_FREXT_profile(profile_idc) ) + len += write_se_v ("PPS: chroma_qp_index_offset", pps->cb_qp_index_offset, bitstream); + else + len += write_se_v ("PPS: chroma_qp_index_offset", pps->chroma_qp_index_offset, bitstream); + + len += write_u_1 ("PPS: deblocking_filter_control_present_flag", pps->deblocking_filter_control_present_flag, bitstream); + len += write_u_1 ("PPS: constrained_intra_pred_flag", pps->constrained_intra_pred_flag, bitstream); + len += write_u_1 ("PPS: redundant_pic_cnt_present_flag", pps->redundant_pic_cnt_present_flag, bitstream); + + // Fidelity Range Extensions stuff + if( is_FREXT_profile(profile_idc) ) + { + len += write_u_1 ("PPS: transform_8x8_mode_flag", pps->transform_8x8_mode_flag, bitstream); + len += write_u_1 ("PPS: pic_scaling_matrix_present_flag", pps->pic_scaling_matrix_present_flag, bitstream); + + if(pps->pic_scaling_matrix_present_flag) + { + ScaleParameters *p_QScale = p_Vid->p_QScale; + unsigned n_ScalingList = 6 + ((p_Vid->active_sps->chroma_format_idc != 3) ? 2 : 6) * pps->transform_8x8_mode_flag; + + for(i=0; ipic_scaling_list_present_flag[i], bitstream); + + if(pps->pic_scaling_list_present_flag[i]) + { + if(i < 6) + len += Scaling_List(p_QScale->ScalingList4x4input[i], p_QScale->ScalingList4x4[i], 16, &p_QScale->UseDefaultScalingMatrix4x4Flag[i], bitstream); + else + len += Scaling_List(p_QScale->ScalingList8x8input[i-6], p_QScale->ScalingList8x8[i-6], 64, &p_QScale->UseDefaultScalingMatrix8x8Flag[i-6], bitstream); + } + } + } + len += write_se_v ("PPS: second_chroma_qp_index_offset", pps->cr_qp_index_offset, bitstream); + } + + SODBtoRBSP(bitstream); // copies the last couple of bits into the byte buffer + + LenInBytes = bitstream->byte_pos; + + // Get rid of the helper structures + free (bitstream); + + return LenInBytes; +} + + + +/*! + ************************************************************************************* + * \brief + * Returns the Profile + * + * \return + * Profile according to Annex A + * + * \note + * Function is currently a dummy. Should "calculate" the profile from those + * config file parameters. E.g. + * + * Profile = Baseline; + * if (CABAC Used || Interlace used) Profile=Main; + * if (!Cabac Used) && (Bframes | SPframes) Profile = Streaming; + * + ************************************************************************************* + */ +int identify_profile(InputParameters *p_Inp) +{ + return p_Inp->ProfileIDC; +} + +/*! + ************************************************************************************* + * \brief + * Returns the Level + * + * \return + * Level according to Annex A + * + * \note + * This function is currently a dummy, but should calculate the level out of + * the config file parameters (primarily the picture size) + ************************************************************************************* + */ +int identify_level(InputParameters *p_Inp) +{ + return p_Inp->LevelIDC; +} + + +/*! + ************************************************************************************* + * \brief + * Function body for VUI Parameter generation (to be done) + * + * \return + * exits with error message + ************************************************************************************* + */ +static int GenerateVUI_parameters_rbsp(seq_parameter_set_rbsp_t *sps, Bitstream *bitstream) +{ + int len=0; + vui_seq_parameters_t *vui_seq_parameters = &(sps->vui_seq_parameters); + + len+=write_u_1 ("VUI: aspect_ratio_info_present_flag", vui_seq_parameters->aspect_ratio_info_present_flag, bitstream); + if (vui_seq_parameters->aspect_ratio_info_present_flag) + { + len+=write_u_v (8,"VUI: aspect_ratio_idc", vui_seq_parameters->aspect_ratio_idc, bitstream); + if (vui_seq_parameters->aspect_ratio_idc == 255) + { + len+=write_u_v (16,"VUI: sar_width", vui_seq_parameters->sar_width, bitstream); + len+=write_u_v (16,"VUI: sar_height", vui_seq_parameters->sar_height, bitstream); + } + } + + len+=write_u_1 ("VUI: overscan_info_present_flag", vui_seq_parameters->overscan_info_present_flag, bitstream); + if (vui_seq_parameters->overscan_info_present_flag) + { + len+=write_u_1 ("VUI: overscan_appropriate_flag", vui_seq_parameters->overscan_appropriate_flag, bitstream); + } + + len+=write_u_1 ("VUI: video_signal_type_present_flag", vui_seq_parameters->video_signal_type_present_flag, bitstream); + if (vui_seq_parameters->video_signal_type_present_flag) + { + len+=write_u_v (3,"VUI: video_format", vui_seq_parameters->video_format, bitstream); + len+=write_u_1 ("VUI: video_full_range_flag", vui_seq_parameters->video_full_range_flag, bitstream); + len+=write_u_1 ("VUI: colour_description_present_flag", vui_seq_parameters->colour_description_present_flag, bitstream); + if (vui_seq_parameters->colour_description_present_flag) + { + len+=write_u_v (8,"VUI: colour_primaries", vui_seq_parameters->colour_primaries, bitstream); + len+=write_u_v (8,"VUI: transfer_characteristics", vui_seq_parameters->transfer_characteristics, bitstream); + len+=write_u_v (8,"VUI: matrix_coefficients", vui_seq_parameters->matrix_coefficients, bitstream); + } + } + + len+=write_u_1 ("VUI: chroma_loc_info_present_flag", vui_seq_parameters->chroma_location_info_present_flag, bitstream); + if (vui_seq_parameters->chroma_location_info_present_flag) + { + len+=write_ue_v ("VUI: chroma_sample_loc_type_top_field", vui_seq_parameters->chroma_sample_loc_type_top_field, bitstream); + len+=write_ue_v ("VUI: chroma_sample_loc_type_bottom_field", vui_seq_parameters->chroma_sample_loc_type_bottom_field, bitstream); + } + + len+=write_u_1 ("VUI: timing_info_present_flag", vui_seq_parameters->timing_info_present_flag, bitstream); + // timing parameters + if (vui_seq_parameters->timing_info_present_flag) + { + len+=write_u_v (32,"VUI: num_units_in_tick", vui_seq_parameters->num_units_in_tick, bitstream); + len+=write_u_v (32,"VUI: time_scale", vui_seq_parameters->time_scale, bitstream); + len+=write_u_1 ("VUI: fixed_frame_rate_flag", vui_seq_parameters->fixed_frame_rate_flag, bitstream); + } + // end of timing parameters + // nal_hrd_parameters_present_flag + len+=write_u_1 ("VUI: nal_hrd_parameters_present_flag", vui_seq_parameters->nal_hrd_parameters_present_flag, bitstream); + if ( vui_seq_parameters->nal_hrd_parameters_present_flag ) + { + len += WriteHRDParameters(sps, bitstream); + } + // vcl_hrd_parameters_present_flag + len+=write_u_1 ("VUI: vcl_hrd_parameters_present_flag", vui_seq_parameters->vcl_hrd_parameters_present_flag, bitstream); + if ( vui_seq_parameters->vcl_hrd_parameters_present_flag ) + { + len += WriteHRDParameters(sps, bitstream); + } + if ( vui_seq_parameters->nal_hrd_parameters_present_flag || vui_seq_parameters->vcl_hrd_parameters_present_flag ) + { + len+=write_u_1 ("VUI: low_delay_hrd_flag", vui_seq_parameters->low_delay_hrd_flag, bitstream ); + } + len+=write_u_1 ("VUI: pic_struct_present_flag", vui_seq_parameters->pic_struct_present_flag, bitstream); + + len+=write_u_1 ("VUI: bitstream_restriction_flag", vui_seq_parameters->bitstream_restriction_flag, bitstream); + if (vui_seq_parameters->bitstream_restriction_flag) + { + len+=write_u_1 ("VUI: motion_vectors_over_pic_boundaries_flag", vui_seq_parameters->motion_vectors_over_pic_boundaries_flag, bitstream); + len+=write_ue_v ("VUI: max_bytes_per_pic_denom", vui_seq_parameters->max_bytes_per_pic_denom, bitstream); + len+=write_ue_v ("VUI: max_bits_per_mb_denom", vui_seq_parameters->max_bits_per_mb_denom, bitstream); + len+=write_ue_v ("VUI: log2_max_mv_length_horizontal", vui_seq_parameters->log2_max_mv_length_horizontal, bitstream); + len+=write_ue_v ("VUI: log2_max_mv_length_vertical", vui_seq_parameters->log2_max_mv_length_vertical, bitstream); + len+=write_ue_v ("VUI: num_reorder_frames", vui_seq_parameters->num_reorder_frames, bitstream); + len+=write_ue_v ("VUI: max_dec_frame_buffering", vui_seq_parameters->max_dec_frame_buffering, bitstream); + } + + return len; +} + +/*! + ************************************************************************************* + * \brief + * Function body for SEI message NALU generation + * + * \return + * A NALU containing the SEI messages + * + ************************************************************************************* + */ +NALU_t *GenerateSEImessage_NALU(InputParameters *p_Inp) +{ + NALU_t *n = AllocNALU(MAXNALUSIZE); + int RBSPlen = 0; + byte rbsp[MAXRBSPSIZE]; + + RBSPlen = GenerateSEImessage_rbsp (p_Inp, NORMAL_SEI, rbsp); + RBSPtoNALU (rbsp, n, RBSPlen, NALU_TYPE_SEI, NALU_PRIORITY_DISPOSABLE, 1); + n->startcodeprefix_len = 4; + + return n; +} + + +/*! + ************************************************************************************* + * \brief + * int GenerateSEImessage_rbsp (int, bufferingperiod_information_struct*, char*) + * + * + * \return + * size of the RBSP in bytes, negative in case of an error + * + * \note + ************************************************************************************* + */ +int GenerateSEImessage_rbsp (InputParameters *p_Inp, int id, byte *rbsp) +{ + Bitstream *bitstream; + + int len = 0, LenInBytes; + assert (rbsp != NULL); + + if ((bitstream=calloc(1, sizeof(Bitstream)))==NULL) + no_mem_exit("SeqParameterSet:bitstream"); + + // .. and use the rbsp provided (or allocated above) for the data + bitstream->streamBuffer = rbsp; + bitstream->bits_to_go = 8; + + { + char sei_message[500] = ""; + char uuid_message[9] = "Random"; // This is supposed to be Random + unsigned int i, message_size = (unsigned int) strlen(p_Inp->SEIMessageText); + TIME_T start_time; + gettime(&start_time); // start time + + if (message_size == 0) + { + message_size = 13; + strncpy(sei_message,"Empty Message",message_size); + } + else + strncpy(sei_message,p_Inp->SEIMessageText,message_size); + + len+=write_u_v (8,"SEI: last_payload_type_byte", 5, bitstream); + message_size += 17; + while (message_size > 254) + { + len+=write_u_v (8,"SEI: ff_byte",255, bitstream); + message_size -= 255; + } + len+=write_u_v (8,"SEI: last_payload_size_byte",message_size, bitstream); + + // Lets randomize uuid based on time +#ifdef _WIN32 + len+=write_u_v (32,"SEI: uuid_iso_iec_11578",(int) start_time.HighPart, bitstream); + len+=write_u_v (32,"SEI: uuid_iso_iec_11578",(int) start_time.QuadPart, bitstream); +#else + len+=write_u_v (32,"SEI: uuid_iso_iec_11578",(int) start_time.tv_sec, bitstream); + len+=write_u_v (32,"SEI: uuid_iso_iec_11578",(int) start_time.tv_usec, bitstream); +#endif + len+=write_u_v (32,"SEI: uuid_iso_iec_11578",(int) (uuid_message[0] << 24) + (uuid_message[1] << 16) + (uuid_message[2] << 8) + (uuid_message[3] << 0), bitstream); + len+=write_u_v (32,"SEI: uuid_iso_iec_11578",(int) (uuid_message[4] << 24) + (uuid_message[5] << 16) + (uuid_message[6] << 8) + (uuid_message[7] << 0), bitstream); + for (i = 0; i < strlen(sei_message); i++) + len+=write_u_v (8,"SEI: user_data_payload_byte",sei_message[i], bitstream); + + len+=write_u_v (8,"SEI: user_data_payload_byte", 0, bitstream); + } + SODBtoRBSP(bitstream); // copies the last couple of bits into the byte buffer + + LenInBytes=bitstream->byte_pos; + + free(bitstream); + return LenInBytes; +} + +/*! + ************************************************************************************* + * \brief + * int WriteHRDParameters((seq_parameter_set_rbsp_t *sps, Bitstream *bitstream) + * + * + * \return + * size of the RBSP in bytes, negative in case of an error + * + * \note + ************************************************************************************* + */ + +int WriteHRDParameters(seq_parameter_set_rbsp_t *sps, Bitstream *bitstream) +{ + // hrd_parameters() + int len = 0; + unsigned int SchedSelIdx = 0; + hrd_parameters_t *hrd = &(sps->vui_seq_parameters.nal_hrd_parameters); + + len+=write_ue_v ("VUI: cpb_cnt_minus1", hrd->cpb_cnt_minus1, bitstream); + len+=write_u_v (4, "VUI: bit_rate_scale", hrd->bit_rate_scale, bitstream); + len+=write_u_v (4, "VUI: cpb_size_scale", hrd->cpb_size_scale, bitstream); + + for( SchedSelIdx = 0; SchedSelIdx <= (hrd->cpb_cnt_minus1); SchedSelIdx++ ) + { + len+=write_ue_v ("VUI: bit_rate_value_minus1", hrd->bit_rate_value_minus1[SchedSelIdx], bitstream); + len+=write_ue_v ("VUI: cpb_size_value_minus1", hrd->cpb_size_value_minus1[SchedSelIdx], bitstream); + len+=write_u_1 ("VUI: cbr_flag", hrd->cbr_flag[SchedSelIdx], bitstream); + } + + len+=write_u_v (5, "VUI: initial_cpb_removal_delay_length_minus1", hrd->initial_cpb_removal_delay_length_minus1, bitstream); + len+=write_u_v (5, "VUI: cpb_removal_delay_length_minus1", hrd->cpb_removal_delay_length_minus1, bitstream); + len+=write_u_v (5, "VUI: dpb_output_delay_length_minus1", hrd->dpb_output_delay_length_minus1, bitstream); + len+=write_u_v (5, "VUI: time_offset_length", hrd->time_offset_length, bitstream); + + return len; +} + +/*! + ************************************************************************ + * \brief + * Returns the size of the dpb depending on level and picture size + * + * + ************************************************************************ + */ +static int getMaxDpbSize(seq_parameter_set_rbsp_t *active_sps) +{ + int pic_size = (active_sps->pic_width_in_mbs_minus1 + 1) * (active_sps->pic_height_in_map_units_minus1 + 1) * (active_sps->frame_mbs_only_flag?1:2) * 384; + + int size = 0; + + switch (active_sps->level_idc) + { + case 9: + size = 152064; + break; + case 10: + size = 152064; + break; + case 11: + if (!is_FREXT_profile(active_sps->profile_idc) && (active_sps->constrained_set3_flag == 1)) + size = 152064; + else + size = 345600; + break; + case 12: + size = 912384; + break; + case 13: + size = 912384; + break; + case 20: + size = 912384; + break; + case 21: + size = 1824768; + break; + case 22: + size = 3110400; + break; + case 30: + size = 3110400; + break; + case 31: + size = 6912000; + break; + case 32: + size = 7864320; + break; + case 40: + size = 12582912; + break; + case 41: + size = 12582912; + break; + case 42: + size = 13369344; + break; + case 50: + size = 42393600; + break; + case 51: + size = 70778880; + break; + default: + error ("undefined level", 500); + break; + } + + size /= pic_size; + size = imin( size, 16); + + return size; +} + +/*! + ************************************************************************************* + * \brief + * void GenerateVUIParameters(seq_parameter_set_rbsp_t *sps, InputParameters *p_Inp) + * + * + * \return + * none + * + * \note + ************************************************************************************* + */ + +void GenerateVUIParameters(seq_parameter_set_rbsp_t *sps, InputParameters *p_Inp) +{ + unsigned int SchedSelIdx; + hrd_parameters_t *nal_hrd = &(sps->vui_seq_parameters.nal_hrd_parameters); + hrd_parameters_t *vcl_hrd = &(sps->vui_seq_parameters.vcl_hrd_parameters); + vui_seq_parameters_t *vui = &(sps->vui_seq_parameters); + VUIParameters *iVui = &p_Inp->VUI; + + vui->aspect_ratio_info_present_flag = (Boolean) iVui->aspect_ratio_info_present_flag; + vui->aspect_ratio_idc = (unsigned int) iVui->aspect_ratio_idc; + vui->sar_width = (unsigned short) iVui->sar_width; + vui->sar_height = (unsigned short) iVui->sar_height; + vui->overscan_info_present_flag = (Boolean) iVui->overscan_info_present_flag; + vui->overscan_appropriate_flag = (Boolean) iVui->overscan_appropriate_flag; + vui->video_signal_type_present_flag = (Boolean) iVui->video_signal_type_present_flag; + vui->video_format = (unsigned int) iVui->video_format; + vui->video_full_range_flag = (Boolean) iVui->video_full_range_flag; + vui->colour_description_present_flag = (Boolean) iVui->colour_description_present_flag; + vui->colour_primaries = (unsigned int) iVui->colour_primaries; + vui->transfer_characteristics = (unsigned int) iVui->transfer_characteristics; + vui->matrix_coefficients = (unsigned int) iVui->matrix_coefficients; + vui->chroma_location_info_present_flag = (Boolean) iVui->chroma_location_info_present_flag; + vui->chroma_sample_loc_type_top_field = (unsigned int) iVui->chroma_sample_loc_type_top_field; + vui->chroma_sample_loc_type_bottom_field = (unsigned int) iVui->chroma_sample_loc_type_bottom_field; + vui->timing_info_present_flag = (Boolean) iVui->timing_info_present_flag; + vui->num_units_in_tick = (unsigned int) iVui->num_units_in_tick; + vui->time_scale = (unsigned int) iVui->time_scale; + vui->fixed_frame_rate_flag = (Boolean) iVui->fixed_frame_rate_flag; + + // NAL HRD parameters + vui->nal_hrd_parameters_present_flag = (Boolean) iVui->nal_hrd_parameters_present_flag; + nal_hrd->cpb_cnt_minus1 = (unsigned int) iVui->nal_cpb_cnt_minus1; + nal_hrd->bit_rate_scale = (unsigned int) iVui->nal_bit_rate_scale; + nal_hrd->cpb_size_scale = (unsigned int) iVui->nal_cpb_size_scale; + for ( SchedSelIdx = 0; SchedSelIdx <= nal_hrd->cpb_cnt_minus1; SchedSelIdx++ ) + { + nal_hrd->bit_rate_value_minus1[SchedSelIdx] = (unsigned int) iVui->nal_bit_rate_value_minus1; + nal_hrd->cpb_size_value_minus1[SchedSelIdx] = (unsigned int) iVui->nal_cpb_size_value_minus1; + nal_hrd->cbr_flag[SchedSelIdx] = (unsigned int) iVui->nal_vbr_cbr_flag; + } + nal_hrd->initial_cpb_removal_delay_length_minus1 = (unsigned int) iVui->nal_initial_cpb_removal_delay_length_minus1; + nal_hrd->cpb_removal_delay_length_minus1 = (unsigned int) iVui->nal_cpb_removal_delay_length_minus1; + nal_hrd->dpb_output_delay_length_minus1 = (unsigned int) iVui->nal_dpb_output_delay_length_minus1; + nal_hrd->time_offset_length = (unsigned int) iVui->nal_time_offset_length; + + // VCL HRD parameters + vui->vcl_hrd_parameters_present_flag = (Boolean) iVui->vcl_hrd_parameters_present_flag; + vcl_hrd->cpb_cnt_minus1 = (unsigned int) iVui->vcl_cpb_cnt_minus1; + vcl_hrd->bit_rate_scale = (unsigned int) iVui->vcl_bit_rate_scale; + vcl_hrd->cpb_size_scale = (unsigned int) iVui->vcl_cpb_size_scale; + for ( SchedSelIdx = 0; SchedSelIdx <= vcl_hrd->cpb_cnt_minus1; SchedSelIdx++ ) + { + vcl_hrd->bit_rate_value_minus1[SchedSelIdx] = (unsigned int) iVui->vcl_bit_rate_value_minus1; + vcl_hrd->cpb_size_value_minus1[SchedSelIdx] = (unsigned int) iVui->vcl_cpb_size_value_minus1; + vcl_hrd->cbr_flag[SchedSelIdx] = (unsigned int) iVui->vcl_vbr_cbr_flag; + } + vcl_hrd->initial_cpb_removal_delay_length_minus1 = (unsigned int) iVui->vcl_initial_cpb_removal_delay_length_minus1; + vcl_hrd->cpb_removal_delay_length_minus1 = (unsigned int) iVui->vcl_cpb_removal_delay_length_minus1; + vcl_hrd->dpb_output_delay_length_minus1 = (unsigned int) iVui->vcl_dpb_output_delay_length_minus1; + vcl_hrd->time_offset_length = (unsigned int) iVui->vcl_time_offset_length; + + vui->low_delay_hrd_flag = (Boolean) iVui->low_delay_hrd_flag; + vui->pic_struct_present_flag = (Boolean) iVui->pic_struct_present_flag; + vui->bitstream_restriction_flag = (Boolean) iVui->bitstream_restriction_flag; + vui->motion_vectors_over_pic_boundaries_flag = (Boolean) iVui->motion_vectors_over_pic_boundaries_flag; + vui->max_bytes_per_pic_denom = (unsigned int) iVui->max_bytes_per_pic_denom; + vui->max_bits_per_mb_denom = (unsigned int) iVui->max_bits_per_mb_denom; + vui->log2_max_mv_length_horizontal = (unsigned int) iVui->log2_max_mv_length_horizontal; + vui->log2_max_mv_length_vertical = (unsigned int) iVui->log2_max_mv_length_vertical; + vui->max_dec_frame_buffering = (unsigned int) imin( iVui->max_dec_frame_buffering, getMaxDpbSize(sps) ); + vui->num_reorder_frames = (unsigned int) imin( iVui->num_reorder_frames, (int)vui->max_dec_frame_buffering ); + + // special case to signal the RGB format + if (p_Inp->output.color_model != CM_YUV && p_Inp->output.yuv_format == YUV444) + { + printf ("VUI: writing Sequence Parameter VUI to signal RGB format\n"); + + vui->aspect_ratio_info_present_flag = FALSE; + vui->overscan_info_present_flag = FALSE; + vui->video_signal_type_present_flag = TRUE; + vui->video_format = 2; + vui->video_full_range_flag = TRUE; + vui->colour_description_present_flag = TRUE; + vui->colour_primaries = 2; + vui->transfer_characteristics = 2; + vui->matrix_coefficients = 0; + vui->chroma_location_info_present_flag = FALSE; + vui->timing_info_present_flag = FALSE; + vui->nal_hrd_parameters_present_flag = FALSE; + vui->vcl_hrd_parameters_present_flag = FALSE; + vui->pic_struct_present_flag = FALSE; + vui->bitstream_restriction_flag = FALSE; + } +} diff --git a/lencod/src/pred_struct.c b/lencod/src/pred_struct.c new file mode 100644 index 0000000..a840ade --- /dev/null +++ b/lencod/src/pred_struct.c @@ -0,0 +1,2602 @@ +/*! + *************************************************************************** + * \file + * pred_struct.c + * + * \author + * Athanasios Leontaris + * + * \date + * June 8, 2009 + * + * \brief + * Source file for prediction structure function + ************************************************************************** + */ + +#include "pred_struct.h" +#include "explicit_seq.h" + +#define DEBUG_PRED_STRUCT 0 + +static inline void pop_pred_struct_frm( PredStructFrm *p_dst, int slice_type, int nal_ref_idc, int disp_offset, int layer, int slice_qp_off, + int random_access, int temporal_layer ); +static void init_gop_struct( InputParameters *p_Inp, SeqStructure *p_seq_struct, int is_idr, int *memory_size ); +static void free_gop_struct( SeqStructure *p_seq_struct ); +static void init_pred_struct( VideoParameters *p_Vid, InputParameters *p_Inp, SeqStructure *p_seq_struct, int *memory_size ); +static void free_pred_struct( SeqStructure *p_seq_struct ); +static PicStructure * alloc_pic_struct( int num_slices, int *memory_size ); +static void free_pic_struct( PicStructure *p_pic ); +static FrameUnitStruct * alloc_frame_buffer( InputParameters *p_Inp, int num_frames, int num_slices, int *memory_size ); +static void free_frame_buffer( FrameUnitStruct *p_frm_struct, int num_frames ); + +static int rand_acc_test_range( int curr_frame, int offset, int idr_period ); +static inline int check_power_of_two( int number ); +static int establish_random_access( InputParameters *p_Inp, SeqStructure *p_seq_struct, int curr_frame, int avail_frames, int sim ); +static int establish_intra( InputParameters *p_Inp, SeqStructure *p_seq_struct, int curr_frame, int avail_frames, int sim ); +static int establish_sp( InputParameters *p_Inp, SeqStructure *p_seq_struct, int curr_frame, int avail_frames, int sim ); +static int get_fixed_frame( InputParameters *p_Inp, SeqStructure *p_seq_struct, int curr_frame, int avail_frames ); +static int get_prd_index( InputParameters *p_Inp, SeqStructure *p_seq_struct, int num_frames ); +static int get_idr_index( InputParameters *p_Inp, SeqStructure *p_seq_struct, int num_frames ); +static int get_intra_index( InputParameters *p_Inp, SeqStructure *p_seq_struct, int num_frames ); +static void populate_frame( InputParameters *p_Inp, SeqStructure *p_seq_struct, FrameUnitStruct *p_frm_struct, PredStructAtom *p_cur_prd, + int curr_frame, int pred_frame, int idx, int num_slices, int no_rnd_acc ); +static void update_frame_indices( SeqStructure *p_seq_struct, FrameUnitStruct *p_frm_struct, int curr_frame, int pred_frame ); +static int populate_reg_pred_struct_atom( VideoParameters *p_Vid, InputParameters *p_Inp, SeqStructure *p_seq_struct, + int curr_frame, int proc_frames, int *terminate_pop ); +static int populate_rnd_acc_pred_struct_atom( VideoParameters *p_Vid, InputParameters *p_Inp, SeqStructure *p_seq_struct, + int curr_frame, int proc_frames, int *terminate_pop, int is_idr, int no_rnd_acc ); +#if (MVC_EXTENSION_ENABLE) +static void copy_frame_mvc( InputParameters *p_Inp, FrameUnitStruct *p_src, FrameUnitStruct *p_dst, int num_slices, int view_id ); +#endif + +/*! + *********************************************************************** + * \brief + * Derivation of Picture Order Count Type 0 + * \param p_Vid + * pointer to the VideoParameters structure + * \param p_Inp + * pointer to the InputParameters structure + * \param p_frm_struct + * pointer to the cyclical frame unit buffer + *********************************************************************** + */ + +void get_poc_type_zero( VideoParameters *p_Vid, InputParameters *p_Inp, FrameUnitStruct *p_frm_struct ) +{ + FrameUnitStruct *p_cur = p_frm_struct; // + (curr_frm_idx % p_Vid->frm_struct_buffer); + int last_disp_order; + + if ( p_Vid->last_idr_code_order > p_Vid->last_mmco_5_code_order ) + { + last_disp_order = p_Vid->last_idr_disp_order; + } + else + { + last_disp_order = p_Vid->last_mmco_5_disp_order; + } + + if ( p_cur->idr_flag ) + { + p_Vid->toppoc = 0; + } + else + { + p_Vid->toppoc = (p_cur->frame_no - last_disp_order) << 1; + } + // frame_skip consideration + p_Vid->toppoc = (1 + p_Inp->frame_skip) * p_Vid->toppoc; + + if ( p_Inp->PicInterlace == FRAME_CODING && p_Inp->MbInterlace == FRAME_CODING ) + { + p_Vid->bottompoc = p_Vid->toppoc; // progressive + } + else + { + p_Vid->bottompoc = p_Vid->toppoc + 1; // hard coded + } + + p_Vid->framepoc = imin( p_Vid->toppoc, p_Vid->bottompoc ); +} + +/*! + *********************************************************************** + * \brief + * Derivation of Picture Order Count Type 1 + * \param p_Vid + * pointer to the VideoParameters structure + * \param p_Inp + * pointer to the InputParameters structure + * \param p_frm_struct + * pointer to the cyclical frame unit buffer + *********************************************************************** + */ + +void get_poc_type_one( VideoParameters *p_Vid, InputParameters *p_Inp, FrameUnitStruct *p_frm_struct ) +{ + FrameUnitStruct *p_cur = p_frm_struct; // + curr_frm_idx; + int i; + int AbsFrameNum; + int ExpectedDeltaPerPicOrderCntCycle; + int PicOrderCntCycleCnt; + int FrameNumInPicOrderCntCycle; + int ExpectedPicOrderCnt; + CodingParameters *p_EncodePar = p_Vid->p_EncodePar[p_Vid->dpb_layer_id]; + + // we need to get type 0 values first and then estimate delta_pic_order_cnt with respect to those + get_poc_type_zero( p_Vid, p_Inp, p_frm_struct ); + + // next we use the decoder POC Type 1 code to determine the delta_pic_order_cnt + + // derive p_Vid->FrameNumOffset + if ( p_cur->idr_flag ) + { + p_Vid->FrameNumOffset = 0; + p_Vid->delta_pic_order_cnt[0] = 0; + } + else + { + if ( p_Vid->last_has_mmco_5 ) + { + p_EncodePar->prevFrameNumOffset = 0; + p_EncodePar->prevFrameNum = 0; + } + if ( p_Vid->frame_num < p_EncodePar->prevFrameNum ) + { + p_Vid->FrameNumOffset = p_EncodePar->prevFrameNumOffset + p_Vid->max_frame_num; + } + else + { + p_Vid->FrameNumOffset = p_EncodePar->prevFrameNumOffset; + } + } + + // derive AbsFrameNum + if ( p_Vid->num_ref_frames_in_pic_order_cnt_cycle ) + { + AbsFrameNum = p_Vid->FrameNumOffset + p_Vid->frame_num; + } + else + { + AbsFrameNum=0; + } + if ( !(p_cur->nal_ref_idc) && AbsFrameNum > 0 ) + { + AbsFrameNum--; + } + + // derive ExpectedPicOrderCnt + ExpectedDeltaPerPicOrderCntCycle=0; + if ( p_Vid->num_ref_frames_in_pic_order_cnt_cycle ) + { + for( i = 0; i < (int) p_Vid->num_ref_frames_in_pic_order_cnt_cycle; i++ ) + { + ExpectedDeltaPerPicOrderCntCycle += p_Vid->offset_for_ref_frame[i]; + } + } + + if ( AbsFrameNum ) + { + PicOrderCntCycleCnt = (AbsFrameNum - 1) / p_Vid->num_ref_frames_in_pic_order_cnt_cycle; + FrameNumInPicOrderCntCycle = (AbsFrameNum - 1) % p_Vid->num_ref_frames_in_pic_order_cnt_cycle; + + ExpectedPicOrderCnt = PicOrderCntCycleCnt * ExpectedDeltaPerPicOrderCntCycle; + for ( i = 0; i <= (int)FrameNumInPicOrderCntCycle; i++ ) + { + ExpectedPicOrderCnt += p_Vid->offset_for_ref_frame[i]; + } + } + else + { + ExpectedPicOrderCnt = 0; + } + + if ( !(p_cur->nal_ref_idc) ) + { + ExpectedPicOrderCnt += p_Vid->offset_for_non_ref_pic; + } + + p_Vid->delta_pic_order_cnt[0] = p_Vid->toppoc - ExpectedPicOrderCnt; + p_Vid->delta_pic_order_cnt[1] = 0; + + // update parameters + p_Vid->prevFrameNum = p_EncodePar->prevFrameNum = p_Vid->frame_num; + p_Vid->prevFrameNumOffset = p_EncodePar->prevFrameNumOffset = p_Vid->FrameNumOffset; +} + +/*! + *********************************************************************** + * \brief + * Initializes POC type 0 and 1: POC type 1 simulates the decoder's behavior and sets values \n + * given the POCs calculated for POC type 0; needs to be tested + * \param p_Vid + * pointer to the VideoParameters structure + *********************************************************************** + */ + +void init_poc(VideoParameters *p_Vid) +{ + InputParameters *p_Inp = p_Vid->p_Inp; + // general parameters + p_Vid->pic_order_cnt_type = p_Inp->pic_order_cnt_type; + if ((p_Inp->PicInterlace == FRAME_CODING) && (p_Inp->MbInterlace == FRAME_CODING)) + { + p_Vid->bottom_field_pic_order_in_frame_present_flag = FALSE; + } + else + { + p_Vid->bottom_field_pic_order_in_frame_present_flag = TRUE; + } + + // POC type 0 + if ((p_Inp->PicInterlace==FRAME_CODING) && (p_Inp->MbInterlace==FRAME_CODING)) + { + p_Vid->delta_pic_order_cnt_bottom = 0; + } + else + { + p_Vid->delta_pic_order_cnt_bottom = 1; + } + + // POC type 1 (parameters need to be *optimized* for the prediction structure) + // should be good enough for now + p_Vid->delta_pic_order_always_zero_flag = FALSE; + p_Vid->num_ref_frames_in_pic_order_cnt_cycle = 1; + + // these initialization have to depend more on the structure + if (p_Inp->BRefPictures == 1) + { + p_Vid->offset_for_non_ref_pic = 0; + p_Vid->offset_for_ref_frame[0] = 2; + } + else + { + p_Vid->offset_for_non_ref_pic = -2*(p_Inp->NumberBFrames); + p_Vid->offset_for_ref_frame[0] = 2*(p_Inp->NumberBFrames + 1); + } + + if ((p_Inp->PicInterlace==FRAME_CODING) && (p_Inp->MbInterlace==FRAME_CODING)) + { + p_Vid->offset_for_top_to_bottom_field = 0; + } + else + { + p_Vid->offset_for_top_to_bottom_field = 1; + } +} + +/*! + *********************************************************************** + * \brief + * Allocate picture structure + * \param num_slices + * max number of slices used to code the current picture + * \param memory_size + * pointer to the memory_size variable to count the number of bytes allocated + * \return + * pointer to the allocated PicStructure + *********************************************************************** + */ + +static PicStructure * alloc_pic_struct( int num_slices, int *memory_size ) +{ + PicStructure *p_pic = (PicStructure *)malloc( sizeof( PicStructure ) ); + if ( p_pic == NULL ) + { + fprintf(stdout, "\nNot enough memory or error during memory allocation: alloc_pic_struct()"); + exit(1); + } + *memory_size += sizeof( PicStructure ); + + p_pic->num_slices = num_slices; // this could also be set on a frame basis + p_pic->p_Slice = (SliceStructure *)malloc( num_slices * sizeof( SliceStructure ) ); + if ( p_pic->p_Slice == NULL ) + { + fprintf(stdout, "\nNot enough memory or error during memory allocation: alloc_pic_struct()"); + exit(1); + } + *memory_size += (num_slices * sizeof( SliceStructure )); + + return p_pic; +} + +/*! + *********************************************************************** + * \brief + * Free picture structure + * \param p_pic + * pointer to the picture structure that we wish to deallocate + *********************************************************************** + */ + +static void free_pic_struct( PicStructure *p_pic ) +{ + if ( p_pic != NULL ) + { + if ( p_pic->p_Slice != NULL ) + { + free( p_pic->p_Slice ); + p_pic->p_Slice = NULL; + } + free( p_pic ); + } +} + +/*! + *********************************************************************** + * \brief + * Allocate frame buffer + * \param p_Inp + * pointer to the InputParameters structure + * \param num_frames + * number of frames/length of the cyclical frame buffer + * \param num_slices + * max number of slices used to code each frame unit in the cyclical frame buffer + * \param memory_size + * pointer to the memory_size variable to count the number of bytes allocated + * \return + * pointer to the allocated FrameUnitStruct + *********************************************************************** + */ + +static FrameUnitStruct * alloc_frame_buffer( InputParameters *p_Inp, int num_frames, int num_slices, int *memory_size ) +{ + int idx; + + // FrameUnitStruct + FrameUnitStruct *p_frm_struct = (FrameUnitStruct *)malloc( num_frames * sizeof( FrameUnitStruct ) ); + FrameUnitStruct *p_frm_tmp; + + if ( p_frm_struct == NULL ) + { + fprintf(stdout, "\nNot enough memory or error during memory allocation: alloc_frame_buffer()"); + exit(1); + } + *memory_size += num_frames * sizeof( FrameUnitStruct ); + + for ( idx = 0; idx < num_frames; idx++ ) + { + p_frm_tmp = p_frm_struct + idx; + + switch( p_Inp->PicInterlace ) + { + default: + case FRAME_CODING: + p_frm_tmp->field_pic_flag = 0; + p_frm_tmp->p_top_fld_pic = NULL; + p_frm_tmp->p_bot_fld_pic = NULL; + p_frm_tmp->p_frame_pic = alloc_pic_struct( num_slices, memory_size ); + break; + case FIELD_CODING: + p_frm_tmp->field_pic_flag = 1; + p_frm_tmp->p_top_fld_pic = alloc_pic_struct( num_slices, memory_size ); + p_frm_tmp->p_bot_fld_pic = alloc_pic_struct( num_slices, memory_size ); + p_frm_tmp->p_frame_pic = NULL; + break; + case ADAPTIVE_CODING: + p_frm_tmp->p_top_fld_pic = alloc_pic_struct( num_slices, memory_size ); + p_frm_tmp->p_bot_fld_pic = alloc_pic_struct( num_slices, memory_size ); + p_frm_tmp->p_frame_pic = alloc_pic_struct( num_slices, memory_size ); + break; + } + } + + return p_frm_struct; +} + +/*! + *********************************************************************** + * \brief + * Free frame buffer + * \param p_frm_struct + * pointer to the cyclical frame buffer + * \param num_frames + * number of frames in the cyclical frame buffer + *********************************************************************** + */ + +static void free_frame_buffer( FrameUnitStruct *p_frm_struct, int num_frames ) +{ + int idx; + FrameUnitStruct *p_frm_tmp; + + if ( p_frm_struct != NULL ) + { + for ( idx = 0; idx < num_frames; idx++ ) + { + p_frm_tmp = p_frm_struct + idx; + + free_pic_struct( p_frm_tmp->p_top_fld_pic ); + p_frm_tmp->p_top_fld_pic = NULL; + + free_pic_struct( p_frm_tmp->p_bot_fld_pic ); + p_frm_tmp->p_bot_fld_pic = NULL; + + free_pic_struct( p_frm_tmp->p_frame_pic ); + p_frm_tmp->p_frame_pic = NULL; + } + + free( p_frm_struct ); + } +} + +/*! + *********************************************************************** + * \brief + * Initialize sequence structure + * \param p_Vid + * pointer to the VideoParameters structure + * \param p_Inp + * pointer to the InputParameters structure + * \param memory_size + * pointer to the memory_size variable that stores the number of bytes allocated so far + * \return + * pointer to SeqStructure + *********************************************************************** + */ + +SeqStructure * init_seq_structure( VideoParameters *p_Vid, InputParameters *p_Inp, int *memory_size ) +{ +#if (MVC_EXTENSION_ENABLE) + int start, end; +#endif + SeqStructure *p_seq_struct = (SeqStructure *)malloc( sizeof( SeqStructure ) ); + int frames_to_pop; + + if ( p_seq_struct == NULL ) + { + fprintf(stdout, "\nNot enough memory or error during memory allocation: init_seq_structure()"); + exit(1); + } + + // set length of frame buffer + p_Inp->FrmStructBufferLength = imax( p_Inp->FrmStructBufferLength, imax( p_Inp->idr_period, p_Inp->intra_period ) + p_Inp->NumberBFrames + p_Inp->intra_delay + 2 ); + p_Vid->frm_struct_buffer = p_Inp->FrmStructBufferLength; + p_Vid->frm_struct_buffer = imin( p_Vid->frm_struct_buffer, p_Inp->no_frames ); + p_Inp->FrmStructBufferLength = imin( p_Inp->FrmStructBufferLength, p_Inp->no_frames ); + + p_Inp->FrmStructBufferLength = p_Inp->no_frames; + p_Vid->frm_struct_buffer = p_Inp->no_frames; + + *memory_size += sizeof( SeqStructure ); + + p_seq_struct->max_num_slices = (p_Inp->slice_mode == 1) ? imax(1, p_Vid->FrameSizeInMbs / p_Inp->slice_argument) : 1; + p_seq_struct->num_frames = p_Vid->frm_struct_buffer; + p_seq_struct->p_frm = alloc_frame_buffer( p_Inp, p_seq_struct->num_frames, p_seq_struct->max_num_slices, memory_size ); + p_seq_struct->last_random_access_frame = 0; + p_seq_struct->last_idr_frame = 0; + p_seq_struct->last_intra_frame = 0; + p_seq_struct->last_mmco_5_frame = -1; + p_seq_struct->curr_num_to_populate = p_Vid->frm_struct_buffer; + p_seq_struct->pop_start_frame = 0; + p_seq_struct->last_rand_access_disp = 0; + p_seq_struct->last_idr_disp = 0; + p_seq_struct->last_intra_disp = 0; + p_seq_struct->last_bl_frm_disposable = 0; + p_seq_struct->last_sp_frame = 0; + p_seq_struct->last_sp_disp = 0; + p_seq_struct->pop_flag = 0; + +#if (MVC_EXTENSION_ENABLE) + p_seq_struct->num_frames_mvc = p_seq_struct->num_frames * p_Inp->num_of_views; // two views hence twice the buffer size + p_seq_struct->p_frm_mvc = alloc_frame_buffer( p_Inp, p_seq_struct->num_frames_mvc, p_seq_struct->max_num_slices, memory_size ); +#endif + + // initialize prediction structures + init_pred_struct( p_Vid, p_Inp, p_seq_struct, memory_size ); + init_gop_struct ( p_Inp, p_seq_struct, 1, memory_size ); // IDR GOPs + init_gop_struct ( p_Inp, p_seq_struct, 0, memory_size ); // Intra GOPs + + frames_to_pop = p_Vid->frm_struct_buffer; + + // populate frames +#if (MVC_EXTENSION_ENABLE) + start = p_seq_struct->pop_start_frame; + populate_frm_struct( p_Vid, p_Inp, p_seq_struct, frames_to_pop, p_Inp->no_frames ); + + if ( p_Inp->num_of_views > 1 ) + { + assert( p_Inp->num_of_views < 4 ); + end = p_seq_struct->pop_start_frame; + populate_frm_struct_mvc( p_Vid, p_Inp, p_seq_struct, start, end ); + } +#else + populate_frm_struct( p_Vid, p_Inp, p_seq_struct, frames_to_pop, p_Inp->no_frames ); +#endif + + return p_seq_struct; +} + +/*! + *********************************************************************** + * \brief + * Free sequence structure + * \param p_seq_struct + * pointer to the sequence structure we wish to deallocate + *********************************************************************** + */ + +void free_seq_structure( SeqStructure *p_seq_struct ) +{ + free_frame_buffer( p_seq_struct->p_frm, p_seq_struct->num_frames ); + p_seq_struct->p_frm = NULL; +#if (MVC_EXTENSION_ENABLE) + free_frame_buffer( p_seq_struct->p_frm_mvc, p_seq_struct->num_frames_mvc ); + p_seq_struct->p_frm_mvc = NULL; +#endif + free_gop_struct( p_seq_struct ); + p_seq_struct->p_gop = NULL; + free_pred_struct( p_seq_struct ); + p_seq_struct->p_prd = NULL; + + if ( p_seq_struct != NULL ) + { + free( p_seq_struct ); + } +} + +/*! + *********************************************************************** + * \brief + * Establish whether the frame with coding order "curr_frame" will be coded as an IDR picture + * \param p_Inp + * pointer to the InputParameters structure + * \param p_seq_struct + * pointer to the sequence structure + * \param curr_frame + * coding order of the current frame + * \param avail_frames + * frames available for population + * \param sim + * simulate the insertion: this disables checks about available frames ;) + * \return + * returns 1 when current frame will be a random access frame \n + * if PreferDispOrder == 1 then it returns 1 + disp_offset (for the random access frame) + *********************************************************************** + */ + +static int establish_random_access( InputParameters *p_Inp, SeqStructure *p_seq_struct, int curr_frame, int avail_frames, int sim ) +{ + int delta; + int is_random_access = 0; + + if ( !curr_frame ) // first frame in coding order + { + if ( !(p_Inp->PreferDispOrder) ) // coding order + { + is_random_access = 1; + } + else + { + int idx; + PredStructAtom *p_cur_gop; + + // test each random access prediction structure, starting from the longest one + // if random access is established then break from this loop and prepare output parameter + for ( idx = (p_seq_struct->num_gops - 1); idx >= 0; idx-- ) + { + p_cur_gop = p_seq_struct->p_gop + idx; + // check if length of structure overflows the available frame number + if ( sim ) + { + if ( (curr_frame + p_cur_gop->length) <= p_Inp->no_frames ) + { + is_random_access = 1 + idx; + break; + } + } + else + { + if ( p_cur_gop->length <= avail_frames ) + { + is_random_access = 1 + idx; + break; + } + } + } + } + } + else if ( p_Inp->idr_period ) + { + if ( !(p_Inp->PreferDispOrder) ) // coding order + { + switch ( p_Inp->adaptive_idr_period ) + { + default: + case 0: + if ( curr_frame % p_Inp->idr_period == 0 ) + { + is_random_access = 1; + } + break; + case 1: + delta = curr_frame - p_seq_struct->last_random_access_frame; + if ( delta >= p_Inp->idr_period && delta >= p_Inp->MinIDRDistance ) + { + is_random_access = 1; + } + break; + case 2: + delta = curr_frame - imax( p_seq_struct->last_random_access_frame, p_seq_struct->last_intra_frame); + if ( delta >= p_Inp->idr_period && delta >= p_Inp->MinIDRDistance ) + { + is_random_access = 1; + } + break; + } + } + else // display order (insertion) + { + int idx; + int offset; + PredStructAtom *p_cur_gop; + + // test each random access prediction structure, starting from the longest one + // if random access is established then break from this loop and prepare output parameter + for ( idx = (p_seq_struct->num_gops - 1); idx >= 0; idx-- ) + { + p_cur_gop = p_seq_struct->p_gop + idx; + // check if length of structure overflows the available frame number + if ( sim ) + { + if ( (curr_frame + p_cur_gop->length) > p_Inp->no_frames ) + { + continue; + } + } + else + { + if ( p_cur_gop->length > avail_frames ) + { + continue; + } + } + // recall that frame "0" is the random access frame in those prediction structures + offset = p_cur_gop->p_frm[0].disp_offset; + + // now test if random access conditions are satisfied + switch ( p_Inp->adaptive_idr_period ) + { + default: + case 0: + if ( !curr_frame || (curr_frame + offset - p_Inp->intra_delay) % p_Inp->idr_period == 0 ) + { + is_random_access = 1 + idx; + } + break; + case 1: + delta = (curr_frame + offset) - p_seq_struct->last_rand_access_disp; + if ( !curr_frame || (delta >= p_Inp->idr_period && delta >= p_Inp->MinIDRDistance) ) + { + is_random_access = 1 + idx; + } + break; + case 2: + delta = (curr_frame + offset) - imax( p_seq_struct->last_rand_access_disp, p_seq_struct->last_intra_disp); + if ( !curr_frame || (delta >= p_Inp->idr_period && delta >= p_Inp->MinIDRDistance) ) + { + is_random_access = 1 + idx; + } + break; + } + if ( is_random_access ) + { + break; + } + } + } + } + // additional case can be added to insert IDR based on *pre-analysis* and other "pre-scient" tools + + return is_random_access; +} + +/*! + *********************************************************************** + * \brief + * Test if IDR is placed between curr_frame and curr_frame + offset inclusive + *********************************************************************** + */ + +static int rand_acc_test_range( int curr_frame, int offset, int idr_period ) +{ + int idx; + int no_rnd_acc = 1; + + if ( idr_period > 0 ) + { + for ( idx = curr_frame; idx <= (curr_frame + offset); idx++ ) + { + if ( idx % idr_period == 0 ) + { + no_rnd_acc = 0; + break; + } + } + } + + return no_rnd_acc; +} + +/*! + *********************************************************************** + * \brief + * Establish whether the frame with coding order "curr_frame" will be coded as an INTRA picture + * \param p_Inp + * pointer to the InputParameters structure + * \param p_seq_struct + * pointer to the sequence structure + * \param curr_frame + * coding order of the current frame + * \param avail_frames + * frames available for population + * \return + * returns 1 when current frame will be an intra frame \n + * if PreferDispOrder == 1 then it returns 1 + disp_offset (for the intra frame) + *********************************************************************** + */ + +static int establish_intra( InputParameters *p_Inp, SeqStructure *p_seq_struct, int curr_frame, int avail_frames, int sim ) +{ + int is_intra = 0; + + if ( !curr_frame ) // first frame in coding order + { + is_intra = 1; + } + else if ( p_Inp->intra_period ) + { + if ( !(p_Inp->PreferDispOrder) ) // coding order + { + switch ( p_Inp->adaptive_intra_period ) + { + default: + case 0: + if ( curr_frame % p_Inp->intra_period == 0 ) + { + is_intra = 1; + } + break; + case 1: + if ( (curr_frame - imax( p_seq_struct->last_idr_frame, p_seq_struct->last_intra_frame)) % p_Inp->intra_period == 0 ) + { + is_intra = 1; + } + break; + } + } + else // display order (insertion) + { + int idx; + int offset; + PredStructAtom *p_cur_gop; + + // test each random access prediction structure, staring from the longest one + // if random access is established then break from this loop and prepare output parameter + for ( idx = (p_seq_struct->num_intra_gops - 1); idx >= 0; idx-- ) + { + p_cur_gop = p_seq_struct->p_intra_gop + idx; + // check if length of structure overflows the available frame number + if ( sim ) + { + if ( (curr_frame + p_cur_gop->length) > p_Inp->no_frames ) + { + continue; + } + } + else + { + if ( p_cur_gop->length > avail_frames ) + { + continue; + } + } + // recall that frame "0" is the random access frame in those prediction structures + offset = p_cur_gop->p_frm[0].disp_offset; + + // now test if random access conditions are satisfied + switch ( p_Inp->adaptive_intra_period ) + { + default: + case 0: + // intra_period operates as if EnableIDRGOP=0 which means that the criteria for its insertion, when inserting based on display order + // may be satisfied before (in terms of coding order) an IDR_period with EnableIDRGOP=1 (for 0 there is no issue) + // this code ensures that IDRPeriod always takes precedence over IntraPeriod + if ( (curr_frame + offset) % p_Inp->intra_period == 0 && rand_acc_test_range( curr_frame, offset, p_Inp->idr_period ) ) + { + is_intra = 1 + idx; + } + break; + case 2: + // see comment above + if ( ((curr_frame + offset) - imax( p_seq_struct->last_idr_disp, p_seq_struct->last_intra_disp)) % p_Inp->intra_period == 0 + && rand_acc_test_range( curr_frame - imax( p_seq_struct->last_idr_disp, p_seq_struct->last_intra_disp), offset, p_Inp->idr_period ) ) + { + is_intra = 1 + idx; + } + break; + } + if ( is_intra ) + { + break; + } + } + } + } + // additional case can be added to insert IDR based on *pre-analysis* and other "pre-scient" tools + + return is_intra; +} + +/*! + *********************************************************************** + * \brief + * Establish whether the frame with coding order "curr_frame" will be coded as an SP picture + * \param p_Inp + * pointer to the InputParameters structure + * \param p_seq_struct + * pointer to the sequence structure + * \param curr_frame + * coding order of the current frame + * \param avail_frames + * frames available for population + * \return + * returns 1 when current frame will be an SP frame \n + * if PreferDispOrder == 1 then it returns 1 + disp_offset (for the SP frame) + *********************************************************************** + */ + +static int establish_sp( InputParameters *p_Inp, SeqStructure *p_seq_struct, int curr_frame, int avail_frames, int sim ) +{ + int is_sp = 0; + + if ( p_Inp->sp_periodicity ) + { + if ( !(p_Inp->PreferDispOrder) ) // coding order + { + if ( (curr_frame - p_seq_struct->last_sp_frame) % p_Inp->sp_periodicity == 0 ) + { + is_sp = 1; + } + } + else // display order (insertion) + { + int idx; + int offset; + PredStructAtom *p_cur_gop; + + // test each random access prediction structure, staring from the longest one + // if random access is established then break from this loop and prepare output parameter + for ( idx = (p_seq_struct->num_gops - 1); idx >= 0; idx-- ) + { + p_cur_gop = p_seq_struct->p_gop + idx; + // check if length of structure overflows the available frame number + if ( sim ) + { + if ( (curr_frame + p_cur_gop->length) > p_Inp->no_frames ) + { + continue; + } + } + else + { + if ( p_cur_gop->length > avail_frames ) + { + continue; + } + } + + // recall that frame "0" is the random access frame in those prediction structures + offset = p_cur_gop->p_frm[0].disp_offset; + + // now test if random access conditions are satisfied + if ( (curr_frame + offset - p_seq_struct->last_sp_disp) % p_Inp->sp_periodicity == 0 ) + { + is_sp = 1 + idx; + break; + } + } + } + } + + return is_sp; +} + +/*! + *********************************************************************** + * \brief + * Find closest fixed frame (IDR, intra, or slice set through some pre-analysis tool) + * \param p_Inp + * pointer to the InputParameters structure + * \param p_seq_struct + * pointer to the sequence structure + * \param curr_frame + * coding order of the current frame + * \param avail_frames + * frames available for population + * \return + * returns the coding order of the first "fixed" frame, if none then -1 + *********************************************************************** + */ + +static int get_fixed_frame( InputParameters *p_Inp, SeqStructure *p_seq_struct, int curr_frame, int avail_frames ) +{ + int idx = 0; + int is_rnd_acc = 0; + int is_intra = 0; + int is_sp = 0; + + while ( idx < avail_frames ) + { + is_rnd_acc = establish_random_access( p_Inp, p_seq_struct, curr_frame + idx, avail_frames - idx, 1 ); + is_intra = establish_intra( p_Inp, p_seq_struct, curr_frame + idx, avail_frames - idx, 1 ); + is_sp = establish_sp( p_Inp, p_seq_struct, curr_frame + idx, avail_frames - idx, 1 ); + if ( is_rnd_acc || is_intra || is_sp ) + { + return idx; + } + idx++; + } + // signal that there is no fixed frame in the processed frames buffer (functions have to interpret the negative output here) + return -1; +} + +/*! + *********************************************************************** + * \brief + * Find closest fixed frame (IDR, intra, or slice set through some pre-analysis tool) + * for regular prediction structures and then try place the longest possible structure... + * \param p_Inp + * pointer to the InputParameters structure + * \param p_seq_struct + * pointer to the sequence structure + * \param curr_frame + * coding order of the current frame + * \param avail_frames + * frames available for population + * \return + * returns the coding order of the first "fixed" frame, if none then -1 + *********************************************************************** + */ + +static int get_fixed_frame_for_prd( InputParameters *p_Inp, SeqStructure *p_seq_struct, int curr_frame, int avail_frames ) +{ + int idx = 0; + int is_rnd_acc = 0; + int is_intra = 0; + int is_sp = 0; + PredStructAtom *p_longest_prd = p_seq_struct->p_prd + p_seq_struct->num_prds - 1; + + while ( idx < avail_frames ) + { + // IDRs + is_rnd_acc = establish_random_access( p_Inp, p_seq_struct, curr_frame + idx, avail_frames - idx, 1 ); + if ( is_rnd_acc ) + { + if ( p_Inp->PreferDispOrder && !(p_Inp->EnableIDRGOP) ) + { + int gop_length; + PredStructAtom *p_curr_gop; + + // decode is_intra + if ( is_rnd_acc > 1 ) + { + p_curr_gop = p_seq_struct->p_gop + is_rnd_acc - 1; + gop_length = p_curr_gop->length; + } + else + { + gop_length = 1; + } + // consider the case where even though there were enough available frames to a large structure, these slots + // instead were allocated to a very long intra structure, which reduces compression efficiency + if ( (idx % p_longest_prd->length) < p_longest_prd->length ) + { + p_seq_struct->pop_flag = POP_IDR; + return ( imin( p_longest_prd->length, idx + gop_length - 1 ) ); + } + else + { + return idx; + } + } + else + { + return idx; + } + } + // Intras + is_intra = establish_intra( p_Inp, p_seq_struct, curr_frame + idx, avail_frames - idx, 1 ); + if ( is_intra ) + { + if ( p_Inp->PreferDispOrder ) + { + int gop_length; + PredStructAtom *p_curr_gop; + + // decode is_intra + if ( is_intra > 1 ) + { + p_curr_gop = p_seq_struct->p_intra_gop + is_intra - 1; + gop_length = p_curr_gop->length; + } + else + { + gop_length = 1; + } + // consider the case where even though there were enough available frames to a large structure, these slots + // instead were allocated to a very long intra structure, which reduces compression efficiency + if ( (idx % p_longest_prd->length) < p_longest_prd->length ) + { + p_seq_struct->pop_flag = POP_INTRA; + return ( imin( p_longest_prd->length, idx + gop_length - 1 ) ); + } + else + { + return idx; + } + } + else + { + return idx; + } + } + // SP/SIs + is_sp = establish_sp( p_Inp, p_seq_struct, curr_frame + idx, avail_frames - idx, 1 ); + if ( is_sp ) + { + if ( p_Inp->PreferDispOrder && !(p_Inp->EnableIDRGOP) ) + { + int gop_length; + PredStructAtom *p_curr_gop; + + // decode is_intra + if ( is_sp > 1 ) + { + p_curr_gop = p_seq_struct->p_gop + is_sp - 1; + gop_length = p_curr_gop->length; + } + else + { + gop_length = 1; + } + // consider the case where even though there were enough available frames to a large structure, these slots + // instead were allocated to a very long intra structure, which reduces compression efficiency + if ( (idx % p_longest_prd->length) < p_longest_prd->length ) + { + p_seq_struct->pop_flag = POP_SP; + return ( imin( p_longest_prd->length, idx + gop_length - 1 ) ); + } + else + { + return idx; + } + } + else + { + return idx; + } + } + idx++; + } + // signal that there is no fixed frame in the processed frames buffer (functions have to interpret the negative output here) + return -1; +} + +/*! + *********************************************************************** + * \brief + * Check if "number" is a power of two + * \param number + * input value + * \return + * 1 if power of two, 0 otherwise + *********************************************************************** + */ + +static inline int check_power_of_two( int number ) +{ + return (((~((~number) + 1)) & number ) == 0) ? 1 : 0; +} + +/*! + *********************************************************************** + * \brief + * Select index to pred structure + * \param p_Inp + * pointer to the InputParameters structure + * \param p_seq_struct + * pointer to the sequence structure + * \param num_frames + * frames available for population + * \return + * returns the index to the selected prediction structure + *********************************************************************** + */ + +static int get_prd_index( InputParameters *p_Inp, SeqStructure *p_seq_struct, int num_frames ) +{ + int curr_prd; + PredStructAtom *p_cur_prd; + + // allocate atoms + for ( curr_prd = (p_seq_struct->num_prds - 1); curr_prd >= 0; curr_prd-- ) + { + p_cur_prd = p_seq_struct->p_prd + curr_prd; + if ( p_Inp->PreferPowerOfTwo && curr_prd != (p_seq_struct->num_prds - 1) && !check_power_of_two( p_cur_prd->length ) ) + { + continue; + } + if ( p_cur_prd->length <= num_frames ) + { + return curr_prd; + } + } + return 0; +} + +/*! + *********************************************************************** + * \brief + * Select index to random access structure + * \param p_Inp + * pointer to the InputParameters structure + * \param p_seq_struct + * pointer to the sequence structure + * \param num_frames + * frames available for population + * \return + * returns the index to the selected prediction structure + *********************************************************************** + */ + +static int get_idr_index( InputParameters *p_Inp, SeqStructure *p_seq_struct, int num_frames ) +{ + int curr_gop; + PredStructAtom *p_cur_gop; + + // allocate atoms + for ( curr_gop = (p_seq_struct->num_gops - 1); curr_gop >= 0; curr_gop-- ) + { + p_cur_gop = p_seq_struct->p_gop + curr_gop; + if ( p_Inp->PreferPowerOfTwo && curr_gop != (p_seq_struct->num_gops - 1) && !check_power_of_two( p_cur_gop->length ) ) + { + continue; + } + if ( p_cur_gop->length <= num_frames ) + { + return curr_gop; + } + } + return 0; +} + +/*! + *********************************************************************** + * \brief + * Select index to intra structure + * \param p_Inp + * pointer to the InputParameters structure + * \param p_seq_struct + * pointer to the sequence structure + * \param num_frames + * frames available for population + * \return + * returns the index to the selected prediction structure + *********************************************************************** + */ + +static int get_intra_index( InputParameters *p_Inp, SeqStructure *p_seq_struct, int num_frames ) +{ + int curr_gop; + PredStructAtom *p_cur_gop; + + // allocate atoms + for ( curr_gop = (p_seq_struct->num_intra_gops - 1); curr_gop >= 0; curr_gop-- ) + { + p_cur_gop = p_seq_struct->p_intra_gop + curr_gop; + if ( p_Inp->PreferPowerOfTwo && curr_gop != (p_seq_struct->num_intra_gops - 1) && !check_power_of_two( p_cur_gop->length ) ) + { + continue; + } + if ( p_cur_gop->length <= num_frames ) + { + return curr_gop; + } + } + return 0; +} + +/*! + *********************************************************************** + * \brief + * Populate a PredStructFrm object + * \param p_dst + * Pointer to the object to be populated with the args of this func. + *********************************************************************** + */ + +static inline void pop_pred_struct_frm( PredStructFrm *p_dst, int slice_type, int nal_ref_idc, int disp_offset, int layer, int slice_qp_off, + int random_access, int temporal_layer ) +{ + p_dst->slice_type = slice_type; + p_dst->nal_ref_idc = nal_ref_idc; + p_dst->disp_offset = disp_offset; + p_dst->layer = layer; + p_dst->slice_qp_off = slice_qp_off; + p_dst->random_access = random_access; + p_dst->temporal_layer = temporal_layer; +} + +/*! + *********************************************************************** + * \brief + * Create random access prediction structures + * \param p_Inp + * pointer to the InputParameters structure + * \param p_seq_struct + * pointer to the sequence structure + * \param memory_size + * frames available for population + * \param is_idr + * initialize an IDR GOP list as opposed to an Intra GOP list + * \return + * returns the index to the selected prediction structure + *********************************************************************** + */ + +static void init_gop_struct( InputParameters *p_Inp, SeqStructure *p_seq_struct, int is_idr, int *memory_size ) +{ + int idx; + int length; + PredStructAtom *p_cur_gop, *p_cur_prd, *p_cur_prd_tail = NULL, *p_gop; + PredStructFrm *p_dst, *p_src; + + if ( is_idr ) + { + // derive number of gop atoms + if ( p_Inp->idr_period == 1 || p_Inp->intra_period == 1 ) + { + p_seq_struct->num_gops = 1; + } + else if ( p_Inp->intra_delay && !p_Inp->EnableIDRGOP ) + { + p_seq_struct->num_gops = 3; + } + else if ( p_Inp->intra_delay || !p_Inp->EnableIDRGOP ) + { + p_seq_struct->num_gops = !p_Inp->EnableIDRGOP ? p_seq_struct->num_prds : 2; + } + else + { + p_seq_struct->num_gops = !p_Inp->EnableIDRGOP ? p_seq_struct->num_prds : 1; + } + + // we assume that intra_delay is ALWAYS lesser than the idr_period + if ( p_Inp->idr_period ) + { + assert( p_Inp->intra_delay < p_Inp->idr_period ); + } + + p_seq_struct->p_gop = (PredStructAtom *)malloc( p_seq_struct->num_gops * sizeof( PredStructAtom ) ); + *memory_size += ( p_seq_struct->num_gops * sizeof( PredStructAtom ) ); + + p_gop = p_seq_struct->p_gop; + } + else // intra GOP + { + // derive number of gop atoms + if ( p_Inp->intra_period == 1 ) + { + p_seq_struct->num_intra_gops = 1; + } + else if ( p_Inp->intra_delay ) + { + p_seq_struct->num_intra_gops = 3; + } + else + { + p_seq_struct->num_intra_gops = p_seq_struct->num_prds; + } + + // we assume that intra_delay is ALWAYS lesser than the intra_period + if ( p_Inp->intra_period ) + { + assert( p_Inp->intra_delay < p_Inp->intra_period ); + } + + p_seq_struct->p_intra_gop = (PredStructAtom *)malloc( p_seq_struct->num_intra_gops * sizeof( PredStructAtom ) ); + *memory_size += ( p_seq_struct->num_intra_gops * sizeof( PredStructAtom ) ); + + p_gop = p_seq_struct->p_intra_gop; + } + + // default IDR GOP + length = 1; + p_cur_gop = p_gop; + p_cur_gop->length = length; + p_cur_gop->p_frm = (PredStructFrm *)malloc( length * sizeof( PredStructFrm ) ); + *memory_size += length * sizeof( PredStructFrm ); + + // initialize atoms + p_cur_gop = p_gop; + p_dst = p_cur_gop->p_frm + 0; + pop_pred_struct_frm( p_dst, I_SLICE, NALU_PRIORITY_HIGHEST, 0, 0, 0, 1, 0 ); + + // intra-delayed IDR GOP + if ( p_Inp->intra_delay && p_Inp->intra_period != 1 && p_Inp->idr_period != 1 ) + { + int proc_frames = 0; + int start_frame = p_Inp->intra_delay - 1; + int idr_idx = 0; + int curr_prd; + + // allocate atoms + length = p_Inp->intra_delay + 1; + p_cur_gop = p_gop + 1; + p_cur_gop->length = length; + p_cur_gop->gop_levels = 1; + p_cur_gop->p_frm = (PredStructFrm *)malloc( length * sizeof( PredStructFrm ) ); + *memory_size += length * sizeof( PredStructFrm ); + + // IDR picture + p_cur_gop = p_gop + 1; + p_dst = p_cur_gop->p_frm + idr_idx; + pop_pred_struct_frm( p_dst, I_SLICE, NALU_PRIORITY_HIGHEST, p_Inp->intra_delay, 0, 0, 1, 0 ); + + idr_idx++; + + while ( proc_frames < p_Inp->intra_delay ) + { + // given the available slots select the index to the pred structure to use for current segment + curr_prd = get_prd_index( p_Inp, p_seq_struct, p_Inp->intra_delay - proc_frames ); + p_cur_prd = p_seq_struct->p_prd + curr_prd; + // set gop_levels + p_cur_gop->gop_levels = p_cur_prd->gop_levels; + // populate gop structure from selected structure + for ( idx = 0; idx < p_cur_prd->length; idx++, idr_idx++, proc_frames++ ) + { + p_dst = p_cur_gop->p_frm + idr_idx; + p_src = p_cur_prd->p_frm + idx; + + pop_pred_struct_frm( p_dst, p_src->slice_type, p_src->nal_ref_idc, start_frame - p_src->disp_offset, p_src->layer, + p_src->slice_qp_off, 0, p_src->temporal_layer ); + } + // update start_frame + start_frame -= p_cur_prd->length; + } + + // a separate case + if ( !p_Inp->EnableIDRGOP || !is_idr ) // an additional atom: the longest one + { + proc_frames = 0; + start_frame = p_Inp->intra_delay - 1; + idr_idx = 0; + + // longest structure + p_cur_prd_tail = p_seq_struct->p_prd + p_seq_struct->num_prds - 1; + + // allocate atoms + length = p_Inp->intra_delay + 1 + (p_cur_prd_tail->length - 1); + p_cur_gop = p_gop + 2; + p_cur_gop->length = length; + p_cur_gop->gop_levels = 1; + p_cur_gop->p_frm = (PredStructFrm *)malloc( length * sizeof( PredStructFrm ) ); + *memory_size += length * sizeof( PredStructFrm ); + idr_idx = 0; + + // IDR picture + p_cur_gop = p_gop + 2; + p_dst = p_cur_gop->p_frm + idr_idx; + pop_pred_struct_frm( p_dst, I_SLICE, NALU_PRIORITY_HIGHEST, p_Inp->intra_delay, 0, 0, 1, 0 ); + + idr_idx++; + + while ( proc_frames < p_Inp->intra_delay ) + { + // given the available slots select the index to the pred structure to use for current segment + curr_prd = get_prd_index( p_Inp, p_seq_struct, p_Inp->intra_delay - proc_frames ); + p_cur_prd = p_seq_struct->p_prd + curr_prd; + // set gop_levels + p_cur_gop->gop_levels = p_cur_prd->gop_levels; + // populate gop structure from selected structure + for ( idx = 0; idx < p_cur_prd->length; idx++, idr_idx++, proc_frames++ ) + { + p_dst = p_cur_gop->p_frm + idr_idx; + p_src = p_cur_prd->p_frm + idx; + + pop_pred_struct_frm( p_dst, p_src->slice_type, p_src->nal_ref_idc, start_frame - p_src->disp_offset, + p_src->layer, p_src->slice_qp_off, 0, p_src->temporal_layer ); + } + // update start_frame + start_frame -= p_cur_prd->length; + } + + // add an offset to the display offsets + for ( idr_idx = 0; idr_idx < (p_Inp->intra_delay + 1); idr_idx++ ) + { + p_cur_gop->p_frm[idr_idx].disp_offset += (p_cur_prd_tail->length - 1); + } + p_cur_gop->gop_levels = imax( p_cur_gop->gop_levels, p_cur_prd_tail->gop_levels ); + // and now place the prd struct *before* them in display order (set idx to 1 to avoid copying the I/P frame, we only seek the hierarchy if it exists) + for ( idx = 1; idx < p_cur_prd_tail->length; idx++, idr_idx++ ) + { + p_dst = p_cur_gop->p_frm + idr_idx; + p_src = p_cur_prd_tail->p_frm + idx; + + pop_pred_struct_frm( p_dst, p_src->slice_type, p_src->nal_ref_idc, p_src->disp_offset, + p_src->layer, p_src->slice_qp_off, 0, p_src->temporal_layer ); + } + } + } + else if ( !(p_Inp->intra_delay) && ((!p_Inp->EnableIDRGOP || !is_idr) && p_Inp->intra_period != 1 && p_Inp->idr_period != 1) ) + { + int gop_idx; + int idr_idx = 0; + + for ( gop_idx = 1; gop_idx < p_seq_struct->num_prds; gop_idx++ ) + { + // set structure + p_cur_prd = p_seq_struct->p_prd + gop_idx; + + // allocate atoms + length = p_cur_prd->length; + p_cur_gop = p_gop + gop_idx; + p_cur_gop->length = length; + p_cur_gop->p_frm = (PredStructFrm *)malloc( length * sizeof( PredStructFrm ) ); + *memory_size += length * sizeof( PredStructFrm ); + idr_idx = 0; + + // IDR picture + p_cur_gop->gop_levels = p_cur_prd->gop_levels; + p_dst = p_cur_gop->p_frm + idr_idx; + pop_pred_struct_frm( p_dst, I_SLICE, NALU_PRIORITY_HIGHEST, p_cur_prd->p_frm[0].disp_offset, 0, 0, 1, 0 ); + idr_idx++; + + // populate gop structure from selected structure + for ( idx = 1; idx < p_cur_prd->length; idx++, idr_idx++ ) + { + p_dst = p_cur_gop->p_frm + idr_idx; + p_src = p_cur_prd->p_frm + idx; + + pop_pred_struct_frm( p_dst, p_src->slice_type, p_src->nal_ref_idc, p_src->disp_offset, + p_src->layer, p_src->slice_qp_off, 0, p_src->temporal_layer ); + } + } // gop_idx + } +} + +/*! + *********************************************************************** + * \brief + * Create non-random access prediction structures + * \param p_Vid + * pointer to the VideoParameters structure + * \param p_Inp + * pointer to the InputParameters structure + * \param p_seq_struct + * pointer to the sequence structure + * \param memory_size + * frames available for population + *********************************************************************** + */ + +static void init_pred_struct( VideoParameters *p_Vid, InputParameters *p_Inp, SeqStructure *p_seq_struct, int *memory_size ) +{ + int curr_prd, idx; + int frm, sum, layer, order; + int length, length2, pred_mode; + PredStructAtom *p_cur_prd; + PredStructFrm *p_dst; + + p_seq_struct->num_prds = (p_Inp->idr_period == 1 || p_Inp->intra_period == 1) ? 1 : (p_Inp->NumberBFrames + 1); + + p_seq_struct->p_prd = (PredStructAtom *)malloc( p_seq_struct->num_prds * sizeof( PredStructAtom ) ); + *memory_size += p_seq_struct->num_prds * sizeof( PredStructAtom ); + + // allocate atoms + for ( curr_prd = (p_seq_struct->num_prds - 1); curr_prd >= 0; curr_prd-- ) + { + // length of prediction structure + length = curr_prd + 1; + + p_cur_prd = p_seq_struct->p_prd + curr_prd; + p_cur_prd->length = length; + p_cur_prd->p_frm = (PredStructFrm *)malloc( length * sizeof( PredStructFrm ) ); + *memory_size += length * sizeof( PredStructFrm ); + } + + // initialize atoms + for ( curr_prd = (p_seq_struct->num_prds - 1); curr_prd >= 0; curr_prd-- ) + { + // length of prediction structure + length = curr_prd + 1; + + switch ( p_Inp->HierarchicalCoding ) + { + default: + case 0: + pred_mode = 0; + break; + case 1: + pred_mode = 1; + break; + case 2: + // check if power of two + pred_mode = check_power_of_two( length ) ? 2 : ( length > 4 ? 1 : 0 ); + break; + case 3: + // check again if power of two + if ( p_Inp->LowDelay ) + { + pred_mode = 3; + } + else + { + pred_mode = (length == (p_Inp->NumberBFrames + 1)) ? 3 : (check_power_of_two( length ) ? 2 : ( length > 4 ? 1 : 0 )); + } + break; + } + + p_cur_prd = p_seq_struct->p_prd + curr_prd; + p_cur_prd->gop_levels = 0; + switch ( pred_mode ) + { + default: + case 0: // disposable B-pics of equal priority + p_dst = p_cur_prd->p_frm + 0; + + p_dst->slice_type = ( p_Inp->BRefPictures == 2 ) ? B_SLICE : P_SLICE; + p_dst->nal_ref_idc = NALU_PRIORITY_HIGH; + p_dst->disp_offset = length - 1; + p_dst->layer = 0; + p_dst->slice_qp_off = ( p_Inp->BRefPictures == 2 ) ? (p_Inp->qp[P_SLICE] - p_Inp->qp[B_SLICE]) : 0; // ensure all types of frames have identical QP (modifiers) + p_dst->random_access = 0; + p_dst->temporal_layer = 0; + + for ( idx = 1; idx < length; idx++ ) + { + p_dst = p_cur_prd->p_frm + idx; + + p_dst->slice_type = ( p_Inp->PReplaceBSlice ) ? P_SLICE : B_SLICE; + p_dst->nal_ref_idc = ( p_Inp->BRefPictures == 1 && p_dst->slice_type == B_SLICE ) ? NALU_PRIORITY_LOW : NALU_PRIORITY_DISPOSABLE; + p_dst->disp_offset = idx - 1; + p_dst->layer = 2; + p_dst->slice_qp_off = ( p_Inp->PReplaceBSlice ) ? (p_Inp->qp[B_SLICE] - p_Inp->qp[P_SLICE]) : (p_dst->nal_ref_idc ? p_Inp->qpBRSOffset : 0); //2; 2 when depends on QPPSlice (highest layer QP) + p_dst->random_access = 0; + p_dst->temporal_layer = 0; + } + if ( length > 1 ) + { + p_cur_prd->gop_levels = 1; + } + break; + case 1: // even/odd hierarchy + length2 = ((length - 1) >> 1) + 1; + + p_dst = p_cur_prd->p_frm + 0; + + p_dst->slice_type = ( p_Inp->BRefPictures == 2 ) ? B_SLICE : P_SLICE; + p_dst->nal_ref_idc = NALU_PRIORITY_HIGH; + p_dst->disp_offset = length - 1; + p_dst->layer = 0; + p_dst->slice_qp_off = ( p_Inp->BRefPictures == 2 ) ? (p_Inp->qp[B_SLICE] - p_Inp->qp[P_SLICE]) : 0; // ensure all types of frames have identical QP (modifiers) + p_dst->random_access = 0; + p_dst->temporal_layer = 0; + + for ( idx = 1; idx < length2; idx++ ) + { + p_dst = p_cur_prd->p_frm + idx; + + p_dst->slice_type = ( p_Inp->PReplaceBSlice ) ? P_SLICE : B_SLICE; + p_dst->nal_ref_idc = NALU_PRIORITY_LOW; + p_dst->disp_offset = 2*(idx - 1) + 1; + p_dst->layer = 1; + p_dst->slice_qp_off = p_dst->slice_type == B_SLICE ? + (p_Inp->HierarchyLevelQPEnable ? -1: p_Inp->qpBRSOffset) : (p_Inp->HierarchyLevelQPEnable ? 1 : 0); // 1 + p_dst->random_access = 0; + p_dst->temporal_layer = 0; + } + for ( idx = length2; idx < length; idx++ ) + { + p_dst = p_cur_prd->p_frm + idx; + + p_dst->slice_type = ( p_Inp->PReplaceBSlice ) ? P_SLICE : B_SLICE; + p_dst->nal_ref_idc = NALU_PRIORITY_DISPOSABLE; + p_dst->disp_offset = (idx - length2) * 2; + p_dst->layer = 2; + p_dst->slice_qp_off = p_dst->slice_type == B_SLICE ? 0 : 2;//2; + p_dst->random_access = 0; + p_dst->temporal_layer = 0; + } + if ( length > 1 ) + { + p_cur_prd->gop_levels = 2; + } + break; + case 2: // full binary decomposition + p_dst = p_cur_prd->p_frm + 0; + + p_dst->slice_type = ( p_Inp->BRefPictures == 2 ) ? B_SLICE : P_SLICE; + p_dst->nal_ref_idc = NALU_PRIORITY_HIGH; + p_dst->disp_offset = length - 1; + p_dst->layer = 0; + p_dst->slice_qp_off = ( p_Inp->BRefPictures == 2 ) ? (p_Inp->qp[B_SLICE] - p_Inp->qp[P_SLICE]) : 0; // ensure all types of frames have identical QP (modifiers) + p_dst->random_access = 0; + p_dst->temporal_layer = 0; + + if ( length > 3 ) + { + int GOPlevels = 1; + + // determine number of levels + while ((length >> GOPlevels) > 1) + { + GOPlevels ++; + } + p_cur_prd->gop_levels = GOPlevels; + + // hierarchical layer (from highest priority to lowest priority) + for ( layer = 1, order = 1, sum = 0, frm = 1; sum < (length - 1); frm = (frm << 1), sum += frm, layer++ ) + { + // layer frames (in *coding* order) + for ( idx = 0; idx < frm; idx++, order++ ) + { + p_dst = p_cur_prd->p_frm + order; + + p_dst->slice_type = ( p_Inp->PReplaceBSlice ) ? P_SLICE : B_SLICE; + p_dst->nal_ref_idc = (frm == (length >> 1)) ? NALU_PRIORITY_DISPOSABLE : NALU_PRIORITY_LOW; + p_dst->disp_offset = ((length - 1) >> layer) + idx * (length >> (layer - 1)); + p_dst->layer = layer; + p_dst->slice_qp_off = p_dst->slice_type == B_SLICE ? + (p_Inp->HierarchyLevelQPEnable ? (-GOPlevels + layer): (p_dst->nal_ref_idc != NALU_PRIORITY_DISPOSABLE ? p_Inp->qpBRSOffset : 0)) : (p_Inp->HierarchyLevelQPEnable ? layer : 0); // layer + p_dst->random_access = 0; + p_dst->temporal_layer = 0; + } + } + } + else + { + for ( idx = 1; idx < length; idx++ ) + { + p_dst = p_cur_prd->p_frm + idx; + + p_dst->slice_type = ( p_Inp->PReplaceBSlice ) ? P_SLICE : B_SLICE; + p_dst->nal_ref_idc = NALU_PRIORITY_DISPOSABLE; + p_dst->disp_offset = idx - 1; + p_dst->layer = 2; + p_dst->slice_qp_off = 0; //2; + p_dst->random_access = 0; + p_dst->temporal_layer = 0; + } + if ( length > 1 ) + { + p_cur_prd->gop_levels = 1; + } + } + break; + case 3: // explicit structure + { + int max_layer = 0; + + // encode without delay + if ( p_Inp->LowDelay ) + { + for ( idx = 0; idx < (length - 1); idx++ ) + { + p_dst = p_cur_prd->p_frm + idx; + + p_dst->slice_type = p_Vid->gop_structure[idx].slice_type; + p_dst->nal_ref_idc = p_Vid->gop_structure[idx].reference_idc; + p_dst->disp_offset = p_Vid->gop_structure[idx].display_no; + p_dst->layer = ((p_Vid->gop_structure[idx].hierarchy_layer == 1) ? 0 : 1) + 1; // invert in new notation + p_dst->slice_qp_off = p_Vid->gop_structure[idx].slice_qp_off; + p_dst->random_access = 0; + + p_dst->temporal_layer = p_Vid->gop_structure[idx].temporal_layer; + + if ( p_Vid->gop_structure[idx - 1].hierarchy_layer > max_layer ) + { + max_layer = p_Vid->gop_structure[idx - 1].hierarchy_layer; + } + } + p_dst = p_cur_prd->p_frm + idx; + p_dst->slice_type = ( p_Inp->BRefPictures == 2 ) ? B_SLICE : P_SLICE; + p_dst->nal_ref_idc = NALU_PRIORITY_HIGH; + p_dst->disp_offset = length - 1; + p_dst->layer = 0; + p_dst->slice_qp_off = ( p_Inp->BRefPictures == 2 ) ? (p_Inp->qp[B_SLICE] - p_Inp->qp[P_SLICE]) : 0; // ensure all types of frames have identical QP (modifiers) + p_dst->random_access = 0; + + p_dst->temporal_layer = 0; + } + else + { + p_dst = p_cur_prd->p_frm + 0; + + p_dst->slice_type = ( p_Inp->BRefPictures == 2 ) ? B_SLICE : P_SLICE; + p_dst->nal_ref_idc = NALU_PRIORITY_HIGH; + p_dst->disp_offset = length - 1; + p_dst->layer = 0; + p_dst->slice_qp_off = ( p_Inp->BRefPictures == 2 ) ? (p_Inp->qp[B_SLICE] - p_Inp->qp[P_SLICE]) : 0; // ensure all types of frames have identical QP (modifiers) + p_dst->random_access = 0; + p_dst->temporal_layer = 0; + + for ( idx = 1; idx < length; idx++ ) + { + p_dst = p_cur_prd->p_frm + idx; + + p_dst->slice_type = p_Vid->gop_structure[idx - 1].slice_type; + p_dst->nal_ref_idc = p_Vid->gop_structure[idx - 1].reference_idc; + p_dst->disp_offset = p_Vid->gop_structure[idx - 1].display_no; + p_dst->layer = ((p_Vid->gop_structure[idx - 1].hierarchy_layer == 1) ? 0 : 1) + 1; // invert in new notation + p_dst->slice_qp_off = p_Vid->gop_structure[idx - 1].slice_qp_off; + p_dst->random_access = 0; + p_dst->temporal_layer = 0; + + if ( p_Vid->gop_structure[idx - 1].hierarchy_layer > max_layer ) + { + max_layer = p_Vid->gop_structure[idx - 1].hierarchy_layer; + } + } + } + p_cur_prd->gop_levels = max_layer + 1; + } + break; + } + } +} + +/*! + *********************************************************************** + * \brief + * Populate frame structure + * \param p_Vid + * pointer to the VideoParameters structure + * \param p_Inp + * pointer to the InputParameters structure + * \param p_seq_struct + * pointer to the sequence structure + * \param num_to_populate + * number of frame unit structures to populate + *********************************************************************** + */ + +void populate_frm_struct( VideoParameters *p_Vid, InputParameters *p_Inp, SeqStructure *p_seq_struct, int num_to_populate, int init_frames_to_code ) +{ + // case 5: read explicit seq info and store it in the frm_struct + int curr_frame; // in coding order + int proc_frames; + int is_idr; + int is_intra; + int is_sp; + int terminate_pop = 0; + + p_seq_struct->curr_num_to_populate = imin( num_to_populate, init_frames_to_code - p_seq_struct->pop_start_frame ); + curr_frame = p_seq_struct->pop_start_frame; + proc_frames = 0; + + while ( proc_frames < p_seq_struct->curr_num_to_populate ) + { + is_idr = 0; + is_intra = 0; + is_sp = 0; + + // we have to check here for inserted frames (e.g. due to pre-analysis) and assign them to the structure (future work) + + // establish if it is an IDR frame + is_idr = establish_random_access( p_Inp, p_seq_struct, curr_frame, p_seq_struct->curr_num_to_populate - proc_frames, 0 ); + if ( is_idr && p_seq_struct->pop_flag != POP_INTRA && p_seq_struct->pop_flag != POP_SP ) + { + // random access structure + int gop_frame = populate_rnd_acc_pred_struct_atom( p_Vid, p_Inp, p_seq_struct, curr_frame, proc_frames, &terminate_pop, is_idr, 0 ); + // update counters + curr_frame += gop_frame; + proc_frames += gop_frame; + if ( terminate_pop ) + { + terminate_pop = 0; + break; // the proc_frames main loop + } + } + else // INTRA frames check (non-IDR) + { + is_intra = establish_intra( p_Inp, p_seq_struct, curr_frame, p_seq_struct->curr_num_to_populate - proc_frames, 0 ); + if ( is_intra && p_seq_struct->pop_flag != POP_SP ) + { + // intra structure (not random access) + int gop_frame = populate_rnd_acc_pred_struct_atom( p_Vid, p_Inp, p_seq_struct, curr_frame, proc_frames, &terminate_pop, is_intra, 1 ); + // update counters + proc_frames += gop_frame; + curr_frame += gop_frame; + if ( terminate_pop ) + { + terminate_pop = 0; + break; // the proc_frames main loop + } + } + else // SP frames check + { + is_sp = establish_sp( p_Inp, p_seq_struct, curr_frame, p_seq_struct->curr_num_to_populate - proc_frames, 0 ); + if ( is_sp ) + { + // intra structure (not random access) + int gop_frame = populate_rnd_acc_pred_struct_atom( p_Vid, p_Inp, p_seq_struct, curr_frame, proc_frames, &terminate_pop, is_sp, 2 ); + // update counters + proc_frames += gop_frame; + curr_frame += gop_frame; + if ( terminate_pop ) + { + terminate_pop = 0; + break; // the proc_frames main loop + } + } + } + } + + if ( !is_idr && !is_intra && !is_sp ) + { + int pred_frame = populate_reg_pred_struct_atom( p_Vid, p_Inp, p_seq_struct, curr_frame, proc_frames, &terminate_pop ); + // update counters + curr_frame += pred_frame; + proc_frames += pred_frame; + if ( terminate_pop ) + { + terminate_pop = 0; + break; // the proc_frames main loop + } + } // is_idr is_intra is_sp + } // proc_frames loop + // update pop_start_frame for subsequent run of this function ;) + p_seq_struct->pop_start_frame = curr_frame; +} + +/*! + *********************************************************************** + * \brief + * Set parameters for a single coded picture (frame/field) + * \param p_Inp + * pointer to the InputParameters structure + * \param p_pic + * pointer to the current picture structure + * \param p_frm_struct + * pointer to the current frame unit structure + * \param num_slices + * number of slices for the current picture + * \param is_bot_fld + * bottom field flag + *********************************************************************** + */ + +void populate_reg_pic( InputParameters *p_Inp, PicStructure *p_pic, FrameUnitStruct *p_frm_struct, int num_slices, int is_bot_fld ) +{ + int slice; + int slice_type = (is_bot_fld && p_frm_struct->type == I_SLICE) ? ( p_Inp->IntraBottom ? I_SLICE : (p_Inp->BRefPictures == 2 ? B_SLICE : P_SLICE) ) : p_frm_struct->type; + int deblock = p_Inp->DFSendParameters ? + (!(p_Inp->DFDisableIdc[p_frm_struct->nal_ref_idc][p_frm_struct->type])) : 1; + int weighted_pred = (slice_type == P_SLICE) ? + p_Inp->WeightedPrediction : ((slice_type == B_SLICE) ? + p_Inp->WeightedBiprediction : 0); + SliceStructure *p_Slice; + + // if bottom field picture idr_flag has to be 0 (even when an IDR top field pic precedes it) + p_pic->idr_flag = !is_bot_fld ? p_frm_struct->idr_flag : 0; + p_pic->random_access = !is_bot_fld ? p_frm_struct->random_access : 0; + p_pic->nal_ref_idc = p_frm_struct->nal_ref_idc; + p_pic->num_slices = num_slices; + + for ( slice = 0; slice < p_pic->num_slices; slice++ ) + { + // Slice Structure + p_Slice = p_pic->p_Slice + slice; + + p_Slice->type = slice_type; + p_Slice->deblock = deblock; + p_Slice->weighted_pred = weighted_pred; + p_Slice->num_refs = p_frm_struct->num_refs; + p_Slice->qp = p_frm_struct->qp; + } +} + +/*! + *********************************************************************** + * \brief + * Set parameters for a single frame unit + * \param p_Inp + * pointer to the InputParameters structure + * \param p_seq_struct + * pointer to the sequence structure + * \param p_frm_struct + * pointer to the current frame unit structure + * \param p_cur_prd + * pointer to the current prediction structure + * \param curr_frame + * global (sequence-level) coding order of the first frame (in coding order) in the PredStructAtom + * \param pred_frame + * coding order with respect to "curr_frame" (pred_frame + curr_frame + idx yields the absolute coding order of the current frame) + * \param idx + * local (PredStructAtom-level) coding order with respect to the start of the current PredStructAtom + * \param num_slices + * number of slices for the current picture + * \param no_rnd_acc + * if 1 the first frame in the Atom is not a random access frame: Intra \n + * if 2 it is an SP frame + *********************************************************************** + */ + +static void populate_frame( InputParameters *p_Inp, SeqStructure *p_seq_struct, FrameUnitStruct *p_frm_struct, PredStructAtom *p_cur_prd, + int curr_frame, int pred_frame, int idx, int num_slices, int no_rnd_acc ) +{ + PredStructFrm *p_src = p_cur_prd->p_frm + idx; + + // Frame Unit Structure + p_frm_struct->type = p_src->slice_type; + p_frm_struct->random_access = p_src->random_access; + p_frm_struct->num_refs = p_Inp->num_ref_frames; + p_frm_struct->layer = p_src->layer; + p_frm_struct->mod_qp = p_src->slice_qp_off; + p_frm_struct->field_pic_flag = 0; // need to add better support for field coded pictures + p_frm_struct->frame_no = curr_frame + pred_frame + p_src->disp_offset; + + p_frm_struct->temporal_layer = p_src->temporal_layer; + + if ( !(curr_frame + pred_frame + idx) ) + { + // first frame in the sequence + p_frm_struct->type = I_SLICE; + p_frm_struct->idr_flag = 1; + p_frm_struct->nal_ref_idc = NALU_PRIORITY_HIGHEST; + } + // disposable P/B frames + else if ( p_Inp->DisposableP && p_frm_struct->type != I_SLICE && !p_frm_struct->layer ) // this way it will also apply to B_SLICEs in layer 0 + { + if ( !(p_seq_struct->last_bl_frm_disposable) ) + { + p_frm_struct->nal_ref_idc = NALU_PRIORITY_DISPOSABLE; + p_frm_struct->mod_qp += p_Inp->DispPQPOffset; + } + else + { + p_frm_struct->nal_ref_idc = p_src->nal_ref_idc; + } + p_frm_struct->idr_flag = (p_src->nal_ref_idc == NALU_PRIORITY_HIGHEST) ? 1 : 0; + } + else if ( no_rnd_acc == 1 && !idx ) // no random access frame and frame first in the struct (hence I_SLICE) + { + p_frm_struct->idr_flag = 0; + p_frm_struct->nal_ref_idc = NALU_PRIORITY_HIGH; + p_frm_struct->random_access = p_Inp->EnableOpenGOP ? 1 : 0; + } + else if ( no_rnd_acc == 2 && !idx ) // no random access frame and frame first in the struct (hence SP_SLICE) + { + p_frm_struct->type = p_Inp->si_frame_indicator ? SI_SLICE : SP_SLICE; + p_frm_struct->idr_flag = 0; + p_frm_struct->nal_ref_idc = NALU_PRIORITY_HIGH; + p_frm_struct->random_access = 0; + } + else + { + p_frm_struct->idr_flag = (p_src->nal_ref_idc == NALU_PRIORITY_HIGHEST) ? 1 : 0; + p_frm_struct->nal_ref_idc = p_src->nal_ref_idc; + } + // final QP (w/o RC) set here to ensure "mod_qp" and "type" have been *finalized* + p_frm_struct->qp = p_frm_struct->mod_qp + p_Inp->qp[p_frm_struct->type]; +#if (DEBUG_PRED_STRUCT) + fprintf( stdout, "\n\n Frame Population Stats for Frame %5d in coding order", curr_frame + pred_frame + idx ); + switch ( p_frm_struct->type ) + { + default: + case I_SLICE: + fprintf( stdout, "\n Slice Type: I_SLICE " ); + break; + case P_SLICE: + fprintf( stdout, "\n Slice Type: P_SLICE " ); + break; + case B_SLICE: + fprintf( stdout, "\n Slice Type: B_SLICE " ); + break; + case SP_SLICE: + fprintf( stdout, "\n Slice Type: SP_SLICE " ); + break; + case SI_SLICE: + fprintf( stdout, "\n Slice Type: SI_SLICE " ); + break; + } + fprintf( stdout, "\n IDR picture: %d ", p_frm_struct->idr_flag ); + switch( p_frm_struct->nal_ref_idc ) + { + default: + case NALU_PRIORITY_HIGHEST: + fprintf( stdout, "\n NAL_reference_idc: NALU_PRIORITY_HIGHEST "); + break; + case NALU_PRIORITY_HIGH: + fprintf( stdout, "\n NAL_reference_idc: NALU_PRIORITY_HIGH "); + break; + case NALU_PRIORITY_LOW: + fprintf( stdout, "\n NAL_reference_idc: NALU_PRIORITY_LOW "); + break; + case NALU_PRIORITY_DISPOSABLE: + fprintf( stdout, "\n NAL_reference_idc: NALU_PRIORITY_DISPOSABLE "); + break; + } + fprintf( stdout, "\n Random Access: %d ", p_frm_struct->random_access ); + fprintf( stdout, "\n Number of References: %2d ", p_frm_struct->num_refs ); + fprintf( stdout, "\n Hierarchy Layer: %d ", p_frm_struct->layer ); + fprintf( stdout, "\n QP Modifier: %2d ", p_frm_struct->qp ); + fprintf( stdout, "\n Field Coding: %d ", p_frm_struct->field_pic_flag ); + fprintf( stdout, "\n Display Order: %5d ", p_frm_struct->frame_no ); +#endif + + // store a pointer to the prediction structure + p_frm_struct->p_atom = p_cur_prd; + p_frm_struct->atom_idx = idx; +#if (MVC_EXTENSION_ENABLE) + p_frm_struct->view_id = 0; +#endif + + // Picture Structure + switch( p_Inp->PicInterlace ) + { + default: + case FRAME_CODING: + // frame + populate_reg_pic( p_Inp, p_frm_struct->p_frame_pic, p_frm_struct, num_slices, 0 ); + break; + case FIELD_CODING: + // top field + populate_reg_pic( p_Inp, p_frm_struct->p_top_fld_pic, p_frm_struct, num_slices, 0 ); + // bottom field + populate_reg_pic( p_Inp, p_frm_struct->p_bot_fld_pic, p_frm_struct, num_slices, 1 ); + break; + case ADAPTIVE_CODING: + // frame + populate_reg_pic( p_Inp, p_frm_struct->p_frame_pic, p_frm_struct, num_slices, 0 ); + // top field + populate_reg_pic( p_Inp, p_frm_struct->p_top_fld_pic, p_frm_struct, num_slices, 0 ); + // bottom field + populate_reg_pic( p_Inp, p_frm_struct->p_bot_fld_pic, p_frm_struct, num_slices, 1 ); + break; + } +} + +/*! + *********************************************************************** + * \brief + * Update frame indices + * \param p_seq_struct + * pointer to the sequence structure + * \param p_frm_struct + * pointer to the current frame unit structure + * \param curr_frame + * global (sequence-level) coding order of the first frame (in coding order) in the PredStructAtom + * \param pred_frame + * coding order with respect to "curr_frame" (pred_frame + curr_frame yields the absolute coding order of the current frame) + *********************************************************************** + */ + +static void update_frame_indices( SeqStructure *p_seq_struct, FrameUnitStruct *p_frm_struct, int curr_frame, int pred_frame ) +{ + if ( p_frm_struct->idr_flag ) + { + // coding order + p_seq_struct->last_intra_frame = curr_frame + pred_frame; + p_seq_struct->last_idr_frame = curr_frame + pred_frame; + p_seq_struct->last_random_access_frame = curr_frame + pred_frame; + // display order + p_seq_struct->last_intra_disp = p_frm_struct->frame_no; + p_seq_struct->last_idr_frame = p_frm_struct->frame_no; + p_seq_struct->last_rand_access_disp = p_frm_struct->frame_no; + // sp frame counter (no need to send SP frames if random access frames) + // coding order + p_seq_struct->last_sp_frame = curr_frame + pred_frame; + // display order + p_seq_struct->last_sp_disp = p_frm_struct->frame_no; + } + else if ( p_frm_struct->type == I_SLICE ) + { + // coding order + p_seq_struct->last_intra_frame = curr_frame + pred_frame; + // display order + p_seq_struct->last_intra_disp = p_frm_struct->frame_no; + if ( p_frm_struct->random_access ) + { + // coding order + p_seq_struct->last_random_access_frame = curr_frame + pred_frame; + // display order + p_seq_struct->last_rand_access_disp = p_frm_struct->frame_no; + } + } + else if ( p_frm_struct->type == SP_SLICE || p_frm_struct->type == SI_SLICE ) + { + // coding order + p_seq_struct->last_sp_frame = curr_frame + pred_frame; + // display order + p_seq_struct->last_sp_disp = p_frm_struct->frame_no; + } + if ( p_frm_struct->layer == 0 ) // support for p_Inp->DisposableP + { + if ( p_frm_struct->nal_ref_idc == NALU_PRIORITY_DISPOSABLE ) + { + p_seq_struct->last_bl_frm_disposable = 1; + } + else + { + p_seq_struct->last_bl_frm_disposable = 0; + } + } +} + +/*! + *********************************************************************** + * \brief + * Populate regular prediction structure atom + * \param p_Vid + * pointer to the VideoParameters structure + * \param p_Inp + * pointer to the InputParameters structure + * \param p_seq_struct + * pointer to the sequence structure + * \param curr_frame + * global (sequence-level) coding order of the first frame (in coding order) in the PredStructAtom + * \param proc_frames + * frames that have been populated so far out of the "num_to_populate" frames + * \param terminate_pop + * pointer to the terminate_pop variable that signals termination of the frame population loop + * \return + * number of frames that have been populated by this function run + *********************************************************************** + */ + +static int populate_reg_pred_struct_atom( VideoParameters *p_Vid, InputParameters *p_Inp, SeqStructure *p_seq_struct, + int curr_frame, int proc_frames, int *terminate_pop ) +{ + int idx; + int fixed_idx; + int avail_frames; + int pred_frame = 0; + int pred_idx; + + FrameUnitStruct *p_frm_struct; + PredStructAtom *p_cur_prd; + + // regular prediction structure (*in-between* idr and intra frames though) + // first determine how many frames are available to be used to place our prediction structures + // this finds the next place for a fixed (IDR/intra/SP/SI) frame *after* current_frame (hence counts from current_frame + 1 on) + if ( !(p_Inp->intra_delay) ) + { + fixed_idx = get_fixed_frame_for_prd( p_Inp, p_seq_struct, curr_frame, p_seq_struct->curr_num_to_populate - proc_frames ); + } + else + { + // IntraDelay currently does not support the improved prediction structure allocation (get_fixed_frame_for_prd) + fixed_idx = get_fixed_frame( p_Inp, p_seq_struct, curr_frame, p_seq_struct->curr_num_to_populate - proc_frames ); + } + if ( fixed_idx == -1 ) + { + avail_frames = p_seq_struct->curr_num_to_populate - proc_frames; + } + else + { + avail_frames = fixed_idx; // frames including "curr_frame" + } + // in the future we have to take care of the avail_frames = 0 case + if ( !avail_frames ) + { + *terminate_pop = 1; + } + + // loop through these frames and apply the appropriate prediction structure (p_prd) + while ( pred_frame < avail_frames ) + { + pred_idx = get_prd_index( p_Inp, p_seq_struct, avail_frames - pred_frame ); + // check here whether the prediction structure does not fit even if there is no fixed frame detected; + // if we proceed we will allocate an inefficient pred structure; better to terminate the frame population here + if ( fixed_idx == -1 && (curr_frame + avail_frames) < p_Inp->no_frames ) + { + if ( (p_seq_struct->num_prds - 1) != pred_idx ) + { + *terminate_pop = 1; + break; // the pred_frame loop + } + } + // prediction structure pointer + p_cur_prd = p_seq_struct->p_prd + pred_idx; + // populate gop structure from selected structure + // "idx" is internal pred struct idx + // "pred_frame" is general index for the current frame starting from "curr_frame" (it is zero for "curr_frame") + for ( idx = 0; idx < p_cur_prd->length; idx++ )//, pred_frame++ ) + { + // set pointer + p_frm_struct = p_seq_struct->p_frm + ((curr_frame + pred_frame + idx) % p_Vid->frm_struct_buffer); + + // assign values + populate_frame( p_Inp, p_seq_struct, p_frm_struct, p_cur_prd, curr_frame, pred_frame, idx, p_seq_struct->max_num_slices, 0 ); + + // update IDR and intra frame counters + update_frame_indices( p_seq_struct, p_frm_struct, curr_frame, pred_frame + idx ); + } + pred_frame += idx; + } + return pred_frame; // number of frames that have been populated by this function run +} + +/*! + *********************************************************************** + * \brief + * Populate intra-coded/random access prediction structure atom + * \param p_Vid + * pointer to the VideoParameters structure + * \param p_Inp + * pointer to the InputParameters structure + * \param p_seq_struct + * pointer to the sequence structure + * \param curr_frame + * global (sequence-level) coding order of the first frame (in coding order) in the PredStructAtom + * \param proc_frames + * frames that have been populated so far out of the "num_to_populate" frames + * \param terminate_pop + * pointer to the terminate_pop variable that signals termination of the frame population loop + * \param is_idr + * first frame of the atom will be IDR if > 0. is_idr - 1 is equal to the index of the Atom used to populate the frames + * \param no_rnd_acc + * if 1, then the first frame in the (given) Atom is not a random access frame: Intra \n + * if 2, then it is an SP frame + * \return + * number of frames that have been populated by this function run + *********************************************************************** + */ + +static int populate_rnd_acc_pred_struct_atom( VideoParameters *p_Vid, InputParameters *p_Inp, SeqStructure *p_seq_struct, + int curr_frame, int proc_frames, int *terminate_pop, int is_idr, int no_rnd_acc ) +{ + // insert IDR pred structure (can be *more* than one frame) + int idx; + int fixed_idx; + int avail_frames; + int gop_frame = 0; + int gop_idx; + int num_gops; + + FrameUnitStruct *p_frm_struct; + PredStructAtom *p_cur_gop; + PredStructAtom *p_gop; + int (*get_rand_acc_index)( InputParameters *, SeqStructure *, int ); + + if ( no_rnd_acc == 1 ) // intra + { + p_gop = p_seq_struct->p_intra_gop; + num_gops = p_seq_struct->num_intra_gops; + get_rand_acc_index = get_intra_index; + } + else + { + p_gop = p_seq_struct->p_gop; + num_gops = p_seq_struct->num_gops; + get_rand_acc_index = get_idr_index; + } + + // regular prediction structure (*in-between* idr and intra frames though) + // first determine how many frames are available to be used to place our prediction structures + // this finds the next place for a fixed (IDR/intra/SP/SI) frame *after* current_frame (hence counts from current_frame + 1 on) + fixed_idx = get_fixed_frame( p_Inp, p_seq_struct, curr_frame + 1, p_seq_struct->curr_num_to_populate - proc_frames - 1 ); + if ( fixed_idx == -1 ) + { + avail_frames = p_seq_struct->curr_num_to_populate - proc_frames; + } + else + { + avail_frames = fixed_idx + 1; // frames including "curr_frame" + } + // in the future we have to take care of the avail_frames = 0 case + + // select prediction structure for random access + if ( !curr_frame && !(p_Inp->EnableIDRGOP) && !(p_Inp->intra_delay) ) + { + gop_idx = 0; // force shortest IDR pred struct for first frame + } + else if ( !curr_frame && !(p_Inp->EnableIDRGOP) && p_Inp->intra_delay ) + { + gop_idx = 1; // force middle IDR pred struct (2 corresponds to the longest that is though reserved for within the sequence) + } + else + { + if ( !(p_Inp->PreferDispOrder) ) // coding order + { + // bias in favor of longest *available* and *possible* structure + gop_idx = get_rand_acc_index( p_Inp, p_seq_struct, avail_frames ); + } + else // display order + { + // selection here is *fixed* and is signaled within is_idr + gop_idx = is_idr - 1; + } + // check here whether the prediction structure does not fit even if there is no fixed frame detected; if we proceed we will allocate an inefficient pred structure; better to terminate the frame population here + if ( fixed_idx == -1 && (curr_frame + avail_frames) < p_Inp->no_frames ) + { + if ( (num_gops - 1) != gop_idx && !p_seq_struct->pop_flag ) + { + *terminate_pop = 1; + return 0; + } + } + } + + // reset flag + if ( p_seq_struct->pop_flag ) + { + p_seq_struct->pop_flag = 0; + } + + // prediction structure pointer + p_cur_gop = p_gop + gop_idx; + // populate gop structure from selected structure + for ( idx = 0; idx < p_cur_gop->length; idx++ )//, gop_frame++ ) + { + // set pointer + p_frm_struct = p_seq_struct->p_frm + ((curr_frame + gop_frame + idx) % p_Vid->frm_struct_buffer); + + // assign values + populate_frame( p_Inp, p_seq_struct, p_frm_struct, p_cur_gop, curr_frame, gop_frame, idx, p_seq_struct->max_num_slices, no_rnd_acc ); + + // update IDR and intra frame counters + update_frame_indices( p_seq_struct, p_frm_struct, curr_frame, gop_frame + idx ); + } + gop_frame += idx; + + return gop_frame; // populated frames +} + +/*! + *********************************************************************** + * \brief + * Free random access prediction structures + * \param p_seq_struct + * pointer to the sequence structure + *********************************************************************** + */ + +static void free_gop_struct( SeqStructure *p_seq_struct ) +{ + int idx; + PredStructAtom *p_cur_gop; + + // IDR GOPs + for ( idx = 0; idx < p_seq_struct->num_gops; idx++ ) + { + p_cur_gop = p_seq_struct->p_gop + idx; + free( p_cur_gop->p_frm ); + } + free( p_seq_struct->p_gop ); + // Intra GOPs + for ( idx = 0; idx < p_seq_struct->num_intra_gops; idx++ ) + { + p_cur_gop = p_seq_struct->p_intra_gop + idx; + free( p_cur_gop->p_frm ); + } + free( p_seq_struct->p_intra_gop ); +} + +/*! + *********************************************************************** + * \brief + * Free non-random access prediction structures + * \param p_seq_struct + * pointer to the sequence structure + *********************************************************************** + */ + +static void free_pred_struct( SeqStructure *p_seq_struct ) +{ + int idx; + PredStructAtom *p_cur_prd; + + // free pred structures + for ( idx = 0; idx < p_seq_struct->num_prds; idx++ ) + { + p_cur_prd = p_seq_struct->p_prd + idx; + free( p_cur_prd->p_frm ); + } + free( p_seq_struct->p_prd ); +} + +/*! + *********************************************************************** + * \brief + * Set parameters for a single frame from the explicit structure + * \param info + * pointer to the Explicit Structure information for the current frame + * \param p_Inp + * pointer to the InputParameters structure + * \param p_frm_struct + * pointer to the current frame structure + * \param num_slices + * number of slices used to code this picture + *********************************************************************** + */ + +void populate_frame_explicit( ExpFrameInfo *info, InputParameters *p_Inp, FrameUnitStruct *p_frm_struct, int num_slices ) +{ + // Frame Unit Structure + p_frm_struct->idr_flag = info->is_idr; + p_frm_struct->nal_ref_idc = info->reference_idc; + p_frm_struct->type = info->slice_type; + p_frm_struct->random_access = info->is_idr; + p_frm_struct->num_refs = p_Inp->num_ref_frames; + p_frm_struct->layer = info->reference_idc ? 0 : 1; + p_frm_struct->mod_qp = 0; + p_frm_struct->field_pic_flag = 0; + p_frm_struct->frame_no = info->seq_number; + p_frm_struct->qp = p_frm_struct->mod_qp + p_Inp->qp[p_frm_struct->type]; + + // Picture Structure + switch( p_Inp->PicInterlace ) + { + default: + case FRAME_CODING: + // frame + populate_reg_pic( p_Inp, p_frm_struct->p_frame_pic, p_frm_struct, num_slices, 0 ); + break; + case FIELD_CODING: + // top field + populate_reg_pic( p_Inp, p_frm_struct->p_top_fld_pic, p_frm_struct, num_slices, 0 ); + // bottom field + populate_reg_pic( p_Inp, p_frm_struct->p_bot_fld_pic, p_frm_struct, num_slices, 1 ); + break; + case ADAPTIVE_CODING: + // frame + populate_reg_pic( p_Inp, p_frm_struct->p_frame_pic, p_frm_struct, num_slices, 0 ); + // top field + populate_reg_pic( p_Inp, p_frm_struct->p_top_fld_pic, p_frm_struct, num_slices, 0 ); + // bottom field + populate_reg_pic( p_Inp, p_frm_struct->p_bot_fld_pic, p_frm_struct, num_slices, 1 ); + break; + } +} + +/*! + *********************************************************************** + * \brief + * Set parameters for a single frame during RD-picture decision + * \param p_Inp + * pointer to the InputParameters structure + * \param p_frm_struct + * pointer to the current frame structure + * \param slice_type + * NEW slice type that is set + * \param num_slices + * number of slices used to code this picture + *********************************************************************** + */ + +void populate_frame_slice_type( InputParameters *p_Inp, FrameUnitStruct *p_frm_struct, int slice_type, int num_slices ) +{ + p_frm_struct->type = slice_type; + + // frame (RDPictureDecision only called for frame pictures so no need to check here) + populate_reg_pic( p_Inp, p_frm_struct->p_frame_pic, p_frm_struct, num_slices, 0 ); +} + +#if (MVC_EXTENSION_ENABLE) +/*! + *********************************************************************** + * \brief + * Populate frame structure + * \param p_Vid + * pointer to the VideoParameters structure + * \param p_Inp + * pointer to the InputParameters structure + * \param p_seq_struct + * pointer to the sequence structure + * \param start + * starting number of frame unit structures to populate + * \param end + * starting number of frame unit structures to populate + *********************************************************************** + */ + +void populate_frm_struct_mvc( VideoParameters *p_Vid, InputParameters *p_Inp, SeqStructure *p_seq_struct, int start, int end ) +{ + int view_id; + int idx; + FrameUnitStruct *p_curr_frm, *p_frm = p_seq_struct->p_frm; + FrameUnitStruct *p_curr_frm_mvc, *p_frm_mvc = p_seq_struct->p_frm_mvc; + + if ( p_Inp->num_of_views == 2 ) + { + for ( idx = start; idx < end; idx++ ) + { + p_curr_frm = p_frm + (idx % p_Vid->frm_struct_buffer); + + // view 0 + p_curr_frm_mvc = p_frm_mvc + ((idx << 1) % p_seq_struct->num_frames_mvc); + copy_frame_mvc( p_Inp, p_curr_frm, p_curr_frm_mvc, p_seq_struct->max_num_slices, 0 ); + // view 1 + p_curr_frm_mvc = p_frm_mvc + (((idx << 1) + 1) % p_seq_struct->num_frames_mvc); + copy_frame_mvc( p_Inp, p_curr_frm, p_curr_frm_mvc, p_seq_struct->max_num_slices, 1 ); + } + } + else + { + assert( p_Inp->num_of_views > 2 ); + for ( idx = start; idx < end; idx++ ) + { + p_curr_frm = p_frm + (idx % p_Vid->frm_struct_buffer); + + for ( view_id = 0; view_id < p_Inp->num_of_views; view_id++ ) + { + // view "view_id" + p_curr_frm_mvc = p_frm_mvc + (((idx * p_Inp->num_of_views) + view_id) % p_seq_struct->num_frames_mvc); + copy_frame_mvc( p_Inp, p_curr_frm, p_curr_frm_mvc, p_seq_struct->max_num_slices, view_id ); + } + } + } +} + + +/*! + *********************************************************************** + * \brief + * Set parameters for a single frame unit for MVC compression + * \param p_Inp + * pointer to the InputParameters structure + * \param p_src + * pointer to the source frame unit structure + * \param p_dst + * pointer to the destination frame unit structure + * \param num_slices + * number of slices for the current picture + * \param view_id + * view id + *********************************************************************** + */ + + +static void copy_frame_mvc( InputParameters *p_Inp, FrameUnitStruct *p_src, FrameUnitStruct *p_dst, int num_slices, int view_id ) +{ + // Frame Unit Structure + + //! Edited by Joao Carreira + if( p_Inp->PFramesOnlyView1 && view_id ) + p_dst->type = P_SLICE; + else + p_dst->type = view_id ? p_Inp->MVCInterViewForceB != 0? B_SLICE : (p_src->type == I_SLICE ? (p_Inp->BRefPictures == 2 ? B_SLICE : P_SLICE) : p_src->type) : p_src->type; + + p_dst->random_access = p_src->random_access; + p_dst->num_refs = p_src->num_refs; + p_dst->layer = p_src->layer; + p_dst->mod_qp = p_src->mod_qp; + p_dst->field_pic_flag = p_src->field_pic_flag; + p_dst->frame_no = p_src->frame_no; + p_dst->idr_flag = view_id ? 0 : p_src->idr_flag; + p_dst->nal_ref_idc = view_id ? (p_src->idr_flag ? NALU_PRIORITY_HIGH : p_src->nal_ref_idc) : p_src->nal_ref_idc; + p_dst->qp = p_src->qp; + p_dst->p_atom = p_src->p_atom; + p_dst->atom_idx = p_src->atom_idx; + p_dst->view_id = view_id; + + // Picture Structure + switch( p_Inp->PicInterlace ) + { + default: + case FRAME_CODING: + // frame + populate_reg_pic( p_Inp, p_dst->p_frame_pic, p_dst, num_slices, 0 ); + break; + case FIELD_CODING: + // top field + populate_reg_pic( p_Inp, p_dst->p_top_fld_pic, p_dst, num_slices, 0 ); + // bottom field + populate_reg_pic( p_Inp, p_dst->p_bot_fld_pic, p_dst, num_slices, 1 ); + break; + case ADAPTIVE_CODING: + // frame + populate_reg_pic( p_Inp, p_dst->p_frame_pic, p_dst, num_slices, 0 ); + // top field + populate_reg_pic( p_Inp, p_dst->p_top_fld_pic, p_dst, num_slices, 0 ); + // bottom field + populate_reg_pic( p_Inp, p_dst->p_bot_fld_pic, p_dst, num_slices, 1 ); + break; + } +} +#endif diff --git a/lencod/src/q_around.c b/lencod/src/q_around.c new file mode 100644 index 0000000..aaaf30c --- /dev/null +++ b/lencod/src/q_around.c @@ -0,0 +1,295 @@ + +/*! + ************************************************************************************* + * \file q_around.c + * + * \brief + * Quantization Adaptive Rounding (JVT-N011) + * \author + * - Alexis Michael Tourapis + * - Ehsan Maani + * + ************************************************************************************* + */ + +#include "global.h" +#include "memalloc.h" +#include "q_offsets.h" +#include "q_around.h" + +static const int AdaptRndCrPos[2][5] = +{ + // P, B, I, SP, SI + { 4, 7, 1, 4, 1}, // Intra MB + { 10, 13, 10, 10, 10} // Inter MB +}; + +static const int AdaptRndPos[4][5] = +{ + // P, B, I, SP, SI + { 3, 6, 0, 3, 0}, // 4x4 Intra MB + { 1, 2, 0, 1, 2}, // 8x8 Intra MB + { 9, 12, 9, 9, 9}, // 4x4 Inter MB + { 3, 4, 3, 3, 3}, // 8x8 Inter MB +}; + + +/*! +************************************************************************ +* \brief +* copy data from to a temporary location +************************************************************************ +*/ +void store_adaptive_rounding_4x4 (VideoParameters *p_Vid, int****ARCofAdj, int mode, int block_y, int block_x) +{ + int j; + + for (j = block_y; j < block_y + 4; j++) + memcpy(&ARCofAdj[0][DUMMY][j][block_x], &ARCofAdj[0][mode][j][block_x], BLOCK_SIZE * sizeof(int)); + + if (p_Vid->P444_joined) + { + for (j = block_y; j < block_y + 4; j++) + { + memcpy(&ARCofAdj[1][DUMMY][j][block_x],&ARCofAdj[1][mode][j][block_x], BLOCK_SIZE * sizeof(int)); + } + for (j = block_y; j < block_y + 4; j++) + { + memcpy(&ARCofAdj[2][DUMMY][j][block_x],&ARCofAdj[2][mode][j][block_x], BLOCK_SIZE * sizeof(int)); + } + } +} + + +/*! +************************************************************************ +* \brief +* copy data from a temporary location to the actual location +************************************************************************ +*/ +void update_adaptive_rounding_4x4 (VideoParameters *p_Vid, int****ARCofAdj , int mode, int block_y, int block_x) +{ + int j; + + for (j = block_y; j < block_y + BLOCK_SIZE; j++) + memcpy (&ARCofAdj[0][mode][j][block_x],&ARCofAdj[0][DUMMY][j][block_x], BLOCK_SIZE * sizeof(int)); + + if (p_Vid->P444_joined) + { + for (j = 0; j < block_y + BLOCK_SIZE; j++) + { + memcpy (&ARCofAdj[1][mode][j][block_x], &ARCofAdj[1][DUMMY][j][block_x], BLOCK_SIZE * sizeof(int)); + } + for (j = 0; j < block_y + BLOCK_SIZE; j++) + { + memcpy (&ARCofAdj[2][mode][j][block_x], &ARCofAdj[2][DUMMY][j][block_x], BLOCK_SIZE * sizeof(int)); + } + } +} + +/*! +************************************************************************ +* \brief +* copy data from to a temporary location +************************************************************************ +*/ +void store_adaptive_rounding_16x16 (VideoParameters *p_Vid, int****ARCofAdj, int mode) +{ + memcpy(&ARCofAdj[0][DUMMY][0][0], &ARCofAdj[0][mode][0][0], MB_PIXELS * sizeof(int)); + if (p_Vid->P444_joined) + { + memcpy(&ARCofAdj[1][DUMMY][0][0], &ARCofAdj[1][mode][0][0], MB_PIXELS * sizeof(int)); + memcpy(&ARCofAdj[2][DUMMY][0][0], &ARCofAdj[2][mode][0][0], MB_PIXELS * sizeof(int)); + } +} + + +/*! +************************************************************************ +* \brief +* copy data from a temporary location to the actual location +************************************************************************ +*/ +void update_adaptive_rounding_16x16(VideoParameters *p_Vid, int****ARCofAdj , int mode) +{ + memcpy (&ARCofAdj[0][mode][0][0],&ARCofAdj[0][DUMMY][0][0], MB_PIXELS * sizeof(int)); + if (p_Vid->P444_joined) + { + memcpy (&ARCofAdj[1][mode][0][0], &ARCofAdj[1][DUMMY][0][0], MB_PIXELS * sizeof(int)); + memcpy (&ARCofAdj[2][mode][0][0], &ARCofAdj[2][DUMMY][0][0], MB_PIXELS * sizeof(int)); + } +} + + + + +/*! +************************************************************************ +* \brief +* update rounding offsets based on JVT-N011 +************************************************************************ +*/ +void update_offset_params(Macroblock *currMB, int mode, byte luma_transform_size_8x8_flag) +{ + VideoParameters *p_Vid = currMB->p_Vid; + InputParameters *p_Inp = currMB->p_Inp; + int is_inter = (mode != I4MB)&&(mode != I16MB) && (mode != I8MB); + int luma_pos = AdaptRndPos[(is_inter<<1) + luma_transform_size_8x8_flag][p_Vid->type]; + int i,j; + int qp = currMB->qp + p_Vid->bitdepth_luma_qp_scale; + int cur_qp = p_Inp->AdaptRoundingFixed ? 0 : qp; + int temp = 0; + QuantParameters *p_Quant = p_Vid->p_Quant; + int offsetRange = 1 << (OffsetBits - 1); + int blk_mask = 0x03 + (luma_transform_size_8x8_flag<<2); + int blk_shift = 2 + luma_transform_size_8x8_flag; + short **offsetList = luma_transform_size_8x8_flag ? p_Quant->OffsetList8x8[cur_qp] : p_Quant->OffsetList4x4[cur_qp]; + short *cur_offset_list = offsetList[luma_pos]; + + int **fAdjust = luma_transform_size_8x8_flag ? p_Vid->ARCofAdj8x8[0][mode] : p_Vid->ARCofAdj4x4[0][mode]; + + if (mode == IPCM) return; + + if( (p_Vid->active_sps->chroma_format_idc == YUV444) && (p_Inp->separate_colour_plane_flag != 0) ) + { + if( luma_transform_size_8x8_flag ) // 8x8 + luma_pos += 5 * p_Vid->colour_plane_id; + else // 4x4 + luma_pos += p_Vid->colour_plane_id; + cur_offset_list = offsetList[luma_pos]; + } + + for (j=0; j < MB_BLOCK_SIZE; j++) + { + int j_pos = ((j & blk_mask)<P444_joined) + { + int **fAdjustCbCr; + int uv; + + for(uv = 0; uv < 2; uv++) + { + luma_pos = AdaptRndPos[(is_inter<<1) + luma_transform_size_8x8_flag][p_Vid->type]; + fAdjustCbCr = luma_transform_size_8x8_flag ? p_Vid->ARCofAdj8x8[uv + 1][mode] : p_Vid->ARCofAdj4x4[uv + 1][mode]; + if(luma_transform_size_8x8_flag ) // 8x8 + luma_pos += 5 * (uv+1); + else // 4x4 + luma_pos += (uv+1); + cur_offset_list = offsetList[luma_pos]; + for (j=0; j < MB_BLOCK_SIZE; j++) + { + int j_pos = ((j & blk_mask)<AdaptRndChroma) && (p_Vid->yuv_format == YUV420 || p_Vid->yuv_format == YUV422 )) + { + int u_pos = AdaptRndCrPos[is_inter][p_Vid->type]; + int v_pos = u_pos + 1; + int k, jpos, uv = 1; + + for (k = u_pos; k <= v_pos; k++) + { + int **fAdjustChroma = (luma_transform_size_8x8_flag && mode == P8x8 )? p_Vid->ARCofAdj4x4[uv][4] : p_Vid->ARCofAdj4x4[uv][mode]; + uv++; + cur_offset_list = p_Quant->OffsetList4x4[cur_qp][k]; + + for (j = 0; j < p_Vid->mb_cr_size_y; j++) + { + jpos = ((j & 0x03)<<2); + for (i = 0; i < p_Vid->mb_cr_size_x; i++) + { + temp = jpos + (i & 0x03); + cur_offset_list[temp] = (short) iClip3(0, offsetRange, cur_offset_list[temp] + (short) fAdjustChroma[j][i]); + } + } + } + } +} + + +/*! +************************************************************************ +* \brief +* resets adaptive rounding p_Inp to zero +************************************************************************ +*/ +void reset_adaptive_rounding(VideoParameters *p_Vid) +{ + int maxplane; + maxplane = (p_Vid->yuv_format == YUV400)? 1 : 3; + memset(&p_Vid->ARCofAdj4x4[0][0][0][0], 0, maxplane * MAXMODE * MB_PIXELS * sizeof (int)); + if (p_Vid->p_Inp->Transform8x8Mode) + { + maxplane = (p_Vid->yuv_format == YUV400)? 1 : (p_Vid->P444_joined ? 3 : 1); + memset(&p_Vid->ARCofAdj8x8[0][0][0][0], 0, maxplane * MAXMODE * MB_PIXELS * sizeof (int)); + } +} + +/*! +************************************************************************ +* \brief +* resets adaptive rounding p_Inp for the direct mode to zero +************************************************************************ +*/ +void reset_adaptive_rounding_direct(VideoParameters *p_Vid) +{ + int maxplane, pl; + maxplane = (p_Vid->yuv_format == YUV400)? 1 : 3; + for (pl = 0; pl < maxplane; pl++) + memset(&p_Vid->ARCofAdj4x4[pl][DUMMY][0][0], 0, MB_PIXELS * sizeof (int)); + + if (p_Vid->p_Inp->Transform8x8Mode) + { + maxplane = (p_Vid->yuv_format == YUV400)? 1 : (p_Vid->P444_joined ? 3 : 1); + for (pl = 0; pl < maxplane; pl++) + memset(&p_Vid->ARCofAdj8x8[pl][DUMMY][0][0], 0, MB_PIXELS * sizeof (int)); + } + +} + +/*! +************************************************************************ +* \brief +* copy data from a temporary location to the actual location for mode P8x8 +************************************************************************ +*/ +void update_adaptive_rounding_8x8(VideoParameters *p_Vid, InputParameters *p_Inp, RD_8x8DATA* dataTr, int**** ARCofAdj) +{ + int b8, pl, plane, j0, i0, j; + + if (p_Inp->AdaptiveRounding) + { + plane = (p_Vid->P444_joined) ? 3 : 1; + for (pl = 0; pl < plane; pl++) + { + for (b8 = 0; b8 < 4; b8++) + { + j0 = 8*(b8 >> 1); + i0 = 8*(b8 & 0x01); + if (dataTr->part[b8].mode > 0) + { + for (j = j0; j < j0 + BLOCK_SIZE_8x8; j++) + { + memcpy(&ARCofAdj[pl][P8x8][j][i0], &ARCofAdj[pl][(short) dataTr->part[b8].mode][j][i0], BLOCK_SIZE_8x8 * sizeof(int)); + } + } + } + } + } +} + + diff --git a/lencod/src/q_matrix.c b/lencod/src/q_matrix.c new file mode 100644 index 0000000..28c9ee8 --- /dev/null +++ b/lencod/src/q_matrix.c @@ -0,0 +1,901 @@ + +/*! + ************************************************************************************* + * \file q_matrix.c + * + * \brief + * read q_matrix parameters from input file: q_matrix.cfg + * + ************************************************************************************* + */ + +#include "global.h" +#include "memalloc.h" +#include "q_matrix.h" + +extern char *GetConfigFileContent (char *Filename, int error_type); + +#define MAX_ITEMS_TO_PARSE 1000 + +static const int quant_coef[6][4][4] = { + {{13107, 8066,13107, 8066},{ 8066, 5243, 8066, 5243},{13107, 8066,13107, 8066},{ 8066, 5243, 8066, 5243}}, + {{11916, 7490,11916, 7490},{ 7490, 4660, 7490, 4660},{11916, 7490,11916, 7490},{ 7490, 4660, 7490, 4660}}, + {{10082, 6554,10082, 6554},{ 6554, 4194, 6554, 4194},{10082, 6554,10082, 6554},{ 6554, 4194, 6554, 4194}}, + {{ 9362, 5825, 9362, 5825},{ 5825, 3647, 5825, 3647},{ 9362, 5825, 9362, 5825},{ 5825, 3647, 5825, 3647}}, + {{ 8192, 5243, 8192, 5243},{ 5243, 3355, 5243, 3355},{ 8192, 5243, 8192, 5243},{ 5243, 3355, 5243, 3355}}, + {{ 7282, 4559, 7282, 4559},{ 4559, 2893, 4559, 2893},{ 7282, 4559, 7282, 4559},{ 4559, 2893, 4559, 2893}} +}; + +const int dequant_coef[6][4][4] = { + {{10, 13, 10, 13},{ 13, 16, 13, 16},{10, 13, 10, 13},{ 13, 16, 13, 16}}, + {{11, 14, 11, 14},{ 14, 18, 14, 18},{11, 14, 11, 14},{ 14, 18, 14, 18}}, + {{13, 16, 13, 16},{ 16, 20, 16, 20},{13, 16, 13, 16},{ 16, 20, 16, 20}}, + {{14, 18, 14, 18},{ 18, 23, 18, 23},{14, 18, 14, 18},{ 18, 23, 18, 23}}, + {{16, 20, 16, 20},{ 20, 25, 20, 25},{16, 20, 16, 20},{ 20, 25, 20, 25}}, + {{18, 23, 18, 23},{ 23, 29, 23, 29},{18, 23, 18, 23},{ 23, 29, 23, 29}} +}; + +static const int quant_coef8[6][8][8] = +{ + { + {13107, 12222, 16777, 12222, 13107, 12222, 16777, 12222}, + {12222, 11428, 15481, 11428, 12222, 11428, 15481, 11428}, + {16777, 15481, 20972, 15481, 16777, 15481, 20972, 15481}, + {12222, 11428, 15481, 11428, 12222, 11428, 15481, 11428}, + {13107, 12222, 16777, 12222, 13107, 12222, 16777, 12222}, + {12222, 11428, 15481, 11428, 12222, 11428, 15481, 11428}, + {16777, 15481, 20972, 15481, 16777, 15481, 20972, 15481}, + {12222, 11428, 15481, 11428, 12222, 11428, 15481, 11428} + }, + { + {11916, 11058, 14980, 11058, 11916, 11058, 14980, 11058}, + {11058, 10826, 14290, 10826, 11058, 10826, 14290, 10826}, + {14980, 14290, 19174, 14290, 14980, 14290, 19174, 14290}, + {11058, 10826, 14290, 10826, 11058, 10826, 14290, 10826}, + {11916, 11058, 14980, 11058, 11916, 11058, 14980, 11058}, + {11058, 10826, 14290, 10826, 11058, 10826, 14290, 10826}, + {14980, 14290, 19174, 14290, 14980, 14290, 19174, 14290}, + {11058, 10826, 14290, 10826, 11058, 10826, 14290, 10826} + }, + { + {10082, 9675, 12710, 9675, 10082, 9675, 12710, 9675}, + {9675, 8943, 11985, 8943, 9675, 8943, 11985, 8943}, + {12710, 11985, 15978, 11985, 12710, 11985, 15978, 11985}, + {9675, 8943, 11985, 8943, 9675, 8943, 11985, 8943}, + {10082, 9675, 12710, 9675, 10082, 9675, 12710, 9675}, + {9675, 8943, 11985, 8943, 9675, 8943, 11985, 8943}, + {12710, 11985, 15978, 11985, 12710, 11985, 15978, 11985}, + {9675, 8943, 11985, 8943, 9675, 8943, 11985, 8943} + }, + { + {9362, 8931, 11984, 8931, 9362, 8931, 11984, 8931}, + {8931, 8228, 11259, 8228, 8931, 8228, 11259, 8228}, + {11984, 11259, 14913, 11259, 11984, 11259, 14913, 11259}, + {8931, 8228, 11259, 8228, 8931, 8228, 11259, 8228}, + {9362, 8931, 11984, 8931, 9362, 8931, 11984, 8931}, + {8931, 8228, 11259, 8228, 8931, 8228, 11259, 8228}, + {11984, 11259, 14913, 11259, 11984, 11259, 14913, 11259}, + {8931, 8228, 11259, 8228, 8931, 8228, 11259, 8228} + }, + { + {8192, 7740, 10486, 7740, 8192, 7740, 10486, 7740}, + {7740, 7346, 9777, 7346, 7740, 7346, 9777, 7346}, + {10486, 9777, 13159, 9777, 10486, 9777, 13159, 9777}, + {7740, 7346, 9777, 7346, 7740, 7346, 9777, 7346}, + {8192, 7740, 10486, 7740, 8192, 7740, 10486, 7740}, + {7740, 7346, 9777, 7346, 7740, 7346, 9777, 7346}, + {10486, 9777, 13159, 9777, 10486, 9777, 13159, 9777}, + {7740, 7346, 9777, 7346, 7740, 7346, 9777, 7346} + }, + { + {7282, 6830, 9118, 6830, 7282, 6830, 9118, 6830}, + {6830, 6428, 8640, 6428, 6830, 6428, 8640, 6428}, + {9118, 8640, 11570, 8640, 9118, 8640, 11570, 8640}, + {6830, 6428, 8640, 6428, 6830, 6428, 8640, 6428}, + {7282, 6830, 9118, 6830, 7282, 6830, 9118, 6830}, + {6830, 6428, 8640, 6428, 6830, 6428, 8640, 6428}, + {9118, 8640, 11570, 8640, 9118, 8640, 11570, 8640}, + {6830, 6428, 8640, 6428, 6830, 6428, 8640, 6428} + } +}; + + + +static const int dequant_coef8[6][8][8] = +{ + { + {20, 19, 25, 19, 20, 19, 25, 19}, + {19, 18, 24, 18, 19, 18, 24, 18}, + {25, 24, 32, 24, 25, 24, 32, 24}, + {19, 18, 24, 18, 19, 18, 24, 18}, + {20, 19, 25, 19, 20, 19, 25, 19}, + {19, 18, 24, 18, 19, 18, 24, 18}, + {25, 24, 32, 24, 25, 24, 32, 24}, + {19, 18, 24, 18, 19, 18, 24, 18} + }, + { + {22, 21, 28, 21, 22, 21, 28, 21}, + {21, 19, 26, 19, 21, 19, 26, 19}, + {28, 26, 35, 26, 28, 26, 35, 26}, + {21, 19, 26, 19, 21, 19, 26, 19}, + {22, 21, 28, 21, 22, 21, 28, 21}, + {21, 19, 26, 19, 21, 19, 26, 19}, + {28, 26, 35, 26, 28, 26, 35, 26}, + {21, 19, 26, 19, 21, 19, 26, 19} + }, + { + {26, 24, 33, 24, 26, 24, 33, 24}, + {24, 23, 31, 23, 24, 23, 31, 23}, + {33, 31, 42, 31, 33, 31, 42, 31}, + {24, 23, 31, 23, 24, 23, 31, 23}, + {26, 24, 33, 24, 26, 24, 33, 24}, + {24, 23, 31, 23, 24, 23, 31, 23}, + {33, 31, 42, 31, 33, 31, 42, 31}, + {24, 23, 31, 23, 24, 23, 31, 23} + }, + { + {28, 26, 35, 26, 28, 26, 35, 26}, + {26, 25, 33, 25, 26, 25, 33, 25}, + {35, 33, 45, 33, 35, 33, 45, 33}, + {26, 25, 33, 25, 26, 25, 33, 25}, + {28, 26, 35, 26, 28, 26, 35, 26}, + {26, 25, 33, 25, 26, 25, 33, 25}, + {35, 33, 45, 33, 35, 33, 45, 33}, + {26, 25, 33, 25, 26, 25, 33, 25} + }, + { + {32, 30, 40, 30, 32, 30, 40, 30}, + {30, 28, 38, 28, 30, 28, 38, 28}, + {40, 38, 51, 38, 40, 38, 51, 38}, + {30, 28, 38, 28, 30, 28, 38, 28}, + {32, 30, 40, 30, 32, 30, 40, 30}, + {30, 28, 38, 28, 30, 28, 38, 28}, + {40, 38, 51, 38, 40, 38, 51, 38}, + {30, 28, 38, 28, 30, 28, 38, 28} + }, + { + {36, 34, 46, 34, 36, 34, 46, 34}, + {34, 32, 43, 32, 34, 32, 43, 32}, + {46, 43, 58, 43, 46, 43, 58, 43}, + {34, 32, 43, 32, 34, 32, 43, 32}, + {36, 34, 46, 34, 36, 34, 46, 34}, + {34, 32, 43, 32, 34, 32, 43, 32}, + {46, 43, 58, 43, 46, 43, 58, 43}, + {34, 32, 43, 32, 34, 32, 43, 32} + } +}; + + +int matrix4x4_check[6] = {0, 0, 0, 0, 0, 0}; +int matrix8x8_check[6] = {0, 0, 0, 0, 0, 0}; + +static const char MatrixType4x4[6][20] = +{ + "INTRA4X4_LUMA", + "INTRA4X4_CHROMAU", + "INTRA4X4_CHROMAV", + "INTER4X4_LUMA", + "INTER4X4_CHROMAU", + "INTER4X4_CHROMAV" +}; + +static const char MatrixType8x8[6][20] = +{ + "INTRA8X8_LUMA", + "INTER8X8_LUMA", + "INTRA8X8_CHROMAU", // only for 4:4:4 + "INTER8X8_CHROMAU", // only for 4:4:4 + "INTRA8X8_CHROMAV", // only for 4:4:4 + "INTER8X8_CHROMAV" // only for 4:4:4 +}; + + +static const short Quant_intra_default[16] = +{ + 6,13,20,28, +13,20,28,32, +20,28,32,37, +28,32,37,42 +}; + +static const short Quant_inter_default[16] = +{ +10,14,20,24, +14,20,24,27, +20,24,27,30, +24,27,30,34 +}; + +static const short Quant8_intra_default[64] = +{ + 6,10,13,16,18,23,25,27, +10,11,16,18,23,25,27,29, +13,16,18,23,25,27,29,31, +16,18,23,25,27,29,31,33, +18,23,25,27,29,31,33,36, +23,25,27,29,31,33,36,38, +25,27,29,31,33,36,38,40, +27,29,31,33,36,38,40,42 +}; + +static const short Quant8_inter_default[64] = +{ + 9,13,15,17,19,21,22,24, +13,13,17,19,21,22,24,25, +15,17,19,21,22,24,25,27, +17,19,21,22,24,25,27,28, +19,21,22,24,25,27,28,30, +21,22,24,25,27,28,30,32, +22,24,25,27,28,30,32,33, +24,25,27,28,30,32,33,35 +}; + + +/*! + *********************************************************************** + * \brief + * Check the parameter name. + * \param s + * parameter name string + * \param type + * 4x4 or 8x8 matrix type + * \return + * the index number if the string is a valid parameter name, \n + * -1 for error + *********************************************************************** + */ +static int CheckParameterName (char *s, int *type) +{ + int i = 0; + + *type = 0; + while ((MatrixType4x4[i] != NULL) && (i<6)) + { + if (0==strcmp (MatrixType4x4[i], s)) + return i; + else + i++; + } + + i = 0; + *type = 1; + while ((MatrixType8x8[i] != NULL) && (i<6)) + { + if (0==strcmp (MatrixType8x8[i], s)) + return i; + else + i++; + } + + return -1; +} + +/*! + *********************************************************************** + * \brief + * Parse the Q matrix values read from cfg file. + * \param p_Vid + * current video parameters + * \param buf + * buffer to be parsed + * \param bufsize + * buffer size of buffer + *********************************************************************** + */ +static void ParseMatrix (VideoParameters *p_Vid, char *buf, int bufsize) +{ + ScaleParameters *p_QScale = p_Vid->p_QScale; + char *items[MAX_ITEMS_TO_PARSE] = {NULL}; + int MapIdx; + int item = 0; + int InString = 0, InItem = 0; + char *p = buf; + char *bufend = &buf[bufsize]; + int IntContent; + int i, j, range, type, cnt; + short *ScalingList; + + while (p < bufend) + { + switch (*p) + { + case 13: + p++; + break; + case '#': // Found comment + *p = '\0'; // Replace '#' with '\0' in case of comment immediately following integer or string + while (*p != '\n' && p < bufend) // Skip till EOL or EOF, whichever comes first + p++; + InString = 0; + InItem = 0; + break; + case '\n': + InItem = 0; + InString = 0; + *p++='\0'; + break; + case ' ': + case '\t': // Skip whitespace, leave state unchanged + if (InString) + p++; + else + { // Terminate non-strings once whitespace is found + *p++ = '\0'; + InItem = 0; + } + break; + + case '"': // Begin/End of String + *p++ = '\0'; + if (!InString) + { + items[item++] = p; + InItem = ~InItem; + } + else + InItem = 0; + InString = ~InString; // Toggle + break; + + case ',': + p++; + InItem = 0; + break; + + default: + if (!InItem) + { + items[item++] = p; + InItem = ~InItem; + } + p++; + } + } + + item--; + + for (i=0; i (MapIdx = CheckParameterName (items[i+cnt], &type))) + { + snprintf (errortext, ET_SIZE, " Parsing error in quantization matrix config file: Parameter Name '%s' not recognized.", items[i+cnt]); + error (errortext, 300); + } + cnt++; + if (strcmp ("=", items[i+cnt])) + { + snprintf (errortext, ET_SIZE, " Parsing error in quantization matrix config file: '=' expected as the second token in each item."); + error (errortext, 300); + } + cnt++; + + if (!type) //4x4 Matrix + { + range = 16; + ScalingList = p_QScale->ScalingList4x4input[MapIdx]; + matrix4x4_check[MapIdx] = 1; //to indicate matrix found in cfg file + } + else //8x8 matrix + { + range = 64; + ScalingList = p_QScale->ScalingList8x8input[MapIdx]; + matrix8x8_check[MapIdx] = 1; //to indicate matrix found in cfg file + } + + for(j=0; jp_QScale; + short *ScalingList; + int i, cnt, fail; + + for(i=0; i<6; i++) + { + if(p_Inp->ScalingListPresentFlag[i]) + { + ScalingList = p_QScale->ScalingList4x4input[i]; + if(matrix4x4_check[i]) + { + fail=0; + for(cnt=0; cnt<16; cnt++) + { + if(ScalingList[cnt]<0 || ScalingList[cnt]>255) // ScalingList[0]=0 to indicate use default matrix + { + fail=1; + break; + } + } + + if(fail) //value of matrix exceed range + { + printf("\n%s value exceed range. (Value must be 1 to 255)\n", MatrixType4x4[i]); + printf("Setting default values for this matrix."); + if(i>2) + memcpy(ScalingList, Quant_inter_default, sizeof(short)*16); + else + memcpy(ScalingList, Quant_intra_default, sizeof(short)*16); + } + } + else //matrix not found, pad with default value + { + printf("\n%s matrix definition not found. Setting default values.", MatrixType4x4[i]); + if(i>2) + memcpy(ScalingList, Quant_inter_default, sizeof(short)*16); + else + memcpy(ScalingList, Quant_intra_default, sizeof(short)*16); + } + } + + if(p_Inp->ScalingListPresentFlag[i+6]) + { + ScalingList = p_QScale->ScalingList8x8input[i]; + if(matrix8x8_check[i]) + { + fail=0; + for(cnt=0; cnt<64; cnt++) + { + if(ScalingList[cnt]<0 || ScalingList[cnt]>255) // ScalingList[0]=0 to indicate use default matrix + { + fail=1; + break; + } + } + + if(fail) //value of matrix exceed range + { + printf("\n%s value exceed range. (Value must be 1 to 255)\n", MatrixType8x8[i]); + printf("Setting default values for this matrix."); + if(i==1 || i==3 || i==5) + memcpy(ScalingList, Quant8_inter_default, sizeof(short)*64); + else + memcpy(ScalingList, Quant8_intra_default, sizeof(short)*64); + } + } + else //matrix not found, pad with default value + { + printf("\n%s matrix definition not found. Setting default values.", MatrixType8x8[i]); + if(i==1 || i==3 || i==5) + memcpy(ScalingList, Quant8_inter_default, sizeof(short)*64); + else + memcpy(ScalingList, Quant8_intra_default, sizeof(short)*64); + } + } + } +} + +/*! + *********************************************************************** + * \brief + * Allocate Q matrix arrays + *********************************************************************** + */ +static void allocate_QMatrix (QuantParameters *p_Quant, InputParameters *p_Inp) +{ + int max_bitdepth = imax(p_Inp->output.bit_depth[0], p_Inp->output.bit_depth[1]); + int max_qp = (3 + 6*(max_bitdepth)); + + int bitdepth_qp_scale = 6*(p_Inp->output.bit_depth[0] - 8); + int i; + + get_mem5Dquant(&p_Quant->q_params_4x4, 3, 2, max_qp + 1, 4, 4); + get_mem5Dquant(&p_Quant->q_params_8x8, 3, 2, max_qp + 1, 8, 8); + + if ((p_Quant->qp_per_matrix = (int*)malloc((MAX_QP + 1 + bitdepth_qp_scale)*sizeof(int))) == NULL) + no_mem_exit("allocate_QMatrix: p_Quant->qp_per_matrix"); + if ((p_Quant->qp_rem_matrix = (int*)malloc((MAX_QP + 1 + bitdepth_qp_scale)*sizeof(int))) == NULL) + no_mem_exit("allocate_QMatrix: p_Quant->qp_per_matrix"); + + for (i = 0; i < MAX_QP + bitdepth_qp_scale + 1; i++) + { + p_Quant->qp_per_matrix[i] = i / 6; + p_Quant->qp_rem_matrix[i] = i % 6; + } +} + +/*! + *********************************************************************** + * \brief + * Free Q matrix arrays + *********************************************************************** + */ +void free_QMatrix (QuantParameters *p_Quant) +{ + free_mem5Dquant(p_Quant->q_params_4x4); + free_mem5Dquant(p_Quant->q_params_8x8); + + free(p_Quant->qp_rem_matrix); + free(p_Quant->qp_per_matrix); +} + + +/*! + *********************************************************************** + * \brief + * Initialise Q matrix values. + *********************************************************************** + */ +void init_qmatrix (VideoParameters *p_Vid, InputParameters *p_Inp) +{ + QuantParameters *p_Quant = p_Vid->p_Quant; + ScaleParameters *p_QScale = p_Vid->p_QScale; + char *content; + + allocate_QMatrix (p_Quant, p_Inp); + + if(p_Inp->ScalingMatrixPresentFlag) + { + printf ("Parsing QMatrix file %s ", p_Inp->QmatrixFile); + content = GetConfigFileContent(p_Inp->QmatrixFile, 0); + if(content!='\0') + ParseMatrix(p_Vid, content, (int) strlen (content)); + else + printf("\nError: %s\nProceeding with default values for all matrices.", errortext); + + PatchMatrix(p_Vid, p_Inp); + printf("\n"); + + memset(p_QScale->UseDefaultScalingMatrix4x4Flag, 0, 6 * sizeof(short)); + memset(p_QScale->UseDefaultScalingMatrix8x8Flag, 0, 6 * sizeof(short)); + + free(content); + } +} + +static void set_default_quant4x4(LevelQuantParams **q_params_4x4, const int (*quant)[4], const int (*dequant)[4]) +{ + int i, j; + for(j=0; j<4; j++) + { + for(i=0; i<4; i++) + { + q_params_4x4[j][i].ScaleComp = quant[j][i]; + q_params_4x4[j][i].InvScaleComp = dequant[j][i]<<4; + } + } +} + +/*! + ************************************************************************ + * \brief + * For calculating the quantisation values at frame level + * + * \par Input: + * none + * + * \par Output: + * none + ************************************************************************ + */ +void CalculateQuant4x4Param(VideoParameters *p_Vid) +{ + QuantParameters *p_Quant = p_Vid->p_Quant; + ScaleParameters *p_QScale = p_Vid->p_QScale; + + pic_parameter_set_rbsp_t *active_pps = p_Vid->active_pps; + seq_parameter_set_rbsp_t *active_sps = p_Vid->active_sps; + + int i, j, k, temp; + int k_mod; + int present[6]; + int no_q_matrix=FALSE; + + int max_bitdepth = imax(p_Vid->bitdepth_luma, p_Vid->bitdepth_chroma); + int max_qp = (3 + 6*(max_bitdepth)); + + + if(!active_sps->seq_scaling_matrix_present_flag && !active_pps->pic_scaling_matrix_present_flag) //set to no q-matrix + no_q_matrix=TRUE; + else + { + memset(present, 0, 6 * sizeof(int)); + + if(active_sps->seq_scaling_matrix_present_flag) + for(i=0; i<6; i++) + present[i] = active_sps->seq_scaling_list_present_flag[i]; + + if(active_pps->pic_scaling_matrix_present_flag) + for(i=0; i<6; i++) + { + if((i==0) || (i==3)) + present[i] |= active_pps->pic_scaling_list_present_flag[i]; + else + present[i] = active_pps->pic_scaling_list_present_flag[i]; + } + } + + if(no_q_matrix==TRUE) + { + for(k_mod=0; k_modq_params_4x4[0][0][k_mod], quant_coef[k], dequant_coef[k]); + set_default_quant4x4(p_Quant->q_params_4x4[0][1][k_mod], quant_coef[k], dequant_coef[k]); + set_default_quant4x4(p_Quant->q_params_4x4[1][0][k_mod], quant_coef[k], dequant_coef[k]); + set_default_quant4x4(p_Quant->q_params_4x4[1][1][k_mod], quant_coef[k], dequant_coef[k]); + set_default_quant4x4(p_Quant->q_params_4x4[2][0][k_mod], quant_coef[k], dequant_coef[k]); + set_default_quant4x4(p_Quant->q_params_4x4[2][1][k_mod], quant_coef[k], dequant_coef[k]); + } + } + else + { + for(k_mod=0; k_modUseDefaultScalingMatrix4x4Flag[0]) + { + p_Quant->q_params_4x4[0][1][k_mod][j][i].ScaleComp = (quant_coef[k][j][i]<<4)/Quant_intra_default[temp]; + p_Quant->q_params_4x4[0][1][k_mod][j][i].InvScaleComp = dequant_coef[k][j][i]*Quant_intra_default[temp]; + } + else + { + p_Quant->q_params_4x4[0][1][k_mod][j][i].ScaleComp = (quant_coef[k][j][i]<<4)/p_QScale->ScalingList4x4[0][temp]; + p_Quant->q_params_4x4[0][1][k_mod][j][i].InvScaleComp = dequant_coef[k][j][i]*p_QScale->ScalingList4x4[0][temp]; + } + + if(!present[1]) + { + p_Quant->q_params_4x4[1][1][k_mod][j][i].ScaleComp = p_Quant->q_params_4x4[0][1][k_mod][j][i].ScaleComp; + p_Quant->q_params_4x4[1][1][k_mod][j][i].InvScaleComp = p_Quant->q_params_4x4[0][1][k_mod][j][i].InvScaleComp; + } + else + { + p_Quant->q_params_4x4[1][1][k_mod][j][i].ScaleComp = (quant_coef[k][j][i]<<4)/(p_QScale->UseDefaultScalingMatrix4x4Flag[1] ? Quant_intra_default[temp]:p_QScale->ScalingList4x4[1][temp]); + p_Quant->q_params_4x4[1][1][k_mod][j][i].InvScaleComp = dequant_coef[k][j][i]*(p_QScale->UseDefaultScalingMatrix4x4Flag[1] ? Quant_intra_default[temp]:p_QScale->ScalingList4x4[1][temp]); + } + + if(!present[2]) + { + p_Quant->q_params_4x4[2][1][k_mod][j][i].ScaleComp = p_Quant->q_params_4x4[1][1][k_mod][j][i].ScaleComp; + p_Quant->q_params_4x4[2][1][k_mod][j][i].InvScaleComp = p_Quant->q_params_4x4[1][1][k_mod][j][i].InvScaleComp; + } + else + { + p_Quant->q_params_4x4[2][1][k_mod][j][i].ScaleComp = (quant_coef[k][j][i]<<4)/(p_QScale->UseDefaultScalingMatrix4x4Flag[2] ? Quant_intra_default[temp]:p_QScale->ScalingList4x4[2][temp]); + p_Quant->q_params_4x4[2][1][k_mod][j][i].InvScaleComp = dequant_coef[k][j][i]*(p_QScale->UseDefaultScalingMatrix4x4Flag[2] ? Quant_intra_default[temp]:p_QScale->ScalingList4x4[2][temp]); + } + + if((!present[3]) || p_QScale->UseDefaultScalingMatrix4x4Flag[3]) + { + p_Quant->q_params_4x4[0][0][k_mod][j][i].ScaleComp = (quant_coef[k][j][i]<<4)/Quant_inter_default[temp]; + p_Quant->q_params_4x4[0][0][k_mod][j][i].InvScaleComp = dequant_coef[k][j][i]*Quant_inter_default[temp]; + } + else + { + p_Quant->q_params_4x4[0][0][k_mod][j][i].ScaleComp = (quant_coef[k][j][i]<<4)/p_QScale->ScalingList4x4[3][temp]; + p_Quant->q_params_4x4[0][0][k_mod][j][i].InvScaleComp = dequant_coef[k][j][i]*p_QScale->ScalingList4x4[3][temp]; + } + + if(!present[4]) + { + p_Quant->q_params_4x4[1][0][k_mod][j][i].ScaleComp = p_Quant->q_params_4x4[0][0][k_mod][j][i].ScaleComp; + p_Quant->q_params_4x4[1][0][k_mod][j][i].InvScaleComp = p_Quant->q_params_4x4[0][0][k_mod][j][i].InvScaleComp; + } + else + { + p_Quant->q_params_4x4[1][0][k_mod][j][i].ScaleComp = (quant_coef[k][j][i]<<4)/(p_QScale->UseDefaultScalingMatrix4x4Flag[4] ? Quant_inter_default[temp]:p_QScale->ScalingList4x4[4][temp]); + p_Quant->q_params_4x4[1][0][k_mod][j][i].InvScaleComp = dequant_coef[k][j][i]*(p_QScale->UseDefaultScalingMatrix4x4Flag[4] ? Quant_inter_default[temp]:p_QScale->ScalingList4x4[4][temp]); + } + + if(!present[5]) + { + p_Quant->q_params_4x4[2][0][k_mod][j][i].ScaleComp = p_Quant->q_params_4x4[1][0][k_mod][j][i].ScaleComp; + p_Quant->q_params_4x4[2][0][k_mod][j][i].InvScaleComp = p_Quant->q_params_4x4[1][0][k_mod][j][i].InvScaleComp; + } + else + { + p_Quant->q_params_4x4[2][0][k_mod][j][i].ScaleComp = (quant_coef[k][j][i]<<4)/(p_QScale->UseDefaultScalingMatrix4x4Flag[5] ? Quant_inter_default[temp]:p_QScale->ScalingList4x4[5][temp]); + p_Quant->q_params_4x4[2][0][k_mod][j][i].InvScaleComp = dequant_coef[k][j][i]*(p_QScale->UseDefaultScalingMatrix4x4Flag[5] ? Quant_inter_default[temp]:p_QScale->ScalingList4x4[5][temp]); + } + } + } + } + } +} + +/*! + ************************************************************************ + * \brief + * Calculate the quantisation and inverse quantisation parameters + * + ************************************************************************ + */ +void CalculateQuant8x8Param(VideoParameters *p_Vid) +{ + QuantParameters *p_Quant = p_Vid->p_Quant; + ScaleParameters *p_QScale = p_Vid->p_QScale; + + pic_parameter_set_rbsp_t *active_pps = p_Vid->active_pps; + seq_parameter_set_rbsp_t *active_sps = p_Vid->active_sps; + + int i, j, k, temp; + int k_mod; + int n_ScalingList8x8; + int present[6]; + int no_q_matrix=FALSE; + int max_bitdepth = imax(p_Vid->bitdepth_luma, p_Vid->bitdepth_chroma); + int max_qp = (3 + 6*(max_bitdepth)); + + + // maximum number of valid 8x8 scaling lists + n_ScalingList8x8 = ( active_sps->chroma_format_idc != 3 ) ? 2 : 6; + + if(!active_sps->seq_scaling_matrix_present_flag && !active_pps->pic_scaling_matrix_present_flag) //set to default matrix + no_q_matrix=TRUE; + else + { + memset(present, 0, sizeof(int)*n_ScalingList8x8); + + if(active_sps->seq_scaling_matrix_present_flag) + { + for(i=0; iseq_scaling_list_present_flag[i+6]; + } + + if(active_pps->pic_scaling_matrix_present_flag) + { + for(i=0; ipic_scaling_list_present_flag[i+6]; + else + present[i] = active_pps->pic_scaling_list_present_flag[i+6]; + } + } + } + + if(no_q_matrix==TRUE) + { + for(k = 0; k < max_qp; k++) + { + k_mod = k %6; + for(j=0; j<8; j++) + { + for(i=0; i<8; i++) + { + // intra Y + p_Quant->q_params_8x8[0][1][k][j][i].ScaleComp = quant_coef8[k_mod][j][i]; + p_Quant->q_params_8x8[0][1][k][j][i].InvScaleComp = dequant_coef8[k_mod][j][i]<<4; + + // inter Y + p_Quant->q_params_8x8[0][0][k][j][i].ScaleComp = quant_coef8[k_mod][j][i]; + p_Quant->q_params_8x8[0][0][k][j][i].InvScaleComp = dequant_coef8[k_mod][j][i]<<4; + + if( n_ScalingList8x8 > 2 ) // 4:4:4 case only + { + // intra U + p_Quant->q_params_8x8[1][1][k][j][i].ScaleComp = quant_coef8[k_mod][j][i]; + p_Quant->q_params_8x8[1][1][k][j][i].InvScaleComp = dequant_coef8[k_mod][j][i]<<4; + + // intra V + p_Quant->q_params_8x8[2][1][k][j][i].ScaleComp = quant_coef8[k_mod][j][i]; + p_Quant->q_params_8x8[2][1][k][j][i].InvScaleComp = dequant_coef8[k_mod][j][i]<<4; + + // inter U + p_Quant->q_params_8x8[1][0][k][j][i].ScaleComp = quant_coef8[k_mod][j][i]; + p_Quant->q_params_8x8[1][0][k][j][i].InvScaleComp = dequant_coef8[k_mod][j][i]<<4; + + // inter V + p_Quant->q_params_8x8[2][0][k][j][i].ScaleComp = quant_coef8[k_mod][j][i]; + p_Quant->q_params_8x8[2][0][k][j][i].InvScaleComp = dequant_coef8[k_mod][j][i]<<4; + + } + } + } + } + } + else + { + for(k = 0; k < max_qp; k++) + { + k_mod = k %6; + for(j=0; j<8; j++) + { + for(i=0; i<8; i++) + { + temp = (j<<3)+i; + if((!present[0]) || p_QScale->UseDefaultScalingMatrix8x8Flag[0]) + { + p_Quant->q_params_8x8[0][1][k][j][i].ScaleComp = (quant_coef8[k_mod][j][i]<<4)/Quant8_intra_default[temp]; + p_Quant->q_params_8x8[0][1][k][j][i].InvScaleComp = dequant_coef8[k_mod][j][i]*Quant8_intra_default[temp]; + } + else + { + p_Quant->q_params_8x8[0][1][k][j][i].ScaleComp = (quant_coef8[k_mod][j][i]<<4)/p_QScale->ScalingList8x8[0][temp]; + p_Quant->q_params_8x8[0][1][k][j][i].InvScaleComp = dequant_coef8[k_mod][j][i]*p_QScale->ScalingList8x8[0][temp]; + } + + if((!present[1]) || p_QScale->UseDefaultScalingMatrix8x8Flag[1]) + { + p_Quant->q_params_8x8[0][0][k][j][i].ScaleComp = (quant_coef8[k_mod][j][i]<<4)/Quant8_inter_default[temp]; + p_Quant->q_params_8x8[0][0][k][j][i].InvScaleComp = dequant_coef8[k_mod][j][i]*Quant8_inter_default[temp]; + } + else + { + p_Quant->q_params_8x8[0][0][k][j][i].ScaleComp = (quant_coef8[k_mod][j][i]<<4)/p_QScale->ScalingList8x8[1][temp]; + p_Quant->q_params_8x8[0][0][k][j][i].InvScaleComp = dequant_coef8[k_mod][j][i]*p_QScale->ScalingList8x8[1][temp]; + } + + if( n_ScalingList8x8 > 2 ) + { + + // Intra U + if(!present[2]) + { + p_Quant->q_params_8x8[1][1][k][j][i].ScaleComp = p_Quant->q_params_8x8[0][1][k][j][i].ScaleComp; + p_Quant->q_params_8x8[1][1][k][j][i].InvScaleComp = p_Quant->q_params_8x8[0][1][k][j][i].InvScaleComp; + } + else + { + p_Quant->q_params_8x8[1][1][k][j][i].ScaleComp = (quant_coef8[k_mod][j][i]<<4)/(p_QScale->UseDefaultScalingMatrix8x8Flag[2] ? Quant8_intra_default[temp]:p_QScale->ScalingList8x8[2][temp]); + p_Quant->q_params_8x8[1][1][k][j][i].InvScaleComp = dequant_coef8[k_mod][j][i]*(p_QScale->UseDefaultScalingMatrix8x8Flag[2] ? Quant8_intra_default[temp]:p_QScale->ScalingList8x8[2][temp]); + } + + // Inter U + if(!present[3]) + { + p_Quant->q_params_8x8[1][0][k][j][i].ScaleComp = p_Quant->q_params_8x8[0][0][k][j][i].ScaleComp; + p_Quant->q_params_8x8[1][0][k][j][i].InvScaleComp = p_Quant->q_params_8x8[0][0][k][j][i].InvScaleComp; + } + else + { + p_Quant->q_params_8x8[1][0][k][j][i].ScaleComp = (quant_coef8[k_mod][j][i]<<4)/(p_QScale->UseDefaultScalingMatrix8x8Flag[3] ? Quant8_inter_default[temp]:p_QScale->ScalingList8x8[3][temp]); + p_Quant->q_params_8x8[1][0][k][j][i].InvScaleComp = dequant_coef8[k_mod][j][i]*(p_QScale->UseDefaultScalingMatrix8x8Flag[3] ? Quant8_inter_default[temp]:p_QScale->ScalingList8x8[3][temp]); + } + + // Intra V + if(!present[4]) + { + p_Quant->q_params_8x8[2][1][k][j][i].ScaleComp = p_Quant->q_params_8x8[1][1][k][j][i].ScaleComp; + p_Quant->q_params_8x8[2][1][k][j][i].InvScaleComp = p_Quant->q_params_8x8[1][1][k][j][i].InvScaleComp; + } + else + { + p_Quant->q_params_8x8[2][1][k][j][i].ScaleComp = (quant_coef8[k_mod][j][i]<<4)/(p_QScale->UseDefaultScalingMatrix8x8Flag[4] ? Quant8_intra_default[temp]:p_QScale->ScalingList8x8[4][temp]); + p_Quant->q_params_8x8[2][1][k][j][i].InvScaleComp = dequant_coef8[k_mod][j][i]*(p_QScale->UseDefaultScalingMatrix8x8Flag[4] ? Quant8_intra_default[temp]:p_QScale->ScalingList8x8[4][temp]); + } + + // Inter V + if(!present[5]) + { + p_Quant->q_params_8x8[2][0][k][j][i].ScaleComp = p_Quant->q_params_8x8[1][0][k][j][i].ScaleComp; + p_Quant->q_params_8x8[2][0][k][j][i].InvScaleComp = p_Quant->q_params_8x8[1][0][k][j][i].InvScaleComp; + } + else + { + p_Quant->q_params_8x8[2][0][k][j][i].ScaleComp = (quant_coef8[k_mod][j][i]<<4)/(p_QScale->UseDefaultScalingMatrix8x8Flag[5] ? Quant8_inter_default[temp]:p_QScale->ScalingList8x8[5][temp]); + p_Quant->q_params_8x8[2][0][k][j][i].InvScaleComp = dequant_coef8[k_mod][j][i] * (p_QScale->UseDefaultScalingMatrix8x8Flag[5] ? Quant8_inter_default[temp]:p_QScale->ScalingList8x8[5][temp]); + } + } + } + } + } + } +} + diff --git a/lencod/src/q_offsets.c b/lencod/src/q_offsets.c new file mode 100644 index 0000000..01fa3b3 --- /dev/null +++ b/lencod/src/q_offsets.c @@ -0,0 +1,636 @@ + +/*! + ************************************************************************************* + * \file q_offsets.c + * + * \brief + * read Quantization Offset matrix parameters from input file: q_OffsetMatrix.cfg + * + ************************************************************************************* + */ + +#include "global.h" +#include "memalloc.h" +#include "q_matrix.h" +#include "q_offsets.h" + +extern char *GetConfigFileContent (char *Filename, int error_type); + +#define MAX_ITEMS_TO_PARSE 2000 + +int offset4x4_check[15] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; +int offset8x8_check[15] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + +static const char OffsetType4x4[15][24] = { + "INTRA4X4_LUMA_INTRA", + "INTRA4X4_CHROMAU_INTRA", + "INTRA4X4_CHROMAV_INTRA", + "INTRA4X4_LUMA_INTERP", + "INTRA4X4_CHROMAU_INTERP", + "INTRA4X4_CHROMAV_INTERP", + "INTRA4X4_LUMA_INTERB", + "INTRA4X4_CHROMAU_INTERB", + "INTRA4X4_CHROMAV_INTERB", + "INTER4X4_LUMA_INTERP", + "INTER4X4_CHROMAU_INTERP", + "INTER4X4_CHROMAV_INTERP", + "INTER4X4_LUMA_INTERB", + "INTER4X4_CHROMAU_INTERB", + "INTER4X4_CHROMAV_INTERB" +}; + +static const char OffsetType8x8[15][24] = { + "INTRA8X8_LUMA_INTRA", + "INTRA8X8_LUMA_INTERP", + "INTRA8X8_LUMA_INTERB", + "INTER8X8_LUMA_INTERP", + "INTER8X8_LUMA_INTERB", + "INTRA8X8_CHROMAU_INTRA", + "INTRA8X8_CHROMAU_INTERP", + "INTRA8X8_CHROMAU_INTERB", + "INTER8X8_CHROMAU_INTERP", + "INTER8X8_CHROMAU_INTERB", + "INTRA8X8_CHROMAV_INTRA", + "INTRA8X8_CHROMAV_INTERP", + "INTRA8X8_CHROMAV_INTERB", + "INTER8X8_CHROMAV_INTERP", + "INTER8X8_CHROMAV_INTERB" +}; + +static const short Offset_intra_default_intra[16] = { + 682, 682, 682, 682, + 682, 682, 682, 682, + 682, 682, 682, 682, + 682, 682, 682, 682 +}; + +static const short Offset_intra_default_chroma[16] = { + 682, 682, 682, 682, + 682, 682, 682, 682, + 682, 682, 682, 682, + 682, 682, 682, 682 +}; + + +static const short Offset_intra_default_inter[16] = { + 342, 342, 342, 342, + 342, 342, 342, 342, + 342, 342, 342, 342, + 342, 342, 342, 342, +}; + +static const short Offset_inter_default[16] = { + 342, 342, 342, 342, + 342, 342, 342, 342, + 342, 342, 342, 342, + 342, 342, 342, 342, +}; + +static const short Offset8_intra_default_intra[64] = { + 682, 682, 682, 682, 682, 682, 682, 682, + 682, 682, 682, 682, 682, 682, 682, 682, + 682, 682, 682, 682, 682, 682, 682, 682, + 682, 682, 682, 682, 682, 682, 682, 682, + 682, 682, 682, 682, 682, 682, 682, 682, + 682, 682, 682, 682, 682, 682, 682, 682, + 682, 682, 682, 682, 682, 682, 682, 682, + 682, 682, 682, 682, 682, 682, 682, 682 +}; + +static const short Offset8_intra_default_chroma[64] = { + 682, 682, 682, 682, 682, 682, 682, 682, + 682, 682, 682, 682, 682, 682, 682, 682, + 682, 682, 682, 682, 682, 682, 682, 682, + 682, 682, 682, 682, 682, 682, 682, 682, + 682, 682, 682, 682, 682, 682, 682, 682, + 682, 682, 682, 682, 682, 682, 682, 682, + 682, 682, 682, 682, 682, 682, 682, 682, + 682, 682, 682, 682, 682, 682, 682, 682 +}; + +static const short Offset8_intra_default_inter[64] = { + 342, 342, 342, 342, 342, 342, 342, 342, + 342, 342, 342, 342, 342, 342, 342, 342, + 342, 342, 342, 342, 342, 342, 342, 342, + 342, 342, 342, 342, 342, 342, 342, 342, + 342, 342, 342, 342, 342, 342, 342, 342, + 342, 342, 342, 342, 342, 342, 342, 342, + 342, 342, 342, 342, 342, 342, 342, 342, + 342, 342, 342, 342, 342, 342, 342, 342 +}; + +static const short Offset8_inter_default[64] = { + 342, 342, 342, 342, 342, 342, 342, 342, + 342, 342, 342, 342, 342, 342, 342, 342, + 342, 342, 342, 342, 342, 342, 342, 342, + 342, 342, 342, 342, 342, 342, 342, 342, + 342, 342, 342, 342, 342, 342, 342, 342, + 342, 342, 342, 342, 342, 342, 342, 342, + 342, 342, 342, 342, 342, 342, 342, 342, + 342, 342, 342, 342, 342, 342, 342, 342 +}; + + +// Function prototypes +void InitOffsetParam (QuantParameters *p_Quant, InputParameters *p_Inp); +/*! + *********************************************************************** + * \brief + * Allocate Q matrix arrays + *********************************************************************** + */ +static void allocate_QOffsets (QuantParameters *p_Quant, InputParameters *p_Inp) +{ + int max_bitdepth = imax(p_Inp->output.bit_depth[0], p_Inp->output.bit_depth[1]); + int max_qp = (3 + 6*(max_bitdepth)); + + + if (p_Inp->AdaptRoundingFixed) + { + get_mem3Dshort(&p_Quant->OffsetList4x4, 1, 25, 16); + get_mem3Dshort(&p_Quant->OffsetList8x8, 1, 15, 64); + } + else + { + get_mem3Dshort(&p_Quant->OffsetList4x4, max_qp + 1, 25, 16); + get_mem3Dshort(&p_Quant->OffsetList8x8, max_qp + 1, 15, 64); + } + + get_mem2Dshort(&p_Quant->OffsetList4x4input, 25, 16); + get_mem2Dshort(&p_Quant->OffsetList8x8input, 15, 64); +} + +static inline void update_q_offset4x4(LevelQuantParams **q_params, short *offsetList, int q_bits) +{ + int i, j; + short *p_list = &offsetList[0]; + for (j = 0; j < 4; j++) + { + for (i = 0; i < 4; i++) + { + q_params[j][i].OffsetComp = (int) *p_list++ << q_bits; + } + } +} + +static inline void update_q_offset8x8(LevelQuantParams **q_params, short *offsetList, int q_bits) +{ + int i, j; + short *p_list; + for (j = 0; j < 8; j++) + { + p_list = &offsetList[j << 3]; + for (i = 0; i < 8; i++) + { + q_params[j][i].OffsetComp = (int) *p_list++ << q_bits; + } + } +} + +/*! + *********************************************************************** + * \brief + * Free Q matrix arrays + *********************************************************************** + */ +void free_QOffsets (QuantParameters *p_Quant, InputParameters *p_Inp) +{ + free_mem3Dshort(p_Quant->OffsetList8x8); + free_mem3Dshort(p_Quant->OffsetList4x4); + free_mem2Dshort(p_Quant->OffsetList8x8input); + free_mem2Dshort(p_Quant->OffsetList4x4input); +} + + +/*! + *********************************************************************** + * \brief + * Check the parameter name. + * \param s + * parameter name string + * \param type + * 4x4 or 8x8 offset matrix type + * \return + * the index number if the string is a valid parameter name, \n + * -1 for error + *********************************************************************** + */ + +int CheckOffsetParameterName (char *s, int *type) +{ + int i = 0; + + *type = 0; + while ((OffsetType4x4[i] != NULL) && (i < 15)) + { + if (0 == strcmp (OffsetType4x4[i], s)) + return i; + else + i++; + } + + i = 0; + *type = 1; + while ((OffsetType8x8[i] != NULL) && (i < 15)) + { + if (0 == strcmp (OffsetType8x8[i], s)) + return i; + else + i++; + } + + return -1; +} + +/*! + *********************************************************************** + * \brief + * Parse the Q Offset Matrix values read from cfg file. + * \param p_Quant + * quantization parameters + * \param buf + * buffer to be parsed + * \param bufsize + * buffer size of buffer + *********************************************************************** + */ +void ParseQOffsetMatrix (QuantParameters *p_Quant, char *buf, int bufsize) +{ + char *items[MAX_ITEMS_TO_PARSE] = {NULL}; + int MapIdx; + int item = 0; + int InString = 0, InItem = 0; + char *p = buf; + char *bufend = &buf[bufsize]; + int IntContent; + int i, j, range, type, cnt; + short *OffsetList; + + while (p < bufend) + { + switch (*p) + { + //case 13: + //p++; + //break; + case '#': // Found comment + *p = '\0'; // Replace '#' with '\0' in case of comment immediately following integer or string + while ((*p != '\n' && *p != '\r') && p < bufend) // Skip till EOL or EOF, whichever comes first + p++; + InString = 0; + InItem = 0; + break; + case '\n': + case '\r': + InItem = 0; + InString = 0; + *p++ = '\0'; + break; + case ' ': + case '\t': // Skip whitespace, leave state unchanged + if (InString) + p++; + else + { // Terminate non-strings once whitespace is found + *p++ = '\0'; + InItem = 0; + } + break; + + case '"': // Begin/End of String + *p++ = '\0'; + if (!InString) + { + items[item++] = p; + InItem = ~InItem; + } + else + InItem = 0; + InString = ~InString; // Toggle + break; + + case ',': + p++; + InItem = 0; + break; + + default: + if (!InItem) + { + items[item++] = p; + InItem = ~InItem; + } + p++; + } + } + + item--; + + for (i = 0; i < item; i += cnt) + { + cnt = 0; + if (0 > (MapIdx = CheckOffsetParameterName (items[i + cnt], &type))) + { + snprintf (errortext, ET_SIZE, + " Parsing error in quantization offset config file: Parameter Name '%s' not recognized.", + items[i + cnt]); + error (errortext, 300); + } + cnt++; + if (strcmp ("=", items[i + cnt])) + { + snprintf (errortext, ET_SIZE, + " Parsing error in quantization offset config file: '=' expected as the second token in each item."); + error (errortext, 300); + } + cnt++; + + if (!type) //4x4 Matrix + { + range = 16; + OffsetList = p_Quant->OffsetList4x4input[MapIdx]; + offset4x4_check[MapIdx] = 1; //to indicate matrix found in cfg file + } + else //8x8 matrix + { + range = 64; + OffsetList = p_Quant->OffsetList8x8input[MapIdx]; + offset8x8_check[MapIdx] = 1; //to indicate matrix found in cfg file + } + + for (j = 0; j < range; j++) + { + if (1 != sscanf (items[i + cnt + j], "%d", &IntContent)) + { + snprintf (errortext, ET_SIZE, + " Parsing error in quantization offset file: Expected numerical value for Parameter of %s, found '%s'.", + items[i], items[i + cnt + j]); + error (errortext, 300); + } + + OffsetList[j] = (short) IntContent; //save value in matrix + } + cnt += j; + printf ("."); + } +} + + +/*! + *********************************************************************** + * \brief + * Initialise Q offset matrix values. + *********************************************************************** + */ +void init_qoffset (VideoParameters *p_Vid) +{ + char *content; + InputParameters *p_Inp = p_Vid->p_Inp; + + allocate_QOffsets (p_Vid->p_Quant, p_Inp); + + if (p_Inp->OffsetMatrixPresentFlag) + { + printf ("Parsing Quantization Offset Matrix file %s ", + p_Inp->QOffsetMatrixFile); + content = GetConfigFileContent (p_Inp->QOffsetMatrixFile, 0); + if (content != '\0') + ParseQOffsetMatrix (p_Vid->p_Quant, content, (int) strlen (content)); + else + { + printf ("\nError: %s\nProceeding with default values for all matrices.", errortext); + p_Inp->OffsetMatrixPresentFlag = 0; + } + + printf ("\n"); + + free (content); + } + //! Now set up all offset p_Inp. This process could be reused if we wish to re-init offsets + InitOffsetParam (p_Vid->p_Quant, p_Inp); +} + +/*! + ************************************************************************ + * \brief + * Init quantization offset parameters + * + * \par Input: + * none + * + * \par Output: + * none + ************************************************************************ + */ + +void InitOffsetParam (QuantParameters *p_Quant, InputParameters *p_Inp) +{ + int i, k; + int max_qp_luma = (4 + 6*(p_Inp->output.bit_depth[0])); + int max_qp_cr = (4 + 6*(p_Inp->output.bit_depth[1])); + + for (i = 0; i < (p_Inp->AdaptRoundingFixed ? 1 : imax(max_qp_luma, max_qp_cr)); i++) + { + if (p_Inp->OffsetMatrixPresentFlag) + { + memcpy(&(p_Quant->OffsetList4x4[i][0][0]),&(p_Quant->OffsetList4x4input[0][0]), 400 * sizeof(short)); // 25 * 16 + memcpy(&(p_Quant->OffsetList8x8[i][0][0]),&(p_Quant->OffsetList8x8input[0][0]), 960 * sizeof(short)); // 15 * 64 + } + else + { + // 0 (INTRA4X4_LUMA_INTRA) + memcpy(&(p_Quant->OffsetList4x4[i][0][0]),&(Offset_intra_default_intra[0]), 16 * sizeof(short)); + for (k = 1; k < 3; k++) // 1,2 (INTRA4X4_CHROMA_INTRA) + memcpy(&(p_Quant->OffsetList4x4[i][k][0]),&(Offset_intra_default_chroma[0]), 16 * sizeof(short)); + for (k = 3; k < 9; k++) // 3,4,5,6,7,8 (INTRA4X4_LUMA/CHROMA_INTERP/INTERB) + memcpy(&(p_Quant->OffsetList4x4[i][k][0]),&(Offset_intra_default_inter[0]), 16 * sizeof(short)); + for (k = 9; k < 25; k++) // 9,10,11,12,13,14 (INTER4X4) + memcpy(&(p_Quant->OffsetList4x4[i][k][0]),&(Offset_inter_default[0]), 16 * sizeof(short)); + + // 0 (INTRA8X8_LUMA_INTRA) + memcpy(&(p_Quant->OffsetList8x8[i][0][0]),&(Offset8_intra_default_intra[0]), 64 * sizeof(short)); + for (k = 1; k < 3; k++) // 1,2 (INTRA8X8_LUMA_INTERP/INTERB) + memcpy(&(p_Quant->OffsetList8x8[i][k][0]),&(Offset8_intra_default_inter[0]), 64 * sizeof(short)); + for (k = 3; k < 5; k++) // 3,4 (INTER8X8_LUMA_INTERP/INTERB) + memcpy(&(p_Quant->OffsetList8x8[i][k][0]),&(Offset8_inter_default[0]), 64 * sizeof(short)); + + // 5 (INTRA8X8_CHROMAU_INTRA) + memcpy(&(p_Quant->OffsetList8x8[i][5][0]),&(Offset8_intra_default_chroma[0]), 64 * sizeof(short)); + for (k = 6; k < 8; k++) // 6,7 (INTRA8X8_CHROMAU_INTERP/INTERB) + memcpy(&(p_Quant->OffsetList8x8[i][k][0]),&(Offset8_intra_default_inter[0]), 64 * sizeof(short)); + for (k = 8; k < 10; k++) // 8,9 (INTER8X8_CHROMAU_INTERP/INTERB) + memcpy(&(p_Quant->OffsetList8x8[i][k][0]),&(Offset8_inter_default[0]), 64 * sizeof(short)); + + // 10 (INTRA8X8_CHROMAV_INTRA) + memcpy(&(p_Quant->OffsetList8x8[i][10][0]),&(Offset8_intra_default_chroma[0]), 64 * sizeof(short)); + for (k = 11; k < 13; k++) // 11,12 (INTRA8X8_CHROMAV_INTERP/INTERB) + memcpy(&(p_Quant->OffsetList8x8[i][k][0]),&(Offset8_intra_default_inter[0]), 64 * sizeof(short)); + for (k = 13; k < 15; k++) // 8,9 (INTER8X8_CHROMAV_INTERP/INTERB) + memcpy(&(p_Quant->OffsetList8x8[i][k][0]),&(Offset8_inter_default[0]), 64 * sizeof(short)); + } + } +} + + +/*! + ************************************************************************ + * \brief + * Calculation of the quantization offset parameters at the frame level + * + * \par Input: + * none + * + * \par Output: + * none + ************************************************************************ + */ +void CalculateOffset4x4Param (VideoParameters *p_Vid) +{ + QuantParameters *p_Quant = p_Vid->p_Quant; + int k; + int qp_per, qp; + int img_type = ((p_Vid->type == SI_SLICE) ? I_SLICE : (p_Vid->type == SP_SLICE ? P_SLICE : p_Vid->type)); + + int max_qp_scale = imax(p_Vid->bitdepth_luma_qp_scale, p_Vid->bitdepth_chroma_qp_scale); + int max_qp = 51 + max_qp_scale; + InputParameters *p_Inp = p_Vid->p_Inp; + + p_Vid->AdaptRndWeight = p_Inp->AdaptRndWFactor [p_Vid->nal_reference_idc != 0][img_type]; + p_Vid->AdaptRndCrWeight = p_Inp->AdaptRndCrWFactor[p_Vid->nal_reference_idc != 0][img_type]; + + if (img_type == I_SLICE ) + { + for (qp = 0; qp < max_qp + 1; qp++) + { + k = p_Quant->qp_per_matrix [qp]; + qp_per = Q_BITS + k - OffsetBits; + k = p_Inp->AdaptRoundingFixed ? 0 : qp; + + // Intra4x4 luma + update_q_offset4x4(p_Quant->q_params_4x4[0][1][qp], p_Quant->OffsetList4x4[k][ 0], qp_per); + // Intra4x4 chroma u + update_q_offset4x4(p_Quant->q_params_4x4[1][1][qp], p_Quant->OffsetList4x4[k][ 1], qp_per); + // Intra4x4 chroma v + update_q_offset4x4(p_Quant->q_params_4x4[2][1][qp], p_Quant->OffsetList4x4[k][ 2], qp_per); + } + } + else if (img_type == B_SLICE) + { + for (qp = 0; qp < max_qp + 1; qp++) + { + k = p_Quant->qp_per_matrix [qp]; + qp_per = Q_BITS + k - OffsetBits; + k = p_Inp->AdaptRoundingFixed ? 0 : qp; + + // Inter4x4 luma + update_q_offset4x4(p_Quant->q_params_4x4[0][0][qp], p_Quant->OffsetList4x4[k][12], qp_per); + // Intra4x4 luma + update_q_offset4x4(p_Quant->q_params_4x4[0][1][qp], p_Quant->OffsetList4x4[k][ 6], qp_per); + // Inter4x4 chroma u + update_q_offset4x4(p_Quant->q_params_4x4[1][0][qp], p_Quant->OffsetList4x4[k][13], qp_per); + // Intra4x4 chroma u + update_q_offset4x4(p_Quant->q_params_4x4[1][1][qp], p_Quant->OffsetList4x4[k][ 7], qp_per); + // Inter4x4 chroma v + update_q_offset4x4(p_Quant->q_params_4x4[2][0][qp], p_Quant->OffsetList4x4[k][14], qp_per); + // Intra4x4 chroma v + update_q_offset4x4(p_Quant->q_params_4x4[2][1][qp], p_Quant->OffsetList4x4[k][ 8], qp_per); + } + } + else + { + for (qp = 0; qp < max_qp + 1; qp++) + { + k = p_Quant->qp_per_matrix [qp]; + qp_per = Q_BITS + k - OffsetBits; + k = p_Inp->AdaptRoundingFixed ? 0 : qp; + + // Inter4x4 luma + update_q_offset4x4(p_Quant->q_params_4x4[0][0][qp], p_Quant->OffsetList4x4[k][ 9], qp_per); + // Intra4x4 luma + update_q_offset4x4(p_Quant->q_params_4x4[0][1][qp], p_Quant->OffsetList4x4[k][ 3], qp_per); + // Inter4x4 chroma u + update_q_offset4x4(p_Quant->q_params_4x4[1][0][qp], p_Quant->OffsetList4x4[k][10], qp_per); + // Intra4x4 chroma u + update_q_offset4x4(p_Quant->q_params_4x4[1][1][qp], p_Quant->OffsetList4x4[k][ 4], qp_per); + // Inter4x4 chroma v + update_q_offset4x4(p_Quant->q_params_4x4[2][0][qp], p_Quant->OffsetList4x4[k][11], qp_per); + // Intra4x4 chroma v + update_q_offset4x4(p_Quant->q_params_4x4[2][1][qp], p_Quant->OffsetList4x4[k][ 5], qp_per); + } + } +} + +/*! + ************************************************************************ + * \brief + * Calculate the quantisation offset parameters + * + ************************************************************************ +*/ +void CalculateOffset8x8Param (VideoParameters *p_Vid) +{ + QuantParameters *p_Quant = p_Vid->p_Quant; + int k; + int q_bits, qp; + + int max_qp_scale = imax(p_Vid->bitdepth_luma_qp_scale, p_Vid->bitdepth_chroma_qp_scale); + int max_qp = 51 + max_qp_scale; + InputParameters *p_Inp = p_Vid->p_Inp; + + if (p_Vid->type == I_SLICE || p_Vid->type == SI_SLICE ) + { + for (qp = 0; qp < max_qp + 1; qp++) + { + q_bits = Q_BITS_8 + p_Quant->qp_per_matrix[qp] - OffsetBits; + k = p_Inp->AdaptRoundingFixed ? 0 : qp; + // INTRA8X8 + update_q_offset8x8(p_Quant->q_params_8x8[0][1][qp], p_Quant->OffsetList8x8[k][0], q_bits); + // INTRA8X8 CHROMAU + update_q_offset8x8(p_Quant->q_params_8x8[1][1][qp], p_Quant->OffsetList8x8[k][5], q_bits); + // INTRA8X8 CHROMAV + update_q_offset8x8(p_Quant->q_params_8x8[2][1][qp], p_Quant->OffsetList8x8[k][10], q_bits); + } + } + else if ((p_Vid->type == P_SLICE) || (p_Vid->type == SP_SLICE)) + { + for (qp = 0; qp < max_qp + 1; qp++) + { + q_bits = Q_BITS_8 + p_Quant->qp_per_matrix[qp] - OffsetBits; + k = p_Inp->AdaptRoundingFixed ? 0 : qp; + + // INTER8X8 + update_q_offset8x8(p_Quant->q_params_8x8[0][0][qp], p_Quant->OffsetList8x8[k][3], q_bits); + // INTRA8X8 + update_q_offset8x8(p_Quant->q_params_8x8[0][1][qp], p_Quant->OffsetList8x8[k][1], q_bits); + // INTER8X8 CHROMAU + update_q_offset8x8(p_Quant->q_params_8x8[1][0][qp], p_Quant->OffsetList8x8[k][8], q_bits); + // INTRA8X8 CHROMAU + update_q_offset8x8(p_Quant->q_params_8x8[1][1][qp], p_Quant->OffsetList8x8[k][6], q_bits); + // INTER8X8 CHROMAV + update_q_offset8x8(p_Quant->q_params_8x8[2][0][qp], p_Quant->OffsetList8x8[k][13], q_bits); + // INTRA8X8 CHROMAV + update_q_offset8x8(p_Quant->q_params_8x8[2][1][qp], p_Quant->OffsetList8x8[k][11], q_bits); + } + } + else + { + for (qp = 0; qp < max_qp + 1; qp++) + { + q_bits = Q_BITS_8 + p_Quant->qp_per_matrix[qp] - OffsetBits; + k = p_Inp->AdaptRoundingFixed ? 0 : qp; + + // INTER8X8 + update_q_offset8x8(p_Quant->q_params_8x8[0][0][qp], p_Quant->OffsetList8x8[k][4], q_bits); + // INTRA8X8 + update_q_offset8x8(p_Quant->q_params_8x8[0][1][qp], p_Quant->OffsetList8x8[k][2], q_bits); + // INTER8X8 CHROMAU + update_q_offset8x8(p_Quant->q_params_8x8[1][0][qp], p_Quant->OffsetList8x8[k][9], q_bits); + // INTRA8X8 CHROMAU + update_q_offset8x8(p_Quant->q_params_8x8[1][1][qp], p_Quant->OffsetList8x8[k][7], q_bits); + // INTER8X8 CHROMAV + update_q_offset8x8(p_Quant->q_params_8x8[2][0][qp], p_Quant->OffsetList8x8[k][14], q_bits); + // INTRA8X8 CHROMAV + update_q_offset8x8(p_Quant->q_params_8x8[2][1][qp], p_Quant->OffsetList8x8[k][12], q_bits); + } + } +} diff --git a/lencod/src/quant4x4.c b/lencod/src/quant4x4.c new file mode 100644 index 0000000..ad8297e --- /dev/null +++ b/lencod/src/quant4x4.c @@ -0,0 +1,66 @@ + +/*! + ************************************************************************************* + * \file quant4x4.c + * + * \brief + * Quantization process for a 4x4 block + * + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * - Alexis Michael Tourapis + * + ************************************************************************************* + */ + +#include "contributors.h" +#include "global.h" +#include "quant4x4.h" + +/*! +************************************************************************ +* \brief +* Quantization initialization function +* +************************************************************************ +*/ +void init_quant_4x4(Slice *currSlice) +{ + VideoParameters *p_Vid = currSlice->p_Vid; + InputParameters *p_Inp = currSlice->p_Inp; + + if (currSlice->UseRDOQuant == 1) + { + currSlice->quant_4x4 = quant_4x4_trellis; + if (p_Inp->RDOQ_DC == 1) + currSlice->quant_dc4x4 = quant_dc4x4_trellis; + else + currSlice->quant_dc4x4 = quant_dc4x4_normal; + currSlice->quant_ac4x4 = quant_ac4x4_trellis; + if (currSlice->symbol_mode == CAVLC) + { + currSlice->rdoq_4x4 = rdoq_4x4_CAVLC; + currSlice->rdoq_dc = rdoq_dc_CAVLC; + currSlice->rdoq_ac4x4 = rdoq_ac4x4_CAVLC; + } + else + { + currSlice->rdoq_4x4 = rdoq_4x4_CABAC; + currSlice->rdoq_dc = rdoq_dc_CABAC; + currSlice->rdoq_ac4x4 = rdoq_ac4x4_CABAC; + } + } + else if (p_Vid->AdaptiveRounding) + { + currSlice->quant_4x4 = quant_4x4_around; + currSlice->quant_dc4x4 = quant_dc4x4_normal; + currSlice->quant_ac4x4 = quant_ac4x4_around; + } + else + { + currSlice->quant_4x4 = quant_4x4_normal; + currSlice->quant_dc4x4 = quant_dc4x4_normal; + currSlice->quant_ac4x4 = quant_ac4x4_normal; + } +} + diff --git a/lencod/src/quant4x4_2step.c b/lencod/src/quant4x4_2step.c new file mode 100644 index 0000000..4b5d375 --- /dev/null +++ b/lencod/src/quant4x4_2step.c @@ -0,0 +1,300 @@ + +/*! + ************************************************************************************* + * \file quant4x4_2step.c + * + * \brief + * Quantization process for a 4x4 block + * + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * - Alexis Michael Tourapis + * + ************************************************************************************* + */ + +#include "contributors.h" + +#include + +#include "global.h" + +#include "image.h" +#include "mb_access.h" +#include "vlc.h" +#include "transform.h" +#include "mc_prediction.h" +#include "q_offsets.h" +#include "q_matrix.h" +#include "quant4x4.h" + +/*! + ************************************************************************ + * \brief + * Quantization process for All coefficients for a 4x4 block + * + ************************************************************************ + */ +int quant_4x4_2step(Macroblock *currMB, int **tblock, struct quant_methods *q_method) +{ + VideoParameters *p_Vid = currMB->p_Vid; + QuantParameters *p_Quant = p_Vid->p_Quant; + Slice *currSlice = currMB->p_Slice; + Boolean is_cavlc = (Boolean) (currSlice->symbol_mode == CAVLC); + + int block_x = q_method->block_x; + int qp = q_method->qp; + int* ACL = &q_method->ACLevel[0]; + int* ACR = &q_method->ACRun[0]; + LevelQuantParams **q_params_4x4 = q_method->q_params; + const byte (*pos_scan)[2] = q_method->pos_scan; + const byte *c_cost = q_method->c_cost; + int *coeff_cost = q_method->coeff_cost; + + + LevelQuantParams *q_params = NULL; + int i,j, coeff_ctr; + + int *m7; + int scaled_coeff; + + int level, run = 0; + int nonzero = FALSE; + int qp_per = p_Quant->qp_per_matrix[qp]; + int q_bits = Q_BITS + qp_per; + const byte *p_scan = &pos_scan[0][0]; + + // Quantization + for (coeff_ctr = 0; coeff_ctr < 16; ++coeff_ctr) + { + i = *p_scan++; // horizontal position + j = *p_scan++; // vertical position + + m7 = &tblock[j][block_x + i]; + + if (*m7 != 0) + { + q_params = &q_params_4x4[j][i]; + scaled_coeff = iabs (*m7) * q_params->ScaleComp; + level = (scaled_coeff + q_params->OffsetComp) >> q_bits; + + if (level != 0) + { + if (is_cavlc) + level = imin(level, CAVLC_LEVEL_LIMIT); + + *coeff_cost += (level > 1) ? MAX_VALUE : c_cost[run]; + + level = isignab(level, *m7); + *m7 = rshift_rnd_sf(((level * q_params->InvScaleComp) << qp_per), 4); + // inverse scale can be alternative performed as follows to ensure 16bit + // arithmetic is satisfied. + // *m7 = (qp_per<4) ? rshift_rnd_sf((level*q_params->InvScaleComp),4-qp_per) : (level*q_params->InvScaleComp)<<(qp_per-4); + *ACL++ = level; + *ACR++ = run; + // reset zero level counter + run = 0; + nonzero = TRUE; + } + else + { + *m7 = 0; + ++run; + } + } + else + { + ++run; + } + } + + *ACL = 0; + + return nonzero; +} + +int quant_ac4x4_2step(Macroblock *currMB, int **tblock, struct quant_methods *q_method) +{ + int block_x = q_method->block_x; + + int qp = q_method->qp; + int* ACL = &q_method->ACLevel[0]; + int* ACR = &q_method->ACRun[0]; + LevelQuantParams **q_params_4x4 = q_method->q_params; + const byte (*pos_scan)[2] = q_method->pos_scan; + const byte *c_cost = q_method->c_cost; + int *coeff_cost = q_method->coeff_cost; + + Boolean is_cavlc = (Boolean) (currMB->p_Slice->symbol_mode == CAVLC); + VideoParameters *p_Vid = currMB->p_Vid; + QuantParameters *p_Quant = p_Vid->p_Quant; + LevelQuantParams *q_params = NULL; + + int i,j, coeff_ctr; + + int *m7; + int scaled_coeff; + + int level, run = 0; + int nonzero = FALSE; + int qp_per = p_Quant->qp_per_matrix[qp]; + int q_bits = Q_BITS + qp_per; + const byte *p_scan1 = &pos_scan[1][0]; + const byte *p_scan = &pos_scan[1][0]; + int temp_block[16][16]; + + int qp_per2 = p_Quant->qp_per_matrix[51]; + int q_bits2 = Q_BITS + qp_per2; + int coeff; + + + // First pass quantization + for (coeff_ctr = 1; coeff_ctr < 16; ++coeff_ctr) + { + i = *p_scan1++; // horizontal position + j = *p_scan1++; // vertical position + + coeff = tblock[j][block_x + i]; + m7 = &temp_block[j][block_x + i]; + if (coeff != 0) + { + q_params = &q_params_4x4[j][i]; + scaled_coeff = iabs (coeff); + level = (scaled_coeff * q_params->ScaleComp) >> q_bits2; + //level = (scaled_coeff) >> q_bits2; + + if (level != 0) + { + if (is_cavlc) + level = imin(level, CAVLC_LEVEL_LIMIT); + + level = isignab(level, coeff); + *m7 = (level << q_bits2) / q_params->ScaleComp; + //*m7 = (level << q_bits2); + } + else + { + *m7 = 0; + } + } + else + *m7 = 0; + } + + // Quantization + for (coeff_ctr = 1; coeff_ctr < 16; ++coeff_ctr) + { + i = *p_scan++; // horizontal position + j = *p_scan++; // vertical position + + coeff = temp_block[j][block_x + i]; + m7 = &tblock[j][block_x + i]; + if (*m7 != 0) + { + q_params = &q_params_4x4[j][i]; + scaled_coeff = iabs (coeff) * q_params->ScaleComp; + level = (scaled_coeff + q_params->OffsetComp) >> q_bits; + + if (level != 0) + { + if (is_cavlc) + level = imin(level, CAVLC_LEVEL_LIMIT); + + *coeff_cost += (level > 1) ? MAX_VALUE : c_cost[run]; + + level = isignab(level, coeff); + *m7 = rshift_rnd_sf(((level * q_params->InvScaleComp) << qp_per), 4); + // inverse scale can be alternative performed as follows to ensure 16bit + // arithmetic is satisfied. + // *m7 = (qp_per<4) ? rshift_rnd_sf((level*q_params->InvScaleComp),4-qp_per) : (level*q_params->InvScaleComp)<<(qp_per-4); + *ACL++ = level; + *ACR++ = run; + // reset zero level counter + run = 0; + nonzero = TRUE; + } + else + { + *m7 = 0; + ++run; + } + } + else + { + *m7 = 0; + ++run; + } + } + + *ACL = 0; + + return nonzero; +} + +/*! + ************************************************************************ + * \brief + * Quantization process for All coefficients for a 4x4 DC block + * + ************************************************************************ + */ +int quant_dc4x4_2step(Macroblock *currMB, int **tblock, int qp, int* DCLevel, int* DCRun, + LevelQuantParams *q_params_4x4, const byte (*pos_scan)[2]) +{ + QuantParameters *p_Quant = currMB->p_Vid->p_Quant; + Boolean is_cavlc = (Boolean) (currMB->p_Slice->symbol_mode == CAVLC); + int i,j, coeff_ctr; + + int *m7; + int scaled_coeff; + + int level, run = 0; + int nonzero = FALSE; + int qp_per = p_Quant->qp_per_matrix[qp]; + int q_bits = Q_BITS + qp_per + 1; + const byte *p_scan = &pos_scan[0][0]; + int* DCL = &DCLevel[0]; + int* DCR = &DCRun[0]; + + // Quantization + for (coeff_ctr = 0; coeff_ctr < 16; ++coeff_ctr) + { + i = *p_scan++; // horizontal position + j = *p_scan++; // vertical position + + m7 = &tblock[j][i]; + + if (*m7 != 0) + { + scaled_coeff = iabs (*m7) * q_params_4x4->ScaleComp; + level = (scaled_coeff + (q_params_4x4->OffsetComp << 1) ) >> q_bits; + + if (level != 0) + { + if (is_cavlc) + level = imin(level, CAVLC_LEVEL_LIMIT); + + level = isignab(level, *m7); + *m7 = level; + *DCL++ = level; + *DCR++ = run; + // reset zero level counter + run = 0; + nonzero = TRUE; + } + else + { + ++run; + *m7 = 0; + } + } + else + { + ++run; + } + } + + *DCL = 0; + + return nonzero; +} diff --git a/lencod/src/quant4x4_around.c b/lencod/src/quant4x4_around.c new file mode 100644 index 0000000..faee08f --- /dev/null +++ b/lencod/src/quant4x4_around.c @@ -0,0 +1,279 @@ + +/*! + ************************************************************************************* + * \file quant4x4_around.c + * + * \brief + * Quantization process for a 4x4 block + * + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * - Alexis Michael Tourapis + * - Limin Liu + * + ************************************************************************************* + */ + +#include "contributors.h" + +#include + +#include "global.h" + +#include "image.h" +#include "mb_access.h" +#include "vlc.h" +#include "transform.h" +#include "mc_prediction.h" +#include "q_offsets.h" +#include "q_matrix.h" +#include "quant4x4.h" + + +/*! + ************************************************************************ + * \brief + * Quantization process for All coefficients for a 4x4 block + * + ************************************************************************ + */ +int quant_4x4_around(Macroblock *currMB, int **tblock, struct quant_methods *q_method) +{ + VideoParameters *p_Vid = currMB->p_Vid; + QuantParameters *p_Quant = p_Vid->p_Quant; + Slice *currSlice = currMB->p_Slice; + Boolean is_cavlc = (Boolean) (currSlice->symbol_mode == CAVLC); + + int AdaptRndWeight = p_Vid->AdaptRndWeight; + + int block_x = q_method->block_x; + int qp = q_method->qp; + int* ACL = &q_method->ACLevel[0]; + int* ACR = &q_method->ACRun[0]; + LevelQuantParams **q_params_4x4 = q_method->q_params; + const byte (*pos_scan)[2] = q_method->pos_scan; + const byte *c_cost = q_method->c_cost; + int *coeff_cost = q_method->coeff_cost; + + + LevelQuantParams *q_params = NULL; + int **fadjust4x4 = q_method->fadjust; + + int i,j, coeff_ctr; + + int *m7; + int scaled_coeff; + + int level, run = 0; + int nonzero = FALSE; + int qp_per = p_Quant->qp_per_matrix[qp]; + int q_bits = Q_BITS + qp_per; + const byte *p_scan = &pos_scan[0][0]; + + int* padjust4x4; + + // Quantization + for (coeff_ctr = 0; coeff_ctr < 16; ++coeff_ctr) + { + i = *p_scan++; // horizontal position + j = *p_scan++; // vertical position + + padjust4x4 = &fadjust4x4[j][block_x + i]; + m7 = &tblock[j][block_x + i]; + + if (*m7 != 0) + { + q_params = &q_params_4x4[j][i]; + scaled_coeff = iabs (*m7) * q_params->ScaleComp; + level = (scaled_coeff + q_params->OffsetComp) >> q_bits; + + if (level != 0) + { + if (is_cavlc) + level = imin(level, CAVLC_LEVEL_LIMIT); + + *padjust4x4 = rshift_rnd_sf((AdaptRndWeight * (scaled_coeff - (level << q_bits))), q_bits + 1); + + *coeff_cost += (level > 1) ? MAX_VALUE : c_cost[run]; + + level = isignab(level, *m7); + *m7 = rshift_rnd_sf(((level * q_params->InvScaleComp) << qp_per), 4); + // inverse scale can be alternative performed as follows to ensure 16bit + // arithmetic is satisfied. + // *m7 = (qp_per<4) ? rshift_rnd_sf((level*q_params->InvScaleComp),4-qp_per) : (level*q_params->InvScaleComp)<<(qp_per-4); + *ACL++ = level; + *ACR++ = run; + // reset zero level counter + run = 0; + nonzero = TRUE; + } + else + { + *padjust4x4 = 0; + *m7 = 0; + ++run; + } + } + else + { + *padjust4x4 = 0; + ++run; + } + } + + *ACL = 0; + + return nonzero; +} + +int quant_ac4x4_around(Macroblock *currMB, int **tblock, struct quant_methods *q_method) +{ + int block_x = q_method->block_x; + + int qp = q_method->qp; + int* ACL = &q_method->ACLevel[0]; + int* ACR = &q_method->ACRun[0]; + LevelQuantParams **q_params_4x4 = q_method->q_params; + int **fadjust4x4 = q_method->fadjust; + const byte (*pos_scan)[2] = q_method->pos_scan; + const byte *c_cost = q_method->c_cost; + int *coeff_cost = q_method->coeff_cost; + + Boolean is_cavlc = (Boolean) (currMB->p_Slice->symbol_mode == CAVLC); + VideoParameters *p_Vid = currMB->p_Vid; + QuantParameters *p_Quant = p_Vid->p_Quant; + int AdaptRndWeight = p_Vid->AdaptRndWeight; + LevelQuantParams *q_params = NULL; + + int i,j, coeff_ctr; + + int *m7; + int scaled_coeff; + + int level, run = 0; + int nonzero = FALSE; + int qp_per = p_Quant->qp_per_matrix[qp]; + int q_bits = Q_BITS + qp_per; + const byte *p_scan = &pos_scan[1][0]; + int* padjust4x4; + + // Quantization + for (coeff_ctr = 1; coeff_ctr < 16; ++coeff_ctr) + { + i = *p_scan++; // horizontal position + j = *p_scan++; // vertical position + + padjust4x4 = &fadjust4x4[j][block_x + i]; + m7 = &tblock[j][block_x + i]; + if (*m7 != 0) + { + q_params = &q_params_4x4[j][i]; + scaled_coeff = iabs (*m7) * q_params->ScaleComp; + level = (scaled_coeff + q_params->OffsetComp) >> q_bits; + + if (level != 0) + { + if (is_cavlc) + level = imin(level, CAVLC_LEVEL_LIMIT); + + *padjust4x4 = rshift_rnd_sf((AdaptRndWeight * (scaled_coeff - (level << q_bits))), (q_bits + 1)); + + *coeff_cost += (level > 1) ? MAX_VALUE : c_cost[run]; + + level = isignab(level, *m7); + *m7 = rshift_rnd_sf(((level * q_params->InvScaleComp) << qp_per), 4); + // inverse scale can be alternative performed as follows to ensure 16bit + // arithmetic is satisfied. + // *m7 = (qp_per<4) ? rshift_rnd_sf((level*q_params->InvScaleComp),4-qp_per) : (level*q_params->InvScaleComp)<<(qp_per-4); + *ACL++ = level; + *ACR++ = run; + // reset zero level counter + run = 0; + nonzero = TRUE; + } + else + { + *padjust4x4 = 0; + *m7 = 0; + ++run; + } + } + else + { + *padjust4x4 = 0; + ++run; + } + } + + *ACL = 0; + + return nonzero; +} + +/*! + ************************************************************************ + * \brief + * Quantization process for All coefficients for a 4x4 DC block + * + ************************************************************************ + */ +int quant_dc4x4_around(Macroblock *currMB, int **tblock, int qp, int* DCLevel, int* DCRun, + LevelQuantParams *q_params_4x4, const byte (*pos_scan)[2]) +{ + QuantParameters *p_Quant = currMB->p_Vid->p_Quant; + Boolean is_cavlc = (Boolean) (currMB->p_Slice->symbol_mode == CAVLC); + int i,j, coeff_ctr; + + int *m7; + int scaled_coeff; + + int level, run = 0; + int nonzero = FALSE; + int qp_per = p_Quant->qp_per_matrix[qp]; + int q_bits = Q_BITS + qp_per + 1; + const byte *p_scan = &pos_scan[0][0]; + int* DCL = &DCLevel[0]; + int* DCR = &DCRun[0]; + + // Quantization + for (coeff_ctr = 0; coeff_ctr < 16; ++coeff_ctr) + { + i = *p_scan++; // horizontal position + j = *p_scan++; // vertical position + + m7 = &tblock[j][i]; + + if (*m7 != 0) + { + scaled_coeff = iabs (*m7) * q_params_4x4->ScaleComp; + level = (scaled_coeff + (q_params_4x4->OffsetComp << 1) ) >> q_bits; + + if (level != 0) + { + if (is_cavlc) + level = imin(level, CAVLC_LEVEL_LIMIT); + + level = isignab(level, *m7); + *m7 = level; + *DCL++ = level; + *DCR++ = run; + // reset zero level counter + run = 0; + nonzero = TRUE; + } + else + { + ++run; + *m7 = 0; + } + } + else + { + ++run; + } + } + + *DCL = 0; + + return nonzero; +} diff --git a/lencod/src/quant4x4_normal.c b/lencod/src/quant4x4_normal.c new file mode 100644 index 0000000..1f23aa5 --- /dev/null +++ b/lencod/src/quant4x4_normal.c @@ -0,0 +1,259 @@ + +/*! + ************************************************************************************* + * \file quant4x4_normal.c + * + * \brief + * Quantization process for a 4x4 block + * + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * - Alexis Michael Tourapis + * - Limin Liu + * + ************************************************************************************* + */ + +#include "contributors.h" + +#include + +#include "global.h" + +#include "image.h" +#include "mb_access.h" +#include "vlc.h" +#include "transform.h" +#include "mc_prediction.h" +#include "q_offsets.h" +#include "q_matrix.h" +#include "quant4x4.h" + +/*! + ************************************************************************ + * \brief + * Quantization process for All coefficients for a 4x4 block + * + ************************************************************************ + */ +int quant_4x4_normal(Macroblock *currMB, int **tblock, struct quant_methods *q_method) +{ + VideoParameters *p_Vid = currMB->p_Vid; + QuantParameters *p_Quant = p_Vid->p_Quant; + Slice *currSlice = currMB->p_Slice; + Boolean is_cavlc = (Boolean) (currSlice->symbol_mode == CAVLC); + + int block_x = q_method->block_x; + int qp = q_method->qp; + int* ACL = &q_method->ACLevel[0]; + int* ACR = &q_method->ACRun[0]; + LevelQuantParams **q_params_4x4 = q_method->q_params; + const byte (*pos_scan)[2] = q_method->pos_scan; + const byte *c_cost = q_method->c_cost; + int *coeff_cost = q_method->coeff_cost; + + + LevelQuantParams *q_params = NULL; + int i,j, coeff_ctr; + + int *m7; + int scaled_coeff; + + int level, run = 0; + int nonzero = FALSE; + int qp_per = p_Quant->qp_per_matrix[qp]; + int q_bits = Q_BITS + qp_per; + const byte *p_scan = &pos_scan[0][0]; + + // Quantization + for (coeff_ctr = 0; coeff_ctr < 16; ++coeff_ctr) + { + i = *p_scan++; // horizontal position + j = *p_scan++; // vertical position + + m7 = &tblock[j][block_x + i]; + + if (*m7 != 0) + { + q_params = &q_params_4x4[j][i]; + scaled_coeff = iabs (*m7) * q_params->ScaleComp; + level = (scaled_coeff + q_params->OffsetComp) >> q_bits; + + if (level != 0) + { + if (is_cavlc) + level = imin(level, CAVLC_LEVEL_LIMIT); + + *coeff_cost += (level > 1) ? MAX_VALUE : c_cost[run]; + + level = isignab(level, *m7); + *m7 = rshift_rnd_sf(((level * q_params->InvScaleComp) << qp_per), 4); + // inverse scale can be alternative performed as follows to ensure 16bit + // arithmetic is satisfied. + // *m7 = (qp_per<4) ? rshift_rnd_sf((level*q_params->InvScaleComp),4-qp_per) : (level*q_params->InvScaleComp)<<(qp_per-4); + *ACL++ = level; + *ACR++ = run; + // reset zero level counter + run = 0; + nonzero = TRUE; + } + else + { + *m7 = 0; + ++run; + } + } + else + { + ++run; + } + } + + *ACL = 0; + + return nonzero; +} + +int quant_ac4x4_normal(Macroblock *currMB, int **tblock, struct quant_methods *q_method) +{ + int block_x = q_method->block_x; + + int qp = q_method->qp; + int* ACL = &q_method->ACLevel[0]; + int* ACR = &q_method->ACRun[0]; + LevelQuantParams **q_params_4x4 = q_method->q_params; + const byte (*pos_scan)[2] = q_method->pos_scan; + const byte *c_cost = q_method->c_cost; + int *coeff_cost = q_method->coeff_cost; + + Boolean is_cavlc = (Boolean) (currMB->p_Slice->symbol_mode == CAVLC); + VideoParameters *p_Vid = currMB->p_Vid; + QuantParameters *p_Quant = p_Vid->p_Quant; + LevelQuantParams *q_params = NULL; + + int i,j, coeff_ctr; + + int *m7; + int scaled_coeff; + + int level, run = 0; + int nonzero = FALSE; + int qp_per = p_Quant->qp_per_matrix[qp]; + int q_bits = Q_BITS + qp_per; + const byte *p_scan = &pos_scan[1][0]; + + // Quantization + for (coeff_ctr = 1; coeff_ctr < 16; ++coeff_ctr) + { + i = *p_scan++; // horizontal position + j = *p_scan++; // vertical position + + m7 = &tblock[j][block_x + i]; + if (*m7 != 0) + { + q_params = &q_params_4x4[j][i]; + scaled_coeff = iabs (*m7) * q_params->ScaleComp; + level = (scaled_coeff + q_params->OffsetComp) >> q_bits; + + if (level != 0) + { + if (is_cavlc) + level = imin(level, CAVLC_LEVEL_LIMIT); + + *coeff_cost += (level > 1) ? MAX_VALUE : c_cost[run]; + + level = isignab(level, *m7); + *m7 = rshift_rnd_sf(((level * q_params->InvScaleComp) << qp_per), 4); + // inverse scale can be alternative performed as follows to ensure 16bit + // arithmetic is satisfied. + // *m7 = (qp_per<4) ? rshift_rnd_sf((level*q_params->InvScaleComp),4-qp_per) : (level*q_params->InvScaleComp)<<(qp_per-4); + *ACL++ = level; + *ACR++ = run; + // reset zero level counter + run = 0; + nonzero = TRUE; + } + else + { + *m7 = 0; + ++run; + } + } + else + { + ++run; + } + } + + *ACL = 0; + + return nonzero; +} + +/*! + ************************************************************************ + * \brief + * Quantization process for All coefficients for a 4x4 DC block + * + ************************************************************************ + */ +int quant_dc4x4_normal(Macroblock *currMB, int **tblock, int qp, int* DCLevel, int* DCRun, + LevelQuantParams *q_params_4x4, const byte (*pos_scan)[2]) +{ + QuantParameters *p_Quant = currMB->p_Vid->p_Quant; + Boolean is_cavlc = (Boolean) (currMB->p_Slice->symbol_mode == CAVLC); + int i,j, coeff_ctr; + + int *m7; + int scaled_coeff; + + int level, run = 0; + int nonzero = FALSE; + int qp_per = p_Quant->qp_per_matrix[qp]; + int q_bits = Q_BITS + qp_per + 1; + const byte *p_scan = &pos_scan[0][0]; + int* DCL = &DCLevel[0]; + int* DCR = &DCRun[0]; + + // Quantization + for (coeff_ctr = 0; coeff_ctr < 16; ++coeff_ctr) + { + i = *p_scan++; // horizontal position + j = *p_scan++; // vertical position + + m7 = &tblock[j][i]; + + if (*m7 != 0) + { + scaled_coeff = iabs (*m7) * q_params_4x4->ScaleComp; + level = (scaled_coeff + (q_params_4x4->OffsetComp << 1) ) >> q_bits; + + if (level != 0) + { + if (is_cavlc) + level = imin(level, CAVLC_LEVEL_LIMIT); + + level = isignab(level, *m7); + *m7 = level; + *DCL++ = level; + *DCR++ = run; + // reset zero level counter + run = 0; + nonzero = TRUE; + } + else + { + ++run; + *m7 = 0; + } + } + else + { + ++run; + } + } + + *DCL = 0; + + return nonzero; +} diff --git a/lencod/src/quant4x4_trellis.c b/lencod/src/quant4x4_trellis.c new file mode 100644 index 0000000..d4b43b4 --- /dev/null +++ b/lencod/src/quant4x4_trellis.c @@ -0,0 +1,433 @@ + +/*! + ************************************************************************************* + * \file quant4x4_trellis.c + * + * \brief + * Quantization process for a 4x4 block using trellis based quantization + * + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * - Qualcomm + * - Limin Liu + * - Alexis Michael Tourapis + * + ************************************************************************************* + */ + +#include "contributors.h" + +#include + +#include "global.h" + +#include "image.h" +#include "mb_access.h" +#include "vlc.h" +#include "transform.h" +#include "mc_prediction.h" +#include "q_offsets.h" +#include "q_matrix.h" +#include "quant4x4.h" +#include "rdoq.h" + +/*! + ************************************************************************ + * \brief + * Quantization process for All coefficients for a 4x4 block + * + ************************************************************************ + */ +int quant_4x4_trellis(Macroblock *currMB, int **tblock, struct quant_methods *q_method) +{ + int block_x = q_method->block_x; + + int* ACL = &q_method->ACLevel[0]; + int* ACR = &q_method->ACRun[0]; + Slice *currSlice = currMB->p_Slice; + QuantParameters *p_Quant = currMB->p_Vid->p_Quant; + int qp = q_method->qp; + LevelQuantParams **q_params_4x4 = q_method->q_params; + const byte (*pos_scan)[2] = q_method->pos_scan; + const byte *c_cost = q_method->c_cost; + int *coeff_cost = q_method->coeff_cost; + + Boolean is_cavlc = (Boolean) (currSlice->symbol_mode == CAVLC); + + int i,j, coeff_ctr; + + int *m7; + + int level, run = 0; + int nonzero = FALSE; + int qp_per = p_Quant->qp_per_matrix[qp]; + const byte *p_scan = &pos_scan[0][0]; + + int levelTrellis[16]; + + currSlice->rdoq_4x4(currMB, tblock, q_method, levelTrellis); + + // Quantization + for (coeff_ctr = 0; coeff_ctr < 16; ++coeff_ctr) + { + i = *p_scan++; // horizontal position + j = *p_scan++; // vertical position + + m7 = &tblock[j][block_x + i]; + + if (*m7 != 0) + { + /* + scaled_coeff = iabs (*m7) * q_params_4x4[j][i].ScaleComp; + level = (scaled_coeff + q_params_4x4[j][i].OffsetComp) >> q_bits; + */ + level = levelTrellis[coeff_ctr]; + + if (level != 0) + { + if (is_cavlc) + level = imin(level, CAVLC_LEVEL_LIMIT); + + *coeff_cost += (level > 1) ? MAX_VALUE : c_cost[run]; + + level = isignab(level, *m7); + *m7 = rshift_rnd_sf(((level * q_params_4x4[j][i].InvScaleComp) << qp_per), 4); + *ACL++ = level; + *ACR++ = run; + // reset zero level counter + run = 0; + nonzero = TRUE; + } + else + { + *m7 = 0; + ++run; + } + } + else + { + ++run; + } + } + + *ACL = 0; + + return nonzero; +} + +/*! +************************************************************************ +* \brief +* Rate distortion optimized Quantization process for +* all coefficients in a 4x4 block (CAVLC) +* +************************************************************************ +*/ +void rdoq_4x4_CAVLC(Macroblock *currMB, int **tblock, struct quant_methods *q_method, int levelTrellis[]) +{ + VideoParameters *p_Vid = currMB->p_Vid; + int block_x = q_method->block_x; + int block_y = q_method->block_y; + LevelQuantParams **q_params_4x4 = q_method->q_params; + const byte (*pos_scan)[2] = q_method->pos_scan; + const byte *p_scan = &pos_scan[0][0]; + int qp = q_method->qp; + QuantParameters *p_Quant = currMB->p_Vid->p_Quant; + int qp_per = p_Quant->qp_per_matrix[qp]; + int qp_rem = p_Quant->qp_rem_matrix[qp]; + + levelDataStruct levelData[16]; + double lambda_md = p_Vid->lambda_rdoq[p_Vid->type][p_Vid->masterQP]; + + int type = LUMA_4x4; + int pos_x = block_x >> BLOCK_SHIFT; + int pos_y = block_y >> BLOCK_SHIFT; + int b8 = 2*(pos_y >> 1) + (pos_x >> 1); + int b4 = 2*(pos_y & 0x01) + (pos_x & 0x01); + + init_trellis_data_4x4_CAVLC(currMB, tblock, block_x, qp_per, qp_rem, q_params_4x4, p_scan, &levelData[0], type); + est_RunLevel_CAVLC(currMB, levelData, levelTrellis, LUMA, b8, b4, 16, lambda_md); +} +/*! +************************************************************************ +* \brief +* Rate distortion optimized Quantization process for +* all coefficients in a 4x4 block (CABAC) +* +************************************************************************ +*/ +void rdoq_4x4_CABAC(Macroblock *currMB, int **tblock, struct quant_methods *q_method, int levelTrellis[]) +{ + VideoParameters *p_Vid = currMB->p_Vid; + + const byte (*pos_scan)[2] = q_method->pos_scan; + const byte *p_scan = &pos_scan[0][0]; + + levelDataStruct levelData[16]; + int kStart=0, kStop=0, noCoeff = 0, estBits; + + double lambda_md = p_Vid->lambda_rdoq[p_Vid->type][p_Vid->masterQP]; + + noCoeff = init_trellis_data_4x4_CABAC(currMB, tblock, q_method, p_scan, &levelData[0], &kStart, &kStop, LUMA_4x4); + estBits = est_write_and_store_CBP_block_bit(currMB, LUMA_4x4); + est_writeRunLevel_CABAC(currMB, levelData, levelTrellis, LUMA_4x4, lambda_md, kStart, kStop, noCoeff, estBits); +} + +/*! + ************************************************************************ + * \brief + * Quantization process for All coefficients for a 4x4 block (LUMA_16AC or CHROMA_AC) + * + ************************************************************************ + */ +int quant_ac4x4_trellis(Macroblock *currMB, int **tblock, struct quant_methods *q_method) +{ + int block_x = q_method->block_x; + + int* ACLevel = q_method->ACLevel; + int* ACRun = q_method->ACRun; + int qp = q_method->qp; + LevelQuantParams **q_params_4x4 = q_method->q_params; + const byte (*pos_scan)[2] = q_method->pos_scan; + const byte *c_cost = q_method->c_cost; + int *coeff_cost = q_method->coeff_cost; + + Slice *currSlice = currMB->p_Slice; + QuantParameters *p_Quant = currMB->p_Vid->p_Quant; + Boolean is_cavlc = (Boolean) (currSlice->symbol_mode == CAVLC); + int i,j, coeff_ctr; + + int *m7; + int level, run = 0; + int nonzero = FALSE; + int qp_per = p_Quant->qp_per_matrix[qp]; + const byte *p_scan = &pos_scan[1][0]; + int* ACL = &ACLevel[0]; + int* ACR = &ACRun[0]; + + int levelTrellis[16]; + + currSlice->rdoq_ac4x4(currMB, tblock, q_method, levelTrellis); + + // Quantization + for (coeff_ctr = 1; coeff_ctr < 16; coeff_ctr++) + { + i = *p_scan++; // horizontal position + j = *p_scan++; // vertical position + + m7 = &tblock[j][block_x + i]; + if (*m7 != 0) + { + /* + scaled_coeff = iabs (*m7) * q_params_4x4[j][i].ScaleComp; + level = (scaled_coeff + q_params_4x4[j][i].OffsetComp) >> q_bits; + */ + level=levelTrellis[coeff_ctr - 1]; + + if (level != 0) + { + if (is_cavlc) + level = imin(level, CAVLC_LEVEL_LIMIT); + + *coeff_cost += (level > 1) ? MAX_VALUE : c_cost[run]; + + level = isignab(level, *m7); + *m7 = rshift_rnd_sf(((level * q_params_4x4[j][i].InvScaleComp) << qp_per), 4); + // inverse scale can be alternative performed as follows to ensure 16bit + // arithmetic is satisfied. + // *m7 = (qp_per<4) ? rshift_rnd_sf((level*q_params_4x4[j][i].InvScaleComp),4-qp_per) : (level*q_params_4x4[j][i].InvScaleComp)<<(qp_per-4); + *ACL++ = level; + *ACR++ = run; + // reset zero level counter + run = 0; + nonzero = TRUE; + } + else + { + run++; + *m7 = 0; + } + } + else + { + run++; + } + } + + *ACL = 0; + + return nonzero; +} + +/*! +************************************************************************ +* \brief +* Rate distortion optimized Quantization process for +* all coefficients in a 4x4 block (CAVLC) +* +************************************************************************ +*/ +void rdoq_ac4x4_CAVLC(Macroblock *currMB, int **tblock, struct quant_methods *q_method, int levelTrellis[]) +{ + VideoParameters *p_Vid = currMB->p_Vid; + int block_x = q_method->block_x; + int block_y = q_method->block_y; + LevelQuantParams **q_params_4x4 = q_method->q_params; + const byte (*pos_scan)[2] = q_method->pos_scan; + int qp = q_method->qp; + int type = q_method->type; + QuantParameters *p_Quant = p_Vid->p_Quant; + int qp_per = p_Quant->qp_per_matrix[qp]; + int qp_rem = p_Quant->qp_rem_matrix[qp]; + + const byte *p_scan = &pos_scan[1][0]; + levelDataStruct levelData[16]; + double lambda_md = p_Vid->lambda_rdoq[p_Vid->type][p_Vid->masterQP]; + + int pos_x = block_x >> BLOCK_SHIFT; + int pos_y = block_y >> BLOCK_SHIFT; + int b8 = 2*(pos_y >> 1) + (pos_x >> 1); + int b4 = 2*(pos_y & 0x01) + (pos_x & 0x01); + int block_type = ( (type == CHROMA_AC) ? CHROMA_AC : LUMA_INTRA16x16AC); + + init_trellis_data_4x4_CAVLC(currMB, tblock, block_x, qp_per, qp_rem, q_params_4x4, p_scan, &levelData[0], type); + est_RunLevel_CAVLC(currMB, levelData, levelTrellis, block_type, b8, b4, 15, lambda_md); +} +/*! +************************************************************************ +* \brief +* Rate distortion optimized Quantization process for +* all coefficients in a 4x4 block (LUMA_16AC or CHROMA_AC) - CABAC +* +************************************************************************ +*/ +void rdoq_ac4x4_CABAC(Macroblock *currMB, int **tblock, struct quant_methods *q_method, int levelTrellis[]) +{ + VideoParameters *p_Vid = currMB->p_Vid; + + const byte (*pos_scan)[2] = q_method->pos_scan; + int type = q_method->type; + + const byte *p_scan = &pos_scan[1][0]; + levelDataStruct levelData[16]; + double lambda_md = p_Vid->lambda_rdoq[p_Vid->type][p_Vid->masterQP]; + int kStart = 0, kStop = 0, noCoeff = 0, estBits; + + noCoeff = init_trellis_data_4x4_CABAC(currMB, tblock, q_method, p_scan, &levelData[0], &kStart, &kStop, type); + estBits = est_write_and_store_CBP_block_bit(currMB, type); + est_writeRunLevel_CABAC(currMB, levelData, levelTrellis, type, lambda_md, kStart, kStop, noCoeff, estBits); +} + +/*! + ************************************************************************ + * \brief + * Quantization process for All coefficients for a 4x4 DC block + * + ************************************************************************ + */ +int quant_dc4x4_trellis(Macroblock *currMB, int **tblock, int qp, int* DCLevel, int* DCRun, + LevelQuantParams *q_params_4x4, const byte (*pos_scan)[2]) +{ + Slice *currSlice = currMB->p_Slice; + QuantParameters *p_Quant = currMB->p_Vid->p_Quant; + Boolean is_cavlc = (Boolean) (currSlice->symbol_mode == CAVLC); + int i,j, coeff_ctr; + + int *m7; + + int level, run = 0; + int nonzero = FALSE; + int qp_per = p_Quant->qp_per_matrix[qp]; + int qp_rem = p_Quant->qp_rem_matrix[qp]; + const byte *p_scan = &pos_scan[0][0]; + int* DCL = &DCLevel[0]; + int* DCR = &DCRun[0]; + + int levelTrellis[16]; + + currSlice->rdoq_dc(currMB, tblock, qp_per, qp_rem, q_params_4x4, pos_scan, levelTrellis, LUMA_16DC); + + // Quantization + for (coeff_ctr = 0; coeff_ctr < 16; coeff_ctr++) + { + i = *p_scan++; // horizontal position + j = *p_scan++; // vertical position + + m7 = &tblock[j][i]; + + if (*m7 != 0) + { + level = levelTrellis[coeff_ctr]; + + if (level != 0) + { + if (is_cavlc) + level = imin(level, CAVLC_LEVEL_LIMIT); + + level = isignab(level, *m7); + *m7 = level; + *DCL++ = level; + *DCR++ = run; + // reset zero level counter + run = 0; + nonzero = TRUE; + } + else + { + run++; + *m7 = 0; + } + } + else + { + run++; + } + } + + *DCL = 0; + + return nonzero; +} + +/*! +************************************************************************ +* \brief +* Rate distortion optimized Quantization process for +* all coefficients in a luma DC block +* +************************************************************************ +*/ +void rdoq_dc_CAVLC(Macroblock *currMB, int **tblock, int qp_per, int qp_rem, LevelQuantParams *q_params_4x4, + const byte (*pos_scan)[2], int levelTrellis[], int type) +{ + VideoParameters *p_Vid = currMB->p_Vid; + const byte *p_scan = &pos_scan[0][0]; + levelDataStruct levelData[16]; + double lambda_md = p_Vid->lambda_rdoq[p_Vid->type][p_Vid->masterQP]; + + init_trellis_data_DC_CAVLC(currMB, tblock, qp_per, qp_rem, q_params_4x4, p_scan, &levelData[0]); + est_RunLevel_CAVLC(currMB, levelData, levelTrellis, LUMA_INTRA16x16DC, 0, 0, 16, lambda_md); +} + + +/*! +************************************************************************ +* \brief +* Rate distortion optimized Quantization process for +* all coefficients in a luma DC block +* +************************************************************************ +*/ +void rdoq_dc_CABAC(Macroblock *currMB, int **tblock, int qp_per, int qp_rem, LevelQuantParams *q_params_4x4, const byte (*pos_scan)[2], int levelTrellis[], int type) +{ + VideoParameters *p_Vid = currMB->p_Vid; + const byte *p_scan = &pos_scan[0][0]; + levelDataStruct levelData[16]; + double lambda_md = p_Vid->lambda_rdoq[p_Vid->type][p_Vid->masterQP]; + int kStart = 0, kStop = 0, noCoeff = 0, estBits; + + noCoeff = init_trellis_data_DC_CABAC(currMB, tblock, qp_per, qp_rem, q_params_4x4, p_scan, &levelData[0], &kStart, &kStop); + estBits = est_write_and_store_CBP_block_bit(currMB, type); + est_writeRunLevel_CABAC(currMB, levelData, levelTrellis, type, lambda_md, kStart, kStop, noCoeff, estBits); +} + diff --git a/lencod/src/quant8x8.c b/lencod/src/quant8x8.c new file mode 100644 index 0000000..b0c2379 --- /dev/null +++ b/lencod/src/quant8x8.c @@ -0,0 +1,51 @@ + +/*! + ************************************************************************************* + * \file quant8x8.c + * + * \brief + * Quantization process for a 8x8 block + * + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * - Alexis Michael Tourapis + * + ************************************************************************************* + */ + +#include "contributors.h" + +#include + +#include "global.h" +#include "quant8x8.h" + + +/*! +************************************************************************ +* \brief +* Quantization initialization function +* +************************************************************************ +*/ +void init_quant_8x8(Slice *currSlice) +{ + VideoParameters *p_Vid = currSlice->p_Vid; + // We may wish to have all these parameters switched at the slice level for speed up. + if (currSlice->UseRDOQuant == 1) + { + currSlice->quant_8x8 = quant_8x8_trellis; + currSlice->quant_8x8cavlc = quant_8x8cavlc_trellis; + } + else if (p_Vid->AdaptiveRounding) + { + currSlice->quant_8x8 = quant_8x8_around; + currSlice->quant_8x8cavlc = quant_8x8cavlc_around; + } + else + { + currSlice->quant_8x8 = quant_8x8_normal; + currSlice->quant_8x8cavlc = quant_8x8cavlc_normal; + } +} + diff --git a/lencod/src/quant8x8_around.c b/lencod/src/quant8x8_around.c new file mode 100644 index 0000000..6d4e31c --- /dev/null +++ b/lencod/src/quant8x8_around.c @@ -0,0 +1,223 @@ + +/*! + ************************************************************************************* + * \file quant8x8_around.c + * + * \brief + * Quantization process for a 8x8 block with adaptive rounding + * + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * - Alexis Michael Tourapis + * + ************************************************************************************* + */ + +#include "contributors.h" + +#include + +#include "global.h" + +#include "image.h" +#include "mb_access.h" +#include "vlc.h" +#include "transform.h" +#include "mc_prediction.h" +#include "q_offsets.h" +#include "q_matrix.h" +#include "quant8x8.h" + + +/*! + ************************************************************************ + * \brief + * Quantization process for All coefficients for a 8x8 block + * + * \par Input: + * + * \par Output: + * + ************************************************************************ + */ +int quant_8x8_around(Macroblock *currMB, int **tblock, struct quant_methods *q_method) +{ + VideoParameters *p_Vid = currMB->p_Vid; + QuantParameters *p_Quant = p_Vid->p_Quant; + int AdaptRndWeight = p_Vid->AdaptRndWeight; + + int block_x = q_method->block_x; + + int qp = q_method->qp; + int* ACLevel = q_method->ACLevel; + int* ACRun = q_method->ACRun; + LevelQuantParams **q_params_8x8 = q_method->q_params; + LevelQuantParams *q_params = NULL; + + int **fadjust8x8 = q_method->fadjust; + const byte (*pos_scan)[2] = q_method->pos_scan; + const byte *c_cost = q_method->c_cost; + int *coeff_cost = q_method->coeff_cost; + + int i,j, coeff_ctr; + + int *m7; + int scaled_coeff; + + int level, run = 0; + int nonzero = FALSE; + int qp_per = p_Quant->qp_per_matrix[qp]; + int q_bits = Q_BITS_8 + qp_per; + const byte *p_scan = &pos_scan[0][0]; + int* ACL = &ACLevel[0]; + int* ACR = &ACRun[0]; + int* padjust8x8; + + // Quantization + for (coeff_ctr = 0; coeff_ctr < 64; ++coeff_ctr) + { + i = *p_scan++; // horizontal position + j = *p_scan++; // vertical position + + padjust8x8 = &fadjust8x8[j][block_x + i]; + m7 = &tblock[j][block_x + i]; + if (*m7 != 0) + { + q_params = &q_params_8x8[j][i]; + scaled_coeff = iabs (*m7) * q_params->ScaleComp; + level = (scaled_coeff + q_params->OffsetComp) >> q_bits; + + if (level != 0) + { + *padjust8x8 = rshift_rnd_sf((AdaptRndWeight * (scaled_coeff - (level << q_bits))), (q_bits + 1)); + + nonzero = TRUE; + + *coeff_cost += (level > 1) ? MAX_VALUE : c_cost[run]; + + level = isignab(level, *m7); + *m7 = rshift_rnd_sf(((level * q_params->InvScaleComp) << qp_per), 6); + *ACL++ = level; + *ACR++ = run; + // reset zero level counter + run = 0; + } + else + { + *padjust8x8 = 0; + ++run; + *m7 = 0; + } + } + else + { + *padjust8x8 = 0; + ++run; + } + } + + *ACL = 0; + + return nonzero; +} + +/*! + ************************************************************************ + * \brief + * Quantization process for All coefficients for a 8x8 block + * CAVLC version + * + * \par Input: + * + * \par Output: + * + ************************************************************************ + */ +int quant_8x8cavlc_around(Macroblock *currMB, int **tblock, struct quant_methods *q_method, int*** cofAC) +{ + VideoParameters *p_Vid = currMB->p_Vid; + QuantParameters *p_Quant = p_Vid->p_Quant; + int AdaptRndWeight = p_Vid->AdaptRndWeight; + int block_x = q_method->block_x; + + int qp = q_method->qp; + LevelQuantParams **q_params_8x8 = q_method->q_params; + int **fadjust8x8 = q_method->fadjust; + const byte (*pos_scan)[2] = q_method->pos_scan; + const byte *c_cost = q_method->c_cost; + int *coeff_cost = q_method->coeff_cost; + + int i,j, k, coeff_ctr; + + int *m7; + int scaled_coeff; + + int level, runs[4] = { 0 }; + int nonzero = FALSE; + int qp_per = p_Quant->qp_per_matrix[qp]; + int q_bits = Q_BITS_8 + qp_per; + const byte *p_scan = &pos_scan[0][0]; + int* ACL[4]; + int* ACR[4]; + int* padjust8x8; + + for (k = 0; k < 4; ++k) + { + ACL[k] = &cofAC[k][0][0]; + ACR[k] = &cofAC[k][1][0]; + } + + // Quantization + for (k = 0; k < 4; ++k) + { + for (coeff_ctr = 0; coeff_ctr < 16; ++coeff_ctr) + { + i = *p_scan++; // horizontal position + j = *p_scan++; // vertical position + + padjust8x8 = &fadjust8x8[j][block_x + i]; + m7 = &tblock[j][block_x + i]; + if (*m7 != 0) + { + scaled_coeff = iabs (*m7) * q_params_8x8[j][i].ScaleComp; + level = (scaled_coeff + q_params_8x8[j][i].OffsetComp) >> q_bits; + + if (level != 0) + { + level = imin(level, CAVLC_LEVEL_LIMIT); + + *padjust8x8 = rshift_rnd_sf((AdaptRndWeight * (scaled_coeff - (level << q_bits))), (q_bits + 1)); + + nonzero=TRUE; + + *coeff_cost += (level > 1) ? MAX_VALUE : c_cost[runs[k]]; + + level = isignab(level, *m7); + *m7 = rshift_rnd_sf(((level * q_params_8x8[j][i].InvScaleComp) << qp_per), 6); + + *(ACL[k])++ = level; + *(ACR[k])++ = runs[k]; + // reset zero level counter + runs[k] = 0; + } + else + { + *padjust8x8 = 0; + *m7 = 0; + ++runs[k]; + } + } + else + { + *padjust8x8 = 0; + ++runs[k]; + } + } + } + + for(k = 0; k < 4; ++k) + *(ACL[k]) = 0; + + return nonzero; +} + diff --git a/lencod/src/quant8x8_normal.c b/lencod/src/quant8x8_normal.c new file mode 100644 index 0000000..51c629c --- /dev/null +++ b/lencod/src/quant8x8_normal.c @@ -0,0 +1,202 @@ + +/*! + ************************************************************************************* + * \file quant8x8_normal.c + * + * \brief + * Quantization process for a 8x8 block without any adaptation + * + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * - Alexis Michael Tourapis + * + ************************************************************************************* + */ + +#include "contributors.h" + +#include + +#include "global.h" + +#include "image.h" +#include "mb_access.h" +#include "vlc.h" +#include "transform.h" +#include "mc_prediction.h" +#include "q_offsets.h" +#include "q_matrix.h" +#include "quant8x8.h" + + +/*! + ************************************************************************ + * \brief + * Quantization process for All coefficients for a 8x8 block + * + * \par Input: + * + * \par Output: + * + ************************************************************************ + */ +int quant_8x8_normal(Macroblock *currMB, int **tblock, struct quant_methods *q_method) +{ + VideoParameters *p_Vid = currMB->p_Vid; + QuantParameters *p_Quant = p_Vid->p_Quant; + int block_x = q_method->block_x; + int* ACLevel = q_method->ACLevel; + int* ACRun = q_method->ACRun; + int qp = q_method->qp; + LevelQuantParams **q_params_8x8 = q_method->q_params; + const byte (*pos_scan)[2] = q_method->pos_scan; + const byte *c_cost = q_method->c_cost; + int *coeff_cost = q_method->coeff_cost; + + int i,j, coeff_ctr; + + int *m7; + int scaled_coeff; + + int level, run = 0; + int nonzero = FALSE; + int qp_per = p_Quant->qp_per_matrix[qp]; + int q_bits = Q_BITS_8 + qp_per; + const byte *p_scan = &pos_scan[0][0]; + int* ACL = &ACLevel[0]; + int* ACR = &ACRun[0]; + + // Quantization + for (coeff_ctr = 0; coeff_ctr < 64; coeff_ctr++) + { + i = *p_scan++; // horizontal position + j = *p_scan++; // vertical position + + m7 = &tblock[j][block_x + i]; + if (*m7 != 0) + { + scaled_coeff = iabs (*m7) * q_params_8x8[j][i].ScaleComp; + level = (scaled_coeff + q_params_8x8[j][i].OffsetComp) >> q_bits; + + if (level != 0) + { + nonzero = TRUE; + + *coeff_cost += (level > 1) ? MAX_VALUE : c_cost[run]; + + level = isignab(level, *m7); + *m7 = rshift_rnd_sf(((level * q_params_8x8[j][i].InvScaleComp) << qp_per), 6); + *ACL++ = level; + *ACR++ = run; + // reset zero level counter + run = 0; + } + else + { + run++; + *m7 = 0; + } + } + else + { + run++; + } + } + + *ACL = 0; + + return nonzero; +} + +/*! + ************************************************************************ + * \brief + * Quantization process for All coefficients for a 8x8 block + * CAVLC version + * + * \par Input: + * + * \par Output: + * + ************************************************************************ + */ +int quant_8x8cavlc_normal(Macroblock *currMB, int **tblock, struct quant_methods *q_method, int*** cofAC) +{ + QuantParameters *p_Quant = currMB->p_Vid->p_Quant; + int block_x = q_method->block_x; + + int qp = q_method->qp; + LevelQuantParams **q_params_8x8 = q_method->q_params; + const byte (*pos_scan)[2] = q_method->pos_scan; + const byte *c_cost = q_method->c_cost; + int *coeff_cost = q_method->coeff_cost; + + + int i,j, k, coeff_ctr; + + int *m7; + int scaled_coeff; + + int level, runs[4] = { 0 }; + int nonzero = FALSE; + int qp_per = p_Quant->qp_per_matrix[qp]; + int q_bits = Q_BITS_8 + qp_per; + const byte *p_scan = &pos_scan[0][0]; + int* ACL[4]; + int* ACR[4]; + + for (k = 0; k < 4; k++) + { + ACL[k] = &cofAC[k][0][0]; + ACR[k] = &cofAC[k][1][0]; + } + + // Quantization + for (k = 0; k < 4; k++) + { + for (coeff_ctr = 0; coeff_ctr < 16; coeff_ctr++) + { + i = *p_scan++; // horizontal position + j = *p_scan++; // vertical position + + m7 = &tblock[j][block_x + i]; + if (*m7 != 0) + { + scaled_coeff = iabs (*m7) * q_params_8x8[j][i].ScaleComp; + level = (scaled_coeff + q_params_8x8[j][i].OffsetComp) >> q_bits; + + if (level != 0) + { + level = imin(level, CAVLC_LEVEL_LIMIT); + + nonzero=TRUE; + + *coeff_cost += (level > 1) ? MAX_VALUE : c_cost[runs[k]]; + + level = isignab(level, *m7); + *m7 = rshift_rnd_sf(((level * q_params_8x8[j][i].InvScaleComp) << qp_per), 6); + + *(ACL[k])++ = level; + *(ACR[k])++ = runs[k]; + // reset zero level counter + runs[k] = 0; + } + else + { + runs[k]++; + *m7 = 0; + } + } + else + { + runs[k]++; + } + } + } + + for(k = 0; k < 4; k++) + *(ACL[k]) = 0; + + return nonzero; +} + diff --git a/lencod/src/quant8x8_trellis.c b/lencod/src/quant8x8_trellis.c new file mode 100644 index 0000000..8a8377e --- /dev/null +++ b/lencod/src/quant8x8_trellis.c @@ -0,0 +1,247 @@ + +/*! + ************************************************************************************* + * \file quant8x8_trellis.c + * + * \brief + * Quantization process for a 4x4 block using trellis based quantization + * + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * + ************************************************************************************* + */ + +#include "contributors.h" + +#include + +#include "global.h" +#include "image.h" +#include "mb_access.h" +#include "vlc.h" +#include "transform.h" +#include "mc_prediction.h" +#include "q_offsets.h" +#include "q_matrix.h" +#include "quant8x8.h" +#include "rdoq.h" + +/*! +************************************************************************ +* \brief +* Rate distortion optimized Quantization process for +* all coefficients in a 8x8 block +* +************************************************************************ +*/ +static void rdoq_8x8_CABAC(Macroblock *currMB, int **tblock, int block_x,int qp_per, int qp_rem, + LevelQuantParams **q_params_8x8, const byte *p_scan, int levelTrellis[64]) +{ + VideoParameters *p_Vid = currMB->p_Vid; + levelDataStruct levelData[64]; + double lambda_md = 0.0; + int kStart = 0, kStop = 0, noCoeff = 0; + + lambda_md = p_Vid->lambda_rdoq[p_Vid->type][p_Vid->masterQP]; + + noCoeff = init_trellis_data_8x8_CABAC(currMB, tblock, block_x, qp_per, qp_rem, q_params_8x8, p_scan, &levelData[0], &kStart, &kStop); + est_writeRunLevel_CABAC(currMB, levelData, levelTrellis, LUMA_8x8, lambda_md, kStart, kStop, noCoeff, 0); +} + +/*! +************************************************************************ +* \brief +* Rate distortion optimized Quantization process for +* all coefficients in a 8x8 block +* +************************************************************************ +*/ +static void rdoq_8x8_CAVLC(Macroblock *currMB, int **tblock, int block_y, int block_x, int qp_per, int qp_rem, + LevelQuantParams **q_params_8x8, const byte *p_scan, int levelTrellis[4][16]) +{ + VideoParameters *p_Vid = currMB->p_Vid; + int k; + levelDataStruct levelData[4][16]; + double lambda_md = 0.0; + + int b8 = ((block_y >> 3) << 1) + (block_x >> 3); + + lambda_md = p_Vid->lambda_rdoq[p_Vid->type][p_Vid->masterQP]; + + init_trellis_data_8x8_CAVLC (currMB, tblock, block_x, qp_per, qp_rem, q_params_8x8, p_scan, levelData); + + for (k = 0; k < 4; k++) + est_RunLevel_CAVLC(currMB, levelData[k], levelTrellis[k], LUMA, b8, k, 16, lambda_md); +} + + +/*! + ************************************************************************ + * \brief + * Quantization process for All coefficients for a 8x8 block + * + * \par Input: + * + * \par Output: + * + ************************************************************************ + */ +int quant_8x8_trellis(Macroblock *currMB, int **tblock, struct quant_methods *q_method) +{ + QuantParameters *p_Quant = currMB->p_Vid->p_Quant; + int block_x = q_method->block_x; + int qp = q_method->qp; + int* ACLevel = q_method->ACLevel; + int* ACRun = q_method->ACRun; + LevelQuantParams **q_params_8x8 = q_method->q_params; + const byte (*pos_scan)[2] = q_method->pos_scan; + const byte *c_cost = q_method->c_cost; + int *coeff_cost = q_method->coeff_cost; + + int i,j, coeff_ctr; + + int *m7; + int level, run = 0; + int nonzero = FALSE; + int qp_per = p_Quant->qp_per_matrix[qp]; + int qp_rem = p_Quant->qp_rem_matrix[qp]; + const byte *p_scan = &pos_scan[0][0]; + int* ACL = &ACLevel[0]; + int* ACR = &ACRun[0]; + int levelTrellis[64]; + + rdoq_8x8_CABAC(currMB, tblock, block_x, qp_per, qp_rem, q_params_8x8, p_scan, levelTrellis); + + // Quantization + for (coeff_ctr = 0; coeff_ctr < 64; coeff_ctr++) + { + i = *p_scan++; // horizontal position + j = *p_scan++; // vertical position + + m7 = &tblock[j][block_x + i]; + if (*m7 != 0) + { + level = levelTrellis[coeff_ctr]; + + if (level != 0) + { + nonzero = TRUE; + + *coeff_cost += (level > 1) ? MAX_VALUE : c_cost[run]; + + level = isignab(level, *m7); + *m7 = rshift_rnd_sf(((level * q_params_8x8[j][i].InvScaleComp) << qp_per), 6); + *ACL++ = level; + *ACR++ = run; + // reset zero level counter + run = 0; + } + else + { + run++; + *m7 = 0; + } + } + else + { + run++; + } + } + + *ACL = 0; + + return nonzero; +} + +/*! + ************************************************************************ + * \brief + * Quantization process for All coefficients for a 8x8 block + * CAVLC version + * + * \par Input: + * + * \par Output: + * + ************************************************************************ + */ +int quant_8x8cavlc_trellis(Macroblock *currMB, int **tblock, struct quant_methods *q_method, int*** cofAC) +{ + QuantParameters *p_Quant = currMB->p_Vid->p_Quant; + int block_x = q_method->block_x; + int block_y = q_method->block_y; + int qp = q_method->qp; + LevelQuantParams **q_params_8x8 = q_method->q_params; + const byte (*pos_scan)[2] = q_method->pos_scan; + const byte *c_cost = q_method->c_cost; + int *coeff_cost = q_method->coeff_cost; + + int i,j, k, coeff_ctr; + + int *m7; + int level, runs[4] = { 0 }; + int nonzero = FALSE; + int qp_per = p_Quant->qp_per_matrix[qp]; + int qp_rem = p_Quant->qp_rem_matrix[qp]; + const byte *p_scan = &pos_scan[0][0]; + int* ACL[4]; + int* ACR[4]; + + int levelTrellis[4][16]; + + rdoq_8x8_CAVLC(currMB, tblock, block_y, block_x, qp_per, qp_rem, q_params_8x8, p_scan, levelTrellis); + + for (k = 0; k < 4; k++) + { + ACL[k] = &cofAC[k][0][0]; + ACR[k] = &cofAC[k][1][0]; + } + + // Quantization + for (k = 0; k < 4; k++) + { + for (coeff_ctr = 0; coeff_ctr < 16; coeff_ctr++) + { + i = *p_scan++; // horizontal position + j = *p_scan++; // vertical position + + m7 = &tblock[j][block_x + i]; + + if (m7 != 0) + { + level = levelTrellis[k][coeff_ctr]; + + if (level != 0) + { + nonzero=TRUE; + + *coeff_cost += (level > 1) ? MAX_VALUE : c_cost[runs[k]]; + + level = isignab(level, *m7); + *m7 = rshift_rnd_sf(((level * q_params_8x8[j][i].InvScaleComp) << qp_per), 6); + + *(ACL[k])++ = level; + *(ACR[k])++ = runs[k]; + // reset zero level counter + runs[k] = 0; + } + else + { + runs[k]++; + *m7 = 0; + } + } + else + { + runs[k]++; + } + } + } + + for(k = 0; k < 4; k++) + *(ACL[k]) = 0; + + return nonzero; +} + diff --git a/lencod/src/quantChroma.c b/lencod/src/quantChroma.c new file mode 100644 index 0000000..07ef9a3 --- /dev/null +++ b/lencod/src/quantChroma.c @@ -0,0 +1,74 @@ + +/*! + ************************************************************************************* + * \file quantChroma.c + * + * \brief + * Quantization initialization function for Chroma blocks + * + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * - Alexis Michael Tourapis + * + ************************************************************************************* + */ + +#include "contributors.h" + +#include "global.h" +#include "quant4x4.h" +#include "quantChroma.h" + +/*! +************************************************************************ +* \brief +* Quantization initialization function +* +************************************************************************ +*/ +void init_quant_Chroma(Slice *currSlice) +{ + VideoParameters *p_Vid = currSlice->p_Vid; + InputParameters *p_Inp = currSlice->p_Inp; + + if (p_Inp->UseRDOQuant == 1 && p_Inp->RDOQ_CR == 1) + { + currSlice->quant_ac4x4cr = quant_ac4x4_trellis; + if (p_Inp->RDOQ_DC_CR) + { + if (p_Vid->yuv_format == YUV422) + currSlice->quant_dc_cr = quant_dc4x2_trellis; + else + currSlice->quant_dc_cr = quant_dc2x2_trellis; + } + else + { + if (p_Vid->yuv_format == YUV422) + currSlice->quant_dc_cr = quant_dc4x2_normal; + else + currSlice->quant_dc_cr = quant_dc2x2_normal; + } + if (currSlice->symbol_mode == CABAC) + currSlice->rdoq_dc_cr = rdoq_dc_cr_CABAC; + else + currSlice->rdoq_dc_cr = rdoq_dc_cr_CAVLC; + } + else if (p_Inp->UseRDOQuant == 1 || (!(currSlice->p_Vid)->AdaptiveRounding)) + { + currSlice->quant_ac4x4cr = quant_ac4x4_normal; + if (p_Vid->yuv_format == YUV422) + currSlice->quant_dc_cr = quant_dc4x2_normal; + else + currSlice->quant_dc_cr = quant_dc2x2_normal; + } + else + { + currSlice->quant_ac4x4cr = quant_ac4x4_around; + if (p_Vid->yuv_format == YUV422) + currSlice->quant_dc_cr = quant_dc4x2_around; + else + currSlice->quant_dc_cr = quant_dc2x2_around; + } +} + + diff --git a/lencod/src/quantChroma_2step.c b/lencod/src/quantChroma_2step.c new file mode 100644 index 0000000..b134fc9 --- /dev/null +++ b/lencod/src/quantChroma_2step.c @@ -0,0 +1,173 @@ + +/*! + ************************************************************************************* + * \file quantChroma_2step.c + * + * \brief + * Quantization process for a Chroma block + * + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * - Alexis Michael Tourapis + * + ************************************************************************************* + */ + +#include "contributors.h" + +#include + +#include "global.h" +#include "q_matrix.h" +#include "quant4x4.h" +#include "quantChroma.h" + + +/*! + ************************************************************************ + * \brief + * Quantization process for All coefficients for a 2x2 DC block + * + * \par Input: + * + * \par Output: + * + ************************************************************************ + */ +int quant_dc2x2_2step(Macroblock *currMB, int **tblock, int qp, int* DCLevel, int* DCRun, + LevelQuantParams *q_params_4x4, int **fadjust, const byte (*pos_scan)[2]) +{ + QuantParameters *p_Quant = currMB->p_Vid->p_Quant; + Boolean is_cavlc = (Boolean) (currMB->p_Slice->symbol_mode == CAVLC); + int coeff_ctr; + + int *m7; + int scaled_coeff; + + int level, run = 0; + int nonzero = FALSE; + int qp_per = p_Quant->qp_per_matrix[qp]; + int q_bits = Q_BITS + qp_per + 1; + //const byte *p_scan = &pos_scan[0][0]; + int* DCL = &DCLevel[0]; + int* DCR = &DCRun[0]; + + m7 = *tblock; + + // Quantization + for (coeff_ctr=0; coeff_ctr < 4; coeff_ctr++) + { + // we need to update q_params_4x4->OffsetComp to a 4x1 array that would contain offset info for + // every 2x2 DC position + if (*m7) + { + scaled_coeff = iabs (*m7) * q_params_4x4->ScaleComp; + level = (scaled_coeff + (q_params_4x4->OffsetComp << 1) ) >> q_bits; + + if (level != 0) + { + if (is_cavlc) + level = imin(level, CAVLC_LEVEL_LIMIT); + + level = isignab(level, *m7); + + *m7++ = ((level * q_params_4x4->InvScaleComp) << qp_per); + + *DCL++ = level; + *DCR++ = run; + run = 0; + nonzero = TRUE; + } + else + { + run++; + *m7++ = 0; + } + } + else + { + run++; + m7++; + } + } + + *DCL = 0; + + return nonzero; +} + +/*! + ************************************************************************ + * \brief + * Quantization process for All coefficients for a 2x2 DC block + * + * \par Input: + * + * \par Output: + * + ************************************************************************ + */ +int quant_dc4x2_2step(Macroblock *currMB, int **tblock, int qp, int* DCLevel, int* DCRun, + LevelQuantParams *q_params_4x4, int **fadjust, const byte (*pos_scan)[2]) +{ + QuantParameters *p_Quant = currMB->p_Vid->p_Quant; + Boolean is_cavlc = (Boolean) (currMB->p_Slice->symbol_mode == CAVLC); + int i,j, coeff_ctr; + + int *m7; + int scaled_coeff; + + int level, run = 0; + int nonzero = FALSE; + int qp_per = p_Quant->qp_per_matrix[qp]; + int q_bits = Q_BITS + qp_per + 1; + const byte *p_scan = &pos_scan[0][0]; + int* DCL = &DCLevel[0]; + int* DCR = &DCRun[0]; + + // Quantization + for (coeff_ctr = 0; coeff_ctr < 8; coeff_ctr++) + { + j = *p_scan++; // note that in this part, somehow coefficients were transposed from 2x4 to 4x2. + i = *p_scan++; + + m7 = &tblock[j][i]; + + if (*m7 != 0) + { + scaled_coeff = iabs (*m7) * q_params_4x4->ScaleComp; + level = (scaled_coeff + (q_params_4x4->OffsetComp << 1) ) >> q_bits; + + if (level != 0) + { + if (is_cavlc) + level = imin(level, CAVLC_LEVEL_LIMIT); + level = isignab(level, *m7); + + *m7 = ((level * q_params_4x4->InvScaleComp) << qp_per); + + *DCL++ = level; + *DCR++ = run; + // reset zero level counter + run = 0; + nonzero = TRUE; + } + else + { + run++; + *m7 = 0; + } + } + else + { + run++; + } + } + + *DCL = 0; + + return nonzero; +} + + + diff --git a/lencod/src/quantChroma_around.c b/lencod/src/quantChroma_around.c new file mode 100644 index 0000000..9820f0a --- /dev/null +++ b/lencod/src/quantChroma_around.c @@ -0,0 +1,174 @@ + +/*! + ************************************************************************************* + * \file quantChroma_around.c + * + * \brief + * Quantization process for a Chroma block + * + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * - Alexis Michael Tourapis + * + ************************************************************************************* + */ + +#include "contributors.h" + +#include + +#include "global.h" +#include "q_matrix.h" +#include "quant4x4.h" +#include "quantChroma.h" + + +/*! + ************************************************************************ + * \brief + * Quantization process for All coefficients for a 2x2 DC block + * + * \par Input: + * + * \par Output: + * + ************************************************************************ + */ +int quant_dc2x2_around(Macroblock *currMB, int **tblock, int qp, int* DCLevel, int* DCRun, + LevelQuantParams *q_params_4x4, int **fadjust, const byte (*pos_scan)[2]) +{ + QuantParameters *p_Quant = currMB->p_Vid->p_Quant; + Boolean is_cavlc = (Boolean) (currMB->p_Slice->symbol_mode == CAVLC); + int coeff_ctr; + + int *m7; + int scaled_coeff; + + int level, run = 0; + int nonzero = FALSE; + int qp_per = p_Quant->qp_per_matrix[qp]; + int q_bits = Q_BITS + qp_per + 1; + //const byte *p_scan = &pos_scan[0][0]; + int* DCL = &DCLevel[0]; + int* DCR = &DCRun[0]; + + m7 = *tblock; + + // Quantization + for (coeff_ctr=0; coeff_ctr < 4; ++coeff_ctr) + { + // we need to update q_params_4x4->OffsetComp to a 4x1 array that would contain offset info for + // every 2x2 DC position + if (*m7) + { + scaled_coeff = iabs (*m7) * q_params_4x4->ScaleComp; + level = (scaled_coeff + (q_params_4x4->OffsetComp << 1) ) >> q_bits; + + if (level != 0) + { + if (is_cavlc) + level = imin(level, CAVLC_LEVEL_LIMIT); + + level = isignab(level, *m7); + + *m7++ = ((level * q_params_4x4->InvScaleComp) << qp_per); + + *DCL++ = level; + *DCR++ = run; + // reset zero level counter + run = 0; + nonzero = TRUE; + } + else + { + ++run; + *m7++ = 0; + } + } + else + { + ++run; + ++m7; + } + } + + *DCL = 0; + + return nonzero; +} + +/*! + ************************************************************************ + * \brief + * Quantization process for All coefficients for a 2x2 DC block + * + * \par Input: + * + * \par Output: + * + ************************************************************************ + */ +int quant_dc4x2_around(Macroblock *currMB, int **tblock, int qp, int* DCLevel, int* DCRun, + LevelQuantParams *q_params_4x4, int **fadjust, const byte (*pos_scan)[2]) +{ + QuantParameters *p_Quant = currMB->p_Vid->p_Quant; + Boolean is_cavlc = (Boolean) (currMB->p_Slice->symbol_mode == CAVLC); + int i,j, coeff_ctr; + + int *m7; + int scaled_coeff; + + int level, run = 0; + int nonzero = FALSE; + int qp_per = p_Quant->qp_per_matrix[qp]; + int q_bits = Q_BITS + qp_per + 1; + const byte *p_scan = &pos_scan[0][0]; + int* DCL = &DCLevel[0]; + int* DCR = &DCRun[0]; + + // Quantization + for (coeff_ctr = 0; coeff_ctr < 8; ++coeff_ctr) + { + j = *p_scan++; // note that in this part, somehow coefficients were transposed from 2x4 to 4x2. + i = *p_scan++; + + m7 = &tblock[j][i]; + + if (*m7 != 0) + { + scaled_coeff = iabs (*m7) * q_params_4x4->ScaleComp; + level = (scaled_coeff + (q_params_4x4->OffsetComp << 1) ) >> q_bits; + + if (level != 0) + { + if (is_cavlc) + level = imin(level, CAVLC_LEVEL_LIMIT); + level = isignab(level, *m7); + + *m7 = ((level * q_params_4x4->InvScaleComp) << qp_per); + + *DCL++ = level; + *DCR++ = run; + // reset zero level counter + run = 0; + nonzero = TRUE; + } + else + { + ++run; + *m7 = 0; + } + } + else + { + ++run; + } + } + + *DCL = 0; + + return nonzero; +} + + + diff --git a/lencod/src/quantChroma_normal.c b/lencod/src/quantChroma_normal.c new file mode 100644 index 0000000..96ad627 --- /dev/null +++ b/lencod/src/quantChroma_normal.c @@ -0,0 +1,173 @@ + +/*! + ************************************************************************************* + * \file quantChroma_normal.c + * + * \brief + * Quantization process for a Chroma block + * + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * - Alexis Michael Tourapis + * + ************************************************************************************* + */ + +#include "contributors.h" + +#include + +#include "global.h" +#include "q_matrix.h" +#include "quant4x4.h" +#include "quantChroma.h" + + +/*! + ************************************************************************ + * \brief + * Quantization process for All coefficients for a 2x2 DC block + * + * \par Input: + * + * \par Output: + * + ************************************************************************ + */ +int quant_dc2x2_normal(Macroblock *currMB, int **tblock, int qp, int* DCLevel, int* DCRun, + LevelQuantParams *q_params_4x4, int **fadjust, const byte (*pos_scan)[2]) +{ + QuantParameters *p_Quant = currMB->p_Vid->p_Quant; + Boolean is_cavlc = (Boolean) (currMB->p_Slice->symbol_mode == CAVLC); + int coeff_ctr; + + int *m7; + int scaled_coeff; + + int level, run = 0; + int nonzero = FALSE; + int qp_per = p_Quant->qp_per_matrix[qp]; + int q_bits = Q_BITS + qp_per + 1; + //const byte *p_scan = &pos_scan[0][0]; + int* DCL = &DCLevel[0]; + int* DCR = &DCRun[0]; + + m7 = *tblock; + + // Quantization + for (coeff_ctr=0; coeff_ctr < 4; coeff_ctr++) + { + // we need to update q_params_4x4->OffsetComp to a 4x1 array that would contain offset info for + // every 2x2 DC position + if (*m7) + { + scaled_coeff = iabs (*m7) * q_params_4x4->ScaleComp; + level = (scaled_coeff + (q_params_4x4->OffsetComp << 1) ) >> q_bits; + + if (level != 0) + { + if (is_cavlc) + level = imin(level, CAVLC_LEVEL_LIMIT); + + level = isignab(level, *m7); + + *m7++ = ((level * q_params_4x4->InvScaleComp) << qp_per); + + *DCL++ = level; + *DCR++ = run; + run = 0; + nonzero = TRUE; + } + else + { + run++; + *m7++ = 0; + } + } + else + { + run++; + m7++; + } + } + + *DCL = 0; + + return nonzero; +} + +/*! + ************************************************************************ + * \brief + * Quantization process for All coefficients for a 2x2 DC block + * + * \par Input: + * + * \par Output: + * + ************************************************************************ + */ +int quant_dc4x2_normal(Macroblock *currMB, int **tblock, int qp, int* DCLevel, int* DCRun, + LevelQuantParams *q_params_4x4, int **fadjust, const byte (*pos_scan)[2]) +{ + QuantParameters *p_Quant = currMB->p_Vid->p_Quant; + Boolean is_cavlc = (Boolean) (currMB->p_Slice->symbol_mode == CAVLC); + int i,j, coeff_ctr; + + int *m7; + int scaled_coeff; + + int level, run = 0; + int nonzero = FALSE; + int qp_per = p_Quant->qp_per_matrix[qp]; + int q_bits = Q_BITS + qp_per + 1; + const byte *p_scan = &pos_scan[0][0]; + int* DCL = &DCLevel[0]; + int* DCR = &DCRun[0]; + + // Quantization + for (coeff_ctr = 0; coeff_ctr < 8; coeff_ctr++) + { + j = *p_scan++; // note that in this part, somehow coefficients were transposed from 2x4 to 4x2. + i = *p_scan++; + + m7 = &tblock[j][i]; + + if (*m7 != 0) + { + scaled_coeff = iabs (*m7) * q_params_4x4->ScaleComp; + level = (scaled_coeff + (q_params_4x4->OffsetComp << 1) ) >> q_bits; + + if (level != 0) + { + if (is_cavlc) + level = imin(level, CAVLC_LEVEL_LIMIT); + level = isignab(level, *m7); + + *m7 = ((level * q_params_4x4->InvScaleComp) << qp_per); + + *DCL++ = level; + *DCR++ = run; + // reset zero level counter + run = 0; + nonzero = TRUE; + } + else + { + run++; + *m7 = 0; + } + } + else + { + run++; + } + } + + *DCL = 0; + + return nonzero; +} + + + diff --git a/lencod/src/quantChroma_trellis.c b/lencod/src/quantChroma_trellis.c new file mode 100644 index 0000000..e3d3751 --- /dev/null +++ b/lencod/src/quantChroma_trellis.c @@ -0,0 +1,223 @@ + +/*! + ************************************************************************************* + * \file quantChroma_trellis.c + * + * \brief + * Quantization process for a Chroma block (trellis based) + * + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * - Alexis Michael Tourapis + * + ************************************************************************************* + */ + +#include "contributors.h" + +#include + +#include "global.h" +#include "q_matrix.h" +#include "quant4x4.h" +#include "quantChroma.h" +#include "rdoq.h" + +/*! + ************************************************************************ + * \brief + * Quantization process for All coefficients for a 2x2 DC block + * + * \par Input: + * + * \par Output: + * + ************************************************************************ + */ +int quant_dc2x2_trellis(Macroblock *currMB, int **tblock, int qp, int* DCLevel, int* DCRun, + LevelQuantParams *q_params_4x4, int **fadjust, const byte (*pos_scan)[2]) +{ + Slice *currSlice = currMB->p_Slice; + QuantParameters *p_Quant = currMB->p_Vid->p_Quant; + Boolean is_cavlc = (Boolean) (currSlice->symbol_mode == CAVLC); + int coeff_ctr; + + int *m7; + + int level, run = 0; + int nonzero = FALSE; + int qp_per = p_Quant->qp_per_matrix[qp]; + int qp_rem = p_Quant->qp_rem_matrix[qp]; + //const byte *p_scan = &pos_scan[0][0]; + int* DCL = &DCLevel[0]; + int* DCR = &DCRun[0]; + + int levelTrellis[16]; + + currSlice->rdoq_dc_cr(currMB, tblock,qp_per,qp_rem, q_params_4x4, pos_scan, levelTrellis, CHROMA_DC); + + m7 = *tblock; + + // Quantization + for (coeff_ctr=0; coeff_ctr < 4; coeff_ctr++) + { + // we need to update q_params_4x4->OffsetComp to a 4x1 array that would contain offset info for + // every 2x2 DC position + if (*m7) + { + level = levelTrellis[coeff_ctr]; + + if (level != 0) + { + if (is_cavlc) + level = imin(level, CAVLC_LEVEL_LIMIT); + + level = isignab(level, *m7); + + *m7++ = ((level * q_params_4x4->InvScaleComp) << qp_per); + + *DCL++ = level; + *DCR++ = run; + // reset zero level counter + run = 0; + nonzero = TRUE; + } + else + { + run++; + *m7++ = 0; + } + } + else + { + run++; + m7++; + } + } + + *DCL = 0; + + return nonzero; +} + +/*! + ************************************************************************ + * \brief + * Quantization process for All coefficients for a 2x2 DC block + * + * \par Input: + * + * \par Output: + * + ************************************************************************ + */ +int quant_dc4x2_trellis(Macroblock *currMB, int **tblock, int qp, int* DCLevel, int* DCRun, + LevelQuantParams *q_params_4x4, int **fadjust, const byte (*pos_scan)[2]) +{ + Slice *currSlice = currMB->p_Slice; + QuantParameters *p_Quant = currMB->p_Vid->p_Quant; + Boolean is_cavlc = (Boolean) (currSlice->symbol_mode == CAVLC); + int i,j, coeff_ctr; + + int *m7; + + int level, run = 0; + int nonzero = FALSE; + int qp_per = p_Quant->qp_per_matrix[qp]; + int qp_rem = p_Quant->qp_rem_matrix[qp]; + const byte *p_scan = &pos_scan[0][0]; + int* DCL = &DCLevel[0]; + int* DCR = &DCRun[0]; + + int levelTrellis[16]; + + currSlice->rdoq_dc_cr(currMB, tblock,qp_per,qp_rem, q_params_4x4,pos_scan, levelTrellis, CHROMA_DC_2x4); + + for (coeff_ctr=0; coeff_ctr < 8; coeff_ctr++) + { + j = *p_scan++; // note that in this part, somehow coefficients were transposed from 2x4 to 4x2. + i = *p_scan++; + + m7 = &tblock[j][i]; + + if (*m7 != 0) + { + level = levelTrellis[coeff_ctr]; + + if (level != 0) + { + if (is_cavlc) + level = imin(level, CAVLC_LEVEL_LIMIT); + level = isignab(level, *m7); + + *m7 = ((level * q_params_4x4->InvScaleComp) << qp_per); + + *DCL++ = level; + *DCR++ = run; + // reset zero level counter + run = 0; + nonzero = TRUE; + } + else + { + run++; + *m7 = 0; + } + } + else + { + run++; + } + } + + *DCL = 0; + + return nonzero; +} + +/*! +************************************************************************ +* \brief +* Rate distortion optimized Quantization process for +* all coefficients in a chroma DC block +* +************************************************************************ +*/ +void rdoq_dc_cr_CAVLC(Macroblock *currMB, int **tblock, int qp_per, int qp_rem, LevelQuantParams *q_params_4x4, const byte (*pos_scan)[2], int levelTrellis[], int type) +{ + VideoParameters *p_Vid = currMB->p_Vid; + + const byte *p_scan = &pos_scan[0][0]; + levelDataStruct levelData[16]; + double lambda_md = 0.0; + + lambda_md = p_Vid->lambda_rdoq[p_Vid->type][p_Vid->masterQP]; + + init_trellis_data_DC_cr_CAVLC(currMB, tblock, qp_per, qp_rem, q_params_4x4, p_scan, &levelData[0]); + est_RunLevel_CAVLC(currMB, levelData, levelTrellis, CHROMA_DC, 0, 0, p_Vid->num_cdc_coeff, lambda_md); +} + +/*! +************************************************************************ +* \brief +* Rate distortion optimized Quantization process for +* all coefficients in a chroma DC block +* +************************************************************************ +*/ +void rdoq_dc_cr_CABAC(Macroblock *currMB, int **tblock, int qp_per, int qp_rem, LevelQuantParams *q_params_4x4, const byte (*pos_scan)[2], int levelTrellis[], int type) +{ + VideoParameters *p_Vid = currMB->p_Vid; + const byte *p_scan = &pos_scan[0][0]; + levelDataStruct levelData[16]; + double lambda_md = 0.0; + int kStart=0, kStop=0, noCoeff = 0, estBits; + + lambda_md = p_Vid->lambda_rdoq[p_Vid->type][p_Vid->masterQP]; + + noCoeff = init_trellis_data_DC_cr_CABAC(currMB, tblock, qp_per, qp_rem, q_params_4x4, p_scan, &levelData[0], &kStart, &kStop); + estBits = est_write_and_store_CBP_block_bit(currMB, type); + est_writeRunLevel_CABAC(currMB, levelData, levelTrellis, type, lambda_md, kStart, kStop, noCoeff, estBits); +} + + diff --git a/lencod/src/ratectl.c b/lencod/src/ratectl.c new file mode 100644 index 0000000..f113b8e --- /dev/null +++ b/lencod/src/ratectl.c @@ -0,0 +1,406 @@ + +/*! + *************************************************************************** + * \file ratectl.c + * + * \brief + * Rate Control algorithm + * + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * - Siwei Ma + * - Zhengguo LI + * + * \date + * 16 Jan. 2003 + ************************************************************************** + */ + +#include +#include + +#include "global.h" +#include "ratectl.h" + + +/*! + ************************************************************************************* + * \brief + * Update Rate Control Parameters + ************************************************************************************* + */ +void rc_store_mad(Macroblock *currMB) +{ + VideoParameters *p_Vid = currMB->p_Vid; + InputParameters *p_Inp = currMB->p_Inp; + RCGeneric *p_gen = p_Vid->p_rc_gen; + + p_gen->MADofMB[currMB->mbAddrX] = ComputeMBMAD(currMB->p_Slice->diffy); + + if(p_Inp->basicunit < p_Vid->FrameSizeInMbs) + { + p_gen->TotalMADBasicUnit += p_gen->MADofMB[currMB->mbAddrX]; + } +} + +/*! + ************************************************************************************* + * \brief + * map QP to Qstep + * + ************************************************************************************* +*/ +double QP2Qstep( int QP ) +{ + int i; + double Qstep; + static const double QP2QSTEP[6] = { 0.625, 0.6875, 0.8125, 0.875, 1.0, 1.125 }; + + Qstep = QP2QSTEP[QP % 6]; + for( i=0; i<(QP/6); i++) + Qstep *= 2; + + return Qstep; +} + + +/*! + ************************************************************************************* + * \brief + * map Qstep to QP + * + ************************************************************************************* +*/ +int Qstep2QP( double Qstep, int qp_offset ) +{ + int q_per = 0, q_rem = 0; + + if( Qstep < QP2Qstep(MIN_QP)) + return MIN_QP; + else if (Qstep > QP2Qstep(MAX_QP + qp_offset) ) + return (MAX_QP + qp_offset); + + while( Qstep > QP2Qstep(5) ) + { + Qstep /= 2.0; + q_per++; + } + + if (Qstep <= 0.65625) + { + //Qstep = 0.625; + q_rem = 0; + } + else if (Qstep <= 0.75) + { + //Qstep = 0.6875; + q_rem = 1; + } + else if (Qstep <= 0.84375) + { + //Qstep = 0.8125; + q_rem = 2; + } + else if (Qstep <= 0.9375) + { + //Qstep = 0.875; + q_rem = 3; + } + else if (Qstep <= 1.0625) + { + //Qstep = 1.0; + q_rem = 4; + } + else + { + //Qstep = 1.125; + q_rem = 5; + } + + return (q_per * 6 + q_rem); +} + +/*! + ************************************************************************************ + * \brief + * calculate MAD for the current macroblock + * + * \return + * calculated MAD + * + ************************************************************************************* +*/ +int ComputeMBMAD(int diffy[16][16]) +{ + int k, l, sum = 0; + + for (k = 0; k < 16; k++) + for (l = 0; l < 16; l++) + sum += iabs(diffy[k][l]); + + return sum; +} + +/*! + ************************************************************************************* + * \brief + * Compute Frame MAD + * + ************************************************************************************* +*/ +double ComputeFrameMAD(VideoParameters *p_Vid) +{ + int64 TotalMAD = 0; + unsigned int i; + for(i = 0; i < p_Vid->FrameSizeInMbs; i++) + TotalMAD += p_Vid->p_rc_gen->MADofMB[i]; + return (double)TotalMAD / (256.0 * (double)p_Vid->FrameSizeInMbs); +} + + +/*! + ************************************************************************************* + * \brief + * Copy JVT rate control objects + * + ************************************************************************************* +*/ +void rc_copy_generic( VideoParameters *p_Vid, RCGeneric *dst, RCGeneric *src ) +{ + /* buffer original addresses for which memory has been allocated */ + int *tmpMADofMB = dst->MADofMB; + + /* copy object */ + + // This could be written as: *dst = *src; + memcpy( (void *)dst, (void *)src, sizeof(RCGeneric) ); + + /* restore original addresses */ + dst->MADofMB = tmpMADofMB; + + /* copy MADs */ + memcpy( (void *)dst->MADofMB, (void *)src->MADofMB, p_Vid->FrameSizeInMbs * sizeof (int) ); +} + +/*! + ************************************************************************************* + * \brief + * Dynamically allocate memory needed for generic rate control + * + ************************************************************************************* + */ +void rc_alloc_generic( VideoParameters *p_Vid, RCGeneric **p_quad ) +{ + *p_quad = (RCGeneric *) malloc ( sizeof( RCGeneric ) ); + if (NULL == *p_quad) + { + no_mem_exit("rc_alloc_generic: rc_alloc_generic"); + } + (*p_quad)->MADofMB = (int *) calloc (p_Vid->FrameSizeInMbs, sizeof (int)); + if (NULL == (*p_quad)->MADofMB) + { + no_mem_exit("rc_alloc_generic: (*p_quad)->MADofMB"); + } + (*p_quad)->FieldFrame = 1; +} + + +/*! + ************************************************************************************* + * \brief + * Free memory needed for generic rate control + * + ************************************************************************************* + */ +void rc_free_generic(RCGeneric **p_quad) +{ + if (NULL!=(*p_quad)->MADofMB) + { + free ((*p_quad)->MADofMB); + (*p_quad)->MADofMB = NULL; + } + if (NULL!=(*p_quad)) + { + free ((*p_quad)); + (*p_quad) = NULL; + } +} + +/*! + ************************************************************************************* + * \brief + * Initialize GOP Level Rate Control parameters + * + ************************************************************************************* + */ +void rc_init_gop_params(VideoParameters *p_Vid, InputParameters *p_Inp) +{ + int np, nb; + RCQuadratic *p_quad = p_Vid->p_rc_quad; + RCGeneric *p_gen = p_Vid->p_rc_gen; + + switch( p_Inp->RCUpdateMode ) + { + case RC_MODE_1: + case RC_MODE_3: + if ( !(p_Vid->curr_frm_idx) ) + { + /* number of P frames */ + np = (int) (p_Inp->no_frames + p_Inp->NumberBFrames) / (1 + p_Inp->NumberBFrames) - 1 + 1; // approximate but good enough (hack...) + /* number of B frames */ + nb = np * p_Inp->NumberBFrames; + + rc_init_GOP(p_Vid, p_Inp, p_quad, p_gen, np, nb); + } + break; + case RC_MODE_0: + case RC_MODE_2: + if (p_Inp->idr_period == 0) + { + if ( !(p_Vid->curr_frm_idx) ) + { + /* number of P frames */ + np = (int) (p_Inp->no_frames + p_Inp->NumberBFrames) / (1 + p_Inp->NumberBFrames) - 1 + 1; // approximate but good enough (hack...) + /* number of B frames */ + nb = np * p_Inp->NumberBFrames; + rc_init_GOP(p_Vid, p_Inp, p_quad, p_gen, np, nb); + } + } + else if ( p_Vid->p_curr_frm_struct->idr_flag ) + { + int M = p_Inp->NumberBFrames + 1; + int n = p_Inp->idr_period; + + /* last GOP may contain less frames */ + if ((p_Vid->curr_frm_idx / p_Inp->idr_period) >= (p_Inp->no_frames / p_Inp->idr_period)) + { + n = p_Inp->no_frames - p_Vid->curr_frm_idx; + } + + /* number of P frames */ + np = (p_Vid->curr_frm_idx == 0) ? 1 + ((n - 2) / M) : (n - 1) / M; + /* number of B frames */ + nb = n - np - 1; + rc_init_GOP(p_Vid, p_Inp, p_quad, p_gen, np, nb); + } + break; + default: + break; + } +} + +/*! + ************************************************************************************* + * \brief + * Initialize Frame Level Rate Control parameters + * + ************************************************************************************* + */ + +void rc_init_frame(VideoParameters *p_Vid, InputParameters *p_Inp) +{ + switch( p_Inp->RCUpdateMode ) + { + case RC_MODE_0: case RC_MODE_1: case RC_MODE_2: case RC_MODE_3: + + // update the number of MBs in the basic unit for MBAFF coding + if( (p_Inp->MbInterlace) && (p_Inp->basicunit < p_Vid->FrameSizeInMbs) && (p_Vid->type == P_SLICE || (p_Inp->RCUpdateMode == RC_MODE_1 && p_Vid->number) ) ) + p_Vid->BasicUnit = p_Inp->basicunit << 1; + else + p_Vid->BasicUnit = p_Inp->basicunit; + + if ( p_Inp->RDPictureDecision ) + { + rc_copy_quadratic( p_Vid, p_Inp, p_Vid->p_rc_quad_init, p_Vid->p_rc_quad ); // store rate allocation quadratic... + rc_copy_generic( p_Vid, p_Vid->p_rc_gen_init, p_Vid->p_rc_gen ); // ...and generic model + } + p_Vid->rc_init_pict_ptr(p_Vid, p_Inp, p_Vid->p_rc_quad, p_Vid->p_rc_gen, 1,0,1, 1.0F); + + if( p_Vid->active_sps->frame_mbs_only_flag) + p_Vid->p_rc_gen->TopFieldFlag=0; + + p_Vid->p_curr_frm_struct->qp = p_Vid->qp = p_Vid->updateQP(p_Vid, p_Inp, p_Vid->p_rc_quad, p_Vid->p_rc_gen, 0) - p_Vid->p_rc_quad->bitdepth_qp_scale; + break; + default: + break; + } +} + +/*! + ************************************************************************************* + * \brief + * Initialize Sequence Level Rate Control parameters + * + ************************************************************************************* + */ + +void rc_init_sequence(VideoParameters *p_Vid, InputParameters *p_Inp) +{ + switch( p_Inp->RCUpdateMode ) + { + case RC_MODE_0: case RC_MODE_1: case RC_MODE_2: case RC_MODE_3: + rc_init_seq(p_Vid, p_Inp, p_Vid->p_rc_quad, p_Vid->p_rc_gen); + break; + default: + break; + } +} + +void rc_store_slice_header_bits( VideoParameters *p_Vid, InputParameters *p_Inp, int len ) +{ + switch (p_Inp->RCUpdateMode) + { + case RC_MODE_0: case RC_MODE_1: case RC_MODE_2: case RC_MODE_3: + p_Vid->p_rc_gen->NumberofHeaderBits +=len; + + // basic unit layer rate control + if(p_Vid->BasicUnit < p_Vid->FrameSizeInMbs) + p_Vid->p_rc_gen->NumberofBasicUnitHeaderBits +=len; + break; + default: + break; + } +} + + +/*! +************************************************************************************* +* \brief +* Update Rate Control Difference +************************************************************************************* +*/ +void rc_store_diff(int diffy[16][16], imgpel **p_curImg, int cpix_x,imgpel **prediction) +{ + int i, j; + int *iDst; + imgpel *Src1, *Src2; + + for(j = 0; j < MB_BLOCK_SIZE; j++) + { + iDst = diffy[j]; + Src1 = &p_curImg[j][cpix_x]; + Src2 = prediction[j]; + for (i = 0; i < MB_BLOCK_SIZE; i++) + { + iDst[i] = Src1[i] - Src2[i]; + } + } +} + +void rc_store_diff_16b(int diffy[16][16], imgpel **p_curImg, int cpix_x,imgpel **prediction) +{ + int i, j; + int *iDst; + uint16 *Src1, *Src2; + + for(j = 0; j < MB_BLOCK_SIZE; j++) + { + iDst = diffy[j]; + Src1 = (uint16*)(p_curImg[j]) +cpix_x; + Src2 = (uint16*)(prediction[j]); + for (i = 0; i < MB_BLOCK_SIZE; i++) + { + iDst[i] = Src1[i] - Src2[i]; + } + } +} diff --git a/lencod/src/rc_quadratic.c b/lencod/src/rc_quadratic.c new file mode 100644 index 0000000..2fbd89f --- /dev/null +++ b/lencod/src/rc_quadratic.c @@ -0,0 +1,2754 @@ + +/*! + *************************************************************************** + * \file rc_quadratic.c + * + * \brief + * Rate Control algorithm + * + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * - Siwei Ma + * - Zhengguo LI + * - Athanasios Leontaris + * + * \date + * 16 Jan. 2003 + ************************************************************************** + */ + +#include +#include + +#include "global.h" +#include "ratectl.h" + + +static const float THETA = 1.3636F; +static const float OMEGA = 0.9F; +static const float MINVALUE = 4.0F; +/*! + ************************************************************************************* + * \brief + * Dynamically allocate memory needed for rate control + * + ************************************************************************************* + */ +void rc_alloc_quadratic( VideoParameters *p_Vid, InputParameters *p_Inp, RCQuadratic **p_quad ) +{ + int rcBufSize = p_Vid->FrameSizeInMbs / p_Inp->basicunit; + + RCQuadratic *lprc; + + (*p_quad) = (RCQuadratic *) malloc ( sizeof( RCQuadratic ) ); + if (NULL==(*p_quad)) + { + no_mem_exit("rc_alloc_quadratic: (*p_quad)"); + } + lprc = *p_quad; + + lprc->PreviousFrameMAD = 1.0; + lprc->CurrentFrameMAD = 1.0; + lprc->Pprev_bits = 0; + lprc->Target = 0; + lprc->TargetField = 0; + lprc->LowerBound = 0; + lprc->UpperBound1 = INT_MAX; + lprc->UpperBound2 = INT_MAX; + lprc->Wp = 0.0; + lprc->Wb = 0.0; + lprc->AveWb = 0.0; + lprc->PAveFrameQP = p_Inp->qp[I_SLICE] + p_Vid->bitdepth_luma_qp_scale; + lprc->m_Qc = lprc->PAveFrameQP; + lprc->FieldQPBuffer = lprc->PAveFrameQP; + lprc->FrameQPBuffer = lprc->PAveFrameQP; + lprc->PAverageQp = lprc->PAveFrameQP; + lprc->MyInitialQp = lprc->PAveFrameQP; + lprc->AveWb = 0.0; + + lprc->BUPFMAD = (double*) calloc ((rcBufSize), sizeof (double)); + if (NULL==lprc->BUPFMAD) + { + no_mem_exit("rc_alloc_quadratic: lprc->BUPFMAD"); + } + + lprc->BUCFMAD = (double*) calloc ((rcBufSize), sizeof (double)); + if (NULL==lprc->BUCFMAD) + { + no_mem_exit("rc_alloc_quadratic: lprc->BUCFMAD"); + } + + lprc->FCBUCFMAD = (double*) calloc ((rcBufSize), sizeof (double)); + if (NULL==lprc->FCBUCFMAD) + { + no_mem_exit("rc_alloc_quadratic: lprc->FCBUCFMAD"); + } + + lprc->FCBUPFMAD = (double*) calloc ((rcBufSize), sizeof (double)); + if (NULL==lprc->FCBUPFMAD) + { + no_mem_exit("rc_alloc_quadratic: lprc->FCBUPFMAD"); + } +} + +/*! + ************************************************************************************* + * \brief + * Copy JVT rate control objects + * + ************************************************************************************* + */ +void rc_copy_quadratic( VideoParameters *p_Vid, InputParameters *p_Inp, RCQuadratic *dst, RCQuadratic *src ) +{ + int rcBufSize = p_Vid->FrameSizeInMbs / p_Inp->basicunit; + /* buffer original addresses for which memory has been allocated */ + double *tmpBUPFMAD = dst->BUPFMAD; + double *tmpBUCFMAD = dst->BUCFMAD; + double *tmpFCBUPFMAD = dst->FCBUPFMAD; + double *tmpFCBUCFMAD = dst->FCBUCFMAD; + + /* copy object */ + memcpy( (void *)dst, (void *)src, sizeof(RCQuadratic) ); + + /* restore original addresses */ + dst->BUPFMAD = tmpBUPFMAD; + dst->BUCFMAD = tmpBUCFMAD; + dst->FCBUPFMAD = tmpFCBUPFMAD; + dst->FCBUCFMAD = tmpFCBUCFMAD; + + /* copy MADs */ + memcpy( (void *)dst->BUPFMAD, (void *)src->BUPFMAD, (rcBufSize) * sizeof (double) ); + memcpy( (void *)dst->BUCFMAD, (void *)src->BUCFMAD, (rcBufSize) * sizeof (double) ); + memcpy( (void *)dst->FCBUPFMAD, (void *)src->FCBUPFMAD, (rcBufSize) * sizeof (double) ); + memcpy( (void *)dst->FCBUCFMAD, (void *)src->FCBUCFMAD, (rcBufSize) * sizeof (double) ); +} + +/*! + ************************************************************************************* + * \brief + * Free memory needed for rate control + * + ************************************************************************************* +*/ +void rc_free_quadratic(RCQuadratic **p_quad) +{ + if (NULL!=(*p_quad)->BUPFMAD) + { + free ((*p_quad)->BUPFMAD); + (*p_quad)->BUPFMAD = NULL; + } + if (NULL!=(*p_quad)->BUCFMAD) + { + free ((*p_quad)->BUCFMAD); + (*p_quad)->BUCFMAD = NULL; + } + if (NULL!=(*p_quad)->FCBUCFMAD) + { + free ((*p_quad)->FCBUCFMAD); + (*p_quad)->FCBUCFMAD = NULL; + } + if (NULL!=(*p_quad)->FCBUPFMAD) + { + free ((*p_quad)->FCBUPFMAD); + (*p_quad)->FCBUPFMAD = NULL; + } + if (NULL!=(*p_quad)) + { + free ((*p_quad)); + (*p_quad) = NULL; + } +} + + +/*! + ************************************************************************************* + * \brief + * Initialize rate control parameters + * + ************************************************************************************* +*/ +void rc_init_seq(VideoParameters *p_Vid, InputParameters *p_Inp, RCQuadratic *p_quad, RCGeneric *p_gen) +{ + double L1,L2,L3,bpp; + int qp, i; + + switch ( p_Inp->RCUpdateMode ) + { + case RC_MODE_0: + p_Vid->updateQP = updateQPRC0; + break; + case RC_MODE_1: + p_Vid->updateQP = updateQPRC1; + break; + case RC_MODE_2: + p_Vid->updateQP = updateQPRC2; + break; + case RC_MODE_3: + p_Vid->updateQP = updateQPRC3; + break; + default: + p_Vid->updateQP = updateQPRC0; + break; + } + // set function pointers + p_Vid->rc_update_pict_frame_ptr = rc_update_pict_frame; + p_Vid->rc_update_picture_ptr = rc_update_picture; + p_Vid->rc_init_pict_ptr = rc_init_pict; + + p_quad->Xp=0; + p_quad->Xb=0; + + p_quad->bit_rate = (float) p_Inp->bit_rate; + p_quad->frame_rate = p_Vid->framerate; + p_quad->PrevBitRate = p_quad->bit_rate; + + /*compute the total number of MBs in a frame*/ + if(p_Inp->basicunit > p_Vid->FrameSizeInMbs) + p_Inp->basicunit = p_Vid->FrameSizeInMbs; + if(p_Inp->basicunit < p_Vid->FrameSizeInMbs) + p_quad->TotalNumberofBasicUnit = p_Vid->FrameSizeInMbs/p_Inp->basicunit; + else + p_quad->TotalNumberofBasicUnit = 1; + + /*initialize the parameters of fluid flow traffic model*/ + p_gen->CurrentBufferFullness = 0; + p_quad->GOPTargetBufferLevel = (double) p_gen->CurrentBufferFullness; + + /*initialize the previous window size*/ + p_quad->m_windowSize = 0; + p_quad->MADm_windowSize = 0; + p_gen->NumberofCodedBFrame = 0; + p_quad->NumberofCodedPFrame = 0; + p_gen->NumberofGOP = 0; + /*remaining # of bits in GOP */ + p_gen->RemainingBits = 0; + /*control parameter */ + if(p_Inp->NumberBFrames>0) + { + p_quad->GAMMAP=0.25; + p_quad->BETAP=0.9; + } + else + { + p_quad->GAMMAP=0.5; + p_quad->BETAP=0.5; + } + + /*quadratic rate-distortion model*/ + p_quad->PPreHeader=0; + + p_quad->Pm_X1 = p_quad->bit_rate * 1.0; + p_quad->Pm_X2 = 0.0; + /* linear prediction model for P picture*/ + p_quad->PMADPictureC1 = 1.0; + p_quad->PMADPictureC2 = 0.0; + + // Initialize values + for(i=0;i<21;i++) + { + p_quad->Pm_rgQp[i] = 0; + p_quad->Pm_rgRp[i] = 0.0; + p_quad->PPictureMAD[i] = 0.0; + } + + //Define the largest variation of quantization parameters + p_quad->PMaxQpChange = p_Inp->RCMaxQPChange; + + /*basic unit layer rate control*/ + p_quad->PAveHeaderBits1 = 0; + p_quad->PAveHeaderBits3 = 0; + p_quad->DDquant = (p_quad->TotalNumberofBasicUnit>=9 ? 1 : 2); + + p_quad->MBPerRow = p_Vid->PicWidthInMbs; + + /*adaptive field/frame coding*/ + p_gen->FieldControl=0; + + if (p_Inp->SeinitialQP==0) + { + /*compute the initial QP*/ + bpp = 1.0*p_quad->bit_rate /(p_quad->frame_rate*p_Vid->size); + + if (p_Vid->width == 176) + { + L1 = 0.1; + L2 = 0.3; + L3 = 0.6; + } + else if (p_Vid->width == 352) + { + L1 = 0.2; + L2 = 0.6; + L3 = 1.2; + } + else + { + L1 = 0.6; + L2 = 1.4; + L3 = 2.4; + } + if (bpp<= L1) + qp = 35; + else if(bpp<=L2) + qp = 25; + else if(bpp<=L3) + qp = 20; + else + qp = 10; + p_Inp->SeinitialQP = qp; + } + + // high bit-depth + p_quad->bitdepth_qp_scale = p_Vid->bitdepth_luma_qp_scale; +} + +/*! + ************************************************************************************* + * \brief + * Initialize one GOP + * + ************************************************************************************* +*/ +void rc_init_GOP(VideoParameters *p_Vid, InputParameters *p_Inp, RCQuadratic *p_quad, RCGeneric *p_gen, int np, int nb) +{ + //int OverBits; + int GOPDquant; + int64 AllocatedBits; + + // bit allocation for RC_MODE_3 + switch( p_Inp->RCUpdateMode ) + { + case RC_MODE_3: + { + int sum = 0, tmp, level, levels = 0, num_frames[RC_MAX_TEMPORAL_LEVELS]; + float numer, denom; + int gop = p_Inp->NumberBFrames + 1; + int i_period = p_Inp->intra_period == 0 ? p_Inp->idr_period : (p_Inp->idr_period == 0 ? p_Inp->intra_period : imin(p_Inp->intra_period, p_Inp->idr_period) ); + + memset( num_frames, 0, RC_MAX_TEMPORAL_LEVELS * sizeof(int) ); + // are there any B frames? + if ( p_Inp->NumberBFrames ) + { + if ( p_Inp->HierarchicalCoding == 1 ) // two layers: even/odd + { + levels = 2; + num_frames[0] = p_Inp->NumberBFrames >> 1; + num_frames[1] = (p_Inp->NumberBFrames - num_frames[0]) >= 0 ? (p_Inp->NumberBFrames - num_frames[0]) : 0; + } + else if ( p_Inp->HierarchicalCoding == 2 ) // binary hierarchical structure + { + // check if gop is power of two + tmp = gop; + while ( tmp ) + { + sum += tmp & 1; + tmp >>= 1; + } + assert( sum == 1 ); + + // determine number of levels + levels = 0; + tmp = gop; + while ( tmp > 1 ) + { + tmp >>= 1; // divide by 2 + num_frames[levels] = 1 << levels; + levels++; + } + assert( levels >= 1 && levels <= RC_MAX_TEMPORAL_LEVELS ); + } + else if ( p_Inp->HierarchicalCoding == 3 ) + { + fprintf(stdout, "\n RCUpdateMode=3 and HierarchicalCoding == 3 are currently not supported"); // This error message should be moved elsewhere and have proper memory deallocation + exit(1); + } + else // all frames of the same priority - level + { + levels = 1; + num_frames[0] = p_Inp->NumberBFrames; + } + p_gen->temporal_levels = levels; + } + else + { + for ( level = 0; level < RC_MAX_TEMPORAL_LEVELS; level++ ) + { + p_Inp->RCBSliceBitRatio[level] = 0.0F; + } + p_gen->temporal_levels = 0; + } + // calculate allocated bits for each type of frame + numer = (float)(( (!i_period ? 1 : i_period) * gop) * ((double)p_Inp->bit_rate / p_Inp->source.frame_rate)); + denom = 0.0F; + + for ( level = 0; level < levels; level++ ) + { + denom += (float)(num_frames[level] * p_Inp->RCBSliceBitRatio[level]); + p_gen->hierNb[level] = num_frames[level] * np; + } + denom += 1.0F; + if ( i_period >= 1 ) + { + denom *= (float)i_period; + denom += (float)p_Inp->RCISliceBitRatio - 1.0F; + } + + // set bit targets for each type of frame + p_gen->RCPSliceBits = (int) floor( numer / denom + 0.5F ); + p_gen->RCISliceBits = i_period ? (int)(p_Inp->RCISliceBitRatio * p_gen->RCPSliceBits + 0.5) : 0; + + for ( level = 0; level < levels; level++ ) + { + p_gen->RCBSliceBits[level] = (int)floor(p_Inp->RCBSliceBitRatio[level] * p_gen->RCPSliceBits + 0.5); + } + + p_gen->NISlice = i_period ? ( p_Inp->no_frames / i_period ) : 0; + p_gen->NPSlice = (p_Inp->no_frames / (1 + p_Inp->NumberBFrames)) - p_gen->NISlice; // approximate but good enough for sufficient number of frames + } + break; + default: + break; + } + + /* check if the last GOP over uses its budget. If yes, the initial QP of the I frame in + the coming GOP will be increased.*/ + + //OverBits=-(int)(p_gen->RemainingBits); + + /*initialize the lower bound and the upper bound for the target bits of each frame, HRD consideration*/ + p_quad->LowerBound = (int)(p_gen->RemainingBits + p_quad->bit_rate / p_quad->frame_rate); + p_quad->UpperBound1 = (int)(p_gen->RemainingBits + (p_quad->bit_rate * 2.048)); + + /*compute the total number of bits for the current GOP*/ + AllocatedBits = (int64) floor((1 + np + nb) * p_quad->bit_rate / p_quad->frame_rate + 0.5); + p_gen->RemainingBits += AllocatedBits; + p_quad->Np = np; + p_quad->Nb = nb; + + p_quad->GOPOverdue=FALSE; + + /*field coding*/ + //p_gen->NoGranularFieldRC = ( p_Inp->PicInterlace || !p_Inp->MbInterlace || p_Inp->basicunit != p_Vid->FrameSizeInMbs ); + if ( !p_Inp->PicInterlace && p_Inp->MbInterlace && p_Inp->basicunit == p_Vid->FrameSizeInMbs ) + p_gen->NoGranularFieldRC = 0; + else + p_gen->NoGranularFieldRC = 1; + + /*Compute InitialQp for each GOP*/ + p_quad->TotalPFrame=np; + p_gen->NumberofGOP++; + if(p_gen->NumberofGOP==1) + { + p_quad->MyInitialQp = p_Inp->SeinitialQP + p_quad->bitdepth_qp_scale; + p_quad->CurrLastQP = p_quad->MyInitialQp - 1; //recent change -0; + p_quad->QPLastGOP = p_quad->MyInitialQp; + + p_quad->PAveFrameQP = p_quad->MyInitialQp; + p_quad->m_Qc = p_quad->PAveFrameQP; + p_quad->FieldQPBuffer = p_quad->PAveFrameQP; + p_quad->FrameQPBuffer = p_quad->PAveFrameQP; + p_quad->PAverageQp = p_quad->PAveFrameQP; + } + else + { + /*adaptive field/frame coding*/ + if( p_Inp->PicInterlace == ADAPTIVE_CODING || p_Inp->MbInterlace ) + { + if (p_gen->FieldFrame == 1) + { + p_quad->TotalQpforPPicture += p_quad->FrameQPBuffer; + p_quad->QPLastPFrame = p_quad->FrameQPBuffer; + } + else + { + p_quad->TotalQpforPPicture += p_quad->FieldQPBuffer; + p_quad->QPLastPFrame = p_quad->FieldQPBuffer; + } + } + /*compute the average QP of P frames in the previous GOP*/ + p_quad->PAverageQp=(int)(1.0 * p_quad->TotalQpforPPicture / p_quad->NumberofPPicture+0.5); + + GOPDquant=(int)((1.0*(np+nb+1)/15.0) + 0.5); + if(GOPDquant>2) + GOPDquant=2; + + p_quad->PAverageQp -= GOPDquant; + + if (p_quad->PAverageQp > (p_quad->QPLastPFrame - 2)) + p_quad->PAverageQp--; + + // QP is constrained by QP of previous GOP + p_quad->PAverageQp = iClip3(p_quad->QPLastGOP - 2, p_quad->QPLastGOP + 2, p_quad->PAverageQp); + // Also clipped within range. + p_quad->PAverageQp = iClip3(p_Vid->RCMinQP + p_quad->bitdepth_qp_scale, p_Vid->RCMaxQP + p_quad->bitdepth_qp_scale, p_quad->PAverageQp); + + p_quad->MyInitialQp = p_quad->PAverageQp; + p_quad->Pm_Qp = p_quad->PAverageQp; + p_quad->PAveFrameQP = p_quad->PAverageQp; + p_quad->QPLastGOP = p_quad->MyInitialQp; + p_quad->PrevLastQP = p_quad->CurrLastQP; + p_quad->CurrLastQP = p_quad->MyInitialQp - 1; + } + + p_quad->TotalQpforPPicture=0; + p_quad->NumberofPPicture=0; + p_quad->NumberofBFrames=0; +} + + +/*! + ************************************************************************************* + * \brief + * Initialize one picture + * + ************************************************************************************* +*/ +void rc_init_pict(VideoParameters *p_Vid, InputParameters *p_Inp, RCQuadratic *p_quad, RCGeneric *p_gen, int fieldpic,int topfield,int targetcomputation, float mult) +{ + int tmp_T; + + /* compute the total number of basic units in a frame */ + if(p_Inp->MbInterlace) + p_quad->TotalNumberofBasicUnit = p_Vid->FrameSizeInMbs / p_Vid->BasicUnit; + else + p_quad->TotalNumberofBasicUnit = p_Vid->FrameSizeInMbs / p_Inp->basicunit; + + p_Vid->NumberofCodedMacroBlocks = 0; + + /* Normally, the bandwidth for the VBR case is estimated by + a congestion control algorithm. A bandwidth curve can be predefined if we only want to + test the proposed algorithm */ + if(p_Inp->channel_type==1) + { + if(p_quad->NumberofCodedPFrame==58) + p_quad->bit_rate *= 1.5; + else if(p_quad->NumberofCodedPFrame==59) + p_quad->PrevBitRate = p_quad->bit_rate; + } + + /* predefine a target buffer level for each frame */ + if((fieldpic||topfield) && targetcomputation) + { + if ( p_Vid->type == P_SLICE || (p_Inp->RCUpdateMode == RC_MODE_1 && (p_Vid->number !=0)) ) + { + /* Since the available bandwidth may vary at any time, the total number of + bits is updated picture by picture*/ + if(p_quad->PrevBitRate!=p_quad->bit_rate) + p_gen->RemainingBits +=(int) floor((p_quad->bit_rate-p_quad->PrevBitRate)*(p_quad->Np + p_quad->Nb)/p_quad->frame_rate+0.5); + + /* predefine the target buffer level for each picture. + frame layer rate control */ + if(p_Vid->BasicUnit == p_Vid->FrameSizeInMbs) + { + if(p_quad->NumberofPPicture==1) + { + p_quad->TargetBufferLevel = (double) p_gen->CurrentBufferFullness; + p_quad->DeltaP = (p_gen->CurrentBufferFullness - p_quad->GOPTargetBufferLevel) / (p_quad->TotalPFrame-1); + p_quad->TargetBufferLevel -= p_quad->DeltaP; + } + else if(p_quad->NumberofPPicture>1) + p_quad->TargetBufferLevel -= p_quad->DeltaP; + } + /* basic unit layer rate control */ + else + { + if(p_quad->NumberofCodedPFrame>0) + { + /* adaptive frame/field coding */ + if(((p_Inp->PicInterlace==ADAPTIVE_CODING)||(p_Inp->MbInterlace))&&(p_gen->FieldControl==1)) + memcpy((void *)p_quad->FCBUPFMAD,(void *)p_quad->FCBUCFMAD, p_quad->TotalNumberofBasicUnit * sizeof(double)); + else + memcpy((void *)p_quad->BUPFMAD,(void *)p_quad->BUCFMAD, p_quad->TotalNumberofBasicUnit * sizeof(double)); + } + + if(p_gen->NumberofGOP==1) + { + if(p_quad->NumberofPPicture==1) + { + p_quad->TargetBufferLevel = (double) p_gen->CurrentBufferFullness; + p_quad->DeltaP = (p_gen->CurrentBufferFullness - p_quad->GOPTargetBufferLevel)/(p_quad->TotalPFrame - 1); + p_quad->TargetBufferLevel -= p_quad->DeltaP; + } + else if(p_quad->NumberofPPicture>1) + p_quad->TargetBufferLevel -= p_quad->DeltaP; + } + else if(p_gen->NumberofGOP>1) + { + if(p_quad->NumberofPPicture==0) + { + p_quad->TargetBufferLevel = (double) p_gen->CurrentBufferFullness; + p_quad->DeltaP = (p_gen->CurrentBufferFullness - p_quad->GOPTargetBufferLevel) / p_quad->TotalPFrame; + p_quad->TargetBufferLevel -= p_quad->DeltaP; + } + else if(p_quad->NumberofPPicture>0) + p_quad->TargetBufferLevel -= p_quad->DeltaP; + } + } + + if(p_quad->NumberofCodedPFrame==1) + p_quad->AveWp = p_quad->Wp; + + if((p_quad->NumberofCodedPFrame<8)&&(p_quad->NumberofCodedPFrame>1)) + p_quad->AveWp = (p_quad->AveWp + p_quad->Wp * (p_quad->NumberofCodedPFrame-1))/p_quad->NumberofCodedPFrame; + else if(p_quad->NumberofCodedPFrame>1) + p_quad->AveWp = (p_quad->Wp + 7 * p_quad->AveWp) / 8; + + // compute the average complexity of B frames + if(p_Inp->NumberBFrames>0) + { + // compute the target buffer level + p_quad->TargetBufferLevel += (p_quad->AveWp * (p_Inp->NumberBFrames + 1)*p_quad->bit_rate\ + /(p_quad->frame_rate*(p_quad->AveWp+p_quad->AveWb*p_Inp->NumberBFrames))-p_quad->bit_rate/p_quad->frame_rate); + } + } + else if ( p_Vid->type == B_SLICE ) + { + /* update the total number of bits if the bandwidth is changed*/ + if(p_quad->PrevBitRate != p_quad->bit_rate) + p_gen->RemainingBits +=(int) floor((p_quad->bit_rate-p_quad->PrevBitRate) * (p_quad->Np + p_quad->Nb) / p_quad->frame_rate+0.5); + if(p_gen->NumberofCodedBFrame == 1) + { + if(p_quad->NumberofCodedPFrame == 1) + { + p_quad->AveWp = p_quad->Wp; + } + p_quad->AveWb = p_quad->Wb; + } + else if(p_gen->NumberofCodedBFrame > 1) + { + //compute the average weight + if(p_gen->NumberofCodedBFrame<8) + p_quad->AveWb = (p_quad->AveWb + p_quad->Wb*(p_gen->NumberofCodedBFrame-1)) / p_gen->NumberofCodedBFrame; + else + p_quad->AveWb = (p_quad->Wb + 7 * p_quad->AveWb) / 8; + } + } + /* Compute the target bit for each frame */ + if( p_Vid->type == P_SLICE || ( (p_Vid->number != 0) && (p_Inp->RCUpdateMode == RC_MODE_1 || p_Inp->RCUpdateMode == RC_MODE_3 ) ) ) + { + /* frame layer rate control */ + if(p_Vid->BasicUnit == p_Vid->FrameSizeInMbs || (p_Inp->RCUpdateMode == RC_MODE_3) ) + { + if(p_quad->NumberofCodedPFrame>0) + { + if (p_Inp->RCUpdateMode == RC_MODE_3) + { + int level_idx = (p_Vid->type == B_SLICE && p_Inp->HierarchicalCoding) ? (p_Vid->p_curr_frm_struct->layer - 1) : 0; + int bitrate = (p_Vid->type == B_SLICE) ? p_gen->RCBSliceBits[ level_idx ] + : ( p_Vid->type == P_SLICE ? p_gen->RCPSliceBits : p_gen->RCISliceBits ); + int level, denom = p_gen->NISlice * p_gen->RCISliceBits + p_gen->NPSlice * p_gen->RCPSliceBits; + + if ( p_Inp->HierarchicalCoding ) + { + for ( level = 0; level < p_gen->temporal_levels; level++ ) + denom += p_gen->hierNb[ level ] * p_gen->RCBSliceBits[ level ]; + } + else + { + denom += p_gen->hierNb[0] * p_gen->RCBSliceBits[0]; + } + // target due to remaining bits + p_quad->Target = (int) floor( (float)(1.0 * bitrate * p_gen->RemainingBits) / (float)denom + 0.5F ); + // target given original taget rate and buffer considerations + tmp_T = imax(0, (int) floor( (double)bitrate - p_quad->GAMMAP * (p_gen->CurrentBufferFullness-p_quad->TargetBufferLevel) + 0.5) ); + // translate Target rate from B or I "domain" to P domain since the P RC model is going to be used to select the QP + // for hierarchical coding adjust the target QP to account for different temporal levels + switch( p_Vid->type ) + { + case B_SLICE: + p_quad->Target = (int) floor( (float)p_quad->Target / p_Inp->RCBoverPRatio + 0.5F); + break; + case I_SLICE: + p_quad->Target = (int) floor( (float)p_quad->Target / (p_Inp->RCIoverPRatio * 4.0) + 0.5F); // 4x accounts for the fact that header bits reduce the percentage of texture + break; + case P_SLICE: + default: + break; + } + } + else + { + p_quad->Target = (int) floor( p_quad->Wp * p_gen->RemainingBits / (p_quad->Np * p_quad->Wp + p_quad->Nb * p_quad->Wb) + 0.5); + tmp_T = imax(0, (int) floor(p_quad->bit_rate / p_quad->frame_rate - p_quad->GAMMAP * (p_gen->CurrentBufferFullness-p_quad->TargetBufferLevel) + 0.5)); + p_quad->Target = (int) floor(p_quad->BETAP * (p_quad->Target - tmp_T) + tmp_T + 0.5); + } + } + } + /* basic unit layer rate control */ + else + { + if(((p_gen->NumberofGOP == 1)&&(p_quad->NumberofCodedPFrame>0)) + || (p_gen->NumberofGOP > 1)) + { + p_quad->Target = (int) (floor( p_quad->Wp * p_gen->RemainingBits / (p_quad->Np * p_quad->Wp + p_quad->Nb * p_quad->Wb) + 0.5)); + tmp_T = imax(0, (int) (floor(p_quad->bit_rate / p_quad->frame_rate - p_quad->GAMMAP * (p_gen->CurrentBufferFullness-p_quad->TargetBufferLevel) + 0.5))); + p_quad->Target = (int) (floor(p_quad->BETAP * (p_quad->Target - tmp_T) + tmp_T + 0.5)); + } + } + p_quad->Target = (int)(mult * p_quad->Target); + + /* HRD consideration */ + if ( p_Inp->RCUpdateMode != RC_MODE_3 || p_Vid->type == P_SLICE ) + p_quad->Target = iClip3(p_quad->LowerBound, p_quad->UpperBound2, p_quad->Target); + if((topfield) || (fieldpic && ((p_Inp->PicInterlace==ADAPTIVE_CODING)||(p_Inp->MbInterlace)))) + p_quad->TargetField=p_quad->Target; + } + } + + if(fieldpic || topfield) + { + /* frame layer rate control */ + p_gen->NumberofHeaderBits = 0; + p_gen->NumberofTextureBits = 0; + + /* basic unit layer rate control */ + if(p_Vid->BasicUnit < p_Vid->FrameSizeInMbs) + { + p_quad->TotalFrameQP = 0; + p_gen->NumberofBasicUnitHeaderBits = 0; + p_gen->NumberofBasicUnitTextureBits = 0; + p_gen->TotalMADBasicUnit = 0; + if(p_gen->FieldControl==0) + p_quad->NumberofBasicUnit = p_quad->TotalNumberofBasicUnit; + else + p_quad->NumberofBasicUnit = p_quad->TotalNumberofBasicUnit >> 1; + } + } + + if( ( p_Vid->type==P_SLICE || (p_Inp->RCUpdateMode == RC_MODE_1 && (p_Vid->number != 0)) ) && p_Vid->BasicUnit < p_Vid->FrameSizeInMbs && p_gen->FieldControl == 1 ) + { + /* top field at basic unit layer rate control */ + if(topfield) + { + p_quad->bits_topfield=0; + p_quad->Target=(int)(p_quad->TargetField*0.6); + } + /* bottom field at basic unit layer rate control */ + else + { + p_quad->Target=p_quad->TargetField-p_quad->bits_topfield; + p_gen->NumberofBasicUnitHeaderBits=0; + p_gen->NumberofBasicUnitTextureBits=0; + p_gen->TotalMADBasicUnit=0; + p_quad->NumberofBasicUnit=p_quad->TotalNumberofBasicUnit >> 1; + } + } +} + +/*! + ************************************************************************************* + * \brief + * update one picture after frame/field encoding + * + * \param p_Vid + * Image encoding parameters for picture + * \param p_Inp + * Input configuration parameters + * \param p_quad + * quadratic model + * \param p_gen + * generic rate control parameters + * \param nbits + * number of bits used for picture + * + ************************************************************************************* +*/ +void rc_update_pict(VideoParameters *p_Vid, InputParameters *p_Inp, RCQuadratic *p_quad, RCGeneric *p_gen, int nbits) +{ + int delta_bits = (nbits - (int)floor(p_quad->bit_rate / p_quad->frame_rate + 0.5F) ); + // remaining # of bits in GOP + p_gen->RemainingBits -= nbits; + p_gen->CurrentBufferFullness += delta_bits; + + // update the lower bound and the upper bound for the target bits of each frame, HRD consideration + p_quad->LowerBound -= (int) delta_bits; + p_quad->UpperBound1 -= (int) delta_bits; + p_quad->UpperBound2 = (int)(OMEGA * p_quad->UpperBound1); + + // update the parameters of quadratic R-D model + if( p_Vid->type==P_SLICE || (p_Inp->RCUpdateMode == RC_MODE_1 && p_Vid->curr_frm_idx) ) + { + updateRCModel(p_Vid, p_Inp, p_quad, p_gen); + if ( p_Inp->RCUpdateMode == RC_MODE_3 ) + p_quad->PreviousWholeFrameMAD = ComputeFrameMAD(p_Vid); + } +} + +/*! + ************************************************************************************* + * \brief + * update one picture after coding + * + * \param p_Vid + * video parameters for current picture + * \param p_Inp + * input parameters from configuration + * \param bits + * number of bits used for picture + * + ************************************************************************************* +*/ + +void rc_update_picture( VideoParameters *p_Vid, InputParameters *p_Inp, int bits ) +{ + rc_update_pict(p_Vid, p_Inp, p_Vid->p_rc_quad, p_Vid->p_rc_gen, bits); +} + +int updateComplexity( VideoParameters *p_Vid, RCQuadratic *p_quad, RCGeneric *p_gen, Boolean is_updated, int nbits ) +{ + double Avem_Qc; + + /* frame layer rate control */ + if(p_Vid->BasicUnit == p_Vid->FrameSizeInMbs) + return ((int) floor(nbits * p_quad->m_Qc + 0.5)); + /* basic unit layer rate control */ + else + { + if( is_updated ) + { + if( p_gen->NoGranularFieldRC == 0 || p_gen->FieldControl == 0 ) + { + Avem_Qc = (double)p_quad->TotalFrameQP / (double)p_quad->TotalNumberofBasicUnit; + return ((int)floor(nbits * Avem_Qc + 0.5)); + } + } + else if( p_Vid->type == B_SLICE ) + return ((int) floor(nbits * p_quad->m_Qc + 0.5)); + } + return 0; +} + +void updatePparams( RCQuadratic *p_quad, RCGeneric *p_gen, int complexity ) +{ + p_quad->Xp = complexity; + p_quad->Np--; + p_quad->Wp = p_quad->Xp; + p_quad->Pm_Hp = p_gen->NumberofHeaderBits; + p_quad->NumberofCodedPFrame++; + p_quad->NumberofPPicture++; +} + +void updateBparams( RCQuadratic *p_quad, RCGeneric *p_gen, int complexity ) +{ + p_quad->Xb = complexity; + p_quad->Nb--; + p_quad->Wb = p_quad->Xb / THETA; + p_quad->NumberofBFrames++; + p_gen->NumberofCodedBFrame++; +} + +/*! + ************************************************************************************* + * \brief + * update after frame encoding + * \param p_Vid + * Image encoding parameters for picture + * \param p_Inp + * Input configuration parameters + * \param p_quad + * quadratic model + * \param p_gen + * generic rate control parameters + * \param nbits + * number of bits used for frame + * + ************************************************************************************* +*/ +void rc_update_pict_frame(VideoParameters *p_Vid, InputParameters *p_Inp, RCQuadratic *p_quad, RCGeneric *p_gen, int nbits) +{ + /* update the complexity weight of I, P, B frame */ + int complexity = 0; + + switch( p_Inp->RCUpdateMode ) + { + case RC_MODE_0: + case RC_MODE_2: + default: + complexity = updateComplexity( p_Vid, p_quad, p_gen, (Boolean) (p_Vid->type == P_SLICE), nbits ); + if ( p_Vid->type == P_SLICE ) + { + if( p_gen->NoGranularFieldRC == 0 || p_gen->FieldControl == 0 ) + updatePparams( p_quad, p_gen, complexity ); + else + p_gen->NoGranularFieldRC = 0; + } + else if ( p_Vid->type == B_SLICE ) + updateBparams( p_quad, p_gen, complexity ); + break; + case RC_MODE_1: + complexity = updateComplexity( p_Vid, p_quad, p_gen, (Boolean) (p_Vid->number != 0), nbits ); + if ( p_Vid->number != 0 ) + { + if( p_gen->NoGranularFieldRC == 0 || p_gen->FieldControl == 0 ) + updatePparams( p_quad, p_gen, complexity ); + else + p_gen->NoGranularFieldRC = 0; + } + break; + case RC_MODE_3: + complexity = updateComplexity( p_Vid, p_quad, p_gen, (Boolean) (p_Vid->type == P_SLICE), nbits ); + if (p_Vid->type == I_SLICE && (p_Vid->number != 0)) + p_gen->NISlice--; + + if ( p_Vid->type == P_SLICE ) + { + if( p_gen->NoGranularFieldRC == 0 || p_gen->FieldControl == 0 ) + { + updatePparams( p_quad, p_gen, complexity ); + p_gen->NPSlice--; + } + else + p_gen->NoGranularFieldRC = 0; + } + else if ( p_Vid->type == B_SLICE ) + { + updateBparams( p_quad, p_gen, complexity ); + p_gen->hierNb[ p_Inp->HierarchicalCoding ? (p_Vid->p_curr_frm_struct->layer - 1) : 0 ]--; + } + break; + } +} + + +/*! + ************************************************************************************* + * \brief + * update the parameters of quadratic R-D model + * + ************************************************************************************* +*/ +void updateRCModel (VideoParameters *p_Vid, InputParameters *p_Inp, RCQuadratic *p_quad, RCGeneric *p_gen) +{ + int n_windowSize; + int i; + double std = 0.0, threshold; + int m_Nc = p_quad->NumberofCodedPFrame; + Boolean MADModelFlag = FALSE; + Boolean m_rgRejected[RC_MODEL_HISTORY]; + double error [RC_MODEL_HISTORY]; + + if( p_Vid->type == P_SLICE || (p_Inp->RCUpdateMode == RC_MODE_1 && (p_Vid->number != 0)) ) + { + /*frame layer rate control*/ + if(p_Vid->BasicUnit == p_Vid->FrameSizeInMbs) + { + p_quad->CurrentFrameMAD = ComputeFrameMAD(p_Vid); + m_Nc=p_quad->NumberofCodedPFrame; + } + /*basic unit layer rate control*/ + else + { + /*compute the MAD of the current basic unit*/ + p_quad->CurrentFrameMAD = (double) ((p_gen->TotalMADBasicUnit >> 8)/p_Vid->BasicUnit); + p_gen->TotalMADBasicUnit=0; + + /* compute the average number of header bits*/ + p_quad->CodedBasicUnit=p_quad->TotalNumberofBasicUnit-p_quad->NumberofBasicUnit; + if(p_quad->CodedBasicUnit > 0) + { + p_quad->PAveHeaderBits1=(int)((double)(p_quad->PAveHeaderBits1*(p_quad->CodedBasicUnit-1)+ + p_gen->NumberofBasicUnitHeaderBits)/p_quad->CodedBasicUnit+0.5); + if(p_quad->PAveHeaderBits3 == 0) + p_quad->PAveHeaderBits2 = p_quad->PAveHeaderBits1; + else + { + p_quad->PAveHeaderBits2 = (int)((double)(p_quad->PAveHeaderBits1 * p_quad->CodedBasicUnit+ + p_quad->PAveHeaderBits3 * p_quad->NumberofBasicUnit)/p_quad->TotalNumberofBasicUnit+0.5); + } + } + + if ((p_quad->NumberofBasicUnit >= p_quad->TotalNumberofBasicUnit) || (p_quad->NumberofBasicUnit<0)) + { + fprintf(stdout, "Write into invalid memory in updateRCModel at frame %d, p_quad->NumberofBasicUnit %d\n", + p_Vid->framepoc, p_quad->NumberofBasicUnit); + } + + /*update the record of MADs for reference*/ + if(((p_Inp->PicInterlace == ADAPTIVE_CODING) || (p_Inp->MbInterlace)) && (p_gen->FieldControl == 1)) + p_quad->FCBUCFMAD[p_quad->TotalNumberofBasicUnit-1-p_quad->NumberofBasicUnit]=p_quad->CurrentFrameMAD; + else + p_quad->BUCFMAD[p_quad->TotalNumberofBasicUnit-1-p_quad->NumberofBasicUnit]=p_quad->CurrentFrameMAD; + + if(p_quad->NumberofBasicUnit != 0) + m_Nc = p_quad->NumberofCodedPFrame * p_quad->TotalNumberofBasicUnit + p_quad->CodedBasicUnit; + else + m_Nc = (p_quad->NumberofCodedPFrame-1) * p_quad->TotalNumberofBasicUnit + p_quad->CodedBasicUnit; + } + + if(m_Nc > 1) + MADModelFlag=TRUE; + + p_quad->PPreHeader = p_gen->NumberofHeaderBits; + for (i = (RC_MODEL_HISTORY-2); i > 0; i--) + {// update the history + p_quad->Pm_rgQp[i] = p_quad->Pm_rgQp[i - 1]; + p_quad->m_rgQp[i] = p_quad->Pm_rgQp[i]; + p_quad->Pm_rgRp[i] = p_quad->Pm_rgRp[i - 1]; + p_quad->m_rgRp[i] = p_quad->Pm_rgRp[i]; + } + p_quad->Pm_rgQp[0] = QP2Qstep(p_quad->m_Qc); //*1.0/p_quad->CurrentFrameMAD; + /*frame layer rate control*/ + if(p_Vid->BasicUnit == p_Vid->FrameSizeInMbs) + p_quad->Pm_rgRp[0] = p_gen->NumberofTextureBits*1.0/p_quad->CurrentFrameMAD; + /*basic unit layer rate control*/ + else + p_quad->Pm_rgRp[0] = p_gen->NumberofBasicUnitTextureBits*1.0/p_quad->CurrentFrameMAD; + + p_quad->m_rgQp[0] = p_quad->Pm_rgQp[0]; + p_quad->m_rgRp[0] = p_quad->Pm_rgRp[0]; + p_quad->m_X1 = p_quad->Pm_X1; + p_quad->m_X2 = p_quad->Pm_X2; + + /*compute the size of window*/ + n_windowSize = (p_quad->CurrentFrameMAD>p_quad->PreviousFrameMAD) + ? (int)(p_quad->PreviousFrameMAD/p_quad->CurrentFrameMAD * (RC_MODEL_HISTORY-1) ) + : (int)(p_quad->CurrentFrameMAD/p_quad->PreviousFrameMAD *(RC_MODEL_HISTORY-1)); + n_windowSize=iClip3(1, m_Nc, n_windowSize); + n_windowSize=imin(n_windowSize,p_quad->m_windowSize+1); + n_windowSize=imin(n_windowSize,(RC_MODEL_HISTORY-1)); + + /*update the previous window size*/ + p_quad->m_windowSize=n_windowSize; + + for (i = 0; i < (RC_MODEL_HISTORY-1); i++) + { + m_rgRejected[i] = FALSE; + } + + // initial RD model estimator + RCModelEstimator (p_Vid, p_Inp, p_quad, n_windowSize, m_rgRejected); + + n_windowSize = p_quad->m_windowSize; + // remove outlier + + for (i = 0; i < (int) n_windowSize; i++) + { + error[i] = p_quad->m_X1 / p_quad->m_rgQp[i] + p_quad->m_X2 / (p_quad->m_rgQp[i] * p_quad->m_rgQp[i]) - p_quad->m_rgRp[i]; + std += error[i] * error[i]; + } + threshold = (n_windowSize == 2) ? 0 : sqrt (std / n_windowSize); + for (i = 0; i < (int) n_windowSize; i++) + { + if (fabs(error[i]) > threshold) + m_rgRejected[i] = TRUE; + } + // always include the last data point + m_rgRejected[0] = FALSE; + + // second RD model estimator + RCModelEstimator (p_Vid, p_Inp, p_quad, n_windowSize, m_rgRejected); + + if( MADModelFlag ) + updateMADModel(p_Vid, p_Inp, p_quad, p_gen); + else if( p_Vid->type == P_SLICE || (p_Inp->RCUpdateMode == RC_MODE_1 && (p_Vid->number != 0)) ) + p_quad->PPictureMAD[0] = p_quad->CurrentFrameMAD; + } +} + +/*! + ************************************************************************************* + * \brief + * Model Estimator + * + ************************************************************************************* +*/ +void RCModelEstimator (VideoParameters *p_Vid, InputParameters *p_Inp, RCQuadratic *p_quad, int n_windowSize, Boolean *m_rgRejected) +{ + int n_realSize = n_windowSize; + int i; + double oneSampleQ = 0; + double a00 = 0.0, a01 = 0.0, a10 = 0.0, a11 = 0.0, b0 = 0.0, b1 = 0.0; + double MatrixValue; + Boolean estimateX2 = FALSE; + + for (i = 0; i < n_windowSize; i++) + {// find the number of samples which are not rejected + if (m_rgRejected[i]) + n_realSize--; + } + + // default RD model estimation results + p_quad->m_X1 = p_quad->m_X2 = 0.0; + + for (i = 0; i < n_windowSize; i++) + { + if (!m_rgRejected[i]) + oneSampleQ = p_quad->m_rgQp[i]; + } + for (i = 0; i < n_windowSize; i++) + {// if all non-rejected Q are the same, take 1st order model + if ((p_quad->m_rgQp[i] != oneSampleQ) && !m_rgRejected[i]) + estimateX2 = TRUE; + if (!m_rgRejected[i]) + p_quad->m_X1 += (p_quad->m_rgQp[i] * p_quad->m_rgRp[i]) / n_realSize; + } + + // take 2nd order model to estimate X1 and X2 + if ((n_realSize >= 1) && estimateX2) + { + for (i = 0; i < n_windowSize; i++) + { + if (!m_rgRejected[i]) + { + a00 = a00 + 1.0; + a01 += 1.0 / p_quad->m_rgQp[i]; + a10 = a01; + a11 += 1.0 / (p_quad->m_rgQp[i] * p_quad->m_rgQp[i]); + b0 += p_quad->m_rgQp[i] * p_quad->m_rgRp[i]; + b1 += p_quad->m_rgRp[i]; + } + } + // solve the equation of AX = B + MatrixValue=a00*a11-a01*a10; + if(fabs(MatrixValue) > 0.000001) + { + p_quad->m_X1 = (b0 * a11 - b1 * a01) / MatrixValue; + p_quad->m_X2 = (b1 * a00 - b0 * a10) / MatrixValue; + } + else + { + p_quad->m_X1 = b0 / a00; + p_quad->m_X2 = 0.0; + } + } + if( p_Vid->type == P_SLICE || (p_Inp->RCUpdateMode == RC_MODE_1 && (p_Vid->number != 0)) ) + { + p_quad->Pm_X1 = p_quad->m_X1; + p_quad->Pm_X2 = p_quad->m_X2; + } +} + +/*! + ************************************************************************************* + * \brief + * update the parameters of linear prediction model + * + ************************************************************************************* +*/ +void updateMADModel (VideoParameters *p_Vid, InputParameters *p_Inp, RCQuadratic *p_quad, RCGeneric *p_gen) +{ + int n_windowSize; + int i; + double std = 0.0, threshold; + int m_Nc = p_quad->NumberofCodedPFrame; + Boolean PictureRejected[RC_MODEL_HISTORY]; + double error [RC_MODEL_HISTORY]; + + if(p_quad->NumberofCodedPFrame>0) + { + //assert (p_Vid->type!=P_SLICE); + /*frame layer rate control*/ + if(p_Vid->BasicUnit == p_Vid->FrameSizeInMbs) + m_Nc = p_quad->NumberofCodedPFrame; + else // basic unit layer rate control + m_Nc=p_quad->NumberofCodedPFrame*p_quad->TotalNumberofBasicUnit+p_quad->CodedBasicUnit; + + for (i = (RC_MODEL_HISTORY-2); i > 0; i--) + {// update the history + p_quad->PPictureMAD[i] = p_quad->PPictureMAD[i - 1]; + p_quad->PictureMAD[i] = p_quad->PPictureMAD[i]; + p_quad->ReferenceMAD[i] = p_quad->ReferenceMAD[i-1]; + } + p_quad->PPictureMAD[0] = p_quad->CurrentFrameMAD; + p_quad->PictureMAD[0] = p_quad->PPictureMAD[0]; + + if(p_Vid->BasicUnit == p_Vid->FrameSizeInMbs) + p_quad->ReferenceMAD[0]=p_quad->PictureMAD[1]; + else + { + if(((p_Inp->PicInterlace==ADAPTIVE_CODING)||(p_Inp->MbInterlace)) &&(p_gen->FieldControl==1)) + p_quad->ReferenceMAD[0]=p_quad->FCBUPFMAD[p_quad->TotalNumberofBasicUnit-1-p_quad->NumberofBasicUnit]; + else + p_quad->ReferenceMAD[0]=p_quad->BUPFMAD[p_quad->TotalNumberofBasicUnit-1-p_quad->NumberofBasicUnit]; + } + p_quad->MADPictureC1 = p_quad->PMADPictureC1; + p_quad->MADPictureC2 = p_quad->PMADPictureC2; + + /*compute the size of window*/ + n_windowSize = (p_quad->CurrentFrameMAD > p_quad->PreviousFrameMAD) + ? (int) ((float)(RC_MODEL_HISTORY-1) * p_quad->PreviousFrameMAD / p_quad->CurrentFrameMAD) + : (int) ((float)(RC_MODEL_HISTORY-1) * p_quad->CurrentFrameMAD / p_quad->PreviousFrameMAD); + n_windowSize = iClip3(1, (m_Nc-1), n_windowSize); + n_windowSize=imin(n_windowSize, imin(20, p_quad->MADm_windowSize + 1)); + + /*update the previous window size*/ + p_quad->MADm_windowSize=n_windowSize; + + for (i = 0; i < (RC_MODEL_HISTORY-1); i++) + { + PictureRejected[i] = FALSE; + } + + //update the MAD for the previous frame + if( p_Vid->type == P_SLICE || (p_Inp->RCUpdateMode == RC_MODE_1 && (p_Vid->number != 0)) ) + p_quad->PreviousFrameMAD=p_quad->CurrentFrameMAD; + + // initial MAD model estimator + MADModelEstimator (p_Vid, p_Inp, p_quad, n_windowSize, PictureRejected); + + // remove outlier + for (i = 0; i < n_windowSize; i++) + { + error[i] = p_quad->MADPictureC1 * p_quad->ReferenceMAD[i] + p_quad->MADPictureC2 - p_quad->PictureMAD[i]; + std += (error[i] * error[i]); + } + + threshold = (n_windowSize == 2) ? 0 : sqrt (std / n_windowSize); + for (i = 0; i < n_windowSize; i++) + { + if (fabs(error[i]) > threshold) + PictureRejected[i] = TRUE; + } + // always include the last data point + PictureRejected[0] = FALSE; + + // second MAD model estimator + MADModelEstimator (p_Vid, p_Inp, p_quad, n_windowSize, PictureRejected); + } +} + + +/*! + ************************************************************************************* + * \brief + * MAD mode estimator + * + ************************************************************************************* +*/ +void MADModelEstimator (VideoParameters *p_Vid, InputParameters *p_Inp, RCQuadratic *p_quad, int n_windowSize, Boolean *PictureRejected) +{ + int n_realSize = n_windowSize; + int i; + double oneSampleQ = 0.0; + double a00 = 0.0, a01 = 0.0, a10 = 0.0, a11 = 0.0, b0 = 0.0, b1 = 0.0; + double MatrixValue; + Boolean estimateX2 = FALSE; + + for (i = 0; i < n_windowSize; i++) + {// find the number of samples which are not rejected + if (PictureRejected[i]) + n_realSize--; + } + + // default MAD model estimation results + p_quad->MADPictureC1 = p_quad->MADPictureC2 = 0.0; + + for (i = 0; i < n_windowSize; i++) + { + if (!PictureRejected[i]) + oneSampleQ = p_quad->PictureMAD[i]; + } + + for (i = 0; i < n_windowSize; i++) + {// if all non-rejected MAD are the same, take 1st order model + if ((p_quad->PictureMAD[i] != oneSampleQ) && !PictureRejected[i]) + estimateX2 = TRUE; + if (!PictureRejected[i]) + p_quad->MADPictureC1 += p_quad->PictureMAD[i] / (p_quad->ReferenceMAD[i]*n_realSize); + } + + // take 2nd order model to estimate X1 and X2 + if ((n_realSize >= 1) && estimateX2) + { + for (i = 0; i < n_windowSize; i++) + { + if (!PictureRejected[i]) + { + a00 = a00 + 1.0; + a01 += p_quad->ReferenceMAD[i]; + a10 = a01; + a11 += p_quad->ReferenceMAD[i] * p_quad->ReferenceMAD[i]; + b0 += p_quad->PictureMAD[i]; + b1 += p_quad->PictureMAD[i] * p_quad->ReferenceMAD[i]; + } + } + // solve the equation of AX = B + MatrixValue = a00 * a11 - a01 * a10; + if(fabs(MatrixValue) > 0.000001) + { + p_quad->MADPictureC2 = (b0 * a11 - b1 * a01) / MatrixValue; + p_quad->MADPictureC1 = (b1 * a00 - b0 * a10) / MatrixValue; + } + else + { + p_quad->MADPictureC1 = b0/a01; + p_quad->MADPictureC2 = 0.0; + } + } + if( p_Vid->type == P_SLICE || (p_Inp->RCUpdateMode == RC_MODE_1 && (p_Vid->number != 0)) ) + { + p_quad->PMADPictureC1 = p_quad->MADPictureC1; + p_quad->PMADPictureC2 = p_quad->MADPictureC2; + } +} + +/*! + ************************************************************************************* + * \brief + * compute a quantization parameter for each frame (RC_MODE_0) + * + ************************************************************************************* +*/ +int updateQPRC0(VideoParameters *p_Vid, InputParameters *p_Inp, RCQuadratic *p_quad, RCGeneric *p_gen, int topfield) +{ + int m_Bits; + int BFrameNumber; + int StepSize; + int SumofBasicUnit; + int MaxQpChange, m_Qp, m_Hp; + + /* frame layer rate control */ + if( p_Vid->BasicUnit == p_Vid->FrameSizeInMbs ) + { + /* fixed quantization parameter is used to coded I frame, the first P frame and the first B frame + the quantization parameter is adjusted according the available channel bandwidth and + the type of video */ + /*top field*/ + if((topfield) || (p_gen->FieldControl==0)) + { + if (p_Vid->type==I_SLICE) + { + p_quad->m_Qc = p_quad->MyInitialQp; + return p_quad->m_Qc; + } + else if(p_Vid->type == B_SLICE) + { + if(p_Inp->NumberBFrames==1) + { + if((p_Inp->PicInterlace==ADAPTIVE_CODING)||(p_Inp->MbInterlace)) + updateQPInterlace( p_quad, p_gen ); + + p_quad->m_Qc = imin(p_quad->PrevLastQP, p_quad->CurrLastQP) + 2; + p_quad->m_Qc = imax(p_quad->m_Qc, imax(p_quad->PrevLastQP, p_quad->CurrLastQP)); + p_quad->m_Qc = imax(p_quad->m_Qc, p_quad->CurrLastQP + 1); + p_quad->m_Qc = iClip3(p_Vid->RCMinQP + p_quad->bitdepth_qp_scale, p_Vid->RCMaxQP + p_quad->bitdepth_qp_scale, p_quad->m_Qc); // Clipping + } + else + { + BFrameNumber = (p_quad->NumberofBFrames + 1) % p_Inp->NumberBFrames; + if(BFrameNumber==0) + BFrameNumber = p_Inp->NumberBFrames; + + /*adaptive field/frame coding*/ + if(BFrameNumber==1) + { + if((p_Inp->PicInterlace==ADAPTIVE_CODING)||(p_Inp->MbInterlace)) + updateQPInterlace( p_quad, p_gen ); + } + + if((p_quad->CurrLastQP-p_quad->PrevLastQP)<=(-2*p_Inp->NumberBFrames-3)) + StepSize=-3; + else if((p_quad->CurrLastQP-p_quad->PrevLastQP)==(-2*p_Inp->NumberBFrames-2)) + StepSize=-2; + else if((p_quad->CurrLastQP-p_quad->PrevLastQP)==(-2*p_Inp->NumberBFrames-1)) + StepSize=-1; + else if((p_quad->CurrLastQP-p_quad->PrevLastQP)==(-2*p_Inp->NumberBFrames)) + StepSize=0; + else if((p_quad->CurrLastQP-p_quad->PrevLastQP)==(-2*p_Inp->NumberBFrames+1)) + StepSize=1; + else + StepSize=2; + + p_quad->m_Qc = p_quad->PrevLastQP + StepSize; + p_quad->m_Qc += iClip3( -2 * (BFrameNumber - 1), 2*(BFrameNumber-1), + (BFrameNumber-1)*(p_quad->CurrLastQP-p_quad->PrevLastQP)/(p_Inp->NumberBFrames-1)); + p_quad->m_Qc = iClip3(p_Vid->RCMinQP + p_quad->bitdepth_qp_scale, p_Vid->RCMaxQP + p_quad->bitdepth_qp_scale, p_quad->m_Qc); // Clipping + } + return p_quad->m_Qc; + } + else if( p_Vid->type == P_SLICE && p_quad->NumberofPPicture == 0 ) + { + p_quad->m_Qc=p_quad->MyInitialQp; + + if(p_gen->FieldControl==0) + updateQPNonPicAFF( p_Vid->active_sps, p_quad ); + return p_quad->m_Qc; + } + else + { + /*adaptive field/frame coding*/ + if( ( p_Inp->PicInterlace == ADAPTIVE_CODING || p_Inp->MbInterlace ) && p_gen->FieldControl == 0 ) + updateQPInterlaceBU( p_quad, p_gen ); + + p_quad->m_X1 = p_quad->Pm_X1; + p_quad->m_X2 = p_quad->Pm_X2; + p_quad->MADPictureC1 = p_quad->PMADPictureC1; + p_quad->MADPictureC2 = p_quad->PMADPictureC2; + p_quad->PreviousPictureMAD = p_quad->PPictureMAD[0]; + + MaxQpChange = p_quad->PMaxQpChange; + m_Qp = p_quad->Pm_Qp; + m_Hp = p_quad->PPreHeader; + + /* predict the MAD of current picture*/ + p_quad->CurrentFrameMAD = p_quad->MADPictureC1*p_quad->PreviousPictureMAD + p_quad->MADPictureC2; + + /*compute the number of bits for the texture*/ + if(p_quad->Target < 0) + { + p_quad->m_Qc=m_Qp+MaxQpChange; + p_quad->m_Qc = iClip3(p_Vid->RCMinQP + p_quad->bitdepth_qp_scale, p_Vid->RCMaxQP + p_quad->bitdepth_qp_scale, p_quad->m_Qc); // Clipping + } + else + { + m_Bits = p_quad->Target-m_Hp; + m_Bits = imax(m_Bits, (int)(p_quad->bit_rate/(MINVALUE*p_quad->frame_rate))); + + updateModelQPFrame( p_quad, m_Bits ); + + p_quad->m_Qc = iClip3(p_Vid->RCMinQP + p_quad->bitdepth_qp_scale, p_Vid->RCMaxQP + p_quad->bitdepth_qp_scale, p_quad->m_Qc); // clipping + p_quad->m_Qc = iClip3(m_Qp-MaxQpChange, m_Qp+MaxQpChange, p_quad->m_Qc); // control variation + } + + if( p_gen->FieldControl == 0 ) + updateQPNonPicAFF( p_Vid->active_sps, p_quad ); + + return p_quad->m_Qc; + } + } + /*bottom field*/ + else + { + if( p_Vid->type == P_SLICE && p_gen->NoGranularFieldRC == 0 ) + updateBottomField( p_Inp, p_quad ); + return p_quad->m_Qc; + } + } + /*basic unit layer rate control*/ + else + { + /*top field of I frame*/ + if (p_Vid->type == I_SLICE) + { + p_quad->m_Qc = p_quad->MyInitialQp; + return p_quad->m_Qc; + } + else if( p_Vid->type == B_SLICE ) + { + /*top field of B frame*/ + if((topfield)||(p_gen->FieldControl==0)) + { + if(p_Inp->NumberBFrames==1) + { + /*adaptive field/frame coding*/ + if((p_Inp->PicInterlace==ADAPTIVE_CODING)||(p_Inp->MbInterlace)) + updateQPInterlace( p_quad, p_gen ); + + if(p_quad->PrevLastQP==p_quad->CurrLastQP) + p_quad->m_Qc=p_quad->PrevLastQP+2; + else + p_quad->m_Qc = ((p_quad->PrevLastQP+p_quad->CurrLastQP) >> 1) + 1; + p_quad->m_Qc = iClip3(p_Vid->RCMinQP + p_quad->bitdepth_qp_scale, p_Vid->RCMaxQP + p_quad->bitdepth_qp_scale, p_quad->m_Qc); // Clipping + } + else + { + BFrameNumber=(p_quad->NumberofBFrames+1)%p_Inp->NumberBFrames; + if(BFrameNumber==0) + BFrameNumber=p_Inp->NumberBFrames; + + /*adaptive field/frame coding*/ + if(BFrameNumber==1) + { + if((p_Inp->PicInterlace==ADAPTIVE_CODING)||(p_Inp->MbInterlace)) + updateQPInterlace( p_quad, p_gen ); + } + + if((p_quad->CurrLastQP-p_quad->PrevLastQP)<=(-2*p_Inp->NumberBFrames-3)) + StepSize=-3; + else if((p_quad->CurrLastQP-p_quad->PrevLastQP)==(-2*p_Inp->NumberBFrames-2)) + StepSize=-2; + else if((p_quad->CurrLastQP-p_quad->PrevLastQP)==(-2*p_Inp->NumberBFrames-1)) + StepSize=-1; + else if((p_quad->CurrLastQP-p_quad->PrevLastQP)==(-2*p_Inp->NumberBFrames)) + StepSize=0;//0 + else if((p_quad->CurrLastQP-p_quad->PrevLastQP)==(-2*p_Inp->NumberBFrames+1)) + StepSize=1;//1 + else + StepSize=2;//2 + p_quad->m_Qc=p_quad->PrevLastQP+StepSize; + p_quad->m_Qc += + iClip3( -2*(BFrameNumber-1), 2*(BFrameNumber-1), (BFrameNumber-1)*(p_quad->CurrLastQP-p_quad->PrevLastQP)/(p_Inp->NumberBFrames-1) ); + p_quad->m_Qc = iClip3(p_Vid->RCMinQP + p_quad->bitdepth_qp_scale, p_Vid->RCMaxQP + p_quad->bitdepth_qp_scale, p_quad->m_Qc); // Clipping + } + return p_quad->m_Qc; + } + /*bottom field of B frame*/ + else + { + return p_quad->m_Qc; + } + } + else if( p_Vid->type == P_SLICE ) + { + if( (p_gen->NumberofGOP == 1) && (p_quad->NumberofPPicture == 0) ) + { + if((p_gen->FieldControl==0)||((p_gen->FieldControl==1) && (p_gen->NoGranularFieldRC==0))) + return updateFirstP( p_Vid, p_Inp, p_quad, p_gen, topfield ); + } + else + { + p_quad->m_X1=p_quad->Pm_X1; + p_quad->m_X2=p_quad->Pm_X2; + p_quad->MADPictureC1=p_quad->PMADPictureC1; + p_quad->MADPictureC2=p_quad->PMADPictureC2; + + m_Qp=p_quad->Pm_Qp; + + if(p_gen->FieldControl==0) + SumofBasicUnit=p_quad->TotalNumberofBasicUnit; + else + SumofBasicUnit=p_quad->TotalNumberofBasicUnit>>1; + + /*the average QP of the previous frame is used to coded the first basic unit of the current frame or field*/ + if(p_quad->NumberofBasicUnit==SumofBasicUnit) + return updateFirstBU( p_Vid, p_Inp, p_quad, p_gen, topfield ); + else + { + /*compute the number of remaining bits*/ + p_quad->Target -= (p_gen->NumberofBasicUnitHeaderBits + p_gen->NumberofBasicUnitTextureBits); + p_gen->NumberofBasicUnitHeaderBits = 0; + p_gen->NumberofBasicUnitTextureBits = 0; + if(p_quad->Target<0) + return updateNegativeTarget( p_Vid, p_Inp, p_quad, p_gen, topfield, m_Qp ); + else + { + /*predict the MAD of current picture*/ + predictCurrPicMAD( p_Inp, p_quad, p_gen ); + + /*compute the total number of bits for the current basic unit*/ + updateModelQPBU( p_Vid, p_Inp, p_quad, m_Qp ); + + p_quad->TotalFrameQP +=p_quad->m_Qc; + p_quad->Pm_Qp=p_quad->m_Qc; + p_quad->NumberofBasicUnit--; + if( p_quad->NumberofBasicUnit == 0 && p_Vid->type == P_SLICE ) + updateLastBU( p_Vid, p_Inp, p_quad, p_gen, topfield ); + + return p_quad->m_Qc; + } + } + } + } + } + return p_quad->m_Qc; +} + +/*! + ************************************************************************************* + * \brief + * compute a quantization parameter for each frame + * + ************************************************************************************* +*/ +int updateQPRC1(VideoParameters *p_Vid, InputParameters *p_Inp, RCQuadratic *p_quad, RCGeneric *p_gen, int topfield) +{ + int m_Bits; + int SumofBasicUnit; + int MaxQpChange, m_Qp, m_Hp; + + /* frame layer rate control */ + if( p_Vid->BasicUnit == p_Vid->FrameSizeInMbs ) + { + /* fixed quantization parameter is used to coded I frame, the first P frame and the first B frame + the quantization parameter is adjusted according the available channel bandwidth and + the type of vide */ + /*top field*/ + if((topfield) || (p_gen->FieldControl==0)) + { + if (p_Vid->number == 0) + { + p_quad->m_Qc = p_quad->MyInitialQp; + return p_quad->m_Qc; + } + else if( p_quad->NumberofPPicture == 0 && (p_Vid->number != 0)) + { + p_quad->m_Qc=p_quad->MyInitialQp; + + if(p_gen->FieldControl==0) + updateQPNonPicAFF( p_Vid->active_sps, p_quad ); + return p_quad->m_Qc; + } + else + { + /*adaptive field/frame coding*/ + if( ( p_Inp->PicInterlace == ADAPTIVE_CODING || p_Inp->MbInterlace ) && p_gen->FieldControl == 0 ) + updateQPInterlaceBU( p_quad, p_gen ); + + p_quad->m_X1 = p_quad->Pm_X1; + p_quad->m_X2 = p_quad->Pm_X2; + p_quad->MADPictureC1 = p_quad->PMADPictureC1; + p_quad->MADPictureC2 = p_quad->PMADPictureC2; + p_quad->PreviousPictureMAD = p_quad->PPictureMAD[0]; + + MaxQpChange = p_quad->PMaxQpChange; + m_Qp = p_quad->Pm_Qp; + m_Hp = p_quad->PPreHeader; + + /* predict the MAD of current picture*/ + p_quad->CurrentFrameMAD=p_quad->MADPictureC1*p_quad->PreviousPictureMAD + p_quad->MADPictureC2; + + /*compute the number of bits for the texture*/ + if(p_quad->Target < 0) + { + p_quad->m_Qc=m_Qp+MaxQpChange; + p_quad->m_Qc = iClip3(p_Vid->RCMinQP + p_quad->bitdepth_qp_scale, p_Vid->RCMaxQP + p_quad->bitdepth_qp_scale, p_quad->m_Qc); // Clipping + } + else + { + m_Bits = p_quad->Target-m_Hp; + m_Bits = imax(m_Bits, (int)(p_quad->bit_rate/(MINVALUE*p_quad->frame_rate))); + + updateModelQPFrame( p_quad, m_Bits ); + + p_quad->m_Qc = iClip3(p_Vid->RCMinQP + p_quad->bitdepth_qp_scale, p_Vid->RCMaxQP + p_quad->bitdepth_qp_scale, p_quad->m_Qc); // clipping + p_quad->m_Qc = iClip3(m_Qp-MaxQpChange, m_Qp+MaxQpChange, p_quad->m_Qc); // control variation + } + + if( p_gen->FieldControl == 0 ) + updateQPNonPicAFF( p_Vid->active_sps, p_quad ); + + return p_quad->m_Qc; + } + } + /*bottom field*/ + else + { + if( p_gen->NoGranularFieldRC == 0 ) + updateBottomField( p_Inp, p_quad ); + return p_quad->m_Qc; + } + } + /*basic unit layer rate control*/ + else + { + /*top field of I frame*/ + if (p_Vid->number == 0) + { + p_quad->m_Qc = p_quad->MyInitialQp; + return p_quad->m_Qc; + } + else + { + if((p_gen->NumberofGOP==1)&&(p_quad->NumberofPPicture==0)) + { + if((p_gen->FieldControl==0)||((p_gen->FieldControl==1) && (p_gen->NoGranularFieldRC==0))) + return updateFirstP( p_Vid, p_Inp, p_quad, p_gen, topfield ); + } + else + { + p_quad->m_X1=p_quad->Pm_X1; + p_quad->m_X2=p_quad->Pm_X2; + p_quad->MADPictureC1=p_quad->PMADPictureC1; + p_quad->MADPictureC2=p_quad->PMADPictureC2; + + m_Qp=p_quad->Pm_Qp; + + if(p_gen->FieldControl==0) + SumofBasicUnit=p_quad->TotalNumberofBasicUnit; + else + SumofBasicUnit=p_quad->TotalNumberofBasicUnit>>1; + + /*the average QP of the previous frame is used to coded the first basic unit of the current frame or field*/ + if(p_quad->NumberofBasicUnit==SumofBasicUnit) + return updateFirstBU( p_Vid, p_Inp, p_quad, p_gen, topfield ); + else + { + /*compute the number of remaining bits*/ + p_quad->Target -= (p_gen->NumberofBasicUnitHeaderBits + p_gen->NumberofBasicUnitTextureBits); + p_gen->NumberofBasicUnitHeaderBits = 0; + p_gen->NumberofBasicUnitTextureBits = 0; + if(p_quad->Target<0) + return updateNegativeTarget( p_Vid, p_Inp, p_quad, p_gen, topfield, m_Qp ); + else + { + /*predict the MAD of current picture*/ + predictCurrPicMAD( p_Inp, p_quad, p_gen ); + + /*compute the total number of bits for the current basic unit*/ + updateModelQPBU( p_Vid, p_Inp, p_quad, m_Qp ); + + p_quad->TotalFrameQP +=p_quad->m_Qc; + p_quad->Pm_Qp=p_quad->m_Qc; + p_quad->NumberofBasicUnit--; + if((p_quad->NumberofBasicUnit==0) && (p_Vid->number != 0)) + updateLastBU( p_Vid, p_Inp, p_quad, p_gen, topfield ); + + return p_quad->m_Qc; + } + } + } + } + } + return p_quad->m_Qc; +} + +/*! + ************************************************************************************* + * \brief + * compute a quantization parameter for each frame + * + ************************************************************************************* +*/ +int updateQPRC2(VideoParameters *p_Vid, InputParameters *p_Inp, RCQuadratic *p_quad, RCGeneric *p_gen, int topfield) +{ + int m_Bits; + int SumofBasicUnit; + int MaxQpChange, m_Qp, m_Hp; + + /* frame layer rate control */ + if( p_Vid->BasicUnit == p_Vid->FrameSizeInMbs ) + { + /* fixed quantization parameter is used to coded I frame, the first P frame and the first B frame + the quantization parameter is adjusted according the available channel bandwidth and + the type of vide */ + /*top field*/ + if((topfield) || (p_gen->FieldControl==0)) + { + if (p_Vid->number == 0) + { + p_quad->m_Qc = p_quad->MyInitialQp; + return p_quad->m_Qc; + } + else if (p_Vid->type==I_SLICE) + { + if((p_Inp->PicInterlace==ADAPTIVE_CODING)||(p_Inp->MbInterlace)) + updateQPInterlace( p_quad, p_gen ); + + if ( p_Vid->p_curr_frm_struct->idr_flag ) + p_quad->m_Qc = p_quad->MyInitialQp; + else + p_quad->m_Qc = p_quad->CurrLastQP; // Set QP to average qp of last P frame + + return p_quad->m_Qc; + } + else if(p_Vid->type == B_SLICE) + { + int prevQP = imax(p_quad->PrevLastQP, p_quad->CurrLastQP); + // for more than one consecutive B frames the below call will overwrite the old anchor frame QP with the current value + // it should be called once in the B-frame sequence....this should be modified for the BU < frame as well + if((p_Inp->PicInterlace==ADAPTIVE_CODING)||(p_Inp->MbInterlace)) + updateQPInterlace( p_quad, p_gen ); + + if (p_Inp->HierarchicalCoding) + { + p_quad->m_Qc = prevQP + p_Vid->p_curr_frm_struct->layer; + } + else + p_quad->m_Qc = prevQP + 2 - p_Vid->nal_reference_idc; + p_quad->m_Qc = iClip3(p_Vid->RCMinQP + p_quad->bitdepth_qp_scale, p_Vid->RCMaxQP + p_quad->bitdepth_qp_scale, p_quad->m_Qc); // Clipping + + return p_quad->m_Qc; + } + else if( p_Vid->type == P_SLICE && p_quad->NumberofPPicture == 0 ) + { + p_quad->m_Qc=p_quad->MyInitialQp; + + if(p_gen->FieldControl==0) + updateQPNonPicAFF( p_Vid->active_sps, p_quad ); + return p_quad->m_Qc; + } + else + { + /*adaptive field/frame coding*/ + if( ( p_Inp->PicInterlace == ADAPTIVE_CODING || p_Inp->MbInterlace ) && p_gen->FieldControl == 0 ) + updateQPInterlaceBU( p_quad, p_gen ); + + p_quad->m_X1 = p_quad->Pm_X1; + p_quad->m_X2 = p_quad->Pm_X2; + p_quad->MADPictureC1 = p_quad->PMADPictureC1; + p_quad->MADPictureC2 = p_quad->PMADPictureC2; + p_quad->PreviousPictureMAD = p_quad->PPictureMAD[0]; + + MaxQpChange = p_quad->PMaxQpChange; + m_Qp = p_quad->Pm_Qp; + m_Hp = p_quad->PPreHeader; + + /* predict the MAD of current picture*/ + p_quad->CurrentFrameMAD=p_quad->MADPictureC1*p_quad->PreviousPictureMAD + p_quad->MADPictureC2; + + /*compute the number of bits for the texture*/ + if(p_quad->Target < 0) + { + p_quad->m_Qc=m_Qp+MaxQpChange; + p_quad->m_Qc = iClip3(p_Vid->RCMinQP + p_quad->bitdepth_qp_scale, p_Vid->RCMaxQP + p_quad->bitdepth_qp_scale, p_quad->m_Qc); // Clipping + } + else + { + m_Bits = p_quad->Target-m_Hp; + m_Bits = imax(m_Bits, (int)(p_quad->bit_rate/(MINVALUE*p_quad->frame_rate))); + + updateModelQPFrame( p_quad, m_Bits ); + + p_quad->m_Qc = iClip3(p_Vid->RCMinQP + p_quad->bitdepth_qp_scale, p_Vid->RCMaxQP + p_quad->bitdepth_qp_scale, p_quad->m_Qc); // clipping + p_quad->m_Qc = iClip3(m_Qp-MaxQpChange, m_Qp+MaxQpChange, p_quad->m_Qc); // control variation + } + + if( p_gen->FieldControl == 0 ) + updateQPNonPicAFF( p_Vid->active_sps, p_quad ); + + return p_quad->m_Qc; + } + } + /*bottom field*/ + else + { + if( p_Vid->type==P_SLICE && p_gen->NoGranularFieldRC == 0 ) + updateBottomField( p_Inp, p_quad ); + return p_quad->m_Qc; + } + } + /*basic unit layer rate control*/ + else + { + /*top field of I frame*/ + if (p_Vid->number == 0) + { + p_quad->m_Qc = p_quad->MyInitialQp; + return p_quad->m_Qc; + } + else if (p_Vid->type==I_SLICE) + { + /*adaptive field/frame coding*/ + if((p_Inp->PicInterlace==ADAPTIVE_CODING)||(p_Inp->MbInterlace)) + updateQPInterlace( p_quad, p_gen ); + + p_quad->m_Qc = p_quad->PrevLastQP; // Set QP to average qp of last P frame + p_quad->PrevLastQP = p_quad->CurrLastQP; + p_quad->CurrLastQP = p_quad->PrevLastQP; + p_quad->PAveFrameQP = p_quad->CurrLastQP; + + return p_quad->m_Qc; + } + else if(p_Vid->type == B_SLICE) + { + int prevQP = imax(p_quad->PrevLastQP, p_quad->CurrLastQP); + if((p_Inp->PicInterlace==ADAPTIVE_CODING)||(p_Inp->MbInterlace)) + updateQPInterlace( p_quad, p_gen ); + + if (p_Inp->HierarchicalCoding) + { + p_quad->m_Qc = prevQP + p_Vid->p_curr_frm_struct->layer; + } + else + p_quad->m_Qc = prevQP + 2 - p_Vid->nal_reference_idc; + + p_quad->m_Qc = iClip3(p_Vid->RCMinQP + p_quad->bitdepth_qp_scale, p_Vid->RCMaxQP + p_quad->bitdepth_qp_scale, p_quad->m_Qc); // Clipping + + return p_quad->m_Qc; + + } + else if( p_Vid->type == P_SLICE ) + { + if((p_gen->NumberofGOP==1)&&(p_quad->NumberofPPicture==0)) + { + if((p_gen->FieldControl==0)||((p_gen->FieldControl==1) && (p_gen->NoGranularFieldRC==0))) + return updateFirstP( p_Vid, p_Inp, p_quad, p_gen, topfield ); + } + else + { + p_quad->m_X1=p_quad->Pm_X1; + p_quad->m_X2=p_quad->Pm_X2; + p_quad->MADPictureC1=p_quad->PMADPictureC1; + p_quad->MADPictureC2=p_quad->PMADPictureC2; + + m_Qp=p_quad->Pm_Qp; + + if(p_gen->FieldControl==0) + SumofBasicUnit=p_quad->TotalNumberofBasicUnit; + else + SumofBasicUnit=p_quad->TotalNumberofBasicUnit>>1; + + /*the average QP of the previous frame is used to coded the first basic unit of the current frame or field*/ + if(p_quad->NumberofBasicUnit==SumofBasicUnit) + return updateFirstBU( p_Vid, p_Inp, p_quad, p_gen, topfield ); + else + { + /*compute the number of remaining bits*/ + p_quad->Target -= (p_gen->NumberofBasicUnitHeaderBits + p_gen->NumberofBasicUnitTextureBits); + p_gen->NumberofBasicUnitHeaderBits = 0; + p_gen->NumberofBasicUnitTextureBits = 0; + if(p_quad->Target<0) + return updateNegativeTarget( p_Vid, p_Inp, p_quad, p_gen, topfield, m_Qp ); + else + { + /*predict the MAD of current picture*/ + predictCurrPicMAD( p_Inp, p_quad, p_gen ); + + /*compute the total number of bits for the current basic unit*/ + updateModelQPBU( p_Vid, p_Inp, p_quad, m_Qp ); + + p_quad->TotalFrameQP +=p_quad->m_Qc; + p_quad->Pm_Qp=p_quad->m_Qc; + p_quad->NumberofBasicUnit--; + if((p_quad->NumberofBasicUnit==0) && p_Vid->type == P_SLICE ) + updateLastBU( p_Vid, p_Inp, p_quad, p_gen, topfield ); + + return p_quad->m_Qc; + } + } + } + } + } + return p_quad->m_Qc; +} + +/*! + ************************************************************************************* + * \brief + * compute a quantization parameter for each frame + * + ************************************************************************************* +*/ +int updateQPRC3(VideoParameters *p_Vid, InputParameters *p_Inp, RCQuadratic *p_quad, RCGeneric *p_gen, int topfield) +{ + int m_Bits; + int SumofBasicUnit; + int MaxQpChange, m_Qp, m_Hp; + + /* frame layer rate control */ + if( p_Vid->BasicUnit == p_Vid->FrameSizeInMbs || p_Vid->type != P_SLICE ) + { + /* fixed quantization parameter is used to coded I frame, the first P frame and the first B frame + the quantization parameter is adjusted according the available channel bandwidth and + the type of video */ + /*top field*/ + if((topfield) || (p_gen->FieldControl==0)) + { + if (p_Vid->number == 0) + { + if((p_Inp->PicInterlace == ADAPTIVE_CODING) || (p_Inp->MbInterlace)) + updateQPInterlace( p_quad, p_gen ); + p_quad->m_Qc = p_quad->MyInitialQp; + return p_quad->m_Qc; + } + else if( p_Vid->type == P_SLICE && p_quad->NumberofPPicture == 0 ) + { + p_quad->m_Qc=p_quad->MyInitialQp; + + if(p_gen->FieldControl==0) + updateQPNonPicAFF( p_Vid->active_sps, p_quad ); + return p_quad->m_Qc; + } + else + { + if( ( (p_Vid->type == B_SLICE && (p_Vid->p_curr_frm_struct->layer && p_Vid->p_curr_frm_struct->atom_idx == 1)) || p_Vid->type == I_SLICE) && ((p_Inp->PicInterlace == ADAPTIVE_CODING) || (p_Inp->MbInterlace)) ) + updateQPInterlace( p_quad, p_gen ); + /*adaptive field/frame coding*/ + if( p_Vid->type == P_SLICE && ( p_Inp->PicInterlace == ADAPTIVE_CODING || p_Inp->MbInterlace ) && p_gen->FieldControl == 0 ) + updateQPInterlaceBU( p_quad, p_gen ); + + p_quad->m_X1 = p_quad->Pm_X1; + p_quad->m_X2 = p_quad->Pm_X2; + p_quad->MADPictureC1 = p_quad->PMADPictureC1; + p_quad->MADPictureC2 = p_quad->PMADPictureC2; + p_quad->PreviousPictureMAD = p_quad->PPictureMAD[0]; + + MaxQpChange = p_quad->PMaxQpChange; + m_Qp = p_quad->Pm_Qp; + m_Hp = p_quad->PPreHeader; + + if ( p_Vid->BasicUnit < p_Vid->FrameSizeInMbs && p_Vid->type != P_SLICE ) + { + // when RC_MODE_3 is set and basic unit is smaller than a frame, note that: + // the linear MAD model and the quadratic QP model operate on small units and not on a whole frame; + // we therefore have to account for this + p_quad->PreviousPictureMAD = p_quad->PreviousWholeFrameMAD; + } + if ( p_Vid->type == I_SLICE ) + m_Hp = 0; // it is usually a very small portion of the total I_SLICE bit budget + + /* predict the MAD of current picture*/ + p_quad->CurrentFrameMAD=p_quad->MADPictureC1*p_quad->PreviousPictureMAD + p_quad->MADPictureC2; + + /*compute the number of bits for the texture*/ + if(p_quad->Target < 0) + { + p_quad->m_Qc=m_Qp+MaxQpChange; + p_quad->m_Qc = iClip3(p_Vid->RCMinQP + p_quad->bitdepth_qp_scale, p_Vid->RCMaxQP + p_quad->bitdepth_qp_scale, p_quad->m_Qc); // Clipping + } + else + { + if ( p_Vid->type != P_SLICE ) + { + if ( p_Vid->BasicUnit < p_Vid->FrameSizeInMbs ) + m_Bits =(p_quad->Target-m_Hp)/p_quad->TotalNumberofBasicUnit; + else + m_Bits =p_quad->Target-m_Hp; + } + else { + m_Bits = p_quad->Target-m_Hp; + m_Bits = imax(m_Bits, (int)(p_quad->bit_rate/(MINVALUE*p_quad->frame_rate))); + } + updateModelQPFrame( p_quad, m_Bits ); + + p_quad->m_Qc = iClip3(p_Vid->RCMinQP + p_quad->bitdepth_qp_scale, p_Vid->RCMaxQP + p_quad->bitdepth_qp_scale, p_quad->m_Qc); // clipping + if ( p_Vid->type == P_SLICE ) + p_quad->m_Qc = iClip3(m_Qp-MaxQpChange, m_Qp+MaxQpChange, p_quad->m_Qc); // control variation + } + + if( p_Vid->type == P_SLICE && p_gen->FieldControl == 0 ) + updateQPNonPicAFF( p_Vid->active_sps, p_quad ); + + if ( p_Vid->type == B_SLICE ) + { + // hierarchical adjustment + int prevqp = ((p_quad->PrevLastQP+p_quad->CurrLastQP) >> 1) + 1; + if ( p_Inp->HierarchicalCoding && p_Vid->p_curr_frm_struct->layer) + p_quad->m_Qc -= (p_Vid->p_curr_frm_struct->p_atom->gop_levels - p_Vid->p_curr_frm_struct->layer); + // check bounds + p_quad->m_Qc = iClip3(prevqp - (p_Inp->HierarchicalCoding ? 0 : 5), prevqp + 5, p_quad->m_Qc); // control variation + p_quad->m_Qc = iClip3(p_Vid->RCMinQP + p_quad->bitdepth_qp_scale, p_Vid->RCMaxQP + p_quad->bitdepth_qp_scale, p_quad->m_Qc); // clipping + } + return p_quad->m_Qc; + } + } + /*bottom field*/ + else + { + if( p_Vid->type==P_SLICE && p_gen->NoGranularFieldRC == 0 ) + updateBottomField( p_Inp, p_quad ); + return p_quad->m_Qc; + } + } + /*basic unit layer rate control*/ + else + { + /*top field of I frame*/ + if (p_Vid->number == 0) + { + if((p_Inp->PicInterlace == ADAPTIVE_CODING) || (p_Inp->MbInterlace)) + updateQPInterlace( p_quad, p_gen ); + p_quad->m_Qc = p_quad->MyInitialQp; + return p_quad->m_Qc; + } + else if( p_Vid->type == P_SLICE ) + { + if((p_gen->NumberofGOP==1)&&(p_quad->NumberofPPicture==0)) + { + if((p_gen->FieldControl==0)||((p_gen->FieldControl==1) && (p_gen->NoGranularFieldRC==0))) + return updateFirstP( p_Vid, p_Inp, p_quad, p_gen, topfield ); + } + else + { + if( ( (p_Vid->type == B_SLICE && (p_Vid->p_curr_frm_struct->layer && p_Vid->p_curr_frm_struct->atom_idx == 1)) || p_Vid->type == I_SLICE) && ((p_Inp->PicInterlace == ADAPTIVE_CODING) || (p_Inp->MbInterlace)) ) + updateQPInterlace( p_quad, p_gen ); + p_quad->m_X1=p_quad->Pm_X1; + p_quad->m_X2=p_quad->Pm_X2; + p_quad->MADPictureC1=p_quad->PMADPictureC1; + p_quad->MADPictureC2=p_quad->PMADPictureC2; + + m_Qp=p_quad->Pm_Qp; + + if(p_gen->FieldControl==0) + SumofBasicUnit=p_quad->TotalNumberofBasicUnit; + else + SumofBasicUnit=p_quad->TotalNumberofBasicUnit>>1; + + /*the average QP of the previous frame is used to coded the first basic unit of the current frame or field*/ + if(p_quad->NumberofBasicUnit==SumofBasicUnit) + return updateFirstBU( p_Vid, p_Inp, p_quad, p_gen, topfield ); + else + { + /*compute the number of remaining bits*/ + p_quad->Target -= (p_gen->NumberofBasicUnitHeaderBits + p_gen->NumberofBasicUnitTextureBits); + p_gen->NumberofBasicUnitHeaderBits = 0; + p_gen->NumberofBasicUnitTextureBits = 0; + if(p_quad->Target<0) + return updateNegativeTarget( p_Vid, p_Inp, p_quad, p_gen, topfield, m_Qp ); + else + { + /*predict the MAD of current picture*/ + predictCurrPicMAD( p_Inp, p_quad, p_gen ); + + /*compute the total number of bits for the current basic unit*/ + updateModelQPBU( p_Vid, p_Inp, p_quad, m_Qp ); + + p_quad->TotalFrameQP +=p_quad->m_Qc; + p_quad->Pm_Qp=p_quad->m_Qc; + p_quad->NumberofBasicUnit--; + if((p_quad->NumberofBasicUnit==0) && p_Vid->type == P_SLICE ) + updateLastBU( p_Vid, p_Inp, p_quad, p_gen, topfield ); + + return p_quad->m_Qc; + } + } + } + } + } + return p_quad->m_Qc; +} + +/*! + ************************************************************************************* + * \brief + * Save previous QP values for interlaced coding + * + ************************************************************************************* +*/ +void updateQPInterlace( RCQuadratic *p_quad, RCGeneric *p_gen ) +{ + if(p_gen->FieldControl==0) + { + /*previous choice is frame coding*/ + if(p_gen->FieldFrame==1) + { + p_quad->PrevLastQP=p_quad->CurrLastQP; + p_quad->CurrLastQP=p_quad->FrameQPBuffer; + } + /*previous choice is field coding*/ + else + { + p_quad->PrevLastQP=p_quad->CurrLastQP; + p_quad->CurrLastQP=p_quad->FieldQPBuffer; + } + } +} + +/*! + ************************************************************************************* + * \brief + * Save previous QP values for the case of non-PicAFF + * + ************************************************************************************* +*/ +void updateQPNonPicAFF( seq_parameter_set_rbsp_t *active_sps, RCQuadratic *p_quad ) +{ + if(active_sps->frame_mbs_only_flag) + { + p_quad->TotalQpforPPicture +=p_quad->m_Qc; + p_quad->PrevLastQP=p_quad->CurrLastQP; + p_quad->CurrLastQP=p_quad->m_Qc; + p_quad->Pm_Qp=p_quad->m_Qc; + } + /*adaptive field/frame coding*/ + else + p_quad->FrameQPBuffer=p_quad->m_Qc; +} + +/*! + ************************************************************************************* + * \brief + * Update QP values for bottom field in field coding + ************************************************************************************* +*/ +void updateBottomField( InputParameters *p_Inp, RCQuadratic *p_quad ) +{ + /*field coding*/ + if(p_Inp->PicInterlace==FIELD_CODING) + { + p_quad->TotalQpforPPicture +=p_quad->m_Qc; + p_quad->PrevLastQP=p_quad->CurrLastQP+1; + p_quad->CurrLastQP=p_quad->m_Qc;//+0 Recent change 13/1/2003 + p_quad->Pm_Qp=p_quad->m_Qc; + } + /*adaptive field/frame coding*/ + else + p_quad->FieldQPBuffer=p_quad->m_Qc; +} + +/*! + ************************************************************************************* + * \brief + * update QP variables for P frames + ************************************************************************************* +*/ +int updateFirstP( VideoParameters *p_Vid, InputParameters *p_Inp, RCQuadratic *p_quad, RCGeneric *p_gen, int topfield ) +{ + + /*top field of the first P frame*/ + p_quad->m_Qc=p_quad->MyInitialQp; + p_gen->NumberofBasicUnitHeaderBits=0; + p_gen->NumberofBasicUnitTextureBits=0; + p_quad->NumberofBasicUnit--; + /*bottom field of the first P frame*/ + if((!topfield)&&(p_quad->NumberofBasicUnit==0)) + { + /*frame coding or field coding*/ + if((p_Vid->active_sps->frame_mbs_only_flag)||(p_Inp->PicInterlace==FIELD_CODING)) + { + p_quad->TotalQpforPPicture +=p_quad->m_Qc; + p_quad->PrevLastQP=p_quad->CurrLastQP; + p_quad->CurrLastQP=p_quad->m_Qc; + p_quad->PAveFrameQP=p_quad->m_Qc; + p_quad->PAveHeaderBits3=p_quad->PAveHeaderBits2; + } + /*adaptive frame/field coding*/ + else if((p_Inp->PicInterlace==ADAPTIVE_CODING)||(p_Inp->MbInterlace)) + { + if(p_gen->FieldControl==0) + { + p_quad->FrameQPBuffer=p_quad->m_Qc; + p_quad->FrameAveHeaderBits=p_quad->PAveHeaderBits2; + } + else + { + p_quad->FieldQPBuffer=p_quad->m_Qc; + p_quad->FieldAveHeaderBits=p_quad->PAveHeaderBits2; + } + } + } + p_quad->Pm_Qp=p_quad->m_Qc; + p_quad->TotalFrameQP +=p_quad->m_Qc; + return p_quad->m_Qc; +} + +/*! + ************************************************************************************* + * \brief + * update QP when bit target is negative + ************************************************************************************* +*/ +int updateNegativeTarget( VideoParameters *p_Vid, InputParameters *p_Inp, RCQuadratic *p_quad, RCGeneric *p_gen, int topfield, int m_Qp ) +{ + int PAverageQP; + + if(p_quad->GOPOverdue==TRUE) + p_quad->m_Qc=m_Qp+2; + else + p_quad->m_Qc=m_Qp+p_quad->DDquant;//2 + + p_quad->m_Qc = imin(p_quad->m_Qc, p_Vid->RCMaxQP + p_quad->bitdepth_qp_scale); // clipping + if(p_Inp->basicunit>=p_quad->MBPerRow) + p_quad->m_Qc = imin(p_quad->m_Qc, p_quad->PAveFrameQP + 6); + else + p_quad->m_Qc = imin(p_quad->m_Qc, p_quad->PAveFrameQP + 3); + + p_quad->TotalFrameQP +=p_quad->m_Qc; + p_quad->NumberofBasicUnit--; + if(p_quad->NumberofBasicUnit==0) + { + if((!topfield)||(p_gen->FieldControl==0)) + { + /*frame coding or field coding*/ + if((p_Vid->active_sps->frame_mbs_only_flag)||(p_Inp->PicInterlace==FIELD_CODING)) + { + PAverageQP=(int)((double)p_quad->TotalFrameQP/(double)p_quad->TotalNumberofBasicUnit+0.5); + if (p_quad->NumberofPPicture == (p_Inp->intra_period - 2)) + p_quad->QPLastPFrame = PAverageQP; + + p_quad->TotalQpforPPicture +=PAverageQP; + if(p_quad->GOPOverdue==TRUE) + { + p_quad->PrevLastQP=p_quad->CurrLastQP+1; + p_quad->CurrLastQP=PAverageQP; + } + else + { + if((p_quad->NumberofPPicture==0)&&(p_gen->NumberofGOP>1)) + { + p_quad->PrevLastQP=p_quad->CurrLastQP; + p_quad->CurrLastQP=PAverageQP; + } + else if(p_quad->NumberofPPicture>0) + { + p_quad->PrevLastQP=p_quad->CurrLastQP+1; + p_quad->CurrLastQP=PAverageQP; + } + } + p_quad->PAveFrameQP=PAverageQP; + p_quad->PAveHeaderBits3=p_quad->PAveHeaderBits2; + } + /*adaptive field/frame coding*/ + else if((p_Inp->PicInterlace==ADAPTIVE_CODING)||(p_Inp->MbInterlace)) + { + if(p_gen->FieldControl==0) + { + PAverageQP=(int)((double)p_quad->TotalFrameQP/(double)p_quad->TotalNumberofBasicUnit+0.5); + p_quad->FrameQPBuffer=PAverageQP; + p_quad->FrameAveHeaderBits=p_quad->PAveHeaderBits2; + } + else + { + PAverageQP=(int)((double)p_quad->TotalFrameQP/(double)p_quad->TotalNumberofBasicUnit+0.5); + p_quad->FieldQPBuffer=PAverageQP; + p_quad->FieldAveHeaderBits=p_quad->PAveHeaderBits2; + } + } + } + } + if(p_quad->GOPOverdue==TRUE) + p_quad->Pm_Qp=p_quad->PAveFrameQP; + else + p_quad->Pm_Qp=p_quad->m_Qc; + + return p_quad->m_Qc; +} + +/*! + ************************************************************************************* + * \brief + * update QP for the first Basic Unit in the picture + ************************************************************************************* +*/ +int updateFirstBU( VideoParameters *p_Vid, InputParameters *p_Inp, RCQuadratic *p_quad, RCGeneric *p_gen, int topfield ) +{ + /*adaptive field/frame coding*/ + if(((p_Inp->PicInterlace==ADAPTIVE_CODING)||(p_Inp->MbInterlace))&&(p_gen->FieldControl==0)) + { + /*previous choice is frame coding*/ + if(p_gen->FieldFrame==1) + { + if(p_quad->NumberofPPicture>0) + p_quad->TotalQpforPPicture +=p_quad->FrameQPBuffer; + p_quad->PAveFrameQP=p_quad->FrameQPBuffer; + p_quad->PAveHeaderBits3=p_quad->FrameAveHeaderBits; + } + /*previous choice is field coding*/ + else + { + if(p_quad->NumberofPPicture>0) + p_quad->TotalQpforPPicture +=p_quad->FieldQPBuffer; + p_quad->PAveFrameQP=p_quad->FieldQPBuffer; + p_quad->PAveHeaderBits3=p_quad->FieldAveHeaderBits; + } + } + + if(p_quad->Target<=0) + { + p_quad->m_Qc = p_quad->PAveFrameQP + 2; + if(p_quad->m_Qc > (p_Vid->RCMaxQP + p_quad->bitdepth_qp_scale)) + p_quad->m_Qc = p_Vid->RCMaxQP + p_quad->bitdepth_qp_scale; + + if(topfield||(p_gen->FieldControl==0)) + p_quad->GOPOverdue=TRUE; + } + else + { + p_quad->m_Qc=p_quad->PAveFrameQP; + } + p_quad->TotalFrameQP +=p_quad->m_Qc; + p_quad->NumberofBasicUnit--; + p_quad->Pm_Qp = p_quad->PAveFrameQP; + + return p_quad->m_Qc; +} + +/*! + ************************************************************************************* + * \brief + * update QP for the last Basic Unit in the picture + ************************************************************************************* +*/ +void updateLastBU( VideoParameters *p_Vid, InputParameters *p_Inp, RCQuadratic *p_quad, RCGeneric *p_gen, int topfield ) +{ + int PAverageQP; + + if((!topfield)||(p_gen->FieldControl==0)) + { + /*frame coding or field coding*/ + if((p_Vid->active_sps->frame_mbs_only_flag)||(p_Inp->PicInterlace==FIELD_CODING)) + { + PAverageQP=(int)((double)p_quad->TotalFrameQP/(double) p_quad->TotalNumberofBasicUnit+0.5); + if (p_quad->NumberofPPicture == (p_Inp->intra_period - 2)) + p_quad->QPLastPFrame = PAverageQP; + + p_quad->TotalQpforPPicture +=PAverageQP; + p_quad->PrevLastQP=p_quad->CurrLastQP; + p_quad->CurrLastQP=PAverageQP; + p_quad->PAveFrameQP=PAverageQP; + p_quad->PAveHeaderBits3=p_quad->PAveHeaderBits2; + } + else if((p_Inp->PicInterlace==ADAPTIVE_CODING)||(p_Inp->MbInterlace)) + { + if(p_gen->FieldControl==0) + { + PAverageQP=(int)((double) p_quad->TotalFrameQP/(double)p_quad->TotalNumberofBasicUnit+0.5); + p_quad->FrameQPBuffer=PAverageQP; + p_quad->FrameAveHeaderBits=p_quad->PAveHeaderBits2; + } + else + { + PAverageQP=(int)((double) p_quad->TotalFrameQP/(double) p_quad->TotalNumberofBasicUnit+0.5); + p_quad->FieldQPBuffer=PAverageQP; + p_quad->FieldAveHeaderBits=p_quad->PAveHeaderBits2; + } + } + } +} + +/*! + ************************************************************************************* + * \brief + * update current picture MAD + ************************************************************************************* +*/ +void predictCurrPicMAD( InputParameters *p_Inp, RCQuadratic *p_quad, RCGeneric *p_gen ) +{ + int i; + if(((p_Inp->PicInterlace==ADAPTIVE_CODING)||(p_Inp->MbInterlace))&&(p_gen->FieldControl==1)) + { + p_quad->CurrentFrameMAD=p_quad->MADPictureC1*p_quad->FCBUPFMAD[p_quad->TotalNumberofBasicUnit-p_quad->NumberofBasicUnit]+p_quad->MADPictureC2; + p_quad->TotalBUMAD=0; + for(i=p_quad->TotalNumberofBasicUnit-1; i>=(p_quad->TotalNumberofBasicUnit-p_quad->NumberofBasicUnit);i--) + { + p_quad->CurrentBUMAD=p_quad->MADPictureC1*p_quad->FCBUPFMAD[i]+p_quad->MADPictureC2; + p_quad->TotalBUMAD +=p_quad->CurrentBUMAD*p_quad->CurrentBUMAD; + } + } + else + { + p_quad->CurrentFrameMAD=p_quad->MADPictureC1*p_quad->BUPFMAD[p_quad->TotalNumberofBasicUnit-p_quad->NumberofBasicUnit]+p_quad->MADPictureC2; + p_quad->TotalBUMAD=0; + for(i=p_quad->TotalNumberofBasicUnit-1; i>=(p_quad->TotalNumberofBasicUnit-p_quad->NumberofBasicUnit);i--) + { + p_quad->CurrentBUMAD=p_quad->MADPictureC1*p_quad->BUPFMAD[i]+p_quad->MADPictureC2; + p_quad->TotalBUMAD +=p_quad->CurrentBUMAD*p_quad->CurrentBUMAD; + } + } +} + +/*! + ************************************************************************************* + * \brief + * update QP using the quadratic model for basic unit coding + ************************************************************************************* +*/ +void updateModelQPBU( VideoParameters *p_Vid, InputParameters *p_Inp, RCQuadratic *p_quad, int m_Qp ) +{ + double dtmp, m_Qstep; + int m_Bits; + /*compute the total number of bits for the current basic unit*/ + m_Bits =(int)(p_quad->Target * p_quad->CurrentFrameMAD * p_quad->CurrentFrameMAD / p_quad->TotalBUMAD); + /*compute the number of texture bits*/ + m_Bits -=p_quad->PAveHeaderBits2; + + m_Bits=imax(m_Bits,(int)(p_quad->bit_rate/(MINVALUE*p_quad->frame_rate*p_quad->TotalNumberofBasicUnit))); + + dtmp = p_quad->CurrentFrameMAD * p_quad->CurrentFrameMAD * p_quad->m_X1 * p_quad->m_X1 \ + + 4 * p_quad->m_X2 * p_quad->CurrentFrameMAD * m_Bits; + if ((p_quad->m_X2 == 0.0) || (dtmp < 0) || ((sqrt (dtmp) - p_quad->m_X1 * p_quad->CurrentFrameMAD) <= 0.0)) // fall back 1st order mode + m_Qstep = (float)(p_quad->m_X1 * p_quad->CurrentFrameMAD / (double) m_Bits); + else // 2nd order mode + m_Qstep = (float) ((2 * p_quad->m_X2 * p_quad->CurrentFrameMAD) / (sqrt (dtmp) - p_quad->m_X1 * p_quad->CurrentFrameMAD)); + + p_quad->m_Qc = Qstep2QP(m_Qstep, p_quad->bitdepth_qp_scale); + p_quad->m_Qc = imin(m_Qp+p_quad->DDquant, p_quad->m_Qc); // control variation + + if(p_Inp->basicunit>=p_quad->MBPerRow) + p_quad->m_Qc = imin(p_quad->PAveFrameQP+6, p_quad->m_Qc); + else + p_quad->m_Qc = imin(p_quad->PAveFrameQP+3, p_quad->m_Qc); + + p_quad->m_Qc = iClip3(m_Qp-p_quad->DDquant, p_Vid->RCMaxQP + p_quad->bitdepth_qp_scale, p_quad->m_Qc); // clipping + if(p_Inp->basicunit>=p_quad->MBPerRow) + p_quad->m_Qc = imax(p_quad->PAveFrameQP-6, p_quad->m_Qc); + else + p_quad->m_Qc = imax(p_quad->PAveFrameQP-3, p_quad->m_Qc); + + p_quad->m_Qc = imax(p_Vid->RCMinQP + p_quad->bitdepth_qp_scale, p_quad->m_Qc); +} + +/*! + ************************************************************************************* + * \brief + * update QP variables for interlaced pictures and basic unit coding + ************************************************************************************* +*/ +void updateQPInterlaceBU( RCQuadratic *p_quad, RCGeneric *p_gen ) +{ + /*previous choice is frame coding*/ + if(p_gen->FieldFrame==1) + { + p_quad->TotalQpforPPicture +=p_quad->FrameQPBuffer; + p_quad->Pm_Qp=p_quad->FrameQPBuffer; + } + /*previous choice is field coding*/ + else + { + p_quad->TotalQpforPPicture +=p_quad->FieldQPBuffer; + p_quad->Pm_Qp=p_quad->FieldQPBuffer; + } +} + +/*! + ************************************************************************************* + * \brief + * update QP with quadratic model + ************************************************************************************* +*/ +void updateModelQPFrame( RCQuadratic *p_quad, int m_Bits ) +{ + double dtmp, m_Qstep; + + dtmp = p_quad->CurrentFrameMAD * p_quad->m_X1 * p_quad->CurrentFrameMAD * p_quad->m_X1 + + 4 * p_quad->m_X2 * p_quad->CurrentFrameMAD * m_Bits; + if ((p_quad->m_X2 == 0.0) || (dtmp < 0) || ((sqrt (dtmp) - p_quad->m_X1 * p_quad->CurrentFrameMAD) <= 0.0)) // fall back 1st order mode + m_Qstep = (float) (p_quad->m_X1 * p_quad->CurrentFrameMAD / (double) m_Bits); + else // 2nd order mode + m_Qstep = (float) ((2 * p_quad->m_X2 * p_quad->CurrentFrameMAD) / (sqrt (dtmp) - p_quad->m_X1 * p_quad->CurrentFrameMAD)); + + p_quad->m_Qc = Qstep2QP(m_Qstep, p_quad->bitdepth_qp_scale); +} + +/*! + ************************************************************************************* + * \brief + * rate control at the MB level + ************************************************************************************* +*/ +int rc_handle_mb( Macroblock *currMB, int prev_mb ) +{ + VideoParameters *p_Vid = currMB->p_Vid; + InputParameters *p_Inp = currMB->p_Inp; + Macroblock *prevMB = currMB->PrevMB; + int mb_qp = p_Vid->qp; + RCGeneric *p_gen = p_Vid->p_rc_gen; + RCQuadratic *p_quad = p_Vid->p_rc_quad; + + if (prev_mb > -1) + { + if ( p_Inp->MbInterlace == ADAPTIVE_CODING && !p_Vid->bot_MB && currMB->mb_field ) + mb_qp = prevMB->qp; + } + + // frame layer rate control + if (p_Inp->basicunit != p_Vid->FrameSizeInMbs) + { + // each I or B frame has only one QP + if ( ((p_Vid->type == I_SLICE || p_Vid->type == B_SLICE) && p_Inp->RCUpdateMode != RC_MODE_1 ) || !(p_Vid->number) ) + { + return mb_qp; + } + else if ( p_Vid->type == P_SLICE || p_Inp->RCUpdateMode == RC_MODE_1 ) + { + if (!p_Vid->write_macroblock) //write macroblock + { + if (prev_mb > -1) + { + if (!((p_Inp->MbInterlace) && p_Vid->bot_MB)) //top macroblock + { + if (prevMB->prev_cbp != 1) + { + mb_qp = prevMB->prev_qp; + } + } + } + } + + // compute the quantization parameter for each basic unit of P frame + + if (!p_Vid->write_macroblock) + { + if(!((p_Inp->MbInterlace) && p_Vid->bot_MB)) + { + if(p_Inp->RCUpdateMode <= MAX_RC_MODE && (p_Vid->NumberofCodedMacroBlocks > 0) && (p_Vid->NumberofCodedMacroBlocks % p_Vid->BasicUnit == 0)) + { + updateRCModel(p_Vid, p_Inp, p_quad, p_gen); + // frame coding + if(p_Vid->active_sps->frame_mbs_only_flag) + { + p_Vid->BasicUnitQP = p_Vid->updateQP(p_Vid, p_Inp, p_quad, p_gen, p_gen->TopFieldFlag) - p_quad->bitdepth_qp_scale; + } + // picture adaptive field/frame coding + else if(p_Inp->MbInterlace || ((p_Inp->PicInterlace!=FRAME_CODING) && (p_gen->NoGranularFieldRC==0))) + { + p_Vid->BasicUnitQP = p_Vid->updateQP(p_Vid, p_Inp, p_quad, p_gen, p_gen->TopFieldFlag) - p_quad->bitdepth_qp_scale; + } + } + + if(p_Vid->current_mb_nr==0) + p_Vid->BasicUnitQP = mb_qp; + + + mb_qp = p_Vid->BasicUnitQP; + mb_qp = iClip3(MIN_QP - p_Vid->bitdepth_luma_qp_scale, MAX_QP, mb_qp); + } + } + } + } + return mb_qp; +} + +/*! + ************************************************************************************* + * \brief + * initialize rate control model for the top field + ************************************************************************************* +*/ +void rc_init_top_field ( VideoParameters *p_Vid, InputParameters *p_Inp ) +{ + RCGeneric *p_gen = p_Vid->p_rc_gen; + RCQuadratic *p_quad = p_Vid->p_rc_quad; + + p_Vid->BasicUnit = p_Inp->basicunit; + p_gen->TopFieldFlag = 1; + p_Vid->rc_init_pict_ptr(p_Vid, p_Inp, p_quad, p_gen, 0, 1, (p_Inp->PicInterlace == FIELD_CODING), 1.0F); + p_Vid->p_curr_frm_struct->qp = p_Vid->qp = p_Vid->updateQP(p_Vid, p_Inp, p_quad, p_gen, 1) - p_quad->bitdepth_qp_scale; +} + +/*! + ************************************************************************************* + * \brief + * initialize rate control model for the bottom field + ************************************************************************************* +*/ +void rc_init_bottom_field ( VideoParameters *p_Vid, InputParameters *p_Inp, int TopFieldBits ) +{ + RCGeneric *p_gen = p_Vid->p_rc_gen; + RCQuadratic *p_quad = p_Vid->p_rc_quad; + + p_quad->bits_topfield = TopFieldBits; + p_gen->TopFieldFlag = 0; + p_Vid->rc_init_pict_ptr(p_Vid, p_Inp, p_quad, p_gen, 0,0,0, 1.0F); + p_Vid->p_curr_frm_struct->qp = p_Vid->qp = p_Vid->updateQP(p_Vid, p_Inp, p_quad, p_gen, 0) - p_quad->bitdepth_qp_scale; +} + +/*! + ************************************************************************************* + * \brief + * initialize rate control for RDPictureDecision + ************************************************************************************* +*/ +void rc_init_frame_rdpic( VideoParameters *p_Vid, InputParameters *p_Inp, float rateRatio ) +{ + RCGeneric *p_gen = p_Vid->p_rc_gen; + RCGeneric *p_gen_init = p_Vid->p_rc_gen_init; + RCQuadratic *p_quad = p_Vid->p_rc_quad; + RCQuadratic *p_quad_init = p_Vid->p_rc_quad_init; + + switch (p_Inp->RCUpdateMode) + { + case RC_MODE_0: case RC_MODE_1: case RC_MODE_2: case RC_MODE_3: + // re-store the initial RC model + rc_copy_quadratic( p_Vid, p_Inp, p_quad, p_quad_init ); + rc_copy_generic( p_Vid, p_gen, p_gen_init ); + p_Vid->rc_init_pict_ptr(p_Vid, p_Inp, p_quad, p_gen, 1, 0, 1, rateRatio ); + p_Vid->p_curr_frm_struct->qp = p_Vid->qp = p_Vid->updateQP(p_Vid, p_Inp, p_quad, p_gen, 0) - p_quad->bitdepth_qp_scale; + break; + default: + break; + } +} + +/*! + ************************************************************************************* + * \brief + * allocate rate control memory + ************************************************************************************* +*/ +void rc_allocate_memory( VideoParameters *p_Vid, InputParameters *p_Inp ) +{ + switch (p_Inp->RCUpdateMode) + { + case RC_MODE_0: + case RC_MODE_1: + case RC_MODE_2: + case RC_MODE_3: + rc_alloc_generic( p_Vid, &p_Vid->p_rc_gen ); + rc_alloc_quadratic( p_Vid, p_Inp, &p_Vid->p_rc_quad ); + + if ( p_Inp->RDPictureDecision || p_Inp->MbInterlace == ADAPTIVE_CODING || p_Inp->PicInterlace == ADAPTIVE_CODING ) + { + rc_alloc_generic( p_Vid, &p_Vid->p_rc_gen_init ); + rc_alloc_quadratic( p_Vid, p_Inp, &p_Vid->p_rc_quad_init ); + rc_alloc_generic( p_Vid, &p_Vid->p_rc_gen_best ); + rc_alloc_quadratic( p_Vid, p_Inp, &p_Vid->p_rc_quad_best ); + } + break; + default: + break; + } +} + +/*! + ************************************************************************************* + * \brief + * free rate control memory + ************************************************************************************* +*/ +void rc_free_memory( VideoParameters *p_Vid, InputParameters *p_Inp ) +{ + switch (p_Inp->RCUpdateMode) + { + case RC_MODE_0: + case RC_MODE_1: + case RC_MODE_2: + case RC_MODE_3: + rc_free_generic( &p_Vid->p_rc_gen ); + rc_free_quadratic( &p_Vid->p_rc_quad ); + + if ( p_Inp->RDPictureDecision || p_Inp->MbInterlace == ADAPTIVE_CODING || p_Inp->PicInterlace == ADAPTIVE_CODING ) + { + rc_free_generic( &p_Vid->p_rc_gen_init ); + rc_free_quadratic( &p_Vid->p_rc_quad_init ); + rc_free_generic( &p_Vid->p_rc_gen_best ); + rc_free_quadratic( &p_Vid->p_rc_quad_best ); + } + break; + default: + break; + } +} + +/*! + ************************************************************************************* + * \brief + * update coding statistics after MB coding + ************************************************************************************* +*/ +void rc_update_mb_stats(Macroblock *currMB) +{ + VideoParameters *p_Vid = currMB->p_Vid; + InputParameters *p_Inp = currMB->p_Inp; + BitCounter *mbBits = &currMB->bits; + RCGeneric *p_gen = p_Vid->p_rc_gen; + + // Rate control + p_Vid->NumberofMBHeaderBits = mbBits->mb_mode + mbBits->mb_inter + + mbBits->mb_cbp + mbBits->mb_delta_quant; + p_Vid->NumberofMBTextureBits = mbBits->mb_y_coeff + mbBits->mb_uv_coeff; + + switch (p_Inp->RCUpdateMode) + { + case RC_MODE_0: case RC_MODE_1: case RC_MODE_2: case RC_MODE_3: + p_gen->NumberofTextureBits += p_Vid->NumberofMBTextureBits; + p_gen->NumberofHeaderBits += p_Vid->NumberofMBHeaderBits; + // basic unit layer rate control + if(p_Vid->BasicUnit < p_Vid->FrameSizeInMbs) + { + p_gen->NumberofBasicUnitHeaderBits += p_Vid->NumberofMBHeaderBits; + p_gen->NumberofBasicUnitTextureBits += p_Vid->NumberofMBTextureBits; + } + break; + default: + break; + } +} + +/*! + ************************************************************************************* + * \brief + * save state of rate control model + ************************************************************************************* +*/ +void rc_save_state( VideoParameters *p_Vid, InputParameters *p_Inp ) +{ + switch (p_Inp->RCUpdateMode) + { + case RC_MODE_0: case RC_MODE_1: case RC_MODE_2: case RC_MODE_3: + rc_copy_quadratic( p_Vid, p_Inp, p_Vid->p_rc_quad_best, p_Vid->p_rc_quad ); + rc_copy_generic( p_Vid, p_Vid->p_rc_gen_best, p_Vid->p_rc_gen ); + break; + default: + break; + } +} + +/*! + ************************************************************************************* + * \brief + * restore state of rate control model + ************************************************************************************* +*/ +void rc_restore_state( VideoParameters *p_Vid, InputParameters *p_Inp ) +{ + switch (p_Inp->RCUpdateMode) + { + case RC_MODE_0: case RC_MODE_1: case RC_MODE_2: case RC_MODE_3: + rc_copy_quadratic( p_Vid, p_Inp, p_Vid->p_rc_quad, p_Vid->p_rc_quad_best ); + rc_copy_generic( p_Vid, p_Vid->p_rc_gen, p_Vid->p_rc_gen_best ); + break; + default: + break; + } +} diff --git a/lencod/src/rd_intra_jm.c b/lencod/src/rd_intra_jm.c new file mode 100644 index 0000000..24063c0 --- /dev/null +++ b/lencod/src/rd_intra_jm.c @@ -0,0 +1,563 @@ +/*! + *************************************************************************** + * \file rd_intra_jm.c + * + * \brief + * Rate-Distortion optimized mode decision + * + * \author + * - Heiko Schwarz + * - Valeri George + * - Lowell Winger + * - Alexis Michael Tourapis + * \date + * 12. April 2001 + ************************************************************************** + */ + +#include + +#include "global.h" + +#include "image.h" +#include "macroblock.h" +#include "mb_access.h" +#include "rdopt_coding_state.h" +#include "mode_decision.h" +#include "rdopt.h" +#include "rd_intra_jm.h" +#include "q_around.h" +#include "intra4x4.h" +#include "intra8x8.h" +#include "md_common.h" +#include "transform8x8.h" +#include "md_distortion.h" +#include "elements.h" +#include "symbol.h" +#include "intra16x16.h" +#include "intra4x4.h" +#include "intra8x8.h" + +extern int MBType2Value (Macroblock* currMB); + +/*! + ************************************************************************************* + * \brief + * Mode Decision for an 4x4 Intra block + ************************************************************************************* + */ +int mode_decision_for_I4x4_blocks_JM_High (Macroblock *currMB, int b8, int b4, int lambda, distblk* min_cost) +{ + VideoParameters *p_Vid = currMB->p_Vid; + InputParameters *p_Inp = currMB->p_Inp; + Slice *currSlice = currMB->p_Slice; + RDOPTStructure *p_RDO = currSlice->p_RDO; + + int ipmode, best_ipmode = 0, y; + int available_mode; + int c_nz, nonzero = 0; + int* ACLevel = currSlice->cofAC[b8][b4][0]; + int* ACRun = currSlice->cofAC[b8][b4][1]; + distblk rdcost = 0; + distblk min_rdcost = DISTBLK_MAX; + int block_x = ((b8 & 0x01) << 3) + ((b4 & 0x01) << 2); + int block_y = ((b8 >> 1) << 3) + ((b4 >> 1) << 2); + int pic_pix_x = currMB->pix_x + block_x; + int pic_pix_y = currMB->pix_y + block_y; + int pic_opix_x = currMB->pix_x + block_x; + int pic_opix_y = currMB->opix_y + block_y; + int pic_block_x = pic_pix_x >> 2; + int pic_block_y = pic_pix_y >> 2; + + int left_available, up_available, all_available; + int *mb_size = p_Vid->mb_size[IS_LUMA]; + + char upMode, leftMode; + int mostProbableMode; + + PixelPos left_block, top_block; + + int lrec4x4[4][4]; + int best_nz_coeff = 0; + int block_x4 = block_x>>2; + int block_y4 = block_y>>2; + +#ifdef BEST_NZ_COEFF + int best_coded_block_flag = 0; + int bit_pos = 1 + ((((b8>>1)<<1)+(b4>>1))<<2) + (((b8&1)<<1)+(b4&1)); + int64 cbp_bits; + + if (b8==0 && b4==0) + cbp_bits = 0; +#endif + + get4x4Neighbour(currMB, block_x - 1, block_y , mb_size, &left_block); + get4x4Neighbour(currMB, block_x, block_y - 1, mb_size, &top_block ); + + // constrained intra pred + if (p_Inp->UseConstrainedIntraPred) + { + left_block.available = left_block.available ? p_Vid->intra_block[left_block.mb_addr] : 0; + top_block.available = top_block.available ? p_Vid->intra_block[top_block.mb_addr] : 0; + } + + upMode = top_block.available ? p_Vid->ipredmode[top_block.pos_y ][top_block.pos_x ] : (char) -1; + leftMode = left_block.available ? p_Vid->ipredmode[left_block.pos_y][left_block.pos_x] : (char) -1; + + mostProbableMode = (upMode < 0 || leftMode < 0) ? DC_PRED : upMode < leftMode ? upMode : leftMode; + *min_cost = DISTBLK_MAX; + + currMB->ipmode_DPCM = NO_INTRA_PMODE; ////For residual DPCM + + //===== INTRA PREDICTION FOR 4x4 BLOCK ===== + // set intra prediction values for 4x4 intra prediction + currSlice->set_intrapred_4x4(currMB, PLANE_Y, pic_pix_x, pic_pix_y, &left_available, &up_available, &all_available); + + //===== LOOP OVER ALL 4x4 INTRA PREDICTION MODES ===== + for (ipmode = 0; ipmode < NO_INTRA_PMODE; ipmode++) + { + available_mode = (all_available) || (ipmode==DC_PRED) || + (up_available && (ipmode==VERT_PRED||ipmode==VERT_LEFT_PRED||ipmode==DIAG_DOWN_LEFT_PRED)) || + (left_available && (ipmode==HOR_PRED||ipmode==HOR_UP_PRED)); + + if (valid_intra_mode(currSlice, ipmode) == 0) + continue; + + if( available_mode) + { + // generate intra 4x4 prediction block given availability + get_intrapred_4x4(currMB, PLANE_Y, ipmode, block_x, block_y, left_available, up_available); + + // get prediction and prediction error + generate_pred_error_4x4(&p_Vid->pCurImg[pic_opix_y], currSlice->mpr_4x4[0][ipmode], &currSlice->mb_pred[0][block_y], &currSlice->mb_ores[0][block_y], pic_opix_x, block_x); + + // get and check rate-distortion cost +#ifdef BEST_NZ_COEFF + currMB->cbp_bits[0] = cbp_bits; +#endif + + rdcost = currSlice->rdcost_for_4x4_intra_blocks (currMB, &c_nz, b8, b4, ipmode, lambda, mostProbableMode, min_rdcost); + if ((rdcost < min_rdcost) || (rdcost == min_rdcost && ipmode == mostProbableMode)) + { + //--- set coefficients --- + memcpy(p_RDO->cofAC4x4[0], ACLevel, 18 * sizeof(int)); + memcpy(p_RDO->cofAC4x4[1], ACRun, 18 * sizeof(int)); + + //--- set reconstruction --- + copy_4x4block(p_RDO->rec4x4[PLANE_Y], &p_Vid->enc_picture->imgY[pic_pix_y], 0, pic_pix_x); + + // SP/SI reconstruction + if(currSlice->slice_type == SP_SLICE && !currSlice->sp2_frame_indicator) + { + for (y=0; y<4; y++) + { + memcpy(lrec4x4[y],&p_Vid->lrec[pic_pix_y+y][pic_pix_x], BLOCK_SIZE * sizeof(int));// stores the mode coefficients + } + } + + //--- flag if transform-coefficients must be coded --- + nonzero = c_nz; + + //--- set best mode update minimum cost --- + *min_cost = rdcost; + min_rdcost = rdcost; + best_ipmode = ipmode; + + best_nz_coeff = p_Vid->nz_coeff [currMB->mbAddrX][block_x4][block_y4]; +#ifdef BEST_NZ_COEFF + best_coded_block_flag = (int)((currMB->cbp_bits[0] >> bit_pos)&(int64)(1)); +#endif + if (p_Vid->AdaptiveRounding) + { + store_adaptive_rounding_4x4 (p_Vid, p_Vid->ARCofAdj4x4, I4MB, block_y, block_x); + } + } + } + } +#if INTRA_RDCOSTCALC_NNZ + p_Vid->nz_coeff [currMB->mbAddrX][block_x4][block_y4] = best_nz_coeff; +#endif +#ifdef BEST_NZ_COEFF + cbp_bits &= (~(int64)(1<ipredmode[pic_block_y][pic_block_x] = (char) best_ipmode; + currMB->intra_pred_modes[4*b8+b4] = + (char) (mostProbableMode == best_ipmode ? -1 : (best_ipmode < mostProbableMode ? best_ipmode : best_ipmode-1)); + + //===== restore coefficients ===== + memcpy (ACLevel, p_RDO->cofAC4x4[0], 18 * sizeof(int)); + memcpy (ACRun, p_RDO->cofAC4x4[1], 18 * sizeof(int)); + + //===== restore reconstruction and prediction (needed if single coeffs are removed) ===== + copy_4x4block(&p_Vid->enc_picture->imgY[pic_pix_y], p_RDO->rec4x4[PLANE_Y], pic_pix_x, 0); + copy_4x4block(&currSlice->mb_pred[0][block_y], currSlice->mpr_4x4[0][best_ipmode], block_x, 0); + + // SP/SI reconstuction + if(currSlice->slice_type == SP_SLICE && !currSlice->sp2_frame_indicator) + { + for (y=0; ylrec[pic_pix_y+y][pic_pix_x], lrec4x4[y], BLOCK_SIZE * sizeof(int));//restore coefficients when encoding primary SP frame + } + } + + if (p_Vid->AdaptiveRounding) + { + update_adaptive_rounding_4x4 (p_Vid,p_Vid->ARCofAdj4x4, I4MB, block_y, block_x); + } + + return nonzero; +} + +/*! +************************************************************************************* +* \brief +* 8x8 Intra mode decision for a macroblock - High complexity +************************************************************************************* +*/ +int mode_decision_for_I8x8_blocks_JM_High (Macroblock *currMB, int b8, int lambda, distblk *min_cost) +{ + VideoParameters *p_Vid = currMB->p_Vid; + InputParameters *p_Inp = currMB->p_Inp; + Slice *currSlice = currMB->p_Slice; + RDOPTStructure *p_RDO = currSlice->p_RDO; + + int ipmode, best_ipmode = 0, j; + int c_nz, nonzero = 0; + distblk rdcost = 0; + distblk min_rdcost = DISTBLK_MAX; + int block_x = (b8 & 0x01) << 3; + int block_y = (b8 >> 1) << 3; + int pic_pix_x = currMB->pix_x + block_x; + int pic_pix_y = currMB->pix_y + block_y; + int pic_opix_x = currMB->pix_x + block_x; + int pic_opix_y = currMB->opix_y + block_y; + int pic_block_x = pic_pix_x >> 2; + int pic_block_y = pic_pix_y >> 2; + int mb_block_y = (currMB->block_y) + ((b8 >> 1) << 1); + int mb_block_x = (currMB->block_x) + ((b8 & 0x01) << 1); + int *p_AC8x8 = p_RDO->coefAC8x8intra[b8][0][0][0]; + int *cofAC = currSlice->cofAC[b8][0][0]; + + int left_available, up_available, all_available; + int **mb_ores = currSlice->mb_ores[0]; + imgpel **mb_pred = currSlice->mb_pred[0]; + + char upMode, leftMode; + int mostProbableMode; + + PixelPos left_block, top_block; + + int *mb_size = p_Vid->mb_size[IS_LUMA]; + + get4x4Neighbour(currMB, block_x - 1, block_y , mb_size, &left_block); + get4x4Neighbour(currMB, block_x, block_y - 1, mb_size, &top_block ); + + // constrained intra pred + if (p_Inp->UseConstrainedIntraPred) + { + left_block.available = left_block.available ? p_Vid->intra_block [left_block.mb_addr] : 0; + top_block.available = top_block.available ? p_Vid->intra_block [top_block.mb_addr ] : 0; + } + + if(b8 >> 1) + upMode = top_block.available ? p_Vid->ipredmode8x8[top_block.pos_y ][top_block.pos_x ] : (char) -1; + else + upMode = top_block.available ? p_Vid->ipredmode [top_block.pos_y ][top_block.pos_x ] : (char) -1; + + if(b8 & 0x01) + leftMode = left_block.available ? p_Vid->ipredmode8x8[left_block.pos_y][left_block.pos_x] : (char) -1; + else + leftMode = left_block.available ? p_Vid->ipredmode[left_block.pos_y][left_block.pos_x] : (char) -1; + + mostProbableMode = (upMode < 0 || leftMode < 0) ? DC_PRED : upMode < leftMode ? upMode : leftMode; + *min_cost = DISTBLK_MAX; + currMB->ipmode_DPCM = NO_INTRA_PMODE; //For residual DPCM + + //===== INTRA PREDICTION FOR 8x8 BLOCK ===== + currSlice->set_intrapred_8x8(currMB, PLANE_Y, pic_pix_x, pic_pix_y, &left_available, &up_available, &all_available); + + //===== LOOP OVER ALL 8x8 INTRA PREDICTION MODES ===== + for (ipmode = 0; ipmode < NO_INTRA_PMODE; ipmode++) + { + if( (ipmode==DC_PRED) || + ((ipmode==VERT_PRED||ipmode==VERT_LEFT_PRED||ipmode==DIAG_DOWN_LEFT_PRED) && up_available ) || + ((ipmode==HOR_PRED||ipmode==HOR_UP_PRED) && left_available ) || + (all_available) ) + { + get_intrapred_8x8(currMB, PLANE_Y, ipmode, left_available, up_available); + // get prediction and prediction error + generate_pred_error_8x8(&p_Vid->pCurImg[pic_opix_y], currSlice->mpr_8x8[0][ipmode], &mb_pred[block_y], &mb_ores[block_y], pic_opix_x, block_x); + + currMB->ipmode_DPCM = (short) ipmode; + + // get and check rate-distortion cost + + rdcost = currSlice->rdcost_for_8x8_intra_blocks (currMB, &c_nz, b8, ipmode, lambda, min_rdcost, mostProbableMode); + if ((rdcost < min_rdcost) || (rdcost == min_rdcost && ipmode == mostProbableMode)) + { + //--- set coefficients --- + memcpy(p_AC8x8, cofAC, 4 * 2 * 65 * sizeof(int)); + + //--- set reconstruction --- + copy_image_data_8x8(p_RDO->rec8x8[PLANE_Y], &p_Vid->enc_picture->imgY[pic_pix_y], 0, pic_pix_x); + + if (p_Vid->AdaptiveRounding) + { + for (j = block_y; j < block_y + 8; j++) + memcpy(&p_Vid->ARCofAdj8x8[0][DUMMY][j][block_x],&p_Vid->ARCofAdj8x8[0][I8MB][j][block_x], 8 * sizeof(int)); + } + + //--- flag if transform coefficients must be coded --- + nonzero = c_nz; + + //--- set best mode update minimum cost --- + *min_cost = rdcost; + min_rdcost = rdcost; + best_ipmode = ipmode; + } + } + } + + //===== set intra mode prediction ===== + p_Vid->ipredmode8x8[pic_block_y][pic_block_x] = (char) best_ipmode; + currMB->ipmode_DPCM = (short) best_ipmode; //For residual DPCM + + currMB->intra_pred_modes8x8[4*b8] = (char) ((mostProbableMode == best_ipmode) + ? -1 + : (best_ipmode < mostProbableMode ? best_ipmode : best_ipmode - 1)); + + memset(&p_Vid->ipredmode8x8[mb_block_y ][mb_block_x], best_ipmode, 2 * sizeof(char)); + memset(&p_Vid->ipredmode8x8[mb_block_y + 1][mb_block_x], best_ipmode, 2 * sizeof(char)); + + //===== restore coefficients ===== + memcpy(cofAC, p_AC8x8, 4 * 2 * 65 * sizeof(int)); + + if (p_Vid->AdaptiveRounding) + { + for (j=block_y; j< block_y + 8; j++) + memcpy(&p_Vid->ARCofAdj8x8[0][I8MB][j][block_x], &p_Vid->ARCofAdj8x8[0][DUMMY][j][block_x], 8 * sizeof(int)); + } + + //===== restore reconstruction and prediction (needed if single coeffs are removed) ===== + copy_image_data_8x8(&p_Vid->enc_picture->imgY[pic_pix_y], p_RDO->rec8x8[0], pic_pix_x, 0); + copy_image_data_8x8(&mb_pred[block_y], currSlice->mpr_8x8[0][best_ipmode], block_x, 0); + + return nonzero; +} + +/*! + ************************************************************************************* + * \brief + * Mode Decision for an 8x8 Intra block + ************************************************************************************* + */ +int Mode_Decision_for_IntraSubMBlocks(Macroblock *currMB, int b8, int lambda, distblk *cost, int non_zero[3]) +{ + Slice *currSlice = currMB->p_Slice; + int b4; + distblk cost4x4; + currMB->cr_cbp[0] = 0; + currMB->cr_cbp[1] = 0; + currMB->cr_cbp[2] = 0; + + memset(non_zero, 0, 3 * sizeof(int)); + *cost = weighted_cost(lambda, 6); //6 * lambda; + for (b4=0; b4<4; b4++) + { + non_zero[0] |= currSlice->mode_decision_for_I4x4_blocks (currMB, b8, b4, lambda, &cost4x4); + non_zero[1] |= currMB->cr_cbp[1]; + non_zero[2] |= currMB->cr_cbp[2]; + *cost += cost4x4; + } + + return non_zero[0]; +} + +/*! + ************************************************************************************* + * \brief + * 4x4 Intra mode decision for an macroblock + ************************************************************************************* + */ +int mode_decision_for_I4x4_MB (Macroblock *currMB, int lambda, distblk* cost) +{ + Slice *currSlice = currMB->p_Slice; + int cbp=0, b8; + distblk cost8x8; + int non_zero[3] = {0, 0, 0}; + + currSlice->cmp_cbp[1] = currSlice->cmp_cbp[2] = 0; + + for (*cost=0, b8=0; b8<4; b8++) + { + if (Mode_Decision_for_IntraSubMBlocks (currMB, b8, lambda, &cost8x8, non_zero)) + { + cbp |= (1<cmp_cbp[1] |= (1<cmp_cbp[1]; + currSlice->cmp_cbp[1] = cbp; + currSlice->cmp_cbp[2] = cbp; + } + if (non_zero[2]) + { + currSlice->cmp_cbp[2] |= (1<cmp_cbp[2]; + currSlice->cmp_cbp[1] = cbp; + currSlice->cmp_cbp[2] = cbp; + } + } + return cbp; +} + +int find_best_mode_I16x16_MB (Macroblock *currMB, int lambda, distblk min_cost) +{ + Slice *currSlice = currMB->p_Slice; + currMB->luma_transform_size_8x8_flag = FALSE; + return (int) currSlice->find_sad_16x16 (currMB); +} + +/*! +************************************************************************************* +* \brief +* Intra 16x16 mode decision +************************************************************************************* +*/ +int mode_decision_for_I16x16_MB (Macroblock* currMB, int lambda) +{ + find_best_mode_I16x16_MB (currMB, lambda, DISTBLK_MAX); + return currMB->residual_transform_quant_luma_16x16 (currMB, PLANE_Y); +} + + +/************************************************************************************ +* \brief +* Intra 16x16 mode decision using Rate-Distortion Optimization +************************************************************************************* +*/ +int mode_decision_for_I16x16_MB_RDO (Macroblock* currMB, int lambda) +{ + VideoParameters *p_Vid = currMB->p_Vid; + InputParameters *p_Inp = currMB->p_Inp; + Slice *currSlice = currMB->p_Slice; + + SyntaxElement se; + const int* partMap = assignSE2partition[currSlice->partition_mode]; + DataPartition* dataPart = &(currSlice->partArr[partMap[SE_MBTYPE]]); + + distblk min_rdcost = DISTBLK_MAX, rdcost; + distblk distortionY; + int rate; + int i,k; + int b8, b4; + + + int up_avail, left_avail, left_up_avail; + + int best_mode = 0, best_cbp = 0; + int bestCofAC[4][4][2][65]; + int bestCofDC[2][18]; + imgpel bestRec[MB_BLOCK_SIZE][MB_BLOCK_SIZE]; + + currMB->mb_type = I16MB; + + currSlice->set_intrapred_16x16(currMB, PLANE_Y, &left_avail, &up_avail, &left_up_avail); + + for (k = 0;k < 4; k++) + { + if (p_Inp->IntraDisableInterOnly == 0 || (currSlice->slice_type != I_SLICE && currSlice->slice_type != SI_SLICE)) + { + if (p_Inp->Intra16x16ParDisable && (k==VERT_PRED_16||k==HOR_PRED_16)) + continue; + + if (p_Inp->Intra16x16PlaneDisable && k==PLANE_16) + continue; + } + + if ((k==0 && !up_avail) || (k==1 && !left_avail) || (k==3 && (!left_avail || !up_avail || !left_up_avail))) + continue; + + get_intrapred_16x16(currMB, PLANE_Y, k, left_avail, up_avail); + + currMB->i16mode = (char) k; + currMB->cbp = currMB->residual_transform_quant_luma_16x16(currMB, PLANE_Y); + distortionY = compute_SSE16x16_thres(&p_Vid->pCurImg[currMB->opix_y], &p_Vid->enc_picture->p_curr_img[currMB->pix_y], currMB->pix_x, currMB->pix_x, min_rdcost); + + if (distortionY < min_rdcost - weighted_cost(lambda, 4)) + { + currSlice->store_coding_state (currMB, currSlice->p_RDO->cs_tmp); + currMB->i16offset = I16Offset (currMB->cbp, currMB->i16mode); + se.value1 = MBType2Value (currMB); + se.value2 = 0; + se.type = SE_MBTYPE; + + currSlice->writeMB_typeInfo (currMB, &se, dataPart); + + rate = se.len; + if (distortionY + weighted_cost(lambda, rate) < min_rdcost) + { + rate += currSlice->writeCoeff16x16 (currMB, PLANE_Y); + currSlice->reset_coding_state (currMB, currSlice->p_RDO->cs_tmp); + + rdcost = distortionY + weighted_cost(lambda, rate); + + if(rdcost < min_rdcost) + { + min_rdcost = rdcost; + best_mode = k; + best_cbp = currMB->cbp; + for(b8 = 0; b8 < 4; b8++) + { + for(b4 = 0; b4 < 4; b4++) + { + memcpy(bestCofAC[b8][b4][0], currSlice->cofAC[b8][b4][0], 2 * sizeof(int) * 65); + memcpy(bestCofAC[b8][b4][1], currSlice->cofAC[b8][b4][1], sizeof(int) * 65); + } + } + + memcpy(bestCofDC[0], currSlice->cofDC[0][0], sizeof(int)*18); + memcpy(bestCofDC[1], currSlice->cofDC[0][1], sizeof(int)*18); + + for(i = 0; i < MB_BLOCK_SIZE; i++) + memcpy(bestRec[i], &p_Vid->enc_picture->p_curr_img[currMB->pix_y + i][currMB->pix_x], sizeof(imgpel)*MB_BLOCK_SIZE); + } + } + else + { + currSlice->reset_coding_state (currMB, currSlice->p_RDO->cs_tmp); + } + } + } + + currMB->i16mode = (char) best_mode; + currMB->cbp = best_cbp; + currMB->i16offset = I16Offset (currMB->cbp, currMB->i16mode); + + for(b8 = 0; b8 < 4; b8++) + { + for(b4 = 0; b4 < 4; b4++) + { + memcpy(currSlice->cofAC[b8][b4][0], bestCofAC[b8][b4][0], sizeof(int)*65); + memcpy(currSlice->cofAC[b8][b4][1], bestCofAC[b8][b4][1], sizeof(int)*65); + } + } + + memcpy(currSlice->cofDC[0][0], bestCofDC[0], sizeof(int)*18); + memcpy(currSlice->cofDC[0][1], bestCofDC[1], sizeof(int)*18); + + for(i = 0; i < MB_BLOCK_SIZE; i++) + { + memcpy(&p_Vid->enc_picture->p_curr_img[currMB->pix_y+i][currMB->pix_x], bestRec[i], MB_BLOCK_SIZE*sizeof(imgpel)); + } + + return currMB->cbp; +} + + diff --git a/lencod/src/rd_intra_jm444.c b/lencod/src/rd_intra_jm444.c new file mode 100644 index 0000000..c93dfa0 --- /dev/null +++ b/lencod/src/rd_intra_jm444.c @@ -0,0 +1,468 @@ +/*! + *************************************************************************** + * \file rd_intra_jm444.c + * + * \brief + * Rate-Distortion optimized mode decision + * + * \author + * - Heiko Schwarz + * - Valeri George + * - Lowell Winger + * - Alexis Michael Tourapis + * \date + * 12. April 2001 + ************************************************************************** + */ + +#include + +#include "global.h" + +#include "image.h" +#include "macroblock.h" +#include "mb_access.h" +#include "rdopt_coding_state.h" +#include "mode_decision.h" +#include "rdopt.h" +#include "rd_intra_jm.h" +#include "q_around.h" +#include "intra4x4.h" +#include "rd_intra_jm.h" +#include "blk_prediction.h" + +/*! + ************************************************************************************* + * \brief + * Mode Decision for an 4x4 Intra block + ************************************************************************************* + */ +int mode_decision_for_I4x4_blocks_JM_High444 (Macroblock *currMB, int b8, int b4, int lambda, distblk* min_cost) +{ + VideoParameters *p_Vid = currMB->p_Vid; + InputParameters *p_Inp = currMB->p_Inp; + Slice *currSlice = currMB->p_Slice; + RDOPTStructure *p_RDO = currSlice->p_RDO; + + int ipmode, best_ipmode = 0, y, dummy; + int c_nz, nonzero = 0; + int* ACLevel = currSlice->cofAC[b8][b4][0]; + int* ACRun = currSlice->cofAC[b8][b4][1]; + int uv; + distblk rdcost = 0; + distblk min_rdcost = DISTBLK_MAX; + int block_x = ((b8 & 0x01) << 3) + ((b4 & 0x01) << 2); + int block_y = ((b8 >> 1) << 3) + ((b4 >> 1) << 2); + int pic_pix_x = currMB->pix_x + block_x; + int pic_pix_y = currMB->pix_y + block_y; + int pic_opix_x = currMB->pix_x + block_x; + int pic_opix_y = currMB->opix_y + block_y; + int pic_block_x = pic_pix_x >> 2; + int pic_block_y = pic_pix_y >> 2; + + int left_available, up_available, all_available; + int *mb_size = p_Vid->mb_size[IS_LUMA]; + + char upMode, leftMode; + int mostProbableMode; + + PixelPos left_block, top_block; + + int lrec4x4[4][4]; + int best_nz_coeff = 0; + int block_x4 = block_x>>2; + int block_y4 = block_y>>2; + +#ifdef BEST_NZ_COEFF + int best_coded_block_flag = 0; + int bit_pos = 1 + ((((b8>>1)<<1)+(b4>>1))<<2) + (((b8&1)<<1)+(b4&1)); + int64 cbp_bits; + + if (b8==0 && b4==0) + cbp_bits = 0; +#endif + + get4x4Neighbour(currMB, block_x - 1, block_y , mb_size, &left_block); + get4x4Neighbour(currMB, block_x, block_y - 1, mb_size, &top_block ); + + // constrained intra pred + if (p_Inp->UseConstrainedIntraPred) + { + left_block.available = left_block.available ? p_Vid->intra_block[left_block.mb_addr] : 0; + top_block.available = top_block.available ? p_Vid->intra_block[top_block.mb_addr] : 0; + } + + upMode = top_block.available ? p_Vid->ipredmode[top_block.pos_y ][top_block.pos_x ] : -1; + leftMode = left_block.available ? p_Vid->ipredmode[left_block.pos_y][left_block.pos_x] : -1; + + mostProbableMode = (upMode < 0 || leftMode < 0) ? DC_PRED : upMode < leftMode ? upMode : leftMode; + *min_cost = DISTBLK_MAX; + currMB->ipmode_DPCM = NO_INTRA_PMODE; ////For residual DPCM + + //===== INTRA PREDICTION FOR 4x4 BLOCK ===== + // set intra prediction values for 4x4 intra prediction + currSlice->set_intrapred_4x4(currMB, PLANE_Y, pic_pix_x, pic_pix_y, &left_available, &up_available, &all_available); + + if (currSlice->P444_joined) + { + select_plane(p_Vid, PLANE_U); + currSlice->set_intrapred_4x4(currMB, PLANE_U, pic_pix_x, pic_pix_y, &left_available, &up_available, &all_available); + select_plane(p_Vid, PLANE_V); + currSlice->set_intrapred_4x4(currMB, PLANE_V, pic_pix_x, pic_pix_y, &left_available, &up_available, &all_available); + select_plane(p_Vid, PLANE_Y); + } + + //===== LOOP OVER ALL 4x4 INTRA PREDICTION MODES ===== + for (ipmode = 0; ipmode < NO_INTRA_PMODE; ipmode++) + { + int available_mode = (all_available) || (ipmode==DC_PRED) || + (up_available && (ipmode==VERT_PRED||ipmode==VERT_LEFT_PRED||ipmode==DIAG_DOWN_LEFT_PRED)) || + (left_available && (ipmode==HOR_PRED||ipmode==HOR_UP_PRED)); + + if (valid_intra_mode(currSlice, ipmode) == 0) + continue; + + if( available_mode) + { + // generate intra 4x4 prediction block given availability + get_intrapred_4x4(currMB, PLANE_Y, ipmode, block_x, block_y, left_available, up_available); + + // get prediction and prediction error + generate_pred_error_4x4(&p_Vid->pCurImg[pic_opix_y], currSlice->mpr_4x4[0][ipmode], &currSlice->mb_pred[0][block_y], &currSlice->mb_ores[0][block_y], pic_opix_x, block_x); + + if (p_Vid->yuv_format == YUV444) + { + currMB->ipmode_DPCM = (short) ipmode; + if ((p_Inp->separate_colour_plane_flag == 0)) + { + // generate intra 4x4 prediction block given availability + get_intrapred_4x4(currMB, PLANE_U, ipmode, block_x, block_y, left_available, up_available); + generate_pred_error_4x4(&p_Vid->pImgOrg[1][pic_opix_y], currSlice->mpr_4x4[1][ipmode], &currSlice->mb_pred[1][block_y], &currSlice->mb_ores[1][block_y], pic_opix_x, block_x); + // generate intra 4x4 prediction block given availability + get_intrapred_4x4(currMB, PLANE_V, ipmode, block_x, block_y, left_available, up_available); + generate_pred_error_4x4(&p_Vid->pImgOrg[2][pic_opix_y], currSlice->mpr_4x4[2][ipmode], &currSlice->mb_pred[2][block_y], &currSlice->mb_ores[2][block_y], pic_opix_x, block_x); + } + } + + // get and check rate-distortion cost +#ifdef BEST_NZ_COEFF + currMB->cbp_bits[0] = cbp_bits; +#endif + + rdcost = currSlice->rdcost_for_4x4_intra_blocks (currMB, &c_nz, b8, b4, ipmode, lambda, mostProbableMode, min_rdcost); + if ((rdcost < min_rdcost) || (rdcost == min_rdcost && ipmode == mostProbableMode)) + { + //--- set coefficients --- + memcpy(p_RDO->cofAC4x4[0], ACLevel, 18 * sizeof(int)); + memcpy(p_RDO->cofAC4x4[1], ACRun, 18 * sizeof(int)); + + //--- set reconstruction --- + copy_4x4block(p_RDO->rec4x4[PLANE_Y], &p_Vid->enc_picture->imgY[pic_pix_y], 0, pic_pix_x); + + // SP/SI reconstruction + if(currSlice->slice_type == SP_SLICE && !currSlice->sp2_frame_indicator) + { + for (y=0; y<4; y++) + { + memcpy(lrec4x4[y],&p_Vid->lrec[pic_pix_y+y][pic_pix_x], BLOCK_SIZE * sizeof(int));// stores the mode coefficients + } + } + + if(currSlice->P444_joined) + { + //--- set coefficients --- + for (uv=0; uv < 2; uv++) + { + memcpy(p_RDO->cofAC4x4CbCr[uv][0],currSlice->cofAC[b8+4+uv*4][b4][0], 18 * sizeof(int)); + memcpy(p_RDO->cofAC4x4CbCr[uv][1],currSlice->cofAC[b8+4+uv*4][b4][1], 18 * sizeof(int)); + currMB->cr_cbp[uv + 1] = currMB->c_nzCbCr[uv + 1]; + + //--- set reconstruction --- + copy_4x4block(p_RDO->rec4x4[uv + 1], &p_Vid->enc_picture->imgUV[uv][pic_pix_y], 0, pic_pix_x); + } + } + //--- flag if transform coefficients must be coded --- + nonzero = c_nz; + + //--- set best mode update minimum cost --- + *min_cost = rdcost; + min_rdcost = rdcost; + best_ipmode = ipmode; + + best_nz_coeff = p_Vid->nz_coeff [currMB->mbAddrX][block_x4][block_y4]; +#ifdef BEST_NZ_COEFF + best_coded_block_flag = (int)((currMB->cbp_bits[0] >> bit_pos)&(int64)(1)); +#endif + if (p_Vid->AdaptiveRounding) + { + store_adaptive_rounding_4x4 (p_Vid, p_Vid->ARCofAdj4x4, I4MB, block_y, block_x); + } + } + } + } +#if INTRA_RDCOSTCALC_NNZ + p_Vid->nz_coeff [currMB->mbAddrX][block_x4][block_y4] = best_nz_coeff; +#endif +#ifdef BEST_NZ_COEFF + cbp_bits &= (~(int64)(1<ipredmode[pic_block_y][pic_block_x] = (char) best_ipmode; + currMB->intra_pred_modes[4*b8+b4] = + (char) (mostProbableMode == best_ipmode ? -1 : (best_ipmode < mostProbableMode ? best_ipmode : best_ipmode-1)); + + if(currSlice->P444_joined) + { + ColorPlane k; + for (k = PLANE_U; k <= PLANE_V; k++) + { + select_plane(p_Vid, k); + + copy_4x4block(&currSlice->mb_pred[k][block_y], currSlice->mpr_4x4[k][best_ipmode], block_x, 0); + /* + for (j=0; j<4; j++) + { + for (i=0; i<4; i++) + { + currSlice->mb_ores[k][block_y+j][block_x+i] = p_Vid->pImgOrg[k][currMB->pix_y+block_y+j][currMB->pix_x+block_x+i] - currSlice->mpr_4x4[k][best_ipmode][j][i]; + } + } + */ + compute_residue(&(p_Vid->pImgOrg[k][currMB->pix_y+block_y]), &currSlice->mb_pred[k][block_y], &currSlice->mb_ores[k][block_y], block_x, currMB->pix_x+block_x, 4, 4); + currMB->cr_cbp[k] = currMB->residual_transform_quant_luma_4x4(currMB, k, block_x,block_y,&dummy,1); + } + select_plane(p_Vid, PLANE_Y); + } + + //===== restore coefficients ===== + memcpy (ACLevel, p_RDO->cofAC4x4[0], 18 * sizeof(int)); + memcpy (ACRun, p_RDO->cofAC4x4[1], 18 * sizeof(int)); + + //===== restore reconstruction and prediction (needed if single coeffs are removed) ===== + copy_4x4block(&p_Vid->enc_picture->imgY[pic_pix_y], p_RDO->rec4x4[PLANE_Y], pic_pix_x, 0); + copy_4x4block(&currSlice->mb_pred[0][block_y], currSlice->mpr_4x4[0][best_ipmode], block_x, 0); + + // SP/SI reconstuction + if(currSlice->slice_type == SP_SLICE && !p_Vid->sp2_frame_indicator) + { + for (y=0; ylrec[pic_pix_y+y][pic_pix_x], lrec4x4[y], BLOCK_SIZE * sizeof(int));//restore coefficients when encoding primary SP frame + } + } + if (currSlice->P444_joined) + { + for (uv=0; uv < 2; uv++ ) + { + //===== restore coefficients ===== + memcpy(currSlice->cofAC[b8+4+uv*4][b4][0], p_RDO->cofAC4x4CbCr[uv][0], 18 * sizeof(int)); + memcpy(currSlice->cofAC[b8+4+uv*4][b4][1], p_RDO->cofAC4x4CbCr[uv][1], 18 * sizeof(int)); + //===== restore reconstruction and prediction (needed if single coeffs are removed) ===== + copy_4x4block(&p_Vid->enc_picture->imgUV[uv][pic_pix_y], p_RDO->rec4x4[uv + 1], pic_pix_x, 0); + copy_4x4block(&currSlice->mb_pred[uv + 1][block_y], currSlice->mpr_4x4[uv + 1][best_ipmode], block_x, 0); + } + } + + if (p_Vid->AdaptiveRounding) + { + update_adaptive_rounding_4x4 (p_Vid,p_Vid->ARCofAdj4x4, I4MB, block_y, block_x); + } + + return nonzero; +} + +/*! + ************************************************************************************* + * \brief + * Mode Decision for an 4x4 Intra block + ************************************************************************************* + */ +int mode_decision_for_I4x4_blocks_JM_Low444 (Macroblock *currMB, int b8, int b4, int lambda, distblk* min_cost) +{ + VideoParameters *p_Vid = currMB->p_Vid; + InputParameters *p_Inp = currMB->p_Inp; + Slice *currSlice = currMB->p_Slice; + + int ipmode, best_ipmode = 0, dummy; + distblk cost; + int nonzero = 0; + + int block_x = ((b8 & 0x01) << 3) + ((b4 & 0x01) << 2); + int block_y = ((b8 >> 1) << 3) + ((b4 >> 1) << 2); + int pic_pix_x = currMB->pix_x + block_x; + int pic_pix_y = currMB->pix_y + block_y; + int pic_opix_x = currMB->pix_x + block_x; + int pic_opix_y = currMB->opix_y + block_y; + int pic_block_x = pic_pix_x >> 2; + int pic_block_y = pic_pix_y >> 2; + + int left_available, up_available, all_available; + + char upMode, leftMode; + int mostProbableMode; + + PixelPos left_block; + PixelPos top_block; + + distblk fixedcost = weighted_cost(lambda, 4); //(int) floor(4 * lambda ); + int *mb_size = p_Vid->mb_size[IS_LUMA]; + int best_nz_coeff = 0; + int block_x4 = block_x>>2; + int block_y4 = block_y>>2; + +#ifdef BEST_NZ_COEFF + int best_coded_block_flag = 0; + int bit_pos = 1 + ((((b8>>1)<<1)+(b4>>1))<<2) + (((b8&1)<<1)+(b4&1)); + int64 cbp_bits; + + if (b8==0 && b4==0) + cbp_bits = 0; +#endif + + get4x4Neighbour(currMB, block_x - 1, block_y , mb_size, &left_block); + get4x4Neighbour(currMB, block_x, block_y - 1, mb_size, &top_block ); + + // constrained intra pred + if (p_Inp->UseConstrainedIntraPred) + { + left_block.available = left_block.available ? p_Vid->intra_block[left_block.mb_addr] : 0; + top_block.available = top_block.available ? p_Vid->intra_block[top_block.mb_addr] : 0; + } + + upMode = top_block.available ? p_Vid->ipredmode[top_block.pos_y ][top_block.pos_x ] : -1; + leftMode = left_block.available ? p_Vid->ipredmode[left_block.pos_y][left_block.pos_x] : -1; + + mostProbableMode = (upMode < 0 || leftMode < 0) ? DC_PRED : upMode < leftMode ? upMode : leftMode; + *min_cost = DISTBLK_MAX; + currMB->ipmode_DPCM = NO_INTRA_PMODE; ////For residual DPCM + + //===== INTRA PREDICTION FOR 4x4 BLOCK ===== + // set intra prediction values for 4x4 intra prediction + currSlice->set_intrapred_4x4(currMB, PLANE_Y, pic_pix_x, pic_pix_y, &left_available, &up_available, &all_available); + + if (currSlice->P444_joined) + { + select_plane(p_Vid, PLANE_U); + currSlice->set_intrapred_4x4(currMB, PLANE_U, pic_pix_x, pic_pix_y, &left_available, &up_available, &all_available); + select_plane(p_Vid, PLANE_V); + currSlice->set_intrapred_4x4(currMB, PLANE_V, pic_pix_x, pic_pix_y, &left_available, &up_available, &all_available); + select_plane(p_Vid, PLANE_Y); + } + + //===== LOOP OVER ALL 4x4 INTRA PREDICTION MODES ===== + for (ipmode = 0; ipmode < NO_INTRA_PMODE; ipmode++) + { + int available_mode = (all_available) || (ipmode==DC_PRED) || + (up_available && (ipmode==VERT_PRED||ipmode==VERT_LEFT_PRED||ipmode==DIAG_DOWN_LEFT_PRED)) || + (left_available && (ipmode==HOR_PRED||ipmode==HOR_UP_PRED)); + + if (valid_intra_mode(currSlice, ipmode) == 0) + continue; + + if( available_mode) + { + // generate intra 4x4 prediction block given availability + // Note that some checks may not be necessary internally since the "available_mode" test has already tested + // mode availability. + get_intrapred_4x4(currMB, PLANE_Y, ipmode, block_x, block_y, left_available, up_available); + cost = (ipmode == mostProbableMode) ? 0 : fixedcost; + cost += currSlice->compute_cost4x4(p_Vid, &p_Vid->pCurImg[pic_opix_y], currSlice->mpr_4x4[0][ipmode], pic_opix_x, *min_cost - cost); + if (currSlice->P444_joined) + { + get_intrapred_4x4(currMB, PLANE_U, ipmode, block_x, block_y, left_available, up_available); + cost += currSlice->compute_cost4x4(p_Vid, &p_Vid->pImgOrg[1][pic_opix_y], currSlice->mpr_4x4[1][ipmode], pic_opix_x, *min_cost - cost); + get_intrapred_4x4(currMB, PLANE_V, ipmode, block_x, block_y, left_available, up_available); + cost += currSlice->compute_cost4x4(p_Vid, &p_Vid->pImgOrg[2][pic_opix_y], currSlice->mpr_4x4[2][ipmode], pic_opix_x, *min_cost - cost); + } + + if (cost < *min_cost) + { + best_ipmode = ipmode; + *min_cost = cost; + } + } + } + +#if INTRA_RDCOSTCALC_NNZ + p_Vid->nz_coeff [currMB->mbAddrX][block_x4][block_y4] = best_nz_coeff; +#endif +#ifdef BEST_NZ_COEFF + cbp_bits &= (~(int64)(1<ipredmode[pic_block_y][pic_block_x] = (char) best_ipmode; + currMB->intra_pred_modes[4*b8+b4] = + (char) (mostProbableMode == best_ipmode ? -1 : (best_ipmode < mostProbableMode ? best_ipmode : best_ipmode-1)); + + // get prediction and prediction error + generate_pred_error_4x4(&p_Vid->pCurImg[pic_opix_y], currSlice->mpr_4x4[0][best_ipmode], &currSlice->mb_pred[0][block_y], &currSlice->mb_ores[0][block_y], pic_opix_x, block_x); + + currMB->ipmode_DPCM = (short) best_ipmode; + + select_transform(currMB); + nonzero = currMB->cr_cbp[0] = currMB->residual_transform_quant_luma_4x4 (currMB, PLANE_Y, block_x, block_y, &dummy, 1); + + if (currSlice->P444_joined) + { + ColorPlane k; + for (k = PLANE_U; k <= PLANE_V; k++) + { + select_plane(p_Vid, k); + /* + for (j=0; j<4; j++) + { + for (i=0; i<4; i++) + { + currSlice->mb_pred[k][block_y + j][block_x+i] = currSlice->mpr_4x4[k][best_ipmode][j][i]; + currSlice->mb_ores[k][block_y + j][block_x+i] = p_Vid->pImgOrg[k][pic_opix_y+j][pic_opix_x+i] - currSlice->mpr_4x4[k][best_ipmode][j][i]; + } + } + */ + copy_4x4block(&currSlice->mb_pred[k][block_y], currSlice->mpr_4x4[k][best_ipmode], block_x, 0); + compute_residue(&(p_Vid->pImgOrg[k][pic_opix_y]), &currSlice->mb_pred[k][block_y], &currSlice->mb_ores[k][block_y], block_x, pic_opix_x, 4, 4); + currMB->cr_cbp[k] = currMB->residual_transform_quant_luma_4x4 (currMB, k, block_x, block_y, &dummy, 1); + } + select_plane(p_Vid, PLANE_Y); + } + + return nonzero; +} + +/*! +************************************************************************************* +* \brief +* Intra 16x16 mode decision +************************************************************************************* +*/ +int mode_decision_for_I16x16_MB_444 (Macroblock* currMB, int lambda) +{ + Slice *currSlice = currMB->p_Slice; + int cbp; + find_best_mode_I16x16_MB(currMB, lambda, DISTBLK_MAX); + + if (!currSlice->P444_joined) + { + cbp = currMB->residual_transform_quant_luma_16x16 (currMB, PLANE_Y); + } + else + { + VideoParameters *p_Vid = currSlice->p_Vid; + + cbp = currMB->residual_transform_quant_luma_16x16 (currMB, PLANE_Y); + select_plane(p_Vid, PLANE_U); + currSlice->cmp_cbp[1] = currMB->residual_transform_quant_luma_16x16 (currMB, PLANE_U); + select_plane(p_Vid, PLANE_V); + currSlice->cmp_cbp[2] = currMB->residual_transform_quant_luma_16x16 (currMB, PLANE_V); + select_plane(p_Vid, PLANE_Y); + + cbp |= (currSlice->cmp_cbp[1] | currSlice->cmp_cbp[2]); + currSlice->cmp_cbp[1] = cbp; + currSlice->cmp_cbp[2] = cbp; + } + + return cbp; +} + diff --git a/lencod/src/rd_intra_jm_low.c b/lencod/src/rd_intra_jm_low.c new file mode 100644 index 0000000..e3ad363 --- /dev/null +++ b/lencod/src/rd_intra_jm_low.c @@ -0,0 +1,278 @@ +/*! + *************************************************************************** + * \file rd_intra_jm_low.c + * + * \brief + * Rate-Distortion optimized mode decision + * + * \author + * - Heiko Schwarz + * - Valeri George + * - Lowell Winger + * - Alexis Michael Tourapis + * \date + * 12. April 2001 + ************************************************************************** + */ + +#include + +#include "global.h" + +#include "image.h" +#include "macroblock.h" +#include "mb_access.h" +#include "rdopt_coding_state.h" +#include "mode_decision.h" +#include "rdopt.h" +#include "rd_intra_jm.h" +#include "q_around.h" +#include "intra4x4.h" +#include "intra8x8.h" + +/*! + ************************************************************************************* + * \brief + * Mode Decision for an 4x4 Intra block + ************************************************************************************* + */ +int mode_decision_for_I4x4_blocks_JM_Low (Macroblock *currMB, int b8, int b4, int lambda, distblk* min_cost) +{ + VideoParameters *p_Vid = currMB->p_Vid; + InputParameters *p_Inp = currMB->p_Inp; + Slice *currSlice = currMB->p_Slice; + + int ipmode, best_ipmode = 0, dummy; + int nonzero = 0; + distblk cost; + int block_x = ((b8 & 0x01) << 3) + ((b4 & 0x01) << 2); + int block_y = ((b8 >> 1) << 3) + ((b4 >> 1) << 2); + int pic_pix_x = currMB->pix_x + block_x; + int pic_pix_y = currMB->pix_y + block_y; + int pic_opix_y = currMB->opix_y + block_y; + + int left_available, up_available, all_available; + int *mb_size = p_Vid->mb_size[IS_LUMA]; + + char upMode, leftMode; + int mostProbableMode; + + PixelPos left_block, top_block; + + distblk fixedcost = weighted_cost(lambda, 4); //(int) floor(4 * lambda ); + distblk onecost = weighted_cost(lambda, 1); //(int) floor( lambda ); + + int best_nz_coeff = 0; + int block_x4 = block_x>>2; + int block_y4 = block_y>>2; + imgpel ***mpr4x4 = currSlice->mpr_4x4[0]; + imgpel **cur_img = &p_Vid->pCurImg[pic_opix_y]; + +#ifdef BEST_NZ_COEFF + int best_coded_block_flag = 0; + int bit_pos = 1 + ((((b8>>1)<<1)+(b4>>1))<<2) + (((b8&1)<<1)+(b4&1)); + int64 cbp_bits; + + if (b8==0 && b4==0) + cbp_bits = 0; +#endif + + get4x4Neighbour(currMB, block_x - 1, block_y , mb_size, &left_block); + get4x4Neighbour(currMB, block_x, block_y - 1, mb_size, &top_block ); + + // constrained intra pred + if (p_Inp->UseConstrainedIntraPred) + { + left_block.available = left_block.available ? p_Vid->intra_block[left_block.mb_addr] : 0; + top_block.available = top_block.available ? p_Vid->intra_block[top_block.mb_addr] : 0; + } + + upMode = top_block.available ? p_Vid->ipredmode[top_block.pos_y ][top_block.pos_x ] : (char) -1; + leftMode = left_block.available ? p_Vid->ipredmode[left_block.pos_y][left_block.pos_x] : (char) -1; + + mostProbableMode = (upMode < 0 || leftMode < 0) ? DC_PRED : upMode < leftMode ? upMode : leftMode; + *min_cost = DISTBLK_MAX; + + currMB->ipmode_DPCM = NO_INTRA_PMODE; ////For residual DPCM + + //===== INTRA PREDICTION FOR 4x4 BLOCK ===== + // set intra prediction values for 4x4 intra prediction + currSlice->set_intrapred_4x4(currMB, PLANE_Y, pic_pix_x, pic_pix_y, &left_available, &up_available, &all_available); + + //===== LOOP OVER ALL 4x4 INTRA PREDICTION MODES ===== + for (ipmode = 0; ipmode < NO_INTRA_PMODE; ipmode++) + { + int available_mode = (all_available) || (ipmode==DC_PRED) || + (up_available && (ipmode==VERT_PRED||ipmode==VERT_LEFT_PRED||ipmode==DIAG_DOWN_LEFT_PRED)) || + (left_available && (ipmode==HOR_PRED||ipmode==HOR_UP_PRED)); + + if( valid_intra_mode(currSlice, ipmode) != 0 && available_mode) + { + // generate intra 4x4 prediction block given availability + get_intrapred_4x4(currMB, PLANE_Y, ipmode, block_x, block_y, left_available, up_available); + + cost = (ipmode == mostProbableMode) ? onecost : fixedcost; + if (cost < *min_cost) + { + cost += currSlice->compute_cost4x4(p_Vid, cur_img, mpr4x4[ipmode], pic_pix_x, *min_cost - cost); + + if (cost < *min_cost) + { + best_ipmode = ipmode; + *min_cost = cost; + } + } + } + } + +#if INTRA_RDCOSTCALC_NNZ + p_Vid->nz_coeff [currMB->mbAddrX][block_x4][block_y4] = best_nz_coeff; +#endif +#ifdef BEST_NZ_COEFF + cbp_bits &= (~(int64)(1<ipredmode[pic_pix_y >> 2][pic_pix_x >> 2] = (char) best_ipmode; + currMB->intra_pred_modes[4*b8+b4] = (char) (mostProbableMode == best_ipmode ? -1 : (best_ipmode < mostProbableMode ? best_ipmode : best_ipmode - 1)); + + // get prediction and prediction error + generate_pred_error_4x4(cur_img, mpr4x4[best_ipmode], &currSlice->mb_pred[0][block_y], &currSlice->mb_ores[0][block_y], pic_pix_x, block_x); + + currMB->ipmode_DPCM = (short) best_ipmode; + + select_transform(currMB); + + if (currMB->mb_type == I4MB) + nonzero = currMB->cr_cbp[0] = residual_transform_quant_luma_4x4 (currMB, PLANE_Y, block_x, block_y, &dummy, 1); + else + nonzero = currMB->cr_cbp[0] = currMB->residual_transform_quant_luma_4x4 (currMB, PLANE_Y, block_x, block_y, &dummy, 1); + + return nonzero; +} + + +/*! + ************************************************************************************* + * \brief + * 8x8 Intra mode decision for a macroblock - Low complexity + ************************************************************************************* + */ +int mode_decision_for_I8x8_blocks_JM_Low (Macroblock *currMB, int b8, int lambda, distblk *min_cost) +{ + VideoParameters *p_Vid = currMB->p_Vid; + InputParameters *p_Inp = currMB->p_Inp; + Slice *currSlice = currMB->p_Slice; + + int ipmode, best_ipmode = 0, j, dummy; + distblk cost; + int nonzero = 0; + int block_x = (b8 & 0x01) << 3; + int block_y = (b8 >> 1) << 3; + int pic_pix_x = currMB->pix_x + block_x; + int pic_pix_y = currMB->pix_y + block_y; + int pic_opix_y = currMB->opix_y + block_y; + int mb_block_y = (currMB->block_y) + (block_y >> 2); + int mb_block_x = (currMB->block_x) + (block_x >> 2); + + int left_available, up_available, all_available; + int **mb_ores = currSlice->mb_ores[0]; + imgpel **mb_pred = currSlice->mb_pred[0]; + int *mb_size = p_Vid->mb_size[IS_LUMA]; + + char upMode; + char leftMode; + int mostProbableMode; + int fixedcost = (int) weighted_cost(lambda, 4); + int mprobcost = (int) weighted_cost(lambda, 1); + + PixelPos left_block, top_block; + + get4x4Neighbour(currMB, block_x - 1, block_y , mb_size, &left_block); + get4x4Neighbour(currMB, block_x, block_y - 1, mb_size, &top_block ); + + if (p_Inp->UseConstrainedIntraPred) + { + top_block.available = top_block.available ? p_Vid->intra_block [top_block.mb_addr] : 0; + left_block.available = left_block.available ? p_Vid->intra_block [left_block.mb_addr] : 0; + } + + if(b8 >> 1) + upMode = top_block.available ? p_Vid->ipredmode8x8[top_block.pos_y ][top_block.pos_x ] : -1; + else + upMode = top_block.available ? p_Vid->ipredmode [top_block.pos_y ][top_block.pos_x ] : -1; + + if(b8 & 0x01) + leftMode = left_block.available ? p_Vid->ipredmode8x8[left_block.pos_y][left_block.pos_x] : -1; + else + leftMode = left_block.available ? p_Vid->ipredmode[left_block.pos_y][left_block.pos_x] : -1; + + mostProbableMode = (upMode < 0 || leftMode < 0) ? DC_PRED : upMode < leftMode ? upMode : leftMode; + *min_cost = DISTBLK_MAX; + currMB->ipmode_DPCM = NO_INTRA_PMODE; //For residual DPCM + + //===== INTRA PREDICTION FOR 8x8 BLOCK ===== + currSlice->set_intrapred_8x8(currMB, PLANE_Y, pic_pix_x, pic_pix_y, &left_available, &up_available, &all_available); + + // first check most probable mode + ipmode = mostProbableMode; + { + if( (ipmode==DC_PRED) || + ((ipmode==VERT_PRED||ipmode==VERT_LEFT_PRED||ipmode==DIAG_DOWN_LEFT_PRED) && up_available ) || + ((ipmode==HOR_PRED||ipmode==HOR_UP_PRED) && left_available ) || + (all_available) ) + { + get_intrapred_8x8(currMB, PLANE_Y, ipmode, left_available, up_available); + cost = mprobcost; + cost += currSlice->compute_cost8x8(p_Vid, &p_Vid->pImgOrg[0][pic_opix_y], currSlice->mpr_8x8[0][ipmode], pic_pix_x, *min_cost - cost); + best_ipmode = mostProbableMode; + *min_cost = cost; + } + } + + //===== LOOP OVER ALL 8x8 INTRA PREDICTION MODES ===== + for (ipmode = 0; ipmode < NO_INTRA_PMODE; ipmode++) + { + if (ipmode != mostProbableMode) + { + if( (ipmode==DC_PRED) || + ((ipmode==VERT_PRED||ipmode==VERT_LEFT_PRED||ipmode==DIAG_DOWN_LEFT_PRED) && up_available ) || + ((ipmode==HOR_PRED||ipmode==HOR_UP_PRED) && left_available ) || + (all_available) ) + { + get_intrapred_8x8(currMB, PLANE_Y, ipmode, left_available, up_available); + cost = fixedcost; + + if (cost < *min_cost) + { + cost += currSlice->compute_cost8x8(p_Vid, &p_Vid->pImgOrg[0][pic_opix_y], currSlice->mpr_8x8[0][ipmode], pic_pix_x, *min_cost - cost); + if (cost < *min_cost) + { + best_ipmode = ipmode; + *min_cost = cost; + } + } + } + } + } + + //===== set intra mode prediction ===== + p_Vid->ipredmode8x8[pic_pix_y >> 2][pic_pix_x >> 2] = (char) best_ipmode; + currMB->ipmode_DPCM = (short) best_ipmode; //For residual DPCM + + currMB->intra_pred_modes8x8[4*b8] = (char)((mostProbableMode == best_ipmode) + ? -1 + : (best_ipmode < mostProbableMode ? best_ipmode : best_ipmode-1)); + + for(j = mb_block_y; j < mb_block_y + 2; j++) //loop 4x4s in the subblock for 8x8 prediction setting + memset(&p_Vid->ipredmode8x8[j][mb_block_x], best_ipmode, 2 * sizeof(char)); + + // get prediction and prediction error + generate_pred_error_8x8(&p_Vid->pCurImg[pic_opix_y], currSlice->mpr_8x8[0][best_ipmode], &mb_pred[block_y], &mb_ores[block_y], pic_pix_x, block_x); + currMB->ipmode_DPCM = (short) best_ipmode; + + nonzero = currMB->residual_transform_quant_luma_8x8 (currMB, PLANE_Y, b8, &dummy, 1); + return nonzero; +} + diff --git a/lencod/src/rdopt.c b/lencod/src/rdopt.c new file mode 100644 index 0000000..6ba9ddd --- /dev/null +++ b/lencod/src/rdopt.c @@ -0,0 +1,4081 @@ +/*! + *************************************************************************** + * \file rdopt.c + * + * \brief + * Rate-Distortion optimized mode decision + * + * \author + * - Heiko Schwarz + * - Valeri George + * - Lowell Winger + * - Alexis Michael Tourapis + * \date + * 12. April 2001 + ************************************************************************** + */ + +#include +#include + +#include "global.h" + +#include "rdopt.h" +#include "q_around.h" +#include "rdopt_coding_state.h" +#include "memalloc.h" +#include "mb_access.h" +#include "elements.h" +#include "intrarefresh.h" +#include "image.h" +#include "transform8x8.h" +#include "cabac.h" +#include "biariencode.h" +#include "slice.h" +#include "vlc.h" +#include "ratectl.h" // head file for rate control +#include "mode_decision.h" +#include "rd_intra_jm.h" +#include "rd_intra_jm444.h" +#include "macroblock.h" +#include "q_offsets.h" +#include "conformance.h" +#include "errdo.h" +#include "mv_search.h" +#include "md_common.h" +#include "md_distortion.h" +#include "intra16x16.h" + +#define FASTMODE 1 + +static void set_stored_macroblock_parameters (Macroblock *currMB); +static void set_stored_macroblock_parameters_sp (Macroblock *currMB); +static void set_stored_macroblock_parameters_mpass(Macroblock *currMB); +static void set_ref_and_motion_vectors_P_slice (Macroblock *currMB, PicMotionParams **motion, Info8x8 *pred, int); +static void set_ref_and_motion_vectors_B_slice (Macroblock *currMB, PicMotionParams **motion, Info8x8 *pred, int); + +static distblk compute_sad4x4_cost (VideoParameters *p_Vid, imgpel **cur_img, imgpel **prd_img, int pic_opix_x, distblk min_cost); +static distblk compute_sse4x4_cost (VideoParameters *p_Vid, imgpel **cur_img, imgpel **prd_img, int pic_opix_x, distblk min_cost); +static distblk compute_satd4x4_cost(VideoParameters *p_Vid, imgpel **cur_img, imgpel **prd_img, int pic_opix_x, distblk min_cost); +static distblk compute_comp4x4_cost(VideoParameters *p_Vid, imgpel **cur_img, imgpel **prd_img, int pic_opix_x, distblk min_cost); + +static distblk rdcost_for_4x4_intra_blocks (Macroblock *currMB, int* nonzero, int b8, int b4, int ipmode, int lambda, int mostProbableMode, distblk min_rdcost); +static distblk rdcost_for_4x4_intra_blocks_444 (Macroblock *currMB, int* nonzero, int b8, int b4, int ipmode, int lambda, int mostProbableMode, distblk min_rdcost); + +static inline void copy_motion_vectors_MB (Slice *currSlice, RD_DATA *rdopt) +{ + memcpy(&currSlice->all_mv [LIST_0][0][0][0][0], &rdopt->all_mv [LIST_0][0][0][0][0], 288 * currSlice->max_num_references * sizeof(MotionVector)); +} + +Info8x8 init_info_8x8_struct(void) +{ + Info8x8 i8x8; + i8x8.mode = 0; + i8x8.pdir = 0; + i8x8.ref[LIST_0] = 0; + i8x8.ref[LIST_1] = -1; + i8x8.bipred = 0; + + return i8x8; +} + +void alloc_rd8x8data (RD_8x8DATA *rd_data) +{ + get_mem2Dint(&rd_data->lrec, MB_BLOCK_SIZE, MB_BLOCK_SIZE); + get_mem2Dpel(&rd_data->mpr8x8, MB_BLOCK_SIZE, MB_BLOCK_SIZE); + get_mem3Dpel(&rd_data->mpr8x8CbCr, 2, MB_BLOCK_SIZE, MB_BLOCK_SIZE); + get_mem2Dpel(&rd_data->rec_mbY8x8, MB_BLOCK_SIZE, MB_BLOCK_SIZE); + get_mem3Dpel(&rd_data->rec_mb8x8_cr, 2, MB_BLOCK_SIZE, MB_BLOCK_SIZE); +} + +void free_rd8x8data (RD_8x8DATA *rd_data) +{ + free_mem3Dpel(rd_data->rec_mb8x8_cr); + free_mem2Dpel(rd_data->rec_mbY8x8); + free_mem3Dpel(rd_data->mpr8x8CbCr); + free_mem2Dpel(rd_data->mpr8x8); + free_mem2Dint(rd_data->lrec); +} + +/*! + ************************************************************************ + * \brief + * delete structure for RD-optimized mode decision + ************************************************************************ + */ +void clear_rdopt (Slice *currSlice) +{ + VideoParameters *p_Vid = currSlice->p_Vid; + InputParameters *p_Inp = currSlice->p_Inp; + RDOPTStructure *p_RDO = currSlice->p_RDO; + + free_mem_DCcoeff (p_RDO->cofDC); + free_mem_ACcoeff (p_RDO->cofAC); + free_mem_ACcoeff (p_RDO->cofAC4x4intern); + free_mem_ACcoeff_new(p_RDO->coefAC8x8); + free_mem_ACcoeff_new(p_RDO->coefAC8x8intra); + + if (p_Inp->Transform8x8Mode) + { + free_mem_ACcoeff_new(p_RDO->cofAC8x8ts); + } + + if (p_Vid->P444_joined) + { + free_mem_ACcoeff_new(p_RDO->cofAC4x4CbCrintern); + } + + // Should create new functions for free/alloc of RD_8x8DATA + free_rd8x8data(p_RDO->tr4x4); + free(p_RDO->tr4x4); + free_rd8x8data(p_RDO->tr8x8); + free(p_RDO->tr8x8); + + free_mem4Dmv(p_RDO->all_mv8x8); + + free_mem3Dpel(p_RDO->rec4x4); + free_mem3Dpel(p_RDO->rec8x8); + free_mem2Dpel(p_RDO->pred); + free_mem3Dpel(p_RDO->rec_mb); + + if (p_Inp->ProfileIDC == EXTENDED) + { + free_mem2Dint(p_RDO->lrec_rec); + free_mem3Dint(p_RDO->lrec_rec_uv); + } + + free_mem2D((byte **) p_RDO->l0_refframe); + free_mem2D((byte **) p_RDO->l1_refframe); + + // structure for saving the coding state + delete_coding_state (p_RDO->cs_mb); + delete_coding_state (p_RDO->cs_b8); + delete_coding_state (p_RDO->cs_cm); + delete_coding_state (p_RDO->cs_tmp); +} + +void setupDistCost(Slice *currSlice, InputParameters *p_Inp) +{ + switch(p_Inp->ModeDecisionMetric) + { + case ERROR_SAD: + currSlice->compute_cost4x4 = compute_sad4x4_cost; + currSlice->compute_cost8x8 = compute_sad8x8_cost; + currSlice->distI16x16 = distI16x16_sad; + break; + case ERROR_SSE: + currSlice->compute_cost4x4 = compute_sse4x4_cost; + currSlice->compute_cost8x8 = compute_sse8x8_cost; + currSlice->distI16x16 = distI16x16_sse; + break; + case ERROR_SATD: + currSlice->compute_cost4x4 = compute_satd4x4_cost; + currSlice->compute_cost8x8 = compute_satd8x8_cost; + currSlice->distI16x16 = distI16x16_satd; + break; + default: + currSlice->compute_cost4x4 = compute_comp4x4_cost; + currSlice->compute_cost8x8 = compute_comp8x8_cost; + currSlice->distI16x16 = distI16x16_satd; + break; + } +} + + +/*! + ************************************************************************ + * \brief + * create structure for RD-optimized mode decision + ************************************************************************ + */ +void init_rdopt (Slice *currSlice) +{ + VideoParameters *p_Vid = currSlice->p_Vid; + InputParameters *p_Inp = currSlice->p_Inp; + RDOPTStructure *p_RDO = currSlice->p_RDO; + + get_mem_DCcoeff (&p_RDO->cofDC); + get_mem_ACcoeff (p_Vid, &p_RDO->cofAC); + get_mem_ACcoeff (p_Vid, &p_RDO->cofAC4x4intern); + p_RDO->cofAC4x4 = p_RDO->cofAC4x4intern[0][0]; + get_mem_ACcoeff_new(&p_RDO->coefAC8x8, 3); + get_mem_ACcoeff_new(&p_RDO->coefAC8x8intra, 3); + + if (p_Inp->Transform8x8Mode) + { + get_mem_ACcoeff_new(&p_RDO->cofAC8x8ts, 3); + } + + if (((p_RDO->tr4x4) = (RD_8x8DATA *) calloc(1, sizeof(RD_8x8DATA)))==NULL) + no_mem_exit("init_rdopt: p_RDO->tr4x4"); + alloc_rd8x8data(p_RDO->tr4x4); + if (((p_RDO->tr8x8) = (RD_8x8DATA *) calloc(1, sizeof(RD_8x8DATA)))==NULL) + no_mem_exit("init_rdopt: p_RDO->tr4x4"); + alloc_rd8x8data(p_RDO->tr8x8); + + get_mem2Dpel(&p_RDO->pred, MB_BLOCK_SIZE, MB_BLOCK_SIZE); + get_mem3Dpel(&p_RDO->rec8x8, 3, BLOCK_SIZE_8x8, BLOCK_SIZE_8x8); + get_mem3Dpel(&p_RDO->rec4x4, 3, BLOCK_SIZE, BLOCK_SIZE); + get_mem3Dpel(&p_RDO->rec_mb, 3, MB_BLOCK_SIZE, MB_BLOCK_SIZE); + + if (p_Inp->ProfileIDC == EXTENDED) + { + get_mem2Dint(&p_RDO->lrec_rec, MB_BLOCK_SIZE, MB_BLOCK_SIZE); + get_mem3Dint(&p_RDO->lrec_rec_uv, 2, MB_BLOCK_SIZE, MB_BLOCK_SIZE); + } + + get_mem2D((byte ***) &p_RDO->l0_refframe, 4, 4); + get_mem2D((byte ***) &p_RDO->l1_refframe, 4, 4); + + get_mem4Dmv(&p_RDO->all_mv8x8, 2, 2, 4, 4); + + if (p_Vid->P444_joined) + { + get_mem_ACcoeff_new(&p_RDO->cofAC4x4CbCrintern, 2); + + p_RDO->cofAC4x4CbCr[0] = p_RDO->cofAC4x4CbCrintern[0][0][0]; + p_RDO->cofAC4x4CbCr[1] = p_RDO->cofAC4x4CbCrintern[0][1][0]; + } + + currSlice->set_lagrangian_multipliers = p_Inp->rdopt == 0 ? SetLagrangianMultipliersOff : SetLagrangianMultipliersOn; + + switch (p_Inp->rdopt) + { + case 0: + currSlice->encode_one_macroblock = encode_one_macroblock_low; + break; + case 1: + default: + currSlice->encode_one_macroblock = encode_one_macroblock_high; + break; + case 2: + currSlice->encode_one_macroblock = encode_one_macroblock_highfast; + break; + case 3: + currSlice->encode_one_macroblock = encode_one_macroblock_highloss; + break; + } + + if (currSlice->mb_aff_frame_flag || (currSlice->UseRDOQuant && currSlice->RDOQ_QP_Num > 1)) + currSlice->set_stored_mb_parameters = set_stored_macroblock_parameters_mpass; + else + { + if (currSlice->slice_type == SP_SLICE || currSlice->slice_type == SI_SLICE) + currSlice->set_stored_mb_parameters = set_stored_macroblock_parameters_sp; + else + currSlice->set_stored_mb_parameters = set_stored_macroblock_parameters; + } + + // structure for saving the coding state + p_RDO->cs_mb = create_coding_state (p_Inp); + p_RDO->cs_b8 = create_coding_state (p_Inp); + p_RDO->cs_cm = create_coding_state (p_Inp); + p_RDO->cs_tmp = create_coding_state (p_Inp); + if (p_Inp->CtxAdptLagrangeMult == 1) + { + p_Vid->mb16x16_cost = CALM_MF_FACTOR_THRESHOLD; + p_RDO->lambda_mf_factor = 1.0; + } + + currSlice->rdcost_for_4x4_intra_blocks = (p_Vid->yuv_format == YUV444) ? rdcost_for_4x4_intra_blocks_444 : rdcost_for_4x4_intra_blocks; + currSlice->rdcost_for_8x8_intra_blocks = (p_Vid->yuv_format == YUV444) ? rdcost_for_8x8_intra_blocks_444 : rdcost_for_8x8_intra_blocks; + + if (currSlice->mb_aff_frame_flag) + currSlice->intra_chroma_RD_decision = intra_chroma_RD_decision_mbaff; + else + currSlice->intra_chroma_RD_decision = intra_chroma_RD_decision; + if (p_Inp->rdopt == 0) + { + currSlice->mode_decision_for_I8x8_blocks = (p_Vid->yuv_format == YUV444) ? mode_decision_for_I8x8_blocks_JM_Low444 : mode_decision_for_I8x8_blocks_JM_Low; + currSlice->mode_decision_for_I4x4_blocks = (p_Vid->yuv_format == YUV444) ? mode_decision_for_I4x4_blocks_JM_Low444 : mode_decision_for_I4x4_blocks_JM_Low; + } + else + { + currSlice->mode_decision_for_I8x8_blocks = (p_Vid->yuv_format == YUV444) ? mode_decision_for_I8x8_blocks_JM_High444 : mode_decision_for_I8x8_blocks_JM_High; + currSlice->mode_decision_for_I4x4_blocks = (p_Vid->yuv_format == YUV444) ? mode_decision_for_I4x4_blocks_JM_High444 : mode_decision_for_I4x4_blocks_JM_High; + } + + currSlice->find_sad_16x16 = find_sad_16x16_JM; + + if (currSlice->slice_type == B_SLICE) + currSlice->set_ref_and_motion_vectors = set_ref_and_motion_vectors_B_slice; + else + currSlice->set_ref_and_motion_vectors = set_ref_and_motion_vectors_P_slice; + + setupDistortion(currSlice); + setupDistCost (currSlice, p_Inp); +} + +/*! + ************************************************************************************* + * \brief + * Updates the pixel map that shows, which reference frames are reliable for + * each MB-area of the picture. + * + * \note + * The new values of the p_Vid->pixel_map are taken from the temporary buffer p_Vid->refresh_map + * + ************************************************************************************* + */ +void UpdatePixelMap(VideoParameters *p_Vid, InputParameters *p_Inp) +{ + int mx,my,y,x,i,j; + if (p_Vid->type==I_SLICE) + { + memset(p_Vid->pixel_map, 1, p_Vid->height * p_Vid->width * sizeof(byte)); + } + else + { + for (my=0; myheight >> 3; my++) + { + for (mx=0; mxwidth >> 3; mx++) + { + j = my*8 + 8; + i = mx*8 + 8; + if (p_Vid->refresh_map[my][mx]) + { + for (y=my*8; ypixel_map[y][mx*8], 1, 8 * sizeof(byte)); + } + else + { + for (y=my*8; ypixel_map[y][x] = (byte) imin(p_Vid->pixel_map[y][x] + 1, p_Inp->num_ref_frames+1); + } + } + } + } + } + } +} + +/*! + ************************************************************************************* + * \brief + * Checks if a given reference frame is reliable for the current + * macroblock, given the motion vectors that the motion search has + * returned. + * + * \return + * If the return value is 1, the reference frame is reliable. If it + * is 0, then it is not reliable. + * + * \note + * A specific area in each reference frame is assumed to be unreliable + * if the same area has been intra-refreshed in a subsequent frame. + * The information about intra-refreshed areas is kept in the p_Vid->pixel_map. + * + ************************************************************************************* + */ +int CheckReliabilityOfRef (Macroblock *currMB, int block, int list_idx, int ref, int mode) +{ + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + + int y,x, block_y, block_x, dy, dx, y_pos, x_pos, yy, xx, pres_x, pres_y; + int maxold_x = p_Vid->width - 1; + int maxold_y = p_Vid->height - 1; + int ref_frame = ref + 1; + + int by0 = (mode>=4?2*(block >> 1):mode==2?2*block:0); + int by1 = by0 + (mode>=4||mode==2?2:4); + int bx0 = (mode>=4?2*(block & 0x01):mode==3?2*block:0); + int bx1 = bx0 + (mode>=4||mode==3?2:4); + + for (block_y=by0; block_yall_mv[list_idx][ref][mode][block_y][block_x].mv_y; + y_pos += (currMB->block_y + block_y) * BLOCK_SIZE * 4; + x_pos = currSlice->all_mv[list_idx][ref][mode][block_y][block_x].mv_x; + x_pos += (currMB->block_x + block_x) * BLOCK_SIZE * 4; + + /* Here we specify which pixels of the reference frame influence + the reference values and check their reliability. This is + based on the function Get_Reference_Pixel */ + + dy = y_pos & 3; + dx = x_pos & 3; + + y_pos = (y_pos - dy) >> 2; + x_pos = (x_pos - dx) >> 2; + + if (dy==0 && dx==0) //full-pel + { + for (y=y_pos ; y < y_pos + BLOCK_SIZE ; y++) + for (x=x_pos ; x < x_pos + BLOCK_SIZE ; x++) + if (p_Vid->pixel_map[iClip3(0,maxold_y,y)][iClip3(0,maxold_x,x)] < ref_frame) + return 0; + } + else /* other positions */ + { + if (dy == 0) + { + for (y = y_pos ; y < y_pos + BLOCK_SIZE ; y++) + { + pres_y = iClip3(0, maxold_y, y); + for (x = x_pos ; x < x_pos + BLOCK_SIZE ; x++) + { + for(xx = -2 ; xx < 4 ; xx++) + { + pres_x = iClip3(0, maxold_x, x + xx); + if (p_Vid->pixel_map[pres_y][pres_x] < ref_frame) + return 0; + } + } + } + } + else if (dx == 0) + { + for (y = y_pos ; y < y_pos + BLOCK_SIZE ; y++) + for (x = x_pos ; x < x_pos + BLOCK_SIZE ; x++) + { + pres_x = iClip3(0,maxold_x,x); + for(yy=-2;yy<4;yy++) + { + pres_y = iClip3(0,maxold_y, yy + y); + if (p_Vid->pixel_map[pres_y][pres_x] < ref_frame) + return 0; + } + } + } + else if (dx == 2) + { + for (y = y_pos ; y < y_pos + BLOCK_SIZE ; y++) + for (x = x_pos ; x < x_pos + BLOCK_SIZE ; x++) + { + for(yy=-2;yy<4;yy++) + { + pres_y = iClip3(0,maxold_y, yy + y); + for(xx=-2;xx<4;xx++) + { + pres_x = iClip3(0,maxold_x, xx + x); + if (p_Vid->pixel_map[pres_y][pres_x] < ref_frame) + return 0; + } + } + } + } + else if (dy == 2) + { + for (y = y_pos ; y < y_pos + BLOCK_SIZE ; y++) + for (x = x_pos ; x < x_pos + BLOCK_SIZE ; x++) + { + for(xx=-2;xx<4;xx++) + { + pres_x = iClip3(0,maxold_x, xx + x); + for(yy=-2;yy<4;yy++) + { + pres_y = iClip3(0,maxold_y, yy + y); + if (p_Vid->pixel_map[pres_y][pres_x] < ref_frame) + return 0; + } + } + } + } + else + { + for (y = y_pos ; y < y_pos + BLOCK_SIZE ; y++) + { + for (x = x_pos ; x < x_pos + BLOCK_SIZE ; x++) + { + pres_y = dy == 1 ? y : y + 1; + pres_y = iClip3(0,maxold_y,pres_y); + + for(xx = -2; xx < 4; xx++) + { + pres_x = iClip3(0,maxold_x,xx + x); + if (p_Vid->pixel_map[pres_y][pres_x] < ref_frame) + return 0; + } + + pres_x = dx == 1 ? x : x + 1; + pres_x = iClip3(0,maxold_x,pres_x); + + for(yy=-2;yy<4;yy++) + { + pres_y = iClip3(0,maxold_y, yy + y); + if (p_Vid->pixel_map[pres_y][pres_x] < ref_frame) + return 0; + } + } + } + } + } + } + } + return 1; +} + +/*! + ************************************************************************************* + * \brief + * R-D Cost for an 4x4 Intra block + ************************************************************************************* + */ +static distblk rdcost_for_4x4_intra_blocks (Macroblock *currMB, + int* nonzero, + int b8, + int b4, + int ipmode, + int lambda, + int mostProbableMode, + distblk min_rdcost) +{ + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + distblk rdcost; + int dummy = 0, rate; + distblk distortion = 0; + int block_x = ((b8 & 0x01) << 3) + ((b4 & 0x01) << 2); + int block_y = ((b8 >> 1) << 3) + ((b4 >> 1) << 2); + int pic_pix_x = currMB->pix_x + block_x; + int pic_pix_y = currMB->pix_y + block_y; + int pic_opix_y = currMB->opix_y + block_y; + + SyntaxElement se; + const int *partMap = assignSE2partition[currSlice->partition_mode]; + //--- choose data partition --- + DataPartition *dataPart = &(currSlice->partArr[partMap[SE_INTRAPREDMODE]]); + + //===== perform forward transform, Q, IQ, inverse transform, Reconstruction ===== + //select_transform(currMB); + + currMB->ipmode_DPCM = (short) ipmode; + *nonzero = currMB->residual_transform_quant_luma_4x4 (currMB, PLANE_Y, block_x, block_y, &dummy, 1); + + //===== get distortion (SSD) of 4x4 block ===== + distortion += compute_SSE4x4(&p_Vid->pCurImg[pic_opix_y], &p_Vid->enc_picture->imgY[pic_pix_y], pic_pix_x, pic_pix_x); +#if INTRA_RDCOSTCALC_ET + // check if already distortion larger than min_rdcost + if (distortion >= min_rdcost) + { + return (distortion); + } +#endif + currMB->ipmode_DPCM = NO_INTRA_PMODE; + + //===== RATE for INTRA PREDICTION MODE (SYMBOL MODE MUST BE SET TO CAVLC) ===== + se.value1 = (mostProbableMode == ipmode) ? -1 : ipmode < mostProbableMode ? ipmode : ipmode - 1; + + //--- set position and type --- + se.context = (b8 << 2) + b4; + se.type = SE_INTRAPREDMODE; + + //--- encode and update rate --- + currSlice->writeIntraPredMode (&se, dataPart); + rate = se.len; + + //===== RATE for LUMINANCE COEFFICIENTS ===== + if (currSlice->symbol_mode == CAVLC) + { + rate += currSlice->writeCoeff4x4_CAVLC (currMB, LUMA, b8, b4, 0); + } + else + { + rate += writeCoeff4x4_CABAC (currMB, PLANE_Y, b8, b4, 1); + } + + rdcost = distortion + weighted_cost(lambda, rate); //((distblk)lambda) * rate; + + currSlice->reset_coding_state (currMB, currSlice->p_RDO->cs_cm); + + return rdcost; +} + + +/*! + ************************************************************************************* + * \brief + * R-D Cost for an 4x4 Intra block (for 4:4:4 formats) + ************************************************************************************* + */ +static distblk rdcost_for_4x4_intra_blocks_444 (Macroblock *currMB, + int* nonzero, + int b8, + int b4, + int ipmode, + int lambda, + int mostProbableMode, + distblk min_rdcost) +{ + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + distblk rdcost; + int dummy = 0, rate; + distblk distortion = 0; + int block_x = ((b8 & 0x01) << 3) + ((b4 & 0x01) << 2); + int block_y = ((b8 >> 1) << 3) + ((b4 >> 1) << 2); + int pic_pix_x = currMB->pix_x + block_x; + int pic_pix_y = currMB->pix_y + block_y; + int pic_opix_y = currMB->opix_y + block_y; + + SyntaxElement se; + const int *partMap = assignSE2partition[currSlice->partition_mode]; + DataPartition *dataPart; + ColorPlane k; + + //===== perform forward transform, Q, IQ, inverse transform, Reconstruction ===== + //select_transform(currMB); + *nonzero = currMB->residual_transform_quant_luma_4x4 (currMB, PLANE_Y, block_x, block_y, &dummy, 1); + + //===== get distortion (SSD) of 4x4 block ===== + distortion += compute_SSE4x4(&p_Vid->pCurImg[pic_opix_y], &p_Vid->enc_picture->imgY[pic_pix_y], pic_pix_x, pic_pix_x); + + if(p_Vid->P444_joined) + { + for (k = PLANE_U; k <= PLANE_V; k++) + { + select_plane(p_Vid, k); + currMB->c_nzCbCr[k] = currMB->residual_transform_quant_luma_4x4(currMB, k, block_x, block_y, &dummy, 1); + distortion += compute_SSE4x4(&p_Vid->pCurImg[pic_opix_y], &p_Vid->enc_picture->p_curr_img[pic_pix_y], pic_pix_x, pic_pix_x); + } + select_plane(p_Vid, PLANE_Y); + } + currMB->ipmode_DPCM = NO_INTRA_PMODE; + + //===== RATE for INTRA PREDICTION MODE (SYMBOL MODE MUST BE SET TO CAVLC) ===== + se.value1 = (mostProbableMode == ipmode) ? -1 : ipmode < mostProbableMode ? ipmode : ipmode - 1; + + //--- set position and type --- + se.context = (b8 << 2) + b4; + se.type = SE_INTRAPREDMODE; + + //--- choose data partition --- + dataPart = &(currSlice->partArr[partMap[SE_INTRAPREDMODE]]); + //--- encode and update rate --- + currSlice->writeIntraPredMode (&se, dataPart); + rate = se.len; + + //===== RATE for LUMINANCE COEFFICIENTS ===== + if (currSlice->symbol_mode == CAVLC) + { + rate += currSlice->writeCoeff4x4_CAVLC (currMB, LUMA, b8, b4, 0); + if(p_Vid->P444_joined) + { + rate += currSlice->writeCoeff4x4_CAVLC (currMB, CB, b8, b4, 0); + rate += currSlice->writeCoeff4x4_CAVLC (currMB, CR, b8, b4, 0); + } + } + else + { + rate += writeCoeff4x4_CABAC (currMB, PLANE_Y, b8, b4, 1); + if(p_Vid->P444_joined) + { + rate += writeCoeff4x4_CABAC (currMB, PLANE_U, b8, b4, 1); + rate += writeCoeff4x4_CABAC (currMB, PLANE_V, b8, b4, 1); + } + } + rdcost = distortion + weight_cost(lambda,rate); + currSlice->reset_coding_state (currMB, currSlice->p_RDO->cs_cm); + + return rdcost; +} + +/*! +************************************************************************************* +* \brief +* R-D Cost for an 8x8 Partition +************************************************************************************* +*/ +distblk rdcost_for_8x8blocks (Macroblock *currMB, // --> Current macroblock to code + RD_8x8DATA *dataTr, + int* cnt_nonz, // --> number of nonzero coefficients + int64* cbp_blk, // --> cbp blk + int lambda, // <-- lagrange multiplier + int block, // <-- 8x8 block number + short mode, // <-- partitioning mode + Info8x8 *part, // <-- partition information + distblk min_rdcost) +{ + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + InputParameters *p_Inp = currMB->p_Inp; + pic_parameter_set_rbsp_t *active_pps = p_Vid->active_pps; + int rate=0; + distblk distortion=0; + + int dummy = 0, mrate; + int list_mode[2]; + int cbp = 0; + int pax = 8*(block & 0x01); + int pay = 8*(block >> 1); + int i0 = pax >> 2; + int j0 = pay >> 2; + int direct = (currSlice->slice_type == B_SLICE && mode==0); + int b8value = B8Mode2Value (currSlice, (short) mode, part->pdir); + + SyntaxElement se; + DataPartition *dataPart; + const int *partMap = assignSE2partition[currSlice->partition_mode]; + + EncodingEnvironmentPtr eep_dp; + + short pdir = part->pdir; + int l0_ref = part->ref[LIST_0]; + int l1_ref = part->ref[LIST_1]; + + //===== + //===== GET COEFFICIENTS, RECONSTRUCTIONS, CBP + //===== + currMB->b8x8[block].bipred = part->bipred; + currMB->ar_mode = (short) ((mode != 0)? mode: P8x8); + + + if (direct) + { + int p_dir = currSlice->direct_pdir[currMB->block_y + j0][currMB->block_x + i0]; + if (p_dir < 0) // mode not allowed + return (DISTBLK_MAX); + else + { + int list_mode[2] = {0, 0}; + *cnt_nonz = currSlice->luma_residual_coding_8x8 (currMB, &cbp, cbp_blk, block, (short) p_dir, list_mode, + currSlice->direct_ref_idx[currMB->block_y+j0][currMB->block_x+i0]); + } + } + else + { + if (pdir == 2 && active_pps->weighted_bipred_idc == 1) + { + + int weight_sum = (active_pps->weighted_bipred_idc == 1)? currSlice->wbp_weight[0][l0_ref][l1_ref][0] + currSlice->wbp_weight[1][l0_ref][l1_ref][0] : 0; + if (weight_sum < -128 || weight_sum > 127) + { + return (DISTBLK_MAX); + } + } + + list_mode[0] = (pdir==0||pdir==2 ? mode : 0); + list_mode[1] = (pdir==1||pdir==2 ? mode : 0); + *cnt_nonz = currSlice->luma_residual_coding_8x8 (currMB, &cbp, cbp_blk, block, pdir, list_mode, part->ref); + } + + if(p_Vid->P444_joined) + { + *cnt_nonz += ( currSlice->coeff_cost_cr[1] + currSlice->coeff_cost_cr[2] ); + } + + // RDOPT with losses + if (p_Inp->rdopt==3) + { + //Zhifeng 090611 + distortion = p_Vid->estimate_distortion(currMB, block, 8, mode, pdir, min_rdcost); //===== get residue ===== + } + else + { + distortion += compute_SSE8x8(&p_Vid->pCurImg[currMB->opix_y + pay], &p_Vid->enc_picture->imgY[currMB->pix_y + pay], currMB->pix_x + pax, currMB->pix_x + pax); + } + + if (p_Vid->P444_joined) + { + distortion += compute_SSE8x8(&p_Vid->pImgOrg[1][currMB->opix_y + pay], &p_Vid->enc_picture->imgUV[0][currMB->pix_y + pay], currMB->pix_x + pax, currMB->pix_x + pax); + distortion += compute_SSE8x8(&p_Vid->pImgOrg[2][currMB->opix_y + pay], &p_Vid->enc_picture->imgUV[1][currMB->pix_y + pay], currMB->pix_x + pax, currMB->pix_x + pax); + } + + // Early termination + if (distortion >= min_rdcost) + return (distortion); + //printf("distortion %.2f %.2f\n", (double) distortion, min_rdcost); + + if(p_Vid->P444_joined) + { + cbp |= currSlice->cmp_cbp[1]; + cbp |= currSlice->cmp_cbp[2]; + + currSlice->cmp_cbp[1] = cbp; + currSlice->cmp_cbp[2] = cbp; + } + + //===== + //===== GET RATE + //===== + //----- block 8x8 mode ----- + if (currSlice->symbol_mode == CAVLC) + { + ue_linfo (b8value, dummy, &mrate, &dummy); + rate += mrate; + } + else + { + se.value1 = b8value; + se.type = SE_MBTYPE; + dataPart = &(currSlice->partArr[partMap[se.type]]); + currSlice->writeB8_typeInfo(&se, dataPart); + rate += se.len; + } + + //----- motion information ----- + if (!direct) + { + if ((currSlice->num_ref_idx_active[LIST_0] > 1 ) && (pdir==0 || pdir==2)) + rate += writeReferenceFrame (currMB, i0, j0, LIST_0, l0_ref); + + if ((currSlice->num_ref_idx_active[LIST_1] > 1 && currSlice->slice_type == B_SLICE ) && (pdir==1 || pdir==2)) + { + rate += writeReferenceFrame (currMB, i0, j0, LIST_1, l1_ref); + } + + if (pdir==0 || pdir==2) + { + rate += writeMotionVector8x8 (currMB, i0, j0, i0 + 2, j0 + 2, l0_ref, LIST_0, mode, currMB->b8x8[block].bipred); + } + if (pdir==1 || pdir==2) + { + rate += writeMotionVector8x8 (currMB, i0, j0, i0 + 2, j0 + 2, l1_ref, LIST_1, mode, currMB->b8x8[block].bipred); + } + } + + //----- coded block pattern (for CABAC only) ----- + if (!currSlice->symbol_mode == CAVLC) + { + dataPart = &(currSlice->partArr[partMap[SE_CBP]]); + eep_dp = &(dataPart->ee_cabac); + mrate = arienco_bits_written (eep_dp); + writeCBP_BIT_CABAC (currMB, block, ((*cnt_nonz>0)?1:0), dataTr->cbp8x8, eep_dp, currSlice->tex_ctx); + mrate = arienco_bits_written (eep_dp) - mrate; + rate += mrate; + } + + //----- luminance coefficients ----- + if (*cnt_nonz) + { + rate += writeCoeff8x8 ( currMB, PLANE_Y, block, mode, currMB->luma_transform_size_8x8_flag); + } + + if(p_Vid->P444_joined) + { + rate += writeCoeff8x8( currMB, PLANE_U, block, mode, currMB->luma_transform_size_8x8_flag ); + rate += writeCoeff8x8( currMB, PLANE_V, block, mode, currMB->luma_transform_size_8x8_flag ); + } + return (distortion + weight_cost(lambda, rate)); +} + + +/* + ************************************************************************************* + * \brief + * Gets mode offset for intra16x16 mode + ************************************************************************************* + */ +short I16Offset (int cbp, short i16mode) +{ + return (short) ((cbp & 15 ? 13 : 1) + i16mode + ((cbp & 0x30) >> 2)); +} + + +/*! + ************************************************************************************* + * \brief + * Sets modes and reference frames for a macroblock + ************************************************************************************* + */ +void set_modes_and_refs_for_blocks_i_slice(Macroblock *currMB, short mode) +{ + VideoParameters *p_Vid = currMB->p_Vid; + + int i,j; + Block8x8Info *b8x8info = p_Vid->b8x8info; + PicMotionParams **motion = p_Vid->enc_picture->mv_info; + + //--- macroblock type --- + currMB->mb_type = mode; + currMB->ar_mode = mode; + + for( i = 0; i < 4; i++) + { + currMB->b8x8[i] = b8x8info->best[mode][i]; + } + + //--- block 8x8 mode and prediction direction --- + switch (mode) + { + case I4MB: + for(i=0;i<4;i++) + { + currMB->b8x8[i].mode = IBLOCK; + currMB->b8x8[i].pdir = -1; + } + break; + case I16MB: + //memset(currMB->b8x8, 0, 4 * sizeof(short)); + currMB->b8x8[0].mode = 0; + currMB->b8x8[1].mode = 0; + currMB->b8x8[2].mode = 0; + currMB->b8x8[3].mode = 0; + for(i=0;i<4;i++) + { + currMB->b8x8[i].pdir = -1; + } + break; + case I8MB: + for(i=0;i<4;i++) + { + currMB->b8x8[i].mode = I8MB; + currMB->b8x8[i].pdir = -1; + } + //switch to 8x8 transform + currMB->luma_transform_size_8x8_flag = TRUE; + break; + case IPCM: + for(i=0;i<4;i++) + { + currMB->b8x8[i].mode = IPCM; + currMB->b8x8[i].pdir = -1; + } + currMB->luma_transform_size_8x8_flag = FALSE; + break; + default: + printf ("Unsupported mode in set_modes_and_refs_for_blocks!\n"); + exit (1); + } + + //--- reference frame arrays --- + for (j = currMB->block_y; j < currMB->block_y + 4; j++) + { + for (i = currMB->block_x; i < currMB->block_x + 4;i++) + { + motion[j][i].ref_pic [LIST_0] = NULL; + motion[j][i].ref_idx [LIST_0] = -1; + } + } +} + + +/*! + ************************************************************************************* + * \brief + * Sets modes and reference frames for a macroblock + ************************************************************************************* + */ +void set_modes_and_refs_for_blocks_p_slice(Macroblock *currMB, short mode) +{ + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + + int i,j,k; + + Block8x8Info *b8x8info = p_Vid->b8x8info; + PicMotionParams **motion = p_Vid->enc_picture->mv_info; + + //--- macroblock type --- + currMB->mb_type = mode; + + for( i = 0; i < 4; i++) + { + currMB->b8x8[i] = b8x8info->best[mode][i]; + } + + currMB->ar_mode = mode; + + //--- block 8x8 mode and prediction direction --- + switch (mode) + { + case 0: + currMB->b8x8[0].mode = 0; + currMB->b8x8[1].mode = 0; + currMB->b8x8[2].mode = 0; + currMB->b8x8[3].mode = 0; + currMB->b8x8[0].pdir = 0; + currMB->b8x8[1].pdir = 0; + currMB->b8x8[2].pdir = 0; + currMB->b8x8[3].pdir = 0; + break; + case 1: + case 2: + case 3: + for(i=0;i<4;i++) + { + currMB->b8x8[i].mode = (char) mode; + } + break; + case P8x8: + break; + case I4MB: + for(i=0;i<4;i++) + { + currMB->b8x8[i].mode = IBLOCK; + currMB->b8x8[i].pdir = -1; + } + break; + case I16MB: + //memset(currMB->b8x8, 0, 4 * sizeof(short)); + currMB->b8x8[0].mode = 0; + currMB->b8x8[1].mode = 0; + currMB->b8x8[2].mode = 0; + currMB->b8x8[3].mode = 0; + for(i=0;i<4;i++) + { + currMB->b8x8[i].pdir = -1; + } + break; + case I8MB: + for(i=0;i<4;i++) + { + currMB->b8x8[i].mode = I8MB; + currMB->b8x8[i].pdir = -1; + } + //switch to 8x8 transform + currMB->luma_transform_size_8x8_flag = TRUE; + break; + case IPCM: + for(i=0;i<4;i++) + { + currMB->b8x8[i].mode = IPCM; + currMB->b8x8[i].pdir = -1; + } + currMB->luma_transform_size_8x8_flag = FALSE; + break; + default: + printf ("Unsupported mode in set_modes_and_refs_for_blocks!\n"); + exit (1); + } + + //--- reference frame arrays --- + if (mode==0 || mode==I4MB || mode==I16MB || mode==I8MB || mode == IPCM || mode == SI4MB) + { + if (!mode) // Skip + { + for (j = currMB->block_y; j < currMB->block_y + 4; j++) + for (i = currMB->block_x; i < currMB->block_x + 4; i++) + motion[j][i].ref_idx[LIST_0] = 0; + } + else // Intra + { + for (j = currMB->block_y; j < currMB->block_y + 4; j++) + for (i = currMB->block_x; i < currMB->block_x + 4; i++) + motion[j][i].ref_idx[LIST_0] = -1; + } + } + else + { + if (mode == 1) + { + short curref = b8x8info->best[mode][0].pdir == 0 ? b8x8info->best[mode][0].ref[LIST_0] : -1; + for (j = currMB->block_y; j < currMB->block_y + 4; j++) + { + for (i = currMB->block_x; i < currMB->block_x + 4;i++) + { + motion[j][i].ref_pic [LIST_0] = currSlice->listX[LIST_0 + currMB->list_offset][curref]; + motion[j][i].ref_idx [LIST_0] = (char) curref; + } + } + } + else if (mode == 2) + { + short curref = b8x8info->best[mode][0].pdir == 0 ? b8x8info->best[mode][0].ref[LIST_0] : -1; + for (j = currMB->block_y; j < currMB->block_y + 2; j++) + { + for (i = currMB->block_x; i < currMB->block_x + 4;i++) + { + motion[j][i].ref_pic [LIST_0] = currSlice->listX[LIST_0 + currMB->list_offset][curref]; + motion[j][i].ref_idx [LIST_0] = (char) curref; + } + } + curref = b8x8info->best[mode][2].pdir == 0 ? b8x8info->best[mode][2].ref[LIST_0] : -1; + for (j = currMB->block_y + 2; j < currMB->block_y + 4; j++) + { + for (i = currMB->block_x; i < currMB->block_x + 4;i++) + { + motion[j][i].ref_pic [LIST_0] = currSlice->listX[LIST_0 + currMB->list_offset][curref]; + motion[j][i].ref_idx [LIST_0] = (char) curref; + } + } + } + else if (mode == 3) + { + short curref = (b8x8info->best[mode][0].pdir == 0) ? b8x8info->best[mode][0].ref[LIST_0] : -1; + for (j = currMB->block_y; j < currMB->block_y + 4; j++) + { + for (i = currMB->block_x; i < currMB->block_x + 2;i++) + { + motion[j][i].ref_pic [LIST_0] = currSlice->listX[LIST_0 + currMB->list_offset][curref]; + motion[j][i].ref_idx [LIST_0] = (char) curref; + } + } + curref = (b8x8info->best[mode][1].pdir == 0) ? b8x8info->best[mode][1].ref[LIST_0] : -1; + for (j = currMB->block_y; j < currMB->block_y + 4; j++) + { + for (i = currMB->block_x + 2; i < currMB->block_x + 4;i++) + { + motion[j][i].ref_pic [LIST_0] = currSlice->listX[LIST_0 + currMB->list_offset][curref]; + motion[j][i].ref_idx [LIST_0] = (char) curref; + } + } + } + else + { + int block_x, block_y; + int curref; + for (j = 0; j < 4; j++) + { + block_x = currMB->block_x; + block_y = currMB->block_y + j; + k = 2*(j >> 1); + curref = b8x8info->best[mode][k++].ref[LIST_0]; + motion[block_y][block_x].ref_pic [LIST_0] = currSlice->listX[LIST_0 + currMB->list_offset][curref]; + motion[block_y][block_x++].ref_idx[LIST_0] = (char) curref; + motion[block_y][block_x].ref_pic [LIST_0] = currSlice->listX[LIST_0 + currMB->list_offset][curref]; + motion[block_y][block_x++].ref_idx[LIST_0] = (char) curref; + curref = b8x8info->best[mode][k].ref[LIST_0]; + motion[block_y][block_x].ref_pic [LIST_0] = currSlice->listX[LIST_0 + currMB->list_offset][curref]; + motion[block_y][block_x++].ref_idx[LIST_0] = (char) curref; + motion[block_y][block_x].ref_pic [LIST_0] = currSlice->listX[LIST_0 + currMB->list_offset][curref]; + motion[block_y][block_x].ref_idx[LIST_0] = (char) curref; + } + } + } +} + + +/*! + ************************************************************************************* + * \brief + * Sets modes and reference frames for a macroblock + ************************************************************************************* + */ +void set_modes_and_refs_for_blocks_b_slice(Macroblock *currMB, short mode) +{ + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + + int i,j,k; + int block_x, block_y, block8x8, block4x4; + int cur_ref; + int clist; + char curref, bestref; + Block8x8Info *b8x8info = p_Vid->b8x8info; + PicMotionParams **motion = p_Vid->enc_picture->mv_info; + + //--- macroblock type --- + currMB->mb_type = mode; + currMB->ar_mode = mode; + + for( i = 0; i < 4; i++) + { + currMB->b8x8[i] = b8x8info->best[mode][i]; + } + + //--- block 8x8 mode and prediction direction --- + switch (mode) + { + case 0: + currMB->b8x8[0].mode = 0; + currMB->b8x8[1].mode = 0; + currMB->b8x8[2].mode = 0; + currMB->b8x8[3].mode = 0; + + currMB->b8x8[0].pdir = currSlice->direct_pdir[currMB->block_y ][currMB->block_x ]; + currMB->b8x8[1].pdir = currSlice->direct_pdir[currMB->block_y ][currMB->block_x + 2]; + currMB->b8x8[2].pdir = currSlice->direct_pdir[currMB->block_y + 2][currMB->block_x ]; + currMB->b8x8[3].pdir = currSlice->direct_pdir[currMB->block_y + 2][currMB->block_x + 2]; + break; + case 1: + case 2: + case 3: + for(i=0;i<4;i++) + { + currMB->b8x8[i].mode = (char) mode; + } + break; + case P8x8: + break; + case I4MB: + for(i=0;i<4;i++) + { + currMB->b8x8[i].mode = IBLOCK; + currMB->b8x8[i].pdir = -1; + } + break; + case I16MB: + for(i=0;i<4;i++) + { + currMB->b8x8[i].mode = 0; + currMB->b8x8[i].pdir = -1; + } + break; + case I8MB: + for(i=0;i<4;i++) + { + currMB->b8x8[i].mode = I8MB; + currMB->b8x8[i].pdir = -1; + } + //switch to 8x8 transform + currMB->luma_transform_size_8x8_flag = TRUE; + break; + case IPCM: + for(i=0;i<4;i++) + { + currMB->b8x8[i].mode = IPCM; + currMB->b8x8[i].pdir = -1; + } + currMB->luma_transform_size_8x8_flag = FALSE; + break; + default: + printf ("Unsupported mode in set_modes_and_refs_for_blocks!\n"); + exit (1); + } + +#define IS_FW ((b8x8info->best[mode][k].pdir==0 || b8x8info->best[mode][k].pdir==2) && (mode!=P8x8 || b8x8info->best[mode][k].mode!=0)) +#define IS_BW ((b8x8info->best[mode][k].pdir==1 || b8x8info->best[mode][k].pdir==2) && (mode!=P8x8 || b8x8info->best[mode][k].mode!=0)) + + //--- reference frame arrays --- + if (mode == 0) // Direct + { + for (clist = LIST_0; clist <= LIST_1; clist++) + { + for (j = currMB->block_y; j < currMB->block_y + 4; j++) + { + for (i = currMB->block_x; i < currMB->block_x + 4; i++) + motion[j][i].ref_idx[clist] = currSlice->direct_ref_idx[j][i][clist]; + } + } + } + else if (mode==I4MB || mode==I16MB || mode==I8MB || mode == IPCM) + { + for (clist = LIST_0; clist <= LIST_1; clist++) + { + for (j = currMB->block_y; j < currMB->block_y + 4; j++) + { + for (i = currMB->block_x; i < currMB->block_x + 4; i++) + motion[j][i].ref_idx[clist] = -1; + } + } + } + else if (mode == 1 || mode == 2 || mode == 3) + { + for (block8x8 = 0; block8x8 < 4; block8x8++) + { + for (clist = LIST_0; clist <= LIST_1; clist++) + { + bestref = b8x8info->best[mode][block8x8].ref[clist]; + + if ( b8x8info->best[mode][block8x8].pdir == 2) + { + curref = (b8x8info->best[mode][block8x8].bipred) ? 0 : bestref; + } + else + { + curref = (clist == b8x8info->best[mode][block8x8].pdir) ? bestref : -1; + } + + for (block4x4 = 0; block4x4 < 4; block4x4++) + { + block_x = currMB->block_x + 2 * (block8x8 & 0x01) + (block4x4 & 0x01); + block_y = currMB->block_y + 2 * (block8x8 >> 1) + (block4x4 >> 1); + motion[block_y][block_x].ref_idx[clist] = curref; + } + } + } + } + else // mode == P8x8 + { + for (j = 0; j < 4; j++) + { + block_y = currMB->block_y + j; + for (i = 0; i < 4; i++) + { + block_x = currMB->block_x + i; + k = 2*(j >> 1) + (i >> 1); + + if(b8x8info->best[mode][k].mode==0) + { + motion[block_y][block_x].ref_idx[LIST_0] = currSlice->direct_ref_idx[block_y][block_x][LIST_0]; + motion[block_y][block_x].ref_idx[LIST_1] = currSlice->direct_ref_idx[block_y][block_x][LIST_1]; + } + else + { + motion[block_y][block_x].ref_idx[LIST_0] = (IS_FW ? b8x8info->best[mode][k].ref[LIST_0] : -1); + motion[block_y][block_x].ref_idx[LIST_1] = (IS_BW ? b8x8info->best[mode][k].ref[LIST_1] : -1); + } + } + } + } + + for (clist = LIST_0; clist <= LIST_1; clist++) + { + StorablePicture **ref_pic = currSlice->listX[clist + currMB->list_offset]; + for (j = currMB->block_y; j < currMB->block_y + 4; j++) + { + for (i = currMB->block_x; i < currMB->block_x + 4;i++) + { + cur_ref = (int) motion[j][i].ref_idx[clist]; + motion[j][i].ref_pic [clist] = (cur_ref < 0 ? NULL : ref_pic[cur_ref]); + } + } + } + +#undef IS_FW +#undef IS_BW +} + + +/*! + ************************************************************************************* + * \brief + * Sets Coefficients and reconstruction for an 8x8 block + ************************************************************************************* + */ +void set_coeff_and_recon_8x8_p_slice (Macroblock* currMB) +{ + VideoParameters *p_Vid = currMB->p_Vid; + InputParameters *p_Inp = currMB->p_Inp; + Slice *currSlice = currMB->p_Slice; + RDOPTStructure *p_RDO = currSlice->p_RDO; + + PicMotionParams **motion = p_Vid->enc_picture->mv_info; + int block, k, j, i, uv; + int cur_ref; + + //============= MIXED TRANSFORM SIZES FOR 8x8 PARTITION ============== + //-------------------------------------------------------------------- + if (currMB->luma_transform_size_8x8_flag && (currMB->valid_8x8)) + { + //============= set mode and ref. frames ============== + memcpy(currMB->b8x8, p_RDO->tr8x8->part, 4 * sizeof(Info8x8)); + + for (j = 0; j < 4; j++) + { + for (i = 0; i < 4; i++) + { + k = 2*(j >> 1)+(i >> 1); + motion[currMB->block_y+j][currMB->block_x+i].ref_idx[LIST_0] = p_RDO->tr8x8->part[k].ref[LIST_0]; + } + } + + for (j = currMB->block_y;jblock_y + BLOCK_MULTIPLE;j++) + { + for (i = currMB->block_x;iblock_x + BLOCK_MULTIPLE;i++) + { + cur_ref = (int) motion[j][i].ref_idx[LIST_0]; + motion[j][i].ref_pic[LIST_0] =(cur_ref >= 0 ? currSlice->listX[LIST_0 + currMB->list_offset][cur_ref] : NULL); + } + } + + + //====== set the mv's for 8x8 partition with transform size 8x8 ====== + //save the mv data for 4x4 transform + StoreMV8x8(currSlice, 1); + //set new mv data for 8x8 transform + RestoreMV8x8(currSlice, 0); + + //============= get pre-calculated data ============== + //restore coefficients from 8x8 transform + + for (block = 0; block<4; block++) + { + memcpy (currSlice->cofAC[block][0][0],p_RDO->cofAC8x8ts[block][0][0][0], 4 * 2 * 65 * sizeof(int)); + } + + if (p_Vid->P444_joined) + { + for (uv=0; uv<2; uv++) + { + for (block = 0; block<4; block++) + { + memcpy (currSlice->cofAC[4+block+uv*4][0][0],p_RDO->cofAC8x8ts[block][uv + 1][0][0], 4 * 2 * 65 * sizeof(int)); + } + } + } + + //restore reconstruction + if (p_RDO->tr8x8->cnt_nonz_8x8 <= _LUMA_8x8_COEFF_COST_ && + ((currMB->qp_scaled[0])!=0 || p_Vid->active_sps->lossless_qpprime_flag==0) && + (currSlice->slice_type != SP_SLICE))// modif ES added last condition (we probably never go there so is the next modification useful ? check) + { + currMB->cbp = 0; + currMB->cbp_blk = 0; + + copy_image_data_16x16(&p_Vid->enc_picture->imgY[currMB->pix_y], p_RDO->tr8x8->mpr8x8, currMB->pix_x, 0); + + if(p_Inp->rdopt == 3) + { + p_Vid->p_decs->rec_type = 0; + } + + if(currSlice->slice_type == SP_SLICE && !p_Vid->sp2_frame_indicator) + { + for (j = 0; j < MB_BLOCK_SIZE; j++) + { + memcpy(&p_Vid->lrec[currMB->pix_y+j][currMB->pix_x],p_RDO->tr8x8->lrec[j], MB_BLOCK_SIZE * sizeof(int)); + } + } + + //memset( currSlice->cofAC[0][0][0], 0, 2080 * sizeof(int)); // 4 * 4 * 2 * 65 + + if(p_Vid->P444_joined) + { + currSlice->cmp_cbp[1] = currSlice->cmp_cbp[2] = 0; + copy_image_data_16x16(&p_Vid->enc_picture->imgUV[0][currMB->pix_y], p_RDO->tr8x8->mpr8x8CbCr[0], currMB->pix_x, 0); + copy_image_data_16x16(&p_Vid->enc_picture->imgUV[1][currMB->pix_y], p_RDO->tr8x8->mpr8x8CbCr[1], currMB->pix_x, 0); + + for (uv=0; uv<2; uv++) + { + for (block = 0; block<4; block++) + { + memset( currSlice->cofAC[4+block+uv*4][0][0], 0, 4 * 2 * 65 * sizeof(int)); + } + } + } + } + else + { + currMB->cbp = p_RDO->tr8x8->cbp8x8; + currMB->cbp_blk = p_RDO->tr8x8->cbp_blk8x8; + + copy_image_data_16x16(&p_Vid->enc_picture->imgY[currMB->pix_y], p_RDO->tr8x8->rec_mbY8x8, currMB->pix_x, 0); + + if (p_Inp->rdopt == 3) + { + p_Vid->p_decs->rec_type = 1; + } + + if(currSlice->slice_type == SP_SLICE && !p_Vid->sp2_frame_indicator) + { + for (j = 0; j < MB_BLOCK_SIZE; j++) + { + memcpy (&p_Vid->lrec[currMB->pix_y+j][currMB->pix_x],p_RDO->tr8x8->lrec[j], MB_BLOCK_SIZE * sizeof(int)); + } + } + + if (p_Vid->P444_joined) + { + currSlice->cmp_cbp[1] = currSlice->cmp_cbp[2] = p_RDO->tr8x8->cbp8x8; + copy_image_data_16x16(&p_Vid->enc_picture->imgUV[0][currMB->pix_y], p_RDO->tr8x8->rec_mb8x8_cr[0], currMB->pix_x, 0); + copy_image_data_16x16(&p_Vid->enc_picture->imgUV[1][currMB->pix_y], p_RDO->tr8x8->rec_mb8x8_cr[1], currMB->pix_x, 0); + } + } + if (p_Inp->rdopt == 3) + { + errdo_get_best_P8x8(currMB, 1); + } + } + else + { + //============= get pre-calculated data ============== + //--------------------------------------------------- + //--- restore coefficients --- + for (block = 0; block < 4; block ++) + { + memcpy (currSlice->cofAC[block][0][0],p_RDO->coefAC8x8[block][0][0][0], 4 * 2 * 65 * sizeof(int)); + } + + if (currSlice->P444_joined) + { + for (block = 0; block<4; block++) + { + memcpy (currSlice->cofAC[block+4][0][0],p_RDO->coefAC8x8[block][1][0][0], 4 * 2 * 65 * sizeof(int)); + memcpy (currSlice->cofAC[block+8][0][0],p_RDO->coefAC8x8[block][2][0][0], 4 * 2 * 65 * sizeof(int)); + } + } + + if (((p_Inp->disthres && p_RDO->tr4x4->cnt_nonz_8x8 <= 0) || (p_RDO->tr4x4->cnt_nonz_8x8 <= 5)) && currSlice->slice_type != SP_SLICE && + ((currMB->qp_scaled[0])!=0 || p_Vid->active_sps->lossless_qpprime_flag==0)) + { + currMB->cbp = 0; + currMB->cbp_blk = 0; + + copy_image_data_16x16(&p_Vid->enc_picture->imgY[currMB->pix_y], p_RDO->tr4x4->mpr8x8, currMB->pix_x, 0); + + if(p_Inp->rdopt == 3) + { + p_Vid->p_decs->rec_type = 0; + } + + if(currSlice->slice_type == SP_SLICE && !p_Vid->sp2_frame_indicator) + { + for (j = 0; j < MB_BLOCK_SIZE; j++) + { + memcpy (&p_Vid->lrec[currMB->pix_y+j][currMB->pix_x],p_RDO->tr4x4->lrec[j], MB_BLOCK_SIZE * sizeof(int)); // restore coeff. SP frame + } + } + + //memset( currSlice->cofAC[0][0][0], 0, 2080 * sizeof(int)); // 4 * 4 * 2 * 65 + + if (currSlice->P444_joined) + { + currSlice->cmp_cbp[1] = currSlice->cmp_cbp[2] = 0; + + copy_image_data_16x16(&p_Vid->enc_picture->imgUV[0][currMB->pix_y], p_RDO->tr4x4->mpr8x8CbCr[0], currMB->pix_x, 0); + copy_image_data_16x16(&p_Vid->enc_picture->imgUV[1][currMB->pix_y], p_RDO->tr4x4->mpr8x8CbCr[1], currMB->pix_x, 0); + + for (uv=0; uv<2; uv++) + { + for (block = 0; block<4; block++) + { + memset( currSlice->cofAC[4+block+uv*4][0][0], 0, 4 * 2 * 65 * sizeof(int)); + } + } + } + } + else + { + currMB->cbp = p_RDO->tr4x4->cbp8x8; + currMB->cbp_blk = p_RDO->tr4x4->cbp_blk8x8; + + copy_image_data_16x16(&p_Vid->enc_picture->imgY[currMB->pix_y], p_RDO->tr4x4->rec_mbY8x8, currMB->pix_x, 0); + + if(p_Inp->rdopt == 3) + { + p_Vid->p_decs->rec_type = 1; + } + + if(currSlice->slice_type == SP_SLICE && !p_Vid->sp2_frame_indicator) + { + for (j = 0; j < MB_BLOCK_SIZE; j++) + { + memcpy (&p_Vid->lrec[currMB->pix_y+j][currMB->pix_x],p_RDO->tr4x4->lrec[j], MB_BLOCK_SIZE * sizeof(int)); + } + } + if (currSlice->P444_joined) + { + currSlice->cmp_cbp[1] = currSlice->cmp_cbp[2] = p_RDO->tr4x4->cbp8x8; + copy_image_data_16x16(&p_Vid->enc_picture->imgUV[0][currMB->pix_y], p_RDO->tr4x4->rec_mb8x8_cr[0], currMB->pix_x, 0); + copy_image_data_16x16(&p_Vid->enc_picture->imgUV[1][currMB->pix_y], p_RDO->tr4x4->rec_mb8x8_cr[1], currMB->pix_x, 0); + } + } + if (p_Inp->rdopt == 3) + { + errdo_get_best_P8x8(currMB, 0); + } + } +} + + +/*! + ************************************************************************************* + * \brief + * Sets Coefficients and reconstruction for an 8x8 block + ************************************************************************************* + */ +void set_coeff_and_recon_8x8_b_slice (Macroblock* currMB) +{ + VideoParameters *p_Vid = currMB->p_Vid; + InputParameters *p_Inp = currMB->p_Inp; + Slice *currSlice = currMB->p_Slice; + RDOPTStructure *p_RDO = currSlice->p_RDO; + + PicMotionParams **motion = p_Vid->enc_picture->mv_info; + int block, k, j, i, uv; + int cur_ref; + + //============= MIXED TRANSFORM SIZES FOR 8x8 PARTITION ============== + //-------------------------------------------------------------------- + if (currMB->luma_transform_size_8x8_flag && (currMB->valid_8x8)) + { + //============= set mode and ref. frames ============== + memcpy(currMB->b8x8, p_RDO->tr8x8->part, 4 * sizeof(Info8x8)); + + for (j = 0;j<4;j++) + { + for (i = 0;i<4;i++) + { + k = 2*(j >> 1)+(i >> 1); + motion[currMB->block_y+j][currMB->block_x+i].ref_idx[LIST_0] = ((currMB->b8x8[k].pdir & 0x01) == 0) ? p_RDO->tr8x8->part[k].ref[LIST_0] : - 1; + motion[currMB->block_y+j][currMB->block_x+i].ref_idx[LIST_1] = (currMB->b8x8[k].pdir > 0) ? p_RDO->tr8x8->part[k].ref[LIST_1] : - 1; + } + } + + for (j = currMB->block_y;jblock_y + BLOCK_MULTIPLE;j++) + { + for (i = currMB->block_x;iblock_x + BLOCK_MULTIPLE;i++) + { + cur_ref = (int) motion[j][i].ref_idx[LIST_0]; + motion[j][i].ref_pic[LIST_0] =(cur_ref >= 0 ? currSlice->listX[LIST_0 + currMB->list_offset][cur_ref] : NULL); + } + } + + for (j = currMB->block_y;jblock_y + BLOCK_MULTIPLE;j++) + { + for (i = currMB->block_x;iblock_x + BLOCK_MULTIPLE;i++) + { + cur_ref = (int) motion[j][i].ref_idx[LIST_1]; + motion[j][i].ref_pic[LIST_1] =(cur_ref >= 0 ? currSlice->listX[LIST_1 + currMB->list_offset][cur_ref] : NULL); + } + } + + //====== set the mv's for 8x8 partition with transform size 8x8 ====== + //save the mv data for 4x4 transform + StoreMV8x8(currSlice, 1); + //set new mv data for 8x8 transform + RestoreMV8x8(currSlice, 0); + + //============= get pre-calculated data ============== + //restore coefficients from 8x8 transform + + for (block = 0; block<4; block++) + { + memcpy (currSlice->cofAC[block][0][0],p_RDO->cofAC8x8ts[block][0][0][0], 4 * 2 * 65 * sizeof(int)); + } + + if (p_Vid->P444_joined) + { + for (uv=0; uv<2; uv++) + { + for (block = 0; block<4; block++) + { + memcpy (currSlice->cofAC[4+block+uv*4][0][0],p_RDO->cofAC8x8ts[block][uv + 1][0][0], 4 * 2 * 65 * sizeof(int)); + } + } + } + + //restore reconstruction + if (p_RDO->tr8x8->cnt_nonz_8x8 <= _LUMA_8x8_COEFF_COST_ && + ((currMB->qp_scaled[0])!=0 || p_Vid->active_sps->lossless_qpprime_flag==0))// modif ES added last condition (we probably never go there so is the next modification useful ? check) + { + currMB->cbp = 0; + currMB->cbp_blk = 0; + + copy_image_data_16x16(&p_Vid->enc_picture->imgY[currMB->pix_y], p_RDO->tr8x8->mpr8x8, currMB->pix_x, 0); + + if(p_Inp->rdopt == 3) + { + p_Vid->p_decs->rec_type = 0; + } + + + //memset( currSlice->cofAC[0][0][0], 0, 2080 * sizeof(int)); // 4 * 4 * 2 * 65 + + if(p_Vid->P444_joined) + { + currSlice->cmp_cbp[1] = currSlice->cmp_cbp[2] = 0; + copy_image_data_16x16(&p_Vid->enc_picture->imgUV[0][currMB->pix_y], p_RDO->tr8x8->mpr8x8CbCr[0], currMB->pix_x, 0); + copy_image_data_16x16(&p_Vid->enc_picture->imgUV[1][currMB->pix_y], p_RDO->tr8x8->mpr8x8CbCr[1], currMB->pix_x, 0); + + for (uv=0; uv<2; uv++) + { + for (block = 0; block<4; block++) + { + memset( currSlice->cofAC[4+block+uv*4][0][0], 0, 4 * 2 * 65 * sizeof(int)); + } + } + } + } + else + { + currMB->cbp = p_RDO->tr8x8->cbp8x8; + currMB->cbp_blk = p_RDO->tr8x8->cbp_blk8x8; + + copy_image_data_16x16(&p_Vid->enc_picture->imgY[currMB->pix_y], p_RDO->tr8x8->rec_mbY8x8, currMB->pix_x, 0); + + if(p_Inp->rdopt == 3) + { + p_Vid->p_decs->rec_type = 1; + } + + + if (p_Vid->P444_joined) + { + currSlice->cmp_cbp[1] = currSlice->cmp_cbp[2] = p_RDO->tr8x8->cbp8x8; + copy_image_data_16x16(&p_Vid->enc_picture->imgUV[0][currMB->pix_y], p_RDO->tr8x8->rec_mb8x8_cr[0], currMB->pix_x, 0); + copy_image_data_16x16(&p_Vid->enc_picture->imgUV[1][currMB->pix_y], p_RDO->tr8x8->rec_mb8x8_cr[1], currMB->pix_x, 0); + } + } + if (p_Inp->rdopt == 3) + { + errdo_get_best_P8x8(currMB, 1); + } + } + else + { + if (currMB->valid_8x8) + { + StoreMV8x8(currSlice, 1); + } + + //============= get pre-calculated data ============== + //--------------------------------------------------- + //--- restore coefficients --- + for (block = 0; block < 4; block ++) + { + memcpy (currSlice->cofAC[block][0][0],p_RDO->coefAC8x8[block][0][0][0], 4 * 2 * 65 * sizeof(int)); + } + + if (currSlice->P444_joined) + { + for (block = 0; block<4; block++) + { + memcpy (currSlice->cofAC[block+4][0][0],p_RDO->coefAC8x8[block][1][0][0], 4 * 2 * 65 * sizeof(int)); + memcpy (currSlice->cofAC[block+8][0][0],p_RDO->coefAC8x8[block][2][0][0], 4 * 2 * 65 * sizeof(int)); + } + } + + if (((p_Inp->disthres && p_RDO->tr4x4->cnt_nonz_8x8 <= 0) || (p_RDO->tr4x4->cnt_nonz_8x8 <= 5)) && + ((currMB->qp_scaled[0])!=0 || p_Vid->active_sps->lossless_qpprime_flag==0)) + { + currMB->cbp = 0; + currMB->cbp_blk = 0; + + copy_image_data_16x16(&p_Vid->enc_picture->imgY[currMB->pix_y], p_RDO->tr4x4->mpr8x8, currMB->pix_x, 0); + + if(p_Inp->rdopt == 3) + { + p_Vid->p_decs->rec_type = 0; + } + + if (currSlice->P444_joined) + { + currSlice->cmp_cbp[1] = currSlice->cmp_cbp[2] = 0; + + copy_image_data_16x16(&p_Vid->enc_picture->imgUV[0][currMB->pix_y], p_RDO->tr4x4->mpr8x8CbCr[0], currMB->pix_x, 0); + copy_image_data_16x16(&p_Vid->enc_picture->imgUV[1][currMB->pix_y], p_RDO->tr4x4->mpr8x8CbCr[1], currMB->pix_x, 0); + + for (uv=0; uv<2; uv++) + { + for (block = 0; block<4; block++) + { + memset( currSlice->cofAC[4+block+uv*4][0][0], 0, 4 * 2 * 65 * sizeof(int)); + } + } + } + } + else + { + currMB->cbp = p_RDO->tr4x4->cbp8x8; + currMB->cbp_blk = p_RDO->tr4x4->cbp_blk8x8; + + copy_image_data_16x16(&p_Vid->enc_picture->imgY[currMB->pix_y], p_RDO->tr4x4->rec_mbY8x8, currMB->pix_x, 0); + + if(p_Inp->rdopt == 3) + { + p_Vid->p_decs->rec_type = 1; + } + + if (currSlice->P444_joined) + { + currSlice->cmp_cbp[1] = currSlice->cmp_cbp[2] = p_RDO->tr4x4->cbp8x8; + copy_image_data_16x16(&p_Vid->enc_picture->imgUV[0][currMB->pix_y], p_RDO->tr4x4->rec_mb8x8_cr[0], currMB->pix_x, 0); + copy_image_data_16x16(&p_Vid->enc_picture->imgUV[1][currMB->pix_y], p_RDO->tr4x4->rec_mb8x8_cr[1], currMB->pix_x, 0); + } + } + if (p_Inp->rdopt == 3) + { + errdo_get_best_P8x8(currMB, 0); + } + } +} + +/*! + ************************************************************************************* + * \brief + * Restore Non zero coefficients + ************************************************************************************* + */ +void restore_nz_coeff(Macroblock *currMB) +{ +#ifdef BEST_NZ_COEFF + VideoParameters *p_Vid = currMB->p_Vid; + int i, j; + int **nz_coeff = p_Vid->nz_coeff[currMB->mbAddrX]; + for (j=0;j<4;j++) + for (i=0; i<(4+p_Vid->num_blk8x8_uv); i++) + nz_coeff[j][i] = p_Vid->gaaiMBAFF_NZCoeff[j][i]; +#endif +} + + +void prepare_ipcm_mode(Macroblock *currMB) +{ + int i, j; + VideoParameters *p_Vid = currMB->p_Vid; + InputParameters *p_Inp = currMB->p_Inp; + // LUMA + copy_image_data_16x16(&p_Vid->enc_picture->imgY[currMB->pix_y], &p_Vid->pCurImg[currMB->opix_y], currMB->pix_x, currMB->pix_x); + + // CHROMA + if ((p_Vid->yuv_format != YUV400) && (p_Inp->separate_colour_plane_flag == 0)) + { + copy_image_data(&p_Vid->enc_picture->imgUV[0][currMB->pix_c_y], &p_Vid->pImgOrg[1][currMB->opix_c_y], currMB->pix_c_x, currMB->pix_c_x, p_Vid->mb_cr_size_x, p_Vid->mb_cr_size_y); + copy_image_data(&p_Vid->enc_picture->imgUV[1][currMB->pix_c_y], &p_Vid->pImgOrg[2][currMB->opix_c_y], currMB->pix_c_x, currMB->pix_c_x, p_Vid->mb_cr_size_x, p_Vid->mb_cr_size_y); + } + + for (j=0;j<4;j++) + for (i=0; i<(4+p_Vid->num_blk8x8_uv); i++) + p_Vid->nz_coeff[currMB->mbAddrX][j][i] = 16; +} + +/*! + ************************************************************************************* + * \brief + * R-D Cost for a macroblock + ************************************************************************************* + */ +int RDCost_for_macroblocks (Macroblock *currMB, // <-- Current Macroblock to code + int lambda, // <-- lagrange multiplier + short mode) // <-- mode (0-COPY/DIRECT, 1-16x16, 2-16x8, 3-8x16, 4-8x8(+), 5-Intra4x4, 6-Intra16x16) +{ + Slice *currSlice = currMB->p_Slice; + RDOPTStructure *p_RDO = currSlice->p_RDO; + VideoParameters *p_Vid = currMB->p_Vid; + InputParameters *p_Inp = currMB->p_Inp; + + seq_parameter_set_rbsp_t *active_sps = p_Vid->active_sps; + + int rate = 0, coeff_rate = 0; + distblk distortion = 0; + Macroblock *prevMB = currMB->PrevMB; + int tmp_cc; + + int use_of_cc = (currSlice->slice_type != I_SLICE && currSlice->slice_type != SI_SLICE && currSlice->symbol_mode == CAVLC); + int cc_rate, dummy; + + distblk rdcost; + distblk dummy_d; + imgpel **mb_pred = currSlice->mb_pred[0]; + imgpel ***curr_mpr_16x16 = currSlice->mpr_16x16[0]; + + // Test MV limits for Skip Mode. This could be necessary for MBAFF case Frame MBs. + if ((currSlice->mb_aff_frame_flag) && (!currMB->mb_field) && (currSlice->slice_type == P_SLICE) && (mode==0) ) + { + if (out_of_bounds_mvs(p_Vid, &currSlice->all_mv[LIST_0][0][0][0][0])) + return 0; + } + + if(/*(p_Inp->rdopt==9) &&*/ (mode==P8x8)) + { + if((currMB->luma_transform_size_8x8_flag && !currMB->valid_8x8) || (!currMB->luma_transform_size_8x8_flag && !currMB->valid_4x4)) + return 0; + } + + //===== + //===== Set Reference Pictures and Block modes + //===== + currSlice->set_modes_and_refs_for_blocks (currMB, mode); + //===== + //===== Set Motion Vectors + //===== + currSlice->set_motion_vectors_mb (currMB); + + //===== + //===== Get coefficients, reconstruction values, CBP etc + //===== + if (mode < P8x8) + { + currSlice->luma_residual_coding(currMB); + } + else if (mode == P8x8) + { + currSlice->set_coeff_and_recon_8x8 (currMB); + } + else if (mode==I4MB) + { + currMB->cbp = mode_decision_for_I4x4_MB (currMB, lambda, &dummy_d); + } + else if (mode==I16MB) + { + currMB->cbp = currSlice->mode_decision_for_I16x16_MB (currMB, lambda); + } + else if(mode==I8MB) + { + currMB->cbp = mode_decision_for_I8x8_MB(currMB, lambda, &dummy_d); + } + else if(mode==IPCM) + { + prepare_ipcm_mode(currMB); + } + + //if (p_Inp->rdopt == 3) + //{ + // // We need the reconstructed prediction residue for the simulated decoders. + // compute_residue_block (currMB, &p_Vid->enc_picture->p_curr_img[currMB->pix_y], p_Vid->p_decs->res_img[0], mode == I16MB ? currSlice->mpr_16x16[0][ (short) currMB->i16mode] : currSlice->mb_pred[0], 0, 16); + //} + + //Rate control + if (p_Inp->RCEnable) + { + if (mode == I16MB) + memcpy(p_RDO->pred[0], curr_mpr_16x16[ (short) currMB->i16mode][0], MB_PIXELS * sizeof(imgpel)); + else + memcpy(p_RDO->pred[0], mb_pred[0], MB_PIXELS * sizeof(imgpel)); + } + + currMB->i16offset = 0; + dummy = 0; + + if (((p_Vid->yuv_format != YUV400) && (active_sps->chroma_format_idc != YUV444)) && (mode != IPCM)) + currSlice->chroma_residual_coding (currMB); + + if (mode==I16MB) + currMB->i16offset = I16Offset (currMB->cbp, currMB->i16mode); + + //===== + //===== GET DISTORTION + //===== + // LUMA + if (p_Inp->rdopt == 3) + { + distortion = p_Vid->estimate_distortion(currMB, 0, 16, mode, 0, currMB->min_rdcost); + //if (errdo_distortion (currMB, mode, &distortion) == 0) + // return 0; + } + else + { + + distortion = currSlice->getDistortion(currMB); + } + if (distortion > currMB->min_rdcost) + return 0; + //printf("passed distortion %.2f %.2f\n", (double)distortion, currMB->min_rdcost); + + if (currMB->qp_scaled[0] == 0 && p_Vid->active_sps->lossless_qpprime_flag == 1 && distortion != 0) + return 0; + + //===== S T O R E C O D I N G S T A T E ===== + //--------------------------------------------------- + currSlice->store_coding_state (currMB, currSlice->p_RDO->cs_cm); + + //===== + //===== GET RATE + //===== + //----- macroblock header ----- + if (use_of_cc) + { + if (currMB->mb_type!=0 || (currSlice->slice_type == B_SLICE && currMB->cbp!=0)) + { + // cod counter and macroblock mode are written ==> do not consider code counter + tmp_cc = p_Vid->cod_counter; + + rate = currSlice->write_MB_layer (currMB, 1, &coeff_rate); + + ue_linfo (tmp_cc, dummy, &cc_rate, &dummy); + rate -= cc_rate; + p_Vid->cod_counter = tmp_cc; + } + else + { + // cod counter is just increased ==> get additional rate + ue_linfo (p_Vid->cod_counter + 1, dummy, &rate, &dummy); + ue_linfo (p_Vid->cod_counter , dummy, &cc_rate, &dummy); + rate -= cc_rate; + } + } + else + { + rate = currSlice->write_MB_layer (currMB, 1, &coeff_rate); + } + + //===== R E S T O R E C O D I N G S T A T E ===== + //------------------------------------------------------- + currSlice->reset_coding_state (currMB, currSlice->p_RDO->cs_cm); + + if (p_Inp->ForceTrueRateRDO == 1) + rdcost = distortion + weight_cost(lambda, rate); + else if (p_Inp->ForceTrueRateRDO == 2) + rdcost = distortion + weight_cost(lambda, (rate + IS_INTRA(currMB))); + else + rdcost = distortion + (rate>0? (weight_cost(lambda, rate)): (weight_cost(lambda, 1)/2)); + +#if JCOST_OVERFLOWCHECK //overflow checking; + if(rdcost < distortion) + { + printf("lambda: %d, rate: %d, error: %s: %d\n", lambda, rate, __FILE__, __LINE__); + exit(-1); + } +#endif //end; + + // + if ((currSlice->slice_type != I_SLICE) && (p_Inp->BiasSkipRDO == 1) && (mode == 1) && (currMB->best_mode == 0) && (currMB->min_dcost > 4 * distortion) && (currMB->min_dcost > ((64 * (256 + 2 * p_Vid->mb_cr_size_y * p_Vid->mb_cr_size_x)) << LAMBDA_ACCURACY_BITS))) + { + currMB->min_rdcost = rdcost + 1; + } + + if ((rdcost >= currMB->min_rdcost) || + ((currMB->qp_scaled[0]) == 0 && p_Vid->active_sps->lossless_qpprime_flag == 1 && distortion != 0)) + { +#if FASTMODE + // Reordering RDCost comparison order of mode 0 and mode 1 in P_SLICE + // if RDcost of mode 0 and mode 1 is same, we choose best_mode is 0 + // This might not always be good since mode 0 is more biased towards rate than quality. + if((currSlice->slice_type != P_SLICE || mode != 0 || rdcost != currMB->min_rdcost) || is_FREXT_profile(p_Inp->ProfileIDC)) +#endif + return 0; + } + + + if ((currSlice->mb_aff_frame_flag) && (mode ? 0: ((currSlice->slice_type == B_SLICE) ? !currMB->cbp:1))) // AFF and current is skip + { + if (currMB->mbAddrX & 0x01) //bottom + { + if (prevMB->mb_type ? 0:((currSlice->slice_type == B_SLICE) ? !prevMB->cbp:1)) //top is skip + { + if (!(field_flag_inference(currMB) == currMB->mb_field)) //skip only allowed when correct inference + return 0; + } + } + } + + //===== U P D A T E M I N I M U M C O S T ===== + //----------------------------------------------------- + currMB->min_rdcost = rdcost; + currMB->min_dcost = distortion; + currMB->min_rate = weight_cost(lambda, coeff_rate); + currMB->min_bits = rate; + +#ifdef BEST_NZ_COEFF + for (j=0;j<4;j++) + memcpy(&p_Vid->gaaiMBAFF_NZCoeff[j][0], &p_Vid->nz_coeff[currMB->mbAddrX][j][0], (4 + p_Vid->num_blk8x8_uv) * sizeof(int)); +#endif + + return 1; +} + +/*! + ************************************************************************************* + * \brief + * Initialize best mode information + ************************************************************************************* + */ +void init_md_best(BestMode *best) +{ + best->cbp = 0; + best->cost = DISTBLK_MAX; //(1i64<<(20+LAMBDA_ACCURACY_BITS)); + best->rdcost = 1e20; + best->dcost = 1e20; + best->rate = 1e20; + best->c_imode = 0; + best->i16offset = 0; + best->mode = 10; +} + +/*! + ************************************************************************************* + * \brief + * Store macroblock parameters + ************************************************************************************* + */ +void store_macroblock_parameters (Macroblock *currMB, int mode) +{ + Slice *currSlice = currMB->p_Slice; + RDOPTStructure *p_RDO = currSlice->p_RDO; + VideoParameters *p_Vid = currMB->p_Vid; + InputParameters *p_Inp = currMB->p_Inp; + PicMotionParams **motion = p_Vid->enc_picture->mv_info; + + int i, j, ****i4p, ***i3p; + + //--- store best mode --- + currMB->best_mode = (short) mode; + currMB->best_c_imode = currMB->c_ipred_mode; + currMB->best_i16offset = currMB->i16offset; + currMB->best_i16mode = currMB->i16mode; + currMB->best_cbp = currMB->cbp; + + memcpy(currSlice->p_RDO->best8x8, currMB->b8x8, BLOCK_MULTIPLE * sizeof(Info8x8)); + + memcpy(currSlice->b4_intra_pred_modes, currMB->intra_pred_modes, MB_BLOCK_PARTITIONS * sizeof(char)); + memcpy(currSlice->b8_intra_pred_modes8x8,currMB->intra_pred_modes8x8, MB_BLOCK_PARTITIONS * sizeof(char)); + + for (j = 0 ; j < BLOCK_MULTIPLE; j++) + { + memcpy(&currSlice->b4_ipredmode[j * BLOCK_MULTIPLE],&p_Vid->ipredmode [currMB->block_y + j][currMB->block_x],BLOCK_MULTIPLE * sizeof(char)); + memcpy(&currSlice->b8_ipredmode8x8[j][0], &p_Vid->ipredmode8x8[currMB->block_y + j][currMB->block_x],BLOCK_MULTIPLE * sizeof(char)); + } + //--- reconstructed blocks ---- + copy_image_data_16x16(p_RDO->rec_mb[0], &p_Vid->enc_picture->imgY[currMB->pix_y], 0, currMB->pix_x); + + if((currSlice->slice_type == SP_SLICE) && p_Vid->sp2_frame_indicator==0) + { + for (j = 0; j < MB_BLOCK_SIZE; j++) + { + memcpy(p_RDO->lrec_rec[j], &p_Vid->lrec[currMB->pix_y+j][currMB->pix_x], MB_BLOCK_SIZE * sizeof(int));//store coefficients SP frame + } + } + + //modes 0, and 1 of a B frame + if (p_Vid->AdaptiveRounding && (currSlice->slice_type == B_SLICE) && mode <= 1) + { + if (currMB->luma_transform_size_8x8_flag) + store_adaptive_rounding_16x16( p_Vid, p_Vid->ARCofAdj8x8, mode); + else + store_adaptive_rounding_16x16( p_Vid, p_Vid->ARCofAdj4x4, mode); + } + + if (p_Vid->yuv_format != YUV400) + { + int k; + for (k = 0; k < 2; k++) + { + copy_image_data(p_RDO->rec_mb[k + 1], &p_Vid->enc_picture->imgUV[k][currMB->pix_c_y], 0, currMB->pix_c_x, p_Vid->mb_cr_size_x, p_Vid->mb_cr_size_y); + } + + if((currSlice->slice_type == SP_SLICE) && p_Vid->sp2_frame_indicator==0) + { + //store uv coefficients SP frame + for (k = 0; k < 2; k++) + { + for (j = 0; jmb_cr_size_y; j++) + { + memcpy(p_RDO->lrec_rec_uv[k][j],&p_Vid->lrec_uv[k][currMB->pix_c_y + j][currMB->pix_c_x], p_Vid->mb_cr_size_x * sizeof(int)); + } + } + } + } + + //--- store results of decoders --- + if (p_Inp->rdopt == 3) + { + errdo_store_best_MB(currMB); + } + + //--- coeff, cbp, kac --- + if (mode || currSlice->slice_type == B_SLICE) + { + i4p = p_RDO->cofAC; + p_RDO->cofAC = currSlice->cofAC; + currSlice->cofAC = i4p; + i3p = p_RDO->cofDC; + p_RDO->cofDC = currSlice->cofDC; + currSlice->cofDC = i3p; + p_RDO->cbp = currMB->cbp; + currSlice->curr_cbp[0] = currSlice->cmp_cbp[1]; + currSlice->curr_cbp[1] = currSlice->cmp_cbp[2]; + + currSlice->cur_cbp_blk[0] = currMB->cbp_blk; + } + else + { + currSlice->cur_cbp_blk[0] = p_RDO->cbp = 0; + currSlice->cmp_cbp[1] = currSlice->cmp_cbp[2] = 0; + } + + //--- store transform size --- + currMB->temp_transform_size_8x8_flag = currMB->luma_transform_size_8x8_flag; + + if (currSlice->slice_type != B_SLICE) + { + for (j = 0; j < 4; j++) + { + for (i = 0; i < 4; i++) + { + p_RDO->l0_refframe[j][i] = motion[currMB->block_y + j][currMB->block_x + i].ref_idx[LIST_0]; + } + } + } + else + { + for (j = 0; j < 4; j++) + { + for (i = 0; i < 4; i++) + { + p_RDO->l0_refframe[j][i] = motion[currMB->block_y + j][currMB->block_x + i].ref_idx[LIST_0]; + p_RDO->l1_refframe[j][i] = motion[currMB->block_y + j][currMB->block_x + i].ref_idx[LIST_1]; + } + } + } +} + +/*! +************************************************************************************* +* \brief +* Set stored macroblock parameters +************************************************************************************* +*/ +static void set_stored_macroblock_parameters_mpass (Macroblock *currMB) +{ + Slice *currSlice = currMB->p_Slice; + RDOPTStructure *p_RDO = currSlice->p_RDO; + VideoParameters *p_Vid = currMB->p_Vid; + InputParameters *p_Inp = currMB->p_Inp; + + imgpel **imgY = p_Vid->enc_picture->imgY; + imgpel ***imgUV = p_Vid->enc_picture->imgUV; + + int mode = currMB->best_mode; + int i, j, k, ****i4p, ***i3p; + int block_x, block_y; + char **ipredmodes = p_Vid->ipredmode; + + PicMotionParams **motion = p_Vid->enc_picture->mv_info; + + //===== reconstruction values ===== + + // Luma + copy_image_data_16x16(&imgY[currMB->pix_y], p_RDO->rec_mb[0], currMB->pix_x, 0); + + // Copy coding info into rddata structure for MBAFF or RDOQ + copy_image_data_16x16(currSlice->rddata->rec_mb[0], p_RDO->rec_mb[0], 0, 0); + + if((currSlice->slice_type == SP_SLICE) && p_Vid->sp2_frame_indicator==0) + { + for (j = 0; j < MB_BLOCK_SIZE; j++) + memcpy(&p_Vid->lrec[currMB->pix_y+j][currMB->pix_x], p_RDO->lrec_rec[j], MB_BLOCK_SIZE * sizeof(int)); //restore coeff SP frame + } + + if (p_Vid->AdaptiveRounding) + { + update_offset_params(currMB, mode, currMB->temp_transform_size_8x8_flag); + } + + if (p_Vid->yuv_format != YUV400) + { + for (k = 0; k < 2; k++) + { + copy_image_data(&imgUV[k][currMB->pix_c_y], p_RDO->rec_mb[k + 1], currMB->pix_c_x, 0, p_Vid->mb_cr_size_x, p_Vid->mb_cr_size_y); + } + + // Copy coding info into rddata structure for MBAFF or RDOQ + copy_image_data(currSlice->rddata->rec_mb[1], p_RDO->rec_mb[1], 0, 0, p_Vid->mb_cr_size_x, p_Vid->mb_cr_size_y); + copy_image_data(currSlice->rddata->rec_mb[2], p_RDO->rec_mb[2], 0, 0, p_Vid->mb_cr_size_x, p_Vid->mb_cr_size_y); + + if((currSlice->slice_type == SP_SLICE) && !p_Vid->sp2_frame_indicator) + { + for (k = 0; k < 2; k++) + { + for (j = 0; jmb_cr_size_y; j++) + { + memcpy(&p_Vid->lrec_uv[k][currMB->pix_c_y + j][currMB->pix_c_x], p_RDO->lrec_rec_uv[k][j], p_Vid->mb_cr_size_x * sizeof(int)); + } + } + } + } + + //===== coefficients and cbp ===== + i4p = p_RDO->cofAC; + p_RDO->cofAC = currSlice->cofAC; + currSlice->cofAC = i4p; + + i3p = p_RDO->cofDC; + p_RDO->cofDC = currSlice->cofDC; + currSlice->cofDC = i3p; + currMB->cbp = p_RDO->cbp; + currMB->cbp_blk = currSlice->cur_cbp_blk[0]; + currMB->cbp |= currSlice->curr_cbp[0]; + currMB->cbp |= currSlice->curr_cbp[1]; + currSlice->cmp_cbp[1] = currMB->cbp; + currSlice->cmp_cbp[2] = currMB->cbp; + + //==== macroblock type ==== + currMB->mb_type = (short) mode; + + memcpy(currMB->b8x8, currSlice->p_RDO->best8x8, BLOCK_MULTIPLE * sizeof(Info8x8)); + + // Copy coding info into rddata structure for MBAFF or RDOQ + currSlice->rddata->mode = mode; + currSlice->rddata->i16offset = currMB->i16offset; + currSlice->rddata->i16mode = currMB->i16mode; + currSlice->rddata->cbp = p_RDO->cbp; + currSlice->rddata->cbp_blk = currSlice->cur_cbp_blk[0]; + currSlice->rddata->mb_type = (short) mode; + + currSlice->rddata->prev_qp = currMB->prev_qp; + currSlice->rddata->prev_dqp = currMB->prev_dqp; + currSlice->rddata->qp = currMB->qp; + currSlice->rddata->prev_cbp = currMB->prev_cbp; + + memcpy(currSlice->rddata->cofAC[0][0][0], currSlice->cofAC[0][0][0], (4+p_Vid->num_blk8x8_uv) * 4 * 2 * 65 * sizeof(int)); + memcpy(currSlice->rddata->cofDC[0][0], currSlice->cofDC[0][0], 3 * 2 * 18 * sizeof(int)); + + memcpy(currSlice->rddata->b8x8, currSlice->p_RDO->best8x8, BLOCK_MULTIPLE * sizeof(Info8x8)); + + //if P8x8 mode and transform size 4x4 choosen, restore motion vector data for this transform size + if (mode == P8x8 && !currMB->temp_transform_size_8x8_flag && p_Inp->Transform8x8Mode && (currMB->valid_8x8 == TRUE)) + { + RestoreMV8x8(currSlice, 1); + } + + //==== transform size flag ==== + if (p_Vid->P444_joined) + { + if (((currMB->cbp == 0) && currSlice->cmp_cbp[1] == 0 && currSlice->cmp_cbp[2] == 0) && (currMB->mb_type != I4MB && currMB->mb_type != I8MB)) + currMB->luma_transform_size_8x8_flag = FALSE; + else + currMB->luma_transform_size_8x8_flag = currMB->temp_transform_size_8x8_flag; + } + else + { + + if (((currMB->cbp & 15) == 0) && (currMB->mb_type != I4MB && currMB->mb_type != I8MB)) + currMB->luma_transform_size_8x8_flag = FALSE; + else + currMB->luma_transform_size_8x8_flag = currMB->temp_transform_size_8x8_flag; + } + + currSlice->rddata->luma_transform_size_8x8_flag = currMB->luma_transform_size_8x8_flag; + + if (p_Inp->rdopt == 3) + { + errdo_get_best_MB(currMB); + } + + //==== reference frames ===== + for (j = 0; j < 4; j++) + { + block_y = currMB->block_y + j; + for (i = 0; i < 4; i++) + { + block_x = currMB->block_x + i; + k = 2*(j >> 1)+(i >> 1); + + // backward prediction or intra + if ((currMB->b8x8[k].pdir == 1) || IS_INTRA(currMB)) + { + motion[block_y][block_x].ref_idx [LIST_0] = -1; + motion[block_y][block_x].mv [LIST_0] = zero_mv; + motion[block_y][block_x].ref_pic [LIST_0] = NULL; + + // MBAFF or RDOQ + currSlice->rddata->refar[LIST_0][j][i] = -1; + } + else + { + if (currMB->b8x8[k].bipred && (currMB->b8x8[k].pdir == 2) && is_bipred_enabled(p_Vid, currMB->mb_type)) + { + motion[block_y][block_x].ref_idx [LIST_0] = 0; + motion[block_y][block_x].ref_pic [LIST_0] = currSlice->listX[LIST_0 + currMB->list_offset][0]; + motion[block_y][block_x].mv [LIST_0] = currSlice->bipred_mv[currMB->b8x8[k].bipred - 1][LIST_0][0][(short) currMB->b8x8[k].mode][j][i]; + + // MBAFF or RDOQ + currSlice->rddata->refar[LIST_0][j][i] = 0; + } + else + { + char cur_ref = p_RDO->l0_refframe[j][i]; + motion[block_y][block_x].ref_idx [LIST_0] = cur_ref; + if(cur_ref >=0) + { + motion[block_y][block_x].ref_pic [LIST_0] = currSlice->listX[LIST_0 + currMB->list_offset][(short)cur_ref]; + motion[block_y][block_x].mv [LIST_0] = currSlice->all_mv[LIST_0][(short)cur_ref][(short) currMB->b8x8[k].mode][j][i]; + } + // MBAFF or RDOQ + currSlice->rddata->refar[LIST_0][j][i] = cur_ref; + } + } + + // forward prediction or intra + if ((currMB->b8x8[k].pdir == 0) || IS_INTRA(currMB)) + { + motion[block_y][block_x].ref_idx [LIST_1] = -1; + motion[block_y][block_x].ref_pic [LIST_1] = NULL; + motion[block_y][block_x].mv [LIST_1] = zero_mv; + + // MBAFF or RDOQ + currSlice->rddata->refar[LIST_1][j][i] = -1; + } + } + } + + if (currSlice->slice_type == B_SLICE) + { + for (j=0; j<4; j++) + { + block_y = currMB->block_y + j; + for (i=0; i<4; i++) + { + block_x = currMB->block_x + i; + k = 2*(j >> 1)+(i >> 1); + + // forward + if (IS_INTRA(currMB)||(currMB->b8x8[k].pdir == 0)) + { + motion[block_y][block_x].ref_idx [LIST_1] = -1; + motion[block_y][block_x].ref_pic [LIST_1] = NULL; + motion[block_y][block_x].mv [LIST_1] = zero_mv; + + // MBAFF or RDOQ + currSlice->rddata->refar[LIST_1][j][i] = -1; + } + else + { + if (currMB->b8x8[k].bipred && (currMB->b8x8[k].pdir == 2) && is_bipred_enabled(p_Vid, currMB->mb_type)) + { + motion[block_y][block_x].ref_idx [LIST_1] = 0; + motion[block_y][block_x].ref_pic [LIST_1] = currSlice->listX[LIST_1 + currMB->list_offset][0]; + motion[block_y][block_x].mv [LIST_1] = currSlice->bipred_mv[currMB->b8x8[k].bipred - 1][LIST_1][0][(short) currMB->b8x8[k].mode][j][i]; + + // MBAFF or RDOQ + currSlice->rddata->refar[LIST_1][j][i] = 0; + } + else + { + motion[block_y][block_x].ref_idx [LIST_1] = p_RDO->l1_refframe[j][i]; + motion[block_y][block_x].ref_pic [LIST_1] = currSlice->listX[LIST_1 + currMB->list_offset][(short)p_RDO->l1_refframe[j][i]]; + motion[block_y][block_x].mv [LIST_1] = currSlice->all_mv[LIST_1][(short)p_RDO->l1_refframe[j][i]][(short) currMB->b8x8[k].mode][j][i]; + + // MBAFF or RDOQ + currSlice->rddata->refar[LIST_1][j][i] = p_RDO->l1_refframe[j][i]; + } + } + } + } + } + + //==== intra prediction modes ==== + currMB->c_ipred_mode = currMB->best_c_imode; + currMB->i16offset = currMB->best_i16offset; + currMB->i16mode = currMB->best_i16mode; + currMB->cbp = currMB->best_cbp; + + if(currMB->mb_type == I8MB) + { + memcpy(currMB->intra_pred_modes8x8,currSlice->b8_intra_pred_modes8x8, MB_BLOCK_PARTITIONS * sizeof(char)); + memcpy(currMB->intra_pred_modes ,currSlice->b8_intra_pred_modes8x8, MB_BLOCK_PARTITIONS * sizeof(char)); + for(j = 0; j < BLOCK_MULTIPLE; j++) + { + memcpy(&p_Vid->ipredmode [currMB->block_y+j][currMB->block_x], currSlice->b8_ipredmode8x8[j], BLOCK_MULTIPLE * sizeof(char)); + memcpy(&p_Vid->ipredmode8x8[currMB->block_y+j][currMB->block_x], currSlice->b8_ipredmode8x8[j], BLOCK_MULTIPLE * sizeof(char)); + } + } + else if (mode!=I4MB && mode!=I8MB) + { + memset(currMB->intra_pred_modes,DC_PRED, MB_BLOCK_PARTITIONS * sizeof(char)); + for(j = currMB->block_y; j < currMB->block_y + BLOCK_MULTIPLE; j++) + memset(&p_Vid->ipredmode[j][currMB->block_x], DC_PRED, BLOCK_MULTIPLE * sizeof(char)); + } + // Residue Color Transform + else if (mode == I4MB) + { + memcpy(currMB->intra_pred_modes,currSlice->b4_intra_pred_modes, MB_BLOCK_PARTITIONS * sizeof(char)); + for(j = 0; j < BLOCK_MULTIPLE; j++) + memcpy(&p_Vid->ipredmode[currMB->block_y + j][currMB->block_x],&currSlice->b4_ipredmode[BLOCK_MULTIPLE * j], BLOCK_MULTIPLE * sizeof(char)); + } + + // Copy coding info into rddata structure for MBAFF or RDOQ + currSlice->rddata->c_ipred_mode = currMB->c_ipred_mode; + currSlice->rddata->i16offset = currMB->i16offset; + currSlice->rddata->i16mode = currMB->i16mode; + currSlice->rddata->cbp = currMB->cbp; + currSlice->rddata->cbp_blk = currMB->cbp_blk; + memcpy(currSlice->rddata->intra_pred_modes,currMB->intra_pred_modes, MB_BLOCK_PARTITIONS * sizeof(char)); + memcpy(currSlice->rddata->intra_pred_modes8x8,currMB->intra_pred_modes8x8, MB_BLOCK_PARTITIONS * sizeof(char)); + for(j = currMB->block_y; j < currMB->block_y + BLOCK_MULTIPLE; j++) + memcpy(&currSlice->rddata->ipredmode[j][currMB->block_x],&ipredmodes[j][currMB->block_x], BLOCK_MULTIPLE * sizeof(char)); + + //==== motion vectors ===== + currSlice->set_motion_vectors_mb (currMB); +} + +/*! +************************************************************************************* +* \brief +* Set stored macroblock parameters (non RDOQ or MBAFF) +************************************************************************************* +*/ +static void set_stored_macroblock_parameters (Macroblock *currMB) +{ + Slice *currSlice = currMB->p_Slice; + RDOPTStructure *p_RDO = currSlice->p_RDO; + VideoParameters *p_Vid = currMB->p_Vid; + InputParameters *p_Inp = currMB->p_Inp; + + imgpel **imgY = p_Vid->enc_picture->imgY; + imgpel ***imgUV = p_Vid->enc_picture->imgUV; + + int mode = currMB->best_mode; + int i, j, k, ****i4p, ***i3p; + int block_x, block_y; + + PicMotionParams **motion = p_Vid->enc_picture->mv_info; + + //===== reconstruction values ===== + + // Luma + copy_image_data_16x16(&imgY[currMB->pix_y], p_RDO->rec_mb[0], currMB->pix_x, 0); + + if (p_Vid->AdaptiveRounding) + { + update_offset_params(currMB, mode, currMB->temp_transform_size_8x8_flag); + } + + if (p_Vid->yuv_format != YUV400) + { + for (k = 0; k < 2; k++) + { + copy_image_data(&imgUV[k][currMB->pix_c_y], p_RDO->rec_mb[k + 1], currMB->pix_c_x, 0, p_Vid->mb_cr_size_x, p_Vid->mb_cr_size_y); + } + } + + //===== coefficients and cbp ===== + i4p = p_RDO->cofAC; + p_RDO->cofAC = currSlice->cofAC; + currSlice->cofAC = i4p; + + i3p = p_RDO->cofDC; + p_RDO->cofDC = currSlice->cofDC; + currSlice->cofDC = i3p; + currMB->cbp = p_RDO->cbp; + currMB->cbp_blk = currSlice->cur_cbp_blk[0]; + currMB->cbp |= currSlice->curr_cbp[0]; + currMB->cbp |= currSlice->curr_cbp[1]; + currSlice->cmp_cbp[1] = currMB->cbp; + currSlice->cmp_cbp[2] = currMB->cbp; + + //==== macroblock type ==== + currMB->mb_type = (short) mode; + + memcpy(currMB->b8x8, currSlice->p_RDO->best8x8, BLOCK_MULTIPLE * sizeof(Info8x8)); + + + //if P8x8 mode and transform size 4x4 choosen, restore motion vector data for this transform size + if (mode == P8x8 && !currMB->temp_transform_size_8x8_flag && p_Inp->Transform8x8Mode && (currMB->valid_8x8 == TRUE)) + { + RestoreMV8x8(currSlice, 1); + } + + //==== transform size flag ==== + if (p_Vid->P444_joined) + { + if (((currMB->cbp == 0) && currSlice->cmp_cbp[1] == 0 && currSlice->cmp_cbp[2] == 0) && (currMB->mb_type != I4MB && currMB->mb_type != I8MB)) + currMB->luma_transform_size_8x8_flag = FALSE; + else + currMB->luma_transform_size_8x8_flag = currMB->temp_transform_size_8x8_flag; + } + else + { + + if (((currMB->cbp & 15) == 0) && (currMB->mb_type != I4MB && currMB->mb_type != I8MB)) + currMB->luma_transform_size_8x8_flag = FALSE; + else + currMB->luma_transform_size_8x8_flag = currMB->temp_transform_size_8x8_flag; + } + + currSlice->rddata->luma_transform_size_8x8_flag = currMB->luma_transform_size_8x8_flag; + + if (p_Inp->rdopt == 3) + { + errdo_get_best_MB(currMB); + } + + //==== reference frames ===== + for (j = 0; j < 4; j++) + { + block_y = currMB->block_y + j; + for (i = 0; i < 4; i++) + { + block_x = currMB->block_x + i; + k = 2*(j >> 1)+(i >> 1); + + // backward prediction or intra + if ((currMB->b8x8[k].pdir == 1) || IS_INTRA(currMB)) + { + motion[block_y][block_x].ref_idx [LIST_0] = -1; + motion[block_y][block_x].ref_pic [LIST_0] = NULL; + motion[block_y][block_x].mv [LIST_0] = zero_mv; + } + else + { + if (currMB->b8x8[k].bipred && (currMB->b8x8[k].pdir == 2) && is_bipred_enabled(p_Vid, currMB->mb_type)) + { + motion[block_y][block_x].ref_idx [LIST_0] = 0; + motion[block_y][block_x].ref_pic [LIST_0] = currSlice->listX[LIST_0 + currMB->list_offset][0]; + motion[block_y][block_x].mv [LIST_0] = currSlice->bipred_mv[currMB->b8x8[k].bipred - 1][LIST_0][0][(short) currMB->b8x8[k].mode][j][i]; + } + else + { + char cur_ref = p_RDO->l0_refframe[j][i]; + motion[block_y][block_x].ref_idx [LIST_0] = cur_ref; + motion[block_y][block_x].ref_pic [LIST_0] = currSlice->listX[LIST_0 + currMB->list_offset][(short)cur_ref]; + motion[block_y][block_x].mv [LIST_0] = currSlice->all_mv[LIST_0][(short)cur_ref][(short) currMB->b8x8[k].mode][j][i]; + } + } + + // forward prediction or intra + if ((currMB->b8x8[k].pdir == 0) || IS_INTRA(currMB)) + { + motion[block_y][block_x].ref_idx [LIST_1] = -1; + motion[block_y][block_x].ref_pic [LIST_1] = NULL; + motion[block_y][block_x].mv [LIST_1] = zero_mv; + } + } + } + + if (currSlice->slice_type == B_SLICE) + { + for (j=0; j<4; j++) + { + block_y = currMB->block_y + j; + for (i=0; i<4; i++) + { + block_x = currMB->block_x + i; + k = 2*(j >> 1)+(i >> 1); + + // forward + if (IS_INTRA(currMB)||(currMB->b8x8[k].pdir == 0)) + { + motion[block_y][block_x].ref_idx [LIST_1] = -1; + motion[block_y][block_x].ref_pic [LIST_1] = NULL; + motion[block_y][block_x].mv [LIST_1] = zero_mv; + } + else + { + if (currMB->b8x8[k].bipred && (currMB->b8x8[k].pdir == 2) && is_bipred_enabled(p_Vid, currMB->mb_type)) + { + motion[block_y][block_x].ref_idx [LIST_1] = 0; + motion[block_y][block_x].ref_pic [LIST_1] = currSlice->listX[LIST_1 + currMB->list_offset][0]; + motion[block_y][block_x].mv [LIST_1] = currSlice->bipred_mv[currMB->b8x8[k].bipred - 1][LIST_1][0][(short) currMB->b8x8[k].mode][j][i]; + } + else + { + motion[block_y][block_x].ref_idx [LIST_1] = p_RDO->l1_refframe[j][i]; + motion[block_y][block_x].ref_pic [LIST_1] = currSlice->listX[LIST_1 + currMB->list_offset][(short)p_RDO->l1_refframe[j][i]]; + motion[block_y][block_x].mv [LIST_1] = currSlice->all_mv[LIST_1][(short)p_RDO->l1_refframe[j][i]][(short) currMB->b8x8[k].mode][j][i]; + } + } + } + } + } + + //==== intra prediction modes ==== + currMB->c_ipred_mode = currMB->best_c_imode; + currMB->i16offset = currMB->best_i16offset; + currMB->i16mode = currMB->best_i16mode; + currMB->cbp = currMB->best_cbp; + + if(currMB->mb_type == I8MB) + { + memcpy(currMB->intra_pred_modes8x8,currSlice->b8_intra_pred_modes8x8, MB_BLOCK_PARTITIONS * sizeof(char)); + memcpy(currMB->intra_pred_modes ,currSlice->b8_intra_pred_modes8x8, MB_BLOCK_PARTITIONS * sizeof(char)); + for(j = 0; j < BLOCK_MULTIPLE; j++) + { + memcpy(&p_Vid->ipredmode [currMB->block_y+j][currMB->block_x], currSlice->b8_ipredmode8x8[j], BLOCK_MULTIPLE * sizeof(char)); + memcpy(&p_Vid->ipredmode8x8[currMB->block_y+j][currMB->block_x], currSlice->b8_ipredmode8x8[j], BLOCK_MULTIPLE * sizeof(char)); + } + } + else if (mode!=I4MB && mode!=I8MB) + { + memset(currMB->intra_pred_modes,DC_PRED, MB_BLOCK_PARTITIONS * sizeof(char)); + for(j = currMB->block_y; j < currMB->block_y + BLOCK_MULTIPLE; j++) + memset(&p_Vid->ipredmode[j][currMB->block_x], DC_PRED, BLOCK_MULTIPLE * sizeof(char)); + } + // Residue Color Transform + else if (mode == I4MB) + { + memcpy(currMB->intra_pred_modes,currSlice->b4_intra_pred_modes, MB_BLOCK_PARTITIONS * sizeof(char)); + for(j = 0; j < BLOCK_MULTIPLE; j++) + memcpy(&p_Vid->ipredmode[currMB->block_y + j][currMB->block_x],&currSlice->b4_ipredmode[BLOCK_MULTIPLE * j], BLOCK_MULTIPLE * sizeof(char)); + } + + //==== motion vectors ===== + currSlice->set_motion_vectors_mb (currMB); +} + + +/*! +************************************************************************************* +* \brief +* Set stored macroblock parameters (non RDOQ or MBAFF) +************************************************************************************* +*/ +static void set_stored_macroblock_parameters_sp (Macroblock *currMB) +{ + Slice *currSlice = currMB->p_Slice; + RDOPTStructure *p_RDO = currSlice->p_RDO; + VideoParameters *p_Vid = currMB->p_Vid; + InputParameters *p_Inp = currMB->p_Inp; + + imgpel **imgY = p_Vid->enc_picture->imgY; + imgpel ***imgUV = p_Vid->enc_picture->imgUV; + + int mode = currMB->best_mode; + int i, j, k, ****i4p, ***i3p; + int block_x, block_y; + PicMotionParams **motion = p_Vid->enc_picture->mv_info; + + //===== reconstruction values ===== + + // Luma + copy_image_data_16x16(&imgY[currMB->pix_y], p_RDO->rec_mb[0], currMB->pix_x, 0); + + if((currSlice->slice_type == SP_SLICE) && p_Vid->sp2_frame_indicator==0) + { + for (j = 0; j < MB_BLOCK_SIZE; j++) + memcpy(&p_Vid->lrec[currMB->pix_y+j][currMB->pix_x], p_RDO->lrec_rec[j], MB_BLOCK_SIZE * sizeof(int)); //restore coeff SP frame + } + + if (p_Vid->AdaptiveRounding) + { + update_offset_params(currMB, mode, currMB->temp_transform_size_8x8_flag); + } + + if (p_Vid->yuv_format != YUV400) + { + for (k = 0; k < 2; k++) + { + copy_image_data(&imgUV[k][currMB->pix_c_y], p_RDO->rec_mb[k + 1], currMB->pix_c_x, 0, p_Vid->mb_cr_size_x, p_Vid->mb_cr_size_y); + } + + if((currSlice->slice_type == SP_SLICE) && !p_Vid->sp2_frame_indicator) + { + for (k = 0; k < 2; k++) + { + for (j = 0; jmb_cr_size_y; j++) + { + memcpy(&p_Vid->lrec_uv[k][currMB->pix_c_y + j][currMB->pix_c_x], p_RDO->lrec_rec_uv[k][j], p_Vid->mb_cr_size_x * sizeof(int)); + } + } + } + } + + //===== coefficients and cbp ===== + i4p = p_RDO->cofAC; + p_RDO->cofAC = currSlice->cofAC; + currSlice->cofAC = i4p; + + i3p = p_RDO->cofDC; + p_RDO->cofDC = currSlice->cofDC; + currSlice->cofDC = i3p; + currMB->cbp = p_RDO->cbp; + currMB->cbp_blk = currSlice->cur_cbp_blk[0]; + currMB->cbp |= currSlice->curr_cbp[0]; + currMB->cbp |= currSlice->curr_cbp[1]; + currSlice->cmp_cbp[1] = currMB->cbp; + currSlice->cmp_cbp[2] = currMB->cbp; + + //==== macroblock type ==== + currMB->mb_type = (short) mode; + + memcpy(currMB->b8x8, currSlice->p_RDO->best8x8, BLOCK_MULTIPLE * sizeof(Info8x8)); + + //if P8x8 mode and transform size 4x4 choosen, restore motion vector data for this transform size + if (mode == P8x8 && !currMB->temp_transform_size_8x8_flag && p_Inp->Transform8x8Mode && (currMB->valid_8x8 == TRUE)) + { + RestoreMV8x8(currSlice, 1); + } + + //==== transform size flag ==== + if (p_Vid->P444_joined) + { + if (((currMB->cbp == 0) && currSlice->cmp_cbp[1] == 0 && currSlice->cmp_cbp[2] == 0) && (currMB->mb_type != I4MB && currMB->mb_type != I8MB)) + currMB->luma_transform_size_8x8_flag = FALSE; + else + currMB->luma_transform_size_8x8_flag = currMB->temp_transform_size_8x8_flag; + } + else + { + + if (((currMB->cbp & 15) == 0) && (currMB->mb_type != I4MB && currMB->mb_type != I8MB)) + currMB->luma_transform_size_8x8_flag = FALSE; + else + currMB->luma_transform_size_8x8_flag = currMB->temp_transform_size_8x8_flag; + } + + currSlice->rddata->luma_transform_size_8x8_flag = currMB->luma_transform_size_8x8_flag; + + if (p_Inp->rdopt == 3) + { + errdo_get_best_MB(currMB); + } + + //==== reference frames ===== + for (j = 0; j < 4; j++) + { + block_y = currMB->block_y + j; + for (i = 0; i < 4; i++) + { + block_x = currMB->block_x + i; + k = 2*(j >> 1)+(i >> 1); + + // backward prediction or intra + if ((currMB->b8x8[k].pdir == 1) || IS_INTRA(currMB)) + { + motion[block_y][block_x].ref_idx [LIST_0] = -1; + motion[block_y][block_x].ref_pic [LIST_0] = NULL; + motion[block_y][block_x].mv [LIST_0] = zero_mv; + } + else + { + if (currMB->b8x8[k].bipred && (currMB->b8x8[k].pdir == 2) && is_bipred_enabled(p_Vid, currMB->mb_type)) + { + motion[block_y][block_x].ref_idx [LIST_0] = 0; + motion[block_y][block_x].ref_pic [LIST_0] = currSlice->listX[LIST_0 + currMB->list_offset][0]; + motion[block_y][block_x].mv [LIST_0] = currSlice->bipred_mv[currMB->b8x8[k].bipred - 1][LIST_0][0][(short) currMB->b8x8[k].mode][j][i]; + } + else + { + char cur_ref = p_RDO->l0_refframe[j][i]; + motion[block_y][block_x].ref_idx [LIST_0] = cur_ref; + motion[block_y][block_x].ref_pic [LIST_0] = currSlice->listX[LIST_0 + currMB->list_offset][(short)cur_ref]; + motion[block_y][block_x].mv [LIST_0] = currSlice->all_mv[LIST_0][(short)cur_ref][(short) currMB->b8x8[k].mode][j][i]; + } + } + + // forward prediction or intra + if ((currMB->b8x8[k].pdir == 0) || IS_INTRA(currMB)) + { + motion[block_y][block_x].ref_idx [LIST_1] = -1; + motion[block_y][block_x].ref_pic [LIST_1] = NULL; + motion[block_y][block_x].mv [LIST_1] = zero_mv; + } + } + } + + if (currSlice->slice_type == B_SLICE) + { + for (j=0; j<4; j++) + { + block_y = currMB->block_y + j; + for (i=0; i<4; i++) + { + block_x = currMB->block_x + i; + k = 2*(j >> 1)+(i >> 1); + + // forward + if (IS_INTRA(currMB)||(currMB->b8x8[k].pdir == 0)) + { + motion[block_y][block_x].ref_idx [LIST_1] = -1; + motion[block_y][block_x].ref_pic [LIST_1] = NULL; + motion[block_y][block_x].mv [LIST_1] = zero_mv; + } + else + { + if (currMB->b8x8[k].bipred && (currMB->b8x8[k].pdir == 2) && is_bipred_enabled(p_Vid, currMB->mb_type)) + { + motion[block_y][block_x].ref_idx [LIST_1] = 0; + motion[block_y][block_x].ref_pic [LIST_1] = currSlice->listX[LIST_1 + currMB->list_offset][0]; + motion[block_y][block_x].mv [LIST_1] = currSlice->bipred_mv[currMB->b8x8[k].bipred - 1][LIST_1][0][(short) currMB->b8x8[k].mode][j][i]; + } + else + { + motion[block_y][block_x].ref_idx [LIST_1] = p_RDO->l1_refframe[j][i]; + motion[block_y][block_x].ref_pic [LIST_1] = currSlice->listX[LIST_1 + currMB->list_offset][(short)p_RDO->l1_refframe[j][i]]; + motion[block_y][block_x].mv [LIST_1] = currSlice->all_mv[LIST_1][(short)p_RDO->l1_refframe[j][i]][(short) currMB->b8x8[k].mode][j][i]; + } + } + } + } + } + + //==== intra prediction modes ==== + currMB->c_ipred_mode = currMB->best_c_imode; + currMB->i16offset = currMB->best_i16offset; + currMB->i16mode = currMB->best_i16mode; + currMB->cbp = currMB->best_cbp; + + if(currMB->mb_type == I8MB) + { + memcpy(currMB->intra_pred_modes8x8,currSlice->b8_intra_pred_modes8x8, MB_BLOCK_PARTITIONS * sizeof(char)); + memcpy(currMB->intra_pred_modes ,currSlice->b8_intra_pred_modes8x8, MB_BLOCK_PARTITIONS * sizeof(char)); + for(j = 0; j < BLOCK_MULTIPLE; j++) + { + memcpy(&p_Vid->ipredmode [currMB->block_y+j][currMB->block_x], currSlice->b8_ipredmode8x8[j], BLOCK_MULTIPLE * sizeof(char)); + memcpy(&p_Vid->ipredmode8x8[currMB->block_y+j][currMB->block_x], currSlice->b8_ipredmode8x8[j], BLOCK_MULTIPLE * sizeof(char)); + } + } + else if (mode!=I4MB && mode!=I8MB) + { + memset(currMB->intra_pred_modes,DC_PRED, MB_BLOCK_PARTITIONS * sizeof(char)); + for(j = currMB->block_y; j < currMB->block_y + BLOCK_MULTIPLE; j++) + memset(&p_Vid->ipredmode[j][currMB->block_x], DC_PRED, BLOCK_MULTIPLE * sizeof(char)); + } + // Residue Color Transform + else if (mode == I4MB) + { + memcpy(currMB->intra_pred_modes,currSlice->b4_intra_pred_modes, MB_BLOCK_PARTITIONS * sizeof(char)); + for(j = 0; j < BLOCK_MULTIPLE; j++) + memcpy(&p_Vid->ipredmode[currMB->block_y + j][currMB->block_x],&currSlice->b4_ipredmode[BLOCK_MULTIPLE * j], BLOCK_MULTIPLE * sizeof(char)); + } + + //==== motion vectors ===== + currSlice->set_motion_vectors_mb (currMB); +} + + +/*! + ************************************************************************************* + * \brief + * Set reference frames and motion vectors + ************************************************************************************* + */ +void set_ref_and_motion_vectors_P_slice (Macroblock *currMB, PicMotionParams **motion, Info8x8 *part, int block) +{ + int mode = part->mode; + int j = 0, i; + int pmode = (mode==1||mode==2||mode==3 ? mode : 4); + int j0 = ((block >> 1)<<1); + int i0 = ((block & 0x01)<<1); + int i1 = i0 + part_size[pmode][0]; + int j1 = j0 + part_size[pmode][1]; + PicMotionParams *mv = NULL; + + if (part->pdir < 0) + { + for (j = currMB->block_y + j0; j < currMB->block_y + j1; j++) + { + for (i = currMB->block_x + i0; i < currMB->block_x + i1; i++) + { + mv = &motion[j][i]; + mv->ref_pic[LIST_0] = NULL; + mv->ref_pic[LIST_1] = NULL; + mv->mv [LIST_0] = zero_mv; + mv->mv [LIST_1] = zero_mv; + mv->ref_idx[LIST_0] = -1; + mv->ref_idx[LIST_1] = -1; + } + } + return; + } + else + { + Slice *currSlice = currMB->p_Slice; + int block_y; + int fwref = part->ref[LIST_0]; + for (j = j0; j < j1; j++) + { + block_y = currMB->block_y + j; + for (i = i0; i < i1; i++) + { + mv = &motion[block_y][currMB->block_x + i]; + mv->ref_pic[LIST_0] = currSlice->listX[LIST_0+currMB->list_offset][fwref];; + mv->mv [LIST_0] = currSlice->all_mv[LIST_0][fwref][mode][j][i]; + mv->ref_idx[LIST_0] = (char) fwref; + } + } + return; + } +} + + +/*! + ************************************************************************************* + * \brief + * Set reference frames and motion vectors + ************************************************************************************* + */ +static void set_ref_and_motion_vectors_B_slice (Macroblock *currMB, PicMotionParams **motion, Info8x8 *part, int block) +{ + int mode = part->mode; + int i, j=0; + int pmode = (mode==1||mode==2||mode==3 ? mode : 4); + int j0 = ((block >> 1)<<1); + int i0 = ((block & 0x01)<<1); + int i1 = i0 + part_size[pmode][0]; + int j1 = j0 + part_size[pmode][1]; + PicMotionParams *mv = NULL; + + if (part->pdir < 0) + { + for (j = currMB->block_y + j0; j < currMB->block_y + j1; j++) + { + for (i = currMB->block_x + i0; i < currMB->block_x + i1; i++) + { + mv = &motion[j][i]; + mv->ref_pic[LIST_0] = NULL; + mv->ref_pic[LIST_1] = NULL; + mv->mv [LIST_0] = zero_mv; + mv->mv [LIST_1] = zero_mv; + mv->ref_idx[LIST_0] = -1; + mv->ref_idx[LIST_1] = -1; + } + } + return; + } + else + { + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + + int block_x, block_y; + + if ((part->pdir == 0 || part->pdir == 2)) + { + if (part->bipred && (part->pdir == 2) && is_bipred_enabled(p_Vid, mode)) + { + for (j=j0; jblock_y + j; + for (i=i0; iblock_x + i; + + motion[block_y][block_x].mv [LIST_0] = currSlice->bipred_mv[part->bipred - 1][LIST_0][0][mode][j][i]; + motion[block_y][block_x].ref_idx [LIST_0] = 0; + motion[block_y][block_x].ref_pic [LIST_0] = currSlice->listX[LIST_0 + currMB->list_offset][0]; + } + } + } + else + { + if (mode==0) + { + int fwref; + for (j=j0; jblock_y + j; + for (i=i0; iblock_x + i; + fwref = currSlice->direct_ref_idx[block_y][block_x][LIST_0]; + motion[block_y][block_x].ref_pic[LIST_0] = currSlice->listX[LIST_0 + currMB->list_offset][fwref]; + motion[block_y][block_x].mv [LIST_0] = currSlice->all_mv[LIST_0][fwref][mode][j][i]; + motion[block_y][block_x].ref_idx[LIST_0] = (char) fwref; + } + } + } + else + { + int fwref = part->ref[LIST_0]; + for (j=j0; jblock_y + j; + for (i = i0; i < i1; i++) + { + block_x = currMB->block_x + i; + motion[block_y][block_x].ref_pic[LIST_0] = currSlice->listX[LIST_0+currMB->list_offset][fwref]; + motion[block_y][block_x].mv [LIST_0] = currSlice->all_mv[LIST_0][fwref][mode][j][i]; + motion[block_y][block_x].ref_idx[LIST_0] = (char) fwref; + } + } + } + } + } + else + { + for (j=currMB->block_y + j0; j < currMB->block_y + j1; j++) + { + for (i = currMB->block_x + i0; i < currMB->block_x + i1; i++) + { + motion[j][i].ref_pic [LIST_0] = NULL; + motion[j][i].mv [LIST_0] = zero_mv; + motion[j][i].ref_idx [LIST_0] = -1; + } + } + } + + + if ((part->pdir==1 || part->pdir==2)) + { + if (part->bipred && (part->pdir == 2) && is_bipred_enabled(p_Vid, mode)) + { + for (j=j0; jblock_y + j; + + for (i=i0; iblock_x + i; + + motion[block_y][block_x].mv [LIST_1] = currSlice->bipred_mv[part->bipred - 1][LIST_1][0][mode][j][i]; + motion[block_y][block_x].ref_idx [LIST_1] = 0; + motion[block_y][block_x].ref_pic [LIST_1] = currSlice->listX[LIST_1+currMB->list_offset][0]; + } + } + } + else + { + if (mode==0) + { + int bwref = part->ref[LIST_1]; + for (j=j0; jblock_y + j; + + for (i = i0; i < i1; i++) + { + block_x = currMB->block_x + i; + if (bwref != currSlice->direct_ref_idx[block_y][block_x][LIST_1]) + printf("error\n"); + motion[block_y][block_x].ref_pic [LIST_1] = currSlice->listX[LIST_1+currMB->list_offset][bwref]; + motion[block_y][block_x].mv [LIST_1] = currSlice->all_mv[LIST_1][bwref][mode][j][i]; + motion[block_y][block_x].ref_idx [LIST_1] = (char) bwref; // currSlice->direct_ref_idx[block_y][block_x][LIST_1]; + //motion[block_y][block_x].ref_pic [LIST_1] = currSlice->listX[LIST_1+currMB->list_offset][(short)motion[block_y][block_x].ref_idx [LIST_1]]; + } + } + } + else + { + int bwref = part->ref[LIST_1]; + for (j=j0; jblock_y + j; + for (i = i0; i < i1; i++) + { + block_x = currMB->block_x + i; + + motion[block_y][block_x].ref_pic [LIST_1] = currSlice->listX[LIST_1+currMB->list_offset][bwref]; + motion[block_y][block_x].mv [LIST_1] = currSlice->all_mv[LIST_1][bwref][mode][j][i]; + motion[block_y][block_x].ref_idx [LIST_1] = (char) bwref; + } + } + } + } + } + else + { + for (block_y = currMB->block_y + j0; block_y < currMB->block_y + j1; block_y++) + { + for (block_x = currMB->block_x + i0; block_x < currMB->block_x + i1; block_x++) + { + motion[block_y][block_x].ref_pic[LIST_1] = NULL; + motion[block_y][block_x].mv [LIST_1] = zero_mv; + motion[block_y][block_x].ref_idx[LIST_1] = -1; + } + } + } + } +} + +/*! + ************************************************************************************* + * \brief + * skip macroblock field inference + * \return + * inferred field flag + ************************************************************************************* + */ +byte field_flag_inference(Macroblock *currMB) +{ + VideoParameters *p_Vid = currMB->p_Vid; + byte mb_field; + + if (currMB->mbAvailA) + { + mb_field = p_Vid->mb_data[currMB->mbAddrA].mb_field; + } + else + { + // check top macroblock pair + if (currMB->mbAvailB) + mb_field = p_Vid->mb_data[currMB->mbAddrB].mb_field; + else + mb_field = 0; + } + + return mb_field; +} + +/*! + ************************************************************************************* + * \brief + * Store motion vectors for 8x8 partition + ************************************************************************************* + */ + +void StoreMVBlock8x8(Slice *currSlice, int dir, int block8x8, int mode, Info8x8 *B8x8Info) +{ + RDOPTStructure *p_RDO = currSlice->p_RDO; + int i0 = (block8x8 & 0x01) << 1; + int j0 = (block8x8 >> 1) << 1; + int j1 = j0 + 1; + + MotionVector *****all_mv = currSlice->all_mv; + MotionVector **lc_l0_mv8x8 = p_RDO->all_mv8x8[dir][LIST_0]; + MotionVector **lc_l1_mv8x8 = p_RDO->all_mv8x8[dir][LIST_1]; + + if (currSlice->slice_type != B_SLICE ) + { + if (B8x8Info->pdir >= 0) //(mode8!=IBLOCK)&&(mode8!=I16MB)) // && ref != -1) + { + int l0_ref = B8x8Info->ref[LIST_0]; + memcpy(&lc_l0_mv8x8[j0][i0], &all_mv[LIST_0][l0_ref][mode][j0][i0], 2 * sizeof(MotionVector)); + memcpy(&lc_l0_mv8x8[j1][i0], &all_mv[LIST_0][l0_ref][mode][j1][i0], 2 * sizeof(MotionVector)); + } + } + else + { + int bipred_me = B8x8Info->bipred; + int pdir8 = B8x8Info->pdir; + + if (pdir8 == 0) // list0 + { + int l0_ref = B8x8Info->ref[LIST_0]; + memcpy(&lc_l0_mv8x8[j0][i0], &all_mv[LIST_0][l0_ref][mode][j0][i0], 2 * sizeof(MotionVector)); + memcpy(&lc_l0_mv8x8[j1][i0], &all_mv[LIST_0][l0_ref][mode][j1][i0], 2 * sizeof(MotionVector)); + } + else if (pdir8 == 1) // list1 + { + int l1_ref = B8x8Info->ref[LIST_1]; + memcpy(&lc_l1_mv8x8[j0][i0], &all_mv[LIST_1][l1_ref][mode][j0][i0], 2 * sizeof(MotionVector)); + memcpy(&lc_l1_mv8x8[j1][i0], &all_mv[LIST_1][l1_ref][mode][j1][i0], 2 * sizeof(MotionVector)); + } + else if (pdir8==2) // bipred + { + int l0_ref = B8x8Info->ref[LIST_0]; + int l1_ref = B8x8Info->ref[LIST_1]; + if (bipred_me) + { + all_mv = currSlice->bipred_mv[bipred_me - 1]; + } + + memcpy(&lc_l0_mv8x8[j0][i0], &all_mv[LIST_0][l0_ref][mode][j0][i0], 2 * sizeof(MotionVector)); + memcpy(&lc_l0_mv8x8[j1][i0], &all_mv[LIST_0][l0_ref][mode][j1][i0], 2 * sizeof(MotionVector)); + memcpy(&lc_l1_mv8x8[j0][i0], &all_mv[LIST_1][l1_ref][mode][j0][i0], 2 * sizeof(MotionVector)); + memcpy(&lc_l1_mv8x8[j1][i0], &all_mv[LIST_1][l1_ref][mode][j1][i0], 2 * sizeof(MotionVector)); + } + else + { + error("invalid direction mode", 255); + } + } +} + +/*! + ************************************************************************************* + * \brief + * Store motion vectors of 8x8 partitions of one macroblock + ************************************************************************************* + */ +void StoreMV8x8(Slice *currSlice, int dir) +{ + RDOPTStructure *p_RDO = currSlice->p_RDO; + int block8x8; + + for (block8x8=0; block8x8<4; block8x8++) + StoreMVBlock8x8(currSlice, dir, block8x8, p_RDO->tr8x8->part[block8x8].mode, &p_RDO->tr8x8->part[block8x8]); +} + +/*! +************************************************************************************* +* \brief +* Restore motion vectors for 8x8 partition +************************************************************************************* +*/ +void RestoreMVBlock8x8(Slice *currSlice, int dir, int block8x8, RD_8x8DATA *tr) +{ + RDOPTStructure *p_RDO = currSlice->p_RDO; + MotionVector *****all_mv = currSlice->all_mv; + MotionVector **lc_l0_mv8x8 = p_RDO->all_mv8x8[dir][LIST_0]; + MotionVector **lc_l1_mv8x8 = p_RDO->all_mv8x8[dir][LIST_1]; + + short pdir8 = tr->part[block8x8].pdir; + short mode = tr->part[block8x8].mode; + short l0_ref = tr->part[block8x8].ref[LIST_0]; + short l1_ref = tr->part[block8x8].ref[LIST_1]; + short bipred_me = tr->part[block8x8].bipred; + + int i0 = (block8x8 & 0x01) << 1; + int j0 = (block8x8 >> 1) << 1; + int j1 = j0 + 1; + + if (currSlice->slice_type != B_SLICE) + { + if (pdir8>=0) //(mode8!=IBLOCK)&&(mode8!=I16MB)) // && ref != -1) + { + memcpy(&all_mv[LIST_0][l0_ref][4][j0][i0], &lc_l0_mv8x8[j0][i0], 2 * sizeof(MotionVector)); + memcpy(&all_mv[LIST_0][l0_ref][4][j1][i0], &lc_l0_mv8x8[j1][i0], 2 * sizeof(MotionVector)); + } + } + else + { + if (pdir8==0) // list0 + { + memcpy(&all_mv[LIST_0][l0_ref][mode][j0][i0], &lc_l0_mv8x8[j0][i0], 2 * sizeof(MotionVector)); + memcpy(&all_mv[LIST_0][l0_ref][mode][j1][i0], &lc_l0_mv8x8[j1][i0], 2 * sizeof(MotionVector)); + } + else if (pdir8==1) // list1 + { + memcpy(&all_mv[LIST_1][l1_ref][mode][j0][i0], &lc_l1_mv8x8[j0][i0], 2 * sizeof(MotionVector)); + memcpy(&all_mv[LIST_1][l1_ref][mode][j1][i0], &lc_l1_mv8x8[j1][i0], 2 * sizeof(MotionVector)); + } + else if (pdir8==2) // bipred + { + if(bipred_me) + { + all_mv = currSlice->bipred_mv[bipred_me - 1]; + } + + memcpy(&all_mv[LIST_0][l0_ref][mode][j0][i0], &lc_l0_mv8x8[j0][i0], 2 * sizeof(MotionVector)); + memcpy(&all_mv[LIST_0][l0_ref][mode][j1][i0], &lc_l0_mv8x8[j1][i0], 2 * sizeof(MotionVector)); + memcpy(&all_mv[LIST_1][l1_ref][mode][j0][i0], &lc_l1_mv8x8[j0][i0], 2 * sizeof(MotionVector)); + memcpy(&all_mv[LIST_1][l1_ref][mode][j1][i0], &lc_l1_mv8x8[j1][i0], 2 * sizeof(MotionVector)); + } + else + { + error("invalid direction mode", 255); + } + } +} + +/*! + ************************************************************************************* + * \brief + * Restore motion vectors of 8x8 partitions of one macroblock + ************************************************************************************* + */ +void RestoreMV8x8(Slice *currSlice, int dir) +{ + RDOPTStructure *p_RDO = currSlice->p_RDO; + int block8x8; + + for (block8x8=0; block8x8<4; block8x8++) + RestoreMVBlock8x8(currSlice, dir, block8x8, p_RDO->tr8x8); +} + + +/*! + ************************************************************************************* + * \brief + * Store predictors for 8x8 partition + ************************************************************************************* + */ +void StoreNewMotionVectorsBlock8x8(Slice *currSlice, int dir, int block8x8, Info8x8 *B8x8Info) +{ + RDOPTStructure *p_RDO = currSlice->p_RDO; + int mode = B8x8Info->mode; + int i0 = (block8x8 & 0x01) << 1; + int j0 = (block8x8 >> 1) << 1; + int j1 = j0 + 1; + + MotionVector ****all_mv_l0 = currSlice->all_mv[LIST_0]; + MotionVector ****all_mv_l1 = currSlice->all_mv[LIST_1]; + MotionVector **lc_l0_mv8x8 = &p_RDO->all_mv8x8[dir][LIST_0][j0]; + MotionVector **lc_l1_mv8x8 = &p_RDO->all_mv8x8[dir][LIST_1][j0]; + + + if (B8x8Info->pdir < 0) + { + memset(&lc_l0_mv8x8[0][i0], 0, 2 * sizeof(MotionVector)); + memset(&lc_l0_mv8x8[1][i0], 0, 2 * sizeof(MotionVector)); + memset(&lc_l1_mv8x8[0][i0], 0, 2 * sizeof(MotionVector)); + memset(&lc_l1_mv8x8[1][i0], 0, 2 * sizeof(MotionVector)); + return; + } + + if (currSlice->slice_type != B_SLICE) + { + int l0_ref = B8x8Info->ref[LIST_0]; + memcpy(&lc_l0_mv8x8[0][i0], &all_mv_l0[l0_ref][mode][j0][i0], 2 * sizeof(MotionVector)); + memcpy(&lc_l0_mv8x8[1][i0], &all_mv_l0[l0_ref][mode][j1][i0], 2 * sizeof(MotionVector)); + memset(&lc_l1_mv8x8[0][i0], 0, 2 * sizeof(MotionVector)); + memset(&lc_l1_mv8x8[1][i0], 0, 2 * sizeof(MotionVector)); + return; + } + else + { + int bipred_me = B8x8Info->bipred; + int pdir8 = B8x8Info->pdir; + if (bipred_me) + { + all_mv_l0 = currSlice->bipred_mv[bipred_me - 1][LIST_0]; + all_mv_l1 = currSlice->bipred_mv[bipred_me - 1][LIST_1]; + } + + if ((pdir8==0 || pdir8==2)) + { + int l0_ref = B8x8Info->ref[LIST_0]; + memcpy(&lc_l0_mv8x8[0][i0], &all_mv_l0[l0_ref][mode][j0][i0], 2 * sizeof(MotionVector)); + memcpy(&lc_l0_mv8x8[1][i0], &all_mv_l0[l0_ref][mode][j1][i0], 2 * sizeof(MotionVector)); + } + else + { + memset(&lc_l0_mv8x8[0][i0], 0, 2 * sizeof(MotionVector)); + memset(&lc_l0_mv8x8[1][i0], 0, 2 * sizeof(MotionVector)); + } + + if ((pdir8==1 || pdir8==2)) + { + int l1_ref = B8x8Info->ref[LIST_1]; + memcpy(&lc_l1_mv8x8[0][i0], &all_mv_l1[l1_ref][mode][j0][i0], 2 * sizeof(MotionVector)); + memcpy(&lc_l1_mv8x8[1][i0], &all_mv_l1[l1_ref][mode][j1][i0], 2 * sizeof(MotionVector)); + } + else + { + memset(&lc_l1_mv8x8[0][i0], 0, 2 * sizeof(MotionVector)); + memset(&lc_l1_mv8x8[1][i0], 0, 2 * sizeof(MotionVector)); + } + } +} + + +/*! + ************************************************************************************* + * \brief + * Store predictors for 8x8 partition + ************************************************************************************* + */ +void store_8x8_motion_vectors_p_slice(Slice *currSlice, int dir, int block8x8, Info8x8 *B8x8Info) +{ + RDOPTStructure *p_RDO = currSlice->p_RDO; + + int i0 = (block8x8 & 0x01) << 1; + int j0 = (block8x8 >> 1) << 1; + + MotionVector **lc_l0_mv8x8 = &p_RDO->all_mv8x8[dir][LIST_0][j0]; + MotionVector **lc_l1_mv8x8 = &p_RDO->all_mv8x8[dir][LIST_1][j0]; + + if (B8x8Info->pdir < 0) + { + memset(&lc_l0_mv8x8[0][i0], 0, 2 * sizeof(MotionVector)); + memset(&lc_l0_mv8x8[1][i0], 0, 2 * sizeof(MotionVector)); + memset(&lc_l1_mv8x8[0][i0], 0, 2 * sizeof(MotionVector)); + memset(&lc_l1_mv8x8[1][i0], 0, 2 * sizeof(MotionVector)); + } + else + { + int j1 = j0 + 1; + MotionVector **all_mv_l0 = currSlice->all_mv[LIST_0][(short) B8x8Info->ref[LIST_0]][(short)B8x8Info->mode]; + memcpy(&lc_l0_mv8x8[0][i0], &all_mv_l0[j0][i0], 2 * sizeof(MotionVector)); + memcpy(&lc_l0_mv8x8[1][i0], &all_mv_l0[j1][i0], 2 * sizeof(MotionVector)); + memset(&lc_l1_mv8x8[0][i0], 0, 2 * sizeof(MotionVector)); + memset(&lc_l1_mv8x8[1][i0], 0, 2 * sizeof(MotionVector)); + } +} + + +/*! + ************************************************************************************* + * \brief + * Store predictors for 8x8 partition + ************************************************************************************* + */ +void store_8x8_motion_vectors_b_slice(Slice *currSlice, int dir, int block8x8, Info8x8 *B8x8Info) +{ + RDOPTStructure *p_RDO = currSlice->p_RDO; + int i0 = (block8x8 & 0x01) << 1; + int j0 = (block8x8 >> 1) << 1; + + MotionVector **lc_l0_mv8x8 = &p_RDO->all_mv8x8[dir][LIST_0][j0]; + MotionVector **lc_l1_mv8x8 = &p_RDO->all_mv8x8[dir][LIST_1][j0]; + + if (B8x8Info->pdir < 0) + { + memset(&lc_l0_mv8x8[0][i0], 0, 2 * sizeof(MotionVector)); + memset(&lc_l0_mv8x8[1][i0], 0, 2 * sizeof(MotionVector)); + memset(&lc_l1_mv8x8[0][i0], 0, 2 * sizeof(MotionVector)); + memset(&lc_l1_mv8x8[1][i0], 0, 2 * sizeof(MotionVector)); + } + else + { + int mode = B8x8Info->mode; + int j1 = j0 + 1; + MotionVector ****all_mv_l0 = currSlice->all_mv[LIST_0]; + MotionVector ****all_mv_l1 = currSlice->all_mv[LIST_1]; + + int bipred_me = B8x8Info->bipred; + int pdir8 = B8x8Info->pdir; + if (bipred_me) + { + all_mv_l0 = currSlice->bipred_mv[bipred_me - 1][LIST_0]; + all_mv_l1 = currSlice->bipred_mv[bipred_me - 1][LIST_1]; + } + + if ((pdir8==0 || pdir8==2)) + { + int l0_ref = B8x8Info->ref[LIST_0]; + memcpy(&lc_l0_mv8x8[0][i0], &all_mv_l0[l0_ref][mode][j0][i0], 2 * sizeof(MotionVector)); + memcpy(&lc_l0_mv8x8[1][i0], &all_mv_l0[l0_ref][mode][j1][i0], 2 * sizeof(MotionVector)); + } + else + { + memset(&lc_l0_mv8x8[0][i0], 0, 2 * sizeof(MotionVector)); + memset(&lc_l0_mv8x8[1][i0], 0, 2 * sizeof(MotionVector)); + } + + if ((pdir8==1 || pdir8==2)) + { + int l1_ref = B8x8Info->ref[LIST_1]; + memcpy(&lc_l1_mv8x8[0][i0], &all_mv_l1[l1_ref][mode][j0][i0], 2 * sizeof(MotionVector)); + memcpy(&lc_l1_mv8x8[1][i0], &all_mv_l1[l1_ref][mode][j1][i0], 2 * sizeof(MotionVector)); + } + else + { + memset(&lc_l1_mv8x8[0][i0], 0, 2 * sizeof(MotionVector)); + memset(&lc_l1_mv8x8[1][i0], 0, 2 * sizeof(MotionVector)); + } + } +} + + +/*! +************************************************************************ +* \brief +* Sets MBAFF RD parameters +************************************************************************ +*/ +void set_mbaff_parameters(Macroblock *currMB) +{ + VideoParameters *p_Vid = currMB->p_Vid; + InputParameters *p_Inp = currMB->p_Inp; + Slice *currSlice = currMB->p_Slice; + + int j, i; + int mode = currMB->best_mode; + char **ipredmodes = p_Vid->ipredmode; + PicMotionParams **motion = p_Vid->enc_picture->mv_info; + RD_DATA *rdopt = currSlice->rddata; + + + //===== reconstruction values ===== + copy_image_data_16x16(rdopt->rec_mb[0], &p_Vid->enc_picture->imgY[currMB->pix_y], 0, currMB->pix_x); + + if (p_Vid->yuv_format != YUV400) + { + copy_image_data(rdopt->rec_mb[1], &p_Vid->enc_picture->imgUV[0][currMB->pix_c_y], 0, currMB->pix_c_x, p_Vid->mb_cr_size_x, p_Vid->mb_cr_size_y); + copy_image_data(rdopt->rec_mb[2], &p_Vid->enc_picture->imgUV[1][currMB->pix_c_y], 0, currMB->pix_c_x, p_Vid->mb_cr_size_x, p_Vid->mb_cr_size_y); + } + + //===== coefficients and cbp ===== + rdopt->mode = mode; + rdopt->i16offset = currMB->i16offset; + rdopt->i16mode = currMB->i16mode; + rdopt->cbp = currMB->cbp; + rdopt->cbp_blk = currMB->cbp_blk; + rdopt->mb_type = currMB->mb_type; + + rdopt->luma_transform_size_8x8_flag = currMB->luma_transform_size_8x8_flag; + + if(rdopt->mb_type == 0 && mode != 0) + { + mode=0; + rdopt->mode=0; + } + + memcpy(rdopt->cofAC[0][0][0], currSlice->cofAC[0][0][0], (4+p_Vid->num_blk8x8_uv) * 4 * 2 * 65 * sizeof(int)); + memcpy(rdopt->cofDC[0][0], currSlice->cofDC[0][0], 3 * 2 * 18 * sizeof(int)); + + memcpy(rdopt->b8x8, currMB->b8x8, BLOCK_MULTIPLE * sizeof(Info8x8)); + + //==== reference frames ===== + if (currSlice->slice_type == B_SLICE) + { + if (p_Inp->BiPredMERefinements == 1) + { + int k; + for (j = 0; j < BLOCK_MULTIPLE; j++) + { + for (i = 0; i < BLOCK_MULTIPLE; i++) + { + k = 2*(j >> 1)+(i >> 1); + if (currMB->b8x8[k].bipred == 0) + { + rdopt->refar[LIST_0][j][i] = motion[currMB->block_y + j][currMB->block_x + i].ref_idx[LIST_0]; + rdopt->refar[LIST_1][j][i] = motion[currMB->block_y + j][currMB->block_x + i].ref_idx[LIST_1]; + } + else + { + rdopt->refar[LIST_0][j][i] = 0; + rdopt->refar[LIST_1][j][i] = 0; + } + } + } + } + else + { + for (j = 0; j < BLOCK_MULTIPLE; j++) + { + for (i = 0; i < BLOCK_MULTIPLE; i++) + { + rdopt->refar[LIST_0][j][i] = motion[currMB->block_y + j][currMB->block_x + i].ref_idx[LIST_0]; + rdopt->refar[LIST_1][j][i] = motion[currMB->block_y + j][currMB->block_x + i].ref_idx[LIST_1]; + } + } + } + } + else + { + for (j = 0; j < BLOCK_MULTIPLE; j++) + { + for (i = 0; i < BLOCK_MULTIPLE; i++) + { + rdopt->refar[LIST_0][j][i] = motion[currMB->block_y + j][currMB->block_x + i].ref_idx[LIST_0]; + //rdopt->refar[LIST_1][j][i] = motion[currMB->block_y + j][currMB->block_x + i].ref_idx[LIST_1]; + } + } + } + + memcpy(rdopt->intra_pred_modes, currMB->intra_pred_modes, MB_BLOCK_PARTITIONS * sizeof(char)); + memcpy(rdopt->intra_pred_modes8x8,currMB->intra_pred_modes8x8, MB_BLOCK_PARTITIONS * sizeof(char)); + for (j = currMB->block_y; j < currMB->block_y + 4; j++) + { + memcpy(&rdopt->ipredmode[j][currMB->block_x],&ipredmodes[j][currMB->block_x], BLOCK_MULTIPLE * sizeof(char)); + } +} + + +void assign_enc_picture_params (Macroblock *currMB, int mode, Info8x8 *best, int block) +{ + int i,j; + int block_y, block_x; + int list; + MotionVector **curr_mv = NULL; + int list_offset = currMB->list_offset; + VideoParameters *p_Vid = currMB->p_Vid; + Slice *currSlice = currMB->p_Slice; + PicMotionParams **motion = p_Vid->enc_picture->mv_info; + int maxlist = (currSlice->slice_type == B_SLICE) ? 1: 0; + StorablePicture *listX = NULL; + + int start_x = 0, start_y = 0, end_x = BLOCK_MULTIPLE, end_y = BLOCK_MULTIPLE; + + switch (mode) + { + case 1: + start_x = 0; + start_y = 0; + end_x = BLOCK_MULTIPLE; + end_y = BLOCK_MULTIPLE; + break; + case 2: + start_x = 0; + start_y = block; + end_x = BLOCK_MULTIPLE; + end_y = block + 2; + break; + case 3: + start_x = block; + start_y = 0; + end_x = block + 2; + end_y = BLOCK_MULTIPLE; + break; + case 4: //P8x8 + default: + start_x = ((block>>1) & 0x01)* 2; + start_y = ((block>>1) & 0x02); + end_x = start_x + 2; + end_y = start_y + 2; + break; + } + + for (list = 0; list <= maxlist; list++) + { + if ((best->pdir != 2) && (best->pdir != list)) + { + for (j = currMB->block_y + start_y; j < currMB->block_y + end_y; j++) + { + for (i = currMB->block_x + start_x; i < currMB->block_x + end_x; i++) + { + motion[j][i].ref_pic[list] = NULL; + motion[j][i].mv[list] = zero_mv; + motion[j][i].ref_idx[list] = - 1; + } + } + } + else + { + switch (best->bipred) + { + case 0: + { + int bestref = best->ref[list]; + curr_mv = currSlice->all_mv[list][bestref][mode]; + listX = currSlice->listX[list + list_offset][bestref]; + if(listX->view_id == 1) + { + int a = 0; + } + } + break; + case 1: + curr_mv = currSlice->bipred_mv[0][list][0][mode] ; //best->ref[LIST_0] has to be zero in this case + listX = currSlice->listX[list + list_offset][0]; + break; + case 2: + curr_mv = currSlice->bipred_mv[1][list][0][mode] ; //best->ref[LIST_0] has to be zero in this case + listX = currSlice->listX[list + list_offset][0]; + break; + default: + break; + } + + for (j = start_y; j < end_y; j++) + { + block_y = currMB->block_y + j; + for (i = start_x; i < end_x; i++) + { + block_x = currMB->block_x + i; + motion[block_y][block_x].ref_pic [list] = listX; + motion[block_y][block_x].mv [list] = curr_mv[j][i]; + } + } + } + } +} + +/*! + ************************************************************************************* + * \brief + * Set block 8x8 mode information + ************************************************************************************* + */ +void set_block8x8_info(Block8x8Info *b8x8info, int mode, int block, Info8x8 *best) +{ + //----- set reference frame and direction parameters ----- + if (mode==3) + { + b8x8info->best[3][block ] = *best; + b8x8info->best[3][block+2] = *best; + } + else if (mode==2) + { + b8x8info->best[2][2*block ] = *best; + b8x8info->best[2][2*block + 1] = *best; + } + else if (mode==1) + { + b8x8info->best[1][0] = *best; + b8x8info->best[1][1] = *best; + b8x8info->best[1][2] = *best; + b8x8info->best[1][3] = *best; + + } + else //P8x8 + { + b8x8info->best[mode][block] = *best; + } +} + +/*! + ************************************************************************************* + * \brief + * Set block 8x8 mode information for P8x8 mode + ************************************************************************************* + */ +void set_subblock8x8_info(Block8x8Info *b8x8info,int mode, int block, RD_8x8DATA *tr) +{ + b8x8info->best [mode][block] = tr->part[block]; +} + + + +void update_refresh_map(Macroblock *currMB, int intra, int intra1) +{ + VideoParameters *p_Vid = currMB->p_Vid; + InputParameters *p_Inp = currMB->p_Inp; + + if (p_Inp->RestrictRef==1) + { + // Modified for Fast Mode Decision. Inchoon Choi, SungKyunKwan Univ. + if (p_Inp->rdopt<2) + { + p_Vid->refresh_map[2*currMB->mb_y ][2*currMB->mb_x ] = (byte) (intra ? 1 : 0); + p_Vid->refresh_map[2*currMB->mb_y ][2*currMB->mb_x+1] = (byte) (intra ? 1 : 0); + p_Vid->refresh_map[2*currMB->mb_y + 1][2*currMB->mb_x ] = (byte) (intra ? 1 : 0); + p_Vid->refresh_map[2*currMB->mb_y + 1][2*currMB->mb_x+1] = (byte) (intra ? 1 : 0); + } + else if (p_Inp->rdopt == 3) + { + p_Vid->refresh_map[2*currMB->mb_y ][2*currMB->mb_x ] = (byte) (intra1==0 && (currMB->mb_type==I16MB || currMB->mb_type==I4MB) ? 1 : 0); + p_Vid->refresh_map[2*currMB->mb_y ][2*currMB->mb_x+1] = (byte) (intra1==0 && (currMB->mb_type==I16MB || currMB->mb_type==I4MB) ? 1 : 0); + p_Vid->refresh_map[2*currMB->mb_y + 1][2*currMB->mb_x ] = (byte) (intra1==0 && (currMB->mb_type==I16MB || currMB->mb_type==I4MB) ? 1 : 0); + p_Vid->refresh_map[2*currMB->mb_y + 1][2*currMB->mb_x+1] = (byte) (intra1==0 && (currMB->mb_type==I16MB || currMB->mb_type==I4MB) ? 1 : 0); + } + } + else if (p_Inp->RestrictRef==2) + { + p_Vid->refresh_map[2*currMB->mb_y ][2*currMB->mb_x ] = (byte) (currMB->mb_type==I16MB || currMB->mb_type==I4MB ? 1 : 0); + p_Vid->refresh_map[2*currMB->mb_y ][2*currMB->mb_x+1] = (byte) (currMB->mb_type==I16MB || currMB->mb_type==I4MB ? 1 : 0); + p_Vid->refresh_map[2*currMB->mb_y + 1][2*currMB->mb_x ] = (byte) (currMB->mb_type==I16MB || currMB->mb_type==I4MB ? 1 : 0); + p_Vid->refresh_map[2*currMB->mb_y + 1][2*currMB->mb_x+1] = (byte) (currMB->mb_type==I16MB || currMB->mb_type==I4MB ? 1 : 0); + } +} + +int valid_intra_mode(Slice *currSlice, int ipmode) +{ + InputParameters *p_Inp = currSlice->p_Inp; + + if (p_Inp->IntraDisableInterOnly==0 || (currSlice->slice_type != I_SLICE && currSlice->slice_type != SI_SLICE)) + { + if (p_Inp->Intra4x4ParDisable && (ipmode==VERT_PRED||ipmode==HOR_PRED)) + return 0; + + if (p_Inp->Intra4x4DiagDisable && (ipmode==DIAG_DOWN_LEFT_PRED||ipmode==DIAG_DOWN_RIGHT_PRED)) + return 0; + + if (p_Inp->Intra4x4DirDisable && ipmode>=VERT_RIGHT_PRED) + return 0; + } + return 1; +} + +distblk compute_sad4x4_cost(VideoParameters *p_Vid, imgpel **cur_img, imgpel **prd_img, int pic_opix_x, distblk min_cost) +{ + imgpel *cur_line, *prd_line; + int i32Cost = 0; + int imin_cost = dist_down(min_cost); + + int j; + for (j = 0; j < BLOCK_SIZE; j++) + { + cur_line = &cur_img[j][pic_opix_x]; + prd_line = prd_img[j]; + + i32Cost += iabs(cur_line[0] - prd_line[0]); + i32Cost += iabs(cur_line[1] - prd_line[1]); + i32Cost += iabs(cur_line[2] - prd_line[2]); + i32Cost += iabs(cur_line[3] - prd_line[3]); + + if (i32Cost > imin_cost) + { + return(min_cost); + } + } + return dist_scale(i32Cost); +} + +distblk compute_sse4x4_cost(VideoParameters *p_Vid, imgpel **cur_img, imgpel **prd_img, int pic_opix_x, distblk min_cost) +{ + int j, i; + imgpel *cur_line, *prd_line; + int i32Cost = 0; + int imin_cost = dist_down(min_cost); + for (j = 0; j < BLOCK_SIZE; j++) + { + cur_line = &cur_img[j][pic_opix_x]; + prd_line = prd_img[j]; + for (i = 0; i < BLOCK_SIZE; i++) + { + i32Cost += iabs2(*cur_line++ - *prd_line++); + } + + if (i32Cost > imin_cost) + { + return(min_cost); + } + } + return dist_scale(i32Cost); +} + +distblk compute_satd4x4_cost(VideoParameters *p_Vid, imgpel **cur_img, imgpel **prd_img, int pic_opix_x, distblk min_cost) +{ + int j, i; + imgpel *cur_line, *prd_line; + short diff[16]; + + short *d = &diff[0]; + + for (j = 0; j < BLOCK_SIZE; j++) + { + cur_line = &cur_img[j][pic_opix_x]; + prd_line = prd_img[j]; + + for (i = 0; i < BLOCK_SIZE; i++) + { + *d++ = *cur_line++ - *prd_line++; + } + } + + return dist_scale(HadamardSAD4x4 (diff)); +} + +static distblk compute_comp4x4_cost(VideoParameters *p_Vid, imgpel **cur_img, imgpel **prd_img, int pic_opix_x, distblk min_cost) +{ + int j, i; + imgpel *cur_line, *prd_line; + short diff[16]; + + short *d = &diff[0]; + + for (j = 0; j < BLOCK_SIZE; j++) + { + cur_line = &cur_img[j][pic_opix_x]; + prd_line = prd_img[j]; + + for (i = 0; i < BLOCK_SIZE; i++) + { + *d++ = *cur_line++ - *prd_line++; + } + } + return(p_Vid->distortion4x4 (diff, min_cost)); +} + + + +void update_qp_cbp_tmp(Macroblock *currMB, int cbp) +{ + if (((cbp!=0 || currMB->best_mode==I16MB) && (currMB->best_mode!=IPCM) )) + currMB->prev_cbp = 1; + else if ((cbp==0) || (currMB->best_mode==IPCM)) + { + currMB->prev_cbp = 0; + currMB->qp = currMB->prev_qp; + currMB->p_Vid->qp = currMB->qp; + update_qp(currMB); + } +} + +/*! + ************************************************************************************* + * \brief + * Update QP Parameters (in case of SKIP MBs or MBAFF) + ************************************************************************************* + */ + +void update_qp_cbp(Macroblock *currMB) +{ + VideoParameters *p_Vid = currMB->p_Vid; + InputParameters *p_Inp = currMB->p_Inp; + + // delta_qp is present only for non-skipped macroblocks + if ((currMB->cbp!=0 || currMB->best_mode == I16MB) && (currMB->best_mode != IPCM)) + currMB->prev_cbp = 1; + else + { + currMB->prev_cbp = 0; + currMB->qp = currMB->prev_qp; + p_Vid->qp = currMB->qp; + update_qp(currMB); + } + + if (p_Inp->MbInterlace) + { + Slice *currSlice = currMB->p_Slice; + // update rdopt buffered qps... + currSlice->rddata->qp = currMB->qp; + currSlice->rddata->prev_cbp = currMB->prev_cbp; + } +} + +/*! +*************************************************************************** +// For MB level field/frame coding +*************************************************************************** +*/ +void copy_rdopt_data (Macroblock *currMB) +{ + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + PicMotionParams **motion = p_Vid->enc_picture->mv_info; + int i, j; + RD_DATA *rdopt = currSlice->rddata; + + int mode; + short b8mode, b8pdir; + int block_y; + + int list_offset = currMB->list_offset; + + mode = rdopt->mode; + currMB->mb_type = rdopt->mb_type; // copy mb_type + currMB->cbp = rdopt->cbp; // copy cbp + currMB->cbp_blk = rdopt->cbp_blk; // copy cbp_blk + currMB->i16offset = rdopt->i16offset; + currMB->i16mode = rdopt->i16mode; + + currMB->prev_qp = rdopt->prev_qp; + currMB->prev_dqp = rdopt->prev_dqp; + currMB->prev_cbp = rdopt->prev_cbp; + currMB->qp = rdopt->qp; + update_qp (currMB); + + currMB->c_ipred_mode = rdopt->c_ipred_mode; + + memcpy(currSlice->cofAC[0][0][0],rdopt->cofAC[0][0][0], (4 + p_Vid->num_blk8x8_uv) * 4 * 2 * 65 * sizeof(int)); + memcpy(currSlice->cofDC[0][0],rdopt->cofDC[0][0], 3 * 2 * 18 * sizeof(int)); + + for (j = 0; j < BLOCK_MULTIPLE; j++) + { + block_y = currMB->block_y + j; + for (i = 0; i < BLOCK_MULTIPLE; i++) + { + motion[block_y][currMB->block_x + i].ref_idx [LIST_0] = rdopt->refar[LIST_0][j][i]; + motion[block_y][currMB->block_x + i].ref_pic [LIST_0] = rdopt->refar[LIST_0][j][i] < 0 ? NULL : + currSlice->listX[LIST_0 + list_offset][(short)rdopt->refar[LIST_0][j][i]]; + } + } + + if (currSlice->slice_type == B_SLICE) + { + for (j = 0; j < BLOCK_MULTIPLE; j++) + { + block_y = currMB->block_y + j; + for (i = 0; i < BLOCK_MULTIPLE; i++) + { + motion[block_y][currMB->block_x + i].ref_idx [LIST_1] = rdopt->refar[LIST_1][j][i]; + motion[block_y][currMB->block_x + i].ref_pic [LIST_1] = rdopt->refar[LIST_1][j][i] < 0 ? NULL : + currSlice->listX[LIST_1 + list_offset][(short) rdopt->refar[LIST_1][j][i]]; + } + } + } + + + //===== reconstruction values ===== + copy_image_data_16x16(&p_Vid->enc_picture->imgY[currMB->pix_y], rdopt->rec_mb[0], currMB->pix_x, 0); + + if (p_Vid->yuv_format != YUV400) + { + copy_image_data(&p_Vid->enc_picture->imgUV[0][currMB->pix_c_y], rdopt->rec_mb[1], currMB->pix_c_x, 0, p_Vid->mb_cr_size_x, p_Vid->mb_cr_size_y); + copy_image_data(&p_Vid->enc_picture->imgUV[1][currMB->pix_c_y], rdopt->rec_mb[2], currMB->pix_c_x, 0, p_Vid->mb_cr_size_x, p_Vid->mb_cr_size_y); + } + + + memcpy(currMB->b8x8, rdopt->b8x8, BLOCK_MULTIPLE * sizeof(Info8x8)); + + currMB->luma_transform_size_8x8_flag = rdopt->luma_transform_size_8x8_flag; + + //==== intra prediction modes ==== + if (mode == P8x8) + { + memcpy(currMB->intra_pred_modes,rdopt->intra_pred_modes, MB_BLOCK_PARTITIONS * sizeof(char)); + for (j = currMB->block_y; j < currMB->block_y + BLOCK_MULTIPLE; j++) + memcpy(&p_Vid->ipredmode[j][currMB->block_x],&rdopt->ipredmode[j][currMB->block_x], BLOCK_MULTIPLE * sizeof(char)); + } + else if (mode != I4MB && mode != I8MB) + { + memset(currMB->intra_pred_modes,DC_PRED, MB_BLOCK_PARTITIONS * sizeof(char)); + for (j = currMB->block_y; j < currMB->block_y + BLOCK_MULTIPLE; j++) + memset(&p_Vid->ipredmode[j][currMB->block_x],DC_PRED, BLOCK_MULTIPLE * sizeof(char)); + } + else if (mode == I4MB || mode == I8MB) + { + memcpy(currMB->intra_pred_modes,rdopt->intra_pred_modes, MB_BLOCK_PARTITIONS * sizeof(char)); + memcpy(currMB->intra_pred_modes8x8,rdopt->intra_pred_modes8x8, MB_BLOCK_PARTITIONS * sizeof(char)); + for (j = currMB->block_y; j < currMB->block_y + BLOCK_MULTIPLE; j++) + { + memcpy(&p_Vid->ipredmode[j][currMB->block_x],&rdopt->ipredmode[j][currMB->block_x], BLOCK_MULTIPLE * sizeof(char)); + } + } + + if (currSlice->mb_aff_frame_flag || (currSlice->UseRDOQuant && currSlice->RDOQ_QP_Num > 1)) + { + // motion vectors + if (currSlice->slice_type != I_SLICE && currSlice->slice_type != SI_SLICE) + copy_motion_vectors_MB (currSlice, rdopt); + + if (!IS_INTRA(currMB)) + { + currMB->b8x8[0].bipred = 0; + currMB->b8x8[1].bipred = 0; + currMB->b8x8[2].bipred = 0; + currMB->b8x8[3].bipred = 0; + + for (j = 0; j < 4; j++) + { + for (i = 0; i < 4; i++) + { + b8mode = currMB->b8x8[(i >> 1) + 2 * (j >> 1)].mode; + b8pdir = currMB->b8x8[(i >> 1) + 2 * (j >> 1)].pdir; + + if (b8pdir!=1) + { + motion[j+currMB->block_y][i+currMB->block_x].mv[LIST_0] = rdopt->all_mv[LIST_0][(short)rdopt->refar[LIST_0][j][i]][b8mode][j][i]; + } + else + { + motion[j+currMB->block_y][i+currMB->block_x].mv[LIST_0] = zero_mv; + } + if (currSlice->slice_type == B_SLICE) + { + if (b8pdir!=0) + { + motion[j+currMB->block_y][i+currMB->block_x].mv[LIST_1] = rdopt->all_mv[LIST_1][(short)rdopt->refar[LIST_1][j][i]][b8mode][j][i]; + } + else + { + motion[j+currMB->block_y][i+currMB->block_x].mv[LIST_1] = zero_mv; + } + } + } + } + } + else + { + + if (currSlice->slice_type == B_SLICE) + { + for (j = currMB->block_y; j < currMB->block_y + 4; j++) + { + for (i = currMB->block_x; i < currMB->block_x + 4; i++) + { + motion[j][i].mv[LIST_0] = zero_mv; + motion[j][i].mv[LIST_1] = zero_mv; + } + } + } + else + { + for (j = currMB->block_y; j < currMB->block_y + 4; j++) + { + for (i = currMB->block_x; i < currMB->block_x + 4; i++) + { + motion[j][i].mv[LIST_0] = zero_mv; + } + } + } + } + } +} // end of copy_rdopt_data + + + diff --git a/lencod/src/rdopt_coding_state.c b/lencod/src/rdopt_coding_state.c new file mode 100644 index 0000000..aa9555b --- /dev/null +++ b/lencod/src/rdopt_coding_state.c @@ -0,0 +1,299 @@ + +/*! + *************************************************************************** + * \file rdopt_coding_state.c + * + * \brief + * Storing/restoring coding state for + * Rate-Distortion optimized mode decision + * + * \author + * Heiko Schwarz + * + * \date + * 17. April 2001 + **************************************************************************/ + +#include "global.h" + +#include "rdopt_coding_state.h" +#include "cabac.h" +#include "memalloc.h" + +/*! + ************************************************************************ + * \brief + * delete structure for storing coding state + ************************************************************************ + */ +void delete_coding_state (CSobj *cs) +{ + if (cs != NULL) + { + //=== structures of data partition array === + if (cs->encenv != NULL) + { + free (cs->encenv); + cs->encenv = NULL; + } + if (cs->bitstream != NULL) + { + free (cs->bitstream); + cs->bitstream = NULL; + } + + //=== contexts for binary arithmetic coding === + delete_contexts_MotionInfo (cs->mot_ctx); + delete_contexts_TextureInfo (cs->tex_ctx); + + if (cs->cbp_bits_8x8 != NULL) + { + free (cs->cbp_bits_8x8); + cs->cbp_bits_8x8 = NULL; + } + + //=== coding state structure === + free (cs); + cs=NULL; + } +} + + +/*! + ************************************************************************ + * \brief + * create structure for storing coding state + ************************************************************************ + */ +CSobj *create_coding_state (InputParameters *p_Inp) +{ + CSobj *cs; + + //=== coding state structure === + if ((cs = (CSobj *) calloc (1, sizeof(CSobj))) == NULL) + no_mem_exit("init_coding_state: cs"); + + //=== important variables of data partition array === + cs->no_part = p_Inp->partition_mode == 0 ? 1 : 3; + if (p_Inp->symbol_mode == CABAC) + { + if ((cs->encenv = (EncodingEnvironment*) calloc (cs->no_part, sizeof(EncodingEnvironment))) == NULL) + no_mem_exit("init_coding_state: cs->encenv"); + + if ((cs->bitstream = (Bitstream*) calloc (cs->no_part, sizeof(Bitstream))) == NULL) + no_mem_exit("init_coding_state: cs->bitstream"); + //=== context for binary arithmetic coding === + cs->mot_ctx = create_contexts_MotionInfo (); + cs->tex_ctx = create_contexts_TextureInfo(); + + } + else + { + cs->encenv = NULL; + if ((cs->bitstream = (Bitstream*) calloc (cs->no_part, sizeof(Bitstream))) == NULL) + no_mem_exit("init_coding_state: cs->bitstream"); + + cs->mot_ctx = NULL; + cs->tex_ctx = NULL; + } + + if (p_Inp->ProfileIDC == 244) + { + if ((cs->cbp_bits_8x8 = (int64 *) calloc (3, sizeof(int64))) == NULL) + no_mem_exit("init_coding_state: cs->cbp_bits_8x8"); + } + else + cs->cbp_bits_8x8 = NULL; + + return cs; +} + +/*! + ************************************************************************ + * \brief + * store coding state (for non rdo case. basically a dummy function) + ************************************************************************ + */ +static void store_coding_state_nordo (Macroblock *currMB, CSobj *cs) +{ +} + + +/*! + ************************************************************************ + * \brief + * store cavlc coding state (for rd-optimized mode decision) + ************************************************************************ + */ +void store_coding_state_cavlc (Macroblock *currMB, CSobj *cs) +{ + int i; + Slice *currSlice = currMB->p_Slice; + int i_last = currSlice->idr_flag? 1 : cs->no_part; + + //=== important variables of data partition array === + for (i = 0; i < i_last; i++) + { + cs->bitstream[i] = *currSlice->partArr[i].bitstream; + } + + //=== syntax element number and bitcounters === + cs->bits = currMB->bits; + + //=== elements of current macroblock === + if (currMB->mb_type <= P8x8) + memcpy (cs->mvd, currMB->mvd, BLOCK_CONTEXT * sizeof(short)); + memcpy (cs->cbp_bits, currMB->cbp_bits, 3 * sizeof(int64)); + + if (currSlice->P444_joined) + memcpy (cs->cbp_bits_8x8, currMB->cbp_bits_8x8, 3 * sizeof(int64)); +} + +/*! + ************************************************************************ + * \brief + * store cabac coding state (for rd-optimized mode decision) + ************************************************************************ + */ +static void store_coding_state_cabac (Macroblock *currMB, CSobj *cs) +{ + int i; + Slice *currSlice = currMB->p_Slice; + int i_last = currSlice->idr_flag? 1:cs->no_part; + DataPartition *partArr = &currSlice->partArr[0]; + + //=== important variables of data partition array === + //only one partition for an IDR picture + for (i = 0; i < i_last; i++) + { + cs->bitstream[i] = *partArr->bitstream; + cs->encenv[i] = (partArr++)->ee_cabac; + } + + //=== contexts for binary arithmetic coding === + *cs->mot_ctx = *currSlice->mot_ctx; + *cs->tex_ctx = *currSlice->tex_ctx; + + //=== syntax element number and bitcounters === + cs->bits = currMB->bits; + + //=== elements of current macroblock === + if (currMB->mb_type <= P8x8) + memcpy (cs->mvd, currMB->mvd, BLOCK_CONTEXT * sizeof(short)); + memcpy (cs->cbp_bits, currMB->cbp_bits, 3 * sizeof(int64)); + + if (currSlice->P444_joined) + memcpy (cs->cbp_bits_8x8, currMB->cbp_bits_8x8, 3 * sizeof(int64)); +} + +/*! + ************************************************************************ + * \brief + * restore coding state (for non rdo case. basically a dummy function) + ************************************************************************ + */ +static void reset_coding_state_nordo (Macroblock *currMB, CSobj *cs) +{ +} + +/*! + ************************************************************************ + * \brief + * restore coding state (for rd-optimized mode decision) + ************************************************************************ + */ +void reset_coding_state_cavlc (Macroblock *currMB, CSobj *cs) +{ + int i; + Slice *currSlice = currMB->p_Slice; + int i_last = currSlice->idr_flag? 1:cs->no_part; + + //=== important variables of data partition array === + //only one partition for an IDR picture + for (i = 0; i < i_last; i++) + { + //--- parameters of encoding environments --- + *currSlice->partArr[i].bitstream = cs->bitstream[i]; + } + + //=== syntax element number and bit counters === + currMB->bits = cs->bits; + + //=== elements of current macroblock === + if (currMB->mb_type <= P8x8) + memcpy (currMB->mvd, cs->mvd, BLOCK_CONTEXT * sizeof(short)); + memcpy (currMB->cbp_bits, cs->cbp_bits, 3 * sizeof(int64)); + if(currSlice->P444_joined) + memcpy (currMB->cbp_bits_8x8, cs->cbp_bits_8x8, 3 * sizeof(int64)); +} + + +/*! + ************************************************************************ + * \brief + * restore coding state (for rd-optimized mode decision) + ************************************************************************ + */ +static void reset_coding_state_cabac (Macroblock *currMB, CSobj *cs) +{ + int i; + Slice *currSlice = currMB->p_Slice; + int i_last = currSlice->idr_flag? 1:cs->no_part; + DataPartition *partArr = &currSlice->partArr[0]; + + //=== important variables of data partition array === + //only one partition for an IDR picture + for (i = 0; i < i_last; i++) + { + //--- parameters of encoding environments --- + *partArr->bitstream = cs->bitstream[i]; + (partArr++)->ee_cabac = cs->encenv[i]; + } + + //=== contexts for binary arithmetic coding === + *currSlice->mot_ctx = *cs->mot_ctx; + *currSlice->tex_ctx = *cs->tex_ctx; + + //=== syntax element number and bit counters === + currMB->bits = cs->bits; + + //=== elements of current macroblock === + if (currMB->mb_type <= P8x8) + memcpy (currMB->mvd, cs->mvd, BLOCK_CONTEXT * sizeof(short)); + + memcpy (currMB->cbp_bits, cs->cbp_bits, 3 * sizeof(int64)); + + if(currSlice->P444_joined) + memcpy (currMB->cbp_bits_8x8, cs->cbp_bits_8x8, 3 * sizeof(int64)); +} + + + + +/*! + ************************************************************************ + * \brief + * Select methods given entropy coding type + ************************************************************************ + */ +void init_coding_state_methods(Slice *currSlice) +{ + if (currSlice->p_Inp->rdopt == 0) + { + currSlice->reset_coding_state = reset_coding_state_nordo; + currSlice->store_coding_state = store_coding_state_nordo; + } + else + { + if (currSlice->symbol_mode == CABAC) + { + currSlice->reset_coding_state = reset_coding_state_cabac; + currSlice->store_coding_state = store_coding_state_cabac; + } + else + { + currSlice->reset_coding_state = reset_coding_state_cavlc; + currSlice->store_coding_state = store_coding_state_cavlc; + } + } +} diff --git a/lencod/src/rdoq.c b/lencod/src/rdoq.c new file mode 100644 index 0000000..e25a602 --- /dev/null +++ b/lencod/src/rdoq.c @@ -0,0 +1,496 @@ +/*! + ************************************************************************************* + * \file rdoq.c + * + * \brief + * Rate Distortion Optimized Quantization based on VCEG-AH21 + ************************************************************************************* + */ + +#include "contributors.h" + +#include +#include + +#include "global.h" +#include "image.h" +#include "fmo.h" +#include "macroblock.h" +#include "mb_access.h" +#include "rdopt.h" +#include "rdoq.h" +#include "mv_search.h" + +#define RDOQ_BASE 0 + + +/*! +**************************************************************************** +* \brief +* Initialize the parameters related to RDO_Q in slice level +**************************************************************************** +*/ +void init_rdoq_slice(Slice *currSlice) +{ + //currSlice->norm_factor_4x4 = (double) ((int64) 1 << (2 * DQ_BITS + 19)); // norm factor 4x4 is basically (1<<31) + //currSlice->norm_factor_8x8 = (double) ((int64) 1 << (2 * Q_BITS_8 + 9)); // norm factor 8x8 is basically (1<<41) + currSlice->norm_factor_4x4 = pow(2, (2 * DQ_BITS + 19)); + currSlice->norm_factor_8x8 = pow(2, (2 * Q_BITS_8 + 9)); +} +/*! +**************************************************************************** +* \brief +* Initialize levelData for Chroma DC +**************************************************************************** +*/ +int init_trellis_data_DC_cr_CAVLC(Macroblock *currMB, int **tblock, int qp_per, int qp_rem, + LevelQuantParams *q_params, const byte *p_scan, + levelDataStruct *dataLevel) +{ + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + int i, j, coeff_ctr, end_coeff_ctr = p_Vid->num_cdc_coeff; + int *m7; + int q_bits = Q_BITS + qp_per + 1; + int q_offset = ( 1 << (q_bits - 1) ); + double err; + int scaled_coeff, level, lowerInt, k; + double estErr = (double) estErr4x4[qp_rem][0][0] / currSlice->norm_factor_4x4; // note that we could also use int64 + + for (coeff_ctr = 0; coeff_ctr < end_coeff_ctr; coeff_ctr++) + { + j = *p_scan++; // horizontal position + i = *p_scan++; // vertical position + + m7 = &tblock[j][i]; + if (*m7 == 0) + { + dataLevel->level[0] = 0; + dataLevel->levelDouble = 0; + dataLevel->errLevel[0] = 0.0; + dataLevel->noLevels = 1; + err = 0.0; + dataLevel->pre_level = 0; + dataLevel->sign = 0; + } + else + { + scaled_coeff = iabs (*m7) * q_params->ScaleComp; + dataLevel->levelDouble = scaled_coeff; + level = (scaled_coeff >> q_bits); + + lowerInt = ((scaled_coeff - (level << q_bits)) < q_offset )? 1 : 0; + dataLevel->level[0] = 0; + if (level == 0 && lowerInt == 1) + { + dataLevel->noLevels = 1; + } + else if (level == 0 && lowerInt == 0) + { + dataLevel->level[1] = level + 1; + dataLevel->noLevels = 2; + } + else if (level > 0 && lowerInt == 1) + { + dataLevel->level[1] = level; + dataLevel->noLevels = 2; + } + else + { + dataLevel->level[1] = level; + dataLevel->level[2] = level + 1; + dataLevel->noLevels = 3; + } + + for (k = 0; k < dataLevel->noLevels; k++) + { + err = (double)(dataLevel->level[k] << q_bits) - (double)scaled_coeff; + dataLevel->errLevel[k] = (err * err * estErr); + } + + if(dataLevel->noLevels == 1) + dataLevel->pre_level = 0; + else + dataLevel->pre_level = (iabs (*m7) * q_params->ScaleComp + q_params->OffsetComp) >> q_bits; + dataLevel->sign = isign(*m7); + } + dataLevel++; + } + return 0; +} + + +/*! +**************************************************************************** +* \brief +* Initialize levelData for Chroma DC +**************************************************************************** +*/ +int init_trellis_data_DC_cr_CABAC(Macroblock *currMB, int **tblock, int qp_per, int qp_rem, + LevelQuantParams *q_params, const byte *p_scan, + levelDataStruct *dataLevel, int* kStart, int* kStop) +{ + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + int noCoeff = 0; + int i, j, coeff_ctr, end_coeff_ctr = p_Vid->num_cdc_coeff; + int *m7; + int q_bits = Q_BITS + qp_per + 1; + int q_offset = ( 1 << (q_bits - 1) ); + double err; + int scaled_coeff, level, lowerInt, k; + double estErr = (double) estErr4x4[qp_rem][0][0] / currSlice->norm_factor_4x4; // note that we could also use int64 + + for (coeff_ctr = 0; coeff_ctr < end_coeff_ctr; coeff_ctr++) + { + j = *p_scan++; // horizontal position + i = *p_scan++; // vertical position + + m7 = &tblock[j][i]; + if (*m7 == 0) + { + dataLevel->level[0] = 0; + dataLevel->levelDouble = 0; + dataLevel->errLevel[0] = 0.0; + dataLevel->noLevels = 1; + err = 0.0; + } + else + { + scaled_coeff = iabs (*m7) * q_params->ScaleComp; + dataLevel->levelDouble = scaled_coeff; + level = (scaled_coeff >> q_bits); + + lowerInt=( (scaled_coeff - (level << q_bits)) < q_offset )? 1 : 0; + dataLevel->level[0] = 0; + if (level == 0) + { + if (lowerInt == 1) + { + dataLevel->noLevels = 1; + } + else + { + dataLevel->level[1] = level + 1; + dataLevel->noLevels = 2; + *kStop = coeff_ctr; + noCoeff++; + } + } + else if (lowerInt == 1) + { + dataLevel->level[1] = level; + dataLevel->noLevels = 2; + *kStop = coeff_ctr; + noCoeff++; + } + else + { + dataLevel->level[1] = level; + dataLevel->level[2] = level + 1; + dataLevel->noLevels = 3; + *kStop = coeff_ctr; + *kStart = coeff_ctr; + noCoeff++; + } + + for (k = 0; k < dataLevel->noLevels; k++) + { + err = (double)(dataLevel->level[k] << q_bits) - (double)scaled_coeff; + dataLevel->errLevel[k] = (err * err * estErr); + } + } + dataLevel++; + } + return (noCoeff); +} + +void get_dQP_table(Slice *currSlice) +{ + int qp_offset = (currSlice->slice_type == B_SLICE) ? (currSlice->RDOQ_QP_Num / 3): (currSlice->RDOQ_QP_Num >> 1); + int deltaQPCnt, deltaQP; + + memset(currSlice->deltaQPTable, 0, sizeof(int)*9); + + for(deltaQPCnt = 0; deltaQPCnt < currSlice->RDOQ_QP_Num; deltaQPCnt++) + { + if (deltaQPCnt == 0) + deltaQP = 0; + else + { + if (deltaQPCnt <= qp_offset) + deltaQP = deltaQPCnt - 1 - qp_offset; + else + deltaQP = deltaQPCnt - qp_offset; + } + currSlice->deltaQPTable[deltaQPCnt] = deltaQP; + } +} + +void trellis_mp(Macroblock *currMB) +{ + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + InputParameters *p_Inp = currMB->p_Inp; + + int masterQP = 0, deltaQP; +#if RDOQ_BASE + int qp_left, qp_up; + const int deltaQPTabB[] = {0, 1, -1, 2, 3, -2, 4, 5, -3}; + const int deltaQPTabP[] = {0, -1, 1, -2, 2, -3, 3, -4, 4}; + int qp_anchor; +#endif + int deltaQPCnt; + + masterQP = p_Vid->masterQP = p_Vid->qp; + + currSlice->rdoq_motion_copy = 0; + + currSlice->rddata_trellis_best.min_rdcost = 1e30; + + if (p_Inp->symbol_mode == CABAC) + { + estRunLevel_CABAC(currMB, LUMA_4x4); + estRunLevel_CABAC(currMB, LUMA_16AC); + estRunLevel_CABAC(currMB, LUMA_16DC); + if (p_Inp->Transform8x8Mode) + estRunLevel_CABAC(currMB, LUMA_8x8); + + if (p_Vid->yuv_format != YUV400) + { + estRunLevel_CABAC(currMB, CHROMA_AC); + if (p_Vid->yuv_format == YUV420) + estRunLevel_CABAC(currMB, CHROMA_DC); + else + estRunLevel_CABAC(currMB, CHROMA_DC_2x4); + } + } + +#if RDOQ_BASE + qp_left = (currMB->mb_left) ? currMB->mb_left->qp : p_Vid->masterQP; + qp_up = (currMB->mb_up) ? currMB->mb_up->qp : p_Vid->masterQP; + qp_anchor = (qp_left + qp_up + 1)>>1; +#endif + + for (deltaQPCnt=0; deltaQPCnt < currSlice->RDOQ_QP_Num; deltaQPCnt++) + { + currSlice->rddata = &currSlice->rddata_trellis_curr; + currSlice->rddata->min_dcost = 1e30; +#if RDOQ_BASE + if (currSlice->slice_type == B_SLICE) + deltaQP = deltaQPTabB[deltaQPCnt]; + else + deltaQP = deltaQPTabP[deltaQPCnt]; +#else + + // It seems that pushing the masterQP as first helps things when fast me is enabled. + // Could there be an issue with motion estimation? + deltaQP = currSlice->deltaQPTable[deltaQPCnt]; +#endif + + p_Vid->qp = iClip3(-p_Vid->bitdepth_luma_qp_scale, 51, masterQP + deltaQP); + deltaQP = p_Vid->qp - masterQP; + + +#if RDOQ_BASE + if(deltaQP != 0 && !(p_Vid->qp - qp_anchor >= -2 && p_Vid->qp - qp_anchor <= 1) && currMB->mb_left && currMB->mb_up && currSlice->slice_type == P_SLICE) + continue; + if(deltaQP != 0 && !(p_Vid->qp - qp_anchor >= -1 && p_Vid->qp - qp_anchor <= 2) && currMB->mb_left && currMB->mb_up && currSlice->slice_type == B_SLICE) + continue; +#endif + + reset_macroblock(currMB); + currMB->qp = (short) p_Vid->qp; + update_qp (currMB); + + currSlice->encode_one_macroblock (currMB); + end_encode_one_macroblock(currMB); + + + if ( currSlice->rddata_trellis_curr.min_rdcost < currSlice->rddata_trellis_best.min_rdcost) + copy_rddata_trellis(currMB, &currSlice->rddata_trellis_best, currSlice->rddata); + + if (p_Inp->RDOQ_CP_MV) + currSlice->rdoq_motion_copy = 1; + +#if (!RDOQ_BASE) + if (p_Inp->RDOQ_Fast == 1) + { + if ((p_Vid->qp - currSlice->rddata_trellis_best.qp > 1)) + break; + if ((currSlice->rddata_trellis_curr.cbp == 0) && (currSlice->rddata_trellis_curr.mb_type != 0)) + break; + if ((currSlice->rddata_trellis_best.mb_type == 0) && (currSlice->rddata_trellis_best.cbp == 0)) + break; + } +#endif + } + + reset_macroblock(currMB); + currSlice->rddata = &currSlice->rddata_trellis_best; + + copy_rdopt_data (currMB); // copy the MB data for Top MB from the temp buffers + write_macroblock (currMB, 1); + p_Vid->qp = masterQP; +} + +void trellis_sp(Macroblock *currMB) +{ + VideoParameters *p_Vid = currMB->p_Vid; + InputParameters *p_Inp = currMB->p_Inp; + Slice *currSlice = currMB->p_Slice; + + p_Vid->masterQP = p_Vid->qp; + + if (currSlice->symbol_mode == CABAC) + { + estRunLevel_CABAC(currMB, LUMA_4x4); + estRunLevel_CABAC(currMB, LUMA_16AC); + + estRunLevel_CABAC(currMB, LUMA_16DC); + if (p_Inp->Transform8x8Mode) + estRunLevel_CABAC(currMB, LUMA_8x8); + + if (p_Vid->yuv_format != YUV400) + { + estRunLevel_CABAC(currMB, CHROMA_AC); + + if (p_Vid->yuv_format == YUV420) + estRunLevel_CABAC(currMB, CHROMA_DC); + else + estRunLevel_CABAC(currMB, CHROMA_DC_2x4); + } + } + + currSlice->encode_one_macroblock (currMB); + end_encode_one_macroblock(currMB); + + write_macroblock (currMB, 1); +} + +void trellis_coding(Macroblock *currMB) +{ + if (currMB->p_Slice->RDOQ_QP_Num > 1) + { + trellis_mp(currMB); + } + else + { + trellis_sp(currMB); + } +} + +void RDOQ_update_mode(Slice *currSlice, RD_PARAMS *enc_mb) +{ + InputParameters *p_Inp = currSlice->p_Inp; +#if (MVC_EXTENSION_ENABLE) + int *InterSearch = p_Inp->InterSearch[(currSlice->p_Vid->num_of_layers > 1) ? currSlice->view_id : 0][(currSlice->slice_type == B_SLICE)]; +#else + int *InterSearch = p_Inp->InterSearch[0][(currSlice->slice_type == B_SLICE)]; +#endif + + int mb_type = currSlice->rddata_trellis_best.mb_type; + int i; + for(i=0; ivalid[i] = 0; + + enc_mb->valid[mb_type] = 1; + + if(mb_type == P8x8) + { + enc_mb->valid[4] = (short) (InterSearch[4]); + enc_mb->valid[5] = (short) (InterSearch[5] && !(p_Inp->Transform8x8Mode==2)); + enc_mb->valid[6] = (short) (InterSearch[6] && !(p_Inp->Transform8x8Mode==2)); + enc_mb->valid[7] = (short) (InterSearch[7] && !(p_Inp->Transform8x8Mode==2)); + } +} + +void copy_rddata_trellis (Macroblock *currMB, RD_DATA *dest, RD_DATA *src) +{ + VideoParameters *p_Vid = currMB->p_Vid; + int j; + + dest->min_rdcost = src->min_rdcost; + dest->min_dcost = src->min_dcost; + dest->min_rate = src->min_rate; + + memcpy(&dest->rec_mb[0][0][0],&src->rec_mb[0][0][0], MB_PIXELS * sizeof(imgpel)); + + if (p_Vid->yuv_format != YUV400) + { + memcpy(&dest->rec_mb[1][0][0],&src->rec_mb[1][0][0], 2 * MB_PIXELS * sizeof(imgpel)); + } + + memcpy(&dest->cofAC[0][0][0][0], &src->cofAC[0][0][0][0], (4 + p_Vid->num_blk8x8_uv) * 4 * 2 * 65 * sizeof(int)); + memcpy(&dest->cofDC[0][0][0], &src->cofDC[0][0][0], 3 * 2 * 18 * sizeof(int)); + + dest->mb_type = src->mb_type; + + memcpy(dest->b8x8, src->b8x8, BLOCK_MULTIPLE * sizeof(Info8x8)); + + dest->cbp = src->cbp; + dest->mode = src->mode; + dest->i16offset = src->i16offset; + dest->i16mode = src->i16mode; + dest->c_ipred_mode = src->c_ipred_mode; + dest->luma_transform_size_8x8_flag = src->luma_transform_size_8x8_flag; + dest->NoMbPartLessThan8x8Flag = src->NoMbPartLessThan8x8Flag; + dest->qp = src->qp; + + dest->prev_qp = src->prev_qp; + dest->prev_dqp = src->prev_dqp; + dest->prev_cbp = src->prev_cbp; + dest->cbp_blk = src->cbp_blk; + + + if (p_Vid->type != I_SLICE && p_Vid->type != SI_SLICE) + { + // note that this is not copying the bipred mvs!!! + memcpy(&dest->all_mv [0][0][0][0][0], &src->all_mv [0][0][0][0][0], p_Vid->max_num_references * 288 * sizeof(MotionVector)); // 2 * ref * 9 * 4 * 4 + } + + memcpy(dest->intra_pred_modes,src->intra_pred_modes, MB_BLOCK_PARTITIONS * sizeof(char)); + memcpy(dest->intra_pred_modes8x8,src->intra_pred_modes8x8, MB_BLOCK_PARTITIONS * sizeof(char)); + for(j = currMB->block_y; j < currMB->block_y + BLOCK_MULTIPLE; j++) + memcpy(&dest->ipredmode[j][currMB->block_x],&src->ipredmode[j][currMB->block_x], BLOCK_MULTIPLE * sizeof(char)); + + memcpy(&dest->refar[LIST_0][0][0], &src->refar[LIST_0][0][0], 2 * BLOCK_MULTIPLE * BLOCK_MULTIPLE * sizeof(char)); + +} + +void updateMV_mp(Macroblock *currMB, distblk *m_cost, short ref, int list, int h, int v, int blocktype, int block8x8) +{ + InputParameters *p_Inp = currMB->p_Inp; + Slice *currSlice = currMB->p_Slice; + + int i, j; + int bsx = block_size[blocktype][0] >> 2; + int bsy = block_size[blocktype][1] >> 2; + MotionVector all_mv; + + if ( (p_Inp->Transform8x8Mode == 1) && (blocktype == 4) && currMB->luma_transform_size_8x8_flag) + { + all_mv = currSlice->tmp_mv8[list][ref][v][h]; + *m_cost = currSlice->motion_cost8[list][ref][block8x8]; + } + else if ( (p_Inp->Transform8x8Mode == 1) && (blocktype == 4) && currMB->luma_transform_size_8x8_flag == 0) + { + all_mv = currSlice->tmp_mv4[list][ref][v][h]; + *m_cost = currSlice->motion_cost4[list][ref][block8x8]; + } + else + { + all_mv = currSlice->rddata_trellis_best.all_mv[list][ref][blocktype][v][h]; + } + + for (j = 0; j < bsy; j++) + { + for (i = 0; i < bsx; i++) + { + currSlice->all_mv[list][ref][blocktype][v+j][h+i] = all_mv; + } + } +} + + + diff --git a/lencod/src/rdoq_cabac.c b/lencod/src/rdoq_cabac.c new file mode 100644 index 0000000..9939e2f --- /dev/null +++ b/lencod/src/rdoq_cabac.c @@ -0,0 +1,888 @@ +/*! + ************************************************************************************* + * \file rdoq_cabac.c + * + * \brief + * Rate Distortion Optimized Quantization based on VCEG-AH21 related to CABAC + ************************************************************************************* + */ + +#include "contributors.h" + +#include +#include + +#include "global.h" +#include "cabac.h" +#include "image.h" +#include "fmo.h" +#include "macroblock.h" +#include "mb_access.h" +#include "rdoq.h" + +#define RDOQ_SQ 0 + +static const int entropyBits[128]= +{ + 895, 943, 994, 1048, 1105, 1165, 1228, 1294, + 1364, 1439, 1517, 1599, 1686, 1778, 1875, 1978, + 2086, 2200, 2321, 2448, 2583, 2725, 2876, 3034, + 3202, 3380, 3568, 3767, 3977, 4199, 4435, 4684, + 4948, 5228, 5525, 5840, 6173, 6527, 6903, 7303, + 7727, 8178, 8658, 9169, 9714, 10294, 10914, 11575, + 12282, 13038, 13849, 14717, 15650, 16653, 17734, 18899, + 20159, 21523, 23005, 24617, 26378, 28306, 30426, 32768, + 32768, 35232, 37696, 40159, 42623, 45087, 47551, 50015, + 52479, 54942, 57406, 59870, 62334, 64798, 67262, 69725, + 72189, 74653, 77117, 79581, 82044, 84508, 86972, 89436, + 91900, 94363, 96827, 99291, 101755, 104219, 106683, 109146, + 111610, 114074, 116538, 119002, 121465, 123929, 126393, 128857, + 131321, 133785, 136248, 138712, 141176, 143640, 146104, 148568, + 151031, 153495, 155959, 158423, 160887, 163351, 165814, 168278, + 170742, 173207, 175669, 178134, 180598, 183061, 185525, 187989 +}; + + +static int biari_no_bits(signed short symbol, BiContextTypePtr bi_ct ) +{ + int ctx_state, estBits; + + symbol = (short) (symbol != 0); + + ctx_state = (symbol == bi_ct->MPS) ? 64 + bi_ct->state : 63 - bi_ct->state; + estBits = entropyBits[127 - ctx_state]; + + return(estBits); +} + +static int biari_state(signed short symbol, BiContextTypePtr bi_ct ) +{ + int ctx_state; + + symbol = (short) (symbol != 0); + ctx_state = (symbol == bi_ct->MPS) ? 64 + bi_ct->state : 63 - bi_ct->state; + + return(ctx_state); +} + +/*! +**************************************************************************** +* \brief +* estimate bit cost for each CBP bit +**************************************************************************** +*/ +static void est_CBP_block_bit (Macroblock* currMB, int type) +{ + Slice *currSlice = currMB->p_Slice; + estBitsCabacStruct *cabacEstBits = &currSlice->estBitsCabac[type]; + int ctx; + + for (ctx=0; ctx<=3; ctx++) + { + cabacEstBits->blockCbpBits[ctx][0]=biari_no_bits(0, currSlice->tex_ctx->bcbp_contexts[type2ctx_bcbp[type]]+ctx); + + cabacEstBits->blockCbpBits[ctx][1]=biari_no_bits(1, currSlice->tex_ctx->bcbp_contexts[type2ctx_bcbp[type]]+ctx); + } +} + +/*! +**************************************************************************** +* \brief +* estimate CABAC bit cost for significant coefficient map +**************************************************************************** +*/ +static void est_significance_map(Macroblock* currMB, int type) +{ + Slice *currSlice = currMB->p_Slice; + int k; + int k1 = maxpos[type]-1; +#if ENABLE_FIELD_CTX + int fld = ( currSlice->structure!=FRAME || currMB->mb_field ); +#else + int fld = 0; +#endif + BiContextTypePtr map_ctx = currSlice->tex_ctx->map_contexts [fld][type2ctx_map [type]]; + BiContextTypePtr last_ctx = currSlice->tex_ctx->last_contexts[fld][type2ctx_last[type]]; + const byte *pos2ctxmap = fld ? pos2ctx_map_int [type] : pos2ctx_map[type]; + const byte *pos2ctxlast = pos2ctx_last[type]; + + estBitsCabacStruct *cabacEstBits = &currSlice->estBitsCabac[type]; + + for (k = 0; k < k1; k++) // if last coeff is reached, it has to be significant + { + cabacEstBits->significantBits[pos2ctxmap[k]][0] = biari_no_bits (0, map_ctx + pos2ctxmap[k]); + + cabacEstBits->significantBits[pos2ctxmap[k]][1] = biari_no_bits (1, map_ctx + pos2ctxmap[k]); + + cabacEstBits->lastBits[pos2ctxlast[k]][0] = biari_no_bits(0, last_ctx+pos2ctxlast[k]); + + cabacEstBits->lastBits[pos2ctxlast[k]][1] = biari_no_bits(1, last_ctx+pos2ctxlast[k]); + } + // if last coeff is reached, it has to be significant + cabacEstBits->significantBits[pos2ctxmap[k1]][0]=0; + cabacEstBits->significantBits[pos2ctxmap[k1]][1]=0; + cabacEstBits->lastBits[pos2ctxlast[k1]][0]=0; + cabacEstBits->lastBits[pos2ctxlast[k1]][1]=0; +} + +/*! +**************************************************************************** +* \brief +* estimate bit cost of significant coefficient +**************************************************************************** +*/ +static void est_significant_coefficients (Macroblock* currMB, int type) +{ + Slice *currSlice = currMB->p_Slice; + int ctx; + int maxCtx = imin(4, max_c2[type]); + estBitsCabacStruct *cabacEstBits = &currSlice->estBitsCabac[type]; + + for (ctx=0; ctx<=4; ctx++){ + cabacEstBits->greaterOneBits[0][ctx][0]= + biari_no_bits (0, currSlice->tex_ctx->one_contexts[type2ctx_one[type]] + ctx); + + cabacEstBits->greaterOneBits[0][ctx][1]= + biari_no_bits (1, currSlice->tex_ctx->one_contexts[type2ctx_one[type]] + ctx); + } + + for (ctx=0; ctx<=maxCtx; ctx++){ + cabacEstBits->greaterOneBits[1][ctx][0]= + biari_no_bits(0, currSlice->tex_ctx->abs_contexts[type2ctx_abs[type]] + ctx); + + cabacEstBits->greaterOneState[ctx]=biari_state(0, currSlice->tex_ctx->abs_contexts[type2ctx_abs[type]] + ctx); + + cabacEstBits->greaterOneBits[1][ctx][1]= + biari_no_bits(1, currSlice->tex_ctx->abs_contexts[type2ctx_abs[type]] + ctx); + } +} + +/*! +**************************************************************************** +* \brief +* estimate unary exp golomb bit cost +**************************************************************************** +*/ +static int est_unary_exp_golomb_level_encode(Macroblock *currMB, unsigned int symbol, int ctx, int type) +{ + Slice *currSlice = currMB->p_Slice; + estBitsCabacStruct *cabacEstBits = &currSlice->estBitsCabac[type]; + unsigned int l = symbol, k = 1; + unsigned int exp_start = 13; // 15-2 : 0,1 level decision always sent + int estBits; + + if (symbol==0) + { + estBits = cabacEstBits->greaterOneBits[1][ctx][0]; + return (estBits); + } + else + { + estBits = cabacEstBits->greaterOneBits[1][ctx][1]; + // this could be done using min of the two conditionals * value + while (((--l)>0) && (++k <= exp_start)) + { + estBits += cabacEstBits->greaterOneBits[1][ctx][1]; + } + + if (symbol < exp_start) + { + estBits += cabacEstBits->greaterOneBits[1][ctx][0]; + } + else + { + estBits += est_exp_golomb_encode_eq_prob(symbol - exp_start); + } + } + return(estBits); +} + +void precalculate_unary_exp_golomb_level(VideoParameters *p_Vid) +{ + int state, ctx_state0, ctx_state1, estBits0, estBits1, symbol; + + for (state=0; state<=63; state++) + { + // symbol 0 is MPS + ctx_state0 = 64 + state; + estBits0 = entropyBits[127-ctx_state0]; + ctx_state1 = 63 - state; + estBits1 = entropyBits[127-ctx_state1]; + + for (symbol = 0; symbol < MAX_PREC_COEFF; symbol++) + { + p_Vid->precalcUnaryLevelTab[ctx_state0][symbol] = est_unary_exp_golomb_level_bits(symbol, estBits0, estBits1); + + // symbol 0 is LPS + p_Vid->precalcUnaryLevelTab[ctx_state1][symbol] =est_unary_exp_golomb_level_bits(symbol, estBits1, estBits0); + } + } +} + +int est_unary_exp_golomb_level_bits(unsigned int symbol, int bits0, int bits1) +{ + unsigned int l,k; + unsigned int exp_start = 13; // 15-2 : 0,1 level decision always sent + int estBits; + + if (symbol == 0) + { + return (bits0); + } + else + { + estBits = bits1; + l = symbol; + k = 1; + while (((--l)>0) && (++k <= exp_start)) + { + estBits += bits1; + } + if (symbol < exp_start) + { + estBits += bits0; + } + else + { + estBits += est_exp_golomb_encode_eq_prob(symbol - exp_start); + } + } + return(estBits); +} + +/*! +**************************************************************************** +* \brief +* estimate exp golomb bit cost +**************************************************************************** +*/ +int est_exp_golomb_encode_eq_prob(unsigned int symbol) +{ + int k = 0, estBits = 0; + + for(;;) + { + if (symbol >= (unsigned int)(1<p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + estBitsCabacStruct *cabacEstBits = &currSlice->estBitsCabac[type]; + + int y_ac = (type==LUMA_16AC || type==LUMA_8x8 || type==LUMA_8x4 || type==LUMA_4x8 || type==LUMA_4x4 + || type==CB_16AC || type==CB_8x8 || type==CB_8x4 || type==CB_4x8 || type==CB_4x4 + || type==CR_16AC || type==CR_8x8 || type==CR_8x4 || type==CR_4x8 || type==CR_4x4); + int y_dc = (type==LUMA_16DC || type==CB_16DC || type==CR_16DC); + int u_ac = (type==CHROMA_AC && !p_Vid->is_v_block); + int v_ac = (type==CHROMA_AC && p_Vid->is_v_block); + int chroma_dc = (type==CHROMA_DC || type==CHROMA_DC_2x4 || type==CHROMA_DC_4x4); + int u_dc = (chroma_dc && !p_Vid->is_v_block); + int v_dc = (chroma_dc && p_Vid->is_v_block); + int j = (y_ac || u_ac || v_ac ? currMB->subblock_y : 0); + int i = (y_ac || u_ac || v_ac ? currMB->subblock_x : 0); + int bit; + int default_bit = (IS_INTRA(currMB) ? 1 : 0); + int upper_bit = default_bit; + int left_bit = default_bit; + int ctx, estBits = 0; + + int bit_pos_a = 0; + int bit_pos_b = 0; + + PixelPos block_a, block_b; + + if (y_ac || y_dc) + { + get4x4Neighbour(currMB, i - 1, j , p_Vid->mb_size[IS_LUMA], &block_a); + get4x4Neighbour(currMB, i, j -1, p_Vid->mb_size[IS_LUMA], &block_b); + if (y_ac) + { + if (block_a.available) + bit_pos_a = 4*block_a.y + block_a.x; + if (block_b.available) + bit_pos_b = 4*block_b.y + block_b.x; + } + } + else + { + get4x4Neighbour(currMB, i - 1, j , p_Vid->mb_size[IS_CHROMA], &block_a); + get4x4Neighbour(currMB, i, j - 1, p_Vid->mb_size[IS_CHROMA], &block_b); + if (u_ac||v_ac) + { + if (block_a.available) + bit_pos_a = (block_a.y << 2) + block_a.x; + if (block_b.available) + bit_pos_b = (block_b.y << 2) + block_b.x; + } + } + + bit = (y_dc ? 0 : y_ac ? 1 : u_dc ? 17 : v_dc ? 18 : u_ac ? 19 : 35); + + if (p_Vid->enc_picture->chroma_format_idc!=YUV444 || (currMB->p_Inp->separate_colour_plane_flag != 0)) + { + if (type!=LUMA_8x8) + { + if (block_b.available) + { + if(p_Vid->mb_data[block_b.mb_addr].mb_type==IPCM) + upper_bit = 1; + else + upper_bit = get_bit(p_Vid->mb_data[block_b.mb_addr].cbp_bits[0], bit + bit_pos_b); + } + + if (block_a.available) + { + if(p_Vid->mb_data[block_a.mb_addr].mb_type==IPCM) + left_bit = 1; + else + left_bit = get_bit(p_Vid->mb_data[block_a.mb_addr].cbp_bits[0], bit + bit_pos_a); + } + + ctx = 2*upper_bit+left_bit; + //===== encode symbol ===== + estBits = cabacEstBits->blockCbpBits[ctx][0] - cabacEstBits->blockCbpBits[ctx][1]; + } + } + else + { + if (block_b.available) + { + if(p_Vid->mb_data[block_b.mb_addr].mb_type == IPCM) + upper_bit=1; + else + { + if(type==LUMA_8x8) + upper_bit = get_bit(p_Vid->mb_data[block_b.mb_addr].cbp_bits_8x8[0], bit + bit_pos_b); + else if (type==CB_8x8) + upper_bit = get_bit(p_Vid->mb_data[block_b.mb_addr].cbp_bits_8x8[1], bit + bit_pos_b); + else if (type==CR_8x8) + upper_bit = get_bit(p_Vid->mb_data[block_b.mb_addr].cbp_bits_8x8[2], bit + bit_pos_b); + else if ((type==CB_4x4)||(type==CB_4x8)||(type==CB_8x4)||(type==CB_16AC)||(type==CB_16DC)) + upper_bit = get_bit(p_Vid->mb_data[block_b.mb_addr].cbp_bits[1], bit + bit_pos_b); + else if ((type==CR_4x4)||(type==CR_4x8)||(type==CR_8x4)||(type==CR_16AC)||(type==CR_16DC)) + upper_bit = get_bit(p_Vid->mb_data[block_b.mb_addr].cbp_bits[2], bit + bit_pos_b); + else + upper_bit = get_bit(p_Vid->mb_data[block_b.mb_addr].cbp_bits[0], bit + bit_pos_b); + } + } + + if (block_a.available) + { + if(p_Vid->mb_data[block_a.mb_addr].mb_type==IPCM) + left_bit = 1; + else + { + if(type==LUMA_8x8) + left_bit = get_bit(p_Vid->mb_data[block_a.mb_addr].cbp_bits_8x8[0],bit + bit_pos_a); + else if (type==CB_8x8) + left_bit = get_bit(p_Vid->mb_data[block_a.mb_addr].cbp_bits_8x8[1],bit + bit_pos_a); + else if (type==CR_8x8) + left_bit = get_bit(p_Vid->mb_data[block_a.mb_addr].cbp_bits_8x8[2],bit + bit_pos_a); + else if ((type==CB_4x4)||(type==CB_4x8)||(type==CB_8x4)||(type==CB_16AC)||(type==CB_16DC)) + left_bit = get_bit(p_Vid->mb_data[block_a.mb_addr].cbp_bits[1], bit + bit_pos_a); + else if ((type==CR_4x4)||(type==CR_4x8)||(type==CR_8x4)||(type==CR_16AC)||(type==CR_16DC)) + left_bit = get_bit(p_Vid->mb_data[block_a.mb_addr].cbp_bits[2], bit + bit_pos_a); + else + left_bit = get_bit(p_Vid->mb_data[block_a.mb_addr].cbp_bits[0], bit + bit_pos_a); + } + } + + ctx = 2*upper_bit+left_bit; + + //===== encode symbol ===== + estBits = cabacEstBits->blockCbpBits[ctx][0] - cabacEstBits->blockCbpBits[ctx][1]; + } + + return(estBits); +} +/*! +**************************************************************************** +* \brief +* Rate distortion optimized trellis quantization +**************************************************************************** +*/ +void est_writeRunLevel_CABAC(Macroblock *currMB, levelDataStruct levelData[], int levelTabMin[], int type, double lambda, int kInit, int kStop, + int noCoeff, int estCBP) +{ + VideoParameters *p_Vid = currMB->p_Vid; + Slice *currSlice = currMB->p_Slice; + estBitsCabacStruct *cabacEstBits = &currSlice->estBitsCabac[type]; + int k, i; + int estBits; + double lagr, lagrMin=0, lagrTabMin, lagrTab; + int c1 = 1, c2 = 0, c1Tab[3], c2Tab[3]; + int iBest, levelTab[64]; + int ctx, greater_one, last, maxK = maxpos[type]; + double lagrAcc, lagrLastMin=0, lagrLast; + int kBest=0, kStart, first; + levelDataStruct *dataLevel; + int *last_bits, *significant_bits; + + memset(levelTabMin, 0, (maxK + 1) * sizeof(int)); + + if (noCoeff > 0) + { + if (noCoeff > 1) + { + kStart = kInit; kBest = 0; first = 1; + lagrAcc = 0.0; + + for (k = kStart; k <= kStop; k++) + { + lagrAcc += levelData[k].errLevel[0]; + } + + if (levelData[kStart].noLevels > 2) + { + last_bits = cabacEstBits->lastBits[pos2ctx_last[type][kStart]]; + lagrAcc -= levelData[kStart].errLevel[0]; + lagrLastMin = lambda * (last_bits[1] - last_bits[0]) + lagrAcc; + + kBest = kStart; + kStart++; + first = 0; + } + + for (k = kStart; k <= kStop; k++) + { + dataLevel = &levelData[k]; + significant_bits = cabacEstBits->significantBits[pos2ctx_map[type][k]]; + lagrMin = dataLevel->errLevel[0] + lambda * significant_bits[0]; + lagrAcc -= dataLevel->errLevel[0]; + + if (dataLevel->noLevels > 1) + { + last_bits = cabacEstBits->lastBits[pos2ctx_last[type][k]]; + estBits = SIGN_BITS + significant_bits[1] + cabacEstBits->greaterOneBits[0][4][0]; + + lagr = dataLevel->errLevel[1] + lambda * (estBits); + + lagrLast = lagr + lambda * last_bits[1] + lagrAcc; + lagr = lagr + lambda * last_bits[0]; + + lagrMin = (lagr < lagrMin) ? lagr : lagrMin; + + if ((lagrLast < lagrLastMin) || (first == 1)) + { + kBest = k; + first = 0; + lagrLastMin = lagrLast; + } + } + lagrAcc += lagrMin; + } + kStart = kBest; + } + else + { + kStart = kStop; + } + + lagrTabMin = 0.0; + for (k = 0; k <= kStart; k++) + { + lagrTabMin += levelData[k].errLevel[0]; + } + // Initial Lagrangian calculation + lagrTab = 0.0; + + ////////////////////////// + + lagrTabMin += (lambda*estCBP); + iBest = 0; first = 1; + for (k = kStart; k >= 0; k--) + { + significant_bits = cabacEstBits->significantBits[pos2ctx_map[type][k]]; + last_bits = cabacEstBits->lastBits[pos2ctx_last[type][k]]; + dataLevel = &levelData[k]; + last = (k == kStart); + + if (!last) + { + lagrMin = dataLevel->errLevel[0] + lambda * significant_bits[0]; + iBest = 0; + first = 0; + } + + for (i = 1; i < dataLevel->noLevels; i++) + { + estBits = SIGN_BITS + significant_bits[1]; + estBits += last_bits[last]; + + // greater than 1 + greater_one = (dataLevel->level[i] > 1); + + c1Tab[i] = c1; + c2Tab[i] = c2; + + ctx = imin(c1Tab[i], 4); + estBits += cabacEstBits->greaterOneBits[0][ctx][greater_one]; + + // magnitude if greater than 1 + if (greater_one) + { + ctx = imin(c2Tab[i], max_c2[type]); + if ( (dataLevel->level[i] - 2) < MAX_PREC_COEFF) + { + estBits += p_Vid->precalcUnaryLevelTab[cabacEstBits->greaterOneState[ctx]][dataLevel->level[i] - 2]; + } + else + { + estBits += est_unary_exp_golomb_level_encode(currMB, dataLevel->level[i] - 2, ctx, type); + } + + c1Tab[i] = 0; + c2Tab[i]++; + } + else if (c1Tab[i]) + { + c1Tab[i]++; + } + + lagr = dataLevel->errLevel[i] + lambda * estBits; + if ((lagr < lagrMin) || (first == 1)) + { + iBest = i; + lagrMin=lagr; + first = 0; + } + } + + if (iBest > 0) + { + c1 = c1Tab[iBest]; + c2 = c2Tab[iBest]; + } + + levelTab[k] = dataLevel->level[iBest]; + lagrTab += lagrMin; + } + /////////////////////////////////// + + if (lagrTab < lagrTabMin) + { + memcpy(levelTabMin, levelTab, (kStart + 1) * sizeof(int)); + } + } +} + + +/*! +**************************************************************************** +* \brief +* Initialize levelData +**************************************************************************** +*/ +int init_trellis_data_4x4_CABAC(Macroblock *currMB, int **tblock, + struct quant_methods *q_method, const byte *p_scan, + levelDataStruct *dataLevel, int* kStart, int* kStop, int type) +{ + VideoParameters *p_Vid = currMB->p_Vid; + LevelQuantParams **q_params_4x4 = q_method->q_params; + QuantParameters *p_Quant = p_Vid->p_Quant; + int qp = q_method->qp; + int qp_per = p_Quant->qp_per_matrix[qp]; + int qp_rem = p_Quant->qp_rem_matrix[qp]; + int block_x = q_method->block_x; + + Slice *currSlice = currMB->p_Slice; + int noCoeff = 0; + int i, j, coeff_ctr; + int *m7; + int end_coeff_ctr = ( ( type == LUMA_4x4 ) ? 16 : 15 ); + int q_bits = Q_BITS + qp_per; + int q_offset = ( 1 << (q_bits - 1) ); + int scaled_coeff, level, lowerInt, k; + double err, estErr; + + for (coeff_ctr = 0; coeff_ctr < end_coeff_ctr; coeff_ctr++) + { + i = *p_scan++; // horizontal position + j = *p_scan++; // vertical position + + m7 = &tblock[j][block_x + i]; + + if (*m7 == 0) + { + dataLevel->level[0] = 0; + dataLevel->levelDouble = 0; + dataLevel->errLevel[0] = 0.0; + dataLevel->noLevels = 1; + //err = 0.0; + } + else + { + estErr = ((double) estErr4x4[qp_rem][j][i]) / currSlice->norm_factor_4x4; + + scaled_coeff = iabs(*m7) * q_params_4x4[j][i].ScaleComp; + dataLevel->levelDouble = scaled_coeff; + level = (scaled_coeff >> q_bits); + + lowerInt = ((scaled_coeff - (level << q_bits)) < q_offset )? 1 : 0; + +#if RDOQ_SQ + dataLevel->level[0] = max(0, level - 1); +#else + dataLevel->level[0] = 0; +#endif + if (level == 0) + { + if (lowerInt == 1) + { + dataLevel->noLevels = 1; + } + else + { + dataLevel->level[1] = 1; + dataLevel->noLevels = 2; + *kStop = coeff_ctr; + noCoeff++; + } + } + else if (lowerInt == 1) + { + dataLevel->level[1] = level; + dataLevel->noLevels = 2; + *kStop = coeff_ctr; + noCoeff++; + } + else + { + dataLevel->level[1] = level; + dataLevel->level[2] = level + 1; + dataLevel->noLevels = 3; + *kStop = coeff_ctr; + *kStart = coeff_ctr; + noCoeff++; + } + + for (k = 0; k < dataLevel->noLevels; k++) + { + err = (double)(dataLevel->level[k] << q_bits) - (double)scaled_coeff; + dataLevel->errLevel[k] = (err * err * estErr); + } + } + + dataLevel++; + } + return (noCoeff); +} + +/* +**************************************************************************** +* \brief +* Initialize levelData +**************************************************************************** +*/ +int init_trellis_data_8x8_CABAC(Macroblock *currMB, int **tblock, int block_x, int qp_per, int qp_rem, LevelQuantParams **q_params, const byte *p_scan, + levelDataStruct *dataLevel, int* kStart, int* kStop) +{ + Slice *currSlice = currMB->p_Slice; + int noCoeff = 0; + int *m7; + int i, j, coeff_ctr, end_coeff_ctr = 64; + int q_bits = Q_BITS_8 + qp_per; + int q_offset = ( 1 << (q_bits - 1) ); + double err, estErr; + int level, lowerInt, k; + int scaled_coeff; + + for (coeff_ctr = 0; coeff_ctr < end_coeff_ctr; coeff_ctr++) + { + i = *p_scan++; // horizontal position + j = *p_scan++; // vertical position + + m7 = &tblock[j][block_x + i]; + + if (*m7 == 0) + { + dataLevel->level[0] = 0; + dataLevel->levelDouble = 0; + dataLevel->errLevel[0] = 0.0; + dataLevel->noLevels = 1; + //err = 0.0; + } + else + { + estErr = (double) estErr8x8[qp_rem][j][i] / currSlice->norm_factor_8x8; + + scaled_coeff = iabs(*m7) * q_params[j][i].ScaleComp; + dataLevel->levelDouble = scaled_coeff; + level = (scaled_coeff >> q_bits); + + lowerInt = ((scaled_coeff - (level << q_bits)) < q_offset )? 1 : 0; + +#if RDOQ_SQ + dataLevel->level[0] = max(0, level - 1); +#else + dataLevel->level[0] = 0; +#endif + if (level == 0) + { + if (lowerInt == 1) + { + dataLevel->noLevels = 1; + } + else + { + dataLevel->level[1] = 1; + dataLevel->noLevels = 2; + *kStop = coeff_ctr; + noCoeff++; + } + } + else if (lowerInt == 1) + { + dataLevel->level[1] = level; + dataLevel->noLevels = 2; + *kStop = coeff_ctr; + noCoeff++; + } + else + { + dataLevel->level[1] = level; + dataLevel->level[2] = level + 1; + dataLevel->noLevels = 3; + *kStop = coeff_ctr; + *kStart = coeff_ctr; + noCoeff++; + } + + for (k = 0; k < dataLevel->noLevels; k++) + { + err = (double)(dataLevel->level[k] << q_bits) - (double)scaled_coeff; + dataLevel->errLevel[k] = (err * err * estErr); + } + } + + dataLevel++; + } + return (noCoeff); +} + +/*! +**************************************************************************** +* \brief +* Initialize levelData for Luma DC +**************************************************************************** +*/ +int init_trellis_data_DC_CABAC(Macroblock *currMB, int **tblock, int qp_per, int qp_rem, + LevelQuantParams *q_params, const byte *p_scan, + levelDataStruct *dataLevel, int* kStart, int* kStop) +{ + Slice *currSlice = currMB->p_Slice; + int noCoeff = 0; + int i, j, coeff_ctr, end_coeff_ctr = 16; + int q_bits = Q_BITS + qp_per + 1; + int q_offset = ( 1 << (q_bits - 1) ); + int scaled_coeff, level, lowerInt, k; + int *m7; + double err, estErr = (double) estErr4x4[qp_rem][0][0] / currSlice->norm_factor_4x4; // note that we could also use int64 + + for (coeff_ctr = 0; coeff_ctr < end_coeff_ctr; coeff_ctr++) + { + i = *p_scan++; // horizontal position + j = *p_scan++; // vertical position + m7 = &tblock[j][i]; + + if (*m7 == 0) + { + dataLevel->level[0] = 0; + dataLevel->levelDouble = 0; + dataLevel->errLevel[0] = 0.0; + dataLevel->noLevels = 1; + //err = 0.0; + } + else + { + scaled_coeff = iabs(*m7) * q_params->ScaleComp; + dataLevel->levelDouble = scaled_coeff; + level = (scaled_coeff >> q_bits); + + lowerInt=( (scaled_coeff - (level<level[0] = max(0, level - 1); +#else + dataLevel->level[0] = 0; +#endif + if (level == 0) + { + if (lowerInt == 1) + { + dataLevel->noLevels = 1; + } + else + { + dataLevel->level[1] = 1; + dataLevel->noLevels = 2; + *kStop = coeff_ctr; + noCoeff++; + } + } + else if (lowerInt == 1) + { + dataLevel->level[1] = level; + dataLevel->noLevels = 2; + *kStop = coeff_ctr; + noCoeff++; + } + else + { + dataLevel->level[1] = level; + dataLevel->level[2] = level + 1; + dataLevel->noLevels = 3; + *kStop = coeff_ctr; + *kStart = coeff_ctr; + noCoeff++; + } + + for (k = 0; k < dataLevel->noLevels; k++) + { + err = (double)(dataLevel->level[k] << q_bits) - (double)scaled_coeff; + dataLevel->errLevel[k] = (err * err * estErr); + } + } + dataLevel++; + } + return (noCoeff); +} + + + diff --git a/lencod/src/rdoq_cavlc.c b/lencod/src/rdoq_cavlc.c new file mode 100644 index 0000000..56732bb --- /dev/null +++ b/lencod/src/rdoq_cavlc.c @@ -0,0 +1,721 @@ +/*! +************************************************************************************* +* \file rdoq_cavlc.c +* +* \brief +* Rate Distortion Optimized Quantization based on VCEG-AH21 related to CAVLC +************************************************************************************* +*/ + +#include "contributors.h" + +#include +#include + +#include "global.h" +#include "image.h" +#include "fmo.h" +#include "macroblock.h" +#include "mb_access.h" +#include "rdoq.h" +#include "block.h" + +/*! +************************************************************************ +* \brief +* estimate VLC for Coeff Level +************************************************************************ +*/ +int estSyntaxElement_Level_VLC1(SyntaxElement *se) +{ + int level = se->value1; + int sign = (level < 0 ? 1 : 0); + int levabs = iabs(level); + + if (levabs < 8) + { + se->len = levabs * 2 + sign - 1; + } + else if (levabs < 16) + { + // escape code1 + se->len = 19; + } + else + { + // escape code2 + se->len = 28; + } + + return (se->len); +} + + + +/*! +************************************************************************ +* \brief +* estimate VLC for Coeff Level +************************************************************************ +*/ +int estSyntaxElement_Level_VLCN(SyntaxElement *se, int vlc)//, DataPartition *this_dataPart) +{ + int level = se->value1; + int levabs = iabs(level) - 1; + + int shift = vlc - 1; + int escape = (15 << shift); + + if (levabs < escape) + { + se->len = ((levabs) >> shift) + 1 + vlc; + } + else + { + se->len = 28; + } + + return (se->len); +} + +int cmp(const void *arg1, const void *arg2) +{ + if(((levelDataStruct *)arg2)->levelDouble != ((levelDataStruct *)arg1)->levelDouble) + return (((levelDataStruct *)arg2)->levelDouble - ((levelDataStruct *)arg1)->levelDouble); + else + return (((levelDataStruct *)arg1)->coeff_ctr - ((levelDataStruct *)arg2)->coeff_ctr); +} + +/*! +************************************************************************ +* \brief +* estimate CAVLC bits +************************************************************************ +*/ +int est_CAVLC_bits (VideoParameters *p_Vid, int level_to_enc[16], int sign_to_enc[16], int nnz, int block_type) +{ + int no_bits = 0; + SyntaxElement se; + + int coeff_ctr, scan_pos = 0; + int k, level = 1, run = -1, vlcnum; + int numcoeff = 0, lastcoeff = 0, numtrailingones = 0; + int numones = 0, totzeros = 0, zerosleft, numcoef; + int numcoeff_vlc; + int level_two_or_higher; + int max_coeff_num = 0, cdc = (block_type == CHROMA_DC ? 1 : 0); + int yuv = p_Vid->yuv_format - 1; + static const int incVlc[] = {0, 3, 6, 12, 24, 48, 32768}; // maximum vlc = 6 + + int pLevel[16] = {0}; + int pRun[16] = {0}; + + static const int Token_lentab[3][4][17] = + { + { + { 1, 6, 8, 9,10,11,13,13,13,14,14,15,15,16,16,16,16}, + { 0, 2, 6, 8, 9,10,11,13,13,14,14,15,15,15,16,16,16}, + { 0, 0, 3, 7, 8, 9,10,11,13,13,14,14,15,15,16,16,16}, + { 0, 0, 0, 5, 6, 7, 8, 9,10,11,13,14,14,15,15,16,16} + }, + { + { 2, 6, 6, 7, 8, 8, 9,11,11,12,12,12,13,13,13,14,14}, + { 0, 2, 5, 6, 6, 7, 8, 9,11,11,12,12,13,13,14,14,14}, + { 0, 0, 3, 6, 6, 7, 8, 9,11,11,12,12,13,13,13,14,14}, + { 0, 0, 0, 4, 4, 5, 6, 6, 7, 9,11,11,12,13,13,13,14} + }, + { + { 4, 6, 6, 6, 7, 7, 7, 7, 8, 8, 9, 9, 9,10,10,10,10}, + { 0, 4, 5, 5, 5, 5, 6, 6, 7, 8, 8, 9, 9, 9,10,10,10}, + { 0, 0, 4, 5, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9,10,10,10}, + { 0, 0, 0, 4, 4, 4, 4, 4, 5, 6, 7, 8, 8, 9,10,10,10} + } + }; + + static const int Totalzeros_lentab[TOTRUN_NUM][16] = + { + { 1,3,3,4,4,5,5,6,6,7,7,8,8,9,9,9}, + { 3,3,3,3,3,4,4,4,4,5,5,6,6,6,6}, + { 4,3,3,3,4,4,3,3,4,5,5,6,5,6}, + { 5,3,4,4,3,3,3,4,3,4,5,5,5}, + { 4,4,4,3,3,3,3,3,4,5,4,5}, + { 6,5,3,3,3,3,3,3,4,3,6}, + { 6,5,3,3,3,2,3,4,3,6}, + { 6,4,5,3,2,2,3,3,6}, + { 6,6,4,2,2,3,2,5}, + { 5,5,3,2,2,2,4}, + { 4,4,3,3,1,3}, + { 4,4,2,1,3}, + { 3,3,1,2}, + { 2,2,1}, + { 1,1}, + }; + static const int Run_lentab[TOTRUN_NUM][16] = + { + {1,1}, + {1,2,2}, + {2,2,2,2}, + {2,2,2,3,3}, + {2,2,3,3,3,3}, + {2,3,3,3,3,3,3}, + {3,3,3,3,3,3,3,4,5,6,7,8,9,10,11}, + }; + static const int Token_lentab_cdc[3][4][17] = + { + //YUV420 + {{ 2, 6, 6, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + { 0, 1, 6, 7, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + { 0, 0, 3, 7, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + { 0, 0, 0, 6, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, + //YUV422 + {{ 1, 7, 7, 9, 9,10,11,12,13, 0, 0, 0, 0, 0, 0, 0, 0}, + { 0, 2, 7, 7, 9,10,11,12,12, 0, 0, 0, 0, 0, 0, 0, 0}, + { 0, 0, 3, 7, 7, 9,10,11,12, 0, 0, 0, 0, 0, 0, 0, 0}, + { 0, 0, 0, 5, 6, 7, 7,10,11, 0, 0, 0, 0, 0, 0, 0, 0}}, + //YUV444 + {{ 1, 6, 8, 9,10,11,13,13,13,14,14,15,15,16,16,16,16}, + { 0, 2, 6, 8, 9,10,11,13,13,14,14,15,15,15,16,16,16}, + { 0, 0, 3, 7, 8, 9,10,11,13,13,14,14,15,15,16,16,16}, + { 0, 0, 0, 5, 6, 7, 8, 9,10,11,13,14,14,15,15,16,16}} + }; + static const int Totalzeros_lentab_cdc[3][TOTRUN_NUM][16] = + { + //YUV420 + {{ 1,2,3,3}, + { 1,2,2}, + { 1,1}}, + //YUV422 + {{ 1,3,3,4,4,4,5,5}, + { 3,2,3,3,3,3,3}, + { 3,3,2,2,3,3}, + { 3,2,2,2,3}, + { 2,2,2,2}, + { 2,2,1}, + { 1,1}}, + //YUV444 + {{ 1,3,3,4,4,5,5,6,6,7,7,8,8,9,9,9}, + { 3,3,3,3,3,4,4,4,4,5,5,6,6,6,6}, + { 4,3,3,3,4,4,3,3,4,5,5,6,5,6}, + { 5,3,4,4,3,3,3,4,3,4,5,5,5}, + { 4,4,4,3,3,3,3,3,4,5,4,5}, + { 6,5,3,3,3,3,3,3,4,3,6}, + { 6,5,3,3,3,2,3,4,3,6}, + { 6,4,5,3,2,2,3,3,6}, + { 6,6,4,2,2,3,2,5}, + { 5,5,3,2,2,2,4}, + { 4,4,3,3,1,3}, + { 4,4,2,1,3}, + { 3,3,1,2}, + { 2,2,1}, + { 1,1}} + }; + + max_coeff_num = ( (block_type == CHROMA_DC) ? p_Vid->num_cdc_coeff : + ( (block_type == LUMA_INTRA16x16AC || block_type == CB_INTRA16x16AC || block_type == CR_INTRA16x16AC || block_type == CHROMA_AC) ? 15 : 16) ); + + //convert zigzag scan to (run, level) pairs + for (coeff_ctr = 0; coeff_ctr < max_coeff_num; coeff_ctr++) + { + run++; + level = level_to_enc[coeff_ctr]; + if (level != 0) + { + pLevel[scan_pos] = isignab(level, sign_to_enc[coeff_ctr]); + pRun [scan_pos] = run; + ++scan_pos; + run = -1; // reset zero level counter + } + } + + level = 1; + for(k = 0; (k < max_coeff_num) && level != 0; k++) + { + level = pLevel[k]; // level + run = pRun[k]; // run + + if (level) + { + totzeros += run; // lets add run always (even if zero) to avoid conditional + if (iabs(level) == 1) + { + numones ++; + numtrailingones ++; + numtrailingones = imin(numtrailingones, 3); // clip to 3 + } + else + { + numtrailingones = 0; + } + numcoeff ++; + lastcoeff = k; + } + } + + if (!cdc) + { + numcoeff_vlc = (nnz < 2) ? 0 : ((nnz < 4) ? 1 : ((nnz < 8) ? 2 : 3)); + } + else + { + // chroma DC (has its own VLC) + // numcoeff_vlc not relevant + numcoeff_vlc = 0; + } + + se.value1 = numcoeff; + se.value2 = numtrailingones; + se.len = numcoeff_vlc; /* use len to pass vlcnum */ + + if (!cdc) + { + if (se.len == 3) + no_bits += 6; // 4 + 2 bit FLC + else + no_bits += Token_lentab[se.len][se.value2][se.value1]; + } + else + no_bits += Token_lentab_cdc[yuv][se.value2][se.value1]; + + if (!numcoeff) + return no_bits; + else + { + if (numtrailingones) + no_bits += numtrailingones; + + // encode levels + level_two_or_higher = (numcoeff > 3 && numtrailingones == 3) ? 0 : 1; + + vlcnum = (numcoeff > 10 && numtrailingones < 3) ? 1 : 0; + + for (k = lastcoeff - numtrailingones; k >= 0; k--) + { + level = pLevel[k]; // level + + se.value1 = level; + + if (level_two_or_higher) + { + level_two_or_higher = 0; + if (se.value1 > 0) + se.value1 --; + else + se.value1 ++; + } + + // encode level + if (vlcnum == 0) + estSyntaxElement_Level_VLC1(&se); + else + estSyntaxElement_Level_VLCN(&se, vlcnum); + + // update VLC table + if (iabs(level) > incVlc[vlcnum]) + vlcnum++; + + if ((k == lastcoeff - numtrailingones) && iabs(level) > 3) + vlcnum = 2; + + no_bits += se.len; + } + + // encode total zeroes + if (numcoeff < max_coeff_num) + { + se.value1 = totzeros; + + vlcnum = numcoeff-1; + + se.len = vlcnum; + + if (!cdc) + no_bits += Totalzeros_lentab[se.len][se.value1]; + else + no_bits += Totalzeros_lentab_cdc[yuv][se.len][se.value1]; + } + + // encode run before each coefficient + zerosleft = totzeros; + numcoef = numcoeff; + for (k = lastcoeff; k >= 0; k--) + { + run = pRun[k]; // run + + se.value1 = run; + + // for last coeff, run is remaining totzeros + // when zerosleft is zero, remaining coeffs have 0 run + if ((!zerosleft) || (numcoeff <= 1 )) + break; + + if (numcoef > 1 && zerosleft) + { + vlcnum = imin(zerosleft - 1, RUNBEFORE_NUM_M1); + se.len = vlcnum; + no_bits += Run_lentab[se.len][se.value1]; + zerosleft -= run; + numcoef --; + } + } + } + + return no_bits; +} + + +/*! +**************************************************************************** +* \brief +* estimate run and level for CAVLC +**************************************************************************** +*/ +void est_RunLevel_CAVLC(Macroblock *currMB, levelDataStruct *levelData, int *levelTrellis, int block_type, + int b8, int b4, int coeff_num, double lambda) +{ + int k, lastnonzero = -1, coeff_ctr; + int level_to_enc[16] = {0}, sign_to_enc[16] = {0}; + int cstat, bestcstat = 0; + int nz_coeff=0; + double lagr, lagrAcc = 0, minlagr = 0; + VideoParameters *p_Vid = currMB->p_Vid; + + int subblock_x = ((b8 & 0x1) == 0) ? (((b4 & 0x1) == 0) ? 0 : 1) : (((b4 & 0x1) == 0) ? 2 : 3); + // horiz. position for coeff_count context + int subblock_y = (b8 < 2) ? ((b4 < 2) ? 0 : 1) :((b4 < 2) ? 2 : 3); + // vert. position for coeff_count context + int nnz; + levelDataStruct *dataLevel = &levelData[0]; + + if (block_type != CHROMA_AC) + nnz = predict_nnz(currMB, LUMA, subblock_x, subblock_y); + else + nnz = predict_nnz_chroma(currMB, currMB->subblock_x >> 2, (currMB->subblock_y >> 2) + 4); + + for (coeff_ctr=0;coeff_ctr < coeff_num;coeff_ctr++) + { + levelTrellis[coeff_ctr] = 0; + + for(k=0; k < dataLevel->noLevels; k++) + { + dataLevel->errLevel[k] /= 32768; + } + + lagrAcc += dataLevel->errLevel[dataLevel->noLevels - 1]; + + level_to_enc[coeff_ctr] = dataLevel->pre_level; + sign_to_enc[coeff_ctr] = dataLevel->sign; + + if(dataLevel->noLevels > 1) + { + dataLevel->coeff_ctr = coeff_ctr; + lastnonzero = coeff_ctr; + } + else + dataLevel->coeff_ctr = -1; + dataLevel++; + } + + if(lastnonzero != -1) + { + //sort the coefficients based on their absolute value + qsort(levelData, lastnonzero + 1, sizeof(levelDataStruct), cmp); + dataLevel = &levelData[lastnonzero]; + + for(coeff_ctr = lastnonzero; coeff_ctr >= 0; coeff_ctr--) // go over all coeff + { + if(dataLevel->noLevels == 1) + { + dataLevel--; + continue; + } + + lagrAcc -= dataLevel->errLevel[dataLevel->noLevels-1]; + for(cstat=0; cstatnoLevels; cstat++) // go over all states of cur coeff k + { + level_to_enc[dataLevel->coeff_ctr] = dataLevel->level[cstat]; + lagr = lagrAcc + dataLevel->errLevel[cstat]; + lagr += lambda * est_CAVLC_bits( p_Vid, level_to_enc, sign_to_enc, nnz, block_type); + if(cstat==0 || lagrerrLevel[bestcstat]; + level_to_enc[dataLevel->coeff_ctr] = dataLevel->level[bestcstat]; + dataLevel--; + } + + for(coeff_ctr = 0; coeff_ctr <= lastnonzero; coeff_ctr++) + { + levelTrellis[coeff_ctr] = level_to_enc[coeff_ctr]; + if (level_to_enc[coeff_ctr] != 0) + nz_coeff++; + } + } + + p_Vid->nz_coeff [p_Vid->current_mb_nr ][subblock_x][subblock_y] = nz_coeff; +} + +/*! +**************************************************************************** +* \brief +* Initialize levelData +**************************************************************************** +*/ +void init_trellis_data_4x4_CAVLC(Macroblock *currMB, int **tblock, int block_x, int qp_per, int qp_rem, LevelQuantParams **q_params, + const byte *p_scan, levelDataStruct *dataLevel, int type) +{ + Slice *currSlice = currMB->p_Slice; + int i, j, coeff_ctr; + int *m7; + int end_coeff_ctr = ( ( type == LUMA_4x4 ) ? 16 : 15 ); + int q_bits = Q_BITS + qp_per; + int q_offset = ( 1 << (q_bits - 1) ); + int scaled_coeff, level, lowerInt, k; + double err, estErr; + + + for (coeff_ctr = 0; coeff_ctr < end_coeff_ctr; coeff_ctr++) + { + i = *p_scan++; // horizontal position + j = *p_scan++; // vertical position + + m7 = &tblock[j][block_x + i]; + + if (*m7 == 0) + { + dataLevel->levelDouble = 0; + dataLevel->level[0] = 0; + dataLevel->noLevels = 1; + err = 0.0; + dataLevel->errLevel[0] = 0.0; + dataLevel->pre_level = 0; + dataLevel->sign = 0; + } + else + { + estErr = ((double) estErr4x4[qp_rem][j][i]) / currSlice->norm_factor_4x4; + + scaled_coeff = iabs(*m7) * q_params[j][i].ScaleComp; + dataLevel->levelDouble = scaled_coeff; + level = (scaled_coeff >> q_bits); + + lowerInt = ((scaled_coeff - (level << q_bits)) < q_offset )? 1 : 0; + + dataLevel->level[0] = 0; + if (level == 0 && lowerInt == 1) + { + dataLevel->noLevels = 1; + } + else if (level == 0 && lowerInt == 0) + { + dataLevel->level[1] = 1; + dataLevel->noLevels = 2; + } + else if (level > 0 && lowerInt == 1) + { + dataLevel->level[1] = level; + dataLevel->noLevels = 2; + } + else + { + dataLevel->level[1] = level; + dataLevel->level[2] = level + 1; + dataLevel->noLevels = 3; + } + + for (k = 0; k < dataLevel->noLevels; k++) + { + err = (double)(dataLevel->level[k] << q_bits) - (double)scaled_coeff; + dataLevel->errLevel[k] = (err * err * estErr); + } + + if(dataLevel->noLevels == 1) + dataLevel->pre_level = 0; + else + dataLevel->pre_level = (iabs (*m7) * q_params[j][i].ScaleComp + q_params[j][i].OffsetComp) >> q_bits; + dataLevel->sign = isign(*m7); + } + dataLevel++; + } +} + +/*! +**************************************************************************** +* \brief +* Initialize levelData for Luma DC +**************************************************************************** +*/ +void init_trellis_data_DC_CAVLC(Macroblock *currMB, int **tblock, int qp_per, int qp_rem, + LevelQuantParams *q_params, const byte *p_scan, + levelDataStruct *dataLevel) +{ + Slice *currSlice = currMB->p_Slice; + int i, j, coeff_ctr, end_coeff_ctr = 16; + int q_bits = Q_BITS + qp_per + 1; + int q_offset = ( 1 << (q_bits - 1) ); + int scaled_coeff, level, lowerInt, k; + int *m7; + double err, estErr = (double) estErr4x4[qp_rem][0][0] / currSlice->norm_factor_4x4; // note that we could also use int64 + + for (coeff_ctr = 0; coeff_ctr < end_coeff_ctr; coeff_ctr++) + { + i = *p_scan++; // horizontal position + j = *p_scan++; // vertical position + m7 = &tblock[j][i]; + + if (*m7 == 0) + { + dataLevel->levelDouble = 0; + dataLevel->level[0] = 0; + dataLevel->noLevels = 1; + err = 0.0; + dataLevel->errLevel[0] = 0.0; + dataLevel->pre_level = 0; + dataLevel->sign = 0; + } + else + { + scaled_coeff = iabs(*m7) * q_params->ScaleComp; + dataLevel->levelDouble = scaled_coeff; + level = (scaled_coeff >> q_bits); + + lowerInt=( (scaled_coeff - (level<level[0] = 0; + if (level == 0 && lowerInt == 1) + { + dataLevel->noLevels = 1; + } + else if (level == 0 && lowerInt == 0) + { + dataLevel->level[1] = 1; + dataLevel->noLevels = 2; + } + else if (level > 0 && lowerInt == 1) + { + dataLevel->level[1] = level; + dataLevel->noLevels = 2; + } + else + { + dataLevel->level[1] = level; + dataLevel->level[2] = level + 1; + dataLevel->noLevels = 3; + } + + for (k = 0; k < dataLevel->noLevels; k++) + { + err = (double)(dataLevel->level[k] << q_bits) - (double)scaled_coeff; + dataLevel->errLevel[k] = (err * err * estErr); + } + + if(dataLevel->noLevels == 1) + dataLevel->pre_level = 0; + else + dataLevel->pre_level = (iabs (*m7) * q_params->ScaleComp + q_params->OffsetComp) >> q_bits; + dataLevel->sign = isign(*m7); + } + dataLevel++; + } +} + +/*! +**************************************************************************** +* \brief +* Initialize levelData +**************************************************************************** +*/ +void init_trellis_data_8x8_CAVLC(Macroblock *currMB, int **tblock, int block_x, int qp_per, int qp_rem, LevelQuantParams **q_params, + const byte *p_scan, levelDataStruct levelData[4][16]) +{ + Slice *currSlice = currMB->p_Slice; + int i, j, block, coeff_ctr; + int *m7; + int q_bits = Q_BITS_8 + qp_per; + int q_offset = ( 1 << (q_bits - 1) ); + double err, estErr; + int scaled_coeff, level, lowerInt, k; + + levelDataStruct *dataLevel = &levelData[0][0]; + + for (coeff_ctr = 0; coeff_ctr < 16; coeff_ctr++) + { + for (block = 0; block < 4; block++) + { + i = *p_scan++; // horizontal position + j = *p_scan++; // vertical position + + m7 = &tblock[j][block_x + i]; + + dataLevel = &levelData[block][coeff_ctr]; + if (*m7 == 0) + { + dataLevel->levelDouble = 0; + dataLevel->level[0] = 0; + dataLevel->noLevels = 1; + err = 0.0; + dataLevel->errLevel[0] = 0.0; + dataLevel->pre_level = 0; + dataLevel->sign = 0; + } + else + { + estErr = (double) estErr8x8[qp_rem][j][i] / currSlice->norm_factor_8x8; + + scaled_coeff = iabs(*m7) * q_params[j][i].ScaleComp; + dataLevel->levelDouble = scaled_coeff; + level = (scaled_coeff >> q_bits); + + lowerInt = ((scaled_coeff - (level << q_bits)) < q_offset ) ? 1 : 0; + + dataLevel->level[0] = 0; + if (level == 0 && lowerInt == 1) + { + dataLevel->noLevels = 1; + } + else if (level == 0 && lowerInt == 0) + { + dataLevel->level[1] = 1; + dataLevel->noLevels = 2; + } + else if (level > 0 && lowerInt == 1) + { + if (level > 1) + { + dataLevel->level[1] = level - 1; + dataLevel->level[2] = level; + dataLevel->noLevels = 3; + } + else + { + dataLevel->level[1] = level; + dataLevel->noLevels = 2; + } + } + else + { + dataLevel->level[1] = level; + dataLevel->level[2] = level + 1; + dataLevel->noLevels = 3; + } + + for (k = 0; k < dataLevel->noLevels; k++) + { + err = (double)(dataLevel->level[k] << q_bits) - (double)scaled_coeff; + dataLevel->errLevel[k] = err * err * estErr; + } + + if(dataLevel->noLevels == 1) + dataLevel->pre_level = 0; + else + dataLevel->pre_level = (iabs (*m7) * q_params[j][i].ScaleComp + q_params[j][i].OffsetComp) >> q_bits; + dataLevel->sign = isign(*m7); + } + } + } +} + + diff --git a/lencod/src/rdpicdecision.c b/lencod/src/rdpicdecision.c new file mode 100644 index 0000000..f93e6fc --- /dev/null +++ b/lencod/src/rdpicdecision.c @@ -0,0 +1,64 @@ + +/*! +************************************************************************************* +* \file rdpicdecision.c +* +* \brief +* Perform RD optimal decisions between multiple coded versions of the same picture +* +* \author +* Main contributors (see contributors.h for copyright, address and affiliation details) +* - Alexis Michael Tourapis +************************************************************************************* +*/ + +#include "global.h" +#include + + +/*! + ************************************************************************ + * \brief + * RD decision between possible encoding cases + ************************************************************************ + */ +int rd_pic_decision(double snrY_version1, double snrY_version2, int bits_version1, int bits_version2, double lambda_picture) +{ + double cost_version1, cost_version2; + + cost_version1 = (double) bits_version1 * lambda_picture + snrY_version1; + cost_version2 = (double) bits_version2 * lambda_picture + snrY_version2; + //printf("%d %d %.2f %.2f %.2f %.2f \n",bits_version1,bits_version2,snrY_version1,snrY_version2,cost_version1,cost_version2); + if (cost_version2 > cost_version1 || (cost_version2 == cost_version1 && snrY_version2 >= snrY_version1) ) + return (0); + else + return (1); +} + +/*! + ************************************************************************ + * \brief + * Picture Coding Decision + ************************************************************************ + */ +int picture_coding_decision (VideoParameters *p_Vid, Picture *picture1, Picture *picture2, int qp) +{ + double lambda_picture; + double sse_picture1, sse_picture2; + + if (p_Vid->p_Inp->NumberBFrames) + lambda_picture = (qp < 20 ? 0.55 : 0.68) * pow (2, (qp - SHIFT_QP) / 3.0) * ((p_Vid->type == B_SLICE) || (p_Vid->type == SP_SLICE || p_Vid->type == SI_SLICE) ? 2 : 1); + else + lambda_picture = (qp < 20 ? 0.55 : 0.68) * pow (2, (qp - SHIFT_QP) / 3.0); + + if(picture1->distortion.value[0] == 0) + return 0; + else if(picture2->distortion.value[0] == 0) + return 1; + + sse_picture1 = picture1->distortion.value[0] + picture1->distortion.value[1] + picture1->distortion.value[2]; + sse_picture2 = picture2->distortion.value[0] + picture2->distortion.value[1] + picture2->distortion.value[2]; + + return rd_pic_decision(sse_picture1, sse_picture2, picture1->bits_per_picture, picture2->bits_per_picture, lambda_picture); +} + diff --git a/lencod/src/report.c b/lencod/src/report.c new file mode 100644 index 0000000..414d98f --- /dev/null +++ b/lencod/src/report.c @@ -0,0 +1,1514 @@ +/*! + * \file + * report.c + * \brief + * Report related files() + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * - Karsten Suehring + * - Alexis Michael Tourapis + *********************************************************************** + */ + +#include "contributors.h" + +#include +#include + +#include "global.h" +#include "context_ini.h" +#include "explicit_gop.h" +#include "filehandle.h" +#include "fmo.h" +#include "image.h" +#include "intrarefresh.h" +#include "leaky_bucket.h" +#include "me_epzs.h" +#include "me_epzs_int.h" +#include "output.h" +#include "parset.h" +#include "report.h" +#include "img_process_types.h" + + +static const char DistortionType[3][20] = {"SAD", "SSE", "Hadamard SAD"}; + +void report_log_mode (VideoParameters *p_Vid, InputParameters *p_Inp, StatParameters *p_Stats); +/*! + ************************************************************************ + * \brief + * Reports frame statistical data to a stats file + ************************************************************************ + */ +void report_frame_statistic(VideoParameters *p_Vid, InputParameters *p_Inp) +{ + DistortionParams *p_Dist = p_Vid->p_Dist; + FILE *p_stat_frm = NULL; + + int i; + char name[30]; + int bitcounter; + StatParameters *cur_stats = &p_Vid->enc_picture->stats; + +#ifndef WIN32 + time_t now; + struct tm *l_time; + char string[1000]; +#else + char timebuf[128]; +#endif + + // write to log file + if ((p_stat_frm = fopen("stat_frame.dat", "r")) == 0) // check if file exists + { + if ((p_stat_frm = fopen("stat_frame.dat", "a")) == NULL) // append new statistic at the end + { + snprintf(errortext, ET_SIZE, "Error open file %s \n", "stat_frame.dat.dat"); + error(errortext, 500); + } + else // Create header for new log file + { + fprintf(p_stat_frm, " --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- \n"); + fprintf(p_stat_frm, "| Encoder statistics. This file is generated during first encoding session, new sessions will be appended |\n"); + fprintf(p_stat_frm, " --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- \n"); + } + } + else + { + fclose (p_stat_frm); + if ((p_stat_frm = fopen("stat_frame.dat", "a")) == NULL) // File exists, just open for appending + { + snprintf(errortext, ET_SIZE, "Error open file %s \n", "stat_frame.dat.dat"); + error(errortext, 500); + } + } + + if (p_Vid->frame_statistic_start) + { + fprintf(p_stat_frm, "| ver | Date | Time | Sequence |Frm | QP |P/MbInt| Bits | SNRY | SNRU | SNRV | I4 | I8 | I16 | IC0 | IC1 | IC2 | IC3 | PI4 | PI8 | PI16 | P0 | P1 | P2 | P3 | P1*4*| P1*8*| P2*4*| P2*8*| P3*4*| P3*8*| P8 | P8:4 | P4*4*| P4*8*| P8:5 | P8:6 | P8:7 | BI4 | BI8 | BI16 | B0 | B1 | B2 | B3 | B0*4*| B0*8*| B1*4*| B1*8*| B2*4*| B2*8*| B3*4*| B3*8*| B8 | B8:0 |B80*4*|B80*8*| B8:4 | B4*4*| B4*8*| B8:5 | B8:6 | B8:7 |\n"); + fprintf(p_stat_frm, " ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ \n"); + } + + //report + fprintf(p_stat_frm, "|%4s/%s", VERSION, EXT_VERSION); + +#ifdef WIN32 + _strdate( timebuf ); + fprintf(p_stat_frm, "| %1.5s |", timebuf); + + _strtime( timebuf); + fprintf(p_stat_frm, " % 1.5s |", timebuf); +#else + now = time ((time_t *) NULL); // Get the system time and put it into 'now' as 'calender time' + time (&now); + l_time = localtime (&now); + strftime (string, sizeof string, "%d-%b-%Y", l_time); + fprintf(p_stat_frm, "| %1.5s |", string ); + + strftime (string, sizeof string, "%H:%M:%S", l_time); + fprintf(p_stat_frm, " %1.5s |", string); +#endif + + for (i=0;i<30;i++) + name[i] = p_Inp->input_file1.fname[i + imax(0,(int) (strlen(p_Inp->input_file1.fname)- 30))]; // write last part of path, max 30 chars + + fprintf(p_stat_frm, "%30.30s|", name); + fprintf(p_stat_frm, "%3d |", p_Vid->frame_no); + fprintf(p_stat_frm, "%3d |", p_Vid->qp); + fprintf(p_stat_frm, " %d/%d |", p_Inp->PicInterlace, p_Inp->MbInterlace); + +#if (MVC_EXTENSION_ENABLE) + if (p_Vid->curr_frm_idx == 0 && p_Vid->frame_num == 0 && !p_Vid->view_id) +#else + if (p_Vid->curr_frm_idx == 0 && p_Vid->frame_num == 0) +#endif + { + bitcounter = (int) p_Vid->p_Stats->bit_counter[I_SLICE]; + } + else + { + bitcounter = (int) (p_Vid->p_Stats->bit_ctr_n - p_Vid->last_bit_ctr_n); + p_Vid->last_bit_ctr_n = p_Vid->p_Stats->bit_ctr_n; + } + + //report bitrate + fprintf(p_stat_frm, " %9d|", bitcounter); + + //report snr's + fprintf(p_stat_frm, " %2.4f| %2.4f| %2.4f|", p_Dist->metric[PSNR].value[0], p_Dist->metric[PSNR].value[1], p_Dist->metric[PSNR].value[2]); + + //report modes + //I-Modes + fprintf(p_stat_frm, " %5" FORMAT_OFF_T "|", cur_stats->mode_use[I_SLICE][I4MB ]); + fprintf(p_stat_frm, " %5" FORMAT_OFF_T "|", cur_stats->mode_use[I_SLICE][I8MB ]); + fprintf(p_stat_frm, " %5" FORMAT_OFF_T "|", cur_stats->mode_use[I_SLICE][I16MB]); + + //chroma intra mode + fprintf(p_stat_frm, " %5" FORMAT_OFF_T "|", cur_stats->intra_chroma_mode[0]); + fprintf(p_stat_frm, " %5" FORMAT_OFF_T "|", cur_stats->intra_chroma_mode[1]); + fprintf(p_stat_frm, " %5" FORMAT_OFF_T "|", cur_stats->intra_chroma_mode[2]); + fprintf(p_stat_frm, " %5" FORMAT_OFF_T "|", cur_stats->intra_chroma_mode[3]); + + //P-Modes + fprintf(p_stat_frm, " %5" FORMAT_OFF_T "|", cur_stats->mode_use[P_SLICE][I4MB ]); + fprintf(p_stat_frm, " %5" FORMAT_OFF_T "|", cur_stats->mode_use[P_SLICE][I8MB ]); + fprintf(p_stat_frm, " %5" FORMAT_OFF_T "|", cur_stats->mode_use[P_SLICE][I16MB]); + fprintf(p_stat_frm, " %5" FORMAT_OFF_T "|", cur_stats->mode_use[P_SLICE][0 ]); + + fprintf(p_stat_frm, " %5" FORMAT_OFF_T "|", cur_stats->mode_use[P_SLICE][1 ]); + fprintf(p_stat_frm, " %5" FORMAT_OFF_T "|", cur_stats->mode_use[P_SLICE][2 ]); + fprintf(p_stat_frm, " %5" FORMAT_OFF_T "|", cur_stats->mode_use[P_SLICE][3 ]); + + fprintf(p_stat_frm, " %5" FORMAT_OFF_T "|", cur_stats->mode_use_transform[P_SLICE][1][0]); + fprintf(p_stat_frm, " %5" FORMAT_OFF_T "|", cur_stats->mode_use_transform[P_SLICE][1][1]); + fprintf(p_stat_frm, " %5" FORMAT_OFF_T "|", cur_stats->mode_use_transform[P_SLICE][2][0]); + fprintf(p_stat_frm, " %5" FORMAT_OFF_T "|", cur_stats->mode_use_transform[P_SLICE][2][1]); + fprintf(p_stat_frm, " %5" FORMAT_OFF_T "|", cur_stats->mode_use_transform[P_SLICE][3][0]); + fprintf(p_stat_frm, " %5" FORMAT_OFF_T "|", cur_stats->mode_use_transform[P_SLICE][3][1]); + + fprintf(p_stat_frm, " %5" FORMAT_OFF_T "|", cur_stats->mode_use[P_SLICE][P8x8 ]); + fprintf(p_stat_frm, " %5" FORMAT_OFF_T "|", cur_stats->mode_use[P_SLICE][4 ]); + fprintf(p_stat_frm, " %5" FORMAT_OFF_T "|", cur_stats->mode_use_transform[P_SLICE][4][0]); + fprintf(p_stat_frm, " %5" FORMAT_OFF_T "|", cur_stats->mode_use_transform[P_SLICE][4][1]); + fprintf(p_stat_frm, " %5" FORMAT_OFF_T "|", cur_stats->mode_use[P_SLICE][5 ]); + fprintf(p_stat_frm, " %5" FORMAT_OFF_T "|", cur_stats->mode_use[P_SLICE][6 ]); + fprintf(p_stat_frm, " %5" FORMAT_OFF_T "|", cur_stats->mode_use[P_SLICE][7 ]); + + //B-Modes + fprintf(p_stat_frm, " %5" FORMAT_OFF_T "|", cur_stats->mode_use[B_SLICE][I4MB ]); + fprintf(p_stat_frm, " %5" FORMAT_OFF_T "|", cur_stats->mode_use[B_SLICE][I8MB ]); + fprintf(p_stat_frm, " %5" FORMAT_OFF_T "|", cur_stats->mode_use[B_SLICE][I16MB]); + fprintf(p_stat_frm, " %5" FORMAT_OFF_T "|", cur_stats->mode_use[B_SLICE][0 ]); + fprintf(p_stat_frm, " %5" FORMAT_OFF_T "|", cur_stats->mode_use[B_SLICE][1 ]); + fprintf(p_stat_frm, " %5" FORMAT_OFF_T "|", cur_stats->mode_use[B_SLICE][2 ]); + fprintf(p_stat_frm, " %5" FORMAT_OFF_T "|", cur_stats->mode_use[B_SLICE][3 ]); + fprintf(p_stat_frm, " %5" FORMAT_OFF_T "|", cur_stats->mode_use_transform[B_SLICE][0][0]); + fprintf(p_stat_frm, " %5" FORMAT_OFF_T "|", cur_stats->mode_use_transform[B_SLICE][0][1]); + fprintf(p_stat_frm, " %5" FORMAT_OFF_T "|", cur_stats->mode_use_transform[B_SLICE][1][0]); + fprintf(p_stat_frm, " %5" FORMAT_OFF_T "|", cur_stats->mode_use_transform[B_SLICE][1][1]); + fprintf(p_stat_frm, " %5" FORMAT_OFF_T "|", cur_stats->mode_use_transform[B_SLICE][2][0]); + fprintf(p_stat_frm, " %5" FORMAT_OFF_T "|", cur_stats->mode_use_transform[B_SLICE][2][1]); + fprintf(p_stat_frm, " %5" FORMAT_OFF_T "|", cur_stats->mode_use_transform[B_SLICE][3][0]); + fprintf(p_stat_frm, " %5" FORMAT_OFF_T "|", cur_stats->mode_use_transform[B_SLICE][3][1]); + + fprintf(p_stat_frm, " %5" FORMAT_OFF_T "|", cur_stats->mode_use[B_SLICE][P8x8]); + fprintf(p_stat_frm, " %d|", (cur_stats->b8_mode_0_use [B_SLICE][0] + cur_stats->b8_mode_0_use [B_SLICE][1])); + fprintf(p_stat_frm, " %5d|", cur_stats->b8_mode_0_use [B_SLICE][0]); + fprintf(p_stat_frm, " %5d|", cur_stats->b8_mode_0_use [B_SLICE][1]); + fprintf(p_stat_frm, " %5" FORMAT_OFF_T "|", cur_stats->mode_use[B_SLICE][4 ]); + fprintf(p_stat_frm, " %5" FORMAT_OFF_T "|", cur_stats->mode_use_transform[B_SLICE][4][0]); + fprintf(p_stat_frm, " %5" FORMAT_OFF_T "|", cur_stats->mode_use_transform[B_SLICE][4][1]); + fprintf(p_stat_frm, " %5" FORMAT_OFF_T "|", cur_stats->mode_use[B_SLICE][5 ]); + fprintf(p_stat_frm, " %5" FORMAT_OFF_T "|", cur_stats->mode_use[B_SLICE][6 ]); + fprintf(p_stat_frm, " %5" FORMAT_OFF_T "|", cur_stats->mode_use[B_SLICE][7 ]); + + fprintf(p_stat_frm, "\n"); + + //save the last results + p_Vid->frame_statistic_start = 0; + fclose(p_stat_frm); +} + + +double report_slice_pred_stats(FILE *p_stat, StatParameters *p_Stats, int slice_type, double bit_use, char *slice_name) +{ + fprintf(p_stat,"\n ---------------------|----------------|-----------------|"); + fprintf(p_stat,"\n %8s | Mode used | MotionInfo bits |", slice_name); + fprintf(p_stat,"\n ---------------------|----------------|-----------------|"); + fprintf(p_stat,"\n Mode 0 (copy) | %5" FORMAT_OFF_T " | %8.2f |", p_Stats->mode_use[slice_type][0 ], (double)p_Stats->bit_use_mode[slice_type][0 ] / bit_use); + fprintf(p_stat,"\n Mode 1 (16x16) | %5" FORMAT_OFF_T " | %8.2f |", p_Stats->mode_use[slice_type][1 ], (double)p_Stats->bit_use_mode[slice_type][1 ] / bit_use); + fprintf(p_stat,"\n Mode 2 (16x8) | %5" FORMAT_OFF_T " | %8.2f |", p_Stats->mode_use[slice_type][2 ], (double)p_Stats->bit_use_mode[slice_type][2 ] / bit_use); + fprintf(p_stat,"\n Mode 3 (8x16) | %5" FORMAT_OFF_T " | %8.2f |", p_Stats->mode_use[slice_type][3 ], (double)p_Stats->bit_use_mode[slice_type][3 ] / bit_use); + fprintf(p_stat,"\n Mode 4 (8x8) | %5" FORMAT_OFF_T " | %8.2f |", p_Stats->mode_use[slice_type][P8x8], (double)p_Stats->bit_use_mode[slice_type][P8x8] / bit_use); + fprintf(p_stat,"\n Mode 5 intra 4x4 | %5" FORMAT_OFF_T " |-----------------|", p_Stats->mode_use[slice_type][I4MB]); + fprintf(p_stat,"\n Mode 6 intra 8x8 | %5" FORMAT_OFF_T " |", p_Stats->mode_use[slice_type][I8MB]); + fprintf(p_stat,"\n Mode 7+ intra 16x16 | %5" FORMAT_OFF_T " |", p_Stats->mode_use[slice_type][I16MB]); + fprintf(p_stat,"\n Mode intra IPCM | %5" FORMAT_OFF_T " |", p_Stats->mode_use[slice_type][IPCM ]); + + return (double)(p_Stats->bit_use_mode[slice_type][0] + p_Stats->bit_use_mode[slice_type][1] + p_Stats->bit_use_mode[slice_type][2] + + p_Stats->bit_use_mode[slice_type][3] + p_Stats->bit_use_mode[slice_type][P8x8]) / bit_use; +} + +/*! + *********************************************************************** + * \brief + * Terminates and reports statistics on error. + * + *********************************************************************** + */ +void report_stats_on_error(void) +{ + free_encoder_memory(p_Enc->p_Vid, p_Enc->p_Inp); + exit (-1); +} + + +void report_stats(VideoParameters *p_Vid, InputParameters *p_Inp, StatParameters *p_Stats, int64 bit_use[NUM_SLICE_TYPES][2]) +{ + DistortionParams *p_Dist = p_Vid->p_Dist; + FILE *p_stat; //!< status file for the last encoding session + double mean_motion_info_bit_use[NUM_SLICE_TYPES] = {0.0}; + int i; + + if (strlen(p_Inp->StatsFile) == 0) + strcpy (p_Inp->StatsFile,"stats.dat"); + + if ((p_stat = fopen(p_Inp->StatsFile, "wt")) == 0) + { + snprintf(errortext, ET_SIZE, "Error open file %s", p_Inp->StatsFile); + error(errortext, 500); + } + + fprintf(p_stat," -------------------------------------------------------------- \n"); + fprintf(p_stat," This file contains statistics for the last encoded sequence \n"); + fprintf(p_stat," -------------------------------------------------------------- \n"); + fprintf(p_stat, " Sequence : %s\n", p_Inp->input_file1.fname); + + + fprintf(p_stat, " No.of coded pictures : %4d\n", p_Stats->frame_counter); + fprintf(p_stat, " Freq. for encoded bitstream : %4.0f\n", p_Inp->output.frame_rate); + + fprintf(p_stat, " I Slice Bitrate(kb/s) : %6.2f\n", p_Stats->bitrate_st[I_SLICE] / 1000.0); + fprintf(p_stat, " P Slice Bitrate(kb/s) : %6.2f\n", p_Stats->bitrate_st[P_SLICE] / 1000.0); + fprintf(p_stat, " B Slice Bitrate(kb/s) : %6.2f\n", p_Stats->bitrate_st[B_SLICE] / 1000.0); + fprintf(p_stat, " Total Bitrate(kb/s) : %6.2f\n", p_Stats->bitrate / 1000.0); + + for (i = 0; i < 3; i++) + { + fprintf(p_stat," ME Level %1d Metric : %s\n", i, DistortionType[p_Inp->MEErrorMetric[i]]); + } + fprintf(p_stat," Mode Decision Metric : %s\n", DistortionType[p_Inp->ModeDecisionMetric]); + + switch ( p_Inp->ChromaMEEnable ) + { + case 1: + fprintf(p_stat," ME for components : YCbCr\n"); + break; + default: + fprintf(p_stat," ME for components : Y\n"); + break; + } + + fprintf(p_stat, " Image format : %dx%d\n", p_Inp->output.width[0], p_Inp->output.height[0]); + + if (p_Inp->intra_upd) + fprintf(p_stat," Error robustness : On\n"); + else + fprintf(p_stat," Error robustness : Off\n"); + + fprintf(p_stat, " Search range : %d\n", p_Inp->search_range[0]); +#if (MVC_EXTENSION_ENABLE) + if ( p_Inp->SepViewInterSearch ) + { + fprintf(p_stat, " Search range (view 1) : %d\n", p_Inp->search_range[1]); + } +#endif + + fprintf(p_stat, " Total number of references : %d\n", p_Inp->num_ref_frames_org); + fprintf(p_stat, " References for P slices : %d\n", p_Inp->P_List0_refs_org[0] ? p_Inp->P_List0_refs_org[0] : p_Inp->num_ref_frames_org); + + if (p_Stats->frame_ctr[B_SLICE]!=0) + { + fprintf(p_stat, " List0 refs for B slices : %d\n", p_Inp->B_List0_refs_org[0] ? p_Inp->B_List0_refs_org[0] : p_Inp->num_ref_frames_org); + fprintf(p_stat, " List1 refs for B slices : %d\n", p_Inp->B_List1_refs_org[0] ? p_Inp->B_List1_refs_org[0] : p_Inp->num_ref_frames_org); + } + +#if (MVC_EXTENSION_ENABLE) + if ( p_Vid->num_of_layers > 1 ) + { + fprintf(p_stat, " View 1 refs for P slices : %d\n", p_Inp->P_List0_refs_org[1] ? p_Inp->P_List0_refs_org[1] : p_Inp->num_ref_frames_org); + fprintf(p_stat, " View 1 L0 refs for B slices : %d\n", p_Inp->B_List0_refs_org[1] ? p_Inp->B_List0_refs_org[1] : p_Inp->num_ref_frames_org); + fprintf(p_stat, " View 1 L1 refs for B slices : %d\n", p_Inp->B_List1_refs_org[1] ? p_Inp->B_List1_refs_org[1] : p_Inp->num_ref_frames_org); + } +#endif + + fprintf(p_stat, " Profile/Level IDC : (%d,%d)\n", p_Inp->ProfileIDC, p_Inp->LevelIDC); + if (p_Inp->symbol_mode == CAVLC) + fprintf(p_stat, " Entropy coding method : CAVLC\n"); + else + fprintf(p_stat, " Entropy coding method : CABAC\n"); + + if (p_Inp->MbInterlace) + fprintf(p_stat, " MB Field Coding : On \n"); + + if (p_Inp->SearchMode[0] == EPZS || p_Inp->SearchMode[1] == EPZS) + { + EPZSOutputStats(p_Inp, p_stat, 1); + } + + if (p_Inp->full_search == 2) + fprintf(p_stat," Search range restrictions : none\n"); + else if (p_Inp->full_search == 1) + fprintf(p_stat," Search range restrictions : older reference frames\n"); + else + fprintf(p_stat," Search range restrictions : smaller blocks and older reference frames\n"); + + if (p_Inp->rdopt) + fprintf(p_stat," RD-optimized mode decision : used\n"); + else + fprintf(p_stat," RD-optimized mode decision : not used\n"); + + fprintf(p_stat,"\n ---------------------|----------------|---------------|"); + fprintf(p_stat,"\n Item | Intra | All frames |"); + fprintf(p_stat,"\n ---------------------|----------------|---------------|"); + fprintf(p_stat,"\n SNR Y(dB) |"); + fprintf(p_stat," %5.2f |", p_Dist->metric[PSNR].avslice[I_SLICE][0]); + fprintf(p_stat," %5.2f |", p_Dist->metric[PSNR].average[0]); + fprintf(p_stat,"\n SNR U/V (dB) |"); + fprintf(p_stat," %5.2f/%5.2f |", p_Dist->metric[PSNR].avslice[I_SLICE][1], p_Dist->metric[PSNR].avslice[I_SLICE][2]); + fprintf(p_stat," %5.2f/%5.2f |", p_Dist->metric[PSNR].average[1], p_Dist->metric[PSNR].average[2]); + fprintf(p_stat,"\n ---------------------|----------------|---------------|"); + fprintf(p_stat,"\n"); + + fprintf(p_stat,"\n ---------------------|----------------|---------------|---------------|"); + fprintf(p_stat,"\n SNR | I | P | B |"); + fprintf(p_stat,"\n ---------------------|----------------|---------------|---------------|"); + fprintf(p_stat,"\n SNR Y(dB) | %5.3f | %5.3f | %5.3f |", + p_Dist->metric[PSNR].avslice[I_SLICE][0], p_Dist->metric[PSNR].avslice[P_SLICE][0], p_Dist->metric[PSNR].avslice[B_SLICE][0]); + fprintf(p_stat,"\n SNR U(dB) | %5.3f | %5.3f | %5.3f |", + p_Dist->metric[PSNR].avslice[I_SLICE][1], p_Dist->metric[PSNR].avslice[P_SLICE][1], p_Dist->metric[PSNR].avslice[B_SLICE][1]); + fprintf(p_stat,"\n SNR V(dB) | %5.3f | %5.3f | %5.3f |", + p_Dist->metric[PSNR].avslice[I_SLICE][2], p_Dist->metric[PSNR].avslice[P_SLICE][2], p_Dist->metric[PSNR].avslice[B_SLICE][2]); + fprintf(p_stat,"\n ---------------------|----------------|---------------|---------------|"); + fprintf(p_stat,"\n"); + + // QUANT. + fprintf(p_stat,"\n ---------------------|----------------|---------------|---------------|"); + fprintf(p_stat,"\n Ave Quant | I | P | B |"); + fprintf(p_stat,"\n ---------------------|----------------|---------------|---------------|"); + fprintf(p_stat,"\n QP | %5.3f | %5.3f | %5.3f |", + (float)p_Stats->quant[I_SLICE]/dmax(1.0,(float)p_Stats->num_macroblocks[I_SLICE]), + (float)p_Stats->quant[P_SLICE]/dmax(1.0,(float)p_Stats->num_macroblocks[P_SLICE]), + (float)p_Stats->quant[B_SLICE]/dmax(1.0,(float)p_Stats->num_macroblocks[B_SLICE])); + fprintf(p_stat,"\n ---------------------|----------------|---------------|---------------|"); + fprintf(p_stat,"\n"); + + // MODE + fprintf(p_stat,"\n ---------------------|----------------|"); + fprintf(p_stat,"\n Intra | Mode used |"); + fprintf(p_stat,"\n ---------------------|----------------|"); + fprintf(p_stat,"\n Mode 0 intra 4x4 | %5" FORMAT_OFF_T " |", p_Stats->mode_use[I_SLICE][I4MB ]); + fprintf(p_stat,"\n Mode 1 intra 8x8 | %5" FORMAT_OFF_T " |", p_Stats->mode_use[I_SLICE][I8MB ]); + fprintf(p_stat,"\n Mode 2+ intra 16x16 | %5" FORMAT_OFF_T " |", p_Stats->mode_use[I_SLICE][I16MB]); + fprintf(p_stat,"\n Mode intra IPCM | %5" FORMAT_OFF_T " |", p_Stats->mode_use[I_SLICE][IPCM ]); + + // P slices + if (p_Stats->frame_ctr[P_SLICE]!=0) + { + mean_motion_info_bit_use[P_SLICE] = report_slice_pred_stats(p_stat, p_Stats, P_SLICE,(double) bit_use[P_SLICE][0], "P Slice "); + } + // B slices + if (p_Stats->frame_ctr[B_SLICE]!=0) + { + mean_motion_info_bit_use[B_SLICE] = report_slice_pred_stats(p_stat, p_Stats, B_SLICE,(double) bit_use[B_SLICE][0], "B Slice "); + } + // SP slices + if (p_Stats->frame_ctr[SP_SLICE]!=0) + { + mean_motion_info_bit_use[SP_SLICE] = report_slice_pred_stats(p_stat, p_Stats, SP_SLICE,(double) bit_use[SP_SLICE][0], "SP Slice"); + } + + + fprintf(p_stat,"\n ---------------------|----------------|"); + fprintf(p_stat,"\n"); + + fprintf(p_stat,"\n ---------------------|----------------|----------------|----------------|----------------|"); + fprintf(p_stat,"\n Bit usage: | Intra | Inter | B frame | SP frame |"); + fprintf(p_stat,"\n ---------------------|----------------|----------------|----------------|----------------|"); + + fprintf(p_stat,"\n Header |"); + fprintf(p_stat," %10.2f |", (float) p_Stats->bit_use_header[I_SLICE] / bit_use[I_SLICE][0]); + fprintf(p_stat," %10.2f |", (float) p_Stats->bit_use_header[P_SLICE] / bit_use[P_SLICE][0]); + fprintf(p_stat," %10.2f |", (float) p_Stats->bit_use_header[B_SLICE] / bit_use[B_SLICE][0]); + + fprintf(p_stat,"\n Mode |"); + fprintf(p_stat," %10.2f |", (float)p_Stats->bit_use_mb_type[I_SLICE] / bit_use[I_SLICE][0]); + fprintf(p_stat," %10.2f |", (float)p_Stats->bit_use_mb_type[P_SLICE] / bit_use[P_SLICE][0]); + fprintf(p_stat," %10.2f |", (float)p_Stats->bit_use_mb_type[B_SLICE] / bit_use[B_SLICE][0]); + + fprintf(p_stat,"\n Motion Info |"); + fprintf(p_stat," ./. |"); + fprintf(p_stat," %10.2f |", mean_motion_info_bit_use[P_SLICE]); + fprintf(p_stat," %10.2f |", mean_motion_info_bit_use[B_SLICE]); + + fprintf(p_stat,"\n CBP Y/C |"); + fprintf(p_stat," %10.2f |", (float) p_Stats->tmp_bit_use_cbp[I_SLICE] / bit_use[I_SLICE][0]); + fprintf(p_stat," %10.2f |", (float) p_Stats->tmp_bit_use_cbp[P_SLICE] / bit_use[P_SLICE][0]); + fprintf(p_stat," %10.2f |", (float) p_Stats->tmp_bit_use_cbp[B_SLICE] / bit_use[B_SLICE][0]); + + // Print SP_SLICE + fprintf(p_stat,"\n Coeffs. Y |"); + fprintf(p_stat," %10.2f |", (float) p_Stats->bit_use_coeff[0][I_SLICE] / bit_use[I_SLICE][0]); + fprintf(p_stat," %10.2f |", (float) p_Stats->bit_use_coeff[0][P_SLICE] / bit_use[P_SLICE][0]); + fprintf(p_stat," %10.2f |", (float) p_Stats->bit_use_coeff[0][B_SLICE] / bit_use[B_SLICE][0]); + fprintf(p_stat," %10.2f |", (float) p_Stats->bit_use_coeff[0][SP_SLICE] / bit_use[SP_SLICE][0]); + + fprintf(p_stat,"\n Coeffs. C |"); + fprintf(p_stat," %10.2f |", (float) p_Stats->bit_use_coeffC[I_SLICE] / bit_use[I_SLICE][0]); + fprintf(p_stat," %10.2f |", (float) p_Stats->bit_use_coeffC[P_SLICE] / bit_use[P_SLICE][0]); + fprintf(p_stat," %10.2f |", (float) p_Stats->bit_use_coeffC[B_SLICE] / bit_use[B_SLICE][0]); + fprintf(p_stat," %10.2f |", (float) p_Stats->bit_use_coeffC[SP_SLICE] / bit_use[SP_SLICE][0]); + + fprintf(p_stat,"\n Coeffs. CB |"); + fprintf(p_stat," %10.2f |", (float) p_Stats->bit_use_coeff[1][I_SLICE] / bit_use[I_SLICE][0]); + fprintf(p_stat," %10.2f |", (float) p_Stats->bit_use_coeff[1][P_SLICE] / bit_use[P_SLICE][0]); + fprintf(p_stat," %10.2f |", (float) p_Stats->bit_use_coeff[1][B_SLICE] / bit_use[B_SLICE][0]); + fprintf(p_stat," %10.2f |", (float) p_Stats->bit_use_coeff[1][SP_SLICE] / bit_use[SP_SLICE][0]); + + fprintf(p_stat,"\n Coeffs. CR |"); + fprintf(p_stat," %10.2f |", (float) p_Stats->bit_use_coeff[2][I_SLICE] / bit_use[I_SLICE][0]); + fprintf(p_stat," %10.2f |", (float) p_Stats->bit_use_coeff[2][P_SLICE] / bit_use[P_SLICE][0]); + fprintf(p_stat," %10.2f |", (float) p_Stats->bit_use_coeff[2][B_SLICE] / bit_use[B_SLICE][0]); + fprintf(p_stat," %10.2f |", (float) p_Stats->bit_use_coeff[2][SP_SLICE] / bit_use[SP_SLICE][0]); + + fprintf(p_stat,"\n Delta quant |"); + fprintf(p_stat," %10.2f |", (float) p_Stats->bit_use_delta_quant[I_SLICE] / bit_use[I_SLICE][0]); + fprintf(p_stat," %10.2f |", (float) p_Stats->bit_use_delta_quant[P_SLICE] / bit_use[P_SLICE][0]); + fprintf(p_stat," %10.2f |", (float) p_Stats->bit_use_delta_quant[B_SLICE] / bit_use[B_SLICE][0]); + + fprintf(p_stat,"\n Stuffing Bits |"); + fprintf(p_stat," %10.2f |", (float) p_Stats->bit_use_stuffing_bits[I_SLICE] / bit_use[I_SLICE][0]); + fprintf(p_stat," %10.2f |", (float) p_Stats->bit_use_stuffing_bits[P_SLICE] / bit_use[P_SLICE][0]); + fprintf(p_stat," %10.2f |", (float) p_Stats->bit_use_stuffing_bits[B_SLICE] / bit_use[B_SLICE][0]); + + fprintf(p_stat,"\n ---------------------|----------------|----------------|----------------|"); + fprintf(p_stat,"\n average bits/frame |"); + fprintf(p_stat," %10.2f |", (float) bit_use[I_SLICE][1] / (float) bit_use[I_SLICE][0] ); + fprintf(p_stat," %10.2f |", (float) bit_use[P_SLICE][1] / (float) bit_use[P_SLICE][0] ); + fprintf(p_stat," %10.2f |", (float) bit_use[B_SLICE][1] / (float) bit_use[B_SLICE][0] ); + fprintf(p_stat,"\n ---------------------|----------------|----------------|----------------|"); + fprintf(p_stat,"\n"); + + fclose(p_stat); +} + + +void report_log(VideoParameters *p_Vid, InputParameters *p_Inp, StatParameters *p_Stats) +{ + DistortionParams *p_Dist = p_Vid->p_Dist; + FILE *p_log = p_Vid->p_log; + char name[40]; + int i; +#ifndef WIN32 + time_t now; + struct tm *l_time; + char string[1000]; +#else + char timebuf[128]; +#endif + + if ((p_log = fopen("log.dat", "r")) == 0) // check if file exists + { + if ((p_log = fopen("log.dat", "a")) == NULL) // append new statistic at the end + { + snprintf(errortext, ET_SIZE, "Error open file %s \n", "log.dat"); + error(errortext, 500); + } + else // Create header for new log file + { + fprintf(p_log," ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ \n"); + fprintf(p_log,"| Encoder statistics. This file is generated during first encoding session, new sessions will be appended |\n"); + fprintf(p_log," ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ \n"); + fprintf(p_log,"| ver | Date | Time | Sequence | #Img |P/MbInt| QPI| QPP| QPB| Format |Iperiod| #B | FMES | Hdmd | S.R |#Ref | Freq |Coding|RD-opt|Intra upd|8x8Tr| SNRY 1| SNRU 1| SNRV 1| SNRY N| SNRU N| SNRV N|#Bitr I|#Bitr P|#Bitr B|#Bitr IPB| Total Time | Me Time |\n"); + fprintf(p_log," ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ \n"); + + } + } + else + { + fclose (p_log); + if ((p_log = fopen("log.dat", "a")) == NULL) // File exists, just open for appending + { + snprintf(errortext, ET_SIZE, "Error open file %s \n", "log.dat"); + error(errortext, 500); + } + } + fprintf(p_log,"|%5s/%-5s", VERSION, EXT_VERSION); + +#ifdef WIN32 + _strdate( timebuf ); + fprintf(p_log,"| %1.5s |", timebuf ); + + _strtime( timebuf); + fprintf(p_log," % 1.5s |", timebuf); +#else + now = time ((time_t *) NULL); // Get the system time and put it into 'now' as 'calender time' + time (&now); + l_time = localtime (&now); + strftime (string, sizeof string, "%d-%b-%Y", l_time); + fprintf(p_log,"| %1.5s |", string ); + + strftime (string, sizeof string, "%H:%M:%S", l_time); + fprintf(p_log," %1.5s |", string ); +#endif + + for (i=0; i < 40; i++) + name[i] = p_Inp->input_file1.fname[i + imax(0, ((int) strlen(p_Inp->input_file1.fname)) - 40)]; // write last part of path, max 40 chars + fprintf(p_log,"%40.40s|",name); + + fprintf(p_log,"%5d | %d/%d |", p_Inp->no_frames, p_Inp->PicInterlace, p_Inp->MbInterlace); + fprintf(p_log," %-3d| %-3d| %-3d|", p_Inp->qp[I_SLICE], p_Inp->qp[P_SLICE], p_Inp->qp[B_SLICE]); + + fprintf(p_log,"%4dx%-4d|", p_Inp->output.width[0], p_Inp->output.height[0]); + fprintf(p_log," %3d |%3d |", p_Inp->intra_period, p_Stats->NumberBFrames); + + + switch( p_Inp->SearchMode[0] ) + { + case UM_HEX: + fprintf(p_log," HEX |"); + break; + case UM_HEX_SIMPLE: + fprintf(p_log," SHEX |"); + break; + case EPZS: + fprintf(p_log," EPZS |"); + break; + case FAST_FULL_SEARCH: + fprintf(p_log," FFS |"); + break; + default: + fprintf(p_log," FS |"); + break; + } +#if (MVC_EXTENSION_ENABLE) + if ( p_Inp->SepViewInterSearch ) + { + switch( p_Inp->SearchMode[1] ) + { + case UM_HEX: + fprintf(p_log," HEX |"); + break; + case UM_HEX_SIMPLE: + fprintf(p_log," SHEX |"); + break; + case EPZS: + fprintf(p_log," EPZS |"); + break; + case FAST_FULL_SEARCH: + fprintf(p_log," FFS |"); + break; + default: + fprintf(p_log," FS |"); + break; + } + } +#endif + + fprintf(p_log," %1d%1d%1d |", p_Inp->MEErrorMetric[F_PEL], p_Inp->MEErrorMetric[H_PEL], p_Inp->MEErrorMetric[Q_PEL]); + +#if (MVC_EXTENSION_ENABLE) + if ( p_Inp->SepViewInterSearch ) + { + fprintf(p_log," %3d (%3d) | %2d |", p_Inp->search_range[0], p_Inp->search_range[1], p_Inp->num_ref_frames ); + } + else +#endif + fprintf(p_log," %3d | %2d |", p_Inp->search_range[0], p_Inp->num_ref_frames ); + + fprintf(p_log," %5.2f|", p_Vid->framerate); + + if (p_Inp->symbol_mode == CAVLC) + fprintf(p_log," CAVLC|"); + else + fprintf(p_log," CABAC|"); + + fprintf(p_log," %d |", p_Inp->rdopt); + + if (p_Inp->intra_upd == 1) + fprintf(p_log," ON |"); + else + fprintf(p_log," OFF |"); + + fprintf(p_log," %d |", p_Inp->Transform8x8Mode); + + fprintf(p_log,"%7.3f|%7.3f|%7.3f|", + p_Dist->metric[PSNR].avslice[I_SLICE][0], + p_Dist->metric[PSNR].avslice[I_SLICE][1], + p_Dist->metric[PSNR].avslice[I_SLICE][2]); + fprintf(p_log,"%7.3f|%7.3f|%7.3f|", p_Dist->metric[PSNR].average[0],p_Dist->metric[PSNR].average[1],p_Dist->metric[PSNR].average[2]); + /* + fprintf(p_log,"%-5.3f|%-5.3f|%-5.3f|", p_Dist->metric[PSNR].avslice[I_SLICE][0], p_Dist->metric[PSNR].avslice[I_SLICE][1], p_Dist->metric[PSNR].avslice[I_SLICE][2]); + fprintf(p_log,"%-5.3f|%-5.3f|%-5.3f|", p_Dist->metric[PSNR].avslice[P_SLICE][0], p_Dist->metric[PSNR].avslice[P_SLICE][1], p_Dist->metric[PSNR].avslice[P_SLICE][2]); + fprintf(p_log,"%-5.3f|%-5.3f|%-5.3f|", p_Dist->metric[PSNR].avslice[B_SLICE][0], p_Dist->metric[PSNR].avslice[B_SLICE][1], p_Dist->metric[PSNR].avslice[B_SLICE][2]); + */ + fprintf(p_log,"%7.0f|%7.0f|%7.0f|%9.0f|", p_Stats->bitrate_st[I_SLICE],p_Stats->bitrate_st[P_SLICE],p_Stats->bitrate_st[B_SLICE], p_Stats->bitrate); + fprintf(p_log," %12d | %12d |", (int)p_Vid->tot_time,(int)p_Vid->me_tot_time); + + + fprintf(p_log,"\n"); + + fclose(p_log); + + p_log = fopen("data.txt", "a"); + + if ((p_Stats->NumberBFrames != 0) && (p_Stats->frame_ctr[B_SLICE] != 0)) // B picture used + { + fprintf(p_log, "%3d %2d %2d %2.2f %2.2f %2.2f %5" FORMAT_OFF_T " " + "%2.2f %2.2f %2.2f %5d " + "%2.2f %2.2f %2.2f %5" FORMAT_OFF_T " %5" FORMAT_OFF_T " %.3f\n", + p_Stats->frame_counter, p_Inp->qp[I_SLICE], p_Inp->qp[P_SLICE], + p_Dist->metric[PSNR].avslice[I_SLICE][0], + p_Dist->metric[PSNR].avslice[I_SLICE][1], + p_Dist->metric[PSNR].avslice[I_SLICE][2], + p_Stats->bit_counter[I_SLICE], + 0.0, + 0.0, + 0.0, + 0, + p_Dist->metric[PSNR].average[0], + p_Dist->metric[PSNR].average[1], + p_Dist->metric[PSNR].average[2], + (p_Stats->bit_counter[I_SLICE] + p_Stats->bit_ctr) / p_Stats->frame_counter, + p_Stats->bit_counter[B_SLICE] / p_Stats->frame_ctr[B_SLICE], + (double) 0.001 * p_Vid->tot_time / (p_Stats->frame_counter)); + } + else + { + if (p_Inp->no_frames != 0) + fprintf(p_log, "%3d %2d %2d %2.2f %2.2f %2.2f %5" FORMAT_OFF_T " " + "%2.2f %2.2f %2.2f %5d " + "%2.2f %2.2f %2.2f %5" FORMAT_OFF_T " %5d %.3f\n", + p_Stats->frame_counter, p_Inp->qp[I_SLICE], p_Inp->qp[P_SLICE], + p_Dist->metric[PSNR].avslice[I_SLICE][0], + p_Dist->metric[PSNR].avslice[I_SLICE][1], + p_Dist->metric[PSNR].avslice[I_SLICE][2], + p_Stats->bit_counter[I_SLICE], + 0.0, + 0.0, + 0.0, + 0, + p_Dist->metric[PSNR].average[0], + p_Dist->metric[PSNR].average[1], + p_Dist->metric[PSNR].average[2], + (p_Stats->bit_counter[I_SLICE] + p_Stats->bit_ctr)/ p_Stats->frame_counter, + 0, + (double)0.001 * p_Vid->tot_time / p_Stats->frame_counter); + } + + fclose(p_log); +} + +/*! + ************************************************************************ + * \brief + * Reports the gathered information to appropriate outputs + * \par Input: + * InputParameters *inp, \n + * VideoParameters *p_Vid, \n + * struct stat_par *p_Stats, \n + * + * \par Output: + * None + ************************************************************************ + */ +void report( VideoParameters *p_Vid, InputParameters *p_Inp, StatParameters *p_Stats) +{ + DistortionParams *p_Dist = p_Vid->p_Dist; + int64 bit_use[NUM_SLICE_TYPES][2]; + int i,j; + int64 total_bits; +#if (MVC_EXTENSION_ENABLE) + int64 total_bits_v[NUM_VIEWS]; +#endif + + bit_use[ I_SLICE][0] = p_Stats->frame_ctr[I_SLICE]; + bit_use[ P_SLICE][0] = imax(p_Stats->frame_ctr[P_SLICE ] + p_Stats->frame_ctr[SP_SLICE], 1); + bit_use[ B_SLICE][0] = imax(p_Stats->frame_ctr[B_SLICE ], 1); + bit_use[SP_SLICE][0] = imax(p_Stats->frame_ctr[SP_SLICE], 1); + + // normalize time p_Stats + p_Vid->tot_time = timenorm(p_Vid->tot_time); + p_Vid->me_tot_time = timenorm(p_Vid->me_tot_time); + // Accumulate bit usage for inter and intra frames + for (j=0; j < NUM_SLICE_TYPES; j++) + { + bit_use[j][1] = 0; + } + + for (j=0; j < NUM_SLICE_TYPES; j++) + { + for(i=0; i < MAXMODE; i++) + bit_use[j][1] += p_Stats->bit_use_mode[j][i]; + + bit_use[j][1] += p_Stats->bit_use_mb_type[j]; + bit_use[j][1] += p_Stats->bit_use_header[j]; + bit_use[j][1] += p_Stats->tmp_bit_use_cbp[j]; + bit_use[j][1] += p_Stats->bit_use_coeffC[j]; + bit_use[j][1] += p_Stats->bit_use_coeff[0][j]; + bit_use[j][1] += p_Stats->bit_use_coeff[1][j]; + bit_use[j][1] += p_Stats->bit_use_coeff[2][j]; + bit_use[j][1] += p_Stats->bit_use_delta_quant[j]; + bit_use[j][1] += p_Stats->bit_use_stuffing_bits[j]; + } + + //! Currently adding NVB bits on P rate. Maybe additional p_Stats info should be created instead and added in log file + p_Stats->bitrate_st[ I_SLICE] = (p_Stats->bit_counter[ I_SLICE]) * (float) (p_Inp->output.frame_rate) / (float) (p_Stats->frame_counter); + p_Stats->bitrate_st[ P_SLICE] = (p_Stats->bit_counter[ P_SLICE]) * (float) (p_Inp->output.frame_rate) / (float) (p_Stats->frame_counter); + p_Stats->bitrate_st[ B_SLICE] = (p_Stats->bit_counter[ B_SLICE]) * (float) (p_Inp->output.frame_rate) / (float) (p_Stats->frame_counter); + p_Stats->bitrate_st[SP_SLICE] = (p_Stats->bit_counter[SP_SLICE]) * (float) (p_Inp->output.frame_rate) / (float) (p_Stats->frame_counter); + + switch (p_Inp->Verbose) + { + case 0: + case 1: + default: + fprintf(stdout,"------------------ Average data all frames -----------------------------------\n\n"); + break; + case 2: + fprintf(stdout,"------------------------------------ Average data all frames ---------------------------------\n\n"); + break; + case 3: + fprintf(stdout,"--------------------------------------- Average data all frames -------------------------------------\n\n"); + break; + } + + if (p_Inp->Verbose != 0) + { + DistMetric *snr = &p_Dist->metric[PSNR ]; + DistMetric *sse = &p_Dist->metric[SSE ]; + DistMetric *snr_rgb = &p_Dist->metric[PSNR_RGB]; + DistMetric *sse_rgb = &p_Dist->metric[SSE_RGB ]; + + int impix = p_Inp->output.size_cmp[0]; + int impix_cr = p_Inp->output.size_cmp[1]; + + float csnr_y = psnr(p_Vid->max_imgpel_value_comp_sq[0], impix , sse->average[0]); + float csnr_u = psnr(p_Vid->max_imgpel_value_comp_sq[1], impix_cr, sse->average[1]); + float csnr_v = psnr(p_Vid->max_imgpel_value_comp_sq[2], impix_cr, sse->average[2]); + +#if (MVC_EXTENSION_ENABLE) + DistMetric *snr_v[2] = {NULL, NULL}; + DistMetric *sse_v[2] = {NULL, NULL}; + float csnr_y_v[2] = {0.0, 0.0}; + float csnr_u_v[2] = {0.0, 0.0}; + float csnr_v_v[2] = {0.0, 0.0}; + + if (p_Inp->num_of_views == 2) + { + snr_v[0] = &p_Dist->metric_v[0][PSNR]; + snr_v[1] = &p_Dist->metric_v[1][PSNR]; + sse_v[0] = &p_Dist->metric_v[0][SSE]; + sse_v[1] = &p_Dist->metric_v[1][SSE]; + csnr_y_v[0] = psnr(p_Vid->max_imgpel_value_comp_sq[0], impix , sse_v[0]->average[0]); + csnr_u_v[0] = psnr(p_Vid->max_imgpel_value_comp_sq[1], impix_cr, sse_v[0]->average[1]); + csnr_v_v[0] = psnr(p_Vid->max_imgpel_value_comp_sq[2], impix_cr, sse_v[0]->average[2]); + csnr_y_v[1] = psnr(p_Vid->max_imgpel_value_comp_sq[0], impix , sse_v[1]->average[0]); + csnr_u_v[1] = psnr(p_Vid->max_imgpel_value_comp_sq[1], impix_cr, sse_v[1]->average[1]); + csnr_v_v[1] = psnr(p_Vid->max_imgpel_value_comp_sq[2], impix_cr, sse_v[1]->average[2]); + } +#endif + + fprintf(stdout, " Total encoding time for the seq. : %7.3f sec (%3.2f fps)\n", (float) p_Vid->tot_time * 0.001, 1000.0 * (float) (p_Stats->frame_counter) / (float)p_Vid->tot_time); + fprintf(stdout, " Total ME time for sequence : %7.3f sec \n\n", (float)p_Vid->me_tot_time * 0.001); + + fprintf(stdout," Y { PSNR (dB), cSNR (dB), MSE } : { %7.3f, %7.3f, %9.5f }\n", + snr->average[0], csnr_y, sse->average[0]/(float)impix); + fprintf(stdout," U { PSNR (dB), cSNR (dB), MSE } : { %7.3f, %7.3f, %9.5f }\n", + snr->average[1], csnr_u, sse->average[1]/(float)impix_cr); + fprintf(stdout," V { PSNR (dB), cSNR (dB), MSE } : { %7.3f, %7.3f, %9.5f }\n", + snr->average[2], csnr_v, sse->average[2]/(float)impix_cr); +#if (MVC_EXTENSION_ENABLE) + if (p_Inp->num_of_views == 2) + { + fprintf( stdout, "\n" ); + fprintf(stdout," View0_Y { PSNR (dB), cSNR (dB), MSE } : { %7.3f, %7.3f, %9.5f }\n", + snr_v[0]->average[0], csnr_y_v[0], sse_v[0]->average[0]/(float)impix); + fprintf(stdout," View0_U { PSNR (dB), cSNR (dB), MSE } : { %7.3f, %7.3f, %9.5f }\n", + snr_v[0]->average[1], csnr_u_v[0], sse_v[0]->average[1]/(float)impix_cr); + fprintf(stdout," View0_V { PSNR (dB), cSNR (dB), MSE } : { %7.3f, %7.3f, %9.5f }\n", + snr_v[0]->average[2], csnr_v_v[0], sse_v[0]->average[2]/(float)impix_cr); + fprintf( stdout, "\n" ); + fprintf(stdout," View1_Y { PSNR (dB), cSNR (dB), MSE } : { %7.3f, %7.3f, %9.5f }\n", + snr_v[1]->average[0], csnr_y_v[1], sse_v[1]->average[0]/(float)impix); + fprintf(stdout," View1_U { PSNR (dB), cSNR (dB), MSE } : { %7.3f, %7.3f, %9.5f }\n", + snr_v[1]->average[1], csnr_u_v[1], sse_v[1]->average[1]/(float)impix_cr); + fprintf(stdout," View1_V { PSNR (dB), cSNR (dB), MSE } : { %7.3f, %7.3f, %9.5f }\n", + snr_v[1]->average[2], csnr_v_v[1], sse_v[1]->average[2]/(float)impix_cr); + } +#endif + + if(p_Inp->DistortionYUVtoRGB == 1) + { + float csnr_r = psnr(p_Vid->max_imgpel_value_comp_sq[0], impix, sse_rgb->average[0]); + float csnr_g = psnr(p_Vid->max_imgpel_value_comp_sq[1], impix, sse_rgb->average[1]); + float csnr_b = psnr(p_Vid->max_imgpel_value_comp_sq[2], impix, sse_rgb->average[2]); + + fprintf(stdout," R { PSNR (dB), cSNR (dB), MSE } : { %7.3f, %7.3f, %9.5f }\n", + snr_rgb->average[0], csnr_r, sse_rgb->average[0] / (float) impix); + fprintf(stdout," G { PSNR (dB), cSNR (dB), MSE } : { %7.3f, %7.3f, %9.5f }\n", + snr_rgb->average[1], csnr_g, sse_rgb->average[1] / (float) impix); + fprintf(stdout," B { PSNR (dB), cSNR (dB), MSE } : { %7.3f, %7.3f, %9.5f }\n", + snr_rgb->average[2], csnr_b, sse_rgb->average[2] / (float) impix); + } + + if (p_Inp->Distortion[SSIM] == 1) + { + if(p_Inp->DistortionYUVtoRGB == 1) + { + fprintf(stdout," SSIM {Y, R} : { %5.4f, %5.4f }\n", p_Dist->metric[SSIM].average[0], p_Dist->metric[SSIM_RGB].average[0]); + fprintf(stdout," SSIM {U, G} : { %5.4f, %5.4f }\n", p_Dist->metric[SSIM].average[1], p_Dist->metric[SSIM_RGB].average[1]); + fprintf(stdout," SSIM {V, B} : { %5.4f, %5.4f }\n", p_Dist->metric[SSIM].average[2], p_Dist->metric[SSIM_RGB].average[2]); + } + else + { + fprintf(stdout," Y SSIM : %5.4f\n", p_Dist->metric[SSIM].average[0]); + fprintf(stdout," U SSIM : %5.4f\n", p_Dist->metric[SSIM].average[1]); + fprintf(stdout," V SSIM : %5.4f\n", p_Dist->metric[SSIM].average[2]); + } + } + if (p_Inp->Distortion[MS_SSIM] == 1) + { + if(p_Inp->DistortionYUVtoRGB == 1) + { + fprintf(stdout," MS-SSIM {Y, R} : { %5.4f, %5.4f }\n", p_Dist->metric[MS_SSIM].average[0], p_Dist->metric[MS_SSIM_RGB].average[0]); + fprintf(stdout," MS-SSIM {U, G} : { %5.4f, %5.4f }\n", p_Dist->metric[MS_SSIM].average[1], p_Dist->metric[MS_SSIM_RGB].average[1]); + fprintf(stdout," MS-SSIM {V, B} : { %5.4f, %5.4f }\n", p_Dist->metric[MS_SSIM].average[2], p_Dist->metric[MS_SSIM_RGB].average[2]); + } + else + { + fprintf(stdout," Y MS-SSIM : %5.4f\n", p_Dist->metric[MS_SSIM].average[0]); + fprintf(stdout," U MS-SSIM : %5.4f\n", p_Dist->metric[MS_SSIM].average[1]); + fprintf(stdout," V MS-SSIM : %5.4f\n", p_Dist->metric[MS_SSIM].average[2]); + } + } + fprintf(stdout,"\n"); + } + else + fprintf(stdout, " Total encoding time for the seq. : %5.3f sec (%5.2f fps)\n\n", p_Vid->tot_time * 0.001, 1000.0 * (p_Stats->frame_counter) / p_Vid->tot_time); + + total_bits = p_Stats->bit_ctr_parametersets; + total_bits += p_Stats->bit_ctr_filler_data; + for (i = 0; i < NUM_SLICE_TYPES; i++) + total_bits += p_Stats->bit_counter[i]; +#if (MVC_EXTENSION_ENABLE) + if (p_Inp->num_of_views == 2) + { + for ( j = 0; j < NUM_VIEWS; j++ ) + { + total_bits_v[j] = p_Stats->bit_ctr_parametersets_v[j]; + total_bits_v[j] += p_Stats->bit_ctr_filler_data_v[j]; + for (i = 0; i < NUM_SLICE_TYPES; i++) + total_bits_v[j] += p_Stats->bit_counter_v[j][i]; + } + } + + if (p_Inp->num_of_views == 2 && p_Vid->fld_flag) + { + if (p_Stats->frame_ctr[B_SLICE] != 0) + { + fprintf(stdout, " Total bits : %" FORMAT_OFF_T " (I %" FORMAT_OFF_T ", P %" FORMAT_OFF_T ", B %" FORMAT_OFF_T " NVB %d) \n", + total_bits_v[0] + total_bits_v[1], + p_Stats->bit_counter_v[0][I_SLICE] + p_Stats->bit_counter_v[1][I_SLICE], + p_Stats->bit_counter_v[0][P_SLICE] + p_Stats->bit_counter_v[1][P_SLICE], + p_Stats->bit_counter_v[0][B_SLICE] + p_Stats->bit_counter_v[1][B_SLICE], + p_Stats->bit_ctr_parametersets_v[0] + p_Stats->bit_ctr_parametersets_v[1]); + } + else + { + fprintf(stdout, " Total bits : %" FORMAT_OFF_T " (I %" FORMAT_OFF_T ", P %" FORMAT_OFF_T ", NVB %d) \n", + total_bits_v[0] + total_bits_v[1], + p_Stats->bit_counter_v[0][I_SLICE] + p_Stats->bit_counter_v[1][I_SLICE], + p_Stats->bit_counter_v[0][P_SLICE] + p_Stats->bit_counter_v[1][P_SLICE], + p_Stats->bit_ctr_parametersets_v[0] + p_Stats->bit_ctr_parametersets_v[1]); + } + } + else +#endif + { + if (p_Stats->frame_ctr[B_SLICE] != 0) + { + fprintf(stdout, " Total bits : %" FORMAT_OFF_T " (I %" FORMAT_OFF_T ", P %" FORMAT_OFF_T ", B %" FORMAT_OFF_T " NVB %d) \n", + total_bits, p_Stats->bit_counter[I_SLICE], p_Stats->bit_counter[P_SLICE], p_Stats->bit_counter[B_SLICE], p_Stats->bit_ctr_parametersets); + } + else + { + fprintf(stdout, " Total bits : %" FORMAT_OFF_T " (I %" FORMAT_OFF_T ", P %" FORMAT_OFF_T ", NVB %d) \n", + total_bits, p_Stats->bit_counter[I_SLICE], p_Stats->bit_counter[P_SLICE], p_Stats->bit_ctr_parametersets); + } + } +#if (MVC_EXTENSION_ENABLE) + if (p_Inp->num_of_views == 2) + { + int view_id; + + for ( view_id = 0; view_id < NUM_VIEWS; view_id++ ) + { + if (p_Stats->frame_ctr[B_SLICE] != 0) + { + fprintf(stdout, " View %1d Total-bits : %" FORMAT_OFF_T " (I %" FORMAT_OFF_T ", P %" FORMAT_OFF_T ", B %" FORMAT_OFF_T " NVB %d) \n", + view_id, total_bits_v[view_id], p_Stats->bit_counter_v[view_id][I_SLICE], p_Stats->bit_counter_v[view_id][P_SLICE], p_Stats->bit_counter_v[view_id][B_SLICE], p_Stats->bit_ctr_parametersets_v[view_id]); + } + else + { + fprintf(stdout, " View %1d Total-bits : %" FORMAT_OFF_T " (I %" FORMAT_OFF_T ", P %" FORMAT_OFF_T ", NVB %d) \n", + view_id, total_bits_v[view_id], p_Stats->bit_counter_v[view_id][I_SLICE], p_Stats->bit_counter_v[view_id][P_SLICE], p_Stats->bit_ctr_parametersets_v[view_id]); + } + } + } + + if (p_Inp->num_of_views == 2) + { + p_Stats->bitrate= ((float) total_bits * (float) p_Inp->output.frame_rate) / ((float) (p_Stats->frame_counter>>1)); + } + else +#endif + p_Stats->bitrate= ((float) total_bits * (float) p_Inp->output.frame_rate) / ((float) (p_Stats->frame_counter)); + fprintf(stdout, " Bit rate (kbit/s) @ %2.2f Hz : %5.2f\n", p_Inp->output.frame_rate, p_Stats->bitrate / 1000.0); +#if (MVC_EXTENSION_ENABLE) + if (p_Inp->num_of_views == 2) + { + p_Stats->bitrate_v[0] = ((float) total_bits_v[0] * (float) p_Inp->output.frame_rate) / ((float) (p_Stats->frame_counter >> 1)); + p_Stats->bitrate_v[1] = ((float) total_bits_v[1] * (float) p_Inp->output.frame_rate) / ((float) (p_Stats->frame_counter >> 1)); + fprintf(stdout, " View 0 BR (kbit/s) @ %2.2f Hz : %5.2f\n", p_Inp->output.frame_rate, p_Stats->bitrate_v[0] / 1000.0); + fprintf(stdout, " View 1 BR (kbit/s) @ %2.2f Hz : %5.2f\n", p_Inp->output.frame_rate, p_Stats->bitrate_v[1] / 1000.0); + } +#endif + + for (i = 0; i < 5; i++) + { + p_Stats->bit_ctr_emulation_prevention += p_Stats->bit_use_stuffing_bits[i]; + } +#if (MVC_EXTENSION_ENABLE) + if ( p_Vid->num_of_layers == 2 ) + { + for (i = 0; i < 5; i++) + { + p_Stats->bit_ctr_emulationprevention_v[p_Vid->dpb_layer_id] += p_Stats->bit_use_stuffing_bits[i]; + } + } +#endif + + fprintf(stdout, " Bits to avoid Startcode Emulation : %" FORMAT_OFF_T " \n", p_Stats->bit_ctr_emulation_prevention); + fprintf(stdout, " Bits for parameter sets : %d \n", p_Stats->bit_ctr_parametersets); + fprintf(stdout, " Bits for filler data : %" FORMAT_OFF_T " \n\n", p_Stats->bit_ctr_filler_data); + + switch (p_Inp->Verbose) + { + case 0: + case 1: + default: + fprintf(stdout,"-------------------------------------------------------------------------------\n"); + break; + case 2: + fprintf(stdout,"------------------------------------------------------------------------------------------------\n"); + break; + case 3: + fprintf(stdout,"-------------------------------------------------------------------------------------------------------\n"); + break; + } + fprintf(stdout,"Exit JM %s encoder ver %s ", JM, VERSION); + fprintf(stdout,"\n"); + + // status file + report_stats(p_Vid, p_Inp, p_Stats, bit_use); + + // write to log file + report_log(p_Vid, p_Inp, p_Stats); + + if (p_Inp->ReportFrameStats) + { + if ((p_Vid->p_log = fopen("stat_frame.dat", "a")) == NULL) // append new statistic at the end + { + snprintf(errortext, ET_SIZE, "Error open file %s \n", "stat_frame.dat.dat"); + // error(errortext, 500); + } + else + { + fprintf(p_Vid->p_log," --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- \n"); + fclose(p_Vid->p_log); + } + report_log_mode(p_Vid, p_Inp, p_Stats); + } +} + + +/*! + ************************************************************************ + * \brief + * Prints the header of the protocol. + * \par Input: + * InputParameters *inp + * \par Output: + * none + ************************************************************************ + */ +void information_init ( VideoParameters *p_Vid, InputParameters *p_Inp, StatParameters *p_Stats) +{ + int i; + static const char yuv_types[4][10] = {"YUV 4:0:0", "YUV 4:2:0", "YUV 4:2:2", "YUV 4:4:4"}; + switch (p_Inp->Verbose) + { + case 0: + case 1: + default: + printf("------------------------------- JM %4.4s %7.7s -------------------------------\n", VERSION, EXT_VERSION); + break; + case 2: + printf("--------------------------------------- JM %4.4s %7.7s ----------------------------------------\n", VERSION, EXT_VERSION); + break; + case 3: + printf("------------------------------------------ JM %4.4s %7.7s ------------------------------------------\n", VERSION, EXT_VERSION); + break; + } + + fprintf(stdout, " Input YUV file : %s \n", p_Inp->input_file1.fname); +#if (MVC_EXTENSION_ENABLE) + if(p_Inp->num_of_views==2) + fprintf(stdout, " Input YUV file 2 : %s \n", p_Inp->input_file2.fname); +#endif + + fprintf(stdout, " Output H.264 bitstream : %s \n", p_Inp->outfile); + if (p_Vid->p_dec != -1) + fprintf(stdout, " Output YUV file : %s \n", p_Inp->ReconFile); +#if (MVC_EXTENSION_ENABLE) + if(p_Inp->num_of_views==2) + { + if (p_Vid->p_dec2 != -1) + fprintf(stdout, " Output YUV file 2 : %s \n", p_Inp->ReconFile2); + } +#endif + fprintf(stdout, " YUV Format : %s \n", &yuv_types[p_Vid->yuv_format][0]);//p_Vid->yuv_format==YUV422?"YUV 4:2:2":(p_Vid->yuv_format==YUV444)?"YUV 4:4:4":"YUV 4:2:0"); + fprintf(stdout, " Frames to be encoded : %d\n", p_Inp->no_frames); + if (p_Inp->Verbose != 0) + { + fprintf(stdout, " Freq. for encoded bitstream : %3.2f\n", p_Inp->output.frame_rate); + fprintf(stdout, " PicInterlace / MbInterlace : %d/%d\n", p_Inp->PicInterlace, p_Inp->MbInterlace); + fprintf(stdout, " Transform8x8Mode : %d\n", p_Inp->Transform8x8Mode); + + for (i=0; i<3; i++) + { + fprintf(stdout," ME Metric for Refinement Level %1d : %s\n", i, DistortionType[p_Inp->MEErrorMetric[i]]); + } + fprintf(stdout, " Mode Decision Metric : %s\n", DistortionType[p_Inp->ModeDecisionMetric]); + + if( p_Inp->OnTheFlyFractMCP ) + { + fprintf(stdout," On-the-fly interpolation mode : OTF_L%d\n", p_Inp->OnTheFlyFractMCP ); + } + + switch ( p_Inp->ChromaMEEnable ) + { + case 1: + fprintf(stdout," Motion Estimation for components : YCbCr\n"); + break; + default: + fprintf(stdout," Motion Estimation for components : Y\n"); + break; + } + + fprintf(stdout, " Image format : %dx%d (%dx%d)\n", p_Inp->output.width[0], p_Inp->output.height[0], p_Vid->width,p_Vid->height); + + if (p_Inp->intra_upd) + fprintf(stdout," Error robustness : On\n"); + else + fprintf(stdout," Error robustness : Off\n"); + fprintf(stdout, " Search range : %d\n", p_Inp->search_range[0]); +#if (MVC_EXTENSION_ENABLE) + if ( p_Inp->SepViewInterSearch ) + { + fprintf(stdout, " Search range (view 1) : %d\n", p_Inp->search_range[1]); + } +#endif + + fprintf(stdout, " Total number of references : %d\n", p_Inp->num_ref_frames_org); + fprintf(stdout, " References for P slices : %d\n", p_Inp->P_List0_refs_org[0] ? p_Inp->P_List0_refs_org[0] : p_Inp->num_ref_frames_org); + fprintf(stdout, " References for B slices (L0, L1) : %d, %d\n", + p_Inp->B_List0_refs_org[0] ? p_Inp->B_List0_refs_org[0] : p_Inp->num_ref_frames_org, + p_Inp->B_List1_refs_org[0] ? p_Inp->B_List1_refs_org[0] : p_Inp->num_ref_frames_org); + + if ( p_Vid->num_of_layers > 1 ) + { + fprintf(stdout, " View 1 refs for P slices : %d\n", p_Inp->P_List0_refs_org[1] ? p_Inp->P_List0_refs_org[1] : p_Inp->num_ref_frames_org); + fprintf(stdout, " View 1 refs for B slices (L0, L1) : %d, %d\n", + p_Inp->B_List0_refs_org[1] ? p_Inp->B_List0_refs_org[1] : p_Inp->num_ref_frames_org, + p_Inp->B_List1_refs_org[1] ? p_Inp->B_List1_refs_org[1] : p_Inp->num_ref_frames_org); + } + + // Sequence Type + fprintf(stdout, " Sequence type :"); + if (p_Stats->NumberBFrames > 0 && p_Inp->HierarchicalCoding) + { + fprintf(stdout, " Hierarchy (QP: I %d, P %d, B %d) \n", + p_Inp->qp[I_SLICE], p_Inp->qp[P_SLICE], p_Inp->qp[B_SLICE]); + } + else if (p_Stats->NumberBFrames > 0) + { + char seqtype[80]; + int i,j; + + strcpy (seqtype,"I"); + + for (j=0; j < 2; j++) + { + for (i=0; i < p_Stats->NumberBFrames; i++) + { + if (p_Inp->BRefPictures) + strncat(seqtype,"-RB", imax(0, (int) (79 - strlen(seqtype)))); + else + strncat(seqtype,"-B", imax(0, (int) (79 - strlen(seqtype)))); + } + strncat(seqtype,"-P", imax(0, (int) (79 - strlen(seqtype)))); + } + if (p_Inp->BRefPictures) + fprintf(stdout, " %s (QP: I %d, P %d, RB %d) \n", seqtype, p_Inp->qp[I_SLICE], p_Inp->qp[P_SLICE], iClip3(0, 51, p_Inp->qp[B_SLICE] + p_Inp->qpBRSOffset)); + else + fprintf(stdout, " %s (QP: I %d, P %d, B %d) \n", seqtype, p_Inp->qp[I_SLICE], p_Inp->qp[P_SLICE], p_Inp->qp[B_SLICE]); + } + else if (p_Stats->NumberBFrames == 0 && (p_Inp->intra_period == 1 || p_Inp->idr_period == 1)) + fprintf(stdout, " IIII (QP: I %d) \n", p_Inp->qp[I_SLICE]); + else if (p_Stats->NumberBFrames == 0 && p_Inp->sp_periodicity == 0) + fprintf(stdout, " IPPP (QP: I %d, P %d) \n", p_Inp->qp[I_SLICE], p_Inp->qp[P_SLICE]); + else + fprintf(stdout, " I-P-P-SP-P (QP: I %d, P %d, SP (%d, %d)) \n", p_Inp->qp[I_SLICE], p_Inp->qp[P_SLICE], p_Inp->qp[SP_SLICE], p_Inp->qpsp); + + // report on entropy coding method + if (p_Inp->symbol_mode == CAVLC) + fprintf(stdout," Entropy coding method : CAVLC\n"); + else + fprintf(stdout," Entropy coding method : CABAC\n"); + + fprintf(stdout, " Profile/Level IDC : (%d,%d)\n", p_Inp->ProfileIDC, p_Inp->LevelIDC); + + if (p_Inp->SearchMode[0] == UM_HEX) + fprintf(stdout, " Motion Estimation Scheme : HEX\n"); + else if (p_Inp->SearchMode[0] == UM_HEX_SIMPLE) + fprintf(stdout, " Motion Estimation Scheme : SHEX\n"); + else if (p_Inp->SearchMode[0] == EPZS) + { + fprintf(stdout, " Motion Estimation Scheme : EPZS\n"); + EPZSOutputStats(p_Inp, stdout, 0); + } + else if (p_Inp->SearchMode[0] == FAST_FULL_SEARCH) + fprintf(stdout, " Motion Estimation Scheme : Fast Full Search\n"); + else + fprintf(stdout, " Motion Estimation Scheme : Full Search\n"); + +#if (MVC_EXTENSION_ENABLE) + if ( p_Inp->SepViewInterSearch ) + { + if (p_Inp->SearchMode[1] == UM_HEX) + fprintf(stdout, " Motion Estimation Scheme : HEX\n"); + else if (p_Inp->SearchMode[1] == UM_HEX_SIMPLE) + fprintf(stdout, " Motion Estimation Scheme : SHEX\n"); + else if (p_Inp->SearchMode[1] == EPZS) + { + fprintf(stdout, " Motion Estimation Scheme : EPZS\n"); + EPZSOutputStats(p_Inp, stdout, 0); + } + else if (p_Inp->SearchMode[1] == FAST_FULL_SEARCH) + fprintf(stdout, " Motion Estimation Scheme : Fast Full Search\n"); + else + fprintf(stdout, " Motion Estimation Scheme : Full Search\n"); + } +#endif + + if (p_Inp->full_search == 2) + fprintf(stdout," Search range restrictions : none\n"); + else if (p_Inp->full_search == 1) + fprintf(stdout," Search range restrictions : older reference frames\n"); + else + fprintf(stdout," Search range restrictions : smaller blocks and older reference frames\n"); + + if (p_Inp->rdopt) + fprintf(stdout," RD-optimized mode decision : used\n"); + else + fprintf(stdout," RD-optimized mode decision : not used\n"); + + switch(p_Inp->partition_mode) + { + case PAR_DP_1: + fprintf(stdout," Data Partitioning Mode : 1 partition \n"); + break; + case PAR_DP_3: + fprintf(stdout," Data Partitioning Mode : 3 partitions \n"); + break; + default: + fprintf(stdout," Data Partitioning Mode : not supported\n"); + break; + } + + switch(p_Inp->of_mode) + { + case PAR_OF_ANNEXB: + fprintf(stdout," Output File Format : H.264/AVC Annex B Byte Stream Format \n"); + break; + case PAR_OF_RTP: + fprintf(stdout," Output File Format : RTP Packet File Format \n"); + break; + default: + fprintf(stdout," Output File Format : not supported\n"); + break; + } + } + + + switch (p_Inp->Verbose) + { + case 0: + default: + printf("-------------------------------------------------------------------------------\n"); + printf("\nEncoding. Please Wait.\n\n"); + break; + case 1: +#if (MVC_EXTENSION_ENABLE) + if (p_Inp->num_of_views == 2) + { + printf("------------------------------------------------------------------------------------\n"); + printf("Frame View Bit/pic QP SnrY SnrU SnrV Time(ms) MET(ms) Frm/Fld Ref \n"); + printf("------------------------------------------------------------------------------------\n"); + } + else +#endif + { + printf("-------------------------------------------------------------------------------\n"); + printf("Frame Bit/pic QP SnrY SnrU SnrV Time(ms) MET(ms) Frm/Fld Ref \n"); + printf("-------------------------------------------------------------------------------\n"); + } + break; + case 2: +#if (MVC_EXTENSION_ENABLE) + if (p_Inp->num_of_views == 2) + { + if (p_Inp->Distortion[SSIM] == 1) + { + printf("----------------------------------------------------------------------------------------------------------------------------------\n"); + printf("Frame View Bit/pic WP QP QL SnrY SnrU SnrV SsimY SsimU SsimV Time(ms) MET(ms) Frm/Fld I D L0 L1 RDP Ref\n"); + printf("----------------------------------------------------------------------------------------------------------------------------------\n"); + } + else + { + printf("---------------------------------------------------------------------------------------------------------\n"); + printf("Frame View Bit/pic WP QP QL SnrY SnrU SnrV Time(ms) MET(ms) Frm/Fld I D L0 L1 RDP Ref\n"); + printf("---------------------------------------------------------------------------------------------------------\n"); + } + } + else +#endif + { + if (p_Inp->Distortion[SSIM] == 1) + { + printf("---------------------------------------------------------------------------------------------------------------------------\n"); + printf("Frame Bit/pic WP QP QL SnrY SnrU SnrV SsimY SsimU SsimV Time(ms) MET(ms) Frm/Fld I D L0 L1 RDP Ref\n"); + printf("---------------------------------------------------------------------------------------------------------------------------\n"); + } + else + { + printf("---------------------------------------------------------------------------------------------------\n"); + printf("Frame Bit/pic WP QP QL SnrY SnrU SnrV Time(ms) MET(ms) Frm/Fld I D L0 L1 RDP Ref\n"); + printf("---------------------------------------------------------------------------------------------------\n"); + } + } + break; + case 3: +#if (MVC_EXTENSION_ENABLE) + if (p_Inp->num_of_views == 2) + { + if (p_Inp->Distortion[SSIM] == 1) + { + printf("---------------------------------------------------------------------------------------------------------------------------------------\n"); + printf("Frame View Bit/pic NVB WP QP QL SnrY SnrU SnrV SsimY SsimU SsimV Time(ms) MET(ms) Frm/Fld I D L0 L1 RDP Ref\n"); + printf("---------------------------------------------------------------------------------------------------------------------------------------\n"); + } + else + { + printf("---------------------------------------------------------------------------------------------------------------\n"); + printf("Frame View Bit/pic NVB WP QP QL SnrY SnrU SnrV Time(ms) MET(ms) Frm/Fld I D L0 L1 RDP Ref\n"); + printf("---------------------------------------------------------------------------------------------------------------\n"); + } + } + else +#endif + { + if (p_Inp->Distortion[SSIM] == 1) + { + printf("--------------------------------------------------------------------------------------------------------------------------------\n"); + printf("Frame Bit/pic NVB WP QP QL SnrY SnrU SnrV SsimY SsimU SsimV Time(ms) MET(ms) Frm/Fld I D L0 L1 RDP Ref\n"); + printf("--------------------------------------------------------------------------------------------------------------------------------\n"); + } + else + { + printf("--------------------------------------------------------------------------------------------------------\n"); + printf("Frame Bit/pic NVB WP QP QL SnrY SnrU SnrV Time(ms) MET(ms) Frm/Fld I D L0 L1 RDP Ref\n"); + printf("--------------------------------------------------------------------------------------------------------\n"); + } + } + break; + case 4: +#if (MVC_EXTENSION_ENABLE) + if (p_Inp->num_of_views == 2) + { + if (p_Inp->Distortion[SSIM] == 1) + { + printf("-------------------------------------------------------------------------------------------------------------------------------------------------\n"); + printf("Frame View Bit/pic Filler NVB WP QP QL SnrY SnrU SnrV SsimY SsimU SsimV Time(ms) MET(ms) Frm/Fld I D L0 L1 RDP Ref\n"); + printf("-------------------------------------------------------------------------------------------------------------------------------------------------\n"); + } + else + { + printf("-------------------------------------------------------------------------------------------------------------------------\n"); + printf("Frame View Bit/pic Filler NVB WP QP QL SnrY SnrU SnrV Time(ms) MET(ms) Frm/Fld I D L0 L1 RDP Ref\n"); + printf("-------------------------------------------------------------------------------------------------------------------------\n"); + } + } + else +#endif + { + if (p_Inp->Distortion[SSIM] == 1) + { + printf("------------------------------------------------------------------------------------------------------------------------------------------\n"); + printf("Frame Bit/pic Filler NVB WP QP QL SnrY SnrU SnrV SsimY SsimU SsimV Time(ms) MET(ms) Frm/Fld I D L0 L1 RDP Ref\n"); + printf("------------------------------------------------------------------------------------------------------------------------------------------\n"); + } + else + { + printf("------------------------------------------------------------------------------------------------------------------\n"); + printf("Frame Bit/pic Filler NVB WP QP QL SnrY SnrU SnrV Time(ms) MET(ms) Frm/Fld I D L0 L1 RDP Ref\n"); + printf("------------------------------------------------------------------------------------------------------------------\n"); + } + } + break; + } +} + +/*! + ************************************************************************ + * \brief + * Report mode distribution of the sequence to log_mode.dat + ************************************************************************ + */ +void report_log_mode(VideoParameters *p_Vid, InputParameters *p_Inp, StatParameters *p_Stats) +{ + FILE *p_stat; //!< status file for the last encoding session + int i; + char name[40]; +#ifndef WIN32 + time_t now; + struct tm *l_time; + char string[1000]; +#else + char timebuf[128]; +#endif + + if ((p_stat = fopen("log_mode.dat", "r")) == 0) // check if file exists + { + if ((p_stat = fopen("log_mode.dat", "a")) == NULL) // append new statistic at the end + { + snprintf(errortext, ET_SIZE, "Error open file %s \n", "log_mode.dat"); + error(errortext, 500); + } + else // Create header for new log file + { + fprintf(p_stat, " ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- \n"); + fprintf(p_stat,"| Encoder statistics. This file is generated during first encoding session, new sessions will be appended |\n"); + fprintf(p_stat, " ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- \n"); + fprintf(p_stat, "| ver | Date | Time | Sequence | QP | I4 | I8 | I16 | IC0 | IC1 | IC2 | IC3 | PI4 | PI8 | PI16 | P0 | P1 | P2 | P3 | P1*4*| P1*8*| P2*4*| P2*8*| P3*4*| P3*8*| P8 | P8:4 | P4*4*| P4*8*| P8:5 | P8:6 | P8:7 | BI4 | BI8 | BI16 | B0 | B1 | B2 | B3 | B0*4*| B0*8*| B1*4*| B1*8*| B2*4*| B2*8*| B3*4*| B3*8*| B8 | B8:0 |B80*4*|B80*8*| B8:4 | B4*4*| B4*8*| B8:5 | B8:6 | B8:7 |\n"); + fprintf(p_stat, " ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- \n"); + } + } + else + { + fclose (p_stat); + if ((p_stat = fopen("log_mode.dat", "a")) == NULL) // File exists, just open for appending + { + snprintf(errortext, ET_SIZE, "Error open file %s \n", "log_mode.dat"); + error(errortext, 500); + } + } + + //report + fprintf(p_stat, "|%4s/%s", VERSION, EXT_VERSION); + +#ifdef WIN32 + _strdate( timebuf ); + fprintf(p_stat, "| %1.5s |", timebuf); + + _strtime( timebuf); + fprintf(p_stat, " % 1.5s |", timebuf); +#else + now = time ((time_t *) NULL); // Get the system time and put it into 'now' as 'calender time' + time (&now); + l_time = localtime (&now); + strftime (string, sizeof string, "%d-%b-%Y", l_time); + fprintf(p_stat, "| %1.5s |", string ); + + strftime (string, sizeof string, "%H:%M:%S", l_time); + fprintf(p_stat, " %1.5s |", string); +#endif + + for (i=0;i<30;i++) + name[i]=p_Inp->input_file1.fname[i + imax(0,(int) (strlen(p_Inp->input_file1.fname)- 30))]; // write last part of path, max 30 chars + + fprintf(p_stat, "%30.30s|", name); + fprintf(p_stat, "%3d |", p_Vid->qp); + + //report modes + //I-Modes + fprintf(p_stat, " %5" FORMAT_OFF_T "|", p_Stats->mode_use[I_SLICE][I4MB ]); + fprintf(p_stat, " %5" FORMAT_OFF_T "|", p_Stats->mode_use[I_SLICE][I8MB ]); + fprintf(p_stat, " %5" FORMAT_OFF_T "|", p_Stats->mode_use[I_SLICE][I16MB]); + + //chroma intra mode + fprintf(p_stat, " %5" FORMAT_OFF_T "|", p_Stats->intra_chroma_mode[0]); + fprintf(p_stat, " %5" FORMAT_OFF_T "|", p_Stats->intra_chroma_mode[1]); + fprintf(p_stat, " %5" FORMAT_OFF_T "|", p_Stats->intra_chroma_mode[2]); + fprintf(p_stat, " %5" FORMAT_OFF_T "|", p_Stats->intra_chroma_mode[3]); + + //P-Modes + fprintf(p_stat, " %5" FORMAT_OFF_T "|", p_Stats->mode_use[P_SLICE][I4MB ]); + fprintf(p_stat, " %5" FORMAT_OFF_T "|", p_Stats->mode_use[P_SLICE][I8MB ]); + fprintf(p_stat, " %5" FORMAT_OFF_T "|", p_Stats->mode_use[P_SLICE][I16MB]); + fprintf(p_stat, " %5" FORMAT_OFF_T "|", p_Stats->mode_use[P_SLICE][0 ]); + + fprintf(p_stat, " %5" FORMAT_OFF_T "|", p_Stats->mode_use[P_SLICE][1 ]); + fprintf(p_stat, " %5" FORMAT_OFF_T "|", p_Stats->mode_use[P_SLICE][2 ]); + fprintf(p_stat, " %5" FORMAT_OFF_T "|", p_Stats->mode_use[P_SLICE][3 ]); + + fprintf(p_stat, " %5" FORMAT_OFF_T "|", p_Stats->mode_use_transform[P_SLICE][1][0]); + fprintf(p_stat, " %5" FORMAT_OFF_T "|", p_Stats->mode_use_transform[P_SLICE][1][1]); + fprintf(p_stat, " %5" FORMAT_OFF_T "|", p_Stats->mode_use_transform[P_SLICE][2][0]); + fprintf(p_stat, " %5" FORMAT_OFF_T "|", p_Stats->mode_use_transform[P_SLICE][2][1]); + fprintf(p_stat, " %5" FORMAT_OFF_T "|", p_Stats->mode_use_transform[P_SLICE][3][0]); + fprintf(p_stat, " %5" FORMAT_OFF_T "|", p_Stats->mode_use_transform[P_SLICE][3][1]); + + fprintf(p_stat, " %5" FORMAT_OFF_T "|", p_Stats->mode_use[P_SLICE][P8x8 ]); + fprintf(p_stat, " %5" FORMAT_OFF_T "|", p_Stats->mode_use[P_SLICE][4 ]); + fprintf(p_stat, " %5" FORMAT_OFF_T "|", p_Stats->mode_use_transform[P_SLICE][4][0]); + fprintf(p_stat, " %5" FORMAT_OFF_T "|", p_Stats->mode_use_transform[P_SLICE][4][1]); + fprintf(p_stat, " %5" FORMAT_OFF_T "|", p_Stats->mode_use[P_SLICE][5 ]); + fprintf(p_stat, " %5" FORMAT_OFF_T "|", p_Stats->mode_use[P_SLICE][6 ]); + fprintf(p_stat, " %5" FORMAT_OFF_T "|", p_Stats->mode_use[P_SLICE][7 ]); + + //B-Modes + fprintf(p_stat, " %5" FORMAT_OFF_T "|", p_Stats->mode_use[B_SLICE][I4MB ]); + fprintf(p_stat, " %5" FORMAT_OFF_T "|", p_Stats->mode_use[B_SLICE][I8MB ]); + fprintf(p_stat, " %5" FORMAT_OFF_T "|", p_Stats->mode_use[B_SLICE][I16MB]); + fprintf(p_stat, " %5" FORMAT_OFF_T "|", p_Stats->mode_use[B_SLICE][0 ]); + fprintf(p_stat, " %5" FORMAT_OFF_T "|", p_Stats->mode_use[B_SLICE][1 ]); + fprintf(p_stat, " %5" FORMAT_OFF_T "|", p_Stats->mode_use[B_SLICE][2 ]); + fprintf(p_stat, " %5" FORMAT_OFF_T "|", p_Stats->mode_use[B_SLICE][3 ]); + fprintf(p_stat, " %5" FORMAT_OFF_T "|", p_Stats->mode_use_transform[B_SLICE][0][0]); + fprintf(p_stat, " %5" FORMAT_OFF_T "|", p_Stats->mode_use_transform[B_SLICE][0][1]); + fprintf(p_stat, " %5" FORMAT_OFF_T "|", p_Stats->mode_use_transform[B_SLICE][1][0]); + fprintf(p_stat, " %5" FORMAT_OFF_T "|", p_Stats->mode_use_transform[B_SLICE][1][1]); + fprintf(p_stat, " %5" FORMAT_OFF_T "|", p_Stats->mode_use_transform[B_SLICE][2][0]); + fprintf(p_stat, " %5" FORMAT_OFF_T "|", p_Stats->mode_use_transform[B_SLICE][2][1]); + fprintf(p_stat, " %5" FORMAT_OFF_T "|", p_Stats->mode_use_transform[B_SLICE][3][0]); + fprintf(p_stat, " %5" FORMAT_OFF_T "|", p_Stats->mode_use_transform[B_SLICE][3][1]); + + fprintf(p_stat, " %5" FORMAT_OFF_T "|", p_Stats->mode_use[B_SLICE][P8x8]); + fprintf(p_stat, " %d|", (p_Stats->b8_mode_0_use [B_SLICE][0]+p_Stats->b8_mode_0_use [B_SLICE][1])); + fprintf(p_stat, " %5d|", p_Stats->b8_mode_0_use [B_SLICE][0]); + fprintf(p_stat, " %5d|", p_Stats->b8_mode_0_use [B_SLICE][1]); + fprintf(p_stat, " %5" FORMAT_OFF_T "|", p_Stats->mode_use[B_SLICE][4 ]); + fprintf(p_stat, " %5" FORMAT_OFF_T "|", p_Stats->mode_use_transform[B_SLICE][4][0]); + fprintf(p_stat, " %5" FORMAT_OFF_T "|", p_Stats->mode_use_transform[B_SLICE][4][1]); + fprintf(p_stat, " %5" FORMAT_OFF_T "|", p_Stats->mode_use[B_SLICE][5 ]); + fprintf(p_stat, " %5" FORMAT_OFF_T "|", p_Stats->mode_use[B_SLICE][6 ]); + fprintf(p_stat, " %5" FORMAT_OFF_T "|", p_Stats->mode_use[B_SLICE][7 ]); + + fprintf(p_stat, "\n"); + fclose(p_stat); +} + + diff --git a/lencod/src/rtp.c b/lencod/src/rtp.c new file mode 100644 index 0000000..4f9402f --- /dev/null +++ b/lencod/src/rtp.c @@ -0,0 +1,324 @@ + +/*! + ***************************************************************************** + * \file rtp.c + * + * \brief + * Functions to handle RTP headers and packets per RFC 3984 + * with restricted functionality. + * + * \author + * Stephan Wenger stewe@cs.tu-berlin.de + *****************************************************************************/ + +#ifdef WIN32 +#include +#else +#include +#endif + +#include "global.h" +#include "rtp.h" +#include "sei.h" + +// A little trick to avoid those horrible #if TRACE all over the source code +#if TRACE +#define SYMTRACESTRING(s) strncpy(sym.tracestring,s,TRACESTRING_SIZE) +#else +#define SYMTRACESTRING(s) // to nothing +#endif + +/*! + ***************************************************************************** + * + * \brief + * ComposeRTPpacket composes the complete RTP packet using the various + * structure members of the RTPpacket_t structure + * + * \return + * 0 in case of success + * negative error code in case of failure + * + * \par Parameters + * Caller is responsible to allocate enough memory for the generated packet + * in parameter->packet. Typically a malloc of 12+paylen bytes is sufficient + * + * \par Side effects + * none + * + * \note + * Function contains assert() tests for debug purposes (consistency checks + * for RTP header fields + * + * \date + * 30 September 2001 + * + * \author + * Stephan Wenger stewe@cs.tu-berlin.de + *****************************************************************************/ + +int ComposeRTPPacket (RTPpacket_t *p) + +{ + unsigned int temp32; + uint16 temp16; + + // Consistency checks through assert, only used for debug purposes + assert (p->v == 2); + assert (p->p == 0); + assert (p->x == 0); + assert (p->cc == 0); // mixer designers need to change this one + assert (p->m == 0 || p->m == 1); + assert (p->pt < 128); + assert (p->payload != NULL); + assert (p->paylen < 65536 - 40); // 2**16 -40 for IP/UDP/RTP header + assert (p->packet != NULL); + + // Compose RTP header, little endian + + p->packet[0] = (byte) + ( ((p->v & 0x03) << 6) + | ((p->p & 0x01) << 5) + | ((p->x & 0x01) << 4) + | ((p->cc & 0x0F) << 0) ); + + p->packet[1] = (byte) + ( ((p->m & 0x01) << 7) + | ((p->pt & 0x7F) << 0) ); + + // sequence number, msb first + temp16 = htons((uint16)p->seq); + memcpy (&p->packet[2], &temp16, 2); // change to shifts for unified byte sex + + //declare a temporary variable to perform network byte order conversion + temp32 = htonl(p->timestamp); + memcpy (&p->packet[4], &temp32, 4); // change to shifts for unified byte sex + + temp32 = htonl(p->ssrc); + memcpy (&p->packet[8], &temp32, 4);// change to shifts for unified byte sex + + // Copy payload + + memcpy (&p->packet[12], p->payload, p->paylen); + p->packlen = p->paylen+12; + return 0; +} + + + +/*! + ***************************************************************************** + * + * \brief + * WriteRTPPacket writes the supplied RTP packet to the output file + * + * \return + * 0 in case of access + * <0 in case of write failure (typically fatal) + * + * \param p + * the RTP packet to be written (after ComposeRTPPacket() ) + * \param f + * output file + * + * \date + * October 23, 2001 + * + * \author + * Stephan Wenger stewe@cs.tu-berlin.de + *****************************************************************************/ + +int WriteRTPPacket (RTPpacket_t *p, FILE *f) + +{ + int intime = -1; + + assert (f != NULL); + assert (p != NULL); + + + if (1 != fwrite (&p->packlen, 4, 1, f)) + return -1; + if (1 != fwrite (&intime, 4, 1, f)) + return -1; + if (1 != fwrite (p->packet, p->packlen, 1, f)) + return -1; + return 0; +} + + + + + +/*! + ***************************************************************************** + * + * \brief + * int RTPWriteNALU write a NALU to the RTP file + * + * \return + * Number of bytes written to output file + * + * \par Side effects + * Packet written, RTPSequenceNumber and RTPTimestamp updated + * + * \date + * December 13, 2002 + * + * \author + * Stephan Wenger stewe@cs.tu-berlin.de + *****************************************************************************/ + + +int WriteRTPNALU (VideoParameters *p_Vid, NALU_t *n, FILE **f_rtp) +{ + RTPpacket_t *p; + + byte first_byte; + + assert ((*f_rtp) != NULL); + assert (n != NULL); + assert (n->len < 65000); + + first_byte = (byte) + (n->forbidden_bit << 7 | + n->nal_reference_idc << 5 | + n->nal_unit_type ); + + // Set RTP structure elements and alloca() memory foor the buffers + if ((p = (RTPpacket_t *) malloc (sizeof (RTPpacket_t))) == NULL) + no_mem_exit ("RTPWriteNALU-1"); + if ((p->packet = (byte *) malloc (MAXRTPPACKETSIZE * sizeof(byte) )) == NULL) + no_mem_exit ("RTPWriteNALU-2"); + if ((p->payload = (byte *) malloc (MAXRTPPACKETSIZE * sizeof(byte) )) == NULL) + no_mem_exit ("RTPWriteNALU-3"); + + p->v=2; + p->p=0; + p->x=0; + p->cc=0; + p->m=(n->startcodeprefix_len==4)&1; // a long startcode of Annex B sets marker bit of RTP + // Not exactly according to the RTP paylaod spec, but + // good enough for now (hopefully). + //! For error resilience work, we need the correct + //! marker bit. Introduce a nalu->marker and set it in + //! terminate_slice()? + p->pt=H264PAYLOADTYPE; + p->seq = p_Vid->CurrentRTPSequenceNumber++; + p->timestamp = p_Vid->CurrentRTPTimestamp; + p->ssrc=H264SSRC; + p->paylen = 1 + n->len; + + p->payload[0] = first_byte; + memcpy (p->payload+1, n->buf, n->len); + + // Generate complete RTP packet + if (ComposeRTPPacket (p) < 0) + { + printf ("Cannot compose RTP packet, exit\n"); + exit (-1); + } + if (WriteRTPPacket (p, *f_rtp) < 0) + { + printf ("Cannot write %d bytes of RTP packet to outfile, exit\n", p->packlen); + exit (-1); + } + free (p->packet); + free (p->payload); + free (p); + return (n->len * 8); +} + + +/*! + ******************************************************************************************** + * \brief + * RTPUpdateTimestamp: patches the RTP timestamp depending on the TR + * + * \param p_Vid + * Image parameters for current picture encoding + * \param tr + * tr: TRof the following NALUs + * + * \return + * none. + * + ******************************************************************************************** +*/ + + +void RTPUpdateTimestamp (VideoParameters *p_Vid, int tr) +{ + int delta; + static int oldtr = -1; + + if (oldtr == -1) // First invocation + { + p_Vid->CurrentRTPTimestamp = 0; //! This is a violation of the security req. of + //! RTP (random timestamp), but easier to debug + oldtr = 0; + return; + } + + /*! The following code assumes a wrap around of TR at 256, and + needs to be changed as soon as this is no more true. + + The support for B frames is a bit tricky, because it is not easy to distinguish + between a natural wrap-around of the tr, and the intentional going back of the + tr because of a B frame. It is solved here by a heuristic means: It is assumed that + B frames are never "older" than 10 tr ticks. Everything higher than 10 is considered + a wrap around. + */ + + delta = tr - oldtr; + + if (delta < -10) // wrap-around + delta+=256; + + p_Vid->CurrentRTPTimestamp += delta * RTP_TR_TIMESTAMP_MULT; + oldtr = tr; +} + + +/*! + ******************************************************************************************** + * \brief + * Opens the output file for the RTP packet stream + * + * \param p_Vid + * Image parameters for current picture encoding + * \param Filename + * The filename of the file to be opened + * + * \return + * none. Function terminates the program in case of an error + * + ******************************************************************************************** +*/ + +void OpenRTPFile (char *Filename, FILE **f_rtp) +{ + if (((*f_rtp) = fopen (Filename, "wb")) == NULL) + { + printf ("Fatal: cannot open bitstream file '%s', exit (-1)\n", Filename); + exit (-1); + } +} + + +/*! + ******************************************************************************************** + * \brief + * Closes the output file for the RTP packet stream + * + * \return + * none. Function terminates the program in case of an error + * + ******************************************************************************************** +*/ + +void CloseRTPFile (FILE *f_rtp) +{ + fclose(f_rtp); +} + diff --git a/lencod/src/sei.c b/lencod/src/sei.c new file mode 100644 index 0000000..e1a2fff --- /dev/null +++ b/lencod/src/sei.c @@ -0,0 +1,3065 @@ + +/*! + ************************************************************************ + * \file + * sei.c + * \brief + * implementation of SEI related functions + * \author(s) + * - Dong Tian + * - Athanasios Leontaris + * + ************************************************************************ + */ + +#include "global.h" +#include "memalloc.h" +#include "rtp.h" +#include "mbuffer.h" +#include "sei.h" +#include "vlc.h" +#include "header.h" + +static void InitRandomAccess (SEIParameters *p_SEI); +static void CloseRandomAccess (SEIParameters *p_SEI); +static void InitToneMapping (SEIParameters *p_SEI, InputParameters *p_Inp); +static void CloseToneMapping (SEIParameters *p_SEI); +static void CloseBufferingPeriod (SEIParameters *p_SEI); +static void InitPicTiming (SEIParameters *p_SEI); +static void ClosePicTiming (SEIParameters *p_SEI); +static void InitDRPMRepetition (SEIParameters *p_SEI); +static void CloseDRPMRepetition (SEIParameters *p_SEI); +static void FinalizeDRPMRepetition (VideoParameters *p_Vid); +static void InitSparePicture (SEIParameters *p_SEI); +static void CloseSparePicture (SEIParameters *p_SEI); +static void FinalizeSpareMBMap (VideoParameters *p_Vid); +static void InitSubseqChar (VideoParameters *p_Vid); +static void ClearSubseqCharPayload (SEIParameters *p_SEI); +static void CloseSubseqChar (SEIParameters *p_SEI); +static void InitSubseqLayerInfo (SEIParameters *p_SEI); +static void InitPanScanRectInfo (SEIParameters *p_SEI); +static void ClosePanScanRectInfo (SEIParameters *p_SEI); +static void InitUser_data_unregistered (SEIParameters *p_SEI); +static void InitUser_data_registered_itu_t_t35(SEIParameters *p_SEI); +static void ClearUser_data_unregistered(SEIParameters *p_SEI); +static void CloseUser_data_unregistered(SEIParameters *p_SEI); +static void ClearUser_data_registered_itu_t_t35(SEIParameters *p_SEI); +static void CloseUser_data_registered_itu_t_t35(SEIParameters *p_SEI); +static void InitPostFilterHints (SEIParameters *p_SEI); +static void ClearPostFilterHints (SEIParameters *p_SEI); +static void ClosePostFilterHints (SEIParameters *p_SEI); +static void InitFramePackingArrangement(VideoParameters *p_Vid); +static void CloseFramePackingArrangement(SEIParameters *p_SEI); + +void init_sei(SEIParameters *p_SEI) +{ + p_SEI->seiHasTemporal_reference=FALSE; + p_SEI->seiHasClock_timestamp=FALSE; + p_SEI->seiHasPanscan_rect=FALSE; + p_SEI->seiHasHrd_picture=FALSE; + p_SEI->seiHasFiller_payload=FALSE; + p_SEI->seiHasUser_data_registered_itu_t_t35=FALSE; + p_SEI->seiHasUser_data_unregistered=FALSE; + p_SEI->seiHasRecoveryPoint_info=FALSE; + p_SEI->seiHasRef_pic_buffer_management_repetition=FALSE; + p_SEI->seiHasSpare_picture=FALSE; + p_SEI->seiHasBufferingPeriod_info=FALSE; + p_SEI->seiHasPicTiming_info=FALSE; + p_SEI->seiHasSceneInformation=FALSE; + p_SEI->seiHasSubseq_information=FALSE; + p_SEI->seiHasSubseq_layer_characteristics=FALSE; + p_SEI->seiHasSubseq_characteristics=FALSE; + p_SEI->seiHasTone_mapping=FALSE; + p_SEI->seiHasPostFilterHints_info=FALSE; + p_SEI->seiHasDRPMRepetition_info=FALSE; + p_SEI->seiHasSparePicture = FALSE; + p_SEI->seiHasSubseqChar = FALSE; + p_SEI->seiHasSubseqInfo = FALSE; + p_SEI->seiHasSubseqLayerInfo = FALSE; + p_SEI->seiHasPanScanRectInfo = FALSE; +} + +/* + ************************************************************************ + * \basic functions on supplemental enhancement information + * \brief + * The implementations are based on FCD + ************************************************************************ + */ +void InitSEIMessages(VideoParameters *p_Vid, InputParameters *p_Inp) +{ + SEIParameters *p_SEI = p_Vid->p_SEI; + int i; + for (i=0; i<2; i++) + { + p_SEI->sei_message[i].data = malloc(MAXRTPPAYLOADLEN); + if( p_SEI->sei_message[i].data == NULL ) no_mem_exit("InitSEIMessages: sei_message[i].data"); + p_SEI->sei_message[i].subPacketType = SEI_PACKET_TYPE; + clear_sei_message(p_SEI, i); + } + + // init sei messages + p_SEI->seiSparePicturePayload.data = NULL; + InitSparePicture(p_SEI); + if (p_Inp->NumFramesInELSubSeq != 0) + { + InitSubseqLayerInfo(p_SEI); + InitSubseqChar(p_Vid); + } + InitSceneInformation(p_SEI); + // init panscanrect sei message + InitPanScanRectInfo(p_SEI); + // init user_data_unregistered + InitUser_data_unregistered(p_SEI); + // init user_data_unregistered + InitUser_data_registered_itu_t_t35(p_SEI); + // init user_RandomAccess + InitRandomAccess(p_SEI); + // Init tone_mapping + InitToneMapping(p_SEI, p_Inp); + // init post_filter_hints + InitPostFilterHints(p_SEI); + // init BufferingPeriod + InitBufferingPeriod(p_Vid); + // init PicTiming + InitPicTiming(p_SEI); + // init DRPM Repetition + InitDRPMRepetition(p_SEI); + // init Frame Packing Arrangement + InitFramePackingArrangement(p_Vid); +} + +void CloseSEIMessages(VideoParameters *p_Vid, InputParameters *p_Inp) +{ + SEIParameters *p_SEI = p_Vid->p_SEI; + int i; + + if (p_Inp->NumFramesInELSubSeq != 0) + CloseSubseqLayerInfo(); + + CloseSubseqChar(p_SEI); + CloseSparePicture(p_SEI); + CloseSceneInformation(p_SEI); + ClosePanScanRectInfo(p_SEI); + CloseUser_data_unregistered(p_SEI); + CloseUser_data_registered_itu_t_t35(p_SEI); + CloseRandomAccess(p_SEI); + CloseToneMapping(p_SEI); + ClosePostFilterHints(p_SEI); + CloseBufferingPeriod(p_SEI); + ClosePicTiming(p_SEI); + CloseDRPMRepetition(p_SEI); + CloseFramePackingArrangement(p_SEI); + + for (i=0; isei_message[i].data ) + free( p_SEI->sei_message[i].data ); + p_SEI->sei_message[i].data = NULL; + } +} + +Boolean HaveAggregationSEI(VideoParameters *p_Vid) +{ + SEIParameters *p_SEI = p_Vid->p_SEI; + if (p_SEI->sei_message[AGGREGATION_SEI].available && p_Vid->type != B_SLICE) + return TRUE; + if (p_SEI->seiHasSubseqInfo) + return TRUE; + if (p_SEI->seiHasSubseqLayerInfo && p_Vid->number == 0) + return TRUE; + if (p_SEI->seiHasSubseqChar) + return TRUE; + if (p_SEI->seiHasSceneInformation) + return TRUE; + if (p_SEI->seiHasPanScanRectInfo) + return TRUE; + if (p_SEI->seiHasUser_data_unregistered_info) + return TRUE; + if (p_SEI->seiHasUser_data_registered_itu_t_t35_info) + return TRUE; + if (p_SEI->seiHasRecoveryPoint_info) + return TRUE; + if (p_SEI->seiHasTone_mapping) + return TRUE; + if (p_SEI->seiHasPostFilterHints_info) + return TRUE; + if (p_SEI->seiHasBufferingPeriod_info) + return TRUE; + if (p_SEI->seiHasPicTiming_info) + return TRUE; + if (p_SEI->seiHasDRPMRepetition_info) + return TRUE; + + return FALSE; +// return p_Inp->SparePictureOption && ( seiHasSpare_picture || seiHasSubseq_information || +// seiHasSubseq_layer_characteristics || seiHasSubseq_characteristics ); +} + +/*! + ************************************************************************ + * \brief + * write one sei payload to the sei message + * \param p_SEI + * SEI message + * \param id + * 0, if this is the normal packet\n + * 1, if this is a aggregation packet + * \param payload + * a pointer that point to the sei payload. Note that the bitstream + * should have be byte aligned already. + * \param payload_size + * the size of the sei payload + * \param payload_type + * the type of the sei payload + * \par Output + * the content of the sei message (sei_message[id]) is updated. + ************************************************************************ + */ +static void write_sei_message(SEIParameters *p_SEI, int id, byte* payload, int payload_size, int payload_type) +{ + int offset, type, size; + assert(payload_type >= 0 && payload_type < SEI_MAX_ELEMENTS); + + type = payload_type; + size = payload_size; + offset = p_SEI->sei_message[id].payloadSize; + + while ( type > 254 ) + { + p_SEI->sei_message[id].data[offset++] = 0xFF; + type = type - 255; + } + p_SEI->sei_message[id].data[offset++] = (byte) type; + + while ( size > 254 ) + { + p_SEI->sei_message[id].data[offset++] = 0xFF; + size = size - 255; + } + p_SEI->sei_message[id].data[offset++] = (byte) size; + + memcpy(p_SEI->sei_message[id].data + offset, payload, payload_size); + offset += payload_size; + + p_SEI->sei_message[id].payloadSize = offset; +} + +/*! + ************************************************************************ + * \brief + * write rbsp_trailing_bits to the sei message + * \param p_SEI + * SEI message + * \param id + * 0, if this is the normal packet \n + * 1, if this is a aggregation packet + * \par Output + * the content of the sei message is updated and ready for packetisation + ************************************************************************ + */ +static void finalize_sei_message(SEIParameters *p_SEI, int id) +{ + int offset = p_SEI->sei_message[id].payloadSize; + + p_SEI->sei_message[id].data[offset] = 0x80; + p_SEI->sei_message[id].payloadSize++; + + p_SEI->sei_message[id].available = TRUE; +} + +/*! + ************************************************************************ + * \brief + * empty the sei message buffer + * \param p_SEI + * the SEI message to be cleared + * \param id + * 0, if this is the normal packet \n + * 1, if this is a aggregation packet + * \par Output + * the content of the sei message is cleared and ready for storing new + * messages + ************************************************************************ + */ +void clear_sei_message(SEIParameters *p_SEI, int id) +{ + memset( p_SEI->sei_message[id].data, 0, MAXRTPPAYLOADLEN); + p_SEI->sei_message[id].payloadSize = 0; + p_SEI->sei_message[id].available = FALSE; +} + +/*! + ************************************************************************ + * \brief + * copy the bits from one bitstream buffer to another one + * \param dest + * pointer to the dest bitstream buffer + * \param source + * pointer to the source bitstream buffer + * \par Output + * the content of the dest bitstream is changed. + ************************************************************************ + */ +void AppendTmpbits2Buf( Bitstream* dest, Bitstream* source ) +{ + int i, j; + byte mask; + int bits_in_last_byte; + + // copy the first bytes in source buffer + for (i=0; ibyte_pos; i++) + { + mask = 0x80; + for (j=0; j<8; j++) + { + dest->byte_buf <<= 1; + if (source->streamBuffer[i] & mask) + dest->byte_buf |= 1; + dest->bits_to_go--; + mask >>= 1; + if (dest->bits_to_go==0) + { + dest->bits_to_go = 8; + dest->streamBuffer[dest->byte_pos++]=dest->byte_buf; + dest->byte_buf = 0; + } + } + } + // copy the last byte, there are still (8-source->bits_to_go) bits in the source buffer + bits_in_last_byte = 8-source->bits_to_go; + if ( bits_in_last_byte > 0 ) + { + mask = (byte) (1 << (bits_in_last_byte-1)); + for (j=0; jbyte_buf <<= 1; + if (source->byte_buf & mask) + dest->byte_buf |= 1; + dest->bits_to_go--; + mask >>= 1; + if (dest->bits_to_go==0) + { + dest->bits_to_go = 8; + dest->streamBuffer[dest->byte_pos++]=dest->byte_buf; + dest->byte_buf = 0; + } + } + } +} + +/* + **++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + * \functions on spare pictures + * \brief + * implementation of Spare Pictures related functions based on + * JVT-D100 + * \author + * Dong Tian + **++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + */ + + +/*! + ************************************************************************ + * \brief + * Init the global variables for spare picture information + ************************************************************************ + */ +static void InitSparePicture(SEIParameters *p_SEI) +{ + if ( p_SEI->seiSparePicturePayload.data != NULL ) + CloseSparePicture(p_SEI); + + p_SEI->seiSparePicturePayload.data = malloc( sizeof(Bitstream) ); + if ( p_SEI->seiSparePicturePayload.data == NULL ) no_mem_exit("InitSparePicture: seiSparePicturePayload.data"); + p_SEI->seiSparePicturePayload.data->streamBuffer = malloc(MAXRTPPAYLOADLEN); + if ( p_SEI->seiSparePicturePayload.data->streamBuffer == NULL ) no_mem_exit("InitSparePicture: seiSparePicturePayload.data->streamBuffer"); + memset( p_SEI->seiSparePicturePayload.data->streamBuffer, 0, MAXRTPPAYLOADLEN); + p_SEI->seiSparePicturePayload.num_spare_pics = 0; + p_SEI->seiSparePicturePayload.target_frame_num = 0; + + p_SEI->seiSparePicturePayload.data->bits_to_go = 8; + p_SEI->seiSparePicturePayload.data->byte_pos = 0; + p_SEI->seiSparePicturePayload.data->byte_buf = 0; +} + +/*! + ************************************************************************ + * \brief + * Close the global variables for spare picture information + ************************************************************************ + */ +static void CloseSparePicture(SEIParameters *p_SEI) +{ + if (p_SEI->seiSparePicturePayload.data->streamBuffer) + free(p_SEI->seiSparePicturePayload.data->streamBuffer); + p_SEI->seiSparePicturePayload.data->streamBuffer = NULL; + if (p_SEI->seiSparePicturePayload.data) + free(p_SEI->seiSparePicturePayload.data); + p_SEI->seiSparePicturePayload.data = NULL; + p_SEI->seiSparePicturePayload.num_spare_pics = 0; + p_SEI->seiSparePicturePayload.target_frame_num = 0; +} + +/*! + ************************************************************************ + * \brief + * Calculate the spare picture info, save the result in map_sp + * then compose the spare picture information. + * \par Output + * the spare picture payload is available in *seiSparePicturePayload* + * the syntax elements in the loop (see FCD), excluding the two elements + * at the beginning. + ************************************************************************ + */ +void CalculateSparePicture() +{ + /* + int i, j, tmp, i0, j0, m; + byte **map_sp; + int delta_spare_frame_num; + Bitstream *tmpBitstream; + + int num_of_mb=(p_Vid->height >> 4) * (p_Vid->width >> 4); + int threshold1 = 16*16*p_Inp->SPDetectionThreshold; + int threshold2 = num_of_mb * p_Inp->SPPercentageThreshold / 100; + int ref_area_indicator; + int CandidateSpareFrameNum, SpareFrameNum; + int possible_spare_pic_num; + + // define it for debug purpose + #define WRITE_MAP_IMAGE + +#ifdef WRITE_MAP_IMAGE + byte **y; + int k; + FILE* fp; + static int first = 1; + char map_file_name[255]="map.yuv"; +#endif + + // basic check + if (fb->picbuf_short[0]->used==0 || fb->picbuf_short[1]->used==0) + { +#ifdef WRITE_MAP_IMAGE + fp = fopen( map_file_name, "wb" ); + assert( fp != NULL ); + // write the map image + for (i=0; i < p_Vid->height; i++) + for (j=0; j < p_Vid->width; j++) + fputc(0, fp); + + for (k=0; k < 2; k++) + for (i=0; i < p_Vid->height >> 1; i++) + for (j=0; j < p_Vid->width >> 1; j++) + fputc(128, fp); + fclose( fp ); +#endif + seiHasSparePicture = FALSE; + return; + } + seiHasSparePicture = TRUE; + + // set the global bitstream memory. + InitSparePicture(); + seiSparePicturePayload.target_frame_num = p_Vid->number % MAX_FN; + // init the local bitstream memory. + tmpBitstream = malloc(sizeof(Bitstream)); + if ( tmpBitstream == NULL ) no_mem_exit("CalculateSparePicture: tmpBitstream"); + tmpBitstream->streamBuffer = malloc(MAXRTPPAYLOADLEN); + if ( tmpBitstream->streamBuffer == NULL ) no_mem_exit("CalculateSparePicture: tmpBitstream->streamBuffer"); + memset( tmpBitstream->streamBuffer, 0, MAXRTPPAYLOADLEN); + +#ifdef WRITE_MAP_IMAGE + if ( first ) + { + fp = fopen( map_file_name, "wb" ); + first = 0; + } + else + fp = fopen( map_file_name, "ab" ); + get_mem2D(&y, p_Vid->height, p_Vid->width); +#endif + get_mem2D(&map_sp, p_Vid->height >> 4, p_Vid->width >> 4); + + if (fb->picbuf_short[2]->used!=0) possible_spare_pic_num = 2; + else possible_spare_pic_num = 1; + // loop over the spare pictures + for (m=0; mbits_to_go = 8; + tmpBitstream->byte_pos = 0; + tmpBitstream->byte_buf = 0; + memset( tmpBitstream->streamBuffer, 0, MAXRTPPAYLOADLEN); + + // set delta_spare_frame_num + // the order of the following lines cannot be changed. + if (m==0) + CandidateSpareFrameNum = seiSparePicturePayload.target_frame_num - 1; // TargetFrameNum - 1; + else + CandidateSpareFrameNum = SpareFrameNum - 1; + if ( CandidateSpareFrameNum < 0 ) CandidateSpareFrameNum = MAX_FN - 1; + SpareFrameNum = fb->picbuf_short[m+1]->frame_num_256; + delta_spare_frame_num = CandidateSpareFrameNum - SpareFrameNum; + assert( delta_spare_frame_num == 0 ); + + // calculate the spare macroblock map of one spare picture + // the results are stored into map_sp[][] + for (i=0; i < p_Vid->height >> 4; i++) + for (j=0; j < p_Vid->width >> 4; j++) + { + tmp = 0; + for (i0=0; i0<16; i0++) + for (j0=0; j0<16; j0++) + tmp+=iabs(fb->picbuf_short[m+1]->Refbuf11[(i*16+i0)*p_Vid->width+j*16+j0]- + fb->picbuf_short[0]->Refbuf11[(i*16+i0)*p_Vid->width+j*16+j0]); + tmp = (tmp<=threshold1? 255 : 0); + map_sp[i][j] = (tmp==0? 1 : 0); +#ifdef WRITE_MAP_IMAGE +// if (m==0) + { + for (i0=0; i0<16; i0++) + for (j0=0; j0<16; j0++) + y[i*16+i0][j*16+j0]=tmp; + } +#endif + } + + // based on map_sp[][], compose the spare picture information + // and write the spare picture information to a temp bitstream + tmp = 0; + for (i=0; i < p_Vid->height >> 4; i++) + for (j=0; j < p_Vid->width >> 4; j++) + if (map_sp[i][j]==0) tmp++; + if ( tmp > threshold2 ) + ref_area_indicator = 0; + else if ( !CompressSpareMBMap(p_Vid, map_sp, tmpBitstream) ) + ref_area_indicator = 1; + else + ref_area_indicator = 2; + +// printf( "ref_area_indicator = %d\n", ref_area_indicator ); + +#ifdef WRITE_MAP_IMAGE + // write the map to a file +// if (m==0) + { + // write the map image + for (i=0; i < p_Vid->height; i++) + for (j=0; j < p_Vid->width; j++) + { + if ( ref_area_indicator == 0 ) fputc(255, fp); + else fputc(y[i][j], fp); + } + + for (k=0; k < 2; k++) + for (i=0; i < p_Vid->height >> 1; i++) + for (j=0; j < p_Vid->width >> 1; j++) + fputc(128, fp); + } +#endif + + // Finnally, write the current spare picture information to + // the global variable: seiSparePicturePayload + ComposeSparePictureMessage(p_SEI, delta_spare_frame_num, ref_area_indicator, tmpBitstream); + seiSparePicturePayload.num_spare_pics++; + } // END for (m=0; m<2; m++) + + free_mem2D( map_sp ); + free( tmpBitstream->streamBuffer ); + free( tmpBitstream ); + +#ifdef WRITE_MAP_IMAGE + free_mem2D( y ); + fclose( fp ); +#undef WRITE_MAP_IMAGE +#endif + */ +} + +/*! + ************************************************************************ + * \brief + * compose the spare picture information. + * \param p_SEI + * SEI message + * \param delta_spare_frame_num + * see FCD + * \param ref_area_indicator + * Indicate how to represent the spare mb map + * \param tmpBitstream + * pointer to a buffer to save the payload + * \par Output + * bitstream: the composed spare picture payload are + * ready to put into the sei_message. + ************************************************************************ + */ +void ComposeSparePictureMessage(SEIParameters *p_SEI, int delta_spare_frame_num, int ref_area_indicator, Bitstream *tmpBitstream) +{ + Bitstream *bitstream = p_SEI->seiSparePicturePayload.data; + SyntaxElement sym; + + sym.type = SE_HEADER; + sym.mapping = ue_linfo; + + sym.value1 = delta_spare_frame_num; + writeSyntaxElement2Buf_UVLC(&sym, bitstream); + sym.value1 = ref_area_indicator; + writeSyntaxElement2Buf_UVLC(&sym, bitstream); + + AppendTmpbits2Buf( bitstream, tmpBitstream ); +} + +/*! + ************************************************************************ + * \brief + * test if the compressed spare mb map will occupy less mem and + * fill the payload buffer. + * \param p_Vid + * Image parameters for current picture coding + * \param map_sp + * in which the spare picture information are stored. + * \param bitstream + * pointer to a buffer to save the payload + * \return + * TRUE: If it is compressed version, \n + * FALSE: If it is not compressed. + ************************************************************************ + */ +Boolean CompressSpareMBMap(VideoParameters *p_Vid, unsigned char **map_sp, Bitstream *bitstream) +{ + int j, k; + int noc, bit0, bitc; + SyntaxElement sym; + int x, y, left, right, bottom, top, directx, directy; + + // this is the size of the uncompressed mb map: + int size_uncompressed = (p_Vid->height >> 4) * (p_Vid->width >> 4); + int size_compressed = 0; + Boolean ret; + + // initialization + sym.type = SE_HEADER; + sym.mapping = ue_linfo; + noc = 0; + bit0 = 0; + bitc = bit0; + + // compress the map, the result goes to the temporal bitstream buffer + x = ( (p_Vid->width >> 4) - 1 ) / 2; + y = ( (p_Vid->height >> 4) - 1 ) / 2; + left = right = x; + top = bottom = y; + directx = 0; + directy = 1; + for (j=0; jheight >> 4; j++) + for (k=0; kwidth >> 4; k++) + { + // check current mb + if ( map_sp[y][x] == bitc ) noc++; + else + { + sym.value1 = noc; + size_compressed += writeSyntaxElement2Buf_UVLC(&sym, bitstream); // the return value indicate the num of bits written + noc=0; + } + // go to the next mb: + if ( directx == -1 && directy == 0 ) + { + if (x > left) x--; + else if (x == 0) + { + y = bottom + 1; + bottom++; + directx = 1; + directy = 0; + } + else if (x == left) + { + x--; + left--; + directx = 0; + directy = 1; + } + } + else if ( directx == 1 && directy == 0 ) + { + if (x < right) x++; + else if (x == (p_Vid->width >> 4) - 1) + { + y = top - 1; + top--; + directx = -1; + directy = 0; + } + else if (x == right) + { + x++; + right++; + directx = 0; + directy = -1; + } + } + else if ( directx == 0 && directy == -1 ) + { + if ( y > top) y--; + else if (y == 0) + { + x = left - 1; + left--; + directx = 0; + directy = 1; + } + else if (y == top) + { + y--; + top--; + directx = -1; + directy = 0; + } + } + else if ( directx == 0 && directy == 1 ) + { + if (y < bottom) y++; + else if (y == (p_Vid->height >> 4) - 1) + { + x = right+1; + right++; + directx = 0; + directy = -1; + } + else if (y == bottom) + { + y++; + bottom++; + directx = 1; + directy = 0; + } + } + } + if (noc!=0) + { + sym.value1 = noc; + size_compressed += writeSyntaxElement2Buf_UVLC(&sym, bitstream); + } + + ret = (size_compressedbyte_buf = 0; + bitstream->bits_to_go = 8; + bitstream->byte_pos = 0; + for (j=0; jheight >> 4; j++) + { + for (k=0; kwidth >> 4; k++) + { + bitstream->byte_buf <<= 1; + if (map_sp[j][k]) bitstream->byte_buf |= 1; + bitstream->bits_to_go--; + if (bitstream->bits_to_go==0) + { + bitstream->bits_to_go = 8; + bitstream->streamBuffer[bitstream->byte_pos++]=bitstream->byte_buf; + bitstream->byte_buf = 0; + } + } + } + } + + return ret; +} + +/*! + ************************************************************************ + * \brief + * Finalize the spare picture SEI payload. + * The spare picture paylaod will be ready for encapsulation, and it + * should be called before current picture packetized. + * \par Input + * seiSparePicturePayload.data: points to the payload starting from + * delta_spare_frame_num. (See FCD) + * \par Output + * seiSparePicturePayload.data is updated, pointing to the whole spare + * picture information: spare_picture( PayloadSize ) (See FCD) + * Make sure it is byte aligned. + ************************************************************************ + */ +static void FinalizeSpareMBMap(VideoParameters *p_Vid) +{ + SEIParameters *p_SEI = p_Vid->p_SEI; + int CurrFrameNum = p_Vid->number % MAX_FN; + int delta_frame_num; + SyntaxElement sym; + Bitstream *dest, *source; + + sym.type = SE_HEADER; + sym.mapping = ue_linfo; + + source = p_SEI->seiSparePicturePayload.data; + dest = malloc(sizeof(Bitstream)); + if ( dest == NULL ) no_mem_exit("FinalizeSpareMBMap: dest"); + dest->streamBuffer = malloc(MAXRTPPAYLOADLEN); + if ( dest->streamBuffer == NULL ) no_mem_exit("FinalizeSpareMBMap: dest->streamBuffer"); + dest->bits_to_go = 8; + dest->byte_pos = 0; + dest->byte_buf = 0; + memset( dest->streamBuffer, 0, MAXRTPPAYLOADLEN); + + // delta_frame_num + delta_frame_num = CurrFrameNum - p_SEI->seiSparePicturePayload.target_frame_num; + if ( delta_frame_num < 0 ) delta_frame_num += MAX_FN; + sym.value1 = delta_frame_num; + writeSyntaxElement2Buf_UVLC(&sym, dest); + + // num_spare_pics_minus1 + sym.value1 = p_SEI->seiSparePicturePayload.num_spare_pics - 1; + writeSyntaxElement2Buf_UVLC(&sym, dest); + + // copy the other bits + AppendTmpbits2Buf( dest, source); + + // make sure the payload is byte aligned, stuff bits are 10..0 + if ( dest->bits_to_go != 8 ) + { + (dest->byte_buf) <<= 1; + dest->byte_buf |= 1; + dest->bits_to_go--; + if ( dest->bits_to_go != 0 ) (dest->byte_buf) <<= (dest->bits_to_go); + dest->bits_to_go = 8; + dest->streamBuffer[dest->byte_pos++]=dest->byte_buf; + dest->byte_buf = 0; + } + p_SEI->seiSparePicturePayload.payloadSize = dest->byte_pos; + + // the payload is ready now + p_SEI->seiSparePicturePayload.data = dest; + free( source->streamBuffer ); + free( source ); +} + +/* + **++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + * \functions on subseq information sei messages + * \brief + * JVT-D098 + * \author + * Dong Tian + **++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + */ + + +/*! + ************************************************************************ + * \brief + * init subseqence info + ************************************************************************ + */ +void InitSubseqInfo(SEIParameters *p_SEI, int currLayer) +{ + static uint16 id = 0; + + p_SEI->seiHasSubseqInfo = TRUE; + p_SEI->seiSubseqInfo[currLayer].subseq_layer_num = currLayer; + p_SEI->seiSubseqInfo[currLayer].subseq_id = id++; + p_SEI->seiSubseqInfo[currLayer].last_picture_flag = 0; + p_SEI->seiSubseqInfo[currLayer].stored_frame_cnt = (unsigned int) -1; + p_SEI->seiSubseqInfo[currLayer].payloadSize = 0; + + p_SEI->seiSubseqInfo[currLayer].data = malloc( sizeof(Bitstream) ); + if ( p_SEI->seiSubseqInfo[currLayer].data == NULL ) no_mem_exit("InitSubseqInfo: p_SEI->seiSubseqInfo[currLayer].data"); + p_SEI->seiSubseqInfo[currLayer].data->streamBuffer = malloc( MAXRTPPAYLOADLEN ); + if ( p_SEI->seiSubseqInfo[currLayer].data->streamBuffer == NULL ) no_mem_exit("InitSubseqInfo: p_SEI->seiSubseqInfo[currLayer].data->streamBuffer"); + p_SEI->seiSubseqInfo[currLayer].data->bits_to_go = 8; + p_SEI->seiSubseqInfo[currLayer].data->byte_pos = 0; + p_SEI->seiSubseqInfo[currLayer].data->byte_buf = 0; + memset( p_SEI->seiSubseqInfo[currLayer].data->streamBuffer, 0, MAXRTPPAYLOADLEN ); +} + +/*! + ************************************************************************ + * \brief + * update subsequence info + ************************************************************************ + */ +void UpdateSubseqInfo(VideoParameters *p_Vid, InputParameters *p_Inp, int currLayer) +{ + SEIParameters *p_SEI = p_Vid->p_SEI; + if (p_Vid->type != B_SLICE) + { + p_SEI->seiSubseqInfo[currLayer].stored_frame_cnt ++; + p_SEI->seiSubseqInfo[currLayer].stored_frame_cnt = p_SEI->seiSubseqInfo[currLayer].stored_frame_cnt % MAX_FN; + } + + if ( currLayer == 0 ) + { + if ( p_Vid->number == p_Inp->no_frames - 1 ) + p_SEI->seiSubseqInfo[currLayer].last_picture_flag = 1; + else + p_SEI->seiSubseqInfo[currLayer].last_picture_flag = 0; + } + if ( currLayer == 1 ) + { + if ( (((p_Vid->curr_frm_idx - p_Vid->last_idr_code_order) % (p_Inp->NumFramesInELSubSeq + 1) == 0) && (p_Inp->NumberBFrames != 0) && ((p_Vid->curr_frm_idx - p_Vid->last_idr_code_order) > 0)) || // there are B frames + (((p_Vid->curr_frm_idx - p_Vid->last_idr_code_order) % (p_Inp->NumFramesInELSubSeq + 1) == p_Inp->NumFramesInELSubSeq) && (p_Inp->NumberBFrames==0)) // there are no B frames + ) + p_SEI->seiSubseqInfo[currLayer].last_picture_flag = 1; + else + p_SEI->seiSubseqInfo[currLayer].last_picture_flag = 0; + } +} + +/*! + ************************************************************************ + * \brief + * Finalize subseqence info + ************************************************************************ + */ +static void FinalizeSubseqInfo(SEIParameters *p_SEI, int currLayer) +{ + SyntaxElement sym; + Bitstream *dest = p_SEI->seiSubseqInfo[currLayer].data; + + sym.type = SE_HEADER; + sym.mapping = ue_linfo; + + sym.value1 = p_SEI->seiSubseqInfo[currLayer].subseq_layer_num; + writeSyntaxElement2Buf_UVLC(&sym, dest); + sym.value1 = p_SEI->seiSubseqInfo[currLayer].subseq_id; + writeSyntaxElement2Buf_UVLC(&sym, dest); + sym.bitpattern = p_SEI->seiSubseqInfo[currLayer].last_picture_flag; + sym.len = 1; + writeSyntaxElement2Buf_Fixed(&sym, dest); + sym.value1 = p_SEI->seiSubseqInfo[currLayer].stored_frame_cnt; + writeSyntaxElement2Buf_UVLC(&sym, dest); + + // make sure the payload is byte aligned, stuff bits are 10..0 + if ( dest->bits_to_go != 8 ) + { + (dest->byte_buf) <<= 1; + dest->byte_buf |= 1; + dest->bits_to_go--; + if ( dest->bits_to_go != 0 ) (dest->byte_buf) <<= (dest->bits_to_go); + dest->bits_to_go = 8; + dest->streamBuffer[dest->byte_pos++]=dest->byte_buf; + dest->byte_buf = 0; + } + p_SEI->seiSubseqInfo[currLayer].payloadSize = dest->byte_pos; + +// printf("layer %d, last picture %d, stored_cnt %d\n", currLayer, p_SEI->seiSubseqInfo[currLayer].last_picture_flag, p_SEI->seiSubseqInfo[currLayer].stored_frame_cnt ); +} + +/*! + ************************************************************************ + * \brief + * Clear the payload buffer + ************************************************************************ + */ +static void ClearSubseqInfoPayload(SEIParameters *p_SEI, int currLayer) +{ + p_SEI->seiSubseqInfo[currLayer].data->bits_to_go = 8; + p_SEI->seiSubseqInfo[currLayer].data->byte_pos = 0; + p_SEI->seiSubseqInfo[currLayer].data->byte_buf = 0; + memset( p_SEI->seiSubseqInfo[currLayer].data->streamBuffer, 0, MAXRTPPAYLOADLEN ); + p_SEI->seiSubseqInfo[currLayer].payloadSize = 0; +} + +/*! + ************************************************************************ + * \brief + * Close the global variables for spare picture information + ************************************************************************ + */ +void CloseSubseqInfo(SEIParameters *p_SEI, int currLayer) +{ + p_SEI->seiSubseqInfo[currLayer].stored_frame_cnt = (unsigned int) -1; + p_SEI->seiSubseqInfo[currLayer].payloadSize = 0; + + free( p_SEI->seiSubseqInfo[currLayer].data->streamBuffer ); + free( p_SEI->seiSubseqInfo[currLayer].data ); +} + +/* + **++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + * \functions on subseq layer characteristic sei messages + * \brief + * JVT-D098 + * \author + * Dong Tian + **++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + */ + +/*! + ************************************************************************ + * \brief + * Init the global variables for spare picture information + ************************************************************************ + */ +static void InitSubseqLayerInfo(SEIParameters *p_SEI) +{ + int i; + p_SEI->seiHasSubseqLayerInfo = TRUE; + p_SEI->seiSubseqLayerInfo.layer_number = 0; + for (i=0; iseiSubseqLayerInfo.bit_rate[i] = 0; + p_SEI->seiSubseqLayerInfo.frame_rate[i] = 0; + p_SEI->seiSubseqLayerInfo.layer_number++; + } +} + +/*! + ************************************************************************ + * \brief + * + ************************************************************************ + */ +void CloseSubseqLayerInfo() +{ +} + +/*! + ************************************************************************ + * \brief + * Write the data to buffer, which is byte aligned + ************************************************************************ + */ +static void FinalizeSubseqLayerInfo(SEIParameters *p_SEI) +{ + int i, pos; + pos = 0; + p_SEI->seiSubseqLayerInfo.payloadSize = 0; + for (i=0; iseiSubseqLayerInfo.layer_number; i++) + { + *((uint16*)&(p_SEI->seiSubseqLayerInfo.data[pos])) = p_SEI->seiSubseqLayerInfo.bit_rate[i]; + pos += 2; + *((uint16*)&(p_SEI->seiSubseqLayerInfo.data[pos])) = p_SEI->seiSubseqLayerInfo.frame_rate[i]; + pos += 2; + p_SEI->seiSubseqLayerInfo.payloadSize += 4; + } +} + +/* + **++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + * \functions on subseq characteristic sei messages + * \brief + * JVT-D098 + * \author + * Dong Tian + **++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + */ + +void InitSubseqChar(VideoParameters *p_Vid) +{ + SEIParameters *p_SEI = p_Vid->p_SEI; + p_SEI->seiSubseqChar.data = malloc( sizeof(Bitstream) ); + if( p_SEI->seiSubseqChar.data == NULL ) no_mem_exit("InitSubseqChar: p_SEI->seiSubseqChar.data"); + p_SEI->seiSubseqChar.data->streamBuffer = malloc(MAXRTPPAYLOADLEN); + if( p_SEI->seiSubseqChar.data->streamBuffer == NULL ) no_mem_exit("InitSubseqChar: p_SEI->seiSubseqChar.data->streamBuffer"); + ClearSubseqCharPayload(p_SEI); + + p_SEI->seiSubseqChar.subseq_layer_num = p_Vid->layer; + p_SEI->seiSubseqChar.subseq_id = p_SEI->seiSubseqInfo[p_Vid->layer].subseq_id; + p_SEI->seiSubseqChar.duration_flag = 0; + p_SEI->seiSubseqChar.average_rate_flag = 0; + p_SEI->seiSubseqChar.num_referenced_subseqs = 0; +} + +static void ClearSubseqCharPayload(SEIParameters *p_SEI) +{ + memset( p_SEI->seiSubseqChar.data->streamBuffer, 0, MAXRTPPAYLOADLEN); + p_SEI->seiSubseqChar.data->bits_to_go = 8; + p_SEI->seiSubseqChar.data->byte_pos = 0; + p_SEI->seiSubseqChar.data->byte_buf = 0; + p_SEI->seiSubseqChar.payloadSize = 0; + + p_SEI->seiHasSubseqChar = FALSE; +} + +void UpdateSubseqChar(VideoParameters *p_Vid) +{ + SEIParameters *p_SEI = p_Vid->p_SEI; + + p_SEI->seiSubseqChar.subseq_layer_num = p_Vid->layer; + p_SEI->seiSubseqChar.subseq_id = p_SEI->seiSubseqInfo[p_Vid->layer].subseq_id; + p_SEI->seiSubseqChar.duration_flag = 0; + p_SEI->seiSubseqChar.average_rate_flag = 0; + p_SEI->seiSubseqChar.average_bit_rate = 100; + p_SEI->seiSubseqChar.average_frame_rate = 30; + p_SEI->seiSubseqChar.num_referenced_subseqs = 0; + p_SEI->seiSubseqChar.ref_subseq_layer_num[0] = 1; + p_SEI->seiSubseqChar.ref_subseq_id[0] = 2; + p_SEI->seiSubseqChar.ref_subseq_layer_num[1] = 3; + p_SEI->seiSubseqChar.ref_subseq_id[1] = 4; + + p_SEI->seiHasSubseqChar = TRUE; +} + +static void FinalizeSubseqChar(SEIParameters *p_SEI) +{ + int i; + SyntaxElement sym; + Bitstream *dest = p_SEI->seiSubseqChar.data; + + sym.type = SE_HEADER; + sym.mapping = ue_linfo; + + sym.value1 = p_SEI->seiSubseqChar.subseq_layer_num; + writeSyntaxElement2Buf_UVLC(&sym, dest); + sym.value1 = p_SEI->seiSubseqChar.subseq_id; + writeSyntaxElement2Buf_UVLC(&sym, dest); + sym.bitpattern = p_SEI->seiSubseqChar.duration_flag; + sym.len = 1; + writeSyntaxElement2Buf_Fixed(&sym, dest); + if ( p_SEI->seiSubseqChar.duration_flag ) + { + sym.bitpattern = p_SEI->seiSubseqChar.subseq_duration; + sym.len = 32; + writeSyntaxElement2Buf_Fixed(&sym, dest); + } + sym.bitpattern = p_SEI->seiSubseqChar.average_rate_flag; + sym.len = 1; + writeSyntaxElement2Buf_Fixed(&sym, dest); + if ( p_SEI->seiSubseqChar.average_rate_flag ) + { + sym.bitpattern = p_SEI->seiSubseqChar.average_bit_rate; + sym.len = 16; + writeSyntaxElement2Buf_Fixed(&sym, dest); + sym.bitpattern = p_SEI->seiSubseqChar.average_frame_rate; + sym.len = 16; + writeSyntaxElement2Buf_Fixed(&sym, dest); + } + sym.value1 = p_SEI->seiSubseqChar.num_referenced_subseqs; + writeSyntaxElement2Buf_UVLC(&sym, dest); + for (i=0; iseiSubseqChar.num_referenced_subseqs; i++) + { + sym.value1 = p_SEI->seiSubseqChar.ref_subseq_layer_num[i]; + writeSyntaxElement2Buf_UVLC(&sym, dest); + sym.value1 = p_SEI->seiSubseqChar.ref_subseq_id[i]; + writeSyntaxElement2Buf_UVLC(&sym, dest); + } + + // make sure the payload is byte aligned, stuff bits are 10..0 + if ( dest->bits_to_go != 8 ) + { + (dest->byte_buf) <<= 1; + dest->byte_buf |= 1; + dest->bits_to_go--; + if ( dest->bits_to_go != 0 ) (dest->byte_buf) <<= (dest->bits_to_go); + dest->bits_to_go = 8; + dest->streamBuffer[dest->byte_pos++]=dest->byte_buf; + dest->byte_buf = 0; + } + p_SEI->seiSubseqChar.payloadSize = dest->byte_pos; +} + +static void CloseSubseqChar(SEIParameters *p_SEI) +{ + if (p_SEI->seiSubseqChar.data) + { + free(p_SEI->seiSubseqChar.data->streamBuffer); + free(p_SEI->seiSubseqChar.data); + } + p_SEI->seiSubseqChar.data = NULL; +} + + +// JVT-D099 +/* + **++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + * \functions on scene information SEI message + * \brief + * JVT-D099 + * \author + * Ye-Kui Wang + **++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + */ +void InitSceneInformation(SEIParameters *p_SEI) +{ + p_SEI->seiHasSceneInformation = TRUE; + + p_SEI->seiSceneInformation.scene_id = 0; + p_SEI->seiSceneInformation.scene_transition_type = 0; + p_SEI->seiSceneInformation.second_scene_id = -1; + + p_SEI->seiSceneInformation.data = malloc( sizeof(Bitstream) ); + if(p_SEI-> seiSceneInformation.data == NULL ) no_mem_exit("InitSceneInformation: seiSceneInformation.data"); + p_SEI->seiSceneInformation.data->streamBuffer = malloc( MAXRTPPAYLOADLEN ); + if( p_SEI->seiSceneInformation.data->streamBuffer == NULL ) no_mem_exit("InitSceneInformation: seiSceneInformation.data->streamBuffer"); + p_SEI->seiSceneInformation.data->bits_to_go = 8; + p_SEI->seiSceneInformation.data->byte_pos = 0; + p_SEI->seiSceneInformation.data->byte_buf = 0; + memset( p_SEI->seiSceneInformation.data->streamBuffer, 0, MAXRTPPAYLOADLEN ); +} + +void CloseSceneInformation(SEIParameters *p_SEI) +{ + if (p_SEI->seiSceneInformation.data) + { + free(p_SEI->seiSceneInformation.data->streamBuffer); + free(p_SEI->seiSceneInformation.data); + } + p_SEI->seiSceneInformation.data = NULL; +} + +void FinalizeSceneInformation(SEIParameters *p_SEI) +{ + SyntaxElement sym; + Bitstream *dest = p_SEI->seiSceneInformation.data; + + sym.type = SE_HEADER; + sym.mapping = ue_linfo; + + sym.bitpattern = p_SEI->seiSceneInformation.scene_id; + sym.len = 8; + writeSyntaxElement2Buf_Fixed(&sym, dest); + + sym.value1 = p_SEI->seiSceneInformation.scene_transition_type; + writeSyntaxElement2Buf_UVLC(&sym, dest); + + if(p_SEI->seiSceneInformation.scene_transition_type > 3) + { + sym.bitpattern = p_SEI->seiSceneInformation.second_scene_id; + sym.len = 8; + writeSyntaxElement2Buf_Fixed(&sym, dest); + } + + // make sure the payload is byte aligned, stuff bits are 10..0 + if ( dest->bits_to_go != 8 ) + { + (dest->byte_buf) <<= 1; + dest->byte_buf |= 1; + dest->bits_to_go--; + if ( dest->bits_to_go != 0 ) (dest->byte_buf) <<= (dest->bits_to_go); + dest->bits_to_go = 8; + dest->streamBuffer[dest->byte_pos++]=dest->byte_buf; + dest->byte_buf = 0; + } + p_SEI->seiSceneInformation.payloadSize = dest->byte_pos; +} + +// HasSceneInformation: To include a scene information SEI into the next slice/DP, +// set HasSceneInformation to be TRUE when calling this function. Otherwise, +// set HasSceneInformation to be FALSE. +void UpdateSceneInformation(SEIParameters *p_SEI, Boolean HasSceneInformation, int sceneID, int sceneTransType, int secondSceneID) +{ + p_SEI->seiHasSceneInformation = HasSceneInformation; + + assert (sceneID < 256); + p_SEI->seiSceneInformation.scene_id = sceneID; + + assert (sceneTransType <= 6 ); + p_SEI->seiSceneInformation.scene_transition_type = sceneTransType; + + if(sceneTransType > 3) + { + assert (secondSceneID < 256); + p_SEI->seiSceneInformation.second_scene_id = secondSceneID; + } +} +// End JVT-D099 + + +/* + **++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + * \functions on Pan Scan messages + * \brief + * Based on FCD + * \author + * Shankar Regunathan + **++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + */ + + +static void InitPanScanRectInfo(SEIParameters *p_SEI) +{ + p_SEI->seiPanScanRectInfo.data = malloc( sizeof(Bitstream) ); + if( p_SEI->seiPanScanRectInfo.data == NULL ) no_mem_exit("InitPanScanRectInfo: p_SEI->seiPanScanRectInfo.data"); + p_SEI->seiPanScanRectInfo.data->streamBuffer = malloc(MAXRTPPAYLOADLEN); + if( p_SEI->seiPanScanRectInfo.data->streamBuffer == NULL ) no_mem_exit("InitPanScanRectInfo: p_SEI->seiPanScanRectInfo.data->streamBuffer"); + ClearPanScanRectInfoPayload(p_SEI); + + p_SEI->seiPanScanRectInfo.pan_scan_rect_id = 0; + p_SEI->seiPanScanRectInfo.pan_scan_rect_cancel_flag = 0; + p_SEI->seiPanScanRectInfo.pan_scan_cnt_minus1 = 0; + memset( p_SEI->seiPanScanRectInfo.pan_scan_rect_left_offset, 0, 3 * sizeof ( int ) ); + memset( p_SEI->seiPanScanRectInfo.pan_scan_rect_right_offset, 0, 3 * sizeof ( int ) ); + memset( p_SEI->seiPanScanRectInfo.pan_scan_rect_top_offset, 0, 3 * sizeof ( int ) ); + memset( p_SEI->seiPanScanRectInfo.pan_scan_rect_bottom_offset, 0, 3 * sizeof ( int ) ); + p_SEI->seiPanScanRectInfo.pac_scan_rect_repetition_period = 0; +} + + +void ClearPanScanRectInfoPayload(SEIParameters *p_SEI) +{ + memset( p_SEI->seiPanScanRectInfo.data->streamBuffer, 0, MAXRTPPAYLOADLEN); + p_SEI->seiPanScanRectInfo.data->bits_to_go = 8; + p_SEI->seiPanScanRectInfo.data->byte_pos = 0; + p_SEI->seiPanScanRectInfo.data->byte_buf = 0; + p_SEI->seiPanScanRectInfo.payloadSize = 0; + + p_SEI->seiHasPanScanRectInfo = FALSE; +} + +void UpdatePanScanRectInfo( VideoParameters *p_Vid ) +{ + SEIParameters *p_SEI = p_Vid->p_SEI; + + { + p_SEI->seiPanScanRectInfo.pan_scan_rect_id = 0; + p_SEI->seiPanScanRectInfo.pan_scan_rect_cancel_flag = 0; + p_SEI->seiPanScanRectInfo.pan_scan_cnt_minus1 = 0; + p_SEI->seiPanScanRectInfo.pan_scan_rect_left_offset[0] = 0; + p_SEI->seiPanScanRectInfo.pan_scan_rect_right_offset[0] = 160; + p_SEI->seiPanScanRectInfo.pan_scan_rect_top_offset[0] = 0; + p_SEI->seiPanScanRectInfo.pan_scan_rect_bottom_offset[0] = 160; + p_SEI->seiPanScanRectInfo.pac_scan_rect_repetition_period = 1; + } + + p_SEI->seiHasPanScanRectInfo = TRUE; +} + +void FinalizePanScanRectInfo(SEIParameters *p_SEI) +{ + int i; + Bitstream *bitstream = p_SEI->seiPanScanRectInfo.data; + + write_ue_v( "SEI: pan_scan_rect_id", p_SEI->seiPanScanRectInfo.pan_scan_rect_id, bitstream ); + write_u_1( "SEI: pan_scan_rect_cancel_flag", p_SEI->seiPanScanRectInfo.pan_scan_rect_cancel_flag, bitstream ); + if ( !(p_SEI->seiPanScanRectInfo.pan_scan_rect_cancel_flag) ) + { + write_ue_v( "SEI: pan_scan_cnt_minus1", p_SEI->seiPanScanRectInfo.pan_scan_cnt_minus1, bitstream ); + for ( i = 0; i <= p_SEI->seiPanScanRectInfo.pan_scan_cnt_minus1; i++ ) + { + write_se_v( "SEI: pan_scan_rect_left_offset[X]", p_SEI->seiPanScanRectInfo.pan_scan_rect_left_offset[i], bitstream ); + write_se_v( "SEI: pan_scan_rect_right_offset[X]", p_SEI->seiPanScanRectInfo.pan_scan_rect_right_offset[i], bitstream ); + write_se_v( "SEI: pan_scan_rect_top_offset[X]", p_SEI->seiPanScanRectInfo.pan_scan_rect_top_offset[i], bitstream ); + write_se_v( "SEI: pan_scan_rect_bottom_offset[X]", p_SEI->seiPanScanRectInfo.pan_scan_rect_bottom_offset[i], bitstream ); + } + write_ue_v( "SEI: pac_scan_rect_repetition_period", p_SEI->seiPanScanRectInfo.pac_scan_rect_repetition_period, bitstream ); + } + + // make sure the payload is byte aligned, stuff bits are 10..0 + if ( bitstream->bits_to_go != 8 ) + { + (bitstream->byte_buf) <<= 1; + bitstream->byte_buf |= 1; + bitstream->bits_to_go--; + if ( bitstream->bits_to_go != 0 ) + (bitstream->byte_buf) <<= (bitstream->bits_to_go); + bitstream->bits_to_go = 8; + bitstream->streamBuffer[bitstream->byte_pos++]=bitstream->byte_buf; + bitstream->byte_buf = 0; + } + p_SEI->seiPanScanRectInfo.payloadSize = bitstream->byte_pos; +} + + +void ClosePanScanRectInfo(SEIParameters *p_SEI) +{ + if (p_SEI->seiPanScanRectInfo.data) + { + free(p_SEI->seiPanScanRectInfo.data->streamBuffer); + free(p_SEI->seiPanScanRectInfo.data); + } + p_SEI->seiPanScanRectInfo.data = NULL; +} + +/* + **++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + * \functions on arbitrary (unregistered) data + * \brief + * Based on FCD + * \author + * Shankar Regunathan + **++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + */ +static void InitUser_data_unregistered(SEIParameters *p_SEI) +{ + + p_SEI->seiUser_data_unregistered.data = malloc( sizeof(Bitstream) ); + if( p_SEI->seiUser_data_unregistered.data == NULL ) no_mem_exit("InitUser_data_unregistered: p_SEI->seiUser_data_unregistered.data"); + p_SEI->seiUser_data_unregistered.data->streamBuffer = malloc(MAXRTPPAYLOADLEN); + if( p_SEI->seiUser_data_unregistered.data->streamBuffer == NULL ) no_mem_exit("InitUser_data_unregistered: p_SEI->seiUser_data_unregistered.data->streamBuffer"); + p_SEI->seiUser_data_unregistered.byte = malloc(MAXRTPPAYLOADLEN); + if( p_SEI->seiUser_data_unregistered.byte == NULL ) no_mem_exit("InitUser_data_unregistered: p_SEI->seiUser_data_unregistered.byte"); + ClearUser_data_unregistered(p_SEI); + +} + + +static void ClearUser_data_unregistered(SEIParameters *p_SEI) +{ + memset( p_SEI->seiUser_data_unregistered.data->streamBuffer, 0, MAXRTPPAYLOADLEN); + p_SEI->seiUser_data_unregistered.data->bits_to_go = 8; + p_SEI->seiUser_data_unregistered.data->byte_pos = 0; + p_SEI->seiUser_data_unregistered.data->byte_buf = 0; + p_SEI->seiUser_data_unregistered.payloadSize = 0; + + memset( p_SEI->seiUser_data_unregistered.byte, 0, MAXRTPPAYLOADLEN); + p_SEI->seiUser_data_unregistered.total_byte = 0; + + p_SEI->seiHasUser_data_unregistered_info = FALSE; +} + +void UpdateUser_data_unregistered(SEIParameters *p_SEI) +{ + int i, temp_data; + int total_byte; + + + total_byte = 7; + for(i = 0; i < total_byte; i++) + { + temp_data = i * 4; + p_SEI->seiUser_data_unregistered.byte[i] = (char) iClip3(0, 255, temp_data); + } + p_SEI->seiUser_data_unregistered.total_byte = total_byte; +} + +static void FinalizeUser_data_unregistered(SEIParameters *p_SEI) +{ + int i; + SyntaxElement sym; + Bitstream *dest = p_SEI->seiUser_data_unregistered.data; + + sym.type = SE_HEADER; + sym.mapping = ue_linfo; + +// #define PRINT_USER_DATA_UNREGISTERED_INFO + for( i = 0; i < p_SEI->seiUser_data_unregistered.total_byte; i++) + { + sym.bitpattern = p_SEI->seiUser_data_unregistered.byte[i]; + sym.len = 8; // b (8) + writeSyntaxElement2Buf_Fixed(&sym, dest); +#ifdef PRINT_USER_DATA_UNREGISTERED_INFO + printf("Unreg data payload_byte = %d\n", p_SEI->seiUser_data_unregistered.byte[i]); +#endif + } +#ifdef PRINT_USER_DATA_UNREGISTERED_INFO +#undef PRINT_USER_DATA_UNREGISTERED_INFO +#endif + // make sure the payload is byte aligned, stuff bits are 10..0 + if ( dest->bits_to_go != 8 ) + { + (dest->byte_buf) <<= 1; + dest->byte_buf |= 1; + dest->bits_to_go--; + if ( dest->bits_to_go != 0 ) (dest->byte_buf) <<= (dest->bits_to_go); + dest->bits_to_go = 8; + dest->streamBuffer[dest->byte_pos++]=dest->byte_buf; + dest->byte_buf = 0; + } + p_SEI->seiUser_data_unregistered.payloadSize = dest->byte_pos; +} + +static void CloseUser_data_unregistered(SEIParameters *p_SEI) +{ + if (p_SEI->seiUser_data_unregistered.data) + { + free(p_SEI->seiUser_data_unregistered.data->streamBuffer); + free(p_SEI->seiUser_data_unregistered.data); + } + p_SEI->seiUser_data_unregistered.data = NULL; + if(p_SEI->seiUser_data_unregistered.byte) + { + free(p_SEI->seiUser_data_unregistered.byte); + } +} + + +/* + **++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + * \functions on registered ITU_T_T35 user data + * \brief + * Based on FCD + * \author + * Shankar Regunathan + **++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + */ +void InitUser_data_registered_itu_t_t35(SEIParameters *p_SEI) +{ + + p_SEI->seiUser_data_registered_itu_t_t35.data = malloc( sizeof(Bitstream) ); + if( p_SEI->seiUser_data_registered_itu_t_t35.data == NULL ) no_mem_exit("InitUser_data_unregistered: p_SEI->seiUser_data_registered_itu_t_t35.data"); + p_SEI->seiUser_data_registered_itu_t_t35.data->streamBuffer = malloc(MAXRTPPAYLOADLEN); + if( p_SEI->seiUser_data_registered_itu_t_t35.data->streamBuffer == NULL ) no_mem_exit("InitUser_data_unregistered: p_SEI->seiUser_data_registered_itu_t_t35.data->streamBuffer"); + p_SEI->seiUser_data_registered_itu_t_t35.byte = malloc(MAXRTPPAYLOADLEN); + if( p_SEI->seiUser_data_registered_itu_t_t35.data == NULL ) no_mem_exit("InitUser_data_unregistered: p_SEI->seiUser_data_registered_itu_t_t35.byte"); + ClearUser_data_registered_itu_t_t35(p_SEI); + +} + + +static void ClearUser_data_registered_itu_t_t35(SEIParameters *p_SEI) +{ + memset( p_SEI->seiUser_data_registered_itu_t_t35.data->streamBuffer, 0, MAXRTPPAYLOADLEN); + p_SEI->seiUser_data_registered_itu_t_t35.data->bits_to_go = 8; + p_SEI->seiUser_data_registered_itu_t_t35.data->byte_pos = 0; + p_SEI->seiUser_data_registered_itu_t_t35.data->byte_buf = 0; + p_SEI->seiUser_data_registered_itu_t_t35.payloadSize = 0; + + memset( p_SEI->seiUser_data_registered_itu_t_t35.byte, 0, MAXRTPPAYLOADLEN); + p_SEI->seiUser_data_registered_itu_t_t35.total_byte = 0; + p_SEI->seiUser_data_registered_itu_t_t35.itu_t_t35_country_code = 0; + p_SEI->seiUser_data_registered_itu_t_t35.itu_t_t35_country_code_extension_byte = 0; + + p_SEI->seiHasUser_data_registered_itu_t_t35_info = FALSE; +} + +void UpdateUser_data_registered_itu_t_t35(SEIParameters *p_SEI) +{ + int i, temp_data; + int total_byte; + int country_code = 82; // Country_code for India + + if(country_code < 0xFF) + { + p_SEI->seiUser_data_registered_itu_t_t35.itu_t_t35_country_code = country_code; + } + else + { + p_SEI->seiUser_data_registered_itu_t_t35.itu_t_t35_country_code = 0xFF; + p_SEI->seiUser_data_registered_itu_t_t35.itu_t_t35_country_code_extension_byte = country_code - 0xFF; + } + + total_byte = 7; + for(i = 0; i < total_byte; i++) + { + temp_data = i * 3; + p_SEI->seiUser_data_registered_itu_t_t35.byte[i] = (char) iClip3(0, 255, temp_data); + } + p_SEI->seiUser_data_registered_itu_t_t35.total_byte = total_byte; +} + +static void FinalizeUser_data_registered_itu_t_t35(SEIParameters *p_SEI) +{ + int i; + SyntaxElement sym; + Bitstream *dest = p_SEI->seiUser_data_registered_itu_t_t35.data; + + sym.type = SE_HEADER; + sym.mapping = ue_linfo; + + sym.bitpattern = p_SEI->seiUser_data_registered_itu_t_t35.itu_t_t35_country_code; + sym.len = 8; + writeSyntaxElement2Buf_Fixed(&sym, dest); + +// #define PRINT_USER_DATA_REGISTERED_ITU_T_T35_INFO +#ifdef PRINT_USER_DATA_REGISTERED_ITU_T_T35_INFO + printf(" ITU_T_T35_COUNTRTY_CODE %d \n", p_SEI->seiUser_data_registered_itu_t_t35.itu_t_t35_country_code); +#endif + + if(p_SEI->seiUser_data_registered_itu_t_t35.itu_t_t35_country_code == 0xFF) + { + sym.bitpattern = p_SEI->seiUser_data_registered_itu_t_t35.itu_t_t35_country_code_extension_byte; + sym.len = 8; + writeSyntaxElement2Buf_Fixed(&sym, dest); +#ifdef PRINT_USER_DATA_REGISTERED_ITU_T_T35_INFO + printf(" ITU_T_T35_COUNTRTY_CODE_EXTENSION_BYTE %d \n", p_SEI->seiUser_data_registered_itu_t_t35.itu_t_t35_country_code_extension_byte); +#endif + } + + for( i = 0; i < p_SEI->seiUser_data_registered_itu_t_t35.total_byte; i++) + { + sym.bitpattern = p_SEI->seiUser_data_registered_itu_t_t35.byte[i]; + sym.len = 8; // b (8) + writeSyntaxElement2Buf_Fixed(&sym, dest); +#ifdef PRINT_USER_DATA_REGISTERED_ITU_T_T35_INFO + printf("itu_t_t35 payload_byte = %d\n", p_SEI->seiUser_data_registered_itu_t_t35.byte[i]); +#endif + } +#ifdef PRINT_USER_DATA_REGISTERED_ITU_T_T35_INFO +#undef PRINT_USER_DATA_REGISTERED_ITU_T_T35_INFO +#endif + // make sure the payload is byte aligned, stuff bits are 10..0 + if ( dest->bits_to_go != 8 ) + { + (dest->byte_buf) <<= 1; + dest->byte_buf |= 1; + dest->bits_to_go--; + if ( dest->bits_to_go != 0 ) (dest->byte_buf) <<= (dest->bits_to_go); + dest->bits_to_go = 8; + dest->streamBuffer[dest->byte_pos++]=dest->byte_buf; + dest->byte_buf = 0; + } + p_SEI->seiUser_data_registered_itu_t_t35.payloadSize = dest->byte_pos; +} + +void CloseUser_data_registered_itu_t_t35(SEIParameters *p_SEI) +{ + if (p_SEI->seiUser_data_registered_itu_t_t35.data) + { + free(p_SEI->seiUser_data_registered_itu_t_t35.data->streamBuffer); + free(p_SEI->seiUser_data_registered_itu_t_t35.data); + } + p_SEI->seiUser_data_registered_itu_t_t35.data = NULL; + if(p_SEI->seiUser_data_registered_itu_t_t35.byte) + { + free(p_SEI->seiUser_data_registered_itu_t_t35.byte); + } +} + +/* + **++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + * \functions on random access message + * \brief + * Based on FCD + * \author + * Shankar Regunathan + **++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + */ +static void InitRandomAccess(SEIParameters *p_SEI) +{ + p_SEI->seiRecoveryPoint.data = malloc( sizeof(Bitstream) ); + if( p_SEI->seiRecoveryPoint.data == NULL ) no_mem_exit("InitRandomAccess: seiRandomAccess.data"); + p_SEI->seiRecoveryPoint.data->streamBuffer = malloc(MAXRTPPAYLOADLEN); + if( p_SEI->seiRecoveryPoint.data->streamBuffer == NULL ) no_mem_exit("InitRandomAccess: seiRandomAccess.data->streamBuffer"); + ClearRandomAccess(p_SEI); +} + + +void ClearRandomAccess(SEIParameters *p_SEI) +{ + memset( p_SEI->seiRecoveryPoint.data->streamBuffer, 0, MAXRTPPAYLOADLEN); + p_SEI->seiRecoveryPoint.data->bits_to_go = 8; + p_SEI->seiRecoveryPoint.data->byte_pos = 0; + p_SEI->seiRecoveryPoint.data->byte_buf = 0; + p_SEI->seiRecoveryPoint.payloadSize = 0; + + p_SEI->seiRecoveryPoint.recovery_frame_cnt = 0; + p_SEI->seiRecoveryPoint.broken_link_flag = 0; + p_SEI->seiRecoveryPoint.exact_match_flag = 0; + p_SEI->seiRecoveryPoint.changing_slice_group_idc = 0; + + p_SEI->seiHasRecoveryPoint_info = FALSE; +} + +void UpdateRandomAccess(VideoParameters *p_Vid) +{ + SEIParameters *p_SEI = p_Vid->p_SEI; + if(p_Vid->type == I_SLICE) + { + p_SEI->seiRecoveryPoint.recovery_frame_cnt = 0; + p_SEI->seiRecoveryPoint.exact_match_flag = 1; + p_SEI->seiRecoveryPoint.broken_link_flag = 0; + p_SEI->seiRecoveryPoint.changing_slice_group_idc = 0; + p_SEI->seiHasRecoveryPoint_info = TRUE; + } + else + { + p_SEI->seiHasRecoveryPoint_info = FALSE; + } +} + +static void FinalizeRandomAccess(SEIParameters *p_SEI) +{ + Bitstream *bitstream = p_SEI->seiRecoveryPoint.data; + + write_ue_v( "SEI: recovery_frame_cnt", p_SEI->seiRecoveryPoint.recovery_frame_cnt, bitstream); + write_u_1 ( "SEI: exact_match_flag", p_SEI->seiRecoveryPoint.exact_match_flag, bitstream); + write_u_1 ( "SEI: broken_link_flag", p_SEI->seiRecoveryPoint.broken_link_flag, bitstream); + write_u_v (2, "SEI: changing_slice_group_idc", p_SEI->seiRecoveryPoint.changing_slice_group_idc, bitstream); + + +// #define PRINT_RECOVERY_POINT +#ifdef PRINT_RECOVERY_POINT + printf(" recovery_frame_cnt %d \n", p_SEI->seiRecoveryPoint.recovery_frame_cnt); + printf(" exact_match_flag %d \n", p_SEI->seiRecoveryPoint.exact_match_flag); + printf(" broken_link_flag %d \n", p_SEI->seiRecoveryPoint.broken_link_flag); + printf(" changing_slice_group_idc %d \n", p_SEI->seiRecoveryPoint.changing_slice_group_idc); + printf(" %d %d \n", bitstream->byte_pos, bitstream->bits_to_go); + +#undef PRINT_RECOVERY_POINT +#endif + // make sure the payload is byte aligned, stuff bits are 10..0 + if ( bitstream->bits_to_go != 8 ) + { + (bitstream->byte_buf) <<= 1; + bitstream->byte_buf |= 1; + bitstream->bits_to_go--; + if ( bitstream->bits_to_go != 0 ) + (bitstream->byte_buf) <<= (bitstream->bits_to_go); + bitstream->bits_to_go = 8; + bitstream->streamBuffer[bitstream->byte_pos++]=bitstream->byte_buf; + bitstream->byte_buf = 0; + } + p_SEI->seiRecoveryPoint.payloadSize = bitstream->byte_pos; +} + +void CloseRandomAccess(SEIParameters *p_SEI) +{ + if (p_SEI->seiRecoveryPoint.data) + { + free(p_SEI->seiRecoveryPoint.data->streamBuffer); + free(p_SEI->seiRecoveryPoint.data); + } + p_SEI->seiRecoveryPoint.data = NULL; +} + +/* + **++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + * \functions on HDR tone-mapping messages + * \brief + * Based on JVT-T060 + * \author + * Jane Zhao, sharp labs of america + **++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + */ +static int ParseToneMappingConfigFile(SEIParameters *p_SEI, InputParameters *p_Inp, ToneMappingSEI *pSeiToneMapping) +{ + int i; + int ret; + FILE* fp; + char buf[1024]; + unsigned int tmp; + + printf ("Parsing Tone mapping cfg file %s ..........\n\n", p_Inp->ToneMappingFile); + if ((fp = fopen(p_Inp->ToneMappingFile, "r")) == NULL) + { + fprintf(stdout, "Tone mapping config file %s is not found, disable tone mapping SEI\n", p_Inp->ToneMappingFile); + p_SEI->seiHasTone_mapping=FALSE; + + return 1; + } + + //read the tone mapping config file + while (fscanf(fp, "%s", buf)!=EOF) + { + ret = 1; + if (strcmp(buf, "tone_map_id")==0) + { + ret = fscanf(fp, " = %ud\n", &(pSeiToneMapping->tone_map_id)); + } + else if (strcmp(buf, "tone_map_cancel_flag")==0) + { + ret = fscanf(fp, " = %ud\n", &tmp); + pSeiToneMapping->tone_map_cancel_flag = (unsigned char) (tmp ? 1 : 0); + } + else if (strcmp(buf, "tone_map_repetition_period")==0) + { + ret = fscanf(fp, " = %ud\n", &(pSeiToneMapping->tone_map_repetition_period)); + } + else if (strcmp(buf, "coded_data_bit_depth")==0) + { + ret = fscanf(fp, " = %ud\n", &tmp); + pSeiToneMapping->coded_data_bit_depth = (unsigned char) tmp; + } + else if (strcmp(buf, "sei_bit_depth")==0) + { + ret = fscanf(fp, " = %ud\n", &tmp); + pSeiToneMapping->sei_bit_depth = (unsigned char) tmp; + } + else if (strcmp(buf, "model_id")==0) + { + ret = fscanf(fp, " = %ud\n", &(pSeiToneMapping->model_id)); + } + //else if (model_id ==0) + else if (strcmp(buf, "min_value")==0) + { + ret = fscanf(fp, " = %d\n", &(pSeiToneMapping->min_value)); + } + else if (strcmp(buf, "max_value")==0) + { + ret = fscanf(fp, " = %d\n", &(pSeiToneMapping->max_value)); + } + //(model_id == 1) + else if (strcmp(buf, "sigmoid_midpoint")==0) + { + ret = fscanf(fp, " = %d\n", &(pSeiToneMapping->sigmoid_midpoint)); + } + else if (strcmp(buf, "sigmoid_width")==0) + { + ret = fscanf(fp, " = %d\n", &(pSeiToneMapping->sigmoid_width)); + } + // (model_id == 2) + else if (strcmp(buf, "start_of_coded_interval")==0) + { + int max_output_num = 1<<(pSeiToneMapping->sei_bit_depth); + ret = fscanf(fp, " = "); + if (ret!=0) + { + error ("ParseToneMappingConfigFile: error parsing tone mapping config file",500); + } + for (i=0; i < max_output_num; i++) + { + ret = fscanf(fp, "%d\n", &(pSeiToneMapping->start_of_coded_interval[i])); + if (ret!=1) + { + error ("ParseToneMappingConfigFile: error parsing tone mapping config file",500); + } + } + } + //(model_id == 3) + else if (strcmp(buf, "num_pivots")==0) + { + ret = fscanf(fp, " = %d\n", &(pSeiToneMapping->num_pivots)); + } + + else if (strcmp(buf, "coded_pivot_value")==0) + { + ret = fscanf(fp, " = "); + if (ret!=0) + { + error ("ParseToneMappingConfigFile: error parsing tone mapping config file",500); + } + for (i=0; i < pSeiToneMapping->num_pivots; i++) + { + ret = fscanf(fp, "%d\n", &(pSeiToneMapping->coded_pivot_value[i])); + if (ret!=1) + { + error ("ParseToneMappingConfigFile: error parsing tone mapping config file",500); + } + } + } + else if (strcmp(buf, "sei_pivot_value")==0) + { + ret = fscanf(fp, " = "); + if (ret!=0) + { + error ("ParseToneMappingConfigFile: error parsing tone mapping config file",500); + } + for (i=0; i < pSeiToneMapping->num_pivots; i++) + { + ret = fscanf(fp, "%d\n", &(pSeiToneMapping->sei_pivot_value[i])); + if (ret!=1) + { + error ("ParseToneMappingConfigFile: error parsing tone mapping config file",500); + } + } + } + else + { + // read till the line end + if (NULL == fgets(buf, sizeof(buf), fp)) + { + error ("ParseToneMappingConfigFile: error parsing tone mapping config file",500); + } + } + if (ret!=1) + { + error ("ParseToneMappingConfigFile: error parsing tone mapping config file",500); + } + } + + fclose(fp); + + return 0; +} + +static void InitToneMapping(SEIParameters *p_SEI, InputParameters *p_Inp) +{ + if (p_Inp->ToneMappingSEIPresentFlag == 0) + { + p_SEI->seiHasTone_mapping = FALSE; + return; + } + else + p_SEI->seiHasTone_mapping = TRUE; + + p_SEI->seiToneMapping.data = malloc( sizeof(Bitstream) ); + if( p_SEI->seiToneMapping.data == NULL ) no_mem_exit("InitToneMapping: seiToneMapping.data"); + p_SEI->seiToneMapping.data->streamBuffer = malloc(MAXRTPPAYLOADLEN); + if( p_SEI->seiToneMapping.data->streamBuffer == NULL ) no_mem_exit("InitToneMapping: seiToneMapping.data->streamBuffer"); + memset( p_SEI->seiToneMapping.data->streamBuffer, 0, MAXRTPPAYLOADLEN); + p_SEI->seiToneMapping.data->bits_to_go = 8; + p_SEI->seiToneMapping.data->byte_pos = 0; + p_SEI->seiToneMapping.data->byte_buf = 0; + p_SEI->seiToneMapping.payloadSize = 0; + + // read tone mapping config from file + ParseToneMappingConfigFile(p_SEI, p_Inp, &p_SEI->seiToneMapping); +} + +static void FinalizeToneMapping(VideoParameters *p_Vid) +{ + SEIParameters *p_SEI = p_Vid->p_SEI; + + Bitstream *bitstream = p_SEI->seiToneMapping.data; + int i; + + write_ue_v("SEI: tone_map_id" , p_SEI->seiToneMapping.tone_map_id, bitstream); + write_u_1("SEI: tone_map_cancel_flag" , p_SEI->seiToneMapping.tone_map_cancel_flag, bitstream); + +#ifdef PRINT_TONE_MAPPING + printf("frame %d: Tone-mapping SEI message\n", p_Vid->frame_num); + printf("tone_map_id = %d\n", p_SEI->seiToneMapping.tone_map_id); + printf("tone_map_cancel_flag = %d\n", p_SEI->seiToneMapping.tone_map_cancel_flag); +#endif + if (!p_SEI->seiToneMapping.tone_map_cancel_flag) + { + write_ue_v( "SEI: tone_map_repetition_period", p_SEI->seiToneMapping.tone_map_repetition_period, bitstream); + write_u_v (8,"SEI: coded_data_bit_depth" , p_SEI->seiToneMapping.coded_data_bit_depth, bitstream); + write_u_v (8,"SEI: sei_bit_depth" , p_SEI->seiToneMapping.sei_bit_depth, bitstream); + write_ue_v( "SEI: model_id" , p_SEI->seiToneMapping.model_id, bitstream); + +#ifdef PRINT_TONE_MAPPING + printf("tone_map_repetition_period = %d\n", p_SEI->seiToneMapping.tone_map_repetition_period); + printf("coded_data_bit_depth = %d\n", p_SEI->seiToneMapping.coded_data_bit_depth); + printf("sei_bit_depth = %d\n", p_SEI->seiToneMapping.sei_bit_depth); + printf("model_id = %d\n", p_SEI->seiToneMapping.model_id); +#endif + if (p_SEI->seiToneMapping.model_id == 0) + { // linear mapping + write_u_v (32,"SEI: min_value", p_SEI->seiToneMapping.min_value, bitstream); + write_u_v (32,"SEI: min_value", p_SEI->seiToneMapping.max_value, bitstream); +#ifdef PRINT_TONE_MAPPING + printf("min_value = %d, max_value = %d\n", p_SEI->seiToneMapping.min_value, p_SEI->seiToneMapping.max_value); +#endif + } + else if (p_SEI->seiToneMapping.model_id == 1) + { // sigmoidal mapping + write_u_v (32,"SEI: sigmoid_midpoint", p_SEI->seiToneMapping.sigmoid_midpoint, bitstream); + write_u_v (32,"SEI: sigmoid_width", p_SEI->seiToneMapping.sigmoid_width, bitstream); +#ifdef PRINT_TONE_MAPPING + printf("sigmoid_midpoint = %d, sigmoid_width = %d\n", p_SEI->seiToneMapping.sigmoid_midpoint, p_SEI->seiToneMapping.sigmoid_width); +#endif + } + else if (p_SEI->seiToneMapping.model_id == 2) + { // user defined table mapping + int bit_depth_val = 1 << p_SEI->seiToneMapping.sei_bit_depth; + for (i=0; iseiToneMapping.coded_data_bit_depth+7)>>3)<<3), "SEI: start_of_coded_interval", p_SEI->seiToneMapping.start_of_coded_interval[i], bitstream); +#ifdef PRINT_TONE_MAPPING + //printf("start_of_coded_interval[%d] = %d\n", i, p_SEI->seiToneMapping.start_of_coded_interval[i]); +#endif + } + } + else if (p_SEI->seiToneMapping.model_id == 3) + { // piece-wise linear mapping + write_u_v (16,"SEI: num_pivots", p_SEI->seiToneMapping.num_pivots, bitstream); +#ifdef PRINT_TONE_MAPPING + printf("num_pivots = %d\n", p_SEI->seiToneMapping.num_pivots); +#endif + for (i=0; i < p_SEI->seiToneMapping.num_pivots; i++) + { + write_u_v( (((p_SEI->seiToneMapping.coded_data_bit_depth+7)>>3)<<3), "SEI: coded_pivot_value", p_SEI->seiToneMapping.coded_pivot_value[i], bitstream); + write_u_v( (((p_SEI->seiToneMapping.sei_bit_depth+7)>>3)<<3), "SEI: sei_pivot_value", p_SEI->seiToneMapping.sei_pivot_value[i], bitstream); +#ifdef PRINT_TONE_MAPPING + printf("coded_pivot_value[%d] = %d, sei_pivot_value[%d] = %d\n", i, p_SEI->seiToneMapping.coded_pivot_value[i], i, p_SEI->seiToneMapping.sei_pivot_value[i]); +#endif + } + } + } // end !tone_map_cancel_flag + + // make sure the payload is byte aligned, stuff bits are 10..0 + if ( bitstream->bits_to_go != 8 ) + { + (bitstream->byte_buf) <<= 1; + bitstream->byte_buf |= 1; + bitstream->bits_to_go--; + if ( bitstream->bits_to_go != 0 ) + (bitstream->byte_buf) <<= (bitstream->bits_to_go); + bitstream->bits_to_go = 8; + bitstream->streamBuffer[bitstream->byte_pos++]=bitstream->byte_buf; + bitstream->byte_buf = 0; + } + p_SEI->seiToneMapping.payloadSize = bitstream->byte_pos; +} + + +void UpdateToneMapping(SEIParameters *p_SEI) +{ + // return; + + // you may manually generate some test case here +} + +static void ClearToneMapping(SEIParameters *p_SEI) +{ + memset( p_SEI->seiToneMapping.data->streamBuffer, 0, MAXRTPPAYLOADLEN); + p_SEI->seiToneMapping.data->bits_to_go = 8; + p_SEI->seiToneMapping.data->byte_pos = 0; + p_SEI->seiToneMapping.data->byte_buf = 0; + p_SEI->seiToneMapping.payloadSize = 0; + + p_SEI->seiHasTone_mapping=FALSE; +} + +static void CloseToneMapping(SEIParameters *p_SEI) +{ + + if (p_SEI->seiToneMapping.data) + { + free(p_SEI->seiToneMapping.data->streamBuffer); + free(p_SEI->seiToneMapping.data); + } + p_SEI->seiToneMapping.data = NULL; + p_SEI->seiHasTone_mapping = FALSE; +} + +/* + ************************************************************************ + * \functions on post-filter message + * \brief + * Based on JVT-U035 + * \author + * Steffen Wittmann + ************************************************************************ + */ +static void InitPostFilterHints(SEIParameters *p_SEI) +{ + p_SEI->seiPostFilterHints.data = malloc( sizeof(Bitstream) ); + if( p_SEI->seiPostFilterHints.data == NULL ) no_mem_exit("InitPostFilterHints: p_SEI->seiPostFilterHints.data"); + p_SEI->seiPostFilterHints.data->streamBuffer = malloc(MAXRTPPAYLOADLEN); + if( p_SEI->seiPostFilterHints.data->streamBuffer == NULL ) no_mem_exit("InitPostFilterHints: p_SEI->seiPostFilterHints.data->streamBuffer"); + ClearPostFilterHints(p_SEI); +} + +static void ClearPostFilterHints(SEIParameters *p_SEI) +{ + memset( p_SEI->seiPostFilterHints.data->streamBuffer, 0, MAXRTPPAYLOADLEN); + p_SEI->seiPostFilterHints.data->bits_to_go = 8; + p_SEI->seiPostFilterHints.data->byte_pos = 0; + p_SEI->seiPostFilterHints.data->byte_buf = 0; + p_SEI->seiPostFilterHints.payloadSize = 0; + + p_SEI->seiPostFilterHints.filter_hint_size_y = 0; + p_SEI->seiPostFilterHints.filter_hint_size_x = 0; + p_SEI->seiPostFilterHints.filter_hint_type = 0; + p_SEI->seiPostFilterHints.additional_extension_flag = 0; +} + +void UpdatePostFilterHints(SEIParameters *p_SEI) +{ + unsigned int color_component, cx, cy; + p_SEI->seiPostFilterHints.filter_hint_type = 0; //define filter_hint_type here + p_SEI->seiPostFilterHints.filter_hint_size_y = p_SEI->seiPostFilterHints.filter_hint_size_x = 5; //define filter_hint_size here + get_mem3Dint(&p_SEI->seiPostFilterHints.filter_hint, 3, p_SEI->seiPostFilterHints.filter_hint_size_y, p_SEI->seiPostFilterHints.filter_hint_size_x); + + for (color_component = 0; color_component < 3; color_component ++) + for (cy = 0; cy < p_SEI->seiPostFilterHints.filter_hint_size_y; cy ++) + for (cx = 0; cx < p_SEI->seiPostFilterHints.filter_hint_size_x; cx ++) + p_SEI->seiPostFilterHints.filter_hint[color_component][cy][cx] = 1; //define filter_hint here + + p_SEI->seiPostFilterHints.additional_extension_flag = 0; +} + +static void FinalizePostFilterHints(SEIParameters *p_SEI) +{ + Bitstream *bitstream = p_SEI->seiPostFilterHints.data; + unsigned int color_component, cx, cy; + + write_ue_v( "SEI: post_filter_hint_size_y", p_SEI->seiPostFilterHints.filter_hint_size_y, bitstream); + write_ue_v( "SEI: post_filter_hint_size_x", p_SEI->seiPostFilterHints.filter_hint_size_x, bitstream); + write_u_v (2,"SEI: post_filter_hint_type", p_SEI->seiPostFilterHints.filter_hint_type, bitstream); + + for (color_component = 0; color_component < 3; color_component ++) + for (cy = 0; cy < p_SEI->seiPostFilterHints.filter_hint_size_y; cy ++) + for (cx = 0; cx < p_SEI->seiPostFilterHints.filter_hint_size_x; cx ++) + write_se_v("SEI: post_filter_hints", p_SEI->seiPostFilterHints.filter_hint[color_component][cy][cx], bitstream); + + write_u_1 ("SEI: post_filter_additional_extension_flag", p_SEI->seiPostFilterHints.additional_extension_flag, bitstream); + +// #define PRINT_POST_FILTER_HINTS +#ifdef PRINT_POST_FILTER_HINTS + printf(" post_filter_hint_size_y %d \n", p_SEI->seiPostFilterHints.filter_hint_size_y); + printf(" post_filter_hint_size_x %d \n", p_SEI->seiPostFilterHints.filter_hint_size_x); + printf(" post_filter_hint_type %d \n", p_SEI->seiPostFilterHints.filter_hint_type); + for (color_component = 0; color_component < 3; color_component ++) + for (cy = 0; cy < p_SEI->seiPostFilterHints.filter_hint_size_y; cy ++) + for (cx = 0; cx < p_SEI->seiPostFilterHints.filter_hint_size_x; cx ++) + printf(" post_filter_hint[%d][%d][%d] %d \n", color_component, cy, cx, filter_hint[color_component][cy][cx]); + + printf(" additional_extension_flag %d \n", p_SEI->seiPostFilterHints.additional_extension_flag); + +#undef PRINT_POST_FILTER_HINTS +#endif + // make sure the payload is byte aligned, stuff bits are 10..0 + if ( bitstream->bits_to_go != 8 ) + { + (bitstream->byte_buf) <<= 1; + bitstream->byte_buf |= 1; + bitstream->bits_to_go--; + if ( bitstream->bits_to_go != 0 ) + (bitstream->byte_buf) <<= (bitstream->bits_to_go); + bitstream->bits_to_go = 8; + bitstream->streamBuffer[bitstream->byte_pos++]=bitstream->byte_buf; + bitstream->byte_buf = 0; + } + p_SEI->seiPostFilterHints.payloadSize = bitstream->byte_pos; +} + +static void ClosePostFilterHints(SEIParameters *p_SEI) +{ + if (p_SEI->seiPostFilterHints.data) + { + free(p_SEI->seiPostFilterHints.data->streamBuffer); + free(p_SEI->seiPostFilterHints.data); + if (p_SEI->seiPostFilterHints.filter_hint) + free_mem3Dint(p_SEI->seiPostFilterHints.filter_hint); + } + p_SEI->seiPostFilterHints.data = NULL; +} + +/* +**++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +* \functions to write SEI message into NAL +* \brief +**++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +*/ +int Write_SEI_NALU(VideoParameters *p_Vid, int len) +{ + NALU_t *nalu = NULL; + int RBSPlen = 0; + byte *rbsp; + + if (HaveAggregationSEI(p_Vid)) + { + SEIParameters *p_SEI = p_Vid->p_SEI; + + nalu = AllocNALU(MAXNALUSIZE); + rbsp = p_SEI->sei_message[AGGREGATION_SEI].data; + RBSPlen = p_SEI->sei_message[AGGREGATION_SEI].payloadSize; + RBSPtoNALU (rbsp, nalu, RBSPlen, NALU_TYPE_SEI, NALU_PRIORITY_DISPOSABLE, 1); + nalu->startcodeprefix_len = 4; + + len += p_Vid->WriteNALU (p_Vid, nalu, p_Vid->f_out); + FreeNALU (nalu); + } + + return len; +} + +/* + **++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + * \functions on buffering period SEI message + * \brief + * Based on final Recommendation + **++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + */ +void InitBufferingPeriod(VideoParameters *p_Vid) +{ + SEIParameters *p_SEI = p_Vid->p_SEI; + p_SEI->seiBufferingPeriod.data = malloc( sizeof(Bitstream) ); + if( p_SEI->seiBufferingPeriod.data == NULL ) + no_mem_exit("InitBufferingPeriod: seiBufferingPeriod.data"); + + p_SEI->seiBufferingPeriod.data->streamBuffer = malloc(MAXRTPPAYLOADLEN); + if( p_SEI->seiBufferingPeriod.data->streamBuffer == NULL ) + no_mem_exit("InitBufferingPeriod: seiBufferingPeriod.data->streamBuffer"); + + ClearBufferingPeriod(p_SEI, p_Vid->active_sps); +} + +void ClearBufferingPeriod(SEIParameters *p_SEI, seq_parameter_set_rbsp_t *active_sps) +{ + unsigned int SchedSelIdx; + memset( p_SEI->seiBufferingPeriod.data->streamBuffer, 0, MAXRTPPAYLOADLEN); + + p_SEI->seiBufferingPeriod.data->bits_to_go = 8; + p_SEI->seiBufferingPeriod.data->byte_pos = 0; + p_SEI->seiBufferingPeriod.data->byte_buf = 0; + p_SEI->seiBufferingPeriod.payloadSize = 0; + + p_SEI->seiBufferingPeriod.seq_parameter_set_id = active_sps->seq_parameter_set_id; + if ( active_sps->vui_seq_parameters.nal_hrd_parameters_present_flag ) + { + for ( SchedSelIdx = 0; SchedSelIdx <= active_sps->vui_seq_parameters.nal_hrd_parameters.cpb_cnt_minus1; SchedSelIdx++ ) + { + p_SEI->seiBufferingPeriod.nal_initial_cpb_removal_delay[SchedSelIdx] = 0; + p_SEI->seiBufferingPeriod.nal_initial_cpb_removal_delay_offset[SchedSelIdx] = 0; + } + } + if ( active_sps->vui_seq_parameters.vcl_hrd_parameters_present_flag ) + { + for ( SchedSelIdx = 0; SchedSelIdx <= active_sps->vui_seq_parameters.vcl_hrd_parameters.cpb_cnt_minus1; SchedSelIdx++ ) + { + p_SEI->seiBufferingPeriod.vcl_initial_cpb_removal_delay[SchedSelIdx] = 0; + p_SEI->seiBufferingPeriod.vcl_initial_cpb_removal_delay_offset[SchedSelIdx] = 0; + } + } + + p_SEI->seiHasBufferingPeriod_info = FALSE; +} + +void UpdateBufferingPeriod(VideoParameters *p_Vid, InputParameters *p_Inp) +{ + SEIParameters *p_SEI = p_Vid->p_SEI; + p_SEI->seiHasBufferingPeriod_info = FALSE; +} + +static void FinalizeBufferingPeriod(SEIParameters *p_SEI, seq_parameter_set_rbsp_t *active_sps) +{ + unsigned int SchedSelIdx; + Bitstream *bitstream = p_SEI->seiBufferingPeriod.data; + + write_ue_v( "SEI: seq_parameter_set_id", p_SEI->seiBufferingPeriod.seq_parameter_set_id, bitstream); + if ( active_sps->vui_seq_parameters.nal_hrd_parameters_present_flag ) + { + for ( SchedSelIdx = 0; SchedSelIdx <= active_sps->vui_seq_parameters.nal_hrd_parameters.cpb_cnt_minus1; SchedSelIdx++ ) + { + write_u_v( active_sps->vui_seq_parameters.nal_hrd_parameters.initial_cpb_removal_delay_length_minus1 + 1, + "SEI: initial_cpb_removal_delay", p_SEI->seiBufferingPeriod.nal_initial_cpb_removal_delay[SchedSelIdx], bitstream); + write_u_v( active_sps->vui_seq_parameters.nal_hrd_parameters.initial_cpb_removal_delay_length_minus1 + 1, + "SEI: initial_cpb_removal_delay_offset", p_SEI->seiBufferingPeriod.nal_initial_cpb_removal_delay_offset[SchedSelIdx], bitstream); + } + } + if ( active_sps->vui_seq_parameters.vcl_hrd_parameters_present_flag ) + { + for ( SchedSelIdx = 0; SchedSelIdx <= active_sps->vui_seq_parameters.vcl_hrd_parameters.cpb_cnt_minus1; SchedSelIdx++ ) + { + write_u_v( active_sps->vui_seq_parameters.vcl_hrd_parameters.initial_cpb_removal_delay_length_minus1 + 1, + "SEI: initial_cpb_removal_delay", p_SEI->seiBufferingPeriod.vcl_initial_cpb_removal_delay[SchedSelIdx], bitstream); + write_u_v( active_sps->vui_seq_parameters.vcl_hrd_parameters.initial_cpb_removal_delay_length_minus1 + 1, + "SEI: initial_cpb_removal_delay_offset", p_SEI->seiBufferingPeriod.vcl_initial_cpb_removal_delay_offset[SchedSelIdx], bitstream); + } + } + + // make sure the payload is byte aligned, stuff bits are 10..0 + if ( bitstream->bits_to_go != 8 ) + { + (bitstream->byte_buf) <<= 1; + bitstream->byte_buf |= 1; + bitstream->bits_to_go--; + if ( bitstream->bits_to_go != 0 ) + (bitstream->byte_buf) <<= (bitstream->bits_to_go); + bitstream->bits_to_go = 8; + bitstream->streamBuffer[bitstream->byte_pos++]=bitstream->byte_buf; + bitstream->byte_buf = 0; + } + p_SEI->seiBufferingPeriod.payloadSize = bitstream->byte_pos; +} + +static void CloseBufferingPeriod(SEIParameters *p_SEI) +{ + if (p_SEI->seiBufferingPeriod.data) + { + free(p_SEI->seiBufferingPeriod.data->streamBuffer); + free(p_SEI->seiBufferingPeriod.data); + } + p_SEI->seiBufferingPeriod.data = NULL; +} + +/* + ************************************************************************ + * \brief + * Initialize Picture Timing SEI data + ************************************************************************ + */ +void InitPicTiming(SEIParameters *p_SEI) +{ + p_SEI->seiPicTiming.data = malloc( sizeof(Bitstream) ); + if( p_SEI->seiPicTiming.data == NULL ) + no_mem_exit("InitPicTiming: seiPicTiming.data"); + + p_SEI->seiPicTiming.data->streamBuffer = malloc(MAXRTPPAYLOADLEN); + if( p_SEI->seiPicTiming.data->streamBuffer == NULL ) + no_mem_exit("InitPicTiming: seiPicTiming.data->streamBuffer"); + + ClearPicTiming(p_SEI); +} + +/* + ************************************************************************ + * \brief + * Clear Picture Timing SEI data + ************************************************************************ + */ +void ClearPicTiming(SEIParameters *p_SEI) +{ + memset( p_SEI->seiPicTiming.data->streamBuffer, 0, MAXRTPPAYLOADLEN); + + p_SEI->seiPicTiming.data->bits_to_go = 8; + p_SEI->seiPicTiming.data->byte_pos = 0; + p_SEI->seiPicTiming.data->byte_buf = 0; + p_SEI->seiPicTiming.payloadSize = 0; + + // initialization + p_SEI->seiPicTiming.cpb_removal_delay = 0; + p_SEI->seiPicTiming.dpb_output_delay = 0; + p_SEI->seiPicTiming.pic_struct = 0; + memset(p_SEI->seiPicTiming.clock_timestamp_flag, 0, MAX_PIC_STRUCT_VALUE * sizeof(Boolean) ); // 0 == FALSE + p_SEI->seiPicTiming.ct_type = 0; + p_SEI->seiPicTiming.nuit_field_based_flag = FALSE; + p_SEI->seiPicTiming.counting_type = 0; + p_SEI->seiPicTiming.full_timestamp_flag = FALSE; + p_SEI->seiPicTiming.discontinuity_flag = FALSE; + p_SEI->seiPicTiming.cnt_dropped_flag = FALSE; + p_SEI->seiPicTiming.n_frames = 0; + p_SEI->seiPicTiming.seconds_value = 0; + p_SEI->seiPicTiming.minutes_value = 0; + p_SEI->seiPicTiming.hours_value = 0; + p_SEI->seiPicTiming.seconds_flag = FALSE; + p_SEI->seiPicTiming.minutes_flag = FALSE; + p_SEI->seiPicTiming.hours_flag = FALSE; + p_SEI->seiPicTiming.time_offset = 0; + + p_SEI->seiHasPicTiming_info = FALSE; +} + +/* + ************************************************************************ + * \brief + * Derive pic_struct flag (Table D-1) + ************************************************************************ + */ +static int get_pic_struct( VideoParameters *p_Vid, int frame_no, int field_pic_flag, int top ) +{ + InputParameters *p_Inp = p_Vid->p_Inp; + + if ( field_pic_flag ) + { + if ( top ) + { + return 1; + } + else + { + return 2; + } + } + else + { + if ( !p_Inp->SEIVUI32Pulldown ) + { + return 0; + } + else + { + int order = (frame_no % 4); + int pic_struct_a[4] = {0,5,6,0}; + int pic_struct_b[4] = {0,0,7,0}; + int pic_struct_c[4] = {3,5,6,3}; + int pic_struct_d[4] = {0,5,4,6}; + int pic_struct_e[4] = {3,5,4,6}; + + switch( p_Inp->SEIVUI32Pulldown ) + { + default: + case 1: + return pic_struct_a[order]; + break; + case 2: + return pic_struct_b[order]; + break; + case 3: + return pic_struct_c[order]; + break; + case 4: + return pic_struct_d[order]; + break; + case 5: + return pic_struct_e[order]; + break; + } + } + } +} + + +/* + ************************************************************************ + * \brief + * Update Picture Timing SEI data + ************************************************************************ + */ +void UpdatePicTiming(VideoParameters *p_Vid, InputParameters *p_Inp) +{ + SEIParameters *p_SEI = p_Vid->p_SEI; + seq_parameter_set_rbsp_t *active_sps = p_Vid->active_sps; + + // initialize the values + if ( p_Inp->SEIVUI32Pulldown ) + { + int fld_flag = p_Vid->fld_flag; + // pic_struct + p_Vid->pic_struct = get_pic_struct( p_Vid, p_Vid->frm_no_in_file, fld_flag, (p_Vid->structure == TOP_FIELD) ? 1 : 0 ); + + if ( active_sps->vui_seq_parameters.pic_struct_present_flag ) + { + int NumClockTS = 0, i; + int bottom_field_flag = (p_Vid->structure == BOTTOM_FIELD); + p_SEI->seiPicTiming.pic_struct = p_Vid->pic_struct; + // interpret pic_struct + switch( p_SEI->seiPicTiming.pic_struct ) + { + case 0: + default: + // frame + assert( fld_flag == FALSE ); + NumClockTS = 1; + break; + case 1: + // top field + assert( (p_Vid->fld_flag == TRUE) && (bottom_field_flag == FALSE) ); + NumClockTS = 1; + break; + case 2: + // bottom field + assert( (p_Vid->fld_flag == TRUE) && (bottom_field_flag == TRUE) ); + NumClockTS = 1; + break; + case 3: + // top field, bottom field, in that order + case 4: + // bottom field, top field, in that order + assert( p_Vid->fld_flag == FALSE ); + NumClockTS = 2; + break; + case 5: + // top field, bottom field, top field repeated, in that order + case 6: + // bottom field, top field, bottom field repeated, in that order + assert( p_Vid->fld_flag == FALSE ); + NumClockTS = 3; + case 7: + // frame doubling + assert( (p_Vid->fld_flag == FALSE) && active_sps->vui_seq_parameters.fixed_frame_rate_flag == 1 ); + NumClockTS = 2; + break; + case 8: + // frame tripling + assert( (p_Vid->fld_flag == FALSE) && active_sps->vui_seq_parameters.fixed_frame_rate_flag == 1 ); + NumClockTS = 3; + break; + } + for ( i = 0; i < NumClockTS; i++ ) + { + p_SEI->seiPicTiming.clock_timestamp_flag[i] = FALSE; + if ( p_SEI->seiPicTiming.clock_timestamp_flag[i] ) + { + int seconds = 0; + int minutes = 0; + int hours = 0; + int time_offset_length; + assert( seconds >= 0 && seconds <= 59 ); + assert( minutes >= 0 && minutes <= 59 ); + assert( hours >= 0 && hours <= 23 ); + + p_SEI->seiPicTiming.ct_type = 0; + p_SEI->seiPicTiming.nuit_field_based_flag = FALSE; + p_SEI->seiPicTiming.counting_type = 0; + p_SEI->seiPicTiming.full_timestamp_flag = FALSE; + p_SEI->seiPicTiming.discontinuity_flag = FALSE; + p_SEI->seiPicTiming.cnt_dropped_flag = FALSE; + p_SEI->seiPicTiming.n_frames = 0; + + if ( p_SEI->seiPicTiming.full_timestamp_flag ) + { + p_SEI->seiPicTiming.seconds_value = seconds; + p_SEI->seiPicTiming.minutes_value = minutes; + p_SEI->seiPicTiming.hours_value = hours; + } + else + { + p_SEI->seiPicTiming.seconds_flag = FALSE; + if (p_SEI->seiPicTiming.seconds_flag) + { + p_SEI->seiPicTiming.seconds_value = seconds; + p_SEI->seiPicTiming.minutes_flag = FALSE; + if (p_SEI->seiPicTiming.minutes_flag) + { + p_SEI->seiPicTiming.minutes_value = minutes; + p_SEI->seiPicTiming.hours_flag = FALSE; + if (p_SEI->seiPicTiming.hours_flag) + { + p_SEI->seiPicTiming.hours_value = hours; + } + } + } + } + if (active_sps->vui_seq_parameters.vcl_hrd_parameters_present_flag) + time_offset_length = active_sps->vui_seq_parameters.vcl_hrd_parameters.time_offset_length; + else if (active_sps->vui_seq_parameters.nal_hrd_parameters_present_flag) + time_offset_length = active_sps->vui_seq_parameters.nal_hrd_parameters.time_offset_length; + else + time_offset_length = 24; + if ( time_offset_length ) + { + p_SEI->seiPicTiming.time_offset = 0; + } + } + } + } + p_SEI->seiHasPicTiming_info = TRUE; + } + else + { + p_SEI->seiHasPicTiming_info = FALSE; + } +} + +/* + ************************************************************************ + * \brief + * Finalize Picture Timing SEI data + ************************************************************************ + */ +static void FinalizePicTiming(VideoParameters *p_Vid) +{ + SEIParameters *p_SEI = p_Vid->p_SEI; + seq_parameter_set_rbsp_t *active_sps = p_Vid->active_sps; + + Bitstream *bitstream = p_SEI->seiPicTiming.data; + // CpbDpbDelaysPresentFlag can also be set "by some means not specified in this Recommendation | International Standard" + Boolean CpbDpbDelaysPresentFlag = (Boolean) (active_sps->vui_parameters_present_flag + && ( (active_sps->vui_seq_parameters.nal_hrd_parameters_present_flag != 0) + ||(active_sps->vui_seq_parameters.vcl_hrd_parameters_present_flag != 0))); + hrd_parameters_t *hrd = NULL; + + if (active_sps->vui_seq_parameters.vcl_hrd_parameters_present_flag) + { + hrd = &(active_sps->vui_seq_parameters.vcl_hrd_parameters); + } + else if (active_sps->vui_seq_parameters.nal_hrd_parameters_present_flag) + { + hrd = &(active_sps->vui_seq_parameters.nal_hrd_parameters); + } + else + { + hrd = NULL; + } + + if ( CpbDpbDelaysPresentFlag ) + { + write_u_v( hrd->cpb_removal_delay_length_minus1 + 1, "SEI: cpb_removal_delay", p_SEI->seiPicTiming.cpb_removal_delay, bitstream); + write_u_v( hrd->dpb_output_delay_length_minus1 + 1, "SEI: dpb_output_delay", p_SEI->seiPicTiming.dpb_output_delay, bitstream); + } + if ( active_sps->vui_seq_parameters.pic_struct_present_flag ) + { + int NumClockTS = 0, i; + int bottom_field_flag = (p_Vid->structure == BOTTOM_FIELD); + + write_u_v( 4, "SEI: pic_struct", p_SEI->seiPicTiming.pic_struct, bitstream); + // interpret pic_struct + switch( p_SEI->seiPicTiming.pic_struct ) + { + case 0: + default: + // frame + assert( p_Vid->fld_flag == FALSE ); + NumClockTS = 1; + break; + case 1: + // top field + assert( (p_Vid->fld_flag == TRUE) && (bottom_field_flag == FALSE) ); + NumClockTS = 1; + break; + case 2: + // bottom field + assert( (p_Vid->fld_flag == TRUE) && (bottom_field_flag == TRUE) ); + NumClockTS = 1; + break; + case 3: + // top field, bottom field, in that order + case 4: + // bottom field, top field, in that order + assert( p_Vid->fld_flag == FALSE ); + NumClockTS = 2; + break; + case 5: + // top field, bottom field, top field repeated, in that order + case 6: + // bottom field, top field, bottom field repeated, in that order + assert( p_Vid->fld_flag == FALSE ); + NumClockTS = 3; + break; + case 7: + // frame doubling + assert( (p_Vid->fld_flag == FALSE) && active_sps->vui_seq_parameters.fixed_frame_rate_flag == 1 ); + NumClockTS = 2; + break; + case 8: + // frame tripling + assert( (p_Vid->fld_flag == FALSE) && active_sps->vui_seq_parameters.fixed_frame_rate_flag == 1 ); + NumClockTS = 3; + break; + } + for ( i = 0; i < NumClockTS; i++ ) + { + write_u_1( "SEI: clock_timestamp_flag", p_SEI->seiPicTiming.clock_timestamp_flag[i], bitstream); + if ( p_SEI->seiPicTiming.clock_timestamp_flag[i] ) + { + write_u_v( 2, "SEI: ct_type", p_SEI->seiPicTiming.ct_type, bitstream); + write_u_1( "SEI: nuit_field_based_flag", p_SEI->seiPicTiming.nuit_field_based_flag, bitstream); + write_u_v( 5, "SEI: counting_type", p_SEI->seiPicTiming.counting_type, bitstream); + write_u_1( "SEI: full_timestamp_flag", p_SEI->seiPicTiming.full_timestamp_flag, bitstream); + write_u_1( "SEI: discontinuity_flag", p_SEI->seiPicTiming.discontinuity_flag, bitstream); + write_u_1( "SEI: cnt_dropped_flag", p_SEI->seiPicTiming.cnt_dropped_flag, bitstream); + write_u_v( 8, "SEI: n_frames", p_SEI->seiPicTiming.n_frames, bitstream); + + if ( p_SEI->seiPicTiming.full_timestamp_flag ) + { + write_u_v( 6, "SEI: seconds_value", p_SEI->seiPicTiming.seconds_value, bitstream); + write_u_v( 6, "SEI: minutes_value", p_SEI->seiPicTiming.minutes_value, bitstream); + write_u_v( 5, "SEI: hours_value", p_SEI->seiPicTiming.hours_value, bitstream); + } + else + { + write_u_1( "SEI: seconds_flag", p_SEI->seiPicTiming.seconds_flag, bitstream); + if (p_SEI->seiPicTiming.seconds_flag) + { + write_u_v( 6, "SEI: seconds_value", p_SEI->seiPicTiming.seconds_value, bitstream); + write_u_1( "SEI: minutes_flag", p_SEI->seiPicTiming.minutes_flag, bitstream); + if (p_SEI->seiPicTiming.minutes_flag) + { + write_u_v( 6, "SEI: minutes_value", p_SEI->seiPicTiming.minutes_value, bitstream); + write_u_1( "SEI: hours_flag", p_SEI->seiPicTiming.hours_flag, bitstream); + if (p_SEI->seiPicTiming.hours_flag) + { + write_u_v( 5, "SEI: hours_value", p_SEI->seiPicTiming.hours_value, bitstream); + } + } + } + } + if ( hrd == NULL ) + { + write_u_v( 24, "SEI: time_offset", p_SEI->seiPicTiming.time_offset, bitstream); + } + else + { + write_u_v( hrd->time_offset_length, "SEI: time_offset", p_SEI->seiPicTiming.time_offset, bitstream); + } + } + } + } + + // make sure the payload is byte aligned, stuff bits are 10..0 + if ( bitstream->bits_to_go != 8 ) + { + (bitstream->byte_buf) <<= 1; + bitstream->byte_buf |= 1; + bitstream->bits_to_go--; + if ( bitstream->bits_to_go != 0 ) + (bitstream->byte_buf) <<= (bitstream->bits_to_go); + bitstream->bits_to_go = 8; + bitstream->streamBuffer[bitstream->byte_pos++]=bitstream->byte_buf; + bitstream->byte_buf = 0; + } + p_SEI->seiPicTiming.payloadSize = bitstream->byte_pos; +} + +/* + ************************************************************************ + * \brief + * Close Picture Timing SEI data + ************************************************************************ + */ +static void ClosePicTiming(SEIParameters *p_SEI) +{ + if (p_SEI->seiPicTiming.data) + { + free(p_SEI->seiPicTiming.data->streamBuffer); + free(p_SEI->seiPicTiming.data); + } + p_SEI->seiPicTiming.data = NULL; +} + +/* + **++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + * \functions on frame packing arrangement SEI message + * \brief + * Based on pre-published Recommendation + **++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + */ + +//! Frame packing arrangement Information +static void InitFramePackingArrangement(VideoParameters *p_Vid) +{ + SEIParameters *p_SEI = p_Vid->p_SEI; + + p_SEI->seiFramePackingArrangement.data = malloc( sizeof(Bitstream) ); + if( p_SEI->seiFramePackingArrangement.data == NULL ) + no_mem_exit("InitFramePackingArrangement: seiFramePackingArrangement.data"); + + p_SEI->seiFramePackingArrangement.data->streamBuffer = malloc(MAXRTPPAYLOADLEN); + if( p_SEI->seiFramePackingArrangement.data->streamBuffer == NULL ) + no_mem_exit("InitFramePackingArrangement: seiFramePackingArrangement.data->streamBuffer"); + + ClearFramePackingArrangement(p_SEI); +} + +void ClearFramePackingArrangement(SEIParameters *p_SEI) +{ + memset( p_SEI->seiFramePackingArrangement.data->streamBuffer, 0, MAXRTPPAYLOADLEN); + + p_SEI->seiFramePackingArrangement.data->bits_to_go = 8; + p_SEI->seiFramePackingArrangement.data->byte_pos = 0; + p_SEI->seiFramePackingArrangement.data->byte_buf = 0; + p_SEI->seiFramePackingArrangement.payloadSize = 0; + + p_SEI->seiFramePackingArrangement.frame_packing_arrangement_id = 0; + p_SEI->seiFramePackingArrangement.frame_packing_arrangement_cancel_flag = FALSE; + p_SEI->seiFramePackingArrangement.frame_packing_arrangement_type = 0; + p_SEI->seiFramePackingArrangement.quincunx_sampling_flag = FALSE; + p_SEI->seiFramePackingArrangement.content_interpretation_type = 0; + p_SEI->seiFramePackingArrangement.spatial_flipping_flag = FALSE; + p_SEI->seiFramePackingArrangement.frame0_flipped_flag = FALSE; + p_SEI->seiFramePackingArrangement.field_views_flag = FALSE; + p_SEI->seiFramePackingArrangement.current_frame_is_frame0_flag = FALSE; + p_SEI->seiFramePackingArrangement.frame0_self_contained_flag = FALSE; + p_SEI->seiFramePackingArrangement.frame1_self_contained_flag = FALSE; + /*p_SEI->seiFramePackingArrangement.frame0_grid_position_x = 0; + p_SEI->seiFramePackingArrangement.frame0_grid_position_y = 0; + p_SEI->seiFramePackingArrangement.frame1_grid_position_x = 0; + p_SEI->seiFramePackingArrangement.frame1_grid_position_y = 0;*/ + p_SEI->seiFramePackingArrangement.frame_packing_arrangement_reserved_byte = 0; + p_SEI->seiFramePackingArrangement.frame_packing_arrangement_repetition_period = 0; + p_SEI->seiFramePackingArrangement.frame_packing_arrangement_extension_flag = FALSE; + + p_SEI->seiHasFramePackingArrangement_info = FALSE; +} + +void UpdateFramePackingArrangement(VideoParameters *p_Vid, InputParameters *p_Inp) +{ + SEIParameters *p_SEI = p_Vid->p_SEI; + + p_SEI->seiHasFramePackingArrangement_info = FALSE; +} + +static void FinalizeFramePackingArrangement(SEIParameters *p_SEI, InputParameters *p_Inp) +{ + Bitstream *bitstream = p_SEI->seiFramePackingArrangement.data; + + write_ue_v( "SEI: frame_packing_arrangement_id", (int)p_SEI->seiFramePackingArrangement.frame_packing_arrangement_id, bitstream ); + write_u_1( "SEI: frame_packing_arrangement_cancel_flag", (int)p_SEI->seiFramePackingArrangement.frame_packing_arrangement_cancel_flag, bitstream ); + if ( p_SEI->seiFramePackingArrangement.frame_packing_arrangement_cancel_flag == FALSE ) + { + write_u_v( 7, "SEI: frame_packing_arrangement_type", (int)p_SEI->seiFramePackingArrangement.frame_packing_arrangement_type, bitstream ); + write_u_1( "SEI: quincunx_sampling_flag", (int)p_SEI->seiFramePackingArrangement.quincunx_sampling_flag, bitstream ); + write_u_v( 6, "SEI: content_interpretation_type", (int)p_SEI->seiFramePackingArrangement.content_interpretation_type, bitstream ); + write_u_1( "SEI: spatial_flipping_flag", (int)p_SEI->seiFramePackingArrangement.spatial_flipping_flag, bitstream ); + write_u_1( "SEI: frame0_flipped_flag", (int)p_SEI->seiFramePackingArrangement.frame0_flipped_flag, bitstream ); + write_u_1( "SEI: field_views_flag", (int)p_SEI->seiFramePackingArrangement.field_views_flag, bitstream ); + write_u_1( "SEI: current_frame_is_frame0_flag", (int)p_SEI->seiFramePackingArrangement.current_frame_is_frame0_flag, bitstream ); + write_u_1( "SEI: frame0_self_contained_flag", (int)p_SEI->seiFramePackingArrangement.frame0_self_contained_flag, bitstream ); + write_u_1( "SEI: frame1_self_contained_flag", (int)p_SEI->seiFramePackingArrangement.frame1_self_contained_flag, bitstream ); + if ( p_SEI->seiFramePackingArrangement.quincunx_sampling_flag == FALSE && p_SEI->seiFramePackingArrangement.frame_packing_arrangement_type != 5 ) + { + write_u_v( 4, "SEI: frame0_grid_position_x", (int)p_SEI->seiFramePackingArrangement.frame0_grid_position_x, bitstream ); + write_u_v( 4, "SEI: frame0_grid_position_y", (int)p_SEI->seiFramePackingArrangement.frame0_grid_position_y, bitstream ); + write_u_v( 4, "SEI: frame1_grid_position_x", (int)p_SEI->seiFramePackingArrangement.frame1_grid_position_x, bitstream ); + write_u_v( 4, "SEI: frame1_grid_position_y", (int)p_SEI->seiFramePackingArrangement.frame1_grid_position_y, bitstream ); + } + + + write_u_v( 8, "SEI: frame_packing_arrangement_reserved_byte", (int)p_SEI->seiFramePackingArrangement.frame_packing_arrangement_reserved_byte, bitstream ); + write_ue_v( "SEI: frame_packing_arrangement_repetition_period", (int)p_SEI->seiFramePackingArrangement.frame_packing_arrangement_repetition_period, bitstream ); + } + write_u_1( "SEI: frame_packing_arrangement_extension_flag", (int)p_SEI->seiFramePackingArrangement.frame_packing_arrangement_extension_flag, bitstream ); + + // make sure the payload is byte aligned, stuff bits are 10..0 + if ( bitstream->bits_to_go != 8 ) + { + (bitstream->byte_buf) <<= 1; + bitstream->byte_buf |= 1; + bitstream->bits_to_go--; + if ( bitstream->bits_to_go != 0 ) + (bitstream->byte_buf) <<= (bitstream->bits_to_go); + bitstream->bits_to_go = 8; + bitstream->streamBuffer[bitstream->byte_pos++]=bitstream->byte_buf; + bitstream->byte_buf = 0; + } + p_SEI->seiFramePackingArrangement.payloadSize = bitstream->byte_pos; +} + +static void CloseFramePackingArrangement(SEIParameters *p_SEI) +{ + if (p_SEI->seiFramePackingArrangement.data) + { + free(p_SEI->seiFramePackingArrangement.data->streamBuffer); + free(p_SEI->seiFramePackingArrangement.data); + } + p_SEI->seiFramePackingArrangement.data = NULL; +} + +/* + ************************************************************************ + * \brief + * Initialize dec_ref_pic_marking Repetition SEI data + ************************************************************************ + */ +static void InitDRPMRepetition(SEIParameters *p_SEI) +{ + p_SEI->seiDRPMRepetition.data = malloc( sizeof(Bitstream) ); + if( p_SEI->seiDRPMRepetition.data == NULL ) + no_mem_exit("InitDRPMRepetition: p_SEI->seiDRPMRepetition.data"); + + p_SEI->seiDRPMRepetition.data->streamBuffer = malloc(MAXRTPPAYLOADLEN); + if( p_SEI->seiDRPMRepetition.data->streamBuffer == NULL ) + no_mem_exit("InitDRPMRepetition: p_SEI->seiDRPMRepetition.data->streamBuffer"); + + ClearDRPMRepetition(p_SEI); +} + +/* + ************************************************************************ + * \brief + * Clear dec_ref_pic_marking Repetition SEI data + ************************************************************************ + */ +void ClearDRPMRepetition(SEIParameters *p_SEI) +{ + memset( p_SEI->seiDRPMRepetition.data->streamBuffer, 0, MAXRTPPAYLOADLEN); + + p_SEI->seiDRPMRepetition.data->bits_to_go = 8; + p_SEI->seiDRPMRepetition.data->byte_pos = 0; + p_SEI->seiDRPMRepetition.data->byte_buf = 0; + p_SEI->seiDRPMRepetition.payloadSize = 0; + + p_SEI->seiDRPMRepetition.original_bottom_field_flag = FALSE; + p_SEI->seiDRPMRepetition.original_field_pic_flag = FALSE; + p_SEI->seiDRPMRepetition.original_frame_num = 0; + p_SEI->seiDRPMRepetition.original_idr_flag = FALSE; + if ( p_SEI->seiDRPMRepetition.dec_ref_pic_marking_buffer_saved != NULL ) + { + free_drpm_buffer( p_SEI->seiDRPMRepetition.dec_ref_pic_marking_buffer_saved ); + p_SEI->seiDRPMRepetition.dec_ref_pic_marking_buffer_saved = NULL; + } + + p_SEI->seiHasDRPMRepetition_info = FALSE; +} + +/* + ************************************************************************ + * \brief + * Update dec_ref_pic_marking Repetition data + ************************************************************************ + */ +void UpdateDRPMRepetition(SEIParameters *p_SEI) +{ + if ( p_SEI->seiDRPMRepetition.dec_ref_pic_marking_buffer_saved != NULL ) + { + p_SEI->seiHasDRPMRepetition_info = TRUE; + } + else + { + p_SEI->seiHasDRPMRepetition_info = FALSE; + } +} + +/* + ************************************************************************ + * \brief + * Finalize dec_ref_pic_marking Repetition SEI data + ************************************************************************ + */ +static void FinalizeDRPMRepetition(VideoParameters *p_Vid) +{ + SEIParameters *p_SEI = p_Vid->p_SEI; + seq_parameter_set_rbsp_t *active_sps = p_Vid->active_sps; + + Bitstream *bitstream = p_SEI->seiDRPMRepetition.data; + + // SEI message bits + write_u_1( "SEI: original_idr_flag", p_SEI->seiDRPMRepetition.original_idr_flag, bitstream); + write_ue_v( "SEI: original_frame_num", p_SEI->seiDRPMRepetition.original_frame_num, bitstream); + if ( !active_sps->frame_mbs_only_flag ) + { + write_u_1( "SEI: original_field_pic_flag", p_SEI->seiDRPMRepetition.original_field_pic_flag, bitstream); + if ( p_SEI->seiDRPMRepetition.original_field_pic_flag ) + { + write_u_1( "SEI: original_bottom_field_flag", p_SEI->seiDRPMRepetition.original_bottom_field_flag, bitstream); + } + } + // now repeat dec_ref_pic_marking_buffer info + dec_ref_pic_marking( bitstream, + p_SEI->seiDRPMRepetition.dec_ref_pic_marking_buffer_saved, + p_SEI->seiDRPMRepetition.original_idr_flag, 0, 0); + + // make sure the payload is byte aligned, stuff bits are 10..0 + if ( bitstream->bits_to_go != 8 ) + { + (bitstream->byte_buf) <<= 1; + bitstream->byte_buf |= 1; + bitstream->bits_to_go--; + if ( bitstream->bits_to_go != 0 ) + (bitstream->byte_buf) <<= (bitstream->bits_to_go); + bitstream->bits_to_go = 8; + bitstream->streamBuffer[bitstream->byte_pos++]=bitstream->byte_buf; + bitstream->byte_buf = 0; + } + p_SEI->seiDRPMRepetition.payloadSize = bitstream->byte_pos; +} + +/* + ************************************************************************ + * \brief + * Close dec_ref_pic_marking Repetition SEI data + ************************************************************************ + */ +static void CloseDRPMRepetition(SEIParameters *p_SEI) +{ + if (p_SEI->seiDRPMRepetition.data) + { + free(p_SEI->seiDRPMRepetition.data->streamBuffer); + free(p_SEI->seiDRPMRepetition.data); + } + p_SEI->seiDRPMRepetition.data = NULL; + if ( p_SEI->seiDRPMRepetition.dec_ref_pic_marking_buffer_saved != NULL ) + { + free_drpm_buffer( p_SEI->seiDRPMRepetition.dec_ref_pic_marking_buffer_saved ); + p_SEI->seiDRPMRepetition.dec_ref_pic_marking_buffer_saved = NULL; + } +} + +/*! + ***************************************************************************** + * \brief + * Prepare the aggregation sei message. + * + * \date + * September 10, 2002 + * + * \author + * Dong Tian tian@cs.tut.fi + *****************************************************************************/ +void PrepareAggregationSEIMessage(VideoParameters *p_Vid) +{ + SEIParameters *p_SEI = p_Vid->p_SEI; + Boolean has_aggregation_sei_message = FALSE; + + clear_sei_message(p_SEI, AGGREGATION_SEI); + + // prepare the sei message here + // write the spare picture sei payload to the aggregation sei message + if (p_SEI->seiHasSparePicture && p_Vid->type != B_SLICE) + { + FinalizeSpareMBMap(p_Vid); + assert(p_SEI->seiSparePicturePayload.data->byte_pos == p_SEI->seiSparePicturePayload.payloadSize); + write_sei_message(p_SEI, AGGREGATION_SEI, p_SEI->seiSparePicturePayload.data->streamBuffer, p_SEI->seiSparePicturePayload.payloadSize, SEI_SPARE_PIC); + has_aggregation_sei_message = TRUE; + } + // write the sub sequence information sei paylaod to the aggregation sei message + if (p_SEI->seiHasSubseqInfo) + { + FinalizeSubseqInfo(p_SEI, p_Vid->layer); + write_sei_message(p_SEI, AGGREGATION_SEI, p_SEI->seiSubseqInfo[p_Vid->layer].data->streamBuffer, p_SEI->seiSubseqInfo[p_Vid->layer].payloadSize, SEI_SUB_SEQ_INFO); + ClearSubseqInfoPayload(p_SEI, p_Vid->layer); + has_aggregation_sei_message = TRUE; + } + // write the sub sequence layer information sei paylaod to the aggregation sei message + if (p_SEI->seiHasSubseqLayerInfo && p_Vid->number == 0) + { + FinalizeSubseqLayerInfo(p_SEI); + write_sei_message(p_SEI, AGGREGATION_SEI, p_SEI->seiSubseqLayerInfo.data, p_SEI->seiSubseqLayerInfo.payloadSize, SEI_SUB_SEQ_LAYER_CHARACTERISTICS); + p_SEI->seiHasSubseqLayerInfo = FALSE; + has_aggregation_sei_message = TRUE; + } + // write the sub sequence characteristics payload to the aggregation sei message + if (p_SEI->seiHasSubseqChar) + { + FinalizeSubseqChar(p_SEI); + write_sei_message(p_SEI, AGGREGATION_SEI, p_SEI->seiSubseqChar.data->streamBuffer, p_SEI->seiSubseqChar.payloadSize, SEI_SUB_SEQ_CHARACTERISTICS); + ClearSubseqCharPayload(p_SEI); + has_aggregation_sei_message = TRUE; + } + // write the pan scan rectangle info sei playload to the aggregation sei message + if (p_SEI->seiHasPanScanRectInfo) + { + FinalizePanScanRectInfo(p_SEI); + write_sei_message(p_SEI, AGGREGATION_SEI, p_SEI->seiPanScanRectInfo.data->streamBuffer, p_SEI->seiPanScanRectInfo.payloadSize, SEI_PAN_SCAN_RECT); + ClearPanScanRectInfoPayload(p_SEI); + has_aggregation_sei_message = TRUE; + } + // write the arbitrary (unregistered) info sei playload to the aggregation sei message + if (p_SEI->seiHasUser_data_unregistered_info) + { + FinalizeUser_data_unregistered(p_SEI); + write_sei_message(p_SEI, AGGREGATION_SEI, p_SEI->seiUser_data_unregistered.data->streamBuffer, p_SEI->seiUser_data_unregistered.payloadSize, SEI_USER_DATA_UNREGISTERED); + ClearUser_data_unregistered(p_SEI); + has_aggregation_sei_message = TRUE; + } + // write the arbitrary (unregistered) info sei playload to the aggregation sei message + if (p_SEI->seiHasUser_data_registered_itu_t_t35_info) + { + FinalizeUser_data_registered_itu_t_t35(p_SEI); + write_sei_message(p_SEI, AGGREGATION_SEI, p_SEI->seiUser_data_registered_itu_t_t35.data->streamBuffer, p_SEI->seiUser_data_registered_itu_t_t35.payloadSize, SEI_USER_DATA_REGISTERED_ITU_T_T35); + ClearUser_data_registered_itu_t_t35(p_SEI); + has_aggregation_sei_message = TRUE; + } + // more aggregation sei payload is written here... + + // write the scene information SEI payload + if (p_SEI->seiHasSceneInformation) + { + FinalizeSceneInformation(p_SEI); + write_sei_message(p_SEI, AGGREGATION_SEI, p_SEI->seiSceneInformation.data->streamBuffer, p_SEI->seiSceneInformation.payloadSize, SEI_SCENE_INFO); + has_aggregation_sei_message = TRUE; + } + + if (p_SEI->seiHasTone_mapping) + { + FinalizeToneMapping(p_Vid); + write_sei_message(p_SEI, AGGREGATION_SEI, p_SEI->seiToneMapping.data->streamBuffer, p_SEI->seiToneMapping.payloadSize, SEI_TONE_MAPPING); + ClearToneMapping(p_SEI); + has_aggregation_sei_message = TRUE; + } + + if (p_SEI->seiHasPostFilterHints_info) + { + FinalizePostFilterHints(p_SEI); + write_sei_message(p_SEI, AGGREGATION_SEI, p_SEI->seiPostFilterHints.data->streamBuffer, p_SEI->seiPostFilterHints.payloadSize, SEI_POST_FILTER_HINTS); + has_aggregation_sei_message = TRUE; + } + + if (p_SEI->seiHasBufferingPeriod_info) + { + FinalizeBufferingPeriod(p_SEI, p_Vid->active_sps); + write_sei_message(p_SEI, AGGREGATION_SEI, p_SEI->seiBufferingPeriod.data->streamBuffer, p_SEI->seiBufferingPeriod.payloadSize, SEI_BUFFERING_PERIOD); + has_aggregation_sei_message = TRUE; + } + + if (p_SEI->seiHasPicTiming_info) + { + FinalizePicTiming(p_Vid); + write_sei_message(p_SEI, AGGREGATION_SEI, p_SEI->seiPicTiming.data->streamBuffer, p_SEI->seiPicTiming.payloadSize, SEI_PIC_TIMING); + has_aggregation_sei_message = TRUE; + } + + if (p_SEI->seiHasRecoveryPoint_info) + { + FinalizeRandomAccess(p_SEI); + write_sei_message(p_SEI, AGGREGATION_SEI, p_SEI->seiRecoveryPoint.data->streamBuffer, p_SEI->seiRecoveryPoint.payloadSize, SEI_RECOVERY_POINT); + ClearRandomAccess(p_SEI); + has_aggregation_sei_message = TRUE; + } + + if (p_SEI->seiHasDRPMRepetition_info) + { + FinalizeDRPMRepetition(p_Vid); + write_sei_message(p_SEI, AGGREGATION_SEI, p_SEI->seiDRPMRepetition.data->streamBuffer, p_SEI->seiDRPMRepetition.payloadSize, SEI_DEC_REF_PIC_MARKING_REPETITION); + has_aggregation_sei_message = TRUE; + ClearDRPMRepetition(p_SEI); // to reset the drpm buffer into NULL + } + + if (p_SEI->seiHasFramePackingArrangement_info) + { + FinalizeFramePackingArrangement(p_SEI, p_Vid->p_Inp); + write_sei_message(p_SEI, AGGREGATION_SEI, p_SEI->seiFramePackingArrangement.data->streamBuffer, p_SEI->seiFramePackingArrangement.payloadSize, SEI_FRAME_PACKING_ARRANGEMENT); + has_aggregation_sei_message = TRUE; + } + + // after all the sei payload is written + if (has_aggregation_sei_message) + { + finalize_sei_message(p_SEI, AGGREGATION_SEI); + } +} + +/*! + ************************************************************************ + * \brief + * Free dec_ref_pic_marking_bufffer structure + ************************************************************************ + */ +void free_drpm_buffer( DecRefPicMarking_t *pDRPM ) +{ + DecRefPicMarking_t *pTmp = pDRPM, *pPrev; + + if (pTmp == NULL) + return; + + while( pTmp->Next != NULL ) + { + pPrev = pTmp; + pTmp = pTmp->Next; + free( pPrev ); + } + free( pTmp ); +} + diff --git a/lencod/src/slice.c b/lencod/src/slice.c new file mode 100644 index 0000000..c4038e7 --- /dev/null +++ b/lencod/src/slice.c @@ -0,0 +1,2451 @@ +/*! + ************************************************************************************** + * \file + * slice.c + * \brief + * generate the slice header, setup the bit buffer for slices, + * and generates the slice NALU(s) + + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * - Thomas Stockhammer + * - Detlev Marpe + * - Stephan Wenger + * - Alexis Michael Tourapis + *************************************************************************************** + */ + +#include "contributors.h" + +#include +#include + +#include "global.h" +#include "header.h" +#include "nal.h" +#include "rtp.h" +#include "fmo.h" +#include "vlc.h" +#include "image.h" +#include "cabac.h" +#include "biariencode.h" +#include "elements.h" +#include "macroblock.h" +#include "memalloc.h" +#include "mode_decision_p8x8.h" +#include "symbol.h" +#include "context_ini.h" +#include "enc_statistics.h" +#include "ratectl.h" +#include "me_epzs.h" +#include "me_epzs_int.h" +#include "wp.h" +#include "slice.h" +#include "rdoq.h" +#include "wp_mcprec.h" +#include "q_offsets.h" +#include "conformance.h" +#include "list_reorder.h" +#include "md_common.h" +#include "mmco.h" +#include "mv_search.h" +#include "quant4x4.h" +#include "quant8x8.h" +#include "quantChroma.h" +#include "rdopt.h" +#include "rdopt_coding_state.h" +#include "lambda.h" +#include "intra4x4.h" +#include "intra8x8.h" +#include "intra16x16.h" +#include "mc_prediction.h" +#include "rd_intra_jm.h" +#include "rd_intra_jm444.h" + +// Local declarations +static Slice *malloc_slice(VideoParameters *p_Vid, InputParameters *p_Inp); +static Slice *malloc_slice_lite(VideoParameters *p_Vid, InputParameters *p_Inp); + +int allocate_block_mem(Slice *currSlice) +{ + int alloc_size = 0; + alloc_size += get_mem2Dint(&currSlice->tblk4x4, BLOCK_SIZE, BLOCK_SIZE); + alloc_size += get_mem2Dint(&currSlice->tblk16x16, MB_BLOCK_SIZE, MB_BLOCK_SIZE); + alloc_size += get_mem4Dint(&currSlice->i16blk4x4, BLOCK_SIZE, BLOCK_SIZE, BLOCK_SIZE, BLOCK_SIZE); + + return (alloc_size); +} + + +void free_block_mem(Slice *currSlice) +{ + if(currSlice->i16blk4x4) + free_mem4Dint(currSlice->i16blk4x4); + if(currSlice->tblk16x16) + free_mem2Dint(currSlice->tblk16x16); + if(currSlice->tblk4x4) + free_mem2Dint(currSlice->tblk4x4); +} + +/*! + *********************************************************************** + * \brief + * Initializes the p_Vid->nz_coeff + * \par Input: + * none + * \par Output: + * none + * \ side effects + * sets currSlice->p_Vid->nz_coef[][][][] to -1 + *********************************************************************** + */ +static void CAVLC_init(Slice *currSlice) +{ + memset(&currSlice->p_Vid->nz_coeff[0][0][0], 0, currSlice->PicSizeInMbs * 4 * (4 + currSlice->num_blk8x8_uv)* sizeof(int)); +} + +static int alloc_rddata(Slice *currSlice, RD_DATA *rd_data) +{ + int alloc_size = 0; + + alloc_size += get_mem3Dpel(&(rd_data->rec_mb), 3, MB_BLOCK_SIZE, MB_BLOCK_SIZE); + + alloc_size += get_mem_ACcoeff (currSlice->p_Vid, &(rd_data->cofAC)); + alloc_size += get_mem_DCcoeff (&(rd_data->cofDC)); + + if ((currSlice->slice_type != I_SLICE) && currSlice->slice_type != SI_SLICE) + { + alloc_size += get_mem5Dmv (&(rd_data->all_mv), 2, currSlice->max_num_references, 9, 4, 4); + } + + // Why is this stored as height_blk * width_blk? + alloc_size += get_mem2D((byte***)&(rd_data->ipredmode), currSlice->height_blk, currSlice->width_blk); + alloc_size += get_mem3D((byte****)&(rd_data->refar), 2, 4, 4); + + return alloc_size; +} + + +static void nullify_rddata(RD_DATA *rd_data) +{ + rd_data->rec_mb = NULL; + + rd_data->cofAC = NULL; + rd_data->cofDC = NULL; + rd_data->all_mv = NULL; + + rd_data->ipredmode = NULL; + rd_data->refar = NULL; + + rd_data->all_mv = NULL; + rd_data->bipred_mv = NULL; +} + +static void free_rddata(Slice *currSlice, RD_DATA *rd_data) +{ + if(rd_data->refar) + free_mem3D((byte***) rd_data->refar); + if(rd_data->ipredmode) + free_mem2D((byte**) rd_data->ipredmode); + + if ((currSlice->slice_type != I_SLICE) && currSlice->slice_type != SI_SLICE) + { + if(rd_data->all_mv) + free_mem5Dmv (rd_data->all_mv); + } + + if(rd_data->cofDC) + free_mem_DCcoeff (rd_data->cofDC); + if(rd_data->cofAC) + free_mem_ACcoeff (rd_data->cofAC); + + if(rd_data->rec_mb) + free_mem3Dpel(rd_data->rec_mb); +} + + +/*! + ************************************************************************ + * \brief + * This function generates the slice (and partition) header(s) + * + * \return number of bits used for the slice (and partition) header(s) + * + * \par Side effects: + * Adds slice/partition header symbols to the symbol buffer + * increments Picture->no_slices, allocates memory for the + * slice, sets p_Vid->currSlice + ************************************************************************ +*/ +static int start_slice(Slice *currSlice, StatParameters *cur_stats) +{ + VideoParameters *p_Vid = currSlice->p_Vid; + EncodingEnvironmentPtr eep; + Bitstream *currStream; + int header_len = 0; + int i; + int NumberOfPartitions = (currSlice->partition_mode == PAR_DP_1?1:3); + + //one partition for an IDR image + if(currSlice->idr_flag) + { + NumberOfPartitions = 1; + } + + RTPUpdateTimestamp (p_Vid, currSlice->frame_no); // this has no side effects, just leave it for all NALs + + for (i = 0; i < NumberOfPartitions; i++) + { + currStream = (currSlice->partArr[i]).bitstream; + + currStream->write_flag = 0; + if (i==0) // First partition + header_len += SliceHeader (currSlice); + else // Second/Third partition + header_len += Partition_BC_Header(currSlice, i); + + //! Initialize CABAC + if (currSlice->symbol_mode == CABAC) + { + eep = &((currSlice->partArr[i]).ee_cabac); + if (currStream->bits_to_go != 8) + header_len += currStream->bits_to_go; + writeVlcByteAlign(p_Vid, currStream, cur_stats); + + eep->p_Vid = p_Vid; + arienco_start_encoding(eep, currStream->streamBuffer, &(currStream->byte_pos)); + + arienco_reset_EC(eep); + } + else + { + // Initialize CA-VLC + CAVLC_init(currSlice); + } + } + + if(currSlice->symbol_mode == CABAC) + { + init_contexts(currSlice); + } + +#if MVC_EXTENSION_ENABLE + if(p_Vid->p_Inp->num_of_views > 1 && p_Vid->view_id == 1 && currSlice->nal_reference_idc && currSlice->slice_nr==0) + { + for(i=0; ilistXsize[0]; i++) + { + if(currSlice->listX[0][i]->view_id == 0) + break; + } + if(ilistXsize[0]) + p_Vid->enc_picture->ref_pic_na[0] = i; + for(i=0; ilistXsize[1]; i++) + { + if(currSlice->listX[1][i]->view_id == 0) + break; + } + if(ilistXsize[1]) + p_Vid->enc_picture->ref_pic_na[1] = i; + } +#endif + return header_len; +} + +/*! +************************************************************************ +* \brief +* This creates a NAL unit structures for all data partition of the slice +* +************************************************************************ +*/ +void create_slice_nalus(Slice *currSlice, int is_bottom) +{ + // KS: this is approx. max. allowed code picture size + //const int buffer_size = 500 + p_Vid->FrameSizeInMbs * (128 + 256 * p_Vid->bitdepth_luma + 512 * p_Vid->bitdepth_chroma); + int buffer_size = currSlice->partArr[0].bitstream->buffer_size; +#if (MVC_EXTENSION_ENABLE) + VideoParameters *p_Vid = currSlice->p_Vid; + //InputParameters *p_Inp = currSlice->p_Inp; +#endif + + int part; + NALU_t *nalu; + + for (part=0; part< currSlice->max_part_nr; part++) + { + if (currSlice->partArr[part].bitstream->write_flag) + { + nalu = AllocNALU(buffer_size); + currSlice->partArr[part].nal_unit = nalu; + nalu->startcodeprefix_len = 1+ (currSlice->start_mb_nr == 0 && part == 0 ?ZEROBYTES_SHORTSTARTCODE+1:ZEROBYTES_SHORTSTARTCODE); + nalu->forbidden_bit = 0; + +#if (MVC_EXTENSION_ENABLE) + if(p_Vid->num_of_layers == 2) //if(p_Inp->num_of_views==2) + { + nalu->priority_id = p_Vid->priority_id; + nalu->view_id = p_Vid->dpb_layer_id; + nalu->temporal_id = p_Vid->temporal_id; + nalu->anchor_pic_flag = p_Vid->anchor_pic_flag[is_bottom]; + nalu->inter_view_flag = p_Vid->inter_view_flag[is_bottom]; + nalu->non_idr_flag = !nalu->anchor_pic_flag; + nalu->reserved_one_bit = 1; + } +#endif + + if (currSlice->idr_flag) + { + nalu->nal_unit_type = NALU_TYPE_IDR; + nalu->nal_reference_idc = NALU_PRIORITY_HIGHEST; + } + else + { + //different nal header for different partitions + if(currSlice->partition_mode == 0) + { +#if (MVC_EXTENSION_ENABLE) + if(p_Vid->num_of_layers == 2) //if(p_Inp->num_of_views==2) + { + nalu->nal_unit_type = p_Vid->dpb_layer_id/*p_Vid->view_id*/==0 ? NALU_TYPE_SLICE : NALU_TYPE_SLC_EXT; + } + else + nalu->nal_unit_type = NALU_TYPE_SLICE; +#else + nalu->nal_unit_type = NALU_TYPE_SLICE; +#endif + } + else + { + nalu->nal_unit_type = (NaluType) (NALU_TYPE_DPA + part); + } + + if (currSlice->nal_reference_idc !=0) + { + nalu->nal_reference_idc = NALU_PRIORITY_HIGH; + } + else + { + nalu->nal_reference_idc = NALU_PRIORITY_DISPOSABLE; + } + } + } + else + { + currSlice->partArr[part].nal_unit = NULL; + } + } +} + +/*! + ************************************************************************ + * \brief + * This function terminates a slice (but doesn't write it out), + * the old terminate_slice (0) + * \return + * 0 if OK, \n + * 1 in case of error + * + ************************************************************************ + */ +static int terminate_slice(Macroblock *currMB, int lastslice, StatParameters *cur_stats ) +{ + Slice *currSlice = currMB->p_Slice; + VideoParameters *p_Vid = currMB->p_Vid; + + Bitstream *currStream; + NALU_t *currNalu; + EncodingEnvironmentPtr eep; + int part; + int tmp_stuffingbits = currMB->bits.mb_stuffing; + + if (currSlice->symbol_mode == CABAC) + write_terminating_bit (currSlice, 1); // only once, not for all partitions + + create_slice_nalus(currSlice, p_Vid->structure == BOTTOM_FIELD ? 1 : 0); + + for (part = 0; part < currSlice->max_part_nr; part++) + { + currStream = (currSlice->partArr[part]).bitstream; + currNalu = (currSlice->partArr[part]).nal_unit; + if (currStream->write_flag) + { + if (currSlice->symbol_mode == CAVLC) + { + SODBtoRBSP(currStream); + currNalu->len = RBSPtoEBSP(currNalu->buf, currStream->streamBuffer, currStream->byte_pos); + } + else // CABAC + { + eep = &((currSlice->partArr[part]).ee_cabac); + // terminate the arithmetic code + arienco_done_encoding(currMB, eep); + set_pic_bin_count(p_Vid, eep); + + currStream->bits_to_go = eep->Ebits_to_go; + currStream->byte_buf = 0; + + currNalu->len = RBSPtoEBSP(currNalu->buf, currStream->streamBuffer, currStream->byte_pos); + + // NumBytesInNALunit is: payload length + 1 byte header + p_Vid->bytes_in_picture += currNalu->len + 1; + + if (lastslice && (part==(currSlice->max_part_nr - 1))) + { + addCabacZeroWords(p_Vid, currNalu, cur_stats); + } + } // CABAC + } + } // partition loop + + if( currSlice->symbol_mode == CABAC ) + { + store_contexts(currSlice); + } + + cur_stats->bit_use_stuffing_bits[currSlice->slice_type] += currMB->bits.mb_stuffing - tmp_stuffingbits; + + return 0; +} + +static void init_bipred_enabled(VideoParameters *p_Vid) +{ + InputParameters *p_Inp = p_Vid->p_Inp; + int mode; + + memset(p_Vid->bipred_enabled, 0, sizeof(int)*MAXMODE); + if (p_Vid->currentSlice->slice_type != B_SLICE || !p_Inp->BiPredMotionEstimation) + return; + + for(mode = 1; mode < 5; mode++) + p_Vid->bipred_enabled[mode] = (p_Inp->BiPredSearch[mode - 1]) ? 1: 0; +} + +/*! +************************************************************************ +* \brief +* Encodes one slice +* \par +* returns the number of coded MBs in the SLice +************************************************************************ +*/ +int encode_one_slice (VideoParameters *p_Vid, int SliceGroupId, int TotalCodedMBs) +{ + InputParameters *p_Inp = p_Vid->p_Inp; + Boolean end_of_slice = FALSE; + + int len; + int NumberOfCodedMBs = 0; + Macroblock* currMB = NULL; + int CurrentMbAddr; + StatParameters *cur_stats = &p_Vid->enc_picture->stats; + Slice *currSlice = NULL; + + if( (p_Inp->separate_colour_plane_flag != 0) ) + { + change_plane_JV( p_Vid, p_Vid->colour_plane_id ); + } + + p_Vid->cod_counter = 0; + + CurrentMbAddr = FmoGetFirstMacroblockInSlice (p_Vid, SliceGroupId); + // printf ("\n\nEncode_one_slice: PictureID %d SliceGroupId %d SliceID %d FirstMB %d \n", p_Vid->frame_no, SliceGroupId, p_Vid->current_slice_nr, CurrentMbInScanOrder); + + p_Vid->enc_picture->temporal_layer = p_Vid->p_curr_frm_struct->temporal_layer; + init_slice (p_Vid, &currSlice, CurrentMbAddr); + currSlice->rdoq_motion_copy = 0; + init_bipred_enabled(p_Vid); + + // Initialize quantization functions based on rounding/quantization method + // Done here since we may wish to disable adaptive rounding on occasional intervals (even at a frame or gop level). + init_quant_4x4 (currSlice); + init_quant_8x8 (currSlice); + init_quant_Chroma(currSlice); + + currSlice->set_lagrangian_multipliers(currSlice); + + if (currSlice->symbol_mode == CABAC) + { + SetCtxModelNumber (currSlice); + } + + p_Vid->checkref = (short) (p_Inp->rdopt && p_Inp->RestrictRef && (currSlice->slice_type==P_SLICE || currSlice->slice_type==SP_SLICE)); + + len = start_slice (currSlice, cur_stats); + + // Rate control + if (p_Inp->RCEnable) + rc_store_slice_header_bits( p_Vid, p_Inp, len ); + + // Update statistics + p_Vid->p_Stats->bit_slice += len; + cur_stats->bit_use_header[currSlice->slice_type] += len; + + if(currSlice->UseRDOQuant == 1 && currSlice->RDOQ_QP_Num > 1) + get_dQP_table(currSlice); + + while (end_of_slice == FALSE) // loop over macroblocks + { + Boolean recode_macroblock = FALSE; + if (p_Vid->AdaptiveRounding && p_Inp->AdaptRndPeriod && (p_Vid->current_mb_nr % p_Inp->AdaptRndPeriod == 0)) + { + CalculateOffset4x4Param(p_Vid); + if(p_Inp->Transform8x8Mode) + CalculateOffset8x8Param(p_Vid); + } + + if(currSlice->UseRDOQuant) // This needs revisit + currSlice->rddata = &currSlice->rddata_trellis_curr; + else + currSlice->rddata = &currSlice->rddata_top_frame_mb; // store data in top frame MB + + start_macroblock (currSlice, &currMB, CurrentMbAddr, FALSE); + + + if(currSlice->UseRDOQuant) + { + trellis_coding(currMB); + } + else + { + p_Vid->masterQP = p_Vid->qp; + + currSlice->encode_one_macroblock (currMB); + end_encode_one_macroblock(currMB); + + write_macroblock (currMB, 1); + } + + end_macroblock (currMB, &end_of_slice, &recode_macroblock); + currMB->prev_recode_mb = recode_macroblock; + // printf ("encode_one_slice: mb %d, slice %d, bitbuf bytepos %d EOS %d\n", + // p_Vid->current_mb_nr, p_Vid->current_slice_nr, + // currSlice->partArr[0].bitstream->byte_pos, end_of_slice); + + if (recode_macroblock == FALSE) // The final processing of the macroblock has been done + { + p_Vid->SumFrameQP += currMB->qp; + CurrentMbAddr = FmoGetNextMBNr (p_Vid, CurrentMbAddr); + if (CurrentMbAddr == -1) // end of slice + { + // printf ("FMO End of Slice Group detected, current MBs %d, force end of slice\n", NumberOfCodedMBs+1); + end_of_slice = TRUE; + } + NumberOfCodedMBs++; // only here we are sure that the coded MB is actually included in the slice + next_macroblock (currMB); + } + else + { + //!Go back to the previous MB to recode it + p_Vid->current_mb_nr = FmoGetPreviousMBNr(p_Vid, p_Vid->current_mb_nr); + p_Vid->NumberofCodedMacroBlocks--; + if(p_Vid->current_mb_nr == -1 ) // The first MB of the slice group is too big, + // which means it's impossible to encode picture using current slice bits restriction + { + snprintf (errortext, ET_SIZE, "Error encoding first MB with specified parameter, bits of current MB may be too big"); + error (errortext, 300); + } + } + } + + + if ((p_Inp->WPIterMC) && (p_Vid->frameOffsetAvail == 0) && p_Vid->nal_reference_idc) + { + compute_offset(currSlice); + } + p_Vid->num_ref_idx_l0_active = currSlice->num_ref_idx_active[LIST_0]; + p_Vid->num_ref_idx_l1_active = currSlice->num_ref_idx_active[LIST_1]; + + terminate_slice (currMB, (NumberOfCodedMBs + TotalCodedMBs >= (int)p_Vid->PicSizeInMbs), cur_stats ); + return NumberOfCodedMBs; +} + + +/*! +************************************************************************ +* \brief +* Encodes one slice (MBAFF Frame) +* \par +* returns the number of coded MBs in the SLice +************************************************************************ +*/ +int encode_one_slice_MBAFF (VideoParameters *p_Vid, int SliceGroupId, int TotalCodedMBs) +{ + InputParameters *p_Inp = p_Vid->p_Inp; + Boolean end_of_slice = FALSE; + Boolean recode_macroblock; + int len; + int NumberOfCodedMBs = 0; + Macroblock* currMB = NULL; + int CurrentMbAddr; + double FrameRDCost = DBL_MAX, FieldRDCost = DBL_MAX; + StatParameters *cur_stats = &p_Vid->enc_picture->stats; + Slice *currSlice = NULL; + + if( (p_Inp->separate_colour_plane_flag != 0) ) + { + change_plane_JV( p_Vid, p_Vid->colour_plane_id ); + } + + p_Vid->cod_counter = 0; + + CurrentMbAddr = FmoGetFirstMacroblockInSlice (p_Vid, SliceGroupId); + // printf ("\n\nEncode_one_slice: PictureID %d SliceGroupId %d SliceID %d FirstMB %d \n", p_Vid->frame_no, SliceGroupId, p_Vid->current_slice_nr, CurrentMbInScanOrder); + + init_slice (p_Vid, &currSlice, CurrentMbAddr); + currSlice->rdoq_motion_copy = 0; + init_bipred_enabled(p_Vid); + + // Initialize quantization functions based on rounding/quantization method + // Done here since we may wish to disable adaptive rounding on occasional intervals (even at a frame or gop level). + init_quant_4x4 (currSlice); + init_quant_8x8 (currSlice); + init_quant_Chroma(currSlice); + + currSlice->set_lagrangian_multipliers(currSlice); + + if (currSlice->symbol_mode == CABAC) + { + SetCtxModelNumber (currSlice); + } + + p_Vid->checkref = (short) (p_Inp->rdopt && p_Inp->RestrictRef && (currSlice->slice_type == P_SLICE || currSlice->slice_type == SP_SLICE)); + + len = start_slice (currSlice, cur_stats); + + // Rate control + if (p_Inp->RCEnable) + rc_store_slice_header_bits( p_Vid, p_Inp, len ); + + // Update statistics + p_Vid->p_Stats->bit_slice += len; + cur_stats->bit_use_header[currSlice->slice_type] += len; + + while (end_of_slice == FALSE) // loop over macroblocks + { + + if (p_Vid->AdaptiveRounding && p_Inp->AdaptRndPeriod && (p_Vid->current_mb_nr % p_Inp->AdaptRndPeriod == 0)) + { + CalculateOffset4x4Param(p_Vid); + if(p_Inp->Transform8x8Mode) + CalculateOffset8x8Param(p_Vid); + } + + //! This following ugly code breaks slices, at least for a slice mode that accumulates a certain + //! number of bits into one slice. + //! The suggested algorithm is as follows: + //! + //! SaveState (Bitstream, stats, etc. etc.); + //! BitsForThisMBPairInFrameMode = CodeMB (Upper, FRAME_MODE) + CodeMB (Lower, FRAME_MODE); + //! DistortionForThisMBPairInFrameMode = CalculateDistortion(Upper) + CalculateDistortion (Lower); + //! RestoreState(); + //! BitsForThisMBPairInFieldMode = CodeMB (Upper, FIELD_MODE) + CodeMB (Lower, FIELD_MODE); + //! DistortionForThisMBPairInFrameMode = CalculateDistortion(Upper) + CalculateDistortion (Lower); + //! FrameFieldMode = Decision (...) + //! RestoreState() + //! if (FrameFieldMode == FRAME) { + //! CodeMB (Upper, FRAME); CodeMB (Lower, FRAME); + //! } else { + //! CodeMB (Upper FIELD); CodeMB (Lower, FIELD); + //! } + //! + //! Open questions/issues: + //! 1. CABAC/CA-VLC state: It seems that the CABAC/CA_VLC states are changed during the + //! dummy encoding processes (for the R-D based selection), but that they are never + //! reset, once the selection is made. I believe that this breaks the MB-adaptive + //! frame/field coding. The necessary code for the state saves is readily available + //! in macroblock.c, start_macroblock() and end_macroblock() (this code needs + //! to be double checked that it works with CA-VLC as well + //! 2. would it be an option to allocate Bitstreams with zero data in them (or copy the + //! already generated bitstream) for the "test coding"? + + p_Vid->write_macroblock = FALSE; + if (p_Inp->MbInterlace == ADAPTIVE_CODING || p_Inp->MbInterlace == FRAME_MB_PAIR_CODING) + { + //================ code MB pair as frame MB ================ + //---------------------------------------------------------- + recode_macroblock = FALSE; + //set mv limits to frame type + update_mv_limits(p_Vid, FALSE); + + p_Vid->field_mode = FALSE; // MB coded as frame + p_Vid->top_field = FALSE; // Set top field to 0 + + //Rate control + p_Vid->write_macroblock = FALSE; + p_Vid->bot_MB = FALSE; + + // save RC state only when it is going to change + if ( p_Inp->RCEnable && p_Inp->RCUpdateMode <= MAX_RC_MODE ) + { + if ( p_Inp->MbInterlace == ADAPTIVE_CODING + && p_Vid->NumberofCodedMacroBlocks > 0 && (p_Vid->NumberofCodedMacroBlocks % p_Vid->BasicUnit) == 0 ) + rc_copy_quadratic( p_Vid, p_Inp, p_Vid->p_rc_quad_init, p_Vid->p_rc_quad ); // save initial RC status + if ( p_Inp->MbInterlace == ADAPTIVE_CODING ) + rc_copy_generic( p_Vid, p_Vid->p_rc_gen_init, p_Vid->p_rc_gen ); // save initial RC status + } + + start_macroblock (currSlice, &currMB, CurrentMbAddr, FALSE); + + currSlice->rddata = &currSlice->rddata_top_frame_mb; // store data in top frame MB + p_Vid->masterQP = p_Vid->qp; + currSlice->encode_one_macroblock (currMB); // code the MB as frame + end_encode_one_macroblock(currMB); + + FrameRDCost = currSlice->rddata->min_rdcost; + //*** Top MB coded as frame MB ***// + + //Rate control + p_Vid->bot_MB = TRUE; //for Rate control + + // go to the bottom MB in the MB pair + p_Vid->field_mode = FALSE; // MB coded as frame //GB + + start_macroblock (currSlice, &currMB, CurrentMbAddr + 1, FALSE); + currSlice->rddata = &currSlice->rddata_bot_frame_mb; // store data in top frame MB + p_Vid->masterQP = p_Vid->qp; + currSlice->encode_one_macroblock (currMB); // code the MB as frame + end_encode_one_macroblock(currMB); + + if ( p_Inp->RCEnable && p_Inp->RCUpdateMode <= MAX_RC_MODE ) + { + if ( p_Inp->MbInterlace == ADAPTIVE_CODING + && p_Vid->NumberofCodedMacroBlocks > 0 && (p_Vid->NumberofCodedMacroBlocks % p_Vid->BasicUnit) == 0 ) + rc_copy_quadratic( p_Vid, p_Inp, p_Vid->p_rc_quad_best, p_Vid->p_rc_quad ); // restore initial RC status + + if ( p_Inp->MbInterlace == ADAPTIVE_CODING ) + rc_copy_generic( p_Vid, p_Vid->p_rc_gen_best, p_Vid->p_rc_gen ); // save frame RC stats + } + + FrameRDCost += currSlice->rddata->min_rdcost; + //*** Bottom MB coded as frame MB ***// + } + + if ((p_Inp->MbInterlace == ADAPTIVE_CODING) || (p_Inp->MbInterlace == FIELD_CODING)) + { + //Rate control + p_Vid->bot_MB = FALSE; + //set mv limits to field type + update_mv_limits(p_Vid, TRUE); + + //=========== start coding the MB pair as a field MB pair ============= + //--------------------------------------------------------------------- + p_Vid->field_mode = TRUE; // MB coded as field + p_Vid->top_field = TRUE; // Set top field to 1 + p_Inp->num_ref_frames <<= 1; + currSlice->num_ref_idx_active[LIST_0] <<= 1; + currSlice->num_ref_idx_active[LIST_0] += 1; + + if ( p_Inp->RCEnable && p_Inp->RCUpdateMode <= MAX_RC_MODE ) + { + if ( p_Inp->MbInterlace == ADAPTIVE_CODING + && p_Vid->NumberofCodedMacroBlocks > 0 && (p_Vid->NumberofCodedMacroBlocks % p_Vid->BasicUnit) == 0 ) + rc_copy_quadratic( p_Vid, p_Inp, p_Vid->p_rc_quad, p_Vid->p_rc_quad_init ); // restore initial RC status + + if ( p_Inp->MbInterlace == ADAPTIVE_CODING ) + rc_copy_generic( p_Vid, p_Vid->p_rc_gen, p_Vid->p_rc_gen_init ); // reset RC stats + } + + start_macroblock (currSlice, &currMB, CurrentMbAddr, TRUE); + + currSlice->rddata = &currSlice->rddata_top_field_mb; // store data in top frame MB + // TopFieldIsSkipped = 0; // set the top field MB skipped flag to 0 + p_Vid->masterQP = p_Vid->qp; + currSlice->encode_one_macroblock (currMB); // code the MB as field + end_encode_one_macroblock(currMB); + + FieldRDCost = currSlice->rddata->min_rdcost; + //*** Top MB coded as field MB ***// + //Rate control + p_Vid->bot_MB = TRUE;//for Rate control + + p_Vid->top_field = FALSE; // Set top field to 0 + start_macroblock (currSlice, &currMB, CurrentMbAddr+1, TRUE); + currSlice->rddata = &currSlice->rddata_bot_field_mb; // store data in top frame MB + p_Vid->masterQP = p_Vid->qp; + currSlice->encode_one_macroblock (currMB); // code the MB as field + end_encode_one_macroblock(currMB); + + FieldRDCost += currSlice->rddata->min_rdcost; + //*** Bottom MB coded as field MB ***// + } + + //Rate control + p_Vid->write_mbaff_frame = 0; //Rate control + + //=========== decide between frame/field MB pair ============ + //----------------------------------------------------------- + if ( ((p_Inp->MbInterlace == ADAPTIVE_CODING) && (FrameRDCost < FieldRDCost)) || p_Inp->MbInterlace == FRAME_MB_PAIR_CODING ) + { + p_Vid->field_mode = FALSE; + p_Vid->MBPairIsField = FALSE; + + if ( p_Inp->MbInterlace != FRAME_MB_PAIR_CODING ) + { + p_Inp->num_ref_frames >>= 1; + currSlice->num_ref_idx_active[LIST_0] -= 1; + currSlice->num_ref_idx_active[LIST_0] >>= 1; + } + + if ( p_Inp->RCEnable && p_Inp->RCUpdateMode <= MAX_RC_MODE ) + { + if ( p_Inp->MbInterlace == ADAPTIVE_CODING + && p_Vid->NumberofCodedMacroBlocks > 0 && (p_Vid->NumberofCodedMacroBlocks % p_Vid->BasicUnit) == 0 ) + rc_copy_quadratic( p_Vid, p_Inp, p_Vid->p_rc_quad, p_Vid->p_rc_quad_best ); // restore initial RC status + + if ( p_Inp->MbInterlace == ADAPTIVE_CODING ) + rc_copy_generic( p_Vid, p_Vid->p_rc_gen, p_Vid->p_rc_gen_best ); // restore frame RC stats + } + + //Rate control + p_Vid->write_mbaff_frame = 1; //for Rate control + } + else + { + p_Vid->field_mode = TRUE; + p_Vid->MBPairIsField = TRUE; + } + + //Rate control + p_Vid->write_macroblock = TRUE;//Rate control + + if (p_Vid->MBPairIsField) + p_Vid->top_field = TRUE; + else + p_Vid->top_field = FALSE; + + //Rate control + p_Vid->bot_MB = FALSE;// for Rate control + + // go back to the Top MB in the MB pair + start_macroblock (currSlice, &currMB, CurrentMbAddr, p_Vid->field_mode); + + currSlice->rddata = p_Vid->field_mode ? &currSlice->rddata_top_field_mb : &currSlice->rddata_top_frame_mb; + copy_rdopt_data (currMB); // copy the MB data for Top MB from the temp buffers + write_macroblock (currMB, 1); // write the Top MB data to the bitstream + end_macroblock (currMB, &end_of_slice, &recode_macroblock); // done coding the Top MB + currMB->prev_recode_mb = recode_macroblock; + + if (recode_macroblock == FALSE) // The final processing of the macroblock has been done + { + p_Vid->SumFrameQP += currMB->qp; + + CurrentMbAddr = FmoGetNextMBNr (p_Vid, CurrentMbAddr); + if (CurrentMbAddr == -1) // end of slice + { + end_of_slice = TRUE; + } + NumberOfCodedMBs++; // only here we are sure that the coded MB is actually included in the slice + next_macroblock (currMB); + + //Rate control + p_Vid->bot_MB = TRUE;//for Rate control + // go to the Bottom MB in the MB pair + p_Vid->top_field = FALSE; + start_macroblock (currSlice, &currMB, CurrentMbAddr, p_Vid->field_mode); + + currSlice->rddata = p_Vid->field_mode ? &currSlice->rddata_bot_field_mb : &currSlice->rddata_bot_frame_mb; + copy_rdopt_data (currMB); // copy the MB data for Bottom MB from the temp buffers + + write_macroblock (currMB, 0); // write the Bottom MB data to the bitstream + end_macroblock (currMB, &end_of_slice, &recode_macroblock); // done coding the Top MB + currMB->prev_recode_mb = recode_macroblock; + if (recode_macroblock == FALSE) // The final processing of the macroblock has been done + { + p_Vid->SumFrameQP += currMB->qp; + + CurrentMbAddr = FmoGetNextMBNr (p_Vid, CurrentMbAddr); + if (CurrentMbAddr == -1) // end of slice + { + end_of_slice = TRUE; + } + NumberOfCodedMBs++; // only here we are sure that the coded MB is actually included in the slice + next_macroblock (currMB); + } + else + { + //Go back to the beginning of the macroblock pair to recode it + p_Vid->current_mb_nr = FmoGetPreviousMBNr(p_Vid, p_Vid->current_mb_nr); + p_Vid->NumberofCodedMacroBlocks -= 2; + if(p_Vid->current_mb_nr == -1 ) // The first MB of the slice group is too big, + // which means it's impossible to encode picture using current slice bits restriction + { + snprintf (errortext, ET_SIZE, "Error encoding first MB with specified parameter, bits of current MB may be too big"); + error (errortext, 300); + } + } + } + else + { + //!Go back to the previous MB to recode it + p_Vid->current_mb_nr = FmoGetPreviousMBNr(p_Vid, p_Vid->current_mb_nr); + p_Vid->NumberofCodedMacroBlocks--; + if(p_Vid->current_mb_nr == -1 ) // The first MB of the slice group is too big, + // which means it's impossible to encode picture using current slice bits restriction + { + snprintf (errortext, ET_SIZE, "Error encoding first MB with specified parameter, bits of current MB may be too big"); + error (errortext, 300); + } + } + + if (p_Vid->MBPairIsField) // if MB Pair was coded as field the buffer size variables back to frame mode + { + p_Inp->num_ref_frames >>= 1; + currSlice->num_ref_idx_active[LIST_0] -= 1; + currSlice->num_ref_idx_active[LIST_0] >>= 1; + } + + p_Vid->field_mode = p_Vid->top_field = FALSE; // reset to frame mode + + if ( !end_of_slice ) + { + assert( CurrentMbAddr < (int)p_Vid->PicSizeInMbs ); + assert( CurrentMbAddr >= 0 ); + if (CurrentMbAddr == FmoGetLastCodedMBOfSliceGroup (p_Vid, FmoMB2SliceGroup (p_Vid, CurrentMbAddr))) + end_of_slice = TRUE; // just in case it doesn't get set in end_macroblock + } + } + + p_Vid->num_ref_idx_l0_active = currSlice->num_ref_idx_active[LIST_0]; + p_Vid->num_ref_idx_l1_active = currSlice->num_ref_idx_active[LIST_1]; + + terminate_slice (currMB, (NumberOfCodedMBs + TotalCodedMBs >= (int)p_Vid->PicSizeInMbs), cur_stats ); + return NumberOfCodedMBs; +} + +static void setup_cabac(Slice *currSlice, char *listXsize) +{ + seq_parameter_set_rbsp_t *active_sps = currSlice->active_sps; + int i; + + if (currSlice->slice_type == I_SLICE) + { + currSlice->set_modes_and_reframe = set_modes_and_reframe_i_slice; + currSlice->store_8x8_motion_vectors = NULL; + currSlice->writeMB_Skip = NULL; + currSlice->writeMB_typeInfo = writeMB_I_typeInfo_CABAC; + currSlice->writeB8_typeInfo = writeB8_typeInfo_CABAC; + currSlice->writeMotionInfo2NAL = NULL; + currSlice->write_MB_layer = write_i_slice_MB_layer; + for (i=0; i<6; i++) + { + currSlice->writeRefFrame[i] = NULL; + } + } + else if (currSlice->slice_type == B_SLICE) + { + currSlice->set_modes_and_reframe = set_modes_and_reframe_b_slice; + currSlice->store_8x8_motion_vectors = store_8x8_motion_vectors_b_slice; + currSlice->writeMB_Skip = writeMB_Bskip_flagInfo_CABAC; + currSlice->writeMB_typeInfo = writeMB_B_typeInfo_CABAC; + currSlice->writeB8_typeInfo = writeB8_B_typeInfo_CABAC; + currSlice->writeMotionInfo2NAL = write_b_slice_motion_info_to_NAL; + currSlice->write_MB_layer = write_b_slice_MB_layer; + for (i=0; i<6; i++) + { + switch (listXsize[i]) + { + case 0: + currSlice->writeRefFrame[i] = NULL; + break; + case 1: + currSlice->writeRefFrame[i] = writeRefPic_Dummy; + break; + default: + currSlice->writeRefFrame[i] = writeRefPic_B_CABAC; + } + } + } + else + { + currSlice->set_modes_and_reframe = set_modes_and_reframe_p_slice; + currSlice->store_8x8_motion_vectors = store_8x8_motion_vectors_p_slice; + currSlice->writeMB_Skip = writeMB_Pskip_flagInfo_CABAC; + currSlice->writeMB_typeInfo = writeMB_P_typeInfo_CABAC; + currSlice->writeB8_typeInfo = writeB8_typeInfo_CABAC; + currSlice->writeMotionInfo2NAL = write_p_slice_motion_info_to_NAL; + currSlice->write_MB_layer = write_p_slice_MB_layer; + + for (i=0; i<6; i++) + { + switch (listXsize[i]) + { + case 0: + currSlice->writeRefFrame[i] = NULL; + break; + case 1: + currSlice->writeRefFrame[i] = writeRefPic_Dummy; + break; + default: + currSlice->writeRefFrame[i] = writeRefPic_P_CABAC; + } + } + } + + currSlice->writeIntraPredMode = writeIntraPredMode_CABAC; + currSlice->writeCoeff16x16 = writeCoeff16x16_CABAC; + currSlice->writeMVD = writeMVD_CABAC; + currSlice->writeCBP = writeCBP_CABAC; + currSlice->writeDquant = writeDquant_CABAC; + currSlice->writeCIPredMode = writeCIPredMode_CABAC; + currSlice->writeFieldModeInfo = writeFieldModeInfo_CABAC; + currSlice->writeMB_transform_size = writeMB_transform_size_CABAC; + + if (active_sps->chroma_format_idc == YUV444) + currSlice->write_and_store_CBP_block_bit = write_and_store_CBP_block_bit_444; + else + currSlice->write_and_store_CBP_block_bit = write_and_store_CBP_block_bit; + + memset(currSlice->coeff, 0 , 64 * sizeof(int)); + currSlice->coeff_ctr = 0; + currSlice->pos = 0; +} + +static void setup_cavlc(Slice *currSlice, char *listXsize) +{ + seq_parameter_set_rbsp_t *active_sps = currSlice->active_sps; + + int i; + currSlice->writeMB_typeInfo = writeUVLC_CAVLC; + currSlice->writeIntraPredMode = writeIntraPredMode_CAVLC; + currSlice->writeB8_typeInfo = writeSE_UVLC; + currSlice->writeCoeff16x16 = writeCoeff16x16_CAVLC; + for (i=0; i<6; i++) + { + switch (listXsize[i]) + { + case 0: + currSlice->writeRefFrame[i] = NULL; + break; + case 1: + currSlice->writeRefFrame[i] = writeRefPic_Dummy; + break; + case 2: + currSlice->writeRefFrame[i] = writeRefPic_2Ref_CAVLC; + break; + default: + currSlice->writeRefFrame[i] = writeRefPic_NRef_CAVLC; + break; + } + } + + if (currSlice->slice_type == I_SLICE || currSlice->slice_type == SI_SLICE) + { + currSlice->set_modes_and_reframe = set_modes_and_reframe; + currSlice->store_8x8_motion_vectors = NULL; + currSlice->writeMotionInfo2NAL = NULL; + currSlice->write_MB_layer = write_i_slice_MB_layer; + } + else if (currSlice->slice_type == B_SLICE) + { + currSlice->set_modes_and_reframe = set_modes_and_reframe_b_slice; + currSlice->store_8x8_motion_vectors = store_8x8_motion_vectors_b_slice; + currSlice->writeMotionInfo2NAL = write_b_slice_motion_info_to_NAL; + currSlice->write_MB_layer = write_b_slice_MB_layer; + } + else + { + currSlice->set_modes_and_reframe = set_modes_and_reframe_p_slice; + currSlice->store_8x8_motion_vectors = store_8x8_motion_vectors_p_slice; + currSlice->writeMotionInfo2NAL = write_p_slice_motion_info_to_NAL; + currSlice->write_MB_layer = write_p_slice_MB_layer; + } + + currSlice->writeMVD = writeSVLC_CAVLC; + currSlice->writeCBP = writeCBP_VLC; + currSlice->writeDquant = writeSVLC_CAVLC; + currSlice->writeCIPredMode = writeUVLC_CAVLC; + currSlice->writeFieldModeInfo = writeFlag_CAVLC; + currSlice->writeMB_transform_size = writeFlag_CAVLC; + + // We should move this to the sequence level and create a new function + if (active_sps->chroma_format_idc == YUV444) + currSlice->writeCoeff4x4_CAVLC = writeCoeff4x4_CAVLC_444; + else + currSlice->writeCoeff4x4_CAVLC = writeCoeff4x4_CAVLC_normal; +} + +static void setup_slice(Slice *currSlice) +{ + if (currSlice->P444_joined) + { + currSlice->luma_residual_coding = luma_residual_coding_p444; + currSlice->luma_residual_coding_8x8 = luma_residual_coding_p444_8x8; + } + else + { + if (currSlice->slice_type == SP_SLICE) + currSlice->luma_residual_coding = luma_residual_coding_sp; + else + currSlice->luma_residual_coding = luma_residual_coding; + currSlice->luma_residual_coding_8x8 = luma_residual_coding_8x8; + } + + if (currSlice->slice_type == SP_SLICE) + currSlice->chroma_residual_coding = chroma_residual_coding_sp; + else if (currSlice->slice_type == SI_SLICE) + currSlice->chroma_residual_coding = chroma_residual_coding_si; + else + currSlice->chroma_residual_coding = chroma_residual_coding; + + if (currSlice->slice_type == P_SLICE || currSlice->slice_type == SP_SLICE) + { + currSlice->weighted_prediction = (byte) currSlice->p_Vid->active_pps->weighted_pred_flag; + currSlice->set_modes_and_refs_for_blocks = set_modes_and_refs_for_blocks_p_slice; + currSlice->set_coeff_and_recon_8x8 = set_coeff_and_recon_8x8_p_slice; + currSlice->init_lists = init_lists_p_slice; + currSlice->submacroblock_mode_decision = submacroblock_mode_decision_p_slice; + currSlice->set_motion_vectors_mb = SetMotionVectorsMBPSlice; + } + else if (currSlice->slice_type == B_SLICE) + { + currSlice->weighted_prediction = (byte) currSlice->p_Vid->active_pps->weighted_bipred_idc; + currSlice->set_modes_and_refs_for_blocks = set_modes_and_refs_for_blocks_b_slice; + currSlice->set_coeff_and_recon_8x8 = set_coeff_and_recon_8x8_b_slice; + currSlice->init_lists = init_lists_b_slice; + currSlice->submacroblock_mode_decision = submacroblock_mode_decision_b_slice; + currSlice->set_motion_vectors_mb = SetMotionVectorsMBBSlice; + } + else + { + currSlice->set_modes_and_refs_for_blocks = set_modes_and_refs_for_blocks_i_slice; + currSlice->set_coeff_and_recon_8x8 = NULL; + currSlice->init_lists = init_lists_i_slice; + currSlice->submacroblock_mode_decision = NULL; + currSlice->set_motion_vectors_mb = SetMotionVectorsMBISlice; + } + + if (currSlice->mb_aff_frame_flag) + { + currSlice->set_intrapred_4x4 = set_intrapred_4x4_mbaff; + currSlice->set_intrapred_8x8 = set_intrapred_8x8_mbaff; + currSlice->set_intrapred_16x16 = set_intrapred_16x16_mbaff; + currSlice->rdo_low_intra_chroma_decision = rdo_low_intra_chroma_decision_mbaff; + currSlice->intra_chroma_prediction = intra_chroma_prediction_mbaff; + if (currSlice->direct_spatial_mv_pred_flag) //spatial direct + currSlice->Get_Direct_Motion_Vectors = Get_Direct_MV_Spatial_MBAFF; + else + currSlice->Get_Direct_Motion_Vectors = Get_Direct_MV_Temporal; + } + else + { + currSlice->set_intrapred_4x4 = set_intrapred_4x4; + currSlice->set_intrapred_8x8 = set_intrapred_8x8; + currSlice->set_intrapred_16x16 = set_intrapred_16x16; + currSlice->rdo_low_intra_chroma_decision = rdo_low_intra_chroma_decision; + currSlice->intra_chroma_prediction = intra_chroma_prediction; + if (currSlice->direct_spatial_mv_pred_flag) //spatial direct + currSlice->Get_Direct_Motion_Vectors = Get_Direct_MV_Spatial_Normal; + else + currSlice->Get_Direct_Motion_Vectors = Get_Direct_MV_Temporal; + } + + if (currSlice->active_sps->chroma_format_idc == YUV444) + currSlice->mode_decision_for_I16x16_MB = mode_decision_for_I16x16_MB_444; + else if(currSlice->p_Inp->I16rdo) + currSlice->mode_decision_for_I16x16_MB = mode_decision_for_I16x16_MB_RDO; + else + currSlice->mode_decision_for_I16x16_MB = mode_decision_for_I16x16_MB; +} + +static void ClipWPParams(Slice *currSlice ) +{ + int list, ref, comp; + + for(list = 0; list <= (currSlice->slice_type == B_SLICE ? LIST_1:LIST_0); list++) + { + for(ref = 0; ref < currSlice->listXsize[list]; ref++) + { + for(comp = PLANE_Y; comp <= PLANE_V; comp++) + { + currSlice->wp_weight[list][ref][comp] = sClip3(-128, 127, currSlice->wp_weight[list][ref][comp]); + currSlice->wp_offset[list][ref][comp] = sClip3(-128, 127, currSlice->wp_offset[list][ref][comp]); + } + } + } +} + +void setup_open_gop_lists(VideoParameters *p_Vid, Slice *currSlice) +{ + int i, j; + + if (currSlice->slice_type != I_SLICE && currSlice->slice_type != SI_SLICE) + { + for (i = 0; i < currSlice->listXsize[0]; i++) + { + if (currSlice->listX[LIST_0][i]->poc < p_Vid->last_valid_reference && p_Vid->ThisPOC >= p_Vid->last_valid_reference) + { + //remove invalid reference from list + for (j = i; j < currSlice->listXsize[LIST_0]-1; j++) + { + currSlice->listX[LIST_0][j] = currSlice->listX[LIST_0][j+1]; + } + currSlice->listXsize[LIST_0]--; + i--; + } + } + + for (i = 0; i < currSlice->listXsize[LIST_1]; i++) + { + if (currSlice->listX[LIST_1][i]->poc < p_Vid->last_valid_reference && p_Vid->ThisPOC >= p_Vid->last_valid_reference) + { + //remove invalid reference from list + for (j = i; j < currSlice->listXsize[LIST_1]-1; j++) + { + currSlice->listX[LIST_1][j] = currSlice->listX[LIST_1][j+1]; + } + currSlice->listXsize[LIST_1]--; + i--; + } + } + + // Below code is not the best but avoids problem with the creation of an empty + // list. For MVC we could be doing more intelligent tricks with the inter-layer + // reference. + if (currSlice->slice_type == B_SLICE) + { + if (currSlice->listXsize[LIST_1] == 0 && currSlice->listXsize[LIST_0] != 0) + { + if (currSlice->listXsize[LIST_0] > 1) + currSlice->listX[LIST_1][0] = currSlice->listX[LIST_0][1]; + else + currSlice->listX[LIST_1][0] = currSlice->listX[LIST_0][0]; + currSlice->listXsize[LIST_1]++; + } + else if (currSlice->listXsize[LIST_0] == 0 && currSlice->listXsize[LIST_1] != 0) + { + if (currSlice->listXsize[LIST_1] > 1) + currSlice->listX[LIST_0][0] = currSlice->listX[LIST_1][1]; + else + currSlice->listX[LIST_0][0] = currSlice->listX[LIST_1][0]; + currSlice->listXsize[LIST_0]++; + } + else + { + // Could this scenario happen for the EL case? Need to revisit. + } + } + + currSlice->num_ref_idx_active[LIST_0] = currSlice->listXsize[LIST_0]; + currSlice->num_ref_idx_active[LIST_1] = currSlice->listXsize[LIST_1]; + } +} + + +void set_slice (VideoParameters *p_Vid, Slice *currSlice) +{ + InputParameters *p_Inp = p_Vid->p_Inp; + // Using this trick we can basically reference back to the image and input parameter structures + currSlice->p_Vid = p_Vid; + currSlice->p_Inp = p_Inp; + currSlice->layer_id = p_Vid->dpb_layer_id; + currSlice->p_Dpb = p_Vid->p_Dpb_layer[p_Vid->dpb_layer_id]; + currSlice->active_sps = p_Vid->active_sps; + currSlice->active_pps = p_Vid->active_pps; + currSlice->picture_id = (p_Vid->frame_no & 0xFF); // % 255 + currSlice->slice_nr = p_Vid->current_slice_nr; + currSlice->idr_flag = p_Vid->currentPicture->idr_flag; + currSlice->slice_type = p_Vid->type; + currSlice->frame_no = p_Vid->frame_no; + currSlice->frame_num = p_Vid->frame_num; + currSlice->max_frame_num = p_Vid->max_frame_num; + currSlice->framepoc = p_Vid->framepoc; + currSlice->ThisPOC = p_Vid->ThisPOC; + currSlice->qp = p_Vid->p_curr_frm_struct->qp; + currSlice->start_mb_nr = p_Vid->current_mb_nr; + currSlice->colour_plane_id = p_Vid->colour_plane_id; + + currSlice->si_frame_indicator = currSlice->slice_type == SI_SLICE ? TRUE : FALSE; + currSlice->sp2_frame_indicator = p_Vid->sp2_frame_indicator; + + currSlice->P444_joined = p_Vid->P444_joined; + currSlice->disthres = p_Inp->disthres; + currSlice->UseRDOQuant = p_Inp->UseRDOQuant; + currSlice->RDOQ_QP_Num = p_Inp->RDOQ_QP_Num; + currSlice->Transform8x8Mode = p_Inp->Transform8x8Mode; + + currSlice->slice_too_big = dummy_slice_too_big; + currSlice->width_blk = p_Vid->width_blk; + currSlice->height_blk = p_Vid->height_blk; + currSlice->partition_mode = (short) p_Inp->partition_mode; + currSlice->PicSizeInMbs = p_Vid->PicSizeInMbs; + currSlice->num_blk8x8_uv = p_Vid->num_blk8x8_uv; + currSlice->nal_reference_idc = p_Vid->nal_reference_idc; + currSlice->bitdepth_luma = p_Vid->bitdepth_luma; + currSlice->bitdepth_chroma = p_Vid->bitdepth_chroma; + currSlice->bitdepth_scale[0] = p_Vid->bitdepth_scale[0]; + currSlice->bitdepth_scale[1] = p_Vid->bitdepth_scale[1]; + currSlice->bitdepth_luma_qp_scale = p_Vid->bitdepth_luma_qp_scale; + currSlice->bitdepth_chroma_qp_scale = p_Vid->bitdepth_chroma_qp_scale; + currSlice->bitdepth_lambda_scale = p_Vid->bitdepth_lambda_scale; + currSlice->direct_spatial_mv_pred_flag = p_Vid->direct_spatial_mv_pred_flag; + currSlice->mb_aff_frame_flag = p_Vid->mb_aff_frame_flag; + currSlice->structure = p_Vid->structure; + currSlice->num_ref_idx_active[LIST_0] = (char) (p_Vid->active_pps->num_ref_idx_l0_default_active_minus1 + 1); + currSlice->num_ref_idx_active[LIST_1] = (char) (p_Vid->active_pps->num_ref_idx_l1_default_active_minus1 + 1); + currSlice->DFDisableIdc = (p_Vid->TurnDBOff) ? 1 : p_Vid->DFDisableIdc; + currSlice->DFAlphaC0Offset = p_Vid->DFAlphaC0Offset; + currSlice->DFBetaOffset = p_Vid->DFBetaOffset; + currSlice->view_id = p_Vid->view_id; + + // Edited by Joao Carreira + currSlice->flag_Force_InterView = p_Vid->p_Inp->Force_InterView; +} + +/*! + ************************************************************************ + * \brief + * Initializes the parameters for a new slice and + * allocates the memory for the coded slice in the Picture structure + * \par Side effects: + * Adds slice/partition header symbols to the symbol buffer + * increments Picture->no_slices, allocates memory for the + * slice, sets p_Vid->currSlice + ************************************************************************ + */ +void init_slice (VideoParameters *p_Vid, Slice **currSlice, int start_mb_addr) +{ + InputParameters *p_Inp = p_Vid->p_Inp; + int i,j; + + int layer_id = p_Vid->dpb_layer_id; + + seq_parameter_set_rbsp_t *active_sps = p_Vid->active_sps; + Picture *currPic = p_Vid->currentPicture; + DataPartition *dataPart; + Bitstream *currStream; + int active_ref_lists = (p_Vid->mb_aff_frame_flag) ? 6 : 2; + DecodedPictureBuffer *p_Dpb = p_Vid->p_Dpb_layer[layer_id]; + int alloc_size = 0; + +#if (MVC_EXTENSION_ENABLE) + if(p_Inp->num_of_views == 2) + { + int enable_inter_view = (p_Vid->nal_reference_idc || p_Inp->enable_inter_view_flag) ? 1 : 0; + p_Vid->priority_id = 0; + p_Vid->temporal_id = 0; + p_Vid->inter_view_flag[0] = ((layer_id == 0) ? enable_inter_view : 0); + p_Vid->inter_view_flag[1] = ((layer_id == 0) ? enable_inter_view : 0); + + if(p_Vid->structure!=BOTTOM_FIELD) + p_Vid->anchor_pic_flag[0] = (((p_Vid->currentPicture->idr_flag && p_Vid->nal_reference_idc!=0) || p_Vid->prev_view_is_anchor) ? 1:0); + else // bottom will follow top for now + p_Vid->anchor_pic_flag[1] = 0; //p_Vid->anchor_pic_flag[0]; + p_Vid->non_idr_flag[p_Vid->structure!=BOTTOM_FIELD ? 0:1] = !p_Vid->anchor_pic_flag[p_Vid->structure!=BOTTOM_FIELD ? 0:1]; + } +#endif + + p_Vid->current_mb_nr = start_mb_addr; + + // Allocate new Slice in the current Picture, and set p_Vid->currentSlice + assert (currPic != NULL); + currPic->no_slices++; + + if (currPic->no_slices >= MAXSLICEPERPICTURE) + error ("Too many slices per picture, increase MAXSLICEPERPICTURE in global.h.", -1); + + currPic->slices[currPic->no_slices - 1] = malloc_slice(p_Vid, p_Inp); + *currSlice = currPic->slices[currPic->no_slices-1]; + + p_Vid->currentSlice = *currSlice; + set_slice (p_Vid, *currSlice); + + // primary and redundant slices: number of references overriding. + if(p_Inp->redundant_pic_flag) + { + if(!p_Vid->redundant_coding) + { + (*currSlice)->num_ref_idx_active[LIST_0] = (char) imin(p_Vid->number,p_Inp->NumRefPrimary); + } + else + { + // 1 reference picture for redundant slices + (*currSlice)->num_ref_idx_active[LIST_0] = 1; + } + } + + for (i = 0; i < (*currSlice)->max_part_nr; i++) + { + dataPart = &(*currSlice)->partArr[i]; + + currStream = dataPart->bitstream; + currStream->bits_to_go = 8; + currStream->byte_pos = 0; + currStream->byte_buf = 0; + } + + // code now also considers fields. Issue whether we should account this within the appropriate input p_Inp directly + if (((*currSlice)->slice_type == P_SLICE || (*currSlice)->slice_type == SP_SLICE) && p_Inp->P_List0_refs[layer_id]) + { + (*currSlice)->num_ref_idx_active[LIST_0] = (char) imin((*currSlice)->num_ref_idx_active[LIST_0], p_Inp->P_List0_refs[layer_id] * ((p_Vid->structure !=0) + 1)); +#if CRA + if ( p_Inp->useCRA ) + { + if ( ( ((*currSlice)->ThisPOC/2) - (p_Vid->p_Inp->NumberBFrames+1) ) % p_Vid->p_Inp->intra_period == 0 ) + { + (*currSlice)->num_ref_idx_active[LIST_0] = (char) 1; + } + } +#endif + } + + if ((*currSlice)->slice_type == B_SLICE ) + { + if (p_Inp->B_List0_refs[layer_id]) + { +#if B0_MORE_REF + if ( p_Inp->BLevel0MoreRef ) + { + if ( ((*currSlice)->ThisPOC/2) % (p_Vid->p_Inp->NumberBFrames+1) == 0 ) + { + (*currSlice)->num_ref_idx_active[LIST_0] = (char) imin((*currSlice)->num_ref_idx_active[LIST_0], p_Inp->P_List0_refs[layer_id] * ((p_Vid->structure !=0) + 1)); + } + else + { + (*currSlice)->num_ref_idx_active[LIST_0] = (char) imin((*currSlice)->num_ref_idx_active[LIST_0], p_Inp->B_List0_refs[layer_id] * ((p_Vid->structure !=0) + 1)); + } + } + else +#endif + (*currSlice)->num_ref_idx_active[LIST_0] = (char) imin((*currSlice)->num_ref_idx_active[LIST_0], p_Inp->B_List0_refs[layer_id] * ((p_Vid->structure !=0) + 1)); + +#if CRA + if ( p_Inp->useCRA ) + { + if ( ( ((*currSlice)->ThisPOC/2) - (p_Vid->p_Inp->NumberBFrames+1) ) % p_Vid->p_Inp->intra_period == 0 ) + { + (*currSlice)->num_ref_idx_active[LIST_0] = (char) 1; + } + } +#endif + } + if (p_Inp->B_List1_refs[layer_id]) + { + (*currSlice)->num_ref_idx_active[LIST_1] = (char) imin((*currSlice)->num_ref_idx_active[LIST_1], p_Inp->B_List1_refs[layer_id] * ((p_Vid->structure !=0) + 1)); +#if B0_MORE_REF + if ( p_Inp->BLevel0MoreRef ) + { + if ( ((*currSlice)->ThisPOC/2) % (p_Vid->p_Inp->NumberBFrames+1) == 0 ) + { + (*currSlice)->num_ref_idx_active[LIST_1] = (*currSlice)->num_ref_idx_active[LIST_0]; + } + } +#endif +#if CRA + if ( p_Inp->useCRA ) + { + if ( ( ((*currSlice)->ThisPOC/2) - (p_Vid->p_Inp->NumberBFrames+1) ) % p_Vid->p_Inp->intra_period == 0 ) + { + (*currSlice)->num_ref_idx_active[LIST_1] = (*currSlice)->num_ref_idx_active[LIST_0]; + } + } +#endif + } + get_mem3D((byte ****)(void*)&(*currSlice)->direct_ref_idx, (*currSlice)->height_blk, (*currSlice)->width_blk, 2); + get_mem2D((byte ***) (void*)&(*currSlice)->direct_pdir, (*currSlice)->height_blk, (*currSlice)->width_blk); + } + + setup_slice(*currSlice); + update_pic_num(*currSlice); + + (*currSlice)->init_lists(*currSlice); + + // assign list sizes + (*currSlice)->num_ref_idx_active[LIST_0] = (*currSlice)->listXsize[LIST_0]; + (*currSlice)->num_ref_idx_active[LIST_1] = (*currSlice)->listXsize[LIST_1]; + +#if CRA + if ( p_Inp->useCRA ) + { + if ( ( (*currSlice)->slice_type == B_SLICE || (*currSlice)->slice_type == P_SLICE ) && (*currSlice)->p_Dpb->ref_frames_in_buffer >= 2 ) + { + if ( ( ((*currSlice)->ThisPOC/2) - (p_Vid->p_Inp->NumberBFrames+1) ) % p_Vid->p_Inp->intra_period == 0 ) + { + cra_ref_management_frame_pic(p_Dpb, p_Vid->frame_num); + } + } + } +#endif + +#if HM50_LIKE_MMCO + if ( p_Inp->HM50LikeMMCO ) + { + if (p_Vid->structure == FRAME && p_Dpb->ref_frames_in_buffer == active_sps->num_ref_frames) + hm50_ref_management_frame_pic(p_Dpb, p_Vid->frame_num); + } +#endif + +#if LD_REF_SETTING + if ( p_Inp->useF701RefForLD ) + { + if (p_Vid->structure == FRAME && p_Dpb->ref_frames_in_buffer == active_sps->num_ref_frames) + low_delay_ref_management_frame_pic(p_Dpb, p_Vid->frame_num); + } +#endif + + //Perform memory management based on poc distances + if (p_Inp->SetFirstAsLongTerm && p_Vid->number == 0) + { + mmco_long_term(p_Vid, p_Vid->number); + } + else if (p_Vid->nal_reference_idc && p_Inp->PocMemoryManagement == 1) + { + if (p_Vid->structure == FRAME && p_Dpb->ref_frames_in_buffer == active_sps->num_ref_frames) + poc_based_ref_management_frame_pic(p_Dpb, p_Vid->frame_num); + else if (p_Vid->structure == TOP_FIELD && p_Dpb->ref_frames_in_buffer== active_sps->num_ref_frames) + poc_based_ref_management_field_pic(p_Dpb, (p_Vid->frame_num << 1) + 1); + else if (p_Vid->structure == BOTTOM_FIELD) + poc_based_ref_management_field_pic(p_Dpb, (p_Vid->frame_num << 1) + 1); + } + else if (p_Vid->nal_reference_idc && p_Inp->PocMemoryManagement == 2) + { + if (p_Vid->structure == FRAME) + tlyr_based_ref_management_frame_pic(p_Vid, p_Vid->frame_num); + } + + set_default_ref_pic_lists(*currSlice); + + // initialize reordering arrays + init_ref_pic_list_reordering(*currSlice, p_Inp->ReferenceReorder); + alloc_ref_pic_list_reordering_buffer(*currSlice); + +#if (MVC_EXTENSION_ENABLE) + p_Vid->MVCInterViewReorder = 0; + + if(p_Vid->num_of_layers == 2 && p_Inp->MVCInterViewReorder == 1 && layer_id == 1) + { + // enable MVC reordering + p_Vid->MVCInterViewReorder = 1; + + (*currSlice)->reorder_lists( *currSlice ); + + if((*currSlice)->listXsize[0]!=0) + { + StorablePicture *Inter_ViewL0 = (*currSlice)->listX[0][(*currSlice)->listXsize[0]-1]; + //StorablePicture *Inter_ViewL0 = (*currSlice)->p_Dpb->fs_ilref[0]->frame; + //printf("value %d %d %d \n", (*currSlice)->listXsize[0], (*currSlice)->p_Dpb->fs_ilref[0]->frame, (*currSlice)->p_Dpb->fs_ilref[0]->frame); + for(i=(*currSlice)->listXsize[0] - 1; i>0; i--) + (*currSlice)->listX[0][i] = (*currSlice)->listX[0][i - 1]; + (*currSlice)->listX[0][0] = Inter_ViewL0; + (*currSlice)->listXsize[0] = (char) imin ((*currSlice)->listXsize[0], (*currSlice)->num_ref_idx_active[LIST_0]); + } + // perform reordering of interview pictures + } + else +#endif + // reference list reordering + // RPLR for redundant pictures + // !KS: that should actually be moved somewhere else + if(p_Inp->redundant_pic_flag && p_Vid->redundant_coding) + { + alloc_ref_pic_list_reordering_buffer(*currSlice); + (*currSlice)->ref_pic_list_reordering_flag[LIST_0] = 1; + (*currSlice)->modification_of_pic_nums_idc[LIST_0][0] = 0; + (*currSlice)->modification_of_pic_nums_idc[LIST_0][1] = 3; + (*currSlice)->abs_diff_pic_num_minus1[LIST_0][0] = p_Vid->redundant_ref_idx - 1; + (*currSlice)->long_term_pic_idx[LIST_0][0] = 0; + + reorder_ref_pic_list ( *currSlice, LIST_0); + } + else if (( (*currSlice)->slice_type == P_SLICE || (*currSlice)->slice_type == B_SLICE) && p_Inp->WPMCPrecision && p_Vid->pWPX->curr_wp_rd_pass->algorithm != WP_REGULAR ) + wp_mcprec_reorder_lists( *currSlice ); + else + (*currSlice)->reorder_lists( *currSlice ); + + if (p_Inp->EnableOpenGOP) + setup_open_gop_lists(p_Vid, *currSlice); + + +#if PRINTREFLIST +#if (MVC_EXTENSION_ENABLE) + if ( (*currSlice)->slice_type == P_SLICE ) + { + // print out for debug purpose + if(p_Vid->current_slice_nr==0) + { + if((*currSlice)->listXsize[0]>0) + { + printf("\n"); + printf(" ** (CurViewID:%d) %s Ref Pic List 0 ****\n", layer_id, (*currSlice)->structure==FRAME ? "FRM":((*currSlice)->structure==TOP_FIELD ? "TOP":"BOT")); + for(i=0; i<(int)((*currSlice)->listXsize[0]); i++) //ref list 0 + { + printf(" %2d -> POC: %4d PicNum: %4d ViewID: %d\n", i, (*currSlice)->listX[0][i]->poc, (*currSlice)->listX[0][i]->pic_num, (*currSlice)->listX[0][i]->view_id); + } + } + } + } + else if ( (*currSlice)->slice_type == B_SLICE ) + { + // print out for debug purpose + if(p_Vid->current_slice_nr==0) + { + if(((*currSlice)->listXsize[0]>0) || ((*currSlice)->listXsize[1]>0)) + printf("\n"); + if((*currSlice)->listXsize[0]>0) + { + printf(" ** (CurViewID:%d) %s Ref Pic List 0 ****\n", layer_id, (*currSlice)->structure==FRAME ? "FRM":((*currSlice)->structure==TOP_FIELD ? "TOP":"BOT")); + for(i=0; i<(int)((*currSlice)->listXsize[0]); i++) //ref list 0 + { + printf(" %2d -> POC: %4d PicNum: %4d ViewID: %d\n", i, (*currSlice)->listX[0][i]->poc, (*currSlice)->listX[0][i]->pic_num, (*currSlice)->listX[0][i]->view_id); + } + } + if((*currSlice)->listXsize[1]>0) + { + printf(" ** (CurViewID:%d) %s Ref Pic List 1 ****\n", layer_id, (*currSlice)->structure==FRAME ? "FRM":((*currSlice)->structure==TOP_FIELD ? "TOP":"BOT")); + for(i=0; i<(int)((*currSlice)->listXsize[1]); i++) //ref list 1 + { + printf(" %2d -> POC: %4d PicNum: %4d ViewID: %d\n", i, (*currSlice)->listX[1][i]->poc, (*currSlice)->listX[1][i]->pic_num, (*currSlice)->listX[1][i]->view_id); + } + } + } + } +#endif +#endif + + (*currSlice)->max_num_references = (short) p_Vid->max_num_references; + + if (((*currSlice)->slice_type != I_SLICE) && (*currSlice)->slice_type != SI_SLICE) + { + alloc_size += get_mem5Dmv (&((*currSlice)->all_mv), 2, (*currSlice)->max_num_references, 9, 4, 4); + + if (p_Inp->BiPredMotionEstimation && ((*currSlice)->slice_type == B_SLICE)) + { + alloc_size += get_mem6Dmv (&((*currSlice)->bipred_mv), 2, 2, (*currSlice)->max_num_references, 9, 4, 4); + } + + if (p_Inp->UseRDOQuant && p_Inp->RDOQ_QP_Num > 1) + { + if (p_Inp->Transform8x8Mode && p_Inp->RDOQ_CP_MV) + { + get_mem4Dmv (&(*currSlice)->tmp_mv8, 2, (*currSlice)->max_num_references, 4, 4); + get_mem3Ddistblk(&(*currSlice)->motion_cost8, 2, (*currSlice)->max_num_references, 4); + get_mem4Dmv (&(*currSlice)->tmp_mv4, 2, (*currSlice)->max_num_references, 4, 4); + get_mem3Ddistblk(&(*currSlice)->motion_cost4, 2, (*currSlice)->max_num_references, 4); + } + } + } + + if (p_Vid->mb_aff_frame_flag) + init_mbaff_lists(*currSlice); + + if (((*currSlice)->slice_type != I_SLICE && (*currSlice)->slice_type != SI_SLICE) && (p_Vid->active_pps->weighted_pred_flag == 1 || (p_Vid->active_pps->weighted_bipred_idc > 0 && ((*currSlice)->slice_type == B_SLICE)))) + { + if ((*currSlice)->slice_type == P_SLICE || (*currSlice)->slice_type == SP_SLICE) + { + int wp_type = (p_Inp->GenerateMultiplePPS && p_Inp->RDPictureDecision) && (p_Vid->enc_picture != p_Vid->enc_frame_picture[1]); + p_Vid->EstimateWPPSlice (*currSlice, wp_type); + } + else + p_Vid->EstimateWPBSlice (*currSlice); + + ClipWPParams( *currSlice ); + } + + if ((*currSlice)->slice_type == B_SLICE) + { + // Allocation should be based on slice size, not image + compute_colocated(*currSlice, (*currSlice)->listX); + } + + if ((*currSlice)->slice_type != I_SLICE && (*currSlice)->slice_type != SI_SLICE) + { + p_Vid->searchRange.min_x = -p_Inp->search_range[layer_id] << 2; + p_Vid->searchRange.max_x = p_Inp->search_range[layer_id] << 2; + p_Vid->searchRange.min_y = -p_Inp->search_range[layer_id] << 2; + p_Vid->searchRange.max_y = p_Inp->search_range[layer_id] << 2; + + if (p_Inp->SearchMode[layer_id] == EPZS) + { + if (((*currSlice)->p_EPZS = (EPZSParameters*) calloc(1, sizeof(EPZSParameters)))==NULL) + no_mem_exit("init_slice: p_EPZS"); + EPZSStructInit (*currSlice); + EPZSSliceInit (*currSlice); + } + } + + + if ((*currSlice)->symbol_mode == CAVLC) + { + setup_cavlc(*currSlice, (*currSlice)->listXsize); + } + else + { + setup_cabac(*currSlice, (*currSlice)->listXsize); + } + + // assign luma common reference picture pointers to be used for ME/sub-pel interpolation + + for(i = 0; i < active_ref_lists; i++) + { + for(j = 0; j < (*currSlice)->listXsize[i]; j++) + { + if( (*currSlice)->listX[i][j] ) + { + (*currSlice)->listX[i][j]->p_curr_img = (*currSlice)->listX[i][j]->p_img [(short) p_Vid->colour_plane_id]; + (*currSlice)->listX[i][j]->p_curr_img_sub = (*currSlice)->listX[i][j]->p_img_sub[(short) p_Vid->colour_plane_id]; + } + } + } + + if (p_Inp->UseRDOQuant) + { + if (((*currSlice)->estBitsCabac = (estBitsCabacStruct*) calloc(NUM_BLOCK_TYPES, sizeof(estBitsCabacStruct)))==NULL) + no_mem_exit("init_slice: (*currSlice)->estBitsCabac"); + + init_rdoq_slice(*currSlice); + + alloc_rddata(*currSlice, &(*currSlice)->rddata_trellis_curr); + if (p_Inp->RDOQ_QP_Num > 1) + { + alloc_rddata(*currSlice, &(*currSlice)->rddata_trellis_best); + } + } + + if(p_Vid->mb_aff_frame_flag) + { + alloc_rddata(*currSlice, &(*currSlice)->rddata_top_frame_mb); + alloc_rddata(*currSlice, &(*currSlice)->rddata_bot_frame_mb); + if ( p_Inp->MbInterlace != FRAME_MB_PAIR_CODING ) + { + alloc_rddata(*currSlice, &(*currSlice)->rddata_top_field_mb); + alloc_rddata(*currSlice, &(*currSlice)->rddata_bot_field_mb); + } + } + + if ((*currSlice)->slice_type == B_SLICE) + { + (*currSlice)->set_motion_vectors_mb = SetMotionVectorsMBBSlice; + } + else if ((*currSlice)->slice_type == P_SLICE || (*currSlice)->slice_type == SP_SLICE) + { + (*currSlice)->set_motion_vectors_mb = SetMotionVectorsMBPSlice; + } + else + { + (*currSlice)->set_motion_vectors_mb = SetMotionVectorsMBISlice; + } + + get_mem3Dpel(&((*currSlice)->mb_pred), MAX_PLANE, MB_BLOCK_SIZE, MB_BLOCK_SIZE); + get_mem3Dint(&((*currSlice)->mb_rres), MAX_PLANE, MB_BLOCK_SIZE, MB_BLOCK_SIZE); + get_mem3Dint(&((*currSlice)->mb_ores), MAX_PLANE, MB_BLOCK_SIZE, MB_BLOCK_SIZE); + get_mem4Dpel(&((*currSlice)->mpr_4x4), MAX_PLANE, 9, MB_BLOCK_SIZE, MB_BLOCK_SIZE); + get_mem4Dpel(&((*currSlice)->mpr_8x8), MAX_PLANE, 9, MB_BLOCK_SIZE, MB_BLOCK_SIZE); + get_mem4Dpel(&((*currSlice)->mpr_16x16), MAX_PLANE, 5, MB_BLOCK_SIZE, MB_BLOCK_SIZE); + + get_mem_ACcoeff (p_Vid, &((*currSlice)->cofAC)); + get_mem_DCcoeff (&((*currSlice)->cofDC)); + + allocate_block_mem(*currSlice); + init_coding_state_methods(*currSlice); + init_rdopt(*currSlice); +} + +/*! + ************************************************************************ + * \brief + * Lite-weight initialization of the parameters for a new slice and + * allocates the memory for the coded slice in the Picture structure + * \par Side effects: + * Adds slice/partition header symbols to the symbol buffer + * increments Picture->no_slices, allocates memory for the + * slice, sets p_Vid->currSlice + ************************************************************************ + */ +void init_slice_lite (VideoParameters *p_Vid, Slice **currSlice, int start_mb_addr) +{ + InputParameters *p_Inp = p_Vid->p_Inp; + int i,j; +#if (MVC_EXTENSION_ENABLE) + int layer_id = p_Vid->dpb_layer_id; +#else + int layer_id = 0; +#endif + int active_ref_lists = (p_Vid->mb_aff_frame_flag) ? 6 : 2; + +#if (MVC_EXTENSION_ENABLE) + if(p_Inp->num_of_views == 2) + { + int enable_inter_view = (p_Vid->nal_reference_idc || p_Inp->enable_inter_view_flag) ? 1 : 0; + p_Vid->priority_id = 0; + p_Vid->temporal_id = 0; + p_Vid->inter_view_flag[0] = ((layer_id == 0) ? enable_inter_view : 0); + p_Vid->inter_view_flag[1] = ((layer_id == 0) ? enable_inter_view : 0); + + if(p_Vid->structure!=BOTTOM_FIELD) + p_Vid->anchor_pic_flag[0] = (((p_Vid->currentPicture->idr_flag && p_Vid->nal_reference_idc!=0) || p_Vid->prev_view_is_anchor) ? 1:0); + else // bottom will follow top for now + p_Vid->anchor_pic_flag[1] = 0; //p_Vid->anchor_pic_flag[0]; + p_Vid->non_idr_flag[p_Vid->structure!=BOTTOM_FIELD ? 0:1] = !p_Vid->anchor_pic_flag[p_Vid->structure!=BOTTOM_FIELD ? 0:1]; + } +#endif + + *currSlice = malloc_slice_lite(p_Vid, p_Inp); + + set_slice (p_Vid, *currSlice); + + + if (((*currSlice)->slice_type == P_SLICE || (*currSlice)->slice_type == SP_SLICE) && p_Inp->P_List0_refs[layer_id]) + { + (*currSlice)->num_ref_idx_active[LIST_0] = (char) imin((*currSlice)->num_ref_idx_active[LIST_0], p_Inp->P_List0_refs[layer_id] * ((p_Vid->structure !=0) + 1)); + } + + if ((*currSlice)->slice_type == B_SLICE ) + { + if (p_Inp->B_List0_refs[layer_id]) + { + (*currSlice)->num_ref_idx_active[LIST_0] = (char) imin((*currSlice)->num_ref_idx_active[LIST_0], p_Inp->B_List0_refs[layer_id] * ((p_Vid->structure !=0) + 1)); + } + if (p_Inp->B_List1_refs[layer_id]) + { + (*currSlice)->num_ref_idx_active[LIST_1] = (char) imin((*currSlice)->num_ref_idx_active[LIST_1], p_Inp->B_List1_refs[layer_id] * ((p_Vid->structure !=0) + 1)); + } + } + + setup_slice(*currSlice); + + update_pic_num(*currSlice); + (*currSlice)->init_lists(*currSlice); + + // assign list 0 size from list size + (*currSlice)->num_ref_idx_active[LIST_0] = (*currSlice)->listXsize[LIST_0]; + (*currSlice)->num_ref_idx_active[LIST_1] = (*currSlice)->listXsize[LIST_1]; + + set_default_ref_pic_lists(*currSlice); + + init_ref_pic_list_reordering(*currSlice, p_Inp->ReferenceReorder); + alloc_ref_pic_list_reordering_buffer(*currSlice); + +#if (MVC_EXTENSION_ENABLE) + p_Vid->MVCInterViewReorder = 0; + if(p_Vid->num_of_layers == 2 && p_Inp->MVCInterViewReorder == 1 && layer_id == 1) + { + // enable MVC reordering + p_Vid->MVCInterViewReorder = 1; + + if((*currSlice)->listXsize[0]!=0) + { + StorablePicture *Inter_ViewL0 = (*currSlice)->listX[0][(*currSlice)->listXsize[0]-1]; + for(i=(*currSlice)->listXsize[0] - 1; i>0; i--) + (*currSlice)->listX[0][i] = (*currSlice)->listX[0][i - 1]; + (*currSlice)->listX[0][0] = Inter_ViewL0; + (*currSlice)->listXsize[0] = (char) imin ((*currSlice)->listXsize[0], (*currSlice)->num_ref_idx_active[LIST_0]); + } + (*currSlice)->reorder_lists( *currSlice ); + // perform reordering of interview pictures + } + else +#endif + // reference list reordering + // RPLR for redundant pictures + // !KS: that should actually be moved somewhere else + if(p_Inp->redundant_pic_flag && p_Vid->redundant_coding) + { + (*currSlice)->ref_pic_list_reordering_flag[LIST_0] = 1; + (*currSlice)->modification_of_pic_nums_idc[LIST_0][0] = 0; + (*currSlice)->modification_of_pic_nums_idc[LIST_0][1] = 3; + (*currSlice)->abs_diff_pic_num_minus1[LIST_0][0] = p_Vid->redundant_ref_idx - 1; + (*currSlice)->long_term_pic_idx[LIST_0][0] = 0; + + reorder_ref_pic_list ( *currSlice, LIST_0); + } + else if ( ((*currSlice)->slice_type == P_SLICE || (*currSlice)->slice_type == B_SLICE) && p_Inp->WPMCPrecision && p_Vid->pWPX->curr_wp_rd_pass->algorithm != WP_REGULAR ) + wp_mcprec_reorder_lists( *currSlice ); + else + (*currSlice)->reorder_lists( *currSlice ); + + if (p_Inp->EnableOpenGOP) + setup_open_gop_lists(p_Vid, *currSlice); + + + (*currSlice)->max_num_references = (short) p_Vid->max_num_references; + + if (p_Vid->mb_aff_frame_flag) + init_mbaff_lists(*currSlice); + + // assign luma common reference picture pointers to be used for ME/sub-pel interpolation + for(i = 0; i < active_ref_lists; i++) + { + for(j = 0; j < (*currSlice)->listXsize[i]; j++) + { + if( (*currSlice)->listX[i][j] ) + { + (*currSlice)->listX[i][j]->p_curr_img = (*currSlice)->listX[i][j]->p_img [(short) p_Vid->colour_plane_id]; + (*currSlice)->listX[i][j]->p_curr_img_sub = (*currSlice)->listX[i][j]->p_img_sub[(short) p_Vid->colour_plane_id]; + } + } + } + + (*currSlice)->estBitsCabac = NULL; + nullify_rddata(&((*currSlice)->rddata_trellis_curr)); + nullify_rddata(&((*currSlice)->rddata_trellis_best)); + + if(p_Vid->mb_aff_frame_flag) + { + nullify_rddata(&((*currSlice)->rddata_top_frame_mb)); + nullify_rddata(&((*currSlice)->rddata_bot_frame_mb)); + if ( p_Inp->MbInterlace != FRAME_MB_PAIR_CODING ) + { + nullify_rddata(&((*currSlice)->rddata_top_field_mb)); + nullify_rddata(&((*currSlice)->rddata_bot_field_mb)); + } + } + + (*currSlice)->all_mv = NULL; + (*currSlice)->bipred_mv = NULL; + + (*currSlice)->tmp_mv8 = NULL; + (*currSlice)->motion_cost8 = NULL; + (*currSlice)->tmp_mv4 = NULL; + (*currSlice)->motion_cost4 = NULL; + (*currSlice)->p_EPZS = NULL; + + (*currSlice)->mb_pred = NULL; + (*currSlice)->mb_rres = NULL; + (*currSlice)->mb_ores = NULL; + (*currSlice)->mpr_4x4 = NULL; + (*currSlice)->mpr_8x8 = NULL; + (*currSlice)->mpr_16x16 = NULL; + + (*currSlice)->cofAC = NULL; + (*currSlice)->cofDC = NULL; + + (*currSlice)->tblk4x4 = NULL; + (*currSlice)->tblk16x16 = NULL; + (*currSlice)->i16blk4x4 = NULL; + + (*currSlice)->direct_ref_idx = NULL; + (*currSlice)->direct_pdir = NULL; + + init_coding_state_methods(*currSlice); +} + +/*! + ************************************************************************ + * \brief + * Allocates a slice structure along with its dependent data structures + * \return + * Pointer to a Slice + ************************************************************************ + */ +static Slice *malloc_slice(VideoParameters *p_Vid, InputParameters *p_Inp) +{ + int i; + DataPartition *dataPart; + Slice *currSlice; + int cr_size = (p_Inp->separate_colour_plane_flag != 0) ? 0 : 512; + + int buffer_size; + + switch (p_Inp->slice_mode) + { + case 2: + //buffer_size = imax(2 * p_Inp->slice_argument, 500 + (128 + 256 * p_Vid->bitdepth_luma + 512 * p_Vid->bitdepth_chroma)); + buffer_size = imax(2 * p_Inp->slice_argument, 764); + break; + case 1: + buffer_size = 500 + p_Inp->slice_argument * ((128 + 256 * p_Vid->bitdepth_luma + cr_size * p_Vid->bitdepth_chroma) >> 3); + break; + default: + buffer_size = 500 + p_Vid->FrameSizeInMbs * ((128 + 256 * p_Vid->bitdepth_luma + cr_size * p_Vid->bitdepth_chroma) >> 3); + break; + } + + // KS: this is approx. max. allowed code picture size + if ((currSlice = (Slice *) calloc(1, sizeof(Slice))) == NULL) no_mem_exit ("malloc_slice: currSlice structure"); + + currSlice->p_Vid = p_Vid; + currSlice->p_Inp = p_Inp; + + if (((currSlice->p_RDO) = (RDOPTStructure *) calloc(1, sizeof(RDOPTStructure)))==NULL) + no_mem_exit("malloc_slice: p_RDO"); + + currSlice->symbol_mode = (char) p_Inp->symbol_mode; + + if (currSlice->symbol_mode == CABAC) + { + // create all context models + currSlice->mot_ctx = create_contexts_MotionInfo (); + currSlice->tex_ctx = create_contexts_TextureInfo(); + } + + currSlice->max_part_nr = p_Inp->partition_mode==0?1:3; + + //for IDR p_Vid there should be only one partition + if(p_Vid->currentPicture->idr_flag) + currSlice->max_part_nr = 1; + + assignSE2partition[0] = assignSE2partition_NoDP; + //ZL + //for IDR p_Vid all the syntax element should be mapped to one partition + if(!p_Vid->currentPicture->idr_flag && p_Inp->partition_mode == 1) + assignSE2partition[1] = assignSE2partition_DP; + else + assignSE2partition[1] = assignSE2partition_NoDP; + + currSlice->num_mb = 0; // no coded MBs so far + + if ((currSlice->partArr = (DataPartition *) calloc(currSlice->max_part_nr, sizeof(DataPartition))) == NULL) + no_mem_exit ("malloc_slice: partArr"); + for (i=0; imax_part_nr; i++) // loop over all data partitions + { + dataPart = &(currSlice->partArr[i]); + if ((dataPart->bitstream = (Bitstream *) calloc(1, sizeof(Bitstream))) == NULL) + no_mem_exit ("malloc_slice: Bitstream"); + if ((dataPart->bitstream->streamBuffer = (byte *) calloc(buffer_size, sizeof(byte))) == NULL) + no_mem_exit ("malloc_slice: StreamBuffer"); + dataPart->bitstream->buffer_size = buffer_size; + // Initialize storage of bitstream parameters + // Set pointers + dataPart->p_Slice = currSlice; + dataPart->p_Vid = p_Vid; + dataPart->p_Inp = p_Inp; + } + + if (p_Inp->WeightedPrediction || p_Inp->WeightedBiprediction || p_Inp->GenerateMultiplePPS) + { + // Currently only use up to 32 references. Need to use different indicator such as maximum num of references in list + get_mem3Dshort(&currSlice->wp_weight , 6, MAX_REFERENCE_PICTURES, 3); + get_mem3Dshort(&currSlice->wp_offset , 6, MAX_REFERENCE_PICTURES, 3); + get_mem4Dshort(&currSlice->wbp_weight, 6, MAX_REFERENCE_PICTURES, MAX_REFERENCE_PICTURES, 3); + } + + return currSlice; +} + + + + +/*! + ************************************************************************ + * \brief + * Allocates a light-weight slice structure along with its dependent data structures + * \return + * Pointer to a Slice + ************************************************************************ + */ +static Slice *malloc_slice_lite(VideoParameters *p_Vid, InputParameters *p_Inp) +{ + Slice *currSlice; + //int cr_size = (p_Inp->separate_colour_plane_flag != 0) ? 0 : 512; + + if ((currSlice = (Slice *) calloc(1, sizeof(Slice))) == NULL) no_mem_exit ("malloc_slice: currSlice structure"); + + currSlice->p_Vid = p_Vid; + currSlice->p_Inp = p_Inp; + currSlice->p_RDO = NULL; + + currSlice->symbol_mode = (char) p_Inp->symbol_mode; + { + // create all context models + currSlice->mot_ctx = NULL; + currSlice->tex_ctx = NULL; + } + + currSlice->max_part_nr = p_Inp->partition_mode==0?1:3; + + //for IDR p_Vid there should be only one partition + if(p_Vid->currentPicture->idr_flag) + currSlice->max_part_nr = 1; + + assignSE2partition[0] = assignSE2partition_NoDP; + //ZL + //for IDR p_Vid all the syntax element should be mapped to one partition + if(!p_Vid->currentPicture->idr_flag && p_Inp->partition_mode == 1) + assignSE2partition[1] = assignSE2partition_DP; + else + assignSE2partition[1] = assignSE2partition_NoDP; + + currSlice->num_mb = 0; // no coded MBs so far + + currSlice->partArr = NULL; + + if (p_Inp->WeightedPrediction || p_Inp->WeightedBiprediction || p_Inp->GenerateMultiplePPS) + { + // Currently only use up to 32 references. Need to use different indicator such as maximum num of references in list + get_mem3Dshort(&currSlice->wp_weight, 6, MAX_REFERENCE_PICTURES, 3); + get_mem3Dshort(&currSlice->wp_offset, 6, MAX_REFERENCE_PICTURES, 3); + get_mem4Dshort(&currSlice->wbp_weight, 6, MAX_REFERENCE_PICTURES, MAX_REFERENCE_PICTURES, 3); + } + + return currSlice; +} + + + +/*! + ************************************************************************ + * \brief + * This function frees nal units + * + ************************************************************************ + */ +static void free_nal_unit(Picture *pic) +{ + int partition, slice; + Slice *currSlice; + + // loop over all slices of the picture + for (slice=0; slice < pic->no_slices; slice++) + { + currSlice = pic->slices[slice]; + + // loop over the partitions + if (currSlice != NULL) + { + for (partition=0; partition < currSlice->max_part_nr; partition++) + { + // free only if the partition has content + if (currSlice->partArr[partition].bitstream->write_flag ) + { + if (currSlice->partArr[partition].nal_unit != NULL) + { + FreeNALU(currSlice->partArr[partition].nal_unit); + currSlice->partArr[partition].nal_unit = NULL; + } + } + } + } + } +} + + +/*! + ************************************************************************ + * \brief + * Memory frees of the Slice structure and of its dependent + * data structures + * \param currSlice + * Slice to be freed + ************************************************************************ + */ +void free_slice(Slice *currSlice) +{ + if (currSlice != NULL) + { + VideoParameters *p_Vid = currSlice->p_Vid; + InputParameters *p_Inp = currSlice->p_Inp; + + int i; + DataPartition *dataPart; + + for (i=0; i<6; i++) + { + if (currSlice->listX[i]) + { + free (currSlice->listX[i]); + currSlice->listX[i] = NULL; + } + } + + for (i = 0; i < currSlice->max_part_nr; i++) // loop over all data partitions + { + if ( currSlice->partArr != NULL) + { + dataPart = &(currSlice->partArr[i]); + + if (dataPart != NULL) + { + if (dataPart->bitstream != NULL) + { + if (dataPart->bitstream->streamBuffer != NULL) + { + free(dataPart->bitstream->streamBuffer); + dataPart->bitstream->streamBuffer = NULL; + } + free(dataPart->bitstream); + dataPart->bitstream = NULL; + } + } + } + } + + if (currSlice->slice_type != I_SLICE && currSlice->slice_type != SI_SLICE) + free_ref_pic_list_reordering_buffer (currSlice); + + // free structure for rd-opt. mode decision + if(currSlice->p_RDO) + { + clear_rdopt (currSlice); + free (currSlice->p_RDO); + } + + if(currSlice->cofAC) + free_mem_ACcoeff (currSlice->cofAC); + if(currSlice->cofDC) + free_mem_DCcoeff (currSlice->cofDC); + + if(currSlice->mb_rres) + free_mem3Dint(currSlice->mb_rres ); + if(currSlice->mb_ores) + free_mem3Dint(currSlice->mb_ores ); + if(currSlice->mb_pred) + free_mem3Dpel(currSlice->mb_pred ); + if(currSlice->mpr_16x16) + free_mem4Dpel(currSlice->mpr_16x16); + if(currSlice->mpr_8x8) + free_mem4Dpel(currSlice->mpr_8x8 ); + if(currSlice->mpr_4x4) + free_mem4Dpel(currSlice->mpr_4x4 ); + + + if (currSlice->partArr != NULL) + { + free(currSlice->partArr); + currSlice->partArr = NULL; + } + + if (currSlice->symbol_mode == CABAC) + { + if(currSlice->mot_ctx) + delete_contexts_MotionInfo(currSlice->mot_ctx); + if(currSlice->tex_ctx) + delete_contexts_TextureInfo(currSlice->tex_ctx); + } + + if (p_Inp->WeightedPrediction || p_Inp->WeightedBiprediction || p_Inp->GenerateMultiplePPS) + { + free_mem3Dshort(currSlice->wp_weight ); + free_mem3Dshort(currSlice->wp_offset ); + free_mem4Dshort(currSlice->wbp_weight); + } + + if (currSlice->UseRDOQuant) + { + free(currSlice->estBitsCabac); + + free_rddata(currSlice, &currSlice->rddata_trellis_curr); + if(currSlice->RDOQ_QP_Num > 1) + { + free_rddata(currSlice, &currSlice->rddata_trellis_best); + } + } + + if(currSlice->mb_aff_frame_flag) + { + free_rddata(currSlice, &currSlice->rddata_top_frame_mb); + free_rddata(currSlice, &currSlice->rddata_bot_frame_mb); + + if ( p_Inp->MbInterlace != FRAME_MB_PAIR_CODING ) + { + free_rddata(currSlice, &currSlice->rddata_top_field_mb); + free_rddata(currSlice, &currSlice->rddata_bot_field_mb); + } + } + + if ((currSlice->slice_type == P_SLICE) || (currSlice->slice_type == SP_SLICE) || (currSlice->slice_type == B_SLICE)) + { + if(currSlice->all_mv) + free_mem5Dmv (currSlice->all_mv); + if (p_Inp->BiPredMotionEstimation && (currSlice->slice_type == B_SLICE) && currSlice->bipred_mv) + free_mem6Dmv(currSlice->bipred_mv); + + if (currSlice->UseRDOQuant && currSlice->RDOQ_QP_Num > 1) + { + if (p_Inp->Transform8x8Mode && p_Inp->RDOQ_CP_MV) + { + if(currSlice->tmp_mv8) + free_mem4Dmv (currSlice->tmp_mv8); + if(currSlice->motion_cost8) + free_mem3Ddistblk(currSlice->motion_cost8); + if(currSlice->tmp_mv4) + free_mem4Dmv (currSlice->tmp_mv4); + if(currSlice->motion_cost4) + free_mem3Ddistblk(currSlice->motion_cost4); + } + } + } + + if (currSlice->slice_type == B_SLICE) + { + if(currSlice->direct_ref_idx) + free_mem3D((byte ***)currSlice->direct_ref_idx); + if(currSlice->direct_pdir) + free_mem2D((byte **) currSlice->direct_pdir); + } + + free_block_mem(currSlice); + + if (currSlice->slice_type != I_SLICE && currSlice->slice_type != SI_SLICE) + { + if (p_Inp->SearchMode[p_Vid->view_id] == EPZS) + { + if(currSlice->p_EPZS) + EPZSStructDelete (currSlice); + } + } + + free(currSlice); + } +} + +/*! + ************************************************************************ + * \brief + * Memory frees of all Slice structures and of its dependent + * data structures + * \par Input: + * Picture *currPic + ************************************************************************ + */ +void free_slice_list(Picture *currPic) +{ + int i; + + if (currPic != NULL) + { + free_nal_unit(currPic); + + for (i = 0; i < currPic->no_slices; i++) + { + free_slice (currPic->slices[i]); + currPic->slices[i] = NULL; + } + } +} + +void UpdateMELambda(Slice *currSlice) +{ + InputParameters *p_Inp = currSlice->p_Inp; + + if (p_Inp->UpdateLambdaChromaME) + { + VideoParameters *p_Vid = currSlice->p_Vid; + int j, k, qp; + if (currSlice->slice_type == B_SLICE) + j = currSlice->nal_reference_idc ? 5 : B_SLICE; + else + j = currSlice->slice_type; + + switch(p_Vid->yuv_format) + { + case YUV420: + for (qp = -currSlice->bitdepth_luma_qp_scale; qp < 52; qp++) + { + for (k = 0; k < 3; k++) + { + if ((p_Inp->MEErrorMetric[k] == ERROR_SAD) && (p_Inp->ChromaMEEnable)) + { + p_Vid->lambda_mf[j][qp][k] = (3 * p_Vid->lambda_mf[j][qp][k] + 1) >> 1; + p_Vid->lambda_me[j][qp][k] *= 1.5; + } + } + } + break; + case YUV422: + for (qp = -currSlice->bitdepth_luma_qp_scale; qp < 52; qp++) + { + for (k = 0; k < 3; k++) + { + if ((p_Inp->MEErrorMetric[k] == ERROR_SAD) && (p_Inp->ChromaMEEnable)) + { + p_Vid->lambda_mf[j][qp][k] *= 2; + p_Vid->lambda_me[j][qp][k] *= 2.0; + } + } + } + break; + case YUV444: + for (qp = -currSlice->bitdepth_luma_qp_scale; qp < 52; qp++) + { + for (k = 0; k < 3; k++) + { + if ((p_Inp->MEErrorMetric[k] == ERROR_SAD) && (p_Inp->ChromaMEEnable)) + { + p_Vid->lambda_mf[j][qp][k] *= 3; + p_Vid->lambda_me[j][qp][k] *= 3.0; + } + } + } + break; + default: + break; + } + } +} + + +void SetLagrangianMultipliersOn(Slice *currSlice) +{ + InputParameters *p_Inp = currSlice->p_Inp; + + if (p_Inp->UseExplicitLambdaParams == 1) // consideration of explicit lambda weights. + get_explicit_lambda(currSlice); + else if (p_Inp->UseExplicitLambdaParams == 2) // consideration of fixed lambda values. + get_fixed_lambda(currSlice); + else + { + switch(currSlice->slice_type) + { + case I_SLICE: + get_implicit_lambda_i_slice(currSlice); + break; + default: + case P_SLICE: + get_implicit_lambda_p_slice(currSlice); + break; + case B_SLICE: + get_implicit_lambda_b_slice(currSlice); + break; + case SI_SLICE: + case SP_SLICE: + get_implicit_lambda_sp_slice(currSlice); + break; + } + } + + UpdateMELambda(currSlice); + if ( p_Inp->UseRDOQuant ) + { + set_rdoq_lambda(currSlice); + } +} + + +void SetLagrangianMultipliersOff(Slice *currSlice) +{ + VideoParameters *p_Vid = currSlice->p_Vid; + InputParameters *p_Inp = currSlice->p_Inp; + + int qp, j, k; + double qp_temp; + + for (j = 0; j < 6; j++) + { + for (qp = -currSlice->bitdepth_luma_qp_scale; qp < 52; qp++) + { + qp_temp = (double)qp + currSlice->bitdepth_luma_qp_scale - SHIFT_QP; + + switch (p_Inp->UseExplicitLambdaParams) + { + case 1: // explicit lambda weights + p_Vid->lambda_md[j][qp] = sqrt(p_Inp->LambdaWeight[j] * pow (2, qp_temp/3.0)); + break; + case 2: // explicit lambda + p_Vid->lambda_md[j][qp] = sqrt(p_Inp->FixedLambda[j]); + break; + default: + p_Vid->lambda_md[j][qp] = QP2QUANT[imax(0,qp - SHIFT_QP)]; + break; + } + + for (k = F_PEL; k <= Q_PEL; k++) + { + p_Vid->lambda_me[j][qp][k] = (p_Inp->MEErrorMetric[k] == ERROR_SSE && p_Inp->MESoftenSSEMetric == 0) ? (p_Vid->lambda_md[j][qp] * p_Vid->lambda_md[j][qp]) : p_Vid->lambda_md[j][qp]; + p_Vid->lambda_mf[j][qp][k] = LAMBDA_FACTOR (p_Vid->lambda_me[j][qp][k]); + } + + if (p_Inp->CtxAdptLagrangeMult == 1) + { + int lambda_qp = (qp >= 32 && !p_Inp->RCEnable) ? imax(0, qp-4) : imax(0, qp-6); + p_Vid->lambda_mf_factor[j][qp] = log (p_Vid->lambda_me[j][lambda_qp][Q_PEL] + 1.0) / log (2.0); + } + } + } + UpdateMELambda(currSlice); + if ( p_Inp->UseRDOQuant ) + { + set_rdoq_lambda(currSlice); + } +} + + + diff --git a/lencod/src/symbol.c b/lencod/src/symbol.c new file mode 100644 index 0000000..c1580f3 --- /dev/null +++ b/lencod/src/symbol.c @@ -0,0 +1,18 @@ + +/*! +*************************************************************************** +* +* \file symbol.c +* +* \brief +* Generic Symbol writing interface +* +* \date +* 18 Jan 2006 +* +* \author +* Karsten Suehring +**************************************************************************/ + +#include "global.h" +#include "symbol.h" diff --git a/lencod/src/transcode_enc.c b/lencod/src/transcode_enc.c new file mode 100644 index 0000000..d5537eb --- /dev/null +++ b/lencod/src/transcode_enc.c @@ -0,0 +1,81 @@ +/*! +************************************************************************ +* \file transcode_enc.c +* +* \brief +* Functions to use trancoding information from decoder +* +* \author +* - João Carreira +* +************************************************************************ +*/ + +#include "transcode_enc.h" +#include "transcode_common.h" + +typedef struct trans_t +{ + int p_filein[MAX_VIEW_NUM]; + int file_EOF[MAX_VIEW_NUM]; + + TRANS_FRAME_NODE_T vTransNodes[MAX_TRANSCODED_FRAMES]; + +} TRANS_ENC_T; + +void initTranscodeModule( VideoParameters *p_Vid ) +{ + TRANS_ENC_T *TranscodeModule = NULL; + char out_ViewFileName[FILE_NAME_SIZE], chBuf[FILE_NAME_SIZE], *pch; + int i, layer; + + if ( ( TranscodeModule = (TRANS_ENC_T*)calloc(1, sizeof(TRANS_ENC_T)) ) == NULL ) + no_mem_exit("alloc_video_params: p_Vid->p_Transcode"); + + memset(TranscodeModule,0,sizeof(TRANS_ENC_T)); + memset(TranscodeModule->p_filein,-1,MAX_NUM_DPB_LAYERS*sizeof(int)); + + for(layer=0; layernum_of_layers; layer++ ) + { + if ((strcasecmp(p_Vid->p_Inp->TranscodeFile[layer], "\"\"")!=0) && (strlen(p_Vid->p_Inp->TranscodeFile[layer])>0)) + { + if ((TranscodeModule->p_filein[layer]=open(p_Vid->p_Inp->TranscodeFile[layer], OPENFLAGS_READ, OPEN_PERMISSIONS))==-1) + { + snprintf(errortext, ET_SIZE, "Error open file %s ", p_Vid->p_Inp->TranscodeFile[layer]); + error(errortext,500); + } + } + } + + p_Vid->p_Transcode = TranscodeModule; +} + +void closeTranscodeModule( VideoParameters *p_Vid ) +{ + TRANS_ENC_T *TranscodeModule = (TRANS_ENC_T *) p_Vid->p_Transcode; + int i; + for(i=0; ip_filein[i] != -1 ) + { + close( TranscodeModule->p_filein[i] ); + } + } + free(TranscodeModule); + p_Vid->p_Transcode = NULL; +} + +void readTranscodingInfo( VideoParameters *p_Vid ) +{ + TRANS_ENC_T *TranscodeModule = (TRANS_ENC_T *) p_Vid->p_Transcode; + int layer_id = p_Vid->view_id; //! improve this + + if( TranscodeModule->file_EOF[layer_id] == 0) + { + if( read_trans_node( TranscodeModule->p_filein[layer_id], &(TranscodeModule->vTransNodes[0])) != 0 ) + { + TranscodeModule->file_EOF[layer_id] = 1; + } + } + return; +} \ No newline at end of file diff --git a/lencod/src/transform8x8.c b/lencod/src/transform8x8.c new file mode 100644 index 0000000..2f52d61 --- /dev/null +++ b/lencod/src/transform8x8.c @@ -0,0 +1,900 @@ +/*! + *************************************************************************** + * \file transform8x8.c + * + * \brief + * 8x8 transform functions + * + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * - Yuri Vatis + * - Jan Muenster + * - Lowell Winger + * \date + * 12. October 2003 + ************************************************************************** + */ + +#include +#include + +#include "global.h" + +#include "image.h" +#include "mb_access.h" +#include "blk_prediction.h" +#include "elements.h" +#include "vlc.h" +#include "transform8x8.h" +#include "transform.h" +#include "macroblock.h" +#include "symbol.h" +#include "mc_prediction.h" +#include "md_distortion.h" +#include "quant8x8.h" +#include "rdoq.h" +#include "q_matrix.h" +#include "q_offsets.h" +#include "rdopt.h" +#include "md_common.h" +#include "intra8x8.h" +#include "rdopt_coding_state.h" + +//! single scan pattern +static const byte SNGL_SCAN8x8[64][2] = { + {0,0}, {1,0}, {0,1}, {0,2}, {1,1}, {2,0}, {3,0}, {2,1}, + {1,2}, {0,3}, {0,4}, {1,3}, {2,2}, {3,1}, {4,0}, {5,0}, + {4,1}, {3,2}, {2,3}, {1,4}, {0,5}, {0,6}, {1,5}, {2,4}, + {3,3}, {4,2}, {5,1}, {6,0}, {7,0}, {6,1}, {5,2}, {4,3}, + {3,4}, {2,5}, {1,6}, {0,7}, {1,7}, {2,6}, {3,5}, {4,4}, + {5,3}, {6,2}, {7,1}, {7,2}, {6,3}, {5,4}, {4,5}, {3,6}, + {2,7}, {3,7}, {4,6}, {5,5}, {6,4}, {7,3}, {7,4}, {6,5}, + {5,6}, {4,7}, {5,7}, {6,6}, {7,5}, {7,6}, {6,7}, {7,7} +}; + +static const byte SNGL_SCAN8x8_CAVLC[64][2] = { + {0,0}, {1,1}, {1,2}, {2,2}, {4,1}, {0,5}, {3,3}, {7,0}, {3,4}, {1,7}, {5,3}, {6,3}, {2,7}, {6,4}, {5,6}, {7,5}, + {1,0}, {2,0}, {0,3}, {3,1}, {3,2}, {0,6}, {4,2}, {6,1}, {2,5}, {2,6}, {6,2}, {5,4}, {3,7}, {7,3}, {4,7}, {7,6}, + {0,1}, {3,0}, {0,4}, {4,0}, {2,3}, {1,5}, {5,1}, {5,2}, {1,6}, {3,5}, {7,1}, {4,5}, {4,6}, {7,4}, {5,7}, {6,7}, + {0,2}, {2,1}, {1,3}, {5,0}, {1,4}, {2,4}, {6,0}, {4,3}, {0,7}, {4,4}, {7,2}, {3,6}, {5,5}, {6,5}, {6,6}, {7,7} +}; + +//! field scan pattern +static const byte FIELD_SCAN8x8[64][2] = { // 8x8 + {0,0}, {0,1}, {0,2}, {1,0}, {1,1}, {0,3}, {0,4}, {1,2}, + {2,0}, {1,3}, {0,5}, {0,6}, {0,7}, {1,4}, {2,1}, {3,0}, + {2,2}, {1,5}, {1,6}, {1,7}, {2,3}, {3,1}, {4,0}, {3,2}, + {2,4}, {2,5}, {2,6}, {2,7}, {3,3}, {4,1}, {5,0}, {4,2}, + {3,4}, {3,5}, {3,6}, {3,7}, {4,3}, {5,1}, {6,0}, {5,2}, + {4,4}, {4,5}, {4,6}, {4,7}, {5,3}, {6,1}, {6,2}, {5,4}, + {5,5}, {5,6}, {5,7}, {6,3}, {7,0}, {7,1}, {6,4}, {6,5}, + {6,6}, {6,7}, {7,2}, {7,3}, {7,4}, {7,5}, {7,6}, {7,7} +}; + +static const byte FIELD_SCAN8x8_CAVLC[64][2] = { + {0,0}, {1,1}, {2,0}, {0,7}, {2,2}, {2,3}, {2,4}, {3,3}, {3,4}, {4,3}, {4,4}, {5,3}, {5,5}, {7,0}, {6,6}, {7,4}, + {0,1}, {0,3}, {1,3}, {1,4}, {1,5}, {3,1}, {2,5}, {4,1}, {3,5}, {5,1}, {4,5}, {6,1}, {5,6}, {7,1}, {6,7}, {7,5}, + {0,2}, {0,4}, {0,5}, {2,1}, {1,6}, {4,0}, {2,6}, {5,0}, {3,6}, {6,0}, {4,6}, {6,2}, {5,7}, {6,4}, {7,2}, {7,6}, + {1,0}, {1,2}, {0,6}, {3,0}, {1,7}, {3,2}, {2,7}, {4,2}, {3,7}, {5,2}, {4,7}, {5,4}, {6,3}, {6,5}, {7,3}, {7,7} +}; + + +//! array used to find expensive coefficients +static const byte COEFF_COST8x8[2][64] = +{ + {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}, + {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} +}; + + +// Predictor array index definitions +#define P_Z (PredPel[0]) +#define P_A (PredPel[1]) +#define P_B (PredPel[2]) +#define P_C (PredPel[3]) +#define P_D (PredPel[4]) +#define P_E (PredPel[5]) +#define P_F (PredPel[6]) +#define P_G (PredPel[7]) +#define P_H (PredPel[8]) +#define P_I (PredPel[9]) +#define P_J (PredPel[10]) +#define P_K (PredPel[11]) +#define P_L (PredPel[12]) +#define P_M (PredPel[13]) +#define P_N (PredPel[14]) +#define P_O (PredPel[15]) +#define P_P (PredPel[16]) +#define P_Q (PredPel[17]) +#define P_R (PredPel[18]) +#define P_S (PredPel[19]) +#define P_T (PredPel[20]) +#define P_U (PredPel[21]) +#define P_V (PredPel[22]) +#define P_W (PredPel[23]) +#define P_X (PredPel[24]) + + + +/*! +************************************************************************ +* \brief +* Residual DPCM for Intra lossless coding +* +* \par Input: +* block_x,block_y: Block position inside a macro block (0,8). +************************************************************************ +*/ +//For residual DPCM +static int Residual_DPCM_8x8(int ipmode, int **ores, int **rres,int block_y, int block_x) +{ + int i,j; + int temp[8][8]; + + if(ipmode==VERT_PRED) + { + for (j=0; j<8; j++) + temp[0][j] = ores[block_y][block_x+j]; + + for (i=1; i<8; i++) + for (j=0; j<8; j++) + temp[i][j] = ores[block_y+i][block_x+j] - ores[block_y+i-1][block_x+j]; + + for (i = 0; i < 8; i++) + for (j = 0; j < 8; j++) + rres[block_y+i][block_x+j] = temp[i][j]; + } + else //HOR_PRED + { + for (i=0; i<8; i++) + temp[i][0] = ores[block_y + i][block_x]; + + for (i=0; i<8; i++) + for (j=1; j<8; j++) + temp[i][j] = ores[block_y+i][block_x+j] - ores[block_y+i][block_x+j-1]; + + for (i=0; i<8; i++) + for (j=0; j<8; j++) + rres[block_y+i][block_x+j] = temp[i][j]; + } + return 0; +} + +/*! +************************************************************************ +* \brief +* Inverse residual DPCM for Intra lossless coding +* +* \par Input: +* block_x,block_y: Block position inside a macro block (0,8). +************************************************************************ +*/ +//For residual DPCM +static int Inv_Residual_DPCM_8x8(Macroblock *currMB, int **m7, int block_y, int block_x) +{ + int i; + int temp[8][8]; + + if(currMB->ipmode_DPCM == VERT_PRED) + { + for(i=0; i<8; i++) + { + temp[0][i] = m7[block_y+0][block_x+i]; + temp[1][i] = temp[0][i] + m7[block_y+1][block_x+i]; + temp[2][i] = temp[1][i] + m7[block_y+2][block_x+i]; + temp[3][i] = temp[2][i] + m7[block_y+3][block_x+i]; + temp[4][i] = temp[3][i] + m7[block_y+4][block_x+i]; + temp[5][i] = temp[4][i] + m7[block_y+5][block_x+i]; + temp[6][i] = temp[5][i] + m7[block_y+6][block_x+i]; + temp[7][i] = temp[6][i] + m7[block_y+7][block_x+i]; + } + for(i=0; i<8; i++) + { + m7[block_y+1][block_x+i] = temp[1][i]; + m7[block_y+2][block_x+i] = temp[2][i]; + m7[block_y+3][block_x+i] = temp[3][i]; + m7[block_y+4][block_x+i] = temp[4][i]; + m7[block_y+5][block_x+i] = temp[5][i]; + m7[block_y+6][block_x+i] = temp[6][i]; + m7[block_y+7][block_x+i] = temp[7][i]; + } + } + else //HOR_PRED + { + for(i=0; i<8; i++) + { + temp[i][0] = m7[block_y+i][block_x+0]; + temp[i][1] = temp[i][0] + m7[block_y+i][block_x+1]; + temp[i][2] = temp[i][1] + m7[block_y+i][block_x+2]; + temp[i][3] = temp[i][2] + m7[block_y+i][block_x+3]; + temp[i][4] = temp[i][3] + m7[block_y+i][block_x+4]; + temp[i][5] = temp[i][4] + m7[block_y+i][block_x+5]; + temp[i][6] = temp[i][5] + m7[block_y+i][block_x+6]; + temp[i][7] = temp[i][6] + m7[block_y+i][block_x+7]; + } + for(i=0; i<8; i++) + { + m7[block_y+i][block_x+1] = temp[i][1]; + m7[block_y+i][block_x+2] = temp[i][2]; + m7[block_y+i][block_x+3] = temp[i][3]; + m7[block_y+i][block_x+4] = temp[i][4]; + m7[block_y+i][block_x+5] = temp[i][5]; + m7[block_y+i][block_x+6] = temp[i][6]; + m7[block_y+i][block_x+7] = temp[i][7]; + } + } + return 0; +} + +/*! + ************************************************************************************* + * \brief + * 8x8 Intra mode decision for a macroblock + ************************************************************************************* + */ +int mode_decision_for_I8x8_MB (Macroblock *currMB, int lambda, distblk *min_cost) +{ + Slice *currSlice = currMB->p_Slice; + int cur_cbp = 0, b8; + //int cr_cbp[3] = { 0, 0, 0}; + distblk cost8x8; + *min_cost = weighted_cost(lambda, 6); //6 bits overhead; + + if (currSlice->P444_joined == 0) + { + for (b8=0; b8<4; b8++) + { + if (currSlice->mode_decision_for_I8x8_blocks (currMB, b8, lambda, &cost8x8)) + { + cur_cbp |= (1<cmp_cbp[1] = currSlice->cmp_cbp[2] = 0; + currMB->cr_cbp[0] = 0; + currMB->cr_cbp[1] = 0; + currMB->cr_cbp[2] = 0; + + for (b8 = 0; b8 < 4; b8++) + { + if (currSlice->mode_decision_for_I8x8_blocks (currMB, b8, lambda, &cost8x8)) + { + cur_cbp |= (1<cr_cbp[k]) //if (cr_cbp[k]) + { + currSlice->cmp_cbp[k] |= (1 << b8); + cur_cbp |= currSlice->cmp_cbp[k]; + currSlice->cmp_cbp[k] = cur_cbp; + } + } + } + } + + return cur_cbp; +} + + +/*! + ************************************************************************************* + * \brief + * R-D Cost for an 8x8 Intra block + ************************************************************************************* + */ +distblk rdcost_for_8x8_intra_blocks(Macroblock *currMB, int *nonzero, int b8, int ipmode, int lambda, distblk min_rdcost, int mostProbableMode) +{ + VideoParameters *p_Vid = currMB->p_Vid; + Slice *currSlice = currMB->p_Slice; + distblk rdcost = 0; + int dummy = 0; + int rate; + distblk distortion = 0; + int block_x = (b8 & 0x01) << 3; + int block_y = (b8 >> 1) << 3; + int pic_pix_x = currMB->pix_x + block_x; + int pic_pix_y = currMB->pix_y + block_y; + int pic_opix_y = currMB->opix_y + block_y; + + SyntaxElement se; + const int *partMap = assignSE2partition[currSlice->partition_mode]; + DataPartition *dataPart; + + //===== perform forward transform, Q, IQ, inverse transform, Reconstruction ===== + *nonzero = currMB->residual_transform_quant_luma_8x8 (currMB, PLANE_Y, b8, &dummy, 1); + + //===== get distortion (SSD) of 8x8 block ===== + distortion += compute_SSE8x8(&p_Vid->pCurImg[pic_opix_y], &p_Vid->enc_picture->imgY[pic_pix_y], pic_pix_x, pic_pix_x); + if (distortion > min_rdcost) + { + //currSlice->reset_coding_state (currMB, currSlice->p_RDO->cs_cm); + return distortion; + } + currMB->ipmode_DPCM = NO_INTRA_PMODE; + + //===== RATE for INTRA PREDICTION MODE (SYMBOL MODE MUST BE SET TO CAVLC) ===== + se.value1 = (mostProbableMode == ipmode) ? -1 : ipmode < mostProbableMode ? ipmode : ipmode-1; + + //--- set position and type --- + se.context = b8; + se.type = SE_INTRAPREDMODE; + + //--- choose data partition --- + if (currSlice->slice_type != B_SLICE) + dataPart = &(currSlice->partArr[partMap[SE_INTRAPREDMODE]]); + else + dataPart = &(currSlice->partArr[partMap[SE_BFRAME]]); + + //--- encode and update rate --- + currSlice->writeIntraPredMode (&se, dataPart); + + rate = se.len; + + //===== RATE for LUMINANCE COEFFICIENTS ===== + + if (currSlice->symbol_mode == CAVLC) + { + rate += currSlice->writeCoeff4x4_CAVLC (currMB, LUMA, b8, 0, 0); + rate += currSlice->writeCoeff4x4_CAVLC (currMB, LUMA, b8, 1, 0); + rate += currSlice->writeCoeff4x4_CAVLC (currMB, LUMA, b8, 2, 0); + rate += currSlice->writeCoeff4x4_CAVLC (currMB, LUMA, b8, 3, 0); + } + else + { + rate += writeCoeff8x8_CABAC (currMB, PLANE_Y, b8, 1); + } + rdcost = distortion + weight_cost(lambda, rate); + currSlice->reset_coding_state (currMB, currSlice->p_RDO->cs_cm); + + return rdcost; +} + + +/*! + ************************************************************************************* + * \brief + * R-D Cost for an 8x8 Intra block + ************************************************************************************* + */ +distblk rdcost_for_8x8_intra_blocks_444(Macroblock *currMB, int *nonzero, int b8, int ipmode, int lambda, distblk min_rdcost, int mostProbableMode) +{ + VideoParameters *p_Vid = currMB->p_Vid; + Slice *currSlice = currMB->p_Slice; + distblk rdcost = 0; + int dummy = 0; + int rate; + distblk distortion = 0; + int block_x = (b8 & 0x01) << 3; + int block_y = (b8 >> 1) << 3; + int pic_pix_x = currMB->pix_x + block_x; + int pic_pix_y = currMB->pix_y + block_y; + int pic_opix_y = currMB->opix_y + block_y; + + SyntaxElement se; + const int *partMap = assignSE2partition[currSlice->partition_mode]; + DataPartition *dataPart; + + if(currSlice->P444_joined == 0) + { + //===== perform forward transform, Q, IQ, inverse transform, Reconstruction ===== + *nonzero = currMB->residual_transform_quant_luma_8x8 (currMB, PLANE_Y, b8, &dummy, 1); + + //===== get distortion (SSD) of 8x8 block ===== + distortion += compute_SSE8x8(&p_Vid->pCurImg[pic_opix_y], &p_Vid->enc_picture->imgY[pic_pix_y], pic_pix_x, pic_pix_x); + + currMB->ipmode_DPCM = NO_INTRA_PMODE; + + //===== RATE for INTRA PREDICTION MODE (SYMBOL MODE MUST BE SET TO CAVLC) ===== + se.value1 = (mostProbableMode == ipmode) ? -1 : ipmode < mostProbableMode ? ipmode : ipmode-1; + + //--- set position and type --- + se.context = b8; + se.type = SE_INTRAPREDMODE; + + //--- choose data partition --- + if (currSlice->slice_type != B_SLICE) + dataPart = &(currSlice->partArr[partMap[SE_INTRAPREDMODE]]); + else + dataPart = &(currSlice->partArr[partMap[SE_BFRAME]]); + + //--- encode and update rate --- + currSlice->writeIntraPredMode (&se, dataPart); + + rate = se.len; + + //===== RATE for LUMINANCE COEFFICIENTS ===== + + if (currSlice->symbol_mode == CAVLC) + { + rate += currSlice->writeCoeff4x4_CAVLC (currMB, LUMA, b8, 0, 0); + rate += currSlice->writeCoeff4x4_CAVLC (currMB, LUMA, b8, 1, 0); + rate += currSlice->writeCoeff4x4_CAVLC (currMB, LUMA, b8, 2, 0); + rate += currSlice->writeCoeff4x4_CAVLC (currMB, LUMA, b8, 3, 0); + } + else + { + rate += writeCoeff8x8_CABAC (currMB, PLANE_Y, b8, 1); + } + } + else + { + ColorPlane k; + //===== perform forward transform, Q, IQ, inverse transform, Reconstruction ===== + *nonzero = currMB->residual_transform_quant_luma_8x8 (currMB, PLANE_Y, b8, &dummy, 1); + + //===== get distortion (SSD) of 8x8 block ===== + distortion += compute_SSE8x8(&p_Vid->pCurImg[pic_opix_y], &p_Vid->enc_picture->imgY[pic_pix_y], pic_pix_x, pic_pix_x); + + for (k = PLANE_U; k <= PLANE_V; k++) + { + select_plane(p_Vid, k); + currMB->c_nzCbCr[k ]= currMB->residual_transform_quant_luma_8x8(currMB, k, b8, &dummy,1); + distortion += compute_SSE8x8(&p_Vid->pImgOrg[k][pic_opix_y], &p_Vid->enc_picture->p_curr_img[pic_pix_y], pic_pix_x, pic_pix_x); + } + currMB->ipmode_DPCM = NO_INTRA_PMODE; + select_plane(p_Vid, PLANE_Y); + + //===== RATE for INTRA PREDICTION MODE (SYMBOL MODE MUST BE SET TO CAVLC) ===== + se.value1 = (mostProbableMode == ipmode) ? -1 : ipmode < mostProbableMode ? ipmode : ipmode-1; + + //--- set position and type --- + se.context = b8; + se.type = SE_INTRAPREDMODE; + + //--- choose data partition --- + if (currSlice->slice_type != B_SLICE) + dataPart = &(currSlice->partArr[partMap[SE_INTRAPREDMODE]]); + else + dataPart = &(currSlice->partArr[partMap[SE_BFRAME]]); + + //--- encode and update rate --- + currSlice->writeIntraPredMode (&se, dataPart); + rate = se.len; + + //===== RATE for LUMINANCE COEFFICIENTS ===== + + if (currSlice->symbol_mode == CAVLC) + { + int b4; + for(b4=0; b4<4; b4++) + { + rate += currSlice->writeCoeff4x4_CAVLC (currMB, LUMA, b8, b4, 0); + rate += currSlice->writeCoeff4x4_CAVLC (currMB, CB, b8, b4, 0); + rate += currSlice->writeCoeff4x4_CAVLC (currMB, CR, b8, b4, 0); + } + } + else + { + rate += writeCoeff8x8_CABAC (currMB, PLANE_Y, b8, 1); + rate += writeCoeff8x8_CABAC (currMB, PLANE_U, b8, 1); + rate += writeCoeff8x8_CABAC (currMB, PLANE_V, b8, 1); + } + } + rdcost = distortion + weight_cost(lambda, rate); + currSlice->reset_coding_state (currMB, currSlice->p_RDO->cs_cm); + + return rdcost; +} + +static inline int check_zero(int **mb_ores, int block_x) +{ + int i, j, k = 0; + + for (j = 0; (j < BLOCK_SIZE_8x8) && (k == 0); j++) + { + for (i = block_x; (i< block_x + BLOCK_SIZE_8x8) && (k == 0); i++) + { + //k |= (mb_ores[j][i] != 0); + k |= mb_ores[j][i]; + } + } + return k; +} + +/*! + ************************************************************************ + * \brief + * The routine performs transform,quantization,inverse transform, adds the diff. + * to the prediction and writes the result to the decoded luma frame. Includes the + * RD constrained quantization also. + * + * \par Input: + * b8: Block position inside a macro block (0,1,2,3). + * + * \par Output: + * nonzero: 0 if no levels are nonzero. 1 if there are nonzero levels. + * coeff_cost: Counter for nonzero coefficients, used to discard expensive levels. + ************************************************************************ + */ +int residual_transform_quant_luma_8x8(Macroblock *currMB, ColorPlane pl, int b8, int *coeff_cost, int intra) +{ + VideoParameters *p_Vid = currMB->p_Vid; + int nonzero = FALSE; + + int block_x = 8*(b8 & 0x01); + int block_y = 8*(b8 >> 1); + int pl_off = b8+ (pl<<2); + Slice *currSlice = currMB->p_Slice; + imgpel **img_enc = p_Vid->enc_picture->p_curr_img; + + imgpel **mb_pred = currSlice->mb_pred[pl]; + int **mb_ores = currSlice->mb_ores[pl]; + int **mb_rres = currSlice->mb_rres[pl]; + + int max_imgpel_value = p_Vid->max_imgpel_value; + + if (check_zero(&mb_ores[block_y], block_x) != 0) + { + int qp = (p_Vid->yuv_format==YUV444 && !currSlice->P444_joined)? currMB->qp_scaled[(int)(p_Vid->colour_plane_id)]: currMB->qp_scaled[pl]; + + // Variable p_Quant and some of its parameters could be all set outside + // to speed up the code (e.g. field mode, coeff_cost, etc). + QuantParameters *p_Quant = p_Vid->p_Quant; + + QuantMethods quant_methods; + quant_methods.block_x = block_x; + quant_methods.block_y = block_y; + + quant_methods.ACLevel = currSlice->cofAC[pl_off][0][0]; + quant_methods.ACRun = currSlice->cofAC[pl_off][0][1]; + + quant_methods.qp = qp; + quant_methods.q_params = p_Quant->q_params_8x8[pl][intra][qp]; + quant_methods.fadjust = p_Vid->AdaptiveRounding ? (&p_Vid->ARCofAdj8x8[pl][currMB->ar_mode][block_y]) : NULL; + quant_methods.coeff_cost = coeff_cost; + quant_methods.pos_scan = currMB->is_field_mode ? FIELD_SCAN8x8 : SNGL_SCAN8x8; + quant_methods.c_cost = COEFF_COST8x8[currSlice->disthres]; + + // Forward 8x8 transform + forward8x8(mb_ores, mb_rres, block_y, block_x); + + // Quantization process + nonzero = currSlice->quant_8x8(currMB, &mb_rres[block_y], &quant_methods); + } + else + { + currSlice->cofAC[pl_off][0][0][0] = 0; + } + + if (nonzero) + { + // Inverse 8x8 transform + inverse8x8(&mb_rres[block_y], &mb_rres[block_y], block_x); + + // generate final block + sample_reconstruct (&img_enc[currMB->pix_y + block_y], &mb_pred[block_y], &mb_rres[block_y], block_x, currMB->pix_x + block_x, BLOCK_SIZE_8x8, BLOCK_SIZE_8x8, max_imgpel_value, DQ_BITS_8); + } + else // if (nonzero) => No transformed residual. Just use prediction. + { + copy_image_data_8x8 (&img_enc[currMB->pix_y + block_y], &mb_pred[block_y], currMB->pix_x + block_x, block_x); + } + + // Decoded block moved to frame memory + return nonzero; +} + +/*! + ************************************************************************ + * \brief + * The routine performs transform,quantization,inverse transform, adds the diff. + * to the prediction and writes the result to the decoded luma frame. Includes the + * RD constrained quantization also. Used for CAVLC. + * + * \par Input: + * b8: Block position inside a macro block (0,1,2,3). + * + * \par Output: + * nonzero: 0 if no levels are nonzero. 1 if there are nonzero levels. + * coeff_cost: Counter for nonzero coefficients, used to discard expensive levels. + ************************************************************************ + */ +int residual_transform_quant_luma_8x8_cavlc(Macroblock *currMB, ColorPlane pl, int b8, int *coeff_cost, int intra) +{ + VideoParameters *p_Vid = currMB->p_Vid; + int nonzero = FALSE; + + int block_x = 8*(b8 & 0x01); + int block_y = 8*(b8 >> 1); + int pl_off = b8+ (pl<<2); + imgpel **img_enc = p_Vid->enc_picture->p_curr_img; + Slice *currSlice = currMB->p_Slice; + imgpel **mb_pred = currSlice->mb_pred[pl]; + int **mb_ores = currSlice->mb_ores[pl]; + int **mb_rres = currSlice->mb_rres[pl]; + + int max_imgpel_value = p_Vid->max_imgpel_value; + + int qp = currMB->qp_scaled[pl]; + + //if (check_zero(&mb_ores[block_y], block_x) != 0) + { + // Variable p_Quant and some of its parameters could be all set outside + // to speed up the code (e.g. field mode, coeff_cost, etc). + QuantParameters *p_Quant = p_Vid->p_Quant; + + QuantMethods quant_methods; + quant_methods.block_x = block_x; + quant_methods.block_y = block_y; + quant_methods.qp = qp; + quant_methods.q_params = p_Quant->q_params_8x8[pl][intra][qp]; + quant_methods.fadjust = p_Vid->AdaptiveRounding ? (&p_Vid->ARCofAdj8x8[pl][currMB->ar_mode][block_y]) : NULL; + quant_methods.coeff_cost = coeff_cost; + // quant_methods.pos_scan = currMB->is_field_mode ? FIELD_SCAN8x8 : SNGL_SCAN8x8; + quant_methods.pos_scan = currMB->is_field_mode ? FIELD_SCAN8x8_CAVLC : SNGL_SCAN8x8_CAVLC; + quant_methods.c_cost = COEFF_COST8x8[currSlice->disthres]; + + // Forward 8x8 transform + forward8x8(mb_ores, mb_rres, block_y, block_x); + + // Quantization process + nonzero = currSlice->quant_8x8cavlc(currMB, &mb_rres[block_y], &quant_methods, currSlice->cofAC[pl_off]); + } + + if (nonzero) + { + // Inverse 8x8 transform + inverse8x8(&mb_rres[block_y], &mb_rres[block_y], block_x); + + // generate final block + sample_reconstruct (&img_enc[currMB->pix_y + block_y], &mb_pred[block_y], &mb_rres[block_y], block_x, currMB->pix_x + block_x, BLOCK_SIZE_8x8, BLOCK_SIZE_8x8, max_imgpel_value, DQ_BITS_8); + } + else // if (nonzero) => No transformed residual. Just use prediction. + { + copy_image_data_8x8(&img_enc[currMB->pix_y + block_y], &mb_pred[block_y], currMB->pix_x + block_x, block_x); + } + + // Decoded block moved to frame memory + return nonzero; +} + +int residual_transform_quant_luma_8x8_ls(Macroblock *currMB, ColorPlane pl, int b8, int *coeff_cost, int intra) +{ + VideoParameters *p_Vid = currMB->p_Vid; + int i,j,coeff_ctr; + int scan_pos = 0,run = -1; + int nonzero = FALSE; + + int block_x = 8*(b8 & 0x01); + int block_y = 8*(b8 >> 1); + int pl_off = b8 + (pl<<2); + Slice *currSlice = currMB->p_Slice; + int* ACLevel = currSlice->cofAC[pl_off][0][0]; + int* ACRun = currSlice->cofAC[pl_off][0][1]; + imgpel **img_enc = p_Vid->enc_picture->p_curr_img; + imgpel **mb_pred = currSlice->mb_pred[pl]; + int **mb_ores = currSlice->mb_ores[pl]; + int **mb_rres = currSlice->mb_rres[pl]; + + int scan_poss[4] = { 0 }, runs[4] = { -1, -1, -1, -1 }; + int MCcoeff = 0; + int *m7; + int is_cavlc = (currSlice->symbol_mode == CAVLC); + + const byte (*pos_scan)[2] = currMB->is_field_mode ? FIELD_SCAN8x8 : SNGL_SCAN8x8; + + int **fadjust8x8 = p_Vid->AdaptiveRounding ? (&p_Vid->ARCofAdj8x8[pl][currMB->ar_mode][block_y]) :NULL; + + runs[0]=runs[1]=runs[2]=runs[3]=-1; + scan_poss[0] = scan_poss[1] = scan_poss[2] = scan_poss[3] = 0; + + if( (currMB->ipmode_DPCM < 2)&&(intra)) + { + Residual_DPCM_8x8(currMB->ipmode_DPCM, mb_ores, mb_rres, block_y, block_x); + } + else + { + for (j = block_y ; j < block_y + BLOCK_SIZE_8x8 ; j ++) + for (i = block_x ; i < block_x + BLOCK_SIZE_8x8 ; i ++) + mb_rres[j][i] = mb_ores[j][i] ; + } + + for (coeff_ctr=0; coeff_ctr < 64; coeff_ctr++) + { + i=pos_scan[coeff_ctr][0]; + j=pos_scan[coeff_ctr][1]; + + run++; + + if (currMB->luma_transform_size_8x8_flag && is_cavlc) + { + MCcoeff = (coeff_ctr & 3); + runs[MCcoeff]++; + } + + m7 = &mb_rres[block_y + j][block_x + i]; + + if (p_Vid->AdaptiveRounding) + { + fadjust8x8[j][block_x+i] = 0; + } + + if (*m7 != 0) + { + nonzero = TRUE; + + if (currMB->luma_transform_size_8x8_flag && is_cavlc) + { + *m7 = iClip3(-CAVLC_LEVEL_LIMIT, CAVLC_LEVEL_LIMIT, *m7); + *coeff_cost += MAX_VALUE; + + currSlice->cofAC[pl_off][MCcoeff][0][scan_poss[MCcoeff] ] = *m7; + currSlice->cofAC[pl_off][MCcoeff][1][scan_poss[MCcoeff]++] = runs[MCcoeff]; + ++scan_pos; + runs[MCcoeff]=-1; + } + else + { + *coeff_cost += MAX_VALUE; + ACLevel[scan_pos ] = *m7; + ACRun [scan_pos++] = run; + run=-1; // reset zero level counter + } + } + } + + if (!currMB->luma_transform_size_8x8_flag || !is_cavlc) + ACLevel[scan_pos] = 0; + else + { + for(i=0; i<4; i++) + currSlice->cofAC[pl_off][i][0][scan_poss[i]] = 0; + } + + if( (currMB->ipmode_DPCM < 2) && (intra)) + { + Inv_Residual_DPCM_8x8(currMB, mb_rres, block_y, block_x); + } + + for( j=block_y; jpix_y + j][currMB->pix_x + i]= (imgpel) mb_rres[j][i]; + } + } + + // Decoded block moved to frame memory + return nonzero; +} + + +/*static inline void compute_diff(int *diff, imgpel *cimg, imgpel *cmpr, int width) +{ + int i; + for (i = 0; i < width; i++) + { + *(diff++) = *(cimg++) - *(cmpr++); + } +}*/ + +/*! +************************************************************************************* +* \brief +* distortion for an 8x8 Intra block +************************************************************************************* +*/ +distblk compute_comp8x8_cost(VideoParameters *p_Vid, imgpel **cur_img, imgpel **mpr8x8, int pic_opix_x, distblk min_cost) +{ + short diff64[64]; + + int i, j; + short *diff = &diff64[0]; + imgpel *cimg, *cmpr; + + for (j=0; j<8; j++) + { + // compute_diff(diff, &cur_img[j][pic_opix_x], &mpr8x8[j][0], BLOCK_SIZE_8x8); + + cimg = &cur_img[j][pic_opix_x]; + cmpr = &mpr8x8[j][0]; + for (i=0; i<8; i++) + { + *diff++ = *cimg++ - *cmpr++; + } + + } + return p_Vid->distortion8x8 (diff64, min_cost); +} + + +/*! +************************************************************************************* +* \brief +* SAD distortion for an 8x8 Intra block +************************************************************************************* +*/ +distblk compute_sad8x8_cost(VideoParameters *p_Vid, imgpel **cur_img, imgpel **mpr8x8, int pic_opix_x, distblk min_cost) +{ + imgpel *cimg, *cmpr; + int i32Cost = 0; + int i, j; + int imin_cost = dist_down(min_cost); + + for (j=0; j<8; j++) + { + cimg = &cur_img[j][pic_opix_x]; + cmpr = &mpr8x8[j][0]; + for (i=0; i<8; i++) + { + i32Cost += iabs(*cimg++ - *cmpr++); + } + + if (i32Cost > imin_cost) + { + return min_cost; + } + } + return dist_scale(i32Cost); +} + +/*! +************************************************************************************* +* \brief +* SSE distortion for an 8x8 Intra block +************************************************************************************* +*/ +distblk compute_sse8x8_cost(VideoParameters *p_Vid, imgpel **cur_img, imgpel **mpr8x8, int pic_opix_x, distblk min_cost) +{ + int i, j; + imgpel *cimg, *cmpr; + int imin_cost = dist_down(min_cost); + int distortion = 0; + + for (j=0; j<8; j++) + { + cimg = &cur_img[j][pic_opix_x]; + cmpr = &mpr8x8[j][0]; + + for (i=0; i<8; i++) + { + distortion += iabs2(*cimg++ - *cmpr++); + } + + if (distortion > imin_cost) + { + return min_cost; + } + } + return dist_scale(distortion); +} +/*! +************************************************************************************* +* \brief +* SATD distortion for an 8x8 Intra block +************************************************************************************* +*/ +distblk compute_satd8x8_cost(VideoParameters *p_Vid, imgpel **cur_img, imgpel **mpr8x8, int pic_opix_x, distblk min_cost) +{ + int i, j; + short diff64[64]; + + short *diff = &diff64[0]; + imgpel *cimg, *cmpr; + + for (j=0; j<8; j++) + { + cimg = &cur_img[j][pic_opix_x]; + cmpr = &mpr8x8[j][0]; + for (i=0; i<8; i++) + { + *diff++ = *cimg++ - *cmpr++; + } + } + + return (dist_scale(HadamardSAD8x8 (diff64))); +} + diff --git a/lencod/src/transform8x8_H444.c b/lencod/src/transform8x8_H444.c new file mode 100644 index 0000000..af74e56 --- /dev/null +++ b/lencod/src/transform8x8_H444.c @@ -0,0 +1,424 @@ +/*! + *************************************************************************** + * \file transform8x8_H444.c + * + * \brief + * 8x8 transform functions + * + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * - Yuri Vatis + * - Jan Muenster + * - Lowell Winger + * \date + * 12. October 2003 + ************************************************************************** + */ + +#include +#include + +#include "global.h" + +#include "image.h" +#include "mb_access.h" +#include "elements.h" +#include "vlc.h" +#include "transform8x8.h" +#include "transform.h" +#include "macroblock.h" +#include "symbol.h" +#include "mc_prediction.h" +#include "quant8x8.h" +#include "rdoq.h" +#include "q_matrix.h" +#include "q_offsets.h" +#include "rdopt.h" +#include "md_common.h" +#include "intra8x8.h" +#include "rdopt_coding_state.h" +#include "blk_prediction.h" + +/*! + ************************************************************************************* + * \brief + * 8x8 Intra mode decision for a macroblock - Low complexity + ************************************************************************************* + */ +int mode_decision_for_I8x8_blocks_JM_Low444 (Macroblock *currMB, int b8, int lambda, distblk *min_cost) +{ + VideoParameters *p_Vid = currMB->p_Vid; + InputParameters *p_Inp = currMB->p_Inp; + Slice *currSlice = currMB->p_Slice; + + int ipmode, best_ipmode = 0, j, dummy; + distblk cost; + int nonzero = 0; + int block_x = (b8 & 0x01) << 3; + int block_y = (b8 >> 1) << 3; + int pic_pix_x = currMB->pix_x + block_x; + int pic_pix_y = currMB->pix_y + block_y; + int pic_opix_x = currMB->pix_x + block_x; + int pic_opix_y = currMB->opix_y + block_y; + int pic_block_x = pic_pix_x >> 2; + int pic_block_y = pic_pix_y >> 2; + int mb_block_y = (currMB->block_y) + ((b8 >> 1) << 1); + int mb_block_x = (currMB->block_x) + ((b8 & 0x01) << 1); + + //imgpel *img_org, *img_prd; + //int *residual; + int left_available, up_available, all_available; + int **mb_ores = currSlice->mb_ores[0]; + imgpel **mb_pred = currSlice->mb_pred[0]; + int *mb_size = p_Vid->mb_size[IS_LUMA]; + + char upMode, leftMode; + int mostProbableMode; + + PixelPos left_block, top_block; + + get4x4Neighbour(currMB, block_x - 1, block_y , mb_size, &left_block); + get4x4Neighbour(currMB, block_x, block_y - 1, mb_size, &top_block ); + + if (p_Inp->UseConstrainedIntraPred) + { + top_block.available = top_block.available ? p_Vid->intra_block [top_block.mb_addr] : 0; + left_block.available = left_block.available ? p_Vid->intra_block [left_block.mb_addr] : 0; + } + + if(b8 >> 1) + upMode = top_block.available ? p_Vid->ipredmode8x8[top_block.pos_y ][top_block.pos_x ] : -1; + else + upMode = top_block.available ? p_Vid->ipredmode [top_block.pos_y ][top_block.pos_x ] : -1; + + if(b8 & 0x01) + leftMode = left_block.available ? p_Vid->ipredmode8x8[left_block.pos_y][left_block.pos_x] : -1; + else + leftMode = left_block.available ? p_Vid->ipredmode[left_block.pos_y][left_block.pos_x] : -1; + + mostProbableMode = (upMode < 0 || leftMode < 0) ? DC_PRED : upMode < leftMode ? upMode : leftMode; + *min_cost = DISTBLK_MAX; + currMB->ipmode_DPCM = NO_INTRA_PMODE; //For residual DPCM + + //===== INTRA PREDICTION FOR 8x8 BLOCK ===== + currSlice->set_intrapred_8x8(currMB, PLANE_Y, pic_pix_x, pic_pix_y, &left_available, &up_available, &all_available); + + if(currSlice->P444_joined) + { + select_plane(p_Vid, PLANE_U); + currSlice->set_intrapred_8x8(currMB, PLANE_U, pic_pix_x, pic_pix_y, &left_available, &up_available, &all_available); + select_plane(p_Vid, PLANE_V); + currSlice->set_intrapred_8x8(currMB, PLANE_V, pic_pix_x, pic_pix_y, &left_available, &up_available, &all_available); + select_plane(p_Vid, PLANE_Y); + } + + //===== LOOP OVER ALL 8x8 INTRA PREDICTION MODES ===== + for (ipmode = 0; ipmode < NO_INTRA_PMODE; ipmode++) + { + if( (ipmode==DC_PRED) || + ((ipmode==VERT_PRED||ipmode==VERT_LEFT_PRED||ipmode==DIAG_DOWN_LEFT_PRED) && up_available ) || + ((ipmode==HOR_PRED||ipmode==HOR_UP_PRED) && left_available ) || + (all_available) ) + { + get_intrapred_8x8(currMB, PLANE_Y, ipmode, left_available, up_available); + cost = (ipmode == mostProbableMode) ? 0 : ( weighted_cost(lambda, 4) ); + cost += currSlice->compute_cost8x8(p_Vid, &p_Vid->pImgOrg[0][pic_opix_y], currSlice->mpr_8x8[0][ipmode], pic_opix_x, *min_cost - cost); + + if(currSlice->P444_joined) + { + get_intrapred_8x8(currMB, PLANE_U, ipmode, left_available, up_available); + cost += currSlice->compute_cost8x8(p_Vid, &p_Vid->pImgOrg[PLANE_U][pic_opix_y], currSlice->mpr_8x8[PLANE_U][ipmode], pic_opix_x, *min_cost - cost); + get_intrapred_8x8(currMB, PLANE_V, ipmode, left_available, up_available); + cost += currSlice->compute_cost8x8(p_Vid, &p_Vid->pImgOrg[PLANE_V][pic_opix_y], currSlice->mpr_8x8[PLANE_V][ipmode], pic_opix_x, *min_cost - cost); + } + + if (cost < *min_cost) + { + best_ipmode = ipmode; + *min_cost = cost; + } + } + } + + //===== set intra mode prediction ===== + p_Vid->ipredmode8x8[pic_block_y][pic_block_x] = (char) best_ipmode; + currMB->ipmode_DPCM = (char) best_ipmode; //For residual DPCM + + if(currSlice->P444_joined) + { + ColorPlane k; + p_Vid->CbCr_predmode_8x8[b8] = best_ipmode; + for (k = PLANE_U; k <= PLANE_V; k++) + { + currMB->cr_cbp[k] = 0; + select_plane(p_Vid, k); + /* + for (j=0; j<8; j++) + { + for (i=0; i<8; i++) + { + currSlice->mb_pred[k][block_y+j][block_x+i] = currSlice->mpr_8x8[k][best_ipmode][j][i]; + currSlice->mb_ores[k][block_y+j][block_x+i] = p_Vid->pImgOrg[k][currMB->pix_y + block_y + j][currMB->pix_x + block_x + i] - currSlice->mpr_8x8[k][best_ipmode][j][i]; + } + } + */ + copy_image_data_8x8(&currSlice->mb_pred[k][block_y], currSlice->mpr_8x8[k][best_ipmode], block_x, 0); + compute_residue(&(p_Vid->pImgOrg[k][currMB->pix_y+block_y]), &currSlice->mb_pred[k][block_y], &currSlice->mb_ores[k][block_y], block_x, currMB->pix_x+block_x, 8, 8); + + currMB->ipmode_DPCM = (short) best_ipmode; + if (currMB->residual_transform_quant_luma_8x8(currMB, k, b8, &dummy, 1)) + currMB->cr_cbp[k] = 1; + } + select_plane(p_Vid, PLANE_Y); + } + + currMB->intra_pred_modes8x8[4*b8] = (char) ((mostProbableMode == best_ipmode) + ? -1 + : (best_ipmode < mostProbableMode ? best_ipmode : best_ipmode-1)); + + for(j = mb_block_y; j < mb_block_y + 2; j++) //loop 4x4s in the subblock for 8x8 prediction setting + memset(&p_Vid->ipredmode8x8[j][mb_block_x], best_ipmode, 2 * sizeof(char)); + + // get prediction and prediction error + /* + for (j = block_y; j < block_y + 8; j++) + { + memcpy(&mb_pred[j][block_x],currSlice->mpr_8x8[0][best_ipmode][j - block_y], 8 * sizeof(imgpel)); + img_org = &p_Vid->pCurImg[currMB->opix_y + j][pic_opix_x]; + img_prd = &mb_pred[j][block_x]; + residual = &mb_ores[j][block_x]; + for (i=0; i<8; i++) + { + *residual++ = *img_org++ - *img_prd++; + } + } + */ + generate_pred_error_8x8(&p_Vid->pCurImg[currMB->opix_y+block_y], currSlice->mpr_8x8[0][best_ipmode], &mb_pred[block_y], &mb_ores[block_y], pic_opix_x, block_x); + + currMB->ipmode_DPCM = (short) best_ipmode; + nonzero = currMB->residual_transform_quant_luma_8x8 (currMB, PLANE_Y, b8, &dummy, 1); + return nonzero; +} + +/*! +************************************************************************************* +* \brief +* 8x8 Intra mode decision for a macroblock - High complexity +************************************************************************************* +*/ +int mode_decision_for_I8x8_blocks_JM_High444 (Macroblock *currMB, int b8, int lambda, distblk *min_cost) +{ + VideoParameters *p_Vid = currMB->p_Vid; + InputParameters *p_Inp = currMB->p_Inp; + Slice *currSlice = currMB->p_Slice; + RDOPTStructure *p_RDO = currSlice->p_RDO; + + int ipmode, best_ipmode = 0, j, dummy; + int c_nz, nonzero = 0; + distblk rdcost = 0; + distblk min_rdcost = DISTBLK_MAX; + int block_x = (b8 & 0x01) << 3; + int block_y = (b8 >> 1) << 3; + int pic_pix_x = currMB->pix_x + block_x; + int pic_pix_y = currMB->pix_y + block_y; + int pic_opix_x = currMB->pix_x + block_x; + int pic_opix_y = currMB->opix_y + block_y; + int pic_block_x = pic_pix_x >> 2; + int pic_block_y = pic_pix_y >> 2; + int mb_block_y = (currMB->block_y) + ((b8 >> 1) << 1); + int mb_block_x = (currMB->block_x) + ((b8 & 0x01) << 1); + + int uv; + int left_available, up_available, all_available; + + char upMode, leftMode; + int mostProbableMode; + + PixelPos left_block, top_block; + + int *mb_size = p_Vid->mb_size[IS_LUMA]; + + get4x4Neighbour(currMB, block_x - 1, block_y , mb_size, &left_block); + get4x4Neighbour(currMB, block_x, block_y - 1, mb_size, &top_block ); + + if (p_Inp->UseConstrainedIntraPred) + { + top_block.available = top_block.available ? p_Vid->intra_block [top_block.mb_addr ] : 0; + left_block.available = left_block.available ? p_Vid->intra_block [left_block.mb_addr] : 0; + } + + if(b8 >> 1) + upMode = top_block.available ? p_Vid->ipredmode8x8[top_block.pos_y ][top_block.pos_x ] : -1; + else + upMode = top_block.available ? p_Vid->ipredmode [top_block.pos_y ][top_block.pos_x ] : -1; + + if(b8 & 0x01) + leftMode = left_block.available ? p_Vid->ipredmode8x8[left_block.pos_y][left_block.pos_x] : -1; + else + leftMode = left_block.available ? p_Vid->ipredmode[left_block.pos_y][left_block.pos_x] : -1; + + mostProbableMode = (upMode < 0 || leftMode < 0) ? DC_PRED : upMode < leftMode ? upMode : leftMode; + *min_cost = DISTBLK_MAX; + currMB->ipmode_DPCM = NO_INTRA_PMODE; //For residual DPCM + + //===== INTRA PREDICTION FOR 8x8 BLOCK ===== + currSlice->set_intrapred_8x8(currMB, PLANE_Y, pic_pix_x, pic_pix_y, &left_available, &up_available, &all_available); + + if(currSlice->P444_joined) + { + select_plane(p_Vid, PLANE_U); + currSlice->set_intrapred_8x8(currMB, PLANE_U, pic_pix_x, pic_pix_y, &left_available, &up_available, &all_available); + select_plane(p_Vid, PLANE_V); + currSlice->set_intrapred_8x8(currMB, PLANE_V, pic_pix_x, pic_pix_y, &left_available, &up_available, &all_available); + select_plane(p_Vid, PLANE_Y); + } + + //===== LOOP OVER ALL 8x8 INTRA PREDICTION MODES ===== + for (ipmode = 0; ipmode < NO_INTRA_PMODE; ipmode++) + { + if( (ipmode==DC_PRED) || + ((ipmode==VERT_PRED||ipmode==VERT_LEFT_PRED||ipmode==DIAG_DOWN_LEFT_PRED) && up_available ) || + ((ipmode==HOR_PRED||ipmode==HOR_UP_PRED) && left_available ) || + (all_available) ) + { + get_intrapred_8x8(currMB, PLANE_Y, ipmode, left_available, up_available); + + // get prediction and prediction error + generate_pred_error_8x8(&p_Vid->pImgOrg[0][pic_opix_y], currSlice->mpr_8x8[0][ipmode], &currSlice->mb_pred[0][block_y], &currSlice->mb_ores[0][block_y], pic_opix_x, block_x); + + if(currSlice->P444_joined) + { + get_intrapred_8x8(currMB, PLANE_U, ipmode, left_available, up_available); + generate_pred_error_8x8(&p_Vid->pImgOrg[1][pic_opix_y], currSlice->mpr_8x8[1][ipmode], &currSlice->mb_pred[1][block_y], &currSlice->mb_ores[1][block_y], pic_opix_x, block_x); + get_intrapred_8x8(currMB, PLANE_V, ipmode, left_available, up_available); + generate_pred_error_8x8(&p_Vid->pImgOrg[2][pic_opix_y], currSlice->mpr_8x8[2][ipmode], &currSlice->mb_pred[2][block_y], &currSlice->mb_ores[2][block_y], pic_opix_x, block_x); + } + + currMB->ipmode_DPCM = (short) ipmode; + + // get and check rate-distortion cost + + rdcost = currSlice->rdcost_for_8x8_intra_blocks (currMB, &c_nz, b8, ipmode, lambda, min_rdcost, mostProbableMode); + if ((rdcost < min_rdcost) || (rdcost == min_rdcost && ipmode == mostProbableMode)) + { + //--- set coefficients --- + memcpy(p_RDO->coefAC8x8intra[b8][0][0][0],currSlice->cofAC[b8][0][0], 4 * 2 * 65 * sizeof(int)); + + //--- set reconstruction --- + copy_image_data_8x8(p_RDO->rec8x8[PLANE_Y], &p_Vid->enc_picture->imgY[pic_pix_y], 0, pic_pix_x); + + if (p_Vid->AdaptiveRounding) + { + for (j = block_y; j < block_y + 8; j++) + memcpy(&p_Vid->ARCofAdj8x8[0][DUMMY][j][block_x],&p_Vid->ARCofAdj8x8[0][I8MB][j][block_x], 8 * sizeof(int)); + + if (currSlice->P444_joined) + { + for (j = block_y; j < block_y + 8; j++) + { + memcpy(&p_Vid->ARCofAdj8x8[1][DUMMY][j][block_x],&p_Vid->ARCofAdj8x8[1][I8MB][j][block_x], 8 * sizeof(int)); + memcpy(&p_Vid->ARCofAdj8x8[2][DUMMY][j][block_x],&p_Vid->ARCofAdj8x8[2][I8MB][j][block_x], 8 * sizeof(int)); + } + } + } + + if (currSlice->P444_joined) + { + //--- set coefficients --- + for (uv=0; uv < 2; uv++) + { + memcpy(p_RDO->coefAC8x8intra[b8][uv + 1][0][0],currSlice->cofAC[4+b8+4*uv][0][0], 2 * 4 * 65 * sizeof(int)); + + currMB->cr_cbp[uv + 1] = currMB->c_nzCbCr[uv + 1]; + //--- set reconstruction --- + copy_image_data_8x8(p_RDO->rec8x8[uv + 1], &p_Vid->enc_picture->imgUV[uv][pic_pix_y], 0, pic_pix_x); + } + } + + //--- flag if transform coefficients must be coded --- + nonzero = c_nz; + + //--- set best mode update minimum cost --- + *min_cost = rdcost; + min_rdcost = rdcost; + best_ipmode = ipmode; + } + } + } + + //===== set intra mode prediction ===== + p_Vid->ipredmode8x8[pic_block_y][pic_block_x] = (char) best_ipmode; + currMB->ipmode_DPCM = (short) best_ipmode; //For residual DPCM + + if(currSlice->P444_joined) + { + ColorPlane k; + p_Vid->CbCr_predmode_8x8[b8] = best_ipmode; + for (k = PLANE_U; k <= PLANE_V; k++) + { + currMB->cr_cbp[k] = 0; + select_plane(p_Vid, k); + /* + for (j=0; j<8; j++) + { + for (i=0; i<8; i++) + { + currSlice->mb_pred[k][block_y+j][block_x+i] = currSlice->mpr_8x8[k][best_ipmode][j][i]; + currSlice->mb_ores[k][block_y+j][block_x+i] = p_Vid->pImgOrg[k][currMB->pix_y + block_y + j][currMB->pix_x + block_x + i] - currSlice->mpr_8x8[k][best_ipmode][j][i]; + } + } + */ + generate_pred_error_8x8(&p_Vid->pImgOrg[k][currMB->pix_y + block_y], currSlice->mpr_8x8[k][best_ipmode], &currSlice->mb_pred[k][block_y], &currSlice->mb_ores[k][block_y], currMB->pix_x + block_x, block_x); + currMB->ipmode_DPCM = (short) best_ipmode; + + if (currMB->residual_transform_quant_luma_8x8(currMB, k, b8, &dummy, 1)) + currMB->cr_cbp[k] = 1; + } + select_plane(p_Vid, PLANE_Y); + } + + currMB->intra_pred_modes8x8[4*b8] = (char) ((mostProbableMode == best_ipmode) + ? -1 + : (best_ipmode < mostProbableMode ? best_ipmode : best_ipmode-1)); + + memset(&p_Vid->ipredmode8x8[mb_block_y ][mb_block_x], best_ipmode, 2 * sizeof(char)); + memset(&p_Vid->ipredmode8x8[mb_block_y + 1][mb_block_x], best_ipmode, 2 * sizeof(char)); + + //===== restore coefficients ===== + memcpy(currSlice->cofAC[b8][0][0], p_RDO->coefAC8x8intra[b8][0][0][0], 4 * 2 * 65 * sizeof(int)); + + if (p_Vid->AdaptiveRounding) + { + for (j=block_y; j< block_y + 8; j++) + memcpy(&p_Vid->ARCofAdj8x8[0][I8MB][j][block_x], &p_Vid->ARCofAdj8x8[0][DUMMY][j][block_x], 8 * sizeof(int)); + + if (currSlice->P444_joined) + { + for (j=0; j<8; j++) + { + memcpy(&p_Vid->ARCofAdj8x8[1][I8MB][block_y + j][block_x], &p_Vid->ARCofAdj8x8[1][DUMMY][block_y + j][block_x], 8 * sizeof(int)); + memcpy(&p_Vid->ARCofAdj8x8[2][I8MB][block_y + j][block_x], &p_Vid->ARCofAdj8x8[2][DUMMY][block_y + j][block_x], 8 * sizeof(int)); + } + } + } + + //===== restore reconstruction and prediction (needed if single coeffs are removed) ===== + copy_image_data_8x8(&p_Vid->enc_picture->imgY[pic_pix_y], p_RDO->rec8x8[0], pic_pix_x, 0); + copy_image_data_8x8(&currSlice->mb_pred[0][block_y], currSlice->mpr_8x8[0][best_ipmode], block_x, 0); + + if (currSlice->P444_joined) + { + //===== restore coefficients ===== + memcpy(currSlice->cofAC[4 + b8 + 4*0][0][0], p_RDO->coefAC8x8intra[b8][1][0][0], 4 * 2 * 65 * sizeof(int)); + memcpy(currSlice->cofAC[4 + b8 + 4*1][0][0], p_RDO->coefAC8x8intra[b8][2][0][0], 4 * 2 * 65 * sizeof(int)); + + //===== restore reconstruction and prediction (needed if single coeffs are removed) ===== + copy_image_data_8x8(&p_Vid->enc_picture->imgUV[0][pic_pix_y], p_RDO->rec8x8[1], pic_pix_x, 0); + copy_image_data_8x8(&p_Vid->enc_picture->imgUV[1][pic_pix_y], p_RDO->rec8x8[2], pic_pix_x, 0); + copy_image_data_8x8(&currSlice->mb_pred[1][block_y], currSlice->mpr_8x8[1][best_ipmode], block_x, 0); + copy_image_data_8x8(&currSlice->mb_pred[2][block_y], currSlice->mpr_8x8[2][best_ipmode], block_x, 0); + } + + return nonzero; +} + diff --git a/lencod/src/vlc.c b/lencod/src/vlc.c new file mode 100644 index 0000000..9fb3b2a --- /dev/null +++ b/lencod/src/vlc.c @@ -0,0 +1,1569 @@ + +/*! + *************************************************************************** + * \file vlc.c + * + * \brief + * (CA)VLC coding functions + * + * \author + * Main contributors (see contributors.h for copyright, address and affiliation details) + * - Inge Lille-Langoy + * - Detlev Marpe + * - Stephan Wenger + *************************************************************************** + */ + +#include "contributors.h" + +#include + +#include "global.h" +#include "enc_statistics.h" +#include "vlc.h" + +#if TRACE +#define SYMTRACESTRING(s) strncpy(sym.tracestring,s,TRACESTRING_SIZE) +#else +#define SYMTRACESTRING(s) // do nothing +#endif + +//! gives codeword number from CBP value, both for intra and inter +static const byte NCBP[2][48][2]= +{ + { // 0 1 2 3 4 5 6 7 8 9 10 11 + { 1, 0},{10, 1},{11, 2},{ 6, 5},{12, 3},{ 7, 6},{14,14},{ 2,10},{13, 4},{15,15},{ 8, 7},{ 3,11}, + { 9, 8},{ 4,12},{ 5,13},{ 0, 9},{ 0, 0},{ 0, 0},{ 0, 0},{ 0, 0},{ 0, 0},{ 0, 0},{ 0, 0},{ 0, 0}, + { 0, 0},{ 0, 0},{ 0, 0},{ 0, 0},{ 0, 0},{ 0, 0},{ 0, 0},{ 0, 0},{ 0, 0},{ 0, 0},{ 0, 0},{ 0, 0}, + { 0, 0},{ 0, 0},{ 0, 0},{ 0, 0},{ 0, 0},{ 0, 0},{ 0, 0},{ 0, 0},{ 0, 0},{ 0, 0},{ 0, 0},{ 0, 0} + }, + { + { 3, 0},{29, 2},{30, 3},{17, 7},{31, 4},{18, 8},{37,17},{ 8,13},{32, 5},{38,18},{19, 9},{ 9,14}, + {20,10},{10,15},{11,16},{ 2,11},{16, 1},{33,32},{34,33},{21,36},{35,34},{22,37},{39,44},{ 4,40}, + {36,35},{40,45},{23,38},{ 5,41},{24,39},{ 6,42},{ 7,43},{ 1,19},{41, 6},{42,24},{43,25},{25,20}, + {44,26},{26,21},{46,46},{12,28},{45,27},{47,47},{27,22},{13,29},{28,23},{14,30},{15,31},{ 0,12} + } +}; + + +/*! + ************************************************************************************* + * \brief + * write_ue_v, writes an ue(v) syntax element, returns the length in bits + * + * \param tracestring + * the string for the trace file + * \param value + * the value to be coded + * \param bitstream + * the target bitstream the value should be coded into + * + * \return + * Number of bits used by the coded syntax element + * + * \ note + * This function writes always the bit buffer for the progressive scan flag, and + * should not be used (or should be modified appropriately) for the interlace crap + * When used in the context of the Parameter Sets, this is obviously not a + * problem. + * + ************************************************************************************* + */ +int write_ue_v (char *tracestring, int value, Bitstream *bitstream) +{ + SyntaxElement symbol, *sym=&symbol; + sym->value1 = value; + sym->value2 = 0; + + //assert (bitstream->streamBuffer != NULL); + + ue_linfo(sym->value1,sym->value2,&(sym->len),&(sym->inf)); + symbol2uvlc(sym); + + writeUVLC2buffer (sym, bitstream); + +#if TRACE + strncpy(sym->tracestring,tracestring,TRACESTRING_SIZE); + trace2out (sym); +#endif + + return (sym->len); +} + + +/*! + ************************************************************************************* + * \brief + * write_se_v, writes an se(v) syntax element, returns the length in bits + * + * \param tracestring + * the string for the trace file + * \param value + * the value to be coded + * \param bitstream + * the target bitstream the value should be coded into + * + * \return + * Number of bits used by the coded syntax element + * + * \ note + * This function writes always the bit buffer for the progressive scan flag, and + * should not be used (or should be modified appropriately) for the interlace crap + * When used in the context of the Parameter Sets, this is obviously not a + * problem. + * + ************************************************************************************* + */ +int write_se_v (char *tracestring, int value, Bitstream *bitstream) +{ + SyntaxElement symbol, *sym=&symbol; + sym->value1 = value; + sym->value2 = 0; + + //assert (bitstream->streamBuffer != NULL); + + se_linfo(sym->value1,sym->value2,&(sym->len),&(sym->inf)); + symbol2uvlc(sym); + + writeUVLC2buffer (sym, bitstream); + +#if TRACE + strncpy(sym->tracestring,tracestring,TRACESTRING_SIZE); + trace2out (sym); +#endif + + return (sym->len); +} + + +/*! + ************************************************************************************* + * \brief + * write_u_1, writes a flag (u(1) syntax element, returns the length in bits, + * always 1 + * + * \param tracestring + * the string for the trace file + * \param value + * the value to be coded + * \param bitstream + * the target bitstream the value should be coded into + * + * \return + * Number of bits used by the coded syntax element (always 1) + * + * \ note + * This function writes always the bit buffer for the progressive scan flag, and + * should not be used (or should be modified appropriately) for the interlace crap + * When used in the context of the Parameter Sets, this is obviously not a + * problem. + * + ************************************************************************************* + */ +Boolean write_u_1 (char *tracestring, int value, Bitstream *bitstream) +{ + SyntaxElement symbol, *sym=&symbol; + + sym->bitpattern = value; + sym->value1 = value; + sym->len = 1; + + //assert (bitstream->streamBuffer != NULL); + + writeUVLC2buffer(sym, bitstream); + +#if TRACE + strncpy(sym->tracestring,tracestring,TRACESTRING_SIZE); + trace2out (sym); +#endif + + return ((Boolean) sym->len); +} + + +/*! + ************************************************************************************* + * \brief + * write_u_v, writes a n bit fixed length syntax element, returns the length in bits, + * + * \param n + * length in bits + * \param tracestring + * the string for the trace file + * \param value + * the value to be coded + * \param bitstream + * the target bitstream the value should be coded into + * + * \return + * Number of bits used by the coded syntax element + * + * \ note + * This function writes always the bit buffer for the progressive scan flag, and + * should not be used (or should be modified appropriately) for the interlace crap + * When used in the context of the Parameter Sets, this is obviously not a + * problem. + * + ************************************************************************************* + */ + +int write_u_v (int n, char *tracestring, int value, Bitstream *bitstream) +{ + SyntaxElement symbol, *sym=&symbol; + + sym->bitpattern = value; + sym->value1 = value; + sym->len = n; + + //assert (bitstream->streamBuffer != NULL); + + writeUVLC2buffer(sym, bitstream); + +#if TRACE + strncpy(sym->tracestring,tracestring,TRACESTRING_SIZE); + trace2out (sym); +#endif + + return (sym->len); +} + + +/*! + ************************************************************************ + * \brief + * mapping for ue(v) syntax elements + * \param ue + * value to be mapped + * \param dummy + * dummy parameter + * \param info + * returns mapped value + * \param len + * returns mapped value length + ************************************************************************ + */ +void ue_linfo(int ue, int dummy, int *len,int *info) +{ + int i, nn =(ue + 1) >> 1; + + for (i=0; i < 33 && nn != 0; i++) + { + nn >>= 1; + } + *len = (i << 1) + 1; + *info = ue + 1 - (1 << i); +} + + +/*! + ************************************************************************ + * \brief + * mapping for se(v) syntax elements + * \param se + * value to be mapped + * \param dummy + * dummy parameter + * \param len + * returns mapped value length + * \param info + * returns mapped value + ************************************************************************ + */ +void se_linfo(int se, int dummy, int *len,int *info) +{ + int sign = (se <= 0) ? 1 : 0; + int n = iabs(se) << 1; // n+1 is the number in the code table. Based on this we find length and info + int nn = (n >> 1); + int i; + for (i = 0; i < 33 && nn != 0; i++) + { + nn >>= 1; + } + *len = (i << 1) + 1; + *info = n - (1 << i) + sign; +} + + +/*! + ************************************************************************ + * \par Input: + * Number in the code table + * \par Output: + * length and info + ************************************************************************ + */ +void cbp_linfo_intra_other(int cbp, int dummy, int *len,int *info) +{ + ue_linfo(NCBP[0][cbp][0], dummy, len, info); +} + + +/*! + ************************************************************************ + * \par Input: + * Number in the code table + * \par Output: + * length and info + ************************************************************************ + */ +void cbp_linfo_intra_normal(int cbp, int dummy, int *len,int *info) +{ + ue_linfo(NCBP[1][cbp][0], dummy, len, info); +} + +/*! + ************************************************************************ + * \par Input: + * Number in the code table + * \par Output: + * length and info + ************************************************************************ + */ +void cbp_linfo_inter_other(int cbp, int dummy, int *len,int *info) +{ + ue_linfo(NCBP[0][cbp][1], dummy, len, info); +} + +/*! + ************************************************************************ + * \par Input: + * Number in the code table + * \par Output: + * length and info + ************************************************************************ + */ +void cbp_linfo_inter_normal(int cbp, int dummy, int *len,int *info) +{ + ue_linfo(NCBP[1][cbp][1], dummy, len, info); +} + +/*! + ************************************************************************ + * \brief + * 2x2 transform of chroma DC + * \par Input: + * level and run for coefficients + * \par Output: + * length and info + * \note + * see ITU document for bit assignment + ************************************************************************ + */ +void levrun_linfo_c2x2(int level,int run,int *len,int *info) +{ + static const byte NTAB[2][2]= + { + {1,5}, + {3,0} + }; + static const byte LEVRUN[4]= + { + 2,1,0,0 + }; + + if (level == 0) // check if the coefficient sign EOB (level=0) + { + *len=1; + return; + } + else + { + int levabs = iabs(level); + int sign = level <= 0 ? 1 : 0; + int n = (levabs <= LEVRUN[run]) ? NTAB[levabs - 1][run] + 1 : (levabs - LEVRUN[run]) * 8 + run * 2; + int nn = n >> 1; + int i; + + for (i=0; i < 16 && nn != 0; i++) + { + nn >>= 1; + } + *len = (i << 1) + 1; + *info = n - (1 << i) + sign; + } +} + + +/*! + ************************************************************************ + * \brief + * Single scan coefficients + * \par Input: + * level and run for coefficients + * \par Output: + * length and info + * \note + * see ITU document for bit assignment + ************************************************************************ + */ +void levrun_linfo_inter(int level,int run,int *len,int *info) +{ + static const byte LEVRUN[16]= + { + 4,2,2,1,1,1,1,1,1,1,0,0,0,0,0,0 + }; + static const byte NTAB[4][10]= + { + { 1, 3, 5, 9,11,13,21,23,25,27}, + { 7,17,19, 0, 0, 0, 0, 0, 0, 0}, + {15, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {29, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + }; + + if (level == 0) // check for EOB + { + *len=1; + return; + } + else + { + + int sign = (level <= 0) ? 1 : 0; + int levabs = iabs(level); + + int n = (levabs <= LEVRUN[run]) ? NTAB[levabs - 1][run] + 1 : (levabs - LEVRUN[run]) * 32 + run * 2; + + int nn = n >> 1; + int i; + + for (i=0; i < 16 && nn != 0; i++) + { + nn >>= 1; + } + *len = (i << 1) + 1; + *info = n - (1 << i) + sign; + } +} + + +/*! + ************************************************************************ + * \brief + * Makes code word and passes it back + * A code word has the following format: 0 0 0 ... 1 Xn ...X2 X1 X0. + * + * \par Input: + * Info : Xn..X2 X1 X0 \n + * Length : Total number of bits in the codeword + ************************************************************************ + */ + // NOTE this function is called with sym->inf > (1<<(sym->len >> 1)). The upper bits of inf are junk +int symbol2uvlc(SyntaxElement *sym) +{ + int suffix_len = sym->len >> 1; + //assert (suffix_len < 32); + suffix_len = (1 << suffix_len); + sym->bitpattern = suffix_len | (sym->inf & (suffix_len - 1)); + return 0; +} + +/*! +************************************************************************ +* \brief +* generates UVLC code and passes the codeword to the buffer +************************************************************************ +*/ +void writeSE_UVLC(SyntaxElement *se, DataPartition *dp) +{ + ue_linfo (se->value1,se->value2,&(se->len),&(se->inf)); + symbol2uvlc(se); + + writeUVLC2buffer(se, dp->bitstream); + + if(se->type != SE_HEADER) + dp->bitstream->write_flag = 1; + +#if TRACE + if(dp->bitstream->trace_enabled) + trace2out (se); +#endif +} + +/*! +************************************************************************ +* \brief +* generates UVLC code and passes the codeword to the buffer +************************************************************************ +*/ +void writeSE_SVLC(SyntaxElement *se, DataPartition *dp) +{ + se_linfo (se->value1,se->value2,&(se->len),&(se->inf)); + symbol2uvlc(se); + + writeUVLC2buffer(se, dp->bitstream); + + if(se->type != SE_HEADER) + dp->bitstream->write_flag = 1; + +#if TRACE + if(dp->bitstream->trace_enabled) + trace2out (se); +#endif +} + +/*! +************************************************************************ +* \brief +* generates UVLC code and passes the codeword to the buffer +************************************************************************ +*/ +void writeCBP_VLC (Macroblock* currMB, SyntaxElement *se, DataPartition *dp) +{ + if (currMB->mb_type == I4MB || currMB->mb_type == SI4MB || currMB->mb_type == I8MB) + { + currMB->cbp_linfo_intra (se->value1, se->value2, &(se->len), &(se->inf)); + } + else + { + currMB->cbp_linfo_inter (se->value1,se->value2,&(se->len),&(se->inf)); + } + symbol2uvlc(se); + + writeUVLC2buffer(se, dp->bitstream); + + if(se->type != SE_HEADER) + dp->bitstream->write_flag = 1; + +#if TRACE + if(dp->bitstream->trace_enabled) + trace2out (se); +#endif +} + + +/*! + ************************************************************************ + * \brief + * generates code and passes the codeword to the buffer + ************************************************************************ + */ +void writeIntraPredMode_CAVLC(SyntaxElement *se, DataPartition *dp) +{ + + if (se->value1 == -1) + { + se->len = 1; + se->inf = 1; + } + else + { + se->len = 4; + se->inf = se->value1; + } + + se->bitpattern = se->inf; + writeUVLC2buffer(se, dp->bitstream); + + if(se->type != SE_HEADER) + dp->bitstream->write_flag = 1; + +#if TRACE + if(dp->bitstream->trace_enabled) + trace2out (se); +#endif + + return; +} + + +/*! + ************************************************************************ + * \brief + * generates UVLC code and passes the codeword to the buffer + * \author + * Tian Dong + ************************************************************************ + */ +int writeSyntaxElement2Buf_UVLC(SyntaxElement *se, Bitstream* this_streamBuffer ) +{ + + se->mapping(se->value1,se->value2,&(se->len),&(se->inf)); + + symbol2uvlc(se); + + writeUVLC2buffer(se, this_streamBuffer ); + +#if TRACE + if(se->type <= 1) + trace2out (se); +#endif + + return (se->len); +} + + +/*! + ************************************************************************ + * \brief + * writes UVLC code to the appropriate buffer + ************************************************************************ + */ +void writeUVLC2buffer(SyntaxElement *se, Bitstream *currStream) +{ + unsigned int mask = 1 << (se->len - 1); + byte *byte_buf = &currStream->byte_buf; + int *bits_to_go = &currStream->bits_to_go; + int i; + + // Add the new bits to the bitstream. + // Write out a byte if it is full + if ( se->len < 33 ) + { + for (i = 0; i < se->len; i++) + { + *byte_buf <<= 1; + + if (se->bitpattern & mask) + *byte_buf |= 1; + + mask >>= 1; + + if ((--(*bits_to_go)) == 0) + { + *bits_to_go = 8; + currStream->streamBuffer[currStream->byte_pos++] = *byte_buf; + *byte_buf = 0; + } + } + } + else + { + // zeros + for (i = 0; i < (se->len - 32); i++) + { + *byte_buf <<= 1; + + if ((--(*bits_to_go)) == 0) + { + *bits_to_go = 8; + currStream->streamBuffer[currStream->byte_pos++] = *byte_buf; + *byte_buf = 0; + } + } + // actual info + mask = (unsigned int) 1 << 31; + for (i = 0; i < 32; i++) + { + *byte_buf <<= 1; + + if (se->bitpattern & mask) + *byte_buf |= 1; + + mask >>= 1; + + if ((--(*bits_to_go)) == 0) + { + *bits_to_go = 8; + currStream->streamBuffer[currStream->byte_pos++] = *byte_buf; + *byte_buf = 0; + } + } + } +} + + +/*! + ************************************************************************ + * \brief + * generates UVLC code and passes the codeword to the buffer + * \author + * Tian Dong + ************************************************************************ + */ +int writeSyntaxElement2Buf_Fixed(SyntaxElement *se, Bitstream* this_streamBuffer ) +{ + writeUVLC2buffer(se, this_streamBuffer ); + +#if TRACE + if(se->type <= 1) + trace2out (se); +#endif + return (se->len); +} + +/*! +************************************************************************ +* \brief +* generates UVLC code and passes the codeword to the buffer +* \author +* Tian Dong +************************************************************************ +*/ +void writeSE_Flag(SyntaxElement *se, DataPartition *dp ) +{ + se->len = 1; + se->bitpattern = (se->value1 & 1); + + writeUVLC2buffer(se, dp->bitstream ); + +#if TRACE + if(dp->bitstream->trace_enabled) + trace2out (se); +#endif +} + +/*! +************************************************************************ +* \brief +* generates UVLC code and passes the codeword to the buffer +* \author +* Tian Dong +************************************************************************ +*/ +void writeSE_invFlag(SyntaxElement *se, DataPartition *dp ) +{ + se->len = 1; + se->bitpattern = 1 - (se->value1 & 1); + + writeUVLC2buffer(se, dp->bitstream ); + +#if TRACE + if(dp->bitstream->trace_enabled) + trace2out (se); +#endif +} + +/*! +************************************************************************ +* \brief +* generates UVLC code and passes the codeword to the buffer +* \author +* Tian Dong +************************************************************************ +*/ +void writeSE_Dummy(SyntaxElement *se, DataPartition *dp ) +{ + se->len = 0; +} + + +/*! +************************************************************************ +* \brief +* generates UVLC code and passes the codeword to the buffer +* \author +* Tian Dong +************************************************************************ +*/ +void writeSE_Fix(SyntaxElement *se, Bitstream *bitstream ) +{ + writeUVLC2buffer(se, bitstream); + +#if TRACE + if(bitstream->trace_enabled) + trace2out (se); +#endif +} + + +/*! + ************************************************************************ + * \brief + * Makes code word and passes it back + * + * \par Input: + * Info : Xn..X2 X1 X0 \n + * Length : Total number of bits in the codeword + ************************************************************************ + */ + +int symbol2vlc(SyntaxElement *sym) +{ + int info_len = sym->len; + + // Convert info into a bitpattern int + sym->bitpattern = 0; + + // vlc coding + while(--info_len >= 0) + { + sym->bitpattern <<= 1; + sym->bitpattern |= (0x01 & (sym->inf >> info_len)); + } + return 0; +} + + +/*! + ************************************************************************ + * \brief + * generates VLC code and passes the codeword to the buffer + ************************************************************************ + */ +int writeSyntaxElement_VLC(SyntaxElement *se, DataPartition *dp) +{ + se->inf = se->value1; + se->len = se->value2; + symbol2vlc(se); + + writeUVLC2buffer(se, dp->bitstream); + + if(se->type != SE_HEADER) + dp->bitstream->write_flag = 1; + +#if TRACE + if(dp->bitstream->trace_enabled) + trace2out (se); +#endif + + return (se->len); +} + + +/*! + ************************************************************************ + * \brief + * write VLC for NumCoeff and TrailingOnes + ************************************************************************ + */ + +int writeSyntaxElement_NumCoeffTrailingOnes(SyntaxElement *se, DataPartition *dp) +{ + static const byte lentab[3][4][17] = + { + { // 0702 + { 1, 6, 8, 9,10,11,13,13,13,14,14,15,15,16,16,16,16}, + { 0, 2, 6, 8, 9,10,11,13,13,14,14,15,15,15,16,16,16}, + { 0, 0, 3, 7, 8, 9,10,11,13,13,14,14,15,15,16,16,16}, + { 0, 0, 0, 5, 6, 7, 8, 9,10,11,13,14,14,15,15,16,16}, + }, + { + { 2, 6, 6, 7, 8, 8, 9,11,11,12,12,12,13,13,13,14,14}, + { 0, 2, 5, 6, 6, 7, 8, 9,11,11,12,12,13,13,14,14,14}, + { 0, 0, 3, 6, 6, 7, 8, 9,11,11,12,12,13,13,13,14,14}, + { 0, 0, 0, 4, 4, 5, 6, 6, 7, 9,11,11,12,13,13,13,14}, + }, + { + { 4, 6, 6, 6, 7, 7, 7, 7, 8, 8, 9, 9, 9,10,10,10,10}, + { 0, 4, 5, 5, 5, 5, 6, 6, 7, 8, 8, 9, 9, 9,10,10,10}, + { 0, 0, 4, 5, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9,10,10,10}, + { 0, 0, 0, 4, 4, 4, 4, 4, 5, 6, 7, 8, 8, 9,10,10,10}, + }, + + }; + + static const byte codtab[3][4][17] = + { + { + { 1, 5, 7, 7, 7, 7,15,11, 8,15,11,15,11,15,11, 7,4}, + { 0, 1, 4, 6, 6, 6, 6,14,10,14,10,14,10, 1,14,10,6}, + { 0, 0, 1, 5, 5, 5, 5, 5,13, 9,13, 9,13, 9,13, 9,5}, + { 0, 0, 0, 3, 3, 4, 4, 4, 4, 4,12,12, 8,12, 8,12,8}, + }, + { + { 3,11, 7, 7, 7, 4, 7,15,11,15,11, 8,15,11, 7, 9,7}, + { 0, 2, 7,10, 6, 6, 6, 6,14,10,14,10,14,10,11, 8,6}, + { 0, 0, 3, 9, 5, 5, 5, 5,13, 9,13, 9,13, 9, 6,10,5}, + { 0, 0, 0, 5, 4, 6, 8, 4, 4, 4,12, 8,12,12, 8, 1,4}, + }, + { + {15,15,11, 8,15,11, 9, 8,15,11,15,11, 8,13, 9, 5,1}, + { 0,14,15,12,10, 8,14,10,14,14,10,14,10, 7,12, 8,4}, + { 0, 0,13,14,11, 9,13, 9,13,10,13, 9,13, 9,11, 7,3}, + { 0, 0, 0,12,11,10, 9, 8,13,12,12,12, 8,12,10, 6,2}, + }, + }; + int vlcnum = se->len; + + // se->value1 : numcoeff + // se->value2 : numtrailingones + + if (vlcnum == 3) + { + se->len = 6; // 4 + 2 bit FLC + if (se->value1 > 0) + { + se->inf = ((se->value1-1) << 2) | se->value2; + } + else + { + se->inf = 3; + } + } + else + { + se->len = lentab[vlcnum][se->value2][se->value1]; + se->inf = codtab[vlcnum][se->value2][se->value1]; + } + + if (se->len == 0) + { + printf("ERROR: (numcoeff,trailingones) not valid: vlc=%d (%d, %d)\n", + vlcnum, se->value1, se->value2); + exit(-1); + } + + symbol2vlc(se); + + writeUVLC2buffer(se, dp->bitstream); + + if(se->type != SE_HEADER) + dp->bitstream->write_flag = 1; + +#if TRACE + if(dp->bitstream->trace_enabled) + trace2out (se); +#endif + + return (se->len); +} + + +/*! + ************************************************************************ + * \brief + * write VLC for NumCoeff and TrailingOnes for Chroma DC + ************************************************************************ + */ +int writeSyntaxElement_NumCoeffTrailingOnesChromaDC(VideoParameters *p_Vid, SyntaxElement *se, DataPartition *dp) +{ + static const byte lentab[3][4][17] = + { + //YUV420 + {{ 2, 6, 6, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + { 0, 1, 6, 7, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + { 0, 0, 3, 7, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + { 0, 0, 0, 6, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, + //YUV422 + {{ 1, 7, 7, 9, 9,10,11,12,13, 0, 0, 0, 0, 0, 0, 0, 0}, + { 0, 2, 7, 7, 9,10,11,12,12, 0, 0, 0, 0, 0, 0, 0, 0}, + { 0, 0, 3, 7, 7, 9,10,11,12, 0, 0, 0, 0, 0, 0, 0, 0}, + { 0, 0, 0, 5, 6, 7, 7,10,11, 0, 0, 0, 0, 0, 0, 0, 0}}, + //YUV444 + {{ 1, 6, 8, 9,10,11,13,13,13,14,14,15,15,16,16,16,16}, + { 0, 2, 6, 8, 9,10,11,13,13,14,14,15,15,15,16,16,16}, + { 0, 0, 3, 7, 8, 9,10,11,13,13,14,14,15,15,16,16,16}, + { 0, 0, 0, 5, 6, 7, 8, 9,10,11,13,14,14,15,15,16,16}} + }; + + static const byte codtab[3][4][17] = + { + //YUV420 + {{ 1, 7, 4, 3, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + { 0, 1, 6, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + { 0, 0, 1, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + { 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, + //YUV422 + {{ 1,15,14, 7, 6, 7, 7, 7, 7, 0, 0, 0, 0, 0, 0, 0, 0}, + { 0, 1,13,12, 5, 6, 6, 6, 5, 0, 0, 0, 0, 0, 0, 0, 0}, + { 0, 0, 1,11,10, 4, 5, 5, 4, 0, 0, 0, 0, 0, 0, 0, 0}, + { 0, 0, 0, 1, 1, 9, 8, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0}}, + //YUV444 + {{ 1, 5, 7, 7, 7, 7,15,11, 8,15,11,15,11,15,11, 7, 4}, + { 0, 1, 4, 6, 6, 6, 6,14,10,14,10,14,10, 1,14,10, 6}, + { 0, 0, 1, 5, 5, 5, 5, 5,13, 9,13, 9,13, 9,13, 9, 5}, + { 0, 0, 0, 3, 3, 4, 4, 4, 4, 4,12,12, 8,12, 8,12, 8}} + + }; + int yuv = p_Vid->yuv_format - 1; + + // se->value1 : numcoeff + // se->value2 : numtrailingones + se->len = lentab[yuv][se->value2][se->value1]; + se->inf = codtab[yuv][se->value2][se->value1]; + + if (se->len == 0) + { + printf("ERROR: (numcoeff,trailingones) not valid: (%d, %d)\n", + se->value1, se->value2); + exit(-1); + } + + symbol2vlc(se); + + writeUVLC2buffer(se, dp->bitstream); + + if(se->type != SE_HEADER) + dp->bitstream->write_flag = 1; + +#if TRACE + if(dp->bitstream->trace_enabled) + trace2out (se); +#endif + + return (se->len); +} + + +/*! + ************************************************************************ + * \brief + * write VLC for TotalZeros + ************************************************************************ + */ +int writeSyntaxElement_TotalZeros(SyntaxElement *se, DataPartition *dp) +{ + static const byte lentab[TOTRUN_NUM][16] = + { + { 1,3,3,4,4,5,5,6,6,7,7,8,8,9,9,9}, + { 3,3,3,3,3,4,4,4,4,5,5,6,6,6,6}, + { 4,3,3,3,4,4,3,3,4,5,5,6,5,6}, + { 5,3,4,4,3,3,3,4,3,4,5,5,5}, + { 4,4,4,3,3,3,3,3,4,5,4,5}, + { 6,5,3,3,3,3,3,3,4,3,6}, + { 6,5,3,3,3,2,3,4,3,6}, + { 6,4,5,3,2,2,3,3,6}, + { 6,6,4,2,2,3,2,5}, + { 5,5,3,2,2,2,4}, + { 4,4,3,3,1,3}, + { 4,4,2,1,3}, + { 3,3,1,2}, + { 2,2,1}, + { 1,1}, + }; + + static const byte codtab[TOTRUN_NUM][16] = + { + {1,3,2,3,2,3,2,3,2,3,2,3,2,3,2,1}, + {7,6,5,4,3,5,4,3,2,3,2,3,2,1,0}, + {5,7,6,5,4,3,4,3,2,3,2,1,1,0}, + {3,7,5,4,6,5,4,3,3,2,2,1,0}, + {5,4,3,7,6,5,4,3,2,1,1,0}, + {1,1,7,6,5,4,3,2,1,1,0}, + {1,1,5,4,3,3,2,1,1,0}, + {1,1,1,3,3,2,2,1,0}, + {1,0,1,3,2,1,1,1,}, + {1,0,1,3,2,1,1,}, + {0,1,1,2,1,3}, + {0,1,1,1,1}, + {0,1,1,1}, + {0,1,1}, + {0,1}, + }; + int vlcnum = se->len; + + // se->value1 : TotalZeros + se->len = lentab[vlcnum][se->value1]; + se->inf = codtab[vlcnum][se->value1]; + + if (se->len == 0) + { + printf("ERROR: (TotalZeros) not valid: (%d)\n",se->value1); + exit(-1); + } + + symbol2vlc(se); + + writeUVLC2buffer(se, dp->bitstream); + + if(se->type != SE_HEADER) + dp->bitstream->write_flag = 1; + +#if TRACE + if(dp->bitstream->trace_enabled) + trace2out (se); +#endif + + return (se->len); +} + + +/*! + ************************************************************************ + * \brief + * write VLC for TotalZeros for Chroma DC + ************************************************************************ + */ +int writeSyntaxElement_TotalZerosChromaDC(VideoParameters *p_Vid, SyntaxElement *se, DataPartition *dp) +{ + static const byte lentab[3][TOTRUN_NUM][16] = + { + //YUV420 + {{ 1,2,3,3}, + { 1,2,2}, + { 1,1}}, + //YUV422 + {{ 1,3,3,4,4,4,5,5}, + { 3,2,3,3,3,3,3}, + { 3,3,2,2,3,3}, + { 3,2,2,2,3}, + { 2,2,2,2}, + { 2,2,1}, + { 1,1}}, + //YUV444 + {{ 1,3,3,4,4,5,5,6,6,7,7,8,8,9,9,9}, + { 3,3,3,3,3,4,4,4,4,5,5,6,6,6,6}, + { 4,3,3,3,4,4,3,3,4,5,5,6,5,6}, + { 5,3,4,4,3,3,3,4,3,4,5,5,5}, + { 4,4,4,3,3,3,3,3,4,5,4,5}, + { 6,5,3,3,3,3,3,3,4,3,6}, + { 6,5,3,3,3,2,3,4,3,6}, + { 6,4,5,3,2,2,3,3,6}, + { 6,6,4,2,2,3,2,5}, + { 5,5,3,2,2,2,4}, + { 4,4,3,3,1,3}, + { 4,4,2,1,3}, + { 3,3,1,2}, + { 2,2,1}, + { 1,1}} + }; + + static const byte codtab[3][TOTRUN_NUM][16] = + { + //YUV420 + {{ 1,1,1,0}, + { 1,1,0}, + { 1,0}}, + //YUV422 + {{ 1,2,3,2,3,1,1,0}, + { 0,1,1,4,5,6,7}, + { 0,1,1,2,6,7}, + { 6,0,1,2,7}, + { 0,1,2,3}, + { 0,1,1}, + { 0,1}}, + //YUV444 + {{1,3,2,3,2,3,2,3,2,3,2,3,2,3,2,1}, + {7,6,5,4,3,5,4,3,2,3,2,3,2,1,0}, + {5,7,6,5,4,3,4,3,2,3,2,1,1,0}, + {3,7,5,4,6,5,4,3,3,2,2,1,0}, + {5,4,3,7,6,5,4,3,2,1,1,0}, + {1,1,7,6,5,4,3,2,1,1,0}, + {1,1,5,4,3,3,2,1,1,0}, + {1,1,1,3,3,2,2,1,0}, + {1,0,1,3,2,1,1,1,}, + {1,0,1,3,2,1,1,}, + {0,1,1,2,1,3}, + {0,1,1,1,1}, + {0,1,1,1}, + {0,1,1}, + {0,1}} + }; + int vlcnum = se->len; + int yuv = p_Vid->yuv_format - 1; + + // se->value1 : TotalZeros + se->len = lentab[yuv][vlcnum][se->value1]; + se->inf = codtab[yuv][vlcnum][se->value1]; + + if (se->len == 0) + { + printf("ERROR: (TotalZeros) not valid: (%d)\n",se->value1); + exit(-1); + } + + symbol2vlc(se); + + writeUVLC2buffer(se, dp->bitstream); + + if(se->type != SE_HEADER) + dp->bitstream->write_flag = 1; + +#if TRACE + if(dp->bitstream->trace_enabled) + trace2out (se); +#endif + + return (se->len); +} + + +/*! + ************************************************************************ + * \brief + * write VLC for Run Before Next Coefficient, VLC0 + ************************************************************************ + */ +int writeSyntaxElement_Run(SyntaxElement *se, DataPartition *dp) +{ + static const byte lentab[TOTRUN_NUM][16] = + { + {1,1}, + {1,2,2}, + {2,2,2,2}, + {2,2,2,3,3}, + {2,2,3,3,3,3}, + {2,3,3,3,3,3,3}, + {3,3,3,3,3,3,3,4,5,6,7,8,9,10,11}, + }; + + static const byte codtab[TOTRUN_NUM][16] = + { + {1,0}, + {1,1,0}, + {3,2,1,0}, + {3,2,1,1,0}, + {3,2,3,2,1,0}, + {3,0,1,3,2,5,4}, + {7,6,5,4,3,2,1,1,1,1,1,1,1,1,1}, + }; + int vlcnum = se->len; + + // se->value1 : run + se->len = lentab[vlcnum][se->value1]; + se->inf = codtab[vlcnum][se->value1]; + + if (se->len == 0) + { + printf("ERROR: (run) not valid: (%d)\n",se->value1); + exit(-1); + } + + symbol2vlc(se); + + writeUVLC2buffer(se, dp->bitstream); + + if(se->type != SE_HEADER) + dp->bitstream->write_flag = 1; + +#if TRACE + if(dp->bitstream->trace_enabled) + trace2out (se); +#endif + + return (se->len); +} + + +/*! + ************************************************************************ + * \brief + * write VLC for Coeff Level (VLC1) + ************************************************************************ + */ +int writeSyntaxElement_Level_VLC1(SyntaxElement *se, DataPartition *dp, int profile_idc) +{ + int level = se->value1; + int sign = (level < 0 ? 1 : 0); + int levabs = iabs(level); + + if (levabs < 8) + { + se->len = levabs * 2 + sign - 1; + se->inf = 1; + } + else if (levabs < 16) + { + // escape code1 + se->len = 19; + se->inf = 16 | ((levabs << 1) - 16) | sign; + } + else + { + int iMask = 4096, numPrefix = 0; + int levabsm16 = levabs + 2032; + + // escape code2 + if ((levabsm16) >= 4096) + { + numPrefix++; + while ((levabsm16) >= (4096 << numPrefix)) + { + numPrefix++; + } + } + + iMask <<= numPrefix; + se->inf = iMask | ((levabsm16 << 1) - iMask) | sign; + + /* Assert to make sure that the code fits in the VLC */ + /* make sure that we are in High Profile to represent level_prefix > 15 */ + if (numPrefix > 0 && !is_FREXT_profile( profile_idc )) + { + //error( "level_prefix must be <= 15 except in High Profile\n", 1000 ); + se->len = 0x0000FFFF; // This can be some other big number + return (se->len); + } + + se->len = 28 + (numPrefix << 1); + } + + symbol2vlc(se); + + writeUVLC2buffer(se, dp->bitstream); + + if(se->type != SE_HEADER) + dp->bitstream->write_flag = 1; + +#if TRACE + if(dp->bitstream->trace_enabled) + trace2out (se); +#endif + + return (se->len); +} + + +/*! + ************************************************************************ + * \brief + * write VLC for Coeff Level + ************************************************************************ + */ +int writeSyntaxElement_Level_VLCN(SyntaxElement *se, int vlc, DataPartition *dp, int profile_idc) +{ + int level = se->value1; + int sign = (level < 0 ? 1 : 0); + int levabs = iabs(level) - 1; + + int shift = vlc - 1; + int escape = (15 << shift); + + if (levabs < escape) + { + int sufmask = ~((0xffffffff) << shift); + int suffix = (levabs) & sufmask; + + se->len = ((levabs) >> shift) + 1 + vlc; + se->inf = (2 << shift) | (suffix << 1) | sign; + } + else + { + int iMask = 4096; + int levabsesc = levabs - escape + 2048; + int numPrefix = 0; + + if ((levabsesc) >= 4096) + { + numPrefix++; + while ((levabsesc) >= (4096 << numPrefix)) + { + numPrefix++; + } + } + + iMask <<= numPrefix; + se->inf = iMask | ((levabsesc << 1) - iMask) | sign; + + /* Assert to make sure that the code fits in the VLC */ + /* make sure that we are in High Profile to represent level_prefix > 15 */ + if (numPrefix > 0 && !is_FREXT_profile( profile_idc )) + { + //error( "level_prefix must be <= 15 except in High Profile\n", 1000 ); + se->len = 0x0000FFFF; // This can be some other big number + return (se->len); + } + se->len = 28 + (numPrefix << 1); + } + + symbol2vlc(se); + + writeUVLC2buffer(se, dp->bitstream); + + if(se->type != SE_HEADER) + dp->bitstream->write_flag = 1; + +#if TRACE + if(dp->bitstream->trace_enabled) + trace2out (se); +#endif + + return (se->len); +} + + +#if TRACE +int bitcounter = 0; + +/*! + ************************************************************************ + * \brief + * Write out a trace string on the trace file + ************************************************************************ + */ +void trace2out(SyntaxElement *sym) +{ + int i, chars; + + if (p_Enc->p_trace != NULL) + { + putc('@', p_Enc->p_trace); + chars = fprintf(p_Enc->p_trace, "%i", bitcounter); + while(chars++ < 6) + putc(' ',p_Enc->p_trace); + + chars += fprintf(p_Enc->p_trace, "%s", sym->tracestring); + while(chars++ < 55) + putc(' ',p_Enc->p_trace); + + // align bit pattern + if(sym->len<15) + { + for(i=0 ; i<15-sym->len ; i++) + fputc(' ', p_Enc->p_trace); + } + + // print bit pattern + bitcounter += sym->len; + for(i=1 ; i<=sym->len ; i++) + { + if((sym->bitpattern >> (sym->len-i)) & 0x1) + fputc('1', p_Enc->p_trace); + else + fputc('0', p_Enc->p_trace); + } + fprintf(p_Enc->p_trace, " (%3d) \n",sym->value1); + } + fflush (p_Enc->p_trace); +} + +void trace2out_cabac(SyntaxElement *sym) +{ + int chars; + + if (p_Enc->p_trace != NULL) + { + putc('@', p_Enc->p_trace); + chars = fprintf(p_Enc->p_trace, "%i", bitcounter); + while(chars++ < 6) + putc(' ',p_Enc->p_trace); + + chars += fprintf(p_Enc->p_trace, "%s", sym->tracestring); + while(chars++ < 70) + putc(' ',p_Enc->p_trace); + + fprintf(p_Enc->p_trace, " (%3d) \n",sym->value1); + } + fflush (p_Enc->p_trace); + bitcounter += sym->len; +} +#endif + + +/*! + ************************************************************************ + * \brief + * puts the less than 8 bits in the byte buffer of the Bitstream into + * the streamBuffer. + * + * \param p_Vid + * VideoParameters for current picture encoding + * \param currStream + * the Bitstream the alignment should be established + * \param cur_stats + * currently used statistics parameters + * + ************************************************************************ + */ +void writeVlcByteAlign(VideoParameters *p_Vid, Bitstream* currStream, StatParameters *cur_stats) +{ + if (currStream->bits_to_go < 8) + { // trailing bits to process + currStream->byte_buf = (byte) ((currStream->byte_buf << currStream->bits_to_go) | (0xff >> (8 - currStream->bits_to_go))); + currStream->streamBuffer[currStream->byte_pos++] = currStream->byte_buf; + cur_stats->bit_use_stuffing_bits[p_Vid->type] += currStream->bits_to_go; + currStream->bits_to_go = 8; + } +} + +/*! + ************************************************************************ + * \brief + * Resets the nz_coeff parameters for a macroblock + ************************************************************************/ +void reset_mb_nz_coeff(VideoParameters *p_Vid, int mb_number) +{ + memset(&p_Vid->nz_coeff [mb_number][0][0], 0, BLOCK_SIZE * (BLOCK_SIZE + p_Vid->num_blk8x8_uv) * sizeof(int)); +} + + +void writeUVLC_CAVLC(Macroblock *currMB, SyntaxElement *se, DataPartition *dp) +{ + writeSE_UVLC(se, dp); +} + +void writeSVLC_CAVLC(Macroblock *currMB, SyntaxElement *se, DataPartition *dp) +{ + writeSE_SVLC(se, dp); +} + +void writeFlag_CAVLC(Macroblock *currMB, SyntaxElement *se, DataPartition *dp) +{ + writeSE_Flag(se, dp); +} + +int bs_bitlength(Bitstream *bitstream) +{ + return (bitstream->byte_pos*8+(8-bitstream->bits_to_go)); +} + +/*! +************************************************************************ +* \brief +* Dummy function for reference picture bit writing +* \author +************************************************************************ +*/ +void writeRefPic_Dummy(Macroblock *currMB, SyntaxElement *se, DataPartition *dp ) +{ + se->len = 0; +} + +/*! +************************************************************************ +* \brief +* Function for writing the bits for the reference index when up to 2 refs are available +* \author +************************************************************************ +*/ +void writeRefPic_2Ref_CAVLC(Macroblock *currMB, SyntaxElement *se, DataPartition *dp ) +{ + writeSE_invFlag( se, dp ); +} + +/*! +************************************************************************ +* \brief +* Function for writing the bits for the reference index when N (N>2) refs are available +* \author +************************************************************************ +*/ +void writeRefPic_NRef_CAVLC(Macroblock *currMB, SyntaxElement *se, DataPartition *dp ) +{ + writeSE_UVLC( se, dp ); +} + +/*! + ************************************************************************ + * \brief + * Reads bits from the bitstream buffer + * + * \param buffer + * containing VLC-coded data bits + * \param totbitoffset + * bit offset from start of partition + * \param info + * returns value of the read bits + * \param bitcount + * total bytes in bitstream + * \param numbits + * number of bits to read + * + ************************************************************************ + */ +int GetBits (byte *buffer, + int totbitoffset, + int *info, + int bitcount, + int numbits) +{ + if ((totbitoffset + numbits ) > bitcount) + { + return 0; + } + else + { + int bitoffset = 7 - (totbitoffset & 0x07); // bit from start of byte + int byteoffset = (totbitoffset >> 3); // byte from start of buffer + int bitcounter = numbits; + byte *curbyte = &(buffer[byteoffset]); + int inf = 0; + + while (numbits--) + { + inf <<=1; + inf |= ((*curbyte)>> (bitoffset--)) & 0x01; + if (bitoffset == -1 ) + { //Move onto next byte to get all of numbits + curbyte++; + bitoffset = 7; + } + // Above conditional could also be avoided using the following: + // curbyte -= (bitoffset >> 3); + // bitoffset &= 0x07; + } + *info = inf; + + return bitcounter; // return absolute offset in bit from start of frame + } +} diff --git a/lencod/src/weighted_prediction.c b/lencod/src/weighted_prediction.c new file mode 100644 index 0000000..4570f74 --- /dev/null +++ b/lencod/src/weighted_prediction.c @@ -0,0 +1,885 @@ + +/*! +************************************************************************************* +* \file weighted_prediction.c +* +* \brief +* Estimate weights for WP using DC method +* +* \author +* Main contributors (see contributors.h for copyright, address and affiliation details) +* - Alexis Michael Tourapis +* - Athanasios Leontaris +************************************************************************************* +*/ +#include "contributors.h" + +#include "global.h" +#include "image.h" +#include "wp.h" + +/*! +************************************************************************ +* \brief +* Initialize weighting parameter functions +************************************************************************ +*/ +void InitWP(VideoParameters *p_Vid, InputParameters *p_Inp, int force_wp_method) +{ + if(force_wp_method) + { + p_Vid->EstimateWPPSlice = EstimateWPPSliceAlg1; + p_Vid->EstimateWPBSlice = EstimateWPBSliceAlg1; + p_Vid->TestWPPSlice = TestWPPSliceAlg1; + p_Vid->TestWPBSlice = TestWPBSliceAlg1; + } + else + if (p_Inp->WPIterMC && p_Vid->nal_reference_idc) + { + p_Vid->EstimateWPPSlice = EstimateWPPSliceAlg2; + p_Vid->EstimateWPBSlice = EstimateWPBSliceAlg2; + p_Vid->TestWPPSlice = TestWPPSliceAlg2; + p_Vid->TestWPBSlice = TestWPBSliceAlg2; + } + else + { + int wp_method = p_Inp->WPMethod; + switch (wp_method) + { + default: + case 0: + p_Vid->EstimateWPPSlice = EstimateWPPSliceAlg0; + p_Vid->EstimateWPBSlice = EstimateWPBSliceAlg0; + p_Vid->TestWPPSlice = TestWPPSliceAlg0; + p_Vid->TestWPBSlice = TestWPBSliceAlg0; + break; + case 1: + p_Vid->EstimateWPPSlice = EstimateWPPSliceAlg1; + p_Vid->EstimateWPBSlice = EstimateWPBSliceAlg1; + p_Vid->TestWPPSlice = TestWPPSliceAlg1; + p_Vid->TestWPBSlice = TestWPBSliceAlg1; + break; + } + } +} + +void ResetWP(VideoParameters *p_Vid, InputParameters *p_Inp) +{ + short default_weights[3]; + short luma_log_weight_denom = 5; + short chroma_log_weight_denom = 5; + int cur_slice, clist, n; + + default_weights[0] = 1 << luma_log_weight_denom; + default_weights[1] = default_weights[2] = 1 << chroma_log_weight_denom; + + p_Vid->wp_parameters_set = 0; + for (cur_slice = 0; cur_slice < p_Vid->num_slices_wp; cur_slice++) + { + for (clist = 0; clist < 2 ; clist++) + { + for (n = 0; n < p_Inp->num_ref_frames; n++) + { + p_Vid->wp_weights[0][clist][n][cur_slice] = default_weights[0]; + p_Vid->wp_offsets[0][clist][n][cur_slice] = 0; + p_Vid->wp_weights[1][clist][n][cur_slice] = default_weights[1]; + p_Vid->wp_offsets[1][clist][n][cur_slice] = 0; + p_Vid->wp_weights[2][clist][n][cur_slice] = default_weights[2]; + p_Vid->wp_offsets[2][clist][n][cur_slice] = 0; + } + } + } +} + +void ComputeImplicitWeights(Slice *currSlice, + short default_weight[3], + short im_weight[6][MAX_REFERENCE_PICTURES][MAX_REFERENCE_PICTURES][3]) +{ + VideoParameters *p_Vid = currSlice->p_Vid; + int i, j, comp; + short tx, td, tb, DistScaleFactor; + + for (i = 0; i < currSlice->listXsize[LIST_0]; i++) + { + for (j = 0; j < currSlice->listXsize[LIST_1]; j++) + { + td = (short) iClip3(-128, 127,(currSlice->listX[LIST_1][j]->poc - currSlice->listX[LIST_0][i]->poc)); + tb = (short) iClip3(-128, 127,(p_Vid->ThisPOC - currSlice->listX[LIST_0][i]->poc)); + for (comp = 0; comp < 3; comp++) + { + // implicit weights + if (td == 0) + { + im_weight[0][i][j][comp] = default_weight[comp]; + im_weight[1][i][j][comp] = default_weight[comp]; + } + else + { + tx = (short) (16384 + iabs(td >> 1))/td; + DistScaleFactor = sClip3(-1024, 1023, (tx*tb + 32 )>>6); + im_weight[1][i][j][comp] = DistScaleFactor >> 2; + if (im_weight[1][i][j][comp] < -64 || im_weight[1][i][j][comp] >128) + im_weight[1][i][j][comp] = default_weight[comp]; + im_weight[0][i][j][comp] = 64 - im_weight[1][i][j][comp]; + } + } + } + } +} + +/*! +************************************************************************ +* \brief +* Compute sum of samples in a picture +************************************************************************ +*/ +double ComputeImgSum(imgpel **CurrentImage, int height, int width) +{ + int i, j; + double sum_value = 0.0; + imgpel *p_tmp; + + for (i = 0; i < height; i++) + { + p_tmp = CurrentImage[i]; + for (j = 0; j < width; j++) + { + sum_value += (double) *(p_tmp++); + } + } + return sum_value; +} + +static int ComputeBlockSum(imgpel **CurrentImage, int height_in_blk, int width_in_blk, int blk_size_y, int blk_size_x, int cur_blk) +{ + int x, y; + int sum = 0; + + int blkx = cur_blk % width_in_blk; + int blky = cur_blk / width_in_blk; + int blk_start_x = blkx*blk_size_x; + int blk_start_y = blky*blk_size_y; + imgpel *img_ptr; + + for(y = 0; y < blk_size_y; y++) + { + img_ptr = &CurrentImage[blk_start_y + y][blk_start_x]; + for(x = 0; x < blk_size_x; x++) + { + sum += img_ptr[x]; + } + } + + return sum; +} + +void ComputeImgSumBlockBased(imgpel **CurrentImage, + int height_in_blk, int width_in_blk, + int blk_size_y, int blk_size_x, + int start_blk, int end_blk, double *dc) +{ + int cur_blk; + int block_sum; + int64 slice_sum = 0; + + for(cur_blk = start_blk; cur_blk < end_blk; cur_blk++) + { + block_sum = ComputeBlockSum(CurrentImage, height_in_blk, width_in_blk, blk_size_y, blk_size_x, cur_blk); + slice_sum += block_sum; + } + (*dc) = (double) slice_sum; +} + +int64 ComputeSumBlockBased(imgpel **CurrentImage, + int height_in_blk, int width_in_blk, + int blk_size_y, int blk_size_x, + int start_blk, int end_blk) +{ + int cur_blk; + int64 slice_sum = 0; + + for(cur_blk = start_blk; cur_blk < end_blk; cur_blk++) + { + slice_sum += ComputeBlockSum(CurrentImage, height_in_blk, width_in_blk, blk_size_y, blk_size_x, cur_blk); + } + return slice_sum; +} + +/*! +************************************************************************ +* \brief +* Estimates reference picture weighting factors for P slices +************************************************************************ +*/ + +void EstimateWPPSliceAlg0(Slice *currSlice, int select_offset) +{ + VideoParameters *p_Vid = currSlice->p_Vid; + InputParameters *p_Inp = currSlice->p_Inp; + double dc_org = 0.0; + double dc_org_UV[2] = {0.0}; + double dc_ref[MAX_REFERENCE_PICTURES] = { 0.0 }; + double dc_ref_UV[MAX_REFERENCE_PICTURES][2] = { {0.0}}; + + int i, n, k; + short default_weight[3]; + short cur_weight, cur_offset; + int list_offset = ((currSlice->mb_aff_frame_flag)&&(p_Vid->mb_data[p_Vid->current_mb_nr].mb_field))? (p_Vid->current_mb_nr & 0x01) ? 4 : 2 : 0; + short weight[2][MAX_REFERENCE_PICTURES][3]; + short offset[2][MAX_REFERENCE_PICTURES][3]; + int clist; + + imgpel **tmpPtr; + + currSlice->luma_log_weight_denom = 5; + currSlice->chroma_log_weight_denom = 5; + + currSlice->wp_luma_round = 1 << (currSlice->luma_log_weight_denom - 1); + currSlice->wp_chroma_round = 1 << (currSlice->chroma_log_weight_denom - 1); + default_weight[0] = 1 << currSlice->luma_log_weight_denom; + default_weight[1] = default_weight[2] = 1 << currSlice->chroma_log_weight_denom; + + dc_org = ComputeImgSum(p_Vid->pCurImg, p_Vid->height, p_Vid->width); + + if (p_Inp->ChromaWeightSupport == 1) + { + for (k = 0; k < 2; k++) + { + dc_org_UV[k] = ComputeImgSum(p_Vid->pImgOrg[k + 1], p_Vid->height_cr, p_Vid->width_cr); + } + } + + for (clist = 0; clist < 2 + list_offset; clist++) + { + for (n = 0; n < currSlice->listXsize[clist]; n++) + { + if ( wpxDetermineWP( currSlice, clist, n ) ) + { + /* set all values to defaults */ + for (i = 0; i < 3; i++) + { + weight[clist][n][i] = default_weight[i]; + currSlice->wp_weight[clist][n][i] = default_weight[i]; + currSlice->wp_offset[clist][n][i] = 0; + offset[clist][n][i] = 0; + } + + // Y + tmpPtr = currSlice->listX[clist][n]->p_curr_img; + dc_ref[n] = ComputeImgSum(tmpPtr, p_Vid->height, p_Vid->width); + + if (p_Inp->ChromaWeightSupport == 1) + { + for (k = 0; k < 2; k++) + { + // UV + tmpPtr = currSlice->listX[clist][n]->imgUV[k]; + dc_ref_UV[n][k] = ComputeImgSum(tmpPtr, p_Vid->height_cr, p_Vid->width_cr); + } + } + + if (select_offset == 0) + { + if (dc_ref[n] != 0.0) + cur_weight = (short) (default_weight[0] * dc_org / dc_ref[n] + 0.5); + else + cur_weight = default_weight[0]; // only used when reference picture is black + cur_weight = sClip3(-128, 127, cur_weight); + cur_offset = 0; + + weight[clist][n][0] = cur_weight; + offset[clist][n][0] = cur_offset; + + if (p_Inp->ChromaWeightSupport == 1) + { + if (dc_ref_UV[n][0] != 0) + weight[clist][n][1] = (short) (default_weight[1] * dc_org_UV[0] / dc_ref_UV[n][0] + 0.5); + else + weight[clist][n][1] = default_weight[1]; // only used when reference picture is black + weight[clist][n][1] = sClip3(-128, 127, weight[clist][n][1]); + + if (dc_ref_UV[n][1] != 0) + weight[clist][n][2] = (short) (default_weight[2] * dc_org_UV[1] / dc_ref_UV[n][1] + 0.5); + else + weight[clist][n][2] = default_weight[2]; // only used when reference picture is black + weight[clist][n][2] = sClip3(-128, 127, weight[clist][n][2]); + } + } + else + { + cur_offset = (short) ((dc_org - dc_ref[n])/(p_Vid->size)+0.5); + cur_offset = (cur_offset+((p_Vid->bitdepth_luma-8)>>1))>>(p_Vid->bitdepth_luma-8); + cur_offset = sClip3( -128, 127, cur_offset); + cur_offset = cur_offset<<(p_Vid->bitdepth_luma-8); + cur_weight = default_weight[0]; + + weight[clist][n][0] = cur_weight; + offset[clist][n][0] = cur_offset; + + if (p_Inp->ChromaWeightSupport == 1) + { + offset[clist][n][1] = (short) ((dc_org_UV[0] - dc_ref_UV[n][0])/(p_Vid->size_cr)+0.5); + offset[clist][n][1] = (offset[clist][n][1] + ((p_Vid->bitdepth_chroma - 8)>>1))>>(p_Vid->bitdepth_chroma-8); + offset[clist][n][1] = sClip3( -128, 127, offset[clist][n][1]); + offset[clist][n][1] = offset[clist][n][1]<<(p_Vid->bitdepth_chroma - 8); + + weight[clist][n][1] = default_weight[1]; + + offset[clist][n][2] = (short) ((dc_org_UV[1] - dc_ref_UV[n][1])/(p_Vid->size_cr)+0.5); + offset[clist][n][2] = (offset[clist][n][2] + ((p_Vid->bitdepth_chroma - 8)>>1))>>(p_Vid->bitdepth_chroma-8); + offset[clist][n][2] = sClip3( -128, 127, offset[clist][n][2]); + offset[clist][n][2] = offset[clist][n][2]<<(p_Vid->bitdepth_chroma - 8); + + weight[clist][n][2] = default_weight[2]; + } + } + + for (i=0; i < 3; i ++) + { + currSlice->wp_weight[clist][n][i] = weight[clist][n][i]; + currSlice->wp_offset[clist][n][i] = offset[clist][n][i]; +#if DEBUG_WP + printf("index %d component %d weight %d offset %d\n",n,i,weight[0][n][i],offset[0][n][i]); +#endif + } + } + } + } +} + +/*! +************************************************************************ +* \brief +* Estimates reference picture weighting factors for B slices +************************************************************************ +*/ +void EstimateWPBSliceAlg0(Slice *currSlice) +{ + VideoParameters *p_Vid = currSlice->p_Vid; + InputParameters *p_Inp = currSlice->p_Inp; + int i, j, k, n; + + int index; + int comp; + double dc_org = 0.0; + double dc_org_UV[2] = { 0.0 }; + double dc_ref[6][MAX_REFERENCE_PICTURES] = { {0.0} }; + double dc_ref_UV[6][MAX_REFERENCE_PICTURES][2] = { {{0.0}} }; + + short default_weight[3]; + int list_offset = ((currSlice->mb_aff_frame_flag)&&(p_Vid->mb_data[p_Vid->current_mb_nr].mb_field))? (p_Vid->current_mb_nr & 0x01) ? 4 : 2 : 0; + short weight[6][MAX_REFERENCE_PICTURES][3]; + short offset[6][MAX_REFERENCE_PICTURES][3]; + short im_weight[6][MAX_REFERENCE_PICTURES][MAX_REFERENCE_PICTURES][3]; + int clist; + short wf_weight, wf_offset; + imgpel **tmpPtr; + + if (p_Vid->active_pps->weighted_bipred_idc == 2) //! implicit mode. Values are fixed and it is important to show it here + { + currSlice->luma_log_weight_denom = 5; + currSlice->chroma_log_weight_denom = 5; + } + else //! explicit mode. Values can be changed for higher precision. + { + currSlice->luma_log_weight_denom = 5; + currSlice->chroma_log_weight_denom = 5; + } + + currSlice->wp_luma_round = 1 << (currSlice->luma_log_weight_denom - 1); + currSlice->wp_chroma_round = 1 << (currSlice->chroma_log_weight_denom - 1); + default_weight[0] = 1 << currSlice->luma_log_weight_denom; + default_weight[1] = 1 << currSlice->chroma_log_weight_denom; + default_weight[2] = 1 << currSlice->chroma_log_weight_denom; + + if (p_Vid->active_pps->weighted_bipred_idc == 2) //! implicit mode + { + ComputeImplicitWeights(currSlice, default_weight, im_weight); + + for (k = 0; k < 2; k++) + { + for (i = 0; i < currSlice->listXsize[LIST_0]; i++) + { + for (j = 0; j < currSlice->listXsize[LIST_1]; j++) + { + for (comp = 0; comp < 3; comp++) + { + currSlice->wbp_weight[k][i][j][comp] = im_weight[k][i][j][comp]; + } + } + } + } + + for (clist=0; clist<2 + list_offset; clist++) + { + for (index = 0; index < currSlice->listXsize[clist]; index++) + { + for (comp = 0; comp < 3; comp++) + { + currSlice->wp_weight[clist][index][comp] = default_weight[comp]; + currSlice->wp_offset[clist][index][comp] = 0; + } + } + } + } + else + { + dc_org = ComputeImgSum(p_Vid->pCurImg, p_Vid->height, p_Vid->width); + + if (p_Inp->ChromaWeightSupport == 1) + { + for (k = 0; k < 2; k++) + { + dc_org_UV[k] = ComputeImgSum(p_Vid->pImgOrg[k + 1], p_Vid->height_cr, p_Vid->width_cr); + } + } + + for (clist=0; clist<2 + list_offset; clist++) + { + for (n = 0; n < currSlice->listXsize[clist]; n++) + { + if ( wpxDetermineWP( currSlice, clist, n ) ) + { + /* set all values to defaults */ + for (i = 0; i < 3; i++) + { + currSlice->wp_weight[clist][n][i] = default_weight[i]; + currSlice->wp_offset[clist][n][i] = 0; + offset [clist][n][i] = 0; + weight [clist][n][i] = default_weight[i]; + } + // To simplify these computations we may wish to perform these after a reference is + // stored in the reference buffer and attach them to the storedimage structure!!! + // Y + tmpPtr = currSlice->listX[clist][n]->p_curr_img; + dc_ref[clist][n] = ComputeImgSum(tmpPtr, p_Vid->height, p_Vid->width); + + if (dc_ref[clist][n] != 0.0) + wf_weight = (short) (default_weight[0] * dc_org / dc_ref[clist][n] + 0.5); + else + wf_weight = default_weight[0]; // only used when reference picture is black + wf_weight = sClip3(-128, 127, wf_weight); + wf_offset = 0; + + // printf("dc_org = %d, dc_ref = %d, weight[%d] = %d\n",dc_org, dc_ref[n],n,weight[n][0]); + + weight[clist][n][0] = wf_weight; + offset[clist][n][0] = wf_offset; + + // UV + if (p_Inp->ChromaWeightSupport == 1) + { + for (k = 0; k < 2; k++) + { + tmpPtr = currSlice->listX[clist][n]->imgUV[k]; + dc_ref_UV[clist][n][k] = ComputeImgSum(tmpPtr, p_Vid->height_cr, p_Vid->width_cr); + + if (dc_ref_UV[clist][n][k] != 0.0) + wf_weight = (short) (default_weight[k + 1] * dc_org_UV[k] / dc_ref_UV[clist][n][k] + 0.5); + else + wf_weight = default_weight[k + 1]; // only used when reference picture is black + wf_weight = sClip3(-128, 127, wf_weight); + wf_offset = 0; + + weight[clist][n][k + 1] = wf_weight; + offset[clist][n][k + 1] = wf_offset; + } + } + else + { + weight[clist][n][1] = default_weight[1]; + weight[clist][n][2] = default_weight[2]; + offset[clist][n][1] = 0; + offset[clist][n][2] = 0; + } + + for (i = 0; i < 3; i++) + { + currSlice->wp_weight[clist][n][i] = weight[clist][n][i]; + currSlice->wp_offset[clist][n][i] = offset[clist][n][i]; +#if DEBUG_WP + printf("%d %d\n",currSlice->wp_weight[clist][index][comp],currSlice->wp_offset[clist][index][comp]); +#endif + } + } + } + } + + if (p_Vid->active_pps->weighted_bipred_idc != 1) + { + for (clist=0; clist<2 + list_offset; clist++) + { + for (index = 0; index < currSlice->listXsize[clist]; index++) + { + memcpy(currSlice->wp_weight[clist][index], default_weight, 3 * sizeof(short)); + memset(currSlice->wp_offset[clist][index], 0, 3 * sizeof(short)); + } + } + } + + + for (i = 0; i < currSlice->listXsize[LIST_0]; i++) + { + for (j = 0; j < currSlice->listXsize[LIST_1]; j++) + { + for (comp = 0; comp < 3; comp++) + { + currSlice->wbp_weight[0][i][j][comp] = currSlice->wp_weight[0][i][comp]; + currSlice->wbp_weight[1][i][j][comp] = currSlice->wp_weight[1][j][comp]; + } +#if DEBUG_WP + printf ("bpw weight[%d][%d] = %d , %d (%d %d %d) (%d %d) (%d %d)\n", i, j, currSlice->wbp_weight[0][i][j][0], currSlice->wbp_weight[1][i][j][0], + p_Vid->enc_picture->poc,currSlice->listX[LIST_0][i]->poc, currSlice->listX[LIST_1][j]->poc, + DistScaleFactor ,tx,tx,tx); +#endif + } + } + } +} + + +/*! +************************************************************************ +* \brief +* Tests P slice weighting factors to perform or not WP RD decision +************************************************************************ +*/ + +int TestWPPSliceAlg0(Slice *currSlice, int select_offset) +{ + VideoParameters *p_Vid = currSlice->p_Vid; + InputParameters *p_Inp = p_Vid->p_Inp; + int i, j, k, n; + + int index; + int comp; + double dc_org = 0.0; + double dc_org_UV[2] = {0.0}; + double dc_ref[MAX_REFERENCE_PICTURES] = { 0.0 }; + double dc_ref_UV[MAX_REFERENCE_PICTURES][2] = { {0.0}}; + + short default_weight[3]; + + int list_offset = ((p_Vid->mb_aff_frame_flag)&&(p_Vid->mb_data[p_Vid->current_mb_nr].mb_field))? (p_Vid->current_mb_nr & 0x01) ? 4 : 2 : 0; + short weight[2][MAX_REFERENCE_PICTURES][3]; + short offset[2][MAX_REFERENCE_PICTURES][3]; + //short wp_weight[6][MAX_REFERENCE_PICTURES][3]; + //short wp_offset[6][MAX_REFERENCE_PICTURES][3]; + int clist; + int perform_wp = 0; + imgpel **tmpPtr; + + short luma_log_weight_denom = 5; + short chroma_log_weight_denom = 5; + + default_weight[0] = 1 << luma_log_weight_denom; + default_weight[1] = default_weight[2] = 1 << chroma_log_weight_denom; + + /* set all values to defaults */ + for (i = 0; i < 2 + list_offset; i++) + { + for (j = 0; j < currSlice->listXsize[i]; j++) + { + for (n = 0; n < 3; n++) + { + weight[i][j][n] = default_weight[n]; + //wp_weight[i][j][n] = default_weight[n]; + //wp_offset[i][j][n] = 0; + offset[i][j][n] = 0; + } + } + } + + dc_org = ComputeImgSum(p_Vid->pCurImg, p_Vid->height, p_Vid->width); + + if (p_Inp->ChromaWeightSupport == 1) + { + for (k = 0; k < 2; k++) + { + dc_org_UV[k] = ComputeImgSum(p_Vid->pImgOrg[k + 1], p_Vid->height_cr, p_Vid->width_cr); + } + } + + for (clist = 0; clist < 2 + list_offset; clist++) + { + for (n = 0; n < currSlice->listXsize[clist]; n++) + { + tmpPtr = currSlice->listX[clist][n]->p_curr_img; + dc_ref[n] = ComputeImgSum(tmpPtr, p_Vid->height, p_Vid->width); + + if (p_Inp->ChromaWeightSupport == 1) + { + for (k = 0; k < 2; k++) + { + tmpPtr = currSlice->listX[clist][n]->imgUV[k]; + dc_ref_UV[n][k] = ComputeImgSum(tmpPtr, p_Vid->height_cr, p_Vid->width_cr); + } + } + + if (select_offset == 0) + { + if (dc_ref[n] != 0.0) + weight[clist][n][0] = (short) (default_weight[0] * dc_org / dc_ref[n] + 0.5); + else + weight[clist][n][0] = default_weight[0]; // only used when reference picture is black + weight[clist][n][0] = sClip3(-128, 127, weight[clist][n][0]); + + if (p_Inp->ChromaWeightSupport == 1) + { + if (dc_ref_UV[n][0] != 0) + weight[clist][n][1] = (short) (default_weight[1] * dc_org_UV[0] / dc_ref_UV[n][0] + 0.5); + else + weight[clist][n][1] = default_weight[1]; // only used when reference picture is black + weight[clist][n][1] = sClip3(-128, 127, weight[clist][n][1]); + + if (dc_ref_UV[n][1] != 0) + weight[clist][n][2] = (short) (default_weight[2] * dc_org_UV[1] / dc_ref_UV[n][1] + 0.5); + else + weight[clist][n][2] = default_weight[2]; // only used when reference picture is black + weight[clist][n][2] = sClip3(-128, 127, weight[clist][n][2]); + } + } + else + { + offset[clist][n][0] = (short) ((dc_org - dc_ref[n])/(p_Vid->size)+0.5); + offset[clist][n][0] = (offset[clist][n][0]+((p_Vid->bitdepth_luma-8)>>1))>>(p_Vid->bitdepth_luma-8); + offset[clist][n][0] = sClip3( -128, 127, offset[clist][n][0]); + offset[clist][n][0] = offset[clist][n][0]<<(p_Vid->bitdepth_luma-8); + weight[clist][n][0] = default_weight[0]; + + if (p_Inp->ChromaWeightSupport == 1) + { + offset[clist][n][1] = (short) ((dc_org_UV[0] - dc_ref_UV[n][0])/(p_Vid->size_cr)+0.5); + offset[clist][n][1] = (offset[clist][n][1] + ((p_Vid->bitdepth_chroma - 8)>>1))>>(p_Vid->bitdepth_chroma-8); + offset[clist][n][1] = sClip3( -128, 127, offset[clist][n][1]); + offset[clist][n][1] = offset[clist][n][1]<<(p_Vid->bitdepth_chroma - 8); + + weight[clist][n][1] = default_weight[1]; + + offset[clist][n][2] = (short) ((dc_org_UV[1] - dc_ref_UV[n][1])/(p_Vid->size_cr)+0.5); + offset[clist][n][2] = (offset[clist][n][2] + ((p_Vid->bitdepth_chroma - 8)>>1))>>(p_Vid->bitdepth_chroma-8); + offset[clist][n][2] = sClip3( -128, 127, offset[clist][n][2]); + offset[clist][n][2] = offset[clist][n][2]<<(p_Vid->bitdepth_chroma - 8); + + weight[clist][n][2] = default_weight[2]; + } + } + } + } + + for (clist=0; clist<2 + list_offset; clist++) + { + for (index = 0; index < currSlice->listXsize[clist]; index++) + { + for (comp=0; comp < 3; comp ++) + { + int offset_test = p_Inp->RDPSliceBTest && p_Vid->active_sps->profile_idc != BASELINE + ? iabs(offset[clist][index][comp]) > 2 + : offset[clist][index][comp] != 0; + + if (weight[clist][index][comp] != default_weight[0] || offset_test) + { + perform_wp = 1; + break; + } + } + if (perform_wp == 1) break; + } + if (perform_wp == 1) break; + } + + return perform_wp; +} + +/*! +************************************************************************ +* \brief +* TestWPBSliceAlg0: +* Tests B slice weighting prediction +************************************************************************ +*/ +int TestWPBSliceAlg0(Slice *currSlice, int select_method) +{ + VideoParameters *p_Vid = currSlice->p_Vid; + InputParameters *p_Inp = p_Vid->p_Inp; + int i, j, k, n; +#if (MVC_EXTENSION_ENABLE) + int view_id = p_Vid->view_id; +#else + int view_id = 0; +#endif + + int index; + int comp; + double dc_org = 0.0; + double dc_org_UV[2] = { 0.0 }; + double dc_ref[6][MAX_REFERENCE_PICTURES] = { {0.0} }; + double dc_ref_UV[6][MAX_REFERENCE_PICTURES][2] = { {{0.0}} }; + + short default_weight[3]; + // this needs to be fixed. + int list_offset = ((p_Vid->mb_aff_frame_flag)&&(p_Vid->mb_data[p_Vid->current_mb_nr].mb_field))? (p_Vid->current_mb_nr & 0x01) ? 4 : 2 : 0; + short weight[6][MAX_REFERENCE_PICTURES][3]; + short offset[6][MAX_REFERENCE_PICTURES][3]; + short im_weight[6][MAX_REFERENCE_PICTURES][MAX_REFERENCE_PICTURES][3]; + short wp_weight[6][MAX_REFERENCE_PICTURES][3]; + short wp_offset[6][MAX_REFERENCE_PICTURES][3]; + //short wbp_weight[6][MAX_REFERENCE_PICTURES][MAX_REFERENCE_PICTURES][3]; + + int clist; + short wf_weight, wf_offset; + int perform_wp = 1; + imgpel **tmpPtr; + + short luma_log_weight_denom = 5; + short chroma_log_weight_denom = 5; + + default_weight[0] = 1 << luma_log_weight_denom; + default_weight[1] = 1 << chroma_log_weight_denom; + default_weight[2] = 1 << chroma_log_weight_denom; + + /* set all values to defaults */ + for (i = 0; i < 2 + list_offset; i++) + { + for (j = 0; j < currSlice->listXsize[i]; j++) + { + for (n = 0; n < 3; n++) + { + wp_weight[i][j][n] = default_weight[n]; + wp_offset[i][j][n] = 0; + weight [i][j][n] = default_weight[n]; + offset [i][j][n] = 0; + } + } + } + + if (select_method == 1) //! implicit mode + { + ComputeImplicitWeights(currSlice, default_weight, im_weight); + + perform_wp = 0; + for (i = 0; i < currSlice->listXsize[LIST_0]; i++) + { + for (j = 0; j < currSlice->listXsize[LIST_1]; j++) + { + if (im_weight[1][i][j][0] != default_weight[0] || im_weight[1][i][j][0] != default_weight[0]) + { + perform_wp = 1; + break; + } + } + if (perform_wp == 1) break; + } + } + else + { + dc_org = ComputeImgSum(p_Vid->pCurImg, p_Vid->height, p_Vid->width); + + if (p_Inp->ChromaWeightSupport == 1) + { + for (k = 0; k < 2; k++) + { + dc_org_UV[k] = ComputeImgSum(p_Vid->pImgOrg[k + 1], p_Vid->height_cr, p_Vid->width_cr); + } + } + + for (clist=0; clist<2 + list_offset; clist++) + { + for (n = 0; n < currSlice->listXsize[clist]; n++) + { + // To simplify these computations we may wish to perform these after a reference is + // stored in the reference buffer and attach them to the storedimage structure!!! + // Y + tmpPtr = currSlice->listX[clist][n]->p_curr_img; + dc_ref[clist][n] = ComputeImgSum(tmpPtr, p_Vid->height, p_Vid->width); + + if (dc_ref[clist][n] != 0.0) + wf_weight = (short) (default_weight[0] * dc_org / dc_ref[clist][n] + 0.5); + else + wf_weight = default_weight[0]; // only used when reference picture is black + wf_weight = sClip3(-128, 127, wf_weight); + wf_offset = 0; + + weight[clist][n][0] = wf_weight; + offset[clist][n][0] = wf_offset; + + // UV + if (p_Inp->ChromaWeightSupport == 1) + { + for (k = 0; k < 2; k++) + { + tmpPtr = currSlice->listX[clist][n]->imgUV[k]; + dc_ref_UV[clist][n][k] = ComputeImgSum(tmpPtr, p_Vid->height_cr, p_Vid->width_cr); + + if (dc_ref_UV[clist][n][k] != 0.0) + wf_weight = (short) (default_weight[k + 1] * dc_org_UV[k] / dc_ref_UV[clist][n][k] + 0.5); + else + wf_weight = default_weight[k + 1]; // only used when reference picture is black + wf_weight = sClip3(-128, 127, wf_weight); + wf_offset = 0; + + weight[clist][n][k + 1] = wf_weight; + offset[clist][n][k + 1] = wf_offset; + } + } + else + { + weight[clist][n][1] = default_weight[1]; + weight[clist][n][2] = default_weight[2]; + offset[clist][n][1] = 0; + offset[clist][n][2] = 0; + } + } + } + + if (select_method == 0) //! explicit mode + { + for (clist=0; clist<2 + list_offset; clist++) + { + for (index = 0; index < currSlice->listXsize[clist]; index++) + { + memcpy(p_Vid->currentSlice->wp_weight[clist][index], weight[clist][index], 3 * sizeof(short)); + memcpy(p_Vid->currentSlice->wp_offset[clist][index], offset[clist][index], 3 * sizeof(short)); + } + } + } + else + { + for (clist=0; clist<2 + list_offset; clist++) + { + for (index = 0; index < currSlice->listXsize[clist]; index++) + { + memcpy(wp_weight[clist][index], default_weight, 3 * sizeof(short)); + memset(wp_offset[clist][index], 0, 3 * sizeof(short)); + } + } + } + } + + if (select_method == 0) //! explicit mode + { + int active_refs[2]; + + active_refs[0] = (p_Inp->B_List0_refs == 0 ? currSlice->listXsize[0] : imin(p_Inp->B_List0_refs[view_id], currSlice->listXsize[0])); + active_refs[1] = (p_Inp->B_List1_refs == 0 ? currSlice->listXsize[1] : imin(p_Inp->B_List1_refs[view_id], currSlice->listXsize[1])); + + perform_wp = 0; + for (clist=0; clist<2 + list_offset; clist++) + { + for (index = 0; index < active_refs[clist]; index++) + { + for (comp=0; comp < 3; comp ++) + { + if (wp_weight[clist][index][comp] != default_weight[comp]) + { + perform_wp = 1; + break; + } + } + if (perform_wp == 1) break; + } + if (perform_wp == 1) break; + } + } + return perform_wp; +} diff --git a/lencod/src/wp_lms.c b/lencod/src/wp_lms.c new file mode 100644 index 0000000..c46ad2f --- /dev/null +++ b/lencod/src/wp_lms.c @@ -0,0 +1,886 @@ + +/*! +************************************************************************************* +* \file wp_lms.c +* +* \brief +* Estimate weights for WP using LMS method +* +* \author +* Main contributors (see contributors.h for copyright, address and affiliation details) +* - Alexis Michael Tourapis +* - Athanasios Leontaris +* - Yan Ye +************************************************************************************* +*/ +#include "contributors.h" + +#include "global.h" +#include "image.h" +#include "wp.h" + + +static double ComputeBlockNormMean(imgpel **CurrentImage, + int height_in_blk, int width_in_blk, + int blk_size_y, int blk_size_x, + int cur_blk, double dc_mean) +{ + int blkx, blky; + int x, y; + double sum = 0.; + imgpel *img_ptr; + + blkx = cur_blk % width_in_blk; + blky = cur_blk / width_in_blk; + + for(y = 0; y < blk_size_y; y++) + { + img_ptr = &CurrentImage[blky*blk_size_y+y][blkx*blk_size_x]; + for(x = 0; x < blk_size_x; x++) + { + sum += dabs((double)img_ptr[x]-dc_mean); + } + } + + return sum; +} + +static void ComputeNormMeanBlockBased(imgpel **CurrentImage, int height_in_blk, int width_in_blk, + int blk_size_y, int blk_size_x, + int start_blk, int end_blk, + double dc_mean, double *norm_slice) +{ + int cur_blk; + double block_norm; + double norm; + + { + norm = 0.; + for(cur_blk = start_blk; cur_blk < end_blk; cur_blk++) + { + block_norm = ComputeBlockNormMean(CurrentImage, height_in_blk, width_in_blk, blk_size_y, blk_size_x, cur_blk, dc_mean); + norm += block_norm; + } + (*norm_slice) = (double) norm; + } +} + +void ComputeExplicitWPParamsLMS(Slice *currSlice, + int select_offset, + int start_mb, + int end_mb, + short default_weight[3], + short weight[6][MAX_REFERENCE_PICTURES][3], + short offset[6][MAX_REFERENCE_PICTURES][3]) +{ + VideoParameters *p_Vid = currSlice->p_Vid; + InputParameters *p_Inp = p_Vid->p_Inp; + int clist, n, k, comp; + int slice_size, slice_size_cr; + + double dc_org[3]; + double dc_ref[3]; + double norm_org[3]; + double norm_ref[3]; + double numer[3]; + double denom[3]; + short cur_weight, cur_offset; + int bit_depth = (p_Vid->bitdepth_luma - 8); + + imgpel **cur_ref; + + for (clist=0; clist< 2; clist++) + { + for (n = 0; n < currSlice->listXsize[clist]; n++) + { + for (comp = 0; comp < 3; comp++) + { + weight[clist][n][comp] = default_weight[comp]; + offset[clist][n][comp] = 0; + } + } + } + + slice_size = (end_mb-start_mb)*MB_BLOCK_SIZE*MB_BLOCK_SIZE; + slice_size_cr = (end_mb-start_mb)*p_Vid->mb_cr_size_x*p_Vid->mb_cr_size_y; + + ComputeImgSumBlockBased(p_Vid->pCurImg, p_Vid->FrameHeightInMbs, p_Vid->PicWidthInMbs, MB_BLOCK_SIZE, MB_BLOCK_SIZE, + start_mb, end_mb, &dc_org[0]); + + norm_org[0] = dc_org[0]/((double) slice_size); + ComputeNormMeanBlockBased(p_Vid->pCurImg, p_Vid->FrameHeightInMbs, p_Vid->PicWidthInMbs, MB_BLOCK_SIZE, MB_BLOCK_SIZE, + start_mb, end_mb, norm_org[0], &numer[0]); + + if (p_Inp->ChromaWeightSupport == 1) + { + for (k = 0; k < 2; k++) + { + ComputeImgSumBlockBased(p_Vid->pImgOrg[k + 1], p_Vid->FrameHeightInMbs, p_Vid->PicWidthInMbs, p_Vid->mb_cr_size_y, p_Vid->mb_cr_size_x, + start_mb, end_mb, &dc_org[k+1]); + } + } + + for (clist = 0; clist < 2; clist++) + { + for (n = 0; n < currSlice->listXsize[clist]; n++) + { + cur_ref = currSlice->listX[clist][n]->p_curr_img; + ComputeImgSumBlockBased(cur_ref, p_Vid->FrameHeightInMbs, p_Vid->PicWidthInMbs, MB_BLOCK_SIZE, MB_BLOCK_SIZE, + start_mb, end_mb, &dc_ref[0]); + norm_ref[0] = dc_ref[0] / ((double) slice_size); + ComputeNormMeanBlockBased(cur_ref, p_Vid->FrameHeightInMbs, p_Vid->PicWidthInMbs, MB_BLOCK_SIZE, MB_BLOCK_SIZE, + start_mb, end_mb, norm_ref[0], &denom[0]); + + if (p_Inp->ChromaWeightSupport == 1) + { + for (k = 0; k < 2; k++) + { + cur_ref = currSlice->listX[clist][n]->imgUV[k]; + ComputeImgSumBlockBased(cur_ref, p_Vid->FrameHeightInMbs, p_Vid->PicWidthInMbs, p_Vid->mb_cr_size_y, p_Vid->mb_cr_size_x, + start_mb, end_mb, &dc_ref[k+1]); + } + } + + if (select_offset == 0) + { + if (denom[0] != 0) + cur_weight = (short) (default_weight[0] * numer[0] / denom[0] + 0.5); + else + cur_weight = default_weight[0]; // only used when reference picture is black + cur_weight = sClip3(-128, 127, cur_weight); + + cur_offset = (short) (norm_org[0] - ((double) cur_weight * norm_ref[0] / (double) default_weight[0]) + 0.5); + cur_offset = (short) ((cur_offset + (bit_depth>>1)) >> bit_depth); + cur_offset = sClip3( -128, 127, cur_offset); + cur_offset = (short) (cur_offset << bit_depth); + weight[clist][n][0] = cur_weight; + offset[clist][n][0] = cur_offset; + + if (p_Inp->ChromaWeightSupport == 1) + { + if (dc_ref[1] != 0) + cur_weight = (short) (default_weight[1] * dc_org[1] / dc_ref[1] + 0.5); + else + cur_weight = default_weight[1]; // only used when reference picture is black + weight[clist][n][1] = sClip3(-128, 127, cur_weight); + + if (dc_ref[2] != 0) + cur_weight = (short) (default_weight[2] * dc_org[2] / dc_ref[2] + 0.5); + else + cur_weight = default_weight[2]; // only used when reference picture is black + weight[clist][n][2] = sClip3(-128, 127, cur_weight); + } + } + else + { + cur_offset = (short) ((dc_org[0] - dc_ref[0])/(slice_size)+0.5); + cur_offset = (cur_offset+((p_Vid->bitdepth_luma-8)>>1))>>(p_Vid->bitdepth_luma-8); + cur_offset = sClip3( -128, 127, cur_offset); + cur_offset = cur_offset<<(p_Vid->bitdepth_luma-8); + cur_weight = default_weight[0]; + + weight[clist][n][0] = cur_weight; + offset[clist][n][0] = cur_offset; + + if (p_Inp->ChromaWeightSupport == 1) + { + cur_offset = (short) ((dc_org[1] - dc_ref[1])/(slice_size_cr)+0.5); + cur_offset = (cur_offset + ((p_Vid->bitdepth_chroma - 8)>>1))>>(p_Vid->bitdepth_chroma-8); + cur_offset = sClip3( -128, 127, cur_offset); + offset[clist][n][1] = cur_offset<<(p_Vid->bitdepth_chroma - 8); + + weight[clist][n][1] = default_weight[1]; + + cur_offset = (short) ((dc_org[2] - dc_ref[2])/(slice_size_cr)+0.5); + cur_offset = (cur_offset + ((p_Vid->bitdepth_chroma - 8)>>1))>>(p_Vid->bitdepth_chroma-8); + cur_offset = sClip3( -128, 127, cur_offset); + offset[clist][n][2] = cur_offset<<(p_Vid->bitdepth_chroma - 8); + + weight[clist][n][2] = default_weight[2]; + } + } + } + } +} + + +void ComputeExplicitWPParamsJNT(Slice *currSlice, + int start_mb, + int end_mb, + short default_weight[3], + short weight[6][MAX_REFERENCE_PICTURES][3], + short offset[6][MAX_REFERENCE_PICTURES][3]) +{ + VideoParameters *p_Vid = currSlice->p_Vid; + InputParameters *p_Inp = p_Vid->p_Inp; + int clist, n, k, comp; + + // all these should be converted to int64 + int64 dc_org[3]; + int64 dc_cur_ref[2][MAX_REFERENCE_PICTURES][3]; + short w0 = default_weight[0], w1 = default_weight[0]; + + imgpel **cur_ref; + + for (clist=0; clist< 2; clist++) + { + for (n = 0; n < currSlice->listXsize[clist]; n++) + { + for (comp = 0; comp < 3; comp++) + { + weight[clist][n][comp] = default_weight[comp]; + offset[clist][n][comp] = 0; + } + } + } + + dc_org[0] = ComputeSumBlockBased (p_Vid->pCurImg, p_Vid->FrameHeightInMbs, p_Vid->PicWidthInMbs, MB_BLOCK_SIZE, MB_BLOCK_SIZE, + start_mb, end_mb); + + if (p_Inp->ChromaWeightSupport == 1) + { + for (k = 0; k < 2; k++) + { + dc_org[k+1] = ComputeSumBlockBased(p_Vid->pImgOrg[k + 1], p_Vid->FrameHeightInMbs, p_Vid->PicWidthInMbs, p_Vid->mb_cr_size_y, p_Vid->mb_cr_size_x, + start_mb, end_mb); + } + } + + for (clist = 0; clist < 2; clist++) + { + for (n = 0; n < currSlice->listXsize[clist]; n++) + { + cur_ref = currSlice->listX[clist][n]->p_curr_img; + dc_cur_ref[clist][n][0] = ComputeSumBlockBased (cur_ref, p_Vid->FrameHeightInMbs, p_Vid->PicWidthInMbs, MB_BLOCK_SIZE, MB_BLOCK_SIZE, start_mb, end_mb); + + if (p_Inp->ChromaWeightSupport == 1) + { + for (k = 0; k < 2; k++) + { + cur_ref = currSlice->listX[clist][n]->imgUV[k]; + dc_cur_ref[clist][n][k+1] = ComputeSumBlockBased (cur_ref, p_Vid->FrameHeightInMbs, p_Vid->PicWidthInMbs, p_Vid->mb_cr_size_y, p_Vid->mb_cr_size_x, start_mb, end_mb); + } + } + } + } + + // setup list 1 references using zero ref of LIST_0 + for (n = 0; n < currSlice->listXsize[LIST_1]; n++) + { + + double temp_weight = ( (double)(dc_cur_ref[LIST_1][n][0] - dc_org[0]) / (double) (dc_org[0] - dc_cur_ref[LIST_0][0][0])); + + w0 = (short) (((double) default_weight[0] * 2 * temp_weight) / ( 1.0 + temp_weight) + 0.5); + w1 = (short) (((double) default_weight[0] * 2) / ( 1.0 + temp_weight) + 0.5); + + //printf("%.2f %.2f %.2f %.2f %d %d\n",temp_weight,dc_ref[1][0],dc_ref[0][0], dc_org[0], cm_weight[0 + list_offset][i][j][0] ,cm_weight[1 + list_offset][i][j][0] ); + if (w0 <= 0 || w1 <= 0) + { + w0 = default_weight[0]; + w1 = default_weight[0]; + } + + if (w1 < -64 || w1 > 128 || w0 < -64 || w0 >128) + { + w0 = default_weight[0]; + w1 = default_weight[0]; + } + + weight[LIST_1][n][0] = w1; + offset[LIST_1][n][0] = 0; + + if (p_Inp->ChromaWeightSupport == 1) + { + weight[LIST_1][n][1] = (short) ((double)(default_weight[1] * w1) / (double) default_weight[0]); + weight[LIST_1][n][2] = (short) ((double)(default_weight[2] * w1) / (double) default_weight[0]); + } + } + + // now set LIST_0 weights with 0 reference of LIST_1 + for (n = 0; n < currSlice->listXsize[LIST_0]; n++) + { + double temp_weight = ( (double)(dc_cur_ref[LIST_1][0][0] - dc_org[0]) / (double) (dc_org[0] - dc_cur_ref[LIST_0][n][0])); + + w0 = (short) (((double) default_weight[0] * 2 * temp_weight) / ( 1.0 + temp_weight) + 0.5); + w1 = (short) (((double) default_weight[0] * 2) / ( 1.0 + temp_weight) + 0.5); + + if (w0 <= 0 || w1 <= 0) + { + w0 = default_weight[0]; + w1 = default_weight[0]; + } + + if (w1 < -64 || w1 > 128 || w0 < -64 || w0 >128) + { + w0 = default_weight[0]; + w1 = default_weight[0]; + } + + weight[LIST_0][n][0] = w0; + offset[LIST_0][n][0] = 0; + + if (p_Inp->ChromaWeightSupport == 1) + { + weight[LIST_0][n][1] = (short) ((double)(default_weight[1] * w0) / (double) default_weight[0]); + weight[LIST_0][n][2] = (short) ((double)(default_weight[2] * w0) / (double) default_weight[0]); + } + } +} + +/*! +************************************************************************ +* \brief +* Estimates reference picture weighting factors for P slices +************************************************************************ +*/ + +void EstimateWPPSliceAlg1(Slice *currSlice, int select_offset) +{ + VideoParameters *p_Vid = currSlice->p_Vid; + InputParameters *p_Inp = currSlice->p_Inp; + + int i, n; + short default_weight[3]; + int list_offset = ((currSlice->mb_aff_frame_flag) && (p_Vid->mb_data[p_Vid->current_mb_nr].mb_field))? (p_Vid->current_mb_nr & 0x01) ? 4 : 2 : 0; + int clist; + + int cur_slice; + + currSlice->luma_log_weight_denom = 5; + currSlice->chroma_log_weight_denom = 5; + + currSlice->wp_luma_round = 1 << (currSlice->luma_log_weight_denom - 1); + currSlice->wp_chroma_round = 1 << (currSlice->chroma_log_weight_denom - 1); + default_weight[0] = 1 << currSlice->luma_log_weight_denom; + default_weight[1] = default_weight[2] = 1 << currSlice->chroma_log_weight_denom; + + if(p_Inp->slice_mode == 1) + { + cur_slice = p_Vid->current_mb_nr / p_Inp->slice_argument; + } + else + cur_slice = 0; + + if(p_Vid->wp_parameters_set == 0) + { + int start_mb, end_mb; + int comp; + + short weight[2][MAX_REFERENCE_PICTURES][3]; + short offset[2][MAX_REFERENCE_PICTURES][3]; + + if(p_Inp->slice_mode == 1) + { + start_mb = cur_slice * p_Inp->slice_argument; + end_mb = start_mb + p_Inp->slice_argument; + if(end_mb > (int)p_Vid->FrameSizeInMbs) + end_mb = p_Vid->FrameSizeInMbs; + } + else + { + start_mb = 0; + end_mb = p_Vid->FrameSizeInMbs; + } + + ComputeExplicitWPParamsLMS(currSlice, select_offset, start_mb, end_mb, default_weight, weight, offset); + + for (clist = 0; clist < 2 + list_offset; clist++) + { + for (n = 0; n < currSlice->listXsize[clist]; n++) + { + if ( wpxDetermineWP( currSlice, clist, n ) ) + { + for (comp=0; comp < 3; comp ++) + { + currSlice->wp_weight[clist][n][comp] = weight[clist][n][comp]; + currSlice->wp_offset[clist][n][comp] = offset[clist][n][comp]; + if(p_Vid->wp_weights) + p_Vid->wp_weights[comp][clist][n][cur_slice] = weight[clist][n][comp]; + if(p_Vid->wp_offsets) + p_Vid->wp_offsets[comp][clist][n][cur_slice] = offset[clist][n][comp]; + } +#if DEBUG_WP + for(comp = 0; comp < 3; comp++) + printf("slice %d: index %d component %d weight %d offset %d\n", cur_slice, n,comp,currSlice->wp_weight[clist][n][comp],currSlice->wp_offset[clist][n][comp]); +#endif + } + } + } + } + else + { + for (clist = 0; clist < 2 + list_offset; clist++) + { + for (n = 0; n < currSlice->listXsize[clist]; n++) + { + if ( wpxDetermineWP( currSlice, clist, n ) ) + { + for (i=0; i < 3; i ++) + { + currSlice->wp_weight[clist][n][i] = p_Vid->wp_weights[i][clist][n][cur_slice]; + currSlice->wp_offset[clist][n][i] = p_Vid->wp_offsets[i][clist][n][cur_slice]; +#if DEBUG_WP + printf("slice %d: index %d component %d weight %d offset %d\n", cur_slice, n,i,currSlice->wp_weight[clist][n][i],currSlice->wp_offset[clist][n][i]); +#endif + } + } + } + } + } +} + +/*! +************************************************************************ +* \brief +* Estimates reference picture weighting factors for B slices +************************************************************************ +*/ +void EstimateWPBSliceAlg1(Slice *currSlice) +{ + VideoParameters *p_Vid = currSlice->p_Vid; + InputParameters *p_Inp = currSlice->p_Inp; + int i, j, n; + + int comp; + int k; + + short default_weight[3]; + int list_offset = ((currSlice->mb_aff_frame_flag) && (p_Vid->mb_data[p_Vid->current_mb_nr].mb_field))? (p_Vid->current_mb_nr & 0x01) ? 4 : 2 : 0; + int clist; + int cur_slice = 0; + + if (p_Vid->active_pps->weighted_bipred_idc == 2) //! implicit mode. Values are fixed and it is important to show it here + { + currSlice->luma_log_weight_denom = 5; + currSlice->chroma_log_weight_denom = 5; + } + else //! explicit mode. Values can be changed for higher precision. + { + currSlice->luma_log_weight_denom = 5; + currSlice->chroma_log_weight_denom = 5; + } + + currSlice->wp_luma_round = 1 << (currSlice->luma_log_weight_denom - 1); + currSlice->wp_chroma_round = 1 << (currSlice->chroma_log_weight_denom - 1); + default_weight[0] = 1 << currSlice->luma_log_weight_denom; + default_weight[1] = 1 << currSlice->chroma_log_weight_denom; + default_weight[2] = 1 << currSlice->chroma_log_weight_denom; + + if(p_Inp->slice_mode == 1) + { + cur_slice = p_Vid->current_mb_nr / p_Inp->slice_argument ; + } + else + cur_slice = 0; + + if (p_Vid->wp_parameters_set == 0) // single-pass coding + { + if (p_Vid->active_pps->weighted_bipred_idc == 2) //! implicit mode + { + short im_weight[6][MAX_REFERENCE_PICTURES][MAX_REFERENCE_PICTURES][3]; + + ComputeImplicitWeights(currSlice, default_weight, im_weight); + + for (k = 0; k < 2; k++) + { + for (i = 0; i < currSlice->listXsize[LIST_0]; i++) + { + for (j = 0; j < currSlice->listXsize[LIST_1]; j++) + { + for (comp = 0; comp < 3; comp++) + { + currSlice->wbp_weight[k][i][j][comp] = im_weight[k][i][j][comp]; + } + } + } + } + + for (clist=0; clist<2 + list_offset; clist++) + { + for (i = 0; i < currSlice->listXsize[clist]; i++) + { + for (comp = 0; comp < 3; comp++) + { + currSlice->wp_weight[clist][i][comp] = default_weight[comp]; + currSlice->wp_offset[clist][i][comp] = 0; + } + } + } + } + else // explicit mode + { + int start_mb, end_mb; + + int comp; + + short wp_weight[6][MAX_REFERENCE_PICTURES][3]; + short wp_offset[6][MAX_REFERENCE_PICTURES][3]; + + if(p_Inp->slice_mode == 1) + { + start_mb = cur_slice * p_Inp->slice_argument; + end_mb = start_mb + p_Inp->slice_argument; + if(end_mb > (int)p_Vid->FrameSizeInMbs) + end_mb = p_Vid->FrameSizeInMbs; + } + else + { + start_mb = 0; + end_mb = p_Vid->FrameSizeInMbs; + } + + if (p_Inp->EnhancedBWeightSupport == 2) + ComputeExplicitWPParamsJNT(currSlice, start_mb, end_mb, default_weight, wp_weight, wp_offset); + else + ComputeExplicitWPParamsLMS(currSlice, 0, start_mb, end_mb, default_weight, wp_weight, wp_offset); + + for (clist=0; clist<2 + list_offset; clist++) + { + for (n = 0; n < currSlice->listXsize[clist]; n++) + { + if ( wpxDetermineWP( currSlice, clist, n ) ) + { + for (comp=0; comp < 3; comp ++) + { + currSlice->wp_weight[clist][n][comp] = wp_weight[clist][n][comp]; + currSlice->wp_offset[clist][n][comp] = wp_offset[clist][n][comp]; + if(p_Vid->wp_weights) + p_Vid->wp_weights[comp][clist][n][cur_slice] = wp_weight[clist][n][comp]; + if(p_Vid->wp_offsets) + p_Vid->wp_offsets[comp][clist][n][cur_slice] = wp_offset[clist][n][comp]; + } + } + } + } + + if (p_Vid->active_pps->weighted_bipred_idc != 1) + { + for (clist=0; clist<2 + list_offset; clist++) + { + for (n = 0; n < currSlice->listXsize[clist]; n++) + { + memcpy(currSlice->wp_weight[clist][n], default_weight, 3 * sizeof(short)); + memset(currSlice->wp_offset[clist][n], 0, 3 * sizeof(short)); + } + } + } + + for (i = 0; i < currSlice->listXsize[LIST_0]; i++) + { + for (j = 0; j < currSlice->listXsize[LIST_1]; j++) + { + for (comp = 0; comp < 3; comp++) + { + currSlice->wbp_weight[0][i][j][comp] = currSlice->wp_weight[0][i][comp]; + currSlice->wbp_weight[1][i][j][comp] = currSlice->wp_weight[1][j][comp]; + } + } + } + } + } + else // multiple-pass coding, WP parameters have been decided and stored + if (p_Vid->active_pps->weighted_bipred_idc == 2) //! implicit mode + { + for (k = 0; k < 2; k++) + { + for (i = 0; i < currSlice->listXsize[LIST_0]; i++) + { + for (j = 0; j < currSlice->listXsize[LIST_1]; j++) + { + for (comp = 0; comp < 3; comp++) + { + currSlice->wbp_weight[k][i][j][comp] = p_Vid->wbp_weight[comp][k][i][j][cur_slice]; + } + } + } + } + } + else // explicit WP mode (or no WP at all) + { + for (clist=0; clist<2 + list_offset; clist++) + { + for (n = 0; n < currSlice->listXsize[clist]; n++) + { + if ( wpxDetermineWP( currSlice, clist, n ) ) // use WP with pre-stored parameters + { + for (i=0; i < 3; i ++) + { + currSlice->wp_weight[clist][n][i] = p_Vid->wp_weights[i][clist][n][cur_slice]; + currSlice->wp_offset[clist][n][i] = p_Vid->wp_offsets[i][clist][n][cur_slice]; + } + } + } + } + for (i = 0; i < currSlice->listXsize[LIST_0]; i++) + { + for (j = 0; j < currSlice->listXsize[LIST_1]; j++) + { + for (comp = 0; comp < 3; comp++) + { + currSlice->wbp_weight[0][i][j][comp] = currSlice->wp_weight[0][i][comp]; + currSlice->wbp_weight[1][i][j][comp] = currSlice->wp_weight[1][j][comp]; + } + } + } + } +} + + +/*! +************************************************************************ +* \brief +* Tests P slice weighting factors to perform or not WP RD decision +************************************************************************ +*/ +int TestWPPSliceAlg1(Slice *currSlice, int select_offset) +{ + VideoParameters *p_Vid = currSlice->p_Vid; + InputParameters *p_Inp = p_Vid->p_Inp; + int i, j, n; + + int index; + int comp; + + short default_weight[3]; + + int list_offset = ((p_Vid->mb_aff_frame_flag)&&(p_Vid->mb_data[p_Vid->current_mb_nr].mb_field))? (p_Vid->current_mb_nr & 0x01) ? 4 : 2 : 0; + short weight[6][MAX_REFERENCE_PICTURES][3]; + short offset[6][MAX_REFERENCE_PICTURES][3]; + int clist; + int perform_wp = 0; + + int cur_slice, num_slice; + int start_mb, end_mb; + + short luma_log_weight_denom = 5; + short chroma_log_weight_denom = 5; + + default_weight[0] = 1 << luma_log_weight_denom; + default_weight[1] = default_weight[2] = 1 << chroma_log_weight_denom; + + /* set all values to defaults */ + for (i = 0; i < 2 + list_offset; i++) + { + for (j = 0; j < currSlice->listXsize[i]; j++) + { + for (n = 0; n < 3; n++) + { + weight[i][j][n] = default_weight[n]; + offset[i][j][n] = 0; + } + } + } + + num_slice = p_Vid->num_slices_wp; + for(cur_slice = 0; cur_slice < num_slice; cur_slice++) + { + if(p_Inp->slice_mode == 1) + { + start_mb = cur_slice * p_Inp->slice_argument; + end_mb = start_mb + p_Inp->slice_argument; + if(end_mb > (int)p_Vid->FrameSizeInMbs) + end_mb = p_Vid->FrameSizeInMbs; + } + else + { + start_mb = 0; + end_mb = p_Vid->FrameSizeInMbs; + } + + ComputeExplicitWPParamsLMS(currSlice, select_offset, start_mb, end_mb, default_weight, weight, offset); + + for (clist = 0; clist < 2; clist++) + { + for (n = 0; n < currSlice->listXsize[clist]; n++) + { + for (comp=0; comp < 3; comp ++) + { + p_Vid->wp_weights[comp][clist][n][cur_slice] = weight[clist][n][comp]; + p_Vid->wp_offsets[comp][clist][n][cur_slice] = offset[clist][n][comp]; + } +#if DEBUG_WP + printf ("weight[%d][%d][0] = %d\n", clist, n, weight[clist][n][0]); + printf ("offset[%d][%d][0] = %d\n", clist, n, offset[clist][n][0]); + printf ("weight[%d][%d][1] = %d\n", clist, n, weight[clist][n][1]); + printf ("offset[%d][%d][1] = %d\n", clist, n, offset[clist][n][1]); + printf ("weight[%d][%d][2] = %d\n", clist, n, weight[clist][n][2]); + printf ("offset[%d][%d][2] = %d\n", clist, n, offset[clist][n][2]); +#endif + } + } + } + + perform_wp = 0; + for(cur_slice = 0; cur_slice < num_slice; cur_slice++) + { + for (clist=0; clist<2 + list_offset; clist++) + { + for (index = 0; index < currSlice->listXsize[clist]; index++) + { + for (comp=0; comp < 3; comp ++) + { + int offset_test = p_Inp->RDPSliceBTest && p_Vid->active_sps->profile_idc != BASELINE + ? iabs(p_Vid->wp_offsets[comp][clist][index][cur_slice]) > 2 + : p_Vid->wp_offsets[comp][clist][index][cur_slice] != 0; + + if (p_Vid->wp_weights[comp][clist][index][cur_slice] != default_weight[comp] || offset_test) + { + perform_wp = 1; + break; + } + } + if (perform_wp == 1) break; + } + if (perform_wp == 1) break; + } + if(perform_wp == 1) break; + } + + p_Vid->wp_parameters_set = 1; + + return perform_wp; +} + + +/*! +************************************************************************ +* \brief +* TestWPBSliceAlg1: +* Tests B slice weighting prediction +************************************************************************ +*/ +int TestWPBSliceAlg1(Slice *currSlice, int select_method) +{ + VideoParameters *p_Vid = currSlice->p_Vid; + InputParameters *p_Inp = p_Vid->p_Inp; + int i, j, n; + + int index, comp; + + short default_weight[3]; + // this needs to be fixed. + int list_offset = ((p_Vid->mb_aff_frame_flag) && (p_Vid->mb_data[p_Vid->current_mb_nr].mb_field))? (p_Vid->current_mb_nr & 0x01) ? 4 : 2 : 0; + short im_weight[6][MAX_REFERENCE_PICTURES][MAX_REFERENCE_PICTURES][3]; + short wp_weight[6][MAX_REFERENCE_PICTURES][3]; + short wp_offset[6][MAX_REFERENCE_PICTURES][3]; + + int cur_slice, num_slice; + int start_mb, end_mb; + + int clist; + int perform_wp = 1; + + short luma_log_weight_denom = 5; + short chroma_log_weight_denom = 5; + + default_weight[0] = 1 << luma_log_weight_denom; + default_weight[1] = 1 << chroma_log_weight_denom; + default_weight[2] = 1 << chroma_log_weight_denom; + + if (select_method == 1) //! implicit WP + { + cur_slice = 0; + num_slice = p_Vid->num_slices_wp; + + ComputeImplicitWeights(currSlice, default_weight, im_weight); + + perform_wp = 1; + for (i = 0; i < currSlice->listXsize[LIST_0]; i++) + { + for (j = 0; j < currSlice->listXsize[LIST_1]; j++) + { + for (comp = 0; comp < 3; comp++) + { + short weight0, weight1; + + if(perform_wp) + { + weight0 = im_weight[0][i][j][comp]; + weight1 = im_weight[1][i][j][comp]; + } + else + { + weight0 = default_weight[comp]; + weight1 = default_weight[comp]; + } + + // propagate implicit weights to all slices + for(cur_slice = 0; cur_slice < num_slice; cur_slice++) + { + p_Vid->wbp_weight[comp][0][i][j][cur_slice] = weight0; + p_Vid->wbp_weight[comp][1][i][j][cur_slice] = weight1; + } + } + } + } + } + else // explicit WP + { + num_slice = p_Vid->num_slices_wp; + for(cur_slice = 0; cur_slice < num_slice; cur_slice++) + { + if(p_Inp->slice_mode == 1) + { + start_mb = cur_slice * p_Inp->slice_argument; + end_mb = start_mb + p_Inp->slice_argument; + if(end_mb > (int)p_Vid->FrameSizeInMbs) + end_mb = p_Vid->FrameSizeInMbs; + } + else + { + start_mb = 0; + end_mb = p_Vid->FrameSizeInMbs; + } + + if (p_Inp->EnhancedBWeightSupport == 2) + ComputeExplicitWPParamsJNT(currSlice, start_mb, end_mb, default_weight, wp_weight, wp_offset); + else + ComputeExplicitWPParamsLMS(currSlice, 0, start_mb, end_mb, default_weight, wp_weight, wp_offset); + + for (clist=0; clist<2 + list_offset; clist++) + { + for (n = 0; n < currSlice->listXsize[clist]; n++) + { + for (comp=0; comp < 3; comp ++) + { + p_Vid->wp_weights[comp][clist][n][cur_slice] = wp_weight[clist][n][comp]; + p_Vid->wp_offsets[comp][clist][n][cur_slice] = wp_offset[clist][n][comp]; + } +#if DEBUG_WP + printf ("weight[%d][%d] = %d\n", clist, n, wp_weight[clist][n][0]); + printf ("offset[%d][%d] = %d\n", clist, n, wp_offset[clist][n][0]); +#endif + } + } + } + + perform_wp = 0; + for(cur_slice = 0; cur_slice < num_slice; cur_slice++) + { + for (clist=0; clist<2 + list_offset; clist++) + { + for (index = 0; index < currSlice->listXsize[clist]; index++) + { + for (comp=0; comp < 3; comp ++) + { + if (p_Vid->wp_weights[comp][clist][index][cur_slice] != default_weight[comp] || + p_Vid->wp_offsets[comp][clist][index][cur_slice] != 0) + { + perform_wp = 1; + break; + } + } + if (perform_wp == 1) break; + } + if (perform_wp == 1) break; + } + if(perform_wp == 1) break; + } + } + + p_Vid->wp_parameters_set = 1; + + return perform_wp; +} + + diff --git a/lencod/src/wp_mciter.c b/lencod/src/wp_mciter.c new file mode 100644 index 0000000..2359eec --- /dev/null +++ b/lencod/src/wp_mciter.c @@ -0,0 +1,874 @@ +/*! +************************************************************************************* +* \file wp_mciter.c +* +* \brief +* Estimate weights for WP using iterative MC method +* +* \author +* Main contributors (see contributors.h for copyright, address and affiliation details) +************************************************************************************* +*/ +#include "contributors.h" + +#include +#include + +#include "global.h" +#include "image.h" +#include "wp.h" + +/*! +************************************************************************ +* \brief +* Estimates reference picture weighting factors for P slices +************************************************************************ +*/ + +void EstimateWPPSliceAlg2(Slice *currSlice, int select_offset) +{ + VideoParameters *p_Vid = currSlice->p_Vid; + InputParameters *p_Inp = currSlice->p_Inp; + double dc_org = 0.0; + double dc_org_UV[2] = {0.0}; + double dc_ref[MAX_REFERENCE_PICTURES] = { 0.0 }; + double dc_ref_UV[MAX_REFERENCE_PICTURES][2] = { {0.0}}; + + int i, n, k; + short default_weight[3]; + int list_offset = ((currSlice->mb_aff_frame_flag)&&(p_Vid->mb_data[p_Vid->current_mb_nr].mb_field))? (p_Vid->current_mb_nr & 0x01) ? 4 : 2 : 0; + short weight[2][MAX_REFERENCE_PICTURES][3]; + short offset[2][MAX_REFERENCE_PICTURES][3]; + int clist; + + imgpel **tmpPtr; + + currSlice->luma_log_weight_denom = 5; + currSlice->chroma_log_weight_denom = 5; + + currSlice->wp_luma_round = 1 << (currSlice->luma_log_weight_denom - 1); + currSlice->wp_chroma_round = 1 << (currSlice->chroma_log_weight_denom - 1); + default_weight[0] = 1 << currSlice->luma_log_weight_denom; + default_weight[1] = default_weight[2] = 1 << currSlice->chroma_log_weight_denom; + + dc_org = ComputeImgSum(p_Vid->pCurImg, p_Vid->height, p_Vid->width); + + if (p_Inp->ChromaWeightSupport == 1) + { + for (k = 0; k < 2; k++) + { + dc_org_UV[k] = ComputeImgSum(p_Vid->pImgOrg[k + 1], p_Vid->height_cr, p_Vid->width_cr); + } + } + + for (clist = 0; clist < 2 + list_offset; clist++) + { + for (n = 0; n < currSlice->listXsize[clist]; n++) + { + if ( wpxDetermineWP( currSlice, clist, n ) ) + { + /* set all values to defaults */ + for (i = 0; i < 3; i++) + { + weight[clist][n][i] = default_weight[i]; + currSlice->wp_weight[clist][n][i] = default_weight[i]; + currSlice->wp_offset[clist][n][i] = 0; + offset[clist][n][i] = 0; + } + + // Y + tmpPtr = currSlice->listX[clist][n]->p_curr_img; + dc_ref[n] = ComputeImgSum(tmpPtr, p_Vid->height, p_Vid->width); + + if (p_Inp->ChromaWeightSupport == 1) + { + for (k = 0; k < 2; k++) + { + // UV + tmpPtr = currSlice->listX[clist][n]->imgUV[k]; + dc_ref_UV[n][k] = ComputeImgSum(tmpPtr, p_Vid->height_cr, p_Vid->width_cr); + } + } + + if (select_offset == 0) + { + if (dc_ref[n] != 0.0) + weight[clist][n][0] = (short) (default_weight[0] * dc_org / dc_ref[n] + 0.5); + else + weight[clist][n][0] = default_weight[0]; // only used when reference picture is black + weight[clist][n][0] = sClip3(-128, 127, weight[clist][n][0]); + if (p_Inp->ChromaWeightSupport == 1) + { + if (dc_ref_UV[n][0] != 0) + weight[clist][n][1] = (short) (default_weight[1] * dc_org_UV[0] / dc_ref_UV[n][0] + 0.5); + else + weight[clist][n][1] = default_weight[1]; // only used when reference picture is black + weight[clist][n][1] = sClip3(-128, 127, weight[clist][n][1]); + + if (dc_ref_UV[n][1] != 0) + weight[clist][n][2] = (short) (default_weight[2] * dc_org_UV[1] / dc_ref_UV[n][1] + 0.5); + else + weight[clist][n][2] = default_weight[2]; // only used when reference picture is black + weight[clist][n][2] = sClip3(-128, 127, weight[clist][n][2]); + } + } + else + { + offset[clist][n][0] = p_Vid->frameOffset[clist][n]; + + offset[clist][n][0] = (offset[clist][n][0]+((p_Vid->bitdepth_luma-8)>>1))>>(p_Vid->bitdepth_luma - 8); + offset[clist][n][0] = sClip3( -128, 127, offset[clist][n][0]); + offset[clist][n][0] = offset[clist][n][0]<<(p_Vid->bitdepth_luma-8); + weight[clist][n][0] = default_weight[0]; + + if (p_Inp->ChromaWeightSupport == 1) + { + offset[clist][n][1] = (short) ((dc_org_UV[0] - dc_ref_UV[n][0])/(p_Vid->size_cr)+0.5); + offset[clist][n][1] = (offset[clist][n][1] + ((p_Vid->bitdepth_chroma - 8)>>1))>>(p_Vid->bitdepth_chroma - 8); + offset[clist][n][1] = sClip3( -128, 127, offset[clist][n][1]); + offset[clist][n][1] = offset[clist][n][1]<<(p_Vid->bitdepth_chroma - 8); + + weight[clist][n][1] = default_weight[1]; + + offset[clist][n][2] = (short) ((dc_org_UV[1] - dc_ref_UV[n][1])/(p_Vid->size_cr)+0.5); + offset[clist][n][2] = (offset[clist][n][2] + ((p_Vid->bitdepth_chroma - 8)>>1))>>(p_Vid->bitdepth_chroma-8); + offset[clist][n][2] = sClip3( -128, 127, offset[clist][n][2]); + offset[clist][n][2] = offset[clist][n][2]<<(p_Vid->bitdepth_chroma - 8); + + weight[clist][n][2] = default_weight[2]; + } + } + + for (i=0; i < 3; i ++) + { + currSlice->wp_weight[clist][n][i] = weight[clist][n][i]; + currSlice->wp_offset[clist][n][i] = offset[clist][n][i]; +#if DEBUG_WP + printf("index %d component %d weight %d offset %d\n",n,i,weight[0][n][i],offset[0][n][i]); +#endif + } + } + } + } +} + +/*! +************************************************************************ +* \brief +* Estimates reference picture weighting factors for B slices +************************************************************************ +*/ +void EstimateWPBSliceAlg2(Slice *currSlice) +{ + VideoParameters *p_Vid = currSlice->p_Vid; + InputParameters *p_Inp = currSlice->p_Inp; + int i, j, k, n; + + short tx, tb, td, DistScaleFactor; + + int index; + int comp; + //double dc_org = 0.0; + double dc_org_UV[2] = { 0.0 }; + double dc_ref_UV[6][MAX_REFERENCE_PICTURES][2] = { {{0.0}} }; + + short default_weight[3]; + int list_offset = ((currSlice->mb_aff_frame_flag)&&(p_Vid->mb_data[p_Vid->current_mb_nr].mb_field))? (p_Vid->current_mb_nr & 0x01) ? 4 : 2 : 0; + short weight[6][MAX_REFERENCE_PICTURES][3]; + short offset[6][MAX_REFERENCE_PICTURES][3]; + short im_weight[6][MAX_REFERENCE_PICTURES][MAX_REFERENCE_PICTURES][3]; + int clist; + short wf_weight, wf_offset; + imgpel **tmpPtr; + + if (p_Vid->active_pps->weighted_bipred_idc == 2) //! implicit mode. Values are fixed and it is important to show it here + { + currSlice->luma_log_weight_denom = 5; + currSlice->chroma_log_weight_denom = 5; + } + else //! explicit mode. Values can be changed for higher precision. + { + currSlice->luma_log_weight_denom = 5; + currSlice->chroma_log_weight_denom = 5; + } + + currSlice->wp_luma_round = 1 << (currSlice->luma_log_weight_denom - 1); + currSlice->wp_chroma_round = 1 << (currSlice->chroma_log_weight_denom - 1); + default_weight[0] = 1 << currSlice->luma_log_weight_denom; + default_weight[1] = 1 << currSlice->chroma_log_weight_denom; + default_weight[2] = 1 << currSlice->chroma_log_weight_denom; + + if (p_Vid->active_pps->weighted_bipred_idc == 2) //! implicit mode + { + for (i = 0; i < currSlice->listXsize[LIST_0]; i++) + { + for (j = 0; j < currSlice->listXsize[LIST_1]; j++) + { + td = (short) iClip3(-128, 127,(currSlice->listX[LIST_1][j]->poc - currSlice->listX[LIST_0][i]->poc)); + tb = (short)iClip3(-128, 127,(p_Vid->enc_picture->poc - currSlice->listX[LIST_0][i]->poc)); + for (comp = 0; comp < 3; comp++) + { + // implicit weights + if (td == 0) + { + im_weight[0][i][j][comp] = default_weight[comp]; + im_weight[1][i][j][comp] = default_weight[comp]; + } + else + { + tx = (short) (16384 + iabs(td >> 1))/td; + DistScaleFactor = (short) iClip3(-1024, 1023, (tx*tb + 32 )>>6); + im_weight[1][i][j][comp] = DistScaleFactor>>2; + if (im_weight[1][i][j][comp] < -64 || im_weight[1][i][j][comp] >128) + im_weight[1][i][j][comp] = default_weight[comp]; + im_weight[0][i][j][comp] = 64 - im_weight[1][i][j][comp]; + } + } +#if DEBUG_WP + printf ("%d imp weight[%d][%d] = %d , %d (%d %d %d) (%d %d) (%d %d)\n",p_Vid->enc_picture->poc, i, j, im_weight[0][i][j][0], im_weight[1][i][j][0], + p_Vid->enc_picture->poc,currSlice->listX[LIST_0][i]->poc, currSlice->listX[LIST_1][j]->poc, + DistScaleFactor ,tx,td,tb); +#endif + } + } + + for (k = 0; k < 2; k++) + { + for (i = 0; i < currSlice->listXsize[LIST_0]; i++) + { + for (j = 0; j < currSlice->listXsize[LIST_1]; j++) + { + for (comp = 0; comp < 3; comp++) + { + currSlice->wbp_weight[k][i][j][comp] = im_weight[k][i][j][comp]; + } + } + } + } + + for (clist=0; clist<2 + list_offset; clist++) + { + for (index = 0; index < currSlice->listXsize[clist]; index++) + { + for (comp = 0; comp < 3; comp++) + { + currSlice->wp_weight[clist][index][comp] = default_weight[comp]; + currSlice->wp_offset[clist][index][comp] = 0; + } + } + } + } + else + { + //dc_org = ComputeImgSum(p_Vid->pCurImg, p_Vid->height, p_Vid->width); + + if (p_Inp->ChromaWeightSupport == 1) + { + for (k = 0; k < 2; k++) + { + dc_org_UV[k] = ComputeImgSum(p_Vid->pImgOrg[k + 1], p_Vid->height_cr, p_Vid->width_cr); + } + } + + for (clist=0; clist<2 + list_offset; clist++) + { + for (n = 0; n < currSlice->listXsize[clist]; n++) + { + if ( wpxDetermineWP( currSlice, clist, n ) ) + { + /* set all values to defaults */ + for (i = 0; i < 3; i++) + { + currSlice->wp_weight[clist][n][i] = default_weight[i]; + currSlice->wp_offset[clist][n][i] = 0; + offset [clist][n][i] = 0; + weight [clist][n][i] = default_weight[i]; + } + + offset[clist][n][0] = wf_offset = p_Vid->frameOffset[clist][n]; + weight[clist][n][0] = wf_weight = default_weight[0]; + + + // UV + if (p_Inp->ChromaWeightSupport == 1) + { + for (k = 0; k < 2; k++) + { + tmpPtr = currSlice->listX[clist][n]->imgUV[k]; + dc_ref_UV[clist][n][k] = ComputeImgSum(tmpPtr, p_Vid->height_cr, p_Vid->width_cr); + + if (dc_ref_UV[clist][n][k] != 0.0) + wf_weight = (short) (default_weight[k + 1] * dc_org_UV[k] / dc_ref_UV[clist][n][k] + 0.5); + else + wf_weight = default_weight[k + 1]; // only used when reference picture is black + wf_weight = sClip3(-128, 127, wf_weight); + wf_offset = 0; + + weight[clist][n][k + 1] = wf_weight; + offset[clist][n][k + 1] = wf_offset; + } + } + else + { + weight[clist][n][1] = default_weight[1]; + weight[clist][n][2] = default_weight[2]; + offset[clist][n][1] = 0; + offset[clist][n][2] = 0; + } + + for (i = 0; i < 3; i++) + { + currSlice->wp_weight[clist][n][i] = weight[clist][n][i]; + currSlice->wp_offset[clist][n][i] = offset[clist][n][i]; +#if DEBUG_WP + printf("%d %d\n",currSlice->wp_weight[clist][index][comp],currSlice->wp_offset[clist][index][comp]); +#endif + } + } + } + } + + if (p_Vid->active_pps->weighted_bipred_idc != 1) + { + for (clist=0; clist<2 + list_offset; clist++) + { + for (index = 0; index < currSlice->listXsize[clist]; index++) + { + memcpy(currSlice->wp_weight[clist][index], default_weight, 3 * sizeof(short)); + memset(currSlice->wp_offset[clist][index], 0, 3 * sizeof(short)); + } + } + } + + + for (i = 0; i < currSlice->listXsize[LIST_0]; i++) + { + for (j = 0; j < currSlice->listXsize[LIST_1]; j++) + { + for (comp = 0; comp < 3; comp++) + { + currSlice->wbp_weight[0][i][j][comp] = currSlice->wp_weight[0][i][comp]; + currSlice->wbp_weight[1][i][j][comp] = currSlice->wp_weight[1][j][comp]; + } +#if DEBUG_WP + printf ("bpw weight[%d][%d] = %d , %d (%d %d %d) (%d %d) (%d %d)\n", i, j, currSlice->wbp_weight[0][i][j][0], currSlice->wbp_weight[1][i][j][0], + p_Vid->enc_picture->poc,currSlice->listX[LIST_0][i]->poc, currSlice->listX[LIST_1][j]->poc, + DistScaleFactor ,tx,tx,tx); +#endif + } + } + } +} + + +/*! +************************************************************************ +* \brief +* Tests P slice weighting factors to perform or not WP RD decision +************************************************************************ +*/ + +int TestWPPSliceAlg2(Slice *currSlice, int select_offset) +{ + VideoParameters *p_Vid = currSlice->p_Vid; + InputParameters *p_Inp = p_Vid->p_Inp; + int i, j, k, n; + + int index; + int comp; + double dc_org = 0.0; + double dc_org_UV[2] = {0.0}; + double dc_ref[MAX_REFERENCE_PICTURES] = { 0.0 }; + double dc_ref_UV[MAX_REFERENCE_PICTURES][2] = { {0.0}}; + + short default_weight[3]; + + int list_offset = ((p_Vid->mb_aff_frame_flag)&&(p_Vid->mb_data[p_Vid->current_mb_nr].mb_field))? (p_Vid->current_mb_nr & 0x01) ? 4 : 2 : 0; + short weight[2][MAX_REFERENCE_PICTURES][3]; + short offset[2][MAX_REFERENCE_PICTURES][3]; + short wp_weight[6][MAX_REFERENCE_PICTURES][3]; + short wp_offset[6][MAX_REFERENCE_PICTURES][3]; + int clist; + int perform_wp = 0; + imgpel **tmpPtr; + + short luma_log_weight_denom = 5; + short chroma_log_weight_denom = 5; + + default_weight[0] = 1 << luma_log_weight_denom; + default_weight[1] = default_weight[2] = 1 << chroma_log_weight_denom; + + /* set all values to defaults */ + for (i = 0; i < 2 + list_offset; i++) + { + for (j = 0; j < currSlice->listXsize[i]; j++) + { + for (n = 0; n < 3; n++) + { + weight[i][j][n] = default_weight[n]; + wp_weight[i][j][n] = default_weight[n]; + wp_offset[i][j][n] = 0; + offset[i][j][n] = 0; + } + } + } + + dc_org = ComputeImgSum(p_Vid->pCurImg, p_Vid->height, p_Vid->width); + + if (p_Inp->ChromaWeightSupport == 1) + { + for (k = 0; k < 2; k++) + { + dc_org_UV[k] = ComputeImgSum(p_Vid->pImgOrg[k + 1], p_Vid->height_cr, p_Vid->width_cr); + } + } + + for (clist = 0; clist < 2 + list_offset; clist++) + { + for (n = 0; n < currSlice->listXsize[clist]; n++) + { + tmpPtr = currSlice->listX[clist][n]->p_curr_img; + dc_ref[n] = ComputeImgSum(tmpPtr, p_Vid->height, p_Vid->width); + + if (p_Inp->ChromaWeightSupport == 1) + { + for (k = 0; k < 2; k++) + { + tmpPtr = currSlice->listX[clist][n]->imgUV[k]; + dc_ref_UV[n][k] = ComputeImgSum(tmpPtr, p_Vid->height_cr, p_Vid->width_cr); + } + } + + if (select_offset == 0) + { + if (dc_ref[n] != 0.0) + weight[clist][n][0] = (short) (default_weight[0] * dc_org / dc_ref[n] + 0.5); + else + weight[clist][n][0] = default_weight[0]; // only used when reference picture is black + weight[clist][n][0] = sClip3(-128, 127, weight[clist][n][0]); + if (p_Inp->ChromaWeightSupport == 1) + { + if (dc_ref_UV[n][0] != 0) + weight[clist][n][1] = (short) (default_weight[1] * dc_org_UV[0] / dc_ref_UV[n][0] + 0.5); + else + weight[clist][n][1] = default_weight[1]; // only used when reference picture is black + weight[clist][n][1] = sClip3(-128, 127, weight[clist][n][1]); + + if (dc_ref_UV[n][1] != 0) + weight[clist][n][2] = (short) (default_weight[2] * dc_org_UV[1] / dc_ref_UV[n][1] + 0.5); + else + weight[clist][n][2] = default_weight[2]; // only used when reference picture is black + weight[clist][n][2] = sClip3(-128, 127, weight[clist][n][2]); + } + } + else + { + offset[clist][n][0] = p_Vid->frameOffset[clist][n]; + + offset[clist][n][0] = (offset[clist][n][0]+((p_Vid->bitdepth_luma-8)>>1))>>(p_Vid->bitdepth_luma-8); + offset[clist][n][0] = sClip3( -128, 127, offset[clist][n][0]); + offset[clist][n][0] = offset[clist][n][0]<<(p_Vid->bitdepth_luma-8); + weight[clist][n][0] = default_weight[0]; + + if (p_Inp->ChromaWeightSupport == 1) + { + offset[clist][n][1] = (short) ((dc_org_UV[0] - dc_ref_UV[n][0])/(p_Vid->size_cr)+0.5); + offset[clist][n][1] = (offset[clist][n][1] + ((p_Vid->bitdepth_chroma - 8)>>1))>>(p_Vid->bitdepth_chroma-8); + offset[clist][n][1] = sClip3( -128, 127, offset[clist][n][1]); + offset[clist][n][1] = offset[clist][n][1]<<(p_Vid->bitdepth_chroma - 8); + + weight[clist][n][1] = default_weight[1]; + + offset[clist][n][2] = (short) ((dc_org_UV[1] - dc_ref_UV[n][1])/(p_Vid->size_cr)+0.5); + offset[clist][n][2] = (offset[clist][n][2] + ((p_Vid->bitdepth_chroma - 8)>>1))>>(p_Vid->bitdepth_chroma-8); + offset[clist][n][2] = sClip3( -128, 127, offset[clist][n][2]); + offset[clist][n][2] = offset[clist][n][2]<<(p_Vid->bitdepth_chroma - 8); + + weight[clist][n][2] = default_weight[2]; + } + } + } + } + + for (clist=0; clist<2 + list_offset; clist++) + { + for (index = 0; index < currSlice->listXsize[clist]; index++) + { + for (comp=0; comp < 3; comp ++) + { + int offset_test = p_Inp->RDPSliceBTest && p_Vid->active_sps->profile_idc != BASELINE + ? iabs(offset[clist][index][comp]) > 2 + : offset[clist][index][comp] != 0; + + if (weight[clist][index][comp] != default_weight[0] || offset_test) + { + perform_wp = 1; + break; + } + } + if (perform_wp == 1) break; + } + if (perform_wp == 1) break; + } + + return perform_wp; +} + +/*! +************************************************************************ +* \brief +* TestWPBSliceAlg2: +* Tests B slice weighting prediction +************************************************************************ +*/ +int TestWPBSliceAlg2(Slice *currSlice, int select_method) +{ + VideoParameters *p_Vid = currSlice->p_Vid; + InputParameters *p_Inp = p_Vid->p_Inp; + int i, j, k, n; +#if (MVC_EXTENSION_ENABLE) + int view_id = p_Vid->view_id; +#else + int view_id = 0; +#endif + + short tx, td, tb, DistScaleFactor; + + int index; + int comp; + double dc_org = 0.0; + double dc_org_UV[2] = { 0.0 }; + double dc_ref_UV[6][MAX_REFERENCE_PICTURES][2] = { {{0.0}} }; + + short default_weight[3]; + // this needs to be fixed. + int list_offset = ((p_Vid->mb_aff_frame_flag)&&(p_Vid->mb_data[p_Vid->current_mb_nr].mb_field))? (p_Vid->current_mb_nr & 0x01) ? 4 : 2 : 0; + short weight[6][MAX_REFERENCE_PICTURES][3]; + short offset[6][MAX_REFERENCE_PICTURES][3]; + short im_weight[6][MAX_REFERENCE_PICTURES][MAX_REFERENCE_PICTURES][3]; + short wp_weight[6][MAX_REFERENCE_PICTURES][3]; + short wp_offset[6][MAX_REFERENCE_PICTURES][3]; + short wbp_weight[6][MAX_REFERENCE_PICTURES][MAX_REFERENCE_PICTURES][3]; + + int clist; + short wf_weight, wf_offset; + int perform_wp = 0; + imgpel **tmpPtr; + + short luma_log_weight_denom = 5; + short chroma_log_weight_denom = 5; + + default_weight[0] = 1 << luma_log_weight_denom; + default_weight[1] = 1 << chroma_log_weight_denom; + default_weight[2] = 1 << chroma_log_weight_denom; + + /* set all values to defaults */ + for (i = 0; i < 2 + list_offset; i++) + { + for (j = 0; j < currSlice->listXsize[i]; j++) + { + for (n = 0; n < 3; n++) + { + wp_weight[i][j][n] = default_weight[n]; + wp_offset[i][j][n] = 0; + offset [i][j][n] = 0; + weight [i][j][n] = default_weight[n]; + } + } + } + + for (i = 0; i < currSlice->listXsize[LIST_0]; i++) + { + for (j = 0; j < currSlice->listXsize[LIST_1]; j++) + { + td = (short) iClip3(-128, 127,(currSlice->listX[LIST_1][j]->poc - currSlice->listX[LIST_0][i]->poc)); + tb = (short) iClip3(-128, 127,(p_Vid->enc_picture->poc - currSlice->listX[LIST_0][i]->poc)); + for (comp = 0; comp < 3; comp++) + { + // implicit weights + if (td == 0) + { + im_weight[0][i][j][comp] = default_weight[comp]; + im_weight[1][i][j][comp] = default_weight[comp]; + } + else + { + tx = (short) (16384 + iabs(td >> 1))/td; + DistScaleFactor = sClip3(-1024, 1023, (tx*tb + 32 )>>6); + im_weight[1][i][j][comp] = DistScaleFactor >> 2; + if (im_weight[1][i][j][comp] < -64 || im_weight[1][i][j][comp] >128) + im_weight[1][i][j][comp] = default_weight[comp]; + im_weight[0][i][j][comp] = 64 - im_weight[1][i][j][comp]; + } + } + } + } + + + if (select_method == 1) //! implicit mode + { + for (i = 0; i < currSlice->listXsize[LIST_0]; i++) + { + for (j = 0; j < currSlice->listXsize[LIST_1]; j++) + { + for (comp = 0; comp < 3; comp++) + { + wbp_weight[1][i][j][comp] = im_weight[1][i][j][comp] ; + wbp_weight[0][i][j][comp] = im_weight[0][i][j][comp]; + } + } + } + + for (clist=0; clist<2 + list_offset; clist++) + { + for (index = 0; index < currSlice->listXsize[clist]; index++) + { + for (comp = 0; comp < 3; comp++) + { + wp_weight[clist][index][comp] = default_weight[comp]; + wp_offset[clist][index][comp] = 0; + } + } + } + } + else + { + dc_org = ComputeImgSum(p_Vid->pCurImg, p_Vid->height, p_Vid->width); + + if (p_Inp->ChromaWeightSupport == 1) + { + for (k = 0; k < 2; k++) + { + dc_org_UV[k] = ComputeImgSum(p_Vid->pImgOrg[k + 1], p_Vid->height_cr, p_Vid->width_cr); + } + } + + for (clist=0; clist<2 + list_offset; clist++) + { + for (n = 0; n < currSlice->listXsize[clist]; n++) + { + offset[clist][n][0] = wf_offset = p_Vid->frameOffset[clist][n]; + weight[clist][n][0] = wf_weight = default_weight[0]; + + // UV + if (p_Inp->ChromaWeightSupport == 1) + { + for (k = 0; k < 2; k++) + { + tmpPtr = currSlice->listX[clist][n]->imgUV[k]; + dc_ref_UV[clist][n][k] = ComputeImgSum(tmpPtr, p_Vid->height_cr, p_Vid->width_cr); + + if (dc_ref_UV[clist][n][k] != 0.0) + wf_weight = (short) (default_weight[k + 1] * dc_org_UV[k] / dc_ref_UV[clist][n][k] + 0.5); + else + wf_weight = default_weight[k + 1]; // only used when reference picture is black + wf_weight = sClip3(-128, 127, wf_weight); + wf_offset = 0; + + weight[clist][n][k + 1] = wf_weight; + offset[clist][n][k + 1] = wf_offset; + } + } + else + { + weight[clist][n][1] = default_weight[1]; + weight[clist][n][2] = default_weight[2]; + offset[clist][n][1] = 0; + offset[clist][n][2] = 0; + } + } + } + + if (select_method == 0) //! explicit mode + { + for (clist=0; clist<2 + list_offset; clist++) + { + for (index = 0; index < currSlice->listXsize[clist]; index++) + { + memcpy(p_Vid->currentSlice->wp_weight[clist][index], weight[clist][index], 3 * sizeof(short)); + memcpy(p_Vid->currentSlice->wp_offset[clist][index], offset[clist][index], 3 * sizeof(short)); + } + } + } + else + { + for (clist=0; clist<2 + list_offset; clist++) + { + for (index = 0; index < currSlice->listXsize[clist]; index++) + { + memcpy(wp_weight[clist][index], default_weight, 3 * sizeof(short)); + memset(wp_offset[clist][index], 0, 3 * sizeof(short)); + } + } + } + + for (i = 0; i < currSlice->listXsize[LIST_0]; i++) + { + for (j = 0; j < currSlice->listXsize[LIST_1]; j++) + { + for (comp = 0; comp < 3; comp++) + { + wbp_weight[0][i][j][comp] = wp_weight[0][i][comp]; + wbp_weight[1][i][j][comp] = wp_weight[1][j][comp]; + } +#if DEBUG_WP + printf ("bpw weight[%d][%d] = %d , %d (%d %d %d) (%d %d) (%d %d)\n", i, j, p_Vid->currentSlice->wbp_weight[0][i][j][0], p_Vid->currentSlice->wbp_weight[1][i][j][0], + p_Vid->enc_picture->poc,currSlice->listX[LIST_0][i]->poc, currSlice->listX[LIST_1][j]->poc, + DistScaleFactor ,tx,tx,tx); +#endif + } + } + } + + if (select_method == 0) //! implicit mode + { + int active_refs[2]; + + active_refs[0] = (p_Inp->B_List0_refs == 0 ? currSlice->listXsize[0] : imin(p_Inp->B_List0_refs[view_id], currSlice->listXsize[0])); + active_refs[1] = (p_Inp->B_List1_refs == 0 ? currSlice->listXsize[1] : imin(p_Inp->B_List1_refs[view_id], currSlice->listXsize[1])); + + for (clist=0; clist<2 + list_offset; clist++) + { + for (index = 0; index < active_refs[clist]; index++) + { + for (comp=0; comp < 3; comp ++) + { + if (wp_weight[clist][index][comp] != default_weight[comp]) + { + perform_wp = 1; + break; + } + } + if (perform_wp == 1) break; + } + if (perform_wp == 1) break; + } + } + return perform_wp; +} + +void compute_offset(Slice *currSlice) +{ + VideoParameters *p_Vid = currSlice->p_Vid; + PicMotionParams **motion = p_Vid->enc_picture->mv_info; + Macroblock *currMB; + int i, j, x, y, xj, yi, temp, valOrg; + int mvx=0, mvy=0; + int ref_frame=0; + int subblock=0; + int x_orig, y_orig; + int x_pos, y_pos; + int out4Y_width = (p_Vid->width + IMG_PAD_SIZE_X) * 4 - 1; + int out4Y_height = (p_Vid->height + IMG_PAD_SIZE_Y) * 4 - 1; + + int frame, list, offset; + double dtemp; + int numlists = (p_Vid->type == B_SLICE) ? 2 : 1; + + + for(list = 0; list < 2; list++) + { + for(frame = 0; frame < MAX_REFERENCE_PICTURES; frame++) + { + p_Vid->frameOffsetTotal[list][frame] = 0; + p_Vid->frameOffsetCount[list][frame] = 0; + } + } + + + for(i=0; iheight >> 4; i++) //y + { + for(j=0; jwidth >> 4; j++) //x + { + { + currMB = &p_Vid->mb_data[((i*p_Vid->width) >> 4)+j]; + if(IS_INTRA(currMB)) //intra macroblocks are not used for calculation of the filter coeffs. + continue; + + x_orig = MB_BLOCK_SIZE*j; + y_orig = MB_BLOCK_SIZE*i; + + for(subblock = 0; subblock < 16; subblock++) + { + //List 0 + x = x_orig+4*(subblock & 0x03); + y = y_orig+4*(subblock >> 2); + mvx = motion[y >> 2][x >> 2].mv[LIST_0].mv_x; + mvy = motion[y >> 2][x >> 2].mv[LIST_0].mv_y; + ref_frame = motion[y >> 2][x >> 2].ref_idx[LIST_0]; + + if(ref_frame != -1) + { + for(yi = 0; yi < 4; yi++) + { //y + for(xj = 0; xj < 4; xj++) + { //x + valOrg = p_Vid->pCurImg[y+yi][x+xj]; + + //y_pos = imax(0,imin(out4Y_height,4*(y+yi)+4*IMG_PAD_SIZE+mvy)); + y_pos = imax(-4*IMG_PAD_SIZE_Y, imin(out4Y_height, 4*(y+yi)+mvy)); + //x_pos = imax(0,imin(out4Y_width, 4*(x+xj)+4*IMG_PAD_SIZE+mvx)); + x_pos = imax(-4*IMG_PAD_SIZE_X, imin(out4Y_width, 4*(x+xj)+mvx)); + + temp=currSlice->listX[LIST_0][ref_frame]->p_curr_img_sub[(y_pos & 0x03)][(x_pos & 0x03)][y_pos >> 2][x_pos >> 2]; + p_Vid->frameOffsetTotal[LIST_0][ref_frame]+=(valOrg-temp); + p_Vid->frameOffsetCount[LIST_0][ref_frame]++; + } + } + } + + + //List 1 + mvx = motion[y >> 2][x >> 2].mv[LIST_1].mv_x; + mvy = motion[y >> 2][x >> 2].mv[LIST_1].mv_y; + ref_frame = motion[y >> 2][x >> 2].ref_idx[LIST_1]; + if(ref_frame != -1) + { + for(yi = 0; yi < 4; yi++) + { //y + for(xj = 0; xj < 4; xj++) + { //x + valOrg = p_Vid->pCurImg[y+yi][x+xj]; + + //y_pos = imax(0,imin(out4Y_height,4*(y+yi)+4*IMG_PAD_SIZE+mvy)); + y_pos = imax(-4*IMG_PAD_SIZE_Y, imin(out4Y_height, 4*(y+yi) + mvy)); + //x_pos = imax(0, imin(out4Y_width, 4*(x+xj)+4*IMG_PAD_SIZE+mvx)); + x_pos = imax(-4*IMG_PAD_SIZE_X, imin(out4Y_width, 4*(x+xj) + mvx)); + + temp=currSlice->listX[LIST_0][ref_frame]->p_curr_img_sub[(y_pos & 0x03)][(x_pos & 0x03)][y_pos >> 2][x_pos >> 2]; + p_Vid->frameOffsetTotal[LIST_1][ref_frame]+=(valOrg-temp); + p_Vid->frameOffsetCount[LIST_1][ref_frame]++; + } + } + } + }// for(subblock = 0; subblock < 16; subblock++) + } + } + + for(list = 0; list < numlists; list++) + { + for(frame = 0; frame < currSlice->listXsize[list]; frame++) + { + dtemp=(double)p_Vid->frameOffsetTotal[list][frame]; + + if (p_Vid->frameOffsetCount[list][frame]>0) + { + offset=(int)(fabs(dtemp)/(double)p_Vid->frameOffsetCount[list][frame]+0.5); + if (p_Vid->frameOffsetTotal[list][frame]>=0) + { + p_Vid->frameOffset[list][frame] = (short) offset; + } + else + { + p_Vid->frameOffset[list][frame] = (short) -offset; + } + //printf("list %d frame %d offset %d frameOffsetCount %d, frameOffsetTotal %d\n", list, frame, p_Vid->frameOffset[list][frame], p_Vid->frameOffsetCount[list][frame], p_Vid->frameOffsetTotal[list][frame]); + } + //else + //{ + //printf("list %d frame %d, frameOffsetCount=0\n", list, frame); + //} + } + } + } +} diff --git a/lencod/src/wp_mcprec.c b/lencod/src/wp_mcprec.c new file mode 100644 index 0000000..cbd731f --- /dev/null +++ b/lencod/src/wp_mcprec.c @@ -0,0 +1,375 @@ + +/*! +************************************************************************************* +* \file wp_mcprec.c +* +* \brief +* Improved Motion Compensation Precision Scheme using Weighted Prediction +* +* \author +* Main contributors (see contributors.h for copyright, address and affiliation details) +* - Athanasios Leontaris +* - Alexis Michael Tourapis +************************************************************************************* +*/ +#include "contributors.h" + +#include "global.h" +#include "image.h" +#include "slice.h" +#include "list_reorder.h" +#include "wp_mcprec.h" +#include "memalloc.h" + +/*! +************************************************************************ +* \brief +* Initialize WPR object structure +************************************************************************ +*/ + +void wpxInitWPXObject( VideoParameters *p_Vid ) +{ + p_Vid->pWPX = (WPXObject *)malloc( sizeof( WPXObject ) ); + if ( p_Vid->pWPX == NULL ) + { + fprintf( stdout, "\n Error initializing memory for WPXObject. Exiting...\n" ); + exit(1); + } + + // wp_ref_l0 + if ((p_Vid->pWPX->wp_ref_list[LIST_0] = (WeightedPredRefX *) calloc(MAX_REFERENCE_PICTURES, sizeof(WeightedPredRefX))) == NULL) + no_mem_exit("wpxInitWPXObject: p_Vid->pWPX->wp_ref_list[0]"); + // wp_ref_l1 + if ((p_Vid->pWPX->wp_ref_list[LIST_1] = (WeightedPredRefX *) calloc(MAX_REFERENCE_PICTURES, sizeof(WeightedPredRefX))) == NULL) + no_mem_exit("wpxInitWPXObject: p_Vid->pWPX->wp_ref_list[1]"); +} + +/*! +************************************************************************ +* \brief +* Free WPR object structure +************************************************************************ +*/ + +void wpxFreeWPXObject( VideoParameters *p_Vid ) +{ + // wp_ref_l0 + if (p_Vid->pWPX->wp_ref_list[LIST_0]) + free(p_Vid->pWPX->wp_ref_list[LIST_0]); + p_Vid->pWPX->wp_ref_list[LIST_0] = NULL; + // wp_ref_l1 + if (p_Vid->pWPX->wp_ref_list[LIST_1]) + free(p_Vid->pWPX->wp_ref_list[LIST_1]); + p_Vid->pWPX->wp_ref_list[LIST_1] = NULL; + + if ( p_Vid->pWPX != NULL ) + { + free( p_Vid->pWPX ); + } +} + +/*! +************************************************************************ +* \brief +* Initialize WPR coding passes +************************************************************************ +*/ + +void wpxInitWPXPasses( VideoParameters *p_Vid, InputParameters *p_Inp ) +{ + // initialize number of wp reference frames in each list + p_Vid->pWPX->num_wp_ref_list[LIST_0] = 0; + p_Vid->pWPX->num_wp_ref_list[LIST_1] = 0; + + switch( p_Inp->WPMCPrecision ) + { + default: + case 0: + break; + case 1: + p_Vid->pWPX->wp_rd_passes[0].algorithm = WP_REGULAR; + p_Vid->pWPX->wp_rd_passes[1].algorithm = WP_MCPREC_MINUS0; + break; + case 2: + p_Vid->pWPX->wp_rd_passes[0].algorithm = WP_REGULAR; + p_Vid->pWPX->wp_rd_passes[1].algorithm = WP_MCPREC_MINUS0; + p_Vid->pWPX->wp_rd_passes[2].algorithm = WP_MCPREC_MINUS1; + break; + } +} + +/*! +************************************************************************ +* \brief +* Modifies ref_pic_list for all lists +************************************************************************ +*/ + +void wpxModifyRefPicList( Slice *currSlice ) +{ + VideoParameters *p_Vid = currSlice->p_Vid; + unsigned int i, j, cloned_refs; + int default_order_list0[32]; + int default_order_list1[32]; + int re_order[32], *list_order; + int pred_list; + DecodedPictureBuffer *p_Dpb = currSlice->p_Dpb; + + StorablePicture **list; + + // set memory + memset( (void *)default_order_list0, 1<<20, 32 * sizeof( int ) ); + memset( (void *)default_order_list1, 1<<20, 32 * sizeof( int ) ); + memset( (void *)re_order, 1<<20, 32 * sizeof( int ) ); + + // First assign default list orders + list = currSlice->listX[LIST_0]; + for (i=0; i<(unsigned int)(currSlice->num_ref_idx_active[LIST_0]); i++) + { + default_order_list0[i] = list[i]->pic_num; + } + if ( currSlice->slice_type == B_SLICE ) + { + list = currSlice->listX[LIST_1]; + for (i=0; i<(unsigned int)(currSlice->num_ref_idx_active[LIST_1]); i++) + { + default_order_list1[i] = list[i]->pic_num; + } + } + + // obtain the ref_pic_list using POC-based reordering if currSlice->slice_type != B_SLICE + if ( currSlice->slice_type == P_SLICE ) + { + int list_sign[32]; + int poc_diff[32]; + int tmp_value; + int abs_poc_dist; + + for (i=0; i < p_Dpb->ref_frames_in_buffer; i++) + { + re_order[i] = p_Dpb->fs_ref[i]->frame->pic_num; + if (p_Dpb->fs_ref[i]->is_used==3 && (p_Dpb->fs_ref[i]->frame->used_for_reference)&&(!p_Dpb->fs_ref[i]->frame->is_long_term)) + { + abs_poc_dist = iabs(p_Dpb->fs_ref[i]->frame->poc - p_Vid->enc_picture->poc) ; + poc_diff[i] = abs_poc_dist; + list_sign[i] = (p_Vid->enc_picture->poc < p_Dpb->fs_ref[i]->frame->poc) ? +1 : -1; + } + } + + // sort these references based on poc (temporal) distance + for (i=0; i< p_Dpb->ref_frames_in_buffer-1; i++) + { + for (j=i+1; j< p_Dpb->ref_frames_in_buffer; j++) + { + if (poc_diff[i]>poc_diff[j] || (poc_diff[i] == poc_diff[j] && list_sign[j] > list_sign[i])) + { + tmp_value = poc_diff[i]; + poc_diff[i] = poc_diff[j]; + poc_diff[j] = tmp_value; + tmp_value = re_order[i]; + re_order[i] = re_order[j]; + re_order[j] = tmp_value ; + tmp_value = list_sign[i]; + list_sign[i] = list_sign[j]; + list_sign[j] = tmp_value ; + } + } + } + } + // end of POC-based reordering of P_SLICE + + // loop over two lists + for ( pred_list = 0; pred_list < (1 + ( currSlice->slice_type == B_SLICE ? 1 : 0 )); pred_list++ ) + { + if ( pred_list == LIST_0 ) + { + list_order = (currSlice->slice_type == P_SLICE) ? re_order : default_order_list0; + } + else + { + list_order = default_order_list1; + } + + // check algorithms (more flexibility for the future...) + switch( p_Vid->pWPX->curr_wp_rd_pass->algorithm ) + { + default: + case WP_MCPREC_PLUS0: + case WP_MCPREC_PLUS1: + case WP_MCPREC_MINUS0: + case WP_MCPREC_MINUS1: + // ref 0 and 1 point to to the same reference + cloned_refs = p_Vid->pWPX->num_wp_ref_list[pred_list] = 2; + for ( j = 0; j < cloned_refs; j++ ) + { + p_Vid->pWPX->wp_ref_list[pred_list][j].PicNum = list_order[0]; + } + // shift the rest + for ( j = cloned_refs; j < p_Dpb->ref_frames_in_buffer; j++ ) + { + p_Vid->pWPX->wp_ref_list[pred_list][j].PicNum = list_order[j - (cloned_refs - 1)]; + p_Vid->pWPX->num_wp_ref_list[pred_list]++; + } + break; + case WP_MCPREC_MINUS_PLUS0: + // ref 0 and 1 point to to the same reference + cloned_refs = p_Vid->pWPX->num_wp_ref_list[pred_list] = 3; + for ( j = 0; j < cloned_refs; j++ ) + { + p_Vid->pWPX->wp_ref_list[pred_list][j].PicNum = list_order[0]; + } + // shift the rest + for ( j = cloned_refs; j < p_Dpb->ref_frames_in_buffer; j++ ) + { + p_Vid->pWPX->wp_ref_list[pred_list][j].PicNum = list_order[j - (cloned_refs - 1)]; + p_Vid->pWPX->num_wp_ref_list[pred_list]++; + } + break; + } + // constrain list length + p_Vid->pWPX->num_wp_ref_list[pred_list] = imin( p_Vid->pWPX->num_wp_ref_list[pred_list], + ( pred_list == LIST_0 ) ? currSlice->num_ref_idx_active[LIST_0] : currSlice->num_ref_idx_active[LIST_1] ); + } +} + +/*! +************************************************************************ +* \brief +* Determine whether it is fine to determine WP parameters +************************************************************************ +*/ + +int wpxDetermineWP( Slice *currSlice, int clist, int n ) +{ + VideoParameters *p_Vid = currSlice->p_Vid; + InputParameters *p_Inp = currSlice->p_Inp; + int i, j, determine_wp = 0; + short default_weight[3]; + // we assume it's the same as in the WP functions + short luma_log_weight_denom = 5; + short chroma_log_weight_denom = 5; + int cur_list = LIST_0; + + default_weight[0] = 1 << luma_log_weight_denom; + default_weight[1] = 1 << chroma_log_weight_denom; + default_weight[2] = 1 << chroma_log_weight_denom; + + if ( !p_Inp->WPMCPrecision ) + { + determine_wp = 1; + } + else + { + determine_wp = 0; + // check slice type + if ( currSlice->slice_type == P_SLICE ) + { + for (i = 0; i < 3; i++) + { + currSlice->wp_weight[clist][n][i] = default_weight[i]; + } + } + else if ( currSlice->slice_type == B_SLICE ) + { + cur_list = ((clist & 0x01) == LIST_1) ? LIST_0 : LIST_1; + + // single-list prediction + for (i = 0; i < 3; i++) + { + currSlice->wp_weight[clist][n][i] = default_weight[i]; + } + // bi-pred + for (j = 0; j < currSlice->listXsize[cur_list]; j++) + { + for (i = 0; i < 3; i++) + currSlice->wbp_weight[clist][n][j][i] = default_weight[i]; + } + } + // algorithm consideration + switch( p_Vid->pWPX->curr_wp_rd_pass->algorithm ) + { + case WP_MCPREC_PLUS0: + for (i = 0; i < 3; i++) + currSlice->wp_offset[clist][n][i] = (n == 1) ? 1 : 0; + break; + case WP_MCPREC_MINUS0: + for (i = 0; i < 3; i++) + currSlice->wp_offset[clist][n][i] = (n == 1) ? -1 : 0; + break; + case WP_MCPREC_PLUS1: + for (i = 0; i < 3; i++) + currSlice->wp_offset[clist][n][i] = (n == 0) ? 1 : 0; + break; + case WP_MCPREC_MINUS1: + for (i = 0; i < 3; i++) + currSlice->wp_offset[clist][n][i] = (n == 0) ? -1 : 0; + break; + case WP_MCPREC_MINUS_PLUS0: + for (i = 0; i < 3; i++) + currSlice->wp_offset[clist][n][i] = (n == 1) ? -1 : ((n == 2) ? 1 : 0); + break; + default: + case WP_REGULAR: + determine_wp = 1; + break; + } + // check list (play with the WP factors) + if ( currSlice->slice_type == B_SLICE && cur_list == LIST_0 ) + { + for (i = 0; i < 3; i++) + currSlice->wp_offset[clist][n][i] *= 2; + } + // zero out chroma offsets + for (i = 1; i < 3; i++) + { + currSlice->wp_offset[clist][n][i] = 0; + } + } + + return determine_wp; +} + +/*! +************************************************************************ +* \brief +* Modify number of references +************************************************************************ +*/ + +void wpxAdaptRefNum( Slice *currSlice ) +{ + InputParameters *p_Inp = currSlice->p_Inp; + VideoParameters *p_Vid = currSlice->p_Vid; +#if (MVC_EXTENSION_ENABLE) + int view_id = p_Vid->view_id; +#else + int view_id = 0; +#endif + + if ( p_Vid->pWPX->curr_wp_rd_pass->algorithm == WP_REGULAR ) + { + switch( currSlice->slice_type ) + { + default: + case I_SLICE: + break; + case P_SLICE: + if ( currSlice->num_ref_idx_active[LIST_0] == ( p_Inp->P_List0_refs[view_id] * ((currSlice->structure !=0) + 1) ) ) + { + currSlice->listXsize[LIST_0] = currSlice->num_ref_idx_active[LIST_0] = (char) imax( ((currSlice->structure !=0) + 1), currSlice->num_ref_idx_active[LIST_0] - ((currSlice->structure !=0) + 1) ); + } + break; + case B_SLICE: + if ( currSlice->num_ref_idx_active[LIST_0] == ( p_Inp->B_List0_refs[view_id] * ((currSlice->structure !=0) + 1) ) ) + { + currSlice->listXsize[LIST_0] = currSlice->num_ref_idx_active[LIST_0] = (char) imax( ((currSlice->structure !=0) + 1), currSlice->num_ref_idx_active[LIST_0] - ((currSlice->structure !=0) + 1) ); + } + if ( currSlice->num_ref_idx_active[LIST_1] == ( p_Inp->B_List1_refs[view_id] * ((currSlice->structure !=0) + 1) ) ) + { + currSlice->listXsize[LIST_1] = currSlice->num_ref_idx_active[LIST_1] = (char) imax( ((currSlice->structure !=0) + 1), currSlice->num_ref_idx_active[LIST_1] - ((currSlice->structure !=0) + 1) ); + } + break; + } + } +} diff --git a/lencod_vc10.vcxproj b/lencod_vc10.vcxproj new file mode 100644 index 0000000..2c72ed2 --- /dev/null +++ b/lencod_vc10.vcxproj @@ -0,0 +1,525 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + lencod + {9A39F1B7-3DFE-4263-8C02-8070F7D99AFA} + lencod + + + + Application + + + Application + + + Application + + + Application + + + Application + MultiByte + + + Application + MultiByte + + + Application + MultiByte + + + Application + MultiByte + + + + + + + + + + + + + + + + + + + <_ProjectFileVersion>10.0.30319.1 + .\bin\ + .\bin\ + .\lencod\$(Configuration)_$(Platform)_vc10\ + .\lencod\$(Configuration)_$(Platform)_vc10\ + true + true + .\bin\ + .\bin\ + .\lencod\$(Configuration)_$(Platform)_vc10\ + .\lencod\$(Configuration)_$(Platform)_vc10\ + false + false + + + + .\lencod/$(Configuration)_$(Platform)_vc9/lencod.tlb + + + + + Disabled + lencod/inc;lcommon/inc;%(AdditionalIncludeDirectories) + WIN32;_DEBUG;_CONSOLE;_CRT_SECURE_NO_DEPRECATE;%(PreprocessorDefinitions) + EnableFastChecks + MultiThreadedDebug + false + + + .\lencod/$(Configuration)_$(Platform)_vc9/lencod.pch + .\lencod\$(Configuration)_$(Platform)_vc10\ + .\lencod\$(Configuration)_$(Platform)_vc10\ + .\lencod\$(Configuration)_$(Platform)_vc10\ + true + Level3 + true + EditAndContinue + Default + + + + + _DEBUG;%(PreprocessorDefinitions) + 0x0409 + + + Ws2_32.lib;%(AdditionalDependencies) + $(OutDir)$(TargetName)$(TargetExt) + true + true + .\lencod/$(Configuration)_$(Platform)_vc10/lencod.pdb + Console + false + + + MachineX86 + + + + + .\lencod/$(Configuration)_$(Platform)_vc9/lencod.tlb + + + + + Disabled + lencod/inc;lcommon/inc;%(AdditionalIncludeDirectories) + WIN32;_DEBUG;_CONSOLE;_CRT_SECURE_NO_DEPRECATE;%(PreprocessorDefinitions) + EnableFastChecks + MultiThreadedDebug + false + + + .\lencod/$(Configuration)_$(Platform)_vc10/lencod.pch + .\lencod\$(Configuration)_$(Platform)_vc10\ + .\lencod\$(Configuration)_$(Platform)_vc10\ + .\lencod\$(Configuration)_$(Platform)_vc10\ + true + Level3 + true + ProgramDatabase + Default + + + _DEBUG;%(PreprocessorDefinitions) + 0x0409 + + + Ws2_32.lib;%(AdditionalDependencies) + $(OutDir)$(TargetName)$(TargetExt) + true + true + .\lencod/$(Configuration)_$(Platform)_vc10/lencod.pdb + Console + false + + + + + + + .\lencod\$(Configuration)_$(Platform)_vc9/lencod.tlb + + + + + MaxSpeed + AnySuitable + lencod\inc;lcommon\inc;%(AdditionalIncludeDirectories) + WIN32;DEBUG;_CONSOLE;_CRT_SECURE_NO_DEPRECATE;%(PreprocessorDefinitions) + true + MultiThreaded + true + false + + + .\lencod\$(Configuration)_$(Platform)_vc9/lencod.pch + .\lencod\$(Configuration)_$(Platform)_vc10\ + .\lencod\$(Configuration)_$(Platform)_vc10\ + .\lencod\$(Configuration)_$(Platform)_vc10\ + true + Level3 + true + ProgramDatabase + Default + + + + + NDEBUG;%(PreprocessorDefinitions) + 0x0409 + + + /FIXED:NO %(AdditionalOptions) + Ws2_32.lib;%(AdditionalDependencies) + $(OutDir)$(TargetName)$(TargetExt) + true + true + .\lencod\$(Configuration)_$(Platform)_vc9/lencod.pdb + Console + false + + + MachineX86 + + + + + .\lencod\$(Configuration)_$(Platform)_vc9/lencod.tlb + + + + + MaxSpeed + AnySuitable + lencod\inc;lcommon\inc;%(AdditionalIncludeDirectories) + WIN32;DEBUG;_CONSOLE;_CRT_SECURE_NO_DEPRECATE;%(PreprocessorDefinitions) + true + MultiThreaded + true + false + + + .\lencod\$(Configuration)_$(Platform)_vc10/lencod.pch + .\lencod\$(Configuration)_$(Platform)_vc10\ + .\lencod\$(Configuration)_$(Platform)_vc10\ + .\lencod\$(Configuration)_$(Platform)_vc10\ + true + Level3 + true + ProgramDatabase + Default + + + NDEBUG;%(PreprocessorDefinitions) + 0x0409 + + + /FIXED:NO %(AdditionalOptions) + Ws2_32.lib;%(AdditionalDependencies) + $(OutDir)$(TargetName)$(TargetExt) + true + true + .\lencod\$(Configuration)_$(Platform)_vc10/lencod.pdb + Console + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/lencod_vc10.vcxproj.filters b/lencod_vc10.vcxproj.filters new file mode 100644 index 0000000..8ace497 --- /dev/null +++ b/lencod_vc10.vcxproj.filters @@ -0,0 +1,787 @@ + + + + + + + + + {38ba5398-c621-4c6f-a003-bd04c2d69989} + + + {cf3919dc-b0a3-47bb-bc41-b5cdb7f032b6} + + + {2187edb6-51a1-40f5-aa88-c7336509cc35} + + + {c774c2aa-4bbe-4343-8cda-2c1546cbfdab} + + + + + Common Header Files + + + Common Header Files + + + Common Header Files + + + Common Header Files + + + Common Header Files + + + Common Header Files + + + Common Header Files + + + Common Header Files + + + Common Header Files + + + Common Header Files + + + Common Header Files + + + Common Header Files + + + Common Header Files + + + Common Header Files + + + Common Header Files + + + Common Header Files + + + Common Header Files + + + Common Header Files + + + Common Header Files + + + Common Header Files + + + Common Header Files + + + Common Header Files + + + Common Header Files + + + Common Header Files + + + Common Header Files + + + Common Header Files + + + Common Header Files + + + Common Header Files + + + Common Header Files + + + Common Header Files + + + Project Header Files + + + Project Header Files + + + Project Header Files + + + Project Header Files + + + Project Header Files + + + Project Header Files + + + Project Header Files + + + Project Header Files + + + Project Header Files + + + Project Header Files + + + Project Header Files + + + Project Header Files + + + Project Header Files + + + Project Header Files + + + Project Header Files + + + Project Header Files + + + Project Header Files + + + Project Header Files + + + Project Header Files + + + Project Header Files + + + Project Header Files + + + Project Header Files + + + Project Header Files + + + Project Header Files + + + Project Header Files + + + Project Header Files + + + Project Header Files + + + Project Header Files + + + Project Header Files + + + Project Header Files + + + Project Header Files + + + Project Header Files + + + Project Header Files + + + Project Header Files + + + Project Header Files + + + Project Header Files + + + Project Header Files + + + Project Header Files + + + Project Header Files + + + Project Header Files + + + Project Header Files + + + Project Header Files + + + Project Header Files + + + Project Header Files + + + Project Header Files + + + Project Header Files + + + Project Header Files + + + Project Header Files + + + Project Header Files + + + Project Header Files + + + Project Header Files + + + Project Header Files + + + Project Header Files + + + Project Header Files + + + Project Header Files + + + Project Header Files + + + Project Header Files + + + Project Header Files + + + Project Header Files + + + Project Header Files + + + Project Header Files + + + Project Header Files + + + Project Header Files + + + Project Header Files + + + Project Header Files + + + Project Header Files + + + Project Header Files + + + Project Header Files + + + Project Header Files + + + Project Header Files + + + Project Header Files + + + Project Header Files + + + Project Header Files + + + Project Header Files + + + Project Header Files + + + Project Header Files + + + Project Header Files + + + Project Header Files + + + Project Header Files + + + Project Header Files + + + Project Header Files + + + Project Header Files + + + Project Header Files + + + Project Header Files + + + Project Header Files + + + Project Header Files + + + Project Header Files + + + Project Header Files + + + Project Header Files + + + Project Header Files + + + Project Header Files + + + Project Header Files + + + Project Header Files + + + Common Header Files + + + Project Header Files + + + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Project Source Files + + + Common Source Files + + + Common Source Files + + + Common Source Files + + + Common Source Files + + + Common Source Files + + + Common Source Files + + + Common Source Files + + + Common Source Files + + + Common Source Files + + + Common Source Files + + + Common Source Files + + + Common Source Files + + + Common Source Files + + + Common Source Files + + + src + + + src + + + Project Source Files + + + Common Source Files + + + Project Source Files + + + \ No newline at end of file diff --git a/lencod_vc10.vcxproj.user b/lencod_vc10.vcxproj.user new file mode 100644 index 0000000..b1d8a11 --- /dev/null +++ b/lencod_vc10.vcxproj.user @@ -0,0 +1,27 @@ + + + + -d trans_encoder_config_base.cfg + + + ..\jm_debug + WindowsLocalDebugger + + + -d trans_encoder_config_base.cfg + + + ..\jm_debug + WindowsLocalDebugger + + + -d encoder_debug.cfg + WindowsLocalDebugger + ..\jm_debug + + + -d trans_encoder_config_base.cfg + WindowsLocalDebugger + ..\jm_debug + + \ No newline at end of file diff --git a/lencod_vc7.vcproj b/lencod_vc7.vcproj new file mode 100644 index 0000000..b34b85a --- /dev/null +++ b/lencod_vc7.vcproj @@ -0,0 +1,1170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lencod_vc8.vcproj b/lencod_vc8.vcproj new file mode 100644 index 0000000..686bed2 --- /dev/null +++ b/lencod_vc8.vcproj @@ -0,0 +1,1980 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lencod_vc9.vcproj b/lencod_vc9.vcproj new file mode 100644 index 0000000..71d39a3 --- /dev/null +++ b/lencod_vc9.vcproj @@ -0,0 +1,1384 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/rtp_loss/Makefile b/rtp_loss/Makefile new file mode 100644 index 0000000..9a0c45e --- /dev/null +++ b/rtp_loss/Makefile @@ -0,0 +1,97 @@ +### +### Makefile for H.264/AVC RTP loss tool +### +### generated for UNIX/LINUX environments +### by H. Schwarz, Limin Wang +### + + + +NAME= rtp_loss + +### include debug information: 1=yes, 0=no +DBG?= 0 +### enforce 32-bit build : 1=yes, 0=no +M32?= 0 + +DEPEND= dependencies + +BINDIR= ../bin +INCDIR= . +SRCDIR= . +OBJDIR= obj + + +LIBS= -lm +AFLAGS= +CFLAGS= -ffloat-store -fsigned-char +FLAGS= $(CFLAGS) -Wall -I$(INCDIR) -D __USE_LARGEFILE64 -D _FILE_OFFSET_BITS=64 + +ifeq ($(M32),1) +FLAGS+=-m32 +endif + +ifeq ($(DBG),1) +SUFFIX= .dbg +FLAGS+= -g +else +SUFFIX= +FLAGS+= -O2 + +endif + +OBJSUF= .o$(SUFFIX) + +SRC= $(wildcard $(SRCDIR)/*.cpp) +OBJ= $(SRC:$(SRCDIR)/%.cpp=$(OBJDIR)/%.o$(SUFFIX)) +BIN= $(BINDIR)/$(NAME)$(SUFFIX).exe + +.PHONY: default distclean clean tags depend + +default: messages objdir_mk depend bin + +messages: +ifeq ($(M32),1) + @echo 'Compiling with M32 support...' +endif +ifeq ($(DBG),1) + @echo 'Compiling with Debug support...' +endif + +clean: + @echo remove all objects + @rm -rf $(OBJDIR) + +distclean: clean + @rm -f $(DEPEND) tags + @rm -f $(BIN) + +tags: + @echo update tag table + @ctags *.h *.cpp + +bin: $(OBJ) + @echo + @echo 'creating binary "$(BIN)"' + @$(CXX) $(FLAGS) -o $(BIN) $(OBJ) $(LIBS) + @echo '... done' + @echo + +depend: + @echo + @echo 'checking dependencies' + @$(SHELL) -ec '$(CXX) $(FLAGS) -MM $(CFLAGS) -I$(INCDIR) $(SRC) \ + | sed '\''s@\(.*\)\.o[ :]@$(OBJDIR)/\1.o$(SUFFIX):@g'\'' \ + >$(DEPEND)' + @echo + +$(OBJDIR)/%.o$(SUFFIX): $(SRCDIR)/%.cpp + @echo 'compiling object file "$@" ...' + @$(CC) -c -o $@ $(FLAGS) $< + +objdir_mk: + @echo 'Creating $(OBJDIR) ...' + @mkdir -p $(OBJDIR) + +-include $(DEPEND) + diff --git a/rtp_loss/ReadMe.txt b/rtp_loss/ReadMe.txt new file mode 100644 index 0000000..68b1c5d --- /dev/null +++ b/rtp_loss/ReadMe.txt @@ -0,0 +1,13 @@ +This programm can be used to simulate packet loss on RTP files. The C random number +generator is used for selecting the packets. The random number generator is not +initialized befur usage. Therefore subsequent runs of the tool will create identical +loss pattern. + +usage: + + rtp_loss.exe infile outfile losspercent + + + is an optinal parameter that specifies the number of RTP +packets that are kept at the beginning of the file. This can be useful if the loss +of parameter sets or IDR pictures shall be avoided. diff --git a/rtp_loss/rtp_loss.cpp b/rtp_loss/rtp_loss.cpp new file mode 100644 index 0000000..86c273f --- /dev/null +++ b/rtp_loss/rtp_loss.cpp @@ -0,0 +1,136 @@ +// rtp_loss.cpp : Defines the entry point for the console application. +// + +#include +#include +#include +#ifdef WIN32 +#include +#else +#include +#endif + +void print_usage(char *argv []) +{ + printf ("Usage: %s input_file output_file loss_percent \n", argv[0]); + exit (-1); +} + +int keep_packet(int loss_percent) +{ + int rnd; + if (loss_percent>100) + return 1; + if (loss_percent<=0) + return 0; + + rnd = int (100 * (((float) rand()) / RAND_MAX)); + + return (rnd >= loss_percent ); +} + +int main(int argc, char* argv[]) +{ + unsigned int bufsize, pacno=0; + unsigned char buf[65000]; + int i, intime; + FILE *fr; // file for reading + FILE *fw; // file for writing + + + if ((argc != 4) && (argc != 5)) + { + print_usage (argv); + } + + if (NULL == (fr = fopen (argv[1], "rb"))) + { + printf ("%s: cannot open H.264 packet file %s for reading\n", argv[0], argv[1]); + return -2; + } + + if (NULL == (fw = fopen (argv[2], "wb"))) + { + printf ("%s: cannot open H.264 packet file %s for reading\n", argv[0], argv[1]); + fclose (fr); + return -2; + } + + + if (argc==5) + { + for (i=0; i< atoi (argv[4]); i++) + { + if (4 != fread (&bufsize, 1, 4, fr)) + return 0; + if (4 != fread (&intime, 1, 4, fr)) + { + printf ("Panic, cannot read timestamp, old software version file?\n"); + return -1; + } + if (bufsize != fread (buf, 1, bufsize, fr)) + { + printf ("Problems while reading buffer, exit\n"); + return -3; + } + + if (4 != fwrite (&bufsize, 1, 4, fw)) + { + printf ("Problems while writing buffer size, exit\n"); + return -1; + } + if (4 != fwrite (&intime, 1, 4, fw)) + { + printf ("Problems while writing timestamp, exit\n"); + return -1; + } + if (bufsize != fwrite (buf, 1, bufsize, fw)) + { + printf ("Problems while writing buffer, exit\n"); + return -3; + } + pacno++; + } + + } + + for (;;) + { + if (4 != fread (&bufsize, 1, 4, fr)) + return 0; + if (4 != fread (&intime, 1, 4, fr)) + { + printf ("Panic, cannot read timestamp, old software version file?\n"); + return -1; + } + if (bufsize != fread (buf, 1, bufsize, fr)) + { + printf ("Problems while reading buffer, exit\n"); + return -3; + } + if (keep_packet(atoi (argv[3]))) + { + if (4 != fwrite (&bufsize, 1, 4, fw)) + { + printf ("Problems while writing buffer size, exit\n"); + return -1; + } + if (4 != fwrite (&intime, 1, 4, fw)) + { + printf ("Problems while writing timestamp, exit\n"); + return -1; + } + if (bufsize != fwrite (buf, 1, bufsize, fw)) + { + printf ("Problems while writing buffer, exit\n"); + return -3; + } + } + else + { + printf ("lost packet #%d\n", pacno); + } + pacno++; + + } +} diff --git a/rtp_loss/rtp_loss.h b/rtp_loss/rtp_loss.h new file mode 100644 index 0000000..e69de29 diff --git a/rtp_loss/rtp_loss_vc10.vcxproj b/rtp_loss/rtp_loss_vc10.vcxproj new file mode 100644 index 0000000..1e173f3 --- /dev/null +++ b/rtp_loss/rtp_loss_vc10.vcxproj @@ -0,0 +1,90 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {5160AFF8-033C-4944-8AD7-6E960344640F} + Win32Proj + rtp_loss_vc10 + rtp_loss + + + + Application + true + Unicode + + + Application + false + true + Unicode + + + + + + + + + + + + + true + ..\bin\ + $(Configuration)_$(Platform)_vc10\ + + + false + ..\bin\ + $(Configuration)_$(Platform)_vc10\ + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + + + Console + true + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + + + Console + true + true + true + + + + + + + + + + + + \ No newline at end of file diff --git a/rtp_loss/rtp_loss_vc10.vcxproj.user b/rtp_loss/rtp_loss_vc10.vcxproj.user new file mode 100644 index 0000000..695b5c7 --- /dev/null +++ b/rtp_loss/rtp_loss_vc10.vcxproj.user @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/rtp_loss/rtp_loss_vc7.vcproj b/rtp_loss/rtp_loss_vc7.vcproj new file mode 100644 index 0000000..0bce436 --- /dev/null +++ b/rtp_loss/rtp_loss_vc7.vcproj @@ -0,0 +1,199 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/rtp_loss/rtp_loss_vc8.vcproj b/rtp_loss/rtp_loss_vc8.vcproj new file mode 100644 index 0000000..0c8f507 --- /dev/null +++ b/rtp_loss/rtp_loss_vc8.vcproj @@ -0,0 +1,360 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/rtp_loss/rtp_loss_vc9.vcproj b/rtp_loss/rtp_loss_vc9.vcproj new file mode 100644 index 0000000..1f37171 --- /dev/null +++ b/rtp_loss/rtp_loss_vc9.vcproj @@ -0,0 +1,349 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/rtpdump/Makefile b/rtpdump/Makefile new file mode 100644 index 0000000..9861833 --- /dev/null +++ b/rtpdump/Makefile @@ -0,0 +1,96 @@ +### +### Makefile for H.264/AVC RTP dump tool +### +### generated for UNIX/LINUX environments +### by H. Schwarz, Limin Wang +### + + + +NAME= rtpdump + +### include debug information: 1=yes, 0=no +DBG?= 0 +### enforce 32-bit build : 1=yes, 0=no +M32?= 0 + +DEPEND= dependencies + +BINDIR= ../bin +INCDIR= . +SRCDIR= . +OBJDIR= obj + + +LIBS= -lm +CFLAGS= -ffloat-store -fsigned-char +FLAGS= $(CFLAGS) -Wall -I$(INCDIR) -D __USE_LARGEFILE64 -D _FILE_OFFSET_BITS=64 + +ifeq ($(M32),1) +FLAGS+=-m32 +endif + +ifeq ($(DBG),1) +SUFFIX= .dbg +FLAGS+= -g +else +SUFFIX= +FLAGS+= -O2 + +endif + +OBJSUF= .o$(SUFFIX) + +SRC= $(wildcard $(SRCDIR)/*.cpp) +OBJ= $(SRC:$(SRCDIR)/%.cpp=$(OBJDIR)/%.o$(SUFFIX)) +BIN= $(BINDIR)/$(NAME)$(SUFFIX).exe + +.PHONY: default distclean clean tags depend + +default: messages objdir_mk depend bin + +messages: +ifeq ($(M32),1) + @echo 'Compiling with M32 support...' +endif +ifeq ($(DBG),1) + @echo 'Compiling with Debug support...' +endif + +clean: + @echo remove all objects + @rm -rf $(OBJDIR) + +distclean: clean + @rm -f $(DEPEND) tags + @rm -f $(BIN) + +tags: + @echo update tag table + @ctags *.h *.cpp + +bin: $(OBJ) + @echo + @echo 'creating binary "$(BIN)"' + @$(CXX) $(FLAGS) -o $(BIN) $(OBJ) $(LIBS) + @echo '... done' + @echo + +depend: + @echo + @echo 'checking dependencies' + @$(SHELL) -ec '$(CXX) $(FLAGS) -MM $(CFLAGS) -I$(INCDIR) $(SRC) \ + | sed '\''s@\(.*\)\.o[ :]@$(OBJDIR)/\1.o$(SUFFIX):@g'\'' \ + >$(DEPEND)' + @echo + +$(OBJDIR)/%.o$(SUFFIX): $(SRCDIR)/%.cpp + @echo 'compiling object file "$@" ...' + @$(CC) -c -o $@ $(FLAGS) $< + +objdir_mk: + @echo 'Creating $(OBJDIR) ...' + @mkdir -p $(OBJDIR) + +-include $(DEPEND) + diff --git a/rtpdump/ReadMe.txt b/rtpdump/ReadMe.txt new file mode 100644 index 0000000..7e3db1b --- /dev/null +++ b/rtpdump/ReadMe.txt @@ -0,0 +1,34 @@ +======================================================================== + CONSOLE APPLICATION : rtpdump +======================================================================== + + +AppWizard has created this rtpdump application for you. + +This file contains a summary of what you will find in each of the files that +make up your rtpdump application. + +rtpdump.dsp + This file (the project file) contains information at the project level and + is used to build a single project or subproject. Other users can share the + project (.dsp) file, but they should export the makefiles locally. + +rtpdump.cpp + This is the main application source file. + + +///////////////////////////////////////////////////////////////////////////// +Other standard files: + +StdAfx.h, StdAfx.cpp + These files are used to build a precompiled header (PCH) file + named rtpdump.pch and a precompiled types file named StdAfx.obj. + + +///////////////////////////////////////////////////////////////////////////// +Other notes: + +AppWizard uses "TODO:" to indicate parts of the source code you +should add to or customize. + +///////////////////////////////////////////////////////////////////////////// diff --git a/rtpdump/rtpdump.cpp b/rtpdump/rtpdump.cpp new file mode 100644 index 0000000..7be3ac6 --- /dev/null +++ b/rtpdump/rtpdump.cpp @@ -0,0 +1,72 @@ + +// rtpdump.cpp : Defines the entry point for the console application. +// + +#include +#include +#include +#if defined(WIN32) || defined(WIN64) +#include +#else +#include +#endif + + +int main(int argc, char* argv[]) +{ + unsigned int bufsize, pacno=0, temp32; + unsigned short temp16; + unsigned char buf[65000]; + int i, intime; + FILE *f; + + if (argc != 2) + { + printf ("Usage: %s \n", argv[0]); + return -1; + } + + if (NULL == (f = fopen (argv[1], "rb"))) + { + printf ("%s: cannot open H.264 packet file %s for reading\n", argv[0], argv[1]); + return -2; + } + + for (;;) + { + if (4 != fread (&bufsize, 1, 4, f)) + return 0; + + if (4 != fread (&intime, 1, 4, f)) + { + printf ("Panic, cannot read timestamp, old software version file?\n"); + return -1; + } + printf ("\n\npacket #%4d containing %5d bytes\n", pacno++, bufsize); + if (bufsize != fread (buf, 1, bufsize, f)) + { + printf ("Problems while reading buffer, exit\n"); + return -3; + } + + for (i=0; i< 25; i++) + printf ("%02x ", buf[i]); + + printf ("\n"); + printf ("Version (V): %d\n", (buf[0] >> 6) & 0x03); + printf ("Padding (P): %d\n", (buf[0] >> 5) & 0x01); + printf ("Extension (X): %d\n", (buf[0] >> 4) & 0x01); + printf ("CSRC count (CC): %d\n", (buf[0] >> 0) & 0x0F); + printf ("Marker bit (M): %d\n", (buf[1] >> 7) & 0x01); + printf ("Payload Type (PT): %d\n", (buf[1] >> 0) & 0x7F); + memcpy (&temp16, &buf[2], 2); + printf ("Sequence Number: %d\n", ntohs(temp16)); + memcpy (&temp32, &buf[4], 4); + printf ("Timestamp: %d\n", ntohl(temp32)); + memcpy (&temp32, &buf[8], 4); + printf ("SSRC: %d\n", ntohl(temp32)); + + printf ("First Byte: 0x%x\n", buf[12]); + } +} + diff --git a/rtpdump/rtpdump_vc10.vcxproj b/rtpdump/rtpdump_vc10.vcxproj new file mode 100644 index 0000000..3574ee0 --- /dev/null +++ b/rtpdump/rtpdump_vc10.vcxproj @@ -0,0 +1,307 @@ + + + + + Debug_Analyser + Win32 + + + Debug_Analyser + x64 + + + Debug + Win32 + + + Debug + x64 + + + Release_Analyser + Win32 + + + Release_Analyser + x64 + + + Release + Win32 + + + Release + x64 + + + + rtpdump + {BE2D5D97-BD3F-489B-A346-123834DDDBBD} + + + + Application + Unicode + true + + + Application + Unicode + true + + + Application + Unicode + true + + + Application + Unicode + true + + + Application + Unicode + + + Application + Unicode + + + Application + Unicode + + + Application + Unicode + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <_ProjectFileVersion>10.0.30319.1 + ..\bin\ + ..\bin\ + ..\bin\ + ..\bin\ + $(Configuration)_$(Platform)_vc10\ + $(Configuration)_$(Platform)_vc10\ + $(Configuration)_$(Platform)_vc10\ + $(Configuration)_$(Platform)_vc10\ + true + true + true + true + ..\bin\ + ..\bin\ + ..\bin\ + ..\bin\ + $(Configuration)_$(Platform)_vc10\ + $(Configuration)_$(Platform)_vc10\ + $(Configuration)_$(Platform)_vc10\ + $(Configuration)_$(Platform)_vc10\ + false + false + false + false + + + + Disabled + WIN32;_DEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + EnableFastChecks + MultiThreadedDebugDLL + + + Level3 + EditAndContinue + + + Ws2_32.lib;%(AdditionalDependencies) + $(OutDir)$(TargetName)$(TargetExt) + true + Console + false + + + MachineX86 + + + + + Disabled + WIN32;_DEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + EnableFastChecks + MultiThreadedDebugDLL + + + Level3 + EditAndContinue + + + Ws2_32.lib;%(AdditionalDependencies) + $(OutDir)$(TargetName)$(TargetExt) + true + Console + false + + + MachineX86 + + + + + Disabled + WIN32;_DEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + EnableFastChecks + MultiThreadedDebugDLL + + + Level3 + ProgramDatabase + + + Ws2_32.lib;%(AdditionalDependencies) + $(OutDir)$(TargetName)$(TargetExt) + true + Console + false + + + + + + + Disabled + WIN32;_DEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + EnableFastChecks + MultiThreadedDebugDLL + + + Level3 + ProgramDatabase + + + Ws2_32.lib;%(AdditionalDependencies) + $(OutDir)$(TargetName)$(TargetExt) + true + Console + false + + + + + + + WIN32;NDEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + MultiThreadedDLL + + + Level3 + ProgramDatabase + + + Ws2_32.lib;%(AdditionalDependencies) + $(OutDir)$(TargetName)$(TargetExt) + Console + true + true + false + + + MachineX86 + + + + + WIN32;NDEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + MultiThreadedDLL + + + Level3 + ProgramDatabase + + + Ws2_32.lib;%(AdditionalDependencies) + $(OutDir)$(TargetName)$(TargetExt) + Console + true + true + false + + + MachineX86 + + + + + WIN32;NDEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + MultiThreadedDLL + + + Level3 + ProgramDatabase + + + Ws2_32.lib;%(AdditionalDependencies) + $(OutDir)$(TargetName)$(TargetExt) + Console + true + true + false + + + + + + + WIN32;NDEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + MultiThreadedDLL + + + Level3 + ProgramDatabase + + + Ws2_32.lib;%(AdditionalDependencies) + $(OutDir)$(TargetName)$(TargetExt) + Console + true + true + false + + + + + + + + + + + \ No newline at end of file diff --git a/rtpdump/rtpdump_vc10.vcxproj.user b/rtpdump/rtpdump_vc10.vcxproj.user new file mode 100644 index 0000000..695b5c7 --- /dev/null +++ b/rtpdump/rtpdump_vc10.vcxproj.user @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/rtpdump/rtpdump_vc7.vcproj b/rtpdump/rtpdump_vc7.vcproj new file mode 100644 index 0000000..861a771 --- /dev/null +++ b/rtpdump/rtpdump_vc7.vcproj @@ -0,0 +1,174 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/rtpdump/rtpdump_vc8.vcproj b/rtpdump/rtpdump_vc8.vcproj new file mode 100644 index 0000000..f1650de --- /dev/null +++ b/rtpdump/rtpdump_vc8.vcproj @@ -0,0 +1,450 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/rtpdump/rtpdump_vc9.vcproj b/rtpdump/rtpdump_vc9.vcproj new file mode 100644 index 0000000..d47eb9c --- /dev/null +++ b/rtpdump/rtpdump_vc9.vcproj @@ -0,0 +1,344 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/unixprep.sh b/unixprep.sh new file mode 100644 index 0000000..246d4e1 --- /dev/null +++ b/unixprep.sh @@ -0,0 +1,28 @@ +#!/bin/sh + +which sed || (echo "sed unavailable" 1>&2; exit 1) + +echo "Removing DOS LF chars..." +for f in l[ed][ne]cod/[si][rn]c/*.[ch] bin/*.cfg +do + sed -e "s/ //" < $f >$f.tmp && mv $f.tmp $f +done + +for f in l[ed][ne]cod/Makefile +do + sed -e "s/ //" < $f >$f.tmp && mv $f.tmp $f +done + +for f in rtpdump/*.cpp rtpdump/Makefile +do + sed -e "s/ //" < $f >$f.tmp && mv $f.tmp $f +done + +for f in rtp_loss/*.cpp rtp_loss/*.h rtp_loss/Makefile +do + sed -e "s/ //" < $f >$f.tmp && mv $f.tmp $f +done + + +echo "Done." +